<!DOCTYPE html> <html> <head> <title>Test for scroll per page</title> <script src="/tests/SimpleTest/SimpleTest.js"></script> <script src="/tests/SimpleTest/EventUtils.js"></script> <script type="text/javascript" src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <pre id="test"> <script class="testbody" type="text/javascript"> SimpleTest.waitForExplicitFinish(); addLoadEvent(() => { open("window_empty_document.html", "_blank", "width=500,height=500,scrollbars=yes"); }); async function doTests(aWindow) { await SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false]]}); await SimpleTest.promiseFocus(aWindow); function getNodeDescription(aNode) { function getElementDescription(aElement) { if (aElement.getAttribute("id") !== null) { return `${aElement.tagName.toLowerCase()}#${aElement.getAttribute("id")}`; } if (aElement.tagName === "BR") { return `${getElementDescription(aElement.previousSibling)} + br`; } return aElement.tagName.toLowerCase(); } switch (aNode.nodeType) { case aNode.TEXT_NODE: return `text node in ${getElementDescription(aNode.parentElement)}`; case aNode.ELEMENT_NODE: return getElementDescription(aNode); case aNode.DOCUMENT_NODE: return `document node`; default: return "unknown node"; } } function getScrollPositionStr(aNode) { return `{ scrollTop: ${aNode.scrollTop}, scrollHeight: ${ aNode.scrollHeight }, scrollLeft: ${aNode.scrollLeft}, scrollWidth: ${aNode.scrollWidth} }`; } async function doPageDownOrUp(aKey, aFocusedElement, aScrollTargetElement) { let scrollEventTarget = aScrollTargetElement === doc.documentElement ? doc : aScrollTargetElement; let scrollEventFired = false; function onScroll(aEvent) { scrollEventFired |= aEvent.target === scrollEventTarget; } scrollEventTarget.addEventListener("scroll", onScroll); if (!navigator.platform.includes("Mac")) { synthesizeKey(`KEY_${aKey}`, {}, aWindow); } else { synthesizeKey(`KEY_${aKey}`, { altKey: true }, aWindow); } let retry = 3; while (retry--) { await waitToClearOutAnyPotentialScrolls(aWindow); if (scrollEventFired) { break; } } ok(scrollEventFired, `Scroll event should've been fired on ${getNodeDescription(scrollEventTarget)}`); scrollEventTarget.removeEventListener("scroll", onScroll); } async function doPageDown(aFocusedElement, aScrollTargetElement) { await doPageDownOrUp("PageDown", aFocusedElement, aScrollTargetElement); } async function doPageUp(aFocusedElement, aScrollTargetElement) { await doPageDownOrUp("PageUp", aFocusedElement, aScrollTargetElement); } // Let's put log of scroll events for making debug this test easier. aWindow.addEventListener("scroll", (aEvent) => { let scrollElement = aEvent.target === doc ? doc.documentElement : aEvent.target; info(`"scroll" event fired on ${getNodeDescription(aEvent.target)}: ${ getScrollPositionStr(scrollElement) }`); }, { capture: true }); let doc = aWindow.document; let body = doc.body; let selection = doc.getSelection(); let container; body.innerHTML = '<div id="largeDiv" style="height: 1500px;">' + "<p>previous line of the editor.</p>" + '<div id="editor" contenteditable style="margin-top 500px; height: 5em; overflow: auto;">' + "Here is first line<br>" + "Here is second line" + "</div>" + "<p>next line of the editor.</p>" + "</div>"; container = doc.documentElement; let editor = doc.getElementById("editor"); editor.focus(); await waitToClearOutAnyPotentialScrolls(aWindow); let description = "PageDown in non-scrollable editing host: "; let previousScrollTop = container.scrollTop; await doPageDown(editor, container); ok(container.scrollTop > previousScrollTop, `${description}the document should be scrolled down even if user presses PageDown in the editing host got: ${container.scrollTop}, previous position: ${previousScrollTop}`); let range = selection.getRangeAt(0); is(range.startContainer, editor.firstChild.nextSibling.nextSibling, `${description}selection start shouldn't be moved to outside of the editing host (got: ${getNodeDescription(range.startContainer)})`); ok(range.collapsed, description + "selection should be collapsed"); is(doc.activeElement, editor, description + "the editing host should keep having focus"); description = "PageUp in non-scrollable editing host: "; previousScrollTop = container.scrollTop; await doPageUp(editor, container); ok(container.scrollTop < previousScrollTop, `${description}the document should be scrolled up even if user presses PageDown in the editing host got: ${container.scrollTop}, previous position: ${previousScrollTop}`); range = selection.getRangeAt(0); is(range.startContainer, editor.firstChild, `${description}selection start shouldn't be moved to outside of the editing host (got: ${getNodeDescription(range.startContainer)})`); ok(range.collapsed, description + "selection should be collapsed"); is(doc.activeElement, editor, description + "the editing host should keep having focus"); body.innerHTML = '<div id="largeDiv" style="height: 1500px;">' + "<p>previous line of the editor.</p>" + '<div id="editor" contenteditable style="margin-top 500px; height: 5em; overflow: auto;">' + '<div id="innerDiv" style="height: 10em;">' + "Here is first line<br>" + "Here is second line" + "</div>" + "</div>" + "<p>next line of the editor.</p>" + "</div>"; editor = doc.getElementById("editor"); container = editor; editor.focus(); await waitToClearOutAnyPotentialScrolls(aWindow); description = "PageDown in scrollable editing host: "; previousScrollTop = container.scrollTop; await doPageDown(editor, container); ok(container.scrollTop > previousScrollTop, `${description}the editor should be scrolled down even if user presses PageDown in the editing host got: ${container.scrollTop}, previous position: ${previousScrollTop}`); range = selection.getRangeAt(0); is(range.startContainer, editor.firstChild.firstChild.nextSibling.nextSibling, `${description}selection start shouldn't be moved to outside of the editing host (got: ${getNodeDescription(range.startContainer)})`); ok(range.collapsed, description + "selection should be collapsed"); is(doc.activeElement, editor, description + "the editing host should keep having focus"); description = "PageUp in scrollable editing host: "; previousScrollTop = container.scrollTop; await doPageUp(editor, container); ok(container.scrollTop < previousScrollTop, `${description}the editor should be scrolled up even if user presses PageDown in the editing host got: ${container.scrollTop}, previous position: ${previousScrollTop}`); range = selection.getRangeAt(0); is(range.startContainer, editor.firstChild.firstChild, `${description}selection start shouldn't be moved to outside of the editing host (got: ${getNodeDescription(range.startContainer)})`); ok(range.collapsed, description + "selection should be collapsed"); is(doc.activeElement, editor, description + "the editing host should keep having focus"); // Should scroll one page of the scrollable element body.innerHTML = `<div id="editor" contenteditable style="height: 1500px;">${"abc<br>".repeat(100)}</div>`; editor = doc.getElementById("editor"); container = doc.documentElement; editor.focus(); await waitToClearOutAnyPotentialScrolls(aWindow); description = "PageDown in too large editing host: "; previousScrollTop = container.scrollTop; await doPageDown(editor, container); ok(container.scrollTop > previousScrollTop, `${description} The document should be scrolled down (got: ${container.scrollTop}, previous position: ${previousScrollTop})`); ok(container.scrollTop <= previousScrollTop + container.clientHeight, `${description} The document should not be scrolled down too much (got: ${container.scrollTop}, previous position: ${previousScrollTop}, scroll height: ${container.clientHeight})`); selection.selectAllChildren(editor); selection.collapseToEnd(); await waitToClearOutAnyPotentialScrolls(aWindow); description = "PageUp in too large editing host: "; container.scrollTop = container.scrollHeight; previousScrollTop = container.scrollTop; await doPageUp(editor, container); ok(container.scrollTop >= previousScrollTop - container.clientHeight, `${description} The document should not be scrolled up too much (got: ${container.scrollTop}, previous position: ${previousScrollTop}, scroll height: ${container.clientHeight})`); // Shouldn't scroll to caret position after pagedown scrolls editing host. body.innerHTML = '<div id="editor" contenteditable style="height: 300px; overflow: auto;"><div style="height: 1500px;">abc<br>def<br></div></div>'; editor = doc.getElementById("editor"); container = editor; editor.focus(); await waitToClearOutAnyPotentialScrolls(aWindow); description = "PageDown in scrollable editing host"; previousScrollTop = container.scrollTop; await doPageDown(editor, container); ok(container.scrollTop > previousScrollTop, `${description} #1: Should be scrolled down (got: ${container.scrollTop}, previous position: ${previousScrollTop})`); previousScrollTop = container.scrollTop; await doPageDown(editor, container); ok(container.scrollTop > previousScrollTop, `${description} #2: should be scrolled down (got:${container.scrollTop}, previous position: ${previousScrollTop})`); previousScrollTop = container.scrollTop; await doPageDown(editor, container); ok(container.scrollTop > previousScrollTop, `${description} #3: should be scrolled down (got:${container.scrollTop}, previous position: ${previousScrollTop})`); await doPageUp(editor, container); ok(container.scrollTop < 300, `PageUp in scrollable editing host after scrolled down 3 pages: should be scrolled up to show caret (got:${container.scrollTop}`); // Shouldn't scroll to caret position after pagedown scrolls outside of editing host. // NOTE: We've set the window height is 500px above, but on Android, the viewport size depends on the screen size. // Therefore, we need to compute enough height to test below with actual height of the window. body.innerHTML = `<div id="editor" contenteditable style="height: ${aWindow.innerHeight * 3}px">abc<br>def<br></div>`; editor = doc.getElementById("editor"); container = doc.documentElement; editor.focus(); selection.collapse(editor.firstChild); await waitToClearOutAnyPotentialScrolls(aWindow); description = "PageDown in too high non-scrollable editing host"; previousScrollTop = container.scrollTop; await doPageDown(editor, container); ok(container.scrollTop > previousScrollTop, `${description} #1: Should be scrolled down (got: ${container.scrollTop}, previous position: ${previousScrollTop})`); previousScrollTop = container.scrollTop; await doPageDown(editor, container); ok(container.scrollTop > previousScrollTop, `${description} #2: should be scrolled down (got:${container.scrollTop}, previous position: ${previousScrollTop})`); previousScrollTop = container.scrollTop; await doPageDown(editor, container); ok(container.scrollTop > previousScrollTop, `${description} #3: should be scrolled down (got:${container.scrollTop}, previous position: ${previousScrollTop})`); await doPageUp(editor, container); ok(container.scrollTop < 300, `PageUp in too high non-scrollable editing host after scrolled down 3 pages: should be scrolled up to show caret (got:${container.scrollTop}`); aWindow.close(); SimpleTest.finish(); } </script> </html>