summaryrefslogtreecommitdiffstats
path: root/dom/events/test/browser_alt_keyup_in_content.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/events/test/browser_alt_keyup_in_content.js')
-rw-r--r--dom/events/test/browser_alt_keyup_in_content.js337
1 files changed, 337 insertions, 0 deletions
diff --git a/dom/events/test/browser_alt_keyup_in_content.js b/dom/events/test/browser_alt_keyup_in_content.js
new file mode 100644
index 0000000000..3a9f541a38
--- /dev/null
+++ b/dom/events/test/browser_alt_keyup_in_content.js
@@ -0,0 +1,337 @@
+"use strict";
+
+const { ContentTaskUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/ContentTaskUtils.sys.mjs"
+);
+
+add_task(async function runTests() {
+ const menubar = document.getElementById("toolbar-menubar");
+ const autohide = menubar.getAttribute("autohide");
+ // This test requires that the window is active because of the limitation of
+ // menubar. Therefore, we should abort if the window becomes inactive during
+ // the tests.
+ let runningTests = true;
+ function onWindowActive(aEvent) {
+ // Don't warn after timed out.
+ if (runningTests && aEvent.target === window) {
+ info(
+ "WARNING: This window shouldn't have been inactivated during tests, but received an activated event!"
+ );
+ }
+ }
+ function onWindowInactive(aEvent) {
+ // Don't warn after timed out.
+ if (runningTests && aEvent.target === window) {
+ info(
+ "WARNING: This window should be active during tests, but inactivated!"
+ );
+ window.focus();
+ }
+ }
+ let menubarActivated = false;
+ function onMenubarActive() {
+ menubarActivated = true;
+ }
+ // In this test, menu popups shouldn't be open, but this helps avoiding
+ // intermittent failure after inactivating the menubar.
+ let popupEvents = 0;
+ function getPopupInfo(aPopupEventTarget) {
+ return `<${
+ aPopupEventTarget.nodeName
+ }${aPopupEventTarget.getAttribute("id") !== null ? ` id="${aPopupEventTarget.getAttribute("id")}"` : ""}>`;
+ }
+ function onPopupShown(aEvent) {
+ // Don't warn after timed out.
+ if (!runningTests) {
+ return;
+ }
+ popupEvents++;
+ info(
+ `A popup (${getPopupInfo(
+ aEvent.target
+ )}) is shown (visible popups: ${popupEvents})`
+ );
+ }
+ function onPopupHidden(aEvent) {
+ // Don't warn after timed out.
+ if (!runningTests) {
+ return;
+ }
+ if (popupEvents === 0) {
+ info(
+ `WARNING: There are some unexpected popups which may be not cleaned up by the previous test (${getPopupInfo(
+ aEvent.target
+ )})`
+ );
+ return;
+ }
+ popupEvents--;
+ info(
+ `A popup (${getPopupInfo(
+ aEvent.target
+ )}) is hidden (visible popups: ${popupEvents})`
+ );
+ }
+ try {
+ Services.prefs.setBoolPref("ui.key.menuAccessKeyFocuses", true);
+ // If this fails, you need to replace "KEY_Alt" with a variable whose
+ // value is considered from the pref.
+ is(
+ Services.prefs.getIntPref("ui.key.menuAccessKey"),
+ 18,
+ "This test assumes that Alt key activates the menubar"
+ );
+ window.addEventListener("activate", onWindowActive);
+ window.addEventListener("deactivate", onWindowInactive);
+ window.addEventListener("popupshown", onPopupShown);
+ window.addEventListener("popuphidden", onPopupHidden);
+ menubar.addEventListener("DOMMenuBarActive", onMenubarActive);
+ async function doTest(aTest) {
+ await new Promise(resolve => {
+ if (Services.focus.activeWindow === window) {
+ resolve();
+ return;
+ }
+ info(
+ `${aTest.description}: The testing window is inactive, trying to activate it...`
+ );
+ Services.focus.focusedWindow = window;
+ TestUtils.waitForCondition(() => {
+ if (Services.focus.activeWindow === window) {
+ resolve();
+ return true;
+ }
+ Services.focus.focusedWindow = window;
+ return false;
+ }, `${aTest.description}: Waiting the window is activated`);
+ });
+ let startTime = performance.now();
+ info(`Start to test: ${aTest.description}...`);
+
+ async function ensureMenubarInactive() {
+ if (!menubar.querySelector("[_moz-menuactive=true]")) {
+ return;
+ }
+ info(`${aTest.description}: Inactivating the menubar...`);
+ let waitForMenuBarInactive = BrowserTestUtils.waitForEvent(
+ menubar,
+ "DOMMenuBarInactive"
+ );
+ EventUtils.synthesizeKey("KEY_Escape", {}, window);
+ await waitForMenuBarInactive;
+ await TestUtils.waitForCondition(() => {
+ return popupEvents === 0;
+ }, `${aTest.description}: Waiting for closing all popups`);
+ }
+
+ try {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: aTest.url,
+ },
+ async browser => {
+ info(`${aTest.description}: Waiting browser getting focus...`);
+ await SimpleTest.promiseFocus(browser);
+ await ensureMenubarInactive();
+ menubarActivated = false;
+
+ let keyupEventFiredInContent = false;
+ BrowserTestUtils.addContentEventListener(
+ browser,
+ "keyup",
+ () => {
+ keyupEventFiredInContent = true;
+ },
+ { capture: true },
+ event => {
+ return event.key === "Alt";
+ }
+ );
+
+ // For making sure adding the above content event listener and
+ // it'll get `keyup` event, let's run `SpecialPowers.spawn` and
+ // wait for focus in the content process.
+ info(
+ `${aTest.description}: Waiting content process getting focus...`
+ );
+ await SpecialPowers.spawn(
+ browser,
+ [aTest.description],
+ async aTestDescription => {
+ await ContentTaskUtils.waitForCondition(() => {
+ if (
+ content.browsingContext.isActive &&
+ content.document.hasFocus()
+ ) {
+ return true;
+ }
+ content.window.focus();
+ return false;
+ }, `${aTestDescription}: Waiting for content gets focus in content process`);
+ }
+ );
+
+ let waitForAllKeyUpEventsInChrome = new Promise(resolve => {
+ // Wait 2 `keyup` events in the main process. First one is
+ // synthesized one. The other is replay event from content.
+ let firstKeyUpEvent;
+ window.addEventListener(
+ "keyup",
+ function onKeyUpInChrome(event) {
+ if (!firstKeyUpEvent) {
+ firstKeyUpEvent = event;
+ return;
+ }
+ window.removeEventListener("keyup", onKeyUpInChrome, {
+ capture: true,
+ });
+ resolve();
+ },
+ { capture: true }
+ );
+ });
+
+ let menubarActivatedPromise;
+ if (aTest.expectMenubarActive) {
+ menubarActivatedPromise = BrowserTestUtils.waitForEvent(
+ menubar,
+ "DOMMenuBarActive"
+ );
+ }
+
+ EventUtils.synthesizeKey("KEY_Alt", {}, window);
+ info(
+ `${aTest.description}: Waiting keyup events of Alt in chrome...`
+ );
+ await waitForAllKeyUpEventsInChrome;
+ info(`${aTest.description}: Waiting keyup event in content...`);
+ try {
+ await TestUtils.waitForCondition(() => {
+ return keyupEventFiredInContent;
+ }, `${aTest.description}: Waiting for content gets focus in chrome process`);
+ } catch (ex) {
+ ok(
+ false,
+ `${aTest.description}: Failed to synthesize Alt key press in the content process`
+ );
+ return;
+ }
+
+ if (aTest.expectMenubarActive) {
+ await menubarActivatedPromise;
+ ok(
+ menubarActivated,
+ `${aTest.description}: Menubar should've been activated by the synthesized Alt key press`
+ );
+ } else {
+ // Wait some ticks to verify not receiving "DOMMenuBarActive" event.
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(resolve => setTimeout(resolve, 500));
+ ok(
+ !menubarActivated,
+ `${aTest.description}: Menubar should not have been activated by the synthesized Alt key press`
+ );
+ }
+ }
+ );
+ } catch (ex) {
+ ok(
+ false,
+ `${aTest.description}: Thrown an exception: ${ex.toString()}`
+ );
+ } finally {
+ await ensureMenubarInactive();
+ info(`End testing: ${aTest.description}`);
+ ChromeUtils.addProfilerMarker(
+ "browser-test",
+ { startTime, category: "Test" },
+ aTest.description
+ );
+ }
+ }
+
+ // Testcases for users who use collapsible menubar (by default)
+ menubar.setAttribute("autohide", "true");
+ await doTest({
+ description: "Testing menubar is shown by Alt keyup",
+ url: "data:text/html;charset=utf-8,<p>static page</p>",
+ expectMenubarActive: true,
+ });
+ await doTest({
+ description:
+ "Testing menubar is shown by Alt keyup when an <input> has focus",
+ url:
+ "data:text/html;charset=utf-8,<input>" +
+ '<script>document.querySelector("input").focus()</script>',
+ expectMenubarActive: true,
+ });
+ await doTest({
+ description:
+ "Testing menubar is shown by Alt keyup when an editing host has focus",
+ url:
+ "data:text/html;charset=utf-8,<p contenteditable></p>" +
+ '<script>document.querySelector("p[contenteditable]").focus()</script>',
+ expectMenubarActive: true,
+ });
+ await doTest({
+ description:
+ "Testing menubar won't be shown by Alt keyup due to suppressed by the page",
+ url:
+ "data:text/html;charset=utf-8,<p>dynamic page</p>" +
+ '<script>window.addEventListener("keyup", event => { event.preventDefault(); })</script>',
+ expectMenubarActive: false,
+ });
+
+ // Testcases for users who always show the menubar.
+ menubar.setAttribute("autohide", "false");
+ await doTest({
+ description: "Testing menubar is activated by Alt keyup",
+ url: "data:text/html;charset=utf-8,<p>static page</p>",
+ expectMenubarActive: true,
+ });
+ await doTest({
+ description:
+ "Testing menubar is activated by Alt keyup when an <input> has focus",
+ url:
+ "data:text/html;charset=utf-8,<input>" +
+ '<script>document.querySelector("input").focus()</script>',
+ expectMenubarActive: true,
+ });
+ await doTest({
+ description:
+ "Testing menubar is activated by Alt keyup when an editing host has focus",
+ url:
+ "data:text/html;charset=utf-8,<p contenteditable></p>" +
+ '<script>document.querySelector("p[contenteditable]").focus()</script>',
+ expectMenubarActive: true,
+ });
+ await doTest({
+ description:
+ "Testing menubar won't be activated by Alt keyup due to suppressed by the page",
+ url:
+ "data:text/html;charset=utf-8,<p>dynamic page</p>" +
+ '<script>window.addEventListener("keyup", event => { event.preventDefault(); })</script>',
+ expectMenubarActive: false,
+ });
+ runningTests = false;
+ } catch (ex) {
+ ok(
+ false,
+ `Aborting this test due to unexpected the exception (${ex.toString()})`
+ );
+ runningTests = false;
+ } finally {
+ if (autohide !== null) {
+ menubar.setAttribute("autohide", autohide);
+ } else {
+ menubar.removeAttribute("autohide");
+ }
+ Services.prefs.clearUserPref("ui.key.menuAccessKeyFocuses");
+ menubar.removeEventListener("DOMMenuBarActive", onMenubarActive);
+ window.removeEventListener("activate", onWindowActive);
+ window.removeEventListener("deactivate", onWindowInactive);
+ window.removeEventListener("popupshown", onPopupShown);
+ window.removeEventListener("popuphidden", onPopupHidden);
+ }
+});