test_scroll_per_page.html (12286B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>Test for scroll per page</title> 5 <script src="/tests/SimpleTest/SimpleTest.js"></script> 6 <script src="/tests/SimpleTest/EventUtils.js"></script> 7 <script type="text/javascript" src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script> 8 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> 9 </head> 10 <body> 11 12 <pre id="test"> 13 <script class="testbody" type="text/javascript"> 14 SimpleTest.waitForExplicitFinish(); 15 addLoadEvent(() => { 16 open("window_empty_document.html", "_blank", "width=500,height=500,scrollbars=yes"); 17 }); 18 19 async function doTests(aWindow) { 20 await SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false]]}); 21 await SimpleTest.promiseFocus(aWindow); 22 23 function getNodeDescription(aNode) { 24 function getElementDescription(aElement) { 25 if (aElement.getAttribute("id") !== null) { 26 return `${aElement.tagName.toLowerCase()}#${aElement.getAttribute("id")}`; 27 } 28 if (aElement.tagName === "BR") { 29 return `${getElementDescription(aElement.previousSibling)} + br`; 30 } 31 return aElement.tagName.toLowerCase(); 32 } 33 switch (aNode.nodeType) { 34 case aNode.TEXT_NODE: 35 return `text node in ${getElementDescription(aNode.parentElement)}`; 36 case aNode.ELEMENT_NODE: 37 return getElementDescription(aNode); 38 case aNode.DOCUMENT_NODE: 39 return `document node`; 40 default: 41 return "unknown node"; 42 } 43 } 44 45 function getScrollPositionStr(aNode) { 46 return `{ scrollTop: ${aNode.scrollTop}, scrollHeight: ${ 47 aNode.scrollHeight 48 }, scrollLeft: ${aNode.scrollLeft}, scrollWidth: ${aNode.scrollWidth} }`; 49 } 50 51 async function doPageDownOrUp(aKey, aFocusedElement, aScrollTargetElement) { 52 let scrollEventTarget = 53 aScrollTargetElement === doc.documentElement 54 ? doc 55 : aScrollTargetElement; 56 let scrollEventFired = false; 57 function onScroll(aEvent) { 58 scrollEventFired |= aEvent.target === scrollEventTarget; 59 } 60 scrollEventTarget.addEventListener("scroll", onScroll); 61 if (!navigator.platform.includes("Mac")) { 62 synthesizeKey(`KEY_${aKey}`, {}, aWindow); 63 } else { 64 synthesizeKey(`KEY_${aKey}`, { altKey: true }, aWindow); 65 } 66 let retry = 3; 67 while (retry--) { 68 await waitToClearOutAnyPotentialScrolls(aWindow); 69 if (scrollEventFired) { 70 break; 71 } 72 } 73 ok(scrollEventFired, 74 `Scroll event should've been fired on ${getNodeDescription(scrollEventTarget)}`); 75 scrollEventTarget.removeEventListener("scroll", onScroll); 76 } 77 78 async function doPageDown(aFocusedElement, aScrollTargetElement) { 79 await doPageDownOrUp("PageDown", aFocusedElement, aScrollTargetElement); 80 } 81 82 async function doPageUp(aFocusedElement, aScrollTargetElement) { 83 await doPageDownOrUp("PageUp", aFocusedElement, aScrollTargetElement); 84 } 85 86 // Let's put log of scroll events for making debug this test easier. 87 aWindow.addEventListener("scroll", (aEvent) => { 88 let scrollElement = 89 aEvent.target === doc 90 ? doc.documentElement 91 : aEvent.target; 92 info(`"scroll" event fired on ${getNodeDescription(aEvent.target)}: ${ 93 getScrollPositionStr(scrollElement) 94 }`); 95 }, { capture: true }); 96 97 let doc = aWindow.document; 98 let body = doc.body; 99 let selection = doc.getSelection(); 100 let container; 101 102 body.innerHTML = '<div id="largeDiv" style="height: 1500px;">' + 103 "<p>previous line of the editor.</p>" + 104 '<div id="editor" contenteditable style="margin-top 500px; height: 5em; overflow: auto;">' + 105 "Here is first line<br>" + 106 "Here is second line" + 107 "</div>" + 108 "<p>next line of the editor.</p>" + 109 "</div>"; 110 container = doc.documentElement; 111 let editor = doc.getElementById("editor"); 112 editor.focus(); 113 await waitToClearOutAnyPotentialScrolls(aWindow); 114 115 let description = "PageDown in non-scrollable editing host: "; 116 let previousScrollTop = container.scrollTop; 117 await doPageDown(editor, container); 118 ok(container.scrollTop > previousScrollTop, 119 `${description}the document should be scrolled down even if user presses PageDown in the editing host got: ${container.scrollTop}, previous position: ${previousScrollTop}`); 120 let range = selection.getRangeAt(0); 121 is(range.startContainer, editor.firstChild.nextSibling.nextSibling, 122 `${description}selection start shouldn't be moved to outside of the editing host (got: ${getNodeDescription(range.startContainer)})`); 123 ok(range.collapsed, description + "selection should be collapsed"); 124 is(doc.activeElement, editor, 125 description + "the editing host should keep having focus"); 126 127 description = "PageUp in non-scrollable editing host: "; 128 previousScrollTop = container.scrollTop; 129 await doPageUp(editor, container); 130 ok(container.scrollTop < previousScrollTop, 131 `${description}the document should be scrolled up even if user presses PageDown in the editing host got: ${container.scrollTop}, previous position: ${previousScrollTop}`); 132 range = selection.getRangeAt(0); 133 is(range.startContainer, editor.firstChild, 134 `${description}selection start shouldn't be moved to outside of the editing host (got: ${getNodeDescription(range.startContainer)})`); 135 ok(range.collapsed, description + "selection should be collapsed"); 136 is(doc.activeElement, editor, 137 description + "the editing host should keep having focus"); 138 139 body.innerHTML = '<div id="largeDiv" style="height: 1500px;">' + 140 "<p>previous line of the editor.</p>" + 141 '<div id="editor" contenteditable style="margin-top 500px; height: 5em; overflow: auto;">' + 142 '<div id="innerDiv" style="height: 10em;">' + 143 "Here is first line<br>" + 144 "Here is second line" + 145 "</div>" + 146 "</div>" + 147 "<p>next line of the editor.</p>" + 148 "</div>"; 149 editor = doc.getElementById("editor"); 150 container = editor; 151 editor.focus(); 152 await waitToClearOutAnyPotentialScrolls(aWindow); 153 154 description = "PageDown in scrollable editing host: "; 155 previousScrollTop = container.scrollTop; 156 await doPageDown(editor, container); 157 ok(container.scrollTop > previousScrollTop, 158 `${description}the editor should be scrolled down even if user presses PageDown in the editing host got: ${container.scrollTop}, previous position: ${previousScrollTop}`); 159 range = selection.getRangeAt(0); 160 is(range.startContainer, editor.firstChild.firstChild.nextSibling.nextSibling, 161 `${description}selection start shouldn't be moved to outside of the editing host (got: ${getNodeDescription(range.startContainer)})`); 162 ok(range.collapsed, description + "selection should be collapsed"); 163 is(doc.activeElement, editor, 164 description + "the editing host should keep having focus"); 165 166 description = "PageUp in scrollable editing host: "; 167 previousScrollTop = container.scrollTop; 168 await doPageUp(editor, container); 169 ok(container.scrollTop < previousScrollTop, 170 `${description}the editor should be scrolled up even if user presses PageDown in the editing host got: ${container.scrollTop}, previous position: ${previousScrollTop}`); 171 range = selection.getRangeAt(0); 172 is(range.startContainer, editor.firstChild.firstChild, 173 `${description}selection start shouldn't be moved to outside of the editing host (got: ${getNodeDescription(range.startContainer)})`); 174 ok(range.collapsed, description + "selection should be collapsed"); 175 is(doc.activeElement, editor, 176 description + "the editing host should keep having focus"); 177 178 // Should scroll one page of the scrollable element 179 body.innerHTML = `<div id="editor" contenteditable style="height: 1500px;">${"abc<br>".repeat(100)}</div>`; 180 editor = doc.getElementById("editor"); 181 container = doc.documentElement; 182 editor.focus(); 183 await waitToClearOutAnyPotentialScrolls(aWindow); 184 185 description = "PageDown in too large editing host: "; 186 previousScrollTop = container.scrollTop; 187 await doPageDown(editor, container); 188 ok(container.scrollTop > previousScrollTop, 189 `${description} The document should be scrolled down (got: ${container.scrollTop}, previous position: ${previousScrollTop})`); 190 ok(container.scrollTop <= previousScrollTop + container.clientHeight, 191 `${description} The document should not be scrolled down too much (got: ${container.scrollTop}, previous position: ${previousScrollTop}, scroll height: ${container.clientHeight})`); 192 193 selection.selectAllChildren(editor); 194 selection.collapseToEnd(); 195 await waitToClearOutAnyPotentialScrolls(aWindow); 196 197 description = "PageUp in too large editing host: "; 198 container.scrollTop = container.scrollHeight; 199 previousScrollTop = container.scrollTop; 200 await doPageUp(editor, container); 201 ok(container.scrollTop >= previousScrollTop - container.clientHeight, 202 `${description} The document should not be scrolled up too much (got: ${container.scrollTop}, previous position: ${previousScrollTop}, scroll height: ${container.clientHeight})`); 203 204 // Shouldn't scroll to caret position after pagedown scrolls editing host. 205 body.innerHTML = '<div id="editor" contenteditable style="height: 300px; overflow: auto;"><div style="height: 1500px;">abc<br>def<br></div></div>'; 206 editor = doc.getElementById("editor"); 207 container = editor; 208 editor.focus(); 209 await waitToClearOutAnyPotentialScrolls(aWindow); 210 211 description = "PageDown in scrollable editing host"; 212 previousScrollTop = container.scrollTop; 213 await doPageDown(editor, container); 214 ok(container.scrollTop > previousScrollTop, 215 `${description} #1: Should be scrolled down (got: ${container.scrollTop}, previous position: ${previousScrollTop})`); 216 previousScrollTop = container.scrollTop; 217 await doPageDown(editor, container); 218 ok(container.scrollTop > previousScrollTop, 219 `${description} #2: should be scrolled down (got:${container.scrollTop}, previous position: ${previousScrollTop})`); 220 previousScrollTop = container.scrollTop; 221 await doPageDown(editor, container); 222 ok(container.scrollTop > previousScrollTop, 223 `${description} #3: should be scrolled down (got:${container.scrollTop}, previous position: ${previousScrollTop})`); 224 await doPageUp(editor, container); 225 ok(container.scrollTop < 300, 226 `PageUp in scrollable editing host after scrolled down 3 pages: should be scrolled up to show caret (got:${container.scrollTop}`); 227 228 // Shouldn't scroll to caret position after pagedown scrolls outside of editing host. 229 // NOTE: We've set the window height is 500px above, but on Android, the viewport size depends on the screen size. 230 // Therefore, we need to compute enough height to test below with actual height of the window. 231 body.innerHTML = `<div id="editor" contenteditable style="height: ${aWindow.innerHeight * 3}px">abc<br>def<br></div>`; 232 editor = doc.getElementById("editor"); 233 container = doc.documentElement; 234 editor.focus(); 235 selection.collapse(editor.firstChild); 236 await waitToClearOutAnyPotentialScrolls(aWindow); 237 238 description = "PageDown in too high non-scrollable editing host"; 239 previousScrollTop = container.scrollTop; 240 await doPageDown(editor, container); 241 ok(container.scrollTop > previousScrollTop, 242 `${description} #1: Should be scrolled down (got: ${container.scrollTop}, previous position: ${previousScrollTop})`); 243 previousScrollTop = container.scrollTop; 244 await doPageDown(editor, container); 245 ok(container.scrollTop > previousScrollTop, 246 `${description} #2: should be scrolled down (got:${container.scrollTop}, previous position: ${previousScrollTop})`); 247 previousScrollTop = container.scrollTop; 248 await doPageDown(editor, container); 249 ok(container.scrollTop > previousScrollTop, 250 `${description} #3: should be scrolled down (got:${container.scrollTop}, previous position: ${previousScrollTop})`); 251 await doPageUp(editor, container); 252 ok(container.scrollTop < 300, 253 `PageUp in too high non-scrollable editing host after scrolled down 3 pages: should be scrolled up to show caret (got:${container.scrollTop}`); 254 255 aWindow.close(); 256 SimpleTest.finish(); 257 } 258 </script> 259 </html>