1204 lines
34 KiB
JavaScript
1204 lines
34 KiB
JavaScript
/* 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) {
|
|
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",
|
|
"overlap",
|
|
"topleft topleft",
|
|
"topcenter topleft",
|
|
"topright topleft",
|
|
"leftcenter topright",
|
|
"rightcenter topright",
|
|
"bottomleft bottomleft",
|
|
"bottomcenter bottomleft",
|
|
"bottomright bottomleft",
|
|
"topleft bottomright",
|
|
"bottomcenter bottomright",
|
|
"rightcenter topright",
|
|
"bottomcenter topcenter",
|
|
"rightcenter leftcenter",
|
|
],
|
|
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",
|
|
"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",
|
|
"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",
|
|
"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",
|
|
"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",
|
|
"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() {
|
|
// 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) {
|
|
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) {
|
|
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() {
|
|
// attribute is empty so does not override
|
|
gMenuPopup.setAttribute("position", "");
|
|
gMenuPopup.openPopup(gTrigger, "before_start", 5, 10, true, true);
|
|
},
|
|
result(testname) {
|
|
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() {
|
|
gMenuPopup.openPopup(null, "after_start", 6, 8, false);
|
|
},
|
|
result(testname) {
|
|
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() {
|
|
var item3 = document.getElementById("item3");
|
|
synthesizeMouse(item3, 4, 4, {});
|
|
},
|
|
result(testname) {
|
|
checkClosed("trigger", testname);
|
|
},
|
|
},
|
|
{
|
|
testname: "close popup",
|
|
condition() {
|
|
return false;
|
|
},
|
|
events: [
|
|
"popuphiding thepopup",
|
|
"popuphidden thepopup",
|
|
"DOMMenuInactive thepopup",
|
|
],
|
|
test() {
|
|
gMenuPopup.hidePopup();
|
|
},
|
|
},
|
|
{
|
|
testname: "open popup at screen",
|
|
events: ["popupshowing thepopup", "popupshown thepopup"],
|
|
test() {
|
|
gExpectedTriggerNode = "notset";
|
|
gMenuPopup.openPopupAtScreen(gScreenX + 24, gScreenY + 20, false);
|
|
},
|
|
result(testname) {
|
|
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() {
|
|
gExpectedTriggerNode = gCachedEvent.target;
|
|
gMenuPopup.openPopupAtScreen(
|
|
gScreenX + 8,
|
|
gScreenY + 16,
|
|
true,
|
|
gCachedEvent
|
|
);
|
|
},
|
|
result(testname) {
|
|
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() {
|
|
gMenuPopup.openPopupAtScreen(
|
|
gScreenX + 8,
|
|
gScreenY + 16,
|
|
true,
|
|
gCachedEvent2
|
|
);
|
|
},
|
|
},
|
|
{
|
|
testname: "open popup with open property",
|
|
events: ["popupshowing thepopup", "popupshown thepopup"],
|
|
test() {
|
|
openMenu(gTrigger);
|
|
},
|
|
result(testname) {
|
|
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() {
|
|
openMenu(document.getElementById("submenu"));
|
|
},
|
|
result(testname) {
|
|
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) {
|
|
checkClosed("trigger", testname);
|
|
checkClosed("submenu", testname);
|
|
},
|
|
},
|
|
{
|
|
testname: "open submenu with open property without parent open",
|
|
test() {
|
|
openMenu(document.getElementById("submenu"));
|
|
},
|
|
result(testname) {
|
|
checkClosed("trigger", testname);
|
|
checkClosed("submenu", testname);
|
|
},
|
|
},
|
|
{
|
|
testname: "open popup with open property and position",
|
|
condition() {
|
|
return gIsMenu;
|
|
},
|
|
events: ["popupshowing thepopup", "popupshown thepopup"],
|
|
test() {
|
|
gMenuPopup.setAttribute("position", "before_start");
|
|
openMenu(gTrigger);
|
|
},
|
|
result(testname) {
|
|
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() {
|
|
closeMenu(gTrigger, gMenuPopup);
|
|
},
|
|
result(testname) {
|
|
checkClosed("trigger", testname);
|
|
},
|
|
},
|
|
{
|
|
testname: "open popup with open property, position, anchor and alignment",
|
|
condition() {
|
|
return gIsMenu;
|
|
},
|
|
events: ["popupshowing thepopup", "popupshown thepopup"],
|
|
autohide: "thepopup",
|
|
test() {
|
|
gMenuPopup.setAttribute("position", "start_after");
|
|
gMenuPopup.setAttribute("popupanchor", "topright");
|
|
gMenuPopup.setAttribute("popupalign", "bottomright");
|
|
openMenu(gTrigger);
|
|
},
|
|
result(testname) {
|
|
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() {
|
|
gMenuPopup.removeAttribute("position");
|
|
gMenuPopup.setAttribute("popupanchor", "bottomright");
|
|
gMenuPopup.setAttribute("popupalign", "topright");
|
|
openMenu(gTrigger);
|
|
},
|
|
result(testname) {
|
|
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() {
|
|
gTrigger.focus();
|
|
synthesizeKey("KEY_ArrowDown", { altKey: !platformIsMac() });
|
|
},
|
|
result(testname) {
|
|
checkOpen("trigger", testname);
|
|
checkActive(gMenuPopup, "", testname);
|
|
},
|
|
},
|
|
{
|
|
testname: "focus and cursor up on trigger",
|
|
condition() {
|
|
return gIsMenu;
|
|
},
|
|
events: ["popupshowing thepopup", "popupshown thepopup"],
|
|
test() {
|
|
gTrigger.focus();
|
|
synthesizeKey("KEY_ArrowUp", { altKey: !platformIsMac() });
|
|
},
|
|
result(testname) {
|
|
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() {
|
|
synthesizeKey("KEY_ArrowDown");
|
|
synthesizeKey("KEY_Enter");
|
|
},
|
|
result(testname) {
|
|
checkClosed("trigger", testname);
|
|
},
|
|
},
|
|
{
|
|
testname: "focus trigger and key to open",
|
|
condition() {
|
|
return gIsMenu;
|
|
},
|
|
events: ["popupshowing thepopup", "popupshown thepopup"],
|
|
autohide: "thepopup",
|
|
test() {
|
|
gTrigger.focus();
|
|
synthesizeKey(platformIsMac() ? " " : "KEY_F4");
|
|
},
|
|
result(testname) {
|
|
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() {
|
|
gTrigger.focus();
|
|
if (platformIsMac()) {
|
|
synthesizeKey("KEY_F4", { altKey: true });
|
|
} else {
|
|
synthesizeKey("", { metaKey: true });
|
|
}
|
|
},
|
|
result(testname) {
|
|
checkClosed("trigger", testname);
|
|
},
|
|
},
|
|
{
|
|
testname: "mouse click on disabled menu",
|
|
condition() {
|
|
return gIsMenu;
|
|
},
|
|
test() {
|
|
gTrigger.setAttribute("disabled", "true");
|
|
synthesizeMouse(gTrigger, 4, 4, {});
|
|
},
|
|
result(testname) {
|
|
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) {
|
|
gMenuPopup.openPopup(gTrigger, { position: "before_start", x: 5, y: 7 });
|
|
checkOpen("trigger", testname);
|
|
},
|
|
result(testname) {
|
|
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) {
|
|
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() {
|
|
gMenuPopup.openPopup();
|
|
},
|
|
result(testname) {
|
|
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) {
|
|
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() {
|
|
var submenupopup = document.getElementById("submenupopup");
|
|
submenupopup.remove();
|
|
var popup = document.getElementById("thepopup");
|
|
popup.remove();
|
|
},
|
|
},
|
|
];
|
|
|
|
function platformIsMac() {
|
|
return navigator.platform.indexOf("Mac") > -1;
|
|
}
|