accessible/src/generic/DocAccessible.h

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

     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 "nsIAccessibleDocument.h"
    10 #include "nsIAccessiblePivot.h"
    12 #include "AccEvent.h"
    13 #include "HyperTextAccessibleWrap.h"
    15 #include "nsClassHashtable.h"
    16 #include "nsDataHashtable.h"
    17 #include "nsIDocument.h"
    18 #include "nsIDocumentObserver.h"
    19 #include "nsIEditor.h"
    20 #include "nsIObserver.h"
    21 #include "nsIScrollPositionListener.h"
    22 #include "nsITimer.h"
    23 #include "nsIWeakReference.h"
    25 class nsAccessiblePivot;
    27 class nsIScrollableView;
    29 const uint32_t kDefaultCacheSize = 256;
    31 namespace mozilla {
    32 namespace a11y {
    34 class DocManager;
    35 class NotificationController;
    36 class RelatedAccIterator;
    37 template<class Class, class Arg>
    38 class TNotification;
    40 class DocAccessible : public HyperTextAccessibleWrap,
    41                       public nsIAccessibleDocument,
    42                       public nsIDocumentObserver,
    43                       public nsIObserver,
    44                       public nsIScrollPositionListener,
    45                       public nsSupportsWeakReference,
    46                       public nsIAccessiblePivotObserver
    47 {
    48   NS_DECL_ISUPPORTS_INHERITED
    49   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DocAccessible, Accessible)
    51   NS_DECL_NSIACCESSIBLEDOCUMENT
    53   NS_DECL_NSIOBSERVER
    55   NS_DECL_NSIACCESSIBLEPIVOTOBSERVER
    57 public:
    59   DocAccessible(nsIDocument* aDocument, nsIContent* aRootContent,
    60                 nsIPresShell* aPresShell);
    61   virtual ~DocAccessible();
    63   // nsIAccessible
    64   NS_IMETHOD TakeFocus(void);
    66   // nsIScrollPositionListener
    67   virtual void ScrollPositionWillChange(nscoord aX, nscoord aY) {}
    68   virtual void ScrollPositionDidChange(nscoord aX, nscoord aY);
    70   // nsIDocumentObserver
    71   NS_DECL_NSIDOCUMENTOBSERVER
    73   // Accessible
    74   virtual void Init();
    75   virtual void Shutdown();
    76   virtual nsIFrame* GetFrame() const;
    77   virtual nsINode* GetNode() const { return mDocumentNode; }
    78   nsIDocument* DocumentNode() const { return mDocumentNode; }
    80   virtual mozilla::a11y::ENameValueFlag Name(nsString& aName);
    81   virtual void Description(nsString& aDescription);
    82   virtual Accessible* FocusedChild();
    83   virtual mozilla::a11y::role NativeRole();
    84   virtual uint64_t NativeState();
    85   virtual uint64_t NativeInteractiveState() const;
    86   virtual bool NativelyUnavailable() const;
    87   virtual void ApplyARIAState(uint64_t* aState) const;
    88   virtual already_AddRefed<nsIPersistentProperties> Attributes();
    90 #ifdef A11Y_LOG
    91   virtual nsresult HandleAccEvent(AccEvent* aEvent);
    92 #endif
    94   virtual void GetBoundsRect(nsRect& aRect, nsIFrame** aRelativeFrame);
    96   // HyperTextAccessible
    97   virtual already_AddRefed<nsIEditor> GetEditor() const;
    99   // DocAccessible
   101   /**
   102    * Return presentation shell for this document accessible.
   103    */
   104   nsIPresShell* PresShell() const { return mPresShell; }
   106   /**
   107    * Return the presentation shell's context.
   108    */
   109   nsPresContext* PresContext() const { return mPresShell->GetPresContext(); }
   111   /**
   112    * Return true if associated DOM document was loaded and isn't unloading.
   113    */
   114   bool IsContentLoaded() const
   115   {
   116     // eDOMLoaded flag check is used for error pages as workaround to make this
   117     // method return correct result since error pages do not receive 'pageshow'
   118     // event and as consequence nsIDocument::IsShowing() returns false.
   119     return mDocumentNode && mDocumentNode->IsVisible() &&
   120       (mDocumentNode->IsShowing() || HasLoadState(eDOMLoaded));
   121   }
   123   /**
   124    * Document load states.
   125    */
   126   enum LoadState {
   127     // initial tree construction is pending
   128     eTreeConstructionPending = 0,
   129     // initial tree construction done
   130     eTreeConstructed = 1,
   131     // DOM document is loaded.
   132     eDOMLoaded = 1 << 1,
   133     // document is ready
   134     eReady = eTreeConstructed | eDOMLoaded,
   135     // document and all its subdocuments are ready
   136     eCompletelyLoaded = eReady | 1 << 2
   137   };
   139   /**
   140    * Return true if the document has given document state.
   141    */
   142   bool HasLoadState(LoadState aState) const
   143     { return (mLoadState & static_cast<uint32_t>(aState)) == 
   144         static_cast<uint32_t>(aState); }
   146   /**
   147    * Return a native window handler or pointer depending on platform.
   148    */
   149   virtual void* GetNativeWindow() const;
   151   /**
   152    * Return the parent document.
   153    */
   154   DocAccessible* ParentDocument() const
   155     { return mParent ? mParent->Document() : nullptr; }
   157   /**
   158    * Return the child document count.
   159    */
   160   uint32_t ChildDocumentCount() const
   161     { return mChildDocuments.Length(); }
   163   /**
   164    * Return the child document at the given index.
   165    */
   166   DocAccessible* GetChildDocumentAt(uint32_t aIndex) const
   167     { return mChildDocuments.SafeElementAt(aIndex, nullptr); }
   169   /**
   170    * Fire accessible event asynchronously.
   171    */
   172   void FireDelayedEvent(AccEvent* aEvent);
   173   void FireDelayedEvent(uint32_t aEventType, Accessible* aTarget);
   175   /**
   176    * Fire value change event on the given accessible if applicable.
   177    */
   178   void MaybeNotifyOfValueChange(Accessible* aAccessible);
   180   /**
   181    * Get/set the anchor jump.
   182    */
   183   Accessible* AnchorJump()
   184     { return GetAccessibleOrContainer(mAnchorJumpElm); }
   186   void SetAnchorJump(nsIContent* aTargetNode)
   187     { mAnchorJumpElm = aTargetNode; }
   189   /**
   190    * Bind the child document to the tree.
   191    */
   192   void BindChildDocument(DocAccessible* aDocument);
   194   /**
   195    * Process the generic notification.
   196    *
   197    * @note  The caller must guarantee that the given instance still exists when
   198    *          notification is processed.
   199    * @see   NotificationController::HandleNotification
   200    */
   201   template<class Class, class Arg>
   202   void HandleNotification(Class* aInstance,
   203                           typename TNotification<Class, Arg>::Callback aMethod,
   204                           Arg* aArg);
   206   /**
   207    * Return the cached accessible by the given DOM node if it's in subtree of
   208    * this document accessible or the document accessible itself, otherwise null.
   209    *
   210    * @return the accessible object
   211    */
   212   Accessible* GetAccessible(nsINode* aNode) const;
   214   /**
   215    * Return an accessible for the given node even if the node is not in
   216    * document's node map cache (like HTML area element).
   217    *
   218    * XXX: it should be really merged with GetAccessible().
   219    */
   220   Accessible* GetAccessibleEvenIfNotInMap(nsINode* aNode) const;
   221   Accessible* GetAccessibleEvenIfNotInMapOrContainer(nsINode* aNode) const;
   223   /**
   224    * Return whether the given DOM node has an accessible or not.
   225    */
   226   bool HasAccessible(nsINode* aNode) const
   227     { return GetAccessible(aNode); }
   229   /**
   230    * Return the cached accessible by the given unique ID within this document.
   231    *
   232    * @note   the unique ID matches with the uniqueID() of Accessible
   233    *
   234    * @param  aUniqueID  [in] the unique ID used to cache the node.
   235    */
   236   Accessible* GetAccessibleByUniqueID(void* aUniqueID)
   237   {
   238     return UniqueID() == aUniqueID ?
   239       this : mAccessibleCache.GetWeak(aUniqueID);
   240   }
   242   /**
   243    * Return the cached accessible by the given unique ID looking through
   244    * this and nested documents.
   245    */
   246   Accessible* GetAccessibleByUniqueIDInSubtree(void* aUniqueID);
   248   /**
   249    * Return an accessible for the given DOM node or container accessible if
   250    * the node is not accessible.
   251    */
   252   Accessible* GetAccessibleOrContainer(nsINode* aNode) const;
   254   /**
   255    * Return a container accessible for the given DOM node.
   256    */
   257   Accessible* GetContainerAccessible(nsINode* aNode) const
   258   {
   259     return aNode ? GetAccessibleOrContainer(aNode->GetParentNode()) : nullptr;
   260   }
   262   /**
   263    * Return an accessible for the given node or its first accessible descendant.
   264    */
   265   Accessible* GetAccessibleOrDescendant(nsINode* aNode) const;
   267   /**
   268    * Return true if the given ID is referred by relation attribute.
   269    *
   270    * @note Different elements may share the same ID if they are hosted inside
   271    *       XBL bindings. Be careful the result of this method may be  senseless
   272    *       while it's called for XUL elements (where XBL is used widely).
   273    */
   274   bool IsDependentID(const nsAString& aID) const
   275     { return mDependentIDsHash.Get(aID, nullptr); }
   277   /**
   278    * Initialize the newly created accessible and put it into document caches.
   279    *
   280    * @param  aAccessible    [in] created accessible
   281    * @param  aRoleMapEntry  [in] the role map entry role the ARIA role or nullptr
   282    *                          if none
   283    */
   284   void BindToDocument(Accessible* aAccessible, nsRoleMapEntry* aRoleMapEntry);
   286   /**
   287    * Remove from document and shutdown the given accessible.
   288    */
   289   void UnbindFromDocument(Accessible* aAccessible);
   291   /**
   292    * Notify the document accessible that content was inserted.
   293    */
   294   void ContentInserted(nsIContent* aContainerNode,
   295                        nsIContent* aStartChildNode,
   296                        nsIContent* aEndChildNode);
   298   /**
   299    * Notify the document accessible that content was removed.
   300    */
   301   void ContentRemoved(nsIContent* aContainerNode, nsIContent* aChildNode);
   303   /**
   304    * Updates accessible tree when rendered text is changed.
   305    */
   306   void UpdateText(nsIContent* aTextNode);
   308   /**
   309    * Recreate an accessible, results in hide/show events pair.
   310    */
   311   void RecreateAccessible(nsIContent* aContent);
   313 protected:
   315   void LastRelease();
   317   // Accessible
   318   virtual void CacheChildren();
   320   // DocAccessible
   321   virtual nsresult AddEventListeners();
   322   virtual nsresult RemoveEventListeners();
   324   /**
   325    * Marks this document as loaded or loading.
   326    */
   327   void NotifyOfLoad(uint32_t aLoadEventType);
   328   void NotifyOfLoading(bool aIsReloading);
   330   friend class DocManager;
   332   /**
   333    * Perform initial update (create accessible tree).
   334    * Can be overridden by wrappers to prepare initialization work.
   335    */
   336   virtual void DoInitialUpdate();
   338   /**
   339    * Process document load notification, fire document load and state busy
   340    * events if applicable.
   341    */
   342   void ProcessLoad();
   344   /**
   345    * Add/remove scroll listeners, @see nsIScrollPositionListener interface.
   346    */
   347   void AddScrollListener();
   348   void RemoveScrollListener();
   350   /**
   351    * Append the given document accessible to this document's child document
   352    * accessibles.
   353    */
   354   bool AppendChildDocument(DocAccessible* aChildDocument)
   355   {
   356     return mChildDocuments.AppendElement(aChildDocument);
   357   }
   359   /**
   360    * Remove the given document accessible from this document's child document
   361    * accessibles.
   362    */
   363   void RemoveChildDocument(DocAccessible* aChildDocument)
   364   {
   365     mChildDocuments.RemoveElement(aChildDocument);
   366   }
   368   /**
   369    * Add dependent IDs pointed by accessible element by relation attribute to
   370    * cache. If the relation attribute is missed then all relation attributes
   371    * are checked.
   372    *
   373    * @param aRelProvider [in] accessible that element has relation attribute
   374    * @param aRelAttr     [in, optional] relation attribute
   375    */
   376   void AddDependentIDsFor(dom::Element* aRelProviderElm,
   377                           nsIAtom* aRelAttr = nullptr);
   379   /**
   380    * Remove dependent IDs pointed by accessible element by relation attribute
   381    * from cache. If the relation attribute is absent then all relation
   382    * attributes are checked.
   383    *
   384    * @param aRelProvider [in] accessible that element has relation attribute
   385    * @param aRelAttr     [in, optional] relation attribute
   386    */
   387   void RemoveDependentIDsFor(dom::Element* aRelProviderElm,
   388                              nsIAtom* aRelAttr = nullptr);
   390   /**
   391    * Update or recreate an accessible depending on a changed attribute.
   392    *
   393    * @param aElement   [in] the element the attribute was changed on
   394    * @param aAttribute [in] the changed attribute
   395    * @return            true if an action was taken on the attribute change
   396    */
   397   bool UpdateAccessibleOnAttrChange(mozilla::dom::Element* aElement,
   398                                     nsIAtom* aAttribute);
   400   /**
   401    * Fire accessible events when attribute is changed.
   402    *
   403    * @param aAccessible   [in] accessible the DOM attribute is changed for
   404    * @param aNameSpaceID  [in] namespace of changed attribute
   405    * @param aAttribute    [in] changed attribute
   406    */
   407   void AttributeChangedImpl(Accessible* aAccessible,
   408                             int32_t aNameSpaceID, nsIAtom* aAttribute);
   410   /**
   411    * Fire accessible events when ARIA attribute is changed.
   412    *
   413    * @param aAccessible  [in] accesislbe the DOM attribute is changed for
   414    * @param aAttribute   [in] changed attribute
   415    */
   416   void ARIAAttributeChanged(Accessible* aAccessible, nsIAtom* aAttribute);
   418   /**
   419    * Process ARIA active-descendant attribute change.
   420    */
   421   void ARIAActiveDescendantChanged(Accessible* aAccessible);
   423   /**
   424    * Update the accessible tree for inserted content.
   425    */
   426   void ProcessContentInserted(Accessible* aContainer,
   427                               const nsTArray<nsCOMPtr<nsIContent> >* aInsertedContent);
   429   /**
   430    * Used to notify the document to make it process the invalidation list.
   431    *
   432    * While children are cached we may encounter the case there's no accessible
   433    * for referred content by related accessible. Store these related nodes to
   434    * invalidate their containers later.
   435    */
   436   void ProcessInvalidationList();
   438   /**
   439    * Update the accessible tree for content insertion or removal.
   440    */
   441   void UpdateTree(Accessible* aContainer, nsIContent* aChildNode,
   442                   bool aIsInsert);
   444   /**
   445    * Helper for UpdateTree() method. Go down to DOM subtree and updates
   446    * accessible tree. Return one of these flags.
   447    */
   448   enum EUpdateTreeFlags {
   449     eNoAccessible = 0,
   450     eAccessible = 1,
   451     eAlertAccessible = 2
   452   };
   454   uint32_t UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
   455                               AccReorderEvent* aReorderEvent);
   457   /**
   458    * Create accessible tree.
   459    *
   460    * @param aRoot       [in] a root of subtree to create
   461    * @param aFocusedAcc [in, optional] a focused accessible under created
   462    *                      subtree if any
   463    */
   464   void CacheChildrenInSubtree(Accessible* aRoot,
   465                               Accessible** aFocusedAcc = nullptr);
   467   /**
   468    * Remove accessibles in subtree from node to accessible map.
   469    */
   470   void UncacheChildrenInSubtree(Accessible* aRoot);
   472   /**
   473    * Shutdown any cached accessible in the subtree.
   474    *
   475    * @param aAccessible  [in] the root of the subrtee to invalidate accessible
   476    *                      child/parent refs in
   477    */
   478   void ShutdownChildrenInSubtree(Accessible* aAccessible);
   480   /**
   481    * Return true if the document is a target of document loading events
   482    * (for example, state busy change or document reload events).
   483    *
   484    * Rules: The root chrome document accessible is never an event target
   485    * (for example, Firefox UI window). If the sub document is loaded within its
   486    * parent document then the parent document is a target only (aka events
   487    * coalescence).
   488    */
   489   bool IsLoadEventTarget() const;
   491   /**
   492    * Used to fire scrolling end event after page scroll.
   493    *
   494    * @param aTimer    [in] the timer object
   495    * @param aClosure  [in] the document accessible where scrolling happens
   496    */
   497   static void ScrollTimerCallback(nsITimer* aTimer, void* aClosure);
   499 protected:
   501   /**
   502    * State and property flags, kept by mDocFlags.
   503    */
   504   enum {
   505     // Whether scroll listeners were added.
   506     eScrollInitialized = 1 << 0,
   508     // Whether the document is a tab document.
   509     eTabDocument = 1 << 1
   510   };
   512   /**
   513    * Cache of accessibles within this document accessible.
   514    */
   515   AccessibleHashtable mAccessibleCache;
   516   nsDataHashtable<nsPtrHashKey<const nsINode>, Accessible*>
   517     mNodeToAccessibleMap;
   519   nsIDocument* mDocumentNode;
   520     nsCOMPtr<nsITimer> mScrollWatchTimer;
   521     uint16_t mScrollPositionChangedTicks; // Used for tracking scroll events
   523   /**
   524    * Bit mask of document load states (@see LoadState).
   525    */
   526   uint32_t mLoadState : 3;
   528   /**
   529    * Bit mask of other states and props.
   530    */
   531   uint32_t mDocFlags : 28;
   533   /**
   534    * Type of document load event fired after the document is loaded completely.
   535    */
   536   uint32_t mLoadEventType;
   538   /**
   539    * Reference to anchor jump element.
   540    */
   541   nsCOMPtr<nsIContent> mAnchorJumpElm;
   543   /**
   544    * A generic state (see items below) before the attribute value was changed.
   545    * @see AttributeWillChange and AttributeChanged notifications.
   546    */
   547   union {
   548     // ARIA attribute value
   549     nsIAtom* mARIAAttrOldValue;
   551     // True if the accessible state bit was on
   552     bool mStateBitWasOn;
   553   };
   555   nsTArray<nsRefPtr<DocAccessible> > mChildDocuments;
   557   /**
   558    * The virtual cursor of the document.
   559    */
   560   nsRefPtr<nsAccessiblePivot> mVirtualCursor;
   562   /**
   563    * A storage class for pairing content with one of its relation attributes.
   564    */
   565   class AttrRelProvider
   566   {
   567   public:
   568     AttrRelProvider(nsIAtom* aRelAttr, nsIContent* aContent) :
   569       mRelAttr(aRelAttr), mContent(aContent) { }
   571     nsIAtom* mRelAttr;
   572     nsCOMPtr<nsIContent> mContent;
   574   private:
   575     AttrRelProvider();
   576     AttrRelProvider(const AttrRelProvider&);
   577     AttrRelProvider& operator =(const AttrRelProvider&);
   578   };
   580   typedef nsTArray<nsAutoPtr<AttrRelProvider> > AttrRelProviderArray;
   581   typedef nsClassHashtable<nsStringHashKey, AttrRelProviderArray>
   582     DependentIDsHashtable;
   584   /**
   585    * The cache of IDs pointed by relation attributes.
   586    */
   587   DependentIDsHashtable mDependentIDsHash;
   589   static PLDHashOperator
   590     CycleCollectorTraverseDepIDsEntry(const nsAString& aKey,
   591                                       AttrRelProviderArray* aProviders,
   592                                       void* aUserArg);
   594   friend class RelatedAccIterator;
   596   /**
   597    * Used for our caching algorithm. We store the list of nodes that should be
   598    * invalidated.
   599    *
   600    * @see ProcessInvalidationList
   601    */
   602   nsTArray<nsIContent*> mInvalidationList;
   604   /**
   605    * Used to process notification from core and accessible events.
   606    */
   607   nsRefPtr<NotificationController> mNotificationController;
   608   friend class EventQueue;
   609   friend class NotificationController;
   611 private:
   613   nsIPresShell* mPresShell;
   614 };
   616 inline DocAccessible*
   617 Accessible::AsDoc()
   618 {
   619   return IsDoc() ? static_cast<DocAccessible*>(this) : nullptr;
   620 }
   622 } // namespace a11y
   623 } // namespace mozilla
   625 #endif

mercurial