Bug 1498816 - part 0: Add automated tests of PageDown/PageUp key handling in an editing host r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 28 Nov 2018 11:04:09 +0000
changeset 505116 3ea2a2c78bf4454226971e87036de12fafeadc4c
parent 505100 c302586458e0a418e972652dfc4351380066c319
child 505117 0a0c7f90e571e351952d2b292641187ee70815ca
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1498816
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1498816 - part 0: Add automated tests of PageDown/PageUp key handling in an editing host r=smaug Even if there is no scrollable element is in focused editing host, its parent scrollable element should be scrolled. However, focus shouldn't be moved and selection ranges should be kept in the editing host. Differential Revision: https://phabricator.services.mozilla.com/D13201
layout/base/tests/mochitest.ini
layout/base/tests/test_scroll_per_page.html
--- a/layout/base/tests/mochitest.ini
+++ b/layout/base/tests/mochitest.ini
@@ -360,16 +360,18 @@ support-files =
   bug1506547-6.html
   bug1506547-4-ref.html
   bug1506547-5-ref.html
 
 [test_remote_frame.html]
 [test_resize_flush.html]
 support-files = resize_flush_iframe.html
 [test_scroll_event_ordering.html]
+[test_scroll_per_page.html]
+support-files = window_empty_document.html
 [test_scroll_selection_into_view.html]
 skip-if = toolkit == 'android' # Bug 1355844
 support-files =
   scroll_selection_into_view_window.html
   scroll_selection_into_view_window_frame.html
 [test_scroll_snapping.html]
 skip-if = toolkit == 'android' # Bug 1355851
 [test_scroll_snapping_scrollbars.html]
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/test_scroll_per_page.html
@@ -0,0 +1,177 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Test for scroll per page</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.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");
+});
+
+async function doTests(aWindow) {
+  const IS_WIN = navigator.platform.includes("Win");
+  // On macOS and Linux, PageUp/PageDown requires native event to resolve
+  // default action of PageDown and PageUp. Although macOS widget has
+  // nsIWidget::AttachNativeKeyEvent(), we cannot use synthesizeKey() for the
+  // following tests.  So, use nsISelectionController.pageMove() instead on
+  // non-Windows platforms.
+  const kUseKeyboardEvent = IS_WIN;
+  let selectionController;
+  if (!kUseKeyboardEvent) {
+    selectionController = SpecialPowers.wrap(aWindow)
+                                       .docShell
+                                       .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
+                                       .getInterface(SpecialPowers.Ci.nsISelectionDisplay)
+                                       .QueryInterface(SpecialPowers.Ci.nsISelectionController);
+  }
+
+  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);
+      default:
+        return "unknown node";
+    }
+  }
+
+  function waitForScrollEvent() {
+    return new Promise(resolve => {
+  	  aWindow.addEventListener("scroll", () => { resolve(); }, {once: true, capture: true});
+	  });
+  }
+
+  async function doPageDown() {
+    let waitForScrolling = waitForScrollEvent();
+    if (kUseKeyboardEvent) {
+      synthesizeKey("KEY_PageDown", {}, aWindow);
+    } else {
+      selectionController.pageMove(true, false);
+	  }
+	  await waitForScrolling;
+  }
+
+  async function doPageUp() {
+    let waitForScrolling = waitForScrollEvent();
+    if (kUseKeyboardEvent) {
+      synthesizeKey("KEY_PageUp", {}, aWindow);
+    } else {
+      selectionController.pageMove(false, false);
+	  }
+	  await waitForScrolling;
+  }
+
+  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="mergin-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();
+
+  let description = "PageDown in non-scrollable editing host: ";
+  let previousScrollTop = container.scrollTop;
+  await doPageDown();
+  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);
+  todo_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");
+  if (kUseKeyboardEvent) {
+    todo_is(doc.activeElement, editor,
+            description + "the editing host should keep having focus");
+  } else {
+    is(doc.activeElement, editor,
+       description + "the editing host should keep having focus");
+  }
+
+  description = "PageUp in non-scrollable editing host: ";
+  previousScrollTop = container.scrollTop;
+  await doPageUp();
+  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);
+  todo_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");
+  if (kUseKeyboardEvent) {
+    todo_is(doc.activeElement, editor,
+            description + "the editing host should keep having focus");
+  } else {
+    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="mergin-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();
+
+  description = "PageDown in scrollable editing host: ";
+  previousScrollTop = container.scrollTop;
+  await doPageDown();
+  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();
+  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");
+
+  aWindow.close();
+  SimpleTest.finish();
+}
+</script>
+</html>