From cb89693561fed4c49f79dd308f117aba4a6350d2 Mon Sep 17 00:00:00 2001 From: Claudia Date: Thu, 2 Sep 2021 15:22:19 +0000 Subject: [PATCH] Bug 1358383 - [devtools] Adds a command history list. r=bomsy Differential Revision: https://phabricator.services.mozilla.com/D122264 --- devtools/client/themes/webconsole.css | 44 ++++++ devtools/client/webconsole/actions/input.js | 27 +++- .../components/Output/MessageContainer.js | 7 + .../Output/message-types/SimpleTable.js | 131 ++++++++++++++++++ .../components/Output/message-types/moz.build | 1 + devtools/client/webconsole/constants.js | 1 + .../webconsole/test/browser/_jsterm.ini | 1 + .../browser_jsterm_autocomplete_commands.js | 8 +- .../browser/browser_jsterm_history_command.js | 43 ++++++ devtools/client/webconsole/utils/messages.js | 13 ++ devtools/server/actors/webconsole/commands.js | 2 +- devtools/server/actors/webconsole/utils.js | 18 +++ 12 files changed, 293 insertions(+), 3 deletions(-) create mode 100644 devtools/client/webconsole/components/Output/message-types/SimpleTable.js create mode 100644 devtools/client/webconsole/test/browser/browser_jsterm_history_command.js diff --git a/devtools/client/themes/webconsole.css b/devtools/client/themes/webconsole.css index 427a00cc315c..5f8d1ce3309c 100644 --- a/devtools/client/themes/webconsole.css +++ b/devtools/client/themes/webconsole.css @@ -848,6 +848,50 @@ a.learn-more-link.webconsole-learn-more-link { background-color: var(--table-zebra-background); } +/* Simple Table */ +.message .simple-table { + width: 100%; + border-collapse: collapse; + --simpletable-border: var(--theme-splitter-color); + margin-block-end: var(--attachment-margin-block-end); + color: var(--theme-body-color); + text-align: left; + max-height: 250px; + overflow-y: auto; + border: 1px solid var(--simpletable-border); + table-layout: fixed; + margin-top: 3px; +} + +.simple-table-header { + background-color: var(--theme-toolbar-background); + text-overflow: ellipsis; + border-bottom: 1px solid var(--simpletable-border); +} + +.simple-table-header > th { + padding: 5px 4px; + font-weight: inherit; +} + +.simple-table-header > th:nth-child(odd) { + width: 10%; +} + +.simple-table td { + padding: 3px 4px; + text-overflow: ellipsis; + border-left: 1px solid var(--simpletable-border); +} + +.simple-table td:nth-child(2n) span { + color: var(--theme-body-color); +} + +.simple-table tr:nth-child(even) { + background-color: var(--table-zebra-background); +} + /* Object Inspector */ .webconsole-app .object-inspector.tree { display: inline-block; diff --git a/devtools/client/webconsole/actions/input.js b/devtools/client/webconsole/actions/input.js index 47e4b3ee1810..d18479af8ffa 100644 --- a/devtools/client/webconsole/actions/input.js +++ b/devtools/client/webconsole/actions/input.js @@ -49,6 +49,12 @@ loader.lazyRequireGetter( "devtools/client/shared/screenshot", true ); +loader.lazyRequireGetter( + this, + "createSimpleTableMessage", + "devtools/client/webconsole/utils/messages", + true +); const HELP_URL = "https://developer.mozilla.org/docs/Tools/Web_Console/Helpers"; @@ -168,7 +174,7 @@ function onExpressionEvaluated(response) { function handleHelperResult(response) { // eslint-disable-next-line complexity - return async ({ dispatch, hud, toolbox, webConsoleUI }) => { + return async ({ dispatch, hud, toolbox, webConsoleUI, getState }) => { const { result, helperResult } = response; const helperHasRawOutput = !!helperResult?.rawOutput; const hasNetworkResourceCommandSupport = hud.resourceCommand.hasResourceCommandSupport( @@ -188,6 +194,25 @@ function handleHelperResult(response) { case "clearHistory": dispatch(historyActions.clearHistory()); break; + case "historyOutput": + const history = getState().history.entries || []; + const columns = new Map([ + ["_index", "(index)"], + ["expression", "Expressions"], + ]); + dispatch( + messagesActions.messagesAdd([ + { + ...createSimpleTableMessage( + columns, + history.map((expression, index) => { + return { _index: index, expression }; + }) + ), + }, + ]) + ); + break; case "inspectObject": { const objectActor = helperResult.object; if (hud.toolbox && !helperResult.forceExpandInConsole) { diff --git a/devtools/client/webconsole/components/Output/MessageContainer.js b/devtools/client/webconsole/components/Output/MessageContainer.js index cbc4ed163b46..c57cd8ca3b41 100644 --- a/devtools/client/webconsole/components/Output/MessageContainer.js +++ b/devtools/client/webconsole/components/Output/MessageContainer.js @@ -52,6 +52,10 @@ const componentMap = new Map([ "PageError", require("devtools/client/webconsole/components/Output/message-types/PageError"), ], + [ + "SimpleTable", + require("devtools/client/webconsole/components/Output/message-types/SimpleTable"), + ], [ "WarningGroup", require("devtools/client/webconsole/components/Output/message-types/WarningGroup"), @@ -136,6 +140,9 @@ function getMessageComponent(message) { if (isWarningGroup(message)) { return componentMap.get("WarningGroup"); } + if (message.type === MESSAGE_TYPE.SIMPLE_TABLE) { + return componentMap.get("SimpleTable"); + } break; } diff --git a/devtools/client/webconsole/components/Output/message-types/SimpleTable.js b/devtools/client/webconsole/components/Output/message-types/SimpleTable.js new file mode 100644 index 000000000000..0a400da68800 --- /dev/null +++ b/devtools/client/webconsole/components/Output/message-types/SimpleTable.js @@ -0,0 +1,131 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { createFactory } = require("devtools/client/shared/vendor/react"); +const dom = require("devtools/client/shared/vendor/react-dom-factories"); + +const GripMessageBody = createFactory( + require("devtools/client/webconsole/components/Output/GripMessageBody") +); + +loader.lazyRequireGetter( + this, + "PropTypes", + "devtools/client/shared/vendor/react-prop-types" +); + +loader.lazyGetter(this, "MODE", function() { + return require("devtools/client/shared/components/reps/index").MODE; +}); + +const Message = createFactory( + require("devtools/client/webconsole/components/Output/Message") +); + +SimpleTable.displayName = "SimpleTable"; + +SimpleTable.propTypes = { + columns: PropTypes.object.isRequired, + items: PropTypes.array.isRequired, + dispatch: PropTypes.func.isRequired, + serviceContainer: PropTypes.object.isRequired, +}; + +function SimpleTable(props) { + const { + dispatch, + message, + serviceContainer, + timestampsVisible, + badge, + open, + } = props; + + const { + source, + type, + level, + id: messageId, + indent, + timeStamp, + columns, + items, + } = message; + + // if we don't have any data, don't show anything. + if (!items.length) { + return null; + } + const headerItems = []; + columns.forEach((value, key) => + headerItems.push( + dom.th( + { + key, + title: value, + }, + value + ) + ) + ); + + const rowItems = items.map((item, index) => { + const cells = []; + + columns.forEach((_, key) => { + const cellValue = item[key]; + const cellContent = + typeof cellValue === "undefined" + ? "" + : GripMessageBody({ + grip: cellValue, + mode: MODE.SHORT, + useQuotes: false, + serviceContainer, + dispatch, + }); + + cells.push( + dom.td( + { + key, + }, + cellContent + ) + ); + }); + return dom.tr({ key: index }, cells); + }); + + const attachment = dom.table( + { + className: "simple-table", + role: "grid", + }, + dom.thead({}, dom.tr({ className: "simple-table-header" }, headerItems)), + dom.tbody({}, rowItems) + ); + + const topLevelClasses = ["cm-s-mozilla"]; + return Message({ + attachment, + badge, + dispatch, + indent, + level, + messageId, + open, + serviceContainer, + source, + timeStamp, + timestampsVisible, + topLevelClasses, + type, + message, + messageBody: [], + }); +} + +module.exports = SimpleTable; diff --git a/devtools/client/webconsole/components/Output/message-types/moz.build b/devtools/client/webconsole/components/Output/message-types/moz.build index 5b24c72b7d3f..6535f1e7f032 100644 --- a/devtools/client/webconsole/components/Output/message-types/moz.build +++ b/devtools/client/webconsole/components/Output/message-types/moz.build @@ -11,5 +11,6 @@ DevToolsModules( "EvaluationResult.js", "NetworkEventMessage.js", "PageError.js", + "SimpleTable.js", "WarningGroup.js", ) diff --git a/devtools/client/webconsole/constants.js b/devtools/client/webconsole/constants.js index 065a0009fbe1..5fe6af8e8c57 100644 --- a/devtools/client/webconsole/constants.js +++ b/devtools/client/webconsole/constants.js @@ -173,6 +173,7 @@ const chromeRDPEnums = { // output anything (e.g. `console.time()` calls). NULL_MESSAGE: "nullMessage", NAVIGATION_MARKER: "navigationMarker", + SIMPLE_TABLE: "simpleTable", }, MESSAGE_LEVEL: { LOG: "log", diff --git a/devtools/client/webconsole/test/browser/_jsterm.ini b/devtools/client/webconsole/test/browser/_jsterm.ini index 184d16903fb3..5424dd533b7f 100644 --- a/devtools/client/webconsole/test/browser/_jsterm.ini +++ b/devtools/client/webconsole/test/browser/_jsterm.ini @@ -129,6 +129,7 @@ skip-if = !fission # context selector is only visible when fission is enabled. [browser_jsterm_helper_keys_values.js] [browser_jsterm_hide_when_devtools_chrome_enabled_false.js] [browser_jsterm_history.js] +[browser_jsterm_history_command.js] [browser_jsterm_history_arrow_keys.js] [browser_jsterm_history_nav.js] [browser_jsterm_history_persist.js] diff --git a/devtools/client/webconsole/test/browser/browser_jsterm_autocomplete_commands.js b/devtools/client/webconsole/test/browser/browser_jsterm_autocomplete_commands.js index abcf281e7110..7156eb97a4b0 100644 --- a/devtools/client/webconsole/test/browser/browser_jsterm_autocomplete_commands.js +++ b/devtools/client/webconsole/test/browser/browser_jsterm_autocomplete_commands.js @@ -18,7 +18,13 @@ add_task(async function() { EventUtils.sendString(":"); await onAutocompleUpdated; - const expectedCommands = [":block", ":help", ":screenshot", ":unblock"]; + const expectedCommands = [ + ":block", + ":help", + ":history", + ":screenshot", + ":unblock", + ]; ok( hasExactPopupLabels(autocompletePopup, expectedCommands), "popup contains expected commands" diff --git a/devtools/client/webconsole/test/browser/browser_jsterm_history_command.js b/devtools/client/webconsole/test/browser/browser_jsterm_history_command.js new file mode 100644 index 000000000000..c22674398091 --- /dev/null +++ b/devtools/client/webconsole/test/browser/browser_jsterm_history_command.js @@ -0,0 +1,43 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests if the command history shows a table with the content we expected. + +"use strict"; + +const TEST_URI = "data:text/html;charset=UTF-8,test"; +const COMMANDS = ["document", "window", "window.location"]; + +add_task(async function() { + const hud = await openNewTabAndConsole(TEST_URI); + const { jsterm } = hud; + jsterm.focus(); + + for (const command of COMMANDS) { + info(`Executing command ${command}`); + await executeAndWaitForMessage(hud, command, "", ".result"); + } + + info(`Executing command :history`); + await executeAndWaitForMessage(hud, ":history", "", ".simpleTable"); + const historyTableRows = hud.ui.outputNode.querySelectorAll( + ".message.simpleTable tbody tr" + ); + + const expectedCommands = [...COMMANDS, ":history"]; + + for (let i = 0; i < historyTableRows.length; i++) { + const cells = historyTableRows[i].querySelectorAll("td"); + + is( + cells[0].textContent, + String(i), + "Check the value of the column (index)" + ); + is( + cells[1].textContent, + expectedCommands[i], + "Check if the value of the column Expressions is the value expected" + ); + } +}); diff --git a/devtools/client/webconsole/utils/messages.js b/devtools/client/webconsole/utils/messages.js index c73f03d97cd6..6d081f5d4034 100644 --- a/devtools/client/webconsole/utils/messages.js +++ b/devtools/client/webconsole/utils/messages.js @@ -522,6 +522,18 @@ function createWarningGroupMessage(id, type, firstMessage) { }); } +function createSimpleTableMessage(columns, items, timeStamp) { + return new ConsoleMessage({ + allowRepeating: false, + level: MESSAGE_LEVEL.LOG, + source: MESSAGE_SOURCE.CONSOLE_FRONTEND, + type: MESSAGE_TYPE.SIMPLE_TABLE, + columns, + items, + timeStamp: timeStamp, + }); +} + /** * Given the a regular warning message, compute the label of the warning group the message * could be in. @@ -778,6 +790,7 @@ function isMessageNetworkError(message) { module.exports = { createWarningGroupMessage, + createSimpleTableMessage, getArrayTypeNames, getDescriptorValue, getInitialMessageCountForViewport, diff --git a/devtools/server/actors/webconsole/commands.js b/devtools/server/actors/webconsole/commands.js index 9e3b8f90d0d5..4bc415e7cee3 100644 --- a/devtools/server/actors/webconsole/commands.js +++ b/devtools/server/actors/webconsole/commands.js @@ -4,7 +4,7 @@ "use strict"; -const validCommands = ["block", "help", "screenshot", "unblock"]; +const validCommands = ["block", "help", "history", "screenshot", "unblock"]; const COMMAND = "command"; const KEY = "key"; diff --git a/devtools/server/actors/webconsole/utils.js b/devtools/server/actors/webconsole/utils.js index 540595491b4b..62641151125c 100644 --- a/devtools/server/actors/webconsole/utils.js +++ b/devtools/server/actors/webconsole/utils.js @@ -580,6 +580,24 @@ WebConsoleCommands._registerOriginal("screenshot", function(owner, args = {}) { })(); }); +/** + * Shows a history of commands and expressions previously executed within the command line. + * + * @param object args + * The arguments to be passed to the history + * @return void + */ +WebConsoleCommands._registerOriginal("history", function(owner, args = {}) { + owner.helperResult = (async () => { + // everything is handled on the client side, so we return a very simple object with + // the args + return { + type: "historyOutput", + args, + }; + })(); +}); + /** * Block specific resource from loading *