Bug 884432 - Background color of request items keep changing when any filter is applied, r=rcampbell
authorVictor Porof <vporof@mozilla.com>
Fri, 21 Jun 2013 17:33:58 +0300
changeset 136010 edfa5974a68e2003c068ab79d67c5e7e386dd51b
parent 136009 1d52e5f47f5aa7c42353a4dffde8baa8a6506ab2
child 136011 43c6fe6a93016794ffaebe9c580fd03e5b06f821
push id24858
push userttaubert@mozilla.com
push dateSat, 22 Jun 2013 04:11:24 +0000
treeherdermozilla-central@cea75ce9a559 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrcampbell
bugs884432
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 884432 - Background color of request items keep changing when any filter is applied, r=rcampbell
browser/devtools/netmonitor/netmonitor-view.js
browser/devtools/netmonitor/test/browser_net_sort-01.js
browser/devtools/netmonitor/test/head.js
browser/devtools/shared/widgets/ViewHelpers.jsm
browser/themes/linux/devtools/netmonitor.css
browser/themes/osx/devtools/netmonitor.css
browser/themes/windows/devtools/netmonitor.css
--- a/browser/devtools/netmonitor/netmonitor-view.js
+++ b/browser/devtools/netmonitor/netmonitor-view.js
@@ -345,16 +345,17 @@ RequestsMenuView.prototype = Heritage.ex
         isXHR: aIsXHR
       }
     });
 
     $("#details-pane-toggle").disabled = false;
     $("#requests-menu-empty-notice").hidden = true;
 
     this.refreshSummary();
+    this.refreshZebra();
   },
 
   /**
    * Filters all network requests in this container by a specified type.
    *
    * @param string aType
    *        Either "all", "html", "css", "js", "xhr", "fonts", "images", "media"
    *        or "flash".
@@ -398,16 +399,17 @@ RequestsMenuView.prototype = Heritage.ex
         this.filterContents(this._onMedia);
         break;
       case "flash":
         this.filterContents(this._onFlash);
         break;
     }
 
     this.refreshSummary();
+    this.refreshZebra();
   },
 
   /**
    * Sorts all network requests in this container by a specified detail.
    *
    * @param string aType
    *        Either "status", "method", "file", "domain", "type", "size" or
    *        "waterfall".
@@ -481,16 +483,19 @@ RequestsMenuView.prototype = Heritage.ex
       case "waterfall":
         if (direction == "ascending") {
           this.sortContents(this._byTiming);
         } else {
           this.sortContents((a, b) => !this._byTiming(a, b));
         }
         break;
     }
+
+    this.refreshSummary();
+    this.refreshZebra();
   },
 
   /**
    * Predicates used when filtering items.
    *
    * @param object aItem
    *        The filtered item.
    * @return boolean
@@ -594,16 +599,36 @@ RequestsMenuView.prototype = Heritage.ex
     this._summary.setAttribute("value", str
       .replace("#1", visibleRequestsCount)
       .replace("#2", L10N.numberWithDecimals((totalBytes || 0) / 1024, 2))
       .replace("#3", L10N.numberWithDecimals((totalMillis || 0) / 1000, 2))
     );
   },
 
   /**
+   * Adds odd/even attributes to all the visible items in this container.
+   */
+  refreshZebra: function() {
+    let visibleItems = this.orderedVisibleItems;
+
+    for (let i = 0, len = visibleItems.length; i < len; i++) {
+      let requestItem = visibleItems[i];
+      let requestTarget = requestItem.target;
+
+      if (i % 2 == 0) {
+        requestTarget.setAttribute("even", "");
+        requestTarget.removeAttribute("odd");
+      } else {
+        requestTarget.setAttribute("odd", "");
+        requestTarget.removeAttribute("even");
+      }
+    }
+  },
+
+  /**
    * Schedules adding additional information to a network request.
    *
    * @param string aId
    *        An identifier coming from the network monitor controller.
    * @param object aData
    *        An object containing several { key: value } tuples of network info.
    *        Supported keys are "httpVersion", "status", "statusText" etc.
    */
@@ -713,16 +738,17 @@ RequestsMenuView.prototype = Heritage.ex
     // Make sure all the requests are sorted and filtered.
     // Freshly added requests may not yet contain all the information required
     // for sorting and filtering predicates, so this is done each time the
     // network requests table is flushed (don't worry, events are drained first
     // so this doesn't happen once per network event update).
     this.sortContents();
     this.filterContents();
     this.refreshSummary();
+    this.refreshZebra();
   },
 
   /**
    * Customization function for creating an item's UI.
    *
    * @param string aMethod
    *        Specifies the request method (e.g. "GET", "POST", etc.)
    * @param string aUrl
--- a/browser/devtools/netmonitor/test/browser_net_sort-01.js
+++ b/browser/devtools/netmonitor/test/browser_net_sort-01.js
@@ -14,136 +14,161 @@ function test() {
 
     RequestsMenu.lazyUpdate = false;
 
     waitForNetworkEvents(aMonitor, 5).then(() => {
       testContents([0, 1, 2, 3, 4])
         .then(() => {
           info("Testing swap(0, 0)");
           RequestsMenu.swapItemsAtIndices(0, 0);
+          RequestsMenu.refreshZebra();
           return testContents([0, 1, 2, 3, 4]);
         })
         .then(() => {
           info("Testing swap(0, 1)");
           RequestsMenu.swapItemsAtIndices(0, 1);
+          RequestsMenu.refreshZebra();
           return testContents([1, 0, 2, 3, 4]);
         })
         .then(() => {
           info("Testing swap(0, 2)");
           RequestsMenu.swapItemsAtIndices(0, 2);
+          RequestsMenu.refreshZebra();
           return testContents([1, 2, 0, 3, 4]);
         })
         .then(() => {
           info("Testing swap(0, 3)");
           RequestsMenu.swapItemsAtIndices(0, 3);
+          RequestsMenu.refreshZebra();
           return testContents([1, 2, 3, 0, 4]);
         })
         .then(() => {
           info("Testing swap(0, 4)");
           RequestsMenu.swapItemsAtIndices(0, 4);
+          RequestsMenu.refreshZebra();
           return testContents([1, 2, 3, 4, 0]);
         })
         .then(() => {
           info("Testing swap(1, 0)");
           RequestsMenu.swapItemsAtIndices(1, 0);
+          RequestsMenu.refreshZebra();
           return testContents([0, 2, 3, 4, 1]);
         })
         .then(() => {
           info("Testing swap(1, 1)");
           RequestsMenu.swapItemsAtIndices(1, 1);
+          RequestsMenu.refreshZebra();
           return testContents([0, 2, 3, 4, 1]);
         })
         .then(() => {
           info("Testing swap(1, 2)");
           RequestsMenu.swapItemsAtIndices(1, 2);
+          RequestsMenu.refreshZebra();
           return testContents([0, 1, 3, 4, 2]);
         })
         .then(() => {
           info("Testing swap(1, 3)");
           RequestsMenu.swapItemsAtIndices(1, 3);
+          RequestsMenu.refreshZebra();
           return testContents([0, 3, 1, 4, 2]);
         })
         .then(() => {
           info("Testing swap(1, 4)");
           RequestsMenu.swapItemsAtIndices(1, 4);
+          RequestsMenu.refreshZebra();
           return testContents([0, 3, 4, 1, 2]);
         })
         .then(() => {
           info("Testing swap(2, 0)");
           RequestsMenu.swapItemsAtIndices(2, 0);
+          RequestsMenu.refreshZebra();
           return testContents([2, 3, 4, 1, 0]);
         })
         .then(() => {
           info("Testing swap(2, 1)");
           RequestsMenu.swapItemsAtIndices(2, 1);
+          RequestsMenu.refreshZebra();
           return testContents([1, 3, 4, 2, 0]);
         })
         .then(() => {
           info("Testing swap(2, 2)");
           RequestsMenu.swapItemsAtIndices(2, 2);
+          RequestsMenu.refreshZebra();
           return testContents([1, 3, 4, 2, 0]);
         })
         .then(() => {
           info("Testing swap(2, 3)");
           RequestsMenu.swapItemsAtIndices(2, 3);
+          RequestsMenu.refreshZebra();
           return testContents([1, 2, 4, 3, 0]);
         })
         .then(() => {
           info("Testing swap(2, 4)");
           RequestsMenu.swapItemsAtIndices(2, 4);
+          RequestsMenu.refreshZebra();
           return testContents([1, 4, 2, 3, 0]);
         })
         .then(() => {
           info("Testing swap(3, 0)");
           RequestsMenu.swapItemsAtIndices(3, 0);
+          RequestsMenu.refreshZebra();
           return testContents([1, 4, 2, 0, 3]);
         })
         .then(() => {
           info("Testing swap(3, 1)");
           RequestsMenu.swapItemsAtIndices(3, 1);
+          RequestsMenu.refreshZebra();
           return testContents([3, 4, 2, 0, 1]);
         })
         .then(() => {
           info("Testing swap(3, 2)");
           RequestsMenu.swapItemsAtIndices(3, 2);
+          RequestsMenu.refreshZebra();
           return testContents([2, 4, 3, 0, 1]);
         })
         .then(() => {
           info("Testing swap(3, 3)");
           RequestsMenu.swapItemsAtIndices(3, 3);
+          RequestsMenu.refreshZebra();
           return testContents([2, 4, 3, 0, 1]);
         })
         .then(() => {
           info("Testing swap(3, 4)");
           RequestsMenu.swapItemsAtIndices(3, 4);
+          RequestsMenu.refreshZebra();
           return testContents([2, 3, 4, 0, 1]);
         })
         .then(() => {
           info("Testing swap(4, 0)");
           RequestsMenu.swapItemsAtIndices(4, 0);
+          RequestsMenu.refreshZebra();
           return testContents([2, 3, 0, 4, 1]);
         })
         .then(() => {
           info("Testing swap(4, 1)");
           RequestsMenu.swapItemsAtIndices(4, 1);
+          RequestsMenu.refreshZebra();
           return testContents([2, 3, 0, 1, 4]);
         })
         .then(() => {
           info("Testing swap(4, 2)");
           RequestsMenu.swapItemsAtIndices(4, 2);
+          RequestsMenu.refreshZebra();
           return testContents([4, 3, 0, 1, 2]);
         })
         .then(() => {
           info("Testing swap(4, 3)");
           RequestsMenu.swapItemsAtIndices(4, 3);
+          RequestsMenu.refreshZebra();
           return testContents([3, 4, 0, 1, 2]);
         })
         .then(() => {
           info("Testing swap(4, 4)");
           RequestsMenu.swapItemsAtIndices(4, 4);
+          RequestsMenu.refreshZebra();
           return testContents([3, 4, 0, 1, 2]);
         })
         .then(() => {
           info("Clearing sort.");
           RequestsMenu.sortBy();
           return testContents([0, 1, 2, 3, 4]);
         })
         .then(() => {
--- a/browser/devtools/netmonitor/test/head.js
+++ b/browser/devtools/netmonitor/test/head.js
@@ -186,16 +186,23 @@ function waitForNetworkEvents(aMonitor, 
 
   return deferred.promise;
 }
 
 function verifyRequestItemTarget(aRequestItem, aMethod, aUrl, aData = {}) {
   info("> Verifying: " + aMethod + " " + aUrl + " " + aData.toSource());
   info("> Request: " + aRequestItem.attachment.toSource());
 
+  let requestsMenu = aRequestItem.ownerView;
+  let widgetIndex = requestsMenu.indexOfItem(aRequestItem);
+  let visibleIndex = requestsMenu.orderedVisibleItems.indexOf(aRequestItem);
+
+  info("Widget index of item: " + widgetIndex);
+  info("Visible index of item: " + visibleIndex);
+
   let { fuzzyUrl, status, statusText, type, fullMimeType, size, time } = aData;
   let { attachment, target } = aRequestItem
 
   let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL);
   let name = uri.fileName || "/";
   let query = uri.query;
   let hostPort = uri.hostPort;
 
@@ -254,9 +261,23 @@ function verifyRequestItemTarget(aReques
   if (time !== undefined) {
     let value = target.querySelector(".requests-menu-timings-total").getAttribute("value");
     let tooltip = target.querySelector(".requests-menu-timings-total").getAttribute("tooltiptext");
     info("Displayed time: " + value);
     info("Tooltip time: " + tooltip);
     ok(~~(value.match(/[0-9]+/)) >= 0, "The displayed time is incorrect.");
     ok(~~(tooltip.match(/[0-9]+/)) >= 0, "The tooltip time is incorrect.");
   }
+
+  if (visibleIndex != -1) {
+    if (visibleIndex % 2 == 0) {
+      ok(aRequestItem.target.hasAttribute("even"),
+        "Unexpected 'even' attribute for " + aRequestItem.value);
+      ok(!aRequestItem.target.hasAttribute("odd"),
+        "Unexpected 'odd' attribute for " + aRequestItem.value);
+    } else {
+      ok(!aRequestItem.target.hasAttribute("even"),
+        "Unexpected 'even' attribute for " + aRequestItem.value);
+      ok(aRequestItem.target.hasAttribute("odd"),
+        "Unexpected 'odd' attribute for " + aRequestItem.value);
+    }
+  }
 }
--- a/browser/devtools/shared/widgets/ViewHelpers.jsm
+++ b/browser/devtools/shared/widgets/ViewHelpers.jsm
@@ -357,25 +357,28 @@ ViewHelpers.Prefs.prototype = {
   }
 };
 
 /**
  * A generic Item is used to describe children present in a Widget.
  * The label, value and description properties are necessarily strings.
  * Iterable via "for (let childItem in parentItem) { }".
  *
+ * @param object aOwnerView
+ *        The owner view creating this item.
  * @param any aAttachment
  *        Some attached primitive/object.
  * @param nsIDOMNode | nsIDOMDocumentFragment | array aContents [optional]
  *        A prebuilt node, or an array containing the following properties:
  *        - aLabel: the label displayed in the widget
  *        - aValue: the actual internal value of the item
  *        - aDescription: an optional description of the item
  */
-function Item(aAttachment, aContents = []) {
+function Item(aOwnerView, aAttachment, aContents = []) {
+  this.ownerView = aOwnerView;
   this.attachment = aAttachment;
 
   let [aLabel, aValue, aDescription] = aContents;
   this._label = aLabel + "";
   this._value = aValue + "";
   this._description = (aDescription || "") + "";
 
   // Allow the insertion of prebuilt nodes, otherwise delegate the item view
@@ -402,17 +405,17 @@ Item.prototype = {
    *        Additional options or flags supported by this operation:
    *          - attachment: some attached primitive/object for the item
    *          - attributes: a batch of attributes set to the displayed element
    *          - finalize: function invoked when the child item is removed
    * @return Item
    *         The item associated with the displayed element.
    */
   append: function(aElement, aOptions = {}) {
-    let item = new Item(aOptions.attachment);
+    let item = new Item(this, aOptions.attachment);
 
     // Entangle the item with the newly inserted child node.
     this._entangleItem(item, this._target.appendChild(aElement));
 
     // Handle any additional options after entangling the item.
     if (aOptions.attributes) {
       aOptions.attributes.forEach(e => item._target.setAttribute(e[0], e[1]));
     }
@@ -603,17 +606,17 @@ this.WidgetMethods = {
    *          - attachment: some attached primitive/object for the item
    *          - attributes: a batch of attributes set to the displayed element
    *          - finalize: function invoked when the item is removed
    * @return Item
    *         The item associated with the displayed element if an unstaged push,
    *         undefined if the item was staged for a later commit.
    */
   push: function(aContents, aOptions = {}) {
-    let item = new Item(aOptions.attachment, aContents);
+    let item = new Item(this, aOptions.attachment, aContents);
 
     // Batch the item to be added later.
     if (aOptions.staged) {
       // An ulterior commit operation will ignore any specified index.
       delete aOptions.index;
       return void this._stagedItems.push({ item: item, options: aOptions });
     }
     // Find the target position in this container and insert the item there.
--- a/browser/themes/linux/devtools/netmonitor.css
+++ b/browser/themes/linux/devtools/netmonitor.css
@@ -307,17 +307,17 @@ box.requests-menu-status[code^="5"] {
 .requests-menu-timings-cap.receive {
   background-color: rgba(255,255,255,1.0);
   box-shadow: 0 0 8px 0 rgba(128,255,255,1.0),
               0 0 4px 0 rgba(255,255,255,1.0) inset;
 }
 
 /* SideMenuWidget */
 
-.side-menu-widget-item:nth-child(even) {
+.side-menu-widget-item[odd] {
   background: rgba(255,255,255,0.05);
 }
 
 .side-menu-widget-item-contents {
   padding: 0;
 }
 
 /* Network request details */
--- a/browser/themes/osx/devtools/netmonitor.css
+++ b/browser/themes/osx/devtools/netmonitor.css
@@ -307,17 +307,17 @@ box.requests-menu-status[code^="5"] {
 .requests-menu-timings-cap.receive {
   background-color: rgba(255,255,255,1.0);
   box-shadow: 0 0 8px 0 rgba(128,255,255,1.0),
               0 0 4px 0 rgba(255,255,255,1.0) inset;
 }
 
 /* SideMenuWidget */
 
-.side-menu-widget-item:nth-child(even) {
+.side-menu-widget-item[odd] {
   background: rgba(255,255,255,0.05);
 }
 
 .side-menu-widget-item-contents {
   padding: 0;
 }
 
 /* Network request details */
--- a/browser/themes/windows/devtools/netmonitor.css
+++ b/browser/themes/windows/devtools/netmonitor.css
@@ -307,17 +307,17 @@ box.requests-menu-status[code^="5"] {
 .requests-menu-timings-cap.receive {
   background-color: rgba(255,255,255,1.0);
   box-shadow: 0 0 8px 0 rgba(128,255,255,1.0),
               0 0 4px 0 rgba(255,255,255,1.0) inset;
 }
 
 /* SideMenuWidget */
 
-.side-menu-widget-item:nth-child(even) {
+.side-menu-widget-item[odd] {
   background: rgba(255,255,255,0.05);
 }
 
 .side-menu-widget-item-contents {
   padding: 0;
 }
 
 /* Network request details */