Bug 1390037 - Code clean up; r?rickychien draft
authorJan Odvarko <odvarko@gmail.com>
Tue, 19 Sep 2017 19:24:35 +0200
changeset 667059 848aaf7cccca09b2eda54ddc5f90ba59e3227a6f
parent 667058 804e61e340c4b50cbcc6680713d19c7560fe3b40
child 667492 dd87e66941a9aff7e3bfa73f245493b7c069a85c
push id80603
push userjodvarko@mozilla.com
push dateTue, 19 Sep 2017 17:25:11 +0000
reviewersrickychien
bugs1390037
milestone57.0a1
Bug 1390037 - Code clean up; r?rickychien MozReview-Commit-ID: BGEt9llDA2W
devtools/client/netmonitor/src/connector/chrome-connector.js
devtools/client/netmonitor/src/connector/chrome/bulk-loader.js
devtools/client/netmonitor/src/connector/chrome/cdp-connector.js
devtools/client/netmonitor/src/connector/chrome/events.js
devtools/client/netmonitor/src/connector/chrome/payloads.js
devtools/client/netmonitor/src/connector/chrome/request-utils.js
devtools/client/netmonitor/src/connector/chrome/request.js
devtools/client/netmonitor/src/connector/chrome/response-utils.js
devtools/client/netmonitor/src/connector/chrome/response.js
devtools/client/netmonitor/src/connector/chrome/utils.js
--- a/devtools/client/netmonitor/src/connector/chrome-connector.js
+++ b/devtools/client/netmonitor/src/connector/chrome-connector.js
@@ -1,17 +1,21 @@
 /* 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 { ACTIVITY_TYPE } = require("../constants");
-const { CDPConnector } = require("./chrome/events");
+const { CDPConnector } = require("./chrome/cdp-connector");
 
+/**
+ * This object is responsible for connecting the Network monitor
+ * panel with Chrome browser.
+ */
 class ChromeConnector {
   constructor() {
     // Internal properties
     this.payloadQueue = [];
     this.connector = undefined;
 
     // Public methods
     this.connect = this.connect.bind(this);
@@ -31,17 +35,18 @@ class ChromeConnector {
     this.connector.willNavigate(this.willNavigate);
   }
 
   async disconnect() {
     this.connector.disconnect();
   }
 
   /**
-   * currently all events are about "navigation" is not support on CDP
+   * Remove all requests upon navigation.
+   * TODO: support 'Persist Log'
    */
   willNavigate() {
     this.actions.batchReset();
     this.actions.clearRequests();
   }
 
   /**
    * Triggers a specific "activity" to be performed by the frontend.
@@ -69,21 +74,21 @@ class ChromeConnector {
 
   /**
    * Send a HTTP request data payload
    *
    * @param {object} data data payload would like to sent to backend
    * @param {function} callback callback will be invoked after the request finished
    */
   sendHTTPRequest(data, callback) {
-    // TODO : not support. currently didn't provide this feature in CDP API.
+    // TODO: not supported currently in CDP API.
   }
 
   setPreferences() {
-    // TODO : implement.
+    // TODO
   }
 
   viewSourceInDebugger() {
-    // TODO : implement.
+    // TODO
   }
 }
 
 module.exports = new ChromeConnector();
--- a/devtools/client/netmonitor/src/connector/chrome/bulk-loader.js
+++ b/devtools/client/netmonitor/src/connector/chrome/bulk-loader.js
@@ -1,17 +1,124 @@
 /* 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";
 
-let bulkLoader = undefined;
+let PriorityLevels = {
+  Critical: 1,
+  Major: 2,
+  Normal: 3,
+  None: 0
+};
+
+let bulkLoader;
+
+/**
+ * In order to reduce the frequency of network request update, bulk-loader
+ * is introducing a task schedule mechanism to queue and delay the upcoming
+ * network request. This is helpful to speed up performance and mitigate UI
+ * frozen when loading large website.
+ *
+ * BulkLoader is implemented as a singleton.
+ */
+const getBulkLoader = () => {
+  if (!bulkLoader) {
+    bulkLoader = new BulkLoader();
+  }
+  return bulkLoader;
+};
+
+// Helpers
+
+const mappingPriority = (priority, options) => {
+  switch (priority) {
+    case PriorityLevels.Critical:
+      return options.Critical;
+    case PriorityLevels.Major:
+      return options.Major;
+    case PriorityLevels.Normal:
+      return options.Normal;
+    case PriorityLevels.None:
+    default:
+      break;
+  }
+  return options.None;
+};
+
+const getTimeoutMS = (priority) => {
+  const delay = {
+    Critical: 3000,
+    Major: 1000,
+    Normal: 500,
+    None: 100
+  };
+  return mappingPriority(priority, delay);
+};
 
-let PriorityLevels = {Critical: 1, Major: 2, Normal: 3, None: 0};
+const getDelayStartMS = (priority) => {
+  const delay = {
+    Critical: 1,
+    Major: 50,
+    Normal: 100,
+    None: 500
+  };
+  return mappingPriority(priority, delay);
+};
+
+const LoaderPromise = (priority, callback) => {
+  return new Promise((resolve, reject) => {
+    const ms = getTimeoutMS(priority);
+    // Set up the real work
+    setTimeout(() => callback(resolve, reject), getDelayStartMS(priority));
+
+    // Set up the timeout
+    setTimeout(() => {
+      reject("Promise timed out after " + ms + " ms");
+    }, ms);
+  });
+};
+
+// TODO: recovery thread after all tasks finished.
+class Thread {
+  constructor() {
+    this.scheduler = new Scheduler();
+  }
+
+  addTask(callback, priority) {
+    this.scheduler.sync(() => {
+      return LoaderPromise(
+        !priority ? PriorityLevels.None : priority,
+        (resolve, reject) => callback(resolve, reject)
+      );
+    });
+  }
+}
+
+class BulkLoader {
+  constructor() {
+    this.threads = new Map();
+    this.tasks = [];
+  }
+
+  add(id, callback, priority) {
+    let thread = this.threads.get(priority);
+    if (!this.threads.has(priority)) {
+      thread = new Thread();
+      this.threads.set(priority, thread);
+    }
+    this.tasks.push({id, priority, task: callback, isFinished: false});
+    return thread.addTask(callback, priority);
+  }
+
+  reset() {
+    this.threads.clear();
+  }
+}
 
 class Scheduler {
   constructor() {
     this.busy = false;
     this.queue = [];
   }
 
   sync(task) {
@@ -22,105 +129,24 @@ class Scheduler {
     return null;
   }
 
   dequeue() {
     let self = this;
     let recursive = (resolve) => {
       self.dequeue();
     };
+
     this.busy = true;
+
     let next = this.queue.shift();
     if (next) {
       next().then(recursive, recursive);
     } else {
       this.busy = false;
     }
   }
 }
-// singleton class
-const getBulkLoader = () => {
-  const mappingPriority = (priority, options) => {
-    switch (priority) {
-      case PriorityLevels.Critical:
-        return options.Critical;
-      case PriorityLevels.Major:
-        return options.Major;
-      case PriorityLevels.Normal:
-        return options.Normal;
-      case PriorityLevels.None:
-      default:
-        break;
-    }
-    return options.None;
-  };
-
-  const getTimeoutMS = (priority) => {
-    const delay = {Critical: 3000, Major: 1000, Normal: 500, None: 100};
-    return mappingPriority(priority, delay);
-  };
-
-  const getDelayStartMS = (priority) => {
-    const delay = {Critical: 1, Major: 50, Normal: 100, None: 500};
-    return mappingPriority(priority, delay);
-  };
-
-  const LoaderPromise = (priority, callback) => {
-    return new Promise((resolve, reject) => {
-      const ms = getTimeoutMS(priority);
-      // Set up the real work
-      setTimeout(() => callback(resolve, reject), getDelayStartMS(priority));
-
-      // Set up the timeout
-      setTimeout(() => {
-        reject("Promise timed out after " + ms + " ms");
-      }, ms);
-    });
-  };
-
-    // TODO : recovery thread after all tasks finished.
-  class Thread {
-    constructor() {
-      this.scheduler = new Scheduler();
-    }
-
-    addTask(callback, priority) {
-      this.scheduler.sync(() => {
-        return LoaderPromise(
-          !priority ? PriorityLevels.None : priority,
-          (resolve, reject) => callback(resolve, reject)
-        );
-      });
-    }
-  }
-
-  class BulkLoader {
-    constructor() {
-      this.threads = new Map();
-      this.tasks = [];
-    }
-
-    add(id, callback, priority) {
-      let thread = this.threads.get(priority);
-      if (!this.threads.has(priority)) {
-        thread = new Thread();
-        this.threads.set(priority, thread);
-      }
-      this.tasks.push({id, priority, task: callback, isFinished: false});
-      return thread.addTask(callback, priority);
-    }
-
-    reset() {
-      this.threads.clear();
-    }
-  }
-
-  if (!bulkLoader) {
-    bulkLoader = new BulkLoader();
-  }
-
-  return bulkLoader;
-};
 
 module.exports = {
   getBulkLoader,
   PriorityLevels
 };
rename from devtools/client/netmonitor/src/connector/chrome/events.js
rename to devtools/client/netmonitor/src/connector/chrome/cdp-connector.js
--- a/devtools/client/netmonitor/src/connector/chrome/events.js
+++ b/devtools/client/netmonitor/src/connector/chrome/cdp-connector.js
@@ -1,38 +1,47 @@
 /* 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 {EVENTS} = require("../../constants");
-const {Payloads} = require("./utils");
-const {getBulkLoader, PriorityLevels} = require("./bulk-loader");
+
+const { EVENTS } = require("../../constants");
+const { Payloads } = require("./payloads");
+const { getBulkLoader, PriorityLevels } = require("./bulk-loader");
 
+/**
+ * CDPConnector handles Network and Page events from Chrome remote debug protocol.
+ * It's using helpers from payloads.js and bulk-loader.js when received request/response
+ * data from CDP.
+ */
 class CDPConnector {
   constructor() {
     this.payloads = new Payloads();
     this.onNetworkUpdate = this.onNetworkUpdate.bind(this);
     this.onResponseReceived = this.onResponseReceived.bind(this);
     this.onDataReceived = this.onDataReceived.bind(this);
     this.onLoadingFinished = this.onLoadingFinished.bind(this);
     this.onLoadingFailed = this.onLoadingFailed.bind(this);
     this.update = this.update.bind(this);
   }
 
   setup(connection, actions) {
     let {Network, Page} = connection;
     this.Network = Network;
     this.Page = Page;
     this.actions = actions;
+
+    // Add event listeners
     Network.requestWillBeSent(this.onNetworkUpdate);
     Network.responseReceived(this.onResponseReceived);
     Network.dataReceived(this.onDataReceived);
     Network.loadingFinished(this.onLoadingFinished);
     Network.loadingFailed(this.onLoadingFailed);
+
     Network.enable();
     Page.enable();
   }
 
   disconnect() {
     this.Network.disable();
     this.Page.disable();
     this.payloads.clear();
@@ -44,17 +53,17 @@ class CDPConnector {
       this.Page.disable(),
       this.payloads.clear(),
       this.Network.enable(),
       this.Page.enable()
     ]);
   }
 
   willNavigate(event) {
-    // not support
+    // TODO
   }
 
   onNetworkUpdate(params) {
     let {requestId} = params;
     let payload = this.payloads.add(requestId);
     return payload.update(params).then(
       ([request, header, postData]) => {
         let bulkloader = getBulkLoader();
@@ -79,35 +88,36 @@ class CDPConnector {
         loader.add(
           requestId,
           (resolve) => {
             this.updateResponseHeader(requestId, header);
             this.updateResponseState(requestId, state);
             this.updateResponseTiming(requestId, timings);
             this.getResponseContent(params);
             resolve();
-          }
-          , PriorityLevels.Major);
+          },
+          PriorityLevels.Major
+        );
       });
   }
 
   onDataReceived(params) {
     let {requestId} = params;
     let payload = this.payloads.get(requestId);
     payload.update(params);
   }
 
   onLoadingFinished(params) {
     let {requestId} = params;
     let payload = this.payloads.get(requestId);
     if (payload) {
       payload.log("LoadingFinished", params);
     }
+
     // TODO: verify getCookie method.
-    //
   }
 
   updateRequestHeader(requestId, header) {
     if (!header) {
       return;
     }
     this.update(requestId, {
       requestHeaders: header
@@ -145,17 +155,16 @@ class CDPConnector {
   }
 
   onLoadingFailed(params) {
     let {requestId} = params;
     let payload = this.payloads.get(requestId);
     if (payload) {
       payload.log("LoadingFailed", params);
     }
-    // console.log(params.requestId);
   }
 
   async getResponseContent(params) {
     let {requestId, response} = params;
 
     return this.Network.getResponseBody({requestId}).then(
       (content) => {
         let payload = this.payloads.get(requestId);
@@ -175,21 +184,19 @@ class CDPConnector {
       }
     );
   }
 
   updateResponseContent(requestId, payload) {
     if (!payload) {
       return;
     }
-    this.actions.updateRequest(requestId, payload, true).then(
-      () => {
-        window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, requestId);
-      }
-    );
+    this.actions.updateRequest(requestId, payload, true).then(() => {
+      window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, requestId);
+    });
   }
 
   updatePostData(requestId, postData) {
     if (!postData) {
       return;
     }
     this.update(requestId, {
       requestPostData: postData,
@@ -208,28 +215,23 @@ class CDPConnector {
       url,
       isXHR,
       cause,
       startedDateTime,
       fromCache,
       fromServiceWorker,
     } = data;
 
-    this.actions.addRequest(
-      id,
-      {
-        startedMillis: startedDateTime,
-        method,
-        url,
-        isXHR,
-        cause,
-        fromCache,
-        fromServiceWorker,
-      },
-      true,
-    )
-      .then(() => window.emit(EVENTS.REQUEST_ADDED, id));
+    this.actions.addRequest(id, {
+      startedMillis: startedDateTime,
+      method,
+      url,
+      isXHR,
+      cause,
+      fromCache,
+      fromServiceWorker,
+    }, true).then(() => window.emit(EVENTS.REQUEST_ADDED, id));
   }
 }
 
 module.exports = {
   CDPConnector
 };
rename from devtools/client/netmonitor/src/connector/chrome/utils.js
rename to devtools/client/netmonitor/src/connector/chrome/payloads.js
--- a/devtools/client/netmonitor/src/connector/chrome/utils.js
+++ b/devtools/client/netmonitor/src/connector/chrome/payloads.js
@@ -1,45 +1,55 @@
 /* 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 { Request, Header, PostData } = require("./request");
-const { State, ResponseContent, Timings} = require("./response");
+const { Request, Header, PostData } = require("./request-utils");
+const { State, ResponseContent, Timings} = require("./response-utils");
 const { getBulkLoader } = require("./bulk-loader");
 
+/**
+ * Payload collects data received over CDP.
+ */
 class Payload {
   constructor() {
     this.payload = {};
     this.update = this.update.bind(this);
   }
+
   async update(payload) {
-    let { request, response, requestId, timestamp,
-          content, dataLength, encodedDataLength } = payload;
+    let {
+      request,
+      response,
+      requestId,
+      timestamp,
+      content,
+      dataLength,
+      encodedDataLength
+    } = payload;
+
     let {
       headers,
       postData,
       timing
     } = (request ? request : response) || {};
 
     const header = await this.mappingHeader(requestId, headers);
 
     this.requestId = requestId;
+    this.updateTimestamp(timestamp);
 
-    this.updateTimestamp(timestamp);
-    let data = await this.mappingAll(
-      requestId,
-      {
-        payload, response, postData,
-        header, content, timing,
-        dataLength, encodedDataLength
-      }
-    );
+    let data = await this.mappingAll(requestId, {
+      payload, response, postData,
+      header, content, timing,
+      dataLength, encodedDataLength
+    });
+
     return data;
   }
 
   log(reason, info) {
     this.updatePayload({
       type: reason,
       log: info
     });
@@ -52,33 +62,54 @@ class Payload {
     );
   }
 
   updatePayload(data) {
     this.payload = Object.assign({}, this.payload, data);
   }
 
   async mappingAll(requestId, data) {
-    let {payload, response, postData,
-         header, content, timing,
-         dataLength, encodedDataLength } = data;
-    let [requests, headers, post,
-         status, timings, responses]
-        = await Promise.all(
-          [
-            this.mappingRequest(requestId, payload),
-            header,
-            this.mappingRequestPostData(requestId, postData, header),
-            this.mappingResponseStatus(requestId, response, header),
-            this.mappingTiming(requestId, timing),
-            this.mappingResponseContent(requestId, response, content)
-          ]);
+    let {
+      payload,
+      response,
+      postData,
+      header,
+      content,
+      timing,
+      dataLength,
+      encodedDataLength
+    } = data;
+
+    let [
+      requests,
+      headers,
+      post,
+      status,
+      timings,
+      responses
+    ] = await Promise.all([
+      this.mappingRequest(requestId, payload),
+      header,
+      this.mappingRequestPostData(requestId, postData, header),
+      this.mappingResponseStatus(requestId, response, header),
+      this.mappingTiming(requestId, timing),
+      this.mappingResponseContent(requestId, response, content)
+    ]);
+
     this.updatePayload({
-      requests, headers, post, status, timings, responses, dataLength, encodedDataLength
+      requests,
+      headers,
+      post,
+      status,
+      timings,
+      responses,
+      dataLength,
+      encodedDataLength
     });
+
     return [ requests, headers, post, status, timings, responses ];
   }
 
   async mappingTiming(requestId, timing) {
     return !timing ? undefined : Timings(requestId, timing);
   }
 
   async mappingRequest(requestId, payload) {
@@ -98,16 +129,17 @@ class Payload {
     return !response ? undefined : State(response, header);
   }
 
   async mappingResponseContent(requestId, response, content) {
     return !response || !content ?
       undefined : ResponseContent(requestId, response, content);
   }
 }
+
 class Payloads {
   constructor() {
     this.payloads = new Map();
   }
 
   add(id) {
     if (!this.payloads.has(id)) {
       this.payloads.set(id, new Payload());
@@ -123,10 +155,10 @@ class Payloads {
   clear() {
     this.payloads.clear();
     let loader = getBulkLoader();
     loader.reset();
   }
 }
 
 module.exports = {
-  Payload, Payloads
+  Payloads
 };
rename from devtools/client/netmonitor/src/connector/chrome/request.js
rename to devtools/client/netmonitor/src/connector/chrome/request-utils.js
--- a/devtools/client/netmonitor/src/connector/chrome/request.js
+++ b/devtools/client/netmonitor/src/connector/chrome/request-utils.js
@@ -26,77 +26,87 @@ function mappingCallFrames(callFrames) {
     }
   );
   return stacktrace;
 }
 
 function Cause(initiator) {
   let {url, type, stack} = initiator;
   let {callFrames} = stack || {};
+
   if (!stack || !callFrames.length) {
     return undefined;
   }
+
   let cause = {
     type: type,
     loadingDocumentUri: url,
     stacktrace: mappingCallFrames(callFrames)
   };
+
   return cause;
 }
 
 function Header(id, headers) {
   let header = [];
   let headersSize = 0;
+
   Object.keys(headers).map((value) => {
-    header.push(
-      {
-        name: value,
-        value: headers[value],
-      }
-    );
+    header.push({
+      name: value,
+      value: headers[value],
+    });
+
     headersSize += value.length + headers[value].length;
   });
 
   return {
     from: id,
     headers: header,
     headersSize: headersSize,
     rawHeaders: undefined,
   };
 }
+
 function PostData(id, postData, header) {
   let {headers, headersSize} = header;
-  let payload = {},
-    requestPostData = {
-      from: id, postDataDiscarded: false, postData: {}
-    };
+  let payload = {};
+  let requestPostData = {
+    from: id,
+    postDataDiscarded: false,
+    postData: {}
+  };
+
   if (postData) {
     requestPostData.postData.text = postData;
     payload.requestPostData = Object.assign({}, requestPostData);
     payload.requestHeadersFromUploadStream = {headers, headersSize};
   }
+
   return payload;
 }
 
 /**
- * Not support on current version.
+ * Not supported by the current protocol version.
  * unstable method: Network.getCookies
  * cause: https://chromedevtools.github.io/devtools-protocol/tot/Network/#method-getCookies
  */
 function Cookie(id, Network) {
   // TODO: verify
 }
 
 function Request(id, requestData) {
   let {request, initiator, timestamp} = requestData;
   let {url, method} = request;
   let cause = !initiator ? undefined : Cause(initiator);
+
   return {
     method, url, cause,
-    isXHR: false, // TODO: verify
+    // TODO: verify
+    isXHR: false,
     startedDateTime: timestamp,
     fromCache: undefined,
     fromServiceWorker: undefined
   };
 }
 
 module.exports = {
   Cause,
rename from devtools/client/netmonitor/src/connector/chrome/response.js
rename to devtools/client/netmonitor/src/connector/chrome/response-utils.js
--- a/devtools/client/netmonitor/src/connector/chrome/response.js
+++ b/devtools/client/netmonitor/src/connector/chrome/response-utils.js
@@ -5,98 +5,108 @@
 "use strict";
 
 const { formDataURI } = require("../../utils/request-utils");
 
 function ResponseInfo(id, response, content) {
   let {
     mimeType
   } = response;
-  const {body, base64Encoded} = content;
+
+  const {
+    body,
+    base64Encoded
+  } = content;
+
   return {
     from: id,
     content: {
       mimeType: mimeType,
       text: !body ? "" : body,
       size: !body ? 0 : body.length,
       encoding: base64Encoded ? "base64" : undefined
     }
   };
 }
 
 function ResponseContent(id, response, content) {
   const {body, base64Encoded} = content;
   let {mimeType, encodedDataLength} = response;
   let responseContent = ResponseInfo(id, response, content);
-  let payload = Object.assign(
-    {
-      responseContent,
-      contentSize: !body ? 0 : body.length,
-      transferredSize: encodedDataLength, // TODO: verify
-      mimeType: mimeType
-    }, body);
+  let payload = Object.assign({
+    responseContent,
+    contentSize: !body ? 0 : body.length,
+    transferredSize: encodedDataLength, // TODO: verify
+    mimeType: mimeType
+  }, body);
+
   if (mimeType.includes("image/")) {
     payload.responseContentDataUri = formDataURI(mimeType, base64Encoded, response);
   }
+
   return payload;
 }
 
 /**
- * Not support on current version.
+ * Not supported by the current protocol version.
  * unstable method: Security
  * cause: https://chromedevtools.github.io/devtools-protocol/tot/Security/
  */
 function SecurityDetails(id, security) {
   // TODO : verify
-
   return {};
 }
 
 function Timings(id, timing) {
-  // TODO : implement
+  // TODO: implement
   let {
     dnsStart,
     dnsEnd,
     connectStart,
     connectEnd,
     sendStart,
     sendEnd,
     receiveHeadersEnd
   } = timing;
+
   let dns = parseInt(dnsEnd - dnsStart, 10);
   let connect = parseInt(connectEnd - connectStart, 10);
   let send = parseInt(sendEnd - sendStart, 10);
   let total = parseInt(receiveHeadersEnd, 10);
+
   return {
     from: id,
     timings: {
       blocked: 0,
       dns: dns,
       connect: connect,
       send: send,
       wait: parseInt(receiveHeadersEnd - (send + connect + dns), 10),
       receive: 0,
     },
     totalTime: total,
   };
 }
+
 function State(response, headers) {
   let { headersSize } = headers;
   let {
     status,
     statusText,
     remoteIPAddress,
     remotePort
   } = response;
+
   return {
     remoteAddress: remoteIPAddress,
     remotePort,
     status,
     statusText,
     headersSize
   };
 }
+
 module.exports = {
   State,
   Timings,
   ResponseContent,
   SecurityDetails
 };