From 2aa4a82499d4becd2284cdb482213d541b8804dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 16:29:10 +0200 Subject: Adding upstream version 86.0.1. Signed-off-by: Daniel Baumann --- accessible/generic/RootAccessible.cpp | 711 ++++++++++++++++++++++++++++++++++ 1 file changed, 711 insertions(+) create mode 100644 accessible/generic/RootAccessible.cpp (limited to 'accessible/generic/RootAccessible.cpp') diff --git a/accessible/generic/RootAccessible.cpp b/accessible/generic/RootAccessible.cpp new file mode 100644 index 0000000000..adcd02f480 --- /dev/null +++ b/accessible/generic/RootAccessible.cpp @@ -0,0 +1,711 @@ +/* -*- 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" +#include "mozilla/PresShell.h" // for nsAccUtils::GetDocAccessibleFor() + +#define CreateEvent CreateEventA + +#include "Accessible-inl.h" +#include "DocAccessible-inl.h" +#include "mozilla/a11y/DocAccessibleParent.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/BindingUtils.h" +#include "mozilla/dom/CustomEvent.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/ScriptSettings.h" +#include "mozilla/dom/BrowserHost.h" + +#include "nsIDocShellTreeOwner.h" +#include "mozilla/dom/Event.h" +#include "mozilla/dom/EventTarget.h" +#include "nsIDOMXULMultSelectCntrlEl.h" +#include "mozilla/dom/Document.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIPropertyBag2.h" +#include "nsPIDOMWindow.h" +#include "nsIWebBrowserChrome.h" +#include "nsReadableUtils.h" +#include "nsFocusManager.h" +#include "nsGlobalWindow.h" + +#ifdef MOZ_XUL +# include "nsIAppWindow.h" +#endif + +using namespace mozilla; +using namespace mozilla::a11y; +using namespace mozilla::dom; + +//////////////////////////////////////////////////////////////////////////////// +// nsISupports + +NS_IMPL_ISUPPORTS_INHERITED(RootAccessible, DocAccessible, nsIDOMEventListener) + +//////////////////////////////////////////////////////////////////////////////// +// Constructor/destructor + +RootAccessible::RootAccessible(Document* aDocument, PresShell* aPresShell) + : DocAccessibleWrap(aDocument, aPresShell) { + mType = eRootType; +} + +RootAccessible::~RootAccessible() {} + +//////////////////////////////////////////////////////////////////////////////// +// Accessible + +ENameValueFlag RootAccessible::Name(nsString& aName) const { + aName.Truncate(); + + if (ARIARoleMap()) { + Accessible::Name(aName); + if (!aName.IsEmpty()) return eNameOK; + } + + mDocumentNode->GetTitle(aName); + return eNameOK; +} + +// RootAccessible protected member +#ifdef MOZ_XUL +uint32_t RootAccessible::GetChromeFlags() const { + // Return the flag set for the top level window as defined + // by nsIWebBrowserChrome::CHROME_WINDOW_[FLAGNAME] + // Not simple: nsIAppWindow is not just a QI from nsIDOMWindow + nsCOMPtr docShell = nsCoreUtils::GetDocShellFor(mDocumentNode); + NS_ENSURE_TRUE(docShell, 0); + nsCOMPtr treeOwner; + docShell->GetTreeOwner(getter_AddRefs(treeOwner)); + NS_ENSURE_TRUE(treeOwner, 0); + nsCOMPtr appWin(do_GetInterface(treeOwner)); + if (!appWin) { + return 0; + } + uint32_t chromeFlags; + appWin->GetChromeFlags(&chromeFlags); + return chromeFlags; +} +#endif + +uint64_t RootAccessible::NativeState() const { + 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.js) + "RadioStateChange", "popupshown", "popuphiding", "DOMMenuInactive", + "DOMMenuItemActive", "DOMMenuItemInactive", "DOMMenuBarActive", + "DOMMenuBarInactive", "scroll"}; + +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. + // We must use the window's parent target in order to receive events from + // iframes and shadow DOM; e.g. ValueChange events from a dropdowns, which use a + // menulist containing div elements instead of XUL menuitems. XUL menuitems + // fire DOMMenuItemActive events from layout instead. + MOZ_ASSERT(aAccessible->Elm()); + if (aAccessible->Elm()->HasAttr(kNameSpaceID_None, + nsGkAtoms::aria_activedescendant)) { + Accessible* activeDescendant = aAccessible->CurrentItem(); + if (activeDescendant) { + FocusMgr()->ActiveItemChanged(activeDescendant, false); +#ifdef A11Y_LOG + if (logging::IsEnabled(logging::eFocus)) { + logging::ActiveItemChangeCausedBy("ARIA activedescendant on popup", + activeDescendant); + } +#endif + } + } + } +} + +void RootAccessible::HandlePopupHidingEvent(nsINode* aPopupNode) { + DocAccessible* document = nsAccUtils::GetDocAccessibleFor(aPopupNode); + if (!document) return; + + if (aPopupNode->IsXULElement(nsGkAtoms::tooltip)) { + document->ContentRemoved(aPopupNode->AsContent()); + return; + } + + // 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. + 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 event = + new AccStateChangeEvent(widget, states::EXPANDED, false); + document->FireDelayedEvent(event); + } +} + +#ifdef MOZ_XUL +static void GetPropertyBagFromEvent(Event* aEvent, + nsIPropertyBag2** aPropertyBag) { + *aPropertyBag = nullptr; + + CustomEvent* customEvent = aEvent->AsCustomEvent(); + if (!customEvent) return; + + AutoJSAPI jsapi; + if (!jsapi.Init(customEvent->GetParentObject())) return; + + JSContext* cx = jsapi.cx(); + JS::Rooted detail(cx); + customEvent->GetDetail(cx, &detail); + if (!detail.isObject()) return; + + JS::Rooted detailObj(cx, &detail.toObject()); + + nsresult rv; + nsCOMPtr propBag; + rv = UnwrapArg(cx, detailObj, getter_AddRefs(propBag)); + if (NS_FAILED(rv)) return; + + propBag.forget(aPropertyBag); +} + +void RootAccessible::HandleTreeRowCountChangedEvent( + Event* aEvent, XULTreeAccessible* aAccessible) { + nsCOMPtr propBag; + GetPropertyBagFromEvent(aEvent, getter_AddRefs(propBag)); + if (!propBag) return; + + nsresult rv; + int32_t index, count; + rv = propBag->GetPropertyAsInt32(u"index"_ns, &index); + if (NS_FAILED(rv)) return; + + rv = propBag->GetPropertyAsInt32(u"count"_ns, &count); + if (NS_FAILED(rv)) return; + + aAccessible->InvalidateCache(index, count); +} + +void RootAccessible::HandleTreeInvalidatedEvent( + Event* aEvent, XULTreeAccessible* aAccessible) { + nsCOMPtr propBag; + GetPropertyBagFromEvent(aEvent, getter_AddRefs(propBag)); + if (!propBag) return; + + int32_t startRow = 0, endRow = -1, startCol = 0, endCol = -1; + propBag->GetPropertyAsInt32(u"startrow"_ns, &startRow); + propBag->GetPropertyAsInt32(u"endrow"_ns, &endRow); + propBag->GetPropertyAsInt32(u"startcolumn"_ns, &startCol); + propBag->GetPropertyAsInt32(u"endcolumn"_ns, &endCol); + + aAccessible->TreeViewInvalidated(startRow, endRow, startCol, endCol); +} +#endif + +ProxyAccessible* RootAccessible::GetPrimaryRemoteTopLevelContentDoc() const { + nsCOMPtr owner; + mDocumentNode->GetDocShell()->GetTreeOwner(getter_AddRefs(owner)); + NS_ENSURE_TRUE(owner, nullptr); + + nsCOMPtr remoteTab; + owner->GetPrimaryRemoteTab(getter_AddRefs(remoteTab)); + if (!remoteTab) { + return nullptr; + } + + auto tab = static_cast(remoteTab.get()); + return tab->GetTopLevelDocAccessible(); +} -- cgit v1.2.3