browser_selectpopup_large.js (9035B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 const PAGECONTENT_SMALL = ` 5 <!doctype html> 6 <html> 7 <body><select id='one'> 8 <option value='One'>One</option> 9 <option value='Two'>Two</option> 10 </select><select id='two'> 11 <option value='Three'>Three</option> 12 <option value='Four'>Four</option> 13 </select><select id='three'> 14 <option value='Five'>Five</option> 15 <option value='Six'>Six</option> 16 </select></body></html> 17 `; 18 19 async function performLargePopupTests(win) { 20 let browser = win.gBrowser.selectedBrowser; 21 22 await SpecialPowers.spawn(browser, [], async function () { 23 let doc = content.document; 24 let select = doc.getElementById("one"); 25 for (var i = 0; i < 180; i++) { 26 select.add(new content.Option("Test" + i)); 27 } 28 29 select.options[60].selected = true; 30 select.focus(); 31 }); 32 33 // Check if a drag-select works and scrolls the list. 34 const selectPopup = await openSelectPopup("mousedown", "select", win); 35 const browserRect = browser.getBoundingClientRect(); 36 37 let getScrollPos = () => selectPopup.scrollBox.scrollbox.scrollTop; 38 let scrollPos = getScrollPos(); 39 let popupRect = selectPopup.getBoundingClientRect(); 40 41 // First, check that scrolling does not occur when the mouse is moved over the 42 // anchor button but not the popup yet. 43 EventUtils.synthesizeMouseAtPoint( 44 popupRect.left + 5, 45 popupRect.top - 10, 46 { 47 type: "mousemove", 48 buttons: 1, 49 }, 50 win 51 ); 52 is( 53 getScrollPos(), 54 scrollPos, 55 "scroll position after mousemove over button should not change" 56 ); 57 58 EventUtils.synthesizeMouseAtPoint( 59 popupRect.left + 20, 60 popupRect.top + 10, 61 { 62 type: "mousemove", 63 buttons: 1, 64 }, 65 win 66 ); 67 68 // Dragging above the popup scrolls it up. 69 let scrolledPromise = BrowserTestUtils.waitForEvent( 70 selectPopup, 71 "scroll", 72 false, 73 () => getScrollPos() < scrollPos - 5 74 ); 75 EventUtils.synthesizeMouseAtPoint( 76 popupRect.left + 20, 77 popupRect.top - 20, 78 { 79 type: "mousemove", 80 buttons: 1, 81 }, 82 win 83 ); 84 await scrolledPromise; 85 ok(true, "scroll position at drag up"); 86 87 // Dragging below the popup scrolls it down. 88 scrollPos = getScrollPos(); 89 scrolledPromise = BrowserTestUtils.waitForEvent( 90 selectPopup, 91 "scroll", 92 false, 93 () => getScrollPos() > scrollPos + 5 94 ); 95 EventUtils.synthesizeMouseAtPoint( 96 popupRect.left + 20, 97 popupRect.bottom + 20, 98 { 99 type: "mousemove", 100 buttons: 1, 101 }, 102 win 103 ); 104 await scrolledPromise; 105 ok(true, "scroll position at drag down"); 106 107 // Releasing the mouse button and moving the mouse does not change the scroll position. 108 scrollPos = getScrollPos(); 109 EventUtils.synthesizeMouseAtPoint( 110 popupRect.left + 20, 111 popupRect.bottom + 25, 112 { type: "mouseup" }, 113 win 114 ); 115 is(getScrollPos(), scrollPos, "scroll position at mouseup should not change"); 116 117 EventUtils.synthesizeMouseAtPoint( 118 popupRect.left + 20, 119 popupRect.bottom + 20, 120 { type: "mousemove" }, 121 win 122 ); 123 is( 124 getScrollPos(), 125 scrollPos, 126 "scroll position at mousemove after mouseup should not change" 127 ); 128 129 // Now check dragging with a mousedown on an item 130 let menuRect = selectPopup.children[51].getBoundingClientRect(); 131 EventUtils.synthesizeMouseAtPoint( 132 menuRect.left + 5, 133 menuRect.top + 5, 134 { type: "mousedown" }, 135 win 136 ); 137 138 // Dragging below the popup scrolls it down. 139 scrolledPromise = BrowserTestUtils.waitForEvent( 140 selectPopup, 141 "scroll", 142 false, 143 () => getScrollPos() > scrollPos + 5 144 ); 145 EventUtils.synthesizeMouseAtPoint( 146 popupRect.left + 20, 147 popupRect.bottom + 20, 148 { 149 type: "mousemove", 150 buttons: 1, 151 }, 152 win 153 ); 154 await scrolledPromise; 155 ok(true, "scroll position at drag down from option"); 156 157 // Dragging above the popup scrolls it up. 158 scrolledPromise = BrowserTestUtils.waitForEvent( 159 selectPopup, 160 "scroll", 161 false, 162 () => getScrollPos() < scrollPos - 5 163 ); 164 EventUtils.synthesizeMouseAtPoint( 165 popupRect.left + 20, 166 popupRect.top - 20, 167 { 168 type: "mousemove", 169 buttons: 1, 170 }, 171 win 172 ); 173 await scrolledPromise; 174 ok(true, "scroll position at drag up from option"); 175 176 scrollPos = getScrollPos(); 177 // We intentionally turn off this a11y check, because the following click 178 // is sent on an arbitrary web content that is not expected to be tested 179 // by itself with the browser mochitests, therefore this rule check shall 180 // be ignored by a11y-checks suite. 181 AccessibilityUtils.setEnv({ labelRule: false }); 182 EventUtils.synthesizeMouseAtPoint( 183 popupRect.left + 20, 184 popupRect.bottom + 25, 185 { type: "mouseup" }, 186 win 187 ); 188 AccessibilityUtils.resetEnv(); 189 is( 190 getScrollPos(), 191 scrollPos, 192 "scroll position at mouseup from option should not change" 193 ); 194 195 EventUtils.synthesizeMouseAtPoint( 196 popupRect.left + 20, 197 popupRect.bottom + 20, 198 { type: "mousemove" }, 199 win 200 ); 201 is( 202 getScrollPos(), 203 scrollPos, 204 "scroll position at mousemove after mouseup should not change" 205 ); 206 207 await hideSelectPopup("escape", win); 208 209 let positions = [ 210 "margin-top: 300px;", 211 "position: fixed; bottom: 200px;", 212 "width: 100%; height: 9999px;", 213 ]; 214 215 let position; 216 while (positions.length) { 217 await openSelectPopup("key", "select", win); 218 219 let rect = selectPopup.getBoundingClientRect(); 220 let marginBottom = parseFloat(getComputedStyle(selectPopup).marginBottom); 221 let marginTop = parseFloat(getComputedStyle(selectPopup).marginTop); 222 Assert.greaterOrEqual( 223 rect.top - marginTop, 224 browserRect.top, 225 "Popup top position in within browser area" 226 ); 227 Assert.lessOrEqual( 228 rect.bottom + marginBottom, 229 browserRect.bottom, 230 "Popup bottom position in within browser area" 231 ); 232 233 let cs = win.getComputedStyle(selectPopup); 234 let csArrow = win.getComputedStyle(selectPopup.scrollBox); 235 let bpBottom = 236 parseFloat(cs.paddingBottom) + 237 parseFloat(cs.borderBottomWidth) + 238 parseFloat(csArrow.paddingBottom) + 239 parseFloat(csArrow.borderBottomWidth); 240 let selectedOption = 60; 241 242 if (Services.prefs.getBoolPref("dom.forms.selectSearch")) { 243 // Use option 61 instead of 60, as the 60th option element is actually the 244 // 61st child, since the first child is now the search input field. 245 selectedOption = 61; 246 } 247 // Some of the styles applied to the menuitems are percentages, meaning 248 // that the final layout calculations returned by getBoundingClientRect() 249 // might return floating point values. We don't care about sub-pixel 250 // accuracy, and only care about the final pixel value, so we add a 251 // fuzz-factor of 1. 252 const fuzzFactor = 1; 253 SimpleTest.isfuzzy( 254 selectPopup.children[selectedOption].getBoundingClientRect().bottom, 255 selectPopup.getBoundingClientRect().bottom - bpBottom + marginBottom, 256 fuzzFactor, 257 "Popup scroll at correct position " + bpBottom 258 ); 259 260 await hideSelectPopup("enter", win); 261 262 position = positions.shift(); 263 264 let contentPainted = BrowserTestUtils.waitForContentEvent( 265 browser, 266 "MozAfterPaint" 267 ); 268 await SpecialPowers.spawn( 269 browser, 270 [position], 271 async function (contentPosition) { 272 let select = content.document.getElementById("one"); 273 select.setAttribute("style", contentPosition || ""); 274 select.getBoundingClientRect(); 275 } 276 ); 277 await contentPainted; 278 } 279 } 280 281 add_setup(async function () { 282 await SpecialPowers.pushPrefEnv({ 283 set: [["test.wait300msAfterTabSwitch", true]], 284 }); 285 }); 286 287 // This test checks select elements with a large number of options to ensure that 288 // the popup appears within the browser area. 289 add_task(async function test_large_popup() { 290 const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL); 291 let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl); 292 293 await performLargePopupTests(window); 294 295 BrowserTestUtils.removeTab(tab); 296 }); 297 298 // This test checks the same as the previous test but in a new, vertically smaller window. 299 add_task(async function test_large_popup_in_small_window() { 300 let newWin = await BrowserTestUtils.openNewBrowserWindow(); 301 302 let resizePromise = BrowserTestUtils.waitForEvent( 303 newWin, 304 "resize", 305 false, 306 () => { 307 info(`Got resize event (innerHeight: ${newWin.innerHeight})`); 308 return newWin.innerHeight <= 450; 309 } 310 ); 311 newWin.resizeTo(600, 450); 312 await resizePromise; 313 314 const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL); 315 let browserLoadedPromise = BrowserTestUtils.browserLoaded( 316 newWin.gBrowser.selectedBrowser 317 ); 318 BrowserTestUtils.startLoadingURIString( 319 newWin.gBrowser.selectedBrowser, 320 pageUrl 321 ); 322 await browserLoadedPromise; 323 324 newWin.gBrowser.selectedBrowser.focus(); 325 326 await performLargePopupTests(newWin); 327 328 await BrowserTestUtils.closeWindow(newWin); 329 });