|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "nsAccessibilityService.h" |
|
7 |
|
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" |
|
37 |
|
38 #ifdef MOZ_ACCESSIBILITY_ATK |
|
39 #include "AtkSocketAccessible.h" |
|
40 #endif |
|
41 |
|
42 #ifdef XP_WIN |
|
43 #include "mozilla/a11y/Compatibility.h" |
|
44 #include "HTMLWin32ObjectAccessible.h" |
|
45 #include "mozilla/StaticPtr.h" |
|
46 #endif |
|
47 |
|
48 #ifdef A11Y_LOG |
|
49 #include "Logging.h" |
|
50 #endif |
|
51 |
|
52 #ifdef MOZ_CRASHREPORTER |
|
53 #include "nsExceptionHandler.h" |
|
54 #endif |
|
55 |
|
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" |
|
71 |
|
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 |
|
84 |
|
85 #if defined(XP_WIN) || defined(MOZ_ACCESSIBILITY_ATK) |
|
86 #include "nsNPAPIPluginInstance.h" |
|
87 #endif |
|
88 |
|
89 using namespace mozilla; |
|
90 using namespace mozilla::a11y; |
|
91 using namespace mozilla::dom; |
|
92 |
|
93 //////////////////////////////////////////////////////////////////////////////// |
|
94 // Statics |
|
95 //////////////////////////////////////////////////////////////////////////////// |
|
96 |
|
97 /** |
|
98 * Return true if the element must be accessible. |
|
99 */ |
|
100 static bool |
|
101 MustBeAccessible(nsIContent* aContent, DocAccessible* aDocument) |
|
102 { |
|
103 if (aContent->GetPrimaryFrame()->IsFocusable()) |
|
104 return true; |
|
105 |
|
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 |
|
114 |
|
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; |
|
120 } |
|
121 } |
|
122 } |
|
123 |
|
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); |
|
129 |
|
130 return false; |
|
131 } |
|
132 |
|
133 //////////////////////////////////////////////////////////////////////////////// |
|
134 // nsAccessibilityService |
|
135 //////////////////////////////////////////////////////////////////////////////// |
|
136 |
|
137 nsAccessibilityService *nsAccessibilityService::gAccessibilityService = nullptr; |
|
138 ApplicationAccessible* nsAccessibilityService::gApplicationAccessible = nullptr; |
|
139 bool nsAccessibilityService::gIsShutdown = true; |
|
140 |
|
141 nsAccessibilityService::nsAccessibilityService() : |
|
142 DocManager(), FocusManager() |
|
143 { |
|
144 } |
|
145 |
|
146 nsAccessibilityService::~nsAccessibilityService() |
|
147 { |
|
148 NS_ASSERTION(gIsShutdown, "Accessibility wasn't shutdown!"); |
|
149 gAccessibilityService = nullptr; |
|
150 } |
|
151 |
|
152 //////////////////////////////////////////////////////////////////////////////// |
|
153 // nsISupports |
|
154 |
|
155 NS_IMPL_ISUPPORTS_INHERITED(nsAccessibilityService, |
|
156 DocManager, |
|
157 nsIAccessibilityService, |
|
158 nsIAccessibleRetrieval, |
|
159 nsIObserver, |
|
160 nsISelectionListener) // from SelectionManager |
|
161 |
|
162 //////////////////////////////////////////////////////////////////////////////// |
|
163 // nsIObserver |
|
164 |
|
165 NS_IMETHODIMP |
|
166 nsAccessibilityService::Observe(nsISupports *aSubject, const char *aTopic, |
|
167 const char16_t *aData) |
|
168 { |
|
169 if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) |
|
170 Shutdown(); |
|
171 |
|
172 return NS_OK; |
|
173 } |
|
174 |
|
175 // nsIAccessibilityService |
|
176 void |
|
177 nsAccessibilityService::NotifyOfAnchorJumpTo(nsIContent* aTargetNode) |
|
178 { |
|
179 nsIDocument* documentNode = aTargetNode->GetCurrentDoc(); |
|
180 if (documentNode) { |
|
181 DocAccessible* document = GetDocAccessible(documentNode); |
|
182 if (document) |
|
183 document->SetAnchorJump(aTargetNode); |
|
184 } |
|
185 } |
|
186 |
|
187 // nsIAccessibilityService |
|
188 void |
|
189 nsAccessibilityService::FireAccessibleEvent(uint32_t aEvent, |
|
190 Accessible* aTarget) |
|
191 { |
|
192 nsEventShell::FireEvent(aEvent, aTarget); |
|
193 } |
|
194 |
|
195 //////////////////////////////////////////////////////////////////////////////// |
|
196 // nsIAccessibilityService |
|
197 |
|
198 Accessible* |
|
199 nsAccessibilityService::GetRootDocumentAccessible(nsIPresShell* aPresShell, |
|
200 bool aCanCreate) |
|
201 { |
|
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(); |
|
212 } |
|
213 |
|
214 return aCanCreate ? GetDocAccessible(ps) : ps->GetDocAccessible(); |
|
215 } |
|
216 } |
|
217 return nullptr; |
|
218 } |
|
219 |
|
220 #ifdef XP_WIN |
|
221 static StaticAutoPtr<nsTArray<nsCOMPtr<nsIContent> > > sPendingPlugins; |
|
222 static StaticAutoPtr<nsTArray<nsCOMPtr<nsITimer> > > sPluginTimers; |
|
223 |
|
224 class PluginTimerCallBack MOZ_FINAL : public nsITimerCallback |
|
225 { |
|
226 public: |
|
227 PluginTimerCallBack(nsIContent* aContent) : mContent(aContent) {} |
|
228 |
|
229 NS_DECL_ISUPPORTS |
|
230 |
|
231 NS_IMETHODIMP Notify(nsITimer* aTimer) MOZ_FINAL |
|
232 { |
|
233 if (!mContent->IsInDoc()) |
|
234 return NS_OK; |
|
235 |
|
236 nsIPresShell* ps = mContent->OwnerDoc()->GetShell(); |
|
237 if (ps) { |
|
238 DocAccessible* doc = ps->GetDocAccessible(); |
|
239 if (doc) { |
|
240 // Make sure that if we created an accessible for the plugin that wasn't |
|
241 // a plugin accessible we remove it before creating the right accessible. |
|
242 doc->RecreateAccessible(mContent); |
|
243 sPluginTimers->RemoveElement(aTimer); |
|
244 return NS_OK; |
|
245 } |
|
246 } |
|
247 |
|
248 // We couldn't get a doc accessible so presumably the document went away. |
|
249 // In this case don't leak our ref to the content or timer. |
|
250 sPendingPlugins->RemoveElement(mContent); |
|
251 sPluginTimers->RemoveElement(aTimer); |
|
252 return NS_OK; |
|
253 } |
|
254 |
|
255 private: |
|
256 nsCOMPtr<nsIContent> mContent; |
|
257 }; |
|
258 |
|
259 NS_IMPL_ISUPPORTS(PluginTimerCallBack, nsITimerCallback) |
|
260 #endif |
|
261 |
|
262 already_AddRefed<Accessible> |
|
263 nsAccessibilityService::CreatePluginAccessible(nsObjectFrame* aFrame, |
|
264 nsIContent* aContent, |
|
265 Accessible* aContext) |
|
266 { |
|
267 // nsObjectFrame means a plugin, so we need to use the accessibility support |
|
268 // of the plugin. |
|
269 if (aFrame->GetRect().IsEmpty()) |
|
270 return nullptr; |
|
271 |
|
272 #if defined(XP_WIN) || defined(MOZ_ACCESSIBILITY_ATK) |
|
273 nsRefPtr<nsNPAPIPluginInstance> pluginInstance; |
|
274 if (NS_SUCCEEDED(aFrame->GetPluginInstance(getter_AddRefs(pluginInstance))) && |
|
275 pluginInstance) { |
|
276 #ifdef XP_WIN |
|
277 if (!sPendingPlugins->Contains(aContent) && |
|
278 (Preferences::GetBool("accessibility.delay_plugins") || |
|
279 Compatibility::IsJAWS() || Compatibility::IsWE())) { |
|
280 nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID); |
|
281 nsRefPtr<PluginTimerCallBack> cb = new PluginTimerCallBack(aContent); |
|
282 timer->InitWithCallback(cb, Preferences::GetUint("accessibility.delay_plugin_time"), |
|
283 nsITimer::TYPE_ONE_SHOT); |
|
284 sPluginTimers->AppendElement(timer); |
|
285 sPendingPlugins->AppendElement(aContent); |
|
286 return nullptr; |
|
287 } |
|
288 |
|
289 // We need to remove aContent from the pending plugins here to avoid |
|
290 // reentrancy. When the timer fires it calls |
|
291 // DocAccessible::ContentInserted() which does the work async. |
|
292 sPendingPlugins->RemoveElement(aContent); |
|
293 |
|
294 // Note: pluginPort will be null if windowless. |
|
295 HWND pluginPort = nullptr; |
|
296 aFrame->GetPluginPort(&pluginPort); |
|
297 |
|
298 nsRefPtr<Accessible> accessible = |
|
299 new HTMLWin32ObjectOwnerAccessible(aContent, aContext->Document(), |
|
300 pluginPort); |
|
301 return accessible.forget(); |
|
302 |
|
303 #elif MOZ_ACCESSIBILITY_ATK |
|
304 if (!AtkSocketAccessible::gCanEmbed) |
|
305 return nullptr; |
|
306 |
|
307 // Note this calls into the plugin, so crazy things may happen and aFrame |
|
308 // may go away. |
|
309 nsCString plugId; |
|
310 nsresult rv = pluginInstance->GetValueFromPlugin( |
|
311 NPPVpluginNativeAccessibleAtkPlugId, &plugId); |
|
312 if (NS_SUCCEEDED(rv) && !plugId.IsEmpty()) { |
|
313 nsRefPtr<AtkSocketAccessible> socketAccessible = |
|
314 new AtkSocketAccessible(aContent, aContext->Document(), plugId); |
|
315 |
|
316 return socketAccessible.forget(); |
|
317 } |
|
318 #endif |
|
319 } |
|
320 #endif |
|
321 |
|
322 return nullptr; |
|
323 } |
|
324 |
|
325 void |
|
326 nsAccessibilityService::DeckPanelSwitched(nsIPresShell* aPresShell, |
|
327 nsIContent* aDeckNode, |
|
328 nsIFrame* aPrevBoxFrame, |
|
329 nsIFrame* aCurrentBoxFrame) |
|
330 { |
|
331 // Ignore tabpanels elements (a deck having an accessible) since their |
|
332 // children are accessible not depending on selected tab. |
|
333 DocAccessible* document = GetDocAccessible(aPresShell); |
|
334 if (!document || document->HasAccessible(aDeckNode)) |
|
335 return; |
|
336 |
|
337 if (aPrevBoxFrame) { |
|
338 nsIContent* panelNode = aPrevBoxFrame->GetContent(); |
|
339 #ifdef A11Y_LOG |
|
340 if (logging::IsEnabled(logging::eTree)) { |
|
341 logging::MsgBegin("TREE", "deck panel unselected"); |
|
342 logging::Node("container", panelNode); |
|
343 logging::Node("content", aDeckNode); |
|
344 logging::MsgEnd(); |
|
345 } |
|
346 #endif |
|
347 |
|
348 document->ContentRemoved(aDeckNode, panelNode); |
|
349 } |
|
350 |
|
351 if (aCurrentBoxFrame) { |
|
352 nsIContent* panelNode = aCurrentBoxFrame->GetContent(); |
|
353 #ifdef A11Y_LOG |
|
354 if (logging::IsEnabled(logging::eTree)) { |
|
355 logging::MsgBegin("TREE", "deck panel selected"); |
|
356 logging::Node("container", panelNode); |
|
357 logging::Node("content", aDeckNode); |
|
358 logging::MsgEnd(); |
|
359 } |
|
360 #endif |
|
361 |
|
362 document->ContentInserted(aDeckNode, panelNode, panelNode->GetNextSibling()); |
|
363 } |
|
364 } |
|
365 |
|
366 void |
|
367 nsAccessibilityService::ContentRangeInserted(nsIPresShell* aPresShell, |
|
368 nsIContent* aContainer, |
|
369 nsIContent* aStartChild, |
|
370 nsIContent* aEndChild) |
|
371 { |
|
372 #ifdef A11Y_LOG |
|
373 if (logging::IsEnabled(logging::eTree)) { |
|
374 logging::MsgBegin("TREE", "content inserted"); |
|
375 logging::Node("container", aContainer); |
|
376 for (nsIContent* child = aStartChild; child != aEndChild; |
|
377 child = child->GetNextSibling()) { |
|
378 logging::Node("content", child); |
|
379 } |
|
380 logging::MsgEnd(); |
|
381 logging::Stack(); |
|
382 } |
|
383 #endif |
|
384 |
|
385 DocAccessible* docAccessible = GetDocAccessible(aPresShell); |
|
386 if (docAccessible) |
|
387 docAccessible->ContentInserted(aContainer, aStartChild, aEndChild); |
|
388 } |
|
389 |
|
390 void |
|
391 nsAccessibilityService::ContentRemoved(nsIPresShell* aPresShell, |
|
392 nsIContent* aContainer, |
|
393 nsIContent* aChild) |
|
394 { |
|
395 #ifdef A11Y_LOG |
|
396 if (logging::IsEnabled(logging::eTree)) { |
|
397 logging::MsgBegin("TREE", "content removed"); |
|
398 logging::Node("container", aContainer); |
|
399 logging::Node("content", aChild); |
|
400 logging::MsgEnd(); |
|
401 logging::Stack(); |
|
402 } |
|
403 #endif |
|
404 |
|
405 DocAccessible* docAccessible = GetDocAccessible(aPresShell); |
|
406 if (docAccessible) |
|
407 docAccessible->ContentRemoved(aContainer, aChild); |
|
408 } |
|
409 |
|
410 void |
|
411 nsAccessibilityService::UpdateText(nsIPresShell* aPresShell, |
|
412 nsIContent* aContent) |
|
413 { |
|
414 DocAccessible* document = GetDocAccessible(aPresShell); |
|
415 if (document) |
|
416 document->UpdateText(aContent); |
|
417 } |
|
418 |
|
419 void |
|
420 nsAccessibilityService::TreeViewChanged(nsIPresShell* aPresShell, |
|
421 nsIContent* aContent, |
|
422 nsITreeView* aView) |
|
423 { |
|
424 DocAccessible* document = GetDocAccessible(aPresShell); |
|
425 if (document) { |
|
426 Accessible* accessible = document->GetAccessible(aContent); |
|
427 if (accessible) { |
|
428 XULTreeAccessible* treeAcc = accessible->AsXULTree(); |
|
429 if (treeAcc) |
|
430 treeAcc->TreeViewChanged(aView); |
|
431 } |
|
432 } |
|
433 } |
|
434 |
|
435 void |
|
436 nsAccessibilityService::RangeValueChanged(nsIPresShell* aPresShell, |
|
437 nsIContent* aContent) |
|
438 { |
|
439 DocAccessible* document = GetDocAccessible(aPresShell); |
|
440 if (document) { |
|
441 Accessible* accessible = document->GetAccessible(aContent); |
|
442 if (accessible) { |
|
443 document->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, |
|
444 accessible); |
|
445 } |
|
446 } |
|
447 } |
|
448 |
|
449 void |
|
450 nsAccessibilityService::UpdateListBullet(nsIPresShell* aPresShell, |
|
451 nsIContent* aHTMLListItemContent, |
|
452 bool aHasBullet) |
|
453 { |
|
454 DocAccessible* document = GetDocAccessible(aPresShell); |
|
455 if (document) { |
|
456 Accessible* accessible = document->GetAccessible(aHTMLListItemContent); |
|
457 if (accessible) { |
|
458 HTMLLIAccessible* listItem = accessible->AsHTMLListItem(); |
|
459 if (listItem) |
|
460 listItem->UpdateBullet(aHasBullet); |
|
461 } |
|
462 } |
|
463 } |
|
464 |
|
465 void |
|
466 nsAccessibilityService::UpdateImageMap(nsImageFrame* aImageFrame) |
|
467 { |
|
468 nsIPresShell* presShell = aImageFrame->PresContext()->PresShell(); |
|
469 DocAccessible* document = GetDocAccessible(presShell); |
|
470 if (document) { |
|
471 Accessible* accessible = |
|
472 document->GetAccessible(aImageFrame->GetContent()); |
|
473 if (accessible) { |
|
474 HTMLImageMapAccessible* imageMap = accessible->AsImageMap(); |
|
475 if (imageMap) { |
|
476 imageMap->UpdateChildAreas(); |
|
477 return; |
|
478 } |
|
479 |
|
480 // If image map was initialized after we created an accessible (that'll |
|
481 // be an image accessible) then recreate it. |
|
482 RecreateAccessible(presShell, aImageFrame->GetContent()); |
|
483 } |
|
484 } |
|
485 } |
|
486 |
|
487 void |
|
488 nsAccessibilityService::UpdateLabelValue(nsIPresShell* aPresShell, |
|
489 nsIContent* aLabelElm, |
|
490 const nsString& aNewValue) |
|
491 { |
|
492 DocAccessible* document = GetDocAccessible(aPresShell); |
|
493 if (document) { |
|
494 Accessible* accessible = document->GetAccessible(aLabelElm); |
|
495 if (accessible) { |
|
496 XULLabelAccessible* xulLabel = accessible->AsXULLabel(); |
|
497 NS_ASSERTION(xulLabel, |
|
498 "UpdateLabelValue was called for wrong accessible!"); |
|
499 if (xulLabel) |
|
500 xulLabel->UpdateLabelValue(aNewValue); |
|
501 } |
|
502 } |
|
503 } |
|
504 |
|
505 void |
|
506 nsAccessibilityService::PresShellActivated(nsIPresShell* aPresShell) |
|
507 { |
|
508 DocAccessible* document = aPresShell->GetDocAccessible(); |
|
509 if (document) { |
|
510 RootAccessible* rootDocument = document->RootAccessible(); |
|
511 NS_ASSERTION(rootDocument, "Entirely broken tree: no root document!"); |
|
512 if (rootDocument) |
|
513 rootDocument->DocumentActivated(document); |
|
514 } |
|
515 } |
|
516 |
|
517 void |
|
518 nsAccessibilityService::RecreateAccessible(nsIPresShell* aPresShell, |
|
519 nsIContent* aContent) |
|
520 { |
|
521 DocAccessible* document = GetDocAccessible(aPresShell); |
|
522 if (document) |
|
523 document->RecreateAccessible(aContent); |
|
524 } |
|
525 |
|
526 //////////////////////////////////////////////////////////////////////////////// |
|
527 // nsIAccessibleRetrieval |
|
528 |
|
529 NS_IMETHODIMP |
|
530 nsAccessibilityService::GetApplicationAccessible(nsIAccessible** aAccessibleApplication) |
|
531 { |
|
532 NS_ENSURE_ARG_POINTER(aAccessibleApplication); |
|
533 |
|
534 NS_IF_ADDREF(*aAccessibleApplication = ApplicationAcc()); |
|
535 |
|
536 return NS_OK; |
|
537 } |
|
538 |
|
539 NS_IMETHODIMP |
|
540 nsAccessibilityService::GetAccessibleFor(nsIDOMNode *aNode, |
|
541 nsIAccessible **aAccessible) |
|
542 { |
|
543 NS_ENSURE_ARG_POINTER(aAccessible); |
|
544 *aAccessible = nullptr; |
|
545 if (!aNode) |
|
546 return NS_OK; |
|
547 |
|
548 nsCOMPtr<nsINode> node(do_QueryInterface(aNode)); |
|
549 if (!node) |
|
550 return NS_ERROR_INVALID_ARG; |
|
551 |
|
552 DocAccessible* document = GetDocAccessible(node->OwnerDoc()); |
|
553 if (document) |
|
554 NS_IF_ADDREF(*aAccessible = document->GetAccessible(node)); |
|
555 |
|
556 return NS_OK; |
|
557 } |
|
558 |
|
559 NS_IMETHODIMP |
|
560 nsAccessibilityService::GetStringRole(uint32_t aRole, nsAString& aString) |
|
561 { |
|
562 #define ROLE(geckoRole, stringRole, atkRole, \ |
|
563 macRole, msaaRole, ia2Role, nameRule) \ |
|
564 case roles::geckoRole: \ |
|
565 CopyUTF8toUTF16(stringRole, aString); \ |
|
566 return NS_OK; |
|
567 |
|
568 switch (aRole) { |
|
569 #include "RoleMap.h" |
|
570 default: |
|
571 aString.AssignLiteral("unknown"); |
|
572 return NS_OK; |
|
573 } |
|
574 |
|
575 #undef ROLE |
|
576 } |
|
577 |
|
578 NS_IMETHODIMP |
|
579 nsAccessibilityService::GetStringStates(uint32_t aState, uint32_t aExtraState, |
|
580 nsISupports **aStringStates) |
|
581 { |
|
582 nsRefPtr<DOMStringList> stringStates = new DOMStringList(); |
|
583 |
|
584 uint64_t state = nsAccUtils::To64State(aState, aExtraState); |
|
585 |
|
586 // states |
|
587 if (state & states::UNAVAILABLE) |
|
588 stringStates->Add(NS_LITERAL_STRING("unavailable")); |
|
589 if (state & states::SELECTED) |
|
590 stringStates->Add(NS_LITERAL_STRING("selected")); |
|
591 if (state & states::FOCUSED) |
|
592 stringStates->Add(NS_LITERAL_STRING("focused")); |
|
593 if (state & states::PRESSED) |
|
594 stringStates->Add(NS_LITERAL_STRING("pressed")); |
|
595 if (state & states::CHECKED) |
|
596 stringStates->Add(NS_LITERAL_STRING("checked")); |
|
597 if (state & states::MIXED) |
|
598 stringStates->Add(NS_LITERAL_STRING("mixed")); |
|
599 if (state & states::READONLY) |
|
600 stringStates->Add(NS_LITERAL_STRING("readonly")); |
|
601 if (state & states::HOTTRACKED) |
|
602 stringStates->Add(NS_LITERAL_STRING("hottracked")); |
|
603 if (state & states::DEFAULT) |
|
604 stringStates->Add(NS_LITERAL_STRING("default")); |
|
605 if (state & states::EXPANDED) |
|
606 stringStates->Add(NS_LITERAL_STRING("expanded")); |
|
607 if (state & states::COLLAPSED) |
|
608 stringStates->Add(NS_LITERAL_STRING("collapsed")); |
|
609 if (state & states::BUSY) |
|
610 stringStates->Add(NS_LITERAL_STRING("busy")); |
|
611 if (state & states::FLOATING) |
|
612 stringStates->Add(NS_LITERAL_STRING("floating")); |
|
613 if (state & states::ANIMATED) |
|
614 stringStates->Add(NS_LITERAL_STRING("animated")); |
|
615 if (state & states::INVISIBLE) |
|
616 stringStates->Add(NS_LITERAL_STRING("invisible")); |
|
617 if (state & states::OFFSCREEN) |
|
618 stringStates->Add(NS_LITERAL_STRING("offscreen")); |
|
619 if (state & states::SIZEABLE) |
|
620 stringStates->Add(NS_LITERAL_STRING("sizeable")); |
|
621 if (state & states::MOVEABLE) |
|
622 stringStates->Add(NS_LITERAL_STRING("moveable")); |
|
623 if (state & states::SELFVOICING) |
|
624 stringStates->Add(NS_LITERAL_STRING("selfvoicing")); |
|
625 if (state & states::FOCUSABLE) |
|
626 stringStates->Add(NS_LITERAL_STRING("focusable")); |
|
627 if (state & states::SELECTABLE) |
|
628 stringStates->Add(NS_LITERAL_STRING("selectable")); |
|
629 if (state & states::LINKED) |
|
630 stringStates->Add(NS_LITERAL_STRING("linked")); |
|
631 if (state & states::TRAVERSED) |
|
632 stringStates->Add(NS_LITERAL_STRING("traversed")); |
|
633 if (state & states::MULTISELECTABLE) |
|
634 stringStates->Add(NS_LITERAL_STRING("multiselectable")); |
|
635 if (state & states::EXTSELECTABLE) |
|
636 stringStates->Add(NS_LITERAL_STRING("extselectable")); |
|
637 if (state & states::PROTECTED) |
|
638 stringStates->Add(NS_LITERAL_STRING("protected")); |
|
639 if (state & states::HASPOPUP) |
|
640 stringStates->Add(NS_LITERAL_STRING("haspopup")); |
|
641 if (state & states::REQUIRED) |
|
642 stringStates->Add(NS_LITERAL_STRING("required")); |
|
643 if (state & states::ALERT) |
|
644 stringStates->Add(NS_LITERAL_STRING("alert")); |
|
645 if (state & states::INVALID) |
|
646 stringStates->Add(NS_LITERAL_STRING("invalid")); |
|
647 if (state & states::CHECKABLE) |
|
648 stringStates->Add(NS_LITERAL_STRING("checkable")); |
|
649 |
|
650 // extraStates |
|
651 if (state & states::SUPPORTS_AUTOCOMPLETION) |
|
652 stringStates->Add(NS_LITERAL_STRING("autocompletion")); |
|
653 if (state & states::DEFUNCT) |
|
654 stringStates->Add(NS_LITERAL_STRING("defunct")); |
|
655 if (state & states::SELECTABLE_TEXT) |
|
656 stringStates->Add(NS_LITERAL_STRING("selectable text")); |
|
657 if (state & states::EDITABLE) |
|
658 stringStates->Add(NS_LITERAL_STRING("editable")); |
|
659 if (state & states::ACTIVE) |
|
660 stringStates->Add(NS_LITERAL_STRING("active")); |
|
661 if (state & states::MODAL) |
|
662 stringStates->Add(NS_LITERAL_STRING("modal")); |
|
663 if (state & states::MULTI_LINE) |
|
664 stringStates->Add(NS_LITERAL_STRING("multi line")); |
|
665 if (state & states::HORIZONTAL) |
|
666 stringStates->Add(NS_LITERAL_STRING("horizontal")); |
|
667 if (state & states::OPAQUE1) |
|
668 stringStates->Add(NS_LITERAL_STRING("opaque")); |
|
669 if (state & states::SINGLE_LINE) |
|
670 stringStates->Add(NS_LITERAL_STRING("single line")); |
|
671 if (state & states::TRANSIENT) |
|
672 stringStates->Add(NS_LITERAL_STRING("transient")); |
|
673 if (state & states::VERTICAL) |
|
674 stringStates->Add(NS_LITERAL_STRING("vertical")); |
|
675 if (state & states::STALE) |
|
676 stringStates->Add(NS_LITERAL_STRING("stale")); |
|
677 if (state & states::ENABLED) |
|
678 stringStates->Add(NS_LITERAL_STRING("enabled")); |
|
679 if (state & states::SENSITIVE) |
|
680 stringStates->Add(NS_LITERAL_STRING("sensitive")); |
|
681 if (state & states::EXPANDABLE) |
|
682 stringStates->Add(NS_LITERAL_STRING("expandable")); |
|
683 |
|
684 //unknown states |
|
685 if (!stringStates->Length()) |
|
686 stringStates->Add(NS_LITERAL_STRING("unknown")); |
|
687 |
|
688 stringStates.forget(aStringStates); |
|
689 return NS_OK; |
|
690 } |
|
691 |
|
692 // nsIAccessibleRetrieval::getStringEventType() |
|
693 NS_IMETHODIMP |
|
694 nsAccessibilityService::GetStringEventType(uint32_t aEventType, |
|
695 nsAString& aString) |
|
696 { |
|
697 NS_ASSERTION(nsIAccessibleEvent::EVENT_LAST_ENTRY == ArrayLength(kEventTypeNames), |
|
698 "nsIAccessibleEvent constants are out of sync to kEventTypeNames"); |
|
699 |
|
700 if (aEventType >= ArrayLength(kEventTypeNames)) { |
|
701 aString.AssignLiteral("unknown"); |
|
702 return NS_OK; |
|
703 } |
|
704 |
|
705 CopyUTF8toUTF16(kEventTypeNames[aEventType], aString); |
|
706 return NS_OK; |
|
707 } |
|
708 |
|
709 // nsIAccessibleRetrieval::getStringRelationType() |
|
710 NS_IMETHODIMP |
|
711 nsAccessibilityService::GetStringRelationType(uint32_t aRelationType, |
|
712 nsAString& aString) |
|
713 { |
|
714 NS_ENSURE_ARG(aRelationType <= static_cast<uint32_t>(RelationType::LAST)); |
|
715 |
|
716 #define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \ |
|
717 case RelationType::geckoType: \ |
|
718 aString.AssignLiteral(geckoTypeName); \ |
|
719 return NS_OK; |
|
720 |
|
721 RelationType relationType = static_cast<RelationType>(aRelationType); |
|
722 switch (relationType) { |
|
723 #include "RelationTypeMap.h" |
|
724 default: |
|
725 aString.AssignLiteral("unknown"); |
|
726 return NS_OK; |
|
727 } |
|
728 |
|
729 #undef RELATIONTYPE |
|
730 } |
|
731 |
|
732 NS_IMETHODIMP |
|
733 nsAccessibilityService::GetAccessibleFromCache(nsIDOMNode* aNode, |
|
734 nsIAccessible** aAccessible) |
|
735 { |
|
736 NS_ENSURE_ARG_POINTER(aAccessible); |
|
737 *aAccessible = nullptr; |
|
738 if (!aNode) |
|
739 return NS_OK; |
|
740 |
|
741 nsCOMPtr<nsINode> node(do_QueryInterface(aNode)); |
|
742 if (!node) |
|
743 return NS_ERROR_INVALID_ARG; |
|
744 |
|
745 // Search for an accessible in each of our per document accessible object |
|
746 // caches. If we don't find it, and the given node is itself a document, check |
|
747 // our cache of document accessibles (document cache). Note usually shutdown |
|
748 // document accessibles are not stored in the document cache, however an |
|
749 // "unofficially" shutdown document (i.e. not from DocManager) can still |
|
750 // exist in the document cache. |
|
751 Accessible* accessible = FindAccessibleInCache(node); |
|
752 if (!accessible) { |
|
753 nsCOMPtr<nsIDocument> document(do_QueryInterface(node)); |
|
754 if (document) |
|
755 accessible = GetExistingDocAccessible(document); |
|
756 } |
|
757 |
|
758 NS_IF_ADDREF(*aAccessible = accessible); |
|
759 return NS_OK; |
|
760 } |
|
761 |
|
762 NS_IMETHODIMP |
|
763 nsAccessibilityService::CreateAccessiblePivot(nsIAccessible* aRoot, |
|
764 nsIAccessiblePivot** aPivot) |
|
765 { |
|
766 NS_ENSURE_ARG_POINTER(aPivot); |
|
767 NS_ENSURE_ARG(aRoot); |
|
768 *aPivot = nullptr; |
|
769 |
|
770 nsRefPtr<Accessible> accessibleRoot(do_QueryObject(aRoot)); |
|
771 NS_ENSURE_TRUE(accessibleRoot, NS_ERROR_INVALID_ARG); |
|
772 |
|
773 nsAccessiblePivot* pivot = new nsAccessiblePivot(accessibleRoot); |
|
774 NS_ADDREF(*aPivot = pivot); |
|
775 |
|
776 return NS_OK; |
|
777 } |
|
778 |
|
779 NS_IMETHODIMP |
|
780 nsAccessibilityService::SetLogging(const nsACString& aModules) |
|
781 { |
|
782 #ifdef A11Y_LOG |
|
783 logging::Enable(PromiseFlatCString(aModules)); |
|
784 #endif |
|
785 return NS_OK; |
|
786 } |
|
787 |
|
788 NS_IMETHODIMP |
|
789 nsAccessibilityService::IsLogged(const nsAString& aModule, bool* aIsLogged) |
|
790 { |
|
791 NS_ENSURE_ARG_POINTER(aIsLogged); |
|
792 *aIsLogged = false; |
|
793 |
|
794 #ifdef A11Y_LOG |
|
795 *aIsLogged = logging::IsEnabled(aModule); |
|
796 #endif |
|
797 |
|
798 return NS_OK; |
|
799 } |
|
800 |
|
801 //////////////////////////////////////////////////////////////////////////////// |
|
802 // nsAccessibilityService public |
|
803 |
|
804 Accessible* |
|
805 nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode, |
|
806 Accessible* aContext, |
|
807 bool* aIsSubtreeHidden) |
|
808 { |
|
809 NS_PRECONDITION(aContext && aNode && !gIsShutdown, |
|
810 "Maybe let'd do a crash? Oh, yes, baby!"); |
|
811 |
|
812 if (aIsSubtreeHidden) |
|
813 *aIsSubtreeHidden = false; |
|
814 |
|
815 DocAccessible* document = aContext->Document(); |
|
816 |
|
817 // Check to see if we already have an accessible for this node in the cache. |
|
818 // XXX: we don't have context check here. It doesn't really necessary until |
|
819 // we have in-law children adoption. |
|
820 Accessible* cachedAccessible = document->GetAccessible(aNode); |
|
821 if (cachedAccessible) |
|
822 return cachedAccessible; |
|
823 |
|
824 // No cache entry, so we must create the accessible. |
|
825 |
|
826 if (aNode->IsNodeOfType(nsINode::eDOCUMENT)) { |
|
827 // If it's document node then ask accessible document loader for |
|
828 // document accessible, otherwise return null. |
|
829 nsCOMPtr<nsIDocument> document(do_QueryInterface(aNode)); |
|
830 return GetDocAccessible(document); |
|
831 } |
|
832 |
|
833 // We have a content node. |
|
834 if (!aNode->IsInDoc()) { |
|
835 NS_WARNING("Creating accessible for node with no document"); |
|
836 return nullptr; |
|
837 } |
|
838 |
|
839 if (aNode->OwnerDoc() != document->DocumentNode()) { |
|
840 NS_ERROR("Creating accessible for wrong document"); |
|
841 return nullptr; |
|
842 } |
|
843 |
|
844 if (!aNode->IsContent()) |
|
845 return nullptr; |
|
846 |
|
847 nsIContent* content = aNode->AsContent(); |
|
848 nsIFrame* frame = content->GetPrimaryFrame(); |
|
849 |
|
850 // Check frame and its visibility. Note, hidden frame allows visible |
|
851 // elements in subtree. |
|
852 if (!frame || !frame->StyleVisibility()->IsVisible()) { |
|
853 if (aIsSubtreeHidden && !frame) |
|
854 *aIsSubtreeHidden = true; |
|
855 |
|
856 return nullptr; |
|
857 } |
|
858 |
|
859 if (frame->GetContent() != content) { |
|
860 // Not the main content for this frame. This happens because <area> |
|
861 // elements return the image frame as their primary frame. The main content |
|
862 // for the image frame is the image content. If the frame is not an image |
|
863 // frame or the node is not an area element then null is returned. |
|
864 // This setup will change when bug 135040 is fixed. Make sure we don't |
|
865 // create area accessible here. Hopefully assertion below will handle that. |
|
866 |
|
867 #ifdef DEBUG |
|
868 nsImageFrame* imageFrame = do_QueryFrame(frame); |
|
869 NS_ASSERTION(imageFrame && content->IsHTML() && content->Tag() == nsGkAtoms::area, |
|
870 "Unknown case of not main content for the frame!"); |
|
871 #endif |
|
872 return nullptr; |
|
873 } |
|
874 |
|
875 #ifdef DEBUG |
|
876 nsImageFrame* imageFrame = do_QueryFrame(frame); |
|
877 NS_ASSERTION(!imageFrame || !content->IsHTML() || content->Tag() != nsGkAtoms::area, |
|
878 "Image map manages the area accessible creation!"); |
|
879 #endif |
|
880 |
|
881 // Attempt to create an accessible based on what we know. |
|
882 nsRefPtr<Accessible> newAcc; |
|
883 |
|
884 // Create accessible for visible text frames. |
|
885 if (content->IsNodeOfType(nsINode::eTEXT)) { |
|
886 nsAutoString text; |
|
887 frame->GetRenderedText(&text, nullptr, nullptr, 0, UINT32_MAX); |
|
888 // Ignore not rendered text nodes and whitespace text nodes between table |
|
889 // cells. |
|
890 if (text.IsEmpty() || |
|
891 (aContext->IsTableRow() && nsCoreUtils::IsWhitespaceString(text))) { |
|
892 if (aIsSubtreeHidden) |
|
893 *aIsSubtreeHidden = true; |
|
894 |
|
895 return nullptr; |
|
896 } |
|
897 |
|
898 newAcc = CreateAccessibleByFrameType(frame, content, aContext); |
|
899 if (!aContext->IsAcceptableChild(newAcc)) |
|
900 return nullptr; |
|
901 |
|
902 document->BindToDocument(newAcc, nullptr); |
|
903 newAcc->AsTextLeaf()->SetText(text); |
|
904 return newAcc; |
|
905 } |
|
906 |
|
907 bool isHTML = content->IsHTML(); |
|
908 if (isHTML && content->Tag() == nsGkAtoms::map) { |
|
909 // Create hyper text accessible for HTML map if it is used to group links |
|
910 // (see http://www.w3.org/TR/WCAG10-HTML-TECHS/#group-bypass). If the HTML |
|
911 // map rect is empty then it is used for links grouping. Otherwise it should |
|
912 // be used in conjunction with HTML image element and in this case we don't |
|
913 // create any accessible for it and don't walk into it. The accessibles for |
|
914 // HTML area (HTMLAreaAccessible) the map contains are attached as |
|
915 // children of the appropriate accessible for HTML image |
|
916 // (ImageAccessible). |
|
917 if (nsLayoutUtils::GetAllInFlowRectsUnion(frame, |
|
918 frame->GetParent()).IsEmpty()) { |
|
919 if (aIsSubtreeHidden) |
|
920 *aIsSubtreeHidden = true; |
|
921 |
|
922 return nullptr; |
|
923 } |
|
924 |
|
925 newAcc = new HyperTextAccessibleWrap(content, document); |
|
926 if (!aContext->IsAcceptableChild(newAcc)) |
|
927 return nullptr; |
|
928 |
|
929 document->BindToDocument(newAcc, aria::GetRoleMap(aNode)); |
|
930 return newAcc; |
|
931 } |
|
932 |
|
933 nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aNode); |
|
934 |
|
935 // If the element is focusable or global ARIA attribute is applied to it or |
|
936 // it is referenced by ARIA relationship then treat role="presentation" on |
|
937 // the element as the role is not there. |
|
938 if (roleMapEntry && roleMapEntry->Is(nsGkAtoms::presentation)) { |
|
939 if (!MustBeAccessible(content, document)) |
|
940 return nullptr; |
|
941 |
|
942 roleMapEntry = nullptr; |
|
943 } |
|
944 |
|
945 if (!newAcc && isHTML) { // HTML accessibles |
|
946 if (roleMapEntry) { |
|
947 // Create pure ARIA grid/treegrid related accessibles if they weren't used |
|
948 // on accessible HTML table elements. |
|
949 if ((roleMapEntry->accTypes & eTableCell)) { |
|
950 if (aContext->IsTableRow() && |
|
951 (frame->AccessibleType() != eHTMLTableCellType || |
|
952 aContext->GetContent() != content->GetParent())) { |
|
953 newAcc = new ARIAGridCellAccessibleWrap(content, document); |
|
954 } |
|
955 |
|
956 } else if ((roleMapEntry->IsOfType(eTable)) && |
|
957 frame->AccessibleType() != eHTMLTableType) { |
|
958 newAcc = new ARIAGridAccessibleWrap(content, document); |
|
959 } |
|
960 } |
|
961 |
|
962 if (!newAcc) { |
|
963 // Prefer to use markup (mostly tag name, perhaps attributes) to decide if |
|
964 // and what kind of accessible to create. |
|
965 newAcc = CreateHTMLAccessibleByMarkup(frame, content, aContext); |
|
966 |
|
967 // Try using frame to do it. |
|
968 if (!newAcc) |
|
969 newAcc = CreateAccessibleByFrameType(frame, content, aContext); |
|
970 |
|
971 // If table has strong ARIA role then all table descendants shouldn't |
|
972 // expose their native roles. |
|
973 if (!roleMapEntry && newAcc) { |
|
974 if (frame->AccessibleType() == eHTMLTableRowType) { |
|
975 nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap(); |
|
976 if (contextRoleMap && !(contextRoleMap->IsOfType(eTable))) |
|
977 roleMapEntry = &aria::gEmptyRoleMap; |
|
978 |
|
979 } else if (frame->AccessibleType() == eHTMLTableCellType && |
|
980 aContext->ARIARoleMap() == &aria::gEmptyRoleMap) { |
|
981 roleMapEntry = &aria::gEmptyRoleMap; |
|
982 |
|
983 } else if (content->Tag() == nsGkAtoms::dt || |
|
984 content->Tag() == nsGkAtoms::li || |
|
985 content->Tag() == nsGkAtoms::dd || |
|
986 frame->AccessibleType() == eHTMLLiType) { |
|
987 nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap(); |
|
988 if (contextRoleMap && !(contextRoleMap->IsOfType(eList))) |
|
989 roleMapEntry = &aria::gEmptyRoleMap; |
|
990 } |
|
991 } |
|
992 } |
|
993 } |
|
994 |
|
995 // Accessible XBL types and deck stuff are used in XUL only currently. |
|
996 if (!newAcc && content->IsXUL()) { |
|
997 // No accessible for not selected deck panel and its children. |
|
998 if (!aContext->IsXULTabpanels()) { |
|
999 nsDeckFrame* deckFrame = do_QueryFrame(frame->GetParent()); |
|
1000 if (deckFrame && deckFrame->GetSelectedBox() != frame) { |
|
1001 if (aIsSubtreeHidden) |
|
1002 *aIsSubtreeHidden = true; |
|
1003 |
|
1004 return nullptr; |
|
1005 } |
|
1006 } |
|
1007 |
|
1008 // XBL bindings may use @role attribute to point the accessible type |
|
1009 // they belong to. |
|
1010 newAcc = CreateAccessibleByType(content, document); |
|
1011 |
|
1012 // Any XUL box can be used as tabpanel, make sure we create a proper |
|
1013 // accessible for it. |
|
1014 if (!newAcc && aContext->IsXULTabpanels() && |
|
1015 content->GetParent() == aContext->GetContent()) { |
|
1016 nsIAtom* frameType = frame->GetType(); |
|
1017 if (frameType == nsGkAtoms::boxFrame || |
|
1018 frameType == nsGkAtoms::scrollFrame) { |
|
1019 newAcc = new XULTabpanelAccessible(content, document); |
|
1020 } |
|
1021 } |
|
1022 } |
|
1023 |
|
1024 if (!newAcc) { |
|
1025 if (content->IsSVG()) { |
|
1026 nsSVGPathGeometryFrame* pathGeometryFrame = do_QueryFrame(frame); |
|
1027 if (pathGeometryFrame) { |
|
1028 // A graphic elements: rect, circle, ellipse, line, path, polygon, |
|
1029 // polyline and image. A 'use' and 'text' graphic elements require |
|
1030 // special support. |
|
1031 newAcc = new EnumRoleAccessible(content, document, roles::GRAPHIC); |
|
1032 } else if (content->Tag() == nsGkAtoms::svg) { |
|
1033 newAcc = new EnumRoleAccessible(content, document, roles::DIAGRAM); |
|
1034 } |
|
1035 } else if (content->IsMathML()){ |
|
1036 if (content->Tag() == nsGkAtoms::math) |
|
1037 newAcc = new EnumRoleAccessible(content, document, roles::EQUATION); |
|
1038 else |
|
1039 newAcc = new HyperTextAccessible(content, document); |
|
1040 } |
|
1041 } |
|
1042 |
|
1043 // If no accessible, see if we need to create a generic accessible because |
|
1044 // of some property that makes this object interesting |
|
1045 // We don't do this for <body>, <html>, <window>, <dialog> etc. which |
|
1046 // correspond to the doc accessible and will be created in any case |
|
1047 if (!newAcc && content->Tag() != nsGkAtoms::body && content->GetParent() && |
|
1048 (roleMapEntry || MustBeAccessible(content, document) || |
|
1049 (isHTML && nsCoreUtils::HasClickListener(content)))) { |
|
1050 // This content is focusable or has an interesting dynamic content accessibility property. |
|
1051 // If it's interesting we need it in the accessibility hierarchy so that events or |
|
1052 // other accessibles can point to it, or so that it can hold a state, etc. |
|
1053 if (isHTML) { |
|
1054 // Interesting HTML container which may have selectable text and/or embedded objects |
|
1055 newAcc = new HyperTextAccessibleWrap(content, document); |
|
1056 } else { // XUL, SVG, MathML etc. |
|
1057 // Interesting generic non-HTML container |
|
1058 newAcc = new AccessibleWrap(content, document); |
|
1059 } |
|
1060 } |
|
1061 |
|
1062 if (!newAcc || !aContext->IsAcceptableChild(newAcc)) |
|
1063 return nullptr; |
|
1064 |
|
1065 document->BindToDocument(newAcc, roleMapEntry); |
|
1066 return newAcc; |
|
1067 } |
|
1068 |
|
1069 //////////////////////////////////////////////////////////////////////////////// |
|
1070 // nsAccessibilityService private |
|
1071 |
|
1072 bool |
|
1073 nsAccessibilityService::Init() |
|
1074 { |
|
1075 // Initialize accessible document manager. |
|
1076 if (!DocManager::Init()) |
|
1077 return false; |
|
1078 |
|
1079 // Add observers. |
|
1080 nsCOMPtr<nsIObserverService> observerService = |
|
1081 mozilla::services::GetObserverService(); |
|
1082 if (!observerService) |
|
1083 return false; |
|
1084 |
|
1085 observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); |
|
1086 |
|
1087 static const char16_t kInitIndicator[] = { '1', 0 }; |
|
1088 observerService->NotifyObservers(nullptr, "a11y-init-or-shutdown", kInitIndicator); |
|
1089 |
|
1090 #ifdef A11Y_LOG |
|
1091 logging::CheckEnv(); |
|
1092 #endif |
|
1093 |
|
1094 gApplicationAccessible = new ApplicationAccessibleWrap(); |
|
1095 NS_ADDREF(gApplicationAccessible); // will release in Shutdown() |
|
1096 |
|
1097 #ifdef MOZ_CRASHREPORTER |
|
1098 CrashReporter:: |
|
1099 AnnotateCrashReport(NS_LITERAL_CSTRING("Accessibility"), |
|
1100 NS_LITERAL_CSTRING("Active")); |
|
1101 #endif |
|
1102 |
|
1103 #ifdef XP_WIN |
|
1104 sPendingPlugins = new nsTArray<nsCOMPtr<nsIContent> >; |
|
1105 sPluginTimers = new nsTArray<nsCOMPtr<nsITimer> >; |
|
1106 #endif |
|
1107 |
|
1108 gIsShutdown = false; |
|
1109 |
|
1110 // Now its safe to start platform accessibility. |
|
1111 PlatformInit(); |
|
1112 |
|
1113 return true; |
|
1114 } |
|
1115 |
|
1116 void |
|
1117 nsAccessibilityService::Shutdown() |
|
1118 { |
|
1119 // Remove observers. |
|
1120 nsCOMPtr<nsIObserverService> observerService = |
|
1121 mozilla::services::GetObserverService(); |
|
1122 if (observerService) { |
|
1123 observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); |
|
1124 |
|
1125 static const char16_t kShutdownIndicator[] = { '0', 0 }; |
|
1126 observerService->NotifyObservers(nullptr, "a11y-init-or-shutdown", kShutdownIndicator); |
|
1127 } |
|
1128 |
|
1129 // Stop accessible document loader. |
|
1130 DocManager::Shutdown(); |
|
1131 |
|
1132 SelectionManager::Shutdown(); |
|
1133 |
|
1134 #ifdef XP_WIN |
|
1135 sPendingPlugins = nullptr; |
|
1136 |
|
1137 uint32_t timerCount = sPluginTimers->Length(); |
|
1138 for (uint32_t i = 0; i < timerCount; i++) |
|
1139 sPluginTimers->ElementAt(i)->Cancel(); |
|
1140 |
|
1141 sPluginTimers = nullptr; |
|
1142 #endif |
|
1143 |
|
1144 // Application is going to be closed, shutdown accessibility and mark |
|
1145 // accessibility service as shutdown to prevent calls of its methods. |
|
1146 // Don't null accessibility service static member at this point to be safe |
|
1147 // if someone will try to operate with it. |
|
1148 |
|
1149 NS_ASSERTION(!gIsShutdown, "Accessibility was shutdown already"); |
|
1150 |
|
1151 gIsShutdown = true; |
|
1152 |
|
1153 PlatformShutdown(); |
|
1154 gApplicationAccessible->Shutdown(); |
|
1155 NS_RELEASE(gApplicationAccessible); |
|
1156 gApplicationAccessible = nullptr; |
|
1157 } |
|
1158 |
|
1159 already_AddRefed<Accessible> |
|
1160 nsAccessibilityService::CreateAccessibleByType(nsIContent* aContent, |
|
1161 DocAccessible* aDoc) |
|
1162 { |
|
1163 nsAutoString role; |
|
1164 for (const nsXBLBinding* binding = aContent->GetXBLBinding(); binding; binding = binding->GetBaseBinding()) { |
|
1165 nsIContent* bindingElm = binding->PrototypeBinding()->GetBindingElement(); |
|
1166 bindingElm->GetAttr(kNameSpaceID_None, nsGkAtoms::role, role); |
|
1167 if (!role.IsEmpty()) |
|
1168 break; |
|
1169 } |
|
1170 |
|
1171 if (role.IsEmpty() || role.EqualsLiteral("none")) |
|
1172 return nullptr; |
|
1173 |
|
1174 if (role.EqualsLiteral("outerdoc")) { |
|
1175 nsRefPtr<Accessible> accessible = new OuterDocAccessible(aContent, aDoc); |
|
1176 return accessible.forget(); |
|
1177 } |
|
1178 |
|
1179 nsRefPtr<Accessible> accessible; |
|
1180 #ifdef MOZ_XUL |
|
1181 // XUL controls |
|
1182 if (role.EqualsLiteral("xul:alert")) { |
|
1183 accessible = new XULAlertAccessible(aContent, aDoc); |
|
1184 |
|
1185 } else if (role.EqualsLiteral("xul:button")) { |
|
1186 accessible = new XULButtonAccessible(aContent, aDoc); |
|
1187 |
|
1188 } else if (role.EqualsLiteral("xul:checkbox")) { |
|
1189 accessible = new XULCheckboxAccessible(aContent, aDoc); |
|
1190 |
|
1191 } else if (role.EqualsLiteral("xul:colorpicker")) { |
|
1192 accessible = new XULColorPickerAccessible(aContent, aDoc); |
|
1193 |
|
1194 } else if (role.EqualsLiteral("xul:colorpickertile")) { |
|
1195 accessible = new XULColorPickerTileAccessible(aContent, aDoc); |
|
1196 |
|
1197 } else if (role.EqualsLiteral("xul:combobox")) { |
|
1198 accessible = new XULComboboxAccessible(aContent, aDoc); |
|
1199 |
|
1200 } else if (role.EqualsLiteral("xul:tabpanels")) { |
|
1201 accessible = new XULTabpanelsAccessible(aContent, aDoc); |
|
1202 |
|
1203 } else if (role.EqualsLiteral("xul:dropmarker")) { |
|
1204 accessible = new XULDropmarkerAccessible(aContent, aDoc); |
|
1205 |
|
1206 } else if (role.EqualsLiteral("xul:groupbox")) { |
|
1207 accessible = new XULGroupboxAccessible(aContent, aDoc); |
|
1208 |
|
1209 } else if (role.EqualsLiteral("xul:image")) { |
|
1210 if (aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::onclick)) { |
|
1211 accessible = new XULToolbarButtonAccessible(aContent, aDoc); |
|
1212 |
|
1213 } else { |
|
1214 // Don't include nameless images in accessible tree. |
|
1215 if (!aContent->HasAttr(kNameSpaceID_None, |
|
1216 nsGkAtoms::tooltiptext)) |
|
1217 return nullptr; |
|
1218 |
|
1219 accessible = new ImageAccessibleWrap(aContent, aDoc); |
|
1220 } |
|
1221 |
|
1222 } else if (role.EqualsLiteral("xul:link")) { |
|
1223 accessible = new XULLinkAccessible(aContent, aDoc); |
|
1224 |
|
1225 } else if (role.EqualsLiteral("xul:listbox")) { |
|
1226 accessible = new XULListboxAccessibleWrap(aContent, aDoc); |
|
1227 |
|
1228 } else if (role.EqualsLiteral("xul:listcell")) { |
|
1229 // Only create cells if there's more than one per row. |
|
1230 nsIContent* listItem = aContent->GetParent(); |
|
1231 if (!listItem) |
|
1232 return nullptr; |
|
1233 |
|
1234 for (nsIContent* child = listItem->GetFirstChild(); child; |
|
1235 child = child->GetNextSibling()) { |
|
1236 if (child->IsXUL(nsGkAtoms::listcell) && child != aContent) { |
|
1237 accessible = new XULListCellAccessibleWrap(aContent, aDoc); |
|
1238 break; |
|
1239 } |
|
1240 } |
|
1241 |
|
1242 } else if (role.EqualsLiteral("xul:listhead")) { |
|
1243 accessible = new XULColumAccessible(aContent, aDoc); |
|
1244 |
|
1245 } else if (role.EqualsLiteral("xul:listheader")) { |
|
1246 accessible = new XULColumnItemAccessible(aContent, aDoc); |
|
1247 |
|
1248 } else if (role.EqualsLiteral("xul:listitem")) { |
|
1249 accessible = new XULListitemAccessible(aContent, aDoc); |
|
1250 |
|
1251 } else if (role.EqualsLiteral("xul:menubar")) { |
|
1252 accessible = new XULMenubarAccessible(aContent, aDoc); |
|
1253 |
|
1254 } else if (role.EqualsLiteral("xul:menulist")) { |
|
1255 accessible = new XULComboboxAccessible(aContent, aDoc); |
|
1256 |
|
1257 } else if (role.EqualsLiteral("xul:menuitem")) { |
|
1258 accessible = new XULMenuitemAccessibleWrap(aContent, aDoc); |
|
1259 |
|
1260 } else if (role.EqualsLiteral("xul:menupopup")) { |
|
1261 #ifdef MOZ_ACCESSIBILITY_ATK |
|
1262 // ATK considers this node to be redundant when within menubars, and it makes menu |
|
1263 // navigation with assistive technologies more difficult |
|
1264 // XXX In the future we will should this for consistency across the nsIAccessible |
|
1265 // implementations on each platform for a consistent scripting environment, but |
|
1266 // then strip out redundant accessibles in the AccessibleWrap class for each platform. |
|
1267 nsIContent *parent = aContent->GetParent(); |
|
1268 if (parent && parent->IsXUL() && parent->Tag() == nsGkAtoms::menu) |
|
1269 return nullptr; |
|
1270 #endif |
|
1271 |
|
1272 accessible = new XULMenupopupAccessible(aContent, aDoc); |
|
1273 |
|
1274 } else if(role.EqualsLiteral("xul:menuseparator")) { |
|
1275 accessible = new XULMenuSeparatorAccessible(aContent, aDoc); |
|
1276 |
|
1277 } else if(role.EqualsLiteral("xul:pane")) { |
|
1278 accessible = new EnumRoleAccessible(aContent, aDoc, roles::PANE); |
|
1279 |
|
1280 } else if (role.EqualsLiteral("xul:panel")) { |
|
1281 if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautofocus, |
|
1282 nsGkAtoms::_true, eCaseMatters)) |
|
1283 accessible = new XULAlertAccessible(aContent, aDoc); |
|
1284 else |
|
1285 accessible = new EnumRoleAccessible(aContent, aDoc, roles::PANE); |
|
1286 |
|
1287 } else if (role.EqualsLiteral("xul:progressmeter")) { |
|
1288 accessible = new XULProgressMeterAccessible(aContent, aDoc); |
|
1289 |
|
1290 } else if (role.EqualsLiteral("xulstatusbar")) { |
|
1291 accessible = new XULStatusBarAccessible(aContent, aDoc); |
|
1292 |
|
1293 } else if (role.EqualsLiteral("xul:scale")) { |
|
1294 accessible = new XULSliderAccessible(aContent, aDoc); |
|
1295 |
|
1296 } else if (role.EqualsLiteral("xul:radiobutton")) { |
|
1297 accessible = new XULRadioButtonAccessible(aContent, aDoc); |
|
1298 |
|
1299 } else if (role.EqualsLiteral("xul:radiogroup")) { |
|
1300 accessible = new XULRadioGroupAccessible(aContent, aDoc); |
|
1301 |
|
1302 } else if (role.EqualsLiteral("xul:tab")) { |
|
1303 accessible = new XULTabAccessible(aContent, aDoc); |
|
1304 |
|
1305 } else if (role.EqualsLiteral("xul:tabs")) { |
|
1306 accessible = new XULTabsAccessible(aContent, aDoc); |
|
1307 |
|
1308 } else if (role.EqualsLiteral("xul:text")) { |
|
1309 accessible = new XULLabelAccessible(aContent, aDoc); |
|
1310 |
|
1311 } else if (role.EqualsLiteral("xul:textbox")) { |
|
1312 accessible = new EnumRoleAccessible(aContent, aDoc, roles::SECTION); |
|
1313 |
|
1314 } else if (role.EqualsLiteral("xul:thumb")) { |
|
1315 accessible = new XULThumbAccessible(aContent, aDoc); |
|
1316 |
|
1317 } else if (role.EqualsLiteral("xul:tree")) { |
|
1318 accessible = CreateAccessibleForXULTree(aContent, aDoc); |
|
1319 |
|
1320 } else if (role.EqualsLiteral("xul:treecolumns")) { |
|
1321 accessible = new XULTreeColumAccessible(aContent, aDoc); |
|
1322 |
|
1323 } else if (role.EqualsLiteral("xul:treecolumnitem")) { |
|
1324 accessible = new XULColumnItemAccessible(aContent, aDoc); |
|
1325 |
|
1326 } else if (role.EqualsLiteral("xul:toolbar")) { |
|
1327 accessible = new XULToolbarAccessible(aContent, aDoc); |
|
1328 |
|
1329 } else if (role.EqualsLiteral("xul:toolbarseparator")) { |
|
1330 accessible = new XULToolbarSeparatorAccessible(aContent, aDoc); |
|
1331 |
|
1332 } else if (role.EqualsLiteral("xul:tooltip")) { |
|
1333 accessible = new XULTooltipAccessible(aContent, aDoc); |
|
1334 |
|
1335 } else if (role.EqualsLiteral("xul:toolbarbutton")) { |
|
1336 accessible = new XULToolbarButtonAccessible(aContent, aDoc); |
|
1337 |
|
1338 } |
|
1339 #endif // MOZ_XUL |
|
1340 |
|
1341 return accessible.forget(); |
|
1342 } |
|
1343 |
|
1344 already_AddRefed<Accessible> |
|
1345 nsAccessibilityService::CreateHTMLAccessibleByMarkup(nsIFrame* aFrame, |
|
1346 nsIContent* aContent, |
|
1347 Accessible* aContext) |
|
1348 { |
|
1349 DocAccessible* document = aContext->Document(); |
|
1350 if (aContext->IsTableRow()) { |
|
1351 if (nsCoreUtils::IsHTMLTableHeader(aContent) && |
|
1352 aContext->GetContent() == aContent->GetParent()) { |
|
1353 nsRefPtr<Accessible> accessible = |
|
1354 new HTMLTableHeaderCellAccessibleWrap(aContent, document); |
|
1355 return accessible.forget(); |
|
1356 } |
|
1357 |
|
1358 return nullptr; |
|
1359 } |
|
1360 |
|
1361 // This method assumes we're in an HTML namespace. |
|
1362 nsIAtom* tag = aContent->Tag(); |
|
1363 if (tag == nsGkAtoms::figcaption) { |
|
1364 nsRefPtr<Accessible> accessible = |
|
1365 new HTMLFigcaptionAccessible(aContent, document); |
|
1366 return accessible.forget(); |
|
1367 } |
|
1368 |
|
1369 if (tag == nsGkAtoms::figure) { |
|
1370 nsRefPtr<Accessible> accessible = |
|
1371 new HTMLFigureAccessible(aContent, document); |
|
1372 return accessible.forget(); |
|
1373 } |
|
1374 |
|
1375 if (tag == nsGkAtoms::legend) { |
|
1376 nsRefPtr<Accessible> accessible = |
|
1377 new HTMLLegendAccessible(aContent, document); |
|
1378 return accessible.forget(); |
|
1379 } |
|
1380 |
|
1381 if (tag == nsGkAtoms::option) { |
|
1382 nsRefPtr<Accessible> accessible = |
|
1383 new HTMLSelectOptionAccessible(aContent, document); |
|
1384 return accessible.forget(); |
|
1385 } |
|
1386 |
|
1387 if (tag == nsGkAtoms::optgroup) { |
|
1388 nsRefPtr<Accessible> accessible = |
|
1389 new HTMLSelectOptGroupAccessible(aContent, document); |
|
1390 return accessible.forget(); |
|
1391 } |
|
1392 |
|
1393 if (tag == nsGkAtoms::ul || tag == nsGkAtoms::ol || |
|
1394 tag == nsGkAtoms::dl) { |
|
1395 nsRefPtr<Accessible> accessible = |
|
1396 new HTMLListAccessible(aContent, document); |
|
1397 return accessible.forget(); |
|
1398 } |
|
1399 |
|
1400 if (tag == nsGkAtoms::a) { |
|
1401 // Only some roles truly enjoy life as HTMLLinkAccessibles, for details |
|
1402 // see closed bug 494807. |
|
1403 nsRoleMapEntry* roleMapEntry = aria::GetRoleMap(aContent); |
|
1404 if (roleMapEntry && roleMapEntry->role != roles::NOTHING && |
|
1405 roleMapEntry->role != roles::LINK) { |
|
1406 nsRefPtr<Accessible> accessible = |
|
1407 new HyperTextAccessibleWrap(aContent, document); |
|
1408 return accessible.forget(); |
|
1409 } |
|
1410 |
|
1411 nsRefPtr<Accessible> accessible = |
|
1412 new HTMLLinkAccessible(aContent, document); |
|
1413 return accessible.forget(); |
|
1414 } |
|
1415 |
|
1416 if (aContext->IsList()) { |
|
1417 // If list item is a child of accessible list then create an accessible for |
|
1418 // it unconditionally by tag name. nsBlockFrame creates the list item |
|
1419 // accessible for other elements styled as list items. |
|
1420 if (aContext->GetContent() == aContent->GetParent()) { |
|
1421 if (tag == nsGkAtoms::dt || tag == nsGkAtoms::li) { |
|
1422 nsRefPtr<Accessible> accessible = |
|
1423 new HTMLLIAccessible(aContent, document); |
|
1424 return accessible.forget(); |
|
1425 } |
|
1426 |
|
1427 if (tag == nsGkAtoms::dd) { |
|
1428 nsRefPtr<Accessible> accessible = |
|
1429 new HyperTextAccessibleWrap(aContent, document); |
|
1430 return accessible.forget(); |
|
1431 } |
|
1432 } |
|
1433 |
|
1434 return nullptr; |
|
1435 } |
|
1436 |
|
1437 if (tag == nsGkAtoms::abbr || |
|
1438 tag == nsGkAtoms::acronym || |
|
1439 tag == nsGkAtoms::article || |
|
1440 tag == nsGkAtoms::aside || |
|
1441 tag == nsGkAtoms::blockquote || |
|
1442 tag == nsGkAtoms::form || |
|
1443 tag == nsGkAtoms::footer || |
|
1444 tag == nsGkAtoms::header || |
|
1445 tag == nsGkAtoms::h1 || |
|
1446 tag == nsGkAtoms::h2 || |
|
1447 tag == nsGkAtoms::h3 || |
|
1448 tag == nsGkAtoms::h4 || |
|
1449 tag == nsGkAtoms::h5 || |
|
1450 tag == nsGkAtoms::h6 || |
|
1451 tag == nsGkAtoms::nav || |
|
1452 tag == nsGkAtoms::q || |
|
1453 tag == nsGkAtoms::section) { |
|
1454 nsRefPtr<Accessible> accessible = |
|
1455 new HyperTextAccessibleWrap(aContent, document); |
|
1456 return accessible.forget(); |
|
1457 } |
|
1458 |
|
1459 if (tag == nsGkAtoms::label) { |
|
1460 nsRefPtr<Accessible> accessible = |
|
1461 new HTMLLabelAccessible(aContent, document); |
|
1462 return accessible.forget(); |
|
1463 } |
|
1464 |
|
1465 if (tag == nsGkAtoms::output) { |
|
1466 nsRefPtr<Accessible> accessible = |
|
1467 new HTMLOutputAccessible(aContent, document); |
|
1468 return accessible.forget(); |
|
1469 } |
|
1470 |
|
1471 if (tag == nsGkAtoms::progress) { |
|
1472 nsRefPtr<Accessible> accessible = |
|
1473 new HTMLProgressMeterAccessible(aContent, document); |
|
1474 return accessible.forget(); |
|
1475 } |
|
1476 |
|
1477 return nullptr; |
|
1478 } |
|
1479 |
|
1480 already_AddRefed<Accessible> |
|
1481 nsAccessibilityService::CreateAccessibleByFrameType(nsIFrame* aFrame, |
|
1482 nsIContent* aContent, |
|
1483 Accessible* aContext) |
|
1484 { |
|
1485 DocAccessible* document = aContext->Document(); |
|
1486 |
|
1487 nsRefPtr<Accessible> newAcc; |
|
1488 switch (aFrame->AccessibleType()) { |
|
1489 case eNoType: |
|
1490 return nullptr; |
|
1491 case eHTMLBRType: |
|
1492 newAcc = new HTMLBRAccessible(aContent, document); |
|
1493 break; |
|
1494 case eHTMLButtonType: |
|
1495 newAcc = new HTMLButtonAccessible(aContent, document); |
|
1496 break; |
|
1497 case eHTMLCanvasType: |
|
1498 newAcc = new HTMLCanvasAccessible(aContent, document); |
|
1499 break; |
|
1500 case eHTMLCaptionType: |
|
1501 if (aContext->IsTable() && |
|
1502 aContext->GetContent() == aContent->GetParent()) { |
|
1503 newAcc = new HTMLCaptionAccessible(aContent, document); |
|
1504 } |
|
1505 break; |
|
1506 case eHTMLCheckboxType: |
|
1507 newAcc = new HTMLCheckboxAccessible(aContent, document); |
|
1508 break; |
|
1509 case eHTMLComboboxType: |
|
1510 newAcc = new HTMLComboboxAccessible(aContent, document); |
|
1511 break; |
|
1512 case eHTMLFileInputType: |
|
1513 newAcc = new HTMLFileInputAccessible(aContent, document); |
|
1514 break; |
|
1515 case eHTMLGroupboxType: |
|
1516 newAcc = new HTMLGroupboxAccessible(aContent, document); |
|
1517 break; |
|
1518 case eHTMLHRType: |
|
1519 newAcc = new HTMLHRAccessible(aContent, document); |
|
1520 break; |
|
1521 case eHTMLImageMapType: |
|
1522 newAcc = new HTMLImageMapAccessible(aContent, document); |
|
1523 break; |
|
1524 case eHTMLLiType: |
|
1525 if (aContext->IsList() && |
|
1526 aContext->GetContent() == aContent->GetParent()) { |
|
1527 newAcc = new HTMLLIAccessible(aContent, document); |
|
1528 } |
|
1529 break; |
|
1530 case eHTMLSelectListType: |
|
1531 newAcc = new HTMLSelectListAccessible(aContent, document); |
|
1532 break; |
|
1533 case eHTMLMediaType: |
|
1534 newAcc = new EnumRoleAccessible(aContent, document, roles::GROUPING); |
|
1535 break; |
|
1536 case eHTMLRadioButtonType: |
|
1537 newAcc = new HTMLRadioButtonAccessible(aContent, document); |
|
1538 break; |
|
1539 case eHTMLRangeType: |
|
1540 newAcc = new HTMLRangeAccessible(aContent, document); |
|
1541 break; |
|
1542 case eHTMLSpinnerType: |
|
1543 newAcc = new HTMLSpinnerAccessible(aContent, document); |
|
1544 break; |
|
1545 case eHTMLTableType: |
|
1546 newAcc = new HTMLTableAccessibleWrap(aContent, document); |
|
1547 break; |
|
1548 case eHTMLTableCellType: |
|
1549 // Accessible HTML table cell should be a child of accessible HTML table |
|
1550 // or its row (CSS HTML tables are polite to the used markup at |
|
1551 // certain degree). |
|
1552 // Otherwise create a generic text accessible to avoid text jamming |
|
1553 // when reading by AT. |
|
1554 if (aContext->IsHTMLTableRow() || aContext->IsHTMLTable()) |
|
1555 newAcc = new HTMLTableCellAccessibleWrap(aContent, document); |
|
1556 else |
|
1557 newAcc = new HyperTextAccessibleWrap(aContent, document); |
|
1558 break; |
|
1559 |
|
1560 case eHTMLTableRowType: { |
|
1561 // Accessible HTML table row must be a child of tbody/tfoot/thead of |
|
1562 // accessible HTML table or must be a child of accessible of HTML table. |
|
1563 if (aContext->IsTable()) { |
|
1564 nsIContent* parentContent = aContent->GetParent(); |
|
1565 nsIFrame* parentFrame = parentContent->GetPrimaryFrame(); |
|
1566 if (parentFrame->GetType() == nsGkAtoms::tableRowGroupFrame) { |
|
1567 parentContent = parentContent->GetParent(); |
|
1568 parentFrame = parentContent->GetPrimaryFrame(); |
|
1569 } |
|
1570 |
|
1571 if (parentFrame->GetType() == nsGkAtoms::tableOuterFrame && |
|
1572 aContext->GetContent() == parentContent) { |
|
1573 newAcc = new HTMLTableRowAccessible(aContent, document); |
|
1574 } |
|
1575 } |
|
1576 break; |
|
1577 } |
|
1578 case eHTMLTextFieldType: |
|
1579 newAcc = new HTMLTextFieldAccessible(aContent, document); |
|
1580 break; |
|
1581 case eHyperTextType: |
|
1582 if (aContent->Tag() != nsGkAtoms::dt && aContent->Tag() != nsGkAtoms::dd) |
|
1583 newAcc = new HyperTextAccessibleWrap(aContent, document); |
|
1584 break; |
|
1585 |
|
1586 case eImageType: |
|
1587 newAcc = new ImageAccessibleWrap(aContent, document); |
|
1588 break; |
|
1589 case eOuterDocType: |
|
1590 newAcc = new OuterDocAccessible(aContent, document); |
|
1591 break; |
|
1592 case ePluginType: { |
|
1593 nsObjectFrame* objectFrame = do_QueryFrame(aFrame); |
|
1594 newAcc = CreatePluginAccessible(objectFrame, aContent, aContext); |
|
1595 break; |
|
1596 } |
|
1597 case eTextLeafType: |
|
1598 newAcc = new TextLeafAccessibleWrap(aContent, document); |
|
1599 break; |
|
1600 default: |
|
1601 MOZ_ASSERT(false); |
|
1602 break; |
|
1603 } |
|
1604 |
|
1605 return newAcc.forget(); |
|
1606 } |
|
1607 |
|
1608 //////////////////////////////////////////////////////////////////////////////// |
|
1609 // nsIAccessibilityService (DON'T put methods here) |
|
1610 |
|
1611 Accessible* |
|
1612 nsAccessibilityService::AddNativeRootAccessible(void* aAtkAccessible) |
|
1613 { |
|
1614 #ifdef MOZ_ACCESSIBILITY_ATK |
|
1615 ApplicationAccessible* applicationAcc = ApplicationAcc(); |
|
1616 if (!applicationAcc) |
|
1617 return nullptr; |
|
1618 |
|
1619 GtkWindowAccessible* nativeWnd = |
|
1620 new GtkWindowAccessible(static_cast<AtkObject*>(aAtkAccessible)); |
|
1621 |
|
1622 if (applicationAcc->AppendChild(nativeWnd)) |
|
1623 return nativeWnd; |
|
1624 #endif |
|
1625 |
|
1626 return nullptr; |
|
1627 } |
|
1628 |
|
1629 void |
|
1630 nsAccessibilityService::RemoveNativeRootAccessible(Accessible* aAccessible) |
|
1631 { |
|
1632 #ifdef MOZ_ACCESSIBILITY_ATK |
|
1633 ApplicationAccessible* applicationAcc = ApplicationAcc(); |
|
1634 |
|
1635 if (applicationAcc) |
|
1636 applicationAcc->RemoveChild(aAccessible); |
|
1637 #endif |
|
1638 } |
|
1639 |
|
1640 //////////////////////////////////////////////////////////////////////////////// |
|
1641 // NS_GetAccessibilityService |
|
1642 //////////////////////////////////////////////////////////////////////////////// |
|
1643 |
|
1644 /** |
|
1645 * Return accessibility service; creating one if necessary. |
|
1646 */ |
|
1647 nsresult |
|
1648 NS_GetAccessibilityService(nsIAccessibilityService** aResult) |
|
1649 { |
|
1650 NS_ENSURE_TRUE(aResult, NS_ERROR_NULL_POINTER); |
|
1651 *aResult = nullptr; |
|
1652 |
|
1653 if (nsAccessibilityService::gAccessibilityService) { |
|
1654 NS_ADDREF(*aResult = nsAccessibilityService::gAccessibilityService); |
|
1655 return NS_OK; |
|
1656 } |
|
1657 |
|
1658 nsRefPtr<nsAccessibilityService> service = new nsAccessibilityService(); |
|
1659 NS_ENSURE_TRUE(service, NS_ERROR_OUT_OF_MEMORY); |
|
1660 |
|
1661 if (!service->Init()) { |
|
1662 service->Shutdown(); |
|
1663 return NS_ERROR_FAILURE; |
|
1664 } |
|
1665 |
|
1666 statistics::A11yInitialized(); |
|
1667 |
|
1668 nsAccessibilityService::gAccessibilityService = service; |
|
1669 NS_ADDREF(*aResult = service); |
|
1670 |
|
1671 return NS_OK; |
|
1672 } |
|
1673 |
|
1674 //////////////////////////////////////////////////////////////////////////////// |
|
1675 // nsAccessibilityService private (DON'T put methods here) |
|
1676 |
|
1677 #ifdef MOZ_XUL |
|
1678 already_AddRefed<Accessible> |
|
1679 nsAccessibilityService::CreateAccessibleForXULTree(nsIContent* aContent, |
|
1680 DocAccessible* aDoc) |
|
1681 { |
|
1682 nsIContent* child = nsTreeUtils::GetDescendantChild(aContent, |
|
1683 nsGkAtoms::treechildren); |
|
1684 if (!child) |
|
1685 return nullptr; |
|
1686 |
|
1687 nsTreeBodyFrame* treeFrame = do_QueryFrame(child->GetPrimaryFrame()); |
|
1688 if (!treeFrame) |
|
1689 return nullptr; |
|
1690 |
|
1691 nsRefPtr<nsTreeColumns> treeCols = treeFrame->Columns(); |
|
1692 int32_t count = 0; |
|
1693 treeCols->GetCount(&count); |
|
1694 |
|
1695 // Outline of list accessible. |
|
1696 if (count == 1) { |
|
1697 nsRefPtr<Accessible> accessible = |
|
1698 new XULTreeAccessible(aContent, aDoc, treeFrame); |
|
1699 return accessible.forget(); |
|
1700 } |
|
1701 |
|
1702 // Table or tree table accessible. |
|
1703 nsRefPtr<Accessible> accessible = |
|
1704 new XULTreeGridAccessibleWrap(aContent, aDoc, treeFrame); |
|
1705 return accessible.forget(); |
|
1706 } |
|
1707 #endif |
|
1708 |
|
1709 //////////////////////////////////////////////////////////////////////////////// |
|
1710 // Services |
|
1711 //////////////////////////////////////////////////////////////////////////////// |
|
1712 |
|
1713 namespace mozilla { |
|
1714 namespace a11y { |
|
1715 |
|
1716 FocusManager* |
|
1717 FocusMgr() |
|
1718 { |
|
1719 return nsAccessibilityService::gAccessibilityService; |
|
1720 } |
|
1721 |
|
1722 SelectionManager* |
|
1723 SelectionMgr() |
|
1724 { |
|
1725 return nsAccessibilityService::gAccessibilityService; |
|
1726 } |
|
1727 |
|
1728 ApplicationAccessible* |
|
1729 ApplicationAcc() |
|
1730 { |
|
1731 return nsAccessibilityService::gApplicationAccessible; |
|
1732 } |
|
1733 |
|
1734 EPlatformDisabledState |
|
1735 PlatformDisabledState() |
|
1736 { |
|
1737 static int disabledState = 0xff; |
|
1738 |
|
1739 if (disabledState == 0xff) { |
|
1740 disabledState = Preferences::GetInt("accessibility.force_disabled", 0); |
|
1741 if (disabledState < ePlatformIsForceEnabled) |
|
1742 disabledState = ePlatformIsForceEnabled; |
|
1743 else if (disabledState > ePlatformIsDisabled) |
|
1744 disabledState = ePlatformIsDisabled; |
|
1745 } |
|
1746 |
|
1747 return (EPlatformDisabledState)disabledState; |
|
1748 } |
|
1749 |
|
1750 } |
|
1751 } |