Bug 1151368 - Unblock requests by URL in DevTools. r=ochameau
authorJ. Ryan Stinnett <jryans@gmail.com>
Fri, 19 Apr 2019 18:25:38 +0000
changeset 470235 b9a2a553d0c4f8b9bd3f98e9680b925c746c5f81
parent 470234 6b031fd49d1e292c4033e6f86f909a689a78b176
child 470236 6d168f63dd8d5567c198695fcde87e6340ef33a8
push id35891
push userrgurzau@mozilla.com
push dateSat, 20 Apr 2019 09:35:22 +0000
treeherdermozilla-central@6e082b675763 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersochameau
bugs1151368
milestone68.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 1151368 - Unblock requests by URL in DevTools. r=ochameau This is nearly the same the first patch for blocking, but adds the unblock option as well. Differential Revision: https://phabricator.services.mozilla.com/D26581
devtools/client/locales/en-US/netmonitor.properties
devtools/client/netmonitor/src/actions/requests.js
devtools/client/netmonitor/src/components/RequestListContent.js
devtools/client/netmonitor/src/connector/chrome-connector.js
devtools/client/netmonitor/src/connector/firefox-connector.js
devtools/client/netmonitor/src/connector/index.js
devtools/client/netmonitor/src/widgets/RequestListContextMenu.js
devtools/server/actors/network-monitor.js
devtools/server/actors/network-monitor/network-observer.js
devtools/server/actors/webconsole.js
devtools/shared/specs/webconsole.js
--- a/devtools/client/locales/en-US/netmonitor.properties
+++ b/devtools/client/locales/en-US/netmonitor.properties
@@ -1075,16 +1075,20 @@ netmonitor.context.editAndResend=Edit an
 # LOCALIZATION NOTE (netmonitor.context.editAndResend.accesskey): This is the access key
 # for the "Edit and Resend" menu item displayed in the context menu for a request
 netmonitor.context.editAndResend.accesskey=E
 
 # LOCALIZATION NOTE (netmonitor.context.blockURL): This is the label displayed
 # on the context menu that blocks any requests matching the selected request's URL.
 netmonitor.context.blockURL=Block URL
 
+# LOCALIZATION NOTE (netmonitor.context.unblockURL): This is the label displayed
+# on the context menu that unblocks any requests matching the selected request's URL.
+netmonitor.context.unblockURL=Unblock URL
+
 # LOCALIZATION NOTE (netmonitor.context.newTab):  This is the label
 # for the Open in New Tab menu item displayed in the context menu of the
 # network container
 netmonitor.context.newTab=Open in New Tab
 
 # LOCALIZATION NOTE (netmonitor.context.newTab.accesskey): This is the access key
 # for the Open in New Tab menu item displayed in the context menu of the
 # network container
--- a/devtools/client/netmonitor/src/actions/requests.js
+++ b/devtools/client/netmonitor/src/actions/requests.js
@@ -89,16 +89,32 @@ function blockSelectedRequestURL(connect
     }
 
     const { url } = selected;
     connector.blockRequest({ url });
   };
 }
 
 /**
+ * Tell the backend to unblock future requests that match the URL of the selected one.
+ */
+function unblockSelectedRequestURL(connector) {
+  return (dispatch, getState) => {
+    const selected = getSelectedRequest(getState());
+
+    if (!selected) {
+      return;
+    }
+
+    const { url } = selected;
+    connector.unblockRequest({ url });
+  };
+}
+
+/**
  * Remove a request from the list. Supports removing only cloned requests with a
  * "isCustom" attribute. Other requests never need to be removed.
  */
 function removeSelectedCustomRequest() {
   return {
     type: REMOVE_SELECTED_CUSTOM_REQUEST,
   };
 }
@@ -121,10 +137,11 @@ function toggleRecording() {
 module.exports = {
   addRequest,
   blockSelectedRequestURL,
   clearRequests,
   cloneSelectedRequest,
   removeSelectedCustomRequest,
   sendCustomRequest,
   toggleRecording,
+  unblockSelectedRequestURL,
   updateRequest,
 };
--- a/devtools/client/netmonitor/src/components/RequestListContent.js
+++ b/devtools/client/netmonitor/src/components/RequestListContent.js
@@ -64,16 +64,17 @@ class RequestListContent extends Compone
       onCauseBadgeMouseDown: PropTypes.func.isRequired,
       onItemMouseDown: PropTypes.func.isRequired,
       onSecurityIconMouseDown: PropTypes.func.isRequired,
       onSelectDelta: PropTypes.func.isRequired,
       onWaterfallMouseDown: PropTypes.func.isRequired,
       openStatistics: PropTypes.func.isRequired,
       scale: PropTypes.number,
       selectedRequest: PropTypes.object,
+      unblockSelectedRequestURL: PropTypes.func.isRequired,
       requestFilterTypes: PropTypes.object.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
     this.isScrolledToBottom = this.isScrolledToBottom.bind(this);
     this.onHover = this.onHover.bind(this);
@@ -266,24 +267,26 @@ class RequestListContent extends Compone
 
     if (!this.contextMenu) {
       const {
         blockSelectedRequestURL,
         connector,
         cloneSelectedRequest,
         sendCustomRequest,
         openStatistics,
+        unblockSelectedRequestURL,
       } = this.props;
       this.contextMenu = new RequestListContextMenu({
         blockSelectedRequestURL,
         connector,
         cloneSelectedRequest,
         sendCustomRequest,
         openStatistics,
         openRequestInTab: this.openRequestInTab,
+        unblockSelectedRequestURL,
       });
     }
 
     this.contextMenu.open(evt, selectedRequest, displayedRequests);
   }
 
   /**
    * If selection has just changed (by keyboard navigation), don't keep the list
@@ -364,16 +367,19 @@ module.exports = connect(
   }),
   (dispatch, props) => ({
     blockSelectedRequestURL: () => {
       dispatch(Actions.blockSelectedRequestURL(props.connector));
     },
     cloneSelectedRequest: () => dispatch(Actions.cloneSelectedRequest()),
     sendCustomRequest: () => dispatch(Actions.sendCustomRequest(props.connector)),
     openStatistics: (open) => dispatch(Actions.openStatistics(props.connector, open)),
+    unblockSelectedRequestURL: () => {
+      dispatch(Actions.unblockSelectedRequestURL(props.connector));
+    },
     /**
      * A handler that opens the stack trace tab when a stack trace is available
      */
     onCauseBadgeMouseDown: (cause) => {
       if (cause.stacktrace && cause.stacktrace.length > 0) {
         dispatch(Actions.selectDetailsPanelTab("stack-trace"));
       }
     },
--- a/devtools/client/netmonitor/src/connector/chrome-connector.js
+++ b/devtools/client/netmonitor/src/connector/chrome-connector.js
@@ -93,16 +93,25 @@ class ChromeConnector {
    * Block future requests matching a filter.
    *
    * @param {object} filter request filter specifying what to block
    */
   blockRequest(filter) {
     // TODO: Implement for Chrome as well.
   }
 
+  /**
+   * Unblock future requests matching a filter.
+   *
+   * @param {object} filter request filter specifying what to unblock
+   */
+  unblockRequest(filter) {
+    // TODO: Implement for Chrome as well.
+  }
+
   setPreferences() {
     // TODO : implement.
   }
 
   viewSourceInDebugger() {
     // TODO : implement.
   }
 }
--- a/devtools/client/netmonitor/src/connector/firefox-connector.js
+++ b/devtools/client/netmonitor/src/connector/firefox-connector.js
@@ -238,16 +238,25 @@ class FirefoxConnector {
    *
    * @param {object} filter request filter specifying what to block
    */
   blockRequest(filter) {
     return this.webConsoleClient.blockRequest(filter);
   }
 
   /**
+   * Unblock future requests matching a filter.
+   *
+   * @param {object} filter request filter specifying what to unblock
+   */
+  unblockRequest(filter) {
+    return this.webConsoleClient.unblockRequest(filter);
+  }
+
+  /**
    * Set network preferences to control network flow
    *
    * @param {object} request request payload would like to sent to backend
    * @param {function} callback callback will be invoked after the request finished
    */
   setPreferences(request) {
     return this.webConsoleClient.setPreferences(request);
   }
--- a/devtools/client/netmonitor/src/connector/index.js
+++ b/devtools/client/netmonitor/src/connector/index.js
@@ -95,16 +95,20 @@ class Connector {
   sendHTTPRequest() {
     return this.connector.sendHTTPRequest(...arguments);
   }
 
   blockRequest() {
     return this.connector.blockRequest(...arguments);
   }
 
+  unblockRequest() {
+    return this.connector.unblockRequest(...arguments);
+  }
+
   setPreferences() {
     return this.connector.setPreferences(...arguments);
   }
 
   triggerActivity() {
     return this.connector.triggerActivity(...arguments);
   }
 
--- a/devtools/client/netmonitor/src/widgets/RequestListContextMenu.js
+++ b/devtools/client/netmonitor/src/widgets/RequestListContextMenu.js
@@ -23,16 +23,17 @@ loader.lazyRequireGetter(this, "HarMenuU
 class RequestListContextMenu {
   constructor(props) {
     this.props = props;
   }
 
   open(event, selectedRequest, requests) {
     const {
       id,
+      blockedReason,
       isCustom,
       formDataSections,
       method,
       mimeType,
       httpVersion,
       requestHeaders,
       requestHeadersAvailable,
       requestPostData,
@@ -45,16 +46,17 @@ class RequestListContextMenu {
     } = selectedRequest;
     const {
       blockSelectedRequestURL,
       connector,
       cloneSelectedRequest,
       sendCustomRequest,
       openStatistics,
       openRequestInTab,
+      unblockSelectedRequestURL,
     } = this.props;
     const menu = [];
     const copySubmenu = [];
 
     copySubmenu.push({
       id: "request-list-context-copy-url",
       label: L10N.getStr("netmonitor.context.copyUrl"),
       accesskey: L10N.getStr("netmonitor.context.copyUrl.accesskey"),
@@ -200,21 +202,28 @@ class RequestListContextMenu {
       accesskey: L10N.getStr("netmonitor.context.editAndResend.accesskey"),
       visible: !!(selectedRequest && !isCustom),
       click: cloneSelectedRequest,
     });
 
     menu.push({
       id: "request-list-context-block-url",
       label: L10N.getStr("netmonitor.context.blockURL"),
-      visible: !!selectedRequest,
+      visible: !!(selectedRequest && !blockedReason),
       click: blockSelectedRequestURL,
     });
 
     menu.push({
+      id: "request-list-context-unblock-url",
+      label: L10N.getStr("netmonitor.context.unblockURL"),
+      visible: !!(selectedRequest && blockedReason),
+      click: unblockSelectedRequestURL,
+    });
+
+    menu.push({
       type: "separator",
       visible: copySubmenu.slice(15, 16).some((subMenu) => subMenu.visible),
     });
 
     menu.push({
       id: "request-list-context-newtab",
       label: L10N.getStr("netmonitor.context.newTab"),
       accesskey: L10N.getStr("netmonitor.context.newTab.accesskey"),
--- a/devtools/server/actors/network-monitor.js
+++ b/devtools/server/actors/network-monitor.js
@@ -48,16 +48,17 @@ const NetworkMonitorActor = ActorClassWi
     this.observer.init();
 
     this.stackTraces = new Set();
 
     this.onStackTraceAvailable = this.onStackTraceAvailable.bind(this);
     this.onRequestContent = this.onRequestContent.bind(this);
     this.onSetPreference = this.onSetPreference.bind(this);
     this.onBlockRequest = this.onBlockRequest.bind(this);
+    this.onUnblockRequest = this.onUnblockRequest.bind(this);
     this.onGetNetworkEventActor = this.onGetNetworkEventActor.bind(this);
     this.onDestroyMessage = this.onDestroyMessage.bind(this);
 
     this.startListening();
   },
 
   onDestroyMessage({ data }) {
     if (data.actorID == this.parentID) {
@@ -69,31 +70,35 @@ const NetworkMonitorActor = ActorClassWi
     this.messageManager.addMessageListener("debug:request-stack-available",
       this.onStackTraceAvailable);
     this.messageManager.addMessageListener("debug:request-content:request",
       this.onRequestContent);
     this.messageManager.addMessageListener("debug:netmonitor-preference",
       this.onSetPreference);
     this.messageManager.addMessageListener("debug:block-request",
       this.onBlockRequest);
+    this.messageManager.addMessageListener("debug:unblock-request",
+      this.onUnblockRequest);
     this.messageManager.addMessageListener("debug:get-network-event-actor:request",
       this.onGetNetworkEventActor);
     this.messageManager.addMessageListener("debug:destroy-network-monitor",
       this.onDestroyMessage);
   },
 
   stopListening() {
     this.messageManager.removeMessageListener("debug:request-stack-available",
       this.onStackTraceAvailable);
     this.messageManager.removeMessageListener("debug:request-content:request",
       this.onRequestContent);
     this.messageManager.removeMessageListener("debug:netmonitor-preference",
       this.onSetPreference);
     this.messageManager.removeMessageListener("debug:block-request",
       this.onBlockRequest);
+    this.messageManager.removeMessageListener("debug:unblock-request",
+      this.onUnblockRequest);
     this.messageManager.removeMessageListener("debug:get-network-event-actor:request",
       this.onGetNetworkEventActor);
     this.messageManager.removeMessageListener("debug:destroy-network-monitor",
       this.onDestroyMessage);
   },
 
   destroy() {
     Actor.prototype.destroy.call(this);
@@ -177,16 +182,21 @@ const NetworkMonitorActor = ActorClassWi
     }
   },
 
   onBlockRequest({ data }) {
     const { filter } = data;
     this.observer.blockRequest(filter);
   },
 
+  onUnblockRequest({ data }) {
+    const { filter } = data;
+    this.observer.unblockRequest(filter);
+  },
+
   onGetNetworkEventActor({ data }) {
     const actor = this.getNetworkEventActor(data.channelId);
     this.messageManager.sendAsyncMessage("debug:get-network-event-actor:response", {
       channelId: data.channelId,
       actor: actor.form(),
     });
   },
 
--- a/devtools/server/actors/network-monitor/network-observer.js
+++ b/devtools/server/actors/network-monitor/network-observer.js
@@ -666,16 +666,31 @@ NetworkObserver.prototype = {
       // For now, ignore anything other than URL.
       return;
     }
 
     this.blockedURLs.add(filter.url);
   },
 
   /**
+   * Unblock a request based on certain filtering options.
+   *
+   * Currently, an exact URL match is the only supported filter type.
+   */
+  unblockRequest(filter) {
+    if (!filter || !filter.url) {
+      // In the future, there may be other types of filters, such as domain.
+      // For now, ignore anything other than URL.
+      return;
+    }
+
+    this.blockedURLs.delete(filter.url);
+  },
+
+  /**
    * Setup the network response listener for the given HTTP activity. The
    * NetworkResponseListener is responsible for storing the response body.
    *
    * @private
    * @param object httpActivity
    *        The HTTP activity object we are tracking.
    */
   _setupResponseListener: function(httpActivity, fromCache) {
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -1699,16 +1699,38 @@ WebConsoleActor.prototype =
         });
       }
     }
 
     return {};
   },
 
   /**
+   * Unblock a request based on certain filtering options.
+   *
+   * Currently, an exact URL match is the only supported filter type.
+   * In the future, there may be other types of filters, such as domain.
+   * For now, ignore anything other than URL.
+   *
+   * @param object filter
+   *   An object containing a `url` key with a URL to unblock.
+   */
+  async unblockRequest({ filter }) {
+    if (this.netmonitors) {
+      for (const { messageManager } of this.netmonitors) {
+        messageManager.sendAsyncMessage("debug:unblock-request", {
+          filter,
+        });
+      }
+    }
+
+    return {};
+  },
+
+  /**
    * Handler for file activity. This method sends the file request information
    * to the remote Web Console client.
    *
    * @see ConsoleProgressListener
    * @param string fileURI
    *        The requested file URI.
    */
   onFileActivity: function(fileURI) {
@@ -1850,11 +1872,12 @@ WebConsoleActor.prototype.requestTypes =
   evaluateJS: WebConsoleActor.prototype.evaluateJS,
   evaluateJSAsync: WebConsoleActor.prototype.evaluateJSAsync,
   autocomplete: WebConsoleActor.prototype.autocomplete,
   clearMessagesCache: WebConsoleActor.prototype.clearMessagesCache,
   getPreferences: WebConsoleActor.prototype.getPreferences,
   setPreferences: WebConsoleActor.prototype.setPreferences,
   sendHTTPRequest: WebConsoleActor.prototype.sendHTTPRequest,
   blockRequest: WebConsoleActor.prototype.blockRequest,
+  unblockRequest: WebConsoleActor.prototype.unblockRequest,
 };
 
 exports.WebConsoleActor = WebConsoleActor;
--- a/devtools/shared/specs/webconsole.js
+++ b/devtools/shared/specs/webconsole.js
@@ -235,15 +235,21 @@ const webconsoleSpecPrototype = {
       response: RetVal("json"),
     },
 
     blockRequest: {
       request: {
         filter: Arg(0, "json"),
       },
     },
+
+    unblockRequest: {
+      request: {
+        filter: Arg(0, "json"),
+      },
+    },
   },
 };
 
 const webconsoleSpec = generateActorSpec(webconsoleSpecPrototype);
 
 exports.webconsoleSpecPrototype = webconsoleSpecPrototype;
 exports.webconsoleSpec = webconsoleSpec;