Bug 1869092 - Fix timeouts in browser_PanelMultiView.js. r=twisniewski,test-only
[gecko.git] / accessible / base / nsAccessibilityService.cpp
blobc31dd666cef3025bd0b4106b42e992c291fe722b
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"
11 #include "ARIAMap.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"
28 #include "nsCRT.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"
38 #endif
39 #include "States.h"
40 #include "Statistics.h"
41 #include "TextLeafAccessible.h"
42 #include "xpcAccessibleApplication.h"
44 #ifdef XP_WIN
45 # include "mozilla/a11y/Compatibility.h"
46 # include "mozilla/StaticPtr.h"
47 #endif
49 #ifdef A11Y_LOG
50 # include "Logging.h"
51 #endif
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;
84 /**
85 * Accessibility service force enable/disable preference.
86 * Supported values:
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 ////////////////////////////////////////////////////////////////////////////////
94 // Statics
95 ////////////////////////////////////////////////////////////////////////////////
97 /**
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.
110 return nullptr;
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) {
118 return nullptr;
120 // That row must be in a table, though there may be an intervening rowgroup.
121 parent = parent->GetNonGenericParent();
122 if (!parent) {
123 return nullptr;
125 if (!parent->IsTable() && parent->Role() == roles::GROUPING) {
126 parent = parent->GetNonGenericParent();
127 if (!parent) {
128 return nullptr;
131 if (parent->IsTable()) {
132 return new ARIAGridCellAccessible(aContent, aDocument);
135 return nullptr;
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.
153 return true;
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))) {
164 return true;
169 // If the given ID is referred by relation attribute then create an
170 // Accessible for it.
171 nsAutoString id;
172 if (nsCoreUtils::GetID(aContent, id) && !id.IsEmpty()) {
173 return aDocument->IsDependentID(aContent->AsElement(), id);
177 return false;
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).
200 return false;
202 nsIFrame* frame = aContent->GetPrimaryFrame();
203 MOZ_ASSERT(frame);
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();
228 MOZ_ASSERT(frame);
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)) {
236 return true;
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();
247 if (!frame) {
248 return false;
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)) {
257 return false;
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.
266 return true;
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.
274 return true;
277 // Otherwise, no alt text means we should not create an accessible.
278 return false;
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)) {
290 return true;
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;
310 #endif
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());
336 return nullptr;
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"
367 #undef MARKUPMAP
369 #define XULMAP(atom, ...) {nsGkAtoms::atom, __VA_ARGS__},
371 #define XULMAP_TYPE(atom, new_type) \
372 XULMAP( \
373 atom, \
374 [](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* { \
375 return new new_type(aElement, aContext->Document()); \
378 static const XULMarkupMapInfo sXULMarkupMapList[] = {
379 #include "XULMap.h"
382 #undef XULMAP_TYPE
383 #undef XULMAP
385 #undef Attr
386 #undef AttrFromDOM
387 #undef AttrFromDOMIf
389 ////////////////////////////////////////////////////////////////////////////////
390 // nsAccessibilityService
391 ////////////////////////////////////////////////////////////////////////////////
393 nsAccessibilityService* nsAccessibilityService::gAccessibilityService = nullptr;
394 ApplicationAccessible* nsAccessibilityService::gApplicationAccessible = nullptr;
395 xpcAccessibleApplication* nsAccessibilityService::gXPCApplicationAccessible =
396 nullptr;
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
412 NS_IMETHODIMP
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()) {
426 continue;
429 uint32_t changeCount;
430 change->GetCountOfEventListenerChangesAffectingAccessibility(&changeCount);
431 NS_ENSURE_SUCCESS(rv, rv);
433 if (changeCount) {
434 Document* ownerDoc = content->OwnerDoc();
435 DocAccessible* document = GetExistingDocAccessible(ownerDoc);
437 if (document) {
438 LocalAccessible* acc = document->GetAccessible(content);
439 if (!acc && (content == document->GetContent() ||
440 content == document->DocumentNode()->GetRootElement())) {
441 acc = document;
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
457 // handler.
458 document->ContentInserted(content, content->GetNextSibling());
459 } else if (acc) {
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);
478 return NS_OK;
481 ////////////////////////////////////////////////////////////////////////////////
482 // nsISupports
484 NS_IMPL_ISUPPORTS_INHERITED(nsAccessibilityService, DocManager, nsIObserver,
485 nsIListenerChangeListener,
486 nsISelectionListener) // from SelectionManager
488 ////////////////////////////////////////////////////////////////////////////////
489 // nsIObserver
491 NS_IMETHODIMP
492 nsAccessibilityService::Observe(nsISupports* aSubject, const char* aTopic,
493 const char16_t* aData) {
494 if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
495 Shutdown();
498 return NS_OK;
501 void nsAccessibilityService::NotifyOfAnchorJumpTo(nsIContent* aTargetNode) {
502 Document* documentNode = aTargetNode->GetUncomposedDoc();
503 if (!documentNode) {
504 return;
506 DocAccessible* document = GetDocAccessible(documentNode);
507 if (!document) {
508 return;
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();
513 if (focusedAcc &&
514 (focusedAcc == document || focusedAcc->IsNonInteractive())) {
515 LocalAccessible* targetAcc = document->GetAccessible(aTargetNode);
516 if (targetAcc) {
517 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SCROLLING_START,
518 targetAcc);
519 document->SetAnchorJump(nullptr);
520 } else {
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);
525 } else {
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();
539 if (document) {
540 // DocAccessible::GetAccessible() won't return the document if a root
541 // element like body is passed.
542 LocalAccessible* accessible = aContent == document->GetContent()
543 ? document
544 : document->GetAccessible(aContent);
545 if (accessible) {
546 document->QueueCacheUpdate(accessible, CacheDomain::Bounds);
552 void nsAccessibilityService::NotifyOfComputedStyleChange(
553 mozilla::PresShell* aPresShell, nsIContent* aContent) {
554 DocAccessible* document = aPresShell->GetDocAccessible();
555 if (!document) {
556 return;
559 // DocAccessible::GetAccessible() won't return the document if a root
560 // element like body is passed.
561 LocalAccessible* accessible = aContent == document->GetContent()
562 ? document
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,
570 // scroll)
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 LocalAccessible* nsAccessibilityService::GetRootDocumentAccessible(
610 PresShell* aPresShell, bool aCanCreate) {
611 PresShell* presShell = aPresShell;
612 Document* documentNode = aPresShell->GetDocument();
613 if (documentNode) {
614 nsCOMPtr<nsIDocShellTreeItem> treeItem(documentNode->GetDocShell());
615 if (treeItem) {
616 nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
617 treeItem->GetInProcessRootTreeItem(getter_AddRefs(rootTreeItem));
618 if (treeItem != rootTreeItem) {
619 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(rootTreeItem));
620 presShell = docShell->GetPresShell();
623 return aCanCreate ? GetDocAccessible(presShell)
624 : presShell->GetDocAccessible();
627 return nullptr;
630 void nsAccessibilityService::NotifyOfTabPanelVisibilityChange(
631 PresShell* aPresShell, Element* aPanel, bool aNowVisible) {
632 MOZ_ASSERT(aPanel->GetParent()->IsXULElement(nsGkAtoms::tabpanels));
634 DocAccessible* document = GetDocAccessible(aPresShell);
635 if (!document) {
636 return;
639 if (LocalAccessible* acc = document->GetAccessible(aPanel)) {
640 RefPtr<AccEvent> event =
641 new AccStateChangeEvent(acc, states::OFFSCREEN, aNowVisible);
642 document->FireDelayedEvent(event);
646 void nsAccessibilityService::ContentRangeInserted(PresShell* aPresShell,
647 nsIContent* aStartChild,
648 nsIContent* aEndChild) {
649 DocAccessible* document = GetDocAccessible(aPresShell);
650 #ifdef A11Y_LOG
651 if (logging::IsEnabled(logging::eTree)) {
652 logging::MsgBegin("TREE", "content inserted; doc: %p", document);
653 logging::Node("container", aStartChild->GetParentNode());
654 for (nsIContent* child = aStartChild; child != aEndChild;
655 child = child->GetNextSibling()) {
656 logging::Node("content", child);
658 logging::MsgEnd();
659 logging::Stack();
661 #endif
663 if (document) {
664 document->ContentInserted(aStartChild, aEndChild);
668 void nsAccessibilityService::ScheduleAccessibilitySubtreeUpdate(
669 PresShell* aPresShell, nsIContent* aContent) {
670 DocAccessible* document = GetDocAccessible(aPresShell);
671 #ifdef A11Y_LOG
672 if (logging::IsEnabled(logging::eTree)) {
673 logging::MsgBegin("TREE", "schedule update; doc: %p", document);
674 logging::Node("content node", aContent);
675 logging::MsgEnd();
677 #endif
679 if (document) {
680 document->ScheduleTreeUpdate(aContent);
684 void nsAccessibilityService::ContentRemoved(PresShell* aPresShell,
685 nsIContent* aChildNode) {
686 DocAccessible* document = GetDocAccessible(aPresShell);
687 #ifdef A11Y_LOG
688 if (logging::IsEnabled(logging::eTree)) {
689 logging::MsgBegin("TREE", "content removed; doc: %p", document);
690 logging::Node("container node", aChildNode->GetFlattenedTreeParent());
691 logging::Node("content node", aChildNode);
692 logging::MsgEnd();
694 #endif
696 if (document) {
697 document->ContentRemoved(aChildNode);
700 #ifdef A11Y_LOG
701 if (logging::IsEnabled(logging::eTree)) {
702 logging::MsgEnd();
703 logging::Stack();
705 #endif
708 void nsAccessibilityService::TableLayoutGuessMaybeChanged(
709 PresShell* aPresShell, nsIContent* aContent) {
710 if (DocAccessible* document = GetDocAccessible(aPresShell)) {
711 if (LocalAccessible* acc = document->GetAccessible(aContent)) {
712 if (LocalAccessible* table = nsAccUtils::TableFor(acc)) {
713 document->QueueCacheUpdate(table, CacheDomain::Table);
719 void nsAccessibilityService::ComboboxOptionMaybeChanged(
720 PresShell* aPresShell, nsIContent* aMutatingNode) {
721 DocAccessible* document = GetDocAccessible(aPresShell);
722 if (!document) {
723 return;
726 for (nsIContent* cur = aMutatingNode; cur; cur = cur->GetParent()) {
727 if (cur->IsHTMLElement(nsGkAtoms::option)) {
728 if (LocalAccessible* accessible = document->GetAccessible(cur)) {
729 document->FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE,
730 accessible);
731 break;
733 if (cur->IsHTMLElement(nsGkAtoms::select)) {
734 break;
740 void nsAccessibilityService::UpdateText(PresShell* aPresShell,
741 nsIContent* aContent) {
742 DocAccessible* document = GetDocAccessible(aPresShell);
743 if (document) document->UpdateText(aContent);
746 void nsAccessibilityService::TreeViewChanged(PresShell* aPresShell,
747 nsIContent* aContent,
748 nsITreeView* aView) {
749 DocAccessible* document = GetDocAccessible(aPresShell);
750 if (document) {
751 LocalAccessible* accessible = document->GetAccessible(aContent);
752 if (accessible) {
753 XULTreeAccessible* treeAcc = accessible->AsXULTree();
754 if (treeAcc) treeAcc->TreeViewChanged(aView);
759 void nsAccessibilityService::RangeValueChanged(PresShell* aPresShell,
760 nsIContent* aContent) {
761 DocAccessible* document = GetDocAccessible(aPresShell);
762 if (document) {
763 LocalAccessible* accessible = document->GetAccessible(aContent);
764 if (accessible) {
765 document->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
766 accessible);
771 void nsAccessibilityService::UpdateImageMap(nsImageFrame* aImageFrame) {
772 PresShell* presShell = aImageFrame->PresShell();
773 DocAccessible* document = GetDocAccessible(presShell);
774 if (document) {
775 LocalAccessible* accessible =
776 document->GetAccessible(aImageFrame->GetContent());
777 if (accessible) {
778 HTMLImageMapAccessible* imageMap = accessible->AsImageMap();
779 if (imageMap) {
780 imageMap->UpdateChildAreas();
781 return;
784 // If image map was initialized after we created an accessible (that'll
785 // be an image accessible) then recreate it.
786 RecreateAccessible(presShell, aImageFrame->GetContent());
791 void nsAccessibilityService::UpdateLabelValue(PresShell* aPresShell,
792 nsIContent* aLabelElm,
793 const nsString& aNewValue) {
794 DocAccessible* document = GetDocAccessible(aPresShell);
795 if (document) {
796 LocalAccessible* accessible = document->GetAccessible(aLabelElm);
797 if (accessible) {
798 XULLabelAccessible* xulLabel = accessible->AsXULLabel();
799 NS_ASSERTION(xulLabel,
800 "UpdateLabelValue was called for wrong accessible!");
801 if (xulLabel) xulLabel->UpdateLabelValue(aNewValue);
806 void nsAccessibilityService::PresShellActivated(PresShell* aPresShell) {
807 DocAccessible* document = aPresShell->GetDocAccessible();
808 if (document) {
809 RootAccessible* rootDocument = document->RootAccessible();
810 NS_ASSERTION(rootDocument, "Entirely broken tree: no root document!");
811 if (rootDocument) rootDocument->DocumentActivated(document);
815 void nsAccessibilityService::RecreateAccessible(PresShell* aPresShell,
816 nsIContent* aContent) {
817 DocAccessible* document = GetDocAccessible(aPresShell);
818 if (document) document->RecreateAccessible(aContent);
821 void nsAccessibilityService::GetStringRole(uint32_t aRole, nsAString& aString) {
822 #define ROLE(geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \
823 msaaRole, ia2Role, androidClass, nameRule) \
824 case roles::geckoRole: \
825 aString.AssignLiteral(stringRole); \
826 return;
828 switch (aRole) {
829 #include "RoleMap.h"
830 default:
831 aString.AssignLiteral("unknown");
832 return;
835 #undef ROLE
838 void nsAccessibilityService::GetStringStates(uint32_t aState,
839 uint32_t aExtraState,
840 nsISupports** aStringStates) {
841 RefPtr<DOMStringList> stringStates =
842 GetStringStates(nsAccUtils::To64State(aState, aExtraState));
844 // unknown state
845 if (!stringStates->Length()) {
846 stringStates->Add(u"unknown"_ns);
849 stringStates.forget(aStringStates);
852 already_AddRefed<DOMStringList> nsAccessibilityService::GetStringStates(
853 uint64_t aStates) const {
854 RefPtr<DOMStringList> stringStates = new DOMStringList();
856 if (aStates & states::UNAVAILABLE) {
857 stringStates->Add(u"unavailable"_ns);
859 if (aStates & states::SELECTED) {
860 stringStates->Add(u"selected"_ns);
862 if (aStates & states::FOCUSED) {
863 stringStates->Add(u"focused"_ns);
865 if (aStates & states::PRESSED) {
866 stringStates->Add(u"pressed"_ns);
868 if (aStates & states::CHECKED) {
869 stringStates->Add(u"checked"_ns);
871 if (aStates & states::MIXED) {
872 stringStates->Add(u"mixed"_ns);
874 if (aStates & states::READONLY) {
875 stringStates->Add(u"readonly"_ns);
877 if (aStates & states::HOTTRACKED) {
878 stringStates->Add(u"hottracked"_ns);
880 if (aStates & states::DEFAULT) {
881 stringStates->Add(u"default"_ns);
883 if (aStates & states::EXPANDED) {
884 stringStates->Add(u"expanded"_ns);
886 if (aStates & states::COLLAPSED) {
887 stringStates->Add(u"collapsed"_ns);
889 if (aStates & states::BUSY) {
890 stringStates->Add(u"busy"_ns);
892 if (aStates & states::FLOATING) {
893 stringStates->Add(u"floating"_ns);
895 if (aStates & states::ANIMATED) {
896 stringStates->Add(u"animated"_ns);
898 if (aStates & states::INVISIBLE) {
899 stringStates->Add(u"invisible"_ns);
901 if (aStates & states::OFFSCREEN) {
902 stringStates->Add(u"offscreen"_ns);
904 if (aStates & states::SIZEABLE) {
905 stringStates->Add(u"sizeable"_ns);
907 if (aStates & states::MOVEABLE) {
908 stringStates->Add(u"moveable"_ns);
910 if (aStates & states::SELFVOICING) {
911 stringStates->Add(u"selfvoicing"_ns);
913 if (aStates & states::FOCUSABLE) {
914 stringStates->Add(u"focusable"_ns);
916 if (aStates & states::SELECTABLE) {
917 stringStates->Add(u"selectable"_ns);
919 if (aStates & states::LINKED) {
920 stringStates->Add(u"linked"_ns);
922 if (aStates & states::TRAVERSED) {
923 stringStates->Add(u"traversed"_ns);
925 if (aStates & states::MULTISELECTABLE) {
926 stringStates->Add(u"multiselectable"_ns);
928 if (aStates & states::EXTSELECTABLE) {
929 stringStates->Add(u"extselectable"_ns);
931 if (aStates & states::PROTECTED) {
932 stringStates->Add(u"protected"_ns);
934 if (aStates & states::HASPOPUP) {
935 stringStates->Add(u"haspopup"_ns);
937 if (aStates & states::REQUIRED) {
938 stringStates->Add(u"required"_ns);
940 if (aStates & states::ALERT) {
941 stringStates->Add(u"alert"_ns);
943 if (aStates & states::INVALID) {
944 stringStates->Add(u"invalid"_ns);
946 if (aStates & states::CHECKABLE) {
947 stringStates->Add(u"checkable"_ns);
949 if (aStates & states::SUPPORTS_AUTOCOMPLETION) {
950 stringStates->Add(u"autocompletion"_ns);
952 if (aStates & states::DEFUNCT) {
953 stringStates->Add(u"defunct"_ns);
955 if (aStates & states::SELECTABLE_TEXT) {
956 stringStates->Add(u"selectable text"_ns);
958 if (aStates & states::EDITABLE) {
959 stringStates->Add(u"editable"_ns);
961 if (aStates & states::ACTIVE) {
962 stringStates->Add(u"active"_ns);
964 if (aStates & states::MODAL) {
965 stringStates->Add(u"modal"_ns);
967 if (aStates & states::MULTI_LINE) {
968 stringStates->Add(u"multi line"_ns);
970 if (aStates & states::HORIZONTAL) {
971 stringStates->Add(u"horizontal"_ns);
973 if (aStates & states::OPAQUE1) {
974 stringStates->Add(u"opaque"_ns);
976 if (aStates & states::SINGLE_LINE) {
977 stringStates->Add(u"single line"_ns);
979 if (aStates & states::TRANSIENT) {
980 stringStates->Add(u"transient"_ns);
982 if (aStates & states::VERTICAL) {
983 stringStates->Add(u"vertical"_ns);
985 if (aStates & states::STALE) {
986 stringStates->Add(u"stale"_ns);
988 if (aStates & states::ENABLED) {
989 stringStates->Add(u"enabled"_ns);
991 if (aStates & states::SENSITIVE) {
992 stringStates->Add(u"sensitive"_ns);
994 if (aStates & states::EXPANDABLE) {
995 stringStates->Add(u"expandable"_ns);
997 if (aStates & states::PINNED) {
998 stringStates->Add(u"pinned"_ns);
1000 if (aStates & states::CURRENT) {
1001 stringStates->Add(u"current"_ns);
1004 return stringStates.forget();
1007 void nsAccessibilityService::GetStringEventType(uint32_t aEventType,
1008 nsAString& aString) {
1009 static_assert(
1010 nsIAccessibleEvent::EVENT_LAST_ENTRY == ArrayLength(kEventTypeNames),
1011 "nsIAccessibleEvent constants are out of sync to kEventTypeNames");
1013 if (aEventType >= ArrayLength(kEventTypeNames)) {
1014 aString.AssignLiteral("unknown");
1015 return;
1018 aString.AssignASCII(kEventTypeNames[aEventType]);
1021 void nsAccessibilityService::GetStringEventType(uint32_t aEventType,
1022 nsACString& aString) {
1023 MOZ_ASSERT(
1024 nsIAccessibleEvent::EVENT_LAST_ENTRY == ArrayLength(kEventTypeNames),
1025 "nsIAccessibleEvent constants are out of sync to kEventTypeNames");
1027 if (aEventType >= ArrayLength(kEventTypeNames)) {
1028 aString.AssignLiteral("unknown");
1029 return;
1032 aString = nsDependentCString(kEventTypeNames[aEventType]);
1035 void nsAccessibilityService::GetStringRelationType(uint32_t aRelationType,
1036 nsAString& aString) {
1037 NS_ENSURE_TRUE_VOID(aRelationType <=
1038 static_cast<uint32_t>(RelationType::LAST));
1040 #define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \
1041 case RelationType::geckoType: \
1042 aString.AssignLiteral(geckoTypeName); \
1043 return;
1045 RelationType relationType = static_cast<RelationType>(aRelationType);
1046 switch (relationType) {
1047 #include "RelationTypeMap.h"
1048 default:
1049 aString.AssignLiteral("unknown");
1050 return;
1053 #undef RELATIONTYPE
1056 ////////////////////////////////////////////////////////////////////////////////
1057 // nsAccessibilityService public
1059 LocalAccessible* nsAccessibilityService::CreateAccessible(
1060 nsINode* aNode, LocalAccessible* aContext, bool* aIsSubtreeHidden) {
1061 MOZ_ASSERT(aContext, "No context provided");
1062 MOZ_ASSERT(aNode, "No node to create an accessible for");
1063 MOZ_ASSERT(gConsumers, "No creation after shutdown");
1065 if (aIsSubtreeHidden) *aIsSubtreeHidden = false;
1067 DocAccessible* document = aContext->Document();
1068 MOZ_ASSERT(!document->GetAccessible(aNode),
1069 "We already have an accessible for this node.");
1071 if (aNode->IsDocument()) {
1072 // If it's document node then ask accessible document loader for
1073 // document accessible, otherwise return null.
1074 return GetDocAccessible(aNode->AsDocument());
1077 // We have a content node.
1078 if (!aNode->GetComposedDoc()) {
1079 NS_WARNING("Creating accessible for node with no document");
1080 return nullptr;
1083 if (aNode->OwnerDoc() != document->DocumentNode()) {
1084 NS_ERROR("Creating accessible for wrong document");
1085 return nullptr;
1088 if (!aNode->IsContent()) return nullptr;
1090 nsIContent* content = aNode->AsContent();
1091 if (aria::HasDefinedARIAHidden(content)) {
1092 if (aIsSubtreeHidden) {
1093 *aIsSubtreeHidden = true;
1095 return nullptr;
1098 // Check frame and its visibility.
1099 nsIFrame* frame = content->GetPrimaryFrame();
1100 if (frame) {
1101 // If invisible or inert, we don't create an accessible, but we don't mark
1102 // it with aIsSubtreeHidden = true, since visibility: hidden frame allows
1103 // visible elements in subtree, and inert elements allow non-inert
1104 // elements.
1105 if (!frame->StyleVisibility()->IsVisible() || frame->StyleUI()->IsInert()) {
1106 return nullptr;
1108 } else if (nsCoreUtils::CanCreateAccessibleWithoutFrame(content)) {
1109 // display:contents element doesn't have a frame, but retains the
1110 // semantics. All its children are unaffected.
1111 const nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(content->AsElement());
1112 RefPtr<LocalAccessible> newAcc = MaybeCreateSpecificARIAAccessible(
1113 roleMapEntry, aContext, content, document);
1114 const MarkupMapInfo* markupMap = nullptr;
1115 if (!newAcc) {
1116 markupMap = GetMarkupMapInfoFor(content);
1117 if (markupMap && markupMap->new_func) {
1118 newAcc = markupMap->new_func(content->AsElement(), aContext);
1122 // Check whether this element has an ARIA role or attribute that requires
1123 // us to create an Accessible.
1124 const bool hasNonPresentationalARIARole =
1125 roleMapEntry && !roleMapEntry->Is(nsGkAtoms::presentation) &&
1126 !roleMapEntry->Is(nsGkAtoms::none);
1127 if (!newAcc && (hasNonPresentationalARIARole ||
1128 AttributesMustBeAccessible(content, document))) {
1129 newAcc = new HyperTextAccessible(content, document);
1132 // If there's still no Accessible but we do have an entry in the markup
1133 // map for this non-presentational element, create a generic
1134 // HyperTextAccessible.
1135 if (!newAcc && markupMap &&
1136 (!roleMapEntry || hasNonPresentationalARIARole)) {
1137 newAcc = new HyperTextAccessible(content, document);
1140 if (newAcc) {
1141 document->BindToDocument(newAcc, roleMapEntry);
1143 return newAcc;
1144 } else {
1145 if (aIsSubtreeHidden) {
1146 *aIsSubtreeHidden = true;
1148 return nullptr;
1151 if (frame->IsHiddenByContentVisibilityOnAnyAncestor(
1152 nsIFrame::IncludeContentVisibility::Hidden)) {
1153 if (aIsSubtreeHidden) {
1154 *aIsSubtreeHidden = true;
1156 return nullptr;
1159 if (nsMenuPopupFrame* popupFrame = do_QueryFrame(frame)) {
1160 // Hidden tooltips and panels don't create accessibles in the whole subtree.
1161 // Showing them gets handled by RootAccessible::ProcessDOMEvent.
1162 if (content->IsAnyOfXULElements(nsGkAtoms::tooltip, nsGkAtoms::panel)) {
1163 nsPopupState popupState = popupFrame->PopupState();
1164 if (popupState == ePopupHiding || popupState == ePopupInvisible ||
1165 popupState == ePopupClosed) {
1166 if (aIsSubtreeHidden) {
1167 *aIsSubtreeHidden = true;
1169 return nullptr;
1174 if (frame->GetContent() != content) {
1175 // Not the main content for this frame. This happens because <area>
1176 // elements return the image frame as their primary frame. The main content
1177 // for the image frame is the image content. If the frame is not an image
1178 // frame or the node is not an area element then null is returned.
1179 // This setup will change when bug 135040 is fixed. Make sure we don't
1180 // create area accessible here. Hopefully assertion below will handle that.
1182 #ifdef DEBUG
1183 nsImageFrame* imageFrame = do_QueryFrame(frame);
1184 NS_ASSERTION(imageFrame && content->IsHTMLElement(nsGkAtoms::area),
1185 "Unknown case of not main content for the frame!");
1186 #endif
1187 return nullptr;
1190 #ifdef DEBUG
1191 nsImageFrame* imageFrame = do_QueryFrame(frame);
1192 NS_ASSERTION(!imageFrame || !content->IsHTMLElement(nsGkAtoms::area),
1193 "Image map manages the area accessible creation!");
1194 #endif
1196 // Attempt to create an accessible based on what we know.
1197 RefPtr<LocalAccessible> newAcc;
1199 // Create accessible for visible text frames.
1200 if (content->IsText()) {
1201 nsIFrame::RenderedText text = frame->GetRenderedText(
1202 0, UINT32_MAX, nsIFrame::TextOffsetType::OffsetsInContentText,
1203 nsIFrame::TrailingWhitespace::DontTrim);
1204 // Ignore not rendered text nodes and whitespace text nodes between table
1205 // cells.
1206 if (text.mString.IsEmpty() ||
1207 (aContext->IsTableRow() &&
1208 nsCoreUtils::IsWhitespaceString(text.mString))) {
1209 if (aIsSubtreeHidden) *aIsSubtreeHidden = true;
1211 return nullptr;
1214 newAcc = CreateAccessibleByFrameType(frame, content, aContext);
1215 MOZ_ASSERT(newAcc, "Accessible not created for text node!");
1216 document->BindToDocument(newAcc, nullptr);
1217 newAcc->AsTextLeaf()->SetText(text.mString);
1218 return newAcc;
1221 if (content->IsHTMLElement(nsGkAtoms::map)) {
1222 // Create hyper text accessible for HTML map if it is used to group links
1223 // (see http://www.w3.org/TR/WCAG10-HTML-TECHS/#group-bypass). If the HTML
1224 // map rect is empty then it is used for links grouping. Otherwise it should
1225 // be used in conjunction with HTML image element and in this case we don't
1226 // create any accessible for it and don't walk into it. The accessibles for
1227 // HTML area (HTMLAreaAccessible) the map contains are attached as
1228 // children of the appropriate accessible for HTML image
1229 // (ImageAccessible).
1230 if (nsLayoutUtils::GetAllInFlowRectsUnion(frame, frame->GetParent())
1231 .IsEmpty()) {
1232 if (aIsSubtreeHidden) *aIsSubtreeHidden = true;
1234 return nullptr;
1237 newAcc = new HyperTextAccessible(content, document);
1238 document->BindToDocument(newAcc, aria::GetRoleMap(content->AsElement()));
1239 return newAcc;
1242 const nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(content->AsElement());
1244 if (roleMapEntry && (roleMapEntry->Is(nsGkAtoms::presentation) ||
1245 roleMapEntry->Is(nsGkAtoms::none))) {
1246 if (MustBeAccessible(content, document)) {
1247 // If the element is focusable, a global ARIA attribute is applied to it
1248 // or it is referenced by an ARIA relationship, then treat
1249 // role="presentation" on the element as if the role is not there.
1250 roleMapEntry = nullptr;
1251 } else if (MustBeGenericAccessible(content, document)) {
1252 // Clear roleMapEntry so that we use the generic role specified below.
1253 // Otherwise, we'd expose roles::NOTHING as specified for presentation in
1254 // ARIAMap.
1255 roleMapEntry = nullptr;
1256 newAcc = new EnumRoleHyperTextAccessible<roles::TEXT_CONTAINER>(content,
1257 document);
1258 } else {
1259 return nullptr;
1263 // We should always use OuterDocAccessible for OuterDocs, even if there's a
1264 // specific ARIA class we would otherwise use.
1265 if (!newAcc && frame->AccessibleType() != eOuterDocType) {
1266 newAcc = MaybeCreateSpecificARIAAccessible(roleMapEntry, aContext, content,
1267 document);
1270 if (!newAcc && content->IsHTMLElement()) { // HTML accessibles
1271 // Prefer to use markup to decide if and what kind of accessible to
1272 // create,
1273 const MarkupMapInfo* markupMap =
1274 mHTMLMarkupMap.Get(content->NodeInfo()->NameAtom());
1275 if (markupMap && markupMap->new_func) {
1276 newAcc = markupMap->new_func(content->AsElement(), aContext);
1279 if (!newAcc) { // try by frame accessible type.
1280 newAcc = CreateAccessibleByFrameType(frame, content, aContext);
1283 // If table has strong ARIA role then all table descendants shouldn't
1284 // expose their native roles.
1285 if (!roleMapEntry && newAcc && aContext->HasStrongARIARole()) {
1286 if (frame->AccessibleType() == eHTMLTableRowType) {
1287 const nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
1288 if (!contextRoleMap->IsOfType(eTable)) {
1289 roleMapEntry = &aria::gEmptyRoleMap;
1292 } else if (frame->AccessibleType() == eHTMLTableCellType &&
1293 aContext->ARIARoleMap() == &aria::gEmptyRoleMap) {
1294 roleMapEntry = &aria::gEmptyRoleMap;
1296 } else if (content->IsAnyOfHTMLElements(nsGkAtoms::dt, nsGkAtoms::li,
1297 nsGkAtoms::dd) ||
1298 frame->AccessibleType() == eHTMLLiType) {
1299 const nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
1300 if (!contextRoleMap->IsOfType(eList)) {
1301 roleMapEntry = &aria::gEmptyRoleMap;
1307 // XUL accessibles.
1308 if (!newAcc && content->IsXULElement()) {
1309 if (content->IsXULElement(nsGkAtoms::panel)) {
1310 // We filter here instead of in the XUL map because
1311 // if we filter there and return null, we still end up
1312 // creating a generic accessible at the end of this function.
1313 // Doing the filtering here ensures we never create accessibles
1314 // for panels whose popups aren't visible.
1315 nsMenuPopupFrame* popupFrame = do_QueryFrame(frame);
1316 if (!popupFrame) {
1317 return nullptr;
1320 nsPopupState popupState = popupFrame->PopupState();
1321 if (popupState == ePopupHiding || popupState == ePopupInvisible ||
1322 popupState == ePopupClosed) {
1323 return nullptr;
1327 // Prefer to use XUL to decide if and what kind of accessible to create.
1328 const XULMarkupMapInfo* xulMap =
1329 mXULMarkupMap.Get(content->NodeInfo()->NameAtom());
1330 if (xulMap && xulMap->new_func) {
1331 newAcc = xulMap->new_func(content->AsElement(), aContext);
1334 // Any XUL/flex box can be used as tabpanel, make sure we create a proper
1335 // accessible for it.
1336 if (!newAcc && aContext->IsXULTabpanels() &&
1337 content->GetParent() == aContext->GetContent()) {
1338 LayoutFrameType frameType = frame->Type();
1339 // FIXME(emilio): Why only these frame types?
1340 if (frameType == LayoutFrameType::FlexContainer ||
1341 frameType == LayoutFrameType::Scroll) {
1342 newAcc = new XULTabpanelAccessible(content, document);
1347 if (!newAcc) {
1348 if (content->IsSVGElement()) {
1349 if (content->IsSVGGeometryElement() ||
1350 content->IsSVGElement(nsGkAtoms::image)) {
1351 // Shape elements: rect, circle, ellipse, line, path, polygon,
1352 // and polyline. 'use' and 'text' graphic elements require
1353 // special support.
1354 if (MustSVGElementBeAccessible(content, document)) {
1355 newAcc = new EnumRoleAccessible<roles::GRAPHIC>(content, document);
1357 } else if (content->IsSVGElement(nsGkAtoms::text)) {
1358 newAcc = new HyperTextAccessible(content->AsElement(), document);
1359 } else if (content->IsSVGElement(nsGkAtoms::svg)) {
1360 // An <svg> element could contain <foreignObject>, which contains HTML
1361 // but does not normally create its own Accessible. This means that the
1362 // <svg> Accessible could have TextLeafAccessible children, so it must
1363 // be a HyperTextAccessible.
1364 newAcc =
1365 new EnumRoleHyperTextAccessible<roles::DIAGRAM>(content, document);
1366 } else if (content->IsSVGElement(nsGkAtoms::g) &&
1367 MustSVGElementBeAccessible(content, document)) {
1368 // <g> can also contain <foreignObject>.
1369 newAcc =
1370 new EnumRoleHyperTextAccessible<roles::GROUPING>(content, document);
1371 } else if (content->IsSVGElement(nsGkAtoms::a)) {
1372 newAcc = new HTMLLinkAccessible(content, document);
1375 } else if (content->IsMathMLElement()) {
1376 const MarkupMapInfo* markupMap =
1377 mMathMLMarkupMap.Get(content->NodeInfo()->NameAtom());
1378 if (markupMap && markupMap->new_func) {
1379 newAcc = markupMap->new_func(content->AsElement(), aContext);
1382 // Fall back to text when encountering Content MathML.
1383 if (!newAcc && !content->IsAnyOfMathMLElements(
1384 nsGkAtoms::annotation_, nsGkAtoms::annotation_xml_,
1385 nsGkAtoms::mpadded_, nsGkAtoms::mphantom_,
1386 nsGkAtoms::maligngroup_, nsGkAtoms::malignmark_,
1387 nsGkAtoms::mspace_, nsGkAtoms::semantics_)) {
1388 newAcc = new HyperTextAccessible(content, document);
1390 } else if (content->IsGeneratedContentContainerForMarker()) {
1391 if (aContext->IsHTMLListItem()) {
1392 newAcc = new HTMLListBulletAccessible(content, document);
1394 if (aIsSubtreeHidden) {
1395 *aIsSubtreeHidden = true;
1400 // If no accessible, see if we need to create a generic accessible because
1401 // of some property that makes this object interesting
1402 // We don't do this for <body>, <html>, <window>, <dialog> etc. which
1403 // correspond to the doc accessible and will be created in any case
1404 if (!newAcc && !content->IsHTMLElement(nsGkAtoms::body) &&
1405 content->GetParent() &&
1406 (roleMapEntry || MustBeAccessible(content, document) ||
1407 (content->IsHTMLElement() && nsCoreUtils::HasClickListener(content)))) {
1408 // This content is focusable or has an interesting dynamic content
1409 // accessibility property. If it's interesting we need it in the
1410 // accessibility hierarchy so that events or other accessibles can point to
1411 // it, or so that it can hold a state, etc.
1412 if (content->IsHTMLElement() || content->IsMathMLElement() ||
1413 content->IsSVGElement(nsGkAtoms::foreignObject)) {
1414 // Interesting container which may have selectable text and/or embedded
1415 // objects.
1416 newAcc = new HyperTextAccessible(content, document);
1417 } else { // XUL, other SVG, etc.
1418 // Interesting generic non-HTML container
1419 newAcc = new AccessibleWrap(content, document);
1421 } else if (!newAcc && MustBeGenericAccessible(content, document)) {
1422 newAcc = new EnumRoleHyperTextAccessible<roles::TEXT_CONTAINER>(content,
1423 document);
1426 if (newAcc) {
1427 document->BindToDocument(newAcc, roleMapEntry);
1429 return newAcc;
1432 #if defined(ANDROID)
1433 # include "mozilla/Monitor.h"
1434 # include "mozilla/Maybe.h"
1436 static Maybe<Monitor> sAndroidMonitor;
1438 mozilla::Monitor& nsAccessibilityService::GetAndroidMonitor() {
1439 if (!sAndroidMonitor.isSome()) {
1440 sAndroidMonitor.emplace("nsAccessibility::sAndroidMonitor");
1443 return *sAndroidMonitor;
1445 #endif
1447 ////////////////////////////////////////////////////////////////////////////////
1448 // nsAccessibilityService private
1450 bool nsAccessibilityService::Init() {
1451 AUTO_PROFILER_MARKER_TEXT("nsAccessibilityService::Init", A11Y, {}, ""_ns);
1452 // DO NOT ADD CODE ABOVE HERE: THIS CODE IS MEASURING TIMINGS.
1454 // Initialize accessible document manager.
1455 if (!DocManager::Init()) return false;
1457 // Add observers.
1458 nsCOMPtr<nsIObserverService> observerService =
1459 mozilla::services::GetObserverService();
1460 if (!observerService) return false;
1462 observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
1464 #if defined(XP_WIN)
1465 // This information needs to be initialized before the observer fires.
1466 if (XRE_IsParentProcess()) {
1467 Compatibility::Init();
1469 #endif // defined(XP_WIN)
1471 // Subscribe to EventListenerService.
1472 nsCOMPtr<nsIEventListenerService> eventListenerService =
1473 do_GetService("@mozilla.org/eventlistenerservice;1");
1474 if (!eventListenerService) return false;
1476 eventListenerService->AddListenerChangeListener(this);
1478 for (uint32_t i = 0; i < ArrayLength(sHTMLMarkupMapList); i++) {
1479 mHTMLMarkupMap.InsertOrUpdate(sHTMLMarkupMapList[i].tag,
1480 &sHTMLMarkupMapList[i]);
1482 for (const auto& info : sMathMLMarkupMapList) {
1483 mMathMLMarkupMap.InsertOrUpdate(info.tag, &info);
1486 for (uint32_t i = 0; i < ArrayLength(sXULMarkupMapList); i++) {
1487 mXULMarkupMap.InsertOrUpdate(sXULMarkupMapList[i].tag,
1488 &sXULMarkupMapList[i]);
1491 #ifdef A11Y_LOG
1492 logging::CheckEnv();
1493 #endif
1495 gAccessibilityService = this;
1496 NS_ADDREF(gAccessibilityService); // will release in Shutdown()
1498 if (XRE_IsParentProcess()) {
1499 gApplicationAccessible = new ApplicationAccessibleWrap();
1500 } else {
1501 gApplicationAccessible = new ApplicationAccessible();
1504 NS_ADDREF(gApplicationAccessible); // will release in Shutdown()
1505 gApplicationAccessible->Init();
1507 CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::Accessibility,
1508 "Active"_ns);
1510 // Now its safe to start platform accessibility.
1511 if (XRE_IsParentProcess()) PlatformInit();
1513 statistics::A11yInitialized();
1515 static const char16_t kInitIndicator[] = {'1', 0};
1516 observerService->NotifyObservers(nullptr, "a11y-init-or-shutdown",
1517 kInitIndicator);
1519 return true;
1522 void nsAccessibilityService::Shutdown() {
1523 // Application is going to be closed, shutdown accessibility and mark
1524 // accessibility service as shutdown to prevent calls of its methods.
1525 // Don't null accessibility service static member at this point to be safe
1526 // if someone will try to operate with it.
1528 MOZ_ASSERT(gConsumers, "Accessibility was shutdown already");
1529 UnsetConsumers(eXPCOM | eMainProcess | ePlatformAPI);
1531 // Remove observers.
1532 nsCOMPtr<nsIObserverService> observerService =
1533 mozilla::services::GetObserverService();
1534 if (observerService) {
1535 observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
1538 // Stop accessible document loader.
1539 DocManager::Shutdown();
1541 SelectionManager::Shutdown();
1543 if (XRE_IsParentProcess()) PlatformShutdown();
1545 gApplicationAccessible->Shutdown();
1546 NS_RELEASE(gApplicationAccessible);
1547 gApplicationAccessible = nullptr;
1549 NS_IF_RELEASE(gXPCApplicationAccessible);
1550 gXPCApplicationAccessible = nullptr;
1552 #if defined(ANDROID)
1553 // Don't allow the service to shut down while an a11y request is being handled
1554 // in the UI thread, as the request may depend on state from the service.
1555 MonitorAutoLock mal(GetAndroidMonitor());
1556 #endif
1557 NS_RELEASE(gAccessibilityService);
1558 gAccessibilityService = nullptr;
1560 if (observerService) {
1561 static const char16_t kShutdownIndicator[] = {'0', 0};
1562 observerService->NotifyObservers(nullptr, "a11y-init-or-shutdown",
1563 kShutdownIndicator);
1567 already_AddRefed<LocalAccessible>
1568 nsAccessibilityService::CreateAccessibleByFrameType(nsIFrame* aFrame,
1569 nsIContent* aContent,
1570 LocalAccessible* aContext) {
1571 DocAccessible* document = aContext->Document();
1573 RefPtr<LocalAccessible> newAcc;
1574 switch (aFrame->AccessibleType()) {
1575 case eNoType:
1576 return nullptr;
1577 case eHTMLBRType:
1578 newAcc = new HTMLBRAccessible(aContent, document);
1579 break;
1580 case eHTMLButtonType:
1581 newAcc = new HTMLButtonAccessible(aContent, document);
1582 break;
1583 case eHTMLCanvasType:
1584 newAcc = new HTMLCanvasAccessible(aContent, document);
1585 break;
1586 case eHTMLCaptionType:
1587 if (aContext->IsTable() &&
1588 aContext->GetContent() == aContent->GetParent()) {
1589 newAcc = new HTMLCaptionAccessible(aContent, document);
1591 break;
1592 case eHTMLCheckboxType:
1593 newAcc = new CheckboxAccessible(aContent, document);
1594 break;
1595 case eHTMLComboboxType:
1596 newAcc = new HTMLComboboxAccessible(aContent, document);
1597 break;
1598 case eHTMLFileInputType:
1599 newAcc = new HTMLFileInputAccessible(aContent, document);
1600 break;
1601 case eHTMLGroupboxType:
1602 newAcc = new HTMLGroupboxAccessible(aContent, document);
1603 break;
1604 case eHTMLHRType:
1605 newAcc = new HTMLHRAccessible(aContent, document);
1606 break;
1607 case eHTMLImageMapType:
1608 newAcc = new HTMLImageMapAccessible(aContent, document);
1609 break;
1610 case eHTMLLiType:
1611 if (aContext->IsList() &&
1612 aContext->GetContent() == aContent->GetParent()) {
1613 newAcc = new HTMLLIAccessible(aContent, document);
1614 } else {
1615 // Otherwise create a generic text accessible to avoid text jamming.
1616 newAcc = new HyperTextAccessible(aContent, document);
1618 break;
1619 case eHTMLSelectListType:
1620 newAcc = new HTMLSelectListAccessible(aContent, document);
1621 break;
1622 case eHTMLMediaType:
1623 newAcc = new EnumRoleAccessible<roles::GROUPING>(aContent, document);
1624 break;
1625 case eHTMLRadioButtonType:
1626 newAcc = new HTMLRadioButtonAccessible(aContent, document);
1627 break;
1628 case eHTMLRangeType:
1629 newAcc = new HTMLRangeAccessible(aContent, document);
1630 break;
1631 case eHTMLSpinnerType:
1632 newAcc = new HTMLSpinnerAccessible(aContent, document);
1633 break;
1634 case eHTMLTableType:
1635 case eHTMLTableCellType:
1636 // We handle markup and ARIA tables elsewhere. If we reach here, this is
1637 // a CSS table part. Just create a generic text container.
1638 newAcc = new HyperTextAccessible(aContent, document);
1639 break;
1640 case eHTMLTableRowType:
1641 // This is a CSS table row. Don't expose it at all.
1642 break;
1643 case eHTMLTextFieldType:
1644 newAcc = new HTMLTextFieldAccessible(aContent, document);
1645 break;
1646 case eHyperTextType: {
1647 if (aContext->IsTable() || aContext->IsTableRow()) {
1648 // This is some generic hyperText, for example a block frame element
1649 // inserted between a table and table row. Treat it as presentational.
1650 return nullptr;
1653 if (!aContent->IsAnyOfHTMLElements(nsGkAtoms::dt, nsGkAtoms::dd,
1654 nsGkAtoms::div, nsGkAtoms::thead,
1655 nsGkAtoms::tfoot, nsGkAtoms::tbody)) {
1656 newAcc = new HyperTextAccessible(aContent, document);
1658 break;
1660 case eImageType:
1661 if (aContent->IsElement() &&
1662 ShouldCreateImgAccessible(aContent->AsElement(), document)) {
1663 newAcc = new ImageAccessible(aContent, document);
1665 break;
1666 case eOuterDocType:
1667 newAcc = new OuterDocAccessible(aContent, document);
1668 break;
1669 case eTextLeafType:
1670 newAcc = new TextLeafAccessible(aContent, document);
1671 break;
1672 default:
1673 MOZ_ASSERT(false);
1674 break;
1677 return newAcc.forget();
1680 void nsAccessibilityService::MarkupAttributes(
1681 Accessible* aAcc, AccAttributes* aAttributes) const {
1682 const mozilla::a11y::MarkupMapInfo* markupMap = GetMarkupMapInfoFor(aAcc);
1683 if (!markupMap) return;
1685 dom::Element* el = aAcc->IsLocal() ? aAcc->AsLocal()->Elm() : nullptr;
1686 for (uint32_t i = 0; i < ArrayLength(markupMap->attrs); i++) {
1687 const MarkupAttrInfo* info = markupMap->attrs + i;
1688 if (!info->name) break;
1690 if (info->DOMAttrName) {
1691 if (!el) {
1692 // XXX Expose DOM attributes for cached RemoteAccessibles.
1693 continue;
1695 if (info->DOMAttrValue) {
1696 if (el->AttrValueIs(kNameSpaceID_None, info->DOMAttrName,
1697 info->DOMAttrValue, eCaseMatters)) {
1698 aAttributes->SetAttribute(info->name, info->DOMAttrValue);
1700 continue;
1703 nsString value;
1704 el->GetAttr(info->DOMAttrName, value);
1706 if (!value.IsEmpty()) {
1707 aAttributes->SetAttribute(info->name, std::move(value));
1710 continue;
1713 aAttributes->SetAttribute(info->name, info->value);
1717 LocalAccessible* nsAccessibilityService::AddNativeRootAccessible(
1718 void* aAtkAccessible) {
1719 #ifdef MOZ_ACCESSIBILITY_ATK
1720 ApplicationAccessible* applicationAcc = ApplicationAcc();
1721 if (!applicationAcc) return nullptr;
1723 GtkWindowAccessible* nativeWnd =
1724 new GtkWindowAccessible(static_cast<AtkObject*>(aAtkAccessible));
1726 if (applicationAcc->AppendChild(nativeWnd)) return nativeWnd;
1727 #endif
1729 return nullptr;
1732 void nsAccessibilityService::RemoveNativeRootAccessible(
1733 LocalAccessible* aAccessible) {
1734 #ifdef MOZ_ACCESSIBILITY_ATK
1735 ApplicationAccessible* applicationAcc = ApplicationAcc();
1737 if (applicationAcc) applicationAcc->RemoveChild(aAccessible);
1738 #endif
1741 bool nsAccessibilityService::HasAccessible(nsINode* aDOMNode) {
1742 if (!aDOMNode) return false;
1744 Document* document = aDOMNode->OwnerDoc();
1745 if (!document) return false;
1747 DocAccessible* docAcc = GetExistingDocAccessible(aDOMNode->OwnerDoc());
1748 if (!docAcc) return false;
1750 return docAcc->HasAccessible(aDOMNode);
1753 ////////////////////////////////////////////////////////////////////////////////
1754 // nsAccessibilityService private (DON'T put methods here)
1756 void nsAccessibilityService::SetConsumers(uint32_t aConsumers, bool aNotify) {
1757 if (gConsumers & aConsumers) {
1758 return;
1761 gConsumers |= aConsumers;
1762 if (aNotify) {
1763 NotifyOfConsumersChange();
1767 void nsAccessibilityService::UnsetConsumers(uint32_t aConsumers) {
1768 if (!(gConsumers & aConsumers)) {
1769 return;
1772 gConsumers &= ~aConsumers;
1773 NotifyOfConsumersChange();
1776 void nsAccessibilityService::GetConsumers(nsAString& aString) {
1777 const char16_t* kJSONFmt =
1778 u"{ \"XPCOM\": %s, \"MainProcess\": %s, \"PlatformAPI\": %s }";
1779 nsString json;
1780 nsTextFormatter::ssprintf(json, kJSONFmt,
1781 gConsumers & eXPCOM ? "true" : "false",
1782 gConsumers & eMainProcess ? "true" : "false",
1783 gConsumers & ePlatformAPI ? "true" : "false");
1784 aString.Assign(json);
1787 void nsAccessibilityService::NotifyOfConsumersChange() {
1788 nsCOMPtr<nsIObserverService> observerService =
1789 mozilla::services::GetObserverService();
1791 if (!observerService) {
1792 return;
1795 nsAutoString consumers;
1796 GetConsumers(consumers);
1797 observerService->NotifyObservers(nullptr, "a11y-consumers-changed",
1798 consumers.get());
1801 const mozilla::a11y::MarkupMapInfo* nsAccessibilityService::GetMarkupMapInfoFor(
1802 Accessible* aAcc) const {
1803 if (LocalAccessible* localAcc = aAcc->AsLocal()) {
1804 return localAcc->HasOwnContent()
1805 ? GetMarkupMapInfoFor(localAcc->GetContent())
1806 : nullptr;
1808 // XXX For now, we assume all RemoteAccessibles are HTML elements. This
1809 // isn't strictly correct, but as far as current callers are concerned,
1810 // this doesn't matter. If that changes in future, we could expose the
1811 // element type via AccGenericType.
1812 return mHTMLMarkupMap.Get(aAcc->TagName());
1815 nsAccessibilityService* GetOrCreateAccService(uint32_t aNewConsumer) {
1816 // Do not initialize accessibility if it is force disabled.
1817 if (PlatformDisabledState() == ePlatformIsDisabled) {
1818 return nullptr;
1821 if (!nsAccessibilityService::gAccessibilityService) {
1822 RefPtr<nsAccessibilityService> service = new nsAccessibilityService();
1823 if (!service->Init()) {
1824 service->Shutdown();
1825 return nullptr;
1829 MOZ_ASSERT(nsAccessibilityService::gAccessibilityService,
1830 "LocalAccessible service is not initialized.");
1831 nsAccessibilityService::gAccessibilityService->SetConsumers(aNewConsumer);
1832 return nsAccessibilityService::gAccessibilityService;
1835 void MaybeShutdownAccService(uint32_t aFormerConsumer) {
1836 nsAccessibilityService* accService =
1837 nsAccessibilityService::gAccessibilityService;
1839 if (!accService || nsAccessibilityService::IsShutdown()) {
1840 return;
1843 // Still used by XPCOM
1844 if (nsCoreUtils::AccEventObserversExist() ||
1845 xpcAccessibilityService::IsInUse() || accService->HasXPCDocuments()) {
1846 // In case the XPCOM flag was unset (possibly because of the shutdown
1847 // timer in the xpcAccessibilityService) ensure it is still present. Note:
1848 // this should be fixed when all the consumer logic is taken out as a
1849 // separate class.
1850 accService->SetConsumers(nsAccessibilityService::eXPCOM, false);
1852 if (aFormerConsumer != nsAccessibilityService::eXPCOM) {
1853 // Only unset non-XPCOM consumers.
1854 accService->UnsetConsumers(aFormerConsumer);
1856 return;
1859 if (nsAccessibilityService::gConsumers & ~aFormerConsumer) {
1860 accService->UnsetConsumers(aFormerConsumer);
1861 } else {
1862 accService
1863 ->Shutdown(); // Will unset all nsAccessibilityService::gConsumers
1867 ////////////////////////////////////////////////////////////////////////////////
1868 // Services
1869 ////////////////////////////////////////////////////////////////////////////////
1871 namespace mozilla {
1872 namespace a11y {
1874 FocusManager* FocusMgr() {
1875 return nsAccessibilityService::gAccessibilityService;
1878 SelectionManager* SelectionMgr() {
1879 return nsAccessibilityService::gAccessibilityService;
1882 ApplicationAccessible* ApplicationAcc() {
1883 return nsAccessibilityService::gApplicationAccessible;
1886 xpcAccessibleApplication* XPCApplicationAcc() {
1887 if (!nsAccessibilityService::gXPCApplicationAccessible &&
1888 nsAccessibilityService::gApplicationAccessible) {
1889 nsAccessibilityService::gXPCApplicationAccessible =
1890 new xpcAccessibleApplication(
1891 nsAccessibilityService::gApplicationAccessible);
1892 NS_ADDREF(nsAccessibilityService::gXPCApplicationAccessible);
1895 return nsAccessibilityService::gXPCApplicationAccessible;
1898 EPlatformDisabledState PlatformDisabledState() {
1899 static bool platformDisabledStateCached = false;
1900 if (platformDisabledStateCached) {
1901 return static_cast<EPlatformDisabledState>(sPlatformDisabledState);
1904 platformDisabledStateCached = true;
1905 Preferences::RegisterCallback(PrefChanged, PREF_ACCESSIBILITY_FORCE_DISABLED);
1906 return ReadPlatformDisabledState();
1909 EPlatformDisabledState ReadPlatformDisabledState() {
1910 sPlatformDisabledState =
1911 Preferences::GetInt(PREF_ACCESSIBILITY_FORCE_DISABLED, 0);
1912 if (sPlatformDisabledState < ePlatformIsForceEnabled) {
1913 sPlatformDisabledState = ePlatformIsForceEnabled;
1914 } else if (sPlatformDisabledState > ePlatformIsDisabled) {
1915 sPlatformDisabledState = ePlatformIsDisabled;
1918 return static_cast<EPlatformDisabledState>(sPlatformDisabledState);
1921 void PrefChanged(const char* aPref, void* aClosure) {
1922 if (ReadPlatformDisabledState() == ePlatformIsDisabled) {
1923 // Force shut down accessibility.
1924 nsAccessibilityService* accService =
1925 nsAccessibilityService::gAccessibilityService;
1926 if (accService && !nsAccessibilityService::IsShutdown()) {
1927 accService->Shutdown();
1932 } // namespace a11y
1933 } // namespace mozilla