Line data Source code
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 "nsIAccessiblePivot.h"
10 :
11 : #include "HyperTextAccessibleWrap.h"
12 : #include "AccEvent.h"
13 :
14 : #include "nsAutoPtr.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"
24 :
25 : class nsAccessiblePivot;
26 :
27 : const uint32_t kDefaultCacheLength = 128;
28 :
29 : namespace mozilla {
30 : namespace a11y {
31 :
32 : class DocManager;
33 : class NotificationController;
34 : class DocAccessibleChild;
35 : class RelatedAccIterator;
36 : template<class Class, class ... Args>
37 : class TNotification;
38 :
39 : class DocAccessible : public HyperTextAccessibleWrap,
40 : public nsIDocumentObserver,
41 : public nsIObserver,
42 : public nsIScrollPositionListener,
43 : public nsSupportsWeakReference,
44 : public nsIAccessiblePivotObserver
45 : {
46 : NS_DECL_ISUPPORTS_INHERITED
47 0 : NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DocAccessible, Accessible)
48 :
49 : NS_DECL_NSIOBSERVER
50 : NS_DECL_NSIACCESSIBLEPIVOTOBSERVER
51 :
52 : public:
53 :
54 : DocAccessible(nsIDocument* aDocument, nsIPresShell* aPresShell);
55 :
56 : // nsIScrollPositionListener
57 0 : virtual void ScrollPositionWillChange(nscoord aX, nscoord aY) override {}
58 : virtual void ScrollPositionDidChange(nscoord aX, nscoord aY) override;
59 :
60 : // nsIDocumentObserver
61 : NS_DECL_NSIDOCUMENTOBSERVER
62 :
63 : // Accessible
64 : virtual void Init();
65 : virtual void Shutdown() override;
66 : virtual nsIFrame* GetFrame() const override;
67 0 : virtual nsINode* GetNode() const override { return mDocumentNode; }
68 0 : nsIDocument* DocumentNode() const { return mDocumentNode; }
69 :
70 : virtual mozilla::a11y::ENameValueFlag Name(nsString& aName) override;
71 : virtual void Description(nsString& aDescription) override;
72 : virtual Accessible* FocusedChild() override;
73 : virtual mozilla::a11y::role NativeRole() override;
74 : virtual uint64_t NativeState() override;
75 : virtual uint64_t NativeInteractiveState() const override;
76 : virtual bool NativelyUnavailable() const override;
77 : virtual void ApplyARIAState(uint64_t* aState) const override;
78 : virtual already_AddRefed<nsIPersistentProperties> Attributes() override;
79 :
80 : virtual void TakeFocus() override;
81 :
82 : #ifdef A11Y_LOG
83 : virtual nsresult HandleAccEvent(AccEvent* aEvent) override;
84 : #endif
85 :
86 : virtual nsRect RelativeBounds(nsIFrame** aRelativeFrame) const override;
87 :
88 : // HyperTextAccessible
89 : virtual already_AddRefed<nsIEditor> GetEditor() const override;
90 :
91 : // DocAccessible
92 :
93 : /**
94 : * Return document URL.
95 : */
96 : void URL(nsAString& aURL) const;
97 :
98 : /**
99 : * Return DOM document title.
100 : */
101 0 : void Title(nsString& aTitle) const { mDocumentNode->GetTitle(aTitle); }
102 :
103 : /**
104 : * Return DOM document mime type.
105 : */
106 0 : void MimeType(nsAString& aType) const { mDocumentNode->GetContentType(aType); }
107 :
108 : /**
109 : * Return DOM document type.
110 : */
111 : void DocType(nsAString& aType) const;
112 :
113 : /**
114 : * Return virtual cursor associated with the document.
115 : */
116 : nsIAccessiblePivot* VirtualCursor();
117 :
118 : /**
119 : * Return presentation shell for this document accessible.
120 : */
121 0 : nsIPresShell* PresShell() const { return mPresShell; }
122 :
123 : /**
124 : * Return the presentation shell's context.
125 : */
126 0 : nsPresContext* PresContext() const { return mPresShell->GetPresContext(); }
127 :
128 : /**
129 : * Return true if associated DOM document was loaded and isn't unloading.
130 : */
131 0 : bool IsContentLoaded() const
132 : {
133 : // eDOMLoaded flag check is used for error pages as workaround to make this
134 : // method return correct result since error pages do not receive 'pageshow'
135 : // event and as consequence nsIDocument::IsShowing() returns false.
136 0 : return mDocumentNode && mDocumentNode->IsVisible() &&
137 0 : (mDocumentNode->IsShowing() || HasLoadState(eDOMLoaded));
138 : }
139 :
140 0 : bool IsHidden() const
141 : {
142 0 : return mDocumentNode->Hidden();
143 : }
144 :
145 : /**
146 : * Document load states.
147 : */
148 : enum LoadState {
149 : // initial tree construction is pending
150 : eTreeConstructionPending = 0,
151 : // initial tree construction done
152 : eTreeConstructed = 1,
153 : // DOM document is loaded.
154 : eDOMLoaded = 1 << 1,
155 : // document is ready
156 : eReady = eTreeConstructed | eDOMLoaded,
157 : // document and all its subdocuments are ready
158 : eCompletelyLoaded = eReady | 1 << 2
159 : };
160 :
161 : /**
162 : * Return true if the document has given document state.
163 : */
164 0 : bool HasLoadState(LoadState aState) const
165 0 : { return (mLoadState & static_cast<uint32_t>(aState)) ==
166 0 : static_cast<uint32_t>(aState); }
167 :
168 : /**
169 : * Return a native window handler or pointer depending on platform.
170 : */
171 : virtual void* GetNativeWindow() const;
172 :
173 : /**
174 : * Return the parent document.
175 : */
176 0 : DocAccessible* ParentDocument() const
177 0 : { return mParent ? mParent->Document() : nullptr; }
178 :
179 : /**
180 : * Return the child document count.
181 : */
182 0 : uint32_t ChildDocumentCount() const
183 0 : { return mChildDocuments.Length(); }
184 :
185 : /**
186 : * Return the child document at the given index.
187 : */
188 0 : DocAccessible* GetChildDocumentAt(uint32_t aIndex) const
189 0 : { return mChildDocuments.SafeElementAt(aIndex, nullptr); }
190 :
191 : /**
192 : * Fire accessible event asynchronously.
193 : */
194 : void FireDelayedEvent(AccEvent* aEvent);
195 : void FireDelayedEvent(uint32_t aEventType, Accessible* aTarget);
196 : void FireEventsOnInsertion(Accessible* aContainer);
197 :
198 : /**
199 : * Fire value change event on the given accessible if applicable.
200 : */
201 : void MaybeNotifyOfValueChange(Accessible* aAccessible);
202 :
203 : /**
204 : * Get/set the anchor jump.
205 : */
206 0 : Accessible* AnchorJump()
207 0 : { return GetAccessibleOrContainer(mAnchorJumpElm); }
208 :
209 0 : void SetAnchorJump(nsIContent* aTargetNode)
210 0 : { mAnchorJumpElm = aTargetNode; }
211 :
212 : /**
213 : * Bind the child document to the tree.
214 : */
215 : void BindChildDocument(DocAccessible* aDocument);
216 :
217 : /**
218 : * Process the generic notification.
219 : *
220 : * @note The caller must guarantee that the given instance still exists when
221 : * notification is processed.
222 : * @see NotificationController::HandleNotification
223 : */
224 : template<class Class, class Arg>
225 : void HandleNotification(Class* aInstance,
226 : typename TNotification<Class, Arg>::Callback aMethod,
227 : Arg* aArg);
228 :
229 : /**
230 : * Return the cached accessible by the given DOM node if it's in subtree of
231 : * this document accessible or the document accessible itself, otherwise null.
232 : *
233 : * @return the accessible object
234 : */
235 0 : Accessible* GetAccessible(nsINode* aNode) const
236 : {
237 0 : return aNode == mDocumentNode ?
238 0 : const_cast<DocAccessible*>(this) : mNodeToAccessibleMap.Get(aNode);
239 : }
240 :
241 : /**
242 : * Return an accessible for the given node even if the node is not in
243 : * document's node map cache (like HTML area element).
244 : *
245 : * XXX: it should be really merged with GetAccessible().
246 : */
247 : Accessible* GetAccessibleEvenIfNotInMap(nsINode* aNode) const;
248 : Accessible* GetAccessibleEvenIfNotInMapOrContainer(nsINode* aNode) const;
249 :
250 : /**
251 : * Return whether the given DOM node has an accessible or not.
252 : */
253 0 : bool HasAccessible(nsINode* aNode) const
254 0 : { return GetAccessible(aNode); }
255 :
256 : /**
257 : * Return the cached accessible by the given unique ID within this document.
258 : *
259 : * @note the unique ID matches with the uniqueID() of Accessible
260 : *
261 : * @param aUniqueID [in] the unique ID used to cache the node.
262 : */
263 0 : Accessible* GetAccessibleByUniqueID(void* aUniqueID)
264 : {
265 0 : return UniqueID() == aUniqueID ?
266 0 : this : mAccessibleCache.GetWeak(aUniqueID);
267 : }
268 :
269 : /**
270 : * Return the cached accessible by the given unique ID looking through
271 : * this and nested documents.
272 : */
273 : Accessible* GetAccessibleByUniqueIDInSubtree(void* aUniqueID);
274 :
275 : /**
276 : * Return an accessible for the given DOM node or container accessible if
277 : * the node is not accessible.
278 : */
279 : Accessible* GetAccessibleOrContainer(nsINode* aNode) const;
280 :
281 : /**
282 : * Return a container accessible for the given DOM node.
283 : */
284 0 : Accessible* GetContainerAccessible(nsINode* aNode) const
285 : {
286 0 : return aNode ? GetAccessibleOrContainer(aNode->GetParentNode()) : nullptr;
287 : }
288 :
289 : /**
290 : * Return an accessible for the given node if any, or an immediate accessible
291 : * container for it.
292 : */
293 : Accessible* AccessibleOrTrueContainer(nsINode* aNode) const;
294 :
295 : /**
296 : * Return an accessible for the given node or its first accessible descendant.
297 : */
298 : Accessible* GetAccessibleOrDescendant(nsINode* aNode) const;
299 :
300 : /**
301 : * Returns aria-owns seized child at the given index.
302 : */
303 0 : Accessible* ARIAOwnedAt(Accessible* aParent, uint32_t aIndex) const
304 : {
305 0 : nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.Get(aParent);
306 0 : if (children) {
307 0 : return children->SafeElementAt(aIndex);
308 : }
309 0 : return nullptr;
310 : }
311 0 : uint32_t ARIAOwnedCount(Accessible* aParent) const
312 : {
313 0 : nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.Get(aParent);
314 0 : return children ? children->Length() : 0;
315 : }
316 :
317 : /**
318 : * Return true if the given ID is referred by relation attribute.
319 : *
320 : * @note Different elements may share the same ID if they are hosted inside
321 : * XBL bindings. Be careful the result of this method may be senseless
322 : * while it's called for XUL elements (where XBL is used widely).
323 : */
324 0 : bool IsDependentID(const nsAString& aID) const
325 0 : { return mDependentIDsHash.Get(aID, nullptr); }
326 :
327 : /**
328 : * Initialize the newly created accessible and put it into document caches.
329 : *
330 : * @param aAccessible [in] created accessible
331 : * @param aRoleMapEntry [in] the role map entry role the ARIA role or nullptr
332 : * if none
333 : */
334 : void BindToDocument(Accessible* aAccessible,
335 : const nsRoleMapEntry* aRoleMapEntry);
336 :
337 : /**
338 : * Remove from document and shutdown the given accessible.
339 : */
340 : void UnbindFromDocument(Accessible* aAccessible);
341 :
342 : /**
343 : * Notify the document accessible that content was inserted.
344 : */
345 : void ContentInserted(nsIContent* aContainerNode,
346 : nsIContent* aStartChildNode,
347 : nsIContent* aEndChildNode);
348 :
349 : /**
350 : * Update the tree on content removal.
351 : */
352 : void ContentRemoved(Accessible* aAccessible);
353 : void ContentRemoved(nsIContent* aContentNode);
354 :
355 : /**
356 : * Updates accessible tree when rendered text is changed.
357 : */
358 : void UpdateText(nsIContent* aTextNode);
359 :
360 : /**
361 : * Recreate an accessible, results in hide/show events pair.
362 : */
363 : void RecreateAccessible(nsIContent* aContent);
364 :
365 : /**
366 : * Schedule ARIA owned element relocation if needed. Return true if relocation
367 : * was scheduled.
368 : */
369 : bool RelocateARIAOwnedIfNeeded(nsIContent* aEl);
370 :
371 : /**
372 : * Return a notification controller associated with the document.
373 : */
374 0 : NotificationController* Controller() const { return mNotificationController; }
375 :
376 : /**
377 : * If this document is in a content process return the object responsible for
378 : * communicating with the main process for it.
379 : */
380 0 : DocAccessibleChild* IPCDoc() const { return mIPCDoc; }
381 :
382 : protected:
383 : virtual ~DocAccessible();
384 :
385 : void LastRelease();
386 :
387 : // DocAccessible
388 : virtual nsresult AddEventListeners();
389 : virtual nsresult RemoveEventListeners();
390 :
391 : /**
392 : * Marks this document as loaded or loading.
393 : */
394 : void NotifyOfLoad(uint32_t aLoadEventType);
395 : void NotifyOfLoading(bool aIsReloading);
396 :
397 : friend class DocManager;
398 :
399 : /**
400 : * Perform initial update (create accessible tree).
401 : * Can be overridden by wrappers to prepare initialization work.
402 : */
403 : virtual void DoInitialUpdate();
404 :
405 : /**
406 : * Updates root element and picks up ARIA role on it if any.
407 : */
408 : void UpdateRootElIfNeeded();
409 :
410 : /**
411 : * Process document load notification, fire document load and state busy
412 : * events if applicable.
413 : */
414 : void ProcessLoad();
415 :
416 : /**
417 : * Add/remove scroll listeners, @see nsIScrollPositionListener interface.
418 : */
419 : void AddScrollListener();
420 : void RemoveScrollListener();
421 :
422 : /**
423 : * Append the given document accessible to this document's child document
424 : * accessibles.
425 : */
426 0 : bool AppendChildDocument(DocAccessible* aChildDocument)
427 : {
428 0 : return mChildDocuments.AppendElement(aChildDocument);
429 : }
430 :
431 : /**
432 : * Remove the given document accessible from this document's child document
433 : * accessibles.
434 : */
435 0 : void RemoveChildDocument(DocAccessible* aChildDocument)
436 : {
437 0 : mChildDocuments.RemoveElement(aChildDocument);
438 0 : }
439 :
440 : /**
441 : * Add dependent IDs pointed by accessible element by relation attribute to
442 : * cache. If the relation attribute is missed then all relation attributes
443 : * are checked.
444 : *
445 : * @param aRelProvider [in] accessible that element has relation attribute
446 : * @param aRelAttr [in, optional] relation attribute
447 : */
448 : void AddDependentIDsFor(Accessible* aRelProvider,
449 : nsIAtom* aRelAttr = nullptr);
450 :
451 : /**
452 : * Remove dependent IDs pointed by accessible element by relation attribute
453 : * from cache. If the relation attribute is absent then all relation
454 : * attributes are checked.
455 : *
456 : * @param aRelProvider [in] accessible that element has relation attribute
457 : * @param aRelAttr [in, optional] relation attribute
458 : */
459 : void RemoveDependentIDsFor(Accessible* aRelProvider,
460 : nsIAtom* aRelAttr = nullptr);
461 :
462 : /**
463 : * Update or recreate an accessible depending on a changed attribute.
464 : *
465 : * @param aElement [in] the element the attribute was changed on
466 : * @param aAttribute [in] the changed attribute
467 : * @return true if an action was taken on the attribute change
468 : */
469 : bool UpdateAccessibleOnAttrChange(mozilla::dom::Element* aElement,
470 : nsIAtom* aAttribute);
471 :
472 : /**
473 : * Fire accessible events when attribute is changed.
474 : *
475 : * @param aAccessible [in] accessible the DOM attribute is changed for
476 : * @param aNameSpaceID [in] namespace of changed attribute
477 : * @param aAttribute [in] changed attribute
478 : */
479 : void AttributeChangedImpl(Accessible* aAccessible,
480 : int32_t aNameSpaceID, nsIAtom* aAttribute);
481 :
482 : /**
483 : * Fire accessible events when ARIA attribute is changed.
484 : *
485 : * @param aAccessible [in] accesislbe the DOM attribute is changed for
486 : * @param aAttribute [in] changed attribute
487 : */
488 : void ARIAAttributeChanged(Accessible* aAccessible, nsIAtom* aAttribute);
489 :
490 : /**
491 : * Process ARIA active-descendant attribute change.
492 : */
493 : void ARIAActiveDescendantChanged(Accessible* aAccessible);
494 :
495 : /**
496 : * Update the accessible tree for inserted content.
497 : */
498 : void ProcessContentInserted(Accessible* aContainer,
499 : const nsTArray<nsCOMPtr<nsIContent> >* aInsertedContent);
500 : void ProcessContentInserted(Accessible* aContainer,
501 : nsIContent* aInsertedContent);
502 :
503 : /**
504 : * Used to notify the document to make it process the invalidation list.
505 : *
506 : * While children are cached we may encounter the case there's no accessible
507 : * for referred content by related accessible. Store these related nodes to
508 : * invalidate their containers later.
509 : */
510 : void ProcessInvalidationList();
511 :
512 : /**
513 : * Steals or puts back accessible subtrees.
514 : */
515 : void DoARIAOwnsRelocation(Accessible* aOwner);
516 :
517 : /**
518 : * Moves children back under their original parents.
519 : */
520 : void PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
521 : uint32_t aStartIdx);
522 :
523 : bool MoveChild(Accessible* aChild, Accessible* aNewParent,
524 : int32_t aIdxInParent);
525 :
526 : /**
527 : * Create accessible tree.
528 : *
529 : * @param aRoot [in] a root of subtree to create
530 : * @param aFocusedAcc [in, optional] a focused accessible under created
531 : * subtree if any
532 : */
533 : void CacheChildrenInSubtree(Accessible* aRoot,
534 : Accessible** aFocusedAcc = nullptr);
535 : void CreateSubtree(Accessible* aRoot);
536 :
537 : /**
538 : * Remove accessibles in subtree from node to accessible map.
539 : */
540 : void UncacheChildrenInSubtree(Accessible* aRoot);
541 :
542 : /**
543 : * Shutdown any cached accessible in the subtree.
544 : *
545 : * @param aAccessible [in] the root of the subrtee to invalidate accessible
546 : * child/parent refs in
547 : */
548 : void ShutdownChildrenInSubtree(Accessible* aAccessible);
549 :
550 : /**
551 : * Return true if the document is a target of document loading events
552 : * (for example, state busy change or document reload events).
553 : *
554 : * Rules: The root chrome document accessible is never an event target
555 : * (for example, Firefox UI window). If the sub document is loaded within its
556 : * parent document then the parent document is a target only (aka events
557 : * coalescence).
558 : */
559 : bool IsLoadEventTarget() const;
560 :
561 : /*
562 : * Set the object responsible for communicating with the main process on
563 : * behalf of this document.
564 : */
565 0 : void SetIPCDoc(DocAccessibleChild* aIPCDoc) { mIPCDoc = aIPCDoc; }
566 :
567 : friend class DocAccessibleChildBase;
568 :
569 : /**
570 : * Used to fire scrolling end event after page scroll.
571 : *
572 : * @param aTimer [in] the timer object
573 : * @param aClosure [in] the document accessible where scrolling happens
574 : */
575 : static void ScrollTimerCallback(nsITimer* aTimer, void* aClosure);
576 :
577 : protected:
578 :
579 : /**
580 : * State and property flags, kept by mDocFlags.
581 : */
582 : enum {
583 : // Whether scroll listeners were added.
584 : eScrollInitialized = 1 << 0,
585 :
586 : // Whether the document is a tab document.
587 : eTabDocument = 1 << 1
588 : };
589 :
590 : /**
591 : * Cache of accessibles within this document accessible.
592 : */
593 : AccessibleHashtable mAccessibleCache;
594 : nsDataHashtable<nsPtrHashKey<const nsINode>, Accessible*>
595 : mNodeToAccessibleMap;
596 :
597 : nsIDocument* mDocumentNode;
598 : nsCOMPtr<nsITimer> mScrollWatchTimer;
599 : uint16_t mScrollPositionChangedTicks; // Used for tracking scroll events
600 :
601 : /**
602 : * Bit mask of document load states (@see LoadState).
603 : */
604 : uint32_t mLoadState : 3;
605 :
606 : /**
607 : * Bit mask of other states and props.
608 : */
609 : uint32_t mDocFlags : 28;
610 :
611 : /**
612 : * Type of document load event fired after the document is loaded completely.
613 : */
614 : uint32_t mLoadEventType;
615 :
616 : /**
617 : * Reference to anchor jump element.
618 : */
619 : nsCOMPtr<nsIContent> mAnchorJumpElm;
620 :
621 : /**
622 : * A generic state (see items below) before the attribute value was changed.
623 : * @see AttributeWillChange and AttributeChanged notifications.
624 : */
625 : union {
626 : // ARIA attribute value
627 : nsIAtom* mARIAAttrOldValue;
628 :
629 : // True if the accessible state bit was on
630 : bool mStateBitWasOn;
631 : };
632 :
633 : nsTArray<RefPtr<DocAccessible> > mChildDocuments;
634 :
635 : /**
636 : * The virtual cursor of the document.
637 : */
638 : RefPtr<nsAccessiblePivot> mVirtualCursor;
639 :
640 : /**
641 : * A storage class for pairing content with one of its relation attributes.
642 : */
643 0 : class AttrRelProvider
644 : {
645 : public:
646 0 : AttrRelProvider(nsIAtom* aRelAttr, nsIContent* aContent) :
647 0 : mRelAttr(aRelAttr), mContent(aContent) { }
648 :
649 : nsIAtom* mRelAttr;
650 : nsCOMPtr<nsIContent> mContent;
651 :
652 : private:
653 : AttrRelProvider();
654 : AttrRelProvider(const AttrRelProvider&);
655 : AttrRelProvider& operator =(const AttrRelProvider&);
656 : };
657 :
658 : /**
659 : * The cache of IDs pointed by relation attributes.
660 : */
661 : typedef nsTArray<nsAutoPtr<AttrRelProvider> > AttrRelProviderArray;
662 : nsClassHashtable<nsStringHashKey, AttrRelProviderArray>
663 : mDependentIDsHash;
664 :
665 : friend class RelatedAccIterator;
666 :
667 : /**
668 : * Used for our caching algorithm. We store the list of nodes that should be
669 : * invalidated.
670 : *
671 : * @see ProcessInvalidationList
672 : */
673 : nsTArray<RefPtr<nsIContent>> mInvalidationList;
674 :
675 : /**
676 : * Holds a list of aria-owns relocations.
677 : */
678 : nsClassHashtable<nsPtrHashKey<Accessible>, nsTArray<RefPtr<Accessible> > >
679 : mARIAOwnsHash;
680 :
681 : /**
682 : * Used to process notification from core and accessible events.
683 : */
684 : RefPtr<NotificationController> mNotificationController;
685 : friend class EventTree;
686 : friend class NotificationController;
687 :
688 : private:
689 :
690 : nsIPresShell* mPresShell;
691 :
692 : // Exclusively owned by IPDL so don't manually delete it!
693 : DocAccessibleChild* mIPCDoc;
694 : };
695 :
696 : inline DocAccessible*
697 0 : Accessible::AsDoc()
698 : {
699 0 : return IsDoc() ? static_cast<DocAccessible*>(this) : nullptr;
700 : }
701 :
702 : } // namespace a11y
703 : } // namespace mozilla
704 :
705 : #endif
|