/* 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; }