Bumping manifests a=b2g-bump
[gecko.git] / accessible / base / nsAccessibilityService.cpp
blobe36a6c5dc55e42f4c735509e31f8fc36e8881637
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 "ARIAGridAccessibleWrap.h"
11 #include "ARIAMap.h"
12 #include "DocAccessible-inl.h"
13 #include "FocusManager.h"
14 #include "HTMLCanvasAccessible.h"
15 #include "HTMLElementAccessibles.h"
16 #include "HTMLImageMapAccessible.h"
17 #include "HTMLLinkAccessible.h"
18 #include "HTMLListAccessible.h"
19 #include "HTMLSelectAccessible.h"
20 #include "HTMLTableAccessibleWrap.h"
21 #include "HyperTextAccessibleWrap.h"
22 #include "RootAccessible.h"
23 #include "nsAccessiblePivot.h"
24 #include "nsAccUtils.h"
25 #include "nsAttrName.h"
26 #include "nsEventShell.h"
27 #include "nsIURI.h"
28 #include "OuterDocAccessible.h"
29 #include "Platform.h"
30 #include "Role.h"
31 #ifdef MOZ_ACCESSIBILITY_ATK
32 #include "RootAccessibleWrap.h"
33 #endif
34 #include "States.h"
35 #include "Statistics.h"
36 #include "TextLeafAccessibleWrap.h"
38 #ifdef MOZ_ACCESSIBILITY_ATK
39 #include "AtkSocketAccessible.h"
40 #endif
42 #ifdef XP_WIN
43 #include "mozilla/a11y/Compatibility.h"
44 #include "HTMLWin32ObjectAccessible.h"
45 #include "mozilla/StaticPtr.h"
46 #endif
48 #ifdef A11Y_LOG
49 #include "Logging.h"
50 #endif
52 #ifdef MOZ_CRASHREPORTER
53 #include "nsExceptionHandler.h"
54 #endif
56 #include "nsImageFrame.h"
57 #include "nsIObserverService.h"
58 #include "nsLayoutUtils.h"
59 #include "nsObjectFrame.h"
60 #include "nsSVGPathGeometryFrame.h"
61 #include "nsTreeBodyFrame.h"
62 #include "nsTreeColumns.h"
63 #include "nsTreeUtils.h"
64 #include "nsXBLPrototypeBinding.h"
65 #include "nsXBLBinding.h"
66 #include "mozilla/ArrayUtils.h"
67 #include "mozilla/dom/DOMStringList.h"
68 #include "mozilla/Preferences.h"
69 #include "mozilla/Services.h"
70 #include "nsDeckFrame.h"
72 #ifdef MOZ_XUL
73 #include "XULAlertAccessible.h"
74 #include "XULColorPickerAccessible.h"
75 #include "XULComboboxAccessible.h"
76 #include "XULElementAccessibles.h"
77 #include "XULFormControlAccessible.h"
78 #include "XULListboxAccessibleWrap.h"
79 #include "XULMenuAccessibleWrap.h"
80 #include "XULSliderAccessible.h"
81 #include "XULTabAccessible.h"
82 #include "XULTreeGridAccessibleWrap.h"
83 #endif
85 #if defined(XP_WIN) || defined(MOZ_ACCESSIBILITY_ATK)
86 #include "nsNPAPIPluginInstance.h"
87 #endif
89 using namespace mozilla;
90 using namespace mozilla::a11y;
91 using namespace mozilla::dom;
93 ////////////////////////////////////////////////////////////////////////////////
94 // Statics
95 ////////////////////////////////////////////////////////////////////////////////
97 /**
98 * Return true if the element must be accessible.
100 static bool
101 MustBeAccessible(nsIContent* aContent, DocAccessible* aDocument)
103 if (aContent->GetPrimaryFrame()->IsFocusable())
104 return true;
106 uint32_t attrCount = aContent->GetAttrCount();
107 for (uint32_t attrIdx = 0; attrIdx < attrCount; attrIdx++) {
108 const nsAttrName* attr = aContent->GetAttrNameAt(attrIdx);
109 if (attr->NamespaceEquals(kNameSpaceID_None)) {
110 nsIAtom* attrAtom = attr->Atom();
111 nsDependentAtomString attrStr(attrAtom);
112 if (!StringBeginsWith(attrStr, NS_LITERAL_STRING("aria-")))
113 continue; // not ARIA
115 // A global state or a property and in case of token defined.
116 uint8_t attrFlags = aria::AttrCharacteristicsFor(attrAtom);
117 if ((attrFlags & ATTR_GLOBAL) && (!(attrFlags & ATTR_VALTOKEN) ||
118 nsAccUtils::HasDefinedARIAToken(aContent, attrAtom))) {
119 return true;
124 // If the given ID is referred by relation attribute then create an accessible
125 // for it.
126 nsAutoString id;
127 if (nsCoreUtils::GetID(aContent, id) && !id.IsEmpty())
128 return aDocument->IsDependentID(id);
130 return false;
133 ////////////////////////////////////////////////////////////////////////////////
134 // nsAccessibilityService
135 ////////////////////////////////////////////////////////////////////////////////
137 nsAccessibilityService *nsAccessibilityService::gAccessibilityService = nullptr;
138 ApplicationAccessible* nsAccessibilityService::gApplicationAccessible = nullptr;
139 bool nsAccessibilityService::gIsShutdown = true;
141 nsAccessibilityService::nsAccessibilityService() :
142 DocManager(), FocusManager()
146 nsAccessibilityService::~nsAccessibilityService()
148 NS_ASSERTION(gIsShutdown, "Accessibility wasn't shutdown!");
149 gAccessibilityService = nullptr;
152 ////////////////////////////////////////////////////////////////////////////////
153 // nsISupports
155 NS_IMPL_ISUPPORTS_INHERITED(nsAccessibilityService,
156 DocManager,
157 nsIAccessibilityService,
158 nsIAccessibleRetrieval,
159 nsIObserver,
160 nsISelectionListener) // from SelectionManager
162 ////////////////////////////////////////////////////////////////////////////////
163 // nsIObserver
165 NS_IMETHODIMP
166 nsAccessibilityService::Observe(nsISupports *aSubject, const char *aTopic,
167 const char16_t *aData)
169 if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID))
170 Shutdown();
172 return NS_OK;
175 // nsIAccessibilityService
176 void
177 nsAccessibilityService::NotifyOfAnchorJumpTo(nsIContent* aTargetNode)
179 nsIDocument* documentNode = aTargetNode->GetCurrentDoc();
180 if (documentNode) {
181 DocAccessible* document = GetDocAccessible(documentNode);
182 if (document)
183 document->SetAnchorJump(aTargetNode);
187 // nsIAccessibilityService
188 void
189 nsAccessibilityService::FireAccessibleEvent(uint32_t aEvent,
190 Accessible* aTarget)
192 nsEventShell::FireEvent(aEvent, aTarget);
195 ////////////////////////////////////////////////////////////////////////////////
196 // nsIAccessibilityService
198 Accessible*
199 nsAccessibilityService::GetRootDocumentAccessible(nsIPresShell* aPresShell,
200 bool aCanCreate)
202 nsIPresShell* ps = aPresShell;
203 nsIDocument* documentNode = aPresShell->GetDocument();
204 if (documentNode) {
205 nsCOMPtr<nsIDocShellTreeItem> treeItem(documentNode->GetDocShell());
206 if (treeItem) {
207 nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
208 treeItem->GetRootTreeItem(getter_AddRefs(rootTreeItem));
209 if (treeItem != rootTreeItem) {
210 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(rootTreeItem));
211 ps = docShell->GetPresShell();
214 return aCanCreate ? GetDocAccessible(ps) : ps->GetDocAccessible();
217 return nullptr;
220 #ifdef XP_WIN
221 static StaticAutoPtr<nsTArray<nsCOMPtr<nsIContent> > > sPendingPlugins;
222 static StaticAutoPtr<nsTArray<nsCOMPtr<nsITimer> > > sPluginTimers;
224 class PluginTimerCallBack MOZ_FINAL : public nsITimerCallback
226 ~PluginTimerCallBack() {}
228 public:
229 PluginTimerCallBack(nsIContent* aContent) : mContent(aContent) {}
231 NS_DECL_ISUPPORTS
233 NS_IMETHODIMP Notify(nsITimer* aTimer) MOZ_FINAL
235 if (!mContent->IsInDoc())
236 return NS_OK;
238 nsIPresShell* ps = mContent->OwnerDoc()->GetShell();
239 if (ps) {
240 DocAccessible* doc = ps->GetDocAccessible();
241 if (doc) {
242 // Make sure that if we created an accessible for the plugin that wasn't
243 // a plugin accessible we remove it before creating the right accessible.
244 doc->RecreateAccessible(mContent);
245 sPluginTimers->RemoveElement(aTimer);
246 return NS_OK;
250 // We couldn't get a doc accessible so presumably the document went away.
251 // In this case don't leak our ref to the content or timer.
252 sPendingPlugins->RemoveElement(mContent);
253 sPluginTimers->RemoveElement(aTimer);
254 return NS_OK;
257 private:
258 nsCOMPtr<nsIContent> mContent;
261 NS_IMPL_ISUPPORTS(PluginTimerCallBack, nsITimerCallback)
262 #endif
264 already_AddRefed<Accessible>
265 nsAccessibilityService::CreatePluginAccessible(nsObjectFrame* aFrame,
266 nsIContent* aContent,
267 Accessible* aContext)
269 // nsObjectFrame means a plugin, so we need to use the accessibility support
270 // of the plugin.
271 if (aFrame->GetRect().IsEmpty())
272 return nullptr;
274 #if defined(XP_WIN) || defined(MOZ_ACCESSIBILITY_ATK)
275 nsRefPtr<nsNPAPIPluginInstance> pluginInstance;
276 if (NS_SUCCEEDED(aFrame->GetPluginInstance(getter_AddRefs(pluginInstance))) &&
277 pluginInstance) {
278 #ifdef XP_WIN
279 if (!sPendingPlugins->Contains(aContent) &&
280 (Preferences::GetBool("accessibility.delay_plugins") ||
281 Compatibility::IsJAWS() || Compatibility::IsWE())) {
282 nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
283 nsRefPtr<PluginTimerCallBack> cb = new PluginTimerCallBack(aContent);
284 timer->InitWithCallback(cb, Preferences::GetUint("accessibility.delay_plugin_time"),
285 nsITimer::TYPE_ONE_SHOT);
286 sPluginTimers->AppendElement(timer);
287 sPendingPlugins->AppendElement(aContent);
288 return nullptr;
291 // We need to remove aContent from the pending plugins here to avoid
292 // reentrancy. When the timer fires it calls
293 // DocAccessible::ContentInserted() which does the work async.
294 sPendingPlugins->RemoveElement(aContent);
296 // Note: pluginPort will be null if windowless.
297 HWND pluginPort = nullptr;
298 aFrame->GetPluginPort(&pluginPort);
300 nsRefPtr<Accessible> accessible =
301 new HTMLWin32ObjectOwnerAccessible(aContent, aContext->Document(),
302 pluginPort);
303 return accessible.forget();
305 #elif MOZ_ACCESSIBILITY_ATK
306 if (!AtkSocketAccessible::gCanEmbed)
307 return nullptr;
309 // Note this calls into the plugin, so crazy things may happen and aFrame
310 // may go away.
311 nsCString plugId;
312 nsresult rv = pluginInstance->GetValueFromPlugin(
313 NPPVpluginNativeAccessibleAtkPlugId, &plugId);
314 if (NS_SUCCEEDED(rv) && !plugId.IsEmpty()) {
315 nsRefPtr<AtkSocketAccessible> socketAccessible =
316 new AtkSocketAccessible(aContent, aContext->Document(), plugId);
318 return socketAccessible.forget();
320 #endif
322 #endif
324 return nullptr;
327 void
328 nsAccessibilityService::DeckPanelSwitched(nsIPresShell* aPresShell,
329 nsIContent* aDeckNode,
330 nsIFrame* aPrevBoxFrame,
331 nsIFrame* aCurrentBoxFrame)
333 // Ignore tabpanels elements (a deck having an accessible) since their
334 // children are accessible not depending on selected tab.
335 DocAccessible* document = GetDocAccessible(aPresShell);
336 if (!document || document->HasAccessible(aDeckNode))
337 return;
339 if (aPrevBoxFrame) {
340 nsIContent* panelNode = aPrevBoxFrame->GetContent();
341 #ifdef A11Y_LOG
342 if (logging::IsEnabled(logging::eTree)) {
343 logging::MsgBegin("TREE", "deck panel unselected");
344 logging::Node("container", panelNode);
345 logging::Node("content", aDeckNode);
346 logging::MsgEnd();
348 #endif
350 document->ContentRemoved(aDeckNode, panelNode);
353 if (aCurrentBoxFrame) {
354 nsIContent* panelNode = aCurrentBoxFrame->GetContent();
355 #ifdef A11Y_LOG
356 if (logging::IsEnabled(logging::eTree)) {
357 logging::MsgBegin("TREE", "deck panel selected");
358 logging::Node("container", panelNode);
359 logging::Node("content", aDeckNode);
360 logging::MsgEnd();
362 #endif
364 document->ContentInserted(aDeckNode, panelNode, panelNode->GetNextSibling());
368 void
369 nsAccessibilityService::ContentRangeInserted(nsIPresShell* aPresShell,
370 nsIContent* aContainer,
371 nsIContent* aStartChild,
372 nsIContent* aEndChild)
374 #ifdef A11Y_LOG
375 if (logging::IsEnabled(logging::eTree)) {
376 logging::MsgBegin("TREE", "content inserted");
377 logging::Node("container", aContainer);
378 for (nsIContent* child = aStartChild; child != aEndChild;
379 child = child->GetNextSibling()) {
380 logging::Node("content", child);
382 logging::MsgEnd();
383 logging::Stack();
385 #endif
387 DocAccessible* docAccessible = GetDocAccessible(aPresShell);
388 if (docAccessible)
389 docAccessible->ContentInserted(aContainer, aStartChild, aEndChild);
392 void
393 nsAccessibilityService::ContentRemoved(nsIPresShell* aPresShell,
394 nsIContent* aContainer,
395 nsIContent* aChild)
397 #ifdef A11Y_LOG
398 if (logging::IsEnabled(logging::eTree)) {
399 logging::MsgBegin("TREE", "content removed");
400 logging::Node("container", aContainer);
401 logging::Node("content", aChild);
402 logging::MsgEnd();
403 logging::Stack();
405 #endif
407 DocAccessible* docAccessible = GetDocAccessible(aPresShell);
408 if (docAccessible)
409 docAccessible->ContentRemoved(aContainer, aChild);
412 void
413 nsAccessibilityService::UpdateText(nsIPresShell* aPresShell,
414 nsIContent* aContent)
416 DocAccessible* document = GetDocAccessible(aPresShell);
417 if (document)
418 document->UpdateText(aContent);
421 void
422 nsAccessibilityService::TreeViewChanged(nsIPresShell* aPresShell,
423 nsIContent* aContent,
424 nsITreeView* aView)
426 DocAccessible* document = GetDocAccessible(aPresShell);
427 if (document) {
428 Accessible* accessible = document->GetAccessible(aContent);
429 if (accessible) {
430 XULTreeAccessible* treeAcc = accessible->AsXULTree();
431 if (treeAcc)
432 treeAcc->TreeViewChanged(aView);
437 void
438 nsAccessibilityService::RangeValueChanged(nsIPresShell* aPresShell,
439 nsIContent* aContent)
441 DocAccessible* document = GetDocAccessible(aPresShell);
442 if (document) {
443 Accessible* accessible = document->GetAccessible(aContent);
444 if (accessible) {
445 document->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
446 accessible);
451 void
452 nsAccessibilityService::UpdateListBullet(nsIPresShell* aPresShell,
453 nsIContent* aHTMLListItemContent,
454 bool aHasBullet)
456 DocAccessible* document = GetDocAccessible(aPresShell);
457 if (document) {
458 Accessible* accessible = document->GetAccessible(aHTMLListItemContent);
459 if (accessible) {
460 HTMLLIAccessible* listItem = accessible->AsHTMLListItem();
461 if (listItem)
462 listItem->UpdateBullet(aHasBullet);
467 void
468 nsAccessibilityService::UpdateImageMap(nsImageFrame* aImageFrame)
470 nsIPresShell* presShell = aImageFrame->PresContext()->PresShell();
471 DocAccessible* document = GetDocAccessible(presShell);
472 if (document) {
473 Accessible* accessible =
474 document->GetAccessible(aImageFrame->GetContent());
475 if (accessible) {
476 HTMLImageMapAccessible* imageMap = accessible->AsImageMap();
477 if (imageMap) {
478 imageMap->UpdateChildAreas();
479 return;
482 // If image map was initialized after we created an accessible (that'll
483 // be an image accessible) then recreate it.
484 RecreateAccessible(presShell, aImageFrame->GetContent());
489 void
490 nsAccessibilityService::UpdateLabelValue(nsIPresShell* aPresShell,
491 nsIContent* aLabelElm,
492 const nsString& aNewValue)
494 DocAccessible* document = GetDocAccessible(aPresShell);
495 if (document) {
496 Accessible* accessible = document->GetAccessible(aLabelElm);
497 if (accessible) {
498 XULLabelAccessible* xulLabel = accessible->AsXULLabel();
499 NS_ASSERTION(xulLabel,
500 "UpdateLabelValue was called for wrong accessible!");
501 if (xulLabel)
502 xulLabel->UpdateLabelValue(aNewValue);
507 void
508 nsAccessibilityService::PresShellActivated(nsIPresShell* aPresShell)
510 DocAccessible* document = aPresShell->GetDocAccessible();
511 if (document) {
512 RootAccessible* rootDocument = document->RootAccessible();
513 NS_ASSERTION(rootDocument, "Entirely broken tree: no root document!");
514 if (rootDocument)
515 rootDocument->DocumentActivated(document);
519 void
520 nsAccessibilityService::RecreateAccessible(nsIPresShell* aPresShell,
521 nsIContent* aContent)
523 DocAccessible* document = GetDocAccessible(aPresShell);
524 if (document)
525 document->RecreateAccessible(aContent);
528 ////////////////////////////////////////////////////////////////////////////////
529 // nsIAccessibleRetrieval
531 NS_IMETHODIMP
532 nsAccessibilityService::GetApplicationAccessible(nsIAccessible** aAccessibleApplication)
534 NS_ENSURE_ARG_POINTER(aAccessibleApplication);
536 NS_IF_ADDREF(*aAccessibleApplication = ApplicationAcc());
538 return NS_OK;
541 NS_IMETHODIMP
542 nsAccessibilityService::GetAccessibleFor(nsIDOMNode *aNode,
543 nsIAccessible **aAccessible)
545 NS_ENSURE_ARG_POINTER(aAccessible);
546 *aAccessible = nullptr;
547 if (!aNode)
548 return NS_OK;
550 nsCOMPtr<nsINode> node(do_QueryInterface(aNode));
551 if (!node)
552 return NS_ERROR_INVALID_ARG;
554 DocAccessible* document = GetDocAccessible(node->OwnerDoc());
555 if (document)
556 NS_IF_ADDREF(*aAccessible = document->GetAccessible(node));
558 return NS_OK;
561 NS_IMETHODIMP
562 nsAccessibilityService::GetStringRole(uint32_t aRole, nsAString& aString)
564 #define ROLE(geckoRole, stringRole, atkRole, \
565 macRole, msaaRole, ia2Role, nameRule) \
566 case roles::geckoRole: \
567 CopyUTF8toUTF16(stringRole, aString); \
568 return NS_OK;
570 switch (aRole) {
571 #include "RoleMap.h"
572 default:
573 aString.AssignLiteral("unknown");
574 return NS_OK;
577 #undef ROLE
580 NS_IMETHODIMP
581 nsAccessibilityService::GetStringStates(uint32_t aState, uint32_t aExtraState,
582 nsISupports **aStringStates)
584 nsRefPtr<DOMStringList> stringStates = new DOMStringList();
586 uint64_t state = nsAccUtils::To64State(aState, aExtraState);
588 // states
589 if (state & states::UNAVAILABLE)
590 stringStates->Add(NS_LITERAL_STRING("unavailable"));
591 if (state & states::SELECTED)
592 stringStates->Add(NS_LITERAL_STRING("selected"));
593 if (state & states::FOCUSED)
594 stringStates->Add(NS_LITERAL_STRING("focused"));
595 if (state & states::PRESSED)
596 stringStates->Add(NS_LITERAL_STRING("pressed"));
597 if (state & states::CHECKED)
598 stringStates->Add(NS_LITERAL_STRING("checked"));
599 if (state & states::MIXED)
600 stringStates->Add(NS_LITERAL_STRING("mixed"));
601 if (state & states::READONLY)
602 stringStates->Add(NS_LITERAL_STRING("readonly"));
603 if (state & states::HOTTRACKED)
604 stringStates->Add(NS_LITERAL_STRING("hottracked"));
605 if (state & states::DEFAULT)
606 stringStates->Add(NS_LITERAL_STRING("default"));
607 if (state & states::EXPANDED)
608 stringStates->Add(NS_LITERAL_STRING("expanded"));
609 if (state & states::COLLAPSED)
610 stringStates->Add(NS_LITERAL_STRING("collapsed"));
611 if (state & states::BUSY)
612 stringStates->Add(NS_LITERAL_STRING("busy"));
613 if (state & states::FLOATING)
614 stringStates->Add(NS_LITERAL_STRING("floating"));
615 if (state & states::ANIMATED)
616 stringStates->Add(NS_LITERAL_STRING("animated"));
617 if (state & states::INVISIBLE)
618 stringStates->Add(NS_LITERAL_STRING("invisible"));
619 if (state & states::OFFSCREEN)
620 stringStates->Add(NS_LITERAL_STRING("offscreen"));
621 if (state & states::SIZEABLE)
622 stringStates->Add(NS_LITERAL_STRING("sizeable"));
623 if (state & states::MOVEABLE)
624 stringStates->Add(NS_LITERAL_STRING("moveable"));
625 if (state & states::SELFVOICING)
626 stringStates->Add(NS_LITERAL_STRING("selfvoicing"));
627 if (state & states::FOCUSABLE)
628 stringStates->Add(NS_LITERAL_STRING("focusable"));
629 if (state & states::SELECTABLE)
630 stringStates->Add(NS_LITERAL_STRING("selectable"));
631 if (state & states::LINKED)
632 stringStates->Add(NS_LITERAL_STRING("linked"));
633 if (state & states::TRAVERSED)
634 stringStates->Add(NS_LITERAL_STRING("traversed"));
635 if (state & states::MULTISELECTABLE)
636 stringStates->Add(NS_LITERAL_STRING("multiselectable"));
637 if (state & states::EXTSELECTABLE)
638 stringStates->Add(NS_LITERAL_STRING("extselectable"));
639 if (state & states::PROTECTED)
640 stringStates->Add(NS_LITERAL_STRING("protected"));
641 if (state & states::HASPOPUP)
642 stringStates->Add(NS_LITERAL_STRING("haspopup"));
643 if (state & states::REQUIRED)
644 stringStates->Add(NS_LITERAL_STRING("required"));
645 if (state & states::ALERT)
646 stringStates->Add(NS_LITERAL_STRING("alert"));
647 if (state & states::INVALID)
648 stringStates->Add(NS_LITERAL_STRING("invalid"));
649 if (state & states::CHECKABLE)
650 stringStates->Add(NS_LITERAL_STRING("checkable"));
652 // extraStates
653 if (state & states::SUPPORTS_AUTOCOMPLETION)
654 stringStates->Add(NS_LITERAL_STRING("autocompletion"));
655 if (state & states::DEFUNCT)
656 stringStates->Add(NS_LITERAL_STRING("defunct"));
657 if (state & states::SELECTABLE_TEXT)
658 stringStates->Add(NS_LITERAL_STRING("selectable text"));
659 if (state & states::EDITABLE)
660 stringStates->Add(NS_LITERAL_STRING("editable"));
661 if (state & states::ACTIVE)
662 stringStates->Add(NS_LITERAL_STRING("active"));
663 if (state & states::MODAL)
664 stringStates->Add(NS_LITERAL_STRING("modal"));
665 if (state & states::MULTI_LINE)
666 stringStates->Add(NS_LITERAL_STRING("multi line"));
667 if (state & states::HORIZONTAL)
668 stringStates->Add(NS_LITERAL_STRING("horizontal"));
669 if (state & states::OPAQUE1)
670 stringStates->Add(NS_LITERAL_STRING("opaque"));
671 if (state & states::SINGLE_LINE)
672 stringStates->Add(NS_LITERAL_STRING("single line"));
673 if (state & states::TRANSIENT)
674 stringStates->Add(NS_LITERAL_STRING("transient"));
675 if (state & states::VERTICAL)
676 stringStates->Add(NS_LITERAL_STRING("vertical"));
677 if (state & states::STALE)
678 stringStates->Add(NS_LITERAL_STRING("stale"));
679 if (state & states::ENABLED)
680 stringStates->Add(NS_LITERAL_STRING("enabled"));
681 if (state & states::SENSITIVE)
682 stringStates->Add(NS_LITERAL_STRING("sensitive"));
683 if (state & states::EXPANDABLE)
684 stringStates->Add(NS_LITERAL_STRING("expandable"));
686 //unknown states
687 if (!stringStates->Length())
688 stringStates->Add(NS_LITERAL_STRING("unknown"));
690 stringStates.forget(aStringStates);
691 return NS_OK;
694 // nsIAccessibleRetrieval::getStringEventType()
695 NS_IMETHODIMP
696 nsAccessibilityService::GetStringEventType(uint32_t aEventType,
697 nsAString& aString)
699 NS_ASSERTION(nsIAccessibleEvent::EVENT_LAST_ENTRY == ArrayLength(kEventTypeNames),
700 "nsIAccessibleEvent constants are out of sync to kEventTypeNames");
702 if (aEventType >= ArrayLength(kEventTypeNames)) {
703 aString.AssignLiteral("unknown");
704 return NS_OK;
707 CopyUTF8toUTF16(kEventTypeNames[aEventType], aString);
708 return NS_OK;
711 // nsIAccessibleRetrieval::getStringRelationType()
712 NS_IMETHODIMP
713 nsAccessibilityService::GetStringRelationType(uint32_t aRelationType,
714 nsAString& aString)
716 NS_ENSURE_ARG(aRelationType <= static_cast<uint32_t>(RelationType::LAST));
718 #define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \
719 case RelationType::geckoType: \
720 aString.AssignLiteral(geckoTypeName); \
721 return NS_OK;
723 RelationType relationType = static_cast<RelationType>(aRelationType);
724 switch (relationType) {
725 #include "RelationTypeMap.h"
726 default:
727 aString.AssignLiteral("unknown");
728 return NS_OK;
731 #undef RELATIONTYPE
734 NS_IMETHODIMP
735 nsAccessibilityService::GetAccessibleFromCache(nsIDOMNode* aNode,
736 nsIAccessible** aAccessible)
738 NS_ENSURE_ARG_POINTER(aAccessible);
739 *aAccessible = nullptr;
740 if (!aNode)
741 return NS_OK;
743 nsCOMPtr<nsINode> node(do_QueryInterface(aNode));
744 if (!node)
745 return NS_ERROR_INVALID_ARG;
747 // Search for an accessible in each of our per document accessible object
748 // caches. If we don't find it, and the given node is itself a document, check
749 // our cache of document accessibles (document cache). Note usually shutdown
750 // document accessibles are not stored in the document cache, however an
751 // "unofficially" shutdown document (i.e. not from DocManager) can still
752 // exist in the document cache.
753 Accessible* accessible = FindAccessibleInCache(node);
754 if (!accessible) {
755 nsCOMPtr<nsIDocument> document(do_QueryInterface(node));
756 if (document)
757 accessible = GetExistingDocAccessible(document);
760 NS_IF_ADDREF(*aAccessible = accessible);
761 return NS_OK;
764 NS_IMETHODIMP
765 nsAccessibilityService::CreateAccessiblePivot(nsIAccessible* aRoot,
766 nsIAccessiblePivot** aPivot)
768 NS_ENSURE_ARG_POINTER(aPivot);
769 NS_ENSURE_ARG(aRoot);
770 *aPivot = nullptr;
772 nsRefPtr<Accessible> accessibleRoot(do_QueryObject(aRoot));
773 NS_ENSURE_TRUE(accessibleRoot, NS_ERROR_INVALID_ARG);
775 nsAccessiblePivot* pivot = new nsAccessiblePivot(accessibleRoot);
776 NS_ADDREF(*aPivot = pivot);
778 return NS_OK;
781 NS_IMETHODIMP
782 nsAccessibilityService::SetLogging(const nsACString& aModules)
784 #ifdef A11Y_LOG
785 logging::Enable(PromiseFlatCString(aModules));
786 #endif
787 return NS_OK;
790 NS_IMETHODIMP
791 nsAccessibilityService::IsLogged(const nsAString& aModule, bool* aIsLogged)
793 NS_ENSURE_ARG_POINTER(aIsLogged);
794 *aIsLogged = false;
796 #ifdef A11Y_LOG
797 *aIsLogged = logging::IsEnabled(aModule);
798 #endif
800 return NS_OK;
803 ////////////////////////////////////////////////////////////////////////////////
804 // nsAccessibilityService public
806 Accessible*
807 nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
808 Accessible* aContext,
809 bool* aIsSubtreeHidden)
811 NS_PRECONDITION(aContext && aNode && !gIsShutdown,
812 "Maybe let'd do a crash? Oh, yes, baby!");
814 if (aIsSubtreeHidden)
815 *aIsSubtreeHidden = false;
817 DocAccessible* document = aContext->Document();
819 // Check to see if we already have an accessible for this node in the cache.
820 // XXX: we don't have context check here. It doesn't really necessary until
821 // we have in-law children adoption.
822 Accessible* cachedAccessible = document->GetAccessible(aNode);
823 if (cachedAccessible)
824 return cachedAccessible;
826 // No cache entry, so we must create the accessible.
828 if (aNode->IsNodeOfType(nsINode::eDOCUMENT)) {
829 // If it's document node then ask accessible document loader for
830 // document accessible, otherwise return null.
831 nsCOMPtr<nsIDocument> document(do_QueryInterface(aNode));
832 return GetDocAccessible(document);
835 // We have a content node.
836 if (!aNode->GetCrossShadowCurrentDoc()) {
837 NS_WARNING("Creating accessible for node with no document");
838 return nullptr;
841 if (aNode->OwnerDoc() != document->DocumentNode()) {
842 NS_ERROR("Creating accessible for wrong document");
843 return nullptr;
846 if (!aNode->IsContent())
847 return nullptr;
849 nsIContent* content = aNode->AsContent();
850 nsIFrame* frame = content->GetPrimaryFrame();
852 // Check frame and its visibility. Note, hidden frame allows visible
853 // elements in subtree.
854 if (!frame || !frame->StyleVisibility()->IsVisible()) {
855 if (aIsSubtreeHidden && !frame)
856 *aIsSubtreeHidden = true;
858 return nullptr;
861 if (frame->GetContent() != content) {
862 // Not the main content for this frame. This happens because <area>
863 // elements return the image frame as their primary frame. The main content
864 // for the image frame is the image content. If the frame is not an image
865 // frame or the node is not an area element then null is returned.
866 // This setup will change when bug 135040 is fixed. Make sure we don't
867 // create area accessible here. Hopefully assertion below will handle that.
869 #ifdef DEBUG
870 nsImageFrame* imageFrame = do_QueryFrame(frame);
871 NS_ASSERTION(imageFrame && content->IsHTML() && content->Tag() == nsGkAtoms::area,
872 "Unknown case of not main content for the frame!");
873 #endif
874 return nullptr;
877 #ifdef DEBUG
878 nsImageFrame* imageFrame = do_QueryFrame(frame);
879 NS_ASSERTION(!imageFrame || !content->IsHTML() || content->Tag() != nsGkAtoms::area,
880 "Image map manages the area accessible creation!");
881 #endif
883 // Attempt to create an accessible based on what we know.
884 nsRefPtr<Accessible> newAcc;
886 // Create accessible for visible text frames.
887 if (content->IsNodeOfType(nsINode::eTEXT)) {
888 nsAutoString text;
889 frame->GetRenderedText(&text, nullptr, nullptr, 0, UINT32_MAX);
890 // Ignore not rendered text nodes and whitespace text nodes between table
891 // cells.
892 if (text.IsEmpty() ||
893 (aContext->IsTableRow() && nsCoreUtils::IsWhitespaceString(text))) {
894 if (aIsSubtreeHidden)
895 *aIsSubtreeHidden = true;
897 return nullptr;
900 newAcc = CreateAccessibleByFrameType(frame, content, aContext);
901 if (!aContext->IsAcceptableChild(newAcc))
902 return nullptr;
904 document->BindToDocument(newAcc, nullptr);
905 newAcc->AsTextLeaf()->SetText(text);
906 return newAcc;
909 bool isHTML = content->IsHTML();
910 if (isHTML && content->Tag() == nsGkAtoms::map) {
911 // Create hyper text accessible for HTML map if it is used to group links
912 // (see http://www.w3.org/TR/WCAG10-HTML-TECHS/#group-bypass). If the HTML
913 // map rect is empty then it is used for links grouping. Otherwise it should
914 // be used in conjunction with HTML image element and in this case we don't
915 // create any accessible for it and don't walk into it. The accessibles for
916 // HTML area (HTMLAreaAccessible) the map contains are attached as
917 // children of the appropriate accessible for HTML image
918 // (ImageAccessible).
919 if (nsLayoutUtils::GetAllInFlowRectsUnion(frame,
920 frame->GetParent()).IsEmpty()) {
921 if (aIsSubtreeHidden)
922 *aIsSubtreeHidden = true;
924 return nullptr;
927 newAcc = new HyperTextAccessibleWrap(content, document);
928 if (!aContext->IsAcceptableChild(newAcc))
929 return nullptr;
931 document->BindToDocument(newAcc, aria::GetRoleMap(aNode));
932 return newAcc;
935 nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aNode);
937 // If the element is focusable or global ARIA attribute is applied to it or
938 // it is referenced by ARIA relationship then treat role="presentation" on
939 // the element as the role is not there.
940 if (roleMapEntry && roleMapEntry->Is(nsGkAtoms::presentation)) {
941 if (!MustBeAccessible(content, document))
942 return nullptr;
944 roleMapEntry = nullptr;
947 if (!newAcc && isHTML) { // HTML accessibles
948 if (roleMapEntry) {
949 // Create pure ARIA grid/treegrid related accessibles if they weren't used
950 // on accessible HTML table elements.
951 if ((roleMapEntry->accTypes & eTableCell)) {
952 if (aContext->IsTableRow() &&
953 (frame->AccessibleType() != eHTMLTableCellType ||
954 aContext->GetContent() != content->GetParent())) {
955 newAcc = new ARIAGridCellAccessibleWrap(content, document);
958 } else if ((roleMapEntry->IsOfType(eTable)) &&
959 frame->AccessibleType() != eHTMLTableType) {
960 newAcc = new ARIAGridAccessibleWrap(content, document);
964 if (!newAcc) {
965 // Prefer to use markup (mostly tag name, perhaps attributes) to decide if
966 // and what kind of accessible to create.
967 newAcc = CreateHTMLAccessibleByMarkup(frame, content, aContext);
969 // Try using frame to do it.
970 if (!newAcc)
971 newAcc = CreateAccessibleByFrameType(frame, content, aContext);
973 // If table has strong ARIA role then all table descendants shouldn't
974 // expose their native roles.
975 if (!roleMapEntry && newAcc && aContext->HasStrongARIARole()) {
976 if (frame->AccessibleType() == eHTMLTableRowType) {
977 nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
978 if (!contextRoleMap->IsOfType(eTable))
979 roleMapEntry = &aria::gEmptyRoleMap;
981 } else if (frame->AccessibleType() == eHTMLTableCellType &&
982 aContext->ARIARoleMap() == &aria::gEmptyRoleMap) {
983 roleMapEntry = &aria::gEmptyRoleMap;
985 } else if (content->Tag() == nsGkAtoms::dt ||
986 content->Tag() == nsGkAtoms::li ||
987 content->Tag() == nsGkAtoms::dd ||
988 frame->AccessibleType() == eHTMLLiType) {
989 nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap();
990 if (!contextRoleMap->IsOfType(eList))
991 roleMapEntry = &aria::gEmptyRoleMap;
997 // Accessible XBL types and deck stuff are used in XUL only currently.
998 if (!newAcc && content->IsXUL()) {
999 // No accessible for not selected deck panel and its children.
1000 if (!aContext->IsXULTabpanels()) {
1001 nsDeckFrame* deckFrame = do_QueryFrame(frame->GetParent());
1002 if (deckFrame && deckFrame->GetSelectedBox() != frame) {
1003 if (aIsSubtreeHidden)
1004 *aIsSubtreeHidden = true;
1006 return nullptr;
1010 // XBL bindings may use @role attribute to point the accessible type
1011 // they belong to.
1012 newAcc = CreateAccessibleByType(content, document);
1014 // Any XUL box can be used as tabpanel, make sure we create a proper
1015 // accessible for it.
1016 if (!newAcc && aContext->IsXULTabpanels() &&
1017 content->GetParent() == aContext->GetContent()) {
1018 nsIAtom* frameType = frame->GetType();
1019 if (frameType == nsGkAtoms::boxFrame ||
1020 frameType == nsGkAtoms::scrollFrame) {
1021 newAcc = new XULTabpanelAccessible(content, document);
1026 if (!newAcc) {
1027 if (content->IsSVG()) {
1028 nsSVGPathGeometryFrame* pathGeometryFrame = do_QueryFrame(frame);
1029 if (pathGeometryFrame) {
1030 // A graphic elements: rect, circle, ellipse, line, path, polygon,
1031 // polyline and image. A 'use' and 'text' graphic elements require
1032 // special support.
1033 newAcc = new EnumRoleAccessible(content, document, roles::GRAPHIC);
1034 } else if (content->Tag() == nsGkAtoms::svg) {
1035 newAcc = new EnumRoleAccessible(content, document, roles::DIAGRAM);
1037 } else if (content->IsMathML()){
1038 if (content->Tag() == nsGkAtoms::math)
1039 newAcc = new EnumRoleAccessible(content, document, roles::EQUATION);
1040 else
1041 newAcc = new HyperTextAccessible(content, document);
1045 // If no accessible, see if we need to create a generic accessible because
1046 // of some property that makes this object interesting
1047 // We don't do this for <body>, <html>, <window>, <dialog> etc. which
1048 // correspond to the doc accessible and will be created in any case
1049 if (!newAcc && content->Tag() != nsGkAtoms::body && content->GetParent() &&
1050 (roleMapEntry || MustBeAccessible(content, document) ||
1051 (isHTML && nsCoreUtils::HasClickListener(content)))) {
1052 // This content is focusable or has an interesting dynamic content accessibility property.
1053 // If it's interesting we need it in the accessibility hierarchy so that events or
1054 // other accessibles can point to it, or so that it can hold a state, etc.
1055 if (isHTML) {
1056 // Interesting HTML container which may have selectable text and/or embedded objects
1057 newAcc = new HyperTextAccessibleWrap(content, document);
1058 } else { // XUL, SVG, MathML etc.
1059 // Interesting generic non-HTML container
1060 newAcc = new AccessibleWrap(content, document);
1064 if (!newAcc || !aContext->IsAcceptableChild(newAcc))
1065 return nullptr;
1067 document->BindToDocument(newAcc, roleMapEntry);
1068 return newAcc;
1071 ////////////////////////////////////////////////////////////////////////////////
1072 // nsAccessibilityService private
1074 bool
1075 nsAccessibilityService::Init()
1077 // Initialize accessible document manager.
1078 if (!DocManager::Init())
1079 return false;
1081 // Add observers.
1082 nsCOMPtr<nsIObserverService> observerService =
1083 mozilla::services::GetObserverService();
1084 if (!observerService)
1085 return false;
1087 observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
1089 static const char16_t kInitIndicator[] = { '1', 0 };
1090 observerService->NotifyObservers(nullptr, "a11y-init-or-shutdown", kInitIndicator);
1092 #ifdef A11Y_LOG
1093 logging::CheckEnv();
1094 #endif
1096 gApplicationAccessible = new ApplicationAccessibleWrap();
1097 NS_ADDREF(gApplicationAccessible); // will release in Shutdown()
1099 #ifdef MOZ_CRASHREPORTER
1100 CrashReporter::
1101 AnnotateCrashReport(NS_LITERAL_CSTRING("Accessibility"),
1102 NS_LITERAL_CSTRING("Active"));
1103 #endif
1105 #ifdef XP_WIN
1106 sPendingPlugins = new nsTArray<nsCOMPtr<nsIContent> >;
1107 sPluginTimers = new nsTArray<nsCOMPtr<nsITimer> >;
1108 #endif
1110 gIsShutdown = false;
1112 // Now its safe to start platform accessibility.
1113 PlatformInit();
1115 return true;
1118 void
1119 nsAccessibilityService::Shutdown()
1121 // Remove observers.
1122 nsCOMPtr<nsIObserverService> observerService =
1123 mozilla::services::GetObserverService();
1124 if (observerService) {
1125 observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
1127 static const char16_t kShutdownIndicator[] = { '0', 0 };
1128 observerService->NotifyObservers(nullptr, "a11y-init-or-shutdown", kShutdownIndicator);
1131 // Stop accessible document loader.
1132 DocManager::Shutdown();
1134 SelectionManager::Shutdown();
1136 #ifdef XP_WIN
1137 sPendingPlugins = nullptr;
1139 uint32_t timerCount = sPluginTimers->Length();
1140 for (uint32_t i = 0; i < timerCount; i++)
1141 sPluginTimers->ElementAt(i)->Cancel();
1143 sPluginTimers = nullptr;
1144 #endif
1146 // Application is going to be closed, shutdown accessibility and mark
1147 // accessibility service as shutdown to prevent calls of its methods.
1148 // Don't null accessibility service static member at this point to be safe
1149 // if someone will try to operate with it.
1151 NS_ASSERTION(!gIsShutdown, "Accessibility was shutdown already");
1153 gIsShutdown = true;
1155 PlatformShutdown();
1156 gApplicationAccessible->Shutdown();
1157 NS_RELEASE(gApplicationAccessible);
1158 gApplicationAccessible = nullptr;
1161 already_AddRefed<Accessible>
1162 nsAccessibilityService::CreateAccessibleByType(nsIContent* aContent,
1163 DocAccessible* aDoc)
1165 nsAutoString role;
1166 for (const nsXBLBinding* binding = aContent->GetXBLBinding(); binding; binding = binding->GetBaseBinding()) {
1167 nsIContent* bindingElm = binding->PrototypeBinding()->GetBindingElement();
1168 bindingElm->GetAttr(kNameSpaceID_None, nsGkAtoms::role, role);
1169 if (!role.IsEmpty())
1170 break;
1173 if (role.IsEmpty() || role.EqualsLiteral("none"))
1174 return nullptr;
1176 if (role.EqualsLiteral("outerdoc")) {
1177 nsRefPtr<Accessible> accessible = new OuterDocAccessible(aContent, aDoc);
1178 return accessible.forget();
1181 nsRefPtr<Accessible> accessible;
1182 #ifdef MOZ_XUL
1183 // XUL controls
1184 if (role.EqualsLiteral("xul:alert")) {
1185 accessible = new XULAlertAccessible(aContent, aDoc);
1187 } else if (role.EqualsLiteral("xul:button")) {
1188 accessible = new XULButtonAccessible(aContent, aDoc);
1190 } else if (role.EqualsLiteral("xul:checkbox")) {
1191 accessible = new XULCheckboxAccessible(aContent, aDoc);
1193 } else if (role.EqualsLiteral("xul:colorpicker")) {
1194 accessible = new XULColorPickerAccessible(aContent, aDoc);
1196 } else if (role.EqualsLiteral("xul:colorpickertile")) {
1197 accessible = new XULColorPickerTileAccessible(aContent, aDoc);
1199 } else if (role.EqualsLiteral("xul:combobox")) {
1200 accessible = new XULComboboxAccessible(aContent, aDoc);
1202 } else if (role.EqualsLiteral("xul:tabpanels")) {
1203 accessible = new XULTabpanelsAccessible(aContent, aDoc);
1205 } else if (role.EqualsLiteral("xul:dropmarker")) {
1206 accessible = new XULDropmarkerAccessible(aContent, aDoc);
1208 } else if (role.EqualsLiteral("xul:groupbox")) {
1209 accessible = new XULGroupboxAccessible(aContent, aDoc);
1211 } else if (role.EqualsLiteral("xul:image")) {
1212 if (aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::onclick)) {
1213 accessible = new XULToolbarButtonAccessible(aContent, aDoc);
1215 } else {
1216 // Don't include nameless images in accessible tree.
1217 if (!aContent->HasAttr(kNameSpaceID_None,
1218 nsGkAtoms::tooltiptext))
1219 return nullptr;
1221 accessible = new ImageAccessibleWrap(aContent, aDoc);
1224 } else if (role.EqualsLiteral("xul:link")) {
1225 accessible = new XULLinkAccessible(aContent, aDoc);
1227 } else if (role.EqualsLiteral("xul:listbox")) {
1228 accessible = new XULListboxAccessibleWrap(aContent, aDoc);
1230 } else if (role.EqualsLiteral("xul:listcell")) {
1231 // Only create cells if there's more than one per row.
1232 nsIContent* listItem = aContent->GetParent();
1233 if (!listItem)
1234 return nullptr;
1236 for (nsIContent* child = listItem->GetFirstChild(); child;
1237 child = child->GetNextSibling()) {
1238 if (child->IsXUL(nsGkAtoms::listcell) && child != aContent) {
1239 accessible = new XULListCellAccessibleWrap(aContent, aDoc);
1240 break;
1244 } else if (role.EqualsLiteral("xul:listhead")) {
1245 accessible = new XULColumAccessible(aContent, aDoc);
1247 } else if (role.EqualsLiteral("xul:listheader")) {
1248 accessible = new XULColumnItemAccessible(aContent, aDoc);
1250 } else if (role.EqualsLiteral("xul:listitem")) {
1251 accessible = new XULListitemAccessible(aContent, aDoc);
1253 } else if (role.EqualsLiteral("xul:menubar")) {
1254 accessible = new XULMenubarAccessible(aContent, aDoc);
1256 } else if (role.EqualsLiteral("xul:menulist")) {
1257 accessible = new XULComboboxAccessible(aContent, aDoc);
1259 } else if (role.EqualsLiteral("xul:menuitem")) {
1260 accessible = new XULMenuitemAccessibleWrap(aContent, aDoc);
1262 } else if (role.EqualsLiteral("xul:menupopup")) {
1263 #ifdef MOZ_ACCESSIBILITY_ATK
1264 // ATK considers this node to be redundant when within menubars, and it makes menu
1265 // navigation with assistive technologies more difficult
1266 // XXX In the future we will should this for consistency across the nsIAccessible
1267 // implementations on each platform for a consistent scripting environment, but
1268 // then strip out redundant accessibles in the AccessibleWrap class for each platform.
1269 nsIContent *parent = aContent->GetParent();
1270 if (parent && parent->IsXUL() && parent->Tag() == nsGkAtoms::menu)
1271 return nullptr;
1272 #endif
1274 accessible = new XULMenupopupAccessible(aContent, aDoc);
1276 } else if(role.EqualsLiteral("xul:menuseparator")) {
1277 accessible = new XULMenuSeparatorAccessible(aContent, aDoc);
1279 } else if(role.EqualsLiteral("xul:pane")) {
1280 accessible = new EnumRoleAccessible(aContent, aDoc, roles::PANE);
1282 } else if (role.EqualsLiteral("xul:panel")) {
1283 if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautofocus,
1284 nsGkAtoms::_true, eCaseMatters))
1285 accessible = new XULAlertAccessible(aContent, aDoc);
1286 else
1287 accessible = new EnumRoleAccessible(aContent, aDoc, roles::PANE);
1289 } else if (role.EqualsLiteral("xul:progressmeter")) {
1290 accessible = new XULProgressMeterAccessible(aContent, aDoc);
1292 } else if (role.EqualsLiteral("xul:statusbar")) {
1293 accessible = new XULStatusBarAccessible(aContent, aDoc);
1295 } else if (role.EqualsLiteral("xul:scale")) {
1296 accessible = new XULSliderAccessible(aContent, aDoc);
1298 } else if (role.EqualsLiteral("xul:radiobutton")) {
1299 accessible = new XULRadioButtonAccessible(aContent, aDoc);
1301 } else if (role.EqualsLiteral("xul:radiogroup")) {
1302 accessible = new XULRadioGroupAccessible(aContent, aDoc);
1304 } else if (role.EqualsLiteral("xul:tab")) {
1305 accessible = new XULTabAccessible(aContent, aDoc);
1307 } else if (role.EqualsLiteral("xul:tabs")) {
1308 accessible = new XULTabsAccessible(aContent, aDoc);
1310 } else if (role.EqualsLiteral("xul:text")) {
1311 accessible = new XULLabelAccessible(aContent, aDoc);
1313 } else if (role.EqualsLiteral("xul:textbox")) {
1314 accessible = new EnumRoleAccessible(aContent, aDoc, roles::SECTION);
1316 } else if (role.EqualsLiteral("xul:thumb")) {
1317 accessible = new XULThumbAccessible(aContent, aDoc);
1319 } else if (role.EqualsLiteral("xul:tree")) {
1320 accessible = CreateAccessibleForXULTree(aContent, aDoc);
1322 } else if (role.EqualsLiteral("xul:treecolumns")) {
1323 accessible = new XULTreeColumAccessible(aContent, aDoc);
1325 } else if (role.EqualsLiteral("xul:treecolumnitem")) {
1326 accessible = new XULColumnItemAccessible(aContent, aDoc);
1328 } else if (role.EqualsLiteral("xul:toolbar")) {
1329 accessible = new XULToolbarAccessible(aContent, aDoc);
1331 } else if (role.EqualsLiteral("xul:toolbarseparator")) {
1332 accessible = new XULToolbarSeparatorAccessible(aContent, aDoc);
1334 } else if (role.EqualsLiteral("xul:tooltip")) {
1335 accessible = new XULTooltipAccessible(aContent, aDoc);
1337 } else if (role.EqualsLiteral("xul:toolbarbutton")) {
1338 accessible = new XULToolbarButtonAccessible(aContent, aDoc);
1341 #endif // MOZ_XUL
1343 return accessible.forget();
1346 already_AddRefed<Accessible>
1347 nsAccessibilityService::CreateHTMLAccessibleByMarkup(nsIFrame* aFrame,
1348 nsIContent* aContent,
1349 Accessible* aContext)
1351 DocAccessible* document = aContext->Document();
1352 if (aContext->IsTableRow()) {
1353 if (nsCoreUtils::IsHTMLTableHeader(aContent) &&
1354 aContext->GetContent() == aContent->GetParent()) {
1355 nsRefPtr<Accessible> accessible =
1356 new HTMLTableHeaderCellAccessibleWrap(aContent, document);
1357 return accessible.forget();
1360 return nullptr;
1363 // This method assumes we're in an HTML namespace.
1364 nsIAtom* tag = aContent->Tag();
1365 if (tag == nsGkAtoms::figcaption) {
1366 nsRefPtr<Accessible> accessible =
1367 new HTMLFigcaptionAccessible(aContent, document);
1368 return accessible.forget();
1371 if (tag == nsGkAtoms::figure) {
1372 nsRefPtr<Accessible> accessible =
1373 new HTMLFigureAccessible(aContent, document);
1374 return accessible.forget();
1377 if (tag == nsGkAtoms::legend) {
1378 nsRefPtr<Accessible> accessible =
1379 new HTMLLegendAccessible(aContent, document);
1380 return accessible.forget();
1383 if (tag == nsGkAtoms::option) {
1384 nsRefPtr<Accessible> accessible =
1385 new HTMLSelectOptionAccessible(aContent, document);
1386 return accessible.forget();
1389 if (tag == nsGkAtoms::optgroup) {
1390 nsRefPtr<Accessible> accessible =
1391 new HTMLSelectOptGroupAccessible(aContent, document);
1392 return accessible.forget();
1395 if (tag == nsGkAtoms::ul || tag == nsGkAtoms::ol ||
1396 tag == nsGkAtoms::dl) {
1397 nsRefPtr<Accessible> accessible =
1398 new HTMLListAccessible(aContent, document);
1399 return accessible.forget();
1402 if (tag == nsGkAtoms::a) {
1403 // Only some roles truly enjoy life as HTMLLinkAccessibles, for details
1404 // see closed bug 494807.
1405 nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aContent);
1406 if (roleMapEntry && roleMapEntry->role != roles::NOTHING &&
1407 roleMapEntry->role != roles::LINK) {
1408 nsRefPtr<Accessible> accessible =
1409 new HyperTextAccessibleWrap(aContent, document);
1410 return accessible.forget();
1413 nsRefPtr<Accessible> accessible =
1414 new HTMLLinkAccessible(aContent, document);
1415 return accessible.forget();
1418 if (aContext->IsList()) {
1419 // If list item is a child of accessible list then create an accessible for
1420 // it unconditionally by tag name. nsBlockFrame creates the list item
1421 // accessible for other elements styled as list items.
1422 if (aContext->GetContent() == aContent->GetParent()) {
1423 if (tag == nsGkAtoms::dt || tag == nsGkAtoms::li) {
1424 nsRefPtr<Accessible> accessible =
1425 new HTMLLIAccessible(aContent, document);
1426 return accessible.forget();
1429 if (tag == nsGkAtoms::dd) {
1430 nsRefPtr<Accessible> accessible =
1431 new HyperTextAccessibleWrap(aContent, document);
1432 return accessible.forget();
1436 return nullptr;
1439 if (tag == nsGkAtoms::abbr ||
1440 tag == nsGkAtoms::acronym ||
1441 tag == nsGkAtoms::article ||
1442 tag == nsGkAtoms::aside ||
1443 tag == nsGkAtoms::blockquote ||
1444 tag == nsGkAtoms::form ||
1445 tag == nsGkAtoms::footer ||
1446 tag == nsGkAtoms::header ||
1447 tag == nsGkAtoms::h1 ||
1448 tag == nsGkAtoms::h2 ||
1449 tag == nsGkAtoms::h3 ||
1450 tag == nsGkAtoms::h4 ||
1451 tag == nsGkAtoms::h5 ||
1452 tag == nsGkAtoms::h6 ||
1453 tag == nsGkAtoms::nav ||
1454 tag == nsGkAtoms::q ||
1455 tag == nsGkAtoms::section) {
1456 nsRefPtr<Accessible> accessible =
1457 new HyperTextAccessibleWrap(aContent, document);
1458 return accessible.forget();
1461 if (tag == nsGkAtoms::label) {
1462 nsRefPtr<Accessible> accessible =
1463 new HTMLLabelAccessible(aContent, document);
1464 return accessible.forget();
1467 if (tag == nsGkAtoms::output) {
1468 nsRefPtr<Accessible> accessible =
1469 new HTMLOutputAccessible(aContent, document);
1470 return accessible.forget();
1473 if (tag == nsGkAtoms::progress) {
1474 nsRefPtr<Accessible> accessible =
1475 new HTMLProgressMeterAccessible(aContent, document);
1476 return accessible.forget();
1479 return nullptr;
1482 already_AddRefed<Accessible>
1483 nsAccessibilityService::CreateAccessibleByFrameType(nsIFrame* aFrame,
1484 nsIContent* aContent,
1485 Accessible* aContext)
1487 DocAccessible* document = aContext->Document();
1489 nsRefPtr<Accessible> newAcc;
1490 switch (aFrame->AccessibleType()) {
1491 case eNoType:
1492 return nullptr;
1493 case eHTMLBRType:
1494 newAcc = new HTMLBRAccessible(aContent, document);
1495 break;
1496 case eHTMLButtonType:
1497 newAcc = new HTMLButtonAccessible(aContent, document);
1498 break;
1499 case eHTMLCanvasType:
1500 newAcc = new HTMLCanvasAccessible(aContent, document);
1501 break;
1502 case eHTMLCaptionType:
1503 if (aContext->IsTable() &&
1504 aContext->GetContent() == aContent->GetParent()) {
1505 newAcc = new HTMLCaptionAccessible(aContent, document);
1507 break;
1508 case eHTMLCheckboxType:
1509 newAcc = new HTMLCheckboxAccessible(aContent, document);
1510 break;
1511 case eHTMLComboboxType:
1512 newAcc = new HTMLComboboxAccessible(aContent, document);
1513 break;
1514 case eHTMLFileInputType:
1515 newAcc = new HTMLFileInputAccessible(aContent, document);
1516 break;
1517 case eHTMLGroupboxType:
1518 newAcc = new HTMLGroupboxAccessible(aContent, document);
1519 break;
1520 case eHTMLHRType:
1521 newAcc = new HTMLHRAccessible(aContent, document);
1522 break;
1523 case eHTMLImageMapType:
1524 newAcc = new HTMLImageMapAccessible(aContent, document);
1525 break;
1526 case eHTMLLiType:
1527 if (aContext->IsList() &&
1528 aContext->GetContent() == aContent->GetParent()) {
1529 newAcc = new HTMLLIAccessible(aContent, document);
1531 break;
1532 case eHTMLSelectListType:
1533 newAcc = new HTMLSelectListAccessible(aContent, document);
1534 break;
1535 case eHTMLMediaType:
1536 newAcc = new EnumRoleAccessible(aContent, document, roles::GROUPING);
1537 break;
1538 case eHTMLRadioButtonType:
1539 newAcc = new HTMLRadioButtonAccessible(aContent, document);
1540 break;
1541 case eHTMLRangeType:
1542 newAcc = new HTMLRangeAccessible(aContent, document);
1543 break;
1544 case eHTMLSpinnerType:
1545 newAcc = new HTMLSpinnerAccessible(aContent, document);
1546 break;
1547 case eHTMLTableType:
1548 newAcc = new HTMLTableAccessibleWrap(aContent, document);
1549 break;
1550 case eHTMLTableCellType:
1551 // Accessible HTML table cell should be a child of accessible HTML table
1552 // or its row (CSS HTML tables are polite to the used markup at
1553 // certain degree).
1554 // Otherwise create a generic text accessible to avoid text jamming
1555 // when reading by AT.
1556 if (aContext->IsHTMLTableRow() || aContext->IsHTMLTable())
1557 newAcc = new HTMLTableCellAccessibleWrap(aContent, document);
1558 else
1559 newAcc = new HyperTextAccessibleWrap(aContent, document);
1560 break;
1562 case eHTMLTableRowType: {
1563 // Accessible HTML table row may be a child of tbody/tfoot/thead of
1564 // accessible HTML table or a direct child of accessible of HTML table.
1565 Accessible* table = aContext->IsTable() ? aContext : nullptr;
1566 if (!table && aContext->Parent() && aContext->Parent()->IsTable())
1567 table = aContext->Parent();
1569 if (table) {
1570 nsIContent* parentContent = aContent->GetParent();
1571 nsIFrame* parentFrame = parentContent->GetPrimaryFrame();
1572 if (parentFrame->GetType() != nsGkAtoms::tableOuterFrame) {
1573 parentContent = parentContent->GetParent();
1574 parentFrame = parentContent->GetPrimaryFrame();
1577 if (parentFrame->GetType() == nsGkAtoms::tableOuterFrame &&
1578 table->GetContent() == parentContent) {
1579 newAcc = new HTMLTableRowAccessible(aContent, document);
1582 break;
1584 case eHTMLTextFieldType:
1585 newAcc = new HTMLTextFieldAccessible(aContent, document);
1586 break;
1587 case eHyperTextType:
1588 if (aContent->Tag() != nsGkAtoms::dt && aContent->Tag() != nsGkAtoms::dd)
1589 newAcc = new HyperTextAccessibleWrap(aContent, document);
1590 break;
1592 case eImageType:
1593 newAcc = new ImageAccessibleWrap(aContent, document);
1594 break;
1595 case eOuterDocType:
1596 newAcc = new OuterDocAccessible(aContent, document);
1597 break;
1598 case ePluginType: {
1599 nsObjectFrame* objectFrame = do_QueryFrame(aFrame);
1600 newAcc = CreatePluginAccessible(objectFrame, aContent, aContext);
1601 break;
1603 case eTextLeafType:
1604 newAcc = new TextLeafAccessibleWrap(aContent, document);
1605 break;
1606 default:
1607 MOZ_ASSERT(false);
1608 break;
1611 return newAcc.forget();
1614 ////////////////////////////////////////////////////////////////////////////////
1615 // nsIAccessibilityService (DON'T put methods here)
1617 Accessible*
1618 nsAccessibilityService::AddNativeRootAccessible(void* aAtkAccessible)
1620 #ifdef MOZ_ACCESSIBILITY_ATK
1621 ApplicationAccessible* applicationAcc = ApplicationAcc();
1622 if (!applicationAcc)
1623 return nullptr;
1625 GtkWindowAccessible* nativeWnd =
1626 new GtkWindowAccessible(static_cast<AtkObject*>(aAtkAccessible));
1628 if (applicationAcc->AppendChild(nativeWnd))
1629 return nativeWnd;
1630 #endif
1632 return nullptr;
1635 void
1636 nsAccessibilityService::RemoveNativeRootAccessible(Accessible* aAccessible)
1638 #ifdef MOZ_ACCESSIBILITY_ATK
1639 ApplicationAccessible* applicationAcc = ApplicationAcc();
1641 if (applicationAcc)
1642 applicationAcc->RemoveChild(aAccessible);
1643 #endif
1646 ////////////////////////////////////////////////////////////////////////////////
1647 // NS_GetAccessibilityService
1648 ////////////////////////////////////////////////////////////////////////////////
1651 * Return accessibility service; creating one if necessary.
1653 nsresult
1654 NS_GetAccessibilityService(nsIAccessibilityService** aResult)
1656 NS_ENSURE_TRUE(aResult, NS_ERROR_NULL_POINTER);
1657 *aResult = nullptr;
1659 if (nsAccessibilityService::gAccessibilityService) {
1660 NS_ADDREF(*aResult = nsAccessibilityService::gAccessibilityService);
1661 return NS_OK;
1664 nsRefPtr<nsAccessibilityService> service = new nsAccessibilityService();
1665 NS_ENSURE_TRUE(service, NS_ERROR_OUT_OF_MEMORY);
1667 if (!service->Init()) {
1668 service->Shutdown();
1669 return NS_ERROR_FAILURE;
1672 statistics::A11yInitialized();
1674 nsAccessibilityService::gAccessibilityService = service;
1675 NS_ADDREF(*aResult = service);
1677 return NS_OK;
1680 ////////////////////////////////////////////////////////////////////////////////
1681 // nsAccessibilityService private (DON'T put methods here)
1683 #ifdef MOZ_XUL
1684 already_AddRefed<Accessible>
1685 nsAccessibilityService::CreateAccessibleForXULTree(nsIContent* aContent,
1686 DocAccessible* aDoc)
1688 nsIContent* child = nsTreeUtils::GetDescendantChild(aContent,
1689 nsGkAtoms::treechildren);
1690 if (!child)
1691 return nullptr;
1693 nsTreeBodyFrame* treeFrame = do_QueryFrame(child->GetPrimaryFrame());
1694 if (!treeFrame)
1695 return nullptr;
1697 nsRefPtr<nsTreeColumns> treeCols = treeFrame->Columns();
1698 int32_t count = 0;
1699 treeCols->GetCount(&count);
1701 // Outline of list accessible.
1702 if (count == 1) {
1703 nsRefPtr<Accessible> accessible =
1704 new XULTreeAccessible(aContent, aDoc, treeFrame);
1705 return accessible.forget();
1708 // Table or tree table accessible.
1709 nsRefPtr<Accessible> accessible =
1710 new XULTreeGridAccessibleWrap(aContent, aDoc, treeFrame);
1711 return accessible.forget();
1713 #endif
1715 ////////////////////////////////////////////////////////////////////////////////
1716 // Services
1717 ////////////////////////////////////////////////////////////////////////////////
1719 namespace mozilla {
1720 namespace a11y {
1722 FocusManager*
1723 FocusMgr()
1725 return nsAccessibilityService::gAccessibilityService;
1728 SelectionManager*
1729 SelectionMgr()
1731 return nsAccessibilityService::gAccessibilityService;
1734 ApplicationAccessible*
1735 ApplicationAcc()
1737 return nsAccessibilityService::gApplicationAccessible;
1740 EPlatformDisabledState
1741 PlatformDisabledState()
1743 static int disabledState = 0xff;
1745 if (disabledState == 0xff) {
1746 disabledState = Preferences::GetInt("accessibility.force_disabled", 0);
1747 if (disabledState < ePlatformIsForceEnabled)
1748 disabledState = ePlatformIsForceEnabled;
1749 else if (disabledState > ePlatformIsDisabled)
1750 disabledState = ePlatformIsDisabled;
1753 return (EPlatformDisabledState)disabledState;