summaryrefslogtreecommitdiffstats
path: root/accessible/generic/RootAccessible.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'accessible/generic/RootAccessible.cpp')
-rw-r--r--accessible/generic/RootAccessible.cpp732
1 files changed, 732 insertions, 0 deletions
diff --git a/accessible/generic/RootAccessible.cpp b/accessible/generic/RootAccessible.cpp
new file mode 100644
index 000000000..5817f2da9
--- /dev/null
+++ b/accessible/generic/RootAccessible.cpp
@@ -0,0 +1,732 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RootAccessible.h"
+
+#include "mozilla/ArrayUtils.h"
+
+#define CreateEvent CreateEventA
+#include "nsIDOMDocument.h"
+
+#include "Accessible-inl.h"
+#include "DocAccessible-inl.h"
+#include "nsAccessibilityService.h"
+#include "nsAccUtils.h"
+#include "nsCoreUtils.h"
+#include "nsEventShell.h"
+#include "Relation.h"
+#include "Role.h"
+#include "States.h"
+#ifdef MOZ_XUL
+#include "XULTreeAccessible.h"
+#endif
+
+#include "mozilla/dom/Element.h"
+
+#include "nsIDocShellTreeItem.h"
+#include "nsIDocShellTreeOwner.h"
+#include "mozilla/dom/Event.h"
+#include "mozilla/dom/EventTarget.h"
+#include "nsIDOMCustomEvent.h"
+#include "nsIDOMXULMultSelectCntrlEl.h"
+#include "nsIDocument.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIPropertyBag2.h"
+#include "nsIServiceManager.h"
+#include "nsPIDOMWindow.h"
+#include "nsIWebBrowserChrome.h"
+#include "nsReadableUtils.h"
+#include "nsFocusManager.h"
+#include "nsGlobalWindow.h"
+
+#ifdef MOZ_XUL
+#include "nsIXULDocument.h"
+#include "nsIXULWindow.h"
+#endif
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+using namespace mozilla::dom;
+
+////////////////////////////////////////////////////////////////////////////////
+// nsISupports
+
+NS_IMPL_ISUPPORTS_INHERITED0(RootAccessible, DocAccessible)
+
+////////////////////////////////////////////////////////////////////////////////
+// Constructor/destructor
+
+RootAccessible::
+ RootAccessible(nsIDocument* aDocument, nsIPresShell* aPresShell) :
+ DocAccessibleWrap(aDocument, aPresShell)
+{
+ mType = eRootType;
+}
+
+RootAccessible::~RootAccessible()
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Accessible
+
+ENameValueFlag
+RootAccessible::Name(nsString& aName)
+{
+ aName.Truncate();
+
+ if (ARIARoleMap()) {
+ Accessible::Name(aName);
+ if (!aName.IsEmpty())
+ return eNameOK;
+ }
+
+ mDocumentNode->GetTitle(aName);
+ return eNameOK;
+}
+
+role
+RootAccessible::NativeRole()
+{
+ // If it's a <dialog> or <wizard>, use roles::DIALOG instead
+ dom::Element* rootElm = mDocumentNode->GetRootElement();
+ if (rootElm && rootElm->IsAnyOfXULElements(nsGkAtoms::dialog,
+ nsGkAtoms::wizard))
+ return roles::DIALOG;
+
+ return DocAccessibleWrap::NativeRole();
+}
+
+// RootAccessible protected member
+#ifdef MOZ_XUL
+uint32_t
+RootAccessible::GetChromeFlags()
+{
+ // Return the flag set for the top level window as defined
+ // by nsIWebBrowserChrome::CHROME_WINDOW_[FLAGNAME]
+ // Not simple: nsIXULWindow is not just a QI from nsIDOMWindow
+ nsCOMPtr<nsIDocShell> docShell = nsCoreUtils::GetDocShellFor(mDocumentNode);
+ NS_ENSURE_TRUE(docShell, 0);
+ nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
+ docShell->GetTreeOwner(getter_AddRefs(treeOwner));
+ NS_ENSURE_TRUE(treeOwner, 0);
+ nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(treeOwner));
+ if (!xulWin) {
+ return 0;
+ }
+ uint32_t chromeFlags;
+ xulWin->GetChromeFlags(&chromeFlags);
+ return chromeFlags;
+}
+#endif
+
+uint64_t
+RootAccessible::NativeState()
+{
+ uint64_t state = DocAccessibleWrap::NativeState();
+ if (state & states::DEFUNCT)
+ return state;
+
+#ifdef MOZ_XUL
+ uint32_t chromeFlags = GetChromeFlags();
+ if (chromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE)
+ state |= states::SIZEABLE;
+ // If it has a titlebar it's movable
+ // XXX unless it's minimized or maximized, but not sure
+ // how to detect that
+ if (chromeFlags & nsIWebBrowserChrome::CHROME_TITLEBAR)
+ state |= states::MOVEABLE;
+ if (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL)
+ state |= states::MODAL;
+#endif
+
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm && fm->GetActiveWindow() == mDocumentNode->GetWindow())
+ state |= states::ACTIVE;
+
+ return state;
+}
+
+const char* const kEventTypes[] = {
+#ifdef DEBUG_DRAGDROPSTART
+ // Capture mouse over events and fire fake DRAGDROPSTART event to simplify
+ // debugging a11y objects with event viewers.
+ "mouseover",
+#endif
+ // Fired when list or tree selection changes.
+ "select",
+ // Fired when value changes immediately, wether or not focused changed.
+ "ValueChange",
+ "AlertActive",
+ "TreeRowCountChanged",
+ "TreeInvalidated",
+ // add ourself as a OpenStateChange listener (custom event fired in tree.xml)
+ "OpenStateChange",
+ // add ourself as a CheckboxStateChange listener (custom event fired in HTMLInputElement.cpp)
+ "CheckboxStateChange",
+ // add ourself as a RadioStateChange Listener ( custom event fired in in HTMLInputElement.cpp & radio.xml)
+ "RadioStateChange",
+ "popupshown",
+ "popuphiding",
+ "DOMMenuInactive",
+ "DOMMenuItemActive",
+ "DOMMenuItemInactive",
+ "DOMMenuBarActive",
+ "DOMMenuBarInactive"
+};
+
+nsresult
+RootAccessible::AddEventListeners()
+{
+ // EventTarget interface allows to register event listeners to
+ // receive untrusted events (synthetic events generated by untrusted code).
+ // For example, XBL bindings implementations for elements that are hosted in
+ // non chrome document fire untrusted events.
+ nsCOMPtr<EventTarget> nstarget = mDocumentNode;
+
+ if (nstarget) {
+ for (const char* const* e = kEventTypes,
+ * const* e_end = ArrayEnd(kEventTypes);
+ e < e_end; ++e) {
+ nsresult rv = nstarget->AddEventListener(NS_ConvertASCIItoUTF16(*e),
+ this, true, true, 2);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ return DocAccessible::AddEventListeners();
+}
+
+nsresult
+RootAccessible::RemoveEventListeners()
+{
+ nsCOMPtr<EventTarget> target = mDocumentNode;
+ if (target) {
+ for (const char* const* e = kEventTypes,
+ * const* e_end = ArrayEnd(kEventTypes);
+ e < e_end; ++e) {
+ nsresult rv = target->RemoveEventListener(NS_ConvertASCIItoUTF16(*e), this, true);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ // Do this before removing clearing caret accessible, so that it can use
+ // shutdown the caret accessible's selection listener
+ DocAccessible::RemoveEventListeners();
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public
+
+void
+RootAccessible::DocumentActivated(DocAccessible* aDocument)
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIDOMEventListener
+
+NS_IMETHODIMP
+RootAccessible::HandleEvent(nsIDOMEvent* aDOMEvent)
+{
+ MOZ_ASSERT(aDOMEvent);
+ Event* event = aDOMEvent->InternalDOMEvent();
+ nsCOMPtr<nsINode> origTargetNode = do_QueryInterface(event->GetOriginalTarget());
+ if (!origTargetNode)
+ return NS_OK;
+
+#ifdef A11Y_LOG
+ if (logging::IsEnabled(logging::eDOMEvents)) {
+ nsAutoString eventType;
+ aDOMEvent->GetType(eventType);
+ logging::DOMEvent("handled", origTargetNode, eventType);
+ }
+#endif
+
+ DocAccessible* document =
+ GetAccService()->GetDocAccessible(origTargetNode->OwnerDoc());
+
+ if (document) {
+ // Root accessible exists longer than any of its descendant documents so
+ // that we are guaranteed notification is processed before root accessible
+ // is destroyed.
+ document->HandleNotification<RootAccessible, nsIDOMEvent>
+ (this, &RootAccessible::ProcessDOMEvent, aDOMEvent);
+ }
+
+ return NS_OK;
+}
+
+// RootAccessible protected
+void
+RootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
+{
+ MOZ_ASSERT(aDOMEvent);
+ Event* event = aDOMEvent->InternalDOMEvent();
+ nsCOMPtr<nsINode> origTargetNode = do_QueryInterface(event->GetOriginalTarget());
+
+ nsAutoString eventType;
+ aDOMEvent->GetType(eventType);
+
+#ifdef A11Y_LOG
+ if (logging::IsEnabled(logging::eDOMEvents))
+ logging::DOMEvent("processed", origTargetNode, eventType);
+#endif
+
+ if (eventType.EqualsLiteral("popuphiding")) {
+ HandlePopupHidingEvent(origTargetNode);
+ return;
+ }
+
+ DocAccessible* targetDocument = GetAccService()->
+ GetDocAccessible(origTargetNode->OwnerDoc());
+ NS_ASSERTION(targetDocument, "No document while accessible is in document?!");
+
+ Accessible* accessible =
+ targetDocument->GetAccessibleOrContainer(origTargetNode);
+ if (!accessible)
+ return;
+
+#ifdef MOZ_XUL
+ XULTreeAccessible* treeAcc = accessible->AsXULTree();
+ if (treeAcc) {
+ if (eventType.EqualsLiteral("TreeRowCountChanged")) {
+ HandleTreeRowCountChangedEvent(aDOMEvent, treeAcc);
+ return;
+ }
+
+ if (eventType.EqualsLiteral("TreeInvalidated")) {
+ HandleTreeInvalidatedEvent(aDOMEvent, treeAcc);
+ return;
+ }
+ }
+#endif
+
+ if (eventType.EqualsLiteral("RadioStateChange")) {
+ uint64_t state = accessible->State();
+ bool isEnabled = (state & (states::CHECKED | states::SELECTED)) != 0;
+
+ if (accessible->NeedsDOMUIEvent()) {
+ RefPtr<AccEvent> accEvent =
+ new AccStateChangeEvent(accessible, states::CHECKED, isEnabled);
+ nsEventShell::FireEvent(accEvent);
+ }
+
+ if (isEnabled) {
+ FocusMgr()->ActiveItemChanged(accessible);
+#ifdef A11Y_LOG
+ if (logging::IsEnabled(logging::eFocus))
+ logging::ActiveItemChangeCausedBy("RadioStateChange", accessible);
+#endif
+ }
+
+ return;
+ }
+
+ if (eventType.EqualsLiteral("CheckboxStateChange")) {
+ if (accessible->NeedsDOMUIEvent()) {
+ uint64_t state = accessible->State();
+ bool isEnabled = !!(state & states::CHECKED);
+
+ RefPtr<AccEvent> accEvent =
+ new AccStateChangeEvent(accessible, states::CHECKED, isEnabled);
+ nsEventShell::FireEvent(accEvent);
+ }
+ return;
+ }
+
+ Accessible* treeItemAcc = nullptr;
+#ifdef MOZ_XUL
+ // If it's a tree element, need the currently selected item.
+ if (treeAcc) {
+ treeItemAcc = accessible->CurrentItem();
+ if (treeItemAcc)
+ accessible = treeItemAcc;
+ }
+
+ if (treeItemAcc && eventType.EqualsLiteral("OpenStateChange")) {
+ uint64_t state = accessible->State();
+ bool isEnabled = (state & states::EXPANDED) != 0;
+
+ RefPtr<AccEvent> accEvent =
+ new AccStateChangeEvent(accessible, states::EXPANDED, isEnabled);
+ nsEventShell::FireEvent(accEvent);
+ return;
+ }
+
+ nsINode* targetNode = accessible->GetNode();
+ if (treeItemAcc && eventType.EqualsLiteral("select")) {
+ // XXX: We shouldn't be based on DOM select event which doesn't provide us
+ // any context info. We should integrate into nsTreeSelection instead.
+ // If multiselect tree, we should fire selectionadd or selection removed
+ if (FocusMgr()->HasDOMFocus(targetNode)) {
+ nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSel =
+ do_QueryInterface(targetNode);
+ nsAutoString selType;
+ multiSel->GetSelType(selType);
+ if (selType.IsEmpty() || !selType.EqualsLiteral("single")) {
+ // XXX: We need to fire EVENT_SELECTION_ADD and EVENT_SELECTION_REMOVE
+ // for each tree item. Perhaps each tree item will need to cache its
+ // selection state and fire an event after a DOM "select" event when
+ // that state changes. XULTreeAccessible::UpdateTreeSelection();
+ nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
+ accessible);
+ return;
+ }
+
+ RefPtr<AccSelChangeEvent> selChangeEvent =
+ new AccSelChangeEvent(treeAcc, treeItemAcc,
+ AccSelChangeEvent::eSelectionAdd);
+ nsEventShell::FireEvent(selChangeEvent);
+ return;
+ }
+ }
+ else
+#endif
+ if (eventType.EqualsLiteral("AlertActive")) {
+ nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_ALERT, accessible);
+ }
+ else if (eventType.EqualsLiteral("popupshown")) {
+ HandlePopupShownEvent(accessible);
+ }
+ else if (eventType.EqualsLiteral("DOMMenuInactive")) {
+ if (accessible->Role() == roles::MENUPOPUP) {
+ nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
+ accessible);
+ }
+ }
+ else if (eventType.EqualsLiteral("DOMMenuItemActive")) {
+ FocusMgr()->ActiveItemChanged(accessible);
+#ifdef A11Y_LOG
+ if (logging::IsEnabled(logging::eFocus))
+ logging::ActiveItemChangeCausedBy("DOMMenuItemActive", accessible);
+#endif
+ }
+ else if (eventType.EqualsLiteral("DOMMenuItemInactive")) {
+ // Process DOMMenuItemInactive event for autocomplete only because this is
+ // unique widget that may acquire focus from autocomplete popup while popup
+ // stays open and has no active item. In case of XUL tree autocomplete
+ // popup this event is fired for tree accessible.
+ Accessible* widget =
+ accessible->IsWidget() ? accessible : accessible->ContainerWidget();
+ if (widget && widget->IsAutoCompletePopup()) {
+ FocusMgr()->ActiveItemChanged(nullptr);
+#ifdef A11Y_LOG
+ if (logging::IsEnabled(logging::eFocus))
+ logging::ActiveItemChangeCausedBy("DOMMenuItemInactive", accessible);
+#endif
+ }
+ }
+ else if (eventType.EqualsLiteral("DOMMenuBarActive")) { // Always from user input
+ nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_START,
+ accessible, eFromUserInput);
+
+ // Notify of active item change when menubar gets active and if it has
+ // current item. This is a case of mouseover (set current menuitem) and
+ // mouse click (activate the menubar). If menubar doesn't have current item
+ // (can be a case of menubar activation from keyboard) then ignore this
+ // notification because later we'll receive DOMMenuItemActive event after
+ // current menuitem is set.
+ Accessible* activeItem = accessible->CurrentItem();
+ if (activeItem) {
+ FocusMgr()->ActiveItemChanged(activeItem);
+#ifdef A11Y_LOG
+ if (logging::IsEnabled(logging::eFocus))
+ logging::ActiveItemChangeCausedBy("DOMMenuBarActive", accessible);
+#endif
+ }
+ }
+ else if (eventType.EqualsLiteral("DOMMenuBarInactive")) { // Always from user input
+ nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_END,
+ accessible, eFromUserInput);
+
+ FocusMgr()->ActiveItemChanged(nullptr);
+#ifdef A11Y_LOG
+ if (logging::IsEnabled(logging::eFocus))
+ logging::ActiveItemChangeCausedBy("DOMMenuBarInactive", accessible);
+#endif
+ }
+ else if (accessible->NeedsDOMUIEvent() &&
+ eventType.EqualsLiteral("ValueChange")) {
+ uint32_t event = accessible->HasNumericValue()
+ ? nsIAccessibleEvent::EVENT_VALUE_CHANGE
+ : nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE;
+ targetDocument->FireDelayedEvent(event, accessible);
+ }
+#ifdef DEBUG_DRAGDROPSTART
+ else if (eventType.EqualsLiteral("mouseover")) {
+ nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_DRAGDROP_START,
+ accessible);
+ }
+#endif
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Accessible
+
+void
+RootAccessible::Shutdown()
+{
+ // Called manually or by Accessible::LastRelease()
+ if (!PresShell())
+ return; // Already shutdown
+
+ DocAccessibleWrap::Shutdown();
+}
+
+Relation
+RootAccessible::RelationByType(RelationType aType)
+{
+ if (!mDocumentNode || aType != RelationType::EMBEDS)
+ return DocAccessibleWrap::RelationByType(aType);
+
+ if (nsPIDOMWindowOuter* rootWindow = mDocumentNode->GetWindow()) {
+ nsCOMPtr<nsPIDOMWindowOuter> contentWindow = nsGlobalWindow::Cast(rootWindow)->GetContent();
+ if (contentWindow) {
+ nsCOMPtr<nsIDocument> contentDocumentNode = contentWindow->GetDoc();
+ if (contentDocumentNode) {
+ DocAccessible* contentDocument =
+ GetAccService()->GetDocAccessible(contentDocumentNode);
+ if (contentDocument)
+ return Relation(contentDocument);
+ }
+ }
+ }
+
+ return Relation();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Protected members
+
+void
+RootAccessible::HandlePopupShownEvent(Accessible* aAccessible)
+{
+ roles::Role role = aAccessible->Role();
+
+ if (role == roles::MENUPOPUP) {
+ // Don't fire menupopup events for combobox and autocomplete lists.
+ nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START,
+ aAccessible);
+ return;
+ }
+
+ if (role == roles::TOOLTIP) {
+ // There is a single <xul:tooltip> node which Mozilla moves around.
+ // The accessible for it stays the same no matter where it moves.
+ // AT's expect to get an EVENT_SHOW for the tooltip.
+ // In event callback the tooltip's accessible will be ready.
+ nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SHOW, aAccessible);
+ return;
+ }
+
+ if (role == roles::COMBOBOX_LIST) {
+ // Fire expanded state change event for comboboxes and autocompeletes.
+ Accessible* combobox = aAccessible->Parent();
+ if (!combobox)
+ return;
+
+ roles::Role comboboxRole = combobox->Role();
+ if (comboboxRole == roles::COMBOBOX ||
+ comboboxRole == roles::AUTOCOMPLETE) {
+ RefPtr<AccEvent> event =
+ new AccStateChangeEvent(combobox, states::EXPANDED, true);
+ if (event)
+ nsEventShell::FireEvent(event);
+ }
+ }
+}
+
+void
+RootAccessible::HandlePopupHidingEvent(nsINode* aPopupNode)
+{
+ // Get popup accessible. There are cases when popup element isn't accessible
+ // but an underlying widget is and behaves like popup, an example is
+ // autocomplete popups.
+ DocAccessible* document = nsAccUtils::GetDocAccessibleFor(aPopupNode);
+ if (!document)
+ return;
+
+ Accessible* popup = document->GetAccessible(aPopupNode);
+ if (!popup) {
+ Accessible* popupContainer = document->GetContainerAccessible(aPopupNode);
+ if (!popupContainer)
+ return;
+
+ uint32_t childCount = popupContainer->ChildCount();
+ for (uint32_t idx = 0; idx < childCount; idx++) {
+ Accessible* child = popupContainer->GetChildAt(idx);
+ if (child->IsAutoCompletePopup()) {
+ popup = child;
+ break;
+ }
+ }
+
+ // No popup no events. Focus is managed by DOM. This is a case for
+ // menupopups of menus on Linux since there are no accessible for popups.
+ if (!popup)
+ return;
+ }
+
+ // In case of autocompletes and comboboxes fire state change event for
+ // expanded state. Note, HTML form autocomplete isn't a subject of state
+ // change event because they aren't autocompletes strictly speaking.
+ // When popup closes (except nested popups and menus) then fire focus event to
+ // where it was. The focus event is expected even if popup didn't take a focus.
+
+ static const uint32_t kNotifyOfFocus = 1;
+ static const uint32_t kNotifyOfState = 2;
+ uint32_t notifyOf = 0;
+
+ // HTML select is target of popuphidding event. Otherwise get container
+ // widget. No container widget means this is either tooltip or menupopup.
+ // No events in the former case.
+ Accessible* widget = nullptr;
+ if (popup->IsCombobox()) {
+ widget = popup;
+ } else {
+ widget = popup->ContainerWidget();
+ if (!widget) {
+ if (!popup->IsMenuPopup())
+ return;
+
+ widget = popup;
+ }
+ }
+
+ if (popup->IsAutoCompletePopup()) {
+ // No focus event for autocomplete because it's managed by
+ // DOMMenuItemInactive events.
+ if (widget->IsAutoComplete())
+ notifyOf = kNotifyOfState;
+
+ } else if (widget->IsCombobox()) {
+ // Fire focus for active combobox, otherwise the focus is managed by DOM
+ // focus notifications. Always fire state change event.
+ if (widget->IsActiveWidget())
+ notifyOf = kNotifyOfFocus;
+ notifyOf |= kNotifyOfState;
+
+ } else if (widget->IsMenuButton()) {
+ // Can be a part of autocomplete.
+ Accessible* compositeWidget = widget->ContainerWidget();
+ if (compositeWidget && compositeWidget->IsAutoComplete()) {
+ widget = compositeWidget;
+ notifyOf = kNotifyOfState;
+ }
+
+ // Autocomplete (like searchbar) can be inactive when popup hiddens
+ notifyOf |= kNotifyOfFocus;
+
+ } else if (widget == popup) {
+ // Top level context menus and alerts.
+ // Ignore submenus and menubar. When submenu is closed then sumbenu
+ // container menuitem takes a focus via DOMMenuItemActive notification.
+ // For menubars processing we listen DOMMenubarActive/Inactive
+ // notifications.
+ notifyOf = kNotifyOfFocus;
+ }
+
+ // Restore focus to where it was.
+ if (notifyOf & kNotifyOfFocus) {
+ FocusMgr()->ActiveItemChanged(nullptr);
+#ifdef A11Y_LOG
+ if (logging::IsEnabled(logging::eFocus))
+ logging::ActiveItemChangeCausedBy("popuphiding", popup);
+#endif
+ }
+
+ // Fire expanded state change event.
+ if (notifyOf & kNotifyOfState) {
+ RefPtr<AccEvent> event =
+ new AccStateChangeEvent(widget, states::EXPANDED, false);
+ document->FireDelayedEvent(event);
+ }
+}
+
+#ifdef MOZ_XUL
+void
+RootAccessible::HandleTreeRowCountChangedEvent(nsIDOMEvent* aEvent,
+ XULTreeAccessible* aAccessible)
+{
+ nsCOMPtr<nsIDOMCustomEvent> customEvent(do_QueryInterface(aEvent));
+ if (!customEvent)
+ return;
+
+ nsCOMPtr<nsIVariant> detailVariant;
+ customEvent->GetDetail(getter_AddRefs(detailVariant));
+ if (!detailVariant)
+ return;
+
+ nsCOMPtr<nsISupports> supports;
+ detailVariant->GetAsISupports(getter_AddRefs(supports));
+ nsCOMPtr<nsIPropertyBag2> propBag(do_QueryInterface(supports));
+ if (!propBag)
+ return;
+
+ nsresult rv;
+ int32_t index, count;
+ rv = propBag->GetPropertyAsInt32(NS_LITERAL_STRING("index"), &index);
+ if (NS_FAILED(rv))
+ return;
+
+ rv = propBag->GetPropertyAsInt32(NS_LITERAL_STRING("count"), &count);
+ if (NS_FAILED(rv))
+ return;
+
+ aAccessible->InvalidateCache(index, count);
+}
+
+void
+RootAccessible::HandleTreeInvalidatedEvent(nsIDOMEvent* aEvent,
+ XULTreeAccessible* aAccessible)
+{
+ nsCOMPtr<nsIDOMCustomEvent> customEvent(do_QueryInterface(aEvent));
+ if (!customEvent)
+ return;
+
+ nsCOMPtr<nsIVariant> detailVariant;
+ customEvent->GetDetail(getter_AddRefs(detailVariant));
+ if (!detailVariant)
+ return;
+
+ nsCOMPtr<nsISupports> supports;
+ detailVariant->GetAsISupports(getter_AddRefs(supports));
+ nsCOMPtr<nsIPropertyBag2> propBag(do_QueryInterface(supports));
+ if (!propBag)
+ return;
+
+ int32_t startRow = 0, endRow = -1, startCol = 0, endCol = -1;
+ propBag->GetPropertyAsInt32(NS_LITERAL_STRING("startrow"),
+ &startRow);
+ propBag->GetPropertyAsInt32(NS_LITERAL_STRING("endrow"),
+ &endRow);
+ propBag->GetPropertyAsInt32(NS_LITERAL_STRING("startcolumn"),
+ &startCol);
+ propBag->GetPropertyAsInt32(NS_LITERAL_STRING("endcolumn"),
+ &endCol);
+
+ aAccessible->TreeViewInvalidated(startRow, endRow, startCol, endCol);
+}
+#endif
+
+ProxyAccessible*
+RootAccessible::GetPrimaryRemoteTopLevelContentDoc() const
+{
+ nsCOMPtr<nsIDocShellTreeOwner> owner;
+ mDocumentNode->GetDocShell()->GetTreeOwner(getter_AddRefs(owner));
+ NS_ENSURE_TRUE(owner, nullptr);
+
+ nsCOMPtr<nsITabParent> tabParent;
+ owner->GetPrimaryTabParent(getter_AddRefs(tabParent));
+ if (!tabParent) {
+ return nullptr;
+ }
+
+ auto tab = static_cast<dom::TabParent*>(tabParent.get());
+ return tab->GetTopLevelDocAccessible();
+}