Bug 1389803 - Scroll the console to the bottom on Evaluation result. r=bgrins draft
authorNicolas Chevobbe <nchevobbe@mozilla.com>
Fri, 18 Aug 2017 17:46:52 +0200
changeset 649707 ca79a1d7751ddd1d3a975dcfa60319f025e6588e
parent 648573 a6a1f5c1d971dbee67ba6eec7ead7902351ddca2
child 727149 db02b1b161ce21f6f7de01a81935aa53e19b734a
push id75109
push userbmo:nchevobbe@mozilla.com
push dateMon, 21 Aug 2017 06:54:46 +0000
reviewersbgrins
bugs1389803
milestone57.0a1
Bug 1389803 - Scroll the console to the bottom on Evaluation result. r=bgrins This ensures that we scroll to the bottom when the user evaluate something in the console. A test is added to make sure this works as expected. MozReview-Commit-ID: Arh6rftQeKo
devtools/client/webconsole/new-console-output/components/console-output.js
devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_scroll.js
--- a/devtools/client/webconsole/new-console-output/components/console-output.js
+++ b/devtools/client/webconsole/new-console-output/components/console-output.js
@@ -17,16 +17,19 @@ const {
   getAllMessagesTableDataById,
   getAllMessagesObjectPropertiesById,
   getAllMessagesObjectEntriesById,
   getAllNetworkMessagesUpdateById,
   getVisibleMessages,
   getAllRepeatById,
 } = require("devtools/client/webconsole/new-console-output/selectors/messages");
 const MessageContainer = createFactory(require("devtools/client/webconsole/new-console-output/components/message-container").MessageContainer);
+const {
+  MESSAGE_TYPE,
+} = require("devtools/client/webconsole/new-console-output/constants");
 
 const ConsoleOutput = createClass({
 
   displayName: "ConsoleOutput",
 
   propTypes: {
     messages: PropTypes.object.isRequired,
     messagesUi: PropTypes.object.isRequired,
@@ -55,21 +58,30 @@ const ConsoleOutput = createClass({
   },
 
   componentWillUpdate(nextProps, nextState) {
     const outputNode = this.outputNode;
     if (!outputNode || !outputNode.lastChild) {
       return;
     }
 
-    // Figure out if we are at the bottom. If so, then any new message should be scrolled
-    // into view.
     const lastChild = outputNode.lastChild;
-    const delta = nextProps.visibleMessages.length - this.props.visibleMessages.length;
-    this.shouldScrollBottom = delta > 0 && isScrolledToBottom(lastChild, outputNode);
+    const visibleMessagesDelta =
+      nextProps.visibleMessages.length - this.props.visibleMessages.length;
+    const messagesDelta =
+      nextProps.messages.length - this.props.messages.length;
+
+    // We need to scroll to the bottom if:
+    // - the number of messages displayed changed
+    //   and we are already scrolled to the bottom
+    // - the number of messages in the store changed
+    //   and the new message is an evaluation result.
+    this.shouldScrollBottom =
+      (messagesDelta > 0 && nextProps.messages.last().type === MESSAGE_TYPE.RESULT) ||
+      (visibleMessagesDelta > 0 && isScrolledToBottom(lastChild, outputNode));
   },
 
   componentDidUpdate() {
     if (this.shouldScrollBottom) {
       scrollToBottom(this.outputNode);
     }
   },
 
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
@@ -49,16 +49,17 @@ skip-if = (os == 'linux' && bits == 32 &
 [browser_webconsole_location_styleeditor_link.js]
 [browser_webconsole_logErrorInPage.js]
 [browser_webconsole_network_messages_click.js]
 [browser_webconsole_nodes_highlight.js]
 [browser_webconsole_nodes_select.js]
 [browser_webconsole_object_inspector_entries.js]
 [browser_webconsole_object_inspector.js]
 [browser_webconsole_observer_notifications.js]
+[browser_webconsole_scroll.js]
 [browser_webconsole_shows_reqs_in_netmonitor.js]
 [browser_webconsole_sourcemap_error.js]
 [browser_webconsole_sourcemap_nosource.js]
 [browser_webconsole_stacktrace_location_debugger_link.js]
 [browser_webconsole_stacktrace_location_scratchpad_link.js]
 [browser_webconsole_string.js]
 [browser_webconsole_timestamps.js]
 [browser_webconsole_warn_about_replaced_api.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_scroll.js
@@ -0,0 +1,69 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,<p>Web Console test for " +
+                 "scroll behavior";
+add_task(async function () {
+  const hud = await openNewTabAndConsole(TEST_URI);
+  let {ui} = hud;
+
+  info("Waiting for logged messages");
+
+  let receievedMessages = waitForMessages({hud, messages: [{
+    text: "init-99"
+  }]});
+  ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
+    for (let i = 0; i < 100; i++) {
+      content.wrappedJSObject.console.log("init-" + i);
+    }
+  });
+  await receievedMessages;
+
+  const outputContainer = ui.outputNode.querySelector(".webconsole-output");
+  ok(outputContainer.scrollHeight > outputContainer.clientHeight,
+    "There is a vertical overflow");
+
+  info("Scroll up");
+  outputContainer.scrollTop = 0;
+
+  info("Add a message to check that the scroll isn't impacted");
+  receievedMessages = waitForMessages({hud, messages: [{
+    text: "stay"
+  }]});
+  ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
+    content.wrappedJSObject.console.log("stay");
+  });
+  await receievedMessages;
+  is(outputContainer.scrollTop, 0, "The console stayed scrolled to the top");
+
+  info("Evaluate a command to check that the console scrolls to the bottom");
+  receievedMessages = waitForMessages({hud, messages: [{
+    text: "42"
+  }]});
+  ui.jsterm.execute("21 + 21");
+  await receievedMessages;
+  is(isScrolledToBottom(outputContainer), true, "The console is scrolled to the bottom");
+
+  info("Add a message to check that the console do scroll since we're at the bottom");
+  receievedMessages = waitForMessages({hud, messages: [{
+    text: "scroll"
+  }]});
+  ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
+    content.wrappedJSObject.console.log("scroll");
+  });
+  await receievedMessages;
+  is(isScrolledToBottom(outputContainer), true, "The console is scrolled to the bottom");
+});
+
+function isScrolledToBottom(container) {
+  if (!container.lastChild) {
+    return true;
+  }
+  let lastNodeHeight = container.lastChild.clientHeight;
+  return container.scrollTop + container.clientHeight >=
+         container.scrollHeight - lastNodeHeight / 2;
+}