Bug 1308480 - Move sorting functions to their own module r?Honza draft
authorJarda Snajdr <jsnajdr@gmail.com>
Tue, 11 Oct 2016 15:13:01 +0200
changeset 424333 df593d7c0c10eb988cc6864f3d7efcca56d4fda7
parent 424226 ec34fbeafcaa2ddf3a5240e77ab1d8fb673e494e
child 533643 7ae3b05d614e5f422d387e91088e11c4fb26333c
push id32121
push userbmo:jsnajdr@gmail.com
push dateWed, 12 Oct 2016 15:07:52 +0000
reviewersHonza
bugs1308480
milestone52.0a1
Bug 1308480 - Move sorting functions to their own module r?Honza MozReview-Commit-ID: LRFMI8hy8Uf
devtools/client/netmonitor/moz.build
devtools/client/netmonitor/netmonitor-view.js
devtools/client/netmonitor/request-utils.js
devtools/client/netmonitor/requests-menu-view.js
devtools/client/netmonitor/sort-predicates.js
--- a/devtools/client/netmonitor/moz.build
+++ b/devtools/client/netmonitor/moz.build
@@ -10,11 +10,12 @@ DIRS += [
 DevToolsModules(
     'events.js',
     'filter-predicates.js',
     'l10n.js',
     'panel.js',
     'prefs.js',
     'request-utils.js',
     'requests-menu-view.js',
+    'sort-predicates.js',
 )
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
--- a/devtools/client/netmonitor/netmonitor-view.js
+++ b/devtools/client/netmonitor/netmonitor-view.js
@@ -14,17 +14,21 @@ XPCOMUtils.defineLazyGetter(this, "Netwo
 
 const {VariablesView} = require("resource://devtools/client/shared/widgets/VariablesView.jsm");
 const {VariablesViewController} = require("resource://devtools/client/shared/widgets/VariablesViewController.jsm");
 const {ToolSidebar} = require("devtools/client/framework/sidebar");
 const { testing: isTesting } = require("devtools/shared/flags");
 const {ViewHelpers, Heritage} = require("devtools/client/shared/widgets/view-helpers");
 const {PluralForm} = require("devtools/shared/plural-form");
 const {Filters} = require("./filter-predicates");
-const {getFormDataSections, formDataURI, writeHeaderText, getKeyWithEvent} = require("./request-utils");
+const {getFormDataSections,
+       formDataURI,
+       writeHeaderText,
+       getKeyWithEvent,
+       getUriHostPort} = require("./request-utils");
 const {L10N} = require("./l10n");
 const {RequestsMenuView} = require("./requests-menu-view");
 
 // ms
 const WDA_DEFAULT_VERIFY_INTERVAL = 50;
 
 // Use longer timeout during testing as the tests need this process to succeed
 // and two seconds is quite short on slow debug builds. The timeout here should
@@ -1310,17 +1314,17 @@ NetworkDetailsView.prototype = {
       let disabledLabel = L10N.getStr("netmonitor.security.disabled");
 
       // Connection parameters
       setValue("#security-protocol-version-value",
         securityInfo.protocolVersion);
       setValue("#security-ciphersuite-value", securityInfo.cipherSuite);
 
       // Host header
-      let domain = NetMonitorView.RequestsMenu._getUriHostPort(url);
+      let domain = getUriHostPort(url);
       let hostHeader = L10N.getFormatStr("netmonitor.security.hostHeader",
         domain);
       setValue("#security-info-host-header", hostHeader);
 
       // Parameters related to the domain
       setValue("#security-http-strict-transport-security-value",
                 securityInfo.hsts ? enabledLabel : disabledLabel);
 
--- a/devtools/client/netmonitor/request-utils.js
+++ b/devtools/client/netmonitor/request-utils.js
@@ -1,13 +1,14 @@
 "use strict";
 
 const { Ci } = require("chrome");
 const { KeyCodes } = require("devtools/client/shared/keycodes");
 const { Task } = require("devtools/shared/task");
+const NetworkHelper = require("devtools/shared/webconsole/network-helper");
 
 /**
  * Helper method to get a wrapped function which can be bound to as
  * an event listener directly and is executed only when data-key is
  * present in event.target.
  *
  * @param function callback
  *          Function to execute execute when data-key
@@ -104,16 +105,58 @@ exports.formDataURI = function (mimeType
  * @return string text
  *         List of headers in text format
  */
 exports.writeHeaderText = function (headers) {
   return headers.map(({name, value}) => name + ": " + value).join("\n");
 };
 
 /**
+ * Helper for getting an abbreviated string for a mime type.
+ *
+ * @param string mimeType
+ * @return string
+ */
+exports.getAbbreviatedMimeType = function (mimeType) {
+  if (!mimeType) {
+    return "";
+  }
+  return (mimeType.split(";")[0].split("/")[1] || "").split("+")[0];
+};
+
+/**
+ * Helpers for getting details about an nsIURL.
+ *
+ * @param nsIURL | string url
+ * @return string
+ */
+exports.getUriNameWithQuery = function (url) {
+  if (!(url instanceof Ci.nsIURL)) {
+    url = NetworkHelper.nsIURL(url);
+  }
+
+  let name = NetworkHelper.convertToUnicode(
+    unescape(url.fileName || url.filePath || "/"));
+  let query = NetworkHelper.convertToUnicode(unescape(url.query));
+
+  return name + (query ? "?" + query : "");
+};
+
+exports.getUriHostPort = function (url) {
+  if (!(url instanceof Ci.nsIURL)) {
+    url = NetworkHelper.nsIURL(url);
+  }
+  return NetworkHelper.convertToUnicode(unescape(url.hostPort));
+};
+
+exports.getUriHost = function (url) {
+  return exports.getUriHostPort(url).replace(/:\d+$/, "");
+};
+
+/**
  * Convert a nsIContentPolicy constant to a display string
  */
 const LOAD_CAUSE_STRINGS = {
   [Ci.nsIContentPolicy.TYPE_INVALID]: "invalid",
   [Ci.nsIContentPolicy.TYPE_OTHER]: "other",
   [Ci.nsIContentPolicy.TYPE_SCRIPT]: "script",
   [Ci.nsIContentPolicy.TYPE_IMAGE]: "img",
   [Ci.nsIContentPolicy.TYPE_STYLESHEET]: "stylesheet",
--- a/devtools/client/netmonitor/requests-menu-view.js
+++ b/devtools/client/netmonitor/requests-menu-view.js
@@ -11,19 +11,27 @@ const {HTMLTooltip} = require("devtools/
 const {setImageTooltip, getImageDimensions} =
   require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
 const {Heritage, WidgetMethods, setNamedTimeout} =
   require("devtools/client/shared/widgets/view-helpers");
 const {gDevTools} = require("devtools/client/framework/devtools");
 const {Curl, CurlUtils} = require("devtools/client/shared/curl");
 const {PluralForm} = require("devtools/shared/plural-form");
 const {Filters, isFreetextMatch} = require("./filter-predicates");
-const {getFormDataSections, formDataURI, writeHeaderText, getKeyWithEvent,
+const {Sorters} = require("./sort-predicates");
+const {L10N, WEBCONSOLE_L10N} = require("./l10n");
+const {getFormDataSections,
+       formDataURI,
+       writeHeaderText,
+       getKeyWithEvent,
+       getAbbreviatedMimeType,
+       getUriNameWithQuery,
+       getUriHostPort,
+       getUriHost,
        loadCauseString} = require("./request-utils");
-const {L10N, WEBCONSOLE_L10N} = require("./l10n");
 
 loader.lazyServiceGetter(this, "clipboardHelper",
   "@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
 
 loader.lazyRequireGetter(this, "HarExporter",
   "devtools/client/netmonitor/har/har-exporter", true);
 
 loader.lazyRequireGetter(this, "NetworkHelper",
@@ -87,19 +95,16 @@ function RequestsMenuView() {
   dumpn("RequestsMenuView was instantiated");
 
   this._flushRequests = this._flushRequests.bind(this);
   this._onHover = this._onHover.bind(this);
   this._onSelect = this._onSelect.bind(this);
   this._onSwap = this._onSwap.bind(this);
   this._onResize = this._onResize.bind(this);
   this._onScroll = this._onScroll.bind(this);
-  this._byFile = this._byFile.bind(this);
-  this._byDomain = this._byDomain.bind(this);
-  this._byType = this._byType.bind(this);
   this._onSecurityIconClick = this._onSecurityIconClick.bind(this);
 }
 
 RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
   /**
    * Initialization function, called when the network monitor is started.
    */
   initialize: function () {
@@ -117,17 +122,17 @@ RequestsMenuView.prototype = Heritage.ex
     this.tooltip = new HTMLTooltip(NetMonitorController._toolbox.doc, { type: "arrow" });
     this.tooltip.startTogglingOnHover(widgetParentEl, this._onHover, {
       toggleDelay: REQUESTS_TOOLTIP_TOGGLE_DELAY,
       interactive: true
     });
     $("#requests-menu-contents").addEventListener("scroll", this._onScroll, true);
 
     Prefs.filters.forEach(type => this.filterOn(type));
-    this.sortContents(this._byTiming);
+    this.sortContents((a, b) => Sorters.waterfall(a.attachment, b.attachment));
 
     this.allowFocusOnRightClick = true;
     this.maintainSelectionVisible = true;
 
     this.widget.addEventListener("select", this._onSelect, false);
     this.widget.addEventListener("swap", this._onSwap, false);
     this._splitter.addEventListener("mousemove", this._onResize, false);
     window.addEventListener("resize", this._onResize, false);
@@ -784,75 +789,75 @@ RequestsMenuView.prototype = Heritage.ex
       // Used to style the next column.
       target.parentNode.setAttribute("active", "true");
     }
 
     // Sort by whatever was requested.
     switch (type) {
       case "status":
         if (direction == "ascending") {
-          this.sortContents(this._byStatus);
+          this.sortContents((a, b) => Sorters.status(a.attachment, b.attachment));
         } else {
-          this.sortContents((a, b) => !this._byStatus(a, b));
+          this.sortContents((a, b) => -Sorters.status(a.attachment, b.attachment));
         }
         break;
       case "method":
         if (direction == "ascending") {
-          this.sortContents(this._byMethod);
+          this.sortContents((a, b) => Sorters.method(a.attachment, b.attachment));
         } else {
-          this.sortContents((a, b) => !this._byMethod(a, b));
+          this.sortContents((a, b) => -Sorters.method(a.attachment, b.attachment));
         }
         break;
       case "file":
         if (direction == "ascending") {
-          this.sortContents(this._byFile);
+          this.sortContents((a, b) => Sorters.file(a.attachment, b.attachment));
         } else {
-          this.sortContents((a, b) => !this._byFile(a, b));
+          this.sortContents((a, b) => -Sorters.file(a.attachment, b.attachment));
         }
         break;
       case "domain":
         if (direction == "ascending") {
-          this.sortContents(this._byDomain);
+          this.sortContents((a, b) => Sorters.domain(a.attachment, b.attachment));
         } else {
-          this.sortContents((a, b) => !this._byDomain(a, b));
+          this.sortContents((a, b) => -Sorters.domain(a.attachment, b.attachment));
         }
         break;
       case "cause":
         if (direction == "ascending") {
-          this.sortContents(this._byCause);
+          this.sortContents((a, b) => Sorters.cause(a.attachment, b.attachment));
         } else {
-          this.sortContents((a, b) => !this._byCause(a, b));
+          this.sortContents((a, b) => -Sorters.cause(a.attachment, b.attachment));
         }
         break;
       case "type":
         if (direction == "ascending") {
-          this.sortContents(this._byType);
+          this.sortContents((a, b) => Sorters.type(a.attachment, b.attachment));
         } else {
-          this.sortContents((a, b) => !this._byType(a, b));
+          this.sortContents((a, b) => -Sorters.type(a.attachment, b.attachment));
         }
         break;
       case "transferred":
         if (direction == "ascending") {
-          this.sortContents(this._byTransferred);
+          this.sortContents((a, b) => Sorters.transferred(a.attachment, b.attachment));
         } else {
-          this.sortContents((a, b) => !this._byTransferred(a, b));
+          this.sortContents((a, b) => -Sorters.transferred(a.attachment, b.attachment));
         }
         break;
       case "size":
         if (direction == "ascending") {
-          this.sortContents(this._bySize);
+          this.sortContents((a, b) => Sorters.size(a.attachment, b.attachment));
         } else {
-          this.sortContents((a, b) => !this._bySize(a, b));
+          this.sortContents((a, b) => -Sorters.size(a.attachment, b.attachment));
         }
         break;
       case "waterfall":
         if (direction == "ascending") {
-          this.sortContents(this._byTiming);
+          this.sortContents((a, b) => Sorters.waterfall(a.attachment, b.attachment));
         } else {
-          this.sortContents((a, b) => !this._byTiming(a, b));
+          this.sortContents((a, b) => -Sorters.waterfall(a.attachment, b.attachment));
         }
         break;
     }
 
     this.refreshSummary();
     this.refreshZebra();
   },
 
@@ -866,86 +871,16 @@ RequestsMenuView.prototype = Heritage.ex
     $("#details-pane-toggle").disabled = true;
     $("#requests-menu-empty-notice").hidden = false;
 
     this.empty();
     this.refreshSummary();
   },
 
   /**
-   * Predicates used when sorting items.
-   *
-   * @param object aFirst
-   *        The first item used in the comparison.
-   * @param object aSecond
-   *        The second item used in the comparison.
-   * @return number
-   *         -1 to sort aFirst to a lower index than aSecond
-   *          0 to leave aFirst and aSecond unchanged with respect to each other
-   *          1 to sort aSecond to a lower index than aFirst
-   */
-  _byTiming: function ({ attachment: first }, { attachment: second }) {
-    return first.startedMillis > second.startedMillis;
-  },
-
-  _byStatus: function ({ attachment: first }, { attachment: second }) {
-    return first.status == second.status
-           ? first.startedMillis > second.startedMillis
-           : first.status > second.status;
-  },
-
-  _byMethod: function ({ attachment: first }, { attachment: second }) {
-    return first.method == second.method
-           ? first.startedMillis > second.startedMillis
-           : first.method > second.method;
-  },
-
-  _byFile: function ({ attachment: first }, { attachment: second }) {
-    let firstUrl = this._getUriNameWithQuery(first.url).toLowerCase();
-    let secondUrl = this._getUriNameWithQuery(second.url).toLowerCase();
-    return firstUrl == secondUrl
-      ? first.startedMillis > second.startedMillis
-      : firstUrl > secondUrl;
-  },
-
-  _byDomain: function ({ attachment: first }, { attachment: second }) {
-    let firstDomain = this._getUriHostPort(first.url).toLowerCase();
-    let secondDomain = this._getUriHostPort(second.url).toLowerCase();
-    return firstDomain == secondDomain
-      ? first.startedMillis > second.startedMillis
-      : firstDomain > secondDomain;
-  },
-
-  _byCause: function ({ attachment: first }, { attachment: second }) {
-    let firstCause = loadCauseString(first.cause.type);
-    let secondCause = loadCauseString(second.cause.type);
-
-    return firstCause == secondCause
-      ? first.startedMillis > second.startedMillis
-      : firstCause > secondCause;
-  },
-
-  _byType: function ({ attachment: first }, { attachment: second }) {
-    let firstType = this._getAbbreviatedMimeType(first.mimeType).toLowerCase();
-    let secondType = this._getAbbreviatedMimeType(second.mimeType).toLowerCase();
-
-    return firstType == secondType
-      ? first.startedMillis > second.startedMillis
-      : firstType > secondType;
-  },
-
-  _byTransferred: function ({ attachment: first }, { attachment: second }) {
-    return first.transferredSize > second.transferredSize;
-  },
-
-  _bySize: function ({ attachment: first }, { attachment: second }) {
-    return first.contentSize > second.contentSize;
-  },
-
-  /**
    * Refreshes the status displayed in this container's footer, providing
    * concise information about all requests.
    */
   refreshSummary: function () {
     let visibleItems = this.visibleItems;
     let visibleRequestsCount = visibleItems.length;
     if (!visibleRequestsCount) {
       this._summary.setAttribute("label", L10N.getStr("networkMenu.empty"));
@@ -1340,19 +1275,19 @@ RequestsMenuView.prototype = Heritage.ex
       case "url": {
         let uri;
         try {
           uri = NetworkHelper.nsIURL(value);
         } catch (e) {
           // User input may not make a well-formed url yet.
           break;
         }
-        let nameWithQuery = this._getUriNameWithQuery(uri);
-        let hostPort = this._getUriHostPort(uri);
-        let host = this._getUriHost(uri);
+        let nameWithQuery = getUriNameWithQuery(uri);
+        let hostPort = getUriHostPort(uri);
+        let host = getUriHost(uri);
         let unicodeUrl = NetworkHelper.convertToUnicode(unescape(uri.spec));
 
         let file = $(".requests-menu-file", target);
         file.setAttribute("value", nameWithQuery);
         file.setAttribute("tooltiptext", unicodeUrl);
 
         let domain = $(".requests-menu-domain", target);
         domain.setAttribute("value", hostPort);
@@ -1459,17 +1394,17 @@ RequestsMenuView.prototype = Heritage.ex
           text = this.getFormattedSize(value);
         }
 
         node.setAttribute("value", text);
         node.setAttribute("tooltiptext", text);
         break;
       }
       case "mimeType": {
-        let type = this._getAbbreviatedMimeType(value);
+        let type = getAbbreviatedMimeType(value);
         let node = $(".requests-menu-type", target);
         let text = CONTENT_MIME_TYPE_ABBREVIATIONS[type] || type;
         node.setAttribute("value", text);
         node.setAttribute("tooltiptext", value);
         break;
       }
       case "responseContent": {
         let { mimeType } = item.attachment;
@@ -1989,58 +1924,16 @@ RequestsMenuView.prototype = Heritage.ex
    */
   _registerLastRequestEnd: function (unixTime) {
     if (this._lastRequestEndedMillis < unixTime) {
       this._lastRequestEndedMillis = unixTime;
     }
   },
 
   /**
-   * Helpers for getting details about an nsIURL.
-   *
-   * @param nsIURL | string url
-   * @return string
-   */
-  _getUriNameWithQuery: function (url) {
-    if (!(url instanceof Ci.nsIURL)) {
-      url = NetworkHelper.nsIURL(url);
-    }
-
-    let name = NetworkHelper.convertToUnicode(
-      unescape(url.fileName || url.filePath || "/"));
-    let query = NetworkHelper.convertToUnicode(unescape(url.query));
-
-    return name + (query ? "?" + query : "");
-  },
-
-  _getUriHostPort: function (url) {
-    if (!(url instanceof Ci.nsIURL)) {
-      url = NetworkHelper.nsIURL(url);
-    }
-    return NetworkHelper.convertToUnicode(unescape(url.hostPort));
-  },
-
-  _getUriHost: function (url) {
-    return this._getUriHostPort(url).replace(/:\d+$/, "");
-  },
-
-  /**
-   * Helper for getting an abbreviated string for a mime type.
-   *
-   * @param string mimeType
-   * @return string
-   */
-  _getAbbreviatedMimeType: function (mimeType) {
-    if (!mimeType) {
-      return "";
-    }
-    return (mimeType.split(";")[0].split("/")[1] || "").split("+")[0];
-  },
-
-  /**
    * Gets the total number of bytes representing the cumulated content size of
    * a set of requests. Returns 0 for an empty set.
    *
    * @param array itemsArray
    * @return number
    */
   _getTotalBytesOfRequests: function (itemsArray) {
     if (!itemsArray.length) {
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/sort-predicates.js
@@ -0,0 +1,92 @@
+"use strict";
+
+const { getAbbreviatedMimeType,
+        getUriNameWithQuery,
+        getUriHostPort,
+        loadCauseString } = require("./request-utils");
+
+/**
+ * Predicates used when sorting items.
+ *
+ * @param object first
+ *        The first item used in the comparison.
+ * @param object second
+ *        The second item used in the comparison.
+ * @return number
+ *         <0 to sort first to a lower index than second
+ *         =0 to leave first and second unchanged with respect to each other
+ *         >0 to sort second to a lower index than first
+ */
+
+function waterfall(first, second) {
+  return first.startedMillis - second.startedMillis;
+}
+
+function status(first, second) {
+  return first.status == second.status
+         ? first.startedMillis - second.startedMillis
+         : first.status - second.status;
+}
+
+function method(first, second) {
+  if (first.method == second.method) {
+    return first.startedMillis - second.startedMillis;
+  }
+  return first.method > second.method ? 1 : -1;
+}
+
+function file(first, second) {
+  let firstUrl = getUriNameWithQuery(first.url).toLowerCase();
+  let secondUrl = getUriNameWithQuery(second.url).toLowerCase();
+  if (firstUrl == secondUrl) {
+    return first.startedMillis - second.startedMillis;
+  }
+  return firstUrl > secondUrl ? 1 : -1;
+}
+
+function domain(first, second) {
+  let firstDomain = getUriHostPort(first.url).toLowerCase();
+  let secondDomain = getUriHostPort(second.url).toLowerCase();
+  if (firstDomain == secondDomain) {
+    return first.startedMillis - second.startedMillis;
+  }
+  return firstDomain > secondDomain ? 1 : -1;
+}
+
+function cause(first, second) {
+  let firstCause = loadCauseString(first.cause.type);
+  let secondCause = loadCauseString(second.cause.type);
+  if (firstCause == secondCause) {
+    return first.startedMillis - second.startedMillis;
+  }
+  return firstCause > secondCause ? 1 : -1;
+}
+
+function type(first, second) {
+  let firstType = getAbbreviatedMimeType(first.mimeType).toLowerCase();
+  let secondType = getAbbreviatedMimeType(second.mimeType).toLowerCase();
+  if (firstType == secondType) {
+    return first.startedMillis - second.startedMillis;
+  }
+  return firstType > secondType ? 1 : -1;
+}
+
+function transferred(first, second) {
+  return first.transferredSize - second.transferredSize;
+}
+
+function size(first, second) {
+  return first.contentSize - second.contentSize;
+}
+
+exports.Sorters = {
+  status,
+  method,
+  file,
+  domain,
+  cause,
+  type,
+  transferred,
+  size,
+  waterfall,
+};