Wed, 31 Dec 2014 07:16:47 +0100
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