Bug 1839425: Use AVSampleBufferDisplayLayer for windowed video. r=mstange
[gecko.git] / accessible / base / nsAccessibilityService.cpp
blobceef229378a032c9993cb98634c9a806396f5973
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::PopovertargetMaybeChanged(PresShell* aPresShell,
720 nsIContent* aContent) {
721 DocAccessible* document = GetDocAccessible(aPresShell);
722 if (!document) {
723 return;
725 if (LocalAccessible* acc = document->GetAccessible(aContent)) {
726 RefPtr<AccEvent> expandedChangeEvent =
727 new AccStateChangeEvent(acc, states::EXPANDED);
728 document->FireDelayedEvent(expandedChangeEvent);
732 void nsAccessibilityService::ComboboxOptionMaybeChanged(
733 PresShell* aPresShell, nsIContent* aMutatingNode) {
734 DocAccessible* document = GetDocAccessible(aPresShell);
735 if (!document) {
736 return;
739 for (nsIContent* cur = aMutatingNode; cur; cur = cur->GetParent()) {
740 if (cur->IsHTMLElement(nsGkAtoms::option)) {
741 if (LocalAccessible* accessible = document->GetAccessible(cur)) {
742 document->FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE,
743 accessible);
744 break;
746 if (cur->IsHTMLElement(nsGkAtoms::select)) {
747 break;
753 void nsAccessibilityService::UpdateText(PresShell* aPresShell,
754 nsIContent* aContent) {
755 DocAccessible* document = GetDocAccessible(aPresShell);
756 if (document) document->UpdateText(aContent);
759 void nsAccessibilityService::TreeViewChanged(PresShell* aPresShell,
760 nsIContent* aContent,
761 nsITreeView* aView) {
762 DocAccessible* document = GetDocAccessible(aPresShell);
763 if (document) {
764 LocalAccessible* accessible = document->GetAccessible(aContent);
765 if (accessible) {
766 XULTreeAccessible* treeAcc = accessible->AsXULTree();
767 if (treeAcc) treeAcc->TreeViewChanged(aView);
772 void nsAccessibilityService::RangeValueChanged(PresShell* aPresShell,
773 nsIContent* aContent) {
774 DocAccessible* document = GetDocAccessible(aPresShell);
775 if (document) {
776 LocalAccessible* accessible = document->GetAccessible(aContent);
777 if (accessible) {
778 document->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
779 accessible);
784 void nsAccessibilityService::UpdateImageMap(nsImageFrame* aImageFrame) {
785 PresShell* presShell = aImageFrame->PresShell();
786 DocAccessible* document = GetDocAccessible(presShell);
787 if (document) {
788 LocalAccessible* accessible =
789 document->GetAccessible(aImageFrame->GetContent());
790 if (accessible) {
791 HTMLImageMapAccessible* imageMap = accessible->AsImageMap();
792 if (imageMap) {
793 imageMap->UpdateChildAreas();
794 return;
797 // If image map was initialized after we created an accessible (that'll
798 // be an image accessible) then recreate it.
799 RecreateAccessible(presShell, aImageFrame->GetContent());
804 void nsAccessibilityService::UpdateLabelValue(PresShell* aPresShell,
805 nsIContent* aLabelElm,
806 const nsString& aNewValue) {
807 DocAccessible* document = GetDocAccessible(aPresShell);
808 if (document) {
809 LocalAccessible* accessible = document->GetAccessible(aLabelElm);
810 if (accessible) {
811 XULLabelAccessible* xulLabel = accessible->AsXULLabel();
812 NS_ASSERTION(xulLabel,
813 "UpdateLabelValue was called for wrong accessible!");
814 if (xulLabel) xulLabel->UpdateLabelValue(aNewValue);
819 void nsAccessibilityService::PresShellActivated(PresShell* aPresShell) {
820 DocAccessible* document = aPresShell->GetDocAccessible();
821 if (document) {
822 RootAccessible* rootDocument = document->RootAccessible();
823 NS_ASSERTION(rootDocument, "Entirely broken tree: no root document!");
824 if (rootDocument) rootDocument->DocumentActivated(document);
828 void nsAccessibilityService::RecreateAccessible(PresShell* aPresShell,
829 nsIContent* aContent) {
830 DocAccessible* document = GetDocAccessible(aPresShell);
831 if (document) document->RecreateAccessible(aContent);
834 void nsAccessibilityService::GetStringRole(uint32_t aRole, nsAString& aString) {
835 #define ROLE(geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \
836 msaaRole, ia2Role, androidClass, nameRule) \
837 case roles::geckoRole: \
838 aString.AssignLiteral(stringRole); \
839 return;
841 switch (aRole) {
842 #include "RoleMap.h"
843 default:
844 aString.AssignLiteral("unknown");
845 return;
848 #undef ROLE
851 void nsAccessibilityService::GetStringStates(uint32_t aState,
852 uint32_t aExtraState,
853 nsISupports** aStringStates) {
854 RefPtr<DOMStringList> stringStates =
855 GetStringStates(nsAccUtils::To64State(aState, aExtraState));
857 // unknown state
858 if (!stringStates->Length()) {
859 stringStates->Add(u"unknown"_ns);
862 stringStates.forget(aStringStates);
865 already_AddRefed<DOMStringList> nsAccessibilityService::GetStringStates(
866 uint64_t aStates) const {
867 RefPtr<DOMStringList> stringStates = new DOMStringList();
869 if (aStates & states::UNAVAILABLE) {
870 stringStates->Add(u"unavailable"_ns);
872 if (aStates & states::SELECTED) {
873 stringStates->Add(u"selected"_ns);
875 if (aStates & states::FOCUSED) {
876 stringStates->Add(u"focused"_ns);
878 if (aStates & states::PRESSED) {
879 stringStates->Add(u"pressed"_ns);
881 if (aStates & states::CHECKED) {
882 stringStates->Add(u"checked"_ns);
884 if (aStates & states::MIXED) {
885 stringStates->Add(u"mixed"_ns);
887 if (aStates & states::READONLY) {
888 stringStates->Add(u"readonly"_ns);
890 if (aStates & states::HOTTRACKED) {
891 stringStates->Add(u"hottracked"_ns);
893 if (aStates & states::DEFAULT) {
894 stringStates->Add(u"default"_ns);
896 if (aStates & states::EXPANDED) {
897 stringStates->Add(u"expanded"_ns);
899 if (aStates & states::COLLAPSED) {
900 stringStates->Add(u"collapsed"_ns);
902 if (aStates & states::BUSY) {
903 stringStates->Add(u"busy"_ns);
905 if (aStates & states::FLOATING) {
906 stringStates->Add(u"floating"_ns);
908 if (aStates & states::ANIMATED) {
909 stringStates->Add(u"animated"_ns);
911 if (aStates & states::INVISIBLE) {
912 stringStates->Add(u"invisible"_ns);
914 if (aStates & states::OFFSCREEN) {
915 stringStates->Add(u"offscreen"_ns);
917 if (aStates & states::SIZEABLE) {
918 stringStates->Add(u"sizeable"_ns);
920 if (aStates & states::MOVEABLE) {
921 stringStates->Add(u"moveable"_ns);
923 if (aStates & states::SELFVOICING) {
924 stringStates->Add(u"selfvoicing"_ns);
926 if (aStates & states::FOCUSABLE) {
927 stringStates->Add(u"focusable"_ns);
929 if (aStates & states::SELECTABLE) {
930 stringStates->Add(u"selectable"_ns);
932 if (aStates & states::LINKED) {
933 stringStates->Add(u"linked"_ns);
935 if (aStates & states::TRAVERSED) {
936 stringStates->Add(u"traversed"_ns);
938 if (aStates & states::MULTISELECTABLE) {
939 stringStates->Add(u"multiselectable"_ns);
941 if (aStates & states::EXTSELECTABLE) {
942 stringStates->Add(u"extselectable"_ns);
944 if (aStates & states::PROTECTED) {
945 stringStates->Add(u"protected"_ns);
947 if (aStates & states::HASPOPUP) {
948 stringStates->Add(u"haspopup"_ns);
950 if (aStates & states::REQUIRED) {
951 stringStates->Add(u"required"_ns);
953 if (aStates & states::ALERT) {
954 stringStates->Add(u"alert"_ns);
956 if (aStates & states::INVALID) {
957 stringStates->Add(u"invalid"_ns);
959 if (aStates & states::CHECKABLE) {
960 stringStates->Add(u"checkable"_ns);
962 if (aStates & states::SUPPORTS_AUTOCOMPLETION) {
963 stringStates->Add(u"autocompletion"_ns);
965 if (aStates & states::DEFUNCT) {
966 stringStates->Add(u"defunct"_ns);
968 if (aStates & states::SELECTABLE_TEXT) {
969 stringStates->Add(u"selectable text"_ns);
971 if (aStates & states::EDITABLE) {
972 stringStates->Add(u"editable"_ns);
974 if (aStates & states::ACTIVE) {
975 stringStates->Add(u"active"_ns);
977 if (aStates & states::MODAL) {
978 stringStates->Add(u"modal"_ns);
980 if (aStates & states::MULTI_LINE) {
981 stringStates->Add(u"multi line"_ns);
983 if (aStates & states::HORIZONTAL) {
984 stringStates->Add(u"horizontal"_ns);
986 if (aStates & states::OPAQUE1) {
987 stringStates->Add(u"opaque"_ns);
989 if (aStates & states::SINGLE_LINE) {
990 stringStates->Add(u"single line"_ns);
992 if (aStates & states::TRANSIENT) {
993 stringStates->Add(u"transient"_ns);
995 if (aStates & states::VERTICAL) {
996 stringStates->Add(u"vertical"_ns);
998 if (aStates & states::STALE) {
999 stringStates->Add(u"stale"_ns);
1001 if (aStates & states::ENABLED) {
1002 stringStates->Add(u"enabled"_ns);
1004 if (aStates & states::SENSITIVE) {
1005 stringStates->Add(u"sensitive"_ns);
1007 if (aStates & states::EXPANDABLE) {
1008 stringStates->Add(u"expandable"_ns);
1010 if (aStates & states::PINNED) {
1011 stringStates->Add(u"pinned"_ns);
1013 if (aStates & states::CURRENT) {
1014 stringStates->Add(u"current"_ns);
1017 return stringStates.forget();
1020 void nsAccessibilityService::GetStringEventType(uint32_t aEventType,
1021 nsAString& aString) {
1022 static_assert(
1023 nsIAccessibleEvent::EVENT_LAST_ENTRY == ArrayLength(kEventTypeNames),
1024 "nsIAccessibleEvent constants are out of sync to kEventTypeNames");
1026 if (aEventType >= ArrayLength(kEventTypeNames)) {
1027 aString.AssignLiteral("unknown");
1028 return;
1031 aString.AssignASCII(kEventTypeNames[aEventType]);
1034 void nsAccessibilityService::GetStringEventType(uint32_t aEventType,
1035 nsACString& aString) {
1036 MOZ_ASSERT(
1037 nsIAccessibleEvent::EVENT_LAST_ENTRY == ArrayLength(kEventTypeNames),
1038 "nsIAccessibleEvent constants are out of sync to kEventTypeNames");
1040 if (aEventType >= ArrayLength(kEventTypeNames)) {
1041 aString.AssignLiteral("unknown");
1042 return;
1045 aString = nsDependentCString(kEventTypeNames[aEventType]);
1048 void nsAccessibilityService::GetStringRelationType(uint32_t aRelationType,
1049 nsAString& aString) {
1050 NS_ENSURE_TRUE_VOID(aRelationType <=
1051 static_cast<uint32_t>(RelationType::LAST));
1053 #define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \
1054 case RelationType::geckoType: \
1055 aString.AssignLiteral(geckoTypeName); \
1056 return;
1058 RelationType relationType = static_cast<RelationType>(aRelationType);
1059 switch (relationType) {
1060 #include "RelationTypeMap.h"
1061 default:
1062 aString.AssignLiteral("unknown");
1063 return;
1066 #undef RELATIONTYPE
1069 ////////////////////////////////////////////////////////////////////////////////
1070 // nsAccessibilityService public
1072 LocalAccessible* nsAccessibilityService::CreateAccessible(
1073 nsINode* aNode, LocalAccessible* aContext, bool* aIsSubtreeHidden) {
1074 MOZ_ASSERT(aContext, "No context provided");
1075 MOZ_ASSERT(aNode, "No node to create an accessible for");
1076 MOZ_ASSERT(gConsumers, "No creation after shutdown");
1078 if (aIsSubtreeHidden) *aIsSubtreeHidden = false;
1080 DocAccessible* document = aContext->Document();
1081 MOZ_ASSERT(!document->GetAccessible(aNode),
1082 "We already have an accessible for this node.");
1084 if (aNode->IsDocument()) {
1085 // If it's document node then ask accessible document loader for
1086 // document accessible, otherwise return null.
1087 return GetDocAccessible(aNode->AsDocument());
1090 // We have a content node.
1091 if (!aNode->GetComposedDoc()) {
1092 NS_WARNING("Creating accessible for node with no document");
1093 return nullptr;
1096 if (aNode->OwnerDoc() != document->DocumentNode()) {
1097 NS_ERROR("Creating accessible for wrong document");
1098 return nullptr;
1101 if (!aNode->IsContent()) return nullptr;
1103 nsIContent* content = aNode->AsContent();
1104 if (aria::HasDefinedARIAHidden(content)) {
1105 if (aIsSubtreeHidden) {
1106 *aIsSubtreeHidden = true;
1108 return nullptr;
1111 // Check frame and its visibility.
1112 nsIFrame* frame = content->GetPrimaryFrame();
1113 if (frame) {
1114 // If invisible or inert, we don't create an accessible, but we don't mark
1115 // it with aIsSubtreeHidden = true, since visibility: hidden frame allows
1116 // visible elements in subtree, and inert elements allow non-inert
1117 // elements.
1118 if (!frame->StyleVisibility()->IsVisible() || frame->StyleUI()->IsInert()) {
1119 return nullptr;
1121 } else if (nsCoreUtils::CanCreateAccessibleWithoutFrame(content)) {
1122 // display:contents element doesn't have a frame, but retains the
1123 // semantics. All its children are unaffected.
1124 const nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(content->AsElement());
1125 RefPtr<LocalAccessible> newAcc = MaybeCreateSpecificARIAAccessible(
1126 roleMapEntry, aContext, content, document);
1127 const MarkupMapInfo* markupMap = nullptr;
1128 if (!newAcc) {
1129 markupMap = GetMarkupMapInfoFor(content);
1130 if (markupMap && markupMap->new_func) {
1131 newAcc = markupMap->new_func(content->AsElement(), aContext);
1135 // Check whether this element has an ARIA role or attribute that requires
1136 // us to create an Accessible.
1137 const bool hasNonPresentationalARIARole =
1138 roleMapEntry && !roleMapEntry->Is(nsGkAtoms::presentation) &&
1139 !roleMapEntry->Is(nsGkAtoms::none);
1140 if (!newAcc && (hasNonPresentationalARIARole ||
1141 AttributesMustBeAccessible(content, document))) {
1142 newAcc = new HyperTextAccessible(content, document);
1145 // If there's still no Accessible but we do have an entry in the markup
1146 // map for this non-presentational element, create a generic
1147 // HyperTextAccessible.
1148 if (!newAcc && markupMap &&
1149 (!roleMapEntry || hasNonPresentationalARIARole)) {
1150 newAcc = new HyperTextAccessible(content, document);
1153 if (newAcc) {
1154 document->BindToDocument(newAcc, roleMapEntry);
1156 return newAcc;
1157 } else {
1158 if (aIsSubtreeHidden) {
1159 *aIsSubtreeHidden = true;
1161 return nullptr;
1164 if (frame->IsHiddenByContentVisibilityOnAnyAncestor(
1165 nsIFrame::IncludeContentVisibility::Hidden)) {
1166 if (aIsSubtreeHidden) {
1167 *aIsSubtreeHidden = true;
1169 return nullptr;
1172 if (nsMenuPopupFrame* popupFrame = do_QueryFrame(frame)) {
1173 // Hidden tooltips and panels don't create accessibles in the whole subtree.
1174 // Showing them gets handled by RootAccessible::ProcessDOMEvent.
1175 if (content->IsAnyOfXULElements(nsGkAtoms::tooltip, nsGkAtoms::panel)) {
1176 nsPopupState popupState = popupFrame->PopupState();
1177 if (popupState == ePopupHiding || popupState == ePopupInvisible ||
1178 popupState == ePopupClosed) {
1179 if (aIsSubtreeHidden) {
1180 *aIsSubtreeHidden = true;
1182 return nullptr;
1187 if (frame->GetContent() != content) {
1188 // Not the main content for this frame. This happens because <area>
1189 // elements return the image frame as their primary frame. The main content
1190 // for the image frame is the image content. If the frame is not an image
1191 // frame or the node is not an area element then null is returned.
1192 // This setup will change when bug 135040 is fixed. Make sure we don't
1193 // create area accessible here. Hopefully assertion below will handle that.
1195 #ifdef DEBUG
1196 nsImageFrame* imageFrame = do_QueryFrame(frame);
1197 NS_ASSERTION(imageFrame && content->IsHTMLElement(nsGkAtoms::area),
1198 "Unknown case of not main content for the frame!");
1199 #endif
1200 return nullptr;
1203 #ifdef DEBUG
1204 nsImageFrame* imageFrame = do_QueryFrame(frame);
1205 NS_ASSERTION(!imageFrame || !content->IsHTMLElement(nsGkAtoms::area),
1206 "Image map manages the area accessible creation!");
1207 #endif
1209 // Attempt to create an accessible based on what we know.
1210 RefPtr<LocalAccessible> newAcc;
1212 // Create accessible for visible text frames.
1213 if (content->IsText()) {
1214 nsIFrame::RenderedText text = frame->GetRenderedText(
1215 0, UINT32_MAX, nsIFrame::TextOffsetType::OffsetsInContentText,
1216 nsIFrame::TrailingWhitespace::DontTrim);
1217 // Ignore not rendered text nodes and whitespace text nodes between table
1218 // cells.
1219 if (text.mString.IsEmpty() ||
1220 (aContext->IsTableRow() &&
1221 nsCoreUtils::IsWhitespaceString(text.mString))) {
1222 if (aIsSubtreeHidden) *aIsSubtreeHidden = true;
1224 return nullptr;
1227 newAcc = CreateAccessibleByFrameType(frame, content, aContext);
1228 MOZ_ASSERT(newAcc, "Accessible not created for text node!");
1229 document->BindToDocument(newAcc, nullptr);
1230 newAcc->AsTextLeaf()->SetText(text.mString);
1231 return newAcc;
1234 if (content->IsHTMLElement(nsGkAtoms::map)) {
1235 // Create hyper text accessible for HTML map if it is used to group links
1236 // (see http://www.w3.org/TR/WCAG10-HTML-TECHS/#group-bypass). If the HTML
1237 // map rect is empty then it is used for links grouping. Otherwise it should
1238 // be used in conjunction with HTML image element and in this case we don't
1239 // create any accessible for it and don't walk into it. The accessibles for
1240 // HTML area (HTMLAreaAccessible) the map contains are attached as
1241 // children of the appropriate accessible for HTML image
1242 // (ImageAccessible).
1243 if (nsLayoutUtils::GetAllInFlowRectsUnion(frame, frame->GetParent())
1244 .IsEmpty()) {
1245 if (aIsSubtreeHidden) *aIsSubtreeHidden = true;
1247 return nullptr;
1250 newAcc = new HyperTextAccessible(content, document);
1251 document->BindToDocument(newAcc, aria::GetRoleMap(content->AsElement()));
1252 return newAcc;
1255 const nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(content->AsElement());
1257 if (roleMapEntry && (roleMapEntry->Is(nsGkAtoms::presentation) ||
1258 roleMapEntry->Is(nsGkAtoms::none))) {
1259 if (MustBeAccessible(content, document)) {
1260 // If the element is focusable, a global ARIA attribute is applied to it
1261 // or it is referenced by an ARIA relationship, then treat
1262 // role="presentation" on the element as if the role is not there.
1263 roleMapEntry = nullptr;
1264 } else if (MustBeGenericAccessible(content, document)) {
1265 // Clear roleMapEntry so that we use the generic role specified below.
1266 // Otherwise, we'd expose roles::NOTHING as specified for presentation in
1267 // ARIAMap.
1268 roleMapEntry = nullptr;
1269 newAcc = new EnumRoleHyperTextAccessible<roles::TEXT_CONTAINER>(content,
1270 document);
1271 } else {
1272 return nullptr;
1276 // We should always use OuterDocAccessible for OuterDocs, even if there's a
1277 // specific ARIA class we would otherwise use.
1278 if (!newAcc && frame->AccessibleType() != eOuterDocType) {
1279 newAcc = MaybeCreateSpecificARIAAccessible(roleMapEntry, aContext, content,
1280 document);
1283 if (!newAcc && content->IsHTMLElement()) { // HTML accessibles
1284 // Prefer to use markup to decide if and what kind of accessible to
1285 // create,
1286 const MarkupMapInfo* markupMap =
1287 mHTMLMarkupMap.Get(content->NodeInfo()->NameAtom());
1288 if (markupMap && markupMap->new_func) {
1289 newAcc = markupMap->new_func(content->AsElement(), aContext);
1292 if (!newAcc) { // try by frame accessible type.
1293 newAcc = CreateAccessibleByFrameType(frame, content, aContext);
1296 // If table has strong ARIA role then all table descendants shouldn't
1297 // expose their native roles.
1298 if (!roleMapEntry && newAcc && aContext->HasStrongARIARole()) {
1299 if (frame->AccessibleType() == eHTMLTableRowType) {
1300 const nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
1301 if (!contextRoleMap->IsOfType(eTable)) {
1302 roleMapEntry = &aria::gEmptyRoleMap;
1305 } else if (frame->AccessibleType() == eHTMLTableCellType &&
1306 aContext->ARIARoleMap() == &aria::gEmptyRoleMap) {
1307 roleMapEntry = &aria::gEmptyRoleMap;
1309 } else if (content->IsAnyOfHTMLElements(nsGkAtoms::dt, nsGkAtoms::li,
1310 nsGkAtoms::dd) ||
1311 frame->AccessibleType() == eHTMLLiType) {
1312 const nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
1313 if (!contextRoleMap->IsOfType(eList)) {
1314 roleMapEntry = &aria::gEmptyRoleMap;
1320 // XUL accessibles.
1321 if (!newAcc && content->IsXULElement()) {
1322 if (content->IsXULElement(nsGkAtoms::panel)) {
1323 // We filter here instead of in the XUL map because
1324 // if we filter there and return null, we still end up
1325 // creating a generic accessible at the end of this function.
1326 // Doing the filtering here ensures we never create accessibles
1327 // for panels whose popups aren't visible.
1328 nsMenuPopupFrame* popupFrame = do_QueryFrame(frame);
1329 if (!popupFrame) {
1330 return nullptr;
1333 nsPopupState popupState = popupFrame->PopupState();
1334 if (popupState == ePopupHiding || popupState == ePopupInvisible ||
1335 popupState == ePopupClosed) {
1336 return nullptr;
1340 // Prefer to use XUL to decide if and what kind of accessible to create.
1341 const XULMarkupMapInfo* xulMap =
1342 mXULMarkupMap.Get(content->NodeInfo()->NameAtom());
1343 if (xulMap && xulMap->new_func) {
1344 newAcc = xulMap->new_func(content->AsElement(), aContext);
1347 // Any XUL/flex box can be used as tabpanel, make sure we create a proper
1348 // accessible for it.
1349 if (!newAcc && aContext->IsXULTabpanels() &&
1350 content->GetParent() == aContext->GetContent()) {
1351 LayoutFrameType frameType = frame->Type();
1352 // FIXME(emilio): Why only these frame types?
1353 if (frameType == LayoutFrameType::FlexContainer ||
1354 frameType == LayoutFrameType::Scroll) {
1355 newAcc = new XULTabpanelAccessible(content, document);
1360 if (!newAcc) {
1361 if (content->IsSVGElement()) {
1362 if (content->IsSVGGeometryElement() ||
1363 content->IsSVGElement(nsGkAtoms::image)) {
1364 // Shape elements: rect, circle, ellipse, line, path, polygon,
1365 // and polyline. 'use' and 'text' graphic elements require
1366 // special support.
1367 if (MustSVGElementBeAccessible(content, document)) {
1368 newAcc = new EnumRoleAccessible<roles::GRAPHIC>(content, document);
1370 } else if (content->IsSVGElement(nsGkAtoms::text)) {
1371 newAcc = new HyperTextAccessible(content->AsElement(), document);
1372 } else if (content->IsSVGElement(nsGkAtoms::svg)) {
1373 // An <svg> element could contain <foreignObject>, which contains HTML
1374 // but does not normally create its own Accessible. This means that the
1375 // <svg> Accessible could have TextLeafAccessible children, so it must
1376 // be a HyperTextAccessible.
1377 newAcc =
1378 new EnumRoleHyperTextAccessible<roles::DIAGRAM>(content, document);
1379 } else if (content->IsSVGElement(nsGkAtoms::g) &&
1380 MustSVGElementBeAccessible(content, document)) {
1381 // <g> can also contain <foreignObject>.
1382 newAcc =
1383 new EnumRoleHyperTextAccessible<roles::GROUPING>(content, document);
1384 } else if (content->IsSVGElement(nsGkAtoms::a)) {
1385 newAcc = new HTMLLinkAccessible(content, document);
1388 } else if (content->IsMathMLElement()) {
1389 const MarkupMapInfo* markupMap =
1390 mMathMLMarkupMap.Get(content->NodeInfo()->NameAtom());
1391 if (markupMap && markupMap->new_func) {
1392 newAcc = markupMap->new_func(content->AsElement(), aContext);
1395 // Fall back to text when encountering Content MathML.
1396 if (!newAcc && !content->IsAnyOfMathMLElements(
1397 nsGkAtoms::annotation_, nsGkAtoms::annotation_xml_,
1398 nsGkAtoms::mpadded_, nsGkAtoms::mphantom_,
1399 nsGkAtoms::maligngroup_, nsGkAtoms::malignmark_,
1400 nsGkAtoms::mspace_, nsGkAtoms::semantics_)) {
1401 newAcc = new HyperTextAccessible(content, document);
1403 } else if (content->IsGeneratedContentContainerForMarker()) {
1404 if (aContext->IsHTMLListItem()) {
1405 newAcc = new HTMLListBulletAccessible(content, document);
1407 if (aIsSubtreeHidden) {
1408 *aIsSubtreeHidden = true;
1413 // If no accessible, see if we need to create a generic accessible because
1414 // of some property that makes this object interesting
1415 // We don't do this for <body>, <html>, <window>, <dialog> etc. which
1416 // correspond to the doc accessible and will be created in any case
1417 if (!newAcc && !content->IsHTMLElement(nsGkAtoms::body) &&
1418 content->GetParent() &&
1419 (roleMapEntry || MustBeAccessible(content, document) ||
1420 (content->IsHTMLElement() && nsCoreUtils::HasClickListener(content)))) {
1421 // This content is focusable or has an interesting dynamic content
1422 // accessibility property. If it's interesting we need it in the
1423 // accessibility hierarchy so that events or other accessibles can point to
1424 // it, or so that it can hold a state, etc.
1425 if (content->IsHTMLElement() || content->IsMathMLElement() ||
1426 content->IsSVGElement(nsGkAtoms::foreignObject)) {
1427 // Interesting container which may have selectable text and/or embedded
1428 // objects.
1429 newAcc = new HyperTextAccessible(content, document);
1430 } else { // XUL, other SVG, etc.
1431 // Interesting generic non-HTML container
1432 newAcc = new AccessibleWrap(content, document);
1434 } else if (!newAcc && MustBeGenericAccessible(content, document)) {
1435 newAcc = new EnumRoleHyperTextAccessible<roles::TEXT_CONTAINER>(content,
1436 document);
1439 if (newAcc) {
1440 document->BindToDocument(newAcc, roleMapEntry);
1442 return newAcc;
1445 #if defined(ANDROID)
1446 # include "mozilla/Monitor.h"
1447 # include "mozilla/Maybe.h"
1449 static Maybe<Monitor> sAndroidMonitor;
1451 mozilla::Monitor& nsAccessibilityService::GetAndroidMonitor() {
1452 if (!sAndroidMonitor.isSome()) {
1453 sAndroidMonitor.emplace("nsAccessibility::sAndroidMonitor");
1456 return *sAndroidMonitor;
1458 #endif
1460 ////////////////////////////////////////////////////////////////////////////////
1461 // nsAccessibilityService private
1463 bool nsAccessibilityService::Init() {
1464 AUTO_PROFILER_MARKER_TEXT("nsAccessibilityService::Init", A11Y, {}, ""_ns);
1465 // DO NOT ADD CODE ABOVE HERE: THIS CODE IS MEASURING TIMINGS.
1467 // Initialize accessible document manager.
1468 if (!DocManager::Init()) return false;
1470 // Add observers.
1471 nsCOMPtr<nsIObserverService> observerService =
1472 mozilla::services::GetObserverService();
1473 if (!observerService) return false;
1475 observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
1477 #if defined(XP_WIN)
1478 // This information needs to be initialized before the observer fires.
1479 if (XRE_IsParentProcess()) {
1480 Compatibility::Init();
1482 #endif // defined(XP_WIN)
1484 // Subscribe to EventListenerService.
1485 nsCOMPtr<nsIEventListenerService> eventListenerService =
1486 do_GetService("@mozilla.org/eventlistenerservice;1");
1487 if (!eventListenerService) return false;
1489 eventListenerService->AddListenerChangeListener(this);
1491 for (uint32_t i = 0; i < ArrayLength(sHTMLMarkupMapList); i++) {
1492 mHTMLMarkupMap.InsertOrUpdate(sHTMLMarkupMapList[i].tag,
1493 &sHTMLMarkupMapList[i]);
1495 for (const auto& info : sMathMLMarkupMapList) {
1496 mMathMLMarkupMap.InsertOrUpdate(info.tag, &info);
1499 for (uint32_t i = 0; i < ArrayLength(sXULMarkupMapList); i++) {
1500 mXULMarkupMap.InsertOrUpdate(sXULMarkupMapList[i].tag,
1501 &sXULMarkupMapList[i]);
1504 #ifdef A11Y_LOG
1505 logging::CheckEnv();
1506 #endif
1508 gAccessibilityService = this;
1509 NS_ADDREF(gAccessibilityService); // will release in Shutdown()
1511 if (XRE_IsParentProcess()) {
1512 gApplicationAccessible = new ApplicationAccessibleWrap();
1513 } else {
1514 gApplicationAccessible = new ApplicationAccessible();
1517 NS_ADDREF(gApplicationAccessible); // will release in Shutdown()
1518 gApplicationAccessible->Init();
1520 CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::Accessibility,
1521 "Active"_ns);
1523 // Now its safe to start platform accessibility.
1524 if (XRE_IsParentProcess()) PlatformInit();
1526 statistics::A11yInitialized();
1528 static const char16_t kInitIndicator[] = {'1', 0};
1529 observerService->NotifyObservers(nullptr, "a11y-init-or-shutdown",
1530 kInitIndicator);
1532 return true;
1535 void nsAccessibilityService::Shutdown() {
1536 // Application is going to be closed, shutdown accessibility and mark
1537 // accessibility service as shutdown to prevent calls of its methods.
1538 // Don't null accessibility service static member at this point to be safe
1539 // if someone will try to operate with it.
1541 MOZ_ASSERT(gConsumers, "Accessibility was shutdown already");
1542 UnsetConsumers(eXPCOM | eMainProcess | ePlatformAPI);
1544 // Remove observers.
1545 nsCOMPtr<nsIObserverService> observerService =
1546 mozilla::services::GetObserverService();
1547 if (observerService) {
1548 observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
1551 // Stop accessible document loader.
1552 DocManager::Shutdown();
1554 SelectionManager::Shutdown();
1556 if (XRE_IsParentProcess()) PlatformShutdown();
1558 gApplicationAccessible->Shutdown();
1559 NS_RELEASE(gApplicationAccessible);
1560 gApplicationAccessible = nullptr;
1562 NS_IF_RELEASE(gXPCApplicationAccessible);
1563 gXPCApplicationAccessible = nullptr;
1565 #if defined(ANDROID)
1566 // Don't allow the service to shut down while an a11y request is being handled
1567 // in the UI thread, as the request may depend on state from the service.
1568 MonitorAutoLock mal(GetAndroidMonitor());
1569 #endif
1570 NS_RELEASE(gAccessibilityService);
1571 gAccessibilityService = nullptr;
1573 if (observerService) {
1574 static const char16_t kShutdownIndicator[] = {'0', 0};
1575 observerService->NotifyObservers(nullptr, "a11y-init-or-shutdown",
1576 kShutdownIndicator);
1580 already_AddRefed<LocalAccessible>
1581 nsAccessibilityService::CreateAccessibleByFrameType(nsIFrame* aFrame,
1582 nsIContent* aContent,
1583 LocalAccessible* aContext) {
1584 DocAccessible* document = aContext->Document();
1586 RefPtr<LocalAccessible> newAcc;
1587 switch (aFrame->AccessibleType()) {
1588 case eNoType:
1589 return nullptr;
1590 case eHTMLBRType:
1591 newAcc = new HTMLBRAccessible(aContent, document);
1592 break;
1593 case eHTMLButtonType:
1594 newAcc = new HTMLButtonAccessible(aContent, document);
1595 break;
1596 case eHTMLCanvasType:
1597 newAcc = new HTMLCanvasAccessible(aContent, document);
1598 break;
1599 case eHTMLCaptionType:
1600 if (aContext->IsTable() &&
1601 aContext->GetContent() == aContent->GetParent()) {
1602 newAcc = new HTMLCaptionAccessible(aContent, document);
1604 break;
1605 case eHTMLCheckboxType:
1606 newAcc = new CheckboxAccessible(aContent, document);
1607 break;
1608 case eHTMLComboboxType:
1609 newAcc = new HTMLComboboxAccessible(aContent, document);
1610 break;
1611 case eHTMLFileInputType:
1612 newAcc = new HTMLFileInputAccessible(aContent, document);
1613 break;
1614 case eHTMLGroupboxType:
1615 newAcc = new HTMLGroupboxAccessible(aContent, document);
1616 break;
1617 case eHTMLHRType:
1618 newAcc = new HTMLHRAccessible(aContent, document);
1619 break;
1620 case eHTMLImageMapType:
1621 newAcc = new HTMLImageMapAccessible(aContent, document);
1622 break;
1623 case eHTMLLiType:
1624 if (aContext->IsList() &&
1625 aContext->GetContent() == aContent->GetParent()) {
1626 newAcc = new HTMLLIAccessible(aContent, document);
1627 } else {
1628 // Otherwise create a generic text accessible to avoid text jamming.
1629 newAcc = new HyperTextAccessible(aContent, document);
1631 break;
1632 case eHTMLSelectListType:
1633 newAcc = new HTMLSelectListAccessible(aContent, document);
1634 break;
1635 case eHTMLMediaType:
1636 newAcc = new EnumRoleAccessible<roles::GROUPING>(aContent, document);
1637 break;
1638 case eHTMLRadioButtonType:
1639 newAcc = new HTMLRadioButtonAccessible(aContent, document);
1640 break;
1641 case eHTMLRangeType:
1642 newAcc = new HTMLRangeAccessible(aContent, document);
1643 break;
1644 case eHTMLSpinnerType:
1645 newAcc = new HTMLSpinnerAccessible(aContent, document);
1646 break;
1647 case eHTMLTableType:
1648 case eHTMLTableCellType:
1649 // We handle markup and ARIA tables elsewhere. If we reach here, this is
1650 // a CSS table part. Just create a generic text container.
1651 newAcc = new HyperTextAccessible(aContent, document);
1652 break;
1653 case eHTMLTableRowType:
1654 // This is a CSS table row. Don't expose it at all.
1655 break;
1656 case eHTMLTextFieldType:
1657 newAcc = new HTMLTextFieldAccessible(aContent, document);
1658 break;
1659 case eHyperTextType: {
1660 if (aContext->IsTable() || aContext->IsTableRow()) {
1661 // This is some generic hyperText, for example a block frame element
1662 // inserted between a table and table row. Treat it as presentational.
1663 return nullptr;
1666 if (!aContent->IsAnyOfHTMLElements(nsGkAtoms::dt, nsGkAtoms::dd,
1667 nsGkAtoms::div, nsGkAtoms::thead,
1668 nsGkAtoms::tfoot, nsGkAtoms::tbody)) {
1669 newAcc = new HyperTextAccessible(aContent, document);
1671 break;
1673 case eImageType:
1674 if (aContent->IsElement() &&
1675 ShouldCreateImgAccessible(aContent->AsElement(), document)) {
1676 newAcc = new ImageAccessible(aContent, document);
1678 break;
1679 case eOuterDocType:
1680 newAcc = new OuterDocAccessible(aContent, document);
1681 break;
1682 case eTextLeafType:
1683 newAcc = new TextLeafAccessible(aContent, document);
1684 break;
1685 default:
1686 MOZ_ASSERT(false);
1687 break;
1690 return newAcc.forget();
1693 void nsAccessibilityService::MarkupAttributes(
1694 Accessible* aAcc, AccAttributes* aAttributes) const {
1695 const mozilla::a11y::MarkupMapInfo* markupMap = GetMarkupMapInfoFor(aAcc);
1696 if (!markupMap) return;
1698 dom::Element* el = aAcc->IsLocal() ? aAcc->AsLocal()->Elm() : nullptr;
1699 for (uint32_t i = 0; i < ArrayLength(markupMap->attrs); i++) {
1700 const MarkupAttrInfo* info = markupMap->attrs + i;
1701 if (!info->name) break;
1703 if (info->DOMAttrName) {
1704 if (!el) {
1705 // XXX Expose DOM attributes for cached RemoteAccessibles.
1706 continue;
1708 if (info->DOMAttrValue) {
1709 if (el->AttrValueIs(kNameSpaceID_None, info->DOMAttrName,
1710 info->DOMAttrValue, eCaseMatters)) {
1711 aAttributes->SetAttribute(info->name, info->DOMAttrValue);
1713 continue;
1716 nsString value;
1717 el->GetAttr(info->DOMAttrName, value);
1719 if (!value.IsEmpty()) {
1720 aAttributes->SetAttribute(info->name, std::move(value));
1723 continue;
1726 aAttributes->SetAttribute(info->name, info->value);
1730 LocalAccessible* nsAccessibilityService::AddNativeRootAccessible(
1731 void* aAtkAccessible) {
1732 #ifdef MOZ_ACCESSIBILITY_ATK
1733 ApplicationAccessible* applicationAcc = ApplicationAcc();
1734 if (!applicationAcc) return nullptr;
1736 GtkWindowAccessible* nativeWnd =
1737 new GtkWindowAccessible(static_cast<AtkObject*>(aAtkAccessible));
1739 if (applicationAcc->AppendChild(nativeWnd)) return nativeWnd;
1740 #endif
1742 return nullptr;
1745 void nsAccessibilityService::RemoveNativeRootAccessible(
1746 LocalAccessible* aAccessible) {
1747 #ifdef MOZ_ACCESSIBILITY_ATK
1748 ApplicationAccessible* applicationAcc = ApplicationAcc();
1750 if (applicationAcc) applicationAcc->RemoveChild(aAccessible);
1751 #endif
1754 bool nsAccessibilityService::HasAccessible(nsINode* aDOMNode) {
1755 if (!aDOMNode) return false;
1757 Document* document = aDOMNode->OwnerDoc();
1758 if (!document) return false;
1760 DocAccessible* docAcc = GetExistingDocAccessible(aDOMNode->OwnerDoc());
1761 if (!docAcc) return false;
1763 return docAcc->HasAccessible(aDOMNode);
1766 ////////////////////////////////////////////////////////////////////////////////
1767 // nsAccessibilityService private (DON'T put methods here)
1769 void nsAccessibilityService::SetConsumers(uint32_t aConsumers, bool aNotify) {
1770 if (gConsumers & aConsumers) {
1771 return;
1774 gConsumers |= aConsumers;
1775 if (aNotify) {
1776 NotifyOfConsumersChange();
1780 void nsAccessibilityService::UnsetConsumers(uint32_t aConsumers) {
1781 if (!(gConsumers & aConsumers)) {
1782 return;
1785 gConsumers &= ~aConsumers;
1786 NotifyOfConsumersChange();
1789 void nsAccessibilityService::GetConsumers(nsAString& aString) {
1790 const char16_t* kJSONFmt =
1791 u"{ \"XPCOM\": %s, \"MainProcess\": %s, \"PlatformAPI\": %s }";
1792 nsString json;
1793 nsTextFormatter::ssprintf(json, kJSONFmt,
1794 gConsumers & eXPCOM ? "true" : "false",
1795 gConsumers & eMainProcess ? "true" : "false",
1796 gConsumers & ePlatformAPI ? "true" : "false");
1797 aString.Assign(json);
1800 void nsAccessibilityService::NotifyOfConsumersChange() {
1801 nsCOMPtr<nsIObserverService> observerService =
1802 mozilla::services::GetObserverService();
1804 if (!observerService) {
1805 return;
1808 nsAutoString consumers;
1809 GetConsumers(consumers);
1810 observerService->NotifyObservers(nullptr, "a11y-consumers-changed",
1811 consumers.get());
1814 const mozilla::a11y::MarkupMapInfo* nsAccessibilityService::GetMarkupMapInfoFor(
1815 Accessible* aAcc) const {
1816 if (LocalAccessible* localAcc = aAcc->AsLocal()) {
1817 return localAcc->HasOwnContent()
1818 ? GetMarkupMapInfoFor(localAcc->GetContent())
1819 : nullptr;
1821 // XXX For now, we assume all RemoteAccessibles are HTML elements. This
1822 // isn't strictly correct, but as far as current callers are concerned,
1823 // this doesn't matter. If that changes in future, we could expose the
1824 // element type via AccGenericType.
1825 return mHTMLMarkupMap.Get(aAcc->TagName());
1828 nsAccessibilityService* GetOrCreateAccService(uint32_t aNewConsumer) {
1829 // Do not initialize accessibility if it is force disabled.
1830 if (PlatformDisabledState() == ePlatformIsDisabled) {
1831 return nullptr;
1834 if (!nsAccessibilityService::gAccessibilityService) {
1835 RefPtr<nsAccessibilityService> service = new nsAccessibilityService();
1836 if (!service->Init()) {
1837 service->Shutdown();
1838 return nullptr;
1842 MOZ_ASSERT(nsAccessibilityService::gAccessibilityService,
1843 "LocalAccessible service is not initialized.");
1844 nsAccessibilityService::gAccessibilityService->SetConsumers(aNewConsumer);
1845 return nsAccessibilityService::gAccessibilityService;
1848 void MaybeShutdownAccService(uint32_t aFormerConsumer) {
1849 nsAccessibilityService* accService =
1850 nsAccessibilityService::gAccessibilityService;
1852 if (!accService || nsAccessibilityService::IsShutdown()) {
1853 return;
1856 // Still used by XPCOM
1857 if (nsCoreUtils::AccEventObserversExist() ||
1858 xpcAccessibilityService::IsInUse() || accService->HasXPCDocuments()) {
1859 // In case the XPCOM flag was unset (possibly because of the shutdown
1860 // timer in the xpcAccessibilityService) ensure it is still present. Note:
1861 // this should be fixed when all the consumer logic is taken out as a
1862 // separate class.
1863 accService->SetConsumers(nsAccessibilityService::eXPCOM, false);
1865 if (aFormerConsumer != nsAccessibilityService::eXPCOM) {
1866 // Only unset non-XPCOM consumers.
1867 accService->UnsetConsumers(aFormerConsumer);
1869 return;
1872 if (nsAccessibilityService::gConsumers & ~aFormerConsumer) {
1873 accService->UnsetConsumers(aFormerConsumer);
1874 } else {
1875 accService
1876 ->Shutdown(); // Will unset all nsAccessibilityService::gConsumers
1880 ////////////////////////////////////////////////////////////////////////////////
1881 // Services
1882 ////////////////////////////////////////////////////////////////////////////////
1884 namespace mozilla {
1885 namespace a11y {
1887 FocusManager* FocusMgr() {
1888 return nsAccessibilityService::gAccessibilityService;
1891 SelectionManager* SelectionMgr() {
1892 return nsAccessibilityService::gAccessibilityService;
1895 ApplicationAccessible* ApplicationAcc() {
1896 return nsAccessibilityService::gApplicationAccessible;
1899 xpcAccessibleApplication* XPCApplicationAcc() {
1900 if (!nsAccessibilityService::gXPCApplicationAccessible &&
1901 nsAccessibilityService::gApplicationAccessible) {
1902 nsAccessibilityService::gXPCApplicationAccessible =
1903 new xpcAccessibleApplication(
1904 nsAccessibilityService::gApplicationAccessible);
1905 NS_ADDREF(nsAccessibilityService::gXPCApplicationAccessible);
1908 return nsAccessibilityService::gXPCApplicationAccessible;
1911 EPlatformDisabledState PlatformDisabledState() {
1912 static bool platformDisabledStateCached = false;
1913 if (platformDisabledStateCached) {
1914 return static_cast<EPlatformDisabledState>(sPlatformDisabledState);
1917 platformDisabledStateCached = true;
1918 Preferences::RegisterCallback(PrefChanged, PREF_ACCESSIBILITY_FORCE_DISABLED);
1919 return ReadPlatformDisabledState();
1922 EPlatformDisabledState ReadPlatformDisabledState() {
1923 sPlatformDisabledState =
1924 Preferences::GetInt(PREF_ACCESSIBILITY_FORCE_DISABLED, 0);
1925 if (sPlatformDisabledState < ePlatformIsForceEnabled) {
1926 sPlatformDisabledState = ePlatformIsForceEnabled;
1927 } else if (sPlatformDisabledState > ePlatformIsDisabled) {
1928 sPlatformDisabledState = ePlatformIsDisabled;
1931 return static_cast<EPlatformDisabledState>(sPlatformDisabledState);
1934 void PrefChanged(const char* aPref, void* aClosure) {
1935 if (ReadPlatformDisabledState() == ePlatformIsDisabled) {
1936 // Force shut down accessibility.
1937 nsAccessibilityService* accService =
1938 nsAccessibilityService::gAccessibilityService;
1939 if (accService && !nsAccessibilityService::IsShutdown()) {
1940 accService->Shutdown();
1945 } // namespace a11y
1946 } // namespace mozilla