summaryrefslogtreecommitdiffstats
path: root/toolkit/content/tests/chrome/window_largemenu.xhtml
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/content/tests/chrome/window_largemenu.xhtml')
-rw-r--r--toolkit/content/tests/chrome/window_largemenu.xhtml430
1 files changed, 430 insertions, 0 deletions
diff --git a/toolkit/content/tests/chrome/window_largemenu.xhtml b/toolkit/content/tests/chrome/window_largemenu.xhtml
new file mode 100644
index 0000000000..d84b045e78
--- /dev/null
+++ b/toolkit/content/tests/chrome/window_largemenu.xhtml
@@ -0,0 +1,430 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window title="Large Menu Tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+<!--
+ This test checks that a large menu is displayed with arrow buttons
+ and is on the screen.
+ -->
+
+<script>
+<![CDATA[
+
+var gOverflowed = false, gUnderflowed = false;
+var gContextMenuTests = false;
+var gScreenY = -1;
+var gTestIndex = 0;
+var gTests = ["open normal", "open when bottom would overlap", "open with scrolling",
+ "open after scrolling", "open small again",
+ "menu movement", "panel movement",
+ "context menu enough space below",
+ "context menu more space above",
+ "context menu too big either side",
+ "context menu larger than screen",
+ "context menu flips horizontally on osx"];
+function getScreenXY(element)
+{
+ var screenX, screenY;
+ var mouseFn = function(event) {
+ screenX = event.screenX - 1;
+ screenY = event.screenY - 1;
+ }
+
+ // a hacky way to get the screen position of an element without using the box object
+ window.addEventListener("mousedown", mouseFn);
+ synthesizeMouse(element, 1, 1, { });
+ window.removeEventListener("mousedown", mouseFn);
+
+ return [screenX, screenY];
+}
+
+function hidePopup() {
+ window.requestAnimationFrame(
+ function() {
+ setTimeout(
+ function() {
+ document.getElementById("popup").hidePopup();
+ }, 0);
+ });
+}
+
+function runTests()
+{
+ [, gScreenY] = getScreenXY(document.documentElement);
+ nextTest();
+}
+
+function nextTest()
+{
+ gOverflowed = false; gUnderflowed = false;
+
+ var y = screen.height;
+ if (gTestIndex == 1) // open with bottom overlap test:
+ y -= 100;
+ else
+ y /= 2;
+
+ var popup = document.getElementById("popup");
+ if (gTestIndex == 2) {
+ // add some more menuitems so that scrolling will be necessary
+ var moreItemCount = Math.round(screen.height / popup.firstChild.getBoundingClientRect().height);
+ for (var t = 1; t <= moreItemCount; t++) {
+ var menu = document.createXULElement("menuitem");
+ menu.setAttribute("label", "More" + t);
+ popup.appendChild(menu);
+ }
+ }
+ else if (gTestIndex == 4) {
+ // remove the items added in test 2 above
+ while (popup.childNodes.length > 15)
+ popup.removeChild(popup.lastChild);
+ }
+
+ window.requestAnimationFrame(function() {
+ setTimeout(
+ function() {
+ popup.openPopupAtScreen(100, y, false);
+ }, 0);
+ });
+}
+
+function popupShown()
+{
+ if (gTests[gTestIndex] == "menu movement")
+ return testPopupMovement();
+
+ if (gContextMenuTests)
+ return contextMenuPopupShown();
+
+ var popup = document.getElementById("popup");
+ var rect = popup.getBoundingClientRect();
+ var marginTop = parseFloat(getComputedStyle(popup).marginTop);
+ var marginBottom = parseFloat(getComputedStyle(popup).marginBottom);
+ var scrollbox = popup.scrollBox.scrollbox;
+ var expectedScrollPos = 0;
+
+ info(`${gTests[gTestIndex]}: ${JSON.stringify(rect)} | ${screen.width}x${screen.height} | ${gScreenY}`);
+ if (gTestIndex == 0) {
+ // the popup should be in the center of the screen
+ // note that if the height is odd, the y-offset will have been rounded
+ // down when we pass the fractional value to openPopupAtScreen above.
+ is(Math.round(rect.top - marginTop) + gScreenY, Math.floor(screen.height / 2),
+ gTests[gTestIndex] + " top");
+ ok(Math.round(rect.bottom - marginBottom) + gScreenY < screen.height,
+ gTests[gTestIndex] + " bottom");
+ ok(!gOverflowed && !gUnderflowed, gTests[gTestIndex] + " overflow")
+ }
+ else if (gTestIndex == 1) {
+ // the popup was supposed to open 100 pixels from the bottom, but that
+ // would put it off screen so ...
+ if (platformIsMac()) {
+ // On OSX the popup is constrained so it remains within the
+ // bounds of the screen
+ ok(Math.round(rect.top) + gScreenY >= screen.top, gTests[gTestIndex] + " top");
+ is(Math.round(rect.bottom) + gScreenY, screen.availTop + screen.availHeight, gTests[gTestIndex] + " bottom");
+ ok(!gOverflowed && !gUnderflowed, gTests[gTestIndex] + " overflow");
+ }
+ else {
+ // On other platforms the menu should be flipped to have its bottom
+ // edge 100 pixels from the bottom
+ ok(Math.round(rect.top - marginTop) + gScreenY >= screen.top, gTests[gTestIndex] + " top");
+ is(Math.round(rect.bottom + marginBottom) + gScreenY, screen.height - 100,
+ gTests[gTestIndex] + " bottom");
+ ok(!gOverflowed && !gUnderflowed, gTests[gTestIndex] + " overflow");
+ }
+ }
+ else if (gTestIndex == 2) {
+ // the popup is too large so ensure that it is on screen
+ ok(Math.round(rect.top - marginTop) + gScreenY >= screen.top, gTests[gTestIndex] + " top");
+ ok(Math.round(rect.bottom + marginBottom) + gScreenY <= screen.height, gTests[gTestIndex] + " bottom");
+ ok(gOverflowed && !gUnderflowed, gTests[gTestIndex] + " overflow")
+
+ scrollbox.scrollTo(0, 40);
+ expectedScrollPos = 40;
+ }
+ else if (gTestIndex == 3) {
+ expectedScrollPos = 40;
+ if (scrollbox.scrollTop != expectedScrollPos) {
+ // TODO(bug 1815608): This never worked on Wayland, but regressed with flexbox emulation.
+ todo_is(scrollbox.scrollTop, expectedScrollPos, "menu scroll position after reopening large menu should not reset");
+ expectedScrollPos = 0;
+ }
+ }
+ else if (gTestIndex == 4) {
+ // note that if the height is odd, the y-offset will have been rounded
+ // down when we pass the fractional value to openPopupAtScreen above.
+ is(Math.round(rect.top - marginTop) + gScreenY, Math.floor(screen.height / 2),
+ gTests[gTestIndex] + " top");
+ ok(Math.round(rect.bottom) + gScreenY < screen.height,
+ gTests[gTestIndex] + " bottom");
+ ok(!gOverflowed && gUnderflowed, gTests[gTestIndex] + " overflow");
+ }
+
+ is(scrollbox.scrollTop, expectedScrollPos, "menu scroll position " + gTests[gTestIndex])
+
+ return hidePopup();
+}
+
+function is(l, r, n) { window.arguments[0].SimpleTest.is(l,r,n); }
+function ok(v, n) { window.arguments[0].SimpleTest.ok(v,n); }
+
+var oldx, oldy, waitSteps = 0;
+function moveWindowTo(x, y, callback, arg)
+{
+ if (!waitSteps) {
+ oldx = window.screenX;
+ oldy = window.screenY;
+ window.moveTo(x, y);
+
+ waitSteps++;
+ setTimeout(moveWindowTo, 100, x, y, callback, arg);
+ return;
+ }
+
+ if (window.screenX == oldx && window.screenY == oldy) {
+ if (waitSteps++ > 10) {
+ ok(false, "Window never moved properly to " + x + "," + y);
+ window.arguments[0].SimpleTest.finish();
+ window.close();
+ }
+
+ setTimeout(moveWindowTo, 100, x, y, callback, arg);
+ }
+ else {
+ waitSteps = 0;
+ callback(arg);
+ }
+}
+
+function popupHidden()
+{
+ gTestIndex++;
+ if (gTestIndex == gTests.length) {
+ window.arguments[0].SimpleTest.finish();
+ window.close();
+ }
+ else if (gTests[gTestIndex] == "context menu enough space below") {
+ gContextMenuTests = true;
+ moveWindowTo(window.screenX, screen.availTop + 10,
+ () => synthesizeMouse(document.getElementById("label"), 4, 4, { type: "contextmenu", button: 2 }));
+ }
+ else if (gTests[gTestIndex] == "menu movement") {
+ document.getElementById("popup").openPopup(
+ document.getElementById("label"), "after_start", 0, 0, false, false);
+ }
+ else if (gTests[gTestIndex] == "panel movement") {
+ document.getElementById("panel").openPopup(
+ document.getElementById("label"), "after_start", 0, 0, false, false);
+ }
+ else if (gContextMenuTests) {
+ contextMenuPopupHidden();
+ }
+ else {
+ nextTest();
+ }
+}
+
+function contextMenuPopupShown()
+{
+ var popup = document.getElementById("popup");
+ var rect = popup.getBoundingClientRect();
+ var marginTop = parseFloat(getComputedStyle(popup).marginTop);
+ var marginLeft = parseFloat(getComputedStyle(popup).marginLeft);
+ var labelrect = document.getElementById("label").getBoundingClientRect();
+
+ // Click to open popup in popupHidden() occurs at (4,4) in label's coordinate space
+ var clickX = 4;
+ var clickY = 4;
+
+ info(`${gTests[gTestIndex]}: ${JSON.stringify(rect)}`);
+
+ var testPopupAppearedRightOfCursor = true;
+ switch (gTests[gTestIndex]) {
+ case "context menu enough space below":
+ is(rect.top - marginTop, labelrect.top + clickY + (platformIsMac() ? -6 : 2), gTests[gTestIndex] + " top");
+ break;
+ case "context menu more space above":
+ if (platformIsMac()) {
+ let screenY;
+ [, screenY] = getScreenXY(popup);
+ // Macs constrain their popup menus vertically rather than flip them.
+ is(screenY, screen.availTop + screen.availHeight - rect.height, gTests[gTestIndex] + " top");
+ } else {
+ is(rect.top + marginTop, labelrect.top + clickY - rect.height - 2, gTests[gTestIndex] + " top");
+ }
+
+ break;
+ case "context menu too big either side":
+ [, gScreenY] = getScreenXY(document.documentElement);
+ // compare against the available size as well as the total size, as some
+ // platforms allow the menu to overlap os chrome and others do not
+ var pos = (screen.availTop + screen.availHeight - rect.height) - gScreenY - marginTop;
+ var availPos = (screen.top + screen.height - rect.height) - gScreenY - marginTop;
+ ok(rect.top == pos || rect.top == availPos,
+ gTests[gTestIndex] + ` top (${pos}/${availPos})`);
+ break;
+ case "context menu larger than screen":
+ ok(rect.top == -(gScreenY - screen.availTop) + marginTop || rect.top == -(gScreenY - screen.top) + marginTop,
+ `${gTests[gTestIndex]} top (top = ${rect.top} screenY = ${gScreenY} screenAvailTop = ${screen.availTop} screenTop = ${screen.top})`);
+ break;
+ case "context menu flips horizontally on osx":
+ testPopupAppearedRightOfCursor = false;
+ if (platformIsMac()) {
+ is(Math.round(rect.right), labelrect.left + clickX - 1, gTests[gTestIndex] + " right");
+ }
+ break;
+ }
+
+ if (testPopupAppearedRightOfCursor) {
+ is(rect.left - marginLeft, labelrect.left + clickX + (platformIsMac() ? 1 : 2), gTests[gTestIndex] + " left");
+ }
+
+ hidePopup();
+}
+
+function contextMenuPopupHidden()
+{
+ var screenAvailBottom = screen.availTop + screen.availHeight;
+
+ if (gTests[gTestIndex] == "context menu more space above") {
+ moveWindowTo(window.screenX, screenAvailBottom - 80, nextContextMenuTest, -1);
+ }
+ else if (gTests[gTestIndex] == "context menu too big either side") {
+ moveWindowTo(window.screenX, screenAvailBottom / 2 - 80, nextContextMenuTest, screenAvailBottom / 2 + 120);
+ }
+ else if (gTests[gTestIndex] == "context menu larger than screen") {
+ nextContextMenuTest(screen.availHeight + 80);
+ }
+ else if (gTests[gTestIndex] == "context menu flips horizontally on osx") {
+ var popup = document.getElementById("popup");
+ var popupWidth = popup.getBoundingClientRect().width;
+ moveWindowTo(screen.availLeft + screen.availWidth - popupWidth, 100, nextContextMenuTest, -1);
+ }
+}
+
+function nextContextMenuTest(desiredHeight)
+{
+ if (desiredHeight >= 0) {
+ var popup = document.getElementById("popup");
+ var height = popup.getBoundingClientRect().height;
+ var itemheight = document.getElementById("firstitem").getBoundingClientRect().height;
+ while (height < desiredHeight) {
+ var menu = document.createXULElement("menuitem");
+ menu.setAttribute("label", "Item");
+ popup.appendChild(menu);
+ height += itemheight;
+ }
+ }
+
+ synthesizeMouse(document.getElementById("label"), 4, 4, { type: "contextmenu", button: 2 });
+}
+
+function testPopupMovement()
+{
+ var isPanelTest = (gTests[gTestIndex] == "panel movement");
+ var popup = document.getElementById(isPanelTest ? "panel" : "popup");
+
+ var screenX, screenY;
+ var rect = popup.getBoundingClientRect();
+ var marginBottom = parseFloat(getComputedStyle(popup).marginBottom);
+ var marginLeft = parseFloat(getComputedStyle(popup).marginLeft);
+ var marginTop = parseFloat(getComputedStyle(popup).marginTop);
+
+ var panelIsTop = SpecialPowers.getBoolPref("ui.panel.default_level_parent");
+ var overlapOSChrome = !platformIsMac() && (!isPanelTest || panelIsTop);
+ popup.moveTo(1, 1);
+ [screenX, screenY] = getScreenXY(popup);
+
+ var expectedx = 1, expectedy = 1;
+ if (!overlapOSChrome) {
+ if (screen.availLeft >= 1) expectedx = screen.availLeft;
+ if (screen.availTop >= 1) expectedy = screen.availTop;
+ }
+ is(screenX, expectedx, gTests[gTestIndex] + " (1, 1) x");
+ is(screenY, expectedy, gTests[gTestIndex] + " (1, 1) y");
+
+ popup.moveTo(100, 8000);
+ expectedy = (overlapOSChrome ? screen.height + screen.top : screen.availHeight + screen.availTop) -
+ Math.round(rect.height) - marginBottom;
+ [screenX, screenY] = getScreenXY(popup);
+ is(screenX, 100, gTests[gTestIndex] + " (100, 8000) x");
+ is(screenY, expectedy, gTests[gTestIndex] + " (100, 8000) y");
+
+ popup.moveTo(6000, 100);
+
+ expectedx = (overlapOSChrome ? screen.width + screen.left : screen.availWidth + screen.availLeft) -
+ Math.round(rect.width) - marginLeft;
+ [screenX, screenY] = getScreenXY(popup);
+ is(screenX, expectedx, gTests[gTestIndex] + " (6000, 100) x");
+ is(screenY, 100, gTests[gTestIndex] + " (6000, 100) y");
+
+ is(popup.getAttribute("left"), "", gTests[gTestIndex] + " left is empty after moving");
+ is(popup.getAttribute("top"), "", gTests[gTestIndex] + " top is empty after moving");
+ popup.setAttribute("left", "80");
+ popup.setAttribute("top", "82");
+ [screenX, screenY] = getScreenXY(popup);
+ is(screenX, 80, gTests[gTestIndex] + " set left and top x");
+ is(screenY, 82, gTests[gTestIndex] + " set left and top y");
+ popup.moveTo(95, 98);
+ [screenX, screenY] = getScreenXY(popup);
+ is(screenX, 95, gTests[gTestIndex] + " move after set left and top x");
+ is(screenY, 98, gTests[gTestIndex] + " move after set left and top y");
+ is(popup.getAttribute("left"), "95", gTests[gTestIndex] + " left is set after moving");
+ is(popup.getAttribute("top"), "98", gTests[gTestIndex] + " top is set after moving");
+ popup.removeAttribute("left");
+ popup.removeAttribute("top");
+
+ popup.moveTo(-1 + marginLeft, -1 + marginTop);
+ [screenX, screenY] = getScreenXY(popup);
+
+ expectedx = (overlapOSChrome ? screen.left : screen.availLeft) + marginLeft;
+ expectedy = (overlapOSChrome ? screen.top : screen.availTop) + marginTop;
+
+ is(screenX, expectedx, gTests[gTestIndex] + " move after set left and top x to -1");
+ is(screenY, expectedy, gTests[gTestIndex] + " move after set left and top y to -1");
+ is(popup.getAttribute("left"), "", gTests[gTestIndex] + " left is not set after moving to -1");
+ is(popup.getAttribute("top"), "", gTests[gTestIndex] + " top is not set after moving to -1");
+
+ popup.hidePopup();
+}
+
+function platformIsMac()
+{
+ return navigator.platform.indexOf("Mac") > -1;
+}
+
+window.arguments[0].SimpleTest.waitForFocus(runTests, window);
+
+]]>
+</script>
+
+<button id="label" label="OK" context="popup"/>
+<menupopup id="popup" onpopupshown="popupShown();" onpopuphidden="popupHidden();"
+ onoverflow="gOverflowed = true" onunderflow="gUnderflowed = true;">
+ <menuitem id="firstitem" label="1"/>
+ <menuitem label="2"/>
+ <menuitem label="3"/>
+ <menuitem label="4"/>
+ <menuitem label="5"/>
+ <menuitem label="6"/>
+ <menuitem label="7"/>
+ <menuitem label="8"/>
+ <menuitem label="9"/>
+ <menuitem label="10"/>
+ <menuitem label="11"/>
+ <menuitem label="12"/>
+ <menuitem label="13"/>
+ <menuitem label="14"/>
+ <menuitem label="15"/>
+</menupopup>
+
+<panel id="panel" onpopupshown="testPopupMovement();" onpopuphidden="popupHidden();" style="margin: 0; -moz-window-input-region-margin: 0;">
+ <button label="OK"/>
+</panel>
+
+</window>