1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/accessible/src/generic/RootAccessible.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,720 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "RootAccessible.h" 1.10 + 1.11 +#include "mozilla/ArrayUtils.h" 1.12 + 1.13 +#define CreateEvent CreateEventA 1.14 +#include "nsIDOMDocument.h" 1.15 + 1.16 +#include "Accessible-inl.h" 1.17 +#include "DocAccessible-inl.h" 1.18 +#include "nsAccessibilityService.h" 1.19 +#include "nsAccUtils.h" 1.20 +#include "nsCoreUtils.h" 1.21 +#include "nsEventShell.h" 1.22 +#include "Relation.h" 1.23 +#include "Role.h" 1.24 +#include "States.h" 1.25 +#ifdef MOZ_XUL 1.26 +#include "XULTreeAccessible.h" 1.27 +#endif 1.28 + 1.29 +#include "mozilla/dom/Element.h" 1.30 + 1.31 +#include "nsIAccessibleRelation.h" 1.32 +#include "nsIDocShellTreeItem.h" 1.33 +#include "nsIDocShellTreeOwner.h" 1.34 +#include "mozilla/dom/Event.h" 1.35 +#include "mozilla/dom/EventTarget.h" 1.36 +#include "nsIDOMCustomEvent.h" 1.37 +#include "nsIDOMXULMultSelectCntrlEl.h" 1.38 +#include "nsIDocument.h" 1.39 +#include "nsIInterfaceRequestorUtils.h" 1.40 +#include "nsIPropertyBag2.h" 1.41 +#include "nsIServiceManager.h" 1.42 +#include "nsPIDOMWindow.h" 1.43 +#include "nsIWebBrowserChrome.h" 1.44 +#include "nsReadableUtils.h" 1.45 +#include "nsFocusManager.h" 1.46 + 1.47 +#ifdef MOZ_XUL 1.48 +#include "nsIXULDocument.h" 1.49 +#include "nsIXULWindow.h" 1.50 +#endif 1.51 + 1.52 +using namespace mozilla; 1.53 +using namespace mozilla::a11y; 1.54 +using namespace mozilla::dom; 1.55 + 1.56 +//////////////////////////////////////////////////////////////////////////////// 1.57 +// nsISupports 1.58 + 1.59 +NS_IMPL_ISUPPORTS_INHERITED(RootAccessible, DocAccessible, nsIAccessibleDocument) 1.60 + 1.61 +//////////////////////////////////////////////////////////////////////////////// 1.62 +// Constructor/destructor 1.63 + 1.64 +RootAccessible:: 1.65 + RootAccessible(nsIDocument* aDocument, nsIContent* aRootContent, 1.66 + nsIPresShell* aPresShell) : 1.67 + DocAccessibleWrap(aDocument, aRootContent, aPresShell) 1.68 +{ 1.69 + mType = eRootType; 1.70 +} 1.71 + 1.72 +RootAccessible::~RootAccessible() 1.73 +{ 1.74 +} 1.75 + 1.76 +//////////////////////////////////////////////////////////////////////////////// 1.77 +// Accessible 1.78 + 1.79 +ENameValueFlag 1.80 +RootAccessible::Name(nsString& aName) 1.81 +{ 1.82 + aName.Truncate(); 1.83 + 1.84 + if (mRoleMapEntry) { 1.85 + Accessible::Name(aName); 1.86 + if (!aName.IsEmpty()) 1.87 + return eNameOK; 1.88 + } 1.89 + 1.90 + mDocumentNode->GetTitle(aName); 1.91 + return eNameOK; 1.92 +} 1.93 + 1.94 +role 1.95 +RootAccessible::NativeRole() 1.96 +{ 1.97 + // If it's a <dialog> or <wizard>, use roles::DIALOG instead 1.98 + dom::Element* rootElm = mDocumentNode->GetRootElement(); 1.99 + if (rootElm && (rootElm->Tag() == nsGkAtoms::dialog || 1.100 + rootElm->Tag() == nsGkAtoms::wizard)) 1.101 + return roles::DIALOG; 1.102 + 1.103 + return DocAccessibleWrap::NativeRole(); 1.104 +} 1.105 + 1.106 +// RootAccessible protected member 1.107 +#ifdef MOZ_XUL 1.108 +uint32_t 1.109 +RootAccessible::GetChromeFlags() 1.110 +{ 1.111 + // Return the flag set for the top level window as defined 1.112 + // by nsIWebBrowserChrome::CHROME_WINDOW_[FLAGNAME] 1.113 + // Not simple: nsIXULWindow is not just a QI from nsIDOMWindow 1.114 + nsCOMPtr<nsIDocShell> docShell = nsCoreUtils::GetDocShellFor(mDocumentNode); 1.115 + NS_ENSURE_TRUE(docShell, 0); 1.116 + nsCOMPtr<nsIDocShellTreeOwner> treeOwner; 1.117 + docShell->GetTreeOwner(getter_AddRefs(treeOwner)); 1.118 + NS_ENSURE_TRUE(treeOwner, 0); 1.119 + nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(treeOwner)); 1.120 + if (!xulWin) { 1.121 + return 0; 1.122 + } 1.123 + uint32_t chromeFlags; 1.124 + xulWin->GetChromeFlags(&chromeFlags); 1.125 + return chromeFlags; 1.126 +} 1.127 +#endif 1.128 + 1.129 +uint64_t 1.130 +RootAccessible::NativeState() 1.131 +{ 1.132 + uint64_t state = DocAccessibleWrap::NativeState(); 1.133 + if (state & states::DEFUNCT) 1.134 + return state; 1.135 + 1.136 +#ifdef MOZ_XUL 1.137 + uint32_t chromeFlags = GetChromeFlags(); 1.138 + if (chromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE) 1.139 + state |= states::SIZEABLE; 1.140 + // If it has a titlebar it's movable 1.141 + // XXX unless it's minimized or maximized, but not sure 1.142 + // how to detect that 1.143 + if (chromeFlags & nsIWebBrowserChrome::CHROME_TITLEBAR) 1.144 + state |= states::MOVEABLE; 1.145 + if (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL) 1.146 + state |= states::MODAL; 1.147 +#endif 1.148 + 1.149 + nsFocusManager* fm = nsFocusManager::GetFocusManager(); 1.150 + if (fm && fm->GetActiveWindow() == mDocumentNode->GetWindow()) 1.151 + state |= states::ACTIVE; 1.152 + 1.153 + return state; 1.154 +} 1.155 + 1.156 +const char* const kEventTypes[] = { 1.157 +#ifdef DEBUG_DRAGDROPSTART 1.158 + // Capture mouse over events and fire fake DRAGDROPSTART event to simplify 1.159 + // debugging a11y objects with event viewers. 1.160 + "mouseover", 1.161 +#endif 1.162 + // Fired when list or tree selection changes. 1.163 + "select", 1.164 + // Fired when value changes immediately, wether or not focused changed. 1.165 + "ValueChange", 1.166 + "AlertActive", 1.167 + "TreeRowCountChanged", 1.168 + "TreeInvalidated", 1.169 + // add ourself as a OpenStateChange listener (custom event fired in tree.xml) 1.170 + "OpenStateChange", 1.171 + // add ourself as a CheckboxStateChange listener (custom event fired in HTMLInputElement.cpp) 1.172 + "CheckboxStateChange", 1.173 + // add ourself as a RadioStateChange Listener ( custom event fired in in HTMLInputElement.cpp & radio.xml) 1.174 + "RadioStateChange", 1.175 + "popupshown", 1.176 + "popuphiding", 1.177 + "DOMMenuInactive", 1.178 + "DOMMenuItemActive", 1.179 + "DOMMenuItemInactive", 1.180 + "DOMMenuBarActive", 1.181 + "DOMMenuBarInactive" 1.182 +}; 1.183 + 1.184 +nsresult 1.185 +RootAccessible::AddEventListeners() 1.186 +{ 1.187 + // EventTarget interface allows to register event listeners to 1.188 + // receive untrusted events (synthetic events generated by untrusted code). 1.189 + // For example, XBL bindings implementations for elements that are hosted in 1.190 + // non chrome document fire untrusted events. 1.191 + nsCOMPtr<EventTarget> nstarget = mDocumentNode; 1.192 + 1.193 + if (nstarget) { 1.194 + for (const char* const* e = kEventTypes, 1.195 + * const* e_end = ArrayEnd(kEventTypes); 1.196 + e < e_end; ++e) { 1.197 + nsresult rv = nstarget->AddEventListener(NS_ConvertASCIItoUTF16(*e), 1.198 + this, true, true, 2); 1.199 + NS_ENSURE_SUCCESS(rv, rv); 1.200 + } 1.201 + } 1.202 + 1.203 + return DocAccessible::AddEventListeners(); 1.204 +} 1.205 + 1.206 +nsresult 1.207 +RootAccessible::RemoveEventListeners() 1.208 +{ 1.209 + nsCOMPtr<EventTarget> target = mDocumentNode; 1.210 + if (target) { 1.211 + for (const char* const* e = kEventTypes, 1.212 + * const* e_end = ArrayEnd(kEventTypes); 1.213 + e < e_end; ++e) { 1.214 + nsresult rv = target->RemoveEventListener(NS_ConvertASCIItoUTF16(*e), this, true); 1.215 + NS_ENSURE_SUCCESS(rv, rv); 1.216 + } 1.217 + } 1.218 + 1.219 + // Do this before removing clearing caret accessible, so that it can use 1.220 + // shutdown the caret accessible's selection listener 1.221 + DocAccessible::RemoveEventListeners(); 1.222 + return NS_OK; 1.223 +} 1.224 + 1.225 +//////////////////////////////////////////////////////////////////////////////// 1.226 +// public 1.227 + 1.228 +void 1.229 +RootAccessible::DocumentActivated(DocAccessible* aDocument) 1.230 +{ 1.231 +} 1.232 + 1.233 +//////////////////////////////////////////////////////////////////////////////// 1.234 +// nsIDOMEventListener 1.235 + 1.236 +NS_IMETHODIMP 1.237 +RootAccessible::HandleEvent(nsIDOMEvent* aDOMEvent) 1.238 +{ 1.239 + MOZ_ASSERT(aDOMEvent); 1.240 + Event* event = aDOMEvent->InternalDOMEvent(); 1.241 + nsCOMPtr<nsINode> origTargetNode = do_QueryInterface(event->GetOriginalTarget()); 1.242 + if (!origTargetNode) 1.243 + return NS_OK; 1.244 + 1.245 +#ifdef A11Y_LOG 1.246 + if (logging::IsEnabled(logging::eDOMEvents)) { 1.247 + nsAutoString eventType; 1.248 + aDOMEvent->GetType(eventType); 1.249 + logging::DOMEvent("handled", origTargetNode, eventType); 1.250 + } 1.251 +#endif 1.252 + 1.253 + DocAccessible* document = 1.254 + GetAccService()->GetDocAccessible(origTargetNode->OwnerDoc()); 1.255 + 1.256 + if (document) { 1.257 + // Root accessible exists longer than any of its descendant documents so 1.258 + // that we are guaranteed notification is processed before root accessible 1.259 + // is destroyed. 1.260 + document->HandleNotification<RootAccessible, nsIDOMEvent> 1.261 + (this, &RootAccessible::ProcessDOMEvent, aDOMEvent); 1.262 + } 1.263 + 1.264 + return NS_OK; 1.265 +} 1.266 + 1.267 +// RootAccessible protected 1.268 +void 1.269 +RootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent) 1.270 +{ 1.271 + MOZ_ASSERT(aDOMEvent); 1.272 + Event* event = aDOMEvent->InternalDOMEvent(); 1.273 + nsCOMPtr<nsINode> origTargetNode = do_QueryInterface(event->GetOriginalTarget()); 1.274 + 1.275 + nsAutoString eventType; 1.276 + aDOMEvent->GetType(eventType); 1.277 + 1.278 +#ifdef A11Y_LOG 1.279 + if (logging::IsEnabled(logging::eDOMEvents)) 1.280 + logging::DOMEvent("processed", origTargetNode, eventType); 1.281 +#endif 1.282 + 1.283 + if (eventType.EqualsLiteral("popuphiding")) { 1.284 + HandlePopupHidingEvent(origTargetNode); 1.285 + return; 1.286 + } 1.287 + 1.288 + DocAccessible* targetDocument = GetAccService()-> 1.289 + GetDocAccessible(origTargetNode->OwnerDoc()); 1.290 + NS_ASSERTION(targetDocument, "No document while accessible is in document?!"); 1.291 + 1.292 + Accessible* accessible = 1.293 + targetDocument->GetAccessibleOrContainer(origTargetNode); 1.294 + if (!accessible) 1.295 + return; 1.296 + 1.297 +#ifdef MOZ_XUL 1.298 + XULTreeAccessible* treeAcc = accessible->AsXULTree(); 1.299 + if (treeAcc) { 1.300 + if (eventType.EqualsLiteral("TreeRowCountChanged")) { 1.301 + HandleTreeRowCountChangedEvent(aDOMEvent, treeAcc); 1.302 + return; 1.303 + } 1.304 + 1.305 + if (eventType.EqualsLiteral("TreeInvalidated")) { 1.306 + HandleTreeInvalidatedEvent(aDOMEvent, treeAcc); 1.307 + return; 1.308 + } 1.309 + } 1.310 +#endif 1.311 + 1.312 + if (eventType.EqualsLiteral("RadioStateChange")) { 1.313 + uint64_t state = accessible->State(); 1.314 + bool isEnabled = (state & (states::CHECKED | states::SELECTED)) != 0; 1.315 + 1.316 + if (accessible->NeedsDOMUIEvent()) { 1.317 + nsRefPtr<AccEvent> accEvent = 1.318 + new AccStateChangeEvent(accessible, states::CHECKED, isEnabled); 1.319 + nsEventShell::FireEvent(accEvent); 1.320 + } 1.321 + 1.322 + if (isEnabled) { 1.323 + FocusMgr()->ActiveItemChanged(accessible); 1.324 +#ifdef A11Y_LOG 1.325 + if (logging::IsEnabled(logging::eFocus)) 1.326 + logging::ActiveItemChangeCausedBy("RadioStateChange", accessible); 1.327 +#endif 1.328 + } 1.329 + 1.330 + return; 1.331 + } 1.332 + 1.333 + if (eventType.EqualsLiteral("CheckboxStateChange")) { 1.334 + if (accessible->NeedsDOMUIEvent()) { 1.335 + uint64_t state = accessible->State(); 1.336 + bool isEnabled = !!(state & states::CHECKED); 1.337 + 1.338 + nsRefPtr<AccEvent> accEvent = 1.339 + new AccStateChangeEvent(accessible, states::CHECKED, isEnabled); 1.340 + nsEventShell::FireEvent(accEvent); 1.341 + } 1.342 + return; 1.343 + } 1.344 + 1.345 + Accessible* treeItemAcc = nullptr; 1.346 +#ifdef MOZ_XUL 1.347 + // If it's a tree element, need the currently selected item. 1.348 + if (treeAcc) { 1.349 + treeItemAcc = accessible->CurrentItem(); 1.350 + if (treeItemAcc) 1.351 + accessible = treeItemAcc; 1.352 + } 1.353 + 1.354 + if (treeItemAcc && eventType.EqualsLiteral("OpenStateChange")) { 1.355 + uint64_t state = accessible->State(); 1.356 + bool isEnabled = (state & states::EXPANDED) != 0; 1.357 + 1.358 + nsRefPtr<AccEvent> accEvent = 1.359 + new AccStateChangeEvent(accessible, states::EXPANDED, isEnabled); 1.360 + nsEventShell::FireEvent(accEvent); 1.361 + return; 1.362 + } 1.363 + 1.364 + nsINode* targetNode = accessible->GetNode(); 1.365 + if (treeItemAcc && eventType.EqualsLiteral("select")) { 1.366 + // XXX: We shouldn't be based on DOM select event which doesn't provide us 1.367 + // any context info. We should integrate into nsTreeSelection instead. 1.368 + // If multiselect tree, we should fire selectionadd or selection removed 1.369 + if (FocusMgr()->HasDOMFocus(targetNode)) { 1.370 + nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSel = 1.371 + do_QueryInterface(targetNode); 1.372 + nsAutoString selType; 1.373 + multiSel->GetSelType(selType); 1.374 + if (selType.IsEmpty() || !selType.EqualsLiteral("single")) { 1.375 + // XXX: We need to fire EVENT_SELECTION_ADD and EVENT_SELECTION_REMOVE 1.376 + // for each tree item. Perhaps each tree item will need to cache its 1.377 + // selection state and fire an event after a DOM "select" event when 1.378 + // that state changes. XULTreeAccessible::UpdateTreeSelection(); 1.379 + nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN, 1.380 + accessible); 1.381 + return; 1.382 + } 1.383 + 1.384 + nsRefPtr<AccSelChangeEvent> selChangeEvent = 1.385 + new AccSelChangeEvent(treeAcc, treeItemAcc, 1.386 + AccSelChangeEvent::eSelectionAdd); 1.387 + nsEventShell::FireEvent(selChangeEvent); 1.388 + return; 1.389 + } 1.390 + } 1.391 + else 1.392 +#endif 1.393 + if (eventType.EqualsLiteral("AlertActive")) { 1.394 + nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_ALERT, accessible); 1.395 + } 1.396 + else if (eventType.EqualsLiteral("popupshown")) { 1.397 + HandlePopupShownEvent(accessible); 1.398 + } 1.399 + else if (eventType.EqualsLiteral("DOMMenuInactive")) { 1.400 + if (accessible->Role() == roles::MENUPOPUP) { 1.401 + nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END, 1.402 + accessible); 1.403 + } 1.404 + } 1.405 + else if (eventType.EqualsLiteral("DOMMenuItemActive")) { 1.406 + FocusMgr()->ActiveItemChanged(accessible); 1.407 +#ifdef A11Y_LOG 1.408 + if (logging::IsEnabled(logging::eFocus)) 1.409 + logging::ActiveItemChangeCausedBy("DOMMenuItemActive", accessible); 1.410 +#endif 1.411 + } 1.412 + else if (eventType.EqualsLiteral("DOMMenuItemInactive")) { 1.413 + // Process DOMMenuItemInactive event for autocomplete only because this is 1.414 + // unique widget that may acquire focus from autocomplete popup while popup 1.415 + // stays open and has no active item. In case of XUL tree autocomplete 1.416 + // popup this event is fired for tree accessible. 1.417 + Accessible* widget = 1.418 + accessible->IsWidget() ? accessible : accessible->ContainerWidget(); 1.419 + if (widget && widget->IsAutoCompletePopup()) { 1.420 + FocusMgr()->ActiveItemChanged(nullptr); 1.421 +#ifdef A11Y_LOG 1.422 + if (logging::IsEnabled(logging::eFocus)) 1.423 + logging::ActiveItemChangeCausedBy("DOMMenuItemInactive", accessible); 1.424 +#endif 1.425 + } 1.426 + } 1.427 + else if (eventType.EqualsLiteral("DOMMenuBarActive")) { // Always from user input 1.428 + nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_START, 1.429 + accessible, eFromUserInput); 1.430 + 1.431 + // Notify of active item change when menubar gets active and if it has 1.432 + // current item. This is a case of mouseover (set current menuitem) and 1.433 + // mouse click (activate the menubar). If menubar doesn't have current item 1.434 + // (can be a case of menubar activation from keyboard) then ignore this 1.435 + // notification because later we'll receive DOMMenuItemActive event after 1.436 + // current menuitem is set. 1.437 + Accessible* activeItem = accessible->CurrentItem(); 1.438 + if (activeItem) { 1.439 + FocusMgr()->ActiveItemChanged(activeItem); 1.440 +#ifdef A11Y_LOG 1.441 + if (logging::IsEnabled(logging::eFocus)) 1.442 + logging::ActiveItemChangeCausedBy("DOMMenuBarActive", accessible); 1.443 +#endif 1.444 + } 1.445 + } 1.446 + else if (eventType.EqualsLiteral("DOMMenuBarInactive")) { // Always from user input 1.447 + nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_END, 1.448 + accessible, eFromUserInput); 1.449 + 1.450 + FocusMgr()->ActiveItemChanged(nullptr); 1.451 +#ifdef A11Y_LOG 1.452 + if (logging::IsEnabled(logging::eFocus)) 1.453 + logging::ActiveItemChangeCausedBy("DOMMenuBarInactive", accessible); 1.454 +#endif 1.455 + } 1.456 + else if (accessible->NeedsDOMUIEvent() && 1.457 + eventType.EqualsLiteral("ValueChange")) { 1.458 + targetDocument->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, 1.459 + accessible); 1.460 + } 1.461 +#ifdef DEBUG_DRAGDROPSTART 1.462 + else if (eventType.EqualsLiteral("mouseover")) { 1.463 + nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_DRAGDROP_START, 1.464 + accessible); 1.465 + } 1.466 +#endif 1.467 +} 1.468 + 1.469 + 1.470 +//////////////////////////////////////////////////////////////////////////////// 1.471 +// Accessible 1.472 + 1.473 +void 1.474 +RootAccessible::Shutdown() 1.475 +{ 1.476 + // Called manually or by Accessible::LastRelease() 1.477 + if (!PresShell()) 1.478 + return; // Already shutdown 1.479 + 1.480 + DocAccessibleWrap::Shutdown(); 1.481 +} 1.482 + 1.483 +// nsIAccessible method 1.484 +Relation 1.485 +RootAccessible::RelationByType(RelationType aType) 1.486 +{ 1.487 + if (!mDocumentNode || aType != RelationType::EMBEDS) 1.488 + return DocAccessibleWrap::RelationByType(aType); 1.489 + 1.490 + nsIDOMWindow* rootWindow = mDocumentNode->GetWindow(); 1.491 + if (rootWindow) { 1.492 + nsCOMPtr<nsIDOMWindow> contentWindow; 1.493 + rootWindow->GetContent(getter_AddRefs(contentWindow)); 1.494 + if (contentWindow) { 1.495 + nsCOMPtr<nsIDOMDocument> contentDOMDocument; 1.496 + contentWindow->GetDocument(getter_AddRefs(contentDOMDocument)); 1.497 + nsCOMPtr<nsIDocument> contentDocumentNode = 1.498 + do_QueryInterface(contentDOMDocument); 1.499 + if (contentDocumentNode) { 1.500 + DocAccessible* contentDocument = 1.501 + GetAccService()->GetDocAccessible(contentDocumentNode); 1.502 + if (contentDocument) 1.503 + return Relation(contentDocument); 1.504 + } 1.505 + } 1.506 + } 1.507 + 1.508 + return Relation(); 1.509 +} 1.510 + 1.511 +//////////////////////////////////////////////////////////////////////////////// 1.512 +// Protected members 1.513 + 1.514 +void 1.515 +RootAccessible::HandlePopupShownEvent(Accessible* aAccessible) 1.516 +{ 1.517 + roles::Role role = aAccessible->Role(); 1.518 + 1.519 + if (role == roles::MENUPOPUP) { 1.520 + // Don't fire menupopup events for combobox and autocomplete lists. 1.521 + nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START, 1.522 + aAccessible); 1.523 + return; 1.524 + } 1.525 + 1.526 + if (role == roles::TOOLTIP) { 1.527 + // There is a single <xul:tooltip> node which Mozilla moves around. 1.528 + // The accessible for it stays the same no matter where it moves. 1.529 + // AT's expect to get an EVENT_SHOW for the tooltip. 1.530 + // In event callback the tooltip's accessible will be ready. 1.531 + nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SHOW, aAccessible); 1.532 + return; 1.533 + } 1.534 + 1.535 + if (role == roles::COMBOBOX_LIST) { 1.536 + // Fire expanded state change event for comboboxes and autocompeletes. 1.537 + Accessible* combobox = aAccessible->Parent(); 1.538 + if (!combobox) 1.539 + return; 1.540 + 1.541 + roles::Role comboboxRole = combobox->Role(); 1.542 + if (comboboxRole == roles::COMBOBOX || 1.543 + comboboxRole == roles::AUTOCOMPLETE) { 1.544 + nsRefPtr<AccEvent> event = 1.545 + new AccStateChangeEvent(combobox, states::EXPANDED, true); 1.546 + if (event) 1.547 + nsEventShell::FireEvent(event); 1.548 + } 1.549 + } 1.550 +} 1.551 + 1.552 +void 1.553 +RootAccessible::HandlePopupHidingEvent(nsINode* aPopupNode) 1.554 +{ 1.555 + // Get popup accessible. There are cases when popup element isn't accessible 1.556 + // but an underlying widget is and behaves like popup, an example is 1.557 + // autocomplete popups. 1.558 + DocAccessible* document = nsAccUtils::GetDocAccessibleFor(aPopupNode); 1.559 + if (!document) 1.560 + return; 1.561 + 1.562 + Accessible* popup = document->GetAccessible(aPopupNode); 1.563 + if (!popup) { 1.564 + Accessible* popupContainer = document->GetContainerAccessible(aPopupNode); 1.565 + if (!popupContainer) 1.566 + return; 1.567 + 1.568 + uint32_t childCount = popupContainer->ChildCount(); 1.569 + for (uint32_t idx = 0; idx < childCount; idx++) { 1.570 + Accessible* child = popupContainer->GetChildAt(idx); 1.571 + if (child->IsAutoCompletePopup()) { 1.572 + popup = child; 1.573 + break; 1.574 + } 1.575 + } 1.576 + 1.577 + // No popup no events. Focus is managed by DOM. This is a case for 1.578 + // menupopups of menus on Linux since there are no accessible for popups. 1.579 + if (!popup) 1.580 + return; 1.581 + } 1.582 + 1.583 + // In case of autocompletes and comboboxes fire state change event for 1.584 + // expanded state. Note, HTML form autocomplete isn't a subject of state 1.585 + // change event because they aren't autocompletes strictly speaking. 1.586 + // When popup closes (except nested popups and menus) then fire focus event to 1.587 + // where it was. The focus event is expected even if popup didn't take a focus. 1.588 + 1.589 + static const uint32_t kNotifyOfFocus = 1; 1.590 + static const uint32_t kNotifyOfState = 2; 1.591 + uint32_t notifyOf = 0; 1.592 + 1.593 + // HTML select is target of popuphidding event. Otherwise get container 1.594 + // widget. No container widget means this is either tooltip or menupopup. 1.595 + // No events in the former case. 1.596 + Accessible* widget = nullptr; 1.597 + if (popup->IsCombobox()) { 1.598 + widget = popup; 1.599 + } else { 1.600 + widget = popup->ContainerWidget(); 1.601 + if (!widget) { 1.602 + if (!popup->IsMenuPopup()) 1.603 + return; 1.604 + 1.605 + widget = popup; 1.606 + } 1.607 + } 1.608 + 1.609 + if (popup->IsAutoCompletePopup()) { 1.610 + // No focus event for autocomplete because it's managed by 1.611 + // DOMMenuItemInactive events. 1.612 + if (widget->IsAutoComplete()) 1.613 + notifyOf = kNotifyOfState; 1.614 + 1.615 + } else if (widget->IsCombobox()) { 1.616 + // Fire focus for active combobox, otherwise the focus is managed by DOM 1.617 + // focus notifications. Always fire state change event. 1.618 + if (widget->IsActiveWidget()) 1.619 + notifyOf = kNotifyOfFocus; 1.620 + notifyOf |= kNotifyOfState; 1.621 + 1.622 + } else if (widget->IsMenuButton()) { 1.623 + // Can be a part of autocomplete. 1.624 + Accessible* compositeWidget = widget->ContainerWidget(); 1.625 + if (compositeWidget && compositeWidget->IsAutoComplete()) { 1.626 + widget = compositeWidget; 1.627 + notifyOf = kNotifyOfState; 1.628 + } 1.629 + 1.630 + // Autocomplete (like searchbar) can be inactive when popup hiddens 1.631 + notifyOf |= kNotifyOfFocus; 1.632 + 1.633 + } else if (widget == popup) { 1.634 + // Top level context menus and alerts. 1.635 + // Ignore submenus and menubar. When submenu is closed then sumbenu 1.636 + // container menuitem takes a focus via DOMMenuItemActive notification. 1.637 + // For menubars processing we listen DOMMenubarActive/Inactive 1.638 + // notifications. 1.639 + notifyOf = kNotifyOfFocus; 1.640 + } 1.641 + 1.642 + // Restore focus to where it was. 1.643 + if (notifyOf & kNotifyOfFocus) { 1.644 + FocusMgr()->ActiveItemChanged(nullptr); 1.645 +#ifdef A11Y_LOG 1.646 + if (logging::IsEnabled(logging::eFocus)) 1.647 + logging::ActiveItemChangeCausedBy("popuphiding", popup); 1.648 +#endif 1.649 + } 1.650 + 1.651 + // Fire expanded state change event. 1.652 + if (notifyOf & kNotifyOfState) { 1.653 + nsRefPtr<AccEvent> event = 1.654 + new AccStateChangeEvent(widget, states::EXPANDED, false); 1.655 + document->FireDelayedEvent(event); 1.656 + } 1.657 +} 1.658 + 1.659 +#ifdef MOZ_XUL 1.660 +void 1.661 +RootAccessible::HandleTreeRowCountChangedEvent(nsIDOMEvent* aEvent, 1.662 + XULTreeAccessible* aAccessible) 1.663 +{ 1.664 + nsCOMPtr<nsIDOMCustomEvent> customEvent(do_QueryInterface(aEvent)); 1.665 + if (!customEvent) 1.666 + return; 1.667 + 1.668 + nsCOMPtr<nsIVariant> detailVariant; 1.669 + customEvent->GetDetail(getter_AddRefs(detailVariant)); 1.670 + if (!detailVariant) 1.671 + return; 1.672 + 1.673 + nsCOMPtr<nsISupports> supports; 1.674 + detailVariant->GetAsISupports(getter_AddRefs(supports)); 1.675 + nsCOMPtr<nsIPropertyBag2> propBag(do_QueryInterface(supports)); 1.676 + if (!propBag) 1.677 + return; 1.678 + 1.679 + nsresult rv; 1.680 + int32_t index, count; 1.681 + rv = propBag->GetPropertyAsInt32(NS_LITERAL_STRING("index"), &index); 1.682 + if (NS_FAILED(rv)) 1.683 + return; 1.684 + 1.685 + rv = propBag->GetPropertyAsInt32(NS_LITERAL_STRING("count"), &count); 1.686 + if (NS_FAILED(rv)) 1.687 + return; 1.688 + 1.689 + aAccessible->InvalidateCache(index, count); 1.690 +} 1.691 + 1.692 +void 1.693 +RootAccessible::HandleTreeInvalidatedEvent(nsIDOMEvent* aEvent, 1.694 + XULTreeAccessible* aAccessible) 1.695 +{ 1.696 + nsCOMPtr<nsIDOMCustomEvent> customEvent(do_QueryInterface(aEvent)); 1.697 + if (!customEvent) 1.698 + return; 1.699 + 1.700 + nsCOMPtr<nsIVariant> detailVariant; 1.701 + customEvent->GetDetail(getter_AddRefs(detailVariant)); 1.702 + if (!detailVariant) 1.703 + return; 1.704 + 1.705 + nsCOMPtr<nsISupports> supports; 1.706 + detailVariant->GetAsISupports(getter_AddRefs(supports)); 1.707 + nsCOMPtr<nsIPropertyBag2> propBag(do_QueryInterface(supports)); 1.708 + if (!propBag) 1.709 + return; 1.710 + 1.711 + int32_t startRow = 0, endRow = -1, startCol = 0, endCol = -1; 1.712 + propBag->GetPropertyAsInt32(NS_LITERAL_STRING("startrow"), 1.713 + &startRow); 1.714 + propBag->GetPropertyAsInt32(NS_LITERAL_STRING("endrow"), 1.715 + &endRow); 1.716 + propBag->GetPropertyAsInt32(NS_LITERAL_STRING("startcolumn"), 1.717 + &startCol); 1.718 + propBag->GetPropertyAsInt32(NS_LITERAL_STRING("endcolumn"), 1.719 + &endCol); 1.720 + 1.721 + aAccessible->TreeViewInvalidated(startRow, endRow, startCol, endCol); 1.722 +} 1.723 +#endif