389 lines
12 KiB
JavaScript
389 lines
12 KiB
JavaScript
/* 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/. */
|
|
|
|
"use strict";
|
|
|
|
const { UrlbarTestUtils } = ChromeUtils.importESModule(
|
|
"resource://testing-common/UrlbarTestUtils.sys.mjs"
|
|
);
|
|
/* import-globals-from ../../mochitest/role.js */
|
|
/* import-globals-from ../../mochitest/states.js */
|
|
loadScripts(
|
|
{ name: "role.js", dir: MOCHITESTS_DIR },
|
|
{ name: "states.js", dir: MOCHITESTS_DIR }
|
|
);
|
|
|
|
function getMacAccessible(accOrElmOrID) {
|
|
return new Promise(resolve => {
|
|
let intervalId = setInterval(() => {
|
|
let acc = getAccessible(accOrElmOrID);
|
|
if (acc) {
|
|
clearInterval(intervalId);
|
|
resolve(
|
|
acc.nativeInterface.QueryInterface(Ci.nsIAccessibleMacInterface)
|
|
);
|
|
}
|
|
}, 10);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Test a11yUtils announcements are exposed to VO
|
|
*/
|
|
add_task(async () => {
|
|
const tab = await BrowserTestUtils.openNewForegroundTab(
|
|
gBrowser,
|
|
"data:text/html,"
|
|
);
|
|
const alert = document.getElementById("a11y-announcement");
|
|
ok(alert, "Found alert to send announcements");
|
|
|
|
const alerted = waitForMacEvent("AXAnnouncementRequested", (iface, data) => {
|
|
return data.AXAnnouncementKey == "hello world";
|
|
});
|
|
|
|
A11yUtils.announce({
|
|
raw: "hello world",
|
|
});
|
|
await alerted;
|
|
await BrowserTestUtils.removeTab(tab);
|
|
});
|
|
|
|
/**
|
|
* Test browser tabs
|
|
*/
|
|
add_task(async () => {
|
|
let newTabs = await Promise.all([
|
|
BrowserTestUtils.openNewForegroundTab(
|
|
gBrowser,
|
|
"data:text/html,<title>Two</title>"
|
|
),
|
|
BrowserTestUtils.openNewForegroundTab(
|
|
gBrowser,
|
|
"data:text/html,<title>Three</title>"
|
|
),
|
|
BrowserTestUtils.openNewForegroundTab(
|
|
gBrowser,
|
|
"data:text/html,<title>Four</title>"
|
|
),
|
|
]);
|
|
|
|
// Mochitests spawn with a tab, and we've opened 3 more for a total of 4 tabs
|
|
is(gBrowser.tabs.length, 4, "We now have 4 open tabs");
|
|
|
|
let tablist = await getMacAccessible("tabbrowser-tabs");
|
|
is(
|
|
tablist.getAttributeValue("AXRole"),
|
|
"AXTabGroup",
|
|
"Correct role for tablist"
|
|
);
|
|
|
|
let tabMacAccs = tablist.getAttributeValue("AXTabs");
|
|
is(tabMacAccs.length, 4, "4 items in AXTabs");
|
|
|
|
let selectedTabs = tablist.getAttributeValue("AXSelectedChildren");
|
|
is(selectedTabs.length, 1, "one selected tab");
|
|
|
|
let tab = selectedTabs[0];
|
|
is(tab.getAttributeValue("AXRole"), "AXRadioButton", "Correct role for tab");
|
|
is(
|
|
tab.getAttributeValue("AXSubrole"),
|
|
"AXTabButton",
|
|
"Correct subrole for tab"
|
|
);
|
|
is(tab.getAttributeValue("AXTitle"), "Four", "Correct title for tab");
|
|
|
|
let tabToSelect = tabMacAccs[2];
|
|
is(
|
|
tabToSelect.getAttributeValue("AXTitle"),
|
|
"Three",
|
|
"Correct title for tab"
|
|
);
|
|
|
|
let actions = tabToSelect.actionNames;
|
|
ok(true, actions);
|
|
ok(actions.includes("AXPress"), "Has switch action");
|
|
|
|
// When tab is clicked selection of tab group changes,
|
|
// and focus goes to the web area. Wait for both.
|
|
let evt = Promise.all([
|
|
waitForMacEvent("AXSelectedChildrenChanged"),
|
|
waitForMacEvent(
|
|
"AXFocusedUIElementChanged",
|
|
iface => iface.getAttributeValue("AXRole") == "AXWebArea"
|
|
),
|
|
]);
|
|
tabToSelect.performAction("AXPress");
|
|
await evt;
|
|
|
|
selectedTabs = tablist.getAttributeValue("AXSelectedChildren");
|
|
is(selectedTabs.length, 1, "one selected tab");
|
|
is(
|
|
selectedTabs[0].getAttributeValue("AXTitle"),
|
|
"Three",
|
|
"Correct title for tab"
|
|
);
|
|
|
|
// Close all open tabs
|
|
await Promise.all(newTabs.map(t => BrowserTestUtils.removeTab(t)));
|
|
});
|
|
|
|
/**
|
|
* Test ignored invisible items in root
|
|
*/
|
|
add_task(async () => {
|
|
await BrowserTestUtils.withNewTab(
|
|
{
|
|
gBrowser,
|
|
url: "about:license",
|
|
},
|
|
async () => {
|
|
let root = await getMacAccessible(document);
|
|
let rootChildCount = () => root.getAttributeValue("AXChildren").length;
|
|
|
|
// With no popups, the root accessible has 5 visible children:
|
|
// 1. Tab bar (#TabsToolbar)
|
|
// 2. Navigation bar (#nav-bar)
|
|
// 3. Notifications toolbar (#notifications-toolbar)
|
|
// 4. Content area (#tabbrowser-tabpanels)
|
|
// 5. Accessibility announcements dialog (#a11y-announcement)
|
|
let baseRootChildCount = 5;
|
|
is(
|
|
rootChildCount(),
|
|
baseRootChildCount,
|
|
`Root with no popups has ${baseRootChildCount} children`
|
|
);
|
|
|
|
// Open a context menu
|
|
const menu = document.getElementById("contentAreaContextMenu");
|
|
if (
|
|
Services.prefs.getBoolPref("widget.macos.native-context-menus", false)
|
|
) {
|
|
// Native context menu - do not expect accessibility notifications.
|
|
let popupshown = BrowserTestUtils.waitForPopupEvent(menu, "shown");
|
|
EventUtils.synthesizeMouseAtCenter(document.body, {
|
|
type: "contextmenu",
|
|
});
|
|
await popupshown;
|
|
|
|
is(
|
|
rootChildCount(),
|
|
baseRootChildCount,
|
|
"Native context menus do not show up in the root children"
|
|
);
|
|
|
|
// Close context menu
|
|
let popuphidden = BrowserTestUtils.waitForPopupEvent(menu, "hidden");
|
|
menu.hidePopup();
|
|
await popuphidden;
|
|
} else {
|
|
// Non-native menu
|
|
EventUtils.synthesizeMouseAtCenter(document.body, {
|
|
type: "contextmenu",
|
|
});
|
|
await waitForMacEvent("AXMenuOpened");
|
|
|
|
// Now root has 1 more child
|
|
is(rootChildCount(), baseRootChildCount + 1, "Root has 1 more child");
|
|
|
|
// Close context menu
|
|
let closed = waitForMacEvent("AXMenuClosed", "contentAreaContextMenu");
|
|
EventUtils.synthesizeKey("KEY_Escape");
|
|
await BrowserTestUtils.waitForPopupEvent(menu, "hidden");
|
|
await closed;
|
|
}
|
|
|
|
// We're back to base child count
|
|
is(rootChildCount(), baseRootChildCount, "Root has original child count");
|
|
|
|
// Open site identity popup
|
|
document.getElementById("identity-icon-box").click();
|
|
const identityPopup = document.getElementById("identity-popup");
|
|
await BrowserTestUtils.waitForPopupEvent(identityPopup, "shown");
|
|
|
|
// Now root has another child
|
|
is(rootChildCount(), baseRootChildCount + 1, "Root has another child");
|
|
|
|
// Close popup
|
|
let hide = waitForMacEvent("AXUIElementDestroyed");
|
|
EventUtils.synthesizeKey("KEY_Escape");
|
|
await BrowserTestUtils.waitForPopupEvent(identityPopup, "hidden");
|
|
await hide;
|
|
|
|
// We're back to the base child count
|
|
is(rootChildCount(), baseRootChildCount, "Root has the base child count");
|
|
}
|
|
);
|
|
});
|
|
|
|
/**
|
|
* Tests for location bar
|
|
*/
|
|
add_task(async () => {
|
|
await BrowserTestUtils.withNewTab(
|
|
{
|
|
gBrowser,
|
|
// eslint-disable-next-line @microsoft/sdl/no-insecure-url
|
|
url: "http://example.com",
|
|
},
|
|
async () => {
|
|
let input = await getMacAccessible(gURLBar.inputField);
|
|
is(
|
|
input.getAttributeValue("AXValue"),
|
|
// eslint-disable-next-line @microsoft/sdl/no-insecure-url
|
|
UrlbarTestUtils.trimURL("http://example.com"),
|
|
"Location bar has correct value"
|
|
);
|
|
}
|
|
);
|
|
});
|
|
|
|
/**
|
|
* Tests attributed text in nav bar has no invisible AXAttachments
|
|
*/
|
|
add_task(async () => {
|
|
await BrowserTestUtils.withNewTab(
|
|
{
|
|
gBrowser,
|
|
// eslint-disable-next-line @microsoft/sdl/no-insecure-url
|
|
url: "http://example.com",
|
|
},
|
|
async () => {
|
|
let root = await getMacAccessible(document);
|
|
let navBar = await getMacAccessible("nav-bar");
|
|
let elemRange = root.getParameterizedAttributeValue(
|
|
"AXTextMarkerRangeForUIElement",
|
|
navBar
|
|
);
|
|
let attributedString = root.getParameterizedAttributeValue(
|
|
"AXAttributedStringForTextMarkerRange",
|
|
elemRange
|
|
);
|
|
let attachmentRoles = attributedString.map(s =>
|
|
s.AXAttachment ? s.AXAttachment.getAttributeValue("AXRole") : null
|
|
);
|
|
ok(
|
|
!attachmentRoles.includes("AXMenu"),
|
|
"Collapsed menu should be embedded in attributed text"
|
|
);
|
|
}
|
|
);
|
|
});
|
|
|
|
/**
|
|
* Test context menu
|
|
*/
|
|
add_task(async () => {
|
|
if (Services.prefs.getBoolPref("widget.macos.native-context-menus", false)) {
|
|
ok(true, "We cannot inspect native context menu contents; skip this test.");
|
|
return;
|
|
}
|
|
|
|
await BrowserTestUtils.withNewTab(
|
|
{
|
|
gBrowser,
|
|
url: 'data:text/html,<a id="exampleLink" href="https://example.com">link</a>',
|
|
},
|
|
async browser => {
|
|
if (!Services.search.isInitialized) {
|
|
let aStatus = await Services.search.init();
|
|
Assert.ok(Components.isSuccessCode(aStatus));
|
|
Assert.ok(Services.search.isInitialized);
|
|
}
|
|
|
|
const hasContainers =
|
|
Services.prefs.getBoolPref("privacy.userContext.enabled") &&
|
|
!!ContextualIdentityService.getPublicIdentities().length;
|
|
info(`${hasContainers ? "Do" : "Don't"} expect containers item.`);
|
|
const hasInspectA11y =
|
|
Services.prefs.getBoolPref("devtools.everOpened", false) ||
|
|
Services.prefs.getIntPref("devtools.selfxss.count", 0) > 0;
|
|
info(`${hasInspectA11y ? "Do" : "Don't"} expect inspect a11y item.`);
|
|
|
|
// synthesize a right click on the link to open the link context menu
|
|
let menu = document.getElementById("contentAreaContextMenu");
|
|
await BrowserTestUtils.synthesizeMouseAtCenter(
|
|
"#exampleLink",
|
|
{ type: "contextmenu" },
|
|
browser
|
|
);
|
|
await waitForMacEvent("AXMenuOpened");
|
|
|
|
menu = await getMacAccessible(menu);
|
|
let menuChildren = menu.getAttributeValue("AXChildren");
|
|
const expectedChildCount = 12 + +hasContainers + +hasInspectA11y;
|
|
is(
|
|
menuChildren.length,
|
|
expectedChildCount,
|
|
`Context menu on link contains ${expectedChildCount} items.`
|
|
);
|
|
// items at indicies 3, 9, and 11 are the splitters when containers exist
|
|
// everything else should be a menu item, otherwise indicies of splitters are
|
|
// 3, 8, and 10
|
|
const splitterIndicies = hasContainers ? [4, 9, 11] : [3, 8, 10];
|
|
for (let i = 0; i < menuChildren.length; i++) {
|
|
if (splitterIndicies.includes(i)) {
|
|
is(
|
|
menuChildren[i].getAttributeValue("AXRole"),
|
|
"AXSplitter",
|
|
"found splitter in menu"
|
|
);
|
|
} else {
|
|
is(
|
|
menuChildren[i].getAttributeValue("AXRole"),
|
|
"AXMenuItem",
|
|
"found menu item in menu"
|
|
);
|
|
}
|
|
}
|
|
|
|
// check the containers sub menu in depth if it exists
|
|
if (hasContainers) {
|
|
is(
|
|
menuChildren[1].getAttributeValue("AXVisibleChildren"),
|
|
null,
|
|
"Submenu 1 has no visible chldren when hidden"
|
|
);
|
|
|
|
// focus the first submenu
|
|
EventUtils.synthesizeKey("KEY_ArrowDown");
|
|
EventUtils.synthesizeKey("KEY_ArrowDown");
|
|
EventUtils.synthesizeKey("KEY_ArrowRight");
|
|
await waitForMacEvent("AXMenuOpened");
|
|
|
|
// after the submenu is opened, refetch it
|
|
menu = document.getElementById("contentAreaContextMenu");
|
|
menu = await getMacAccessible(menu);
|
|
menuChildren = menu.getAttributeValue("AXChildren");
|
|
|
|
// verify submenu-menuitem's attributes
|
|
is(
|
|
menuChildren[1].getAttributeValue("AXChildren").length,
|
|
1,
|
|
"Submenu 1 has one child when open"
|
|
);
|
|
const subMenu = menuChildren[1].getAttributeValue("AXChildren")[0];
|
|
is(
|
|
subMenu.getAttributeValue("AXRole"),
|
|
"AXMenu",
|
|
"submenu has role of menu"
|
|
);
|
|
const subMenuChildren = subMenu.getAttributeValue("AXChildren");
|
|
is(subMenuChildren.length, 4, "sub menu has 4 children");
|
|
is(
|
|
subMenu.getAttributeValue("AXVisibleChildren").length,
|
|
4,
|
|
"submenu has 4 visible children"
|
|
);
|
|
|
|
// close context menu
|
|
EventUtils.synthesizeKey("KEY_Escape");
|
|
await waitForMacEvent("AXMenuClosed");
|
|
}
|
|
|
|
EventUtils.synthesizeKey("KEY_Escape");
|
|
await waitForMacEvent("AXMenuClosed");
|
|
}
|
|
);
|
|
});
|