diff --git a/devtools/client/webconsole/components/ConsoleOutput.js b/devtools/client/webconsole/components/ConsoleOutput.js index 2d06c1e8d6d6..1b9cceff544c 100644 --- a/devtools/client/webconsole/components/ConsoleOutput.js +++ b/devtools/client/webconsole/components/ConsoleOutput.js @@ -67,6 +67,7 @@ class ConsoleOutput extends Component { constructor(props) { super(props); this.onContextMenu = this.onContextMenu.bind(this); + this.maybeScrollToBottom = this.maybeScrollToBottom.bind(this); } componentDidMount() { @@ -125,6 +126,10 @@ class ConsoleOutput extends Component { } componentDidUpdate() { + this.maybeScrollToBottom(); + } + + maybeScrollToBottom() { if (this.shouldScrollBottom) { scrollToBottom(this.outputNode); } @@ -177,6 +182,7 @@ class ConsoleOutput extends Component { pausedExecutionPoint, getMessage: () => messages.get(messageId), isPaused: pausedMessage && pausedMessage.id == messageId, + maybeScrollToBottom: this.maybeScrollToBottom, })); return ( diff --git a/devtools/client/webconsole/components/GripMessageBody.js b/devtools/client/webconsole/components/GripMessageBody.js index 0a8ff4db4ba0..46d777e9bc7d 100644 --- a/devtools/client/webconsole/components/GripMessageBody.js +++ b/devtools/client/webconsole/components/GripMessageBody.js @@ -36,6 +36,7 @@ GripMessageBody.propTypes = { escapeWhitespace: PropTypes.bool, type: PropTypes.string, helperType: PropTypes.string, + maybeScrollToBottom: PropTypes.func, }; GripMessageBody.defaultProps = { @@ -51,6 +52,7 @@ function GripMessageBody(props) { escapeWhitespace, mode = MODE.LONG, dispatch, + maybeScrollToBottom, } = props; let styleObject; @@ -61,6 +63,7 @@ function GripMessageBody(props) { const objectInspectorProps = { autoExpandDepth: shouldAutoExpandObjectInspector(props) ? 1 : 0, mode, + maybeScrollToBottom, // TODO: we disable focus since the tabbing trail is a bit weird in the output (e.g. // location links are not focused). Let's remove the property below when we found and // fixed the issue (See Bug 1456060). diff --git a/devtools/client/webconsole/components/Message.js b/devtools/client/webconsole/components/Message.js index 8899aaf33064..93e1af35d9fd 100644 --- a/devtools/client/webconsole/components/Message.js +++ b/devtools/client/webconsole/components/Message.js @@ -70,6 +70,7 @@ class Message extends Component { frame: PropTypes.any, })), isPaused: PropTypes.bool, + maybeScrollToBottom: PropTypes.func, }; } @@ -210,6 +211,7 @@ class Message extends Component { onViewSourceInScratchpad: serviceContainer.onViewSourceInScratchpad || serviceContainer.onViewSource, onViewSource: serviceContainer.onViewSource, + onReady: this.props.maybeScrollToBottom, sourceMapService: serviceContainer.sourceMapService, }), ); diff --git a/devtools/client/webconsole/components/message-types/ConsoleApiCall.js b/devtools/client/webconsole/components/message-types/ConsoleApiCall.js index bfcf2991e3b5..72db471c7b40 100644 --- a/devtools/client/webconsole/components/message-types/ConsoleApiCall.js +++ b/devtools/client/webconsole/components/message-types/ConsoleApiCall.js @@ -24,6 +24,7 @@ ConsoleApiCall.propTypes = { open: PropTypes.bool, serviceContainer: PropTypes.object.isRequired, timestampsVisible: PropTypes.bool.isRequired, + maybeScrollToBottom: PropTypes.func, }; ConsoleApiCall.defaultProps = { @@ -41,6 +42,7 @@ function ConsoleApiCall(props) { repeat, pausedExecutionPoint, isPaused, + maybeScrollToBottom, } = props; const { id: messageId, @@ -66,6 +68,7 @@ function ConsoleApiCall(props) { userProvidedStyles, serviceContainer, type, + maybeScrollToBottom, }; if (type === "trace") { @@ -137,6 +140,7 @@ function ConsoleApiCall(props) { timeStamp, timestampsVisible, parameters, + maybeScrollToBottom, }); } @@ -150,6 +154,7 @@ function formatReps(options = {}) { serviceContainer, userProvidedStyles, type, + maybeScrollToBottom, } = options; return ( @@ -166,6 +171,7 @@ function formatReps(options = {}) { loadedObjectProperties, loadedObjectEntries, type, + maybeScrollToBottom, })) // Interleave spaces. .reduce((arr, v, i) => { diff --git a/devtools/client/webconsole/components/message-types/ConsoleCommand.js b/devtools/client/webconsole/components/message-types/ConsoleCommand.js index 0aa9e3ec8ca5..2fcbea2cf046 100644 --- a/devtools/client/webconsole/components/message-types/ConsoleCommand.js +++ b/devtools/client/webconsole/components/message-types/ConsoleCommand.js @@ -17,6 +17,7 @@ ConsoleCommand.propTypes = { message: PropTypes.object.isRequired, timestampsVisible: PropTypes.bool.isRequired, serviceContainer: PropTypes.object, + maybeScrollToBottom: PropTypes.func, }; /** @@ -27,6 +28,7 @@ function ConsoleCommand(props) { message, timestampsVisible, serviceContainer, + maybeScrollToBottom, } = props; const { @@ -51,6 +53,7 @@ function ConsoleCommand(props) { indent, timeStamp, timestampsVisible, + maybeScrollToBottom, }); } diff --git a/devtools/client/webconsole/components/message-types/EvaluationResult.js b/devtools/client/webconsole/components/message-types/EvaluationResult.js index d4837af963cb..497a3b67e550 100644 --- a/devtools/client/webconsole/components/message-types/EvaluationResult.js +++ b/devtools/client/webconsole/components/message-types/EvaluationResult.js @@ -19,6 +19,7 @@ EvaluationResult.propTypes = { message: PropTypes.object.isRequired, timestampsVisible: PropTypes.bool.isRequired, serviceContainer: PropTypes.object, + maybeScrollToBottom: PropTypes.func, }; function EvaluationResult(props) { @@ -27,6 +28,7 @@ function EvaluationResult(props) { message, serviceContainer, timestampsVisible, + maybeScrollToBottom, } = props; const { @@ -63,6 +65,7 @@ function EvaluationResult(props) { escapeWhitespace: false, type, helperType, + maybeScrollToBottom, }); } @@ -83,6 +86,7 @@ function EvaluationResult(props) { parameters, notes, timestampsVisible, + maybeScrollToBottom, }); } diff --git a/devtools/client/webconsole/components/message-types/PageError.js b/devtools/client/webconsole/components/message-types/PageError.js index 3c5fcf281424..71dfed37f4fb 100644 --- a/devtools/client/webconsole/components/message-types/PageError.js +++ b/devtools/client/webconsole/components/message-types/PageError.js @@ -18,6 +18,7 @@ PageError.propTypes = { open: PropTypes.bool, timestampsVisible: PropTypes.bool.isRequired, serviceContainer: PropTypes.object, + maybeScrollToBottom: PropTypes.func, }; PageError.defaultProps = { @@ -33,6 +34,7 @@ function PageError(props) { serviceContainer, timestampsVisible, isPaused, + maybeScrollToBottom, } = props; const { id: messageId, @@ -77,6 +79,7 @@ function PageError(props) { timeStamp, notes, timestampsVisible, + maybeScrollToBottom, }); } diff --git a/devtools/client/webconsole/test/mochitest/browser_webconsole_scroll.js b/devtools/client/webconsole/test/mochitest/browser_webconsole_scroll.js index 49ef23d7db64..91eba671d79f 100644 --- a/devtools/client/webconsole/test/mochitest/browser_webconsole_scroll.js +++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_scroll.js @@ -8,9 +8,16 @@ const TEST_URI = `data:text/html;charset=utf-8,

Web Console test for scroll.

`; add_task(async function() { @@ -23,6 +30,10 @@ add_task(async function() { ok(hasVerticalOverflow(outputContainer), "There is a vertical overflow"); ok(isScrolledToBottom(outputContainer), "The console is scrolled to the bottom"); + info("Wait until all stacktraces are rendered"); + await waitFor(() => outputContainer.querySelectorAll(".frames").length === 10); + ok(isScrolledToBottom(outputContainer), "The console is scrolled to the bottom"); + await refreshTab(); info("Console should be scrolled to bottom after refresh from page logs"); @@ -30,39 +41,68 @@ add_task(async function() { ok(hasVerticalOverflow(outputContainer), "There is a vertical overflow"); ok(isScrolledToBottom(outputContainer), "The console is scrolled to the bottom"); + info("Wait until all stacktraces are rendered"); + await waitFor(() => outputContainer.querySelectorAll(".frames").length === 10); + ok(isScrolledToBottom(outputContainer), "The console is scrolled to the bottom"); + info("Scroll up"); outputContainer.scrollTop = 0; - info("Add a message to check that the scroll isn't impacted"); - let receievedMessages = waitForMessages({hud, messages: [{ - text: "stay", - }]}); + info("Add a console.trace message to check that the scroll isn't impacted"); + let onMessage = waitForMessage(hud, "trace in C"); ContentTask.spawn(gBrowser.selectedBrowser, {}, function() { - content.wrappedJSObject.console.log("stay"); + content.wrappedJSObject.c(); }); - await receievedMessages; + let message = await onMessage; ok(hasVerticalOverflow(outputContainer), "There is a vertical overflow"); is(outputContainer.scrollTop, 0, "The console stayed scrolled to the top"); + info("Wait until the stacktrace is rendered"); + await waitFor(() => message.node.querySelector(".frame")); + 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", - }]}); + onMessage = waitForMessage(hud, "42"); ui.jsterm.execute("21 + 21"); - await receievedMessages; + await onMessage; ok(hasVerticalOverflow(outputContainer), "There is a vertical overflow"); ok(isScrolledToBottom(outputContainer), "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", - }]}); + onMessage = waitForMessage(hud, "scroll"); ContentTask.spawn(gBrowser.selectedBrowser, {}, function() { content.wrappedJSObject.console.log("scroll"); }); - await receievedMessages; + await onMessage; ok(hasVerticalOverflow(outputContainer), "There is a vertical overflow"); ok(isScrolledToBottom(outputContainer), "The console is scrolled to the bottom"); + + info("Evaluate an Error object to check that the console scrolls to the bottom"); + onMessage = waitForMessage(hud, "myErrorObject", ".message.result"); + ui.jsterm.execute(` + x = new Error("myErrorObject"); + x.stack = "a@b/c.js:1:2\\nd@e/f.js:3:4"; + x;` + ); + message = await onMessage; + ok(isScrolledToBottom(outputContainer), "The console is scrolled to the bottom"); + + info("Wait until the stacktrace is rendered and check the console is scrolled"); + await waitFor(() => message.node.querySelector(".objectBox-stackTrace .frame")); + ok(isScrolledToBottom(outputContainer), "The console is scrolled to the bottom"); + + info("Add a console.trace message to check that the console stays scrolled to bottom"); + onMessage = waitForMessage(hud, "trace in C"); + ContentTask.spawn(gBrowser.selectedBrowser, {}, function() { + content.wrappedJSObject.c(); + }); + message = await onMessage; + ok(hasVerticalOverflow(outputContainer), "There is a vertical overflow"); + ok(isScrolledToBottom(outputContainer), "The console is scrolled to the bottom"); + + info("Wait until the stacktrace is rendered"); + await waitFor(() => message.node.querySelector(".frame")); + ok(isScrolledToBottom(outputContainer), "The console is scrolled to the bottom"); }); function hasVerticalOverflow(container) { diff --git a/devtools/client/webconsole/utils/object-inspector.js b/devtools/client/webconsole/utils/object-inspector.js index d0a725cc8432..c6bda44f64e7 100644 --- a/devtools/client/webconsole/utils/object-inspector.js +++ b/devtools/client/webconsole/utils/object-inspector.js @@ -62,6 +62,7 @@ function getObjectInspector(grip, serviceContainer, override = {}) { ? serviceContainer.onViewSourceInScratchpad || serviceContainer.onViewSource : null, onViewSource: serviceContainer.onViewSource, + onReady: override.maybeScrollToBottom, sourceMapService: serviceContainer ? serviceContainer.sourceMapService : null, }), };