Bug 1856666 - run snap tests as cron r=releng-reviewers,ahal
[gecko.git] / accessible / generic / DocAccessible.h
bloba3764ba48d269704f797bdd1fa02dbe0001e119c
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef mozilla_a11y_DocAccessible_h__
7 #define mozilla_a11y_DocAccessible_h__
9 #include "HyperTextAccessible.h"
10 #include "AccEvent.h"
12 #include "nsClassHashtable.h"
13 #include "nsTHashMap.h"
14 #include "mozilla/UniquePtr.h"
15 #include "nsIDocumentObserver.h"
16 #include "nsITimer.h"
17 #include "nsTHashSet.h"
18 #include "nsWeakReference.h"
20 const uint32_t kDefaultCacheLength = 128;
22 namespace mozilla {
24 class EditorBase;
25 class PresShell;
27 namespace dom {
28 class Document;
31 namespace a11y {
33 class DocManager;
34 class NotificationController;
35 class DocAccessibleChild;
36 class RelatedAccIterator;
37 template <class Class, class... Args>
38 class TNotification;
40 /**
41 * An accessibility tree node that originated in a content process and
42 * represents a document. Tabs, in-process iframes, and out-of-process iframes
43 * all use this class to represent the doc they contain.
45 class DocAccessible : public HyperTextAccessible,
46 public nsIDocumentObserver,
47 public nsSupportsWeakReference {
48 NS_DECL_ISUPPORTS_INHERITED
49 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DocAccessible, LocalAccessible)
51 protected:
52 typedef mozilla::dom::Document Document;
54 public:
55 DocAccessible(Document* aDocument, PresShell* aPresShell);
57 // nsIDocumentObserver
58 NS_DECL_NSIDOCUMENTOBSERVER
60 // LocalAccessible
61 virtual void Init();
62 virtual void Shutdown() override;
63 virtual nsIFrame* GetFrame() const override;
64 virtual nsINode* GetNode() const override;
65 Document* DocumentNode() const { return mDocumentNode; }
67 virtual mozilla::a11y::ENameValueFlag Name(nsString& aName) const override;
68 virtual void Description(nsString& aDescription) const override;
69 virtual Accessible* FocusedChild() override;
70 virtual mozilla::a11y::role NativeRole() const override;
71 virtual uint64_t NativeState() const override;
72 virtual uint64_t NativeInteractiveState() const override;
73 virtual bool NativelyUnavailable() const override;
74 virtual void ApplyARIAState(uint64_t* aState) const override;
76 virtual void TakeFocus() const override;
78 #ifdef A11Y_LOG
79 virtual nsresult HandleAccEvent(AccEvent* aEvent) override;
80 #endif
82 virtual nsRect RelativeBounds(nsIFrame** aRelativeFrame) const override;
84 // ActionAccessible
85 virtual bool HasPrimaryAction() const override;
86 virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
88 // HyperTextAccessible
89 virtual already_AddRefed<EditorBase> GetEditor() const override;
91 // DocAccessible
93 /**
94 * Return document URL.
96 void URL(nsAString& aURL) const;
98 /**
99 * Return DOM document title.
101 void Title(nsString& aTitle) const;
104 * Return DOM document mime type.
106 void MimeType(nsAString& aType) const;
108 * Return DOM document type.
110 void DocType(nsAString& aType) const;
113 * Adds an entry to mQueuedCacheUpdates indicating aAcc requires
114 * a cache update on domain aNewDomain. If we've already queued an update
115 * for aAcc, aNewDomain is or'd with the existing domain(s)
116 * and the map is updated. Otherwise, the entry is simply inserted.
117 * This function also schedules processing on the controller.
118 * Note that this CANNOT be used for anything which fires events, since events
119 * must be fired after their associated cache update.
121 void QueueCacheUpdate(LocalAccessible* aAcc, uint64_t aNewDomain);
124 * Walks the mDependentIDsHashes list for the given accessible and
125 * queues a CacheDomain::Relations cache update fore each related acc.
126 * We call this when we observe an ID mutation or when an acc is bound
127 * to its document.
129 void QueueCacheUpdateForDependentRelations(LocalAccessible* aAcc);
132 * Returns true if the instance has shutdown.
134 bool HasShutdown() const { return !mPresShell; }
137 * Return presentation shell for this document accessible.
139 PresShell* PresShellPtr() const {
140 MOZ_DIAGNOSTIC_ASSERT(!HasShutdown());
141 return mPresShell;
145 * Return the presentation shell's context.
147 nsPresContext* PresContext() const;
150 * Return true if associated DOM document was loaded and isn't unloading.
152 bool IsContentLoaded() const;
154 bool IsHidden() const;
156 void SetViewportCacheDirty(bool aDirty) { mViewportCacheDirty = aDirty; }
159 * Document load states.
161 enum LoadState {
162 // initial tree construction is pending
163 eTreeConstructionPending = 0,
164 // initial tree construction done
165 eTreeConstructed = 1,
166 // DOM document is loaded.
167 eDOMLoaded = 1 << 1,
168 // document is ready
169 eReady = eTreeConstructed | eDOMLoaded,
170 // document and all its subdocuments are ready
171 eCompletelyLoaded = eReady | 1 << 2
175 * Return true if the document has given document state.
177 bool HasLoadState(LoadState aState) const {
178 return (mLoadState & static_cast<uint32_t>(aState)) ==
179 static_cast<uint32_t>(aState);
183 * Return a native window handler or pointer depending on platform.
185 virtual void* GetNativeWindow() const;
188 * Return the parent document.
190 DocAccessible* ParentDocument() const {
191 return mParent ? mParent->Document() : nullptr;
195 * Return the child document count.
197 uint32_t ChildDocumentCount() const { return mChildDocuments.Length(); }
200 * Return the child document at the given index.
202 DocAccessible* GetChildDocumentAt(uint32_t aIndex) const {
203 return mChildDocuments.SafeElementAt(aIndex, nullptr);
207 * Fire accessible event asynchronously.
209 void FireDelayedEvent(AccEvent* aEvent);
210 void FireDelayedEvent(uint32_t aEventType, LocalAccessible* aTarget);
211 void FireEventsOnInsertion(LocalAccessible* aContainer);
214 * Fire value change event on the given accessible if applicable.
216 void MaybeNotifyOfValueChange(LocalAccessible* aAccessible);
219 * Get/set the anchor jump.
221 LocalAccessible* AnchorJump() {
222 return GetAccessibleOrContainer(mAnchorJumpElm);
225 void SetAnchorJump(nsIContent* aTargetNode) { mAnchorJumpElm = aTargetNode; }
228 * Bind the child document to the tree.
230 void BindChildDocument(DocAccessible* aDocument);
233 * Process the generic notification.
235 * @note The caller must guarantee that the given instance still exists when
236 * notification is processed.
237 * @see NotificationController::HandleNotification
239 template <class Class, class... Args>
240 void HandleNotification(
241 Class* aInstance,
242 typename TNotification<Class, Args...>::Callback aMethod, Args*... aArgs);
245 * Return the cached accessible by the given DOM node if it's in subtree of
246 * this document accessible or the document accessible itself, otherwise null.
248 * @return the accessible object
250 LocalAccessible* GetAccessible(nsINode* aNode) const;
253 * Return an accessible for the given node even if the node is not in
254 * document's node map cache (like HTML area element).
256 * XXX: it should be really merged with GetAccessible().
258 LocalAccessible* GetAccessibleEvenIfNotInMap(nsINode* aNode) const;
259 LocalAccessible* GetAccessibleEvenIfNotInMapOrContainer(nsINode* aNode) const;
262 * Return whether the given DOM node has an accessible or not.
264 bool HasAccessible(nsINode* aNode) const { return GetAccessible(aNode); }
267 * Return the cached accessible by the given unique ID within this document.
269 * @note the unique ID matches with the uniqueID() of Accessible
271 * @param aUniqueID [in] the unique ID used to cache the node.
273 LocalAccessible* GetAccessibleByUniqueID(void* aUniqueID) {
274 return UniqueID() == aUniqueID ? this : mAccessibleCache.GetWeak(aUniqueID);
278 * Return the cached accessible by the given unique ID looking through
279 * this and nested documents.
281 LocalAccessible* GetAccessibleByUniqueIDInSubtree(void* aUniqueID);
284 * Return an accessible for the given DOM node or container accessible if
285 * the node is not accessible. If aNoContainerIfPruned is true it will return
286 * null if the node is in a pruned subtree (eg. aria-hidden or unselected deck
287 * panel)
289 LocalAccessible* GetAccessibleOrContainer(
290 nsINode* aNode, bool aNoContainerIfPruned = false) const;
293 * Return a container accessible for the given DOM node.
295 LocalAccessible* GetContainerAccessible(nsINode* aNode) const;
298 * Return an accessible for the given node if any, or an immediate accessible
299 * container for it.
301 LocalAccessible* AccessibleOrTrueContainer(
302 nsINode* aNode, bool aNoContainerIfPruned = false) const;
305 * Return an accessible for the given node or its first accessible descendant.
307 LocalAccessible* GetAccessibleOrDescendant(nsINode* aNode) const;
310 * Returns aria-owns seized child at the given index.
312 LocalAccessible* ARIAOwnedAt(LocalAccessible* aParent,
313 uint32_t aIndex) const {
314 nsTArray<RefPtr<LocalAccessible>>* children = mARIAOwnsHash.Get(aParent);
315 if (children) {
316 return children->SafeElementAt(aIndex);
318 return nullptr;
320 uint32_t ARIAOwnedCount(LocalAccessible* aParent) const {
321 nsTArray<RefPtr<LocalAccessible>>* children = mARIAOwnsHash.Get(aParent);
322 return children ? children->Length() : 0;
326 * Return true if the given ID is referred by relation attribute.
328 bool IsDependentID(dom::Element* aElement, const nsAString& aID) const {
329 return GetRelProviders(aElement, aID);
333 * Initialize the newly created accessible and put it into document caches.
335 * @param aAccessible [in] created accessible
336 * @param aRoleMapEntry [in] the role map entry role the ARIA role or
337 * nullptr if none
339 void BindToDocument(LocalAccessible* aAccessible,
340 const nsRoleMapEntry* aRoleMapEntry);
343 * Remove from document and shutdown the given accessible.
345 void UnbindFromDocument(LocalAccessible* aAccessible);
348 * Notify the document accessible that content was inserted.
350 void ContentInserted(nsIContent* aStartChildNode, nsIContent* aEndChildNode);
353 * @see nsAccessibilityService::ScheduleAccessibilitySubtreeUpdate
355 void ScheduleTreeUpdate(nsIContent* aContent);
358 * Update the tree on content removal.
360 void ContentRemoved(LocalAccessible* aAccessible);
361 void ContentRemoved(nsIContent* aContentNode);
364 * Updates accessible tree when rendered text is changed.
366 void UpdateText(nsIContent* aTextNode);
369 * Recreate an accessible, results in hide/show events pair.
371 void RecreateAccessible(nsIContent* aContent);
374 * Schedule ARIA owned element relocation if needed. Return true if relocation
375 * was scheduled.
377 bool RelocateARIAOwnedIfNeeded(nsIContent* aEl);
380 * Return a notification controller associated with the document.
382 NotificationController* Controller() const { return mNotificationController; }
385 * If this document is in a content process return the object responsible for
386 * communicating with the main process for it.
388 DocAccessibleChild* IPCDoc() const { return mIPCDoc; }
391 * Notify the document that a DOM node has been scrolled. document will
392 * dispatch throttled accessibility events for scrolling, and a scroll-end
393 * event. This function also queues a cache update for ScrollPosition.
395 void HandleScroll(nsINode* aTarget);
398 * Retrieves the scroll frame (if it exists) for the given accessible
399 * and returns its scroll position and scroll range. If the given
400 * accessible is `this`, return the scroll position and range of
401 * the root scroll frame. Return values have been scaled by the
402 * PresShell's resolution.
404 std::pair<nsPoint, nsRect> ComputeScrollData(LocalAccessible* aAcc);
407 * Only works in content process documents.
409 bool IsAccessibleBeingMoved(LocalAccessible* aAcc) {
410 return mMovedAccessibles.Contains(aAcc);
413 protected:
414 virtual ~DocAccessible();
416 void LastRelease();
418 // DocAccessible
419 virtual nsresult AddEventListeners();
420 virtual nsresult RemoveEventListeners();
423 * Marks this document as loaded or loading.
425 void NotifyOfLoad(uint32_t aLoadEventType);
426 void NotifyOfLoading(bool aIsReloading);
428 friend class DocManager;
431 * Perform initial update (create accessible tree).
432 * Can be overridden by wrappers to prepare initialization work.
434 virtual void DoInitialUpdate();
437 * Updates root element and picks up ARIA role on it if any.
439 void UpdateRootElIfNeeded();
442 * Process document load notification, fire document load and state busy
443 * events if applicable.
445 void ProcessLoad();
448 * Append the given document accessible to this document's child document
449 * accessibles.
451 bool AppendChildDocument(DocAccessible* aChildDocument) {
452 // XXX(Bug 1631371) Check if this should use a fallible operation as it
453 // pretended earlier, or change the return type to void.
454 mChildDocuments.AppendElement(aChildDocument);
455 return true;
459 * Remove the given document accessible from this document's child document
460 * accessibles.
462 void RemoveChildDocument(DocAccessible* aChildDocument) {
463 mChildDocuments.RemoveElement(aChildDocument);
467 * Add dependent IDs pointed by accessible element by relation attribute to
468 * cache. If the relation attribute is missed then all relation attributes
469 * are checked.
471 * @param aRelProvider [in] accessible that element has relation attribute
472 * @param aRelAttr [in, optional] relation attribute
474 void AddDependentIDsFor(LocalAccessible* aRelProvider,
475 nsAtom* aRelAttr = nullptr);
478 * Remove dependent IDs pointed by accessible element by relation attribute
479 * from cache. If the relation attribute is absent then all relation
480 * attributes are checked.
482 * @param aRelProvider [in] accessible that element has relation attribute
483 * @param aRelAttr [in, optional] relation attribute
485 void RemoveDependentIDsFor(LocalAccessible* aRelProvider,
486 nsAtom* aRelAttr = nullptr);
489 * Update or recreate an accessible depending on a changed attribute.
491 * @param aElement [in] the element the attribute was changed on
492 * @param aAttribute [in] the changed attribute
493 * @return true if an action was taken on the attribute change
495 bool UpdateAccessibleOnAttrChange(mozilla::dom::Element* aElement,
496 nsAtom* aAttribute);
499 * Process ARIA active-descendant attribute change.
501 void ARIAActiveDescendantChanged(LocalAccessible* aAccessible);
504 * Update the accessible tree for inserted content.
506 void ProcessContentInserted(
507 LocalAccessible* aContainer,
508 const nsTArray<nsCOMPtr<nsIContent>>* aInsertedContent);
509 void ProcessContentInserted(LocalAccessible* aContainer,
510 nsIContent* aInsertedContent);
513 * Used to notify the document to make it process the invalidation list.
515 * While children are cached we may encounter the case there's no accessible
516 * for referred content by related accessible. Store these related nodes to
517 * invalidate their containers later.
519 void ProcessInvalidationList();
522 * Process mPendingUpdates
524 void ProcessPendingUpdates();
527 * Called from NotificationController to process this doc's
528 * mQueuedCacheUpdates list. For each acc in the map, this function
529 * sends a cache update with its corresponding CacheDomain.
531 void ProcessQueuedCacheUpdates();
534 * Called from NotificationController before mutation events are processed to
535 * notify the parent process which Accessibles are being moved (if any).
537 void SendAccessiblesWillMove();
540 * Called from NotificationController after all mutation events have been
541 * processed to clear our data about Accessibles that were moved during this
542 * tick.
544 void ClearMovedAccessibles() {
545 mMovedAccessibles.Clear();
546 mInsertedAccessibles.Clear();
550 * Steals or puts back accessible subtrees.
552 void DoARIAOwnsRelocation(LocalAccessible* aOwner);
555 * Moves children back under their original parents.
557 void PutChildrenBack(nsTArray<RefPtr<LocalAccessible>>* aChildren,
558 uint32_t aStartIdx);
560 bool MoveChild(LocalAccessible* aChild, LocalAccessible* aNewParent,
561 int32_t aIdxInParent);
564 * Create accessible tree.
566 * @param aRoot [in] a root of subtree to create
567 * @param aFocusedAcc [in, optional] a focused accessible under created
568 * subtree if any
570 void CacheChildrenInSubtree(LocalAccessible* aRoot,
571 LocalAccessible** aFocusedAcc = nullptr);
572 void CreateSubtree(LocalAccessible* aRoot);
575 * Remove accessibles in subtree from node to accessible map.
577 void UncacheChildrenInSubtree(LocalAccessible* aRoot);
580 * Shutdown any cached accessible in the subtree.
582 * @param aAccessible [in] the root of the subrtee to invalidate accessible
583 * child/parent refs in
585 void ShutdownChildrenInSubtree(LocalAccessible* aAccessible);
588 * Return true if the document is a target of document loading events
589 * (for example, state busy change or document reload events).
591 * Rules: The root chrome document accessible is never an event target
592 * (for example, Firefox UI window). If the sub document is loaded within its
593 * parent document then the parent document is a target only (aka events
594 * coalescence).
596 bool IsLoadEventTarget() const;
599 * Set the object responsible for communicating with the main process on
600 * behalf of this document.
602 void SetIPCDoc(DocAccessibleChild* aIPCDoc);
604 friend class DocAccessibleChild;
607 * Used to fire scrolling end event after page scroll.
609 * @param aTimer [in] the timer object
610 * @param aClosure [in] the document accessible where scrolling happens
612 static void ScrollTimerCallback(nsITimer* aTimer, void* aClosure);
614 void DispatchScrollingEvent(nsINode* aTarget, uint32_t aEventType);
617 * Check if an id attribute change affects aria-activedescendant and handle
618 * the aria-activedescendant change if appropriate.
619 * If the currently focused element has aria-activedescendant and an
620 * element's id changes to match this, the id was probably moved from the
621 * previous active descendant, thus making this element the new active
622 * descendant. In that case, accessible focus must be changed accordingly.
624 void ARIAActiveDescendantIDMaybeMoved(LocalAccessible* aAccessible);
627 * Traverse content subtree and for each node do one of 3 things:
628 * 1. Check if content node has an accessible that should be removed and
629 * remove it.
630 * 2. Check if content node has an accessible that needs to be recreated.
631 * Remove it and schedule it for reinsertion.
632 * 3. Check if content node has no accessible but needs one. Schedule one for
633 * insertion.
635 * Returns true if the root node should be reinserted.
637 bool PruneOrInsertSubtree(nsIContent* aRoot);
639 protected:
641 * State and property flags, kept by mDocFlags.
643 enum {
644 // Whether the document is a top level content document in this process.
645 eTopLevelContentDocInProcess = 1 << 0
649 * Cache of accessibles within this document accessible.
651 AccessibleHashtable mAccessibleCache;
652 nsTHashMap<nsPtrHashKey<const nsINode>, LocalAccessible*>
653 mNodeToAccessibleMap;
655 Document* mDocumentNode;
656 nsCOMPtr<nsITimer> mScrollWatchTimer;
657 nsTHashMap<nsPtrHashKey<nsINode>, TimeStamp> mLastScrollingDispatch;
660 * Bit mask of document load states (@see LoadState).
662 uint32_t mLoadState : 3;
665 * Bit mask of other states and props.
667 uint32_t mDocFlags : 27;
670 * Tracks whether we have seen changes to this document's content that
671 * indicate we should re-send the viewport cache we use for hittesting.
672 * This value is set in `BundleFieldsForCache` and processed in
673 * `ProcessQueuedCacheUpdates`.
675 bool mViewportCacheDirty : 1;
678 * Type of document load event fired after the document is loaded completely.
680 uint32_t mLoadEventType;
683 * Reference to anchor jump element.
685 nsCOMPtr<nsIContent> mAnchorJumpElm;
688 * A generic state (see items below) before the attribute value was changed.
689 * @see AttributeWillChange and AttributeChanged notifications.
692 // Previous state bits before attribute change
693 uint64_t mPrevStateBits;
695 nsTArray<RefPtr<DocAccessible>> mChildDocuments;
698 * A storage class for pairing content with one of its relation attributes.
700 class AttrRelProvider {
701 public:
702 AttrRelProvider(nsAtom* aRelAttr, nsIContent* aContent)
703 : mRelAttr(aRelAttr), mContent(aContent) {}
705 nsAtom* mRelAttr;
706 nsCOMPtr<nsIContent> mContent;
708 private:
709 AttrRelProvider();
710 AttrRelProvider(const AttrRelProvider&);
711 AttrRelProvider& operator=(const AttrRelProvider&);
714 typedef nsTArray<mozilla::UniquePtr<AttrRelProvider>> AttrRelProviders;
715 typedef nsClassHashtable<nsStringHashKey, AttrRelProviders>
716 DependentIDsHashtable;
719 * Returns/creates/removes attribute relation providers associated with
720 * a DOM document if the element is in uncomposed document or associated
721 * with shadow DOM the element is in.
723 AttrRelProviders* GetRelProviders(dom::Element* aElement,
724 const nsAString& aID) const;
725 AttrRelProviders* GetOrCreateRelProviders(dom::Element* aElement,
726 const nsAString& aID);
727 void RemoveRelProvidersIfEmpty(dom::Element* aElement, const nsAString& aID);
730 * The cache of IDs pointed by relation attributes.
732 nsClassHashtable<nsPtrHashKey<dom::DocumentOrShadowRoot>,
733 DependentIDsHashtable>
734 mDependentIDsHashes;
736 friend class RelatedAccIterator;
739 * Used for our caching algorithm. We store the list of nodes that should be
740 * invalidated.
742 * @see ProcessInvalidationList
744 nsTArray<RefPtr<nsIContent>> mInvalidationList;
747 * Holds a list of aria-owns relocations.
749 nsClassHashtable<nsPtrHashKey<LocalAccessible>,
750 nsTArray<RefPtr<LocalAccessible>>>
751 mARIAOwnsHash;
754 * Keeps a list of pending subtrees to update post-refresh.
756 nsTArray<RefPtr<nsIContent>> mPendingUpdates;
759 * Used to process notification from core and accessible events.
761 RefPtr<NotificationController> mNotificationController;
762 friend class EventTree;
763 friend class NotificationController;
765 private:
766 void SetRoleMapEntryForDoc(dom::Element* aElement);
769 * This must be called whenever an Accessible is moved in a content process.
770 * It keeps track of Accessibles moved during this tick.
772 void TrackMovedAccessible(LocalAccessible* aAcc);
775 * For hidden subtrees, fire a name/description change event if the subtree
776 * is a target of aria-labelledby/describedby.
777 * This does nothing if it is called on a node which is not part of a hidden
778 * aria-labelledby/describedby target.
780 void MaybeHandleChangeToHiddenNameOrDescription(nsIContent* aChild);
782 PresShell* mPresShell;
784 // Exclusively owned by IPDL so don't manually delete it!
785 DocAccessibleChild* mIPCDoc;
787 // A hash map between LocalAccessibles and CacheDomains, tracking
788 // cache updates that have been queued during the current tick
789 // but not yet sent. It is possible for this map to contain a reference
790 // to the document it lives on. We clear the list in Shutdown() to
791 // avoid cyclical references.
792 nsTHashMap<RefPtr<LocalAccessible>, uint64_t> mQueuedCacheUpdates;
794 // A set of Accessibles moved during this tick. Only used in content
795 // processes.
796 nsTHashSet<RefPtr<LocalAccessible>> mMovedAccessibles;
797 // A set of Accessibles inserted during this tick. Only used in content
798 // processes. This is needed to prevent insertions + moves of the same
799 // Accessible in the same tick from being tracked as moves.
800 nsTHashSet<RefPtr<LocalAccessible>> mInsertedAccessibles;
803 inline DocAccessible* LocalAccessible::AsDoc() {
804 return IsDoc() ? static_cast<DocAccessible*>(this) : nullptr;
807 } // namespace a11y
808 } // namespace mozilla
810 #endif