Bug 1699053: Ensure popup custom DOM events cause content insertion/removal on XUL panels. r=Jamie

Differential Revision: https://phabricator.services.mozilla.com/D109558
This commit is contained in:
Morgan Reschenberg 2021-04-07 13:48:27 +00:00
parent 87d0f2905e
commit c5b80c6ab7
5 changed files with 111 additions and 8 deletions

View file

@ -1063,6 +1063,24 @@ LocalAccessible* nsAccessibilityService::CreateAccessible(
}
}
if (content->IsXULElement(nsGkAtoms::panel)) {
// We filter here instead of in the XUL map because
// if we filter there and return null, we still end up
// creating a generic accessible at the end of this function.
// Doing the filtering here ensures we never create accessibles
// for panels whose popups aren't visible.
nsMenuPopupFrame* popupFrame = do_QueryFrame(frame);
if (!popupFrame) {
return nullptr;
}
nsPopupState popupState = popupFrame->PopupState();
if (popupState == ePopupHiding || popupState == ePopupInvisible ||
popupState == ePopupClosed) {
return nullptr;
}
}
#ifdef MOZ_XUL
// Prefer to use XUL to decide if and what kind of accessible to create.
const XULMarkupMapInfo* xulMap =

View file

@ -275,7 +275,8 @@ void RootAccessible::ProcessDOMEvent(Event* aDOMEvent, nsINode* aTarget) {
}
if (eventType.EqualsLiteral("popupshown") &&
aTarget->IsXULElement(nsGkAtoms::tooltip)) {
(aTarget->IsXULElement(nsGkAtoms::tooltip) ||
aTarget->IsXULElement(nsGkAtoms::panel))) {
targetDocument->ContentInserted(aTarget->AsContent(),
aTarget->GetNextSibling());
return;
@ -545,7 +546,8 @@ void RootAccessible::HandlePopupHidingEvent(nsINode* aPopupNode) {
DocAccessible* document = nsAccUtils::GetDocAccessibleFor(aPopupNode);
if (!document) return;
if (aPopupNode->IsXULElement(nsGkAtoms::tooltip)) {
if (aPopupNode->IsXULElement(nsGkAtoms::tooltip) ||
aPopupNode->IsXULElement(nsGkAtoms::panel)) {
document->ContentRemoved(aPopupNode->AsContent());
return;
}

View file

@ -16,3 +16,4 @@ skip-if =
os == 'win' && os_version == '10.0' && webrender # Bug 1492259
[browser_test_A11yUtils_announce.js]
[browser_test_selection_urlbar.js]
[browser_test_panel.js]

View file

@ -0,0 +1,54 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* import-globals-from ../../mochitest/role.js */
loadScripts({ name: "role.js", dir: MOCHITESTS_DIR });
// Verify we recieve hide and show notifications when the chrome
// XUL alert is closed or opened. Mac expects both notifications to
// properly communicate live region changes.
async function runTests(browser) {
ok(PopupNotifications, "PopupNotifications object exists");
ok(PopupNotifications.panel, "PopupNotifications panel exists");
// When available, the popup panel makes itself a child of the chrome window.
// To verify it isn't accessible without reproducing the entirety of the chrome
// window tree, we check instead that the panel is not accessible.
ok(!isAccessible(PopupNotifications.panel), "Popup panel is not accessible");
const panelShown = waitForEvent(EVENT_SHOW, PopupNotifications.panel);
const notification = PopupNotifications.show(
browser,
"test-notification",
"hello world",
PopupNotifications.panel.id
);
await panelShown;
ok(isAccessible(PopupNotifications.panel), "Popup panel is accessible");
testAccessibleTree(PopupNotifications.panel, {
ALERT: [
{ LABEL: [{ TEXT_LEAF: [] }] },
{ PUSHBUTTON: [] },
{ PUSHBUTTON: [] },
],
});
// Verify the popup panel is associated with the chrome window.
is(
PopupNotifications.panel.ownerGlobal,
getMainChromeWindow(window),
"Popup panel is associated with the chrome window"
);
const panelHidden = waitForEvent(EVENT_HIDE, PopupNotifications.panel);
PopupNotifications.remove(notification);
await panelHidden;
ok(!isAccessible(PopupNotifications.panel), "Popup panel is not accessible");
}
addAccessibleTask(``, runTests);

View file

@ -20,15 +20,14 @@ add_task(async function test_searchbar_a11y_tree() {
// Make sure the popup has been rendered so it shows up in the a11y tree.
let popup = document.getElementById("PopupSearchAutoComplete");
let promise = BrowserTestUtils.waitForEvent(popup, "popupshown", false);
let promise = Promise.all([
BrowserTestUtils.waitForEvent(popup, "popupshown", false),
waitForEvent(EVENT_SHOW, popup),
]);
searchbar.textbox.openPopup();
await promise;
promise = BrowserTestUtils.waitForEvent(popup, "popuphidden", false);
searchbar.textbox.closePopup();
await promise;
const TREE = {
let TREE = {
role: ROLE_EDITCOMBOBOX,
children: [
@ -53,4 +52,33 @@ add_task(async function test_searchbar_a11y_tree() {
};
testAccessibleTree(searchbar, TREE);
promise = Promise.all([
BrowserTestUtils.waitForEvent(popup, "popuphidden", false),
waitForEvent(EVENT_HIDE, popup),
]);
searchbar.textbox.closePopup();
await promise;
TREE = {
role: ROLE_EDITCOMBOBOX,
children: [
// input element
{
role: ROLE_ENTRY,
children: [],
},
// context menu
{
role: ROLE_COMBOBOX_LIST,
children: [],
},
// the result list should be removed from the tree on popuphidden
],
};
testAccessibleTree(searchbar, TREE);
});