Bug 1700051: part 46) Const-qualify `mozInlineSpellStatus::mAnchorRange`. r=smaug
[gecko.git] / accessible / generic / RootAccessible.cpp
blob0742a46326316caec5f28a166b7680f1db467687
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "RootAccessible.h"
8 #include "mozilla/ArrayUtils.h"
9 #include "mozilla/PresShell.h" // for nsAccUtils::GetDocAccessibleFor()
11 #define CreateEvent CreateEventA
13 #include "LocalAccessible-inl.h"
14 #include "DocAccessible-inl.h"
15 #include "mozilla/a11y/DocAccessibleParent.h"
16 #include "nsAccessibilityService.h"
17 #include "nsAccUtils.h"
18 #include "nsCoreUtils.h"
19 #include "nsEventShell.h"
20 #include "Relation.h"
21 #include "Role.h"
22 #include "States.h"
23 #ifdef MOZ_XUL
24 # include "XULTreeAccessible.h"
25 #endif
27 #include "mozilla/dom/BindingUtils.h"
28 #include "mozilla/dom/CustomEvent.h"
29 #include "mozilla/dom/Element.h"
30 #include "mozilla/dom/ScriptSettings.h"
31 #include "mozilla/dom/BrowserHost.h"
33 #include "nsIDocShellTreeOwner.h"
34 #include "mozilla/dom/Event.h"
35 #include "mozilla/dom/EventTarget.h"
36 #include "nsIDOMXULMultSelectCntrlEl.h"
37 #include "mozilla/dom/Document.h"
38 #include "nsIInterfaceRequestorUtils.h"
39 #include "nsIPropertyBag2.h"
40 #include "nsPIDOMWindow.h"
41 #include "nsIWebBrowserChrome.h"
42 #include "nsReadableUtils.h"
43 #include "nsFocusManager.h"
44 #include "nsGlobalWindow.h"
46 #ifdef MOZ_XUL
47 # include "nsIAppWindow.h"
48 #endif
50 using namespace mozilla;
51 using namespace mozilla::a11y;
52 using namespace mozilla::dom;
54 ////////////////////////////////////////////////////////////////////////////////
55 // nsISupports
57 NS_IMPL_ISUPPORTS_INHERITED(RootAccessible, DocAccessible, nsIDOMEventListener)
59 ////////////////////////////////////////////////////////////////////////////////
60 // Constructor/destructor
62 RootAccessible::RootAccessible(Document* aDocument, PresShell* aPresShell)
63 : DocAccessibleWrap(aDocument, aPresShell) {
64 mType = eRootType;
67 RootAccessible::~RootAccessible() {}
69 ////////////////////////////////////////////////////////////////////////////////
70 // LocalAccessible
72 ENameValueFlag RootAccessible::Name(nsString& aName) const {
73 aName.Truncate();
75 if (ARIARoleMap()) {
76 LocalAccessible::Name(aName);
77 if (!aName.IsEmpty()) return eNameOK;
80 mDocumentNode->GetTitle(aName);
81 return eNameOK;
84 // RootAccessible protected member
85 #ifdef MOZ_XUL
86 uint32_t RootAccessible::GetChromeFlags() const {
87 // Return the flag set for the top level window as defined
88 // by nsIWebBrowserChrome::CHROME_WINDOW_[FLAGNAME]
89 // Not simple: nsIAppWindow is not just a QI from nsIDOMWindow
90 nsCOMPtr<nsIDocShell> docShell = nsCoreUtils::GetDocShellFor(mDocumentNode);
91 NS_ENSURE_TRUE(docShell, 0);
92 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
93 docShell->GetTreeOwner(getter_AddRefs(treeOwner));
94 NS_ENSURE_TRUE(treeOwner, 0);
95 nsCOMPtr<nsIAppWindow> appWin(do_GetInterface(treeOwner));
96 if (!appWin) {
97 return 0;
99 uint32_t chromeFlags;
100 appWin->GetChromeFlags(&chromeFlags);
101 return chromeFlags;
103 #endif
105 uint64_t RootAccessible::NativeState() const {
106 uint64_t state = DocAccessibleWrap::NativeState();
107 if (state & states::DEFUNCT) return state;
109 #ifdef MOZ_XUL
110 uint32_t chromeFlags = GetChromeFlags();
111 if (chromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE) {
112 state |= states::SIZEABLE;
114 // If it has a titlebar it's movable
115 // XXX unless it's minimized or maximized, but not sure
116 // how to detect that
117 if (chromeFlags & nsIWebBrowserChrome::CHROME_TITLEBAR) {
118 state |= states::MOVEABLE;
120 if (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL) state |= states::MODAL;
121 #endif
123 nsFocusManager* fm = nsFocusManager::GetFocusManager();
124 if (fm && fm->GetActiveWindow() == mDocumentNode->GetWindow()) {
125 state |= states::ACTIVE;
128 return state;
131 const char* const kEventTypes[] = {
132 #ifdef DEBUG_DRAGDROPSTART
133 // Capture mouse over events and fire fake DRAGDROPSTART event to simplify
134 // debugging a11y objects with event viewers.
135 "mouseover",
136 #endif
137 // Fired when list or tree selection changes.
138 "select",
139 // Fired when value changes immediately, wether or not focused changed.
140 "ValueChange", "AlertActive", "TreeRowCountChanged", "TreeInvalidated",
141 // add ourself as a OpenStateChange listener (custom event fired in
142 // tree.xml)
143 "OpenStateChange",
144 // add ourself as a CheckboxStateChange listener (custom event fired in
145 // HTMLInputElement.cpp)
146 "CheckboxStateChange",
147 // add ourself as a RadioStateChange Listener (custom event fired in in
148 // HTMLInputElement.cpp & radio.js)
149 "RadioStateChange", "popupshown", "popuphiding", "DOMMenuInactive",
150 "DOMMenuItemActive", "DOMMenuItemInactive", "DOMMenuBarActive",
151 "DOMMenuBarInactive", "scroll"};
153 nsresult RootAccessible::AddEventListeners() {
154 // EventTarget interface allows to register event listeners to
155 // receive untrusted events (synthetic events generated by untrusted code).
156 // For example, XBL bindings implementations for elements that are hosted in
157 // non chrome document fire untrusted events.
158 // We must use the window's parent target in order to receive events from
159 // iframes and shadow DOM; e.g. ValueChange events from a <select> in an
160 // iframe or shadow DOM. The root document itself doesn't receive these.
161 nsPIDOMWindowOuter* window = mDocumentNode->GetWindow();
162 nsCOMPtr<EventTarget> nstarget = window ? window->GetParentTarget() : nullptr;
164 if (nstarget) {
165 for (const char *const *e = kEventTypes, *const *e_end =
166 ArrayEnd(kEventTypes);
167 e < e_end; ++e) {
168 nsresult rv = nstarget->AddEventListener(NS_ConvertASCIItoUTF16(*e), this,
169 true, true);
170 NS_ENSURE_SUCCESS(rv, rv);
174 return DocAccessible::AddEventListeners();
177 nsresult RootAccessible::RemoveEventListeners() {
178 nsPIDOMWindowOuter* window = mDocumentNode->GetWindow();
179 nsCOMPtr<EventTarget> target = window ? window->GetParentTarget() : nullptr;
180 if (target) {
181 for (const char *const *e = kEventTypes, *const *e_end =
182 ArrayEnd(kEventTypes);
183 e < e_end; ++e) {
184 target->RemoveEventListener(NS_ConvertASCIItoUTF16(*e), this, true);
188 // Do this before removing clearing caret accessible, so that it can use
189 // shutdown the caret accessible's selection listener
190 DocAccessible::RemoveEventListeners();
191 return NS_OK;
194 ////////////////////////////////////////////////////////////////////////////////
195 // public
197 void RootAccessible::DocumentActivated(DocAccessible* aDocument) {}
199 ////////////////////////////////////////////////////////////////////////////////
200 // nsIDOMEventListener
202 NS_IMETHODIMP
203 RootAccessible::HandleEvent(Event* aDOMEvent) {
204 MOZ_ASSERT(aDOMEvent);
205 if (IsDefunct()) {
206 // Even though we've been shut down, RemoveEventListeners might not have
207 // removed the event handlers on the window's parent target if GetWindow
208 // returned null, so we might still get events here in this case. We should
209 // just ignore these events.
210 return NS_OK;
213 nsCOMPtr<nsINode> origTargetNode =
214 do_QueryInterface(aDOMEvent->GetOriginalTarget());
215 if (!origTargetNode) return NS_OK;
217 #ifdef A11Y_LOG
218 if (logging::IsEnabled(logging::eDOMEvents)) {
219 nsAutoString eventType;
220 aDOMEvent->GetType(eventType);
221 logging::DOMEvent("handled", origTargetNode, eventType);
223 #endif
225 DocAccessible* document =
226 GetAccService()->GetDocAccessible(origTargetNode->OwnerDoc());
228 if (document) {
229 nsAutoString eventType;
230 aDOMEvent->GetType(eventType);
231 if (eventType.EqualsLiteral("scroll")) {
232 // We don't put this in the notification queue for 2 reasons:
233 // 1. We will flood the queue with repetitive events.
234 // 2. Since this doesn't necessarily touch layout, we are not
235 // guaranteed to have a WillRefresh tick any time soon.
236 document->HandleScroll(origTargetNode);
237 } else {
238 // Root accessible exists longer than any of its descendant documents so
239 // that we are guaranteed notification is processed before root accessible
240 // is destroyed.
241 // For shadow DOM, GetOriginalTarget on the Event returns null if we
242 // process the event async, so we must pass the target node as well.
243 document->HandleNotification<RootAccessible, Event, nsINode>(
244 this, &RootAccessible::ProcessDOMEvent, aDOMEvent, origTargetNode);
248 return NS_OK;
251 // RootAccessible protected
252 void RootAccessible::ProcessDOMEvent(Event* aDOMEvent, nsINode* aTarget) {
253 MOZ_ASSERT(aDOMEvent);
254 MOZ_ASSERT(aTarget);
256 nsAutoString eventType;
257 aDOMEvent->GetType(eventType);
259 #ifdef A11Y_LOG
260 if (logging::IsEnabled(logging::eDOMEvents)) {
261 logging::DOMEvent("processed", aTarget, eventType);
263 #endif
265 if (eventType.EqualsLiteral("popuphiding")) {
266 HandlePopupHidingEvent(aTarget);
267 return;
270 DocAccessible* targetDocument =
271 GetAccService()->GetDocAccessible(aTarget->OwnerDoc());
272 if (!targetDocument) {
273 // Document has ceased to exist.
274 return;
277 if (eventType.EqualsLiteral("popupshown") &&
278 (aTarget->IsXULElement(nsGkAtoms::tooltip) ||
279 aTarget->IsXULElement(nsGkAtoms::panel))) {
280 targetDocument->ContentInserted(aTarget->AsContent(),
281 aTarget->GetNextSibling());
282 return;
285 LocalAccessible* accessible =
286 targetDocument->GetAccessibleOrContainer(aTarget);
287 if (!accessible) return;
289 #ifdef MOZ_XUL
290 XULTreeAccessible* treeAcc = accessible->AsXULTree();
291 if (treeAcc) {
292 if (eventType.EqualsLiteral("TreeRowCountChanged")) {
293 HandleTreeRowCountChangedEvent(aDOMEvent, treeAcc);
294 return;
297 if (eventType.EqualsLiteral("TreeInvalidated")) {
298 HandleTreeInvalidatedEvent(aDOMEvent, treeAcc);
299 return;
302 #endif
304 if (eventType.EqualsLiteral("RadioStateChange")) {
305 uint64_t state = accessible->State();
306 bool isEnabled = (state & (states::CHECKED | states::SELECTED)) != 0;
308 if (accessible->NeedsDOMUIEvent()) {
309 RefPtr<AccEvent> accEvent =
310 new AccStateChangeEvent(accessible, states::CHECKED, isEnabled);
311 nsEventShell::FireEvent(accEvent);
314 if (isEnabled) {
315 FocusMgr()->ActiveItemChanged(accessible);
316 #ifdef A11Y_LOG
317 if (logging::IsEnabled(logging::eFocus)) {
318 logging::ActiveItemChangeCausedBy("RadioStateChange", accessible);
320 #endif
323 return;
326 if (eventType.EqualsLiteral("CheckboxStateChange")) {
327 if (accessible->NeedsDOMUIEvent()) {
328 uint64_t state = accessible->State();
329 bool isEnabled = !!(state & states::CHECKED);
331 RefPtr<AccEvent> accEvent =
332 new AccStateChangeEvent(accessible, states::CHECKED, isEnabled);
333 nsEventShell::FireEvent(accEvent);
335 return;
338 LocalAccessible* treeItemAcc = nullptr;
339 #ifdef MOZ_XUL
340 // If it's a tree element, need the currently selected item.
341 if (treeAcc) {
342 treeItemAcc = accessible->CurrentItem();
343 if (treeItemAcc) accessible = treeItemAcc;
346 if (treeItemAcc && eventType.EqualsLiteral("OpenStateChange")) {
347 uint64_t state = accessible->State();
348 bool isEnabled = (state & states::EXPANDED) != 0;
350 RefPtr<AccEvent> accEvent =
351 new AccStateChangeEvent(accessible, states::EXPANDED, isEnabled);
352 nsEventShell::FireEvent(accEvent);
353 return;
356 nsINode* targetNode = accessible->GetNode();
357 if (treeItemAcc && eventType.EqualsLiteral("select")) {
358 // XXX: We shouldn't be based on DOM select event which doesn't provide us
359 // any context info. We should integrate into nsTreeSelection instead.
360 // If multiselect tree, we should fire selectionadd or selection removed
361 if (FocusMgr()->HasDOMFocus(targetNode)) {
362 nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSel =
363 targetNode->AsElement()->AsXULMultiSelectControl();
364 if (!multiSel) {
365 // This shouldn't be possible. All XUL trees should have
366 // nsIDOMXULMultiSelectControlElement, and the tree is focused, so it
367 // shouldn't be dying. Nevertheless, this sometimes happens in the wild
368 // (bug 1597043).
369 MOZ_ASSERT_UNREACHABLE(
370 "XUL tree doesn't have nsIDOMXULMultiSelectControlElement");
371 return;
373 nsAutoString selType;
374 multiSel->GetSelType(selType);
375 if (selType.IsEmpty() || !selType.EqualsLiteral("single")) {
376 // XXX: We need to fire EVENT_SELECTION_ADD and EVENT_SELECTION_REMOVE
377 // for each tree item. Perhaps each tree item will need to cache its
378 // selection state and fire an event after a DOM "select" event when
379 // that state changes. XULTreeAccessible::UpdateTreeSelection();
380 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
381 accessible);
382 return;
385 RefPtr<AccSelChangeEvent> selChangeEvent = new AccSelChangeEvent(
386 treeAcc, treeItemAcc, AccSelChangeEvent::eSelectionAdd);
387 nsEventShell::FireEvent(selChangeEvent);
388 return;
390 } else
391 #endif
392 if (eventType.EqualsLiteral("AlertActive")) {
393 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_ALERT, accessible);
394 } else if (eventType.EqualsLiteral("popupshown")) {
395 HandlePopupShownEvent(accessible);
396 } else if (eventType.EqualsLiteral("DOMMenuInactive")) {
397 if (accessible->Role() == roles::MENUPOPUP) {
398 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
399 accessible);
401 } else if (eventType.EqualsLiteral("DOMMenuItemActive")) {
402 FocusMgr()->ActiveItemChanged(accessible);
403 #ifdef A11Y_LOG
404 if (logging::IsEnabled(logging::eFocus)) {
405 logging::ActiveItemChangeCausedBy("DOMMenuItemActive", accessible);
407 #endif
408 } else if (eventType.EqualsLiteral("DOMMenuItemInactive")) {
409 // Process DOMMenuItemInactive event for autocomplete only because this is
410 // unique widget that may acquire focus from autocomplete popup while popup
411 // stays open and has no active item. In case of XUL tree autocomplete
412 // popup this event is fired for tree accessible.
413 LocalAccessible* widget =
414 accessible->IsWidget() ? accessible : accessible->ContainerWidget();
415 if (widget && widget->IsAutoCompletePopup()) {
416 FocusMgr()->ActiveItemChanged(nullptr);
417 #ifdef A11Y_LOG
418 if (logging::IsEnabled(logging::eFocus)) {
419 logging::ActiveItemChangeCausedBy("DOMMenuItemInactive", accessible);
421 #endif
423 } else if (eventType.EqualsLiteral(
424 "DOMMenuBarActive")) { // Always from user input
425 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_START, accessible,
426 eFromUserInput);
428 // Notify of active item change when menubar gets active and if it has
429 // current item. This is a case of mouseover (set current menuitem) and
430 // mouse click (activate the menubar). If menubar doesn't have current item
431 // (can be a case of menubar activation from keyboard) then ignore this
432 // notification because later we'll receive DOMMenuItemActive event after
433 // current menuitem is set.
434 LocalAccessible* activeItem = accessible->CurrentItem();
435 if (activeItem) {
436 FocusMgr()->ActiveItemChanged(activeItem);
437 #ifdef A11Y_LOG
438 if (logging::IsEnabled(logging::eFocus)) {
439 logging::ActiveItemChangeCausedBy("DOMMenuBarActive", accessible);
441 #endif
443 } else if (eventType.EqualsLiteral(
444 "DOMMenuBarInactive")) { // Always from user input
445 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_END, accessible,
446 eFromUserInput);
448 FocusMgr()->ActiveItemChanged(nullptr);
449 #ifdef A11Y_LOG
450 if (logging::IsEnabled(logging::eFocus)) {
451 logging::ActiveItemChangeCausedBy("DOMMenuBarInactive", accessible);
453 #endif
454 } else if (accessible->NeedsDOMUIEvent() &&
455 eventType.EqualsLiteral("ValueChange")) {
456 uint32_t event = accessible->HasNumericValue()
457 ? nsIAccessibleEvent::EVENT_VALUE_CHANGE
458 : nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE;
459 targetDocument->FireDelayedEvent(event, accessible);
461 #ifdef DEBUG_DRAGDROPSTART
462 else if (eventType.EqualsLiteral("mouseover")) {
463 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_DRAGDROP_START,
464 accessible);
466 #endif
469 ////////////////////////////////////////////////////////////////////////////////
470 // LocalAccessible
472 void RootAccessible::Shutdown() {
473 // Called manually or by LocalAccessible::LastRelease()
474 if (HasShutdown()) {
475 return;
477 DocAccessibleWrap::Shutdown();
480 Relation RootAccessible::RelationByType(RelationType aType) const {
481 if (!mDocumentNode || aType != RelationType::EMBEDS) {
482 return DocAccessibleWrap::RelationByType(aType);
485 if (nsIDocShell* docShell = mDocumentNode->GetDocShell()) {
486 nsCOMPtr<nsIDocShellTreeOwner> owner;
487 docShell->GetTreeOwner(getter_AddRefs(owner));
488 if (owner) {
489 nsCOMPtr<nsIDocShellTreeItem> contentShell;
490 owner->GetPrimaryContentShell(getter_AddRefs(contentShell));
491 if (contentShell) {
492 return Relation(nsAccUtils::GetDocAccessibleFor(contentShell));
497 return Relation();
500 ////////////////////////////////////////////////////////////////////////////////
501 // Protected members
503 void RootAccessible::HandlePopupShownEvent(LocalAccessible* aAccessible) {
504 roles::Role role = aAccessible->Role();
506 if (role == roles::MENUPOPUP) {
507 // Don't fire menupopup events for combobox and autocomplete lists.
508 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START,
509 aAccessible);
510 return;
513 if (role == roles::COMBOBOX_LIST) {
514 // Fire expanded state change event for comboboxes and autocompeletes.
515 LocalAccessible* combobox = aAccessible->LocalParent();
516 if (!combobox) return;
518 if (combobox->IsCombobox() || combobox->IsAutoComplete()) {
519 RefPtr<AccEvent> event =
520 new AccStateChangeEvent(combobox, states::EXPANDED, true);
521 nsEventShell::FireEvent(event);
524 // If aria-activedescendant is present, redirect focus.
525 // This is needed for parent process <select> dropdowns, which use a
526 // menulist containing div elements instead of XUL menuitems. XUL menuitems
527 // fire DOMMenuItemActive events from layout instead.
528 MOZ_ASSERT(aAccessible->Elm());
529 if (aAccessible->Elm()->HasAttr(kNameSpaceID_None,
530 nsGkAtoms::aria_activedescendant)) {
531 LocalAccessible* activeDescendant = aAccessible->CurrentItem();
532 if (activeDescendant) {
533 FocusMgr()->ActiveItemChanged(activeDescendant, false);
534 #ifdef A11Y_LOG
535 if (logging::IsEnabled(logging::eFocus)) {
536 logging::ActiveItemChangeCausedBy("ARIA activedescendant on popup",
537 activeDescendant);
539 #endif
545 void RootAccessible::HandlePopupHidingEvent(nsINode* aPopupNode) {
546 DocAccessible* document = nsAccUtils::GetDocAccessibleFor(aPopupNode);
547 if (!document) return;
549 if (aPopupNode->IsXULElement(nsGkAtoms::tooltip) ||
550 aPopupNode->IsXULElement(nsGkAtoms::panel)) {
551 document->ContentRemoved(aPopupNode->AsContent());
552 return;
555 // Get popup accessible. There are cases when popup element isn't accessible
556 // but an underlying widget is and behaves like popup, an example is
557 // autocomplete popups.
558 LocalAccessible* popup = document->GetAccessible(aPopupNode);
559 if (!popup) {
560 LocalAccessible* popupContainer =
561 document->GetContainerAccessible(aPopupNode);
562 if (!popupContainer) return;
564 uint32_t childCount = popupContainer->ChildCount();
565 for (uint32_t idx = 0; idx < childCount; idx++) {
566 LocalAccessible* child = popupContainer->LocalChildAt(idx);
567 if (child->IsAutoCompletePopup()) {
568 popup = child;
569 break;
573 // No popup no events. Focus is managed by DOM. This is a case for
574 // menupopups of menus on Linux since there are no accessible for popups.
575 if (!popup) return;
578 // In case of autocompletes and comboboxes fire state change event for
579 // expanded state. Note, HTML form autocomplete isn't a subject of state
580 // change event because they aren't autocompletes strictly speaking.
581 // When popup closes (except nested popups and menus) then fire focus event to
582 // where it was. The focus event is expected even if popup didn't take a
583 // focus.
585 static const uint32_t kNotifyOfFocus = 1;
586 static const uint32_t kNotifyOfState = 2;
587 uint32_t notifyOf = 0;
589 // HTML select is target of popuphidding event. Otherwise get container
590 // widget. No container widget means this is either tooltip or menupopup.
591 // No events in the former case.
592 LocalAccessible* widget = nullptr;
593 if (popup->IsCombobox()) {
594 widget = popup;
595 } else {
596 widget = popup->ContainerWidget();
597 if (!widget) {
598 if (!popup->IsMenuPopup()) return;
600 widget = popup;
604 if (popup->IsAutoCompletePopup()) {
605 // No focus event for autocomplete because it's managed by
606 // DOMMenuItemInactive events.
607 if (widget->IsAutoComplete()) notifyOf = kNotifyOfState;
609 } else if (widget->IsCombobox()) {
610 // Fire focus for active combobox, otherwise the focus is managed by DOM
611 // focus notifications. Always fire state change event.
612 if (widget->IsActiveWidget()) notifyOf = kNotifyOfFocus;
613 notifyOf |= kNotifyOfState;
615 } else if (widget->IsMenuButton()) {
616 // Can be a part of autocomplete.
617 LocalAccessible* compositeWidget = widget->ContainerWidget();
618 if (compositeWidget && compositeWidget->IsAutoComplete()) {
619 widget = compositeWidget;
620 notifyOf = kNotifyOfState;
623 // Autocomplete (like searchbar) can be inactive when popup hiddens
624 notifyOf |= kNotifyOfFocus;
626 } else if (widget == popup) {
627 // Top level context menus and alerts.
628 // Ignore submenus and menubar. When submenu is closed then sumbenu
629 // container menuitem takes a focus via DOMMenuItemActive notification.
630 // For menubars processing we listen DOMMenubarActive/Inactive
631 // notifications.
632 notifyOf = kNotifyOfFocus;
635 // Restore focus to where it was.
636 if (notifyOf & kNotifyOfFocus) {
637 FocusMgr()->ActiveItemChanged(nullptr);
638 #ifdef A11Y_LOG
639 if (logging::IsEnabled(logging::eFocus)) {
640 logging::ActiveItemChangeCausedBy("popuphiding", popup);
642 #endif
645 // Fire expanded state change event.
646 if (notifyOf & kNotifyOfState) {
647 RefPtr<AccEvent> event =
648 new AccStateChangeEvent(widget, states::EXPANDED, false);
649 document->FireDelayedEvent(event);
653 #ifdef MOZ_XUL
654 static void GetPropertyBagFromEvent(Event* aEvent,
655 nsIPropertyBag2** aPropertyBag) {
656 *aPropertyBag = nullptr;
658 CustomEvent* customEvent = aEvent->AsCustomEvent();
659 if (!customEvent) return;
661 AutoJSAPI jsapi;
662 if (!jsapi.Init(customEvent->GetParentObject())) return;
664 JSContext* cx = jsapi.cx();
665 JS::Rooted<JS::Value> detail(cx);
666 customEvent->GetDetail(cx, &detail);
667 if (!detail.isObject()) return;
669 JS::Rooted<JSObject*> detailObj(cx, &detail.toObject());
671 nsresult rv;
672 nsCOMPtr<nsIPropertyBag2> propBag;
673 rv = UnwrapArg<nsIPropertyBag2>(cx, detailObj, getter_AddRefs(propBag));
674 if (NS_FAILED(rv)) return;
676 propBag.forget(aPropertyBag);
679 void RootAccessible::HandleTreeRowCountChangedEvent(
680 Event* aEvent, XULTreeAccessible* aAccessible) {
681 nsCOMPtr<nsIPropertyBag2> propBag;
682 GetPropertyBagFromEvent(aEvent, getter_AddRefs(propBag));
683 if (!propBag) return;
685 nsresult rv;
686 int32_t index, count;
687 rv = propBag->GetPropertyAsInt32(u"index"_ns, &index);
688 if (NS_FAILED(rv)) return;
690 rv = propBag->GetPropertyAsInt32(u"count"_ns, &count);
691 if (NS_FAILED(rv)) return;
693 aAccessible->InvalidateCache(index, count);
696 void RootAccessible::HandleTreeInvalidatedEvent(
697 Event* aEvent, XULTreeAccessible* aAccessible) {
698 nsCOMPtr<nsIPropertyBag2> propBag;
699 GetPropertyBagFromEvent(aEvent, getter_AddRefs(propBag));
700 if (!propBag) return;
702 int32_t startRow = 0, endRow = -1, startCol = 0, endCol = -1;
703 propBag->GetPropertyAsInt32(u"startrow"_ns, &startRow);
704 propBag->GetPropertyAsInt32(u"endrow"_ns, &endRow);
705 propBag->GetPropertyAsInt32(u"startcolumn"_ns, &startCol);
706 propBag->GetPropertyAsInt32(u"endcolumn"_ns, &endCol);
708 aAccessible->TreeViewInvalidated(startRow, endRow, startCol, endCol);
710 #endif
712 RemoteAccessible* RootAccessible::GetPrimaryRemoteTopLevelContentDoc() const {
713 nsCOMPtr<nsIDocShellTreeOwner> owner;
714 mDocumentNode->GetDocShell()->GetTreeOwner(getter_AddRefs(owner));
715 NS_ENSURE_TRUE(owner, nullptr);
717 nsCOMPtr<nsIRemoteTab> remoteTab;
718 owner->GetPrimaryRemoteTab(getter_AddRefs(remoteTab));
719 if (!remoteTab) {
720 return nullptr;
723 auto tab = static_cast<dom::BrowserHost*>(remoteTab.get());
724 return tab->GetTopLevelDocAccessible();