accessible/src/generic/RootAccessible.cpp

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

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 "RootAccessible.h"
michael@0 7
michael@0 8 #include "mozilla/ArrayUtils.h"
michael@0 9
michael@0 10 #define CreateEvent CreateEventA
michael@0 11 #include "nsIDOMDocument.h"
michael@0 12
michael@0 13 #include "Accessible-inl.h"
michael@0 14 #include "DocAccessible-inl.h"
michael@0 15 #include "nsAccessibilityService.h"
michael@0 16 #include "nsAccUtils.h"
michael@0 17 #include "nsCoreUtils.h"
michael@0 18 #include "nsEventShell.h"
michael@0 19 #include "Relation.h"
michael@0 20 #include "Role.h"
michael@0 21 #include "States.h"
michael@0 22 #ifdef MOZ_XUL
michael@0 23 #include "XULTreeAccessible.h"
michael@0 24 #endif
michael@0 25
michael@0 26 #include "mozilla/dom/Element.h"
michael@0 27
michael@0 28 #include "nsIAccessibleRelation.h"
michael@0 29 #include "nsIDocShellTreeItem.h"
michael@0 30 #include "nsIDocShellTreeOwner.h"
michael@0 31 #include "mozilla/dom/Event.h"
michael@0 32 #include "mozilla/dom/EventTarget.h"
michael@0 33 #include "nsIDOMCustomEvent.h"
michael@0 34 #include "nsIDOMXULMultSelectCntrlEl.h"
michael@0 35 #include "nsIDocument.h"
michael@0 36 #include "nsIInterfaceRequestorUtils.h"
michael@0 37 #include "nsIPropertyBag2.h"
michael@0 38 #include "nsIServiceManager.h"
michael@0 39 #include "nsPIDOMWindow.h"
michael@0 40 #include "nsIWebBrowserChrome.h"
michael@0 41 #include "nsReadableUtils.h"
michael@0 42 #include "nsFocusManager.h"
michael@0 43
michael@0 44 #ifdef MOZ_XUL
michael@0 45 #include "nsIXULDocument.h"
michael@0 46 #include "nsIXULWindow.h"
michael@0 47 #endif
michael@0 48
michael@0 49 using namespace mozilla;
michael@0 50 using namespace mozilla::a11y;
michael@0 51 using namespace mozilla::dom;
michael@0 52
michael@0 53 ////////////////////////////////////////////////////////////////////////////////
michael@0 54 // nsISupports
michael@0 55
michael@0 56 NS_IMPL_ISUPPORTS_INHERITED(RootAccessible, DocAccessible, nsIAccessibleDocument)
michael@0 57
michael@0 58 ////////////////////////////////////////////////////////////////////////////////
michael@0 59 // Constructor/destructor
michael@0 60
michael@0 61 RootAccessible::
michael@0 62 RootAccessible(nsIDocument* aDocument, nsIContent* aRootContent,
michael@0 63 nsIPresShell* aPresShell) :
michael@0 64 DocAccessibleWrap(aDocument, aRootContent, aPresShell)
michael@0 65 {
michael@0 66 mType = eRootType;
michael@0 67 }
michael@0 68
michael@0 69 RootAccessible::~RootAccessible()
michael@0 70 {
michael@0 71 }
michael@0 72
michael@0 73 ////////////////////////////////////////////////////////////////////////////////
michael@0 74 // Accessible
michael@0 75
michael@0 76 ENameValueFlag
michael@0 77 RootAccessible::Name(nsString& aName)
michael@0 78 {
michael@0 79 aName.Truncate();
michael@0 80
michael@0 81 if (mRoleMapEntry) {
michael@0 82 Accessible::Name(aName);
michael@0 83 if (!aName.IsEmpty())
michael@0 84 return eNameOK;
michael@0 85 }
michael@0 86
michael@0 87 mDocumentNode->GetTitle(aName);
michael@0 88 return eNameOK;
michael@0 89 }
michael@0 90
michael@0 91 role
michael@0 92 RootAccessible::NativeRole()
michael@0 93 {
michael@0 94 // If it's a <dialog> or <wizard>, use roles::DIALOG instead
michael@0 95 dom::Element* rootElm = mDocumentNode->GetRootElement();
michael@0 96 if (rootElm && (rootElm->Tag() == nsGkAtoms::dialog ||
michael@0 97 rootElm->Tag() == nsGkAtoms::wizard))
michael@0 98 return roles::DIALOG;
michael@0 99
michael@0 100 return DocAccessibleWrap::NativeRole();
michael@0 101 }
michael@0 102
michael@0 103 // RootAccessible protected member
michael@0 104 #ifdef MOZ_XUL
michael@0 105 uint32_t
michael@0 106 RootAccessible::GetChromeFlags()
michael@0 107 {
michael@0 108 // Return the flag set for the top level window as defined
michael@0 109 // by nsIWebBrowserChrome::CHROME_WINDOW_[FLAGNAME]
michael@0 110 // Not simple: nsIXULWindow is not just a QI from nsIDOMWindow
michael@0 111 nsCOMPtr<nsIDocShell> docShell = nsCoreUtils::GetDocShellFor(mDocumentNode);
michael@0 112 NS_ENSURE_TRUE(docShell, 0);
michael@0 113 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
michael@0 114 docShell->GetTreeOwner(getter_AddRefs(treeOwner));
michael@0 115 NS_ENSURE_TRUE(treeOwner, 0);
michael@0 116 nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(treeOwner));
michael@0 117 if (!xulWin) {
michael@0 118 return 0;
michael@0 119 }
michael@0 120 uint32_t chromeFlags;
michael@0 121 xulWin->GetChromeFlags(&chromeFlags);
michael@0 122 return chromeFlags;
michael@0 123 }
michael@0 124 #endif
michael@0 125
michael@0 126 uint64_t
michael@0 127 RootAccessible::NativeState()
michael@0 128 {
michael@0 129 uint64_t state = DocAccessibleWrap::NativeState();
michael@0 130 if (state & states::DEFUNCT)
michael@0 131 return state;
michael@0 132
michael@0 133 #ifdef MOZ_XUL
michael@0 134 uint32_t chromeFlags = GetChromeFlags();
michael@0 135 if (chromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE)
michael@0 136 state |= states::SIZEABLE;
michael@0 137 // If it has a titlebar it's movable
michael@0 138 // XXX unless it's minimized or maximized, but not sure
michael@0 139 // how to detect that
michael@0 140 if (chromeFlags & nsIWebBrowserChrome::CHROME_TITLEBAR)
michael@0 141 state |= states::MOVEABLE;
michael@0 142 if (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL)
michael@0 143 state |= states::MODAL;
michael@0 144 #endif
michael@0 145
michael@0 146 nsFocusManager* fm = nsFocusManager::GetFocusManager();
michael@0 147 if (fm && fm->GetActiveWindow() == mDocumentNode->GetWindow())
michael@0 148 state |= states::ACTIVE;
michael@0 149
michael@0 150 return state;
michael@0 151 }
michael@0 152
michael@0 153 const char* const kEventTypes[] = {
michael@0 154 #ifdef DEBUG_DRAGDROPSTART
michael@0 155 // Capture mouse over events and fire fake DRAGDROPSTART event to simplify
michael@0 156 // debugging a11y objects with event viewers.
michael@0 157 "mouseover",
michael@0 158 #endif
michael@0 159 // Fired when list or tree selection changes.
michael@0 160 "select",
michael@0 161 // Fired when value changes immediately, wether or not focused changed.
michael@0 162 "ValueChange",
michael@0 163 "AlertActive",
michael@0 164 "TreeRowCountChanged",
michael@0 165 "TreeInvalidated",
michael@0 166 // add ourself as a OpenStateChange listener (custom event fired in tree.xml)
michael@0 167 "OpenStateChange",
michael@0 168 // add ourself as a CheckboxStateChange listener (custom event fired in HTMLInputElement.cpp)
michael@0 169 "CheckboxStateChange",
michael@0 170 // add ourself as a RadioStateChange Listener ( custom event fired in in HTMLInputElement.cpp & radio.xml)
michael@0 171 "RadioStateChange",
michael@0 172 "popupshown",
michael@0 173 "popuphiding",
michael@0 174 "DOMMenuInactive",
michael@0 175 "DOMMenuItemActive",
michael@0 176 "DOMMenuItemInactive",
michael@0 177 "DOMMenuBarActive",
michael@0 178 "DOMMenuBarInactive"
michael@0 179 };
michael@0 180
michael@0 181 nsresult
michael@0 182 RootAccessible::AddEventListeners()
michael@0 183 {
michael@0 184 // EventTarget interface allows to register event listeners to
michael@0 185 // receive untrusted events (synthetic events generated by untrusted code).
michael@0 186 // For example, XBL bindings implementations for elements that are hosted in
michael@0 187 // non chrome document fire untrusted events.
michael@0 188 nsCOMPtr<EventTarget> nstarget = mDocumentNode;
michael@0 189
michael@0 190 if (nstarget) {
michael@0 191 for (const char* const* e = kEventTypes,
michael@0 192 * const* e_end = ArrayEnd(kEventTypes);
michael@0 193 e < e_end; ++e) {
michael@0 194 nsresult rv = nstarget->AddEventListener(NS_ConvertASCIItoUTF16(*e),
michael@0 195 this, true, true, 2);
michael@0 196 NS_ENSURE_SUCCESS(rv, rv);
michael@0 197 }
michael@0 198 }
michael@0 199
michael@0 200 return DocAccessible::AddEventListeners();
michael@0 201 }
michael@0 202
michael@0 203 nsresult
michael@0 204 RootAccessible::RemoveEventListeners()
michael@0 205 {
michael@0 206 nsCOMPtr<EventTarget> target = mDocumentNode;
michael@0 207 if (target) {
michael@0 208 for (const char* const* e = kEventTypes,
michael@0 209 * const* e_end = ArrayEnd(kEventTypes);
michael@0 210 e < e_end; ++e) {
michael@0 211 nsresult rv = target->RemoveEventListener(NS_ConvertASCIItoUTF16(*e), this, true);
michael@0 212 NS_ENSURE_SUCCESS(rv, rv);
michael@0 213 }
michael@0 214 }
michael@0 215
michael@0 216 // Do this before removing clearing caret accessible, so that it can use
michael@0 217 // shutdown the caret accessible's selection listener
michael@0 218 DocAccessible::RemoveEventListeners();
michael@0 219 return NS_OK;
michael@0 220 }
michael@0 221
michael@0 222 ////////////////////////////////////////////////////////////////////////////////
michael@0 223 // public
michael@0 224
michael@0 225 void
michael@0 226 RootAccessible::DocumentActivated(DocAccessible* aDocument)
michael@0 227 {
michael@0 228 }
michael@0 229
michael@0 230 ////////////////////////////////////////////////////////////////////////////////
michael@0 231 // nsIDOMEventListener
michael@0 232
michael@0 233 NS_IMETHODIMP
michael@0 234 RootAccessible::HandleEvent(nsIDOMEvent* aDOMEvent)
michael@0 235 {
michael@0 236 MOZ_ASSERT(aDOMEvent);
michael@0 237 Event* event = aDOMEvent->InternalDOMEvent();
michael@0 238 nsCOMPtr<nsINode> origTargetNode = do_QueryInterface(event->GetOriginalTarget());
michael@0 239 if (!origTargetNode)
michael@0 240 return NS_OK;
michael@0 241
michael@0 242 #ifdef A11Y_LOG
michael@0 243 if (logging::IsEnabled(logging::eDOMEvents)) {
michael@0 244 nsAutoString eventType;
michael@0 245 aDOMEvent->GetType(eventType);
michael@0 246 logging::DOMEvent("handled", origTargetNode, eventType);
michael@0 247 }
michael@0 248 #endif
michael@0 249
michael@0 250 DocAccessible* document =
michael@0 251 GetAccService()->GetDocAccessible(origTargetNode->OwnerDoc());
michael@0 252
michael@0 253 if (document) {
michael@0 254 // Root accessible exists longer than any of its descendant documents so
michael@0 255 // that we are guaranteed notification is processed before root accessible
michael@0 256 // is destroyed.
michael@0 257 document->HandleNotification<RootAccessible, nsIDOMEvent>
michael@0 258 (this, &RootAccessible::ProcessDOMEvent, aDOMEvent);
michael@0 259 }
michael@0 260
michael@0 261 return NS_OK;
michael@0 262 }
michael@0 263
michael@0 264 // RootAccessible protected
michael@0 265 void
michael@0 266 RootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
michael@0 267 {
michael@0 268 MOZ_ASSERT(aDOMEvent);
michael@0 269 Event* event = aDOMEvent->InternalDOMEvent();
michael@0 270 nsCOMPtr<nsINode> origTargetNode = do_QueryInterface(event->GetOriginalTarget());
michael@0 271
michael@0 272 nsAutoString eventType;
michael@0 273 aDOMEvent->GetType(eventType);
michael@0 274
michael@0 275 #ifdef A11Y_LOG
michael@0 276 if (logging::IsEnabled(logging::eDOMEvents))
michael@0 277 logging::DOMEvent("processed", origTargetNode, eventType);
michael@0 278 #endif
michael@0 279
michael@0 280 if (eventType.EqualsLiteral("popuphiding")) {
michael@0 281 HandlePopupHidingEvent(origTargetNode);
michael@0 282 return;
michael@0 283 }
michael@0 284
michael@0 285 DocAccessible* targetDocument = GetAccService()->
michael@0 286 GetDocAccessible(origTargetNode->OwnerDoc());
michael@0 287 NS_ASSERTION(targetDocument, "No document while accessible is in document?!");
michael@0 288
michael@0 289 Accessible* accessible =
michael@0 290 targetDocument->GetAccessibleOrContainer(origTargetNode);
michael@0 291 if (!accessible)
michael@0 292 return;
michael@0 293
michael@0 294 #ifdef MOZ_XUL
michael@0 295 XULTreeAccessible* treeAcc = accessible->AsXULTree();
michael@0 296 if (treeAcc) {
michael@0 297 if (eventType.EqualsLiteral("TreeRowCountChanged")) {
michael@0 298 HandleTreeRowCountChangedEvent(aDOMEvent, treeAcc);
michael@0 299 return;
michael@0 300 }
michael@0 301
michael@0 302 if (eventType.EqualsLiteral("TreeInvalidated")) {
michael@0 303 HandleTreeInvalidatedEvent(aDOMEvent, treeAcc);
michael@0 304 return;
michael@0 305 }
michael@0 306 }
michael@0 307 #endif
michael@0 308
michael@0 309 if (eventType.EqualsLiteral("RadioStateChange")) {
michael@0 310 uint64_t state = accessible->State();
michael@0 311 bool isEnabled = (state & (states::CHECKED | states::SELECTED)) != 0;
michael@0 312
michael@0 313 if (accessible->NeedsDOMUIEvent()) {
michael@0 314 nsRefPtr<AccEvent> accEvent =
michael@0 315 new AccStateChangeEvent(accessible, states::CHECKED, isEnabled);
michael@0 316 nsEventShell::FireEvent(accEvent);
michael@0 317 }
michael@0 318
michael@0 319 if (isEnabled) {
michael@0 320 FocusMgr()->ActiveItemChanged(accessible);
michael@0 321 #ifdef A11Y_LOG
michael@0 322 if (logging::IsEnabled(logging::eFocus))
michael@0 323 logging::ActiveItemChangeCausedBy("RadioStateChange", accessible);
michael@0 324 #endif
michael@0 325 }
michael@0 326
michael@0 327 return;
michael@0 328 }
michael@0 329
michael@0 330 if (eventType.EqualsLiteral("CheckboxStateChange")) {
michael@0 331 if (accessible->NeedsDOMUIEvent()) {
michael@0 332 uint64_t state = accessible->State();
michael@0 333 bool isEnabled = !!(state & states::CHECKED);
michael@0 334
michael@0 335 nsRefPtr<AccEvent> accEvent =
michael@0 336 new AccStateChangeEvent(accessible, states::CHECKED, isEnabled);
michael@0 337 nsEventShell::FireEvent(accEvent);
michael@0 338 }
michael@0 339 return;
michael@0 340 }
michael@0 341
michael@0 342 Accessible* treeItemAcc = nullptr;
michael@0 343 #ifdef MOZ_XUL
michael@0 344 // If it's a tree element, need the currently selected item.
michael@0 345 if (treeAcc) {
michael@0 346 treeItemAcc = accessible->CurrentItem();
michael@0 347 if (treeItemAcc)
michael@0 348 accessible = treeItemAcc;
michael@0 349 }
michael@0 350
michael@0 351 if (treeItemAcc && eventType.EqualsLiteral("OpenStateChange")) {
michael@0 352 uint64_t state = accessible->State();
michael@0 353 bool isEnabled = (state & states::EXPANDED) != 0;
michael@0 354
michael@0 355 nsRefPtr<AccEvent> accEvent =
michael@0 356 new AccStateChangeEvent(accessible, states::EXPANDED, isEnabled);
michael@0 357 nsEventShell::FireEvent(accEvent);
michael@0 358 return;
michael@0 359 }
michael@0 360
michael@0 361 nsINode* targetNode = accessible->GetNode();
michael@0 362 if (treeItemAcc && eventType.EqualsLiteral("select")) {
michael@0 363 // XXX: We shouldn't be based on DOM select event which doesn't provide us
michael@0 364 // any context info. We should integrate into nsTreeSelection instead.
michael@0 365 // If multiselect tree, we should fire selectionadd or selection removed
michael@0 366 if (FocusMgr()->HasDOMFocus(targetNode)) {
michael@0 367 nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSel =
michael@0 368 do_QueryInterface(targetNode);
michael@0 369 nsAutoString selType;
michael@0 370 multiSel->GetSelType(selType);
michael@0 371 if (selType.IsEmpty() || !selType.EqualsLiteral("single")) {
michael@0 372 // XXX: We need to fire EVENT_SELECTION_ADD and EVENT_SELECTION_REMOVE
michael@0 373 // for each tree item. Perhaps each tree item will need to cache its
michael@0 374 // selection state and fire an event after a DOM "select" event when
michael@0 375 // that state changes. XULTreeAccessible::UpdateTreeSelection();
michael@0 376 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
michael@0 377 accessible);
michael@0 378 return;
michael@0 379 }
michael@0 380
michael@0 381 nsRefPtr<AccSelChangeEvent> selChangeEvent =
michael@0 382 new AccSelChangeEvent(treeAcc, treeItemAcc,
michael@0 383 AccSelChangeEvent::eSelectionAdd);
michael@0 384 nsEventShell::FireEvent(selChangeEvent);
michael@0 385 return;
michael@0 386 }
michael@0 387 }
michael@0 388 else
michael@0 389 #endif
michael@0 390 if (eventType.EqualsLiteral("AlertActive")) {
michael@0 391 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_ALERT, accessible);
michael@0 392 }
michael@0 393 else if (eventType.EqualsLiteral("popupshown")) {
michael@0 394 HandlePopupShownEvent(accessible);
michael@0 395 }
michael@0 396 else if (eventType.EqualsLiteral("DOMMenuInactive")) {
michael@0 397 if (accessible->Role() == roles::MENUPOPUP) {
michael@0 398 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
michael@0 399 accessible);
michael@0 400 }
michael@0 401 }
michael@0 402 else if (eventType.EqualsLiteral("DOMMenuItemActive")) {
michael@0 403 FocusMgr()->ActiveItemChanged(accessible);
michael@0 404 #ifdef A11Y_LOG
michael@0 405 if (logging::IsEnabled(logging::eFocus))
michael@0 406 logging::ActiveItemChangeCausedBy("DOMMenuItemActive", accessible);
michael@0 407 #endif
michael@0 408 }
michael@0 409 else if (eventType.EqualsLiteral("DOMMenuItemInactive")) {
michael@0 410 // Process DOMMenuItemInactive event for autocomplete only because this is
michael@0 411 // unique widget that may acquire focus from autocomplete popup while popup
michael@0 412 // stays open and has no active item. In case of XUL tree autocomplete
michael@0 413 // popup this event is fired for tree accessible.
michael@0 414 Accessible* widget =
michael@0 415 accessible->IsWidget() ? accessible : accessible->ContainerWidget();
michael@0 416 if (widget && widget->IsAutoCompletePopup()) {
michael@0 417 FocusMgr()->ActiveItemChanged(nullptr);
michael@0 418 #ifdef A11Y_LOG
michael@0 419 if (logging::IsEnabled(logging::eFocus))
michael@0 420 logging::ActiveItemChangeCausedBy("DOMMenuItemInactive", accessible);
michael@0 421 #endif
michael@0 422 }
michael@0 423 }
michael@0 424 else if (eventType.EqualsLiteral("DOMMenuBarActive")) { // Always from user input
michael@0 425 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_START,
michael@0 426 accessible, eFromUserInput);
michael@0 427
michael@0 428 // Notify of active item change when menubar gets active and if it has
michael@0 429 // current item. This is a case of mouseover (set current menuitem) and
michael@0 430 // mouse click (activate the menubar). If menubar doesn't have current item
michael@0 431 // (can be a case of menubar activation from keyboard) then ignore this
michael@0 432 // notification because later we'll receive DOMMenuItemActive event after
michael@0 433 // current menuitem is set.
michael@0 434 Accessible* activeItem = accessible->CurrentItem();
michael@0 435 if (activeItem) {
michael@0 436 FocusMgr()->ActiveItemChanged(activeItem);
michael@0 437 #ifdef A11Y_LOG
michael@0 438 if (logging::IsEnabled(logging::eFocus))
michael@0 439 logging::ActiveItemChangeCausedBy("DOMMenuBarActive", accessible);
michael@0 440 #endif
michael@0 441 }
michael@0 442 }
michael@0 443 else if (eventType.EqualsLiteral("DOMMenuBarInactive")) { // Always from user input
michael@0 444 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_END,
michael@0 445 accessible, eFromUserInput);
michael@0 446
michael@0 447 FocusMgr()->ActiveItemChanged(nullptr);
michael@0 448 #ifdef A11Y_LOG
michael@0 449 if (logging::IsEnabled(logging::eFocus))
michael@0 450 logging::ActiveItemChangeCausedBy("DOMMenuBarInactive", accessible);
michael@0 451 #endif
michael@0 452 }
michael@0 453 else if (accessible->NeedsDOMUIEvent() &&
michael@0 454 eventType.EqualsLiteral("ValueChange")) {
michael@0 455 targetDocument->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
michael@0 456 accessible);
michael@0 457 }
michael@0 458 #ifdef DEBUG_DRAGDROPSTART
michael@0 459 else if (eventType.EqualsLiteral("mouseover")) {
michael@0 460 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_DRAGDROP_START,
michael@0 461 accessible);
michael@0 462 }
michael@0 463 #endif
michael@0 464 }
michael@0 465
michael@0 466
michael@0 467 ////////////////////////////////////////////////////////////////////////////////
michael@0 468 // Accessible
michael@0 469
michael@0 470 void
michael@0 471 RootAccessible::Shutdown()
michael@0 472 {
michael@0 473 // Called manually or by Accessible::LastRelease()
michael@0 474 if (!PresShell())
michael@0 475 return; // Already shutdown
michael@0 476
michael@0 477 DocAccessibleWrap::Shutdown();
michael@0 478 }
michael@0 479
michael@0 480 // nsIAccessible method
michael@0 481 Relation
michael@0 482 RootAccessible::RelationByType(RelationType aType)
michael@0 483 {
michael@0 484 if (!mDocumentNode || aType != RelationType::EMBEDS)
michael@0 485 return DocAccessibleWrap::RelationByType(aType);
michael@0 486
michael@0 487 nsIDOMWindow* rootWindow = mDocumentNode->GetWindow();
michael@0 488 if (rootWindow) {
michael@0 489 nsCOMPtr<nsIDOMWindow> contentWindow;
michael@0 490 rootWindow->GetContent(getter_AddRefs(contentWindow));
michael@0 491 if (contentWindow) {
michael@0 492 nsCOMPtr<nsIDOMDocument> contentDOMDocument;
michael@0 493 contentWindow->GetDocument(getter_AddRefs(contentDOMDocument));
michael@0 494 nsCOMPtr<nsIDocument> contentDocumentNode =
michael@0 495 do_QueryInterface(contentDOMDocument);
michael@0 496 if (contentDocumentNode) {
michael@0 497 DocAccessible* contentDocument =
michael@0 498 GetAccService()->GetDocAccessible(contentDocumentNode);
michael@0 499 if (contentDocument)
michael@0 500 return Relation(contentDocument);
michael@0 501 }
michael@0 502 }
michael@0 503 }
michael@0 504
michael@0 505 return Relation();
michael@0 506 }
michael@0 507
michael@0 508 ////////////////////////////////////////////////////////////////////////////////
michael@0 509 // Protected members
michael@0 510
michael@0 511 void
michael@0 512 RootAccessible::HandlePopupShownEvent(Accessible* aAccessible)
michael@0 513 {
michael@0 514 roles::Role role = aAccessible->Role();
michael@0 515
michael@0 516 if (role == roles::MENUPOPUP) {
michael@0 517 // Don't fire menupopup events for combobox and autocomplete lists.
michael@0 518 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START,
michael@0 519 aAccessible);
michael@0 520 return;
michael@0 521 }
michael@0 522
michael@0 523 if (role == roles::TOOLTIP) {
michael@0 524 // There is a single <xul:tooltip> node which Mozilla moves around.
michael@0 525 // The accessible for it stays the same no matter where it moves.
michael@0 526 // AT's expect to get an EVENT_SHOW for the tooltip.
michael@0 527 // In event callback the tooltip's accessible will be ready.
michael@0 528 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SHOW, aAccessible);
michael@0 529 return;
michael@0 530 }
michael@0 531
michael@0 532 if (role == roles::COMBOBOX_LIST) {
michael@0 533 // Fire expanded state change event for comboboxes and autocompeletes.
michael@0 534 Accessible* combobox = aAccessible->Parent();
michael@0 535 if (!combobox)
michael@0 536 return;
michael@0 537
michael@0 538 roles::Role comboboxRole = combobox->Role();
michael@0 539 if (comboboxRole == roles::COMBOBOX ||
michael@0 540 comboboxRole == roles::AUTOCOMPLETE) {
michael@0 541 nsRefPtr<AccEvent> event =
michael@0 542 new AccStateChangeEvent(combobox, states::EXPANDED, true);
michael@0 543 if (event)
michael@0 544 nsEventShell::FireEvent(event);
michael@0 545 }
michael@0 546 }
michael@0 547 }
michael@0 548
michael@0 549 void
michael@0 550 RootAccessible::HandlePopupHidingEvent(nsINode* aPopupNode)
michael@0 551 {
michael@0 552 // Get popup accessible. There are cases when popup element isn't accessible
michael@0 553 // but an underlying widget is and behaves like popup, an example is
michael@0 554 // autocomplete popups.
michael@0 555 DocAccessible* document = nsAccUtils::GetDocAccessibleFor(aPopupNode);
michael@0 556 if (!document)
michael@0 557 return;
michael@0 558
michael@0 559 Accessible* popup = document->GetAccessible(aPopupNode);
michael@0 560 if (!popup) {
michael@0 561 Accessible* popupContainer = document->GetContainerAccessible(aPopupNode);
michael@0 562 if (!popupContainer)
michael@0 563 return;
michael@0 564
michael@0 565 uint32_t childCount = popupContainer->ChildCount();
michael@0 566 for (uint32_t idx = 0; idx < childCount; idx++) {
michael@0 567 Accessible* child = popupContainer->GetChildAt(idx);
michael@0 568 if (child->IsAutoCompletePopup()) {
michael@0 569 popup = child;
michael@0 570 break;
michael@0 571 }
michael@0 572 }
michael@0 573
michael@0 574 // No popup no events. Focus is managed by DOM. This is a case for
michael@0 575 // menupopups of menus on Linux since there are no accessible for popups.
michael@0 576 if (!popup)
michael@0 577 return;
michael@0 578 }
michael@0 579
michael@0 580 // In case of autocompletes and comboboxes fire state change event for
michael@0 581 // expanded state. Note, HTML form autocomplete isn't a subject of state
michael@0 582 // change event because they aren't autocompletes strictly speaking.
michael@0 583 // When popup closes (except nested popups and menus) then fire focus event to
michael@0 584 // where it was. The focus event is expected even if popup didn't take a focus.
michael@0 585
michael@0 586 static const uint32_t kNotifyOfFocus = 1;
michael@0 587 static const uint32_t kNotifyOfState = 2;
michael@0 588 uint32_t notifyOf = 0;
michael@0 589
michael@0 590 // HTML select is target of popuphidding event. Otherwise get container
michael@0 591 // widget. No container widget means this is either tooltip or menupopup.
michael@0 592 // No events in the former case.
michael@0 593 Accessible* widget = nullptr;
michael@0 594 if (popup->IsCombobox()) {
michael@0 595 widget = popup;
michael@0 596 } else {
michael@0 597 widget = popup->ContainerWidget();
michael@0 598 if (!widget) {
michael@0 599 if (!popup->IsMenuPopup())
michael@0 600 return;
michael@0 601
michael@0 602 widget = popup;
michael@0 603 }
michael@0 604 }
michael@0 605
michael@0 606 if (popup->IsAutoCompletePopup()) {
michael@0 607 // No focus event for autocomplete because it's managed by
michael@0 608 // DOMMenuItemInactive events.
michael@0 609 if (widget->IsAutoComplete())
michael@0 610 notifyOf = kNotifyOfState;
michael@0 611
michael@0 612 } else if (widget->IsCombobox()) {
michael@0 613 // Fire focus for active combobox, otherwise the focus is managed by DOM
michael@0 614 // focus notifications. Always fire state change event.
michael@0 615 if (widget->IsActiveWidget())
michael@0 616 notifyOf = kNotifyOfFocus;
michael@0 617 notifyOf |= kNotifyOfState;
michael@0 618
michael@0 619 } else if (widget->IsMenuButton()) {
michael@0 620 // Can be a part of autocomplete.
michael@0 621 Accessible* compositeWidget = widget->ContainerWidget();
michael@0 622 if (compositeWidget && compositeWidget->IsAutoComplete()) {
michael@0 623 widget = compositeWidget;
michael@0 624 notifyOf = kNotifyOfState;
michael@0 625 }
michael@0 626
michael@0 627 // Autocomplete (like searchbar) can be inactive when popup hiddens
michael@0 628 notifyOf |= kNotifyOfFocus;
michael@0 629
michael@0 630 } else if (widget == popup) {
michael@0 631 // Top level context menus and alerts.
michael@0 632 // Ignore submenus and menubar. When submenu is closed then sumbenu
michael@0 633 // container menuitem takes a focus via DOMMenuItemActive notification.
michael@0 634 // For menubars processing we listen DOMMenubarActive/Inactive
michael@0 635 // notifications.
michael@0 636 notifyOf = kNotifyOfFocus;
michael@0 637 }
michael@0 638
michael@0 639 // Restore focus to where it was.
michael@0 640 if (notifyOf & kNotifyOfFocus) {
michael@0 641 FocusMgr()->ActiveItemChanged(nullptr);
michael@0 642 #ifdef A11Y_LOG
michael@0 643 if (logging::IsEnabled(logging::eFocus))
michael@0 644 logging::ActiveItemChangeCausedBy("popuphiding", popup);
michael@0 645 #endif
michael@0 646 }
michael@0 647
michael@0 648 // Fire expanded state change event.
michael@0 649 if (notifyOf & kNotifyOfState) {
michael@0 650 nsRefPtr<AccEvent> event =
michael@0 651 new AccStateChangeEvent(widget, states::EXPANDED, false);
michael@0 652 document->FireDelayedEvent(event);
michael@0 653 }
michael@0 654 }
michael@0 655
michael@0 656 #ifdef MOZ_XUL
michael@0 657 void
michael@0 658 RootAccessible::HandleTreeRowCountChangedEvent(nsIDOMEvent* aEvent,
michael@0 659 XULTreeAccessible* aAccessible)
michael@0 660 {
michael@0 661 nsCOMPtr<nsIDOMCustomEvent> customEvent(do_QueryInterface(aEvent));
michael@0 662 if (!customEvent)
michael@0 663 return;
michael@0 664
michael@0 665 nsCOMPtr<nsIVariant> detailVariant;
michael@0 666 customEvent->GetDetail(getter_AddRefs(detailVariant));
michael@0 667 if (!detailVariant)
michael@0 668 return;
michael@0 669
michael@0 670 nsCOMPtr<nsISupports> supports;
michael@0 671 detailVariant->GetAsISupports(getter_AddRefs(supports));
michael@0 672 nsCOMPtr<nsIPropertyBag2> propBag(do_QueryInterface(supports));
michael@0 673 if (!propBag)
michael@0 674 return;
michael@0 675
michael@0 676 nsresult rv;
michael@0 677 int32_t index, count;
michael@0 678 rv = propBag->GetPropertyAsInt32(NS_LITERAL_STRING("index"), &index);
michael@0 679 if (NS_FAILED(rv))
michael@0 680 return;
michael@0 681
michael@0 682 rv = propBag->GetPropertyAsInt32(NS_LITERAL_STRING("count"), &count);
michael@0 683 if (NS_FAILED(rv))
michael@0 684 return;
michael@0 685
michael@0 686 aAccessible->InvalidateCache(index, count);
michael@0 687 }
michael@0 688
michael@0 689 void
michael@0 690 RootAccessible::HandleTreeInvalidatedEvent(nsIDOMEvent* aEvent,
michael@0 691 XULTreeAccessible* aAccessible)
michael@0 692 {
michael@0 693 nsCOMPtr<nsIDOMCustomEvent> customEvent(do_QueryInterface(aEvent));
michael@0 694 if (!customEvent)
michael@0 695 return;
michael@0 696
michael@0 697 nsCOMPtr<nsIVariant> detailVariant;
michael@0 698 customEvent->GetDetail(getter_AddRefs(detailVariant));
michael@0 699 if (!detailVariant)
michael@0 700 return;
michael@0 701
michael@0 702 nsCOMPtr<nsISupports> supports;
michael@0 703 detailVariant->GetAsISupports(getter_AddRefs(supports));
michael@0 704 nsCOMPtr<nsIPropertyBag2> propBag(do_QueryInterface(supports));
michael@0 705 if (!propBag)
michael@0 706 return;
michael@0 707
michael@0 708 int32_t startRow = 0, endRow = -1, startCol = 0, endCol = -1;
michael@0 709 propBag->GetPropertyAsInt32(NS_LITERAL_STRING("startrow"),
michael@0 710 &startRow);
michael@0 711 propBag->GetPropertyAsInt32(NS_LITERAL_STRING("endrow"),
michael@0 712 &endRow);
michael@0 713 propBag->GetPropertyAsInt32(NS_LITERAL_STRING("startcolumn"),
michael@0 714 &startCol);
michael@0 715 propBag->GetPropertyAsInt32(NS_LITERAL_STRING("endcolumn"),
michael@0 716 &endCol);
michael@0 717
michael@0 718 aAccessible->TreeViewInvalidated(startRow, endRow, startCol, endCol);
michael@0 719 }
michael@0 720 #endif

mercurial