diff options
Diffstat (limited to 'accessible/ipc/RemoteAccessibleBase.h')
-rw-r--r-- | accessible/ipc/RemoteAccessibleBase.h | 532 |
1 files changed, 532 insertions, 0 deletions
diff --git a/accessible/ipc/RemoteAccessibleBase.h b/accessible/ipc/RemoteAccessibleBase.h new file mode 100644 index 0000000000..a1de33ca3f --- /dev/null +++ b/accessible/ipc/RemoteAccessibleBase.h @@ -0,0 +1,532 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_a11y_RemoteAccessibleBase_h +#define mozilla_a11y_RemoteAccessibleBase_h + +#include "mozilla/a11y/Accessible.h" +#include "mozilla/a11y/CacheConstants.h" +#include "mozilla/a11y/HyperTextAccessibleBase.h" +#include "mozilla/a11y/Role.h" +#include "AccAttributes.h" +#include "nsIAccessibleText.h" +#include "nsIAccessibleTypes.h" +#include "nsTArray.h" +#include "nsRect.h" +#include "LocalAccessible.h" + +namespace mozilla { +namespace a11y { + +class Attribute; +class DocAccessibleParent; +class RemoteAccessible; +enum class RelationType; + +/** + * The base type for an accessibility tree node that originated in the parent + * process. + */ +template <class Derived> +class RemoteAccessibleBase : public Accessible, public HyperTextAccessibleBase { + public: + virtual ~RemoteAccessibleBase() { MOZ_ASSERT(!mWrapper); } + + virtual bool IsRemote() const override { return true; } + + void AddChildAt(uint32_t aIdx, Derived* aChild) { + mChildren.InsertElementAt(aIdx, aChild); + if (IsHyperText()) { + InvalidateCachedHyperTextOffsets(); + } + } + + virtual uint32_t ChildCount() const override { return mChildren.Length(); } + Derived* RemoteChildAt(uint32_t aIdx) const { + return mChildren.SafeElementAt(aIdx); + } + Derived* RemoteFirstChild() const { + return mChildren.Length() ? mChildren[0] : nullptr; + } + Derived* RemoteLastChild() const { + return mChildren.Length() ? mChildren[mChildren.Length() - 1] : nullptr; + } + Derived* RemotePrevSibling() const { + if (IsDoc()) { + // The normal code path doesn't work for documents because the parent + // might be a local OuterDoc, but IndexInParent() will return 1. + // A document is always a single child of an OuterDoc anyway. + return nullptr; + } + int32_t idx = IndexInParent(); + if (idx == -1) { + return nullptr; // No parent. + } + return idx > 0 ? RemoteParent()->mChildren[idx - 1] : nullptr; + } + Derived* RemoteNextSibling() const { + if (IsDoc()) { + // The normal code path doesn't work for documents because the parent + // might be a local OuterDoc, but IndexInParent() will return 1. + // A document is always a single child of an OuterDoc anyway. + return nullptr; + } + int32_t idx = IndexInParent(); + if (idx == -1) { + return nullptr; // No parent. + } + MOZ_ASSERT(idx >= 0); + size_t newIdx = idx + 1; + return newIdx < RemoteParent()->mChildren.Length() + ? RemoteParent()->mChildren[newIdx] + : nullptr; + } + + // Accessible hierarchy method overrides + + virtual Accessible* Parent() const override { return RemoteParent(); } + + virtual Accessible* ChildAt(uint32_t aIndex) const override { + return RemoteChildAt(aIndex); + } + + virtual Accessible* NextSibling() const override { + return RemoteNextSibling(); + } + + virtual Accessible* PrevSibling() const override { + return RemotePrevSibling(); + } + + // XXX evaluate if this is fast enough. + virtual int32_t IndexInParent() const override { + Derived* parent = RemoteParent(); + if (!parent) { + return -1; + } + return parent->mChildren.IndexOf(static_cast<const Derived*>(this)); + } + virtual uint32_t EmbeddedChildCount() override; + virtual int32_t IndexOfEmbeddedChild(Accessible* aChild) override; + virtual Accessible* EmbeddedChildAt(uint32_t aChildIdx) override; + + void Shutdown(); + + void SetChildDoc(DocAccessibleParent* aChildDoc); + void ClearChildDoc(DocAccessibleParent* aChildDoc); + + /** + * Remove The given child. + */ + void RemoveChild(Derived* aChild) { + mChildren.RemoveElement(aChild); + if (IsHyperText()) { + InvalidateCachedHyperTextOffsets(); + } + } + + /** + * Return the proxy for the parent of the wrapped accessible. + */ + Derived* RemoteParent() const; + + LocalAccessible* OuterDocOfRemoteBrowser() const; + + /** + * Get the role of the accessible we're proxying. + */ + virtual role Role() const override { return mRole; } + + /** + * Return true if this is an embedded object. + */ + bool IsEmbeddedObject() const { return !IsText(); } + + virtual bool IsLink() const override { + if (IsHTMLLink()) { + // XXX: HTML links always return true for IsLink. + return true; + } + + if (IsText()) { + return false; + } + + if (Accessible* parent = Parent()) { + return parent->IsHyperText(); + } + + return false; + } + + virtual bool HasNumericValue() const override { + // XXX: We combine the aria and native "has numeric value" field + // when we serialize the local accessible into eNumericValue. + return HasGenericType(eNumericValue); + } + + // Methods that potentially access a cache. + + virtual ENameValueFlag Name(nsString& aName) const override; + virtual void Description(nsString& aDescription) const override; + virtual void Value(nsString& aValue) const override; + + virtual double CurValue() const override; + virtual double MinValue() const override; + virtual double MaxValue() const override; + virtual double Step() const override; + virtual bool SetCurValue(double aValue) override; + + virtual Accessible* ChildAtPoint( + int32_t aX, int32_t aY, + LocalAccessible::EWhichChildAtPoint aWhichChild) override; + + virtual LayoutDeviceIntRect Bounds() const override; + + virtual nsRect BoundsInAppUnits() const override; + + virtual Relation RelationByType(RelationType aType) const override; + + virtual uint64_t State() override; + + virtual already_AddRefed<AccAttributes> Attributes() override; + + virtual nsAtom* TagName() const override; + + virtual already_AddRefed<nsAtom> InputType() const override; + + virtual already_AddRefed<nsAtom> DisplayStyle() const override; + + virtual float Opacity() const override; + + virtual void LiveRegionAttributes(nsAString* aLive, nsAString* aRelevant, + Maybe<bool>* aAtomic, + nsAString* aBusy) const override; + + virtual Maybe<bool> ARIASelected() const override; + + virtual uint8_t ActionCount() const override; + + virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override; + + virtual bool DoAction(uint8_t aIndex) const override; + + virtual KeyBinding AccessKey() const override; + + virtual void SelectionRanges(nsTArray<TextRange>* aRanges) const override; + + MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual bool RemoveFromSelection( + int32_t aSelectionNum) override; + + virtual Maybe<int32_t> GetIntARIAAttr(nsAtom* aAttrName) const override; + + virtual void Language(nsAString& aLocale) override; + + ////////////////////////////////////////////////////////////////////////////// + // EditableTextAccessible + + MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual void ReplaceText( + const nsAString& aText) override; + MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual void InsertText( + const nsAString& aText, int32_t aPosition) override; + MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual void CopyText(int32_t aStartPos, + int32_t aEndPos) override; + MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual void CutText(int32_t aStartPos, + int32_t aEndPos) override; + MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual void DeleteText(int32_t aStartPos, + int32_t aEndPos) override; + MOZ_CAN_RUN_SCRIPT virtual void PasteText(int32_t aPosition) override; + + ////////////////////////////////////////////////////////////////////////////// + // SelectAccessible + + virtual void SelectedItems(nsTArray<Accessible*>* aItems) override; + + virtual uint32_t SelectedItemCount() override; + + virtual Accessible* GetSelectedItem(uint32_t aIndex) override; + + virtual bool IsItemSelected(uint32_t aIndex) override; + + virtual bool AddItemToSelection(uint32_t aIndex) override; + + virtual bool RemoveItemFromSelection(uint32_t aIndex) override; + + virtual bool SelectAll() override; + + virtual bool UnselectAll() override; + + virtual void TakeSelection() override; + + virtual void SetSelected(bool aSelect) override; + + // Methods that interact with content. + + virtual void TakeFocus() const override; + virtual void ScrollTo(uint32_t aHow) const override; + virtual void SetCaretOffset(int32_t aOffset) override; + + /** + * Allow the platform to store a pointers worth of data on us. + */ + uintptr_t GetWrapper() const { return mWrapper; } + void SetWrapper(uintptr_t aWrapper) { mWrapper = aWrapper; } + + virtual uint64_t ID() const override { return mID; } + + /** + * Return the document containing this proxy, or the proxy itself if it is a + * document. + */ + DocAccessibleParent* Document() const { return mDoc; } + + DocAccessibleParent* AsDoc() const { return IsDoc() ? mDoc : nullptr; } + + void ApplyCache(CacheUpdateType aUpdateType, AccAttributes* aFields) { + const nsTArray<bool> relUpdatesNeeded = PreProcessRelations(aFields); + if (auto maybeViewportCache = + aFields->GetAttribute<nsTArray<uint64_t>>(nsGkAtoms::viewport)) { + // Updating the viewport cache means the offscreen state of this + // document's accessibles has changed. Update the HashSet we use for + // checking offscreen state here. + MOZ_ASSERT(IsDoc(), + "Fetched the viewport cache from a non-doc accessible?"); + AsDoc()->mOnScreenAccessibles.Clear(); + for (auto id : *maybeViewportCache) { + AsDoc()->mOnScreenAccessibles.Insert(id); + } + } + + if (aUpdateType == CacheUpdateType::Initial) { + mCachedFields = aFields; + } else { + if (!mCachedFields) { + // The fields cache can be uninitialized if there were no cache-worthy + // fields in the initial cache push. + // We don't do a simple assign because we don't want to store the + // DeleteEntry entries. + mCachedFields = new AccAttributes(); + } + mCachedFields->Update(aFields); + } + + if (IsTextLeaf()) { + Derived* parent = RemoteParent(); + if (parent && parent->IsHyperText()) { + parent->InvalidateCachedHyperTextOffsets(); + } + } + + PostProcessRelations(relUpdatesNeeded); + } + + void UpdateStateCache(uint64_t aState, bool aEnabled) { + if (aState & kRemoteCalculatedStates) { + return; + } + uint64_t state = 0; + if (mCachedFields) { + if (auto oldState = + mCachedFields->GetAttribute<uint64_t>(nsGkAtoms::state)) { + state = *oldState; + } + } else { + mCachedFields = new AccAttributes(); + } + if (aEnabled) { + state |= aState; + } else { + state &= ~aState; + } + mCachedFields->SetAttribute(nsGkAtoms::state, state); + } + + void InvalidateGroupInfo(); + + virtual void AppendTextTo(nsAString& aText, uint32_t aStartOffset = 0, + uint32_t aLength = UINT32_MAX) override; + + virtual bool TableIsProbablyForLayout(); + + /** + * Iterates through each atom in kRelationTypeAtoms, checking to see + * if it is present in aFields. If it is present (or if aFields contains + * a DeleteEntry() for this atom) and mCachedFields is initialized, + * fetches the old rel targets and removes their existing reverse relations + * stored in mReverseRelations. + * Returns an array of bools where the ith array entry corresponds + * to whether or not the rel at the ith entry of kRelationTypeAtoms + * requires a post-processing update. + */ + nsTArray<bool> PreProcessRelations(AccAttributes* aFields); + + /** + * Takes in the array returned from PreProcessRelations. + * For each entry requiring an update, fetches the new relation + * targets stored in mCachedFields and appropriately + * updates their reverse relations in mReverseRelations. + */ + void PostProcessRelations(const nsTArray<bool>& aToUpdate); + + /** + * This method is called during shutdown, before we clear our + * reverse rel map from the document's mReverseRelations cache. + * Here, we traverse our reverse relations, removing our ID from + * the corresponding forward relation's target list. This ensures + * the stored forward relations do not reference defunct accessibles. + */ + void PruneRelationsOnShutdown(); + + uint32_t GetCachedTextLength(); + Maybe<const nsTArray<int32_t>&> GetCachedTextLines(); + nsRect GetCachedCharRect(int32_t aOffset); + RefPtr<const AccAttributes> GetCachedTextAttributes(); + RefPtr<const AccAttributes> GetCachedARIAAttributes() const; + + nsString GetCachedHTMLNameAttribute() const; + + virtual HyperTextAccessibleBase* AsHyperTextBase() override { + return IsHyperText() ? static_cast<HyperTextAccessibleBase*>(this) + : nullptr; + } + + virtual TableAccessible* AsTable() override; + virtual TableCellAccessible* AsTableCell() override; + + virtual void DOMNodeID(nsString& aID) const override; + + // HyperTextAccessibleBase + virtual already_AddRefed<AccAttributes> DefaultTextAttributes() override; + + /** + * Invalidate cached HyperText offsets. This should be called whenever a + * child is added or removed or the text of a text leaf child is changed. + * Although GetChildOffset can either fully or partially invalidate the + * offsets cache, calculating which offset to invalidate is not worthwhile + * because a client might not even query offsets. This is in contrast to + * LocalAccessible, where the offsets are always needed to fire text change + * events. For RemoteAccessible, it's cheaper overall to just rebuild the + * offsets cache when a client next needs it. + */ + void InvalidateCachedHyperTextOffsets() { + if (mCachedFields) { + mCachedFields->Remove(nsGkAtoms::offset); + } + } + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf); + virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf); + + protected: + RemoteAccessibleBase(uint64_t aID, + DocAccessibleParent* aDoc, role aRole, AccType aType, + AccGenericType aGenericTypes, uint8_t aRoleMapEntryIndex) + : Accessible(aType, aGenericTypes, aRoleMapEntryIndex), + mParent(kNoParent), + mDoc(aDoc), + mWrapper(0), + mID(aID), + mCachedFields(nullptr), + mRole(aRole) {} + + explicit RemoteAccessibleBase(DocAccessibleParent* aThisAsDoc) + : Accessible(), + mParent(kNoParent), + mDoc(aThisAsDoc), + mWrapper(0), + mID(0), + mCachedFields(nullptr), + mRole(roles::DOCUMENT) { + mGenericTypes = eDocument | eHyperText; + } + + protected: + void SetParent(Derived* aParent); + Maybe<nsRect> RetrieveCachedBounds() const; + bool ApplyTransform(nsRect& aCumulativeBounds) const; + bool ApplyScrollOffset(nsRect& aBounds) const; + void ApplyCrossDocOffset(nsRect& aBounds) const; + LayoutDeviceIntRect BoundsWithOffset( + Maybe<nsRect> aOffset, bool aBoundsAreForHittesting = false) const; + bool IsFixedPos() const; + bool IsOverflowHidden() const; + + /** + * Returns true if an accessible's frame has no scrollable overflow, and + * false otherwise. + * Does not return true for partially clipped accessibles. + */ + bool IsClipped() const; + + /** + * Checks if our hittesting match has any clipped children and, if so + * descends it and subsequent TEXT_CONTAINERs in search of a text leaf. + * We do this because some sites use clipping to hide text that is only + * visible to a11y, while displaying a visual version of the same text on + * the web page. We want a hittest of the visible text to resolve to the + * hidden, a11y-only text node. + */ + RemoteAccessible* DoFuzzyHittesting(); + + // This function is used exclusively for hit testing. + bool ContainsPoint(int32_t aX, int32_t aY); + + virtual void ARIAGroupPosition(int32_t* aLevel, int32_t* aSetSize, + int32_t* aPosInSet) const override; + + virtual AccGroupInfo* GetGroupInfo() const override; + + virtual AccGroupInfo* GetOrCreateGroupInfo() override; + + virtual void GetPositionAndSetSize(int32_t* aPosInSet, + int32_t* aSetSize) override; + + virtual bool HasPrimaryAction() const override; + + nsAtom* GetPrimaryAction() const; + + virtual nsTArray<int32_t>& GetCachedHyperTextOffsets() override; + + // XXX: Declare ourselves as a template friend to work around a suspected gcc + // bug with calling protected functions. See Bug 1825516. + template <class> + friend class RemoteAccessibleBase; + + private: + uintptr_t mParent; + static const uintptr_t kNoParent = UINTPTR_MAX; + + friend Derived; + friend DocAccessibleParent; + friend TextLeafPoint; + friend HyperTextAccessibleBase; + friend class xpcAccessible; + friend class CachedTableCellAccessible; +#ifdef XP_WIN + friend class sdnAccessible; +#endif + + nsTArray<Derived*> mChildren; + DocAccessibleParent* mDoc; + uintptr_t mWrapper; + uint64_t mID; + + protected: + virtual const Accessible* Acc() const override { return this; } + + RefPtr<AccAttributes> mCachedFields; + + // XXX DocAccessibleParent gets to change this to change the role of + // documents. + role mRole : 27; +}; + +extern template class RemoteAccessibleBase<RemoteAccessible>; + +} // namespace a11y +} // namespace mozilla + +#endif |