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 #include "nsAccessibilityService.h"
8 // NOTE: alphabetically ordered
9 #include "ApplicationAccessibleWrap.h"
10 #include "ARIAGridAccessible.h"
12 #include "DocAccessible-inl.h"
13 #include "DocAccessibleChild.h"
14 #include "FocusManager.h"
15 #include "HTMLCanvasAccessible.h"
16 #include "HTMLElementAccessibles.h"
17 #include "HTMLImageMapAccessible.h"
18 #include "HTMLLinkAccessible.h"
19 #include "HTMLListAccessible.h"
20 #include "HTMLSelectAccessible.h"
21 #include "HTMLTableAccessible.h"
22 #include "HyperTextAccessible.h"
23 #include "RootAccessible.h"
24 #include "nsAccUtils.h"
25 #include "nsArrayUtils.h"
26 #include "nsAttrName.h"
27 #include "nsDOMTokenList.h"
29 #include "nsEventShell.h"
30 #include "nsGkAtoms.h"
31 #include "nsIFrameInlines.h"
32 #include "nsServiceManagerUtils.h"
33 #include "nsTextFormatter.h"
34 #include "OuterDocAccessible.h"
35 #include "mozilla/a11y/Role.h"
36 #ifdef MOZ_ACCESSIBILITY_ATK
37 # include "RootAccessibleWrap.h"
40 #include "Statistics.h"
41 #include "TextLeafAccessible.h"
42 #include "xpcAccessibleApplication.h"
45 # include "mozilla/a11y/Compatibility.h"
46 # include "mozilla/StaticPtr.h"
53 #include "nsExceptionHandler.h"
54 #include "nsImageFrame.h"
55 #include "nsIObserverService.h"
56 #include "nsMenuPopupFrame.h"
57 #include "nsLayoutUtils.h"
58 #include "nsTreeBodyFrame.h"
59 #include "nsTreeUtils.h"
60 #include "mozilla/a11y/AccTypes.h"
61 #include "mozilla/ArrayUtils.h"
62 #include "mozilla/dom/DOMStringList.h"
63 #include "mozilla/dom/EventTarget.h"
64 #include "mozilla/dom/HTMLTableElement.h"
65 #include "mozilla/Preferences.h"
66 #include "mozilla/PresShell.h"
67 #include "mozilla/ProfilerMarkers.h"
68 #include "mozilla/RefPtr.h"
69 #include "mozilla/Services.h"
71 #include "XULAlertAccessible.h"
72 #include "XULComboboxAccessible.h"
73 #include "XULElementAccessibles.h"
74 #include "XULFormControlAccessible.h"
75 #include "XULListboxAccessible.h"
76 #include "XULMenuAccessible.h"
77 #include "XULTabAccessible.h"
78 #include "XULTreeGridAccessible.h"
80 using namespace mozilla
;
81 using namespace mozilla::a11y
;
82 using namespace mozilla::dom
;
85 * Accessibility service force enable/disable preference.
87 * Accessibility is force enabled (accessibility should always be enabled): -1
88 * Accessibility is enabled (will be started upon a request, default value): 0
89 * Accessibility is force disabled (never enable accessibility): 1
91 #define PREF_ACCESSIBILITY_FORCE_DISABLED "accessibility.force_disabled"
93 ////////////////////////////////////////////////////////////////////////////////
95 ////////////////////////////////////////////////////////////////////////////////
98 * If the element has an ARIA attribute that requires a specific Accessible
99 * class, create and return it. Otherwise, return null.
101 static LocalAccessible
* MaybeCreateSpecificARIAAccessible(
102 const nsRoleMapEntry
* aRoleMapEntry
, const LocalAccessible
* aContext
,
103 nsIContent
* aContent
, DocAccessible
* aDocument
) {
104 if (aRoleMapEntry
&& aRoleMapEntry
->accTypes
& eTableCell
) {
105 if (aContent
->IsAnyOfHTMLElements(nsGkAtoms::td
, nsGkAtoms::th
) &&
106 aContext
->IsHTMLTableRow()) {
107 // Don't use ARIAGridCellAccessible for a valid td/th because
108 // HTMLTableCellAccessible can provide additional info; e.g. row/col span
109 // from the layout engine.
112 // A cell must be in a row.
113 const Accessible
* parent
= aContext
;
114 if (parent
->IsGeneric()) {
115 parent
= parent
->GetNonGenericParent();
117 if (!parent
|| parent
->Role() != roles::ROW
) {
120 // That row must be in a table, though there may be an intervening rowgroup.
121 parent
= parent
->GetNonGenericParent();
125 if (!parent
->IsTable() && parent
->Role() == roles::GROUPING
) {
126 parent
= parent
->GetNonGenericParent();
131 if (parent
->IsTable()) {
132 return new ARIAGridCellAccessible(aContent
, aDocument
);
139 * Return true if the element has an attribute (ARIA, title, or relation) that
140 * requires the creation of an Accessible for the element.
142 static bool AttributesMustBeAccessible(nsIContent
* aContent
,
143 DocAccessible
* aDocument
) {
144 if (aContent
->IsElement()) {
145 uint32_t attrCount
= aContent
->AsElement()->GetAttrCount();
146 for (uint32_t attrIdx
= 0; attrIdx
< attrCount
; attrIdx
++) {
147 const nsAttrName
* attr
= aContent
->AsElement()->GetAttrNameAt(attrIdx
);
148 if (attr
->NamespaceEquals(kNameSpaceID_None
)) {
149 nsAtom
* attrAtom
= attr
->Atom();
150 if (attrAtom
== nsGkAtoms::title
&& aContent
->IsHTMLElement()) {
151 // If the author provided a title on an element that would not
152 // be accessible normally, assume an intent and make it accessible.
156 nsDependentAtomString
attrStr(attrAtom
);
157 if (!StringBeginsWith(attrStr
, u
"aria-"_ns
)) continue; // not ARIA
159 // A global state or a property and in case of token defined.
160 uint8_t attrFlags
= aria::AttrCharacteristicsFor(attrAtom
);
161 if ((attrFlags
& ATTR_GLOBAL
) &&
162 (!(attrFlags
& ATTR_VALTOKEN
) ||
163 nsAccUtils::HasDefinedARIAToken(aContent
, attrAtom
))) {
169 // If the given ID is referred by relation attribute then create an
170 // Accessible for it.
172 if (nsCoreUtils::GetID(aContent
, id
) && !id
.IsEmpty()) {
173 return aDocument
->IsDependentID(aContent
->AsElement(), id
);
181 * Return true if the element must be a generic Accessible, even if it has been
182 * marked presentational with role="presentation", etc. MustBeAccessible causes
183 * an Accessible to be created as if it weren't marked presentational at all;
184 * e.g. <table role="presentation" tabindex="0"> will expose roles::TABLE and
185 * support TableAccessible. In contrast, this function causes a generic
186 * Accessible to be created; e.g. <table role="presentation" style="position:
187 * fixed;"> will expose roles::TEXT_CONTAINER and will not support
188 * TableAccessible. This is necessary in certain cases for the
189 * RemoteAccessible cache.
191 static bool MustBeGenericAccessible(nsIContent
* aContent
,
192 DocAccessible
* aDocument
) {
193 if (aContent
->IsInNativeAnonymousSubtree() || aContent
->IsSVGElement()) {
194 // We should not force create accs for anonymous content.
195 // This is an issue for inputs, which have an intermediate
196 // container with relevant overflow styling between the input
197 // and its internal input content.
198 // We should also avoid this for SVG elements (ie. `<foreignobject>`s
199 // which have default overflow:hidden styling).
202 nsIFrame
* frame
= aContent
->GetPrimaryFrame();
204 nsAutoCString overflow
;
205 frame
->Style()->GetComputedPropertyValue(eCSSProperty_overflow
, overflow
);
206 // If the frame has been transformed, and the content has any children, we
207 // should create an Accessible so that we can account for the transform when
208 // calculating the Accessible's bounds using the parent process cache.
209 // Ditto for content which is position: fixed or sticky or has overflow
210 // styling (auto, scroll, hidden).
211 // However, don't do this for XUL widgets, as this breaks XUL a11y code
212 // expectations in some cases. XUL widgets are only used in the parent
213 // process and can't be cached anyway.
214 return !aContent
->IsXULElement() &&
215 ((aContent
->HasChildren() && frame
->IsTransformed()) ||
216 frame
->IsStickyPositioned() ||
217 (frame
->StyleDisplay()->mPosition
== StylePositionProperty::Fixed
&&
218 nsLayoutUtils::IsReallyFixedPos(frame
)) ||
219 overflow
.Equals("auto"_ns
) || overflow
.Equals("scroll"_ns
) ||
220 overflow
.Equals("hidden"_ns
));
224 * Return true if the element must be accessible.
226 static bool MustBeAccessible(nsIContent
* aContent
, DocAccessible
* aDocument
) {
227 nsIFrame
* frame
= aContent
->GetPrimaryFrame();
229 // This document might be invisible when it first loads. Therefore, we must
230 // check focusability irrespective of visibility here. Otherwise, we might not
231 // create Accessibles for some focusable elements; e.g. a span with only a
232 // tabindex. Elements that are invisible within this document are excluded
233 // earlier in CreateAccessible.
234 if (frame
->IsFocusable(/* aWithMouse */ false,
235 /* aCheckVisibility */ false)) {
239 return AttributesMustBeAccessible(aContent
, aDocument
);
242 bool nsAccessibilityService::ShouldCreateImgAccessible(
243 mozilla::dom::Element
* aElement
, DocAccessible
* aDocument
) {
244 // The element must have a layout frame for us to proceed. If there is no
245 // frame, the image is likely hidden.
246 nsIFrame
* frame
= aElement
->GetPrimaryFrame();
251 // If the element is not an img, and also not an embedded image via embed or
252 // object, then we should not create an accessible.
253 if (!aElement
->IsHTMLElement(nsGkAtoms::img
) &&
254 ((!aElement
->IsHTMLElement(nsGkAtoms::embed
) &&
255 !aElement
->IsHTMLElement(nsGkAtoms::object
)) ||
256 frame
->AccessibleType() != AccType::eImageType
)) {
260 nsAutoString newAltText
;
261 const bool hasAlt
= aElement
->GetAttr(nsGkAtoms::alt
, newAltText
);
262 if (!hasAlt
|| !newAltText
.IsEmpty()) {
263 // If there is no alt attribute, we should create an accessible. The
264 // author may have missed the attribute, and the AT may want to provide a
265 // name. If there is alt text, we should create an accessible.
269 if (newAltText
.IsEmpty() && (nsCoreUtils::HasClickListener(aElement
) ||
270 MustBeAccessible(aElement
, aDocument
))) {
271 // If there is empty alt text, but there is a click listener for this img,
272 // or if it otherwise must be an accessible (e.g., if it has an aria-label
273 // attribute), we should create an accessible.
277 // Otherwise, no alt text means we should not create an accessible.
282 * Return true if the SVG element should be accessible
284 static bool MustSVGElementBeAccessible(nsIContent
* aContent
,
285 DocAccessible
* aDocument
) {
286 // https://w3c.github.io/svg-aam/#include_elements
287 for (nsIContent
* childElm
= aContent
->GetFirstChild(); childElm
;
288 childElm
= childElm
->GetNextSibling()) {
289 if (childElm
->IsAnyOfSVGElements(nsGkAtoms::title
, nsGkAtoms::desc
)) {
293 return MustBeAccessible(aContent
, aDocument
);
297 * Used by XULMap.h to map both menupopup and popup elements
299 LocalAccessible
* CreateMenupopupAccessible(Element
* aElement
,
300 LocalAccessible
* aContext
) {
301 #ifdef MOZ_ACCESSIBILITY_ATK
302 // ATK considers this node to be redundant when within menubars, and it makes
303 // menu navigation with assistive technologies more difficult
304 // XXX In the future we will should this for consistency across the
305 // nsIAccessible implementations on each platform for a consistent scripting
306 // environment, but then strip out redundant accessibles in the AccessibleWrap
307 // class for each platform.
308 nsIContent
* parent
= aElement
->GetParent();
309 if (parent
&& parent
->IsXULElement(nsGkAtoms::menu
)) return nullptr;
312 return new XULMenupopupAccessible(aElement
, aContext
->Document());
315 ////////////////////////////////////////////////////////////////////////////////
316 // LocalAccessible constructors
318 static LocalAccessible
* New_HyperText(Element
* aElement
,
319 LocalAccessible
* aContext
) {
320 return new HyperTextAccessible(aElement
, aContext
->Document());
323 template <typename AccClass
>
324 static LocalAccessible
* New_HTMLDtOrDd(Element
* aElement
,
325 LocalAccessible
* aContext
) {
326 nsIContent
* parent
= aContext
->GetContent();
327 if (parent
->IsHTMLElement(nsGkAtoms::div
)) {
328 // It is conforming in HTML to use a div to group dt/dd elements.
329 parent
= parent
->GetParent();
332 if (parent
&& parent
->IsHTMLElement(nsGkAtoms::dl
)) {
333 return new AccClass(aElement
, aContext
->Document());
340 * Cached value of the PREF_ACCESSIBILITY_FORCE_DISABLED preference.
342 static int32_t sPlatformDisabledState
= 0;
344 ////////////////////////////////////////////////////////////////////////////////
345 // Markup maps array.
347 #define Attr(name, value) \
348 { nsGkAtoms::name, nsGkAtoms::value }
350 #define AttrFromDOM(name, DOMAttrName) \
351 { nsGkAtoms::name, nullptr, nsGkAtoms::DOMAttrName }
353 #define AttrFromDOMIf(name, DOMAttrName, DOMAttrValue) \
354 { nsGkAtoms::name, nullptr, nsGkAtoms::DOMAttrName, nsGkAtoms::DOMAttrValue }
356 #define MARKUPMAP(atom, new_func, r, ...) \
357 {nsGkAtoms::atom, new_func, static_cast<a11y::role>(r), {__VA_ARGS__}},
359 static const MarkupMapInfo sHTMLMarkupMapList
[] = {
360 #include "HTMLMarkupMap.h"
363 static const MarkupMapInfo sMathMLMarkupMapList
[] = {
364 #include "MathMLMarkupMap.h"
369 #define XULMAP(atom, ...) {nsGkAtoms::atom, __VA_ARGS__},
371 #define XULMAP_TYPE(atom, new_type) \
374 [](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* { \
375 return new new_type(aElement, aContext->Document()); \
378 static const XULMarkupMapInfo sXULMarkupMapList
[] = {
389 ////////////////////////////////////////////////////////////////////////////////
390 // nsAccessibilityService
391 ////////////////////////////////////////////////////////////////////////////////
393 nsAccessibilityService
* nsAccessibilityService::gAccessibilityService
= nullptr;
394 ApplicationAccessible
* nsAccessibilityService::gApplicationAccessible
= nullptr;
395 xpcAccessibleApplication
* nsAccessibilityService::gXPCApplicationAccessible
=
397 uint32_t nsAccessibilityService::gConsumers
= 0;
399 nsAccessibilityService::nsAccessibilityService()
400 : mHTMLMarkupMap(ArrayLength(sHTMLMarkupMapList
)),
401 mMathMLMarkupMap(ArrayLength(sMathMLMarkupMapList
)),
402 mXULMarkupMap(ArrayLength(sXULMarkupMapList
)) {}
404 nsAccessibilityService::~nsAccessibilityService() {
405 NS_ASSERTION(IsShutdown(), "Accessibility wasn't shutdown!");
406 gAccessibilityService
= nullptr;
409 ////////////////////////////////////////////////////////////////////////////////
410 // nsIListenerChangeListener
413 nsAccessibilityService::ListenersChanged(nsIArray
* aEventChanges
) {
414 uint32_t targetCount
;
415 nsresult rv
= aEventChanges
->GetLength(&targetCount
);
416 NS_ENSURE_SUCCESS(rv
, rv
);
418 for (uint32_t i
= 0; i
< targetCount
; i
++) {
419 nsCOMPtr
<nsIEventListenerChange
> change
=
420 do_QueryElementAt(aEventChanges
, i
);
422 RefPtr
<EventTarget
> target
;
423 change
->GetTarget(getter_AddRefs(target
));
424 nsIContent
* content(nsIContent::FromEventTargetOrNull(target
));
425 if (!content
|| !content
->IsHTMLElement()) {
429 uint32_t changeCount
;
430 change
->GetCountOfEventListenerChangesAffectingAccessibility(&changeCount
);
431 NS_ENSURE_SUCCESS(rv
, rv
);
434 Document
* ownerDoc
= content
->OwnerDoc();
435 DocAccessible
* document
= GetExistingDocAccessible(ownerDoc
);
438 LocalAccessible
* acc
= document
->GetAccessible(content
);
439 if (!acc
&& (content
== document
->GetContent() ||
440 content
== document
->DocumentNode()->GetRootElement())) {
443 if (!acc
&& content
->IsElement() &&
444 content
->AsElement()->IsHTMLElement(nsGkAtoms::area
)) {
445 // For area accessibles, we have to recreate the entire image map,
446 // since the image map accessible manages the tree itself. The click
447 // listener change may require us to update the role for the
448 // accessible associated with the area element.
449 LocalAccessible
* areaAcc
=
450 document
->GetAccessibleEvenIfNotInMap(content
);
451 if (areaAcc
&& areaAcc
->LocalParent()) {
452 document
->RecreateAccessible(areaAcc
->LocalParent()->GetContent());
455 if (!acc
&& nsCoreUtils::HasClickListener(content
)) {
456 // Create an accessible for a inaccessible element having click event
458 document
->ContentInserted(content
, content
->GetNextSibling());
460 if ((acc
->IsHTMLLink() && !acc
->AsHTMLLink()->IsLinked()) ||
461 (content
->IsElement() &&
462 content
->AsElement()->IsHTMLElement(nsGkAtoms::a
) &&
463 !acc
->IsHTMLLink())) {
464 // An HTML link without an href attribute should have a generic
465 // role, unless it has a click listener. Since we might have gained
466 // or lost a click listener here, recreate the accessible so that we
467 // can create the correct type of accessible. If it was a link, it
468 // may no longer be one. If it wasn't, it may become one.
469 document
->RecreateAccessible(content
);
472 // A click listener change might mean losing or gaining an action.
473 document
->QueueCacheUpdate(acc
, CacheDomain::Actions
);
481 ////////////////////////////////////////////////////////////////////////////////
484 NS_IMPL_ISUPPORTS_INHERITED(nsAccessibilityService
, DocManager
, nsIObserver
,
485 nsIListenerChangeListener
,
486 nsISelectionListener
) // from SelectionManager
488 ////////////////////////////////////////////////////////////////////////////////
492 nsAccessibilityService::Observe(nsISupports
* aSubject
, const char* aTopic
,
493 const char16_t
* aData
) {
494 if (!nsCRT::strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
)) {
501 void nsAccessibilityService::NotifyOfAnchorJumpTo(nsIContent
* aTargetNode
) {
502 Document
* documentNode
= aTargetNode
->GetUncomposedDoc();
506 DocAccessible
* document
= GetDocAccessible(documentNode
);
510 // If the document has focus when we get this notification, ensure that
511 // we fire a start scrolling event.
512 const Accessible
* focusedAcc
= FocusedAccessible();
514 (focusedAcc
== document
|| focusedAcc
->IsNonInteractive())) {
515 LocalAccessible
* targetAcc
= document
->GetAccessible(aTargetNode
);
517 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SCROLLING_START
,
519 document
->SetAnchorJump(nullptr);
521 // We can't find the target accessible in the document yet. Set the
522 // anchor jump so that we can fire the scrolling start event later.
523 document
->SetAnchorJump(aTargetNode
);
526 document
->SetAnchorJump(aTargetNode
);
530 void nsAccessibilityService::FireAccessibleEvent(uint32_t aEvent
,
531 LocalAccessible
* aTarget
) {
532 nsEventShell::FireEvent(aEvent
, aTarget
);
535 void nsAccessibilityService::NotifyOfPossibleBoundsChange(
536 mozilla::PresShell
* aPresShell
, nsIContent
* aContent
) {
537 if (IPCAccessibilityActive()) {
538 DocAccessible
* document
= aPresShell
->GetDocAccessible();
540 // DocAccessible::GetAccessible() won't return the document if a root
541 // element like body is passed.
542 LocalAccessible
* accessible
= aContent
== document
->GetContent()
544 : document
->GetAccessible(aContent
);
546 document
->QueueCacheUpdate(accessible
, CacheDomain::Bounds
);
552 void nsAccessibilityService::NotifyOfComputedStyleChange(
553 mozilla::PresShell
* aPresShell
, nsIContent
* aContent
) {
554 DocAccessible
* document
= aPresShell
->GetDocAccessible();
559 // DocAccessible::GetAccessible() won't return the document if a root
560 // element like body is passed.
561 LocalAccessible
* accessible
= aContent
== document
->GetContent()
563 : document
->GetAccessible(aContent
);
564 if (!accessible
&& aContent
&& aContent
->HasChildren() &&
565 !aContent
->IsInNativeAnonymousSubtree()) {
566 // If the content has children and its frame has a transform, create an
567 // Accessible so that we can account for the transform when calculating
568 // the Accessible's bounds using the parent process cache. Ditto for
569 // position: fixed/sticky and content with overflow styling (hidden, auto,
571 if (const nsIFrame
* frame
= aContent
->GetPrimaryFrame()) {
572 const auto& disp
= *frame
->StyleDisplay();
573 if (disp
.HasTransform(frame
) ||
574 disp
.mPosition
== StylePositionProperty::Fixed
||
575 disp
.mPosition
== StylePositionProperty::Sticky
||
576 disp
.IsScrollableOverflow()) {
577 document
->ContentInserted(aContent
, aContent
->GetNextSibling());
580 } else if (accessible
&& IPCAccessibilityActive()) {
581 accessible
->MaybeQueueCacheUpdateForStyleChanges();
585 void nsAccessibilityService::NotifyOfResolutionChange(
586 mozilla::PresShell
* aPresShell
, float aResolution
) {
587 DocAccessible
* document
= aPresShell
->GetDocAccessible();
588 if (document
&& document
->IPCDoc()) {
589 AutoTArray
<mozilla::a11y::CacheData
, 1> data
;
590 RefPtr
<AccAttributes
> fields
= new AccAttributes();
591 fields
->SetAttribute(CacheKey::Resolution
, aResolution
);
592 data
.AppendElement(mozilla::a11y::CacheData(0, fields
));
593 document
->IPCDoc()->SendCache(CacheUpdateType::Update
, data
);
597 void nsAccessibilityService::NotifyOfDevPixelRatioChange(
598 mozilla::PresShell
* aPresShell
, int32_t aAppUnitsPerDevPixel
) {
599 DocAccessible
* document
= aPresShell
->GetDocAccessible();
600 if (document
&& document
->IPCDoc()) {
601 AutoTArray
<mozilla::a11y::CacheData
, 1> data
;
602 RefPtr
<AccAttributes
> fields
= new AccAttributes();
603 fields
->SetAttribute(CacheKey::AppUnitsPerDevPixel
, aAppUnitsPerDevPixel
);
604 data
.AppendElement(mozilla::a11y::CacheData(0, fields
));
605 document
->IPCDoc()->SendCache(CacheUpdateType::Update
, data
);
609 void nsAccessibilityService::NotifyAttrElementWillChange(
610 mozilla::dom::Element
* aElement
, nsAtom
* aAttr
) {
611 mozilla::dom::Document
* doc
= aElement
->OwnerDoc();
613 if (DocAccessible
* docAcc
= GetDocAccessible(doc
)) {
614 docAcc
->AttrElementWillChange(aElement
, aAttr
);
618 void nsAccessibilityService::NotifyAttrElementChanged(
619 mozilla::dom::Element
* aElement
, nsAtom
* aAttr
) {
620 mozilla::dom::Document
* doc
= aElement
->OwnerDoc();
622 if (DocAccessible
* docAcc
= GetDocAccessible(doc
)) {
623 docAcc
->AttrElementChanged(aElement
, aAttr
);
627 LocalAccessible
* nsAccessibilityService::GetRootDocumentAccessible(
628 PresShell
* aPresShell
, bool aCanCreate
) {
629 PresShell
* presShell
= aPresShell
;
630 Document
* documentNode
= aPresShell
->GetDocument();
632 nsCOMPtr
<nsIDocShellTreeItem
> treeItem(documentNode
->GetDocShell());
634 nsCOMPtr
<nsIDocShellTreeItem
> rootTreeItem
;
635 treeItem
->GetInProcessRootTreeItem(getter_AddRefs(rootTreeItem
));
636 if (treeItem
!= rootTreeItem
) {
637 nsCOMPtr
<nsIDocShell
> docShell(do_QueryInterface(rootTreeItem
));
638 presShell
= docShell
->GetPresShell();
641 return aCanCreate
? GetDocAccessible(presShell
)
642 : presShell
->GetDocAccessible();
648 void nsAccessibilityService::NotifyOfTabPanelVisibilityChange(
649 PresShell
* aPresShell
, Element
* aPanel
, bool aNowVisible
) {
650 MOZ_ASSERT(aPanel
->GetParent()->IsXULElement(nsGkAtoms::tabpanels
));
652 DocAccessible
* document
= GetDocAccessible(aPresShell
);
657 if (LocalAccessible
* acc
= document
->GetAccessible(aPanel
)) {
658 RefPtr
<AccEvent
> event
=
659 new AccStateChangeEvent(acc
, states::OFFSCREEN
, aNowVisible
);
660 document
->FireDelayedEvent(event
);
664 void nsAccessibilityService::ContentRangeInserted(PresShell
* aPresShell
,
665 nsIContent
* aStartChild
,
666 nsIContent
* aEndChild
) {
667 DocAccessible
* document
= GetDocAccessible(aPresShell
);
669 if (logging::IsEnabled(logging::eTree
)) {
670 logging::MsgBegin("TREE", "content inserted; doc: %p", document
);
671 logging::Node("container", aStartChild
->GetParentNode());
672 for (nsIContent
* child
= aStartChild
; child
!= aEndChild
;
673 child
= child
->GetNextSibling()) {
674 logging::Node("content", child
);
682 document
->ContentInserted(aStartChild
, aEndChild
);
686 void nsAccessibilityService::ScheduleAccessibilitySubtreeUpdate(
687 PresShell
* aPresShell
, nsIContent
* aContent
) {
688 DocAccessible
* document
= GetDocAccessible(aPresShell
);
690 if (logging::IsEnabled(logging::eTree
)) {
691 logging::MsgBegin("TREE", "schedule update; doc: %p", document
);
692 logging::Node("content node", aContent
);
698 document
->ScheduleTreeUpdate(aContent
);
702 void nsAccessibilityService::ContentRemoved(PresShell
* aPresShell
,
703 nsIContent
* aChildNode
) {
704 DocAccessible
* document
= GetDocAccessible(aPresShell
);
706 if (logging::IsEnabled(logging::eTree
)) {
707 logging::MsgBegin("TREE", "content removed; doc: %p", document
);
708 logging::Node("container node", aChildNode
->GetFlattenedTreeParent());
709 logging::Node("content node", aChildNode
);
715 document
->ContentRemoved(aChildNode
);
719 if (logging::IsEnabled(logging::eTree
)) {
726 void nsAccessibilityService::TableLayoutGuessMaybeChanged(
727 PresShell
* aPresShell
, nsIContent
* aContent
) {
728 if (DocAccessible
* document
= GetDocAccessible(aPresShell
)) {
729 if (LocalAccessible
* acc
= document
->GetAccessible(aContent
)) {
730 if (LocalAccessible
* table
= nsAccUtils::TableFor(acc
)) {
731 document
->QueueCacheUpdate(table
, CacheDomain::Table
);
737 void nsAccessibilityService::ComboboxOptionMaybeChanged(
738 PresShell
* aPresShell
, nsIContent
* aMutatingNode
) {
739 DocAccessible
* document
= GetDocAccessible(aPresShell
);
744 for (nsIContent
* cur
= aMutatingNode
; cur
; cur
= cur
->GetParent()) {
745 if (cur
->IsHTMLElement(nsGkAtoms::option
)) {
746 if (LocalAccessible
* accessible
= document
->GetAccessible(cur
)) {
747 document
->FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE
,
751 if (cur
->IsHTMLElement(nsGkAtoms::select
)) {
758 void nsAccessibilityService::UpdateText(PresShell
* aPresShell
,
759 nsIContent
* aContent
) {
760 DocAccessible
* document
= GetDocAccessible(aPresShell
);
761 if (document
) document
->UpdateText(aContent
);
764 void nsAccessibilityService::TreeViewChanged(PresShell
* aPresShell
,
765 nsIContent
* aContent
,
766 nsITreeView
* aView
) {
767 DocAccessible
* document
= GetDocAccessible(aPresShell
);
769 LocalAccessible
* accessible
= document
->GetAccessible(aContent
);
771 XULTreeAccessible
* treeAcc
= accessible
->AsXULTree();
772 if (treeAcc
) treeAcc
->TreeViewChanged(aView
);
777 void nsAccessibilityService::RangeValueChanged(PresShell
* aPresShell
,
778 nsIContent
* aContent
) {
779 DocAccessible
* document
= GetDocAccessible(aPresShell
);
781 LocalAccessible
* accessible
= document
->GetAccessible(aContent
);
783 document
->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE
,
789 void nsAccessibilityService::UpdateImageMap(nsImageFrame
* aImageFrame
) {
790 PresShell
* presShell
= aImageFrame
->PresShell();
791 DocAccessible
* document
= GetDocAccessible(presShell
);
793 LocalAccessible
* accessible
=
794 document
->GetAccessible(aImageFrame
->GetContent());
796 HTMLImageMapAccessible
* imageMap
= accessible
->AsImageMap();
798 imageMap
->UpdateChildAreas();
802 // If image map was initialized after we created an accessible (that'll
803 // be an image accessible) then recreate it.
804 RecreateAccessible(presShell
, aImageFrame
->GetContent());
809 void nsAccessibilityService::UpdateLabelValue(PresShell
* aPresShell
,
810 nsIContent
* aLabelElm
,
811 const nsString
& aNewValue
) {
812 DocAccessible
* document
= GetDocAccessible(aPresShell
);
814 LocalAccessible
* accessible
= document
->GetAccessible(aLabelElm
);
816 XULLabelAccessible
* xulLabel
= accessible
->AsXULLabel();
817 NS_ASSERTION(xulLabel
,
818 "UpdateLabelValue was called for wrong accessible!");
819 if (xulLabel
) xulLabel
->UpdateLabelValue(aNewValue
);
824 void nsAccessibilityService::PresShellActivated(PresShell
* aPresShell
) {
825 DocAccessible
* document
= aPresShell
->GetDocAccessible();
827 RootAccessible
* rootDocument
= document
->RootAccessible();
828 NS_ASSERTION(rootDocument
, "Entirely broken tree: no root document!");
829 if (rootDocument
) rootDocument
->DocumentActivated(document
);
833 void nsAccessibilityService::RecreateAccessible(PresShell
* aPresShell
,
834 nsIContent
* aContent
) {
835 DocAccessible
* document
= GetDocAccessible(aPresShell
);
836 if (document
) document
->RecreateAccessible(aContent
);
839 void nsAccessibilityService::GetStringRole(uint32_t aRole
, nsAString
& aString
) {
840 #define ROLE(geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \
841 msaaRole, ia2Role, androidClass, iosIsElement, uiaControlType, \
843 case roles::geckoRole: \
844 aString.AssignLiteral(stringRole); \
850 aString
.AssignLiteral("unknown");
857 void nsAccessibilityService::GetStringStates(uint32_t aState
,
858 uint32_t aExtraState
,
859 nsISupports
** aStringStates
) {
860 RefPtr
<DOMStringList
> stringStates
=
861 GetStringStates(nsAccUtils::To64State(aState
, aExtraState
));
864 if (!stringStates
->Length()) {
865 stringStates
->Add(u
"unknown"_ns
);
868 stringStates
.forget(aStringStates
);
871 already_AddRefed
<DOMStringList
> nsAccessibilityService::GetStringStates(
872 uint64_t aStates
) const {
873 RefPtr
<DOMStringList
> stringStates
= new DOMStringList();
875 if (aStates
& states::UNAVAILABLE
) {
876 stringStates
->Add(u
"unavailable"_ns
);
878 if (aStates
& states::SELECTED
) {
879 stringStates
->Add(u
"selected"_ns
);
881 if (aStates
& states::FOCUSED
) {
882 stringStates
->Add(u
"focused"_ns
);
884 if (aStates
& states::PRESSED
) {
885 stringStates
->Add(u
"pressed"_ns
);
887 if (aStates
& states::CHECKED
) {
888 stringStates
->Add(u
"checked"_ns
);
890 if (aStates
& states::MIXED
) {
891 stringStates
->Add(u
"mixed"_ns
);
893 if (aStates
& states::READONLY
) {
894 stringStates
->Add(u
"readonly"_ns
);
896 if (aStates
& states::HOTTRACKED
) {
897 stringStates
->Add(u
"hottracked"_ns
);
899 if (aStates
& states::DEFAULT
) {
900 stringStates
->Add(u
"default"_ns
);
902 if (aStates
& states::EXPANDED
) {
903 stringStates
->Add(u
"expanded"_ns
);
905 if (aStates
& states::COLLAPSED
) {
906 stringStates
->Add(u
"collapsed"_ns
);
908 if (aStates
& states::BUSY
) {
909 stringStates
->Add(u
"busy"_ns
);
911 if (aStates
& states::FLOATING
) {
912 stringStates
->Add(u
"floating"_ns
);
914 if (aStates
& states::ANIMATED
) {
915 stringStates
->Add(u
"animated"_ns
);
917 if (aStates
& states::INVISIBLE
) {
918 stringStates
->Add(u
"invisible"_ns
);
920 if (aStates
& states::OFFSCREEN
) {
921 stringStates
->Add(u
"offscreen"_ns
);
923 if (aStates
& states::SIZEABLE
) {
924 stringStates
->Add(u
"sizeable"_ns
);
926 if (aStates
& states::MOVEABLE
) {
927 stringStates
->Add(u
"moveable"_ns
);
929 if (aStates
& states::SELFVOICING
) {
930 stringStates
->Add(u
"selfvoicing"_ns
);
932 if (aStates
& states::FOCUSABLE
) {
933 stringStates
->Add(u
"focusable"_ns
);
935 if (aStates
& states::SELECTABLE
) {
936 stringStates
->Add(u
"selectable"_ns
);
938 if (aStates
& states::LINKED
) {
939 stringStates
->Add(u
"linked"_ns
);
941 if (aStates
& states::TRAVERSED
) {
942 stringStates
->Add(u
"traversed"_ns
);
944 if (aStates
& states::MULTISELECTABLE
) {
945 stringStates
->Add(u
"multiselectable"_ns
);
947 if (aStates
& states::EXTSELECTABLE
) {
948 stringStates
->Add(u
"extselectable"_ns
);
950 if (aStates
& states::PROTECTED
) {
951 stringStates
->Add(u
"protected"_ns
);
953 if (aStates
& states::HASPOPUP
) {
954 stringStates
->Add(u
"haspopup"_ns
);
956 if (aStates
& states::REQUIRED
) {
957 stringStates
->Add(u
"required"_ns
);
959 if (aStates
& states::ALERT
) {
960 stringStates
->Add(u
"alert"_ns
);
962 if (aStates
& states::INVALID
) {
963 stringStates
->Add(u
"invalid"_ns
);
965 if (aStates
& states::CHECKABLE
) {
966 stringStates
->Add(u
"checkable"_ns
);
968 if (aStates
& states::SUPPORTS_AUTOCOMPLETION
) {
969 stringStates
->Add(u
"autocompletion"_ns
);
971 if (aStates
& states::DEFUNCT
) {
972 stringStates
->Add(u
"defunct"_ns
);
974 if (aStates
& states::SELECTABLE_TEXT
) {
975 stringStates
->Add(u
"selectable text"_ns
);
977 if (aStates
& states::EDITABLE
) {
978 stringStates
->Add(u
"editable"_ns
);
980 if (aStates
& states::ACTIVE
) {
981 stringStates
->Add(u
"active"_ns
);
983 if (aStates
& states::MODAL
) {
984 stringStates
->Add(u
"modal"_ns
);
986 if (aStates
& states::MULTI_LINE
) {
987 stringStates
->Add(u
"multi line"_ns
);
989 if (aStates
& states::HORIZONTAL
) {
990 stringStates
->Add(u
"horizontal"_ns
);
992 if (aStates
& states::OPAQUE1
) {
993 stringStates
->Add(u
"opaque"_ns
);
995 if (aStates
& states::SINGLE_LINE
) {
996 stringStates
->Add(u
"single line"_ns
);
998 if (aStates
& states::TRANSIENT
) {
999 stringStates
->Add(u
"transient"_ns
);
1001 if (aStates
& states::VERTICAL
) {
1002 stringStates
->Add(u
"vertical"_ns
);
1004 if (aStates
& states::STALE
) {
1005 stringStates
->Add(u
"stale"_ns
);
1007 if (aStates
& states::ENABLED
) {
1008 stringStates
->Add(u
"enabled"_ns
);
1010 if (aStates
& states::SENSITIVE
) {
1011 stringStates
->Add(u
"sensitive"_ns
);
1013 if (aStates
& states::EXPANDABLE
) {
1014 stringStates
->Add(u
"expandable"_ns
);
1016 if (aStates
& states::PINNED
) {
1017 stringStates
->Add(u
"pinned"_ns
);
1019 if (aStates
& states::CURRENT
) {
1020 stringStates
->Add(u
"current"_ns
);
1023 return stringStates
.forget();
1026 void nsAccessibilityService::GetStringEventType(uint32_t aEventType
,
1027 nsAString
& aString
) {
1029 nsIAccessibleEvent::EVENT_LAST_ENTRY
== ArrayLength(kEventTypeNames
),
1030 "nsIAccessibleEvent constants are out of sync to kEventTypeNames");
1032 if (aEventType
>= ArrayLength(kEventTypeNames
)) {
1033 aString
.AssignLiteral("unknown");
1037 aString
.AssignASCII(kEventTypeNames
[aEventType
]);
1040 void nsAccessibilityService::GetStringEventType(uint32_t aEventType
,
1041 nsACString
& aString
) {
1043 nsIAccessibleEvent::EVENT_LAST_ENTRY
== ArrayLength(kEventTypeNames
),
1044 "nsIAccessibleEvent constants are out of sync to kEventTypeNames");
1046 if (aEventType
>= ArrayLength(kEventTypeNames
)) {
1047 aString
.AssignLiteral("unknown");
1051 aString
= nsDependentCString(kEventTypeNames
[aEventType
]);
1054 void nsAccessibilityService::GetStringRelationType(uint32_t aRelationType
,
1055 nsAString
& aString
) {
1056 NS_ENSURE_TRUE_VOID(aRelationType
<=
1057 static_cast<uint32_t>(RelationType::LAST
));
1059 #define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \
1060 case RelationType::geckoType: \
1061 aString.AssignLiteral(geckoTypeName); \
1064 RelationType relationType
= static_cast<RelationType
>(aRelationType
);
1065 switch (relationType
) {
1066 #include "RelationTypeMap.h"
1068 aString
.AssignLiteral("unknown");
1075 ////////////////////////////////////////////////////////////////////////////////
1076 // nsAccessibilityService public
1078 LocalAccessible
* nsAccessibilityService::CreateAccessible(
1079 nsINode
* aNode
, LocalAccessible
* aContext
, bool* aIsSubtreeHidden
) {
1080 MOZ_ASSERT(aContext
, "No context provided");
1081 MOZ_ASSERT(aNode
, "No node to create an accessible for");
1082 MOZ_ASSERT(gConsumers
, "No creation after shutdown");
1084 if (aIsSubtreeHidden
) *aIsSubtreeHidden
= false;
1086 DocAccessible
* document
= aContext
->Document();
1087 MOZ_ASSERT(!document
->GetAccessible(aNode
),
1088 "We already have an accessible for this node.");
1090 if (aNode
->IsDocument()) {
1091 // If it's document node then ask accessible document loader for
1092 // document accessible, otherwise return null.
1093 return GetDocAccessible(aNode
->AsDocument());
1096 // We have a content node.
1097 if (!aNode
->GetComposedDoc()) {
1098 NS_WARNING("Creating accessible for node with no document");
1102 if (aNode
->OwnerDoc() != document
->DocumentNode()) {
1103 NS_ERROR("Creating accessible for wrong document");
1107 if (!aNode
->IsContent()) return nullptr;
1109 nsIContent
* content
= aNode
->AsContent();
1110 if (aria::HasDefinedARIAHidden(content
)) {
1111 if (aIsSubtreeHidden
) {
1112 *aIsSubtreeHidden
= true;
1117 // Check frame and its visibility.
1118 nsIFrame
* frame
= content
->GetPrimaryFrame();
1120 // If invisible or inert, we don't create an accessible, but we don't mark
1121 // it with aIsSubtreeHidden = true, since visibility: hidden frame allows
1122 // visible elements in subtree, and inert elements allow non-inert
1124 if (!frame
->StyleVisibility()->IsVisible() || frame
->StyleUI()->IsInert()) {
1127 } else if (nsCoreUtils::CanCreateAccessibleWithoutFrame(content
)) {
1128 // display:contents element doesn't have a frame, but retains the
1129 // semantics. All its children are unaffected.
1130 const nsRoleMapEntry
* roleMapEntry
= aria::GetRoleMap(content
->AsElement());
1131 RefPtr
<LocalAccessible
> newAcc
= MaybeCreateSpecificARIAAccessible(
1132 roleMapEntry
, aContext
, content
, document
);
1133 const MarkupMapInfo
* markupMap
= nullptr;
1135 markupMap
= GetMarkupMapInfoFor(content
);
1136 if (markupMap
&& markupMap
->new_func
) {
1137 newAcc
= markupMap
->new_func(content
->AsElement(), aContext
);
1141 // Check whether this element has an ARIA role or attribute that requires
1142 // us to create an Accessible.
1143 const bool hasNonPresentationalARIARole
=
1144 roleMapEntry
&& !roleMapEntry
->Is(nsGkAtoms::presentation
) &&
1145 !roleMapEntry
->Is(nsGkAtoms::none
);
1146 if (!newAcc
&& (hasNonPresentationalARIARole
||
1147 AttributesMustBeAccessible(content
, document
))) {
1148 newAcc
= new HyperTextAccessible(content
, document
);
1151 // If there's still no Accessible but we do have an entry in the markup
1152 // map for this non-presentational element, create a generic
1153 // HyperTextAccessible.
1154 if (!newAcc
&& markupMap
&&
1155 (!roleMapEntry
|| hasNonPresentationalARIARole
)) {
1156 newAcc
= new HyperTextAccessible(content
, document
);
1160 document
->BindToDocument(newAcc
, roleMapEntry
);
1164 if (aIsSubtreeHidden
) {
1165 *aIsSubtreeHidden
= true;
1170 if (frame
->IsHiddenByContentVisibilityOnAnyAncestor(
1171 nsIFrame::IncludeContentVisibility::Hidden
)) {
1172 if (aIsSubtreeHidden
) {
1173 *aIsSubtreeHidden
= true;
1178 if (nsMenuPopupFrame
* popupFrame
= do_QueryFrame(frame
)) {
1179 // Hidden tooltips and panels don't create accessibles in the whole subtree.
1180 // Showing them gets handled by RootAccessible::ProcessDOMEvent.
1181 if (content
->IsAnyOfXULElements(nsGkAtoms::tooltip
, nsGkAtoms::panel
)) {
1182 nsPopupState popupState
= popupFrame
->PopupState();
1183 if (popupState
== ePopupHiding
|| popupState
== ePopupInvisible
||
1184 popupState
== ePopupClosed
) {
1185 if (aIsSubtreeHidden
) {
1186 *aIsSubtreeHidden
= true;
1193 if (frame
->GetContent() != content
) {
1194 // Not the main content for this frame. This happens because <area>
1195 // elements return the image frame as their primary frame. The main content
1196 // for the image frame is the image content. If the frame is not an image
1197 // frame or the node is not an area element then null is returned.
1198 // This setup will change when bug 135040 is fixed. Make sure we don't
1199 // create area accessible here. Hopefully assertion below will handle that.
1202 nsImageFrame
* imageFrame
= do_QueryFrame(frame
);
1203 NS_ASSERTION(imageFrame
&& content
->IsHTMLElement(nsGkAtoms::area
),
1204 "Unknown case of not main content for the frame!");
1210 nsImageFrame
* imageFrame
= do_QueryFrame(frame
);
1211 NS_ASSERTION(!imageFrame
|| !content
->IsHTMLElement(nsGkAtoms::area
),
1212 "Image map manages the area accessible creation!");
1215 // Attempt to create an accessible based on what we know.
1216 RefPtr
<LocalAccessible
> newAcc
;
1218 // Create accessible for visible text frames.
1219 if (content
->IsText()) {
1220 nsIFrame::RenderedText text
= frame
->GetRenderedText(
1221 0, UINT32_MAX
, nsIFrame::TextOffsetType::OffsetsInContentText
,
1222 nsIFrame::TrailingWhitespace::DontTrim
);
1223 // Ignore not rendered text nodes and whitespace text nodes between table
1225 if (text
.mString
.IsEmpty() ||
1226 (aContext
->IsTableRow() &&
1227 nsCoreUtils::IsWhitespaceString(text
.mString
))) {
1228 if (aIsSubtreeHidden
) *aIsSubtreeHidden
= true;
1233 newAcc
= CreateAccessibleByFrameType(frame
, content
, aContext
);
1234 MOZ_ASSERT(newAcc
, "Accessible not created for text node!");
1235 document
->BindToDocument(newAcc
, nullptr);
1236 newAcc
->AsTextLeaf()->SetText(text
.mString
);
1240 if (content
->IsHTMLElement(nsGkAtoms::map
)) {
1241 // Create hyper text accessible for HTML map if it is used to group links
1242 // (see http://www.w3.org/TR/WCAG10-HTML-TECHS/#group-bypass). If the HTML
1243 // map rect is empty then it is used for links grouping. Otherwise it should
1244 // be used in conjunction with HTML image element and in this case we don't
1245 // create any accessible for it and don't walk into it. The accessibles for
1246 // HTML area (HTMLAreaAccessible) the map contains are attached as
1247 // children of the appropriate accessible for HTML image
1248 // (ImageAccessible).
1249 if (nsLayoutUtils::GetAllInFlowRectsUnion(frame
, frame
->GetParent())
1251 if (aIsSubtreeHidden
) *aIsSubtreeHidden
= true;
1256 newAcc
= new HyperTextAccessible(content
, document
);
1257 document
->BindToDocument(newAcc
, aria::GetRoleMap(content
->AsElement()));
1261 const nsRoleMapEntry
* roleMapEntry
= aria::GetRoleMap(content
->AsElement());
1263 if (roleMapEntry
&& (roleMapEntry
->Is(nsGkAtoms::presentation
) ||
1264 roleMapEntry
->Is(nsGkAtoms::none
))) {
1265 if (MustBeAccessible(content
, document
)) {
1266 // If the element is focusable, a global ARIA attribute is applied to it
1267 // or it is referenced by an ARIA relationship, then treat
1268 // role="presentation" on the element as if the role is not there.
1269 roleMapEntry
= nullptr;
1270 } else if (MustBeGenericAccessible(content
, document
)) {
1271 // Clear roleMapEntry so that we use the generic role specified below.
1272 // Otherwise, we'd expose roles::NOTHING as specified for presentation in
1274 roleMapEntry
= nullptr;
1275 newAcc
= new EnumRoleHyperTextAccessible
<roles::TEXT_CONTAINER
>(content
,
1282 // We should always use OuterDocAccessible for OuterDocs, even if there's a
1283 // specific ARIA class we would otherwise use.
1284 if (!newAcc
&& frame
->AccessibleType() != eOuterDocType
) {
1285 newAcc
= MaybeCreateSpecificARIAAccessible(roleMapEntry
, aContext
, content
,
1289 if (!newAcc
&& content
->IsHTMLElement()) { // HTML accessibles
1290 // Prefer to use markup to decide if and what kind of accessible to
1292 const MarkupMapInfo
* markupMap
=
1293 mHTMLMarkupMap
.Get(content
->NodeInfo()->NameAtom());
1294 if (markupMap
&& markupMap
->new_func
) {
1295 newAcc
= markupMap
->new_func(content
->AsElement(), aContext
);
1298 if (!newAcc
) { // try by frame accessible type.
1299 newAcc
= CreateAccessibleByFrameType(frame
, content
, aContext
);
1302 // If table has strong ARIA role then all table descendants shouldn't
1303 // expose their native roles.
1304 if (!roleMapEntry
&& newAcc
&& aContext
->HasStrongARIARole()) {
1305 if (frame
->AccessibleType() == eHTMLTableRowType
) {
1306 const nsRoleMapEntry
* contextRoleMap
= aContext
->ARIARoleMap();
1307 if (!contextRoleMap
->IsOfType(eTable
)) {
1308 roleMapEntry
= &aria::gEmptyRoleMap
;
1311 } else if (frame
->AccessibleType() == eHTMLTableCellType
&&
1312 aContext
->ARIARoleMap() == &aria::gEmptyRoleMap
) {
1313 roleMapEntry
= &aria::gEmptyRoleMap
;
1315 } else if (content
->IsAnyOfHTMLElements(nsGkAtoms::dt
, nsGkAtoms::li
,
1317 frame
->AccessibleType() == eHTMLLiType
) {
1318 const nsRoleMapEntry
* contextRoleMap
= aContext
->ARIARoleMap();
1319 if (!contextRoleMap
->IsOfType(eList
)) {
1320 roleMapEntry
= &aria::gEmptyRoleMap
;
1327 if (!newAcc
&& content
->IsXULElement()) {
1328 if (content
->IsXULElement(nsGkAtoms::panel
)) {
1329 // We filter here instead of in the XUL map because
1330 // if we filter there and return null, we still end up
1331 // creating a generic accessible at the end of this function.
1332 // Doing the filtering here ensures we never create accessibles
1333 // for panels whose popups aren't visible.
1334 nsMenuPopupFrame
* popupFrame
= do_QueryFrame(frame
);
1339 nsPopupState popupState
= popupFrame
->PopupState();
1340 if (popupState
== ePopupHiding
|| popupState
== ePopupInvisible
||
1341 popupState
== ePopupClosed
) {
1346 // Prefer to use XUL to decide if and what kind of accessible to create.
1347 const XULMarkupMapInfo
* xulMap
=
1348 mXULMarkupMap
.Get(content
->NodeInfo()->NameAtom());
1349 if (xulMap
&& xulMap
->new_func
) {
1350 newAcc
= xulMap
->new_func(content
->AsElement(), aContext
);
1353 // Any XUL/flex box can be used as tabpanel, make sure we create a proper
1354 // accessible for it.
1355 if (!newAcc
&& aContext
->IsXULTabpanels() &&
1356 content
->GetParent() == aContext
->GetContent()) {
1357 LayoutFrameType frameType
= frame
->Type();
1358 // FIXME(emilio): Why only these frame types?
1359 if (frameType
== LayoutFrameType::FlexContainer
||
1360 frameType
== LayoutFrameType::Scroll
) {
1361 newAcc
= new XULTabpanelAccessible(content
, document
);
1367 if (content
->IsSVGElement()) {
1368 if (content
->IsSVGGeometryElement() ||
1369 content
->IsSVGElement(nsGkAtoms::image
)) {
1370 // Shape elements: rect, circle, ellipse, line, path, polygon,
1371 // and polyline. 'use' and 'text' graphic elements require
1373 if (MustSVGElementBeAccessible(content
, document
)) {
1374 newAcc
= new EnumRoleAccessible
<roles::GRAPHIC
>(content
, document
);
1376 } else if (content
->IsSVGElement(nsGkAtoms::text
)) {
1377 newAcc
= new HyperTextAccessible(content
->AsElement(), document
);
1378 } else if (content
->IsSVGElement(nsGkAtoms::svg
)) {
1379 // An <svg> element could contain <foreignObject>, which contains HTML
1380 // but does not normally create its own Accessible. This means that the
1381 // <svg> Accessible could have TextLeafAccessible children, so it must
1382 // be a HyperTextAccessible.
1384 new EnumRoleHyperTextAccessible
<roles::DIAGRAM
>(content
, document
);
1385 } else if (content
->IsSVGElement(nsGkAtoms::g
) &&
1386 MustSVGElementBeAccessible(content
, document
)) {
1387 // <g> can also contain <foreignObject>.
1389 new EnumRoleHyperTextAccessible
<roles::GROUPING
>(content
, document
);
1390 } else if (content
->IsSVGElement(nsGkAtoms::a
)) {
1391 newAcc
= new HTMLLinkAccessible(content
, document
);
1394 } else if (content
->IsMathMLElement()) {
1395 const MarkupMapInfo
* markupMap
=
1396 mMathMLMarkupMap
.Get(content
->NodeInfo()->NameAtom());
1397 if (markupMap
&& markupMap
->new_func
) {
1398 newAcc
= markupMap
->new_func(content
->AsElement(), aContext
);
1401 // Fall back to text when encountering Content MathML.
1402 if (!newAcc
&& !content
->IsAnyOfMathMLElements(
1403 nsGkAtoms::annotation_
, nsGkAtoms::annotation_xml_
,
1404 nsGkAtoms::mpadded_
, nsGkAtoms::mphantom_
,
1405 nsGkAtoms::maligngroup_
, nsGkAtoms::malignmark_
,
1406 nsGkAtoms::mspace_
, nsGkAtoms::semantics_
)) {
1407 newAcc
= new HyperTextAccessible(content
, document
);
1409 } else if (content
->IsGeneratedContentContainerForMarker()) {
1410 if (aContext
->IsHTMLListItem()) {
1411 newAcc
= new HTMLListBulletAccessible(content
, document
);
1413 if (aIsSubtreeHidden
) {
1414 *aIsSubtreeHidden
= true;
1419 // If no accessible, see if we need to create a generic accessible because
1420 // of some property that makes this object interesting
1421 // We don't do this for <body>, <html>, <window>, <dialog> etc. which
1422 // correspond to the doc accessible and will be created in any case
1423 if (!newAcc
&& !content
->IsHTMLElement(nsGkAtoms::body
) &&
1424 content
->GetParent() &&
1425 (roleMapEntry
|| MustBeAccessible(content
, document
) ||
1426 (content
->IsHTMLElement() && nsCoreUtils::HasClickListener(content
)))) {
1427 // This content is focusable or has an interesting dynamic content
1428 // accessibility property. If it's interesting we need it in the
1429 // accessibility hierarchy so that events or other accessibles can point to
1430 // it, or so that it can hold a state, etc.
1431 if (content
->IsHTMLElement() || content
->IsMathMLElement() ||
1432 content
->IsSVGElement(nsGkAtoms::foreignObject
)) {
1433 // Interesting container which may have selectable text and/or embedded
1435 newAcc
= new HyperTextAccessible(content
, document
);
1436 } else { // XUL, other SVG, etc.
1437 // Interesting generic non-HTML container
1438 newAcc
= new AccessibleWrap(content
, document
);
1440 } else if (!newAcc
&& MustBeGenericAccessible(content
, document
)) {
1441 newAcc
= new EnumRoleHyperTextAccessible
<roles::TEXT_CONTAINER
>(content
,
1446 document
->BindToDocument(newAcc
, roleMapEntry
);
1451 #if defined(ANDROID)
1452 # include "mozilla/Monitor.h"
1453 # include "mozilla/Maybe.h"
1455 static Maybe
<Monitor
> sAndroidMonitor
;
1457 mozilla::Monitor
& nsAccessibilityService::GetAndroidMonitor() {
1458 if (!sAndroidMonitor
.isSome()) {
1459 sAndroidMonitor
.emplace("nsAccessibility::sAndroidMonitor");
1462 return *sAndroidMonitor
;
1466 ////////////////////////////////////////////////////////////////////////////////
1467 // nsAccessibilityService private
1469 bool nsAccessibilityService::Init() {
1470 AUTO_PROFILER_MARKER_TEXT("nsAccessibilityService::Init", A11Y
, {}, ""_ns
);
1471 // DO NOT ADD CODE ABOVE HERE: THIS CODE IS MEASURING TIMINGS.
1473 // Initialize accessible document manager.
1474 if (!DocManager::Init()) return false;
1477 nsCOMPtr
<nsIObserverService
> observerService
=
1478 mozilla::services::GetObserverService();
1479 if (!observerService
) return false;
1481 observerService
->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, false);
1484 // This information needs to be initialized before the observer fires.
1485 if (XRE_IsParentProcess()) {
1486 Compatibility::Init();
1488 #endif // defined(XP_WIN)
1490 // Subscribe to EventListenerService.
1491 nsCOMPtr
<nsIEventListenerService
> eventListenerService
=
1492 do_GetService("@mozilla.org/eventlistenerservice;1");
1493 if (!eventListenerService
) return false;
1495 eventListenerService
->AddListenerChangeListener(this);
1497 for (uint32_t i
= 0; i
< ArrayLength(sHTMLMarkupMapList
); i
++) {
1498 mHTMLMarkupMap
.InsertOrUpdate(sHTMLMarkupMapList
[i
].tag
,
1499 &sHTMLMarkupMapList
[i
]);
1501 for (const auto& info
: sMathMLMarkupMapList
) {
1502 mMathMLMarkupMap
.InsertOrUpdate(info
.tag
, &info
);
1505 for (uint32_t i
= 0; i
< ArrayLength(sXULMarkupMapList
); i
++) {
1506 mXULMarkupMap
.InsertOrUpdate(sXULMarkupMapList
[i
].tag
,
1507 &sXULMarkupMapList
[i
]);
1511 logging::CheckEnv();
1514 gAccessibilityService
= this;
1515 NS_ADDREF(gAccessibilityService
); // will release in Shutdown()
1517 if (XRE_IsParentProcess()) {
1518 gApplicationAccessible
= new ApplicationAccessibleWrap();
1520 gApplicationAccessible
= new ApplicationAccessible();
1523 NS_ADDREF(gApplicationAccessible
); // will release in Shutdown()
1524 gApplicationAccessible
->Init();
1526 CrashReporter::RecordAnnotationCString(
1527 CrashReporter::Annotation::Accessibility
, "Active");
1529 // Now its safe to start platform accessibility.
1530 if (XRE_IsParentProcess()) PlatformInit();
1532 statistics::A11yInitialized();
1534 static const char16_t kInitIndicator
[] = {'1', 0};
1535 observerService
->NotifyObservers(nullptr, "a11y-init-or-shutdown",
1541 void nsAccessibilityService::Shutdown() {
1542 // Application is going to be closed, shutdown accessibility and mark
1543 // accessibility service as shutdown to prevent calls of its methods.
1544 // Don't null accessibility service static member at this point to be safe
1545 // if someone will try to operate with it.
1547 MOZ_ASSERT(gConsumers
, "Accessibility was shutdown already");
1548 UnsetConsumers(eXPCOM
| eMainProcess
| ePlatformAPI
);
1550 // Remove observers.
1551 nsCOMPtr
<nsIObserverService
> observerService
=
1552 mozilla::services::GetObserverService();
1553 if (observerService
) {
1554 observerService
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
);
1557 // Stop accessible document loader.
1558 DocManager::Shutdown();
1560 SelectionManager::Shutdown();
1562 if (XRE_IsParentProcess()) PlatformShutdown();
1564 gApplicationAccessible
->Shutdown();
1565 NS_RELEASE(gApplicationAccessible
);
1566 gApplicationAccessible
= nullptr;
1568 NS_IF_RELEASE(gXPCApplicationAccessible
);
1569 gXPCApplicationAccessible
= nullptr;
1571 #if defined(ANDROID)
1572 // Don't allow the service to shut down while an a11y request is being handled
1573 // in the UI thread, as the request may depend on state from the service.
1574 MonitorAutoLock
mal(GetAndroidMonitor());
1576 NS_RELEASE(gAccessibilityService
);
1577 gAccessibilityService
= nullptr;
1579 if (observerService
) {
1580 static const char16_t kShutdownIndicator
[] = {'0', 0};
1581 observerService
->NotifyObservers(nullptr, "a11y-init-or-shutdown",
1582 kShutdownIndicator
);
1586 already_AddRefed
<LocalAccessible
>
1587 nsAccessibilityService::CreateAccessibleByFrameType(nsIFrame
* aFrame
,
1588 nsIContent
* aContent
,
1589 LocalAccessible
* aContext
) {
1590 DocAccessible
* document
= aContext
->Document();
1592 RefPtr
<LocalAccessible
> newAcc
;
1593 switch (aFrame
->AccessibleType()) {
1597 newAcc
= new HTMLBRAccessible(aContent
, document
);
1599 case eHTMLButtonType
:
1600 newAcc
= new HTMLButtonAccessible(aContent
, document
);
1602 case eHTMLCanvasType
:
1603 newAcc
= new HTMLCanvasAccessible(aContent
, document
);
1605 case eHTMLCaptionType
:
1606 if (aContext
->IsTable() &&
1607 aContext
->GetContent() == aContent
->GetParent()) {
1608 newAcc
= new HTMLCaptionAccessible(aContent
, document
);
1611 case eHTMLCheckboxType
:
1612 newAcc
= new CheckboxAccessible(aContent
, document
);
1614 case eHTMLComboboxType
:
1615 newAcc
= new HTMLComboboxAccessible(aContent
, document
);
1617 case eHTMLFileInputType
:
1618 newAcc
= new HTMLFileInputAccessible(aContent
, document
);
1620 case eHTMLGroupboxType
:
1621 newAcc
= new HTMLGroupboxAccessible(aContent
, document
);
1624 newAcc
= new HTMLHRAccessible(aContent
, document
);
1626 case eHTMLImageMapType
:
1627 newAcc
= new HTMLImageMapAccessible(aContent
, document
);
1630 if (aContext
->IsList() &&
1631 aContext
->GetContent() == aContent
->GetParent()) {
1632 newAcc
= new HTMLLIAccessible(aContent
, document
);
1634 // Otherwise create a generic text accessible to avoid text jamming.
1635 newAcc
= new HyperTextAccessible(aContent
, document
);
1638 case eHTMLSelectListType
:
1639 newAcc
= new HTMLSelectListAccessible(aContent
, document
);
1641 case eHTMLMediaType
:
1642 newAcc
= new EnumRoleAccessible
<roles::GROUPING
>(aContent
, document
);
1644 case eHTMLRadioButtonType
:
1645 newAcc
= new HTMLRadioButtonAccessible(aContent
, document
);
1647 case eHTMLRangeType
:
1648 newAcc
= new HTMLRangeAccessible(aContent
, document
);
1650 case eHTMLSpinnerType
:
1651 newAcc
= new HTMLSpinnerAccessible(aContent
, document
);
1653 case eHTMLTableType
:
1654 case eHTMLTableCellType
:
1655 // We handle markup and ARIA tables elsewhere. If we reach here, this is
1656 // a CSS table part. Just create a generic text container.
1657 newAcc
= new HyperTextAccessible(aContent
, document
);
1659 case eHTMLTableRowType
:
1660 // This is a CSS table row. Don't expose it at all.
1662 case eHTMLTextFieldType
:
1663 newAcc
= new HTMLTextFieldAccessible(aContent
, document
);
1665 case eHyperTextType
: {
1666 if (aContext
->IsTable() || aContext
->IsTableRow()) {
1667 // This is some generic hyperText, for example a block frame element
1668 // inserted between a table and table row. Treat it as presentational.
1672 if (!aContent
->IsAnyOfHTMLElements(nsGkAtoms::dt
, nsGkAtoms::dd
,
1673 nsGkAtoms::div
, nsGkAtoms::thead
,
1674 nsGkAtoms::tfoot
, nsGkAtoms::tbody
)) {
1675 newAcc
= new HyperTextAccessible(aContent
, document
);
1680 if (aContent
->IsElement() &&
1681 ShouldCreateImgAccessible(aContent
->AsElement(), document
)) {
1682 newAcc
= new ImageAccessible(aContent
, document
);
1686 newAcc
= new OuterDocAccessible(aContent
, document
);
1689 newAcc
= new TextLeafAccessible(aContent
, document
);
1696 return newAcc
.forget();
1699 void nsAccessibilityService::MarkupAttributes(
1700 Accessible
* aAcc
, AccAttributes
* aAttributes
) const {
1701 const mozilla::a11y::MarkupMapInfo
* markupMap
= GetMarkupMapInfoFor(aAcc
);
1702 if (!markupMap
) return;
1704 dom::Element
* el
= aAcc
->IsLocal() ? aAcc
->AsLocal()->Elm() : nullptr;
1705 for (uint32_t i
= 0; i
< ArrayLength(markupMap
->attrs
); i
++) {
1706 const MarkupAttrInfo
* info
= markupMap
->attrs
+ i
;
1707 if (!info
->name
) break;
1709 if (info
->DOMAttrName
) {
1711 // XXX Expose DOM attributes for cached RemoteAccessibles.
1714 if (info
->DOMAttrValue
) {
1715 if (el
->AttrValueIs(kNameSpaceID_None
, info
->DOMAttrName
,
1716 info
->DOMAttrValue
, eCaseMatters
)) {
1717 aAttributes
->SetAttribute(info
->name
, info
->DOMAttrValue
);
1723 el
->GetAttr(info
->DOMAttrName
, value
);
1725 if (!value
.IsEmpty()) {
1726 aAttributes
->SetAttribute(info
->name
, std::move(value
));
1732 aAttributes
->SetAttribute(info
->name
, info
->value
);
1736 LocalAccessible
* nsAccessibilityService::AddNativeRootAccessible(
1737 void* aAtkAccessible
) {
1738 #ifdef MOZ_ACCESSIBILITY_ATK
1739 ApplicationAccessible
* applicationAcc
= ApplicationAcc();
1740 if (!applicationAcc
) return nullptr;
1742 GtkWindowAccessible
* nativeWnd
=
1743 new GtkWindowAccessible(static_cast<AtkObject
*>(aAtkAccessible
));
1745 if (applicationAcc
->AppendChild(nativeWnd
)) return nativeWnd
;
1751 void nsAccessibilityService::RemoveNativeRootAccessible(
1752 LocalAccessible
* aAccessible
) {
1753 #ifdef MOZ_ACCESSIBILITY_ATK
1754 ApplicationAccessible
* applicationAcc
= ApplicationAcc();
1756 if (applicationAcc
) applicationAcc
->RemoveChild(aAccessible
);
1760 bool nsAccessibilityService::HasAccessible(nsINode
* aDOMNode
) {
1761 if (!aDOMNode
) return false;
1763 Document
* document
= aDOMNode
->OwnerDoc();
1764 if (!document
) return false;
1766 DocAccessible
* docAcc
= GetExistingDocAccessible(aDOMNode
->OwnerDoc());
1767 if (!docAcc
) return false;
1769 return docAcc
->HasAccessible(aDOMNode
);
1772 ////////////////////////////////////////////////////////////////////////////////
1773 // nsAccessibilityService private (DON'T put methods here)
1775 void nsAccessibilityService::SetConsumers(uint32_t aConsumers
, bool aNotify
) {
1776 if (gConsumers
& aConsumers
) {
1780 gConsumers
|= aConsumers
;
1782 NotifyOfConsumersChange();
1786 void nsAccessibilityService::UnsetConsumers(uint32_t aConsumers
) {
1787 if (!(gConsumers
& aConsumers
)) {
1791 gConsumers
&= ~aConsumers
;
1792 NotifyOfConsumersChange();
1795 void nsAccessibilityService::GetConsumers(nsAString
& aString
) {
1796 const char16_t
* kJSONFmt
=
1797 u
"{ \"XPCOM\": %s, \"MainProcess\": %s, \"PlatformAPI\": %s }";
1799 nsTextFormatter::ssprintf(json
, kJSONFmt
,
1800 gConsumers
& eXPCOM
? "true" : "false",
1801 gConsumers
& eMainProcess
? "true" : "false",
1802 gConsumers
& ePlatformAPI
? "true" : "false");
1803 aString
.Assign(json
);
1806 void nsAccessibilityService::NotifyOfConsumersChange() {
1807 nsCOMPtr
<nsIObserverService
> observerService
=
1808 mozilla::services::GetObserverService();
1810 if (!observerService
) {
1814 nsAutoString consumers
;
1815 GetConsumers(consumers
);
1816 observerService
->NotifyObservers(nullptr, "a11y-consumers-changed",
1820 const mozilla::a11y::MarkupMapInfo
* nsAccessibilityService::GetMarkupMapInfoFor(
1821 Accessible
* aAcc
) const {
1822 if (LocalAccessible
* localAcc
= aAcc
->AsLocal()) {
1823 return localAcc
->HasOwnContent()
1824 ? GetMarkupMapInfoFor(localAcc
->GetContent())
1827 // XXX For now, we assume all RemoteAccessibles are HTML elements. This
1828 // isn't strictly correct, but as far as current callers are concerned,
1829 // this doesn't matter. If that changes in future, we could expose the
1830 // element type via AccGenericType.
1831 return mHTMLMarkupMap
.Get(aAcc
->TagName());
1834 nsAccessibilityService
* GetOrCreateAccService(uint32_t aNewConsumer
) {
1835 // Do not initialize accessibility if it is force disabled.
1836 if (PlatformDisabledState() == ePlatformIsDisabled
) {
1840 if (!nsAccessibilityService::gAccessibilityService
) {
1841 RefPtr
<nsAccessibilityService
> service
= new nsAccessibilityService();
1842 if (!service
->Init()) {
1843 service
->Shutdown();
1848 MOZ_ASSERT(nsAccessibilityService::gAccessibilityService
,
1849 "LocalAccessible service is not initialized.");
1850 nsAccessibilityService::gAccessibilityService
->SetConsumers(aNewConsumer
);
1851 return nsAccessibilityService::gAccessibilityService
;
1854 void MaybeShutdownAccService(uint32_t aFormerConsumer
) {
1855 nsAccessibilityService
* accService
=
1856 nsAccessibilityService::gAccessibilityService
;
1858 if (!accService
|| nsAccessibilityService::IsShutdown()) {
1862 // Still used by XPCOM
1863 if (nsCoreUtils::AccEventObserversExist() ||
1864 xpcAccessibilityService::IsInUse() || accService
->HasXPCDocuments()) {
1865 // In case the XPCOM flag was unset (possibly because of the shutdown
1866 // timer in the xpcAccessibilityService) ensure it is still present. Note:
1867 // this should be fixed when all the consumer logic is taken out as a
1869 accService
->SetConsumers(nsAccessibilityService::eXPCOM
, false);
1871 if (aFormerConsumer
!= nsAccessibilityService::eXPCOM
) {
1872 // Only unset non-XPCOM consumers.
1873 accService
->UnsetConsumers(aFormerConsumer
);
1878 if (nsAccessibilityService::gConsumers
& ~aFormerConsumer
) {
1879 accService
->UnsetConsumers(aFormerConsumer
);
1882 ->Shutdown(); // Will unset all nsAccessibilityService::gConsumers
1886 ////////////////////////////////////////////////////////////////////////////////
1888 ////////////////////////////////////////////////////////////////////////////////
1893 FocusManager
* FocusMgr() {
1894 return nsAccessibilityService::gAccessibilityService
;
1897 SelectionManager
* SelectionMgr() {
1898 return nsAccessibilityService::gAccessibilityService
;
1901 ApplicationAccessible
* ApplicationAcc() {
1902 return nsAccessibilityService::gApplicationAccessible
;
1905 xpcAccessibleApplication
* XPCApplicationAcc() {
1906 if (!nsAccessibilityService::gXPCApplicationAccessible
&&
1907 nsAccessibilityService::gApplicationAccessible
) {
1908 nsAccessibilityService::gXPCApplicationAccessible
=
1909 new xpcAccessibleApplication(
1910 nsAccessibilityService::gApplicationAccessible
);
1911 NS_ADDREF(nsAccessibilityService::gXPCApplicationAccessible
);
1914 return nsAccessibilityService::gXPCApplicationAccessible
;
1917 EPlatformDisabledState
PlatformDisabledState() {
1918 static bool platformDisabledStateCached
= false;
1919 if (platformDisabledStateCached
) {
1920 return static_cast<EPlatformDisabledState
>(sPlatformDisabledState
);
1923 platformDisabledStateCached
= true;
1924 Preferences::RegisterCallback(PrefChanged
, PREF_ACCESSIBILITY_FORCE_DISABLED
);
1925 return ReadPlatformDisabledState();
1928 EPlatformDisabledState
ReadPlatformDisabledState() {
1929 sPlatformDisabledState
=
1930 Preferences::GetInt(PREF_ACCESSIBILITY_FORCE_DISABLED
, 0);
1931 if (sPlatformDisabledState
< ePlatformIsForceEnabled
) {
1932 sPlatformDisabledState
= ePlatformIsForceEnabled
;
1933 } else if (sPlatformDisabledState
> ePlatformIsDisabled
) {
1934 sPlatformDisabledState
= ePlatformIsDisabled
;
1937 return static_cast<EPlatformDisabledState
>(sPlatformDisabledState
);
1940 void PrefChanged(const char* aPref
, void* aClosure
) {
1941 if (ReadPlatformDisabledState() == ePlatformIsDisabled
) {
1942 // Force shut down accessibility.
1943 nsAccessibilityService
* accService
=
1944 nsAccessibilityService::gAccessibilityService
;
1945 if (accService
&& !nsAccessibilityService::IsShutdown()) {
1946 accService
->Shutdown();
1952 } // namespace mozilla