|
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 "nsIAccessibleDocument.h" |
|
10 #include "nsIAccessiblePivot.h" |
|
11 |
|
12 #include "AccEvent.h" |
|
13 #include "HyperTextAccessibleWrap.h" |
|
14 |
|
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" |
|
24 |
|
25 class nsAccessiblePivot; |
|
26 |
|
27 class nsIScrollableView; |
|
28 |
|
29 const uint32_t kDefaultCacheSize = 256; |
|
30 |
|
31 namespace mozilla { |
|
32 namespace a11y { |
|
33 |
|
34 class DocManager; |
|
35 class NotificationController; |
|
36 class RelatedAccIterator; |
|
37 template<class Class, class Arg> |
|
38 class TNotification; |
|
39 |
|
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) |
|
50 |
|
51 NS_DECL_NSIACCESSIBLEDOCUMENT |
|
52 |
|
53 NS_DECL_NSIOBSERVER |
|
54 |
|
55 NS_DECL_NSIACCESSIBLEPIVOTOBSERVER |
|
56 |
|
57 public: |
|
58 |
|
59 DocAccessible(nsIDocument* aDocument, nsIContent* aRootContent, |
|
60 nsIPresShell* aPresShell); |
|
61 virtual ~DocAccessible(); |
|
62 |
|
63 // nsIAccessible |
|
64 NS_IMETHOD TakeFocus(void); |
|
65 |
|
66 // nsIScrollPositionListener |
|
67 virtual void ScrollPositionWillChange(nscoord aX, nscoord aY) {} |
|
68 virtual void ScrollPositionDidChange(nscoord aX, nscoord aY); |
|
69 |
|
70 // nsIDocumentObserver |
|
71 NS_DECL_NSIDOCUMENTOBSERVER |
|
72 |
|
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; } |
|
79 |
|
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(); |
|
89 |
|
90 #ifdef A11Y_LOG |
|
91 virtual nsresult HandleAccEvent(AccEvent* aEvent); |
|
92 #endif |
|
93 |
|
94 virtual void GetBoundsRect(nsRect& aRect, nsIFrame** aRelativeFrame); |
|
95 |
|
96 // HyperTextAccessible |
|
97 virtual already_AddRefed<nsIEditor> GetEditor() const; |
|
98 |
|
99 // DocAccessible |
|
100 |
|
101 /** |
|
102 * Return presentation shell for this document accessible. |
|
103 */ |
|
104 nsIPresShell* PresShell() const { return mPresShell; } |
|
105 |
|
106 /** |
|
107 * Return the presentation shell's context. |
|
108 */ |
|
109 nsPresContext* PresContext() const { return mPresShell->GetPresContext(); } |
|
110 |
|
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 } |
|
122 |
|
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 }; |
|
138 |
|
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); } |
|
145 |
|
146 /** |
|
147 * Return a native window handler or pointer depending on platform. |
|
148 */ |
|
149 virtual void* GetNativeWindow() const; |
|
150 |
|
151 /** |
|
152 * Return the parent document. |
|
153 */ |
|
154 DocAccessible* ParentDocument() const |
|
155 { return mParent ? mParent->Document() : nullptr; } |
|
156 |
|
157 /** |
|
158 * Return the child document count. |
|
159 */ |
|
160 uint32_t ChildDocumentCount() const |
|
161 { return mChildDocuments.Length(); } |
|
162 |
|
163 /** |
|
164 * Return the child document at the given index. |
|
165 */ |
|
166 DocAccessible* GetChildDocumentAt(uint32_t aIndex) const |
|
167 { return mChildDocuments.SafeElementAt(aIndex, nullptr); } |
|
168 |
|
169 /** |
|
170 * Fire accessible event asynchronously. |
|
171 */ |
|
172 void FireDelayedEvent(AccEvent* aEvent); |
|
173 void FireDelayedEvent(uint32_t aEventType, Accessible* aTarget); |
|
174 |
|
175 /** |
|
176 * Fire value change event on the given accessible if applicable. |
|
177 */ |
|
178 void MaybeNotifyOfValueChange(Accessible* aAccessible); |
|
179 |
|
180 /** |
|
181 * Get/set the anchor jump. |
|
182 */ |
|
183 Accessible* AnchorJump() |
|
184 { return GetAccessibleOrContainer(mAnchorJumpElm); } |
|
185 |
|
186 void SetAnchorJump(nsIContent* aTargetNode) |
|
187 { mAnchorJumpElm = aTargetNode; } |
|
188 |
|
189 /** |
|
190 * Bind the child document to the tree. |
|
191 */ |
|
192 void BindChildDocument(DocAccessible* aDocument); |
|
193 |
|
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); |
|
205 |
|
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; |
|
213 |
|
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; |
|
222 |
|
223 /** |
|
224 * Return whether the given DOM node has an accessible or not. |
|
225 */ |
|
226 bool HasAccessible(nsINode* aNode) const |
|
227 { return GetAccessible(aNode); } |
|
228 |
|
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 } |
|
241 |
|
242 /** |
|
243 * Return the cached accessible by the given unique ID looking through |
|
244 * this and nested documents. |
|
245 */ |
|
246 Accessible* GetAccessibleByUniqueIDInSubtree(void* aUniqueID); |
|
247 |
|
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; |
|
253 |
|
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 } |
|
261 |
|
262 /** |
|
263 * Return an accessible for the given node or its first accessible descendant. |
|
264 */ |
|
265 Accessible* GetAccessibleOrDescendant(nsINode* aNode) const; |
|
266 |
|
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); } |
|
276 |
|
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); |
|
285 |
|
286 /** |
|
287 * Remove from document and shutdown the given accessible. |
|
288 */ |
|
289 void UnbindFromDocument(Accessible* aAccessible); |
|
290 |
|
291 /** |
|
292 * Notify the document accessible that content was inserted. |
|
293 */ |
|
294 void ContentInserted(nsIContent* aContainerNode, |
|
295 nsIContent* aStartChildNode, |
|
296 nsIContent* aEndChildNode); |
|
297 |
|
298 /** |
|
299 * Notify the document accessible that content was removed. |
|
300 */ |
|
301 void ContentRemoved(nsIContent* aContainerNode, nsIContent* aChildNode); |
|
302 |
|
303 /** |
|
304 * Updates accessible tree when rendered text is changed. |
|
305 */ |
|
306 void UpdateText(nsIContent* aTextNode); |
|
307 |
|
308 /** |
|
309 * Recreate an accessible, results in hide/show events pair. |
|
310 */ |
|
311 void RecreateAccessible(nsIContent* aContent); |
|
312 |
|
313 protected: |
|
314 |
|
315 void LastRelease(); |
|
316 |
|
317 // Accessible |
|
318 virtual void CacheChildren(); |
|
319 |
|
320 // DocAccessible |
|
321 virtual nsresult AddEventListeners(); |
|
322 virtual nsresult RemoveEventListeners(); |
|
323 |
|
324 /** |
|
325 * Marks this document as loaded or loading. |
|
326 */ |
|
327 void NotifyOfLoad(uint32_t aLoadEventType); |
|
328 void NotifyOfLoading(bool aIsReloading); |
|
329 |
|
330 friend class DocManager; |
|
331 |
|
332 /** |
|
333 * Perform initial update (create accessible tree). |
|
334 * Can be overridden by wrappers to prepare initialization work. |
|
335 */ |
|
336 virtual void DoInitialUpdate(); |
|
337 |
|
338 /** |
|
339 * Process document load notification, fire document load and state busy |
|
340 * events if applicable. |
|
341 */ |
|
342 void ProcessLoad(); |
|
343 |
|
344 /** |
|
345 * Add/remove scroll listeners, @see nsIScrollPositionListener interface. |
|
346 */ |
|
347 void AddScrollListener(); |
|
348 void RemoveScrollListener(); |
|
349 |
|
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 } |
|
358 |
|
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 } |
|
367 |
|
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); |
|
378 |
|
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); |
|
389 |
|
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); |
|
399 |
|
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); |
|
409 |
|
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); |
|
417 |
|
418 /** |
|
419 * Process ARIA active-descendant attribute change. |
|
420 */ |
|
421 void ARIAActiveDescendantChanged(Accessible* aAccessible); |
|
422 |
|
423 /** |
|
424 * Update the accessible tree for inserted content. |
|
425 */ |
|
426 void ProcessContentInserted(Accessible* aContainer, |
|
427 const nsTArray<nsCOMPtr<nsIContent> >* aInsertedContent); |
|
428 |
|
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(); |
|
437 |
|
438 /** |
|
439 * Update the accessible tree for content insertion or removal. |
|
440 */ |
|
441 void UpdateTree(Accessible* aContainer, nsIContent* aChildNode, |
|
442 bool aIsInsert); |
|
443 |
|
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 }; |
|
453 |
|
454 uint32_t UpdateTreeInternal(Accessible* aChild, bool aIsInsert, |
|
455 AccReorderEvent* aReorderEvent); |
|
456 |
|
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); |
|
466 |
|
467 /** |
|
468 * Remove accessibles in subtree from node to accessible map. |
|
469 */ |
|
470 void UncacheChildrenInSubtree(Accessible* aRoot); |
|
471 |
|
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); |
|
479 |
|
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; |
|
490 |
|
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); |
|
498 |
|
499 protected: |
|
500 |
|
501 /** |
|
502 * State and property flags, kept by mDocFlags. |
|
503 */ |
|
504 enum { |
|
505 // Whether scroll listeners were added. |
|
506 eScrollInitialized = 1 << 0, |
|
507 |
|
508 // Whether the document is a tab document. |
|
509 eTabDocument = 1 << 1 |
|
510 }; |
|
511 |
|
512 /** |
|
513 * Cache of accessibles within this document accessible. |
|
514 */ |
|
515 AccessibleHashtable mAccessibleCache; |
|
516 nsDataHashtable<nsPtrHashKey<const nsINode>, Accessible*> |
|
517 mNodeToAccessibleMap; |
|
518 |
|
519 nsIDocument* mDocumentNode; |
|
520 nsCOMPtr<nsITimer> mScrollWatchTimer; |
|
521 uint16_t mScrollPositionChangedTicks; // Used for tracking scroll events |
|
522 |
|
523 /** |
|
524 * Bit mask of document load states (@see LoadState). |
|
525 */ |
|
526 uint32_t mLoadState : 3; |
|
527 |
|
528 /** |
|
529 * Bit mask of other states and props. |
|
530 */ |
|
531 uint32_t mDocFlags : 28; |
|
532 |
|
533 /** |
|
534 * Type of document load event fired after the document is loaded completely. |
|
535 */ |
|
536 uint32_t mLoadEventType; |
|
537 |
|
538 /** |
|
539 * Reference to anchor jump element. |
|
540 */ |
|
541 nsCOMPtr<nsIContent> mAnchorJumpElm; |
|
542 |
|
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; |
|
550 |
|
551 // True if the accessible state bit was on |
|
552 bool mStateBitWasOn; |
|
553 }; |
|
554 |
|
555 nsTArray<nsRefPtr<DocAccessible> > mChildDocuments; |
|
556 |
|
557 /** |
|
558 * The virtual cursor of the document. |
|
559 */ |
|
560 nsRefPtr<nsAccessiblePivot> mVirtualCursor; |
|
561 |
|
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) { } |
|
570 |
|
571 nsIAtom* mRelAttr; |
|
572 nsCOMPtr<nsIContent> mContent; |
|
573 |
|
574 private: |
|
575 AttrRelProvider(); |
|
576 AttrRelProvider(const AttrRelProvider&); |
|
577 AttrRelProvider& operator =(const AttrRelProvider&); |
|
578 }; |
|
579 |
|
580 typedef nsTArray<nsAutoPtr<AttrRelProvider> > AttrRelProviderArray; |
|
581 typedef nsClassHashtable<nsStringHashKey, AttrRelProviderArray> |
|
582 DependentIDsHashtable; |
|
583 |
|
584 /** |
|
585 * The cache of IDs pointed by relation attributes. |
|
586 */ |
|
587 DependentIDsHashtable mDependentIDsHash; |
|
588 |
|
589 static PLDHashOperator |
|
590 CycleCollectorTraverseDepIDsEntry(const nsAString& aKey, |
|
591 AttrRelProviderArray* aProviders, |
|
592 void* aUserArg); |
|
593 |
|
594 friend class RelatedAccIterator; |
|
595 |
|
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; |
|
603 |
|
604 /** |
|
605 * Used to process notification from core and accessible events. |
|
606 */ |
|
607 nsRefPtr<NotificationController> mNotificationController; |
|
608 friend class EventQueue; |
|
609 friend class NotificationController; |
|
610 |
|
611 private: |
|
612 |
|
613 nsIPresShell* mPresShell; |
|
614 }; |
|
615 |
|
616 inline DocAccessible* |
|
617 Accessible::AsDoc() |
|
618 { |
|
619 return IsDoc() ? static_cast<DocAccessible*>(this) : nullptr; |
|
620 } |
|
621 |
|
622 } // namespace a11y |
|
623 } // namespace mozilla |
|
624 |
|
625 #endif |