summaryrefslogtreecommitdiffstats
path: root/toolkit/content/tests/chrome/popup_trigger.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/content/tests/chrome/popup_trigger.js')
-rw-r--r--toolkit/content/tests/chrome/popup_trigger.js1208
1 files changed, 1208 insertions, 0 deletions
diff --git a/toolkit/content/tests/chrome/popup_trigger.js b/toolkit/content/tests/chrome/popup_trigger.js
new file mode 100644
index 0000000000..357da29053
--- /dev/null
+++ b/toolkit/content/tests/chrome/popup_trigger.js
@@ -0,0 +1,1208 @@
+/* import-globals-from ../widgets/popup_shared.js */
+
+var gMenuPopup = null;
+var gTrigger = null;
+var gIsMenu = false;
+var gScreenX = -1,
+ gScreenY = -1;
+var gCachedEvent = null;
+var gCachedEvent2 = null;
+
+function cacheEvent(modifiers) {
+ var cachedEvent = null;
+
+ var mouseFn = function (event) {
+ cachedEvent = event;
+ };
+
+ window.addEventListener("mousedown", mouseFn);
+ synthesizeMouse(document.documentElement, 0, 0, modifiers);
+ window.removeEventListener("mousedown", mouseFn);
+
+ return cachedEvent;
+}
+
+function runTests() {
+ if (screen.height < 768) {
+ ok(
+ false,
+ "popup tests are likely to fail for screen heights less than 768 pixels"
+ );
+ }
+
+ gMenuPopup = document.getElementById("thepopup");
+ gTrigger = document.getElementById("trigger");
+
+ gIsMenu = gTrigger.hasMenu();
+
+ // a hacky way to get the screen position of the document. Cache the event
+ // so that we can use it in calls to openPopup.
+ gCachedEvent = cacheEvent({ shiftKey: true });
+ gScreenX = gCachedEvent.screenX;
+ gScreenY = gCachedEvent.screenY;
+ gCachedEvent2 = cacheEvent({
+ altKey: true,
+ ctrlKey: true,
+ shiftKey: true,
+ metaKey: true,
+ });
+
+ startPopupTests(popupTests);
+}
+
+var popupTests = [
+ {
+ testname: "mouse click on trigger",
+ events: ["popupshowing thepopup", "popupshown thepopup"],
+ test() {
+ // for menus, no trigger will be set. For non-menus using the popup
+ // attribute, the trigger will be set to the node with the popup attribute
+ gExpectedTriggerNode = gIsMenu ? "notset" : gTrigger;
+ synthesizeMouse(gTrigger, 4, 4, {});
+ },
+ async result(testname) {
+ gExpectedTriggerNode = null;
+ // menus are the anchor but non-menus are opened at screen coordinates
+ is(
+ gMenuPopup.anchorNode,
+ gIsMenu ? gTrigger : null,
+ testname + " anchorNode"
+ );
+ // menus are opened internally, but non-menus have a mouse event which
+ // triggered them
+ is(
+ gMenuPopup.triggerNode,
+ gIsMenu ? null : gTrigger,
+ testname + " triggerNode"
+ );
+
+ // Popup may have wrong initial size in non e10s mode tests, because
+ // layout is not yet ready for popup content lazy population on
+ // popupshowing event.
+ await new Promise(r =>
+ requestAnimationFrame(() => requestAnimationFrame(r))
+ );
+
+ // this will be used in some tests to ensure the size doesn't change
+ var popuprect = gMenuPopup.getBoundingClientRect();
+ gPopupWidth = Math.round(popuprect.width);
+ gPopupHeight = Math.round(popuprect.height);
+
+ checkActive(gMenuPopup, "", testname);
+ checkOpen("trigger", testname);
+ // if a menu, the popup should be opened underneath the menu in the
+ // 'after_start' position, otherwise it is opened at the mouse position
+ if (gIsMenu) {
+ compareEdge(gTrigger, gMenuPopup, "after_start", 0, 0, testname);
+ }
+ },
+ },
+ {
+ // check that pressing cursor down while there is no selection
+ // highlights the first item
+ testname: "cursor down no selection",
+ events: ["DOMMenuItemActive item1"],
+ test() {
+ synthesizeKey("KEY_ArrowDown");
+ },
+ result(testname) {
+ checkActive(gMenuPopup, "item1", testname);
+ },
+ },
+ {
+ // check that pressing cursor up wraps and highlights the last item
+ testname: "cursor up wrap",
+ events() {
+ // No wrapping on menus on Mac
+ return platformIsMac()
+ ? []
+ : ["DOMMenuItemInactive item1", "DOMMenuItemActive last"];
+ },
+ test() {
+ synthesizeKey("KEY_ArrowUp");
+ },
+ result(testname) {
+ checkActive(gMenuPopup, platformIsMac() ? "item1" : "last", testname);
+ },
+ },
+ {
+ // check that pressing cursor down wraps and highlights the first item
+ testname: "cursor down wrap",
+ condition() {
+ return !platformIsMac();
+ },
+ events: ["DOMMenuItemInactive last", "DOMMenuItemActive item1"],
+ test() {
+ synthesizeKey("KEY_ArrowDown");
+ },
+ result(testname) {
+ checkActive(gMenuPopup, "item1", testname);
+ },
+ },
+ {
+ // check that pressing cursor down highlights the second item
+ testname: "cursor down",
+ events: ["DOMMenuItemInactive item1", "DOMMenuItemActive item2"],
+ test() {
+ synthesizeKey("KEY_ArrowDown");
+ },
+ result(testname) {
+ checkActive(gMenuPopup, "item2", testname);
+ },
+ },
+ {
+ // check that pressing cursor up highlights the second item
+ testname: "cursor up",
+ events: ["DOMMenuItemInactive item2", "DOMMenuItemActive item1"],
+ test() {
+ synthesizeKey("KEY_ArrowUp");
+ },
+ result(testname) {
+ checkActive(gMenuPopup, "item1", testname);
+ },
+ },
+ {
+ // cursor left should not do anything
+ testname: "cursor left",
+ test() {
+ synthesizeKey("KEY_ArrowLeft");
+ },
+ result(testname) {
+ checkActive(gMenuPopup, "item1", testname);
+ },
+ },
+ {
+ // cursor right should not do anything
+ testname: "cursor right",
+ test() {
+ synthesizeKey("KEY_ArrowRight");
+ },
+ result(testname) {
+ checkActive(gMenuPopup, "item1", testname);
+ },
+ },
+ {
+ // check cursor down when a disabled item exists in the menu
+ testname: "cursor down disabled",
+ events() {
+ // On Windows, disabled items are included when navigating, but on
+ // other platforms, disabled items are skipped over
+ if (navigator.platform.indexOf("Win") == 0) {
+ return ["DOMMenuItemInactive item1", "DOMMenuItemActive item2"];
+ }
+ return ["DOMMenuItemInactive item1", "DOMMenuItemActive amenu"];
+ },
+ test() {
+ document.getElementById("item2").disabled = true;
+ synthesizeKey("KEY_ArrowDown");
+ },
+ },
+ {
+ // check cursor up when a disabled item exists in the menu
+ testname: "cursor up disabled",
+ events() {
+ if (navigator.platform.indexOf("Win") == 0) {
+ return [
+ "DOMMenuItemInactive item2",
+ "DOMMenuItemActive amenu",
+ "DOMMenuItemInactive amenu",
+ "DOMMenuItemActive item2",
+ "DOMMenuItemInactive item2",
+ "DOMMenuItemActive item1",
+ ];
+ }
+ return ["DOMMenuItemInactive amenu", "DOMMenuItemActive item1"];
+ },
+ test() {
+ if (navigator.platform.indexOf("Win") == 0) {
+ synthesizeKey("KEY_ArrowDown");
+ }
+ synthesizeKey("KEY_ArrowUp");
+ if (navigator.platform.indexOf("Win") == 0) {
+ synthesizeKey("KEY_ArrowUp");
+ }
+ },
+ },
+ {
+ testname: "mouse click outside",
+ events: [
+ "popuphiding thepopup",
+ "popuphidden thepopup",
+ "DOMMenuItemInactive item1",
+ "DOMMenuInactive thepopup",
+ ],
+ test() {
+ gMenuPopup.hidePopup();
+ // XXXndeakin event simulation fires events outside of the platform specific
+ // widget code so the popup capturing isn't handled. Thus, the menu won't
+ // rollup this way.
+ // synthesizeMouse(gTrigger, 0, -12, { });
+ },
+ result(testname, step) {
+ is(gMenuPopup.anchorNode, null, testname + " anchorNode");
+ is(gMenuPopup.triggerNode, null, testname + " triggerNode");
+ checkClosed("trigger", testname);
+ },
+ },
+ {
+ // these tests check to ensure that passing an anchor and position
+ // puts the popup in the right place
+ testname: "open popup anchored",
+ events: ["popupshowing thepopup", "popupshown thepopup"],
+ autohide: "thepopup",
+ steps: [
+ "before_start",
+ "before_end",
+ "after_start",
+ "after_end",
+ "start_before",
+ "start_after",
+ "end_before",
+ "end_after",
+ "after_pointer",
+ "overlap",
+ "topleft topleft",
+ "topcenter topleft",
+ "topright topleft",
+ "leftcenter topright",
+ "rightcenter topright",
+ "bottomleft bottomleft",
+ "bottomcenter bottomleft",
+ "bottomright bottomleft",
+ "topleft bottomright",
+ "bottomcenter bottomright",
+ "rightcenter topright",
+ ],
+ test(testname, step) {
+ gExpectedTriggerNode = "notset";
+ gMenuPopup.openPopup(gTrigger, step, 0, 0, false, false);
+ },
+ result(testname, step) {
+ // no triggerNode because it was opened without passing an event
+ gExpectedTriggerNode = null;
+ is(gMenuPopup.anchorNode, gTrigger, testname + " anchorNode");
+ is(gMenuPopup.triggerNode, null, testname + " triggerNode");
+ compareEdge(gTrigger, gMenuPopup, step, 0, 0, testname);
+ },
+ },
+ {
+ // these tests check the same but with a 10 pixel margin on the popup
+ testname: "open popup anchored with margin",
+ events: ["popupshowing thepopup", "popupshown thepopup"],
+ autohide: "thepopup",
+ steps: [
+ "before_start",
+ "before_end",
+ "after_start",
+ "after_end",
+ "start_before",
+ "start_after",
+ "end_before",
+ "end_after",
+ "after_pointer",
+ "overlap",
+ "topleft topleft",
+ "topcenter topleft",
+ "topright topleft",
+ "leftcenter topright",
+ "rightcenter topright",
+ "bottomleft bottomleft",
+ "bottomcenter bottomleft",
+ "bottomright bottomleft",
+ "topleft bottomright",
+ "bottomcenter bottomright",
+ "rightcenter topright",
+ ],
+ test(testname, step) {
+ gMenuPopup.setAttribute("style", "margin: 10px;");
+ gMenuPopup.openPopup(gTrigger, step, 0, 0, false, false);
+ },
+ result(testname, step) {
+ var rightmod =
+ step == "before_end" ||
+ step == "after_end" ||
+ step == "start_before" ||
+ step == "start_after" ||
+ step.match(/topright$/) ||
+ step.match(/bottomright$/);
+ var bottommod =
+ step == "before_start" ||
+ step == "before_end" ||
+ step == "start_after" ||
+ step == "end_after" ||
+ step.match(/bottomleft$/) ||
+ step.match(/bottomright$/);
+ compareEdge(
+ gTrigger,
+ gMenuPopup,
+ step,
+ rightmod ? -10 : 10,
+ bottommod ? -10 : 10,
+ testname
+ );
+ gMenuPopup.removeAttribute("style");
+ },
+ },
+ {
+ // these tests check the same but with a -8 pixel margin on the popup
+ testname: "open popup anchored with negative margin",
+ events: ["popupshowing thepopup", "popupshown thepopup"],
+ autohide: "thepopup",
+ steps: [
+ "before_start",
+ "before_end",
+ "after_start",
+ "after_end",
+ "start_before",
+ "start_after",
+ "end_before",
+ "end_after",
+ "after_pointer",
+ "overlap",
+ ],
+ test(testname, step) {
+ gMenuPopup.setAttribute("style", "margin: -8px;");
+ gMenuPopup.openPopup(gTrigger, step, 0, 0, false, false);
+ },
+ result(testname, step) {
+ var rightmod =
+ step == "before_end" ||
+ step == "after_end" ||
+ step == "start_before" ||
+ step == "start_after";
+ var bottommod =
+ step == "before_start" ||
+ step == "before_end" ||
+ step == "start_after" ||
+ step == "end_after";
+ compareEdge(
+ gTrigger,
+ gMenuPopup,
+ step,
+ rightmod ? 8 : -8,
+ bottommod ? 8 : -8,
+ testname
+ );
+ gMenuPopup.removeAttribute("style");
+ },
+ },
+ {
+ testname: "open popup with large positive margin",
+ events: ["popupshowing thepopup", "popupshown thepopup"],
+ autohide: "thepopup",
+ steps: [
+ "before_start",
+ "before_end",
+ "after_start",
+ "after_end",
+ "start_before",
+ "start_after",
+ "end_before",
+ "end_after",
+ "after_pointer",
+ "overlap",
+ ],
+ test(testname, step) {
+ gMenuPopup.setAttribute("style", "margin: 1000px;");
+ gMenuPopup.openPopup(gTrigger, step, 0, 0, false, false);
+ },
+ result(testname, step) {
+ var popuprect = gMenuPopup.getBoundingClientRect();
+ // as there is more room on the 'end' or 'after' side, popups will always
+ // appear on the right or bottom corners, depending on which side they are
+ // allowed to be flipped by.
+ var expectedleft =
+ step == "before_end" || step == "after_end"
+ ? 0
+ : Math.round(window.innerWidth - gPopupWidth);
+ var expectedtop =
+ step == "start_after" || step == "end_after"
+ ? 0
+ : Math.round(window.innerHeight - gPopupHeight);
+ is(
+ Math.round(popuprect.left),
+ expectedleft,
+ testname + " x position " + step
+ );
+ is(
+ Math.round(popuprect.top),
+ expectedtop,
+ testname + " y position " + step
+ );
+ gMenuPopup.removeAttribute("style");
+ },
+ },
+ {
+ testname: "open popup with large negative margin",
+ events: ["popupshowing thepopup", "popupshown thepopup"],
+ autohide: "thepopup",
+ steps: [
+ "before_start",
+ "before_end",
+ "after_start",
+ "after_end",
+ "start_before",
+ "start_after",
+ "end_before",
+ "end_after",
+ "after_pointer",
+ "overlap",
+ ],
+ test(testname, step) {
+ gMenuPopup.setAttribute("style", "margin: -1000px;");
+ gMenuPopup.openPopup(gTrigger, step, 0, 0, false, false);
+ },
+ result(testname, step) {
+ var popuprect = gMenuPopup.getBoundingClientRect();
+ // using negative margins causes the reverse of positive margins, and
+ // popups will appear on the left or top corners.
+ var expectedleft =
+ step == "before_end" || step == "after_end"
+ ? Math.round(window.innerWidth - gPopupWidth)
+ : 0;
+ var expectedtop =
+ step == "start_after" || step == "end_after"
+ ? Math.round(window.innerHeight - gPopupHeight)
+ : 0;
+ is(
+ Math.round(popuprect.left),
+ expectedleft,
+ testname + " x position " + step
+ );
+ is(
+ Math.round(popuprect.top),
+ expectedtop,
+ testname + " y position " + step
+ );
+ gMenuPopup.removeAttribute("style");
+ },
+ },
+ {
+ testname: "popup with unknown step",
+ events: ["popupshowing thepopup", "popupshown thepopup"],
+ autohide: "thepopup",
+ test() {
+ gMenuPopup.openPopup(gTrigger, "other", 0, 0, false, false);
+ },
+ result(testname) {
+ var triggerrect = gMenuPopup.getBoundingClientRect();
+ var popuprect = gMenuPopup.getBoundingClientRect();
+ is(
+ Math.round(popuprect.left),
+ triggerrect.left,
+ testname + " x position "
+ );
+ is(Math.round(popuprect.top), triggerrect.top, testname + " y position ");
+ },
+ },
+ {
+ // these tests check to ensure that the position attribute can be used
+ // to set the position of a popup instead of passing it as an argument
+ testname: "open popup anchored with attribute",
+ events: ["popupshowing thepopup", "popupshown thepopup"],
+ autohide: "thepopup",
+ steps: [
+ "before_start",
+ "before_end",
+ "after_start",
+ "after_end",
+ "start_before",
+ "start_after",
+ "end_before",
+ "end_after",
+ "after_pointer",
+ "overlap",
+ "topcenter topleft",
+ "topright bottomright",
+ "leftcenter topright",
+ ],
+ test(testname, step) {
+ gMenuPopup.setAttribute("position", step);
+ gMenuPopup.openPopup(gTrigger, "", 0, 0, false, false);
+ },
+ result(testname, step) {
+ compareEdge(gTrigger, gMenuPopup, step, 0, 0, testname);
+ },
+ },
+ {
+ // this test checks to ensure that the attributes override flag to openPopup
+ // can be used to override the popup's position. This test also passes an
+ // event to openPopup to check the trigger node.
+ testname: "open popup anchored with override",
+ events: ["popupshowing thepopup 0010", "popupshown thepopup"],
+ test(testname, step) {
+ // attribute overrides the position passed in
+ gMenuPopup.setAttribute("position", "end_after");
+ gExpectedTriggerNode = gCachedEvent.target;
+ gMenuPopup.openPopup(
+ gTrigger,
+ "before_start",
+ 0,
+ 0,
+ false,
+ true,
+ gCachedEvent
+ );
+ },
+ result(testname, step) {
+ gExpectedTriggerNode = null;
+ is(gMenuPopup.anchorNode, gTrigger, testname + " anchorNode");
+ is(
+ gMenuPopup.triggerNode,
+ gCachedEvent.target,
+ testname + " triggerNode"
+ );
+ compareEdge(gTrigger, gMenuPopup, "end_after", 0, 0, testname);
+ },
+ },
+ {
+ testname: "close popup with escape",
+ events: [
+ "popuphiding thepopup",
+ "popuphidden thepopup",
+ "DOMMenuInactive thepopup",
+ ],
+ test(testname, step) {
+ synthesizeKey("KEY_Escape");
+ checkClosed("trigger", testname);
+ },
+ },
+ {
+ // check that offsets may be supplied to the openPopup method
+ testname: "open popup anchored with offsets",
+ events: ["popupshowing thepopup", "popupshown thepopup"],
+ autohide: "thepopup",
+ test(testname, step) {
+ // attribute is empty so does not override
+ gMenuPopup.setAttribute("position", "");
+ gMenuPopup.openPopup(gTrigger, "before_start", 5, 10, true, true);
+ },
+ result(testname, step) {
+ compareEdge(gTrigger, gMenuPopup, "before_start", 5, 10, testname);
+ },
+ },
+ {
+ // if no anchor is supplied to openPopup, it should be opened relative
+ // to the viewport.
+ testname: "open popup unanchored",
+ events: ["popupshowing thepopup", "popupshown thepopup"],
+ test(testname, step) {
+ gMenuPopup.openPopup(null, "after_start", 6, 8, false);
+ },
+ result(testname, step) {
+ var rect = gMenuPopup.getBoundingClientRect();
+ ok(
+ rect.left == 6 && rect.top == 8 && rect.right && rect.bottom,
+ testname
+ );
+ },
+ },
+ {
+ testname: "activate menuitem with mouse",
+ events: [
+ "DOMMenuInactive thepopup",
+ "command item3",
+ "popuphiding thepopup",
+ "popuphidden thepopup",
+ ],
+ test(testname, step) {
+ var item3 = document.getElementById("item3");
+ synthesizeMouse(item3, 4, 4, {});
+ },
+ result(testname, step) {
+ checkClosed("trigger", testname);
+ },
+ },
+ {
+ testname: "close popup",
+ condition() {
+ return false;
+ },
+ events: [
+ "popuphiding thepopup",
+ "popuphidden thepopup",
+ "DOMMenuInactive thepopup",
+ ],
+ test(testname, step) {
+ gMenuPopup.hidePopup();
+ },
+ },
+ {
+ testname: "open popup at screen",
+ events: ["popupshowing thepopup", "popupshown thepopup"],
+ test(testname, step) {
+ gExpectedTriggerNode = "notset";
+ gMenuPopup.openPopupAtScreen(gScreenX + 24, gScreenY + 20, false);
+ },
+ result(testname, step) {
+ gExpectedTriggerNode = null;
+ is(gMenuPopup.anchorNode, null, testname + " anchorNode");
+ is(gMenuPopup.triggerNode, null, testname + " triggerNode");
+ var rect = gMenuPopup.getBoundingClientRect();
+ is(rect.left, 24, testname + " left");
+ is(rect.top, 20, testname + " top");
+ ok(rect.right, testname + " right is " + rect.right);
+ ok(rect.bottom, testname + " bottom is " + rect.bottom);
+ },
+ },
+ {
+ // check that pressing a menuitem's accelerator selects it. Note that
+ // the menuitem with the M accesskey overrides the earlier menuitem that
+ // begins with M.
+ testname: "menuitem accelerator",
+ events: [
+ "DOMMenuItemActive amenu",
+ "DOMMenuItemInactive amenu",
+ "DOMMenuInactive thepopup",
+ "command amenu",
+ "popuphiding thepopup",
+ "popuphidden thepopup",
+ ],
+ test() {
+ sendString("M");
+ },
+ result(testname) {
+ checkClosed("trigger", testname);
+ },
+ },
+ {
+ testname: "open context popup at screen",
+ events: ["popupshowing thepopup 0010", "popupshown thepopup"],
+ test(testname, step) {
+ gExpectedTriggerNode = gCachedEvent.target;
+ gMenuPopup.openPopupAtScreen(
+ gScreenX + 8,
+ gScreenY + 16,
+ true,
+ gCachedEvent
+ );
+ },
+ result(testname, step) {
+ gExpectedTriggerNode = null;
+ is(gMenuPopup.anchorNode, null, testname + " anchorNode");
+ is(
+ gMenuPopup.triggerNode,
+ gCachedEvent.target,
+ testname + " triggerNode"
+ );
+
+ var openX = 8;
+ var openY = 16;
+ var rect = gMenuPopup.getBoundingClientRect();
+ is(rect.left, openX + (platformIsMac() ? 1 : 2), testname + " left");
+ is(rect.top, openY + (platformIsMac() ? -6 : 2), testname + " top");
+ ok(rect.right, testname + " right is " + rect.right);
+ ok(rect.bottom, testname + " bottom is " + rect.bottom);
+ },
+ },
+ {
+ // pressing a letter that doesn't correspond to an accelerator, but does
+ // correspond to the first letter in a menu's label. The menu should not
+ // close because there is more than one item corresponding to that letter
+ testname: "menuitem with non accelerator",
+ events: ["DOMMenuItemActive one"],
+ test() {
+ sendString("O");
+ },
+ result(testname) {
+ checkOpen("trigger", testname);
+ checkActive(gMenuPopup, "one", testname);
+ },
+ },
+ {
+ // pressing the letter again should select the next one that starts with
+ // that letter
+ testname: "menuitem with non accelerator again",
+ events: ["DOMMenuItemInactive one", "DOMMenuItemActive submenu"],
+ test() {
+ sendString("O");
+ },
+ result(testname) {
+ // 'submenu' is a menu but it should not be open
+ checkOpen("trigger", testname);
+ checkClosed("submenu", testname);
+ checkActive(gMenuPopup, "submenu", testname);
+ },
+ },
+ {
+ // open the submenu with the cursor right key
+ testname: "open submenu with cursor right",
+ events: [
+ "popupshowing submenupopup",
+ "DOMMenuItemActive submenuitem",
+ "popupshown submenupopup",
+ ],
+ test() {
+ synthesizeKey("KEY_ArrowRight");
+ },
+ result(testname) {
+ checkOpen("trigger", testname);
+ checkOpen("submenu", testname);
+ checkActive(gMenuPopup, "submenu", testname);
+ checkActive(
+ document.getElementById("submenupopup"),
+ "submenuitem",
+ testname
+ );
+ },
+ },
+ {
+ // close the submenu with the cursor left key
+ testname: "close submenu with cursor left",
+ events: [
+ "popuphiding submenupopup",
+ "popuphidden submenupopup",
+ "DOMMenuItemInactive submenuitem",
+ "DOMMenuInactive submenupopup",
+ ],
+ test() {
+ synthesizeKey("KEY_ArrowLeft");
+ },
+ result(testname) {
+ checkOpen("trigger", testname);
+ checkClosed("submenu", testname);
+ checkActive(gMenuPopup, "submenu", testname);
+ checkActive(document.getElementById("submenupopup"), "", testname);
+ },
+ },
+ {
+ // open the submenu with the enter key
+ testname: "open submenu with enter",
+ events: [
+ "popupshowing submenupopup",
+ "DOMMenuItemActive submenuitem",
+ "popupshown submenupopup",
+ ],
+ test() {
+ synthesizeKey("KEY_Enter");
+ },
+ result(testname) {
+ checkOpen("trigger", testname);
+ checkOpen("submenu", testname);
+ checkActive(gMenuPopup, "submenu", testname);
+ checkActive(
+ document.getElementById("submenupopup"),
+ "submenuitem",
+ testname
+ );
+ },
+ },
+ {
+ // close the submenu with the escape key
+ testname: "close submenu with escape",
+ events: [
+ "popuphiding submenupopup",
+ "popuphidden submenupopup",
+ "DOMMenuItemInactive submenuitem",
+ "DOMMenuInactive submenupopup",
+ ],
+ test() {
+ synthesizeKey("KEY_Escape");
+ },
+ result(testname) {
+ checkOpen("trigger", testname);
+ checkClosed("submenu", testname);
+ checkActive(gMenuPopup, "submenu", testname);
+ checkActive(document.getElementById("submenupopup"), "", testname);
+ },
+ },
+ {
+ // pressing the letter again when the next item is disabled should still
+ // select the disabled item on Windows, but select the next item on other
+ // platforms
+ testname: "menuitem with non accelerator disabled",
+ events() {
+ if (navigator.platform.indexOf("Win") == 0) {
+ return [
+ "DOMMenuItemInactive submenu",
+ "DOMMenuItemActive other",
+ "DOMMenuItemInactive other",
+ "DOMMenuItemActive item1",
+ ];
+ }
+ return [
+ "DOMMenuItemInactive submenu",
+ "DOMMenuItemActive last",
+ "DOMMenuItemInactive last",
+ "DOMMenuItemActive item1",
+ ];
+ },
+ test() {
+ sendString("OF");
+ },
+ result(testname) {
+ checkActive(gMenuPopup, "item1", testname);
+ },
+ },
+ {
+ // pressing a letter that doesn't correspond to an accelerator nor the
+ // first letter of a menu. This should have no effect.
+ testname: "menuitem with keypress no accelerator found",
+ test() {
+ sendString("G");
+ },
+ result(testname) {
+ checkOpen("trigger", testname);
+ checkActive(gMenuPopup, "item1", testname);
+ },
+ },
+ {
+ // when only one menuitem starting with that letter exists, it should be
+ // selected and the menu closed
+ testname: "menuitem with non accelerator single",
+ events: [
+ "DOMMenuItemInactive item1",
+ "DOMMenuItemActive amenu",
+ "DOMMenuItemInactive amenu",
+ "DOMMenuInactive thepopup",
+ "command amenu",
+ "popuphiding thepopup",
+ "popuphidden thepopup",
+ ],
+ test() {
+ sendString("M");
+ },
+ result(testname) {
+ checkClosed("trigger", testname);
+ checkActive(gMenuPopup, "", testname);
+ },
+ },
+ {
+ testname: "open context popup at screen with all modifiers set",
+ events: ["popupshowing thepopup 1111", "popupshown thepopup"],
+ autohide: "thepopup",
+ test(testname, step) {
+ gMenuPopup.openPopupAtScreen(
+ gScreenX + 8,
+ gScreenY + 16,
+ true,
+ gCachedEvent2
+ );
+ },
+ },
+ {
+ testname: "open popup with open property",
+ events: ["popupshowing thepopup", "popupshown thepopup"],
+ test(testname, step) {
+ openMenu(gTrigger);
+ },
+ result(testname, step) {
+ checkOpen("trigger", testname);
+ if (gIsMenu) {
+ compareEdge(gTrigger, gMenuPopup, "after_start", 0, 0, testname);
+ }
+ },
+ },
+ {
+ testname: "open submenu with open property",
+ events: [
+ "popupshowing submenupopup",
+ "DOMMenuItemActive submenu",
+ "popupshown submenupopup",
+ ],
+ test(testname, step) {
+ openMenu(document.getElementById("submenu"));
+ },
+ result(testname, step) {
+ checkOpen("trigger", testname);
+ checkOpen("submenu", testname);
+ // XXXndeakin
+ // getBoundingClientRect doesn't seem to working right for submenus
+ // so disable this test for now
+ // compareEdge(document.getElementById("submenu"),
+ // document.getElementById("submenupopup"), "end_before", 0, 0, testname);
+ },
+ },
+ {
+ testname: "hidePopup hides entire chain",
+ events: [
+ "popuphiding submenupopup",
+ "popuphidden submenupopup",
+ "popuphiding thepopup",
+ "popuphidden thepopup",
+ "DOMMenuInactive submenupopup",
+ "DOMMenuItemInactive submenu",
+ "DOMMenuInactive thepopup",
+ ],
+ test() {
+ gMenuPopup.hidePopup();
+ },
+ result(testname, step) {
+ checkClosed("trigger", testname);
+ checkClosed("submenu", testname);
+ },
+ },
+ {
+ testname: "open submenu with open property without parent open",
+ test(testname, step) {
+ openMenu(document.getElementById("submenu"));
+ },
+ result(testname, step) {
+ checkClosed("trigger", testname);
+ checkClosed("submenu", testname);
+ },
+ },
+ {
+ testname: "open popup with open property and position",
+ condition() {
+ return gIsMenu;
+ },
+ events: ["popupshowing thepopup", "popupshown thepopup"],
+ test(testname, step) {
+ gMenuPopup.setAttribute("position", "before_start");
+ openMenu(gTrigger);
+ },
+ result(testname, step) {
+ compareEdge(gTrigger, gMenuPopup, "before_start", 0, 0, testname);
+ },
+ },
+ {
+ testname: "close popup with open property",
+ condition() {
+ return gIsMenu;
+ },
+ events: [
+ "popuphiding thepopup",
+ "popuphidden thepopup",
+ "DOMMenuInactive thepopup",
+ ],
+ test(testname, step) {
+ closeMenu(gTrigger, gMenuPopup);
+ },
+ result(testname, step) {
+ checkClosed("trigger", testname);
+ },
+ },
+ {
+ testname: "open popup with open property, position, anchor and alignment",
+ condition() {
+ return gIsMenu;
+ },
+ events: ["popupshowing thepopup", "popupshown thepopup"],
+ autohide: "thepopup",
+ test(testname, step) {
+ gMenuPopup.setAttribute("position", "start_after");
+ gMenuPopup.setAttribute("popupanchor", "topright");
+ gMenuPopup.setAttribute("popupalign", "bottomright");
+ openMenu(gTrigger);
+ },
+ result(testname, step) {
+ compareEdge(gTrigger, gMenuPopup, "start_after", 0, 0, testname);
+ },
+ },
+ {
+ testname: "open popup with open property, anchor and alignment",
+ condition() {
+ return gIsMenu;
+ },
+ events: ["popupshowing thepopup", "popupshown thepopup"],
+ autohide: "thepopup",
+ test(testname, step) {
+ gMenuPopup.removeAttribute("position");
+ gMenuPopup.setAttribute("popupanchor", "bottomright");
+ gMenuPopup.setAttribute("popupalign", "topright");
+ openMenu(gTrigger);
+ },
+ result(testname, step) {
+ compareEdge(gTrigger, gMenuPopup, "after_end", 0, 0, testname);
+ gMenuPopup.removeAttribute("popupanchor");
+ gMenuPopup.removeAttribute("popupalign");
+ },
+ },
+ {
+ testname: "focus and cursor down on trigger",
+ condition() {
+ return gIsMenu;
+ },
+ events: ["popupshowing thepopup", "popupshown thepopup"],
+ autohide: "thepopup",
+ test(testname, step) {
+ gTrigger.focus();
+ synthesizeKey("KEY_ArrowDown", { altKey: !platformIsMac() });
+ },
+ result(testname, step) {
+ checkOpen("trigger", testname);
+ checkActive(gMenuPopup, "", testname);
+ },
+ },
+ {
+ testname: "focus and cursor up on trigger",
+ condition() {
+ return gIsMenu;
+ },
+ events: ["popupshowing thepopup", "popupshown thepopup"],
+ test(testname, step) {
+ gTrigger.focus();
+ synthesizeKey("KEY_ArrowUp", { altKey: !platformIsMac() });
+ },
+ result(testname, step) {
+ checkOpen("trigger", testname);
+ checkActive(gMenuPopup, "", testname);
+ },
+ },
+ {
+ testname: "select and enter on menuitem",
+ condition() {
+ return gIsMenu;
+ },
+ events: [
+ "DOMMenuItemActive item1",
+ "DOMMenuItemInactive item1",
+ "DOMMenuInactive thepopup",
+ "command item1",
+ "popuphiding thepopup",
+ "popuphidden thepopup",
+ ],
+ test(testname, step) {
+ synthesizeKey("KEY_ArrowDown");
+ synthesizeKey("KEY_Enter");
+ },
+ result(testname, step) {
+ checkClosed("trigger", testname);
+ },
+ },
+ {
+ testname: "focus trigger and key to open",
+ condition() {
+ return gIsMenu;
+ },
+ events: ["popupshowing thepopup", "popupshown thepopup"],
+ autohide: "thepopup",
+ test(testname, step) {
+ gTrigger.focus();
+ synthesizeKey(platformIsMac() ? " " : "KEY_F4");
+ },
+ result(testname, step) {
+ checkOpen("trigger", testname);
+ checkActive(gMenuPopup, "", testname);
+ },
+ },
+ {
+ // the menu should only open when the meta or alt key is not pressed
+ testname: "focus trigger and key wrong modifier",
+ condition() {
+ return gIsMenu;
+ },
+ test(testname, step) {
+ gTrigger.focus();
+ if (platformIsMac()) {
+ synthesizeKey("KEY_F4", { altKey: true });
+ } else {
+ synthesizeKey("", { metaKey: true });
+ }
+ },
+ result(testname, step) {
+ checkClosed("trigger", testname);
+ },
+ },
+ {
+ testname: "mouse click on disabled menu",
+ condition() {
+ return gIsMenu;
+ },
+ test(testname, step) {
+ gTrigger.setAttribute("disabled", "true");
+ synthesizeMouse(gTrigger, 4, 4, {});
+ },
+ result(testname, step) {
+ checkClosed("trigger", testname);
+ gTrigger.removeAttribute("disabled");
+ },
+ },
+ {
+ // openPopup using object as position argument
+ testname: "openPopup with object argument",
+ events: ["popupshowing thepopup 0000", "popupshown thepopup"],
+ autohide: "thepopup",
+ test(testname, step) {
+ gMenuPopup.openPopup(gTrigger, { position: "before_start", x: 5, y: 7 });
+ checkOpen("trigger", testname);
+ },
+ result(testname, step) {
+ var triggerrect = gTrigger.getBoundingClientRect();
+ var popuprect = gMenuPopup.getBoundingClientRect();
+ is(
+ Math.round(popuprect.left),
+ Math.round(triggerrect.left + 5),
+ testname + " x position "
+ );
+ is(
+ Math.round(popuprect.bottom),
+ Math.round(triggerrect.top + 7),
+ testname + " y position "
+ );
+ },
+ },
+ {
+ testname: "openPopup with object argument with event",
+ events: ["popupshowing thepopup 1000", "popupshown thepopup"],
+ autohide: "thepopup",
+ test(testname, step) {
+ gMenuPopup.openPopup(gTrigger, {
+ position: "after_start",
+ x: 0,
+ y: 0,
+ triggerEvent: new MouseEvent("mousedown", { altKey: true }),
+ });
+ checkOpen("trigger", testname);
+ },
+ },
+ {
+ testname: "openPopup with no arguments",
+ events: ["popupshowing thepopup", "popupshown thepopup"],
+ autohide: "thepopup",
+ test(testname, step) {
+ gMenuPopup.openPopup();
+ },
+ result(testname, step) {
+ let isMenu = gTrigger.type == "menu";
+ // With no arguments, open in default menu position
+ var triggerrect = gTrigger.getBoundingClientRect();
+ var popuprect = gMenuPopup.getBoundingClientRect();
+ is(
+ Math.round(popuprect.left),
+ isMenu ? Math.round(triggerrect.left) : 0,
+ testname + " x position "
+ );
+ is(
+ Math.round(popuprect.top),
+ isMenu ? Math.round(triggerrect.bottom) : 0,
+ testname + " y position "
+ );
+ },
+ },
+ {
+ // openPopup should open the menu synchronously, however popupshown
+ // is fired asynchronously
+ testname: "openPopup synchronous",
+ events: [
+ "popupshowing thepopup",
+ "popupshowing submenupopup",
+ "popupshown thepopup",
+ "DOMMenuItemActive submenu",
+ "popupshown submenupopup",
+ ],
+ test(testname, step) {
+ gMenuPopup.openPopup(gTrigger, "after_start", 0, 0, false, true);
+ document
+ .getElementById("submenupopup")
+ .openPopup(gTrigger, "end_before", 0, 0, false, true);
+ checkOpen("trigger", testname);
+ checkOpen("submenu", testname);
+ },
+ },
+ {
+ // remove the content nodes for the popup
+ testname: "remove content",
+ test(testname, step) {
+ var submenupopup = document.getElementById("submenupopup");
+ submenupopup.remove();
+ var popup = document.getElementById("thepopup");
+ popup.remove();
+ },
+ },
+];
+
+function platformIsMac() {
+ return navigator.platform.indexOf("Mac") > -1;
+}