tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

DocAccessible.h (29806B)


      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/. */
      5 
      6 #ifndef mozilla_a11y_DocAccessible_h__
      7 #define mozilla_a11y_DocAccessible_h__
      8 
      9 #include "HyperTextAccessible.h"
     10 #include "AccEvent.h"
     11 #include "nsAccessibilityService.h"
     12 
     13 #include "nsClassHashtable.h"
     14 #include "nsTHashMap.h"
     15 #include "mozilla/UniquePtr.h"
     16 #include "nsIDocumentObserver.h"
     17 #include "nsITimer.h"
     18 #include "nsTHashSet.h"
     19 #include "nsWeakReference.h"
     20 
     21 const uint32_t kDefaultCacheLength = 128;
     22 
     23 namespace mozilla {
     24 
     25 class EditorBase;
     26 class PresShell;
     27 
     28 namespace dom {
     29 class Document;
     30 }
     31 
     32 namespace a11y {
     33 
     34 class DocManager;
     35 class NotificationController;
     36 class DocAccessibleChild;
     37 class RelatedAccIterator;
     38 template <class Class, class... Args>
     39 class TNotification;
     40 
     41 /**
     42 * An accessibility tree node that originated in a content process and
     43 * represents a document. Tabs, in-process iframes, and out-of-process iframes
     44 * all use this class to represent the doc they contain.
     45 */
     46 class DocAccessible : public HyperTextAccessible,
     47                      public nsIDocumentObserver,
     48                      public nsSupportsWeakReference {
     49  NS_DECL_ISUPPORTS_INHERITED
     50  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DocAccessible, LocalAccessible)
     51 
     52 protected:
     53  typedef mozilla::dom::Document Document;
     54 
     55 public:
     56  DocAccessible(Document* aDocument, PresShell* aPresShell);
     57 
     58  // nsIDocumentObserver
     59  NS_DECL_NSIDOCUMENTOBSERVER
     60 
     61  // LocalAccessible
     62  virtual void Init();
     63  virtual void Shutdown() override;
     64  virtual nsIFrame* GetFrame() const override;
     65  virtual nsINode* GetNode() const override;
     66  Document* DocumentNode() const { return mDocumentNode; }
     67 
     68  virtual mozilla::a11y::ENameValueFlag DirectName(
     69      nsString& aName) const override;
     70  virtual EDescriptionValueFlag Description(
     71      nsString& aDescription) const override;
     72  virtual Accessible* FocusedChild() override;
     73  virtual mozilla::a11y::role NativeRole() const override;
     74  virtual uint64_t NativeState() const override;
     75  virtual uint64_t NativeInteractiveState() const override;
     76  virtual bool NativelyUnavailable() const override;
     77  virtual void ApplyARIAState(uint64_t* aState) const override;
     78 
     79  virtual void TakeFocus() const override;
     80 
     81 #ifdef A11Y_LOG
     82  virtual nsresult HandleAccEvent(AccEvent* aEvent) override;
     83 #endif
     84 
     85  virtual nsRect RelativeBounds(nsIFrame** aRelativeFrame) const override;
     86 
     87  // ActionAccessible
     88  virtual bool HasPrimaryAction() const override;
     89  virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
     90 
     91  // HyperTextAccessible
     92  virtual already_AddRefed<EditorBase> GetEditor() const override;
     93 
     94  // DocAccessible
     95 
     96  /**
     97   * Return document URL.
     98   */
     99  void URL(nsAString& aURL) const;
    100 
    101  /**
    102   * Return DOM document title.
    103   */
    104  void Title(nsString& aTitle) const;
    105 
    106  /**
    107   * Return DOM document mime type.
    108   */
    109  void MimeType(nsAString& aType) const;
    110  /**
    111   * Return DOM document type.
    112   */
    113  void DocType(nsAString& aType) const;
    114 
    115  /**
    116   * Adds an entry to queued cache updates indicating aAcc requires
    117   * a cache update on domain aNewDomain. If we've already queued an update
    118   * for aAcc, aNewDomain is or'd with the existing domain(s)
    119   * and the map is updated. Otherwise, the entry is simply inserted.
    120   * This function also schedules processing on the controller.
    121   * Note that this CANNOT be used for anything which fires events, since events
    122   * must be fired after their associated cache update.
    123   */
    124  void QueueCacheUpdate(LocalAccessible* aAcc, uint64_t aNewDomain,
    125                        bool aBypassActiveDomains = false);
    126 
    127  /**
    128   * Walks the dependent ids and elements maps for the given accessible and
    129   * queues a CacheDomain::Relations cache update fore each related acc.
    130   * We call this when we observe an ID mutation or when an acc is bound
    131   * to its document.
    132   */
    133  void QueueCacheUpdateForDependentRelations(
    134      LocalAccessible* aAcc, const nsAttrValue* aOldId = nullptr);
    135 
    136  /**
    137   * Returns true if the instance has shutdown.
    138   */
    139  bool HasShutdown() const { return !mPresShell; }
    140 
    141  /**
    142   * Return presentation shell for this document accessible.
    143   */
    144  PresShell* PresShellPtr() const {
    145    MOZ_DIAGNOSTIC_ASSERT(!HasShutdown());
    146    return mPresShell;
    147  }
    148 
    149  /**
    150   * Return the presentation shell's context.
    151   */
    152  nsPresContext* PresContext() const;
    153 
    154  /**
    155   * Return true if associated DOM document was loaded and isn't unloading.
    156   */
    157  bool IsContentLoaded() const;
    158 
    159  bool IsHidden() const;
    160 
    161  void SetViewportCacheDirty(bool aDirty) { mViewportCacheDirty = aDirty; }
    162 
    163  /**
    164   * Document load states.
    165   */
    166  enum LoadState {
    167    // initial tree construction is pending
    168    eTreeConstructionPending = 0,
    169    // initial tree construction done
    170    eTreeConstructed = 1,
    171    // DOM document is loaded.
    172    eDOMLoaded = 1 << 1,
    173    // document is ready
    174    eReady = eTreeConstructed | eDOMLoaded,
    175    // document and all its subdocuments are ready
    176    eCompletelyLoaded = eReady | 1 << 2
    177  };
    178 
    179  /**
    180   * Return true if the document has given document state.
    181   */
    182  bool HasLoadState(LoadState aState) const {
    183    return (mLoadState & static_cast<uint32_t>(aState)) ==
    184           static_cast<uint32_t>(aState);
    185  }
    186 
    187  /**
    188   * Return a native window handler or pointer depending on platform.
    189   */
    190  virtual void* GetNativeWindow() const;
    191 
    192  /**
    193   * Return the parent document.
    194   */
    195  DocAccessible* ParentDocument() const {
    196    return mParent ? mParent->Document() : nullptr;
    197  }
    198 
    199  /**
    200   * Return the child document count.
    201   */
    202  uint32_t ChildDocumentCount() const { return mChildDocuments.Length(); }
    203 
    204  /**
    205   * Return the child document at the given index.
    206   */
    207  DocAccessible* GetChildDocumentAt(uint32_t aIndex) const {
    208    return mChildDocuments.SafeElementAt(aIndex, nullptr);
    209  }
    210 
    211  /**
    212   * Fire accessible event asynchronously.
    213   */
    214  void FireDelayedEvent(AccEvent* aEvent);
    215  void FireDelayedEvent(uint32_t aEventType, LocalAccessible* aTarget);
    216  void FireEventsOnInsertion(LocalAccessible* aContainer);
    217 
    218  /**
    219   * Fire value change event on the given accessible if applicable.
    220   */
    221  void MaybeNotifyOfValueChange(LocalAccessible* aAccessible);
    222 
    223  void SetAnchorJump(nsIContent* aTargetNode) { mAnchorJumpElm = aTargetNode; }
    224 
    225  /**
    226   * Process an anchor jump, if any. Returns false if the current focus caused
    227   * us to ignore the anchor jump, true otherwise.
    228   */
    229  bool ProcessAnchorJump();
    230 
    231  /**
    232   * Bind the child document to the tree.
    233   */
    234  void BindChildDocument(DocAccessible* aDocument);
    235 
    236  /**
    237   * Process the generic notification.
    238   *
    239   * @note  The caller must guarantee that the given instance still exists when
    240   *          notification is processed.
    241   * @see   NotificationController::HandleNotification
    242   */
    243  template <class Class, class... Args>
    244  void HandleNotification(
    245      Class* aInstance,
    246      typename TNotification<Class, Args...>::Callback aMethod, Args*... aArgs);
    247 
    248  /**
    249   * Return the cached accessible by the given DOM node if it's in subtree of
    250   * this document accessible or the document accessible itself, otherwise null.
    251   *
    252   * @return the accessible object
    253   */
    254  LocalAccessible* GetAccessible(nsINode* aNode) const;
    255 
    256  /**
    257   * Return an accessible for the given node even if the node is not in
    258   * document's node map cache (like HTML area element).
    259   *
    260   * XXX: it should be really merged with GetAccessible().
    261   */
    262  LocalAccessible* GetAccessibleEvenIfNotInMap(nsINode* aNode) const;
    263  LocalAccessible* GetAccessibleEvenIfNotInMapOrContainer(nsINode* aNode) const;
    264 
    265  /**
    266   * Return whether the given DOM node has an accessible or not.
    267   */
    268  bool HasAccessible(nsINode* aNode) const { return GetAccessible(aNode); }
    269 
    270  /**
    271   * Return the cached accessible by the given unique ID within this document.
    272   *
    273   * @note   the unique ID matches with the uniqueID() of Accessible
    274   *
    275   * @param  aUniqueID  [in] the unique ID used to cache the node.
    276   */
    277  LocalAccessible* GetAccessibleByUniqueID(void* aUniqueID) {
    278    return UniqueID() == aUniqueID ? this : mAccessibleCache.GetWeak(aUniqueID);
    279  }
    280 
    281  /**
    282   * Return the cached accessible by the given unique ID looking through
    283   * this and nested documents.
    284   */
    285  LocalAccessible* GetAccessibleByUniqueIDInSubtree(void* aUniqueID);
    286 
    287  /**
    288   * Return an accessible for the given DOM node or container accessible if
    289   * the node is not accessible. If aNoContainerIfPruned is true it will return
    290   * null if the node is in a pruned subtree (eg. aria-hidden or unselected deck
    291   * panel)
    292   */
    293  LocalAccessible* GetAccessibleOrContainer(
    294      nsINode* aNode, bool aNoContainerIfPruned = false) const;
    295 
    296  /**
    297   * Return a container accessible for the given DOM node.
    298   */
    299  LocalAccessible* GetContainerAccessible(nsINode* aNode) const;
    300 
    301  /**
    302   * Return an accessible for the given node if any, or an immediate accessible
    303   * container for it.
    304   */
    305  LocalAccessible* AccessibleOrTrueContainer(
    306      nsINode* aNode, bool aNoContainerIfPruned = false) const;
    307 
    308  /**
    309   * Return an accessible for the given node or its first accessible descendant.
    310   */
    311  LocalAccessible* GetAccessibleOrDescendant(nsINode* aNode) const;
    312 
    313  /**
    314   * Returns aria-owns seized child at the given index.
    315   */
    316  LocalAccessible* ARIAOwnedAt(LocalAccessible* aParent,
    317                               uint32_t aIndex) const {
    318    nsTArray<RefPtr<LocalAccessible>>* children = mARIAOwnsHash.Get(aParent);
    319    if (children) {
    320      return children->SafeElementAt(aIndex);
    321    }
    322    return nullptr;
    323  }
    324  uint32_t ARIAOwnedCount(LocalAccessible* aParent) const {
    325    nsTArray<RefPtr<LocalAccessible>>* children = mARIAOwnsHash.Get(aParent);
    326    return children ? children->Length() : 0;
    327  }
    328 
    329  /**
    330   * Return true if the given ID is referred by relation attribute.
    331   */
    332  bool IsDependentID(dom::Element* aElement, const nsAString& aID) const {
    333    return GetRelProviders(aElement, aID);
    334  }
    335 
    336  /**
    337   * Initialize the newly created accessible and put it into document caches.
    338   *
    339   * @param  aAccessible    [in] created accessible
    340   * @param  aRoleMapEntry  [in] the role map entry role the ARIA role or
    341   * nullptr if none
    342   */
    343  void BindToDocument(LocalAccessible* aAccessible,
    344                      const nsRoleMapEntry* aRoleMapEntry);
    345 
    346  /**
    347   * Remove from document and shutdown the given accessible.
    348   */
    349  void UnbindFromDocument(LocalAccessible* aAccessible);
    350 
    351  /**
    352   * Notify the document accessible that content was inserted.
    353   */
    354  void ContentInserted(nsIContent* aStartChildNode, nsIContent* aEndChildNode);
    355 
    356  /**
    357   * @see nsAccessibilityService::ScheduleAccessibilitySubtreeUpdate
    358   */
    359  void ScheduleTreeUpdate(nsIContent* aContent);
    360 
    361  /**
    362   * Update the tree on content removal.
    363   */
    364  void ContentRemoved(LocalAccessible* aAccessible);
    365  void ContentRemoved(nsIContent* aContentNode);
    366 
    367  /**
    368   * Updates accessible tree when rendered text is changed.
    369   */
    370  void UpdateText(nsIContent* aTextNode);
    371 
    372  /**
    373   * Recreate an accessible, results in hide/show events pair.
    374   */
    375  void RecreateAccessible(nsIContent* aContent);
    376 
    377  /**
    378   * Schedule ARIA owned element relocation if needed. Return true if relocation
    379   * was scheduled.
    380   */
    381  bool RelocateARIAOwnedIfNeeded(nsIContent* aEl);
    382 
    383  /**
    384   * Return a notification controller associated with the document.
    385   */
    386  NotificationController* Controller() const { return mNotificationController; }
    387 
    388  /**
    389   * If this document is in a content process return the object responsible for
    390   * communicating with the main process for it.
    391   */
    392  DocAccessibleChild* IPCDoc() const { return mIPCDoc; }
    393 
    394  /**
    395   * Notify the document that a DOM node has been scrolled. document will
    396   * dispatch throttled accessibility events for scrolling, and a scroll-end
    397   * event. This function also queues a cache update for ScrollPosition.
    398   */
    399  void HandleScroll(nsINode* aTarget);
    400 
    401  /**
    402   * Retrieves the scroll frame (if it exists) for the given accessible
    403   * and returns its scroll position and scroll range. If the given
    404   * accessible is `this`, return the scroll position and range of
    405   * the root scroll frame. Return values have been scaled by the
    406   * PresShell's resolution when aShouldScaleByResolution is explicitly
    407   * true or unspecified.
    408   */
    409  std::pair<nsPoint, nsRect> ComputeScrollData(
    410      const LocalAccessible* aAcc, bool aShouldScaleByResolution = true);
    411 
    412  /**
    413   * Only works in content process documents.
    414   */
    415  bool IsAccessibleBeingMoved(LocalAccessible* aAcc) {
    416    return mMovedAccessibles.Contains(aAcc);
    417  }
    418 
    419  void AttrElementWillChange(dom::Element* aElement, nsAtom* aAttr);
    420  void AttrElementChanged(dom::Element* aElement, nsAtom* aAttr);
    421 
    422  /**
    423   * Given an accessible, check if it is anchored to other frames, and
    424   * refresh the cache on each of those frames' accessibles.
    425   */
    426  void RefreshAnchorRelationCacheForTarget(LocalAccessible* aTarget);
    427 
    428 protected:
    429  virtual ~DocAccessible();
    430 
    431  void LastRelease();
    432 
    433  // DocAccessible
    434  virtual nsresult AddEventListeners();
    435  virtual nsresult RemoveEventListeners();
    436 
    437  /**
    438   * Marks this document as loaded or loading.
    439   */
    440  void NotifyOfLoad(uint32_t aLoadEventType);
    441  void NotifyOfLoading(bool aIsReloading);
    442 
    443  friend class DocManager;
    444 
    445  /**
    446   * Perform initial update (create accessible tree).
    447   * Can be overridden by wrappers to prepare initialization work.
    448   */
    449  virtual void DoInitialUpdate();
    450 
    451  /**
    452   * Updates root element and picks up ARIA role on it if any.
    453   */
    454  void UpdateRootElIfNeeded();
    455 
    456  /**
    457   * Process document load notification, fire document load and state busy
    458   * events if applicable.
    459   */
    460  void ProcessLoad();
    461 
    462  /**
    463   * Append the given document accessible to this document's child document
    464   * accessibles.
    465   */
    466  bool AppendChildDocument(DocAccessible* aChildDocument) {
    467    // XXX(Bug 1631371) Check if this should use a fallible operation as it
    468    // pretended earlier, or change the return type to void.
    469    mChildDocuments.AppendElement(aChildDocument);
    470    return true;
    471  }
    472 
    473  /**
    474   * Remove the given document accessible from this document's child document
    475   * accessibles.
    476   */
    477  void RemoveChildDocument(DocAccessible* aChildDocument) {
    478    mChildDocuments.RemoveElement(aChildDocument);
    479  }
    480 
    481  /**
    482   * Add dependent IDs pointed by accessible element by relation attribute to
    483   * cache. If the relation attribute is missed then all relation attributes
    484   * are checked.
    485   *
    486   * @param aRelProvider [in] accessible that element has relation attribute
    487   * @param aRelAttr     [in, optional] relation attribute
    488   */
    489  void AddDependentIDsFor(LocalAccessible* aRelProvider,
    490                          nsAtom* aRelAttr = nullptr);
    491 
    492  /**
    493   * Remove dependent IDs pointed by accessible element by relation attribute
    494   * from cache. If the relation attribute is absent then all relation
    495   * attributes are checked.
    496   *
    497   * @param aRelProvider [in] accessible that element has relation attribute
    498   * @param aRelAttr     [in, optional] relation attribute
    499   */
    500  void RemoveDependentIDsFor(LocalAccessible* aRelProvider,
    501                             nsAtom* aRelAttr = nullptr);
    502 
    503  /**
    504   * Add dependent elements targeted by a relation attribute on an accessible
    505   * element to the dependent elements cache. This is used for reflected IDL
    506   * attributes which return DOM elements and reflect a content attribute, where
    507   * the IDL attribute has been set to an element. For example, if the
    508   * .popoverTargetElement IDL attribute is set to an element using JS, the
    509   * target element will be added to the dependent elements cache. If the
    510   * relation attribute is not specified, then all relation attributes are
    511   * checked.
    512   *
    513   * @param aRelProvider [in] the accessible with the relation IDL attribute.
    514   * @param aRelAttr [in, optional] the name of the reflected content attribute.
    515   *   For example, for the popoverTargetElement IDL attribute, this would be
    516   * "popovertarget".
    517   */
    518  void AddDependentElementsFor(LocalAccessible* aRelProvider,
    519                               nsAtom* aRelAttr = nullptr);
    520 
    521  /**
    522   * Remove dependent elements targeted by a relation attribute on an accessible
    523   * element from the dependent elements cache. If the relation attribute is
    524   * not specified, then all relation attributes are checked.
    525   *
    526   * @param aRelProvider [in] the accessible with the relation IDL attribute.
    527   * @param aRelAttr [in, optional] the name of the reflected content attribute.
    528   */
    529  void RemoveDependentElementsFor(LocalAccessible* aRelProvider,
    530                                  nsAtom* aRelAttr = nullptr);
    531 
    532  /**
    533   * Update or recreate an accessible depending on a changed attribute.
    534   *
    535   * @param aElement   [in] the element the attribute was changed on
    536   * @param aAttribute [in] the changed attribute
    537   * @return            true if an action was taken on the attribute change
    538   */
    539  bool UpdateAccessibleOnAttrChange(mozilla::dom::Element* aElement,
    540                                    nsAtom* aAttribute);
    541 
    542  /**
    543   * Process ARIA active-descendant attribute change.
    544   */
    545  void ARIAActiveDescendantChanged(LocalAccessible* aAccessible);
    546 
    547  /**
    548   * Update the accessible tree for inserted content.
    549   */
    550  void ProcessContentInserted(
    551      LocalAccessible* aContainer,
    552      const nsTArray<nsCOMPtr<nsIContent>>* aInsertedContent);
    553  void ProcessContentInserted(LocalAccessible* aContainer,
    554                              nsIContent* aInsertedContent);
    555 
    556  /**
    557   * Used to notify the document to make it process the invalidation list.
    558   *
    559   * While children are cached we may encounter the case there's no accessible
    560   * for referred content by related accessible. Store these related nodes to
    561   * invalidate their containers later.
    562   */
    563  void ProcessInvalidationList();
    564 
    565  /**
    566   * Process mPendingUpdates
    567   */
    568  void ProcessPendingUpdates();
    569 
    570  /**
    571   * Called from NotificationController to process this doc's
    572   * queued cache updates. For each acc in the map, this function
    573   * sends a cache update with its corresponding CacheDomain.
    574   * Each domain bit in aInitialDomains indicates that this is the first push
    575   * for that cache domain.
    576   */
    577  void ProcessQueuedCacheUpdates(uint64_t aInitialDomains = 0);
    578 
    579  /**
    580   * Called from NotificationController before mutation events are processed to
    581   * notify the parent process which Accessibles are being moved (if any).
    582   */
    583  void SendAccessiblesWillMove();
    584 
    585  /**
    586   * Called from NotificationController after all mutation events have been
    587   * processed to clear our data about mutations during this tick.
    588   */
    589  void ClearMutationData() {
    590    mMovedAccessibles.Clear();
    591    mInsertedAccessibles.Clear();
    592    mRemovedNodes.Clear();
    593  }
    594 
    595  /**
    596   * Steals or puts back accessible subtrees.
    597   */
    598  void DoARIAOwnsRelocation(LocalAccessible* aOwner);
    599 
    600  /**
    601   * Moves children back under their original parents.
    602   */
    603  void PutChildrenBack(nsTArray<RefPtr<LocalAccessible>>* aChildren,
    604                       uint32_t aStartIdx);
    605 
    606  bool MoveChild(LocalAccessible* aChild, LocalAccessible* aNewParent,
    607                 int32_t aIdxInParent);
    608 
    609  /**
    610   * Create accessible tree.
    611   *
    612   * @param aRoot       [in] a root of subtree to create
    613   * @param aFocusedAcc [in, optional] a focused accessible under created
    614   *                      subtree if any
    615   */
    616  void CacheChildrenInSubtree(LocalAccessible* aRoot,
    617                              LocalAccessible** aFocusedAcc = nullptr);
    618  void CreateSubtree(LocalAccessible* aRoot);
    619 
    620  /**
    621   * Remove accessibles in subtree from node to accessible map.
    622   */
    623  void UncacheChildrenInSubtree(LocalAccessible* aRoot);
    624 
    625  /**
    626   * Shutdown any cached accessible in the subtree.
    627   *
    628   * @param aAccessible  [in] the root of the subrtee to invalidate accessible
    629   *                      child/parent refs in
    630   */
    631  void ShutdownChildrenInSubtree(LocalAccessible* aAccessible);
    632 
    633  /**
    634   * Return true if the document is a target of document loading events
    635   * (for example, state busy change or document reload events).
    636   *
    637   * Rule: The root chrome document accessible is never an event target
    638   * (for example, Firefox UI window).
    639   */
    640  bool IsLoadEventTarget() const;
    641 
    642  /*
    643   * Set the object responsible for communicating with the main process on
    644   * behalf of this document.
    645   */
    646  void SetIPCDoc(DocAccessibleChild* aIPCDoc);
    647 
    648  friend class DocAccessibleChild;
    649 
    650  /**
    651   * Used to fire scrolling end event after page scroll.
    652   *
    653   * @param aTimer    [in] the timer object
    654   * @param aClosure  [in] the document accessible where scrolling happens
    655   */
    656  static void ScrollTimerCallback(nsITimer* aTimer, void* aClosure);
    657 
    658  void DispatchScrollingEvent(nsINode* aTarget, uint32_t aEventType);
    659 
    660  /**
    661   * Check if an id attribute change affects aria-activedescendant and handle
    662   * the aria-activedescendant change if appropriate.
    663   * If the currently focused element has aria-activedescendant and an
    664   * element's id changes to match this, the id was probably moved from the
    665   * previous active descendant, thus making this element the new active
    666   * descendant. In that case, accessible focus must be changed accordingly.
    667   */
    668  void ARIAActiveDescendantIDMaybeMoved(LocalAccessible* aAccessible);
    669 
    670  /**
    671   * Traverse content subtree and for each node do one of 3 things:
    672   * 1. Check if content node has an accessible that should be removed and
    673   *    remove it.
    674   * 2. Check if content node has an accessible that needs to be recreated.
    675   *    Remove it and schedule it for reinsertion.
    676   * 3. Check if content node has no accessible but needs one. Schedule one for
    677   *    insertion.
    678   *
    679   * Returns true if the root node should be reinserted.
    680   */
    681  bool PruneOrInsertSubtree(nsIContent* aRoot);
    682 
    683 protected:
    684  /**
    685   * State and property flags, kept by mDocFlags.
    686   */
    687  enum {
    688    // Whether the document is a top level content document in this process.
    689    eTopLevelContentDocInProcess = 1 << 0
    690  };
    691 
    692  /**
    693   * Cache of accessibles within this document accessible.
    694   */
    695  AccessibleHashtable mAccessibleCache;
    696  nsTHashMap<nsPtrHashKey<const nsINode>, LocalAccessible*>
    697      mNodeToAccessibleMap;
    698 
    699  Document* mDocumentNode;
    700  nsCOMPtr<nsITimer> mScrollWatchTimer;
    701  nsTHashMap<nsPtrHashKey<nsINode>, TimeStamp> mLastScrollingDispatch;
    702 
    703  /**
    704   * Bit mask of document load states (@see LoadState).
    705   */
    706  uint32_t mLoadState : 3;
    707 
    708  /**
    709   * Bit mask of other states and props.
    710   */
    711  uint32_t mDocFlags : 27;
    712 
    713  /**
    714   * Tracks whether we have seen changes to this document's content that
    715   * indicate we should re-send the viewport cache we use for hittesting.
    716   * This value is set in `BundleFieldsForCache` and processed in
    717   * `ProcessQueuedCacheUpdates`.
    718   */
    719  bool mViewportCacheDirty : 1;
    720 
    721  /**
    722   * Type of document load event fired after the document is loaded completely.
    723   */
    724  uint32_t mLoadEventType;
    725 
    726  /**
    727   * Reference to anchor jump element.
    728   */
    729  nsCOMPtr<nsIContent> mAnchorJumpElm;
    730 
    731  /**
    732   * A generic state (see items below) before the attribute value was changed.
    733   * @see AttributeWillChange and AttributeChanged notifications.
    734   */
    735 
    736  // Previous state bits before attribute change
    737  uint64_t mPrevStateBits;
    738 
    739  nsTArray<RefPtr<DocAccessible>> mChildDocuments;
    740 
    741  /**
    742   * A storage class for pairing content with one of its relation attributes.
    743   */
    744  class AttrRelProvider {
    745   public:
    746    AttrRelProvider(nsAtom* aRelAttr, nsIContent* aContent)
    747        : mRelAttr(aRelAttr), mContent(aContent) {}
    748 
    749    nsAtom* mRelAttr;
    750    nsCOMPtr<nsIContent> mContent;
    751 
    752   private:
    753    AttrRelProvider();
    754    AttrRelProvider(const AttrRelProvider&);
    755    AttrRelProvider& operator=(const AttrRelProvider&);
    756  };
    757 
    758  typedef nsTArray<mozilla::UniquePtr<AttrRelProvider>> AttrRelProviders;
    759  typedef nsClassHashtable<nsStringHashKey, AttrRelProviders>
    760      DependentIDsHashtable;
    761 
    762  /**
    763   * Returns/creates/removes attribute relation providers associated with
    764   * a DOM document if the element is in uncomposed document or associated
    765   * with shadow DOM the element is in.
    766   */
    767  AttrRelProviders* GetRelProviders(dom::Element* aElement,
    768                                    const nsAString& aID) const;
    769  AttrRelProviders* GetOrCreateRelProviders(dom::Element* aElement,
    770                                            const nsAString& aID);
    771  void RemoveRelProvidersIfEmpty(dom::Element* aElement, const nsAString& aID);
    772 
    773  /**
    774   * A map used to look up the target node for an implicit reverse relation
    775   * where the target of the explicit relation is specified as an id.
    776   * For example:
    777   * <div id="label">Name:</div><input aria-labelledby="label">
    778   * The div should get a LABEL_FOR relation targeting the input. To facilitate
    779   * that, mDependentIDsHashes maps from "label" to an AttrRelProvider
    780   * specifying aria-labelledby and the input. Because ids are scoped to the
    781   * nearest ancestor document or shadow root, mDependentIDsHashes maps from the
    782   * DocumentOrShadowRoot first.
    783   */
    784  nsClassHashtable<nsPtrHashKey<dom::DocumentOrShadowRoot>,
    785                   DependentIDsHashtable>
    786      mDependentIDsHashes;
    787 
    788  /**
    789   * A map used to look up the target element for an implicit reverse relation
    790   * where the target of the explicit relation is also specified as an element.
    791   * This is similar to mDependentIDsHashes, except that this is used when a
    792   * DOM property is used to set the relation target element directly, rather
    793   * than using an id. For example:
    794   * <button>More info</button><div popover>Some info</div>
    795   * The button's .popoverTargetElement property is set to the div so that the
    796   * button invokes the popover.
    797   * To facilitate finding the invoker given the popover, mDependentElementsMap
    798   * maps from the div to an AttrRelProvider specifying popovertarget and the
    799   * button.
    800   */
    801  nsTHashMap<nsIContent*, AttrRelProviders> mDependentElementsMap;
    802 
    803  friend class RelatedAccIterator;
    804 
    805  /**
    806   * Used for our caching algorithm. We store the list of nodes that should be
    807   * invalidated.
    808   *
    809   * @see ProcessInvalidationList
    810   */
    811  nsTArray<RefPtr<nsIContent>> mInvalidationList;
    812 
    813  /**
    814   * Holds a list of aria-owns relocations.
    815   */
    816  nsClassHashtable<nsPtrHashKey<LocalAccessible>,
    817                   nsTArray<RefPtr<LocalAccessible>>>
    818      mARIAOwnsHash;
    819 
    820  /**
    821   * Keeps a list of pending subtrees to update post-refresh.
    822   */
    823  nsTArray<RefPtr<nsIContent>> mPendingUpdates;
    824 
    825  /**
    826   * Used to process notification from core and accessible events.
    827   */
    828  RefPtr<NotificationController> mNotificationController;
    829  friend class EventTree;
    830  friend class NotificationController;
    831 
    832  /*
    833   * The accessibility service may need to process queued cache updates outside
    834   * of the regular NotificationController flow.
    835   */
    836  friend class ::nsAccessibilityService;
    837 
    838 private:
    839  void SetRoleMapEntryForDoc(dom::Element* aElement);
    840 
    841  /**
    842   * This must be called whenever an Accessible is moved in a content process.
    843   * It keeps track of Accessibles moved during this tick.
    844   */
    845  void TrackMovedAccessible(LocalAccessible* aAcc);
    846 
    847  /**
    848   * For hidden subtrees, fire a name/description change event if the subtree
    849   * is a target of aria-labelledby/describedby.
    850   * This does nothing if it is called on a node which is not part of a hidden
    851   * aria-labelledby/describedby target.
    852   */
    853  void MaybeHandleChangeToHiddenNameOrDescription(nsIContent* aChild);
    854 
    855  void MaybeHandleChangeToAriaActions(LocalAccessible* aAcc,
    856                                      const nsAtom* aAttribute);
    857 
    858  void MaybeFireEventsForChangedPopover(LocalAccessible* aAcc);
    859 
    860  PresShell* mPresShell;
    861 
    862  // Exclusively owned by IPDL so don't manually delete it!
    863  // Cleared in ActorDestroy
    864  DocAccessibleChild* mIPCDoc;
    865 
    866  // These data structures map between LocalAccessibles and CacheDomains,
    867  // tracking cache updates that have been queued during the current tick but
    868  // not yet sent. If there are a lot of nearby text cache updates (e.g. during
    869  // a reflow), it is much more performant to process them in order because we
    870  // then benefit from the layout line cursor. However, we still only want to
    871  // process each LocalAccessible only once. Therefore, we use an array for
    872  // ordering and a hash map to avoid duplicates, since Gecko has no ordered
    873  // set data structure. The array contains pairs of LocalAccessible and cache
    874  // domain. The hash map maps from LocalAccessible to the corresponding index
    875  // in the array. These data structures must be kept in sync. It is possible
    876  // for these to contain a reference to the document they live on. We clear
    877  // them in Shutdown() to avoid cyclical references.
    878  nsTArray<std::pair<RefPtr<LocalAccessible>, uint64_t>>
    879      mQueuedCacheUpdatesArray;
    880  nsTHashMap<LocalAccessible*, size_t> mQueuedCacheUpdatesHash;
    881 
    882  // A set of Accessibles moved during this tick. Only used in content
    883  // processes.
    884  nsTHashSet<RefPtr<LocalAccessible>> mMovedAccessibles;
    885  // A set of Accessibles inserted during this tick. Only used in content
    886  // processes. This is needed to prevent insertions + moves of the same
    887  // Accessible in the same tick from being tracked as moves.
    888  nsTHashSet<RefPtr<LocalAccessible>> mInsertedAccessibles;
    889  // A set of DOM nodes removed during this tick. This avoids a lot of pointless
    890  // recursive DOM traversals.
    891  nsTHashSet<nsIContent*> mRemovedNodes;
    892 };
    893 
    894 inline DocAccessible* LocalAccessible::AsDoc() {
    895  return IsDoc() ? static_cast<DocAccessible*>(this) : nullptr;
    896 }
    897 
    898 }  // namespace a11y
    899 }  // namespace mozilla
    900 
    901 #endif