/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ const PAGECONTENT_SMALL = ` `; async function performLargePopupTests(win) { let browser = win.gBrowser.selectedBrowser; await SpecialPowers.spawn(browser, [], async function () { let doc = content.document; let select = doc.getElementById("one"); for (var i = 0; i < 180; i++) { select.add(new content.Option("Test" + i)); } select.options[60].selected = true; select.focus(); }); // Check if a drag-select works and scrolls the list. const selectPopup = await openSelectPopup("mousedown", "select", win); const browserRect = browser.getBoundingClientRect(); let getScrollPos = () => selectPopup.scrollBox.scrollbox.scrollTop; let scrollPos = getScrollPos(); let popupRect = selectPopup.getBoundingClientRect(); // First, check that scrolling does not occur when the mouse is moved over the // anchor button but not the popup yet. EventUtils.synthesizeMouseAtPoint( popupRect.left + 5, popupRect.top - 10, { type: "mousemove", buttons: 1, }, win ); is( getScrollPos(), scrollPos, "scroll position after mousemove over button should not change" ); EventUtils.synthesizeMouseAtPoint( popupRect.left + 20, popupRect.top + 10, { type: "mousemove", buttons: 1, }, win ); // Dragging above the popup scrolls it up. let scrolledPromise = BrowserTestUtils.waitForEvent( selectPopup, "scroll", false, () => getScrollPos() < scrollPos - 5 ); EventUtils.synthesizeMouseAtPoint( popupRect.left + 20, popupRect.top - 20, { type: "mousemove", buttons: 1, }, win ); await scrolledPromise; ok(true, "scroll position at drag up"); // Dragging below the popup scrolls it down. scrollPos = getScrollPos(); scrolledPromise = BrowserTestUtils.waitForEvent( selectPopup, "scroll", false, () => getScrollPos() > scrollPos + 5 ); EventUtils.synthesizeMouseAtPoint( popupRect.left + 20, popupRect.bottom + 20, { type: "mousemove", buttons: 1, }, win ); await scrolledPromise; ok(true, "scroll position at drag down"); // Releasing the mouse button and moving the mouse does not change the scroll position. scrollPos = getScrollPos(); EventUtils.synthesizeMouseAtPoint( popupRect.left + 20, popupRect.bottom + 25, { type: "mouseup" }, win ); is(getScrollPos(), scrollPos, "scroll position at mouseup should not change"); EventUtils.synthesizeMouseAtPoint( popupRect.left + 20, popupRect.bottom + 20, { type: "mousemove" }, win ); is( getScrollPos(), scrollPos, "scroll position at mousemove after mouseup should not change" ); // Now check dragging with a mousedown on an item let menuRect = selectPopup.children[51].getBoundingClientRect(); EventUtils.synthesizeMouseAtPoint( menuRect.left + 5, menuRect.top + 5, { type: "mousedown" }, win ); // Dragging below the popup scrolls it down. scrolledPromise = BrowserTestUtils.waitForEvent( selectPopup, "scroll", false, () => getScrollPos() > scrollPos + 5 ); EventUtils.synthesizeMouseAtPoint( popupRect.left + 20, popupRect.bottom + 20, { type: "mousemove", buttons: 1, }, win ); await scrolledPromise; ok(true, "scroll position at drag down from option"); // Dragging above the popup scrolls it up. scrolledPromise = BrowserTestUtils.waitForEvent( selectPopup, "scroll", false, () => getScrollPos() < scrollPos - 5 ); EventUtils.synthesizeMouseAtPoint( popupRect.left + 20, popupRect.top - 20, { type: "mousemove", buttons: 1, }, win ); await scrolledPromise; ok(true, "scroll position at drag up from option"); scrollPos = getScrollPos(); EventUtils.synthesizeMouseAtPoint( popupRect.left + 20, popupRect.bottom + 25, { type: "mouseup" }, win ); is( getScrollPos(), scrollPos, "scroll position at mouseup from option should not change" ); EventUtils.synthesizeMouseAtPoint( popupRect.left + 20, popupRect.bottom + 20, { type: "mousemove" }, win ); is( getScrollPos(), scrollPos, "scroll position at mousemove after mouseup should not change" ); await hideSelectPopup("escape", win); let positions = [ "margin-top: 300px;", "position: fixed; bottom: 200px;", "width: 100%; height: 9999px;", ]; let position; while (positions.length) { await openSelectPopup("key", "select", win); let rect = selectPopup.getBoundingClientRect(); let marginBottom = parseFloat(getComputedStyle(selectPopup).marginBottom); let marginTop = parseFloat(getComputedStyle(selectPopup).marginTop); ok( rect.top - marginTop >= browserRect.top, "Popup top position in within browser area" ); ok( rect.bottom + marginBottom <= browserRect.bottom, "Popup bottom position in within browser area" ); let cs = win.getComputedStyle(selectPopup); let csArrow = win.getComputedStyle(selectPopup.scrollBox); let bpBottom = parseFloat(cs.paddingBottom) + parseFloat(cs.borderBottomWidth) + parseFloat(csArrow.paddingBottom) + parseFloat(csArrow.borderBottomWidth); let selectedOption = 60; if (Services.prefs.getBoolPref("dom.forms.selectSearch")) { // Use option 61 instead of 60, as the 60th option element is actually the // 61st child, since the first child is now the search input field. selectedOption = 61; } // Some of the styles applied to the menuitems are percentages, meaning // that the final layout calculations returned by getBoundingClientRect() // might return floating point values. We don't care about sub-pixel // accuracy, and only care about the final pixel value, so we add a // fuzz-factor of 1. // // FIXME(emilio): In win7 scroll position is off by 20px more, but that's // not reproducible in win10 even with the win7 "native" menus enabled. const fuzzFactor = matchMedia("(-moz-platform: windows-win7)").matches ? 21 : 1; SimpleTest.isfuzzy( selectPopup.children[selectedOption].getBoundingClientRect().bottom, selectPopup.getBoundingClientRect().bottom - bpBottom + marginBottom, fuzzFactor, "Popup scroll at correct position " + bpBottom ); await hideSelectPopup("enter", win); position = positions.shift(); let contentPainted = BrowserTestUtils.waitForContentEvent( browser, "MozAfterPaint" ); await SpecialPowers.spawn( browser, [position], async function (contentPosition) { let select = content.document.getElementById("one"); select.setAttribute("style", contentPosition || ""); select.getBoundingClientRect(); } ); await contentPainted; } if (navigator.platform.indexOf("Mac") == 0) { await SpecialPowers.spawn(browser, [], async function () { let doc = content.document; doc.body.style = "padding-top: 400px;"; let select = doc.getElementById("one"); select.options[41].selected = true; select.focus(); }); await openSelectPopup("key", "select", win); ok( selectPopup.getBoundingClientRect().top > browser.getBoundingClientRect().top, "select popup appears over selected item" ); await hideSelectPopup("escape", win); } } // This test checks select elements with a large number of options to ensure that // the popup appears within the browser area. add_task(async function test_large_popup() { const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL); let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl); await performLargePopupTests(window); BrowserTestUtils.removeTab(tab); }); // This test checks the same as the previous test but in a new, vertically smaller window. add_task(async function test_large_popup_in_small_window() { let newWin = await BrowserTestUtils.openNewBrowserWindow(); let resizePromise = BrowserTestUtils.waitForEvent( newWin, "resize", false, e => { info(`Got resize event (innerHeight: ${newWin.innerHeight})`); return newWin.innerHeight <= 450; } ); newWin.resizeTo(600, 450); await resizePromise; const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL); let browserLoadedPromise = BrowserTestUtils.browserLoaded( newWin.gBrowser.selectedBrowser ); BrowserTestUtils.loadURIString(newWin.gBrowser.selectedBrowser, pageUrl); await browserLoadedPromise; newWin.gBrowser.selectedBrowser.focus(); await performLargePopupTests(newWin); await BrowserTestUtils.closeWindow(newWin); });