accessible/src/base/nsAccessibilityService.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial