Bug 1356957 - call updateRequest once when update request in netmonitor-controller;r=rickychien draft
authorFred Lin <gasolin@mozilla.com>
Mon, 17 Apr 2017 14:21:59 +0800
changeset 567444 5b765c111f1f0bffa7b8e8e5216d870fa4e96b8a
parent 567425 abdcc8dfc28397b95338245390e12c56658ad182
child 625645 31ee4dc5d3db19d28536b34bfd6622d4b808a323
push id55569
push userbmo:gasolin@mozilla.com
push dateTue, 25 Apr 2017 00:54:46 +0000
reviewersrickychien
bugs1356957
milestone55.0a1
Bug 1356957 - call updateRequest once when update request in netmonitor-controller;r=rickychien MozReview-Commit-ID: J6lLXNlnJnM
devtools/client/netmonitor/src/components/request-list-column-file.js
devtools/client/netmonitor/src/constants.js
devtools/client/netmonitor/src/netmonitor-controller.js
devtools/client/netmonitor/test/browser_net_icon-preview.js
devtools/client/netmonitor/test/browser_net_image-tooltip.js
devtools/client/netmonitor/test/browser_net_simple-request-data.js
--- a/devtools/client/netmonitor/src/components/request-list-column-file.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-file.js
@@ -35,17 +35,16 @@ const RequestListColumnFile = createClas
     return (
       div({
         className: "requests-list-column requests-list-file",
         title: urlDetails.unicodeUrl,
       },
         img({
           className: "requests-list-icon",
           src: responseContentDataUri,
-          "data-type": responseContentDataUri ? "thumbnail" : undefined,
         }),
         urlDetails.baseNameWithQuery
       )
     );
   }
 });
 
 module.exports = RequestListColumnFile;
--- a/devtools/client/netmonitor/src/constants.js
+++ b/devtools/client/netmonitor/src/constants.js
@@ -88,28 +88,16 @@ const EVENTS = {
   UPDATING_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdating:EventTimings",
   RECEIVED_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdated:EventTimings",
 
   // When response content begins, updates and finishes receiving.
   STARTED_RECEIVING_RESPONSE: "NetMonitor:NetworkEventUpdating:ResponseStart",
   UPDATING_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdating:ResponseContent",
   RECEIVED_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdated:ResponseContent",
 
-  // When the request post params are displayed in the UI.
-  REQUEST_POST_PARAMS_DISPLAYED: "NetMonitor:RequestPostParamsAvailable",
-
-  // When the image response thumbnail is displayed in the UI.
-  RESPONSE_IMAGE_THUMBNAIL_DISPLAYED:
-    "NetMonitor:ResponseImageThumbnailAvailable",
-
-  // Fired when charts have been displayed in the PerformanceStatisticsView.
-  PLACEHOLDER_CHARTS_DISPLAYED: "NetMonitor:PlaceholderChartsDisplayed",
-  PRIMED_CACHE_CHART_DISPLAYED: "NetMonitor:PrimedChartsDisplayed",
-  EMPTY_CACHE_CHART_DISPLAYED: "NetMonitor:EmptyChartsDisplayed",
-
   // Fired once the NetMonitorController establishes a connection to the debug
   // target.
   CONNECTED: "connected",
 };
 
 const HEADERS = [
   {
     name: "status",
--- a/devtools/client/netmonitor/src/netmonitor-controller.js
+++ b/devtools/client/netmonitor/src/netmonitor-controller.js
@@ -2,20 +2,17 @@
  * 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 { TimelineFront } = require("devtools/shared/fronts/timeline");
 const { CurlUtils } = require("devtools/client/shared/curl");
 const { ACTIVITY_TYPE, EVENTS } = require("./constants");
-const {
-  getRequestById,
-  getDisplayedRequestById,
-} = require("./selectors/index");
+const { getDisplayedRequestById } = require("./selectors/index");
 const {
   fetchHeaders,
   formDataURI,
 } = require("./utils/request-utils");
 const {
   getLongString,
   getWebConsoleClient,
   onFirefoxConnect,
@@ -302,17 +299,16 @@ function NetworkEventsHandler() {
   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);
-  this._onResponseContent = this._onResponseContent.bind(this);
   this._onSecurityInfo = this._onSecurityInfo.bind(this);
   this._onEventTimings = this._onEventTimings.bind(this);
 }
 
 NetworkEventsHandler.prototype = {
   get client() {
     return NetMonitorController._target.client;
   },
@@ -423,125 +419,151 @@ NetworkEventsHandler.prototype = {
         fromCache,
         fromServiceWorker,
       },
       true
     )
     .then(() => window.emit(EVENTS.REQUEST_ADDED, id));
   },
 
-  async updateRequest(id, data) {
-    await this.actions.updateRequest(id, data, true);
-    let {
-      responseContent,
-      responseCookies,
-      responseHeaders,
-      requestCookies,
-      requestHeaders,
-      requestPostData,
-    } = data;
-    let request = getRequestById(window.gStore.getState(), id);
-
-    if (requestHeaders && requestHeaders.headers && requestHeaders.headers.length) {
-      let headers = await fetchHeaders(requestHeaders, getLongString);
-      if (headers) {
-        await this.actions.updateRequest(
-          id,
-          { requestHeaders: headers },
-          true,
-        );
-      }
-    }
-
-    if (responseHeaders && responseHeaders.headers && responseHeaders.headers.length) {
-      let headers = await fetchHeaders(responseHeaders, getLongString);
-      if (headers) {
-        await this.actions.updateRequest(
-          id,
-          { responseHeaders: headers },
-          true,
-        );
-      }
-    }
-
-    if (request && responseContent && responseContent.content) {
-      let { mimeType } = request;
-      let { text, encoding } = responseContent.content;
+  async fetchImage(mimeType, responseContent) {
+    let payload = {};
+    if (mimeType && responseContent && responseContent.content) {
+      let { encoding, text } = responseContent.content;
       let response = await getLongString(text);
-      let payload = {};
 
       if (mimeType.includes("image/")) {
         payload.responseContentDataUri = formDataURI(mimeType, encoding, response);
       }
 
       responseContent.content.text = response;
       payload.responseContent = responseContent;
-
-      await this.actions.updateRequest(id, payload, true);
+    }
+    return payload;
+  },
 
-      if (mimeType.includes("image/")) {
-        window.emit(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED);
+  async fetchRequestHeaders(requestHeaders) {
+    let payload = {};
+    if (requestHeaders && requestHeaders.headers && requestHeaders.headers.length) {
+      let headers = await fetchHeaders(requestHeaders, getLongString);
+      if (headers) {
+        payload.requestHeaders = headers;
       }
     }
+    return payload;
+  },
 
-    // Search the POST data upload stream for request headers and add
-    // them as a separate property, different from the classic headers.
+  async fetchResponseHeaders(responseHeaders) {
+    let payload = {};
+    if (responseHeaders && responseHeaders.headers && responseHeaders.headers.length) {
+      let headers = await fetchHeaders(responseHeaders, getLongString);
+      if (headers) {
+        payload.responseHeaders = headers;
+      }
+    }
+    return payload;
+  },
+
+  // Search the POST data upload stream for request headers and add
+  // them as a separate property, different from the classic headers.
+  async fetchPostData(requestPostData) {
+    let payload = {};
     if (requestPostData && requestPostData.postData) {
       let { text } = requestPostData.postData;
       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 };
-
-      await this.actions.updateRequest(id, payload, true);
     }
+    return payload;
+  },
 
-    // Fetch request and response cookies long value.
-    // Actor does not provide full sized cookie value when the value is too long
-    // To display values correctly, we need fetch them in each request.
+  async fetchResponseCookies(responseCookies) {
+    let payload = {};
+    if (responseCookies) {
+      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 getLongString(cookie.value),
+          }));
+        }
+        if (resCookies.length) {
+          payload.responseCookies = resCookies;
+        }
+      }
+    }
+    return payload;
+  },
+
+  // Fetch request and response cookies long value.
+  // Actor does not provide full sized cookie value when the value is too long
+  // To display values correctly, we need fetch them in each request.
+  async fetchRequestCookies(requestCookies) {
+    let payload = {};
     if (requestCookies) {
       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 getLongString(cookie.value),
           }));
         }
         if (reqCookies.length) {
-          await this.actions.updateRequest(id, { requestCookies: reqCookies }, true);
+          payload.requestCookies = reqCookies;
         }
       }
     }
+    return payload;
+  },
 
-    if (responseCookies) {
-      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 getLongString(cookie.value),
-          }));
-        }
-        if (resCookies.length) {
-          await this.actions.updateRequest(id, { responseCookies: resCookies }, true);
-        }
-      }
-    }
+  async updateRequest(id, data) {
+    let {
+      mimeType,
+      responseContent,
+      responseCookies,
+      responseHeaders,
+      requestCookies,
+      requestHeaders,
+      requestPostData,
+    } = data;
+
+    // fetch request detail contents in parallel
+    let [
+      imageObj,
+      requestHeadersObj,
+      responseHeadersObj,
+      postDataObj,
+      requestCookiesObj,
+      responseCookiesObj,
+    ] = await Promise.all([
+      this.fetchImage(mimeType, responseContent),
+      this.fetchRequestHeaders(requestHeaders),
+      this.fetchResponseHeaders(responseHeaders),
+      this.fetchPostData(requestPostData),
+      this.fetchRequestCookies(requestCookies),
+      this.fetchResponseCookies(responseCookies),
+    ]);
+
+    let payload = Object.assign({}, data,
+                                    imageObj, requestHeadersObj, responseHeadersObj,
+                                    postDataObj, requestCookiesObj, responseCookiesObj);
+    await this.actions.updateRequest(id, payload, true);
   },
 
   /**
    * The "networkEventUpdate" message type handler.
    *
    * @param string type
    *        Message type.
    * @param object packet
@@ -563,19 +585,20 @@ NetworkEventsHandler.prototype = {
       case "requestPostData":
         this.webConsoleClient.getRequestPostData(actor,
           this._onRequestPostData);
         window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, actor);
         break;
       case "securityInfo":
         this.updateRequest(actor, {
           securityState: networkInfo.securityInfo,
+        }).then(() => {
+          this.webConsoleClient.getSecurityInfo(actor, this._onSecurityInfo);
+          window.emit(EVENTS.UPDATING_SECURITY_INFO, actor);
         });
-        this.webConsoleClient.getSecurityInfo(actor, this._onSecurityInfo);
-        window.emit(EVENTS.UPDATING_SECURITY_INFO, actor);
         break;
       case "responseHeaders":
         this.webConsoleClient.getResponseHeaders(actor,
           this._onResponseHeaders);
         window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, actor);
         break;
       case "responseCookies":
         this.webConsoleClient.getResponseCookies(actor,
@@ -585,35 +608,36 @@ NetworkEventsHandler.prototype = {
       case "responseStart":
         this.updateRequest(actor, {
           httpVersion: networkInfo.response.httpVersion,
           remoteAddress: networkInfo.response.remoteAddress,
           remotePort: networkInfo.response.remotePort,
           status: networkInfo.response.status,
           statusText: networkInfo.response.statusText,
           headersSize: networkInfo.response.headersSize
+        }).then(() => {
+          window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
         });
-        window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
         break;
       case "responseContent":
-        this.updateRequest(actor, {
-          contentSize: networkInfo.response.bodySize,
-          transferredSize: networkInfo.response.transferredSize,
-          mimeType: networkInfo.response.content.mimeType
-        });
         this.webConsoleClient.getResponseContent(actor,
-          this._onResponseContent);
+          this._onResponseContent.bind(this, {
+            contentSize: networkInfo.response.bodySize,
+            transferredSize: networkInfo.response.transferredSize,
+            mimeType: networkInfo.response.content.mimeType
+          }));
         window.emit(EVENTS.UPDATING_RESPONSE_CONTENT, actor);
         break;
       case "eventTimings":
         this.updateRequest(actor, {
           totalTime: networkInfo.totalTime
+        }).then(() => {
+          this.webConsoleClient.getEventTimings(actor, this._onEventTimings);
+          window.emit(EVENTS.UPDATING_EVENT_TIMINGS, actor);
         });
-        this.webConsoleClient.getEventTimings(actor, this._onEventTimings);
-        window.emit(EVENTS.UPDATING_EVENT_TIMINGS, actor);
         break;
     }
   },
 
   /**
    * Handles additional information received for a "requestHeaders" packet.
    *
    * @param object response
@@ -695,23 +719,24 @@ NetworkEventsHandler.prototype = {
     }).then(() => {
       window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from);
     });
   },
 
   /**
    * Handles additional information received for a "responseContent" packet.
    *
+   * @param object data
+   *        The message received from the server event.
    * @param object response
    *        The message received from the server.
    */
-  _onResponseContent: function (response) {
-    this.updateRequest(response.from, {
-      responseContent: response
-    }).then(() => {
+  _onResponseContent: function (data, response) {
+    let payload = Object.assign({ responseContent: response }, data);
+    this.updateRequest(response.from, payload).then(() => {
       window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, response.from);
     });
   },
 
   /**
    * Handles additional information received for a "eventTimings" packet.
    *
    * @param object response
--- a/devtools/client/netmonitor/test/browser_net_icon-preview.js
+++ b/devtools/client/netmonitor/test/browser_net_icon-preview.js
@@ -4,74 +4,66 @@
 "use strict";
 
 /**
  * Tests if image responses show a thumbnail in the requests menu.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL);
+  const SELECTOR = ".requests-list-icon[src]";
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let { NetMonitorController } =
     windowRequire("devtools/client/netmonitor/src/netmonitor-controller");
-  let {
-    ACTIVITY_TYPE,
-    EVENTS,
-  } = windowRequire("devtools/client/netmonitor/src/constants");
+  let { ACTIVITY_TYPE } = windowRequire("devtools/client/netmonitor/src/constants");
 
   gStore.dispatch(Actions.batchEnable(false));
 
-  let wait = waitForEvents();
+  let wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS);
   yield performRequests();
   yield wait;
+  yield waitUntil(() => !!document.querySelector(SELECTOR));
 
   info("Checking the image thumbnail when all items are shown.");
   checkImageThumbnail();
 
   gStore.dispatch(Actions.sortBy("contentSize"));
   info("Checking the image thumbnail when all items are sorted.");
   checkImageThumbnail();
 
   gStore.dispatch(Actions.toggleRequestFilterType("images"));
   info("Checking the image thumbnail when only images are shown.");
   checkImageThumbnail();
 
   info("Reloading the debuggee and performing all requests again...");
-  wait = waitForEvents();
+  wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS);
   yield reloadAndPerformRequests();
   yield wait;
+  yield waitUntil(() => !!document.querySelector(SELECTOR));
 
   info("Checking the image thumbnail after a reload.");
   checkImageThumbnail();
 
   yield teardown(monitor);
 
-  function waitForEvents() {
-    return promise.all([
-      waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS),
-      monitor.panelWin.once(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED)
-    ]);
-  }
-
   function performRequests() {
     return ContentTask.spawn(tab.linkedBrowser, {}, function* () {
       content.wrappedJSObject.performRequests();
     });
   }
 
   function* reloadAndPerformRequests() {
     yield NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
     yield performRequests();
   }
 
   function checkImageThumbnail() {
-    is(document.querySelectorAll(".requests-list-icon[data-type=thumbnail]").length, 1,
+    is(document.querySelectorAll(SELECTOR).length, 1,
       "There should be only one image request with a thumbnail displayed.");
-    is(document.querySelector(".requests-list-icon[data-type=thumbnail]").src,
-      TEST_IMAGE_DATA_URI,
+    is(document.querySelector(SELECTOR).src, TEST_IMAGE_DATA_URI,
       "The image requests-list-icon thumbnail is displayed correctly.");
-    is(document.querySelector(".requests-list-icon[data-type=thumbnail]").hidden, false,
+    is(document.querySelector(SELECTOR).hidden, false,
       "The image requests-list-icon thumbnail should not be hidden.");
   }
 });
--- a/devtools/client/netmonitor/test/browser_net_image-tooltip.js
+++ b/devtools/client/netmonitor/test/browser_net_image-tooltip.js
@@ -6,53 +6,49 @@
 const IMAGE_TOOLTIP_URL = EXAMPLE_URL + "html_image-tooltip-test-page.html";
 const IMAGE_TOOLTIP_REQUESTS = 1;
 
 /**
  * Tests if image responses show a popup in the requests menu when hovered.
  */
 add_task(function* test() {
   let { tab, monitor } = yield initNetMonitor(IMAGE_TOOLTIP_URL);
+  const SELECTOR = ".requests-list-icon[src]";
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let { NetMonitorController } =
     windowRequire("devtools/client/netmonitor/src/netmonitor-controller");
-  let {
-    ACTIVITY_TYPE,
-    EVENTS,
-  } = windowRequire("devtools/client/netmonitor/src/constants");
+  let { ACTIVITY_TYPE } = windowRequire("devtools/client/netmonitor/src/constants");
   let toolboxDoc = monitor.panelWin.parent.document;
 
   gStore.dispatch(Actions.batchEnable(false));
 
   let onEvents = waitForNetworkEvents(monitor, IMAGE_TOOLTIP_REQUESTS);
-  let onThumbnail = monitor.panelWin.once(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED);
   yield performRequests();
   yield onEvents;
-  yield onThumbnail;
+  yield waitUntil(() => !!document.querySelector(SELECTOR));
 
   info("Checking the image thumbnail after a few requests were made...");
   yield showTooltipAndVerify(document.querySelectorAll(".request-list-item")[0]);
 
   // Hide tooltip before next test, to avoid the situation that tooltip covers
   // the icon for the request of the next test.
   info("Checking the image thumbnail gets hidden...");
   yield hideTooltipAndVerify(document.querySelectorAll(".request-list-item")[0]);
 
   // +1 extra document reload
   onEvents = waitForNetworkEvents(monitor, IMAGE_TOOLTIP_REQUESTS + 1);
-  onThumbnail = monitor.panelWin.once(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED);
 
   info("Reloading the debuggee and performing all requests again...");
   yield NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
   yield performRequests();
   yield onEvents;
-  yield onThumbnail;
+  yield waitUntil(() => !!document.querySelector(SELECTOR));
 
   info("Checking the image thumbnail after a reload.");
   yield showTooltipAndVerify(document.querySelectorAll(".request-list-item")[1]);
 
   info("Checking if the image thumbnail is hidden when mouse leaves the menu widget");
   let requestsListContents = document.querySelector(".requests-list-contents");
   EventUtils.synthesizeMouse(requestsListContents, 0, 0, { type: "mouseout" },
                              monitor.panelWin);
--- a/devtools/client/netmonitor/test/browser_net_simple-request-data.js
+++ b/devtools/client/netmonitor/test/browser_net_simple-request-data.js
@@ -188,44 +188,26 @@ function test() {
         SIMPLE_SJS,
         {
           status: "200",
           statusText: "Och Aye"
         }
       );
     });
 
-    monitor.panelWin.once(EVENTS.UPDATING_RESPONSE_CONTENT, () => {
+    monitor.panelWin.once(EVENTS.RECEIVED_RESPONSE_CONTENT, () => {
       let requestItem = getSortedRequests(gStore.getState()).get(0);
 
       is(requestItem.transferredSize, "12",
         "The transferredSize data has an incorrect value.");
       is(requestItem.contentSize, "12",
         "The contentSize data has an incorrect value.");
       is(requestItem.mimeType, "text/plain; charset=utf-8",
         "The mimeType data has an incorrect value.");
 
-      verifyRequestItemTarget(
-        document,
-        getDisplayedRequests(gStore.getState()),
-        requestItem,
-        "GET",
-        SIMPLE_SJS,
-        {
-          type: "plain",
-          fullMimeType: "text/plain; charset=utf-8",
-          transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
-          size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
-        }
-      );
-    });
-
-    monitor.panelWin.once(EVENTS.RECEIVED_RESPONSE_CONTENT, () => {
-      let requestItem = getSortedRequests(gStore.getState()).get(0);
-
       ok(requestItem.responseContent,
         "There should be a responseContent data available.");
       // eslint-disable-next-line mozilla/no-cpows-in-tests
       is(requestItem.responseContent.content.mimeType,
         "text/plain; charset=utf-8",
         "The responseContent data has an incorrect |content.mimeType| property.");
       // eslint-disable-next-line mozilla/no-cpows-in-tests
       is(requestItem.responseContent.content.text,