Bug 1344158 - PART 2: remove gNetwork and move getString to utils/client;r=honza draft
authorFred Lin <gasolin@mozilla.com>
Tue, 07 Mar 2017 15:32:29 +0800
changeset 503310 1e07c1736112178b3838063632fe7da4f8ea76f3
parent 503309 a87a1c1bde5eeb67216abdf8b9ace05000c9583c
child 550384 eb79e37cfcb29c9937c53b9993b3584150c9f250
push id50531
push userbmo:gasolin@mozilla.com
push dateThu, 23 Mar 2017 02:04:49 +0000
reviewershonza
bugs1344158
milestone55.0a1
Bug 1344158 - PART 2: remove gNetwork and move getString to utils/client;r=honza MozReview-Commit-ID: GvlbCT7ncoH
devtools/client/netmonitor/components/monitor-panel.js
devtools/client/netmonitor/har/har-builder.js
devtools/client/netmonitor/netmonitor-controller.js
devtools/client/netmonitor/request-list-context-menu.js
devtools/client/netmonitor/request-list-tooltip.js
devtools/client/netmonitor/test/browser_net_curl-utils.js
devtools/client/netmonitor/utils/client.js
devtools/client/netmonitor/utils/request-utils.js
--- a/devtools/client/netmonitor/components/monitor-panel.js
+++ b/devtools/client/netmonitor/components/monitor-panel.js
@@ -8,16 +8,17 @@ const {
   createClass,
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
 const Actions = require("../actions/index");
+const { getLongString } = require("../utils/client");
 const { Prefs } = require("../utils/prefs");
 const { getFormDataSections } = require("../utils/request-utils");
 const { getSelectedRequest } = require("../selectors/index");
 
 // Components
 const SplitBox = createFactory(require("devtools/client/shared/components/splitter/split-box"));
 const NetworkDetailsPanel = createFactory(require("../shared/components/network-details-panel"));
 const RequestList = createFactory(require("./request-list"));
@@ -64,17 +65,17 @@ const MonitorPanel = createClass({
     } = request;
 
     if (!formDataSections && requestHeaders &&
         requestHeadersFromUploadStream && requestPostData) {
       getFormDataSections(
         requestHeaders,
         requestHeadersFromUploadStream,
         requestPostData,
-        window.gNetwork.getString.bind(window.gNetwork),
+        getLongString,
       ).then((newFormDataSections) => {
         updateRequest(
           request.id,
           { formDataSections: newFormDataSections },
           true,
         );
       });
     }
--- a/devtools/client/netmonitor/har/har-builder.js
+++ b/devtools/client/netmonitor/har/har-builder.js
@@ -264,17 +264,16 @@ HarBuilder.prototype = {
       if (CurlUtils.isUrlEncodedRequest({ headers, postDataText })) {
         postData.mimeType = "application/x-www-form-urlencoded";
 
         // Extract form parameters and produce nice HAR array.
         getFormDataSections(
           file.requestHeaders,
           file.requestHeadersFromUploadStream,
           file.requestPostData,
-          this._options.getString
         ).then(formDataSections => {
           formDataSections.forEach(section => {
             let paramsArray = parseQueryString(section);
             if (paramsArray) {
               postData.params = [...postData.params, ...paramsArray];
             }
           });
         });
--- a/devtools/client/netmonitor/netmonitor-controller.js
+++ b/devtools/client/netmonitor/netmonitor-controller.js
@@ -8,16 +8,17 @@ const { TimelineFront } = require("devto
 const { CurlUtils } = require("devtools/client/shared/curl");
 const { ACTIVITY_TYPE, EVENTS } = require("./constants");
 const Actions = require("./actions/index");
 const {
   fetchHeaders,
   formDataURI,
 } = require("./utils/request-utils");
 const {
+  getLongString,
   getWebConsoleClient,
   onFirefoxConnect,
   onFirefoxDisconnect,
 } = require("./utils/client");
 const {
   getRequestById,
   getDisplayedRequestById,
 } = require("./selectors/index");
@@ -103,17 +104,16 @@ var NetMonitorController = {
 
       onFirefoxConnect(this._target);
       this._target.on("close", this._onTabDetached);
 
       this.webConsoleClient = getWebConsoleClient();
       this.NetworkEventsHandler = new NetworkEventsHandler();
       this.NetworkEventsHandler.connect();
 
-      window.gNetwork = this.NetworkEventsHandler;
       window.emit(EVENTS.CONNECTED);
 
       resolve();
       this._connected = true;
     });
     return this._connection;
   },
 
@@ -368,17 +368,16 @@ var NetMonitorController = {
 };
 
 /**
  * Functions handling target network events.
  */
 function NetworkEventsHandler() {
   this.addRequest = this.addRequest.bind(this);
   this.updateRequest = this.updateRequest.bind(this);
-  this.getString = this.getString.bind(this);
   this._onNetworkEvent = this._onNetworkEvent.bind(this);
   this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
   this._onDocLoadingMarker = this._onDocLoadingMarker.bind(this);
   this._onRequestHeaders = this._onRequestHeaders.bind(this);
   this._onRequestCookies = this._onRequestCookies.bind(this);
   this._onRequestPostData = this._onRequestPostData.bind(this);
   this._onResponseHeaders = this._onResponseHeaders.bind(this);
   this._onResponseCookies = this._onResponseCookies.bind(this);
@@ -511,41 +510,41 @@ NetworkEventsHandler.prototype = {
       responseHeaders,
       requestCookies,
       requestHeaders,
       requestPostData,
     } = action.data;
     let request = getRequestById(gStore.getState(), action.id);
 
     if (requestHeaders && requestHeaders.headers && requestHeaders.headers.length) {
-      let headers = await fetchHeaders(requestHeaders, this.getString);
+      let headers = await fetchHeaders(requestHeaders, getLongString);
       if (headers) {
         await gStore.dispatch(Actions.updateRequest(
           action.id,
           { requestHeaders: headers },
           true,
         ));
       }
     }
 
     if (responseHeaders && responseHeaders.headers && responseHeaders.headers.length) {
-      let headers = await fetchHeaders(responseHeaders, this.getString);
+      let headers = await fetchHeaders(responseHeaders, getLongString);
       if (headers) {
         await gStore.dispatch(Actions.updateRequest(
           action.id,
           { responseHeaders: headers },
           true,
         ));
       }
     }
 
     if (request && responseContent && responseContent.content) {
       let { mimeType } = request;
       let { text, encoding } = responseContent.content;
-      let response = await this.getString(text);
+      let response = await getLongString(text);
       let payload = {};
 
       if (mimeType.includes("image/")) {
         payload.responseContentDataUri = formDataURI(mimeType, encoding, response);
       }
 
       responseContent.content.text = response;
       payload.responseContent = responseContent;
@@ -556,17 +555,17 @@ NetworkEventsHandler.prototype = {
         window.emit(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED);
       }
     }
 
     // Search the POST data upload stream for request headers and add
     // them as a separate property, different from the classic headers.
     if (requestPostData && requestPostData.postData) {
       let { text } = requestPostData.postData;
-      let postData = await this.getString(text);
+      let postData = await getLongString(text);
       const headers = CurlUtils.getHeadersFromMultipartText(postData);
       const headersSize = headers.reduce((acc, { name, value }) => {
         return acc + name.length + value.length + 2;
       }, 0);
       let payload = {};
       requestPostData.postData.text = postData;
       payload.requestPostData = Object.assign({}, requestPostData);
       payload.requestHeadersFromUploadStream = { headers, headersSize };
@@ -581,17 +580,17 @@ NetworkEventsHandler.prototype = {
       let reqCookies = [];
       // request store cookies in requestCookies or requestCookies.cookies
       let cookies = requestCookies.cookies ?
         requestCookies.cookies : requestCookies;
       // make sure cookies is iterable
       if (typeof cookies[Symbol.iterator] === "function") {
         for (let cookie of cookies) {
           reqCookies.push(Object.assign({}, cookie, {
-            value: await this.getString(cookie.value),
+            value: await getLongString(cookie.value),
           }));
         }
         if (reqCookies.length) {
           await gStore.dispatch(Actions.updateRequest(
             action.id,
             { requestCookies: reqCookies },
             true));
         }
@@ -602,17 +601,17 @@ NetworkEventsHandler.prototype = {
       let resCookies = [];
       // response store cookies in responseCookies or responseCookies.cookies
       let cookies = responseCookies.cookies ?
         responseCookies.cookies : responseCookies;
       // make sure cookies is iterable
       if (typeof cookies[Symbol.iterator] === "function") {
         for (let cookie of cookies) {
           resCookies.push(Object.assign({}, cookie, {
-            value: await this.getString(cookie.value),
+            value: await getLongString(cookie.value),
           }));
         }
         if (resCookies.length) {
           await gStore.dispatch(Actions.updateRequest(
             action.id,
             { responseCookies: resCookies },
             true));
         }
@@ -799,33 +798,12 @@ NetworkEventsHandler.prototype = {
    *        The message received from the server.
    */
   _onEventTimings: function (response) {
     this.updateRequest(response.from, {
       eventTimings: response
     }).then(() => {
       window.emit(EVENTS.RECEIVED_EVENT_TIMINGS, response.from);
     });
-  },
-
-  /**
-   * Fetches the full text of a LongString.
-   *
-   * @param object | string stringGrip
-   *        The long string grip containing the corresponding actor.
-   *        If you pass in a plain string (by accident or because you're lazy),
-   *        then a promise of the same string is simply returned.
-   * @return object Promise
-   *         A promise that is resolved when the full string contents
-   *         are available, or rejected if something goes wrong.
-   */
-  getString: function (stringGrip) {
-    // FIXME: this.webConsoleClient will be undefined in mochitest,
-    // so we return string instantly to skip undefined error
-    if (typeof stringGrip === "string") {
-      return Promise.resolve(stringGrip);
-    }
-
-    return this.webConsoleClient.getString(stringGrip);
   }
 };
 
 exports.NetMonitorController = NetMonitorController;
--- a/devtools/client/netmonitor/request-list-context-menu.js
+++ b/devtools/client/netmonitor/request-list-context-menu.js
@@ -6,16 +6,17 @@
 
 const Services = require("Services");
 const { Curl } = require("devtools/client/shared/curl");
 const { gDevTools } = require("devtools/client/framework/devtools");
 const Menu = require("devtools/client/framework/menu");
 const MenuItem = require("devtools/client/framework/menu-item");
 const clipboardHelper = require("devtools/shared/platform/clipboard");
 const { HarExporter } = require("./har/har-exporter");
+const { getLongString } = require("./utils/client");
 const { L10N } = require("./utils/l10n");
 const {
   formDataURI,
   getFormDataSections,
   getUrlQuery,
   parseQueryString,
 } = require("./utils/request-utils");
 const {
@@ -141,17 +142,18 @@ RequestListContextMenu.prototype = {
       label: L10N.getStr("netmonitor.context.saveAllAsHar"),
       accesskey: L10N.getStr("netmonitor.context.saveAllAsHar.accesskey"),
       visible: this.sortedRequests.size > 0,
       click: () => this.saveAllAsHar(),
     }));
 
     menu.append(new MenuItem({
       type: "separator",
-      visible: !!selectedRequest,
+      visible: !!(window.NetMonitorController.supportsCustomRequest &&
+               selectedRequest && !selectedRequest.isCustom),
     }));
 
     menu.append(new MenuItem({
       id: "request-list-context-resend",
       label: L10N.getStr("netmonitor.context.editAndResend"),
       accesskey: L10N.getStr("netmonitor.context.editAndResend.accesskey"),
       visible: !!(window.NetMonitorController.supportsCustomRequest &&
                selectedRequest && !selectedRequest.isCustom),
@@ -216,34 +218,34 @@ RequestListContextMenu.prototype = {
   async copyPostData() {
     let selected = this.selectedRequest;
 
     // Try to extract any form data parameters.
     let formDataSections = await getFormDataSections(
       selected.requestHeaders,
       selected.requestHeadersFromUploadStream,
       selected.requestPostData,
-      window.gNetwork.getString.bind(window.gNetwork));
+      getLongString);
 
     let params = [];
     formDataSections.forEach(section => {
       let paramsArray = parseQueryString(section);
       if (paramsArray) {
         params = [...params, ...paramsArray];
       }
     });
 
     let string = params
       .map(param => param.name + (param.value ? "=" + param.value : ""))
       .join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n");
 
     // Fall back to raw payload.
     if (!string) {
       let postData = selected.requestPostData.postData.text;
-      string = await window.gNetwork.getString(postData);
+      string = await getLongString(postData);
       if (Services.appinfo.OS !== "WINNT") {
         string = string.replace(/\r/g, "");
       }
     }
 
     clipboardHelper.copyString(string);
   },
 
@@ -259,24 +261,24 @@ RequestListContextMenu.prototype = {
       method: selected.method,
       headers: [],
       httpVersion: selected.httpVersion,
       postDataText: null
     };
 
     // Fetch header values.
     for (let { name, value } of selected.requestHeaders.headers) {
-      let text = await window.gNetwork.getString(value);
+      let text = await getLongString(value);
       data.headers.push({ name: name, value: text });
     }
 
     // Fetch the request payload.
     if (selected.requestPostData) {
       let postData = selected.requestPostData.postData.text;
-      data.postDataText = await window.gNetwork.getString(postData);
+      data.postDataText = await getLongString(postData);
     }
 
     clipboardHelper.copyString(Curl.generateCommand(data));
   },
 
   /**
    * Copy the raw request headers from the currently selected item.
    */
@@ -300,29 +302,29 @@ RequestListContextMenu.prototype = {
   },
 
   /**
    * Copy image as data uri.
    */
   copyImageAsDataUri() {
     const { mimeType, text, encoding } = this.selectedRequest.responseContent.content;
 
-    window.gNetwork.getString(text).then(string => {
+    getLongString(text).then(string => {
       let data = formDataURI(mimeType, encoding, string);
       clipboardHelper.copyString(data);
     });
   },
 
   /**
    * Copy response data as a string.
    */
   copyResponse() {
     const { text } = this.selectedRequest.responseContent.content;
 
-    window.gNetwork.getString(text).then(string => {
+    getLongString(text).then(string => {
       clipboardHelper.copyString(string);
     });
   },
 
   /**
    * Copy HAR from the network panel content to the clipboard.
    */
   copyAllAsHar() {
@@ -336,16 +338,16 @@ RequestListContextMenu.prototype = {
     return HarExporter.save(this.getDefaultHarOptions());
   },
 
   getDefaultHarOptions() {
     let form = window.NetMonitorController._target.form;
     let title = form.title || form.url;
 
     return {
-      getString: window.gNetwork.getString.bind(window.gNetwork),
+      getString: getLongString,
       items: this.sortedRequests,
       title: title
     };
   }
 };
 
 module.exports = RequestListContextMenu;
--- a/devtools/client/netmonitor/request-list-tooltip.js
+++ b/devtools/client/netmonitor/request-list-tooltip.js
@@ -3,32 +3,33 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {
   setImageTooltip,
   getImageDimensions,
 } = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
+const { getLongString } = require("./utils/client");
 const { WEBCONSOLE_L10N } = require("./utils/l10n");
 const { formDataURI } = require("./utils/request-utils");
 
 const REQUESTS_TOOLTIP_IMAGE_MAX_DIM = 400; // px
 const REQUESTS_TOOLTIP_STACK_TRACE_WIDTH = 600; // px
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 
 async function setTooltipImageContent(tooltip, itemEl, requestItem) {
   let { mimeType, text, encoding } = requestItem.responseContent.content;
 
   if (!mimeType || !mimeType.includes("image/")) {
     return false;
   }
 
-  let string = await window.gNetwork.getString(text);
+  let string = await getLongString(text);
   let src = formDataURI(mimeType, encoding, string);
   let maxDim = REQUESTS_TOOLTIP_IMAGE_MAX_DIM;
   let { naturalWidth, naturalHeight } = await getImageDimensions(tooltip.doc, src);
   let options = { maxDim, naturalWidth, naturalHeight };
   setImageTooltip(tooltip, tooltip.doc, src, options);
 
   return itemEl.querySelector(".requests-list-icon");
 }
--- a/devtools/client/netmonitor/test/browser_net_curl-utils.js
+++ b/devtools/client/netmonitor/test/browser_net_curl-utils.js
@@ -8,49 +8,50 @@
  */
 
 const { CurlUtils } = require("devtools/client/shared/curl");
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CURL_UTILS_URL);
   info("Starting test... ");
 
-  let { gStore, windowRequire, gNetwork } = monitor.panelWin;
+  let { gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
   let { getSortedRequests } = windowRequire("devtools/client/netmonitor/selectors/index");
+  let { getLongString } = windowRequire("devtools/client/netmonitor/utils/client");
 
   gStore.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1, 3);
   yield ContentTask.spawn(tab.linkedBrowser, SIMPLE_SJS, function* (url) {
     content.wrappedJSObject.performRequests(url);
   });
   yield wait;
 
   let requests = {
     get: getSortedRequests(gStore.getState()).get(0),
     post: getSortedRequests(gStore.getState()).get(1),
     multipart: getSortedRequests(gStore.getState()).get(2),
     multipartForm: getSortedRequests(gStore.getState()).get(3),
   };
 
-  let data = yield createCurlData(requests.get, gNetwork);
+  let data = yield createCurlData(requests.get, getLongString);
   testFindHeader(data);
 
-  data = yield createCurlData(requests.post, gNetwork);
+  data = yield createCurlData(requests.post, getLongString);
   testIsUrlEncodedRequest(data);
   testWritePostDataTextParams(data);
 
-  data = yield createCurlData(requests.multipart, gNetwork);
+  data = yield createCurlData(requests.multipart, getLongString);
   testIsMultipartRequest(data);
   testGetMultipartBoundary(data);
   testMultiPartHeaders(data);
   testRemoveBinaryDataFromMultipartText(data);
 
-  data = yield createCurlData(requests.multipartForm, gNetwork);
+  data = yield createCurlData(requests.multipartForm, getLongString);
   testMultiPartHeaders(data);
 
   testGetHeadersFromMultipartText({
     postDataText: "Content-Type: text/plain\r\n\r\n",
   });
 
   if (Services.appinfo.OS != "WINNT") {
     testEscapeStringPosix();
@@ -209,34 +210,34 @@ function testEscapeStringWin() {
     "Backslashes should be escaped.");
 
   let newLines = "line1\r\nline2\r\nline3";
   is(CurlUtils.escapeStringWin(newLines),
     '"line1"^\u000d\u000A"line2"^\u000d\u000A"line3"',
     "Newlines should be escaped.");
 }
 
-function* createCurlData(selected, network, controller) {
+function* createCurlData(selected, getLongString) {
   let { url, method, httpVersion } = selected;
 
   // Create a sanitized object for the Curl command generator.
   let data = {
     url,
     method,
     headers: [],
     httpVersion,
     postDataText: null
   };
 
   // Fetch header values.
   for (let { name, value } of selected.requestHeaders.headers) {
-    let text = yield network.getString(value);
+    let text = yield getLongString(value);
     data.headers.push({ name: name, value: text });
   }
 
   // Fetch the request payload.
   if (selected.requestPostData) {
     let postData = selected.requestPostData.postData.text;
-    data.postDataText = yield network.getString(postData);
+    data.postDataText = yield getLongString(postData);
   }
 
   return data;
 }
--- a/devtools/client/netmonitor/utils/client.js
+++ b/devtools/client/netmonitor/utils/client.js
@@ -67,13 +67,35 @@ function onFirefoxDisconnect(tabTarget) 
  * Retrieve webconsole object
  *
  * @returns {Object} webConsole
  */
 function getWebConsoleClient() {
   return activeConsole;
 }
 
+/**
+ * Fetches the full text of a LongString.
+ *
+ * @param object | string stringGrip
+ *        The long string grip containing the corresponding actor.
+ *        If you pass in a plain string (by accident or because you're lazy),
+ *        then a promise of the same string is simply returned.
+ * @return object Promise
+ *         A promise that is resolved when the full string contents
+ *         are available, or rejected if something goes wrong.
+ */
+function getLongString(stringGrip) {
+  // FIXME: this.webConsoleClient will be undefined in mochitest,
+  // so we return string instantly to skip undefined error
+  if (typeof stringGrip === "string") {
+    return Promise.resolve(stringGrip);
+  }
+
+  return activeConsole.getString(stringGrip);
+}
+
 module.exports = {
+  getLongString,
   getWebConsoleClient,
   onFirefoxConnect,
   onFirefoxDisconnect,
 };
--- a/devtools/client/netmonitor/utils/request-utils.js
+++ b/devtools/client/netmonitor/utils/request-utils.js
@@ -8,37 +8,36 @@
 
 /**
  * Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a
  * POST request.
  *
  * @param {object} headers - the "requestHeaders".
  * @param {object} uploadHeaders - the "requestHeadersFromUploadStream".
  * @param {object} postData - the "requestPostData".
- * @param {function} getString - callback to retrieve a string from a LongStringGrip.
  * @return {array} a promise list that is resolved with the extracted form data.
  */
-async function getFormDataSections(headers, uploadHeaders, postData, getString) {
+async function getFormDataSections(headers, uploadHeaders, postData, getLongString) {
   let formDataSections = [];
 
   let requestHeaders = headers.headers;
   let payloadHeaders = uploadHeaders ? uploadHeaders.headers : [];
   let allHeaders = [...payloadHeaders, ...requestHeaders];
 
   let contentTypeHeader = allHeaders.find(e => {
     return e.name.toLowerCase() == "content-type";
   });
 
   let contentTypeLongString = contentTypeHeader ? contentTypeHeader.value : "";
 
-  let contentType = await getString(contentTypeLongString);
+  let contentType = await getLongString(contentTypeLongString);
 
   if (contentType.includes("x-www-form-urlencoded")) {
     let postDataLongString = postData.postData.text;
-    let text = await getString(postDataLongString);
+    let text = await getLongString(postDataLongString);
 
     for (let section of text.split(/\r\n|\r|\n/)) {
       // Before displaying it, make sure this section of the POST data
       // isn't a line containing upload stream headers.
       if (payloadHeaders.every(header => !section.startsWith(header.name))) {
         formDataSections.push(section);
       }
     }
@@ -46,22 +45,21 @@ async function getFormDataSections(heade
 
   return formDataSections;
 }
 
 /**
  * Fetch headers full content from actor server
  *
  * @param {object} headers - a object presents headers data
- * @param {function} getString - callback to retrieve a string from a LongStringGrip
  * @return {object} a headers object with updated content payload
  */
-async function fetchHeaders(headers, getString) {
+async function fetchHeaders(headers, getLongString) {
   for (let { value } of headers.headers) {
-    headers.headers.value = await getString(value);
+    headers.headers.value = await getLongString(value);
   }
   return headers;
 }
 
 /**
  * Form a data: URI given a mime type, encoding, and some text.
  *
  * @param {string} mimeType - mime type