diff --git a/devtools/client/locales/en-US/webconsole.properties b/devtools/client/locales/en-US/webconsole.properties index b3b3a1df535e..7fdd7521d9f6 100644 --- a/devtools/client/locales/en-US/webconsole.properties +++ b/devtools/client/locales/en-US/webconsole.properties @@ -430,6 +430,20 @@ webconsole.cssWarningElements.label=Elements matching selector: %S # Parameters: %S is the URL to file a bug about the error. webconsole.message.componentDidCatch.label=[DEVTOOLS ERROR] We’re sorry, we couldn’t render the message. This shouldn’t have happened - please file a bug at %S with the message metadata in the description. +# LOCALIZATION NOTE (webconsole.message.commands.blockedURL) +# Label displayed when the :block command is successful +# Parameters: %S is the URL filter +webconsole.message.commands.blockedURL=Requests to URL containing "%S" are now blocked + +# LOCALIZATION NOTE (webconsole.message.commands.unblockedURL) +# Label displayed when the :unblock command is successful +# Parameters: %S is the URL filter +webconsole.message.commands.unblockedURL=Removed blocking filter "%S" + +# LOCALIZATION NOTE (webconsole.messages.commands.blockArgMissing) +# Message displayed when no filter is passed to block/unblock command +webconsole.messages.commands.blockArgMissing=No filter was specified + # LOCALIZATION NOTE (webconsole.message.componentDidCatch.copyButton.label) # Label displayed on the button next to the message we display when the webconsole # couldn't handle a given packet (See webconsole.message.componentDidCatch.label). diff --git a/devtools/client/webconsole/actions/input.js b/devtools/client/webconsole/actions/input.js index 9efa85e116d8..774db9af063f 100644 --- a/devtools/client/webconsole/actions/input.js +++ b/devtools/client/webconsole/actions/input.js @@ -11,6 +11,7 @@ const { SET_TERMINAL_EAGER_RESULT, } = require("devtools/client/webconsole/constants"); const { getAllPrefs } = require("devtools/client/webconsole/selectors/prefs"); +const l10n = require("devtools/client/webconsole/utils/l10n"); loader.lazyServiceGetter( this, @@ -39,6 +40,12 @@ loader.lazyRequireGetter( "devtools/client/webconsole/types", true ); +loader.lazyRequireGetter( + this, + "netmonitorBlockingActions", + "devtools/client/netmonitor/src/actions/request-blocking" +); + const HELP_URL = "https://developer.mozilla.org/docs/Tools/Web_Console/Helpers"; async function getMappedExpression(hud, expression) { @@ -162,7 +169,7 @@ function onExpressionEvaluated(response) { } function handleHelperResult(response) { - return async ({ dispatch, hud, webConsoleUI }) => { + return async ({ dispatch, hud, toolbox, webConsoleUI }) => { const { result, helperResult } = response; const helperHasRawOutput = !!helperResult?.rawOutput; @@ -204,6 +211,47 @@ function handleHelperResult(response) { })) ) ); + break; + case "blockURL": + const blockURL = helperResult.args.url; + + toolbox + .getPanel("netmonitor") + ?.panelWin.store.dispatch( + netmonitorBlockingActions.addBlockedUrl(blockURL) + ); + + dispatch( + messagesActions.messagesAdd([ + { + type: "logMessage", + message: l10n.getFormatStr( + "webconsole.message.commands.blockedURL", + [blockURL] + ), + }, + ]) + ); + break; + case "unblockURL": + const unblockURL = helperResult.args.url; + toolbox + .getPanel("netmonitor") + ?.panelWin.store.dispatch( + netmonitorBlockingActions.removeBlockedUrl(unblockURL) + ); + + dispatch( + messagesActions.messagesAdd([ + { + type: "logMessage", + message: l10n.getFormatStr( + "webconsole.message.commands.unblockedURL", + [unblockURL] + ), + }, + ]) + ); // early return as we already dispatched necessary messages. return; } diff --git a/devtools/client/webconsole/test/browser/_jsterm.ini b/devtools/client/webconsole/test/browser/_jsterm.ini index 2b70a7a8939c..4bc3d1e8ff9f 100644 --- a/devtools/client/webconsole/test/browser/_jsterm.ini +++ b/devtools/client/webconsole/test/browser/_jsterm.ini @@ -4,6 +4,8 @@ subsuite = devtools support-files = head.js test-autocomplete-in-stackframe.html + test-block-action.html + test-block-action-style.css test-console-evaluation-context-selector-child.html test-console-evaluation-context-selector.html test-console.html @@ -71,6 +73,7 @@ skip-if = debug && (os == "win" && bits == 32) # Bug 1620856 [browser_jsterm_await_paused.js] skip-if = debug # crashes on "Unexpected UpdateTransformLayer hint" bug 1570685 [browser_jsterm_await.js] +[browser_jsterm_block_command.js] [browser_jsterm_completion_bracket_cached_results.js] [browser_jsterm_completion_bracket.js] skip-if = debug && (os == "win" && os_version == "6.1") # Bug 1620724 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 125be829fc36..abcf281e7110 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,7 @@ add_task(async function() { EventUtils.sendString(":"); await onAutocompleUpdated; - const expectedCommands = [":help", ":screenshot"]; + const expectedCommands = [":block", ":help", ":screenshot", ":unblock"]; ok( hasExactPopupLabels(autocompletePopup, expectedCommands), "popup contains expected commands" diff --git a/devtools/client/webconsole/test/browser/browser_jsterm_block_command.js b/devtools/client/webconsole/test/browser/browser_jsterm_block_command.js new file mode 100644 index 000000000000..13a2f3d8a723 --- /dev/null +++ b/devtools/client/webconsole/test/browser/browser_jsterm_block_command.js @@ -0,0 +1,67 @@ +"use strict"; + +const TEST_URI = + "http://example.com/browser/devtools/client/webconsole/" + + "test/browser/test-block-action.html"; +const TIMEOUT = "TIMEOUT"; + +add_task(async function() { + const hud = await openNewTabAndConsole(TEST_URI); + + ok(hud, "web console opened"); + + const filter = "test-block-action-style.css"; + const blockCommand = `:block ${filter}`; + const unblockCommand = `:unblock ${filter}`; + + info("Before blocking"); + await tryFetching(); + const resp1 = await waitFor(() => findMessage(hud, "successful")); + ok(resp1, "the request was not blocked"); + + await executeAndWaitForMessage(hud, blockCommand, "are now blocked"); + + info("After blocking"); + await tryFetching(); + const resp2 = await waitFor(() => findMessage(hud, "blocked")); + ok(resp2, "the request was blocked as expected"); + + await executeAndWaitForMessage(hud, unblockCommand, "Removed blocking"); + + info("After unblocking"); + await tryFetching(); + + const resp3 = await waitFor(() => findMessage(hud, "successful")); + ok(resp3, "the request was not blocked"); +}); + +async function tryFetching() { + await SpecialPowers.spawn(gBrowser.selectedBrowser, [TIMEOUT], async function( + timeoutStr + ) { + const win = content.wrappedJSObject; + const FETCH_URI = + "http://example.com/browser/devtools/client/webconsole/" + + "test/browser/test-block-action-style.css"; + const timeout = new Promise(res => + win.setTimeout(() => res(timeoutStr), 1000) + ); + const fetchPromise = win.fetch(FETCH_URI); + + try { + const resp = await Promise.race([fetchPromise, timeout]); + if (typeof resp === "object") { + // Request Promise + win.console.log("the request was successful"); + } else if (resp === timeoutStr) { + // Timeout + win.console.log("the request was blocked"); + } else { + win.console.error("Unkown response"); + } + } catch { + // NetworkError + win.console.log("the request was blocked"); + } + }); +} diff --git a/devtools/client/webconsole/test/browser/test-block-action-style.css b/devtools/client/webconsole/test/browser/test-block-action-style.css new file mode 100644 index 000000000000..4717ad4b94f6 --- /dev/null +++ b/devtools/client/webconsole/test/browser/test-block-action-style.css @@ -0,0 +1,3 @@ +h1 { + color: red; +} \ No newline at end of file diff --git a/devtools/client/webconsole/test/browser/test-block-action.html b/devtools/client/webconsole/test/browser/test-block-action.html new file mode 100644 index 000000000000..8f62f2e555eb --- /dev/null +++ b/devtools/client/webconsole/test/browser/test-block-action.html @@ -0,0 +1,12 @@ + + + + + Test for bug 1546394 - :block command + + + +

I won't be red for once.

+ + + \ No newline at end of file diff --git a/devtools/server/actors/webconsole/commands.js b/devtools/server/actors/webconsole/commands.js index f93e837e3292..9e3b8f90d0d5 100644 --- a/devtools/server/actors/webconsole/commands.js +++ b/devtools/server/actors/webconsole/commands.js @@ -4,7 +4,7 @@ "use strict"; -const validCommands = ["help", "screenshot"]; +const validCommands = ["block", "help", "screenshot", "unblock"]; const COMMAND = "command"; const KEY = "key"; @@ -16,7 +16,9 @@ const KEY_PREFIX = /^--/; // default value for flags const DEFAULT_VALUE = true; const COMMAND_DEFAULT_FLAG = { + block: "url", screenshot: "filename", + unblock: "url", }; /** diff --git a/devtools/server/actors/webconsole/utils.js b/devtools/server/actors/webconsole/utils.js index 4f08585afe8f..b4dd3de786ae 100644 --- a/devtools/server/actors/webconsole/utils.js +++ b/devtools/server/actors/webconsole/utils.js @@ -648,6 +648,60 @@ WebConsoleCommands._registerOriginal("screenshot", function(owner, args = {}) { })(); }); +/** + * Block specific resource from loading + * + * @param object args + * an object with key "url", i.e. a filter + * + * @return void + */ +WebConsoleCommands._registerOriginal("block", function(owner, args = {}) { + if (!args.url) { + owner.helperResult = { + type: "error", + message: "webconsole.messages.commands.blockArgMissing", + }; + return; + } + + owner.helperResult = (async () => { + await owner.consoleActor.blockRequest(args); + + return { + type: "blockURL", + args, + }; + })(); +}); + +/* + * Unblock a blocked a resource + * + * @param object filter + * an object with key "url", i.e. a filter + * + * @return void + */ +WebConsoleCommands._registerOriginal("unblock", function(owner, args = {}) { + if (!args.url) { + owner.helperResult = { + type: "error", + message: "webconsole.messages.commands.blockArgMissing", + }; + return; + } + + owner.helperResult = (async () => { + await owner.consoleActor.unblockRequest(args); + + return { + type: "unblockURL", + args, + }; + })(); +}); + /** * (Internal only) Add the bindings to |owner.sandbox|. * This is intended to be used by the WebConsole actor only.