michael@0: var gMenuPopup = null; michael@0: var gTrigger = null; michael@0: var gIsMenu = false; michael@0: var gScreenX = -1, gScreenY = -1; michael@0: var gCachedEvent = null; michael@0: var gCachedEvent2 = null; michael@0: michael@0: function cacheEvent(modifiers) michael@0: { michael@0: var cachedEvent = null; michael@0: michael@0: var mouseFn = function(event) { michael@0: cachedEvent = event; michael@0: } michael@0: michael@0: window.addEventListener("mousedown", mouseFn, false); michael@0: synthesizeMouse(document.documentElement, 0, 0, modifiers); michael@0: window.removeEventListener("mousedown", mouseFn, false); michael@0: michael@0: return cachedEvent; michael@0: } michael@0: michael@0: function runTests() michael@0: { michael@0: if (screen.height < 768) { michael@0: ok(false, "popup tests are likely to fail for screen heights less than 768 pixels"); michael@0: } michael@0: michael@0: gMenuPopup = document.getElementById("thepopup"); michael@0: gTrigger = document.getElementById("trigger"); michael@0: michael@0: gIsMenu = gTrigger.boxObject instanceof Components.interfaces.nsIMenuBoxObject; michael@0: michael@0: // a hacky way to get the screen position of the document. Cache the event michael@0: // so that we can use it in calls to openPopup. michael@0: gCachedEvent = cacheEvent({ shiftKey: true }); michael@0: gScreenX = gCachedEvent.screenX; michael@0: gScreenY = gCachedEvent.screenY; michael@0: gCachedEvent2 = cacheEvent({ altKey: true, ctrlKey: true, shiftKey: true, metaKey: true }); michael@0: michael@0: startPopupTests(popupTests); michael@0: } michael@0: michael@0: var popupTests = [ michael@0: { michael@0: testname: "mouse click on trigger", michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: test: function() { michael@0: // for menus, no trigger will be set. For non-menus using the popup michael@0: // attribute, the trigger will be set to the node with the popup attribute michael@0: gExpectedTriggerNode = gIsMenu ? "notset" : gTrigger; michael@0: synthesizeMouse(gTrigger, 4, 4, { }); michael@0: }, michael@0: result: function (testname) { michael@0: gExpectedTriggerNode = null; michael@0: // menus are the anchor but non-menus are opened at screen coordinates michael@0: is(gMenuPopup.anchorNode, gIsMenu ? gTrigger : null, testname + " anchorNode"); michael@0: // menus are opened internally, but non-menus have a mouse event which michael@0: // triggered them michael@0: is(gMenuPopup.triggerNode, gIsMenu ? null : gTrigger, testname + " triggerNode"); michael@0: is(document.popupNode, gIsMenu ? null : gTrigger, testname + " document.popupNode"); michael@0: is(document.tooltipNode, null, testname + " document.tooltipNode"); michael@0: // check to ensure the popup node for a different document isn't used michael@0: if (window.opener) michael@0: is(window.opener.document.popupNode, null, testname + " opener.document.popupNode"); michael@0: michael@0: // this will be used in some tests to ensure the size doesn't change michael@0: var popuprect = gMenuPopup.getBoundingClientRect(); michael@0: gPopupWidth = Math.round(popuprect.width); michael@0: gPopupHeight = Math.round(popuprect.height); michael@0: michael@0: checkActive(gMenuPopup, "", testname); michael@0: checkOpen("trigger", testname); michael@0: // if a menu, the popup should be opened underneath the menu in the michael@0: // 'after_start' position, otherwise it is opened at the mouse position michael@0: if (gIsMenu) michael@0: compareEdge(gTrigger, gMenuPopup, "after_start", 0, 0, testname); michael@0: } michael@0: }, michael@0: { michael@0: // check that pressing cursor down while there is no selection michael@0: // highlights the first item michael@0: testname: "cursor down no selection", michael@0: events: [ "DOMMenuItemActive item1" ], michael@0: test: function() { synthesizeKey("VK_DOWN", { }); }, michael@0: result: function(testname) { checkActive(gMenuPopup, "item1", testname); } michael@0: }, michael@0: { michael@0: // check that pressing cursor up wraps and highlights the last item michael@0: testname: "cursor up wrap", michael@0: events: [ "DOMMenuItemInactive item1", "DOMMenuItemActive last" ], michael@0: test: function() { synthesizeKey("VK_UP", { }); }, michael@0: result: function(testname) { michael@0: checkActive(gMenuPopup, "last", testname); michael@0: } michael@0: }, michael@0: { michael@0: // check that pressing cursor down wraps and highlights the first item michael@0: testname: "cursor down wrap", michael@0: events: [ "DOMMenuItemInactive last", "DOMMenuItemActive item1" ], michael@0: test: function() { synthesizeKey("VK_DOWN", { }); }, michael@0: result: function(testname) { checkActive(gMenuPopup, "item1", testname); } michael@0: }, michael@0: { michael@0: // check that pressing cursor down highlights the second item michael@0: testname: "cursor down", michael@0: events: [ "DOMMenuItemInactive item1", "DOMMenuItemActive item2" ], michael@0: test: function() { synthesizeKey("VK_DOWN", { }); }, michael@0: result: function(testname) { checkActive(gMenuPopup, "item2", testname); } michael@0: }, michael@0: { michael@0: // check that pressing cursor up highlights the second item michael@0: testname: "cursor up", michael@0: events: [ "DOMMenuItemInactive item2", "DOMMenuItemActive item1" ], michael@0: test: function() { synthesizeKey("VK_UP", { }); }, michael@0: result: function(testname) { checkActive(gMenuPopup, "item1", testname); } michael@0: }, michael@0: { michael@0: // cursor left should not do anything michael@0: testname: "cursor left", michael@0: test: function() { synthesizeKey("VK_LEFT", { }); }, michael@0: result: function(testname) { checkActive(gMenuPopup, "item1", testname); } michael@0: }, michael@0: { michael@0: // cursor right should not do anything michael@0: testname: "cursor right", michael@0: test: function() { synthesizeKey("VK_RIGHT", { }); }, michael@0: result: function(testname) { checkActive(gMenuPopup, "item1", testname); } michael@0: }, michael@0: { michael@0: // check cursor down when a disabled item exists in the menu michael@0: testname: "cursor down disabled", michael@0: events: function() { michael@0: // On Windows, disabled items are included when navigating, but on michael@0: // other platforms, disabled items are skipped over michael@0: if (navigator.platform.indexOf("Win") == 0) michael@0: return [ "DOMMenuItemInactive item1", "DOMMenuItemActive item2" ]; michael@0: else michael@0: return [ "DOMMenuItemInactive item1", "DOMMenuItemActive amenu" ]; michael@0: }, michael@0: test: function() { michael@0: document.getElementById("item2").disabled = true; michael@0: synthesizeKey("VK_DOWN", { }); michael@0: } michael@0: }, michael@0: { michael@0: // check cursor up when a disabled item exists in the menu michael@0: testname: "cursor up disabled", michael@0: events: function() { michael@0: if (navigator.platform.indexOf("Win") == 0) michael@0: return [ "DOMMenuItemInactive item2", "DOMMenuItemActive amenu", michael@0: "DOMMenuItemInactive amenu", "DOMMenuItemActive item2", michael@0: "DOMMenuItemInactive item2", "DOMMenuItemActive item1" ]; michael@0: else michael@0: return [ "DOMMenuItemInactive amenu", "DOMMenuItemActive item1" ]; michael@0: }, michael@0: test: function() { michael@0: if (navigator.platform.indexOf("Win") == 0) michael@0: synthesizeKey("VK_DOWN", { }); michael@0: synthesizeKey("VK_UP", { }); michael@0: if (navigator.platform.indexOf("Win") == 0) michael@0: synthesizeKey("VK_UP", { }); michael@0: } michael@0: }, michael@0: { michael@0: testname: "mouse click outside", michael@0: events: [ "popuphiding thepopup", "popuphidden thepopup", michael@0: "DOMMenuItemInactive item1", "DOMMenuInactive thepopup" ], michael@0: test: function() { michael@0: gMenuPopup.hidePopup(); michael@0: // XXXndeakin event simulation fires events outside of the platform specific michael@0: // widget code so the popup capturing isn't handled. Thus, the menu won't michael@0: // rollup this way. michael@0: // synthesizeMouse(gTrigger, 0, -12, { }); michael@0: }, michael@0: result: function(testname, step) { michael@0: is(gMenuPopup.anchorNode, null, testname + " anchorNode"); michael@0: is(gMenuPopup.triggerNode, null, testname + " triggerNode"); michael@0: is(document.popupNode, null, testname + " document.popupNode"); michael@0: checkClosed("trigger", testname); michael@0: } michael@0: }, michael@0: { michael@0: // these tests check to ensure that passing an anchor and position michael@0: // puts the popup in the right place michael@0: testname: "open popup anchored", michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: autohide: "thepopup", michael@0: steps: ["before_start", "before_end", "after_start", "after_end", michael@0: "start_before", "start_after", "end_before", "end_after", "after_pointer", "overlap", michael@0: "topleft topleft", "topcenter topleft", "topright topleft", michael@0: "leftcenter topright", "rightcenter topright", michael@0: "bottomleft bottomleft", "bottomcenter bottomleft", "bottomright bottomleft", michael@0: "topleft bottomright", "bottomcenter bottomright", "rightcenter topright"], michael@0: test: function(testname, step) { michael@0: gExpectedTriggerNode = "notset"; michael@0: gMenuPopup.openPopup(gTrigger, step, 0, 0, false, false); michael@0: }, michael@0: result: function(testname, step) { michael@0: // no triggerNode because it was opened without passing an event michael@0: gExpectedTriggerNode = null; michael@0: is(gMenuPopup.anchorNode, gTrigger, testname + " anchorNode"); michael@0: is(gMenuPopup.triggerNode, null, testname + " triggerNode"); michael@0: is(document.popupNode, null, testname + " document.popupNode"); michael@0: compareEdge(gTrigger, gMenuPopup, step, 0, 0, testname); michael@0: } michael@0: }, michael@0: { michael@0: // these tests check the same but with a 10 pixel margin on the popup michael@0: testname: "open popup anchored with margin", michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: autohide: "thepopup", michael@0: steps: ["before_start", "before_end", "after_start", "after_end", michael@0: "start_before", "start_after", "end_before", "end_after", "after_pointer", "overlap", michael@0: "topleft topleft", "topcenter topleft", "topright topleft", michael@0: "leftcenter topright", "rightcenter topright", michael@0: "bottomleft bottomleft", "bottomcenter bottomleft", "bottomright bottomleft", michael@0: "topleft bottomright", "bottomcenter bottomright", "rightcenter topright"], michael@0: test: function(testname, step) { michael@0: gMenuPopup.setAttribute("style", "margin: 10px;"); michael@0: gMenuPopup.openPopup(gTrigger, step, 0, 0, false, false); michael@0: }, michael@0: result: function(testname, step) { michael@0: var rightmod = step == "before_end" || step == "after_end" || michael@0: step == "start_before" || step == "start_after" || michael@0: step.match(/topright$/) || step.match(/bottomright$/); michael@0: var bottommod = step == "before_start" || step == "before_end" || michael@0: step == "start_after" || step == "end_after" || michael@0: step.match(/bottomleft$/) || step.match(/bottomright$/); michael@0: compareEdge(gTrigger, gMenuPopup, step, rightmod ? -10 : 10, bottommod ? -10 : 10, testname); michael@0: gMenuPopup.removeAttribute("style"); michael@0: } michael@0: }, michael@0: { michael@0: // these tests check the same but with a -8 pixel margin on the popup michael@0: testname: "open popup anchored with negative margin", michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: autohide: "thepopup", michael@0: steps: ["before_start", "before_end", "after_start", "after_end", michael@0: "start_before", "start_after", "end_before", "end_after", "after_pointer", "overlap"], michael@0: test: function(testname, step) { michael@0: gMenuPopup.setAttribute("style", "margin: -8px;"); michael@0: gMenuPopup.openPopup(gTrigger, step, 0, 0, false, false); michael@0: }, michael@0: result: function(testname, step) { michael@0: var rightmod = step == "before_end" || step == "after_end" || michael@0: step == "start_before" || step == "start_after"; michael@0: var bottommod = step == "before_start" || step == "before_end" || michael@0: step == "start_after" || step == "end_after"; michael@0: compareEdge(gTrigger, gMenuPopup, step, rightmod ? 8 : -8, bottommod ? 8 : -8, testname); michael@0: gMenuPopup.removeAttribute("style"); michael@0: } michael@0: }, michael@0: { michael@0: testname: "open popup with large positive margin", michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: autohide: "thepopup", michael@0: steps: ["before_start", "before_end", "after_start", "after_end", michael@0: "start_before", "start_after", "end_before", "end_after", "after_pointer", "overlap"], michael@0: test: function(testname, step) { michael@0: gMenuPopup.setAttribute("style", "margin: 1000px;"); michael@0: gMenuPopup.openPopup(gTrigger, step, 0, 0, false, false); michael@0: }, michael@0: result: function(testname, step) { michael@0: var popuprect = gMenuPopup.getBoundingClientRect(); michael@0: // as there is more room on the 'end' or 'after' side, popups will always michael@0: // appear on the right or bottom corners, depending on which side they are michael@0: // allowed to be flipped by. michael@0: var expectedleft = step == "before_end" || step == "after_end" ? michael@0: 0 : Math.round(window.innerWidth - gPopupWidth); michael@0: var expectedtop = step == "start_after" || step == "end_after" ? michael@0: 0 : Math.round(window.innerHeight - gPopupHeight); michael@0: is(Math.round(popuprect.left), expectedleft, testname + " x position " + step); michael@0: is(Math.round(popuprect.top), expectedtop, testname + " y position " + step); michael@0: gMenuPopup.removeAttribute("style"); michael@0: } michael@0: }, michael@0: { michael@0: testname: "open popup with large negative margin", michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: autohide: "thepopup", michael@0: steps: ["before_start", "before_end", "after_start", "after_end", michael@0: "start_before", "start_after", "end_before", "end_after", "after_pointer", "overlap"], michael@0: test: function(testname, step) { michael@0: gMenuPopup.setAttribute("style", "margin: -1000px;"); michael@0: gMenuPopup.openPopup(gTrigger, step, 0, 0, false, false); michael@0: }, michael@0: result: function(testname, step) { michael@0: var popuprect = gMenuPopup.getBoundingClientRect(); michael@0: // using negative margins causes the reverse of positive margins, and michael@0: // popups will appear on the left or top corners. michael@0: var expectedleft = step == "before_end" || step == "after_end" ? michael@0: Math.round(window.innerWidth - gPopupWidth) : 0; michael@0: var expectedtop = step == "start_after" || step == "end_after" ? michael@0: Math.round(window.innerHeight - gPopupHeight) : 0; michael@0: is(Math.round(popuprect.left), expectedleft, testname + " x position " + step); michael@0: is(Math.round(popuprect.top), expectedtop, testname + " y position " + step); michael@0: gMenuPopup.removeAttribute("style"); michael@0: } michael@0: }, michael@0: { michael@0: testname: "popup with unknown step", michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: autohide: "thepopup", michael@0: test: function() { michael@0: gMenuPopup.openPopup(gTrigger, "other", 0, 0, false, false); michael@0: }, michael@0: result: function (testname) { michael@0: var triggerrect = gMenuPopup.getBoundingClientRect(); michael@0: var popuprect = gMenuPopup.getBoundingClientRect(); michael@0: is(Math.round(popuprect.left), triggerrect.left, testname + " x position "); michael@0: is(Math.round(popuprect.top), triggerrect.top, testname + " y position "); michael@0: } michael@0: }, michael@0: { michael@0: // these tests check to ensure that the position attribute can be used michael@0: // to set the position of a popup instead of passing it as an argument michael@0: testname: "open popup anchored with attribute", michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: autohide: "thepopup", michael@0: steps: ["before_start", "before_end", "after_start", "after_end", michael@0: "start_before", "start_after", "end_before", "end_after", "after_pointer", "overlap", michael@0: "topcenter topleft", "topright bottomright", "leftcenter topright"], michael@0: test: function(testname, step) { michael@0: gMenuPopup.setAttribute("position", step); michael@0: gMenuPopup.openPopup(gTrigger, "", 0, 0, false, false); michael@0: }, michael@0: result: function(testname, step) { compareEdge(gTrigger, gMenuPopup, step, 0, 0, testname); } michael@0: }, michael@0: { michael@0: // this test checks to ensure that the attributes override flag to openPopup michael@0: // can be used to override the popup's position. This test also passes an michael@0: // event to openPopup to check the trigger node. michael@0: testname: "open popup anchored with override", michael@0: events: [ "popupshowing thepopup 0010", "popupshown thepopup" ], michael@0: test: function(testname, step) { michael@0: // attribute overrides the position passed in michael@0: gMenuPopup.setAttribute("position", "end_after"); michael@0: gExpectedTriggerNode = gCachedEvent.target; michael@0: gMenuPopup.openPopup(gTrigger, "before_start", 0, 0, false, true, gCachedEvent); michael@0: }, michael@0: result: function(testname, step) { michael@0: gExpectedTriggerNode = null; michael@0: is(gMenuPopup.anchorNode, gTrigger, testname + " anchorNode"); michael@0: is(gMenuPopup.triggerNode, gCachedEvent.target, testname + " triggerNode"); michael@0: is(document.popupNode, gCachedEvent.target, testname + " document.popupNode"); michael@0: compareEdge(gTrigger, gMenuPopup, "end_after", 0, 0, testname); michael@0: } michael@0: }, michael@0: { michael@0: testname: "close popup with escape", michael@0: events: [ "popuphiding thepopup", "popuphidden thepopup", michael@0: "DOMMenuInactive thepopup", ], michael@0: test: function(testname, step) { michael@0: synthesizeKey("VK_ESCAPE", { }); michael@0: checkClosed("trigger", testname); michael@0: } michael@0: }, michael@0: { michael@0: // check that offsets may be supplied to the openPopup method michael@0: testname: "open popup anchored with offsets", michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: autohide: "thepopup", michael@0: test: function(testname, step) { michael@0: // attribute is empty so does not override michael@0: gMenuPopup.setAttribute("position", ""); michael@0: gMenuPopup.openPopup(gTrigger, "before_start", 5, 10, true, true); michael@0: }, michael@0: result: function(testname, step) { compareEdge(gTrigger, gMenuPopup, "before_start", 5, 10, testname); } michael@0: }, michael@0: { michael@0: // these tests check to ensure that passing an anchor and position michael@0: // puts the popup in the right place michael@0: testname: "show popup anchored", michael@0: condition: function() { michael@0: // only perform this test for popups not in a menu, such as those using michael@0: // the popup attribute, as the showPopup implementation in popup.xml michael@0: // calls openMenu if the popup is inside a menu michael@0: return !gIsMenu; michael@0: }, michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: autohide: "thepopup", michael@0: steps: [["topleft", "topleft"], michael@0: ["topleft", "topright"], ["topleft", "bottomleft"], michael@0: ["topright", "topleft"], ["topright", "bottomright"], michael@0: ["bottomleft", "bottomright"], ["bottomleft", "topleft"], michael@0: ["bottomright", "bottomleft"], ["bottomright", "topright"]], michael@0: test: function(testname, step) { michael@0: // the attributes should be ignored michael@0: gMenuPopup.setAttribute("popupanchor", "topright"); michael@0: gMenuPopup.setAttribute("popupalign", "bottomright"); michael@0: gMenuPopup.setAttribute("position", "end_after"); michael@0: gMenuPopup.showPopup(gTrigger, -1, -1, "popup", step[0], step[1]); michael@0: }, michael@0: result: function(testname, step) { michael@0: var pos = convertPosition(step[0], step[1]); michael@0: compareEdge(gTrigger, gMenuPopup, pos, 0, 0, testname); michael@0: gMenuPopup.removeAttribute("popupanchor"); michael@0: gMenuPopup.removeAttribute("popupalign"); michael@0: gMenuPopup.removeAttribute("position"); michael@0: } michael@0: }, michael@0: { michael@0: testname: "show popup with position", michael@0: condition: function() { return !gIsMenu; }, michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: autohide: "thepopup", michael@0: test: function(testname, step) { michael@0: gMenuPopup.showPopup(gTrigger, gScreenX + 60, gScreenY + 15, michael@0: "context", "topleft", "bottomright"); michael@0: }, michael@0: result: function(testname, step) { michael@0: var rect = gMenuPopup.getBoundingClientRect(); michael@0: ok(true, gScreenX + "," + gScreenY); michael@0: is(rect.left, 60, testname + " left"); michael@0: is(rect.top, 15, testname + " top"); michael@0: ok(rect.right, testname + " right is " + rect.right); michael@0: ok(rect.bottom, testname + " bottom is " + rect.bottom); michael@0: } michael@0: }, michael@0: { michael@0: // if no anchor is supplied to openPopup, it should be opened relative michael@0: // to the viewport. michael@0: testname: "open popup unanchored", michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: test: function(testname, step) { gMenuPopup.openPopup(null, "after_start", 6, 8, false); }, michael@0: result: function(testname, step) { michael@0: var rect = gMenuPopup.getBoundingClientRect(); michael@0: ok(rect.left == 6 && rect.top == 8 && rect.right && rect.bottom, testname); michael@0: } michael@0: }, michael@0: { michael@0: testname: "activate menuitem with mouse", michael@0: events: [ "DOMMenuInactive thepopup", "command item3", michael@0: "popuphiding thepopup", "popuphidden thepopup", michael@0: "DOMMenuItemInactive item3" ], michael@0: test: function(testname, step) { michael@0: var item3 = document.getElementById("item3"); michael@0: synthesizeMouse(item3, 4, 4, { }); michael@0: }, michael@0: result: function(testname, step) { checkClosed("trigger", testname); } michael@0: }, michael@0: { michael@0: testname: "close popup", michael@0: condition: function() { return false; }, michael@0: events: [ "popuphiding thepopup", "popuphidden thepopup", michael@0: "DOMMenuInactive thepopup" ], michael@0: test: function(testname, step) { gMenuPopup.hidePopup(); } michael@0: }, michael@0: { michael@0: testname: "open popup at screen", michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: test: function(testname, step) { michael@0: gExpectedTriggerNode = "notset"; michael@0: gMenuPopup.openPopupAtScreen(gScreenX + 24, gScreenY + 20, false); michael@0: }, michael@0: result: function(testname, step) { michael@0: gExpectedTriggerNode = null; michael@0: is(gMenuPopup.anchorNode, null, testname + " anchorNode"); michael@0: is(gMenuPopup.triggerNode, null, testname + " triggerNode"); michael@0: is(document.popupNode, null, testname + " document.popupNode"); michael@0: var rect = gMenuPopup.getBoundingClientRect(); michael@0: is(rect.left, 24, testname + " left"); michael@0: is(rect.top, 20, testname + " top"); michael@0: ok(rect.right, testname + " right is " + rect.right); michael@0: ok(rect.bottom, testname + " bottom is " + rect.bottom); michael@0: } michael@0: }, michael@0: { michael@0: // check that pressing a menuitem's accelerator selects it. Note that michael@0: // the menuitem with the M accesskey overrides the earlier menuitem that michael@0: // begins with M. michael@0: testname: "menuitem accelerator", michael@0: events: [ "DOMMenuItemActive amenu", "DOMMenuItemInactive amenu", michael@0: "DOMMenuInactive thepopup", michael@0: "command amenu", "popuphiding thepopup", "popuphidden thepopup", michael@0: "DOMMenuItemInactive amenu" michael@0: ], michael@0: test: function() { synthesizeKey("M", { }); }, michael@0: result: function(testname) { checkClosed("trigger", testname); } michael@0: }, michael@0: { michael@0: testname: "open context popup at screen", michael@0: events: [ "popupshowing thepopup 0010", "popupshown thepopup" ], michael@0: test: function(testname, step) { michael@0: gExpectedTriggerNode = gCachedEvent.target; michael@0: gMenuPopup.openPopupAtScreen(gScreenX + 8, gScreenY + 16, true, gCachedEvent); michael@0: }, michael@0: result: function(testname, step) { michael@0: gExpectedTriggerNode = null; michael@0: is(gMenuPopup.anchorNode, null, testname + " anchorNode"); michael@0: is(gMenuPopup.triggerNode, gCachedEvent.target, testname + " triggerNode"); michael@0: is(document.popupNode, gCachedEvent.target, testname + " document.popupNode"); michael@0: michael@0: var childframe = document.getElementById("childframe"); michael@0: if (childframe) { michael@0: for (var t = 0; t < 2; t++) { michael@0: var child = childframe.contentDocument; michael@0: var evt = child.createEvent("Event"); michael@0: evt.initEvent("click", true, true); michael@0: child.documentElement.dispatchEvent(evt); michael@0: is(child.documentElement.getAttribute("data"), "xnull", michael@0: "cannot get popupNode from other document"); michael@0: child.documentElement.setAttribute("data", "none"); michael@0: // now try again with document.popupNode set explicitly michael@0: document.popupNode = gCachedEvent.target; michael@0: } michael@0: } michael@0: michael@0: var rect = gMenuPopup.getBoundingClientRect(); michael@0: is(rect.left, 10, testname + " left"); michael@0: is(rect.top, 18, testname + " top"); michael@0: ok(rect.right, testname + " right is " + rect.right); michael@0: ok(rect.bottom, testname + " bottom is " + rect.bottom); michael@0: } michael@0: }, michael@0: { michael@0: // pressing a letter that doesn't correspond to an accelerator, but does michael@0: // correspond to the first letter in a menu's label. The menu should not michael@0: // close because there is more than one item corresponding to that letter michael@0: testname: "menuitem with non accelerator", michael@0: events: [ "DOMMenuItemActive one" ], michael@0: test: function() { synthesizeKey("O", { }); }, michael@0: result: function(testname) { michael@0: checkOpen("trigger", testname); michael@0: checkActive(gMenuPopup, "one", testname); michael@0: } michael@0: }, michael@0: { michael@0: // pressing the letter again should select the next one that starts with michael@0: // that letter michael@0: testname: "menuitem with non accelerator again", michael@0: events: [ "DOMMenuItemInactive one", "DOMMenuItemActive submenu" ], michael@0: test: function() { synthesizeKey("O", { }); }, michael@0: result: function(testname) { michael@0: // 'submenu' is a menu but it should not be open michael@0: checkOpen("trigger", testname); michael@0: checkClosed("submenu", testname); michael@0: checkActive(gMenuPopup, "submenu", testname); michael@0: } michael@0: }, michael@0: { michael@0: // open the submenu with the cursor right key michael@0: testname: "open submenu with cursor right", michael@0: events: [ "popupshowing submenupopup", "DOMMenuItemActive submenuitem", michael@0: "popupshown submenupopup" ], michael@0: test: function() { synthesizeKey("VK_RIGHT", { }); }, michael@0: result: function(testname) { michael@0: checkOpen("trigger", testname); michael@0: checkOpen("submenu", testname); michael@0: checkActive(gMenuPopup, "submenu", testname); michael@0: checkActive(document.getElementById("submenupopup"), "submenuitem", testname); michael@0: } michael@0: }, michael@0: { michael@0: // close the submenu with the cursor left key michael@0: testname: "close submenu with cursor left", michael@0: events: [ "popuphiding submenupopup", "popuphidden submenupopup", michael@0: "DOMMenuItemInactive submenuitem", "DOMMenuInactive submenupopup", michael@0: "DOMMenuItemActive submenu" ], michael@0: test: function() { synthesizeKey("VK_LEFT", { }); }, michael@0: result: function(testname) { michael@0: checkOpen("trigger", testname); michael@0: checkClosed("submenu", testname); michael@0: checkActive(gMenuPopup, "submenu", testname); michael@0: checkActive(document.getElementById("submenupopup"), "", testname); michael@0: } michael@0: }, michael@0: { michael@0: // open the submenu with the enter key michael@0: testname: "open submenu with enter", michael@0: events: [ "popupshowing submenupopup", "DOMMenuItemActive submenuitem", michael@0: "popupshown submenupopup" ], michael@0: test: function() { synthesizeKey("VK_RETURN", { }); }, michael@0: result: function(testname) { michael@0: checkOpen("trigger", testname); michael@0: checkOpen("submenu", testname); michael@0: checkActive(gMenuPopup, "submenu", testname); michael@0: checkActive(document.getElementById("submenupopup"), "submenuitem", testname); michael@0: } michael@0: }, michael@0: { michael@0: // close the submenu with the escape key michael@0: testname: "close submenu with escape", michael@0: events: [ "popuphiding submenupopup", "popuphidden submenupopup", michael@0: "DOMMenuItemInactive submenuitem", "DOMMenuInactive submenupopup", michael@0: "DOMMenuItemActive submenu" ], michael@0: test: function() { synthesizeKey("VK_ESCAPE", { }); }, michael@0: result: function(testname) { michael@0: checkOpen("trigger", testname); michael@0: checkClosed("submenu", testname); michael@0: checkActive(gMenuPopup, "submenu", testname); michael@0: checkActive(document.getElementById("submenupopup"), "", testname); michael@0: } michael@0: }, michael@0: { michael@0: // pressing the letter again when the next item is disabled should still michael@0: // select the disabled item on Windows, but select the next item on other michael@0: // platforms michael@0: testname: "menuitem with non accelerator disabled", michael@0: events: function() { michael@0: if (navigator.platform.indexOf("Win") == 0) michael@0: return [ "DOMMenuItemInactive submenu", "DOMMenuItemActive other", michael@0: "DOMMenuItemInactive other", "DOMMenuItemActive item1" ]; michael@0: else michael@0: return [ "DOMMenuItemInactive submenu", "DOMMenuItemActive last", michael@0: "DOMMenuItemInactive last", "DOMMenuItemActive item1" ]; michael@0: }, michael@0: test: function() { synthesizeKey("O", { }); synthesizeKey("F", { }); }, michael@0: result: function(testname) { michael@0: checkActive(gMenuPopup, "item1", testname); michael@0: } michael@0: }, michael@0: { michael@0: // pressing a letter that doesn't correspond to an accelerator nor the michael@0: // first letter of a menu. This should have no effect. michael@0: testname: "menuitem with keypress no accelerator found", michael@0: test: function() { synthesizeKey("G", { }); }, michael@0: result: function(testname) { michael@0: checkOpen("trigger", testname); michael@0: checkActive(gMenuPopup, "item1", testname); michael@0: } michael@0: }, michael@0: { michael@0: // when only one menuitem starting with that letter exists, it should be michael@0: // selected and the menu closed michael@0: testname: "menuitem with non accelerator single", michael@0: events: [ "DOMMenuItemInactive item1", "DOMMenuItemActive amenu", michael@0: "DOMMenuItemInactive amenu", "DOMMenuInactive thepopup", michael@0: "command amenu", "popuphiding thepopup", "popuphidden thepopup", michael@0: "DOMMenuItemInactive amenu", michael@0: ], michael@0: test: function() { synthesizeKey("M", { }); }, michael@0: result: function(testname) { michael@0: checkClosed("trigger", testname); michael@0: checkActive(gMenuPopup, "", testname); michael@0: } michael@0: }, michael@0: { michael@0: testname: "open context popup at screen with all modifiers set", michael@0: events: [ "popupshowing thepopup 1111", "popupshown thepopup" ], michael@0: autohide: "thepopup", michael@0: test: function(testname, step) { michael@0: gMenuPopup.openPopupAtScreen(gScreenX + 8, gScreenY + 16, true, gCachedEvent2); michael@0: } michael@0: }, michael@0: { michael@0: testname: "open popup with open property", michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: test: function(testname, step) { openMenu(gTrigger); }, michael@0: result: function(testname, step) { michael@0: checkOpen("trigger", testname); michael@0: if (gIsMenu) michael@0: compareEdge(gTrigger, gMenuPopup, "after_start", 0, 0, testname); michael@0: } michael@0: }, michael@0: { michael@0: testname: "open submenu with open property", michael@0: events: [ "popupshowing submenupopup", "DOMMenuItemActive submenu", michael@0: "popupshown submenupopup" ], michael@0: test: function(testname, step) { openMenu(document.getElementById("submenu")); }, michael@0: result: function(testname, step) { michael@0: checkOpen("trigger", testname); michael@0: checkOpen("submenu", testname); michael@0: // XXXndeakin michael@0: // getBoundingClientRect doesn't seem to working right for submenus michael@0: // so disable this test for now michael@0: // compareEdge(document.getElementById("submenu"), michael@0: // document.getElementById("submenupopup"), "end_before", 0, 0, testname); michael@0: } michael@0: }, michael@0: { michael@0: testname: "hidePopup hides entire chain", michael@0: events: [ "popuphiding submenupopup", "popuphidden submenupopup", michael@0: "popuphiding thepopup", "popuphidden thepopup", michael@0: "DOMMenuInactive submenupopup", michael@0: "DOMMenuItemInactive submenu", "DOMMenuItemInactive submenu", michael@0: "DOMMenuInactive thepopup", ], michael@0: test: function() { gMenuPopup.hidePopup(); }, michael@0: result: function(testname, step) { michael@0: checkClosed("trigger", testname); michael@0: checkClosed("submenu", testname); michael@0: } michael@0: }, michael@0: { michael@0: testname: "open submenu with open property without parent open", michael@0: test: function(testname, step) { openMenu(document.getElementById("submenu")); }, michael@0: result: function(testname, step) { michael@0: checkClosed("trigger", testname); michael@0: checkClosed("submenu", testname); michael@0: } michael@0: }, michael@0: { michael@0: testname: "open popup with open property and position", michael@0: condition: function() { return gIsMenu; }, michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: test: function(testname, step) { michael@0: gMenuPopup.setAttribute("position", "before_start"); michael@0: openMenu(gTrigger); michael@0: }, michael@0: result: function(testname, step) { michael@0: compareEdge(gTrigger, gMenuPopup, "before_start", 0, 0, testname); michael@0: } michael@0: }, michael@0: { michael@0: testname: "close popup with open property", michael@0: condition: function() { return gIsMenu; }, michael@0: events: [ "popuphiding thepopup", "popuphidden thepopup", michael@0: "DOMMenuInactive thepopup" ], michael@0: test: function(testname, step) { closeMenu(gTrigger, gMenuPopup); }, michael@0: result: function(testname, step) { checkClosed("trigger", testname); } michael@0: }, michael@0: { michael@0: testname: "open popup with open property, position, anchor and alignment", michael@0: condition: function() { return gIsMenu; }, michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: autohide: "thepopup", michael@0: test: function(testname, step) { michael@0: gMenuPopup.setAttribute("position", "start_after"); michael@0: gMenuPopup.setAttribute("popupanchor", "topright"); michael@0: gMenuPopup.setAttribute("popupalign", "bottomright"); michael@0: openMenu(gTrigger); michael@0: }, michael@0: result: function(testname, step) { michael@0: compareEdge(gTrigger, gMenuPopup, "start_after", 0, 0, testname); michael@0: } michael@0: }, michael@0: { michael@0: testname: "open popup with open property, anchor and alignment", michael@0: condition: function() { return gIsMenu; }, michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: autohide: "thepopup", michael@0: test: function(testname, step) { michael@0: gMenuPopup.removeAttribute("position"); michael@0: gMenuPopup.setAttribute("popupanchor", "bottomright"); michael@0: gMenuPopup.setAttribute("popupalign", "topright"); michael@0: openMenu(gTrigger); michael@0: }, michael@0: result: function(testname, step) { michael@0: compareEdge(gTrigger, gMenuPopup, "after_end", 0, 0, testname); michael@0: gMenuPopup.removeAttribute("popupanchor"); michael@0: gMenuPopup.removeAttribute("popupalign"); michael@0: } michael@0: }, michael@0: { michael@0: testname: "focus and cursor down on trigger", michael@0: condition: function() { return gIsMenu; }, michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: autohide: "thepopup", michael@0: test: function(testname, step) { michael@0: gTrigger.focus(); michael@0: synthesizeKey("VK_DOWN", { altKey: (navigator.platform.indexOf("Mac") == -1) }); michael@0: }, michael@0: result: function(testname, step) { michael@0: checkOpen("trigger", testname); michael@0: checkActive(gMenuPopup, "", testname); michael@0: } michael@0: }, michael@0: { michael@0: testname: "focus and cursor up on trigger", michael@0: condition: function() { return gIsMenu; }, michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: test: function(testname, step) { michael@0: gTrigger.focus(); michael@0: synthesizeKey("VK_UP", { altKey: (navigator.platform.indexOf("Mac") == -1) }); michael@0: }, michael@0: result: function(testname, step) { michael@0: checkOpen("trigger", testname); michael@0: checkActive(gMenuPopup, "", testname); michael@0: } michael@0: }, michael@0: { michael@0: testname: "select and enter on menuitem", michael@0: condition: function() { return gIsMenu; }, michael@0: events: [ "DOMMenuItemActive item1", "DOMMenuItemInactive item1", michael@0: "DOMMenuInactive thepopup", "command item1", michael@0: "popuphiding thepopup", "popuphidden thepopup", michael@0: "DOMMenuItemInactive item1" ], michael@0: test: function(testname, step) { michael@0: synthesizeKey("VK_DOWN", { }); michael@0: synthesizeKey("VK_RETURN", { }); michael@0: }, michael@0: result: function(testname, step) { checkClosed("trigger", testname); } michael@0: }, michael@0: { michael@0: testname: "focus trigger and key to open", michael@0: condition: function() { return gIsMenu; }, michael@0: events: [ "popupshowing thepopup", "popupshown thepopup" ], michael@0: autohide: "thepopup", michael@0: test: function(testname, step) { michael@0: gTrigger.focus(); michael@0: synthesizeKey((navigator.platform.indexOf("Mac") == -1) ? "VK_F4" : " ", { }); michael@0: }, michael@0: result: function(testname, step) { michael@0: checkOpen("trigger", testname); michael@0: checkActive(gMenuPopup, "", testname); michael@0: } michael@0: }, michael@0: { michael@0: // the menu should only open when the meta or alt key is not pressed michael@0: testname: "focus trigger and key wrong modifier", michael@0: condition: function() { return gIsMenu; }, michael@0: test: function(testname, step) { michael@0: gTrigger.focus(); michael@0: if (navigator.platform.indexOf("Mac") == -1) michael@0: synthesizeKey("", { metaKey: true }); michael@0: else michael@0: synthesizeKey("VK_F4", { altKey: true }); michael@0: }, michael@0: result: function(testname, step) { michael@0: checkClosed("trigger", testname); michael@0: } michael@0: }, michael@0: { michael@0: testname: "mouse click on disabled menu", michael@0: condition: function() { return gIsMenu; }, michael@0: test: function(testname, step) { michael@0: gTrigger.setAttribute("disabled", "true"); michael@0: synthesizeMouse(gTrigger, 4, 4, { }); michael@0: }, michael@0: result: function(testname, step) { michael@0: checkClosed("trigger", testname); michael@0: gTrigger.removeAttribute("disabled"); michael@0: } michael@0: }, michael@0: { michael@0: // openPopup should open the menu synchronously, however popupshown michael@0: // is fired asynchronously michael@0: testname: "openPopup synchronous", michael@0: events: [ "popupshowing thepopup", "popupshowing submenupopup", michael@0: "popupshown thepopup", "DOMMenuItemActive submenu", michael@0: "popupshown submenupopup" ], michael@0: test: function(testname, step) { michael@0: gMenuPopup.openPopup(gTrigger, "after_start", 0, 0, false, true); michael@0: document.getElementById("submenupopup"). michael@0: openPopup(gTrigger, "end_before", 0, 0, false, true); michael@0: checkOpen("trigger", testname); michael@0: checkOpen("submenu", testname); michael@0: } michael@0: }, michael@0: { michael@0: // remove the content nodes for the popup michael@0: testname: "remove content", michael@0: test: function(testname, step) { michael@0: var submenupopup = document.getElementById("submenupopup"); michael@0: submenupopup.parentNode.removeChild(submenupopup); michael@0: var popup = document.getElementById("thepopup"); michael@0: popup.parentNode.removeChild(popup); michael@0: } michael@0: } michael@0: michael@0: ];