Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 11 Mar 2016 16:57:57 +0100
changeset 288289 28bca8e29596cfa1c24b864925cb5cb5e20e35ab
parent 288288 2f5f092a64add23c4bb911179a21352046848406 (current diff)
parent 288230 f907faa919be95dd411706b0c69a1acad340f81d (diff)
child 288290 c8b421423059068586d436fb7fbab9be5059faf5
push id30079
push userryanvm@gmail.com
push dateSat, 12 Mar 2016 20:24:19 +0000
treeherdermozilla-central@d1d47ba19ce9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone48.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
Merge mozilla-central to mozilla-inbound
mobile/android/base/resources/anim/grow_fade_in_center.xml
mobile/android/base/resources/color/floating_hint_text.xml
mobile/android/base/resources/drawable-hdpi/reader_cropped.png
mobile/android/base/resources/drawable-large-v11/firstrun_background_devices.png
mobile/android/base/resources/drawable-xhdpi/reader_cropped.png
mobile/android/base/resources/drawable/divider_horizontal.xml
mobile/android/base/resources/drawable/ic_menu_quit.xml
mobile/android/base/resources/layout/notification_icon_text.xml
mobile/android/base/resources/layout/notification_progress.xml
mobile/android/base/resources/layout/notification_progress_text.xml
mobile/android/base/resources/layout/overlay_share_toast.xml
mobile/android/base/resources/layout/simple_dropdown_item_1line.xml
mobile/android/services/src/main/res/drawable/sync_pin_background.xml
testing/mozharness/configs/android/androidarm.py
testing/mozharness/configs/android/androidarm_4_3.py
toolkit/components/passwordmgr/test/subtst_notifications_7.html
toolkit/components/passwordmgr/test/test_notifications.html
toolkit/components/search/tests/xpcshell/test_migration.js
--- a/browser/base/content/browser-customization.js
+++ b/browser/base/content/browser-customization.js
@@ -80,20 +80,18 @@ var CustomizationHandler = {
     DownloadsButton.customizeDone();
 
     // The url bar splitter state is dependent on whether stop/reload
     // and the location bar are combined, so we need this ordering
     CombinedStopReload.init();
     UpdateUrlbarSearchSplitterState();
 
     // Update the urlbar
-    if (gURLBar) {
-      URLBarSetURI();
-      XULBrowserWindow.asyncUpdateUI();
-    }
+    URLBarSetURI();
+    XULBrowserWindow.asyncUpdateUI();
 
     // Re-enable parts of the UI we disabled during the dialog
     let menubar = document.getElementById("main-menubar");
     for (let childNode of menubar.childNodes)
       childNode.setAttribute("disabled", false);
     let cmd = document.getElementById("cmd_CustomizeToolbars");
     cmd.removeAttribute("disabled");
 
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -380,18 +380,17 @@ var PlacesCommandHook = {
       PlacesUtils.transactionManager.doTransaction(txn);
       itemId = txn.item.id;
       // Set the character-set.
       if (charset && !PrivateBrowsingUtils.isBrowserPrivate(aBrowser))
         PlacesUtils.setCharsetForURI(uri, charset);
     }
 
     // Revert the contents of the location bar
-    if (gURLBar)
-      gURLBar.handleRevert();
+    gURLBar.handleRevert();
 
     // If it was not requested to open directly in "edit" mode, we are done.
     if (!aShowEditUI)
       return;
 
     // Try to dock the panel to:
     // 1. the bookmarks menu button
     // 2. the identity icon
@@ -455,18 +454,17 @@ var PlacesCommandHook = {
       info.guid = yield PlacesTransactions.NewBookmark(info).transact();
 
       // Set the character-set
       if (charset && !PrivateBrowsingUtils.isBrowserPrivate(aBrowser))
          PlacesUtils.setCharsetForURI(makeURI(url.href), charset);
     }
 
     // Revert the contents of the location bar
-    if (gURLBar)
-      gURLBar.handleRevert();
+    gURLBar.handleRevert();
 
     // If it was not requested to open directly in "edit" mode, we are done.
     if (!aShowEditUI)
       return;
 
     let node = yield PlacesUIUtils.promiseNodeLikeFromFetchInfo(info);
 
     // Try to dock the panel to:
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -403,20 +403,18 @@ const gSessionHistoryObserver = {
     var backCommand = document.getElementById("Browser:Back");
     backCommand.setAttribute("disabled", "true");
     var fwdCommand = document.getElementById("Browser:Forward");
     fwdCommand.setAttribute("disabled", "true");
 
     // Hide session restore button on about:home
     window.messageManager.broadcastAsyncMessage("Browser:HideSessionRestoreButton");
 
-    if (gURLBar) {
-      // Clear undo history of the URL bar
-      gURLBar.editor.transactionManager.clear()
-    }
+    // Clear undo history of the URL bar
+    gURLBar.editor.transactionManager.clear()
   }
 };
 
 /**
  * Given a starting docshell and a URI to look up, find the docshell the URI
  * is loaded in.
  * @param   aDocument
  *          A document to find instead of using just a URI - this is more specific.
@@ -457,35 +455,33 @@ var gPopupBlockerObserver = {
             .openPopup(this._reportButton, "after_end", 0, 2, false, false, aEvent);
   },
 
   handleEvent: function (aEvent)
   {
     if (aEvent.originalTarget != gBrowser.selectedBrowser)
       return;
 
-    if (!this._reportButton && gURLBar)
+    if (!this._reportButton)
       this._reportButton = document.getElementById("page-report-button");
 
     if (!gBrowser.selectedBrowser.blockedPopups) {
       // Hide the icon in the location bar (if the location bar exists)
-      if (gURLBar)
-        this._reportButton.hidden = true;
+      this._reportButton.hidden = true;
 
       // Hide the notification box (if it's visible).
       let notificationBox = gBrowser.getNotificationBox();
       let notification = notificationBox.getNotificationWithValue("popup-blocked");
       if (notification) {
         notificationBox.removeNotification(notification, false);
       }
       return;
     }
 
-    if (gURLBar)
-      this._reportButton.hidden = false;
+    this._reportButton.hidden = false;
 
     // Only show the notification again if we've not already shown it. Since
     // notifications are per-browser, we don't need to worry about re-adding
     // it.
     if (!gBrowser.selectedBrowser.blockedPopups.reported) {
       if (gPrefService.getBoolPref("privacy.popups.showBrowserMessage")) {
         var brandBundle = document.getElementById("bundle_brand");
         var brandShortName = brandBundle.getString("brandShortName");
@@ -992,20 +988,18 @@ var gBrowserInit = {
 
       if (width < TARGET_WIDTH && height < TARGET_HEIGHT) {
         document.documentElement.setAttribute("sizemode", "maximized");
       }
     }
 
     if (!window.toolbar.visible) {
       // adjust browser UI for popups
-      if (gURLBar) {
-        gURLBar.setAttribute("readonly", "true");
-        gURLBar.setAttribute("enablehistory", "false");
-      }
+      gURLBar.setAttribute("readonly", "true");
+      gURLBar.setAttribute("enablehistory", "false");
       goSetCommandEnabled("cmd_newNavigatorTab", false);
     }
 
     // Misc. inits.
     TabletModeUpdater.init();
     CombinedStopReload.init();
     gPrivateBrowsingUI.init();
     TabsInTitlebar.init();
@@ -4335,24 +4329,25 @@ var XULBrowserWindow = {
       if ((location == "about:blank" && checkEmptyPageOrigin()) ||
           location == "") {  // Second condition is for new tabs, otherwise
                              // reload function is enabled until tab is refreshed.
         this.reloadCommand.setAttribute("disabled", "true");
       } else {
         this.reloadCommand.removeAttribute("disabled");
       }
 
-      if (gURLBar) {
-        URLBarSetURI(aLocationURI);
-
-        BookmarkingUI.onLocationChange();
-        SocialUI.updateState();
-        UITour.onLocationChange(location);
-        gTabletModePageCounter.inc();
-      }
+      URLBarSetURI(aLocationURI);
+
+      BookmarkingUI.onLocationChange();
+
+      SocialUI.updateState();
+
+      UITour.onLocationChange(location);
+
+      gTabletModePageCounter.inc();
 
       // Utility functions for disabling find
       var shouldDisableFind = function shouldDisableFind(aDocument) {
         let docElt = aDocument.documentElement;
         return docElt && docElt.getAttribute("disablefastfind") == "true";
       }
 
       var disableFindCommands = function disableFindCommands(aDisable) {
@@ -7283,29 +7278,27 @@ var gPrivateBrowsingUI = {
             newWindow.label = newPrivateWindow.label;
             newWindow.accessKey = newPrivateWindow.accessKey;
             newWindow.command = newPrivateWindow.command;
           }
         });
       }
     }
 
-    if (gURLBar) {
-      let value = gURLBar.getAttribute("autocompletesearchparam") || "";
-      if (!PrivateBrowsingUtils.permanentPrivateBrowsing &&
-          !value.includes("disable-private-actions")) {
-        // Disable switch to tab autocompletion for private windows.
-        // We leave it enabled for permanent private browsing mode though.
-        value += " disable-private-actions";
-      }
-      if (!value.includes("private-window")) {
-        value += " private-window";
-      }
-      gURLBar.setAttribute("autocompletesearchparam", value);
-    }
+    let urlBarSearchParam = gURLBar.getAttribute("autocompletesearchparam") || "";
+    if (!PrivateBrowsingUtils.permanentPrivateBrowsing &&
+        !urlBarSearchParam.includes("disable-private-actions")) {
+      // Disable switch to tab autocompletion for private windows.
+      // We leave it enabled for permanent private browsing mode though.
+      urlBarSearchParam += " disable-private-actions";
+    }
+    if (!urlBarSearchParam.includes("private-window")) {
+      urlBarSearchParam += " private-window";
+    }
+    gURLBar.setAttribute("autocompletesearchparam", urlBarSearchParam);
   }
 };
 
 var gRemoteTabsUI = {
   init: function() {
     if (window.location.href != getBrowserURL() &&
         // Also check hidden window for the Mac no-window case
         window.location.href != "chrome://browser/content/hiddenWindow.xul") {
--- a/browser/base/content/socialchat.xml
+++ b/browser/base/content/socialchat.xml
@@ -245,16 +245,19 @@
       </method>
 
       <method name="close">
         <body><![CDATA[
         if (this.chatbar)
           this.chatbar.remove(this);
         else
           window.close();
+
+        if (!this.swappingWindows)
+          this.dispatchEvent(new CustomEvent("ChatboxClosed"));
         ]]></body>
       </method>
 
       <method name="swapWindows">
         <body><![CDATA[
         let deferred = Promise.defer();
         let title = this.getAttribute("label");
         if (this.chatbar) {
@@ -280,16 +283,17 @@
 
           cb.promiseChatLoaded.then(
             () => {
               this.setDecorationAttributes(cb);
 
               this.swapDocShells(cb);
 
               chatbar.focus();
+              this.swappingWindows = true;
               this.close();
 
               // chatboxForURL is a map of URL -> chatbox used to avoid opening
               // duplicate chat windows. Ensure reattached chat windows aren't
               // registered with about:blank as their URL, otherwise reattaching
               // more than one chat window isn't possible.
               chatbar.chatboxForURL.delete("about:blank");
               chatbar.chatboxForURL.set(this.src, Cu.getWeakReference(cb));
@@ -791,24 +795,37 @@
                 setAttribute("customSize", aChatbox.getAttribute("customSize"));
             }
 
             otherWin.removeEventListener("load", _chatLoad, true);
             let otherChatbox = otherWin.document.getElementById("chatter");
             aChatbox.setDecorationAttributes(otherChatbox);
             aChatbox.swapDocShells(otherChatbox);
 
+            aChatbox.swappingWindows = true;
             aChatbox.close();
-            chatbar.chatboxForURL.set(aChatbox.src, Cu.getWeakReference(otherChatbox));
+            let url = aChatbox.src;
+            chatbar.chatboxForURL.set(url, Cu.getWeakReference(otherChatbox));
 
             // All processing is done, now we can fire the event.
             otherChatbox.content.messageManager.sendAsyncMessage("Social:CustomEvent", {
               name: "socialFrameDetached"
             });
 
+            Services.obs.addObserver(function onDOMWindowClosed(subject) {
+              if (subject !== otherWin)
+                return;
+
+              Services.obs.removeObserver(onDOMWindowClosed, "domwindowclosed");
+              chatbar.chatboxForURL.delete(url);
+
+              if (!otherChatbox.swappingWindows)
+                otherChatbox.dispatchEvent(new CustomEvent("ChatboxClosed"));
+            }, "domwindowclosed", false);
+
             deferred.resolve(otherChatbox);
           }, true);
           return deferred.promise;
         ]]></body>
       </method>
 
     </implementation>
 
--- a/browser/extensions/loop/chrome/content/modules/MozLoopService.jsm
+++ b/browser/extensions/loop/chrome/content/modules/MozLoopService.jsm
@@ -1008,34 +1008,16 @@ var MozLoopServiceInternal = {
               }
             }
           });
 
           // Handle window.close correctly on the chatbox.
           mm.sendAsyncMessage("Social:HookWindowCloseForPanelClose");
           messageName = "Social:DOMWindowClose";
           mm.addMessageListener(messageName, listeners[messageName] = () => {
-            // Remove message listeners.
-            for (let name of Object.getOwnPropertyNames(listeners)) {
-              mm.removeMessageListener(name, listeners[name]);
-            }
-            listeners = {};
-
-            windowCloseCallback();
-
-            if (conversationWindowData.type == "room") {
-              // NOTE: if you add something here, please also consider if something
-              //       needs to be done on the content side as well (e.g.
-              //       activeRoomStore#windowUnload).
-              LoopAPI.sendMessageToHandler({
-                name: "HangupNow",
-                data: [conversationWindowData.roomToken, windowId]
-              });
-            }
-
             chatbox.close();
           });
 
           mm.sendAsyncMessage("Loop:MonitorPeerConnectionLifecycle");
           messageName = "Loop:PeerConnectionLifecycleChange";
           mm.addMessageListener(messageName, listeners[messageName] = message => {
             // Chat Window Id, this is different that the internal winId
             let chatWindowId = message.data.locationHash.slice(1);
@@ -1060,37 +1042,63 @@ var MozLoopServiceInternal = {
                   if (Services.telemetry.canRecordExtended) {
                     this.stageForTelemetryUpload(chatbox.content, message.data);
                   }
                   break;
               }
             }
           });
 
+          let closeListener = function() {
+            this.removeEventListener("ChatboxClosed", closeListener);
+
+            // Remove message listeners.
+            for (let name of Object.getOwnPropertyNames(listeners)) {
+              mm.removeMessageListener(name, listeners[name]);
+            }
+            listeners = {};
+
+            windowCloseCallback();
+
+            if (conversationWindowData.type == "room") {
+              // NOTE: if you add something here, please also consider if something
+              //       needs to be done on the content side as well (e.g.
+              //       activeRoomStore#windowUnload).
+              LoopAPI.sendMessageToHandler({
+                name: "HangupNow",
+                data: [conversationWindowData.roomToken, windowId]
+              });
+            }
+          };
+
           // When a chat window is attached or detached, the docShells hosting
           // about:loopconverstation is swapped to the newly created chat window.
           // (Be it inside a popup or back inside a chatbox element attached to the
           // chatbar.)
           // Since a swapDocShells call does not swap the messageManager instances
           // attached to a browser, we'll need to add the message listeners to
           // the new messageManager. This is not a bug in swapDocShells, merely
           // a design decision.
           chatbox.content.addEventListener("SwapDocShells", function swapped(ev) {
-            chatbox.content.removeEventListener("SwapDocShells", swapped);
+            this.removeEventListener("SwapDocShells", swapped);
+            this.removeEventListener("ChatboxClosed", closeListener);
 
             let otherBrowser = ev.detail;
             chatbox = otherBrowser.ownerDocument.getBindingParent(otherBrowser);
             mm = otherBrowser.messageManager;
             otherBrowser.addEventListener("SwapDocShells", swapped);
+            chatbox.addEventListener("ChatboxClosed", closeListener);
 
             for (let name of Object.getOwnPropertyNames(listeners)) {
               mm.addMessageListener(name, listeners[name]);
             }
           });
 
+          chatbox.addEventListener("ChatboxClosed", closeListener);
+
           UITour.notify("Loop:ChatWindowOpened");
           resolve(windowId);
         };
 
         mm.sendAsyncMessage("WaitForDOMContentLoaded");
         mm.addMessageListener("DOMContentLoaded", loaded);
       };
 
--- a/devtools/client/framework/test/shared-head.js
+++ b/devtools/client/framework/test/shared-head.js
@@ -35,16 +35,36 @@ const URL_ROOT_SSL = CHROME_URL_ROOT.rep
                                              "https://example.com/");
 
 // All test are asynchronous
 waitForExplicitFinish();
 
 // Uncomment this pref to dump all devtools emitted events to the console.
 // Services.prefs.setBoolPref("devtools.dump.emit", true);
 
+/**
+ * Watch console messages for failed propType definitions in React components.
+ */
+const ConsoleObserver = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+
+  observe: function(subject, topic, data) {
+    var message = subject.wrappedJSObject.arguments[0];
+
+    if (/Failed propType/.test(message)) {
+      ok(false, message);
+    }
+  }
+};
+
+Services.obs.addObserver(ConsoleObserver, "console-api-log-event", false);
+registerCleanupFunction(() => {
+  Services.obs.removeObserver(ConsoleObserver, "console-api-log-event");
+});
+
 function getFrameScript() {
   let mm = gBrowser.selectedBrowser.messageManager;
   let frameURL = "chrome://devtools/content/shared/frame-script-utils.js";
   mm.loadFrameScript(frameURL, false);
   SimpleTest.registerCleanupFunction(() => {
     mm = null;
   });
   return mm;
--- a/devtools/client/inspector/rules/models/rule.js
+++ b/devtools/client/inspector/rules/models/rule.js
@@ -172,21 +172,23 @@ Rule.prototype = {
    * Create a new TextProperty to include in the rule.
    *
    * @param {String} name
    *        The text property name (such as "background" or "border-top").
    * @param {String} value
    *        The property's value (not including priority).
    * @param {String} priority
    *        The property's priority (either "important" or an empty string).
+   * @param {Boolean} enabled
+   *        True if the property should be enabled.
    * @param {TextProperty} siblingProp
    *        Optional, property next to which the new property will be added.
    */
-  createProperty: function(name, value, priority, siblingProp) {
-    let prop = new TextProperty(this, name, value, priority);
+  createProperty: function(name, value, priority, enabled, siblingProp) {
+    let prop = new TextProperty(this, name, value, priority, enabled);
 
     let ind;
     if (siblingProp) {
       ind = this.textProps.indexOf(siblingProp) + 1;
       this.textProps.splice(ind, 0, prop);
     } else {
       ind = this.textProps.length;
       this.textProps.push(prop);
--- a/devtools/client/inspector/rules/test/browser.ini
+++ b/devtools/client/inspector/rules/test/browser.ini
@@ -31,16 +31,17 @@ support-files =
   doc_urls_clickable.css
   doc_urls_clickable.html
   head.js
 
 [browser_rules_add-property-and-reselect.js]
 [browser_rules_add-property-cancel_01.js]
 [browser_rules_add-property-cancel_02.js]
 [browser_rules_add-property-cancel_03.js]
+[browser_rules_add-property-commented.js]
 [browser_rules_add-property_01.js]
 [browser_rules_add-property_02.js]
 [browser_rules_add-property-svg.js]
 [browser_rules_add-rule_01.js]
 [browser_rules_add-rule_02.js]
 [browser_rules_add-rule_03.js]
 [browser_rules_add-rule_04.js]
 [browser_rules_add-rule_05.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/browser_rules_add-property-commented.js
@@ -0,0 +1,47 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that commented properties can be added and are disabled.
+
+const TEST_URI = "<div id='testid'></div>";
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testCreateNewSetOfCommentedAndUncommentedProperties(view);
+});
+
+function* testCreateNewSetOfCommentedAndUncommentedProperties(view) {
+  info("Test creating a new set of commented and uncommented properties");
+
+  info("Focusing a new property name in the rule-view");
+  let ruleEditor = getRuleViewRuleEditor(view, 0);
+  let editor = yield focusEditableField(view, ruleEditor.closeBrace);
+  is(inplaceEditor(ruleEditor.newPropSpan), editor,
+    "The new property editor has focus");
+
+  info(
+    "Entering a commented property/value pair into the property name editor");
+  let input = editor.input;
+  input.value = `color: blue;
+                 /* background-color: yellow; */
+                 width: 200px;
+                 height: 100px;
+                 /* padding-bottom: 1px; */`;
+
+  info("Pressing return to commit and focus the new value field");
+  let onModifications = view.once("ruleview-changed");
+  EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
+  yield onModifications;
+
+  let textProps = ruleEditor.rule.textProps;
+  ok(textProps[0].enabled, "The 'color' property is enabled.");
+  ok(!textProps[1].enabled, "The 'background-color' property is disabled.");
+  ok(textProps[2].enabled, "The 'width' property is enabled.");
+  ok(textProps[3].enabled, "The 'height' property is enabled.");
+  ok(!textProps[4].enabled, "The 'padding-bottom' property is disabled.");
+}
--- a/devtools/client/inspector/rules/views/rule-editor.js
+++ b/devtools/client/inspector/rules/views/rule-editor.js
@@ -338,23 +338,26 @@ RuleEditor.prototype = {
    * Programatically add a new property to the rule.
    *
    * @param {String} name
    *        Property name.
    * @param {String} value
    *        Property value.
    * @param {String} priority
    *        Property priority.
+   * @param {Boolean} enabled
+   *        True if the property should be enabled.
    * @param {TextProperty} siblingProp
    *        Optional, property next to which the new property will be added.
    * @return {TextProperty}
    *        The new property
    */
-  addProperty: function(name, value, priority, siblingProp) {
-    let prop = this.rule.createProperty(name, value, priority, siblingProp);
+  addProperty: function(name, value, priority, enabled, siblingProp) {
+    let prop = this.rule.createProperty(name, value, priority, enabled,
+      siblingProp);
     let index = this.rule.textProps.indexOf(prop);
     let editor = new TextPropertyEditor(this, prop);
 
     // Insert this node before the DOM node that is currently at its new index
     // in the property list.  There is currently one less node in the DOM than
     // in the property list, so this causes it to appear after siblingProp.
     // If there is no node at its index, as is the case where this is the last
     // node being inserted, then this behaves as appendChild.
@@ -381,17 +384,20 @@ RuleEditor.prototype = {
    */
   addProperties: function(properties, siblingProp) {
     if (!properties || !properties.length) {
       return;
     }
 
     let lastProp = siblingProp;
     for (let p of properties) {
-      lastProp = this.addProperty(p.name, p.value, p.priority, lastProp);
+      let isCommented = Boolean(p.commentOffsets);
+      let enabled = !isCommented;
+      lastProp = this.addProperty(p.name, p.value, p.priority, enabled,
+        lastProp);
     }
 
     // Either focus on the last value if incomplete, or start a new one.
     if (lastProp && lastProp.value.trim() === "") {
       lastProp.editor.valueSpan.click();
     } else {
       this.newProperty();
     }
@@ -450,17 +456,17 @@ RuleEditor.prototype = {
     if (!value || !commit) {
       return;
     }
 
     // parseDeclarations allows for name-less declarations, but in the present
     // case, we're creating a new declaration, it doesn't make sense to accept
     // these entries
     this.multipleAddedProperties =
-      parseDeclarations(value).filter(d => d.name);
+      parseDeclarations(value, true).filter(d => d.name);
 
     // Blur the editor field now and deal with adding declarations later when
     // the field gets destroyed (see _newPropertyDestroy)
     this.editor.input.blur();
   },
 
   /**
    * Called when the new property editor is destroyed.
--- a/devtools/client/inspector/test/browser.ini
+++ b/devtools/client/inspector/test/browser.ini
@@ -1,15 +1,16 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
   doc_inspector_breadcrumbs.html
   doc_inspector_delete-selected-node-01.html
   doc_inspector_delete-selected-node-02.html
+  doc_inspector_embed.html
   doc_inspector_gcli-inspect-command.html
   doc_inspector_highlight_after_transition.html
   doc_inspector_highlighter-comments.html
   doc_inspector_highlighter-geometry_01.html
   doc_inspector_highlighter-geometry_02.html
   doc_inspector_highlighter_csstransform.html
   doc_inspector_highlighter_dom.html
   doc_inspector_highlighter_inline.html
@@ -45,16 +46,17 @@ support-files =
 [browser_inspector_highlighter-01.js]
 [browser_inspector_highlighter-02.js]
 [browser_inspector_highlighter-03.js]
 [browser_inspector_highlighter-04.js]
 [browser_inspector_highlighter-by-type.js]
 [browser_inspector_highlighter-comments.js]
 [browser_inspector_highlighter-csstransform_01.js]
 [browser_inspector_highlighter-csstransform_02.js]
+[browser_inspector_highlighter-embed.js]
 [browser_inspector_highlighter-geometry_01.js]
 [browser_inspector_highlighter-geometry_02.js]
 [browser_inspector_highlighter-geometry_03.js]
 [browser_inspector_highlighter-geometry_04.js]
 [browser_inspector_highlighter-geometry_05.js]
 [browser_inspector_highlighter-hover_01.js]
 [browser_inspector_highlighter-hover_02.js]
 [browser_inspector_highlighter-hover_03.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-embed.js
@@ -0,0 +1,30 @@
+/* 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";
+
+// Test that the highlighter can go inside <embed> elements
+
+const TEST_URL = URL_ROOT + "doc_inspector_embed.html";
+
+add_task(function*() {
+  let {inspector} = yield openInspectorForURL(TEST_URL);
+
+  info("Get a node inside the <embed> element and select/highlight it");
+  let body = yield getEmbeddedBody(inspector);
+  yield selectAndHighlightNode(body, inspector);
+
+  let selectedNode = inspector.selection.nodeFront;
+  is(selectedNode.tagName.toLowerCase(), "body", "The selected node is <body>");
+  ok(selectedNode.baseURI.endsWith("doc_inspector_menu.html"),
+     "The selected node is the <body> node inside the <embed> element");
+});
+
+function* getEmbeddedBody({walker}) {
+  let embed = yield walker.querySelector(walker.rootNode, "embed");
+  let {nodes} = yield walker.children(embed);
+  let contentDoc = nodes[0];
+  let body = yield walker.querySelector(contentDoc, "body");
+  return body;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/test/doc_inspector_embed.html
@@ -0,0 +1,6 @@
+<!doctype html><html><head><meta charset="UTF-8"></head><body>
+<object>
+  <embed src="doc_inspector_menu.html" type="application/html"
+         width="422" height="258"></embed>
+</object>
+</body></html>
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -142,16 +142,17 @@ devtools.jar:
     content/responsive.html/index.xhtml (responsive.html/index.xhtml)
     content/responsive.html/index.js (responsive.html/index.js)
 %   skin devtools classic/1.0 %skin/
     skin/devtools-browser.css (themes/devtools-browser.css)
     skin/common.css (themes/common.css)
     skin/splitters.css (themes/splitters.css)
     skin/dark-theme.css (themes/dark-theme.css)
     skin/light-theme.css (themes/light-theme.css)
+    skin/firebug-theme.css (themes/firebug-theme.css)
     skin/toolbars.css (themes/toolbars.css)
     skin/variables.css (themes/variables.css)
     skin/images/add.svg (themes/images/add.svg)
     skin/images/filters.svg (themes/images/filters.svg)
     skin/images/filter-swatch.svg (themes/images/filter-swatch.svg)
     skin/images/pseudo-class.svg (themes/images/pseudo-class.svg)
     skin/images/controls.png (themes/images/controls.png)
     skin/images/controls@2x.png (themes/images/controls@2x.png)
@@ -338,10 +339,16 @@ devtools.jar:
     skin/tooltip/arrow-vertical-light@2x.png (themes/tooltip/arrow-vertical-light@2x.png)
     skin/images/reload.svg (themes/images/reload.svg)
     skin/images/security-state-broken.svg (themes/images/security-state-broken.svg)
     skin/images/security-state-insecure.svg (themes/images/security-state-insecure.svg)
     skin/images/security-state-local.svg (themes/images/security-state-local.svg)
     skin/images/security-state-secure.svg (themes/images/security-state-secure.svg)
     skin/images/security-state-weak.svg (themes/images/security-state-weak.svg)
     skin/images/diff.svg (themes/images/diff.svg)
+    skin/images/firebug/read-only.svg (themes/images/firebug/read-only.svg)
+    skin/images/firebug/spinner.png (themes/images/firebug/spinner.png)
+    skin/images/firebug/twisty-closed-firebug.svg (themes/images/firebug/twisty-closed-firebug.svg)
+    skin/images/firebug/twisty-open-firebug.svg (themes/images/firebug/twisty-open-firebug.svg)
+    skin/images/firebug/arrow-down.svg (themes/images/firebug/arrow-down.svg)
+    skin/images/firebug/arrow-up.svg (themes/images/firebug/arrow-up.svg)
     skin/images/pane-collapse.svg (themes/images/pane-collapse.svg)
     skin/images/pane-expand.svg (themes/images/pane-expand.svg)
--- a/devtools/client/netmonitor/har/har-automation.js
+++ b/devtools/client/netmonitor/har/har-automation.js
@@ -1,31 +1,31 @@
 /* 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 { Cu, Ci, Cc } = require("chrome");
+const { Cu, Ci } = require("chrome");
 const { Class } = require("sdk/core/heritage");
-const { defer, resolve } = require("promise");
+const { resolve } = require("promise");
 const Services = require("Services");
 
 Cu.import("resource://gre/modules/Task.jsm");
 
 loader.lazyRequireGetter(this, "HarCollector", "devtools/client/netmonitor/har/har-collector", true);
 loader.lazyRequireGetter(this, "HarExporter", "devtools/client/netmonitor/har/har-exporter", true);
 loader.lazyRequireGetter(this, "HarUtils", "devtools/client/netmonitor/har/har-utils", true);
 
 const prefDomain = "devtools.netmonitor.har.";
 
 // Helper tracer. Should be generic sharable by other modules (bug 1171927)
 const trace = {
   log: function(...args) {
   }
-}
+};
 
 /**
  * This object is responsible for automated HAR export. It listens
  * for Network activity, collects all HTTP data and triggers HAR
  * export when the page is loaded.
  *
  * The user needs to enable the following preference to make the
  * auto-export work: devtools.netmonitor.har.enableAutoExportToFile
@@ -72,17 +72,17 @@ var HarAutomation = Class({
     this.debuggerClient = client;
     this.tabClient = this.toolbox.target.activeTab;
     this.webConsoleClient = this.toolbox.target.activeConsole;
 
     this.tabWatcher = new TabWatcher(this.toolbox, this);
     this.tabWatcher.connect();
   },
 
-  pageLoadBegin: function(aResponse) {
+  pageLoadBegin: function(response) {
     this.resetCollector();
   },
 
   resetCollector: function() {
     if (this.collector) {
       this.collector.stop();
     }
 
@@ -101,18 +101,18 @@ var HarAutomation = Class({
    * some requests for additional page resources might be pending,
    * so export all after all has been properly received from the backend.
    *
    * This collector still works and collects any consequent HTTP
    * traffic (e.g. XHRs) happening after the page is loaded and
    * The additional traffic can be exported by executing
    * triggerExport on this object.
    */
-  pageLoadDone: function(aResponse) {
-    trace.log("HarAutomation.pageLoadDone; ", aResponse);
+  pageLoadDone: function(response) {
+    trace.log("HarAutomation.pageLoadDone; ", response);
 
     if (this.collector) {
       this.collector.waitForHarLoad().then(collector => {
         return this.autoExport();
       });
     }
   },
 
@@ -123,17 +123,17 @@ var HarAutomation = Class({
     if (!autoExport) {
       return resolve();
     }
 
     // Auto export to file is enabled, so save collected data
     // into a file and use all the default options.
     let data = {
       fileName: Services.prefs.getCharPref(prefDomain + "defaultFileName"),
-    }
+    };
 
     return this.executeExport(data);
   },
 
   // Public API
 
   /**
    * Export all what is currently collected.
@@ -164,17 +164,17 @@ var HarAutomation = Class({
     let items = this.collector.getItems();
     let form = this.toolbox.target.form;
     let title = form.title || form.url;
 
     let options = {
       getString: this.getString.bind(this),
       view: this,
       items: items,
-    }
+    };
 
     options.defaultFileName = data.fileName;
     options.compress = data.compress;
     options.title = data.title || title;
     options.id = data.id;
     options.jsonp = data.jsonp;
     options.includeResponseBodies = data.includeResponseBodies;
     options.jsonpCallback = data.jsonpCallback;
@@ -193,49 +193,53 @@ var HarAutomation = Class({
 
       return jsonString;
     });
   },
 
   /**
    * Fetches the full text of a string.
    */
-  getString: function(aStringGrip) {
-    return this.webConsoleClient.getString(aStringGrip);
+  getString: function(stringGrip) {
+    return this.webConsoleClient.getString(stringGrip);
   },
 
   /**
    * Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a
    * POST request.
    *
-   * @param object aHeaders
+   * @param object headers
    *        The "requestHeaders".
-   * @param object aUploadHeaders
+   * @param object uploadHeaders
    *        The "requestHeadersFromUploadStream".
-   * @param object aPostData
+   * @param object postData
    *        The "requestPostData".
    * @return array
    *        A promise that is resolved with the extracted form data.
    */
-  _getFormDataSections: Task.async(function*(aHeaders, aUploadHeaders, aPostData) {
+  _getFormDataSections: Task.async(function*(headers, uploadHeaders, postData) {
     let formDataSections = [];
 
-    let { headers: requestHeaders } = aHeaders;
-    let { headers: payloadHeaders } = aUploadHeaders;
+    let { headers: requestHeaders } = headers;
+    let { headers: payloadHeaders } = uploadHeaders;
     let allHeaders = [...payloadHeaders, ...requestHeaders];
 
-    let contentTypeHeader = allHeaders.find(e => e.name.toLowerCase() == "content-type");
-    let contentTypeLongString = contentTypeHeader ? contentTypeHeader.value : "";
+    let contentTypeHeader = allHeaders.find(e => {
+      return e.name.toLowerCase() == "content-type";
+    });
+
+    let contentTypeLongString = contentTypeHeader ?
+      contentTypeHeader.value : "";
     let contentType = yield this.getString(contentTypeLongString);
 
     if (contentType.includes("x-www-form-urlencoded")) {
-      let postDataLongString = aPostData.postData.text;
-      let postData = yield this.getString(postDataLongString);
+      let postDataLongString = postData.postData.text;
+      let data = yield this.getString(postDataLongString);
 
-      for (let section of postData.split(/\r\n|\r|\n/)) {
+      for (let section of data.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);
         }
       }
     }
 
@@ -274,24 +278,24 @@ TabWatcher.prototype = {
   /**
    * Called for each location change in the monitored tab.
    *
    * @param string aType
    *        Packet type.
    * @param object aPacket
    *        Packet received from the server.
    */
-  onTabNavigated: function(aType, aPacket) {
-    switch (aType) {
+  onTabNavigated: function(type, packet) {
+    switch (type) {
       case "will-navigate": {
-        this.listener.pageLoadBegin(aPacket);
+        this.listener.pageLoadBegin(packet);
         break;
       }
       case "navigate": {
-        this.listener.pageLoadDone(aPacket);
+        this.listener.pageLoadDone(packet);
         break;
       }
     }
   },
 };
 
 // Protocol Helpers
 
--- a/devtools/client/netmonitor/har/har-builder.js
+++ b/devtools/client/netmonitor/har/har-builder.js
@@ -1,16 +1,15 @@
 /* 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 { Cu, Ci, Cc } = require("chrome");
-const { defer, all, resolve } = require("promise");
-const Services = require("Services");
+const { Ci, Cc } = require("chrome");
+const { defer, all } = require("promise");
 
 loader.lazyImporter(this, "ViewHelpers", "resource://devtools/client/shared/widgets/ViewHelpers.jsm");
 loader.lazyRequireGetter(this, "NetworkHelper", "devtools/shared/webconsole/network-helper");
 
 loader.lazyGetter(this, "appInfo", () => {
   return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
 });
 
@@ -37,17 +36,17 @@ const HAR_VERSION = "1.1";
  * - title {String}: Title of the exported page.
  *
  * - includeResponseBodies {Boolean}: Set to true to include HTTP response
  *   bodies in the result data structure.
  */
 var HarBuilder = function(options) {
   this._options = options;
   this._pageMap = [];
-}
+};
 
 HarBuilder.prototype = {
   // Public API
 
   /**
    * This is the main method used to build the entire result HAR data.
    * The process is asynchronous since it can involve additional RDP
    * communication (e.g. resolving long strings).
@@ -58,17 +57,17 @@ HarBuilder.prototype = {
   build: function() {
     this.promises = [];
 
     // Build basic structure for data.
     let log = this.buildLog();
 
     // Build entries.
     let items = this._options.items;
-    for (let i=0; i<items.length; i++) {
+    for (let i = 0; i < items.length; i++) {
       let file = items[i].attachment;
       log.entries.push(this.buildEntry(log, file));
     }
 
     // Some data needs to be fetched from the backend during the
     // build process, so wait till all is done.
     let { resolve, promise } = defer();
     all(this.promises).then(results => resolve({ log: log }));
@@ -86,17 +85,17 @@ HarBuilder.prototype = {
         version: appInfo.version
       },
       browser: {
         name: appInfo.name,
         version: appInfo.version
       },
       pages: [],
       entries: [],
-    }
+    };
   },
 
   buildPage: function(file) {
     let page = {};
 
     // Page start time is set when the first request is processed
     // (see buildEntry)
     page.startedDateTime = 0;
@@ -223,17 +222,17 @@ HarBuilder.prototype = {
     // Make sure header values are fully fetched from the server.
     entries.forEach(entry => {
       this.fetchData(entry.value).then(value => {
         result.push({
           name: entry.name,
           value: value
         });
       });
-    })
+    });
 
     return result;
   },
 
   buildPostData: function(file) {
     let postData = {
       mimeType: findValue(file.requestHeaders.headers, "content-type"),
       params: [],
@@ -276,17 +275,17 @@ HarBuilder.prototype = {
 
   buildResponse: function(file) {
     let response = {
       status: 0
     };
 
     // Arbitrary value if it's aborted to make sure status has a number
     if (file.status) {
-      response.status = parseInt(file.status);
+      response.status = parseInt(file.status, 10);
     }
 
     let responseHeaders = file.responseHeaders;
 
     response.statusText = file.statusText || "";
     response.httpVersion = file.httpVersion || "";
 
     response.headers = this.buildHeaders(responseHeaders);
@@ -331,17 +330,17 @@ HarBuilder.prototype = {
     // is explicitly discarded.
     if (!includeBodies || contentDiscarded) {
       content.comment = L10N.getStr("har.responseBodyNotIncluded");
       return content;
     }
 
     if (responseContent) {
       let text = responseContent.content.text;
-      let promise = this.fetchData(text).then(value => {
+      this.fetchData(text).then(value => {
         content.text = value;
       });
     }
 
     return content;
   },
 
   buildCache: function(file) {
@@ -399,25 +398,25 @@ HarBuilder.prototype = {
     });
 
     // Building HAR is asynchronous and not done till all
     // collected promises are resolved.
     this.promises.push(promise);
 
     return promise;
   }
-}
+};
 
 // Helpers
 
 /**
  * Returns true if specified request body is URL encoded.
  */
 function isURLEncodedFile(file, text) {
-  let contentType = "content-type: application/x-www-form-urlencoded"
+  let contentType = "content-type: application/x-www-form-urlencoded";
   if (text && text.toLowerCase().indexOf(contentType) != -1) {
     return true;
   }
 
   // The header value doesn't have to be always exactly
   // "application/x-www-form-urlencoded",
   // there can be even charset specified. So, use indexOf rather than just
   // "==".
@@ -464,22 +463,22 @@ function dateToJSON(date) {
     }
     let s = new String(n);
     while (s.length < c) {
       s = "0" + s;
     }
     return s;
   }
 
-  let result = date.getFullYear() + '-' +
-    f(date.getMonth() + 1) + '-' +
-    f(date.getDate()) + 'T' +
-    f(date.getHours()) + ':' +
-    f(date.getMinutes()) + ':' +
-    f(date.getSeconds()) + '.' +
+  let result = date.getFullYear() + "-" +
+    f(date.getMonth() + 1) + "-" +
+    f(date.getDate()) + "T" +
+    f(date.getHours()) + ":" +
+    f(date.getMinutes()) + ":" +
+    f(date.getSeconds()) + "." +
     f(date.getMilliseconds(), 3);
 
   let offset = date.getTimezoneOffset();
   let positive = offset > 0;
 
   // Convert to positive number before using Math.floor (see issue 5512)
   offset = Math.abs(offset);
   let offsetHours = Math.floor(offset / 60);
--- a/devtools/client/netmonitor/har/har-collector.js
+++ b/devtools/client/netmonitor/har/har-collector.js
@@ -1,24 +1,24 @@
 /* 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 { Cu, Ci, Cc } = require("chrome");
+const { Cu } = require("chrome");
 const { defer, all } = require("promise");
 const { setTimeout, clearTimeout } = require("sdk/timers");
 const { makeInfallible } = require("devtools/shared/DevToolsUtils");
 const Services = require("Services");
 
 // Helper tracer. Should be generic sharable by other modules (bug 1171927)
 const trace = {
   log: function(...args) {
   }
-}
+};
 
 /**
  * This object is responsible for collecting data related to all
  * HTTP requests executed by the page (including inner iframes).
  */
 function HarCollector(options) {
   this.webConsoleClient = options.webConsoleClient;
   this.debuggerClient = options.debuggerClient;
@@ -38,36 +38,39 @@ function HarCollector(options) {
   this.clear();
 }
 
 HarCollector.prototype = {
   // Connection
 
   start: function() {
     this.debuggerClient.addListener("networkEvent", this.onNetworkEvent);
-    this.debuggerClient.addListener("networkEventUpdate", this.onNetworkEventUpdate);
+    this.debuggerClient.addListener("networkEventUpdate",
+      this.onNetworkEventUpdate);
   },
 
   stop: function() {
     this.debuggerClient.removeListener("networkEvent", this.onNetworkEvent);
-    this.debuggerClient.removeListener("networkEventUpdate", this.onNetworkEventUpdate);
+    this.debuggerClient.removeListener("networkEventUpdate",
+      this.onNetworkEventUpdate);
   },
 
   clear: function() {
     // Any pending requests events will be ignored (they turn
     // into zombies, since not present in the files array).
     this.files = new Map();
     this.items = [];
     this.firstRequestStart = -1;
     this.lastRequestStart = -1;
     this.requests = [];
   },
 
   waitForHarLoad: function() {
-    // There should be yet another timeout 'devtools.netmonitor.har.pageLoadTimeout'
+    // There should be yet another timeout e.g.:
+    // 'devtools.netmonitor.har.pageLoadTimeout'
     // that should force export even if page isn't fully loaded.
     let deferred = defer();
     this.waitForResponses().then(() => {
       trace.log("HarCollector.waitForHarLoad; DONE HAR loaded!");
       deferred.resolve(this);
     });
 
     return deferred.promise;
@@ -90,17 +93,17 @@ HarCollector.prototype = {
       return this.waitForTimeout().then(() => {
         // Page loaded!
       }, () => {
         trace.log("HarCollector.waitForResponses; NEW requests " +
           "appeared during page timeout!");
 
         // New requests executed, let's wait again.
         return this.waitForResponses();
-      })
+      });
     });
   },
 
   // Page Loaded Timeout
 
   /**
    * The page is loaded when there are no new requests within given period
    * of time. The time is set in preferences:
@@ -220,63 +223,70 @@ HarCollector.prototype = {
       packet.updateType, packet);
 
     let includeResponseBodies = Services.prefs.getBoolPref(
       "devtools.netmonitor.har.includeResponseBodies");
 
     let request;
     switch (packet.updateType) {
       case "requestHeaders":
-        request = this.getData(actor, "getRequestHeaders", this.onRequestHeaders);
+        request = this.getData(actor, "getRequestHeaders",
+          this.onRequestHeaders);
         break;
       case "requestCookies":
-        request = this.getData(actor, "getRequestCookies", this.onRequestCookies);
+        request = this.getData(actor, "getRequestCookies",
+          this.onRequestCookies);
         break;
       case "requestPostData":
-        request = this.getData(actor, "getRequestPostData", this.onRequestPostData);
+        request = this.getData(actor, "getRequestPostData",
+          this.onRequestPostData);
         break;
       case "responseHeaders":
-        request = this.getData(actor, "getResponseHeaders", this.onResponseHeaders);
+        request = this.getData(actor, "getResponseHeaders",
+          this.onResponseHeaders);
         break;
       case "responseCookies":
-        request = this.getData(actor, "getResponseCookies", this.onResponseCookies);
+        request = this.getData(actor, "getResponseCookies",
+          this.onResponseCookies);
         break;
       case "responseStart":
         file.httpVersion = packet.response.httpVersion;
         file.status = packet.response.status;
         file.statusText = packet.response.statusText;
         break;
       case "responseContent":
         file.contentSize = packet.contentSize;
         file.mimeType = packet.mimeType;
         file.transferredSize = packet.transferredSize;
 
         if (includeResponseBodies) {
-          request = this.getData(actor, "getResponseContent", this.onResponseContent);
+          request = this.getData(actor, "getResponseContent",
+            this.onResponseContent);
         }
         break;
       case "eventTimings":
-        request = this.getData(actor, "getEventTimings", this.onEventTimings);
+        request = this.getData(actor, "getEventTimings",
+          this.onEventTimings);
         break;
     }
 
     if (request) {
       this.requests.push(request);
     }
 
     this.resetPageLoadTimeout();
   },
 
   getData: function(actor, method, callback) {
     let deferred = defer();
 
     if (!this.webConsoleClient[method]) {
       Cu.reportError("HarCollector.getData; ERROR " +
         "Unknown method!");
-      return;
+      return deferred.resolve();
     }
 
     let file = this.getFile(actor);
 
     trace.log("HarCollector.getData; REQUEST " + method +
       ", " + file.url, file);
 
     this.webConsoleClient[method](actor, response => {
@@ -327,18 +337,18 @@ HarCollector.prototype = {
 
     let file = this.getFile(response.from);
     file.requestPostData = response;
 
     // Resolve long string
     let text = response.postData.text;
     if (typeof text == "object") {
       this.getString(text).then(value => {
-          response.postData.text = value;
-      })
+        response.postData.text = value;
+      });
     }
   },
 
   /**
    * Handles additional information received for a "responseHeaders" packet.
    *
    * @param object response
    *        The message received from the server.
@@ -373,17 +383,17 @@ HarCollector.prototype = {
     let file = this.getFile(response.from);
     file.responseContent = response;
 
     // Resolve long string
     let text = response.content.text;
     if (typeof text == "object") {
       this.getString(text).then(value => {
         response.content.text = value;
-      })
+      });
     }
   },
 
   /**
    * Handles additional information received for a "eventTimings" packet.
    *
    * @param object response
    *        The message received from the server.
--- a/devtools/client/netmonitor/har/har-exporter.js
+++ b/devtools/client/netmonitor/har/har-exporter.js
@@ -1,31 +1,31 @@
 /* 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 { Cu, Cc, Ci } = require("chrome");
 const Services = require("Services");
-const { defer, resolve } = require("promise");
+const { resolve } = require("promise");
 const { HarUtils } = require("./har-utils.js");
 const { HarBuilder } = require("./har-builder.js");
 
 XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
-  return Cc["@mozilla.org/widget/clipboardhelper;1"].
-    getService(Ci.nsIClipboardHelper);
+  return Cc["@mozilla.org/widget/clipboardhelper;1"]
+    .getService(Ci.nsIClipboardHelper);
 });
 
 var uid = 1;
 
 // Helper tracer. Should be generic sharable by other modules (bug 1171927)
 const trace = {
   log: function(...args) {
   }
-}
+};
 
 /**
  * This object represents the main public API designed to access
  * Network export logic. Clients, such as the Network panel itself,
  * should use this API to export collected HTTP data from the panel.
  */
 const HarExporter = {
   // Public API
@@ -112,19 +112,20 @@ const HarExporter = {
   fetchHarData: function(options) {
     // Generate page ID
     options.id = options.id || uid++;
 
     // Set default generic HAR export options.
     options.jsonp = options.jsonp ||
       Services.prefs.getBoolPref("devtools.netmonitor.har.jsonp");
     options.includeResponseBodies = options.includeResponseBodies ||
-      Services.prefs.getBoolPref("devtools.netmonitor.har.includeResponseBodies");
+      Services.prefs.getBoolPref(
+        "devtools.netmonitor.har.includeResponseBodies");
     options.jsonpCallback = options.jsonpCallback ||
-      Services.prefs.getCharPref( "devtools.netmonitor.har.jsonpCallback");
+      Services.prefs.getCharPref("devtools.netmonitor.har.jsonpCallback");
     options.forceExport = options.forceExport ||
       Services.prefs.getBoolPref("devtools.netmonitor.har.forceExport");
 
     // Build HAR object.
     return this.buildHarData(options).then(har => {
       // Do not export an empty HAR file, unless the user
       // explicitly says so (using the forceExport option).
       if (!har.log.entries.length && !options.forceExport) {
@@ -167,17 +168,16 @@ const HarExporter = {
    */
   stringify: function(har) {
     if (!har) {
       return null;
     }
 
     try {
       return JSON.stringify(har, null, "  ");
-    }
-    catch (err) {
+    } catch (err) {
       Cu.reportError(err);
     }
   },
 };
 
 // Exports from this module
 exports.HarExporter = HarExporter;
--- a/devtools/client/netmonitor/har/har-utils.js
+++ b/devtools/client/netmonitor/har/har-utils.js
@@ -1,18 +1,18 @@
 /* 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 { Cu, Ci, Cc, CC } = require("chrome");
-const Services = require("Services");
 
 XPCOMUtils.defineLazyGetter(this, "dirService", function() {
-  return Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
+  return Cc["@mozilla.org/file/directory_service;1"]
+    .getService(Ci.nsIProperties);
 });
 
 XPCOMUtils.defineLazyGetter(this, "ZipWriter", function() {
   return CC("@mozilla.org/zipwriter;1", "nsIZipWriter");
 });
 
 XPCOMUtils.defineLazyGetter(this, "LocalFile", function() {
   return new CC("@mozilla.org/file/local;1", "nsILocalFile", "initWithPath");
@@ -20,38 +20,39 @@ XPCOMUtils.defineLazyGetter(this, "Local
 
 XPCOMUtils.defineLazyGetter(this, "getMostRecentBrowserWindow", function() {
   return require("sdk/window/utils").getMostRecentBrowserWindow;
 });
 
 const nsIFilePicker = Ci.nsIFilePicker;
 
 const OPEN_FLAGS = {
-  RDONLY: parseInt("0x01"),
-  WRONLY: parseInt("0x02"),
-  CREATE_FILE: parseInt("0x08"),
-  APPEND: parseInt("0x10"),
-  TRUNCATE: parseInt("0x20"),
-  EXCL: parseInt("0x80")
+  RDONLY: parseInt("0x01", 16),
+  WRONLY: parseInt("0x02", 16),
+  CREATE_FILE: parseInt("0x08", 16),
+  APPEND: parseInt("0x10", 16),
+  TRUNCATE: parseInt("0x20", 16),
+  EXCL: parseInt("0x80", 16)
 };
 
 /**
  * Helper API for HAR export features.
  */
 var HarUtils = {
   /**
    * Open File Save As dialog and let the user pick the proper file
    * location for generated HAR log.
    */
   getTargetFile: function(fileName, jsonp, compress) {
     let browser = getMostRecentBrowserWindow();
 
     let fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
     fp.init(browser, null, nsIFilePicker.modeSave);
-    fp.appendFilter("HTTP Archive Files", "*.har; *.harp; *.json; *.jsonp; *.zip");
+    fp.appendFilter(
+      "HTTP Archive Files", "*.har; *.harp; *.json; *.jsonp; *.zip");
     fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText);
     fp.filterIndex = 1;
 
     fp.defaultString = this.getHarFileName(fileName, jsonp, compress);
 
     let rv = fp.show();
     if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
       return fp.file;
@@ -60,18 +61,18 @@ var HarUtils = {
     return null;
   },
 
   getHarFileName: function(defaultFileName, jsonp, compress) {
     let extension = jsonp ? ".harp" : ".har";
 
     // Read more about toLocaleFormat & format string.
     // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleFormat
-    var now = new Date();
-    var name = now.toLocaleFormat(defaultFileName);
+    let now = new Date();
+    let name = now.toLocaleFormat(defaultFileName);
     name = name.replace(/\:/gm, "-", "");
     name = name.replace(/\//gm, "_", "");
 
     let fileName = name + extension;
 
     // Default file extension is zip if compressing is on.
     if (compress) {
       fileName += ".zip";
@@ -101,18 +102,18 @@ var HarUtils = {
       foStream.init(file, openFlags, permFlags, 0);
 
       let convertor = Cc["@mozilla.org/intl/converter-output-stream;1"]
         .createInstance(Ci.nsIConverterOutputStream);
       convertor.init(foStream, "UTF-8", 0, 0);
 
       // The entire jsonString can be huge so, write the data in chunks.
       let chunkLength = 1024 * 1024;
-      for (let i=0; i<=jsonString.length; i++) {
-        let data = jsonString.substr(i, chunkLength+1);
+      for (let i = 0; i <= jsonString.length; i++) {
+        let data = jsonString.substr(i, chunkLength + 1);
         if (data) {
           convertor.writeString(data);
         }
 
         i = i + chunkLength;
       }
 
       // this closes foStream
@@ -131,18 +132,18 @@ var HarUtils = {
     let originalFilePath = file.path;
     let originalFileName = file.leafName;
 
     try {
       // Rename using unique name (the file is going to be removed).
       file.moveTo(null, "temp" + (new Date()).getTime() + "temphar");
 
       // Create compressed file with the original file path name.
-      let zipFile = Cc["@mozilla.org/file/local;1"].
-        createInstance(Ci.nsILocalFile);
+      let zipFile = Cc["@mozilla.org/file/local;1"]
+        .createInstance(Ci.nsILocalFile);
       zipFile.initWithPath(originalFilePath);
 
       // The file within the zipped file doesn't use .zip extension.
       let fileName = originalFileName;
       if (fileName.indexOf(".zip") == fileName.length - 4) {
         fileName = fileName.substr(0, fileName.indexOf(".zip"));
       }
 
@@ -174,12 +175,12 @@ var HarUtils = {
       dir.append("logs");
     } else {
       dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
       dir.initWithPath(path);
     }
 
     return dir;
   },
-}
+};
 
 // Exports from this module
 exports.HarUtils = HarUtils;
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/har/test/.eslintrc
@@ -0,0 +1,4 @@
+{
+  // Extend from the shared list of defined globals for mochitests.
+  "extends": "../../../../.eslintrc.mochitests"
+}
--- a/devtools/client/netmonitor/har/test/browser_net_har_copy_all_as_har.js
+++ b/devtools/client/netmonitor/har/test/browser_net_har_copy_all_as_har.js
@@ -1,27 +1,31 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Basic tests for exporting Network panel content into HAR format.
  */
 add_task(function*() {
-  let [ aTab, aDebuggee, aMonitor ] = yield initNetMonitor(SIMPLE_URL);
+  // The first 'tab' isn't necessary so, don't create a var for it
+  // to avoid eslint warning.
+  let [ , debuggee, monitor ] = yield initNetMonitor(SIMPLE_URL);
 
   info("Starting test... ");
 
-  let { NetMonitorView } = aMonitor.panelWin;
+  let { NetMonitorView } = monitor.panelWin;
   let { RequestsMenu } = NetMonitorView;
 
   RequestsMenu.lazyUpdate = false;
 
-  aDebuggee.location.reload();
+  debuggee.location.reload();
 
-  yield waitForNetworkEvents(aMonitor, 1);
+  yield waitForNetworkEvents(monitor, 1);
   yield RequestsMenu.copyAllAsHar();
 
   let jsonString = SpecialPowers.getClipboardData("text/unicode");
   let har = JSON.parse(jsonString);
 
   // Check out HAR log
   isnot(har.log, null, "The HAR log must exist");
   is(har.log.creator.name, "Firefox", "The creator field must be set");
@@ -31,14 +35,16 @@ add_task(function*() {
 
   let entry = har.log.entries[0];
   is(entry.request.method, "GET", "Check the method");
   is(entry.request.url, SIMPLE_URL, "Check the URL");
   is(entry.request.headers.length, 8, "Check number of request headers");
   is(entry.response.status, 200, "Check response status");
   is(entry.response.statusText, "OK", "Check response status text");
   is(entry.response.headers.length, 6, "Check number of response headers");
-  is(entry.response.content.mimeType, "text/html", "Check response content type");
-  isnot(entry.response.content.text, undefined, "Check response body");
+  is(entry.response.content.mimeType, // eslint-disable-line
+    "text/html", "Check response content type"); // eslint-disable-line
+  isnot(entry.response.content.text, undefined, // eslint-disable-line
+    "Check response body");
   isnot(entry.timings, undefined, "Check timings");
 
-  teardown(aMonitor).then(finish);
+  teardown(monitor).then(finish);
 });
--- a/devtools/client/netmonitor/har/test/browser_net_har_post_data.js
+++ b/devtools/client/netmonitor/har/test/browser_net_har_post_data.js
@@ -1,28 +1,32 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Tests for exporting POST data into HAR format.
  */
 add_task(function*() {
-  let [ aTab, aDebuggee, aMonitor ] = yield initNetMonitor(
+  // The first 'tab' isn't necessary so, don't create a var for it
+  // to avoid eslint warning.
+  let [ , debuggee, monitor ] = yield initNetMonitor(
     HAR_EXAMPLE_URL + "html_har_post-data-test-page.html");
 
   info("Starting test... ");
 
-  let { NetMonitorView } = aMonitor.panelWin;
+  let { NetMonitorView } = monitor.panelWin;
   let { RequestsMenu } = NetMonitorView;
 
   RequestsMenu.lazyUpdate = false;
 
   // Execute one POST request on the page and wait till its done.
-  aDebuggee.executeTest();
-  yield waitForNetworkEvents(aMonitor, 0, 1);
+  debuggee.executeTest();
+  yield waitForNetworkEvents(monitor, 0, 1);
 
   // Copy HAR into the clipboard (asynchronous).
   let jsonString = yield RequestsMenu.copyAllAsHar();
   let har = JSON.parse(jsonString);
 
   // Check out the HAR log.
   isnot(har.log, null, "The HAR log must exist");
   is(har.log.pages.length, 1, "There must be one page");
@@ -30,10 +34,10 @@ add_task(function*() {
 
   let entry = har.log.entries[0];
   is(entry.request.postData.mimeType, "application/json",
     "Check post data content type");
   is(entry.request.postData.text, "{'first': 'John', 'last': 'Doe'}",
     "Check post data payload");
 
   // Clean up
-  teardown(aMonitor).then(finish);
+  teardown(monitor).then(finish);
 });
--- a/devtools/client/netmonitor/har/test/head.js
+++ b/devtools/client/netmonitor/har/test/head.js
@@ -1,12 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+
 "use strict";
 
+/* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */
+/* import-globals-from ../../test/head.js */
+
 var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 
 // Load the NetMonitor head.js file to share its API.
 var netMonitorHead = "chrome://mochitests/content/browser/devtools/client/netmonitor/test/head.js";
 Services.scriptloader.loadSubScript(netMonitorHead, this);
 
 // Directory with HAR related test files.
 const HAR_EXAMPLE_URL = "http://example.com/browser/devtools/client/netmonitor/har/test/";
--- a/devtools/client/netmonitor/har/toolbox-overlay.js
+++ b/devtools/client/netmonitor/har/toolbox-overlay.js
@@ -1,14 +1,13 @@
 /* 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 { Cu, Ci } = require("chrome");
 const Services = require("Services");
 
 loader.lazyRequireGetter(this, "HarAutomation", "devtools/client/netmonitor/har/har-automation", true);
 
 // Map of all created overlays. There is always one instance of
 // an overlay per Toolbox instance (i.e. one per browser tab).
 const overlays = new WeakMap();
 
--- a/devtools/client/netmonitor/netmonitor-controller.js
+++ b/devtools/client/netmonitor/netmonitor-controller.js
@@ -1,20 +1,17 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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/. */
+/* globals window, document, NetMonitorView */
 "use strict";
 
-var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-const NET_STRINGS_URI = "chrome://devtools/locale/netmonitor.properties";
-const PKI_STRINGS_URI = "chrome://pippki/locale/pippki.properties";
-const LISTENERS = [ "NetworkActivity" ];
+var { utils: Cu } = Components;
 
 // The panel's window global is an EventEmitter firing the following events:
 const EVENTS = {
   // When the monitored target begins and finishes navigating.
   TARGET_WILL_NAVIGATE: "NetMonitor:TargetWillNavigate",
   TARGET_DID_NAVIGATE: "NetMonitor:TargetNavigate",
 
   // When a network or timeline event is received.
@@ -64,17 +61,18 @@ const EVENTS = {
 
   // When the response body is displayed in the UI.
   RESPONSE_BODY_DISPLAYED: "NetMonitor:ResponseBodyAvailable",
 
   // When the html response preview is displayed in the UI.
   RESPONSE_HTML_PREVIEW_DISPLAYED: "NetMonitor:ResponseHtmlPreviewAvailable",
 
   // When the image response thumbnail is displayed in the UI.
-  RESPONSE_IMAGE_THUMBNAIL_DISPLAYED: "NetMonitor:ResponseImageThumbnailAvailable",
+  RESPONSE_IMAGE_THUMBNAIL_DISPLAYED:
+    "NetMonitor:ResponseImageThumbnailAvailable",
 
   // When a tab is selected in the NetworkDetailsView and subsequently rendered.
   TAB_UPDATED: "NetMonitor:TabUpdated",
 
   // Fired when Sidebar has finished being populated.
   SIDEBAR_POPULATED: "NetMonitor:SidebarPopulated",
 
   // Fired when NetworkDetailsView has finished being populated.
@@ -109,26 +107,22 @@ const ACTIVITY_TYPE = {
   ENABLE_CACHE: 3,
   DISABLE_CACHE: 4
 };
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
 Cu.import("resource://devtools/client/shared/widgets/VariablesView.jsm");
 Cu.import("resource://devtools/client/shared/widgets/VariablesViewController.jsm");
-Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
 
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const promise = require("promise");
 const Services = require("Services");
 const EventEmitter = require("devtools/shared/event-emitter");
 const Editor = require("devtools/client/sourceeditor/editor");
-const {Tooltip} = require("devtools/client/shared/widgets/Tooltip");
-const {ToolSidebar} = require("devtools/client/framework/sidebar");
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const {TimelineFront} = require("devtools/server/actors/timeline");
 
 XPCOMUtils.defineConstant(this, "EVENTS", EVENTS);
 XPCOMUtils.defineConstant(this, "ACTIVITY_TYPE", ACTIVITY_TYPE);
 XPCOMUtils.defineConstant(this, "Editor", Editor);
 
 XPCOMUtils.defineLazyModuleGetter(this, "Chart",
   "resource://devtools/client/shared/widgets/Chart.jsm");
@@ -186,17 +180,17 @@ var NetMonitorController = {
    *
    * @return object
    *         A promise that is resolved when the monitor finishes shutdown.
    */
   shutdownNetMonitor: Task.async(function*() {
     if (this._shutdown) {
       return this._shutdown.promise;
     }
-    this._shutdown = promise.defer();;
+    this._shutdown = promise.defer();
     {
       NetMonitorView.destroy();
       this.TargetEventsHandler.disconnect();
       this.NetworkEventsHandler.disconnect();
       yield this.disconnect();
     }
     this._shutdown.resolve();
   }),
@@ -221,17 +215,18 @@ var NetMonitorController = {
     if (this._target.isTabActor) {
       this.tabClient = this._target.activeTab;
     }
 
     let connectTimeline = () => {
       // Don't start up waiting for timeline markers if the server isn't
       // recent enough to emit the markers we're interested in.
       if (this._target.getTrait("documentLoadingMarkers")) {
-        this.timelineFront = new TimelineFront(this._target.client, this._target.form);
+        this.timelineFront = new TimelineFront(this._target.client,
+          this._target.form);
         return this.timelineFront.start({ withDocLoadingEvents: true });
       }
     };
 
     this.webConsoleClient = this._target.activeConsole;
     yield connectTimeline();
 
     this.TargetEventsHandler.connect();
@@ -285,26 +280,26 @@ var NetMonitorController = {
    * Gets the activity currently performed by the frontend.
    * @return number
    */
   getCurrentActivity: function() {
     return this._currentActivity || ACTIVITY_TYPE.NONE;
   },
 
   /**
-   * Triggers a specific "activity" to be performed by the frontend. This can be,
-   * for example, triggering reloads or enabling/disabling cache.
+   * Triggers a specific "activity" to be performed by the frontend.
+   * This can be, for example, triggering reloads or enabling/disabling cache.
    *
-   * @param number aType
+   * @param number type
    *        The activity type. See the ACTIVITY_TYPE const.
    * @return object
    *         A promise resolved once the activity finishes and the frontend
    *         is back into "standby" mode.
    */
-  triggerActivity: function(aType) {
+  triggerActivity: function(type) {
     // Puts the frontend into "standby" (when there's no particular activity).
     let standBy = () => {
       this._currentActivity = ACTIVITY_TYPE.NONE;
     };
 
     // Waits for a series of "navigation start" and "navigation stop" events.
     let waitForNavigation = () => {
       let deferred = promise.defer();
@@ -312,48 +307,60 @@ var NetMonitorController = {
         this._target.once("navigate", () => {
           deferred.resolve();
         });
       });
       return deferred.promise;
     };
 
     // Reconfigures the tab, optionally triggering a reload.
-    let reconfigureTab = aOptions => {
+    let reconfigureTab = options => {
       let deferred = promise.defer();
-      this._target.activeTab.reconfigure(aOptions, deferred.resolve);
+      this._target.activeTab.reconfigure(options, deferred.resolve);
       return deferred.promise;
     };
 
     // Reconfigures the tab and waits for the target to finish navigating.
-    let reconfigureTabAndWaitForNavigation = aOptions => {
-      aOptions.performReload = true;
+    let reconfigureTabAndWaitForNavigation = options => {
+      options.performReload = true;
       let navigationFinished = waitForNavigation();
-      return reconfigureTab(aOptions).then(() => navigationFinished);
-    }
-    if (aType == ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT) {
+      return reconfigureTab(options).then(() => navigationFinished);
+    };
+    if (type == ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT) {
       return reconfigureTabAndWaitForNavigation({}).then(standBy);
     }
-    if (aType == ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED) {
+    if (type == ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED) {
       this._currentActivity = ACTIVITY_TYPE.ENABLE_CACHE;
-      this._target.once("will-navigate", () => this._currentActivity = aType);
-      return reconfigureTabAndWaitForNavigation({ cacheDisabled: false, performReload: true }).then(standBy);
+      this._target.once("will-navigate", () => this._currentActivity = type);
+      return reconfigureTabAndWaitForNavigation({
+        cacheDisabled: false,
+        performReload: true
+      }).then(standBy);
     }
-    if (aType == ACTIVITY_TYPE.RELOAD.WITH_CACHE_DISABLED) {
+    if (type == ACTIVITY_TYPE.RELOAD.WITH_CACHE_DISABLED) {
       this._currentActivity = ACTIVITY_TYPE.DISABLE_CACHE;
-      this._target.once("will-navigate", () => this._currentActivity = aType);
-      return reconfigureTabAndWaitForNavigation({ cacheDisabled: true, performReload: true }).then(standBy);
+      this._target.once("will-navigate", () => this._currentActivity = type);
+      return reconfigureTabAndWaitForNavigation({
+        cacheDisabled: true,
+        performReload: true
+      }).then(standBy);
     }
-    if (aType == ACTIVITY_TYPE.ENABLE_CACHE) {
-      this._currentActivity = aType;
-      return reconfigureTab({ cacheDisabled: false, performReload: false }).then(standBy);
+    if (type == ACTIVITY_TYPE.ENABLE_CACHE) {
+      this._currentActivity = type;
+      return reconfigureTab({
+        cacheDisabled: false,
+        performReload: false
+      }).then(standBy);
     }
-    if (aType == ACTIVITY_TYPE.DISABLE_CACHE) {
-      this._currentActivity = aType;
-      return reconfigureTab({ cacheDisabled: true, performReload: false }).then(standBy);
+    if (type == ACTIVITY_TYPE.DISABLE_CACHE) {
+      this._currentActivity = type;
+      return reconfigureTab({
+        cacheDisabled: true,
+        performReload: false
+      }).then(standBy);
     }
     this._currentActivity = ACTIVITY_TYPE.NONE;
     return promise.reject(new Error("Invalid activity type"));
   },
 
   /**
    * Selects the specified request in the waterfall and opens the details view.
    *
@@ -378,17 +385,17 @@ var NetMonitorController = {
 
       // If the request was found, select it. Otherwise this function will be
       // called again once new requests arrive.
       if (request) {
         window.off(EVENTS.REQUEST_ADDED, inspector);
         NetMonitorView.RequestsMenu.selectedItem = request;
         deferred.resolve();
       }
-    }
+    };
 
     inspector();
     if (!request) {
       window.on(EVENTS.REQUEST_ADDED, inspector);
     }
     return deferred.promise;
   },
 
@@ -456,23 +463,23 @@ TargetEventsHandler.prototype = {
     this.target.off("close", this._onTabDetached);
     this.target.off("navigate", this._onTabNavigated);
     this.target.off("will-navigate", this._onTabNavigated);
   },
 
   /**
    * Called for each location change in the monitored tab.
    *
-   * @param string aType
+   * @param string type
    *        Packet type.
-   * @param object aPacket
+   * @param object packet
    *        Packet received from the server.
    */
-  _onTabNavigated: function(aType, aPacket) {
-    switch (aType) {
+  _onTabNavigated: function(type, packet) {
+    switch (type) {
       case "will-navigate": {
         // Reset UI.
         if (!Services.prefs.getBoolPref("devtools.webconsole.persistlog")) {
           NetMonitorView.RequestsMenu.reset();
           NetMonitorView.Sidebar.toggle(false);
         }
         // Switch to the default network traffic inspector view.
         if (NetMonitorController.getCurrentActivity() == ACTIVITY_TYPE.NONE) {
@@ -526,17 +533,20 @@ NetworkEventsHandler.prototype = {
     return NetMonitorController.webConsoleClient;
   },
 
   get timelineFront() {
     return NetMonitorController.timelineFront;
   },
 
   get firstDocumentDOMContentLoadedTimestamp() {
-    let marker = this._markers.filter(e => e.name == "document::DOMContentLoaded")[0];
+    let marker = this._markers.filter(e => {
+      return e.name == "document::DOMContentLoaded";
+    })[0];
+
     return marker ? marker.unixTime / 1000 : -1;
   },
 
   get firstDocumentLoadTimestamp() {
     let marker = this._markers.filter(e => e.name == "document::Load")[0];
     return marker ? marker.unixTime / 1000 : -1;
   },
 
@@ -603,17 +613,23 @@ NetworkEventsHandler.prototype = {
    * The "networkEvent" message type handler.
    *
    * @param string type
    *        Message type.
    * @param object networkInfo
    *        The network request information.
    */
   _onNetworkEvent: function(type, networkInfo) {
-    let { actor, startedDateTime, request: { method, url }, isXHR, fromCache, fromServiceWorker } = networkInfo;
+    let { actor,
+      startedDateTime,
+      request: { method, url },
+      isXHR,
+      fromCache,
+      fromServiceWorker
+    } = networkInfo;
 
     NetMonitorView.RequestsMenu.addRequest(
       actor, startedDateTime, method, url, isXHR, fromCache, fromServiceWorker
     );
     window.emit(EVENTS.NETWORK_EVENT, actor);
   },
 
   /**
@@ -622,43 +638,47 @@ NetworkEventsHandler.prototype = {
    * @param string type
    *        Message type.
    * @param object packet
    *        The message received from the server.
    * @param object networkInfo
    *        The network request information.
    */
   _onNetworkEventUpdate: function(type, { packet, networkInfo }) {
-    let { actor, request: { url } } = networkInfo;
+    let { actor } = networkInfo;
+
     switch (packet.updateType) {
       case "requestHeaders":
         this.webConsoleClient.getRequestHeaders(actor, this._onRequestHeaders);
         window.emit(EVENTS.UPDATING_REQUEST_HEADERS, actor);
         break;
       case "requestCookies":
         this.webConsoleClient.getRequestCookies(actor, this._onRequestCookies);
         window.emit(EVENTS.UPDATING_REQUEST_COOKIES, actor);
         break;
       case "requestPostData":
-        this.webConsoleClient.getRequestPostData(actor, this._onRequestPostData);
+        this.webConsoleClient.getRequestPostData(actor,
+          this._onRequestPostData);
         window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, actor);
         break;
       case "securityInfo":
         NetMonitorView.RequestsMenu.updateRequest(actor, {
           securityState: networkInfo.securityInfo,
         });
         this.webConsoleClient.getSecurityInfo(actor, this._onSecurityInfo);
         window.emit(EVENTS.UPDATING_SECURITY_INFO, actor);
         break;
       case "responseHeaders":
-        this.webConsoleClient.getResponseHeaders(actor, this._onResponseHeaders);
+        this.webConsoleClient.getResponseHeaders(actor,
+          this._onResponseHeaders);
         window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, actor);
         break;
       case "responseCookies":
-        this.webConsoleClient.getResponseCookies(actor, this._onResponseCookies);
+        this.webConsoleClient.getResponseCookies(actor,
+          this._onResponseCookies);
         window.emit(EVENTS.UPDATING_RESPONSE_COOKIES, actor);
         break;
       case "responseStart":
         NetMonitorView.RequestsMenu.updateRequest(actor, {
           httpVersion: networkInfo.response.httpVersion,
           remoteAddress: networkInfo.response.remoteAddress,
           remotePort: networkInfo.response.remotePort,
           status: networkInfo.response.status,
@@ -668,186 +688,172 @@ NetworkEventsHandler.prototype = {
         window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
         break;
       case "responseContent":
         NetMonitorView.RequestsMenu.updateRequest(actor, {
           contentSize: networkInfo.response.bodySize,
           transferredSize: networkInfo.response.transferredSize,
           mimeType: networkInfo.response.content.mimeType
         });
-        this.webConsoleClient.getResponseContent(actor, this._onResponseContent);
+        this.webConsoleClient.getResponseContent(actor,
+          this._onResponseContent);
         window.emit(EVENTS.UPDATING_RESPONSE_CONTENT, actor);
         break;
       case "eventTimings":
         NetMonitorView.RequestsMenu.updateRequest(actor, {
           totalTime: networkInfo.totalTime
         });
         this.webConsoleClient.getEventTimings(actor, this._onEventTimings);
         window.emit(EVENTS.UPDATING_EVENT_TIMINGS, actor);
         break;
     }
   },
 
   /**
    * Handles additional information received for a "requestHeaders" packet.
    *
-   * @param object aResponse
+   * @param object response
    *        The message received from the server.
    */
-  _onRequestHeaders: function(aResponse) {
-    NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
-      requestHeaders: aResponse
+  _onRequestHeaders: function(response) {
+    NetMonitorView.RequestsMenu.updateRequest(response.from, {
+      requestHeaders: response
     }, () => {
-      window.emit(EVENTS.RECEIVED_REQUEST_HEADERS, aResponse.from);
+      window.emit(EVENTS.RECEIVED_REQUEST_HEADERS, response.from);
     });
   },
 
   /**
    * Handles additional information received for a "requestCookies" packet.
    *
-   * @param object aResponse
+   * @param object response
    *        The message received from the server.
    */
-  _onRequestCookies: function(aResponse) {
-    NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
-      requestCookies: aResponse
+  _onRequestCookies: function(response) {
+    NetMonitorView.RequestsMenu.updateRequest(response.from, {
+      requestCookies: response
     }, () => {
-      window.emit(EVENTS.RECEIVED_REQUEST_COOKIES, aResponse.from);
+      window.emit(EVENTS.RECEIVED_REQUEST_COOKIES, response.from);
     });
   },
 
   /**
    * Handles additional information received for a "requestPostData" packet.
    *
-   * @param object aResponse
+   * @param object response
    *        The message received from the server.
    */
-  _onRequestPostData: function(aResponse) {
-    NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
-      requestPostData: aResponse
+  _onRequestPostData: function(response) {
+    NetMonitorView.RequestsMenu.updateRequest(response.from, {
+      requestPostData: response
     }, () => {
-      window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA, aResponse.from);
+      window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA, response.from);
     });
   },
 
   /**
    * Handles additional information received for a "securityInfo" packet.
    *
-   * @param object aResponse
+   * @param object response
    *        The message received from the server.
    */
-   _onSecurityInfo: function(aResponse) {
-     NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
-       securityInfo: aResponse.securityInfo
-     }, () => {
-       window.emit(EVENTS.RECEIVED_SECURITY_INFO, aResponse.from);
-     });
-   },
+  _onSecurityInfo: function(response) {
+    NetMonitorView.RequestsMenu.updateRequest(response.from, {
+      securityInfo: response.securityInfo
+    }, () => {
+      window.emit(EVENTS.RECEIVED_SECURITY_INFO, response.from);
+    });
+  },
 
   /**
    * Handles additional information received for a "responseHeaders" packet.
    *
-   * @param object aResponse
+   * @param object response
    *        The message received from the server.
    */
-  _onResponseHeaders: function(aResponse) {
-    NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
-      responseHeaders: aResponse
+  _onResponseHeaders: function(response) {
+    NetMonitorView.RequestsMenu.updateRequest(response.from, {
+      responseHeaders: response
     }, () => {
-      window.emit(EVENTS.RECEIVED_RESPONSE_HEADERS, aResponse.from);
+      window.emit(EVENTS.RECEIVED_RESPONSE_HEADERS, response.from);
     });
   },
 
   /**
    * Handles additional information received for a "responseCookies" packet.
    *
-   * @param object aResponse
+   * @param object response
    *        The message received from the server.
    */
-  _onResponseCookies: function(aResponse) {
-    NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
-      responseCookies: aResponse
+  _onResponseCookies: function(response) {
+    NetMonitorView.RequestsMenu.updateRequest(response.from, {
+      responseCookies: response
     }, () => {
-      window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, aResponse.from);
+      window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from);
     });
   },
 
   /**
    * Handles additional information received for a "responseContent" packet.
    *
-   * @param object aResponse
+   * @param object response
    *        The message received from the server.
    */
-  _onResponseContent: function(aResponse) {
-    NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
-      responseContent: aResponse
+  _onResponseContent: function(response) {
+    NetMonitorView.RequestsMenu.updateRequest(response.from, {
+      responseContent: response
     }, () => {
-      window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, aResponse.from);
+      window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, response.from);
     });
   },
 
   /**
    * Handles additional information received for a "eventTimings" packet.
    *
-   * @param object aResponse
+   * @param object response
    *        The message received from the server.
    */
-  _onEventTimings: function(aResponse) {
-    NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
-      eventTimings: aResponse
+  _onEventTimings: function(response) {
+    NetMonitorView.RequestsMenu.updateRequest(response.from, {
+      eventTimings: response
     }, () => {
-      window.emit(EVENTS.RECEIVED_EVENT_TIMINGS, aResponse.from);
+      window.emit(EVENTS.RECEIVED_EVENT_TIMINGS, response.from);
     });
   },
 
   /**
    * Clears all accumulated markers.
    */
   clearMarkers: function() {
     this._markers.length = 0;
   },
 
   /**
    * Fetches the full text of a LongString.
    *
-   * @param object | string aStringGrip
+   * @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(aStringGrip) {
-    return this.webConsoleClient.getString(aStringGrip);
+  getString: function(stringGrip) {
+    return this.webConsoleClient.getString(stringGrip);
   }
 };
 
 /**
- * Localization convenience methods.
- */
-var L10N = new ViewHelpers.L10N(NET_STRINGS_URI);
-var PKI_L10N = new ViewHelpers.L10N(PKI_STRINGS_URI);
-
-/**
- * Shortcuts for accessing various network monitor preferences.
- */
-var Prefs = new ViewHelpers.Prefs("devtools.netmonitor", {
-  networkDetailsWidth: ["Int", "panes-network-details-width"],
-  networkDetailsHeight: ["Int", "panes-network-details-height"],
-  statistics: ["Bool", "statistics"],
-  filters: ["Json", "filters"]
-});
-
-/**
  * Returns true if this is document is in RTL mode.
  * @return boolean
  */
 XPCOMUtils.defineLazyGetter(window, "isRTL", function() {
-  return window.getComputedStyle(document.documentElement, null).direction == "rtl";
+  return window.getComputedStyle(document.documentElement, null)
+    .direction == "rtl";
 });
 
 /**
  * Convenient way of emitting events from the panel window.
  */
 EventEmitter.decorate(this);
 
 /**
@@ -864,54 +870,16 @@ Object.defineProperties(window, {
     get: function() {
       return NetMonitorController.NetworkEventsHandler;
     },
     configurable: true
   }
 });
 
 /**
- * Makes sure certain properties are available on all objects in a data store.
- *
- * @param array aDataStore
- *        A list of objects for which to check the availability of properties.
- * @param array aMandatoryFields
- *        A list of strings representing properties of objects in aDataStore.
- * @return object
- *         A promise resolved when all objects in aDataStore contain the
- *         properties defined in aMandatoryFields.
- */
-function whenDataAvailable(aDataStore, aMandatoryFields) {
-  let deferred = promise.defer();
-
-  let interval = setInterval(() => {
-    if (aDataStore.every(item => aMandatoryFields.every(field => field in item))) {
-      clearInterval(interval);
-      clearTimeout(timer);
-      deferred.resolve();
-    }
-  }, WDA_DEFAULT_VERIFY_INTERVAL);
-
-  let timer = setTimeout(() => {
-    clearInterval(interval);
-    deferred.reject(new Error("Timed out while waiting for data"));
-  }, WDA_DEFAULT_GIVE_UP_TIMEOUT);
-
-  return deferred.promise;
-};
-
-const WDA_DEFAULT_VERIFY_INTERVAL = 50; // ms
-
-// 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
-// be at least equal to the general mochitest timeout of 45 seconds so that this
-// never gets hit during testing.
-const WDA_DEFAULT_GIVE_UP_TIMEOUT = DevToolsUtils.testing ? 45000 : 2000; // ms
-
-/**
  * Helper method for debugging.
  * @param string
  */
 function dumpn(str) {
   if (wantLogging) {
     dump("NET-FRONTEND: " + str + "\n");
   }
 }
--- a/devtools/client/netmonitor/netmonitor-view.js
+++ b/devtools/client/netmonitor/netmonitor-view.js
@@ -1,42 +1,87 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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/. */
-/* import-globals-from netmonitor-controller.js */
-/* globals window, document */
+/* import-globals-from ./netmonitor-controller.js */
+/* import-globals-from ../shared/widgets/ViewHelpers.jsm */
+/* globals gNetwork, setInterval, setTimeout, clearInterval,
+   clearTimeout btoa */
 "use strict";
 
+var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
 XPCOMUtils.defineLazyGetter(this, "HarExporter", function() {
   return require("devtools/client/netmonitor/har/har-exporter").HarExporter;
 });
 
 XPCOMUtils.defineLazyGetter(this, "NetworkHelper", function() {
   return require("devtools/shared/webconsole/network-helper");
 });
 
+const {ToolSidebar} = require("devtools/client/framework/sidebar");
+const {Tooltip} = require("devtools/client/shared/widgets/Tooltip");
+const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+
+Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
+
+/**
+ * Localization convenience methods.
+ */
+const NET_STRINGS_URI = "chrome://devtools/locale/netmonitor.properties";
+var L10N = new ViewHelpers.L10N(NET_STRINGS_URI);
+
+// 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
+// be at least equal to the general mochitest timeout of 45 seconds so that this
+// never gets hit during testing.
+// ms
+const WDA_DEFAULT_GIVE_UP_TIMEOUT = DevToolsUtils.testing ? 45000 : 2000;
+
+/**
+ * Shortcuts for accessing various network monitor preferences.
+ */
+var Prefs = new ViewHelpers.Prefs("devtools.netmonitor", {
+  networkDetailsWidth: ["Int", "panes-network-details-width"],
+  networkDetailsHeight: ["Int", "panes-network-details-height"],
+  statistics: ["Bool", "statistics"],
+  filters: ["Json", "filters"]
+});
+
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const EPSILON = 0.001;
-const SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE = 102400; // 100 KB in bytes
-const RESIZE_REFRESH_RATE = 50; // ms
-const REQUESTS_REFRESH_RATE = 50; // ms
-const REQUESTS_HEADERS_SAFE_BOUNDS = 30; // px
+// 100 KB in bytes
+const SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE = 102400;
+// ms
+const RESIZE_REFRESH_RATE = 50;
+// ms
+const REQUESTS_REFRESH_RATE = 50;
 const REQUESTS_TOOLTIP_POSITION = "topcenter bottomleft";
-const REQUESTS_TOOLTIP_IMAGE_MAX_DIM = 400; // px
-const REQUESTS_WATERFALL_SAFE_BOUNDS = 90; // px
-const REQUESTS_WATERFALL_HEADER_TICKS_MULTIPLE = 5; // ms
-const REQUESTS_WATERFALL_HEADER_TICKS_SPACING_MIN = 60; // px
-const REQUESTS_WATERFALL_BACKGROUND_TICKS_MULTIPLE = 5; // ms
+// px
+const REQUESTS_TOOLTIP_IMAGE_MAX_DIM = 400;
+// px
+const REQUESTS_WATERFALL_SAFE_BOUNDS = 90;
+// ms
+const REQUESTS_WATERFALL_HEADER_TICKS_MULTIPLE = 5;
+// px
+const REQUESTS_WATERFALL_HEADER_TICKS_SPACING_MIN = 60;
+// ms
+const REQUESTS_WATERFALL_BACKGROUND_TICKS_MULTIPLE = 5;
 const REQUESTS_WATERFALL_BACKGROUND_TICKS_SCALES = 3;
-const REQUESTS_WATERFALL_BACKGROUND_TICKS_SPACING_MIN = 10; // px
+// px
+const REQUESTS_WATERFALL_BACKGROUND_TICKS_SPACING_MIN = 10;
 const REQUESTS_WATERFALL_BACKGROUND_TICKS_COLOR_RGB = [128, 136, 144];
-const REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_MIN = 32; // byte
-const REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_ADD = 32; // byte
+const REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_MIN = 32;
+// byte
+const REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_ADD = 32;
 const REQUESTS_WATERFALL_DOMCONTENTLOADED_TICKS_COLOR_RGBA = [255, 0, 0, 128];
 const REQUESTS_WATERFALL_LOAD_TICKS_COLOR_RGBA = [0, 0, 255, 128];
 const REQUEST_TIME_DECIMALS = 2;
 const HEADERS_SIZE_DECIMALS = 3;
 const CONTENT_SIZE_DECIMALS = 2;
 const CONTENT_MIME_TYPE_ABBREVIATIONS = {
   "ecmascript": "js",
   "javascript": "js",
@@ -58,26 +103,29 @@ const CONTENT_MIME_TYPE_MAPPINGS = {
 };
 const DEFAULT_EDITOR_CONFIG = {
   mode: Editor.modes.text,
   readOnly: true,
   lineNumbers: true
 };
 const GENERIC_VARIABLES_VIEW_SETTINGS = {
   lazyEmpty: true,
-  lazyEmptyDelay: 10, // ms
+  // ms
+  lazyEmptyDelay: 10,
   searchEnabled: true,
   editableValueTooltip: "",
   editableNameTooltip: "",
   preventDisableOnChange: true,
   preventDescriptorModifiers: true,
   eval: () => {}
 };
-const NETWORK_ANALYSIS_PIE_CHART_DIAMETER = 200; // px
-const FREETEXT_FILTER_SEARCH_DELAY = 200; // ms
+// px
+const NETWORK_ANALYSIS_PIE_CHART_DIAMETER = 200;
+// ms
+const FREETEXT_FILTER_SEARCH_DELAY = 200;
 
 const {DeferredTask} = Cu.import("resource://gre/modules/DeferredTask.jsm", {});
 
 /**
  * Object defining the network monitor view components.
  */
 var NetMonitorView = {
   /**
@@ -154,43 +202,43 @@ var NetMonitorView = {
    */
   get detailsPaneHidden() {
     return this._detailsPane.hasAttribute("pane-collapsed");
   },
 
   /**
    * Sets the network details pane hidden or visible.
    *
-   * @param object aFlags
+   * @param object flags
    *        An object containing some of the following properties:
    *        - visible: true if the pane should be shown, false to hide
    *        - animated: true to display an animation on toggle
    *        - delayed: true to wait a few cycles before toggle
    *        - callback: a function to invoke when the toggle finishes
-   * @param number aTabIndex [optional]
+   * @param number tabIndex [optional]
    *        The index of the intended selected tab in the details pane.
    */
-  toggleDetailsPane: function(aFlags, aTabIndex) {
+  toggleDetailsPane: function(flags, tabIndex) {
     let pane = this._detailsPane;
     let button = this._detailsPaneToggleButton;
 
-    ViewHelpers.togglePane(aFlags, pane);
-
-    if (aFlags.visible) {
+    ViewHelpers.togglePane(flags, pane);
+
+    if (flags.visible) {
       this._body.removeAttribute("pane-collapsed");
       button.removeAttribute("pane-collapsed");
       button.setAttribute("tooltiptext", this._collapsePaneString);
     } else {
       this._body.setAttribute("pane-collapsed", "");
       button.setAttribute("pane-collapsed", "");
       button.setAttribute("tooltiptext", this._expandPaneString);
     }
 
-    if (aTabIndex !== undefined) {
-      $("#event-details-pane").selectedIndex = aTabIndex;
+    if (tabIndex !== undefined) {
+      $("#event-details-pane").selectedIndex = tabIndex;
     }
   },
 
   /**
    * Gets the current mode for this tool.
    * @return string (e.g, "network-inspector-view" or "network-statistics-view")
    */
   get currentFrontendMode() {
@@ -249,41 +297,42 @@ var NetMonitorView = {
       }
 
       statisticsView.createPrimedCacheChart(requestsView.items);
       statisticsView.createEmptyCacheChart(requestsView.items);
     });
   },
 
   reloadPage: function() {
-    NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT);
+    NetMonitorController.triggerActivity(
+      ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT);
   },
 
   /**
    * Lazily initializes and returns a promise for a Editor instance.
    *
-   * @param string aId
+   * @param string id
    *        The id of the editor placeholder node.
    * @return object
    *         A promise that is resolved when the editor is available.
    */
-  editor: function(aId) {
-    dumpn("Getting a NetMonitorView editor: " + aId);
-
-    if (this._editorPromises.has(aId)) {
-      return this._editorPromises.get(aId);
+  editor: function(id) {
+    dumpn("Getting a NetMonitorView editor: " + id);
+
+    if (this._editorPromises.has(id)) {
+      return this._editorPromises.get(id);
     }
 
     let deferred = promise.defer();
-    this._editorPromises.set(aId, deferred.promise);
+    this._editorPromises.set(id, deferred.promise);
 
     // Initialize the source editor and store the newly created instance
     // in the ether of a resolved promise's value.
     let editor = new Editor(DEFAULT_EDITOR_CONFIG);
-    editor.appendTo($(aId)).then(() => deferred.resolve(editor));
+    editor.appendTo($(id)).then(() => deferred.resolve(editor));
 
     return deferred.promise;
   },
 
   _body: null,
   _detailsPane: null,
   _detailsPaneToggleButton: null,
   _collapsePaneString: "",
@@ -303,26 +352,28 @@ function ToolbarView() {
 ToolbarView.prototype = {
   /**
    * Initialization function, called when the debugger is started.
    */
   initialize: function() {
     dumpn("Initializing the ToolbarView");
 
     this._detailsPaneToggleButton = $("#details-pane-toggle");
-    this._detailsPaneToggleButton.addEventListener("mousedown", this._onTogglePanesPressed, false);
+    this._detailsPaneToggleButton.addEventListener("mousedown",
+      this._onTogglePanesPressed, false);
   },
 
   /**
    * Destruction function, called when the debugger is closed.
    */
   destroy: function() {
     dumpn("Destroying the ToolbarView");
 
-    this._detailsPaneToggleButton.removeEventListener("mousedown", this._onTogglePanesPressed, false);
+    this._detailsPaneToggleButton.removeEventListener("mousedown",
+      this._onTogglePanesPressed, false);
   },
 
   /**
    * Listener handling the toggle button click event.
    */
   _onTogglePanesPressed: function() {
     let requestsMenu = NetMonitorView.RequestsMenu;
     let selectedIndex = requestsMenu.selectedIndex;
@@ -364,17 +415,18 @@ RequestsMenuView.prototype = Heritage.ex
    */
   initialize: function() {
     dumpn("Initializing the RequestsMenuView");
 
     this.widget = new SideMenuWidget($("#requests-menu-contents"));
     this._splitter = $("#network-inspector-view-splitter");
     this._summary = $("#requests-menu-network-summary-button");
     this._summary.setAttribute("label", L10N.getStr("networkMenu.empty"));
-    this.userInputTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    this.userInputTimer = Cc["@mozilla.org/timer;1"]
+      .createInstance(Ci.nsITimer);
 
     Prefs.filters.forEach(type => this.filterOn(type));
     this.sortContents(this._byTiming);
 
     this.allowFocusOnRightClick = true;
     this.maintainSelectionVisible = true;
 
     this.widget.addEventListener("select", this._onSelect, false);
@@ -383,118 +435,162 @@ RequestsMenuView.prototype = Heritage.ex
     window.addEventListener("resize", this._onResize, false);
 
     this.requestsMenuSortEvent = getKeyWithEvent(this.sortBy.bind(this));
     this.requestsMenuFilterEvent = getKeyWithEvent(this.filterOn.bind(this));
     this.reqeustsMenuClearEvent = this.clear.bind(this);
     this._onContextShowing = this._onContextShowing.bind(this);
     this._onContextNewTabCommand = this.openRequestInTab.bind(this);
     this._onContextCopyUrlCommand = this.copyUrl.bind(this);
-    this._onContextCopyImageAsDataUriCommand = this.copyImageAsDataUri.bind(this);
+    this._onContextCopyImageAsDataUriCommand =
+      this.copyImageAsDataUri.bind(this);
     this._onContextCopyResponseCommand = this.copyResponse.bind(this);
     this._onContextResendCommand = this.cloneSelectedRequest.bind(this);
     this._onContextToggleRawHeadersCommand = this.toggleRawHeaders.bind(this);
     this._onContextPerfCommand = () => NetMonitorView.toggleFrontendMode();
     this._onReloadCommand = () => NetMonitorView.reloadPage();
-    this._flushRequestsTask = new DeferredTask(this._flushRequests, REQUESTS_REFRESH_RATE);
+    this._flushRequestsTask = new DeferredTask(this._flushRequests,
+      REQUESTS_REFRESH_RATE);
 
     this.sendCustomRequestEvent = this.sendCustomRequest.bind(this);
     this.closeCustomRequestEvent = this.closeCustomRequest.bind(this);
     this.cloneSelectedRequestEvent = this.cloneSelectedRequest.bind(this);
     this.toggleRawHeadersEvent = this.toggleRawHeaders.bind(this);
 
-    this.requestsFreetextFilterEvent = this.requestsFreetextFilterEvent.bind(this);
+    this.requestsFreetextFilterEvent =
+      this.requestsFreetextFilterEvent.bind(this);
     this.reFilterRequests = this.reFilterRequests.bind(this);
 
     this.freetextFilterBox = $("#requests-menu-filter-freetext-text");
-    this.freetextFilterBox.addEventListener("input", this.requestsFreetextFilterEvent, false);
-    this.freetextFilterBox.addEventListener("command", this.requestsFreetextFilterEvent, false);
-
-    $("#toolbar-labels").addEventListener("click", this.requestsMenuSortEvent, false);
-    $("#requests-menu-filter-buttons").addEventListener("click", this.requestsMenuFilterEvent, false);
-    $("#requests-menu-clear-button").addEventListener("click", this.reqeustsMenuClearEvent, false);
-    $("#network-request-popup").addEventListener("popupshowing", this._onContextShowing, false);
-    $("#request-menu-context-newtab").addEventListener("command", this._onContextNewTabCommand, false);
-    $("#request-menu-context-copy-url").addEventListener("command", this._onContextCopyUrlCommand, false);
-    $("#request-menu-context-copy-response").addEventListener("command", this._onContextCopyResponseCommand, false);
-    $("#request-menu-context-copy-image-as-data-uri").addEventListener("command", this._onContextCopyImageAsDataUriCommand, false);
-    $("#toggle-raw-headers").addEventListener("click", this.toggleRawHeadersEvent, false);
+    this.freetextFilterBox.addEventListener("input",
+      this.requestsFreetextFilterEvent, false);
+    this.freetextFilterBox.addEventListener("command",
+      this.requestsFreetextFilterEvent, false);
+
+    $("#toolbar-labels").addEventListener("click",
+      this.requestsMenuSortEvent, false);
+    $("#requests-menu-filter-buttons").addEventListener("click",
+      this.requestsMenuFilterEvent, false);
+    $("#requests-menu-clear-button").addEventListener("click",
+      this.reqeustsMenuClearEvent, false);
+    $("#network-request-popup").addEventListener("popupshowing",
+      this._onContextShowing, false);
+    $("#request-menu-context-newtab").addEventListener("command",
+      this._onContextNewTabCommand, false);
+    $("#request-menu-context-copy-url").addEventListener("command",
+      this._onContextCopyUrlCommand, false);
+    $("#request-menu-context-copy-response").addEventListener("command",
+      this._onContextCopyResponseCommand, false);
+    $("#request-menu-context-copy-image-as-data-uri").addEventListener(
+      "command", this._onContextCopyImageAsDataUriCommand, false);
+    $("#toggle-raw-headers").addEventListener("click",
+      this.toggleRawHeadersEvent, false);
 
     window.once("connected", this._onConnect.bind(this));
   },
 
   _onConnect: function() {
-    $("#requests-menu-reload-notice-button").addEventListener("command", this._onReloadCommand, false);
+    $("#requests-menu-reload-notice-button").addEventListener("command",
+      this._onReloadCommand, false);
 
     if (NetMonitorController.supportsCustomRequest) {
-      $("#request-menu-context-resend").addEventListener("command", this._onContextResendCommand, false);
-      $("#custom-request-send-button").addEventListener("click", this.sendCustomRequestEvent, false);
-      $("#custom-request-close-button").addEventListener("click", this.closeCustomRequestEvent, false);
-      $("#headers-summary-resend").addEventListener("click", this.cloneSelectedRequestEvent, false);
+      $("#request-menu-context-resend").addEventListener("command",
+        this._onContextResendCommand, false);
+      $("#custom-request-send-button").addEventListener("click",
+        this.sendCustomRequestEvent, false);
+      $("#custom-request-close-button").addEventListener("click",
+        this.closeCustomRequestEvent, false);
+      $("#headers-summary-resend").addEventListener("click",
+        this.cloneSelectedRequestEvent, false);
     } else {
       $("#request-menu-context-resend").hidden = true;
       $("#headers-summary-resend").hidden = true;
     }
 
     if (NetMonitorController.supportsPerfStats) {
-      $("#request-menu-context-perf").addEventListener("command", this._onContextPerfCommand, false);
-      $("#requests-menu-perf-notice-button").addEventListener("command", this._onContextPerfCommand, false);
-      $("#requests-menu-network-summary-button").addEventListener("command", this._onContextPerfCommand, false);
-      $("#network-statistics-back-button").addEventListener("command", this._onContextPerfCommand, false);
+      $("#request-menu-context-perf").addEventListener("command",
+        this._onContextPerfCommand, false);
+      $("#requests-menu-perf-notice-button").addEventListener("command",
+        this._onContextPerfCommand, false);
+      $("#requests-menu-network-summary-button").addEventListener("command",
+        this._onContextPerfCommand, false);
+      $("#network-statistics-back-button").addEventListener("command",
+        this._onContextPerfCommand, false);
     } else {
       $("#notice-perf-message").hidden = true;
       $("#request-menu-context-perf").hidden = true;
       $("#requests-menu-network-summary-button").hidden = true;
     }
 
     if (!NetMonitorController.supportsTransferredResponseSize) {
       $("#requests-menu-transferred-header-box").hidden = true;
-      $("#requests-menu-item-template .requests-menu-transferred").hidden = true;
+      $("#requests-menu-item-template .requests-menu-transferred")
+        .hidden = true;
     }
   },
 
   /**
    * Destruction function, called when the network monitor is closed.
    */
   destroy: function() {
     dumpn("Destroying the SourcesView");
 
     Prefs.filters = this._activeFilters;
 
     this.widget.removeEventListener("select", this._onSelect, false);
     this.widget.removeEventListener("swap", this._onSwap, false);
     this._splitter.removeEventListener("mousemove", this._onResize, false);
     window.removeEventListener("resize", this._onResize, false);
 
-    $("#toolbar-labels").removeEventListener("click", this.requestsMenuSortEvent, false);
-    $("#requests-menu-filter-buttons").removeEventListener("click", this.requestsMenuFilterEvent, false);
-    $("#requests-menu-clear-button").removeEventListener("click", this.reqeustsMenuClearEvent, false);
-    this.freetextFilterBox.removeEventListener("input", this.requestsFreetextFilterEvent, false);
-    this.freetextFilterBox.removeEventListener("command", this.requestsFreetextFilterEvent, false);
+    $("#toolbar-labels").removeEventListener("click",
+      this.requestsMenuSortEvent, false);
+    $("#requests-menu-filter-buttons").removeEventListener("click",
+      this.requestsMenuFilterEvent, false);
+    $("#requests-menu-clear-button").removeEventListener("click",
+      this.reqeustsMenuClearEvent, false);
+    this.freetextFilterBox.removeEventListener("input",
+      this.requestsFreetextFilterEvent, false);
+    this.freetextFilterBox.removeEventListener("command",
+      this.requestsFreetextFilterEvent, false);
 
     this.userInputTimer.cancel();
     this._flushRequestsTask.disarm();
 
-    $("#network-request-popup").removeEventListener("popupshowing", this._onContextShowing, false);
-    $("#request-menu-context-newtab").removeEventListener("command", this._onContextNewTabCommand, false);
-    $("#request-menu-context-copy-url").removeEventListener("command", this._onContextCopyUrlCommand, false);
-    $("#request-menu-context-copy-response").removeEventListener("command", this._onContextCopyResponseCommand, false);
-    $("#request-menu-context-copy-image-as-data-uri").removeEventListener("command", this._onContextCopyImageAsDataUriCommand, false);
-    $("#request-menu-context-resend").removeEventListener("command", this._onContextResendCommand, false);
-    $("#request-menu-context-perf").removeEventListener("command", this._onContextPerfCommand, false);
-
-    $("#requests-menu-reload-notice-button").removeEventListener("command", this._onReloadCommand, false);
-    $("#requests-menu-perf-notice-button").removeEventListener("command", this._onContextPerfCommand, false);
-    $("#requests-menu-network-summary-button").removeEventListener("command", this._onContextPerfCommand, false);
-    $("#network-statistics-back-button").removeEventListener("command", this._onContextPerfCommand, false);
-
-    $("#custom-request-send-button").removeEventListener("click", this.sendCustomRequestEvent, false);
-    $("#custom-request-close-button").removeEventListener("click", this.closeCustomRequestEvent, false);
-    $("#headers-summary-resend").removeEventListener("click", this.cloneSelectedRequestEvent, false);
-    $("#toggle-raw-headers").removeEventListener("click", this.toggleRawHeadersEvent, false);
+    $("#network-request-popup").removeEventListener("popupshowing",
+      this._onContextShowing, false);
+    $("#request-menu-context-newtab").removeEventListener("command",
+      this._onContextNewTabCommand, false);
+    $("#request-menu-context-copy-url").removeEventListener("command",
+      this._onContextCopyUrlCommand, false);
+    $("#request-menu-context-copy-response").removeEventListener("command",
+      this._onContextCopyResponseCommand, false);
+    $("#request-menu-context-copy-image-as-data-uri").removeEventListener(
+      "command", this._onContextCopyImageAsDataUriCommand, false);
+    $("#request-menu-context-resend").removeEventListener("command",
+      this._onContextResendCommand, false);
+    $("#request-menu-context-perf").removeEventListener("command",
+      this._onContextPerfCommand, false);
+
+    $("#requests-menu-reload-notice-button").removeEventListener("command",
+      this._onReloadCommand, false);
+    $("#requests-menu-perf-notice-button").removeEventListener("command",
+      this._onContextPerfCommand, false);
+    $("#requests-menu-network-summary-button").removeEventListener("command",
+      this._onContextPerfCommand, false);
+    $("#network-statistics-back-button").removeEventListener("command",
+      this._onContextPerfCommand, false);
+
+    $("#custom-request-send-button").removeEventListener("click",
+      this.sendCustomRequestEvent, false);
+    $("#custom-request-close-button").removeEventListener("click",
+      this.closeCustomRequestEvent, false);
+    $("#headers-summary-resend").removeEventListener("click",
+      this.cloneSelectedRequestEvent, false);
+    $("#toggle-raw-headers").removeEventListener("click",
+      this.toggleRawHeadersEvent, false);
   },
 
   /**
    * Resets this container (removes all the networking information).
    */
   reset: function() {
     this.empty();
     this._addQueue = [];
@@ -517,34 +613,36 @@ RequestsMenuView.prototype = Heritage.ex
     if (!value) {
       this._flushRequests();
     }
   },
 
   /**
    * Adds a network request to this container.
    *
-   * @param string aId
+   * @param string id
    *        An identifier coming from the network monitor controller.
-   * @param string aStartedDateTime
+   * @param string startedDateTime
    *        A string representation of when the request was started, which
    *        can be parsed by Date (for example "2012-09-17T19:50:03.699Z").
-   * @param string aMethod
+   * @param string method
    *        Specifies the request method (e.g. "GET", "POST", etc.)
-   * @param string aUrl
+   * @param string url
    *        Specifies the request's url.
-   * @param boolean aIsXHR
+   * @param boolean isXHR
    *        True if this request was initiated via XHR.
-   * @param boolean aFromCache
+   * @param boolean fromCache
    *        Indicates if the result came from the browser cache
-   * @param boolean aFromServiceWorker
+   * @param boolean fromServiceWorker
    *        Indicates if the request has been intercepted by a Service Worker
    */
-  addRequest: function(aId, aStartedDateTime, aMethod, aUrl, aIsXHR, aFromCache, aFromServiceWorker) {
-    this._addQueue.push([aId, aStartedDateTime, aMethod, aUrl, aIsXHR, aFromCache, aFromServiceWorker]);
+  addRequest: function(id, startedDateTime, method, url, isXHR, fromCache,
+    fromServiceWorker) {
+    this._addQueue.push([id, startedDateTime, method, url, isXHR, fromCache,
+      fromServiceWorker]);
 
     // Lazy updating is disabled in some tests.
     if (!this.lazyUpdate) {
       return void this._flushRequests();
     }
 
     this._flushRequestsTask.arm();
   },
@@ -562,67 +660,74 @@ RequestsMenuView.prototype = Heritage.ex
    * Copy the request url from the currently selected item.
    */
   copyUrl: function() {
     let selected = this.selectedItem.attachment;
     clipboardHelper.copyString(selected.url);
   },
 
   /**
-   * Copy the request url query string parameters from the currently selected item.
+   * Copy the request url query string parameters from the currently
+   * selected item.
    */
   copyUrlParams: function() {
     let selected = this.selectedItem.attachment;
     let params = NetworkHelper.nsIURL(selected.url).query.split("&");
     let string = params.join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n");
     clipboardHelper.copyString(string);
   },
 
   /**
    * Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a
    * POST request.
    *
-   * @param object aHeaders
+   * @param object headers
    *        The "requestHeaders".
-   * @param object aUploadHeaders
+   * @param object uploadHeaders
    *        The "requestHeadersFromUploadStream".
-   * @param object aPostData
+   * @param object postData
    *        The "requestPostData".
    * @return array
    *        A promise that is resolved with the extracted form data.
    */
-  _getFormDataSections: Task.async(function*(aHeaders, aUploadHeaders, aPostData) {
+  _getFormDataSections: Task.async(function*(headers, uploadHeaders, postData) {
     let formDataSections = [];
 
-    let { headers: requestHeaders } = aHeaders;
-    let { headers: payloadHeaders } = aUploadHeaders;
+    let { headers: requestHeaders } = headers;
+    let { headers: payloadHeaders } = uploadHeaders;
     let allHeaders = [...payloadHeaders, ...requestHeaders];
 
-    let contentTypeHeader = allHeaders.find(e => e.name.toLowerCase() == "content-type");
-    let contentTypeLongString = contentTypeHeader ? contentTypeHeader.value : "";
+    let contentTypeHeader = allHeaders.find(e => {
+      return e.name.toLowerCase() == "content-type";
+    });
+
+    let contentTypeLongString = contentTypeHeader ?
+      contentTypeHeader.value : "";
+
     let contentType = yield gNetwork.getString(contentTypeLongString);
 
     if (contentType.includes("x-www-form-urlencoded")) {
-      let postDataLongString = aPostData.postData.text;
-      let postData = yield gNetwork.getString(postDataLongString);
-
-      for (let section of postData.split(/\r\n|\r|\n/)) {
+      let postDataLongString = postData.postData.text;
+      let text = yield gNetwork.getString(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);
         }
       }
     }
 
     return formDataSections;
   }),
 
   /**
-   * Copy the request form data parameters (or raw payload) from the currently selected item.
+   * Copy the request form data parameters (or raw payload) from
+   * the currently selected item.
    */
   copyPostData: Task.async(function*() {
     let selected = this.selectedItem.attachment;
     let view = this;
 
     // Try to extract any form data parameters.
     let formDataSections = yield view._getFormDataSections(
       selected.requestHeaders,
@@ -739,31 +844,31 @@ RequestsMenuView.prototype = Heritage.ex
 
   /**
    * Copy image as data uri.
    */
   copyImageAsDataUri: function() {
     let selected = this.selectedItem.attachment;
     let { mimeType, text, encoding } = selected.responseContent.content;
 
-    gNetwork.getString(text).then(aString => {
-      let data = formDataURI(mimeType, encoding, aString);
+    gNetwork.getString(text).then(string => {
+      let data = formDataURI(mimeType, encoding, string);
       clipboardHelper.copyString(data);
     });
   },
 
   /**
    * Copy response data as a string.
    */
   copyResponse: function() {
     let selected = this.selectedItem.attachment;
     let text = selected.responseContent.content.text;
 
-    gNetwork.getString(text).then(aString => {
-      clipboardHelper.copyString(aString);
+    gNetwork.getString(text).then(string => {
+      clipboardHelper.copyString(string);
     });
   },
 
   /**
    * Create a new custom request form populated with the data from
    * the currently selected request.
    */
   cloneSelectedRequest: function() {
@@ -796,18 +901,18 @@ RequestsMenuView.prototype = Heritage.ex
     };
     if (selected.requestHeaders) {
       data.headers = selected.requestHeaders.headers;
     }
     if (selected.requestPostData) {
       data.body = selected.requestPostData.postData.text;
     }
 
-    NetMonitorController.webConsoleClient.sendHTTPRequest(data, aResponse => {
-      let id = aResponse.eventActor.actor;
+    NetMonitorController.webConsoleClient.sendHTTPRequest(data, response => {
+      let id = response.eventActor.actor;
       this._preferredItemId = id;
     });
 
     this.closeCustomRequest();
   },
 
   /**
    * Remove the currently selected custom request.
@@ -847,133 +952,133 @@ RequestsMenuView.prototype = Heritage.ex
     this._currentFreetextFilter = this.freetextFilterBox.value || "";
 
     if (this._currentFreetextFilter.length === 0) {
       this.freetextFilterBox.removeAttribute("filled");
     } else {
       this.freetextFilterBox.setAttribute("filled", true);
     }
 
-    this.userInputTimer.initWithCallback(this.reFilterRequests, FREETEXT_FILTER_SEARCH_DELAY, Ci.nsITimer.TYPE_ONE_SHOT);
+    this.userInputTimer.initWithCallback(this.reFilterRequests,
+      FREETEXT_FILTER_SEARCH_DELAY, Ci.nsITimer.TYPE_ONE_SHOT);
   },
 
   /**
    * Refreshes the view contents with the newly selected filters
    */
   reFilterRequests: function() {
     this.filterContents(this._filterPredicate);
     this.refreshSummary();
     this.refreshZebra();
   },
 
   /**
    * Filters all network requests in this container by a specified type.
    *
-   * @param string aType
+   * @param string type
    *        Either "all", "html", "css", "js", "xhr", "fonts", "images", "media"
    *        "flash" or "other".
    */
-  filterOn: function(aType = "all") {
-    if (aType === "all") {
+  filterOn: function(type = "all") {
+    if (type === "all") {
       // The filter "all" is special as it doesn't toggle.
       // - If some filters are selected and 'all' is clicked, the previously
       //   selected filters will be disabled and 'all' is the only active one.
       // - If 'all' is already selected, do nothing.
       if (this._activeFilters.indexOf("all") !== -1) {
         return;
       }
 
       // Uncheck all other filters and select 'all'. Must create a copy as
       // _disableFilter removes the filters from the list while it's being
       // iterated. 'all' will be enabled automatically by _disableFilter once
       // the last filter is disabled.
       this._activeFilters.slice().forEach(this._disableFilter, this);
-    }
-    else if (this._activeFilters.indexOf(aType) === -1) {
-      this._enableFilter(aType);
-    }
-    else {
-      this._disableFilter(aType);
+    } else if (this._activeFilters.indexOf(type) === -1) {
+      this._enableFilter(type);
+    } else {
+      this._disableFilter(type);
     }
 
     this.reFilterRequests();
   },
 
   /**
    * Same as `filterOn`, except that it only allows a single type exclusively.
    *
-   * @param string aType
+   * @param string type
    *        @see RequestsMenuView.prototype.fitlerOn
    */
-  filterOnlyOn: function(aType = "all") {
+  filterOnlyOn: function(type = "all") {
     this._activeFilters.slice().forEach(this._disableFilter, this);
-    this.filterOn(aType);
+    this.filterOn(type);
   },
 
   /**
    * Disables the given filter, its button and toggles 'all' on if the filter to
    * be disabled is the last one active.
    *
-   * @param string aType
+   * @param string type
    *        Either "all", "html", "css", "js", "xhr", "fonts", "images", "media"
    *        "flash" or "other".
    */
-  _disableFilter: function (aType) {
+  _disableFilter: function(type) {
     // Remove the filter from list of active filters.
-    this._activeFilters.splice(this._activeFilters.indexOf(aType), 1);
+    this._activeFilters.splice(this._activeFilters.indexOf(type), 1);
 
     // Remove the checked status from the filter.
-    let target = $("#requests-menu-filter-" + aType + "-button");
+    let target = $("#requests-menu-filter-" + type + "-button");
     target.removeAttribute("checked");
 
     // Check if the filter disabled was the last one. If so, toggle all on.
     if (this._activeFilters.length === 0) {
       this._enableFilter("all");
     }
   },
 
   /**
    * Enables the given filter, its button and toggles 'all' off if the filter to
    * be enabled is the first one active.
    *
-   * @param string aType
+   * @param string type
    *        Either "all", "html", "css", "js", "xhr", "fonts", "images", "media"
    *        "flash" or "other".
    */
-  _enableFilter: function (aType) {
+  _enableFilter: function(type) {
     // Make sure this is a valid filter type.
-    if (Object.keys(this._allFilterPredicates).indexOf(aType) == -1) {
+    if (Object.keys(this._allFilterPredicates).indexOf(type) == -1) {
       return;
     }
 
     // Add the filter to the list of active filters.
-    this._activeFilters.push(aType);
+    this._activeFilters.push(type);
 
     // Add the checked status to the filter button.
-    let target = $("#requests-menu-filter-" + aType + "-button");
+    let target = $("#requests-menu-filter-" + type + "-button");
     target.setAttribute("checked", true);
 
     // Check if 'all' was selected before. If so, disable it.
-    if (aType !== "all" && this._activeFilters.indexOf("all") !== -1) {
+    if (type !== "all" && this._activeFilters.indexOf("all") !== -1) {
       this._disableFilter("all");
     }
   },
 
   /**
    * Returns a predicate that can be used to test if a request matches any of
    * the active filters.
    */
   get _filterPredicate() {
     let filterPredicates = this._allFilterPredicates;
     let currentFreetextFilter = this._currentFreetextFilter;
 
     return requestItem => {
       return this._activeFilters.some(filterName => {
         return filterPredicates[filterName].call(this, requestItem) &&
-                filterPredicates["freetext"].call(this, requestItem, currentFreetextFilter);
+          filterPredicates.freetext.call(this, requestItem,
+            currentFreetextFilter);
       });
     };
   },
 
   /**
    * Returns an object with all the filter predicates as [key: function] pairs.
    */
   get _allFilterPredicates() {
@@ -990,47 +1095,49 @@ RequestsMenuView.prototype = Heritage.ex
       other: this.isOther,
       freetext: this.isFreetextMatch
     };
   },
 
   /**
    * Sorts all network requests in this container by a specified detail.
    *
-   * @param string aType
+   * @param string type
    *        Either "status", "method", "file", "domain", "type", "transferred",
    *        "size" or "waterfall".
    */
-  sortBy: function(aType = "waterfall") {
-    let target = $("#requests-menu-" + aType + "-button");
+  sortBy: function(type = "waterfall") {
+    let target = $("#requests-menu-" + type + "-button");
     let headers = document.querySelectorAll(".requests-menu-header-button");
 
     for (let header of headers) {
       if (header != target) {
         header.removeAttribute("sorted");
         header.removeAttribute("tooltiptext");
         header.parentNode.removeAttribute("active");
       }
     }
 
     let direction = "";
     if (target) {
       if (target.getAttribute("sorted") == "ascending") {
         target.setAttribute("sorted", direction = "descending");
-        target.setAttribute("tooltiptext", L10N.getStr("networkMenu.sortedDesc"));
+        target.setAttribute("tooltiptext",
+          L10N.getStr("networkMenu.sortedDesc"));
       } else {
         target.setAttribute("sorted", direction = "ascending");
-        target.setAttribute("tooltiptext", L10N.getStr("networkMenu.sortedAsc"));
+        target.setAttribute("tooltiptext",
+          L10N.getStr("networkMenu.sortedAsc"));
       }
       // Used to style the next column.
       target.parentNode.setAttribute("active", "true");
     }
 
     // Sort by whatever was requested.
-    switch (aType) {
+    switch (type) {
       case "status":
         if (direction == "ascending") {
           this.sortContents(this._byStatus);
         } else {
           this.sortContents((a, b) => !this._byStatus(a, b));
         }
         break;
       case "method":
@@ -1098,17 +1205,17 @@ RequestsMenuView.prototype = Heritage.ex
 
     this.empty();
     this.refreshSummary();
   },
 
   /**
    * Predicates used when filtering items.
    *
-   * @param object aItem
+   * @param object item
    *        The filtered item.
    * @return boolean
    *         True if the item should be visible, false otherwise.
    */
   isHtml: function({ attachment: { mimeType } }) {
     return mimeType && mimeType.includes("/html");
   },
 
@@ -1173,20 +1280,20 @@ RequestsMenuView.prototype = Heritage.ex
   isFreetextMatch: function({ attachment: { url } }, text) {
     let lowerCaseUrl = url.toLowerCase();
     let lowerCaseText = text.toLowerCase();
     let textLength = text.length;
     // Support negative filtering
     if (text.startsWith("-") && textLength > 1) {
       lowerCaseText = lowerCaseText.substring(1, textLength);
       return !lowerCaseUrl.includes(lowerCaseText);
-    } else {
-      //no text is a positive match
-      return !text || lowerCaseUrl.includes(lowerCaseText);
     }
+
+    // no text is a positive match
+    return !text || lowerCaseUrl.includes(lowerCaseText);
   },
 
   /**
    * Predicates used when sorting items.
    *
    * @param object aFirst
    *        The first item used in the comparison.
    * @param object aSecond
@@ -1225,17 +1332,19 @@ RequestsMenuView.prototype = Heritage.ex
     let secondDomain = this._getUriHostPort(second.url).toLowerCase();
     return firstDomain == secondDomain
       ? first.startedMillis > second.startedMillis
       : firstDomain > secondDomain;
   },
 
   _byType: function({ attachment: first }, { attachment: second }) {
     let firstType = this._getAbbreviatedMimeType(first.mimeType).toLowerCase();
-    let secondType = this._getAbbreviatedMimeType(second.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;
   },
@@ -1257,21 +1366,25 @@ RequestsMenuView.prototype = Heritage.ex
     }
 
     let totalBytes = this._getTotalBytesOfRequests(visibleItems);
     let totalMillis =
       this._getNewestRequest(visibleItems).attachment.endedMillis -
       this._getOldestRequest(visibleItems).attachment.startedMillis;
 
     // https://developer.mozilla.org/en-US/docs/Localization_and_Plurals
-    let str = PluralForm.get(visibleRequestsCount, L10N.getStr("networkMenu.summary"));
+    let str = PluralForm.get(visibleRequestsCount,
+      L10N.getStr("networkMenu.summary"));
+
     this._summary.setAttribute("label", str
       .replace("#1", visibleRequestsCount)
-      .replace("#2", L10N.numberWithDecimals((totalBytes || 0) / 1024, CONTENT_SIZE_DECIMALS))
-      .replace("#3", L10N.numberWithDecimals((totalMillis || 0) / 1000, REQUEST_TIME_DECIMALS))
+      .replace("#2", L10N.numberWithDecimals((totalBytes || 0) / 1024,
+        CONTENT_SIZE_DECIMALS))
+      .replace("#3", L10N.numberWithDecimals((totalMillis || 0) / 1000,
+        REQUEST_TIME_DECIMALS))
     );
   },
 
   /**
    * Adds odd/even attributes to all the visible items in this container.
    */
   refreshZebra: function() {
     let visibleItems = this.visibleItems;
@@ -1288,50 +1401,50 @@ RequestsMenuView.prototype = Heritage.ex
         requestTarget.removeAttribute("even");
       }
     }
   },
 
   /**
    * Refreshes the toggling anchor for the specified item's tooltip.
    *
-   * @param object aItem
+   * @param object item
    *        The network request item in this container.
    */
-  refreshTooltip: function(aItem) {
-    let tooltip = aItem.attachment.tooltip;
+  refreshTooltip: function(item) {
+    let tooltip = item.attachment.tooltip;
     tooltip.hide();
-    tooltip.startTogglingOnHover(aItem.target, this._onHover);
+    tooltip.startTogglingOnHover(item.target, this._onHover);
     tooltip.defaultPosition = REQUESTS_TOOLTIP_POSITION;
   },
 
   /**
    * Attaches security icon click listener for the given request menu item.
    *
    * @param object item
    *        The network request item to attach the listener to.
    */
-  attachSecurityIconClickListener: function ({ target }) {
+  attachSecurityIconClickListener: function({ target }) {
     let icon = $(".requests-security-state-icon", target);
     icon.addEventListener("click", this._onSecurityIconClick);
   },
 
   /**
    * Schedules adding additional information to a network request.
    *
-   * @param string aId
+   * @param string id
    *        An identifier coming from the network monitor controller.
-   * @param object aData
+   * @param object data
    *        An object containing several { key: value } tuples of network info.
    *        Supported keys are "httpVersion", "status", "statusText" etc.
-   * @param function aCallback
+   * @param function callback
    *        A function to call once the request has been updated in the view.
    */
-  updateRequest: function(aId, aData, aCallback) {
-    this._updateQueue.push([aId, aData, aCallback]);
+  updateRequest: function(id, data, callback) {
+    this._updateQueue.push([id, data, callback]);
 
     // Lazy updating is disabled in some tests.
     if (!this.lazyUpdate) {
       return void this._flushRequests();
     }
 
     this._flushRequestsTask.arm();
   },
@@ -1343,17 +1456,18 @@ RequestsMenuView.prototype = Heritage.ex
     // Prevent displaying any updates received after the target closed.
     if (NetMonitorView._isDestroyed) {
       return;
     }
 
     let widget = NetMonitorView.RequestsMenu.widget;
     let isScrolledToBottom = widget.isScrolledToBottom();
 
-    for (let [id, startedDateTime, method, url, isXHR, fromCache, fromServiceWorker] of this._addQueue) {
+    for (let [id, startedDateTime, method, url, isXHR, fromCache,
+      fromServiceWorker] of this._addQueue) {
       // Convert the received date/time string to a unix timestamp.
       let unixTime = Date.parse(startedDateTime);
 
       // Create the element node for the network request item.
       let menuView = this._createMenuView(method, url);
 
       // Remember the first and last event boundaries.
       this._registerFirstRequestStart(unixTime);
@@ -1368,17 +1482,17 @@ RequestsMenuView.prototype = Heritage.ex
           url: url,
           isXHR: isXHR,
           fromCache: fromCache,
           fromServiceWorker: fromServiceWorker
         }
       });
 
       // Create a tooltip for the newly appended network request item.
-      let requestTooltip = requestItem.attachment.tooltip = new Tooltip(document, {
+      requestItem.attachment.tooltip = new Tooltip(document, {
         closeOnEvents: [{
           emitter: $("#requests-menu-contents"),
           event: "scroll",
           useCapture: true
         }]
       });
 
       this.refreshTooltip(requestItem);
@@ -1401,141 +1515,144 @@ RequestsMenuView.prototype = Heritage.ex
       if (!requestItem) {
         // Packet corresponds to a dead request item, target navigated.
         continue;
       }
 
       // Each information packet may contain several { key: value } tuples of
       // network info, so update the view based on each one.
       for (let key in data) {
-        let value = data[key];
-        if (value === undefined) {
+        let val = data[key];
+        if (val === undefined) {
           // The information in the packet is empty, it can be safely ignored.
           continue;
         }
 
         switch (key) {
           case "requestHeaders":
-            requestItem.attachment.requestHeaders = value;
+            requestItem.attachment.requestHeaders = val;
             break;
           case "requestCookies":
-            requestItem.attachment.requestCookies = value;
+            requestItem.attachment.requestCookies = val;
             break;
           case "requestPostData":
             // Search the POST data upload stream for request headers and add
             // them to a separate store, different from the classic headers.
             // XXX: Be really careful here! We're creating a function inside
             // a loop, so remember the actual request item we want to modify.
             let currentItem = requestItem;
             let currentStore = { headers: [], headersSize: 0 };
 
             Task.spawn(function*() {
-              let postData = yield gNetwork.getString(value.postData.text);
-              let payloadHeaders = CurlUtils.getHeadersFromMultipartText(postData);
+              let postData = yield gNetwork.getString(val.postData.text);
+              let payloadHeaders = CurlUtils.getHeadersFromMultipartText(
+                postData);
 
               currentStore.headers = payloadHeaders;
               currentStore.headersSize = payloadHeaders.reduce(
-                (acc, { name, value }) => acc + name.length + value.length + 2, 0);
+                (acc, { name, value }) =>
+                  acc + name.length + value.length + 2, 0);
 
               // The `getString` promise is async, so we need to refresh the
               // information displayed in the network details pane again here.
               refreshNetworkDetailsPaneIfNecessary(currentItem);
             });
 
-            requestItem.attachment.requestPostData = value;
-            requestItem.attachment.requestHeadersFromUploadStream = currentStore;
+            requestItem.attachment.requestPostData = val;
+            requestItem.attachment.requestHeadersFromUploadStream =
+              currentStore;
             break;
           case "securityState":
-            requestItem.attachment.securityState = value;
-            this.updateMenuView(requestItem, key, value);
+            requestItem.attachment.securityState = val;
+            this.updateMenuView(requestItem, key, val);
             break;
           case "securityInfo":
-            requestItem.attachment.securityInfo = value;
+            requestItem.attachment.securityInfo = val;
             break;
           case "responseHeaders":
-            requestItem.attachment.responseHeaders = value;
+            requestItem.attachment.responseHeaders = val;
             break;
           case "responseCookies":
-            requestItem.attachment.responseCookies = value;
+            requestItem.attachment.responseCookies = val;
             break;
           case "httpVersion":
-            requestItem.attachment.httpVersion = value;
+            requestItem.attachment.httpVersion = val;
             break;
           case "remoteAddress":
-            requestItem.attachment.remoteAddress = value;
-            this.updateMenuView(requestItem, key, value);
+            requestItem.attachment.remoteAddress = val;
+            this.updateMenuView(requestItem, key, val);
             break;
           case "remotePort":
-            requestItem.attachment.remotePort = value;
+            requestItem.attachment.remotePort = val;
             break;
           case "status":
-            requestItem.attachment.status = value;
+            requestItem.attachment.status = val;
             this.updateMenuView(requestItem, key, {
-              status: value,
+              status: val,
               cached: requestItem.attachment.fromCache,
               serviceWorker: requestItem.attachment.fromServiceWorker
             });
             break;
           case "statusText":
-            requestItem.attachment.statusText = value;
+            requestItem.attachment.statusText = val;
             let text = (requestItem.attachment.status + " " +
                         requestItem.attachment.statusText);
-            if(requestItem.attachment.fromCache) {
+            if (requestItem.attachment.fromCache) {
               text += " (cached)";
-            } else if(requestItem.attachment.fromServiceWorker) {
+            } else if (requestItem.attachment.fromServiceWorker) {
               text += " (service worker)";
             }
 
             this.updateMenuView(requestItem, key, text);
             break;
           case "headersSize":
-            requestItem.attachment.headersSize = value;
+            requestItem.attachment.headersSize = val;
             break;
           case "contentSize":
-            requestItem.attachment.contentSize = value;
-            this.updateMenuView(requestItem, key, value);
+            requestItem.attachment.contentSize = val;
+            this.updateMenuView(requestItem, key, val);
             break;
           case "transferredSize":
-            if(requestItem.attachment.fromCache) {
+            if (requestItem.attachment.fromCache) {
               requestItem.attachment.transferredSize = 0;
-              this.updateMenuView(requestItem, key, 'cached');
-            }
-            else if(requestItem.attachment.fromServiceWorker) {
+              this.updateMenuView(requestItem, key, "cached");
+            } else if (requestItem.attachment.fromServiceWorker) {
               requestItem.attachment.transferredSize = 0;
-              this.updateMenuView(requestItem, key, 'service worker');
-            }
-            else {
-              requestItem.attachment.transferredSize = value;
-              this.updateMenuView(requestItem, key, value);
+              this.updateMenuView(requestItem, key, "service worker");
+            } else {
+              requestItem.attachment.transferredSize = val;
+              this.updateMenuView(requestItem, key, val);
             }
             break;
           case "mimeType":
-            requestItem.attachment.mimeType = value;
-            this.updateMenuView(requestItem, key, value);
+            requestItem.attachment.mimeType = val;
+            this.updateMenuView(requestItem, key, val);
             break;
           case "responseContent":
             // If there's no mime type available when the response content
             // is received, assume text/plain as a fallback.
             if (!requestItem.attachment.mimeType) {
               requestItem.attachment.mimeType = "text/plain";
               this.updateMenuView(requestItem, "mimeType", "text/plain");
             }
-            requestItem.attachment.responseContent = value;
-            this.updateMenuView(requestItem, key, value);
+            requestItem.attachment.responseContent = val;
+            this.updateMenuView(requestItem, key, val);
             break;
           case "totalTime":
-            requestItem.attachment.totalTime = value;
-            requestItem.attachment.endedMillis = requestItem.attachment.startedMillis + value;
-            this.updateMenuView(requestItem, key, value);
+            requestItem.attachment.totalTime = val;
+            requestItem.attachment.endedMillis =
+              requestItem.attachment.startedMillis + val;
+
+            this.updateMenuView(requestItem, key, val);
             this._registerLastRequestEnd(requestItem.attachment.endedMillis);
             break;
           case "eventTimings":
-            requestItem.attachment.eventTimings = value;
+            requestItem.attachment.eventTimings = val;
             this._createWaterfallView(
-              requestItem, value.timings,
+              requestItem, val.timings,
               requestItem.attachment.fromCache ||
               requestItem.attachment.fromServiceWorker
             );
             break;
         }
       }
       refreshNetworkDetailsPaneIfNecessary(requestItem);
 
@@ -1544,23 +1661,23 @@ RequestsMenuView.prototype = Heritage.ex
       }
     }
 
     /**
      * Refreshes the information displayed in the sidebar, in case this update
      * may have additional information about a request which isn't shown yet
      * in the network details pane.
      *
-     * @param object aRequestItem
+     * @param object requestItem
      *        The item to repopulate the sidebar with in case it's selected in
      *        this requests menu.
      */
-    function refreshNetworkDetailsPaneIfNecessary(aRequestItem) {
+    function refreshNetworkDetailsPaneIfNecessary(requestItem) {
       let selectedItem = NetMonitorView.RequestsMenu.selectedItem;
-      if (selectedItem == aRequestItem) {
+      if (selectedItem == requestItem) {
         NetMonitorView.NetworkDetails.populate(selectedItem.attachment);
       }
     }
 
     // We're done flushing all the requests, clear the update queue.
     this._updateQueue = [];
     this._addQueue = [];
 
@@ -1579,65 +1696,66 @@ RequestsMenuView.prototype = Heritage.ex
 
     // Rescale all the waterfalls so that everything is visible at once.
     this._flushWaterfallViews();
   },
 
   /**
    * Customization function for creating an item's UI.
    *
-   * @param string aMethod
+   * @param string method
    *        Specifies the request method (e.g. "GET", "POST", etc.)
-   * @param string aUrl
+   * @param string url
    *        Specifies the request's url.
    * @return nsIDOMNode
    *         The network request view.
    */
-  _createMenuView: function(aMethod, aUrl) {
+  _createMenuView: function(method, url) {
     let template = $("#requests-menu-item-template");
     let fragment = document.createDocumentFragment();
 
-    this.updateMenuView(template, 'method', aMethod);
-    this.updateMenuView(template, 'url', aUrl);
+    this.updateMenuView(template, "method", method);
+    this.updateMenuView(template, "url", url);
 
     // Flatten the DOM by removing one redundant box (the template container).
     for (let node of template.childNodes) {
       fragment.appendChild(node.cloneNode(true));
     }
 
     return fragment;
   },
 
   /**
    * Updates the information displayed in a network request item view.
    *
-   * @param object aItem
+   * @param object item
    *        The network request item in this container.
-   * @param string aKey
+   * @param string key
    *        The type of information that is to be updated.
-   * @param any aValue
+   * @param any value
    *        The new value to be shown.
    * @return object
    *         A promise that is resolved once the information is displayed.
    */
-  updateMenuView: Task.async(function*(aItem, aKey, aValue) {
-    let target = aItem.target || aItem;
-
-    switch (aKey) {
+  updateMenuView: Task.async(function*(item, key, value) {
+    let target = item.target || item;
+
+    switch (key) {
       case "method": {
         let node = $(".requests-menu-method", target);
-        node.setAttribute("value", aValue);
+        node.setAttribute("value", value);
         break;
       }
       case "url": {
         let uri;
         try {
-          uri = NetworkHelper.nsIURL(aValue);
-        } catch(e) {
-          break; // User input may not make a well-formed url yet.
+          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 unicodeUrl = NetworkHelper.convertToUnicode(unescape(uri.spec));
 
         let file = $(".requests-menu-file", target);
         file.setAttribute("value", nameWithQuery);
@@ -1667,200 +1785,204 @@ RequestsMenuView.prototype = Heritage.ex
           icon.setAttribute("tooltiptext", tooltip);
         }
 
         break;
       }
       case "remoteAddress":
         let domain = $(".requests-menu-domain", target);
         let tooltip = (domain.getAttribute("value") +
-                       (aValue ? " (" + aValue + ")" : ""));
+                       (value ? " (" + value + ")" : ""));
         domain.setAttribute("tooltiptext", tooltip);
         break;
       case "securityState": {
         let icon = $(".requests-security-state-icon", target);
-        this.attachSecurityIconClickListener(aItem);
+        this.attachSecurityIconClickListener(item);
 
         // Security icon for local hosts is set in the "url" branch
         if (icon.classList.contains("security-state-local")) {
           break;
         }
 
-        let tooltip = L10N.getStr("netmonitor.security.state." + aValue);
-        icon.classList.add("security-state-" + aValue);
-        icon.setAttribute("tooltiptext", tooltip);
+        let tooltip2 = L10N.getStr("netmonitor.security.state." + value);
+        icon.classList.add("security-state-" + value);
+        icon.setAttribute("tooltiptext", tooltip2);
         break;
       }
       case "status": {
         let node = $(".requests-menu-status-icon", target);
         let code;
-        if (aValue.cached) {
+        if (value.cached) {
           code = L10N.getStr("netmonitor.status.cached");
           code = "cached";
-        } else if (aValue.serviceWorker) {
+        } else if (value.serviceWorker) {
           code = L10N.getStr("netmonitor.status.serviceWorker");
         } else {
-          code = aValue.status;
+          code = value.status;
         }
         node.setAttribute("code", code);
         let codeNode = $(".requests-menu-status-code", target);
-        codeNode.setAttribute("value", aValue.status);
+        codeNode.setAttribute("value", value.status);
         break;
       }
       case "statusText": {
         let node = $(".requests-menu-status", target);
-        node.setAttribute("tooltiptext", aValue);
+        node.setAttribute("tooltiptext", value);
         break;
       }
       case "contentSize": {
-        let kb = aValue / 1024;
+        let kb = value / 1024;
         let size = L10N.numberWithDecimals(kb, CONTENT_SIZE_DECIMALS);
         let node = $(".requests-menu-size", target);
         let text = L10N.getFormatStr("networkMenu.sizeKB", size);
         node.setAttribute("value", text);
         node.setAttribute("tooltiptext", text);
         break;
       }
       case "transferredSize": {
         let node = $(".requests-menu-transferred", target);
 
         let text;
-        if (aValue === null) {
+        if (value === null) {
           text = L10N.getStr("networkMenu.sizeUnavailable");
-        }
-        else if(aValue === "cached") {
+        } else if (value === "cached") {
           text = L10N.getStr("networkMenu.sizeCached");
-          node.classList.add('theme-comment');
-        }
-        else if(aValue === "service worker") {
+          node.classList.add("theme-comment");
+        } else if (value === "service worker") {
           text = L10N.getStr("networkMenu.sizeServiceWorker");
-          node.classList.add('theme-comment');
-        }
-        else {
-          let kb = aValue / 1024;
+          node.classList.add("theme-comment");
+        } else {
+          let kb = value / 1024;
           let size = L10N.numberWithDecimals(kb, CONTENT_SIZE_DECIMALS);
           text = L10N.getFormatStr("networkMenu.sizeKB", size);
         }
 
         node.setAttribute("value", text);
         node.setAttribute("tooltiptext", text);
         break;
       }
       case "mimeType": {
-        let type = this._getAbbreviatedMimeType(aValue);
+        let type = this._getAbbreviatedMimeType(value);
         let node = $(".requests-menu-type", target);
         let text = CONTENT_MIME_TYPE_ABBREVIATIONS[type] || type;
         node.setAttribute("value", text);
-        node.setAttribute("tooltiptext", aValue);
+        node.setAttribute("tooltiptext", value);
         break;
       }
       case "responseContent": {
-        let { mimeType } = aItem.attachment;
+        let { mimeType } = item.attachment;
 
         if (mimeType.includes("image/")) {
-          let { text, encoding } = aValue.content;
+          let { text, encoding } = value.content;
           let responseBody = yield gNetwork.getString(text);
-          let node = $(".requests-menu-icon", aItem.target);
+          let node = $(".requests-menu-icon", item.target);
           node.src = formDataURI(mimeType, encoding, responseBody);
           node.setAttribute("type", "thumbnail");
           node.removeAttribute("hidden");
 
           window.emit(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED);
         }
         break;
       }
       case "totalTime": {
         let node = $(".requests-menu-timings-total", target);
-        let text = L10N.getFormatStr("networkMenu.totalMS", aValue); // integer
+
+        // integer
+        let text = L10N.getFormatStr("networkMenu.totalMS", value);
         node.setAttribute("value", text);
         node.setAttribute("tooltiptext", text);
         break;
       }
     }
   }),
 
   /**
-   * Creates a waterfall representing timing information in a network request item view.
+   * Creates a waterfall representing timing information in a network
+   * request item view.
    *
-   * @param object aItem
+   * @param object item
    *        The network request item in this container.
-   * @param object aTimings
+   * @param object timings
    *        An object containing timing information.
-   * @param boolean aFromCache
-   *        Indicates if the result came from the browser cache or a service worker
+   * @param boolean fromCache
+   *        Indicates if the result came from the browser cache or
+   *        a service worker
    */
-  _createWaterfallView: function(aItem, aTimings, aFromCache) {
-    let { target, attachment } = aItem;
+  _createWaterfallView: function(item, timings, fromCache) {
+    let { target } = item;
     let sections = ["dns", "connect", "send", "wait", "receive"];
     // Skipping "blocked" because it doesn't work yet.
 
     let timingsNode = $(".requests-menu-timings", target);
     let timingsTotal = $(".requests-menu-timings-total", timingsNode);
 
-    if(aFromCache) {
-      timingsTotal.style.display = 'none';
+    if (fromCache) {
+      timingsTotal.style.display = "none";
       return;
     }
 
     // Add a set of boxes representing timing information.
     for (let key of sections) {
-      let width = aTimings[key];
+      let width = timings[key];
 
       // Don't render anything if it surely won't be visible.
       // One millisecond == one unscaled pixel.
       if (width > 0) {
         let timingBox = document.createElement("hbox");
         timingBox.className = "requests-menu-timings-box " + key;
         timingBox.setAttribute("width", width);
         timingsNode.insertBefore(timingBox, timingsTotal);
       }
     }
   },
 
   /**
    * Rescales and redraws all the waterfall views in this container.
    *
-   * @param boolean aReset
+   * @param boolean reset
    *        True if this container's width was changed.
    */
-  _flushWaterfallViews: function(aReset) {
+  _flushWaterfallViews: function(reset) {
     // Don't paint things while the waterfall view isn't even visible,
     // or there are no items added to this container.
-    if (NetMonitorView.currentFrontendMode != "network-inspector-view" || !this.itemCount) {
+    if (NetMonitorView.currentFrontendMode !=
+      "network-inspector-view" || !this.itemCount) {
       return;
     }
 
     // To avoid expensive operations like getBoundingClientRect() and
     // rebuilding the waterfall background each time a new request comes in,
     // stuff is cached. However, in certain scenarios like when the window
     // is resized, this needs to be invalidated.
-    if (aReset) {
+    if (reset) {
       this._cachedWaterfallWidth = 0;
     }
 
     // Determine the scaling to be applied to all the waterfalls so that
     // everything is visible at once. One millisecond == one unscaled pixel.
     let availableWidth = this._waterfallWidth - REQUESTS_WATERFALL_SAFE_BOUNDS;
-    let longestWidth = this._lastRequestEndedMillis - this._firstRequestStartedMillis;
+    let longestWidth = this._lastRequestEndedMillis -
+      this._firstRequestStartedMillis;
     let scale = Math.min(Math.max(availableWidth / longestWidth, EPSILON), 1);
 
     // Redraw and set the canvas background for each waterfall view.
     this._showWaterfallDivisionLabels(scale);
     this._drawWaterfallBackground(scale);
 
     // Apply CSS transforms to each waterfall in this container totalTime
     // accurately translate and resize as needed.
     for (let { target, attachment } of this) {
       let timingsNode = $(".requests-menu-timings", target);
       let totalNode = $(".requests-menu-timings-total", target);
       let direction = window.isRTL ? -1 : 1;
 
       // Render the timing information at a specific horizontal translation
       // based on the delta to the first monitored event network.
-      let translateX = "translateX(" + (direction * attachment.startedDeltaMillis) + "px)";
+      let translateX = "translateX(" + (direction *
+        attachment.startedDeltaMillis) + "px)";
 
       // Based on the total time passed until the last request, rescale
       // all the waterfalls to a reasonable size.
       let scaleX = "scaleX(" + scale + ")";
 
       // Certain nodes should not be scaled, even if they're children of
       // another scaled node. In this case, apply a reversed transformation.
       let revScaleX = "scaleX(" + (1 / scale) + ")";
@@ -1868,102 +1990,104 @@ RequestsMenuView.prototype = Heritage.ex
       timingsNode.style.transform = scaleX + " " + translateX;
       totalNode.style.transform = revScaleX;
     }
   },
 
   /**
    * Creates the labels displayed on the waterfall header in this container.
    *
-   * @param number aScale
+   * @param number scale
    *        The current waterfall scale.
    */
-  _showWaterfallDivisionLabels: function(aScale) {
+  _showWaterfallDivisionLabels: function(scale) {
     let container = $("#requests-menu-waterfall-label-wrapper");
     let availableWidth = this._waterfallWidth - REQUESTS_WATERFALL_SAFE_BOUNDS;
 
     // Nuke all existing labels.
     while (container.hasChildNodes()) {
       container.firstChild.remove();
     }
 
     // Build new millisecond tick labels...
     let timingStep = REQUESTS_WATERFALL_HEADER_TICKS_MULTIPLE;
     let optimalTickIntervalFound = false;
 
     while (!optimalTickIntervalFound) {
       // Ignore any divisions that would end up being too close to each other.
-      let scaledStep = aScale * timingStep;
+      let scaledStep = scale * timingStep;
       if (scaledStep < REQUESTS_WATERFALL_HEADER_TICKS_SPACING_MIN) {
         timingStep <<= 1;
         continue;
       }
       optimalTickIntervalFound = true;
 
       // Insert one label for each division on the current scale.
       let fragment = document.createDocumentFragment();
       let direction = window.isRTL ? -1 : 1;
 
       for (let x = 0; x < availableWidth; x += scaledStep) {
         let translateX = "translateX(" + ((direction * x) | 0) + "px)";
-        let millisecondTime = x / aScale;
+        let millisecondTime = x / scale;
 
         let normalizedTime = millisecondTime;
         let divisionScale = "millisecond";
 
         // If the division is greater than 1 minute.
         if (normalizedTime > 60000) {
           normalizedTime /= 60000;
           divisionScale = "minute";
-        }
-        // If the division is greater than 1 second.
-        else if (normalizedTime > 1000) {
+        } else if (normalizedTime > 1000) {
+          // If the division is greater than 1 second.
           normalizedTime /= 1000;
           divisionScale = "second";
         }
 
         // Showing too many decimals is bad UX.
         if (divisionScale == "millisecond") {
           normalizedTime |= 0;
         } else {
-          normalizedTime = L10N.numberWithDecimals(normalizedTime, REQUEST_TIME_DECIMALS);
+          normalizedTime = L10N.numberWithDecimals(normalizedTime,
+            REQUEST_TIME_DECIMALS);
         }
 
         let node = document.createElement("label");
-        let text = L10N.getFormatStr("networkMenu." + divisionScale, normalizedTime);
+        let text = L10N.getFormatStr("networkMenu." +
+          divisionScale, normalizedTime);
         node.className = "plain requests-menu-timings-division";
         node.setAttribute("division-scale", divisionScale);
         node.style.transform = translateX;
 
         node.setAttribute("value", text);
         fragment.appendChild(node);
       }
       container.appendChild(fragment);
 
-      container.className = 'requests-menu-waterfall-visible';
+      container.className = "requests-menu-waterfall-visible";
     }
   },
 
   /**
    * Creates the background displayed on each waterfall view in this container.
    *
-   * @param number aScale
+   * @param number scale
    *        The current waterfall scale.
    */
-  _drawWaterfallBackground: function(aScale) {
+  _drawWaterfallBackground: function(scale) {
     if (!this._canvas || !this._ctx) {
       this._canvas = document.createElementNS(HTML_NS, "canvas");
       this._ctx = this._canvas.getContext("2d");
     }
     let canvas = this._canvas;
     let ctx = this._ctx;
 
     // Nuke the context.
     let canvasWidth = canvas.width = this._waterfallWidth;
-    let canvasHeight = canvas.height = 1; // Awww yeah, 1px, repeats on Y axis.
+    // Awww yeah, 1px, repeats on Y axis.
+    let canvasHeight = canvas.height = 1;
 
     // Start over.
     let imageData = ctx.createImageData(canvasWidth, canvasHeight);
     let pixelArray = imageData.data;
 
     let buf = new ArrayBuffer(pixelArray.length);
     let view8bit = new Uint8ClampedArray(buf);
     let view32bit = new Uint32Array(buf);
@@ -1971,45 +2095,51 @@ RequestsMenuView.prototype = Heritage.ex
     // Build new millisecond tick lines...
     let timingStep = REQUESTS_WATERFALL_BACKGROUND_TICKS_MULTIPLE;
     let [r, g, b] = REQUESTS_WATERFALL_BACKGROUND_TICKS_COLOR_RGB;
     let alphaComponent = REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_MIN;
     let optimalTickIntervalFound = false;
 
     while (!optimalTickIntervalFound) {
       // Ignore any divisions that would end up being too close to each other.
-      let scaledStep = aScale * timingStep;
+      let scaledStep = scale * timingStep;
       if (scaledStep < REQUESTS_WATERFALL_BACKGROUND_TICKS_SPACING_MIN) {
         timingStep <<= 1;
         continue;
       }
       optimalTickIntervalFound = true;
 
       // Insert one pixel for each division on each scale.
       for (let i = 1; i <= REQUESTS_WATERFALL_BACKGROUND_TICKS_SCALES; i++) {
         let increment = scaledStep * Math.pow(2, i);
         for (let x = 0; x < canvasWidth; x += increment) {
           let position = (window.isRTL ? canvasWidth - x : x) | 0;
-          view32bit[position] = (alphaComponent << 24) | (b << 16) | (g << 8) | r;
+          view32bit[position] =
+            (alphaComponent << 24) | (b << 16) | (g << 8) | r;
         }
         alphaComponent += REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_ADD;
       }
     }
 
     {
-      let t = NetMonitorController.NetworkEventsHandler.firstDocumentDOMContentLoadedTimestamp;
-      let delta = Math.floor((t - this._firstRequestStartedMillis) * aScale);
-      let [r, g, b, a] = REQUESTS_WATERFALL_DOMCONTENTLOADED_TICKS_COLOR_RGBA;
-      view32bit[delta] = (a << 24) | (r << 16) | (g << 8) | b;
+      let t = NetMonitorController.NetworkEventsHandler
+        .firstDocumentDOMContentLoadedTimestamp;
+
+      let delta = Math.floor((t - this._firstRequestStartedMillis) * scale);
+      let [r1, g1, b1, a1] =
+        REQUESTS_WATERFALL_DOMCONTENTLOADED_TICKS_COLOR_RGBA;
+      view32bit[delta] = (a1 << 24) | (r1 << 16) | (g1 << 8) | b1;
     }
     {
-      let t = NetMonitorController.NetworkEventsHandler.firstDocumentLoadTimestamp;
-      let delta = Math.floor((t - this._firstRequestStartedMillis) * aScale);
-      let [r, g, b, a] = REQUESTS_WATERFALL_LOAD_TICKS_COLOR_RGBA;
-      view32bit[delta] = (a << 24) | (r << 16) | (g << 8) | b;
+      let t = NetMonitorController.NetworkEventsHandler
+        .firstDocumentLoadTimestamp;
+
+      let delta = Math.floor((t - this._firstRequestStartedMillis) * scale);
+      let [r2, g2, b2, a2] = REQUESTS_WATERFALL_LOAD_TICKS_COLOR_RGBA;
+      view32bit[delta] = (a2 << 24) | (r2 << 16) | (g2 << 8) | b2;
     }
 
     // Flush the image data and cache the waterfall background.
     pixelArray.set(view8bit);
     ctx.putImageData(imageData, 0, 0);
     document.mozSetImageElement("waterfall-background", canvas);
   },
 
@@ -2033,46 +2163,47 @@ RequestsMenuView.prototype = Heritage.ex
     // Sorting will create new anchor nodes for all the swapped request items
     // in this container, so it's necessary to refresh the Tooltip instances.
     this.refreshTooltip(firstItem);
     this.refreshTooltip(secondItem);
 
     // Reattach click listener to the security icons
     this.attachSecurityIconClickListener(firstItem);
     this.attachSecurityIconClickListener(secondItem);
-
   },
 
   /**
    * The predicate used when deciding whether a popup should be shown
    * over a request item or not.
    *
-   * @param nsIDOMNode aTarget
+   * @param nsIDOMNode target
    *        The element node currently being hovered.
-   * @param object aTooltip
+   * @param object tooltip
    *        The current tooltip instance.
    */
-  _onHover: function(aTarget, aTooltip) {
-    let requestItem = this.getItemForElement(aTarget);
+  _onHover: function(target, tooltip) {
+    let requestItem = this.getItemForElement(target);
     if (!requestItem || !requestItem.attachment.responseContent) {
-      return;
+      return null;
     }
 
     let hovered = requestItem.attachment;
-    let { url } = hovered;
     let { mimeType, text, encoding } = hovered.responseContent.content;
 
     if (mimeType && mimeType.includes("image/") && (
-      aTarget.classList.contains("requests-menu-icon") ||
-      aTarget.classList.contains("requests-menu-file")))
-    {
-      return gNetwork.getString(text).then(aString => {
+      target.classList.contains("requests-menu-icon") ||
+      target.classList.contains("requests-menu-file"))) {
+      return gNetwork.getString(text).then(string => {
         let anchor = $(".requests-menu-icon", requestItem.target);
-        let src = formDataURI(mimeType, encoding, aString);
-        aTooltip.setImageContent(src, { maxDim: REQUESTS_TOOLTIP_IMAGE_MAX_DIM });
+        let src = formDataURI(mimeType, encoding, string);
+
+        tooltip.setImageContent(src, {
+          maxDim: REQUESTS_TOOLTIP_IMAGE_MAX_DIM
+        });
+
         return anchor;
       });
     }
   },
 
   /**
    * A handler that opens the security tab in the details view if secure or
    * broken security indicator is clicked.
@@ -2085,58 +2216,66 @@ RequestsMenuView.prototype = Heritage.ex
     }
   },
 
   /**
    * The resize listener for this container's window.
    */
   _onResize: function(e) {
     // Allow requests to settle down first.
-    setNamedTimeout(
-      "resize-events", RESIZE_REFRESH_RATE, () => this._flushWaterfallViews(true));
+    setNamedTimeout("resize-events",
+      RESIZE_REFRESH_RATE, () => this._flushWaterfallViews(true));
   },
 
   /**
    * Handle the context menu opening. Hide items if no request is selected.
    */
   _onContextShowing: function() {
     let selectedItem = this.selectedItem;
 
     let resendElement = $("#request-menu-context-resend");
     resendElement.hidden = !NetMonitorController.supportsCustomRequest ||
       !selectedItem || selectedItem.attachment.isCustom;
 
     let copyUrlElement = $("#request-menu-context-copy-url");
     copyUrlElement.hidden = !selectedItem;
 
     let copyUrlParamsElement = $("#request-menu-context-copy-url-params");
-    copyUrlParamsElement.hidden = !selectedItem || !NetworkHelper.nsIURL(selectedItem.attachment.url).query;
+    copyUrlParamsElement.hidden = !selectedItem ||
+      !NetworkHelper.nsIURL(selectedItem.attachment.url).query;
 
     let copyPostDataElement = $("#request-menu-context-copy-post-data");
-    copyPostDataElement.hidden = !selectedItem || !selectedItem.attachment.requestPostData;
+    copyPostDataElement.hidden = !selectedItem ||
+      !selectedItem.attachment.requestPostData;
 
     let copyAsCurlElement = $("#request-menu-context-copy-as-curl");
     copyAsCurlElement.hidden = !selectedItem || !selectedItem.attachment;
 
-    let copyRequestHeadersElement = $("#request-menu-context-copy-request-headers");
-    copyRequestHeadersElement.hidden = !selectedItem || !selectedItem.attachment.requestHeaders;
-
-    let copyResponseHeadersElement = $("#response-menu-context-copy-response-headers");
-    copyResponseHeadersElement.hidden = !selectedItem || !selectedItem.attachment.responseHeaders;
+    let copyRequestHeadersElement =
+      $("#request-menu-context-copy-request-headers");
+    copyRequestHeadersElement.hidden = !selectedItem ||
+    !selectedItem.attachment.requestHeaders;
+
+    let copyResponseHeadersElement =
+      $("#response-menu-context-copy-response-headers");
+    copyResponseHeadersElement.hidden = !selectedItem ||
+      !selectedItem.attachment.responseHeaders;
 
     let copyResponse = $("#request-menu-context-copy-response");
     copyResponse.hidden = !selectedItem ||
       !selectedItem.attachment.responseContent ||
       !selectedItem.attachment.responseContent.content.text ||
       selectedItem.attachment.responseContent.content.text.length === 0;
 
-    let copyImageAsDataUriElement = $("#request-menu-context-copy-image-as-data-uri");
+    let copyImageAsDataUriElement =
+      $("#request-menu-context-copy-image-as-data-uri");
     copyImageAsDataUriElement.hidden = !selectedItem ||
       !selectedItem.attachment.responseContent ||
-      !selectedItem.attachment.responseContent.content.mimeType.includes("image/");
+      !selectedItem.attachment.responseContent.content
+        .mimeType.includes("image/");
 
     let separators = $all(".request-menu-context-separator");
     Array.forEach(separators, separator => separator.hidden = !selectedItem);
 
     let copyAsHar = $("#request-menu-context-copy-all-as-har");
     copyAsHar.hidden = !NetMonitorView.RequestsMenu.items.length;
 
     let saveAsHar = $("#request-menu-context-save-all-as-har");
@@ -2145,138 +2284,146 @@ RequestsMenuView.prototype = Heritage.ex
     let newTabElement = $("#request-menu-context-newtab");
     newTabElement.hidden = !selectedItem;
   },
 
   /**
    * Checks if the specified unix time is the first one to be known of,
    * and saves it if so.
    *
-   * @param number aUnixTime
+   * @param number unixTime
    *        The milliseconds to check and save.
    */
-  _registerFirstRequestStart: function(aUnixTime) {
+  _registerFirstRequestStart: function(unixTime) {
     if (this._firstRequestStartedMillis == -1) {
-      this._firstRequestStartedMillis = aUnixTime;
+      this._firstRequestStartedMillis = unixTime;
     }
   },
 
   /**
    * Checks if the specified unix time is the last one to be known of,
    * and saves it if so.
    *
-   * @param number aUnixTime
+   * @param number unixTime
    *        The milliseconds to check and save.
    */
-  _registerLastRequestEnd: function(aUnixTime) {
-    if (this._lastRequestEndedMillis < aUnixTime) {
-      this._lastRequestEndedMillis = aUnixTime;
+  _registerLastRequestEnd: function(unixTime) {
+    if (this._lastRequestEndedMillis < unixTime) {
+      this._lastRequestEndedMillis = unixTime;
     }
   },
 
   /**
    * Helpers for getting details about an nsIURL.
    *
-   * @param nsIURL | string aUrl
+   * @param nsIURL | string url
    * @return string
    */
-  _getUriNameWithQuery: function(aUrl) {
-    if (!(aUrl instanceof Ci.nsIURL)) {
-      aUrl = NetworkHelper.nsIURL(aUrl);
+  _getUriNameWithQuery: function(url) {
+    if (!(url instanceof Ci.nsIURL)) {
+      url = NetworkHelper.nsIURL(url);
     }
-    let name = NetworkHelper.convertToUnicode(unescape(aUrl.fileName || aUrl.filePath || "/"));
-    let query = NetworkHelper.convertToUnicode(unescape(aUrl.query));
+
+    let name = NetworkHelper.convertToUnicode(
+      unescape(url.fileName || url.filePath || "/"));
+    let query = NetworkHelper.convertToUnicode(unescape(url.query));
+
     return name + (query ? "?" + query : "");
   },
-  _getUriHostPort: function(aUrl) {
-    if (!(aUrl instanceof Ci.nsIURL)) {
-      aUrl = NetworkHelper.nsIURL(aUrl);
+
+  _getUriHostPort: function(url) {
+    if (!(url instanceof Ci.nsIURL)) {
+      url = NetworkHelper.nsIURL(url);
     }
-    return NetworkHelper.convertToUnicode(unescape(aUrl.hostPort));
+    return NetworkHelper.convertToUnicode(unescape(url.hostPort));
   },
-  _getUriHost: function(aUrl) {
-    return this._getUriHostPort(aUrl).replace(/:\d+$/, "");
+
+  _getUriHost: function(url) {
+    return this._getUriHostPort(url).replace(/:\d+$/, "");
   },
 
   /**
    * Helper for getting an abbreviated string for a mime type.
    *
-   * @param string aMimeType
+   * @param string mimeType
    * @return string
    */
-  _getAbbreviatedMimeType: function(aMimeType) {
-    if (!aMimeType) {
+  _getAbbreviatedMimeType: function(mimeType) {
+    if (!mimeType) {
       return "";
     }
-    return (aMimeType.split(";")[0].split("/")[1] || "").split("+")[0];
+    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 aItemsArray
+   * @param array itemsArray
    * @return number
    */
-  _getTotalBytesOfRequests: function(aItemsArray) {
-    if (!aItemsArray.length) {
+  _getTotalBytesOfRequests: function(itemsArray) {
+    if (!itemsArray.length) {
       return 0;
     }
 
     let result = 0;
-    aItemsArray.forEach(item => {
+    itemsArray.forEach(item => {
       let size = item.attachment.contentSize;
       result += (typeof size == "number") ? size : 0;
     });
 
     return result;
   },
 
   /**
    * Gets the oldest (first performed) request in a set. Returns null for an
    * empty set.
    *
-   * @param array aItemsArray
+   * @param array itemsArray
    * @return object
    */
-  _getOldestRequest: function(aItemsArray) {
-    if (!aItemsArray.length) {
+  _getOldestRequest: function(itemsArray) {
+    if (!itemsArray.length) {
       return null;
     }
-    return aItemsArray.reduce((prev, curr) =>
-      prev.attachment.startedMillis < curr.attachment.startedMillis ? prev : curr);
+    return itemsArray.reduce((prev, curr) =>
+      prev.attachment.startedMillis < curr.attachment.startedMillis ?
+        prev : curr);
   },
 
   /**
    * Gets the newest (latest performed) request in a set. Returns null for an
    * empty set.
    *
-   * @param array aItemsArray
+   * @param array itemsArray
    * @return object
    */
-  _getNewestRequest: function(aItemsArray) {
-    if (!aItemsArray.length) {
+  _getNewestRequest: function(itemsArray) {
+    if (!itemsArray.length) {
       return null;
     }
-    return aItemsArray.reduce((prev, curr) =>
-      prev.attachment.startedMillis > curr.attachment.startedMillis ? prev : curr);
+    return itemsArray.reduce((prev, curr) =>
+      prev.attachment.startedMillis > curr.attachment.startedMillis ?
+        prev : curr);
   },
 
   /**
    * Gets the available waterfall width in this container.
    * @return number
    */
   get _waterfallWidth() {
     if (this._cachedWaterfallWidth == 0) {
       let container = $("#requests-menu-toolbar");
       let waterfall = $("#requests-menu-waterfall-header-box");
       let containerBounds = container.getBoundingClientRect();
       let waterfallBounds = waterfall.getBoundingClientRect();
       if (!window.isRTL) {
-        this._cachedWaterfallWidth = containerBounds.width - waterfallBounds.left;
+        this._cachedWaterfallWidth = containerBounds.width -
+          waterfallBounds.left;
       } else {
         this._cachedWaterfallWidth = waterfallBounds.right;
       }
     }
     return this._cachedWaterfallWidth;
   },
 
   _splitter: null,
@@ -2300,184 +2447,188 @@ RequestsMenuView.prototype = Heritage.ex
 function SidebarView() {
   dumpn("SidebarView was instantiated");
 }
 
 SidebarView.prototype = {
   /**
    * Sets this view hidden or visible. It's visible by default.
    *
-   * @param boolean aVisibleFlag
+   * @param boolean visibleFlag
    *        Specifies the intended visibility.
    */
-  toggle: function(aVisibleFlag) {
-    NetMonitorView.toggleDetailsPane({ visible: aVisibleFlag });
+  toggle: function(visibleFlag) {
+    NetMonitorView.toggleDetailsPane({ visible: visibleFlag });
     NetMonitorView.RequestsMenu._flushWaterfallViews(true);
   },
 
   /**
    * Populates this view with the specified data.
    *
-   * @param object aData
+   * @param object data
    *        The data source (this should be the attachment of a request item).
    * @return object
    *        Returns a promise that resolves upon population of the subview.
    */
-  populate: Task.async(function*(aData) {
-    let isCustom = aData.isCustom;
+  populate: Task.async(function*(data) {
+    let isCustom = data.isCustom;
     let view = isCustom ?
       NetMonitorView.CustomRequest :
       NetMonitorView.NetworkDetails;
 
-    yield view.populate(aData);
+    yield view.populate(data);
     $("#details-pane").selectedIndex = isCustom ? 0 : 1;
 
     window.emit(EVENTS.SIDEBAR_POPULATED);
   })
-}
+};
 
 /**
  * Functions handling the custom request view.
  */
 function CustomRequestView() {
   dumpn("CustomRequestView was instantiated");
 }
 
 CustomRequestView.prototype = {
   /**
    * Initialization function, called when the network monitor is started.
    */
   initialize: function() {
     dumpn("Initializing the CustomRequestView");
 
     this.updateCustomRequestEvent = getKeyWithEvent(this.onUpdate.bind(this));
-    $("#custom-pane").addEventListener("input", this.updateCustomRequestEvent, false);
+    $("#custom-pane").addEventListener("input",
+      this.updateCustomRequestEvent, false);
   },
 
   /**
    * Destruction function, called when the network monitor is closed.
    */
   destroy: function() {
     dumpn("Destroying the CustomRequestView");
 
-    $("#custom-pane").removeEventListener("input", this.updateCustomRequestEvent, false);
+    $("#custom-pane").removeEventListener("input",
+      this.updateCustomRequestEvent, false);
   },
 
   /**
    * Populates this view with the specified data.
    *
-   * @param object aData
+   * @param object data
    *        The data source (this should be the attachment of a request item).
    * @return object
    *        Returns a promise that resolves upon population the view.
    */
-  populate: Task.async(function*(aData) {
-    $("#custom-url-value").value = aData.url;
-    $("#custom-method-value").value = aData.method;
-    this.updateCustomQuery(aData.url);
-
-    if (aData.requestHeaders) {
-      let headers = aData.requestHeaders.headers;
+  populate: Task.async(function*(data) {
+    $("#custom-url-value").value = data.url;
+    $("#custom-method-value").value = data.method;
+    this.updateCustomQuery(data.url);
+
+    if (data.requestHeaders) {
+      let headers = data.requestHeaders.headers;
       $("#custom-headers-value").value = writeHeaderText(headers);
     }
-    if (aData.requestPostData) {
-      let postData = aData.requestPostData.postData.text;
+    if (data.requestPostData) {
+      let postData = data.requestPostData.postData.text;
       $("#custom-postdata-value").value = yield gNetwork.getString(postData);
     }
 
     window.emit(EVENTS.CUSTOMREQUESTVIEW_POPULATED);
   }),
 
   /**
    * Handle user input in the custom request form.
    *
-   * @param object aField
+   * @param object field
    *        the field that the user updated.
    */
-  onUpdate: function(aField) {
+  onUpdate: function(field) {
     let selectedItem = NetMonitorView.RequestsMenu.selectedItem;
-    let field = aField;
     let value;
 
-    switch(aField) {
-      case 'method':
+    switch (field) {
+      case "method":
         value = $("#custom-method-value").value.trim();
         selectedItem.attachment.method = value;
         break;
-      case 'url':
+      case "url":
         value = $("#custom-url-value").value;
         this.updateCustomQuery(value);
         selectedItem.attachment.url = value;
         break;
-      case 'query':
+      case "query":
         let query = $("#custom-query-value").value;
         this.updateCustomUrl(query);
-        field = 'url';
-        value = $("#custom-url-value").value
+        field = "url";
+        value = $("#custom-url-value").value;
         selectedItem.attachment.url = value;
         break;
-      case 'body':
+      case "body":
         value = $("#custom-postdata-value").value;
         selectedItem.attachment.requestPostData = { postData: { text: value } };
         break;
-      case 'headers':
+      case "headers":
         let headersText = $("#custom-headers-value").value;
         value = parseHeadersText(headersText);
         selectedItem.attachment.requestHeaders = { headers: value };
         break;
     }
 
     NetMonitorView.RequestsMenu.updateMenuView(selectedItem, field, value);
   },
 
   /**
    * Update the query string field based on the url.
    *
-   * @param object aUrl
+   * @param object url
    *        The URL to extract query string from.
    */
-  updateCustomQuery: function(aUrl) {
-    let paramsArray = NetworkHelper.parseQueryString(NetworkHelper.nsIURL(aUrl).query);
+  updateCustomQuery: function(url) {
+    let paramsArray = NetworkHelper.parseQueryString(
+      NetworkHelper.nsIURL(url).query);
+
     if (!paramsArray) {
       $("#custom-query").hidden = true;
       return;
     }
+
     $("#custom-query").hidden = false;
     $("#custom-query-value").value = writeQueryText(paramsArray);
   },
 
   /**
    * Update the url based on the query string field.
    *
-   * @param object aQueryText
+   * @param object queryText
    *        The contents of the query string field.
    */
-  updateCustomUrl: function(aQueryText) {
-    let params = parseQueryText(aQueryText);
+  updateCustomUrl: function(queryText) {
+    let params = parseQueryText(queryText);
     let queryString = writeQueryString(params);
 
     let url = $("#custom-url-value").value;
     let oldQuery = NetworkHelper.nsIURL(url).query;
     let path = url.replace(oldQuery, queryString);
 
     $("#custom-url-value").value = path;
   }
-}
+};
 
 /**
  * Functions handling the requests details view.
  */
 function NetworkDetailsView() {
   dumpn("NetworkDetailsView was instantiated");
 
   // The ToolSidebar requires the panel object to be able to emit events.
   EventEmitter.decorate(this);
 
   this._onTabSelect = this._onTabSelect.bind(this);
-};
+}
 
 NetworkDetailsView.prototype = {
   /**
    * An object containing the state of tabs.
    */
   _viewState: {
     // if updating[tab] is true a task is currently updating the given tab.
     updating: [],
@@ -2535,64 +2686,66 @@ NetworkDetailsView.prototype = {
   },
 
   /**
    * Destruction function, called when the network monitor is closed.
    */
   destroy: function() {
     dumpn("Destroying the NetworkDetailsView");
     this.sidebar.destroy();
-    $("tabpanels", this.widget).removeEventListener("select", this._onTabSelect);
+    $("tabpanels", this.widget).removeEventListener("select",
+      this._onTabSelect);
   },
 
   /**
    * Populates this view with the specified data.
    *
-   * @param object aData
+   * @param object data
    *        The data source (this should be the attachment of a request item).
    * @return object
    *        Returns a promise that resolves upon population the view.
    */
-  populate: function(aData) {
+  populate: function(data) {
     $("#request-params-box").setAttribute("flex", "1");
     $("#request-params-box").hidden = false;
     $("#request-post-data-textarea-box").hidden = true;
     $("#response-content-info-header").hidden = true;
     $("#response-content-json-box").hidden = true;
     $("#response-content-textarea-box").hidden = true;
     $("#raw-headers").hidden = true;
     $("#response-content-image-box").hidden = true;
 
-    let isHtml = RequestsMenuView.prototype.isHtml({ attachment: aData });
+    let isHtml = RequestsMenuView.prototype.isHtml({ attachment: data });
 
     // Show the "Preview" tabpanel only for plain HTML responses.
     this.sidebar.toggleTab(isHtml, "preview-tab");
 
     // Show the "Security" tab only for requests that
     //   1) are https (state != insecure)
     //   2) come from a target that provides security information.
-    let hasSecurityInfo = aData.securityState &&
-                          aData.securityState !== "insecure";
+    let hasSecurityInfo = data.securityState &&
+                          data.securityState !== "insecure";
     this.sidebar.toggleTab(hasSecurityInfo, "security-tab");
 
     // Switch to the "Headers" tabpanel if the "Preview" previously selected
     // and this is not an HTML response or "Security" was selected but this
     // request has no security information.
 
     if (!isHtml && this.widget.selectedPanel === $("#preview-tabpanel") ||
-        !hasSecurityInfo && this.widget.selectedPanel === $("#security-tabpanel")) {
+        !hasSecurityInfo && this.widget.selectedPanel ===
+          $("#security-tabpanel")) {
       this.widget.selectedIndex = 0;
     }
 
     this._headers.empty();
     this._cookies.empty();
     this._params.empty();
     this._json.empty();
 
-    this._dataSrc = { src: aData, populated: [] };
+    this._dataSrc = { src: data, populated: [] };
     this._onTabSelect();
     window.emit(EVENTS.NETWORKDETAILSVIEW_POPULATED);
 
     return promise.resolve();
   },
 
   /**
    * Listener handling the tab selection event.
@@ -2616,240 +2769,245 @@ NetworkDetailsView.prototype = {
       viewState.dirty[tab] = true;
       viewState.latestData = src;
       return;
     }
 
     Task.spawn(function*() {
       viewState.updating[tab] = true;
       switch (tab) {
-        case 0: // "Headers"
+        // "Headers"
+        case 0:
           yield view._setSummary(src);
           yield view._setResponseHeaders(src.responseHeaders);
           yield view._setRequestHeaders(
             src.requestHeaders,
             src.requestHeadersFromUploadStream);
           break;
-        case 1: // "Cookies"
+        // "Cookies"
+        case 1:
           yield view._setResponseCookies(src.responseCookies);
           yield view._setRequestCookies(src.requestCookies);
           break;
-        case 2: // "Params"
+        // "Params"
+        case 2:
           yield view._setRequestGetParams(src.url);
           yield view._setRequestPostParams(
             src.requestHeaders,
             src.requestHeadersFromUploadStream,
             src.requestPostData);
           break;
-        case 3: // "Response"
+        // "Response"
+        case 3:
           yield view._setResponseBody(src.url, src.responseContent);
           break;
-        case 4: // "Timings"
+        // "Timings"
+        case 4:
           yield view._setTimingsInformation(src.eventTimings);
           break;
-        case 5: // "Security"
+        // "Security"
+        case 5:
           yield view._setSecurityInfo(src.securityInfo, src.url);
           break;
-        case 6: // "Preview"
+        // "Preview"
+        case 6:
           yield view._setHtmlPreview(src.responseContent);
           break;
       }
       viewState.updating[tab] = false;
     }).then(() => {
       if (tab == this.widget.selectedIndex) {
         if (viewState.dirty[tab]) {
           // The request information was updated while the task was running.
           viewState.dirty[tab] = false;
           view.populate(viewState.latestData);
-        }
-        else {
+        } else {
           // Tab is selected but not dirty. We're done here.
           populated[tab] = true;
           window.emit(EVENTS.TAB_UPDATED);
 
           if (NetMonitorController.isConnected()) {
             NetMonitorView.RequestsMenu.ensureSelectedItemIsVisible();
           }
         }
-      }
-      else {
-        if (viewState.dirty[tab]) {
-          // Tab is dirty but no longer selected. Don't refresh it now, it'll be
-          // done if the tab is shown again.
-          viewState.dirty[tab] = false;
-        }
+      } else if (viewState.dirty[tab]) {
+        // Tab is dirty but no longer selected. Don't refresh it now, it'll be
+        // done if the tab is shown again.
+        viewState.dirty[tab] = false;
       }
     }, Cu.reportError);
   },
 
   /**
    * Sets the network request summary shown in this view.
    *
-   * @param object aData
+   * @param object data
    *        The data source (this should be the attachment of a request item).
    */
-  _setSummary: function(aData) {
-    if (aData.url) {
-      let unicodeUrl = NetworkHelper.convertToUnicode(unescape(aData.url));
+  _setSummary: function(data) {
+    if (data.url) {
+      let unicodeUrl = NetworkHelper.convertToUnicode(unescape(data.url));
       $("#headers-summary-url-value").setAttribute("value", unicodeUrl);
       $("#headers-summary-url-value").setAttribute("tooltiptext", unicodeUrl);
       $("#headers-summary-url").removeAttribute("hidden");
     } else {
       $("#headers-summary-url").setAttribute("hidden", "true");
     }
 
-    if (aData.method) {
-      $("#headers-summary-method-value").setAttribute("value", aData.method);
+    if (data.method) {
+      $("#headers-summary-method-value").setAttribute("value", data.method);
       $("#headers-summary-method").removeAttribute("hidden");
     } else {
       $("#headers-summary-method").setAttribute("hidden", "true");
     }
 
-    if (aData.remoteAddress) {
-      let address = aData.remoteAddress;
+    if (data.remoteAddress) {
+      let address = data.remoteAddress;
       if (address.indexOf(":") != -1) {
         address = `[${address}]`;
       }
-      if(aData.remotePort) {
-        address += `:${aData.remotePort}`;
+      if (data.remotePort) {
+        address += `:${data.remotePort}`;
       }
       $("#headers-summary-address-value").setAttribute("value", address);
       $("#headers-summary-address-value").setAttribute("tooltiptext", address);
       $("#headers-summary-address").removeAttribute("hidden");
     } else {
       $("#headers-summary-address").setAttribute("hidden", "true");
     }
 
-    if (aData.status) {
+    if (data.status) {
       let code;
-      if (aData.fromCache) {
+      if (data.fromCache) {
         code = L10N.getStr("netmonitor.status.cached");
-      } else if (aData.fromServiceWorker) {
+      } else if (data.fromServiceWorker) {
         code = L10N.getStr("netmonitor.status.serviceWorker");
       } else {
-        code = aData.status;
+        code = data.status;
       }
       $("#headers-summary-status-circle").setAttribute("code", code);
-      $("#headers-summary-status-value").setAttribute("value", aData.status + " " + aData.statusText);
+      $("#headers-summary-status-value").setAttribute("value",
+        data.status + " " + data.statusText);
       $("#headers-summary-status").removeAttribute("hidden");
     } else {
       $("#headers-summary-status").setAttribute("hidden", "true");
     }
 
-    if (aData.httpVersion) {
-      $("#headers-summary-version-value").setAttribute("value", aData.httpVersion);
+    if (data.httpVersion) {
+      $("#headers-summary-version-value").setAttribute("value",
+        data.httpVersion);
       $("#headers-summary-version").removeAttribute("hidden");
     } else {
       $("#headers-summary-version").setAttribute("hidden", "true");
     }
   },
 
   /**
    * Sets the network request headers shown in this view.
    *
-   * @param object aHeaders
+   * @param object headers
    *        The "requestHeaders" message received from the server.
-   * @param object aUploadHeaders
+   * @param object uploadHeaders
    *        The "requestHeadersFromUploadStream" inferred from the POST payload.
    * @return object
    *        A promise that resolves when request headers are set.
    */
-  _setRequestHeaders: Task.async(function*(aHeaders, aUploadHeaders) {
-    if (aHeaders && aHeaders.headers.length) {
-      yield this._addHeaders(this._requestHeaders, aHeaders);
+  _setRequestHeaders: Task.async(function*(headers, uploadHeaders) {
+    if (headers && headers.headers.length) {
+      yield this._addHeaders(this._requestHeaders, headers);
     }
-    if (aUploadHeaders && aUploadHeaders.headers.length) {
-      yield this._addHeaders(this._requestHeadersFromUpload, aUploadHeaders);
+    if (uploadHeaders && uploadHeaders.headers.length) {
+      yield this._addHeaders(this._requestHeadersFromUpload, uploadHeaders);
     }
   }),
 
   /**
    * Sets the network response headers shown in this view.
    *
-   * @param object aResponse
+   * @param object response
    *        The message received from the server.
    * @return object
    *        A promise that resolves when response headers are set.
    */
-  _setResponseHeaders: Task.async(function*(aResponse) {
-    if (aResponse && aResponse.headers.length) {
-      aResponse.headers.sort((a, b) => a.name > b.name);
-      yield this._addHeaders(this._responseHeaders, aResponse);
+  _setResponseHeaders: Task.async(function*(response) {
+    if (response && response.headers.length) {
+      response.headers.sort((a, b) => a.name > b.name);
+      yield this._addHeaders(this._responseHeaders, response);
     }
   }),
 
   /**
    * Populates the headers container in this view with the specified data.
    *
-   * @param string aName
+   * @param string name
    *        The type of headers to populate (request or response).
-   * @param object aResponse
+   * @param object response
    *        The message received from the server.
    * @return object
    *        A promise that resolves when headers are added.
    */
-  _addHeaders: Task.async(function*(aName, aResponse) {
-    let kb = aResponse.headersSize / 1024;
+  _addHeaders: Task.async(function*(name, response) {
+    let kb = response.headersSize / 1024;
     let size = L10N.numberWithDecimals(kb, HEADERS_SIZE_DECIMALS);
     let text = L10N.getFormatStr("networkMenu.sizeKB", size);
 
-    let headersScope = this._headers.addScope(aName + " (" + text + ")");
+    let headersScope = this._headers.addScope(name + " (" + text + ")");
     headersScope.expanded = true;
 
-    for (let header of aResponse.headers) {
+    for (let header of response.headers) {
       let headerVar = headersScope.addItem(header.name, {}, {relaxed: true});
       let headerValue = yield gNetwork.getString(header.value);
       headerVar.setGrip(headerValue);
     }
   }),
 
   /**
    * Sets the network request cookies shown in this view.
    *
-   * @param object aResponse
+   * @param object response
    *        The message received from the server.
    * @return object
    *        A promise that is resolved when the request cookies are set.
    */
-  _setRequestCookies: Task.async(function*(aResponse) {
-    if (aResponse && aResponse.cookies.length) {
-      aResponse.cookies.sort((a, b) => a.name > b.name);
-      yield this._addCookies(this._requestCookies, aResponse);
+  _setRequestCookies: Task.async(function*(response) {
+    if (response && response.cookies.length) {
+      response.cookies.sort((a, b) => a.name > b.name);
+      yield this._addCookies(this._requestCookies, response);
     }
   }),
 
   /**
    * Sets the network response cookies shown in this view.
    *
-   * @param object aResponse
+   * @param object response
    *        The message received from the server.
    * @return object
    *        A promise that is resolved when the response cookies are set.
    */
-  _setResponseCookies: Task.async(function*(aResponse) {
-    if (aResponse && aResponse.cookies.length) {
-      yield this._addCookies(this._responseCookies, aResponse);
+  _setResponseCookies: Task.async(function*(response) {
+    if (response && response.cookies.length) {
+      yield this._addCookies(this._responseCookies, response);
     }
   }),
 
   /**
    * Populates the cookies container in this view with the specified data.
    *
-   * @param string aName
+   * @param string name
    *        The type of cookies to populate (request or response).
-   * @param object aResponse
+   * @param object response
    *        The message received from the server.
    * @return object
    *        Returns a promise that resolves upon the adding of cookies.
    */
-  _addCookies: Task.async(function*(aName, aResponse) {
-    let cookiesScope = this._cookies.addScope(aName);
+  _addCookies: Task.async(function*(name, response) {
+    let cookiesScope = this._cookies.addScope(name);
     cookiesScope.expanded = true;
 
-    for (let cookie of aResponse.cookies) {
+    for (let cookie of response.cookies) {
       let cookieVar = cookiesScope.addItem(cookie.name, {}, {relaxed: true});
       let cookieValue = yield gNetwork.getString(cookie.value);
       cookieVar.setGrip(cookieValue);
 
       // By default the cookie name and value are shown. If this is the only
       // information available, then nothing else is to be displayed.
       let cookieProps = Object.keys(cookie);
       if (cookieProps.length == 2) {
@@ -2867,126 +3025,127 @@ NetworkDetailsView.prototype = {
       cookieVar.twisty = true;
       cookieVar.expanded = true;
     }
   }),
 
   /**
    * Sets the network request get params shown in this view.
    *
-   * @param string aUrl
+   * @param string url
    *        The request's url.
    */
-  _setRequestGetParams: function(aUrl) {
-    let query = NetworkHelper.nsIURL(aUrl).query;
+  _setRequestGetParams: function(url) {
+    let query = NetworkHelper.nsIURL(url).query;
     if (query) {
       this._addParams(this._paramsQueryString, query);
     }
   },
 
   /**
    * Sets the network request post params shown in this view.
    *
-   * @param object aHeaders
+   * @param object headers
    *        The "requestHeaders" message received from the server.
-   * @param object aUploadHeaders
+   * @param object uploadHeaders
    *        The "requestHeadersFromUploadStream" inferred from the POST payload.
-   * @param object aPostData
+   * @param object postData
    *        The "requestPostData" message received from the server.
    * @return object
    *        A promise that is resolved when the request post params are set.
    */
-  _setRequestPostParams: Task.async(function*(aHeaders, aUploadHeaders, aPostData) {
-    if (!aHeaders || !aUploadHeaders || !aPostData) {
+  _setRequestPostParams: Task.async(function*(headers, uploadHeaders,
+    postData) {
+    if (!headers || !uploadHeaders || !postData) {
       return;
     }
 
-    let formDataSections = yield RequestsMenuView.prototype._getFormDataSections(
-      aHeaders, aUploadHeaders, aPostData);
+    let formDataSections = yield RequestsMenuView.prototype
+      ._getFormDataSections(headers, uploadHeaders, postData);
 
     this._params.onlyEnumVisible = false;
 
     // Handle urlencoded form data sections (e.g. "?foo=bar&baz=42").
     if (formDataSections.length > 0) {
       formDataSections.forEach(section => {
         this._addParams(this._paramsFormData, section);
       });
-    }
-    // Handle JSON and actual forms ("multipart/form-data" content type).
-    else {
-      let postDataLongString = aPostData.postData.text;
-      let postData = yield gNetwork.getString(postDataLongString);
+    } else {
+      // Handle JSON and actual forms ("multipart/form-data" content type).
+      let postDataLongString = postData.postData.text;
+      let text = yield gNetwork.getString(postDataLongString);
       let jsonVal = null;
       try {
-        jsonVal = JSON.parse(postData);
-      } catch (ex) { }
+        jsonVal = JSON.parse(text);
+      } catch (ex) { // eslint-disable-line
+      }
+
       if (jsonVal) {
         this._params.onlyEnumVisible = true;
         let jsonScopeName = L10N.getStr("jsonScopeName");
-        let jsonVar = { label: jsonScopeName, rawObject: jsonVal };
         let jsonScope = this._params.addScope(jsonScopeName);
         jsonScope.expanded = true;
         let jsonItem = jsonScope.addItem("", { enumerable: true });
         jsonItem.populate(jsonVal, { sorted: true });
       } else {
         // This is really awkward, but hey, it works. Let's show an empty
         // scope in the params view and place the source editor containing
         // the raw post data directly underneath.
         $("#request-params-box").removeAttribute("flex");
         let paramsScope = this._params.addScope(this._paramsPostPayload);
         paramsScope.expanded = true;
         paramsScope.locked = true;
 
         $("#request-post-data-textarea-box").hidden = false;
         let editor = yield NetMonitorView.editor("#request-post-data-textarea");
         editor.setMode(Editor.modes.text);
-        editor.setText(postData);
+        editor.setText(text);
       }
     }
 
     window.emit(EVENTS.REQUEST_POST_PARAMS_DISPLAYED);
   }),
 
   /**
    * Populates the params container in this view with the specified data.
    *
-   * @param string aName
+   * @param string name
    *        The type of params to populate (get or post).
-   * @param string aQueryString
+   * @param string queryString
    *        A query string of params (e.g. "?foo=bar&baz=42").
    */
-  _addParams: function(aName, aQueryString) {
-    let paramsArray = NetworkHelper.parseQueryString(aQueryString);
+  _addParams: function(name, queryString) {
+    let paramsArray = NetworkHelper.parseQueryString(queryString);
     if (!paramsArray) {
       return;
     }
-    let paramsScope = this._params.addScope(aName);
+    let paramsScope = this._params.addScope(name);
     paramsScope.expanded = true;
 
     for (let param of paramsArray) {
       let paramVar = paramsScope.addItem(param.name, {}, {relaxed: true});
       paramVar.setGrip(param.value);
     }
   },
 
   /**
    * Sets the network response body shown in this view.
    *
-   * @param string aUrl
+   * @param string url
    *        The request's url.
-   * @param object aResponse
+   * @param object response
    *        The message received from the server.
    * @return object
    *         A promise that is resolved when the response body is set.
    */
-  _setResponseBody: Task.async(function*(aUrl, aResponse) {
-    if (!aResponse) {
+  _setResponseBody: Task.async(function*(url, response) {
+    if (!response) {
       return;
     }
-    let { mimeType, text, encoding } = aResponse.content;
+    let { mimeType, text, encoding } = response.content;
     let responseBody = yield gNetwork.getString(text);
 
     // Handle json, which we tentatively identify by checking the MIME type
     // for "json" after any word boundary. This works for the standard
     // "application/json", and also for custom types like "x-bigcorp-json".
     // Additionally, we also directly parse the response text content to
     // verify whether it's json or not, to handle responses incorrectly
     // labeled as text/plain instead.
@@ -2997,17 +3156,18 @@ NetworkDetailsView.prototype = {
     } catch (e) {
       jsonObjectParseError = e;
     }
     if (jsonMimeType || jsonObject) {
       // Extract the actual json substring in case this might be a "JSONP".
       // This regex basically parses a function call and captures the
       // function name and arguments in two separate groups.
       let jsonpRegex = /^\s*([\w$]+)\s*\(\s*([^]*)\s*\)\s*;?\s*$/;
-      let [_, callbackPadding, jsonpString] = responseBody.match(jsonpRegex) || [];
+      let [_, callbackPadding, jsonpString] = // eslint-disable-line
+        responseBody.match(jsonpRegex) || [];
 
       // Make sure this is a valid JSON object first. If so, nicely display
       // the parsing results in a variables view. Otherwise, simply show
       // the contents as plain text.
       if (callbackPadding && jsonpString) {
         try {
           jsonObject = JSON.parse(jsonpString);
         } catch (e) {
@@ -3019,89 +3179,92 @@ NetworkDetailsView.prototype = {
       if (jsonObject) {
         $("#response-content-json-box").hidden = false;
         let jsonScopeName = callbackPadding
           ? L10N.getFormatStr("jsonpScopeName", callbackPadding)
           : L10N.getStr("jsonScopeName");
 
         let jsonVar = { label: jsonScopeName, rawObject: jsonObject };
         yield this._json.controller.setSingleVariable(jsonVar).expanded;
-      }
-      // Malformed JSON.
-      else {
+      } else {
+        // Malformed JSON.
         $("#response-content-textarea-box").hidden = false;
         let infoHeader = $("#response-content-info-header");
         infoHeader.setAttribute("value", jsonObjectParseError);
         infoHeader.setAttribute("tooltiptext", jsonObjectParseError);
         infoHeader.hidden = false;
 
         let editor = yield NetMonitorView.editor("#response-content-textarea");
         editor.setMode(Editor.modes.js);
         editor.setText(responseBody);
       }
-    }
-    // Handle images.
-    else if (mimeType.includes("image/")) {
+    } else if (mimeType.includes("image/")) {
+      // Handle images.
       $("#response-content-image-box").setAttribute("align", "center");
       $("#response-content-image-box").setAttribute("pack", "center");
       $("#response-content-image-box").hidden = false;
       $("#response-content-image").src =
         formDataURI(mimeType, encoding, responseBody);
 
       // Immediately display additional information about the image:
       // file name, mime type and encoding.
-      $("#response-content-image-name-value").setAttribute("value", NetworkHelper.nsIURL(aUrl).fileName);
+      $("#response-content-image-name-value").setAttribute("value",
+        NetworkHelper.nsIURL(url).fileName);
       $("#response-content-image-mime-value").setAttribute("value", mimeType);
 
       // Wait for the image to load in order to display the width and height.
       $("#response-content-image").onload = e => {
         // XUL images are majestic so they don't bother storing their dimensions
         // in width and height attributes like the rest of the folk. Hack around
         // this by getting the bounding client rect and subtracting the margins.
         let { width, height } = e.target.getBoundingClientRect();
         let dimensions = (width - 2) + " \u00D7 " + (height - 2);
-        $("#response-content-image-dimensions-value").setAttribute("value", dimensions);
+        $("#response-content-image-dimensions-value").setAttribute("value",
+          dimensions);
       };
-    }
-    // Handle anything else.
-    else {
+    } else {
       $("#response-content-textarea-box").hidden = false;
       let editor = yield NetMonitorView.editor("#response-content-textarea");
       editor.setMode(Editor.modes.text);
       editor.setText(responseBody);
 
       // Maybe set a more appropriate mode in the Source Editor if possible,
       // but avoid doing this for very large files.
       if (responseBody.length < SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE) {
-        let mapping = Object.keys(CONTENT_MIME_TYPE_MAPPINGS).find(key => mimeType.includes(key));
+        let mapping = Object.keys(CONTENT_MIME_TYPE_MAPPINGS).find(key => {
+          return mimeType.includes(key);
+        });
+
         if (mapping) {
           editor.setMode(CONTENT_MIME_TYPE_MAPPINGS[mapping]);
         }
       }
     }
 
     window.emit(EVENTS.RESPONSE_BODY_DISPLAYED);
   }),
 
   /**
    * Sets the timings information shown in this view.
    *
-   * @param object aResponse
+   * @param object response
    *        The message received from the server.
    */
-  _setTimingsInformation: function(aResponse) {
-    if (!aResponse) {
+  _setTimingsInformation: function(response) {
+    if (!response) {
       return;
     }
-    let { blocked, dns, connect, send, wait, receive } = aResponse.timings;
+    let { blocked, dns, connect, send, wait, receive } = response.timings;
 
     let tabboxWidth = $("#details-pane").getAttribute("width");
-    let availableWidth = tabboxWidth / 2; // Other nodes also take some space.
-    let scale = (aResponse.totalTime > 0 ?
-                 Math.max(availableWidth / aResponse.totalTime, 0) :
+
+    // Other nodes also take some space.
+    let availableWidth = tabboxWidth / 2;
+    let scale = (response.totalTime > 0 ?
+                 Math.max(availableWidth / response.totalTime, 0) :
                  0);
 
     $("#timings-summary-blocked .requests-menu-timings-box")
       .setAttribute("width", blocked * scale);
     $("#timings-summary-blocked .requests-menu-timings-total")
       .setAttribute("value", L10N.getFormatStr("networkMenu.totalMS", blocked));
 
     $("#timings-summary-dns .requests-menu-timings-box")
@@ -3129,47 +3292,55 @@ NetworkDetailsView.prototype = {
     $("#timings-summary-receive .requests-menu-timings-total")
       .setAttribute("value", L10N.getFormatStr("networkMenu.totalMS", receive));
 
     $("#timings-summary-dns .requests-menu-timings-box")
       .style.transform = "translateX(" + (scale * blocked) + "px)";
     $("#timings-summary-connect .requests-menu-timings-box")
       .style.transform = "translateX(" + (scale * (blocked + dns)) + "px)";
     $("#timings-summary-send .requests-menu-timings-box")
-      .style.transform = "translateX(" + (scale * (blocked + dns + connect)) + "px)";
+      .style.transform =
+        "translateX(" + (scale * (blocked + dns + connect)) + "px)";
     $("#timings-summary-wait .requests-menu-timings-box")
-      .style.transform = "translateX(" + (scale * (blocked + dns + connect + send)) + "px)";
+      .style.transform =
+        "translateX(" + (scale * (blocked + dns + connect + send)) + "px)";
     $("#timings-summary-receive .requests-menu-timings-box")
-      .style.transform = "translateX(" + (scale * (blocked + dns + connect + send + wait)) + "px)";
+      .style.transform =
+        "translateX(" + (scale * (blocked + dns + connect + send + wait)) +
+          "px)";
 
     $("#timings-summary-dns .requests-menu-timings-total")
       .style.transform = "translateX(" + (scale * blocked) + "px)";
     $("#timings-summary-connect .requests-menu-timings-total")
       .style.transform = "translateX(" + (scale * (blocked + dns)) + "px)";
     $("#timings-summary-send .requests-menu-timings-total")
-      .style.transform = "translateX(" + (scale * (blocked + dns + connect)) + "px)";
+      .style.transform =
+        "translateX(" + (scale * (blocked + dns + connect)) + "px)";
     $("#timings-summary-wait .requests-menu-timings-total")
-      .style.transform = "translateX(" + (scale * (blocked + dns + connect + send)) + "px)";
+      .style.transform =
+        "translateX(" + (scale * (blocked + dns + connect + send)) + "px)";
     $("#timings-summary-receive .requests-menu-timings-total")
-      .style.transform = "translateX(" + (scale * (blocked + dns + connect + send + wait)) + "px)";
+      .style.transform =
+        "translateX(" + (scale * (blocked + dns + connect + send + wait)) +
+         "px)";
   },
 
   /**
    * Sets the preview for HTML responses shown in this view.
    *
-   * @param object aResponse
+   * @param object response
    *        The message received from the server.
    * @return object
    *        A promise that is resolved when the html preview is rendered.
    */
-  _setHtmlPreview: Task.async(function*(aResponse) {
-    if (!aResponse) {
+  _setHtmlPreview: Task.async(function*(response) {
+    if (!response) {
       return promise.resolve();
     }
-    let { text } = aResponse.content;
+    let { text } = response.content;
     let responseBody = yield gNetwork.getString(text);
 
     // Always disable JS when previewing HTML responses.
     let iframe = $("#response-preview");
     iframe.contentDocument.docShell.allowJavascript = false;
     iframe.contentDocument.documentElement.innerHTML = responseBody;
 
     window.emit(EVENTS.RESPONSE_HTML_PREVIEW_DISPLAYED);
@@ -3204,17 +3375,18 @@ NetworkDetailsView.prototype = {
      *        A selector for the element.
      * @param string value
      *        The value to set. If this evaluates to false a placeholder string
      *        <Not Available> is used instead.
      */
     function setValue(selector, value) {
       let label = $(selector);
       if (!value) {
-        label.setAttribute("value", L10N.getStr("netmonitor.security.notAvailable"));
+        label.setAttribute("value", L10N.getStr(
+          "netmonitor.security.notAvailable"));
         label.setAttribute("tooltiptext", label.getAttribute("value"));
       } else {
         label.setAttribute("value", value);
         label.setAttribute("tooltiptext", value);
       }
     }
 
     let errorbox = $("#security-error");
@@ -3232,22 +3404,24 @@ NetworkDetailsView.prototype = {
       } else {
         cipher.hidden = true;
       }
 
       let enabledLabel = L10N.getStr("netmonitor.security.enabled");
       let disabledLabel = L10N.getStr("netmonitor.security.disabled");
 
       // Connection parameters
-      setValue("#security-protocol-version-value", securityInfo.protocolVersion);
+      setValue("#security-protocol-version-value",
+        securityInfo.protocolVersion);
       setValue("#security-ciphersuite-value", securityInfo.cipherSuite);
 
       // Host header
       let domain = NetMonitorView.RequestsMenu._getUriHostPort(url);
-      let hostHeader = L10N.getFormatStr("netmonitor.security.hostHeader", domain);
+      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);
 
       setValue("#security-public-key-pinning-value",
                 securityInfo.hpkp ? enabledLabel : disabledLabel);
@@ -3267,17 +3441,18 @@ NetworkDetailsView.prototype = {
 
       setValue("#security-cert-sha1-fingerprint", cert.fingerprint.sha1);
       setValue("#security-cert-sha256-fingerprint", cert.fingerprint.sha256);
     } else {
       infobox.hidden = true;
       errorbox.hidden = false;
 
       // Strip any HTML from the message.
-      let plain = DOMParser.parseFromString(securityInfo.errorMessage, "text/html");
+      let plain = DOMParser.parseFromString(securityInfo.errorMessage,
+        "text/html");
       setValue("#security-error-message", plain.body.textContent);
     }
   }),
 
   _dataSrc: null,
   _headers: null,
   _cookies: null,
   _params: null,
@@ -3311,42 +3486,42 @@ PerformanceStatisticsView.prototype = {
       title: "charts.cacheDisabled"
     });
     window.emit(EVENTS.PLACEHOLDER_CHARTS_DISPLAYED);
   },
 
   /**
    * Populates and displays the primed cache chart in this container.
    *
-   * @param array aItems
+   * @param array items
    *        @see this._sanitizeChartDataSource
    */
-  createPrimedCacheChart: function(aItems) {
+  createPrimedCacheChart: function(items) {
     this._createChart({
       id: "#primed-cache-chart",
       title: "charts.cacheEnabled",
-      data: this._sanitizeChartDataSource(aItems),
+      data: this._sanitizeChartDataSource(items),
       strings: this._commonChartStrings,
       totals: this._commonChartTotals,
       sorted: true
     });
     window.emit(EVENTS.PRIMED_CACHE_CHART_DISPLAYED);
   },
 
   /**
    * Populates and displays the empty cache chart in this container.
    *
-   * @param array aItems
+   * @param array items
    *        @see this._sanitizeChartDataSource
    */
-  createEmptyCacheChart: function(aItems) {
+  createEmptyCacheChart: function(items) {
     this._createChart({
       id: "#empty-cache-chart",
       title: "charts.cacheDisabled",
-      data: this._sanitizeChartDataSource(aItems, true),
+      data: this._sanitizeChartDataSource(items, true),
       strings: this._commonChartStrings,
       totals: this._commonChartTotals,
       sorted: true
     });
     window.emit(EVENTS.EMPTY_CACHE_CHART_DISPLAYED);
   },
 
   /**
@@ -3366,17 +3541,18 @@ PerformanceStatisticsView.prototype = {
   _commonChartTotals: {
     size: total => {
       let string = L10N.numberWithDecimals(total / 1024, CONTENT_SIZE_DECIMALS);
       return L10N.getFormatStr("charts.totalSize", string);
     },
     time: total => {
       let seconds = total / 1000;
       let string = L10N.numberWithDecimals(seconds, REQUEST_TIME_DECIMALS);
-      return PluralForm.get(seconds, L10N.getStr("charts.totalSeconds")).replace("#1", string);
+      return PluralForm.get(seconds,
+        L10N.getStr("charts.totalSeconds")).replace("#1", string);
     },
     cached: total => {
       return L10N.getFormatStr("charts.totalCached", total);
     },
     count: total => {
       return L10N.getFormatStr("charts.totalCount", total);
     }
   },
@@ -3414,156 +3590,166 @@ PerformanceStatisticsView.prototype = {
 
     container.appendChild(chart.node);
   },
 
   /**
    * Sanitizes the data source used for creating charts, to follow the
    * data format spec defined in Chart.jsm.
    *
-   * @param array aItems
+   * @param array items
    *        A collection of request items used as the data source for the chart.
-   * @param boolean aEmptyCache
+   * @param boolean emptyCache
    *        True if the cache is considered enabled, false for disabled.
    */
-  _sanitizeChartDataSource: function(aItems, aEmptyCache) {
+  _sanitizeChartDataSource: function(items, emptyCache) {
     let data = [
       "html", "css", "js", "xhr", "fonts", "images", "media", "flash", "other"
     ].map(e => ({
       cached: 0,
       count: 0,
       label: e,
       size: 0,
       time: 0
     }));
 
-    for (let requestItem of aItems) {
+    for (let requestItem of items) {
       let details = requestItem.attachment;
       let type;
 
       if (RequestsMenuView.prototype.isHtml(requestItem)) {
-        type = 0; // "html"
+        // "html"
+        type = 0;
       } else if (RequestsMenuView.prototype.isCss(requestItem)) {
-        type = 1; // "css"
+        // "css"
+        type = 1;
       } else if (RequestsMenuView.prototype.isJs(requestItem)) {
-        type = 2; // "js"
+        // "js"
+        type = 2;
       } else if (RequestsMenuView.prototype.isFont(requestItem)) {
-        type = 4; // "fonts"
+        // "fonts"
+        type = 4;
       } else if (RequestsMenuView.prototype.isImage(requestItem)) {
-        type = 5; // "images"
+        // "images"
+        type = 5;
       } else if (RequestsMenuView.prototype.isMedia(requestItem)) {
-        type = 6; // "media"
+        // "media"
+        type = 6;
       } else if (RequestsMenuView.prototype.isFlash(requestItem)) {
-        type = 7; // "flash"
+        // "flash"
+        type = 7;
       } else if (RequestsMenuView.prototype.isXHR(requestItem)) {
         // Verify XHR last, to categorize other mime types in their own blobs.
-        type = 3; // "xhr"
+        // "xhr"
+        type = 3;
       } else {
-        type = 8; // "other"
+        // "other"
+        type = 8;
       }
 
-      if (aEmptyCache || !responseIsFresh(details)) {
+      if (emptyCache || !responseIsFresh(details)) {
         data[type].time += details.totalTime || 0;
         data[type].size += details.contentSize || 0;
       } else {
         data[type].cached++;
       }
       data[type].count++;
     }
 
     return data.filter(e => e.count > 0);
   },
 };
 
 /**
  * DOM query helper.
  */
-var $ = (aSelector, aTarget = document) => aTarget.querySelector(aSelector);
-var $all = (aSelector, aTarget = document) => aTarget.querySelectorAll(aSelector);
+var $ = (selector, target = document) => target.querySelector(selector);
+var $all = (selector, target = document) => target.querySelectorAll(selector);
 
 /**
  * Parse text representation of multiple HTTP headers.
  *
- * @param string aText
+ * @param string text
  *        Text of headers
  * @return array
  *         Array of headers info {name, value}
  */
-function parseHeadersText(aText) {
-  return parseRequestText(aText, "\\S+?", ":");
+function parseHeadersText(text) {
+  return parseRequestText(text, "\\S+?", ":");
 }
 
 /**
  * Parse readable text list of a query string.
  *
- * @param string aText
+ * @param string text
  *        Text of query string represetation
  * @return array
  *         Array of query params {name, value}
  */
-function parseQueryText(aText) {
-  return parseRequestText(aText, ".+?", "=");
+function parseQueryText(text) {
+  return parseRequestText(text, ".+?", "=");
 }
 
 /**
  * Parse a text representation of a name[divider]value list with
  * the given name regex and divider character.
  *
- * @param string aText
+ * @param string text
  *        Text of list
  * @return array
  *         Array of headers info {name, value}
  */
-function parseRequestText(aText, aName, aDivider) {
-  let regex = new RegExp("(" + aName + ")\\" + aDivider + "\\s*(.+)");
+function parseRequestText(text, namereg, divider) {
+  let regex = new RegExp("(" + namereg + ")\\" + divider + "\\s*(.+)");
   let pairs = [];
-  for (let line of aText.split("\n")) {
+
+  for (let line of text.split("\n")) {
     let matches;
-    if (matches = regex.exec(line)) {
+    if (matches = regex.exec(line)) { // eslint-disable-line
       let [, name, value] = matches;
       pairs.push({name: name, value: value});
     }
   }
   return pairs;
 }
 
 /**
  * Write out a list of headers into a chunk of text
  *
- * @param array aHeaders
+ * @param array headers
  *        Array of headers info {name, value}
- * @return string aText
+ * @return string text
  *         List of headers in text format
  */
-function writeHeaderText(aHeaders) {
-  return aHeaders.map(({name, value}) => name + ": " + value).join("\n");
+function writeHeaderText(headers) {
+  return headers.map(({name, value}) => name + ": " + value).join("\n");
 }
 
 /**
  * Write out a list of query params into a chunk of text
  *
- * @param array aParams
+ * @param array params
  *        Array of query params {name, value}
  * @return string
  *         List of query params in text format
  */
-function writeQueryText(aParams) {
-  return aParams.map(({name, value}) => name + "=" + value).join("\n");
+function writeQueryText(params) {
+  return params.map(({name, value}) => name + "=" + value).join("\n");
 }
 
 /**
  * Write out a list of query params into a query string
  *
- * @param array aParams
+ * @param array params
  *        Array of query  params {name, value}
  * @return string
  *         Query string that can be appended to a url.
  */
-function writeQueryString(aParams) {
-  return aParams.map(({name, value}) => name + "=" + value).join("&");
+function writeQueryString(params) {
+  return params.map(({name, value}) => name + "=" + value).join("&");
 }
 
 /**
  * Checks if the "Expiration Calculations" defined in section 13.2.4 of the
  * "HTTP/1.1: Caching in HTTP" spec holds true for a collection of headers.
  *
  * @param object
  *        An object containing the { responseHeaders, status } properties.
@@ -3572,17 +3758,20 @@ function writeQueryString(aParams) {
  */
 function responseIsFresh({ responseHeaders, status }) {
   // Check for a "304 Not Modified" status and response headers availability.
   if (status != 304 || !responseHeaders) {
     return false;
   }
 
   let list = responseHeaders.headers;
-  let cacheControl = list.filter(e => e.name.toLowerCase() == "cache-control")[0];
+  let cacheControl = list.filter(e => {
+    return e.name.toLowerCase() == "cache-control";
+  })[0];
+
   let expires = list.filter(e => e.name.toLowerCase() == "expires")[0];
 
   // Check the "Cache-Control" header for a maximum age value.
   if (cacheControl) {
     let maxAgeMatch =
       cacheControl.value.match(/s-maxage\s*=\s*(\d+)/) ||
       cacheControl.value.match(/max-age\s*=\s*(\d+)/);
 
@@ -3595,26 +3784,29 @@ function responseIsFresh({ responseHeade
   if (expires && Date.parse(expires.value)) {
     return true;
   }
 
   return false;
 }
 
 /**
- * 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.
+ * 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 is present in event.target.
+ *          Function to execute execute when data-key
+ *          is present in event.target.
  * @return function
  *          Wrapped function with the target data-key as the first argument.
  */
 function getKeyWithEvent(callback) {
   return function(event) {
-    var key = event.target.getAttribute("data-key");
+    let key = event.target.getAttribute("data-key");
     if (key) {
       callback.call(null, key);
     }
   };
 }
 
 /**
  * Form a data: URI given a mime type, encoding, and some text.
@@ -3629,16 +3821,48 @@ function formDataURI(mimeType, encoding,
   if (!encoding) {
     encoding = "base64";
     text = btoa(text);
   }
   return "data:" + mimeType + ";" + encoding + "," + text;
 }
 
 /**
+ * Makes sure certain properties are available on all objects in a data store.
+ *
+ * @param array dataStore
+ *        A list of objects for which to check the availability of properties.
+ * @param array mandatoryFields
+ *        A list of strings representing properties of objects in dataStore.
+ * @return object
+ *         A promise resolved when all objects in dataStore contain the
+ *         properties defined in mandatoryFields.
+ */
+function whenDataAvailable(dataStore, mandatoryFields) {
+  let deferred = promise.defer();
+
+  let interval = setInterval(() => {
+    if (dataStore.every(item => {
+      return mandatoryFields.every(field => field in item);
+    })) {
+      clearInterval(interval);
+      clearTimeout(timer);
+      deferred.resolve();
+    }
+  }, WDA_DEFAULT_VERIFY_INTERVAL);
+
+  let timer = setTimeout(() => {
+    clearInterval(interval);
+    deferred.reject(new Error("Timed out while waiting for data"));
+  }, WDA_DEFAULT_GIVE_UP_TIMEOUT);
+
+  return deferred.promise;
+}
+
+/**
  * Preliminary setup for the NetMonitorView object.
  */
 NetMonitorView.Toolbar = new ToolbarView();
 NetMonitorView.RequestsMenu = new RequestsMenuView();
 NetMonitorView.Sidebar = new SidebarView();
 NetMonitorView.CustomRequest = new CustomRequestView();
 NetMonitorView.NetworkDetails = new NetworkDetailsView();
 NetMonitorView.PerformanceStatistics = new PerformanceStatisticsView();
--- a/devtools/client/netmonitor/panel.js
+++ b/devtools/client/netmonitor/panel.js
@@ -1,30 +1,28 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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 { Cc, Ci, Cu, Cr } = require("chrome");
 const promise = require("promise");
 const EventEmitter = require("devtools/shared/event-emitter");
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 
 function NetMonitorPanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
   this._toolbox = toolbox;
 
   this._view = this.panelWin.NetMonitorView;
   this._controller = this.panelWin.NetMonitorController;
   this._controller._target = this.target;
 
   EventEmitter.decorate(this);
-};
+}
 
 exports.NetMonitorPanel = NetMonitorPanel;
 
 NetMonitorPanel.prototype = {
   /**
    * Open is effectively an asynchronous constructor.
    *
    * @return object
--- a/devtools/client/shared/components/frame.js
+++ b/devtools/client/shared/components/frame.js
@@ -1,16 +1,16 @@
 /* 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 { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
-const { getSourceNames } = require("devtools/client/shared/source-utils");
+const { getSourceNames, parseURL } = require("devtools/client/shared/source-utils");
 const { L10N } = require("resource://devtools/client/shared/widgets/ViewHelpers.jsm").ViewHelpers;
 const l10n = new L10N("chrome://devtools/locale/components.properties");
 
 module.exports = createClass({
   propTypes: {
     // SavedFrame, or an object containing all the required properties.
     frame: PropTypes.shape({
       functionDisplayName: PropTypes.string,
@@ -33,64 +33,77 @@ module.exports = createClass({
     };
   },
 
   displayName: "Frame",
 
   render() {
     let { onClick, frame, showFunctionName, showHost } = this.props;
     const { short, long, host } = getSourceNames(frame.source);
+    // Reparse the URL to determine if we should link this; `getSourceNames`
+    // has already cached this indirectly. We don't want to attempt to
+    // link to "self-hosted" and "(unknown)".
+    const isLinkable = !!parseURL(frame.source);
+    const elements = [];
 
     let tooltip = long;
     // Exclude all falsy values, including `0`, as even
     // a number 0 for line doesn't make sense, and should not be displayed.
-    if (frame.line) {
+    // If source isn't linkable, don't attempt to append line and column
+    // info, as this probably doesn't make sense.
+    if (isLinkable && frame.line) {
       tooltip += `:${frame.line}`;
       // Intentionally exclude 0
       if (frame.column) {
         tooltip += `:${frame.column}`;
       }
     }
 
     let onClickTooltipString = l10n.getFormatStr("frame.viewsourceindebugger", tooltip);
     let attributes = {
       "data-url": long,
       className: "frame-link",
       title: tooltip,
     };
 
-    let fields = [
-      dom.a({
+    if (isLinkable) {
+      elements.push(dom.a({
         className: "frame-link-filename",
         onClick,
         title: onClickTooltipString
-      }, short)
-    ];
+      }, short));
+    } else {
+      // If source is not a URL (self-hosted, eval, etc.), don't make
+      // it an anchor link, as we can't link to it.
+      elements.push(dom.span({
+        className: "frame-link-filename"
+      }, short));
+    }
 
-    // Intentionally exclude 0
-    if (frame.line) {
-      fields.push(dom.span({ className: "frame-link-colon" }, ":"));
-      fields.push(dom.span({ className: "frame-link-line" }, frame.line));
+    // If source is linkable, and we have a line number > 0
+    if (isLinkable && frame.line) {
+      elements.push(dom.span({ className: "frame-link-colon" }, ":"));
+      elements.push(dom.span({ className: "frame-link-line" }, frame.line));
       // Intentionally exclude 0
       if (frame.column) {
-        fields.push(dom.span({ className: "frame-link-colon" }, ":"));
-        fields.push(dom.span({ className: "frame-link-column" }, frame.column));
+        elements.push(dom.span({ className: "frame-link-colon" }, ":"));
+        elements.push(dom.span({ className: "frame-link-column" }, frame.column));
         // Add `data-column` attribute for testing
         attributes["data-column"] = frame.column;
       }
 
       // Add `data-line` attribute for testing
       attributes["data-line"] = frame.line;
     }
 
     if (showFunctionName && frame.functionDisplayName) {
-      fields.unshift(
+      elements.unshift(
         dom.span({ className: "frame-link-function-display-name" }, frame.functionDisplayName)
       );
     }
 
     if (showHost && host) {
-      fields.push(dom.span({ className: "frame-link-host" }, host));
+      elements.push(dom.span({ className: "frame-link-host" }, host));
     }
 
-    return dom.span(attributes, ...fields);
+    return dom.span(attributes, ...elements);
   }
 });
--- a/devtools/client/shared/components/moz.build
+++ b/devtools/client/shared/components/moz.build
@@ -1,16 +1,17 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 DIRS += [
     'reps',
+    'tree'
 ]
 
 DevToolsModules(
     'frame.js',
     'h-split-box.js',
     'tree.js',
 )
 
--- a/devtools/client/shared/components/reps/array.js
+++ b/devtools/client/shared/components/reps/array.js
@@ -6,17 +6,16 @@
 
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function(require, exports, module) {
   // Dependencies
   const React = require("devtools/client/shared/vendor/react");
   const { createFactories } = require("./rep-utils");
-  const { Rep } = createFactories(require("./rep"));
   const { ObjectBox } = createFactories(require("./object-box"));
   const { Caption } = createFactories(require("./caption"));
 
   // Shortcuts
   const DOM = React.DOM;
 
   /**
    * Renders an array. The array is enclosed by left and right bracket
@@ -164,16 +163,18 @@ define(function(require, exports, module
 
   /**
    * Renders array item. Individual values are separated by a comma.
    */
   let ItemRep = React.createFactory(React.createClass({
     displayName: "ItemRep",
 
     render: function() {
+      const { Rep } = createFactories(require("./rep"));
+
       let object = this.props.object;
       let delim = this.props.delim;
       return (
         DOM.span({},
           Rep({object: object}),
           delim
         )
       );
--- a/devtools/client/shared/components/test/mochitest/head.js
+++ b/devtools/client/shared/components/test/mochitest/head.js
@@ -132,38 +132,44 @@ var TEST_TREE = {
     O: "N"
   },
   expanded: new Set(),
 };
 
 /**
  * Frame
  */
-function checkFrameString (component, file, line, column) {
-  let el = component.getDOMNode();
+function checkFrameString({ frame, file, line, column, shouldLink }) {
+  let el = frame.getDOMNode();
   let $ = selector => el.querySelector(selector);
 
   let $line = $(".frame-link-line");
   let $column = $(".frame-link-column");
 
   is($(".frame-link-filename").textContent, file);
 
   is(el.getAttribute("data-line"), line ? `${line}` : null, "Expected `data-line` found");
   is(el.getAttribute("data-column"), column ? `${column}` : null, "Expected `data-column` found");
 
+  if (shouldLink) {
+    ok($("a.frame-link-filename"), "Should have a linkable filename");
+  } else {
+    ok(!$("a.frame-link-filename"), "Should not have a linkable filename");
+  }
+
   if (line != null) {
     is(+$line.textContent, +line);
   } else {
     ok(!$line, "Should not have an element for `line`");
   }
 
   if (column != null) {
     is(+$column.textContent, +column);
   } else {
     ok(!$column, "Should not have an element for `column`");
   }
 }
 
 function checkFrameTooltips (component, mainTooltip, linkTooltip) {
   let el = component.getDOMNode();
   is(el.getAttribute("title"), mainTooltip);
-  is(el.querySelector("a.frame-link-filename").getAttribute("title"), linkTooltip);
+  is(el.querySelector(".frame-link-filename").getAttribute("title"), linkTooltip);
 }
--- a/devtools/client/shared/components/test/mochitest/test_frame_01.html
+++ b/devtools/client/shared/components/test/mochitest/test_frame_01.html
@@ -27,86 +27,120 @@ window.onload = Task.async(function* () 
       frame: {
         source: "http://myfile.com/mahscripts.js",
         line: 55,
         column: 10,
       },
       onClick: ()=>{},
     }), window.document.body);
     yield forceRender(frame);
-    checkFrameString(frame, "mahscripts.js", 55, 10);
+    checkFrameString({
+      frame,
+      file: "mahscripts.js",
+      line: 55,
+      column: 10,
+      shouldLink: true,
+    });
 
     // Check when there's no column
     frame = ReactDOM.render(Frame({
       frame: {
         source: "http://myfile.com/mahscripts.js",
         line: 55,
       },
       onClick: ()=>{},
     }), window.document.body);
     yield forceRender(frame);
-    checkFrameString(frame, "mahscripts.js", 55);
+    checkFrameString({
+      frame,
+      file: "mahscripts.js",
+      line: 55,
+      shouldLink: true,
+    });
 
     // Check when column === 0
     frame = ReactDOM.render(Frame({
       frame: {
         source: "http://myfile.com/mahscripts.js",
         line: 55,
         column: 0,
       },
       onClick: ()=>{},
     }), window.document.body);
     yield forceRender(frame);
-    checkFrameString(frame, "mahscripts.js", 55);
+    checkFrameString({
+      frame,
+      file: "mahscripts.js",
+      line: 55,
+      shouldLink: true,
+    });
 
-    // Check when there's no parseable URL source
+    // Check when there's no parseable URL source;
+    // should not render line/columns
     frame = ReactDOM.render(Frame({
       frame: {
         source: "self-hosted",
         line: 1,
       },
       onClick: ()=>{},
     }), window.document.body);
     yield forceRender(frame);
-    checkFrameString(frame, "self-hosted",1);
+    checkFrameString({
+      frame,
+      file: "self-hosted",
+      shouldLink: false,
+    });
 
-    // Check when there's no source
+    // Check when there's no source;
+    // should not render line/columns
     frame = ReactDOM.render(Frame({
       frame: {
         line: 1,
       },
       onClick: ()=>{},
     }), window.document.body);
     yield forceRender(frame);
-    checkFrameString(frame, "(unknown)",1);
+    checkFrameString({
+      frame,
+      file: "(unknown)",
+      shouldLink: false,
+    });
 
     // Check when there's a column, but no number;
     // no line/column info should render
     frame = ReactDOM.render(Frame({
       frame: {
         source: "http://myfile.com/mahscripts.js",
         column: 55,
       },
       onClick: ()=>{},
     }), window.document.body);
     yield forceRender(frame);
-    checkFrameString(frame, "mahscripts.js");
+    checkFrameString({
+      frame,
+      file: "mahscripts.js",
+      shouldLink: true,
+    });
 
     // Check when line is 0; this should be an invalid
     // line option, so don't render line/column
     frame = ReactDOM.render(Frame({
       frame: {
         source: "http://myfile.com/mahscripts.js",
         line: 0,
         column: 55,
       },
       onClick: ()=>{},
     }), window.document.body);
     yield forceRender(frame);
-    checkFrameString(frame, "mahscripts.js");
+    checkFrameString({
+      frame,
+      file: "mahscripts.js",
+      shouldLink: true,
+    });
 
   } catch (e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 });
 </script>
--- a/devtools/client/shared/components/test/mochitest/test_frame_02.html
+++ b/devtools/client/shared/components/test/mochitest/test_frame_02.html
@@ -53,30 +53,30 @@ window.onload = Task.async(function* () 
       frame: {
         source: "self-hosted",
         line: 1,
       },
       onClick: ()=>{},
     }), window.document.body);
     yield forceRender(frame);
     checkFrameTooltips(frame,
-      "self-hosted:1",
-      "View source in Debugger → self-hosted:1");
+      "self-hosted",
+      null);
 
     // Check when there's no source
     frame = ReactDOM.render(Frame({
       frame: {
         line: 1,
       },
       onClick: ()=>{},
     }), window.document.body);
     yield forceRender(frame);
     checkFrameTooltips(frame,
-      "(unknown):1",
-      "View source in Debugger → (unknown):1");
+      "(unknown)",
+      null);
 
   } catch(e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 });
 </script>
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/components/tree/label-cell.js
@@ -0,0 +1,52 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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";
+
+// ReactJS
+const React = require("devtools/client/shared/vendor/react");
+
+// Shortcuts
+const { td, span } = React.DOM;
+const PropTypes = React.PropTypes;
+
+/**
+ * Render the default cell used for toggle buttons
+ */
+var LabelCell = React.createClass({
+  // See the TreeView component for details related
+  // to the 'member' object.
+  propTypes: {
+    member: PropTypes.object.isRequired
+  },
+
+  displayName: "LabelCell",
+
+  render: function() {
+    let member = this.props.member;
+    let level = member.level || 0;
+
+    // Compute indentation dynamically. The deeper the item is
+    // inside the hierarchy, the bigger is the left padding.
+    let rowStyle = {
+      "paddingLeft": (level * 16) + "px",
+    };
+
+    return (
+      td({
+        className: "treeLabelCell",
+        key: "default",
+        style: rowStyle},
+        span({ className: "treeIcon" }),
+        span({ className: "treeLabel " + member.type + "Label" },
+          member.name
+        )
+      )
+    );
+  }
+});
+
+// Exports from this module
+module.exports = LabelCell;
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/components/tree/moz.build
@@ -0,0 +1,14 @@
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+    'label-cell.js',
+    'object-provider.js',
+    'tree-cell.js',
+    'tree-header.js',
+    'tree-row.js',
+    'tree-view.css',
+    'tree-view.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/components/tree/object-provider.js
@@ -0,0 +1,87 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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";
+
+/**
+ * Implementation of the default data provider. A provider is state less
+ * object responsible for transformation data (usually a state) to
+ * a structure that can be directly consumed by the tree-view component.
+ */
+var ObjectProvider = {
+  getChildren: function(object) {
+    let children = [];
+
+    if (object instanceof ObjectProperty) {
+      object = object.value;
+    }
+
+    if (!object) {
+      return [];
+    }
+
+    if (typeof (object) == "string") {
+      return [];
+    }
+
+    for (let prop in object) {
+      try {
+        children.push(new ObjectProperty(prop, object[prop]));
+      } catch (e) {
+        console.error(e);
+      }
+    }
+    return children;
+  },
+
+  hasChildren: function(object) {
+    if (object instanceof ObjectProperty) {
+      object = object.value;
+    }
+
+    if (!object) {
+      return false;
+    }
+
+    if (typeof object == "string") {
+      return false;
+    }
+
+    if (typeof object !== "object") {
+      return false;
+    }
+
+    return Object.keys(object).length > 1;
+  },
+
+  getLabel: function(object) {
+    return (object instanceof ObjectProperty) ?
+      object.name : null;
+  },
+
+  getValue: function(object) {
+    return (object instanceof ObjectProperty) ?
+      object.value : null;
+  },
+
+  getKey: function(object) {
+    return (object instanceof ObjectProperty) ?
+      object.name : null;
+  },
+
+  getType: function(object) {
+    return (object instanceof ObjectProperty) ?
+      typeof object.value : typeof object;
+  }
+};
+
+function ObjectProperty(name, value) {
+  this.name = name;
+  this.value = value;
+}
+
+// Exports from this module
+exports.ObjectProperty = ObjectProperty;
+exports.ObjectProvider = ObjectProvider;
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/components/tree/tree-cell.js
@@ -0,0 +1,96 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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 React = require("devtools/client/shared/vendor/react");
+
+// Shortcuts
+const { td, span } = React.DOM;
+const PropTypes = React.PropTypes;
+
+/**
+ * This template represents a cell in TreeView row. It's rendered
+ * using <td> element (the row is <tr> and the entire tree is <table>).
+ */
+var TreeCell = React.createClass({
+  // See TreeView component for detailed property explanation.
+  propTypes: {
+    value: PropTypes.any,
+    decorator: PropTypes.object,
+    id: PropTypes.string.isRequired,
+    member: PropTypes.object.isRequired,
+    renderValue: PropTypes.func.isRequired
+  },
+
+  displayName: "TreeCell",
+
+  /**
+   * Optimize cell rendering. If value is the same do not render.
+   */
+  shouldComponentUpdate: function(nextProps) {
+    return (this.props.value != nextProps.value);
+  },
+
+  getCellClass: function(object, id) {
+    let decorator = this.props.decorator;
+    if (!decorator || !decorator.getCellClass) {
+      return [];
+    }
+
+    // Decorator can return a simple string or array of strings.
+    let classNames = decorator.getCellClass(object, id);
+    if (!classNames) {
+      return [];
+    }
+
+    if (typeof classNames == "string") {
+      classNames = [classNames];
+    }
+
+    return classNames;
+  },
+
+  render: function() {
+    let member = this.props.member;
+    let type = member.type || "";
+    let id = this.props.id;
+    let value = this.props.value;
+    let decorator = this.props.decorator;
+
+    // Compute class name list for the <td> element.
+    let classNames = this.getCellClass(member.object, id) || [];
+    classNames.push("treeValueCell");
+    classNames.push(type + "Cell");
+
+    // Render value using a default render function or custom
+    // provided function from props or a decorator.
+    let renderValue = this.props.renderValue || defaultRenderValue;
+    if (decorator && decorator.renderValue) {
+      renderValue = decorator.renderValue(member.object, id) || renderValue;
+    }
+
+    let props = Object.assign({}, this.props, {
+      object: value,
+    });
+
+    // Render me!
+    return (
+      td({ className: classNames.join(" ") },
+        span({}, renderValue(props))
+      )
+    );
+  }
+});
+
+// Default value rendering.
+var defaultRenderValue = props => {
+  return (
+    props.object + ""
+  );
+};
+
+// Exports from this module
+module.exports = TreeCell;
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/components/tree/tree-header.js
@@ -0,0 +1,97 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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";
+
+// ReactJS
+const React = require("devtools/client/shared/vendor/react");
+
+// Shortcuts
+const { thead, tr, td, div } = React.DOM;
+const PropTypes = React.PropTypes;
+
+/**
+ * This component is responsible for rendering tree header.
+ * It's based on <thead> element.
+ */
+var TreeHeader = React.createClass({
+  // See also TreeView component for detailed info about properties.
+  propTypes: {
+    // Custom tree decorator
+    decorator: PropTypes.object,
+    // True if the header should be visible
+    header: PropTypes.bool,
+    // Array with column definition
+    columns: PropTypes.array
+  },
+
+  displayName: "TreeHeader",
+
+  getDefaultProps: function() {
+    return {
+      columns: [{
+        id: "default"
+      }]
+    };
+  },
+
+  getHeaderClass: function(colId) {
+    let decorator = this.props.decorator;
+    if (!decorator || !decorator.getHeaderClass) {
+      return [];
+    }
+
+    // Decorator can return a simple string or array of strings.
+    let classNames = decorator.getHeaderClass(colId);
+    if (!classNames) {
+      return [];
+    }
+
+    if (typeof classNames == "string") {
+      classNames = [classNames];
+    }
+
+    return classNames;
+  },
+
+  render: function() {
+    let cells = [];
+    let visible = this.props.header;
+
+    // Render the rest of the columns (if any)
+    this.props.columns.forEach(col => {
+      let cellStyle = {
+        "width": col.width ? col.width : "",
+      };
+
+      let classNames = [];
+
+      if (visible) {
+        classNames = this.getHeaderClass(col.id);
+        classNames.push("treeHeaderCell");
+      }
+
+      cells.push(
+        td({
+          className: classNames.join(" "),
+          style: cellStyle,
+          key: col.id},
+          div({ className: visible ? "treeHeaderCellBox" : "" },
+            visible ? col.title : ""
+          )
+        )
+      );
+    });
+
+    return (
+      thead({}, tr({ className: visible ? "treeHeaderRow" : "" },
+        cells
+      ))
+    );
+  }
+});
+
+// Exports from this module
+module.exports = TreeHeader;
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/components/tree/tree-row.js
@@ -0,0 +1,181 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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";
+
+// ReactJS
+const React = require("devtools/client/shared/vendor/react");
+const ReactDOM = require("devtools/client/shared/vendor/react-dom");
+
+// Tree
+const TreeCell = React.createFactory(require("./tree-cell"));
+const LabelCell = React.createFactory(require("./label-cell"));
+
+// Shortcuts
+const { tr } = React.DOM;
+const PropTypes = React.PropTypes;
+
+/**
+ * This template represents a node in TreeView component. It's rendered
+ * using <tr> element (the entire tree is one big <table>).
+ */
+var TreeRow = React.createClass({
+  // See TreeView component for more details about the props and
+  // the 'member' object.
+  propTypes: {
+    member: PropTypes.shape({
+      object: PropTypes.obSject,
+      name: PropTypes.sring,
+      type: PropTypes.string.isRequired,
+      rowClass: PropTypes.string.isRequired,
+      level: PropTypes.number.isRequired,
+      hasChildren: PropTypes.bool,
+      value: PropTypes.any,
+      open: PropTypes.bool.isRequired,
+      path: PropTypes.string.isRequired,
+      hidden: PropTypes.bool,
+    }),
+    decorator: PropTypes.object,
+    renderCell: PropTypes.object,
+    renderLabelCell: PropTypes.object,
+    columns: PropTypes.array.isRequired,
+    provider: PropTypes.object.isRequired,
+    onClick: PropTypes.func.isRequired
+  },
+
+  displayName: "TreeRow",
+
+  /**
+   * Optimize row rendering. If props are the same do not render.
+   * This makes the rendering a lot faster!
+   */
+  shouldComponentUpdate: function(nextProps) {
+    let props = ["name", "open", "value", "loading"];
+    for (let p in props) {
+      if (nextProps.member[props[p]] != this.props.member[props[p]]) {
+        return true;
+      }
+    }
+
+    return false;
+  },
+
+  componentWillReceiveProps(nextProps) {
+    // I don't like accessing the underlying DOM elements directly,
+    // but this optimization makes the filtering so damn fast!
+    // The row doesn't have to be re-rendered, all we really need
+    // to do is toggling a class name.
+    // The important part is that DOM elements don't need to be
+    // re-created when they should appear again.
+    if (nextProps.member.hidden != this.props.member.hidden) {
+      let row = ReactDOM.findDOMNode(this);
+      row.classList.toggle("hidden");
+    }
+  },
+
+  getRowClass: function(object) {
+    let decorator = this.props.decorator;
+    if (!decorator || !decorator.getRowClass) {
+      return [];
+    }
+
+    // Decorator can return a simple string or array of strings.
+    let classNames = decorator.getRowClass(object);
+    if (!classNames) {
+      return [];
+    }
+
+    if (typeof classNames == "string") {
+      classNames = [classNames];
+    }
+
+    return classNames;
+  },
+
+  render: function() {
+    let member = this.props.member;
+    let decorator = this.props.decorator;
+
+    // Compute class name list for the <tr> element.
+    let classNames = this.getRowClass(member.object) || [];
+    classNames.push("treeRow");
+    classNames.push(member.type + "Row");
+
+    if (member.hasChildren) {
+      classNames.push("hasChildren");
+    }
+
+    if (member.open) {
+      classNames.push("opened");
+    }
+
+    if (member.loading) {
+      classNames.push("loading");
+    }
+
+    if (member.hidden) {
+      classNames.push("hidden");
+    }
+
+    // The label column (with toggle buttons) is usually
+    // the first one, but there might be cases (like in
+    // the Memory panel) where the toggling is done
+    // in the last column.
+    let cells = [];
+
+    // Get components for rendering cells.
+    let renderCell = this.props.renderCell || RenderCell;
+    let renderLabelCell = this.props.renderLabelCell || RenderLabelCell;
+    if (decorator && decorator.renderLabelCell) {
+      renderLabelCell = decorator.renderLabelCell(member.object) ||
+        renderLabelCell;
+    }
+
+    // Render a cell for every column.
+    this.props.columns.forEach(col => {
+      let props = Object.assign({}, this.props, {
+        key: col.id,
+        id: col.id,
+        value: this.props.provider.getValue(member.object, col.id)
+      });
+
+      if (decorator && decorator.renderCell) {
+        renderCell = decorator.renderCell(member.object, col.id);
+      }
+
+      let render = (col.id == "default") ? renderLabelCell : renderCell;
+
+      // Some cells don't have to be rendered. This happens when some
+      // other cells span more columns. Note that the label cells contains
+      // toggle buttons and should be usually there unless we are rendering
+      // a simple non-expandable table.
+      if (render) {
+        cells.push(render(props));
+      }
+    });
+
+    // Render tree row
+    return (
+      tr({
+        className: classNames.join(" "),
+        onClick: this.props.onClick},
+        cells
+      )
+    );
+  }
+});
+
+// Helpers
+
+var RenderCell = props => {
+  return TreeCell(props);
+};
+
+var RenderLabelCell = props => {
+  return LabelCell(props);
+};
+
+// Exports from this module
+module.exports = TreeRow;
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/components/tree/tree-view.css
@@ -0,0 +1,201 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+@import url('resource://devtools/client/shared/components/reps/reps.css');
+
+/******************************************************************************/
+/* TreeView Colors */
+
+:root {
+  --tree-link-color: blue;
+  --tree-header-background: #C8D2DC;
+  --tree-header-sorted-background: #AAC3DC;
+}
+
+/******************************************************************************/
+/* TreeView Table*/
+
+.treeTable .treeLabelCell {
+  padding: 2px 0 2px 0px;
+  vertical-align: middle;
+  white-space: nowrap;
+}
+
+.treeTable .treeValueCell {
+  padding: 2px 0 2px 5px;
+  overflow: hidden;
+}
+
+.treeTable .treeLabel {
+  cursor: default;
+  overflow: hidden;
+  padding-left: 4px;
+  white-space: nowrap;
+}
+
+/* No paddding if there is actually no label */
+.treeTable .treeLabel:empty {
+  padding-left: 0;
+}
+
+.treeTable .treeRow.hasChildren > .treeLabelCell > .treeLabel:hover {
+  cursor: pointer;
+  color: var(--tree-link-color);
+  text-decoration: underline;
+}
+
+.treeTable .treeRow:hover {
+  background-color: var(--theme-body-background);
+}
+
+/* Filtering */
+.treeTable .treeRow.hidden {
+  display: none;
+}
+
+/******************************************************************************/
+/* Toggle Icon */
+
+.treeTable .treeRow .treeIcon {
+  height: 12px;
+  width: 14px;
+  display: inline-block;
+  vertical-align: bottom;
+  margin-left: 3px;
+  padding-top: 1px;
+}
+
+/* All expanded/collapsed styles need to apply on immediate children
+  since there might be nested trees within a tree. */
+.treeTable .treeRow.hasChildren > .treeLabelCell > .treeIcon {
+  cursor: pointer;
+  background-repeat: no-repeat;
+}
+
+/* Spinner (used for async fetch). Needs to have higher priority than
+   theme toggle icons */
+.treeTable .treeRow.hasChildren.loading > .treeLabelCell > .treeIcon {
+  background-image: url(chrome://devtools/skin/images/firebug/spinner.png) !important;
+  background-position: 2px 1px !important;
+  background-size: 9px 9px !important;
+}
+
+/* Default toggle icon. The immediate children operator must be
+ used here since there might be nested tree components inside
+ a tree and we don't want to alter those. */
+.treeTable .treeRow.hasChildren > .treeLabelCell > .treeIcon {
+  background-image: url(chrome://devtools/skin/images/firebug/twisty-closed-firebug.svg);
+}
+
+.treeTable .treeRow.hasChildren.opened > .treeLabelCell > .treeIcon {
+  background-image: url(chrome://devtools/skin/images/firebug/twisty-open-firebug.svg);
+}
+
+/******************************************************************************/
+/* Header */
+
+.treeTable .treeHeaderRow {
+  height: 18px;
+}
+
+.treeTable .treeHeaderCell {
+  cursor: pointer;
+  -moz-user-select: none;
+  border-bottom: 1px solid rgba(0, 0, 0, 0.2);
+  padding: 0 !important;
+  background: linear-gradient(
+          rgba(255, 255, 255, 0.05),
+          rgba(0, 0, 0, 0.05)),
+      radial-gradient(1px 60% at right,
+          rgba(0, 0, 0, 0.8) 0%,
+          transparent 80%) repeat-x var(--tree-header-background);
+  color: var(--theme-body-color);
+  white-space: nowrap;
+}
+
+.treeTable .treeHeaderCellBox {
+  padding: 2px 14px 2px 10px;
+}
+
+.treeTable .treeHeaderRow > .treeHeaderCell:first-child > .treeHeaderCellBox {
+  padding: 0;
+}
+
+.treeTable .treeHeaderSorted {
+  background-color: var(--tree-header-sorted-background);
+}
+
+.treeTable .treeHeaderSorted > .treeHeaderCellBox {
+  background: url(chrome://devtools/skin/images/firebug/arrow-down.svg) no-repeat calc(100% - 4px);
+}
+
+.treeTable .treeHeaderSorted.sortedAscending > .treeHeaderCellBox {
+  background-image: url(chrome://devtools/skin/images/firebug/arrow-up.svg);
+}
+
+.treeTable .treeHeaderCell:hover:active {
+  background-image: linear-gradient(
+          rgba(0, 0, 0, 0.1),
+          transparent),
+      radial-gradient(1px 60% at right,
+          rgba(0, 0, 0, 0.8) 0%,
+          transparent 80%);
+}
+
+/******************************************************************************/
+/* Themes */
+
+/* Light Theme: toggle icon */
+.theme-light .treeTable .treeRow.hasChildren > .treeLabelCell > .treeIcon {
+  background-image: url("chrome://devtools/skin/images/controls.png");
+  background-size: 56px 28px;
+  background-position: 0 -14px;
+}
+
+.theme-light .treeTable .treeRow.hasChildren.opened > .treeLabelCell > .treeIcon {
+  background-image: url("chrome://devtools/skin/images/controls.png");
+  background-size: 56px 28px;
+  background-position: -14px -14px;
+}
+
+/* Dark Theme: toggle icon */
+.theme-dark .treeTable .treeRow.hasChildren > .treeLabelCell > .treeIcon {
+  background-image: url("chrome://devtools/skin/images/controls.png");
+  background-size: 56px 28px;
+  background-position: -28px -14px;
+}
+
+.theme-dark .treeTable .treeRow.hasChildren.opened > .treeLabelCell > .treeIcon {
+  background-image: url("chrome://devtools/skin/images/controls.png");
+  background-size: 56px 28px;
+  background-position: -42px -14px;
+}
+
+.theme-dark .treeTable .treeRow:hover {
+  background-color: var(--theme-selection-background-semitransparent);
+}
+
+/* Dark and Light Themes: colors */
+.theme-light .treeTable .treeRow:hover,
+.theme-dark .treeTable .treeRow:hover {
+  background-color: var(--theme-selection-background) !important;
+}
+
+.theme-light .treeTable .treeLabel,
+.theme-dark .treeTable .treeLabel {
+  color: var(--theme-highlight-pink);
+}
+
+.theme-light .treeTable .treeRow.hasChildren > .treeLabelCell > .treeLabel:hover {
+  color: var(--theme-highlight-pink);
+}
+
+/* Dark and Light Themes: Support for retina displays */
+@media (min-resolution: 1.1dppx) {
+.theme-dark .treeTable .treeRow.hasChildren > .treeLabelCell > .treeIcon,
+.theme-light .treeTable .treeRow.hasChildren > .treeLabelCell > .treeIcon {
+  background-image: url("chrome://devtools/skin/images/controls@2x.png");
+}
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/components/tree/tree-view.js
@@ -0,0 +1,348 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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";
+
+// ReactJS
+const React = require("devtools/client/shared/vendor/react");
+
+// Reps
+const { ObjectProvider } = require("./object-provider");
+const TreeRow = React.createFactory(require("./tree-row"));
+const TreeHeader = React.createFactory(require("./tree-header"));
+
+// Shortcuts
+const DOM = React.DOM;
+const PropTypes = React.PropTypes;
+
+/**
+ * This component represents a tree view with expandable/collapsible nodes.
+ * The tree is rendered using <table> element where every node is represented
+ * by <tr> element. The tree is one big table where nodes (rows) are properly
+ * indented from the left to mimic hierarchical structure of the data.
+ *
+ * The tree can have arbitrary number of columns and so, might be use
+ * as an expandable tree-table UI widget as well. By default, there is
+ * one column for node label and one for node value.
+ *
+ * The tree is maintaining its (presentation) state, which consists
+ * from list of expanded nodes and list of columns.
+ *
+ * Complete data provider interface:
+ * var TreeProvider = {
+ *   getChildren: function(object);
+ *   hasChildren: function(object);
+ *   getLabel: function(object, colId);
+ *   getValue: function(object, colId);
+ *   getKey: function(object);
+ *   getType: function(object);
+ * }
+ *
+ * Complete tree decorator interface:
+ * var TreeDecorator = {
+ *   getRowClass: function(object);
+ *   getCellClass: function(object, colId);
+ *   getHeaderClass: function(colId);
+ *   renderValue: function(object, colId);
+ *   renderRow: function(object);
+ *   renderCelL: function(object, colId);
+ *   renderLabelCell: function(object);
+ * }
+ */
+var TreeView = React.createClass({
+  // The only required property (not set by default) is the input data
+  // object that is used to puputate the tree.
+  propTypes: {
+    // The input data object.
+    object: PropTypes.any,
+    className: PropTypes.string,
+    // Data provider (see also the interface above)
+    provider: PropTypes.shape({
+      getChildren: PropTypes.func,
+      hasChildren: PropTypes.func,
+      getLabel: PropTypes.func,
+      getValue: PropTypes.func,
+      getKey: PropTypes.func,
+      getType: PropTypes.func,
+    }).isRequired,
+    // Tree decorator (see also the interface above)
+    decorator: PropTypes.shape({
+      getRowClass: PropTypes.func,
+      getCellClass: PropTypes.func,
+      getHeaderClass: PropTypes.func,
+      renderValue: PropTypes.func,
+      renderRow: PropTypes.func,
+      renderCelL: PropTypes.func,
+      renderLabelCell: PropTypes.func,
+    }),
+    // Custom tree row (node) renderer
+    renderRow: PropTypes.func,
+    // Custom cell renderer
+    renderCell: PropTypes.func,
+    // Custom value renderef
+    renderValue: PropTypes.func,
+    // Custom tree label (including a toggle button) renderer
+    renderLabelCell: PropTypes.func,
+    // Set of expanded nodes
+    expandedNodes: PropTypes.object,
+    // Custom filtering callback
+    onFilter: PropTypes.func,
+    // Custom sorting callback
+    onSort: PropTypes.func,
+    // A header is displayed if set to true
+    header: PropTypes.bool,
+    // Array of columns
+    columns: PropTypes.arrayOf(PropTypes.shape({
+      id: PropTypes.string.isRequired,
+      title: PropTypes.string,
+      width: PropTypes.string
+    }))
+  },
+
+  displayName: "TreeView",
+
+  getDefaultProps: function() {
+    return {
+      object: null,
+      renderRow: null,
+      provider: ObjectProvider,
+      expandedNodes: new Set(),
+      columns: []
+    };
+  },
+
+  getInitialState: function() {
+    return {
+      expandedNodes: this.props.expandedNodes,
+      columns: ensureDefaultColumn(this.props.columns)
+    };
+  },
+
+  // Node expand/collapse
+
+  toggle: function(nodePath) {
+    let nodes = this.state.expandedNodes;
+    if (this.isExpanded(nodePath)) {
+      nodes.delete(nodePath);
+    } else {
+      nodes.add(nodePath);
+    }
+
+    // Compute new state and update the tree.
+    this.setState(Object.assign({}, this.state, {
+      expandedNodes: nodes
+    }));
+  },
+
+  isExpanded: function(nodePath) {
+    return this.state.expandedNodes.has(nodePath);
+  },
+
+  // Event Handlers
+
+  onClickRow: function(nodePath, event) {
+    event.stopPropagation();
+    this.toggle(nodePath);
+  },
+
+  // Filtering & Sorting
+
+  /**
+   * Filter out nodes that don't correspond to the current filter.
+   * @return {Boolean} true if the node should be visible otherwise false.
+   */
+  onFilter: function(object) {
+    let onFilter = this.props.onFilter;
+    return onFilter ? onFilter(object) : true;
+  },
+
+  onSort: function(parent, children) {
+    let onSort = this.props.onSort;
+    return onSort ? onSort(parent, children) : children;
+  },
+
+  // Members
+
+  /**
+   * Return children node objects (so called 'members') for given
+   * parent object.
+   */
+  getMembers: function(parent, level, path) {
+    // Strings don't have children. Note that 'long' strings are using
+    // the expander icon (+/-) to display the entire original value,
+    // but there are no child items.
+    if (typeof parent == "string") {
+      return [];
+    }
+
+    let provider = this.props.provider;
+    let children = provider.getChildren(parent) || [];
+
+    // If the return value is non-array, the children
+    // are being loaded asynchronously.
+    if (!Array.isArray(children)) {
+      return children;
+    }
+
+    children = this.onSort(parent, children) || children;
+
+    return children.map(child => {
+      let key = provider.getKey(child);
+      let nodePath = path + "/" + key;
+      let type = provider.getType(child);
+      let hasChildren = provider.hasChildren(child);
+
+      // Value with no column specified is used for optimization.
+      // The row is re-rendered only if this value changes.
+      // Value for actual column is get when a cell is rendered.
+      let value = provider.getValue(child);
+
+      if (isLongString(value)) {
+        hasChildren = true;
+      }
+
+      // Return value is a 'member' object containing meta-data about
+      // tree node. It describes node label, value, type, etc.
+      return {
+        // An object associated with this node.
+        object: child,
+        // A label for the child node
+        name: provider.getLabel(child),
+        // Data type of the child node (used for CSS customization)
+        type: type,
+        // Class attribute computed from the type.
+        rowClass: "treeRow-" + type,
+        // Level of the child within the hierarchy (top == 0)
+        level: level,
+        // True if this node has children.
+        hasChildren: hasChildren,
+        // Value associated with this node (as provided by the data provider)
+        value: value,
+        // True if the node is expanded.
+        open: this.isExpanded(nodePath),
+        // Node path
+        path: nodePath,
+        // True if the node is hidden (used for filtering)
+        hidden: !this.onFilter(child)
+      };
+    });
+  },
+
+  /**
+   * Render tree rows/nodes.
+   */
+  renderRows: function(parent, level = 0, path = "") {
+    let rows = [];
+    let decorator = this.props.decorator;
+    let renderRow = this.props.renderRow || TreeRow;
+
+    // Get children for given parent node, iterate over them and render
+    // a row for every one. Use row template (a component) from properties.
+    // If the return value is non-array, the children are being loaded
+    // asynchronously.
+    let members = this.getMembers(parent, level, path);
+    if (!Array.isArray(members)) {
+      return members;
+    }
+
+    members.forEach(member => {
+      if (decorator && decorator.renderRow) {
+        renderRow = decorator.renderRow(member.object) || renderRow;
+      }
+
+      let props = Object.assign({}, this.props, {
+        key: member.path,
+        member: member,
+        columns: this.state.columns,
+        onClick: this.onClickRow.bind(this, member.path)
+      });
+
+      // Render single row.
+      rows.push(renderRow(props));
+
+      // If a child node is expanded render its rows too.
+      if (member.hasChildren && member.open) {
+        let childRows = this.renderRows(member.object, level + 1, member.path);
+
+        // If children needs to be asynchronously fetched first,
+        // set 'loading' property to the parent row. Otherwise
+        // just append children rows to the array of all rows.
+        if (!Array.isArray(childRows)) {
+          let lastIndex = rows.length - 1;
+          props.member.loading = true;
+          rows[lastIndex] = React.cloneElement(rows[lastIndex], props);
+        } else {
+          rows = rows.concat(childRows);
+        }
+      }
+    });
+
+    return rows;
+  },
+
+  render: function() {
+    let root = this.props.object;
+    let classNames = ["treeTable"];
+
+    // Use custom class name from props.
+    let className = this.props.className;
+    if (className) {
+      classNames.push(...className.split(" "));
+    }
+
+    // Alright, let's render all tree rows. The tree is one big <table>.
+    let rows = this.renderRows(root, 0, "");
+
+    // This happens when the view needs to do initial asynchronous
+    // fetch for the root object. The tree might provide a hook API
+    // for rendering animated spinner (just like for tree nodes).
+    if (!Array.isArray(rows)) {
+      rows = [];
+    }
+
+    let props = Object.assign({}, this.props, {
+      columns: this.state.columns
+    });
+
+    return (
+      DOM.table({
+        className: classNames.join(" "),
+        cellPadding: 0,
+        cellSpacing: 0},
+        TreeHeader(props),
+        DOM.tbody({},
+          rows
+        )
+      )
+    );
+  }
+});
+
+// Helpers
+
+/**
+ * There should always be at least one column (the one with toggle buttons)
+ * and this function ensures that it's true.
+ */
+function ensureDefaultColumn(columns) {
+  if (!columns) {
+    columns = [];
+  }
+
+  let defaultColumn = columns.filter(col => col.id == "default");
+  if (defaultColumn.length) {
+    return columns;
+  }
+
+  // The default column is usually the first one.
+  return [{id: "default"}, ...columns];
+}
+
+function isLongString(value) {
+  return typeof value == "string" && value.length > 50;
+}
+
+// Exports from this module
+module.exports = TreeView;
--- a/devtools/client/themes/components-frame.css
+++ b/devtools/client/themes/components-frame.css
@@ -8,37 +8,30 @@
  * Styles for React component at `devtools/client/shared/components/frame.js`
  */
 
 .frame-link {
   margin-left: 7px;
   display: flex;
 }
 
-.focused .frame-link-filename,
-.focused .frame-link-column,
-.focused .frame-link-line,
-.focused .frame-link-host,
-.focused .frame-link-colon {
-  color: var(--theme-selection-color);
-}
-
-.frame-link a.frame-link-filename {
-  color: var(--theme-highlight-blue);
-  cursor: pointer;
+.frame-link .frame-link-filename {
   text-overflow: ellipsis;
   overflow: hidden;
   flex: 1;
   text-align: right;
   /* overrides styling some tools have with anchors */
   text-decoration: none;
   font-style: normal;
 }
-
-.frame-link .frame-link-filename:hover {
+.frame-link a.frame-link-filename {
+  cursor: pointer;
+  color: var(--theme-highlight-blue);
+}
+.frame-link a.frame-link-filename:hover {
   text-decoration: underline;
 }
 
 .frame-link .frame-link-host {
   margin-inline-start: 5px;
   font-size: 90%;
   color: var(--theme-content-color2);
 }
@@ -48,8 +41,17 @@
 }
 
 .frame-link .frame-link-column,
 .frame-link .frame-link-line,
 .frame-link .frame-link-colon {
   color: var(--theme-highlight-orange);
   display: block;
 }
+
+.focused a.frame-link-filename,
+.focused span.frame-link-filename,
+.focused .frame-link-column,
+.focused .frame-link-line,
+.focused .frame-link-host,
+.focused .frame-link-colon {
+  color: var(--theme-selection-color);
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/firebug-theme.css
@@ -0,0 +1,14 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+@import url(variables.css);
+@import url(common.css);
+@import url(toolbars.css);
+@import url(theme-light.css);
+
+.theme-body {
+  font-size: 11px;
+  font-family: Lucida Grande, Tahoma, sans-serif;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/firebug/arrow-down.svg
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- See license.txt for terms of usage -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   version="1.1"
+   width="7"
+   height="7"
+   id="svg2">
+  <defs
+     id="defs4" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     transform="matrix(0.90660857,0,0,0.92758296,0.32686996,-969.40669)"
+     id="layer1"
+     style="fill:#39424a;fill-opacity:1;stroke:#39424a;stroke-opacity:1">
+    <path
+       d="M 1.7741953,4.4861735 0.25734764,1.8589164 -1.2594999,-0.76834045 l 3.0336951,-8e-8 3.033695,-8e-8 -1.5168475,2.62725711 z"
+       transform="matrix(0.98012309,0,0,1.1317487,1.7610704,1046.7584)"
+       id="path2996"
+       style="fill:#39424a;fill-opacity:1;stroke:#39424a;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+  </g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/firebug/arrow-up.svg
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- See license.txt for terms of usage -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   version="1.1"
+   width="7"
+   height="7"
+   id="svg2">
+  <defs
+     id="defs4" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     transform="matrix(0.90660857,0,0,-0.92758296,0.32686996,976.40677)"
+     id="layer1"
+     style="fill:#39424a;fill-opacity:1;stroke:#39424a;stroke-opacity:1">
+    <path
+       d="M 1.7741953,4.4861735 0.25734764,1.8589164 -1.2594999,-0.76834045 l 3.0336951,-8e-8 3.033695,-8e-8 -1.5168475,2.62725711 z"
+       transform="matrix(0.98012309,0,0,1.1317487,1.7610704,1046.7584)"
+       id="path2996"
+       style="fill:#39424a;fill-opacity:1;stroke:#39424a;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+  </g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/firebug/moz.build
@@ -0,0 +1,11 @@
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+    'read-only.svg',
+    'spinner.png',
+    'twisty-closed-firebug.svg',
+    'twisty-open-firebug.svg',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/firebug/read-only.svg
@@ -0,0 +1,255 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   version="1.1"
+   width="12"
+   height="12"
+   id="svg2">
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient3892">
+      <stop
+         id="stop3894"
+         style="stop-color:#787878;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3896"
+         style="stop-color:#b4b4b4;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3765">
+      <stop
+         id="stop3767"
+         style="stop-color:#505050;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3769"
+         style="stop-color:#787878;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3757">
+      <stop
+         id="stop3759"
+         style="stop-color:#c8c8c8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3761"
+         style="stop-color:#dcdcdc;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3765-6">
+      <stop
+         id="stop3767-0"
+         style="stop-color:#a0a0a0;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3769-4"
+         style="stop-color:#c8c8c8;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11.376831"
+       y1="1052.0852"
+       x2="4.5592546"
+       y2="1040.6658"
+       id="linearGradient3830"
+       xlink:href="#linearGradient3757"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.87012989,0,0,0.82456145,1.0389611,183.57212)" />
+    <linearGradient
+       x1="8.8415442"
+       y1="1053.3849"
+       x2="1.9174434"
+       y2="1041.9227"
+       id="linearGradient3832"
+       xlink:href="#linearGradient3765-6"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.87012989,0,0,0.82456145,1.0389611,183.57212)" />
+    <linearGradient
+       x1="8.6370001"
+       y1="4.3111062"
+       x2="6.3403625"
+       y2="0.58274388"
+       id="linearGradient3861"
+       xlink:href="#linearGradient3757"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.89600004,0,0,0.97745455,0.83199985,0.9945)" />
+    <linearGradient
+       x1="7.1882658"
+       y1="5.0780835"
+       x2="4.9555426"
+       y2="1.392331"
+       id="linearGradient3866"
+       xlink:href="#linearGradient3765-6"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.89600004,0,0,0.97745455,0.83199985,0.9945)" />
+    <linearGradient
+       x1="9.3919191"
+       y1="12.115811"
+       x2="7.4015098"
+       y2="7.4140091"
+       id="linearGradient3909"
+       xlink:href="#linearGradient3892"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       x1="8.5397177"
+       y1="12.497831"
+       x2="6.6080809"
+       y2="7.825417"
+       id="linearGradient3917"
+       xlink:href="#linearGradient3765"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       x1="8.5397177"
+       y1="12.497831"
+       x2="6.6080809"
+       y2="7.825417"
+       id="linearGradient3039"
+       xlink:href="#linearGradient3765"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.71428573,0,0,0.71492245,0.28571425,0.27617311)" />
+    <linearGradient
+       x1="9.3919191"
+       y1="12.115811"
+       x2="7.4015098"
+       y2="7.4140091"
+       id="linearGradient3041"
+       xlink:href="#linearGradient3892"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.71428573,0,0,0.71492245,0.28571425,0.27617311)" />
+    <linearGradient
+       x1="11.376831"
+       y1="1052.0852"
+       x2="4.5592546"
+       y2="1040.6658"
+       id="linearGradient3044"
+       xlink:href="#linearGradient3757"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.62152136,0,0,0.58949749,1.0278293,-609.4026)" />
+    <linearGradient
+       x1="8.8415442"
+       y1="1053.3849"
+       x2="1.9174434"
+       y2="1041.9227"
+       id="linearGradient3046"
+       xlink:href="#linearGradient3765-6"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.62152136,0,0,0.58949749,1.0278293,-609.4026)" />
+    <linearGradient
+       x1="8.6370001"
+       y1="4.3111062"
+       x2="6.3403625"
+       y2="0.58274388"
+       id="linearGradient3049"
+       xlink:href="#linearGradient3757"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.64000004,0,0,0.6988042,0.87999987,0.98716349)" />
+    <linearGradient
+       x1="7.1882658"
+       y1="5.0780835"
+       x2="4.9555426"
+       y2="1.392331"
+       id="linearGradient3051"
+       xlink:href="#linearGradient3765-6"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.64000004,0,0,0.6988042,0.87999987,0.98716349)" />
+    <linearGradient
+       x1="8.6370001"
+       y1="4.3111062"
+       x2="6.3403625"
+       y2="0.58274388"
+       id="linearGradient3826"
+       xlink:href="#linearGradient3757"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.64000004,0,0,0.6988042,0.87999987,0.98716349)" />
+    <linearGradient
+       x1="7.1882658"
+       y1="5.0780835"
+       x2="4.9555426"
+       y2="1.392331"
+       id="linearGradient3828"
+       xlink:href="#linearGradient3765-6"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.64000004,0,0,0.6988042,0.87999987,0.98716349)" />
+    <linearGradient
+       x1="11.376831"
+       y1="1052.0852"
+       x2="4.5592546"
+       y2="1040.6658"
+       id="linearGradient3831"
+       xlink:href="#linearGradient3757"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.62152136,0,0,0.58949749,1.0278293,-609.4026)" />
+    <linearGradient
+       x1="8.8415442"
+       y1="1053.3849"
+       x2="1.9174434"
+       y2="1041.9227"
+       id="linearGradient3833"
+       xlink:href="#linearGradient3765-6"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.62152136,0,0,0.58949749,1.0278293,-609.4026)" />
+    <linearGradient
+       x1="8.5397177"
+       y1="12.497831"
+       x2="6.6080809"
+       y2="7.825417"
+       id="linearGradient3835"
+       xlink:href="#linearGradient3765"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.71428573,0,0,0.71492245,0.28571425,0.27617311)" />
+    <linearGradient
+       x1="9.3919191"
+       y1="12.115811"
+       x2="7.4015098"
+       y2="7.4140091"
+       id="linearGradient3837"
+       xlink:href="#linearGradient3892"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.71428573,0,0,0.71492245,0.28571425,0.27617311)" />
+  </defs>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     transform="matrix(1.1910164,0,0,1.1910639,-1.146234,-1.1462857)"
+     id="g3821">
+    <path
+       d="m 6.0000001,1.2145088 c -1.6317576,0 -2.9446627,1.2860444 -2.9910715,2.9043725 0.1136269,-0.018496 0.2381347,-0.044683 0.3571429,-0.044683 l 0.9375,0 c 0.082361,-0.8799332 0.7978203,-1.6532581 1.6964286,-1.6532581 0.8986083,0 1.6140679,0.7733249 1.6964286,1.6532581 l 0.9375,0 c 0.1190079,0 0.2435157,0.026187 0.3571429,0.044683 C 8.944663,2.5005532 7.6317573,1.2145088 6.0000001,1.2145088 z"
+       id="path3773"
+       style="fill:url(#linearGradient3826);fill-opacity:1;stroke:url(#linearGradient3828);stroke-width:0.50376141;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+    <rect
+       width="9.5714283"
+       height="6.7202706"
+       rx="1.679238"
+       ry="1.6791711"
+       x="1.2142856"
+       y="4.0652847"
+       id="rect2987-9"
+       style="fill:url(#linearGradient3831);fill-opacity:1;stroke:url(#linearGradient3833);stroke-width:0.50376141;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+    <path
+       d="m 6.0000001,5.5040435 c -0.6706315,0 -1.2053572,0.5352023 -1.2053572,1.2064317 0,0.5619236 0.3753047,1.0226472 0.8928572,1.1617489 l 0,1.4745276 0.625,0 0,-1.4745276 C 6.8300525,7.7331224 7.2053573,7.2723988 7.2053573,6.7104752 c 0,-0.6712294 -0.5347257,-1.2064317 -1.2053572,-1.2064317 z"
+       id="path3898"
+       style="fill:url(#linearGradient3835);fill-opacity:1;stroke:url(#linearGradient3837);stroke-width:0.33584094;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+  </g>
+</svg>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..eec57a80962434f313b2ec53f4992568bd2c402d
GIT binary patch
literal 6125
zc${_DdpMK*9|!Q;Ls^a+VnmrdWlwU*`OHwvp)5RE&WoHGn^QT&u#i;9DWXOQKj%|O
zM70bF)f$?IVIfIE_}%sEcU}H?+I4OBbq(L^_PXDn@Avb$ddB(`KaT_t008{vW+oW$
zy6?vuvKRcEys;MwUbG0N=Lly5JqclM!5)B7fVaOJ!Q+B7_?Obg!5(gez+mZ6Wd(JG
z6Vh^FVPOhEUO_lNw~!Ez5QV_ti%3HMGc>q}_K!tuJ#bzD-Z(cuY3~5fz+iA4@4$c%
zKutkKL0L^l+MO71!4F(n?u3Gpf{Lm%F3>*+{1taU59G<C-#x)qAZ|EYOYkZLzJXi*
zT%W;<gy;Xh1O9*S!2e&moU;HBrKbKA066xWn;6-`GyPu_1w1i?1Cxc$be-+^ua^9>
zq(2Wo3=kKM&pDT6t8Q`orKP3ifXoI&MTP3PIu2-9%*4v8BFpy<S+u3!doPdJ{K5?Y
zRcslSZlnCMJJzK>O>_$8MR*%S28ew-M>vC28^*ebCoY#YY->sibZ<rP6lb)K`>6}h
zu5IF*=ynAx#^W2h1APuzTUBH1<`0|E*s;|ypN8bg9`~B?O7_O6VUKegqo5Fc0?6q{
z<>due$vNJ+@jDnB5l)nuXAtd{eFgDQ;)iLx+0B0((d)KZ9<LyAfH<zsvo7L;CM^%j
zJP%PONV3FXF>(~)T1SNuFIBW0MIHB%*0{h;t))Q_OTZ_%sm&_4uAx`Sq0S5VU5p(6
z&Ny@OJq-MzxaS2UTkyL-eo)r<#Q&f)7{|{CiZJp?DC6X_&p9DEl5K>25UG!6{-c<#
zB{(FoFh5x4aN@pk)?y<84<)5*m_1stPN)+9+<yoFxQt>uma26JDy)V-LJ+Hw?at;%
z7~m?Bi1>>V>QASUpoRG>1`}(g9uDJz-JgbujQkV5S-B<sz~o|(rXK*zw<fy5oii{l
zQ4@-4CYJKljR)mEoEM?X=ON2SD3+?!+CTd6Jk58#b5Ri3;Bxq*qa;fO%G9zZg7i_%
z<fT8|x>mkTPmsRwb%UU-ho`OB<d%5Bdj(*Dl6?_Gr?oL6qs*T^Kt4Cus(;kfhF~Ky
z-CCv6tt6US!;PNXPx&GR9Gude*`ZH+<vW=;s_dfV{C7&;LBVgkD7k)6DqD0LiU5;g
zYiVJ(eP2;09ij5*BQW&y*}?RF2PhXlD2SKbKgpb*xtkI&bF}%w?3w@UCo!{(<h}@I
zpmOW$>lFk9)@KAc0NsJ%-fSIet@G8{D<RPwfDZVN2?O?z7y-UA^ZO71uw`CgQ6sOP
z8^*Zr{qDTNZLArxEw$z_p!vM^qty17&8<@VicBz>3M4npMS^zV88`JjJ?qk8<tG0&
zw^VUEO0nF@l&8fAKYD?*d);5t5#uI3`>IEL%-fv}Ql`Frih&CxU0PZHUM^21ncCdX
z#OPJ7UYXJysA5OARkbZ?(i$-Hr_d3p@m;R_xr}5evXs|I*d1O(C~P@FIdpC_dYSi(
z-P!L8;`Fn-DEIs(l@d{LASM4V%Dq1*^?ZM(Qlp^2C25TteRnY38k2m_0;)q!i8n?J
zQjC)XtP=uIiLGICnAUpQ7WeWY5)%s{rP0EwPuBSEEnLsx0Ky9{g3n%_;%I>!fG!Kd
z3^qGrj7*C58o45vp^|;<Ds~6QBo3Z9t;BaaYGML-%u_7)j^q~pCCVU9xV(JBA?taG
zw|aWXoWA)@#q3yeHHzH+1fCZ{3p;C$Zt<muR-Nb-Af-RG6++32m}<#2`rl5QdYDq&
ztj$k!a)BTYLTmD;BLc(|YZ~O)ukiUAv~@w&<>&BAs#YtvF|9Ioc2ZNC3Ph*U)~X2%
zdP&tv=P2nB=i_#EEAvp+AwQ2lg|e%5@p1nqiK$h-AfL!DKFH7g(YpHwUrgWe_VEd}
zv#Zfi?13(OMDC<g_lN?5BsMGjQUj5`r$Ju$DehUe(Fj!6llt*x&+2v?BN4$>b2{cY
z5GzlWh3&_(9eo)i*y>beVb=Sgc4zjz=uSlr*ADtZKqGyjt>J3|SwcoalT`)Boju59
z)CF$?V%cx~zVdW3HKXfCJ0g~N)5tAr9p&ZoiU8oJo_F23dM&@io0f^`+uYjb5O~1e
z5C#^*MNBPd5y5ieIHBG*HxW!*Wajmg_f~e?YR=`WT7_7c4BI<oVIa!4F$G9Uf%YBR
z-;#Z$<ZwmOw}2-4ZSZE7!{rm8dt_$FZ%lja;EA`SA&KTUXAe2c2Ayd>X}vpy`+gI`
zok9&z5&lb&=Z7NV^v`-8b8Z`NcfMU#MoiwLDw@!JW1r>O6mRI8ZJ;&=fV{f@93}C|
zGF{0ySCxF_qUW9JRwtKXRk-28a3Kc(<>C;sd6>bIi8)MpZSSiWnCsc-TpcE#EW;J+
zF-8>gr+2`HLY&50tJqXfg#^7W%F<Q0T7d(!I{8aiTsoE}U_-w2#+td-fZ2%gvo~dT
zIDotI)Ue7`PFO-oMzAiN&;CD2qmia2IO@tHew<&e;&g;QGT9w>-#ZdV|Jnd$UeLZ1
zy`4~AE)dy1&W-yx`mB~ui)Df6xsma?V`3@B94QD}eu2*qq}E#V1F=utujH@5Va6ts
z2xeA@ev|(tbxDCR19xf=+jb*aVV5NDZ}RAEISxt+{F3B@XA;3f=I42B5SF{M`2DG6
zoV0bOs3Usf?)fHbMy;s!Yi>xhM-U#$<9+CHD`CmC=w|<Mf+B=@IQ)5xdAfp>_M5<M
z-5(FxFI%g2G%=~WD6o=o-dBv);+^Hul5crrFEH9SwOmzRewy|Eyp6~nl?feIK%DeB
z^$g4nD3b>WxFs8r<Z^FlHYdkmzBSENzcBD(-P>dNgY4CUR>q<|bm;Ez+=Wt10ZUv(
zP{O4C7Diyl+!)bg4aa3Igz8uhfB76JNBu0HHpOk?7+Ep$LYj$15{*n0O}>oyG=O2g
z-mw&8fvna8V*5RDP~}(HBrLmQH1aw*#j{RJt@j^;=p8ThPoE@Jclq)ECW-g-?}L7V
zzx)J#B=LB_A4YIsFfi%)cMq;C5>yENz)#6bwYcOar|p+N+QedAk72peiYt8ng62Jp
z8!UNtM3%%UpYt{+@LYV^gEyrvTtK?)Ra8&2TyjlAqj#mA7AuRaX+HegwC=4Xg85Es
zOr!S^D`GoDzwlCTHuuuj*mSTs24P|end5xCMbO@gHHhz;+ygV9)~13rDLJ#Ue!zB?
zpgph<x@lNHS{d2iQGp(PcW14`v|+RlTRB>ZrnM*_{xQqI9IIRnuY3D`OYqvsslBj6
z#*hj*>WQaXF1(0<OiVSF9f<T$Bz;nqxE9?+Ur_o3c(b&YtSril=*7G^!us9=KaW8l
z&A^21@n|OsaP#5~cL56iCV*-SRv_SkUx3j6bvZu+xD(9HVt9PwNIv_tb<f1(*z-je
z+(r6^N&1E-bVO=Hz3>Xw0#c~k0s&6`&t*maz;u}^wM<a|w3&TXWY)QU7yxWU%zgdI
z<ijSq#yv}5Tj-cI9VOY9Xn91~yYkKfTx3K+=f}`CS!!+C=tiqtvY@YjA)aQ8G7|)<
z;-te9TtLMk_=^1)(af{XAyQLvJgNHwfBq-kr4Qk{&e~P%#@zlHCKwX0O0nK-(X;4E
zIm*J0kW8agpMw^PT!!(fcT{NaEE@947c<45&zjDVHJ8z1h0xX$piFc0ddpisZAQRa
z8fN1uoXrUs=9UDvJ6~_2`zMo$c6Mb;o4DR=F5nvn>|k8pvgA>>U6`<c$7HEuopxag
z{j6qm_y4Nd-&-MlA<JFc!I2U;_{t^-hQ#J;f8!Cq(^Dxon+<tYIr$GW-D=jhiyQh*
zDBwmx%Xp#1tc@5q@>_o)Cjd2tsq~Vt>?;_bz;<WFhSAf#*=)_Up&ZL259N}%V03iZ
zMdWl+#%Dt$F^;^{n{7>Tu1+GQs{@jPN!?=KMwZqNeb$XRPCY@mDZ36HHell!@@8lu
zbV)t0AGWMpJ-4~-2X5*Y9Mp%g8pl#=v?%qfr+sYDtR5mm8RsWW()@lvleI|>&ta{@
zMN{?&qb&cf7}1a!Pd$0B4qF*Iy}2#My8O;d6}~SHs2K4{!bDm?wD!dIErecjt87=K
z*4`>XZUZZ~uCBOV>*?EtDg5u4JjGY*K}_f`Op%|M);~L)LyMSZUfu)oZpCHyKJ8^#
zo#RW10=Of983BP*feoOfZr{}uPB(ld(?{C-G9a)hkcMJ1U2{Rmy+FE$xE=aW6q%p(
zJvC&zUn@o;@q>cZ*|PnnMwyt1i8Z4!;y`N6K#8}#KOOAMCz9OG;kuQCoW?i-NVn4E
zD=v6R12d#cAKtRQR-PSEFmZXJGf~(^gmHS9P$rCWq}F~d<p5%p8^ut~+0bA^$nhO>
z3(}yGhE>SL+?kUQ{IW)P0SiwHyq}?SP5Gp9uU79jT}WJCdAYtE<rsRd(hcsC^3)yN
z@6gCx*19OdRv4Am=s%V=<>xd;0RV%HJC`cFMtOC13GV+*9KAMapdjp*peR_|)OMfv
zGw|3C1GwPrFG8piwD#!xD|HD9j9j(BA`R&-EwRC(_Sp26v$qAhDEDea@{>`|_Q(}1
z{UgWYbRuIgEZUhDkTw^3+vras^P1$#QPRlG%DoT&uE=bBrJCt9wgC3cj>uK}vDGj;
zySftZMVPWmFW2GTb0&}+&c{wruB9M_v?;O5)l*}{8+KqH$k$!UiVzGM3U0Z)xVfDb
zL@Q03`l!eO7~0ugqP!;bXc<6Jk9N*DjTQLQxu3x4!89>&F8aQwW64F$YUM%0IM$#3
zLY^85PDF>96;LLX$jHg~Yz|pAM)E&gk?Whnn;@N(dXJ#U_n_{^NGDcARSbtf?`1yv
zNd5g?gkrx5;kMjSkWlm&A$(Uw?<{URR*P+L-H3B7yC+iXF{jYnOx`2L%{`ZX{IvAk
z{N0qi3!%aIS<HLX{w8S%C0$K6tdEk_^VEnZdgT-VfL=tWQ@z<&F+Qh#+efhmSl_^s
z;W7l+NVit8M&I4Q%$LbiuNTWD+S%Qaz>Pm_z!4;m%8*il$)2^Xi{I4p`WI7`74!O!
z$V3#xfEe=;<60jf-Je3+7zfWHOkB_{@4*zA+Q?^OqCOI|!E8+ysv7p}_s>0Ybt?P`
zTxa7zoV4bH@_DrH9Vocf#cHv>pMl^NDVLX4BaO$1)oqN$QmFgOca2*!`A5mmr24ds
zdw5OWmuBb#kEh*hlneqsgokCEcd6OsDE^x$UK78xt7DY-dCdHByt!H39v`nCE5enh
z9Y&lFz~f<@PLfjje?-Jiazf^z8hBA21p!8_W<65^nmky8GzfeCl*^HTj9vk^k<Yq+
z(^{J4se|yki>d3iqx$xCM5)9g5B4f{gs2xryOydvVDhDg5s+j;@TqjNw^N%T!&Q1u
z0zpDtUA*7X!(V9FP3#!)C3&{apZ;)+eWyE3);u7$MC&UF8iky82RB6#(;Dew{p-bE
znI3N^JL}*2?ag1=Z`=buhGQnWGR4y_*_KDR(PbKlp5wPLf6>+-4bMrUkxLDuOIUW%
z=mrLz6V}G4nNaO-8r|3j15Y15SZV%B)WFI?I8LnY?aOW812(i{uB+30m*j!pB=W$E
zo1o;uUy_o$C4<L&n@jjJ)QQk8*Rqg_yTjKMoVdD*n9^wu8(d6IvdsNkZ|t;1x{9vf
zeL7O632{J2t}C|0Pu`6){&`@V--P%sXb!9ho4hyeIc;)TE}4l&UzMevaI18J<2JDD
zqm<W|a!d3mug~~yWwF*{m3w<h>H2`=9wVhBm%6tfMPV+f%D8tv4Pd>ol=!MY6wVP^
zZ(<fjOqDO#h%9hC=J%^{u6E&=jI*m4$(+(;^n2DFCn71w<|CXRYZ(Np=dGw(b#6cL
zQq=}K$PRhxzBz(+F{Vu3aZK;wh)>wHlQ?i*t_GLjLFhY<Ne#+V{n{AO{}`lcu}V{G
zqR`%az(u^|l*WC*FoVxpd$o6YO8q98L(Q|hJP-byWz2fc{Fn&oQ|(RorUXtmB>|Gu
z#m0x{s*;Cfctn*Q#QfrU6HQYfxjpJieBCb+IN3;hBTc?1lW-ipv%TrS5hH=l???c!
zvlGX>0H=q2GTP*C31mU5VaJGqzmX^iC*X;B!Dy{2d{KK@G1(pLj1V#%>Q*XOoU1kX
z;|zHaUq4)5zua+Kc3&@E*A9*=bQ)_nLtb|?7BL<3ryqQfT2oZTPU#20<Lqs*Tr%QA
zz9KjvPe*LKNWf58?_8DMF!KxPja&I8b8CU0q9ztv7em{fCyrIIB{E)#vo4oTEKQut
zTJ>}oeWTjX+pVOp$rAQ&z?%2r4~pY5ltWFBdViAqd~-v&fs1Z%dxQOTQDm1W<!8G*
z5B(;er+hg;Pnlny(!26$U=`dVX*FyPF^liQB%!*r&a@`qOOb0P-%^~siIBPsh`|U#
zEK5|WXBn=%nFn>ly^%{H?D?uhJ{MT)r2t@tyqfVD4Z&mWMnWC3T=G{=A$o|N6Bm<9
zyun)Z7BSsytvZ!jvsNq@nObw0wq5~s=YmhwRIw{Q1cd`JvgXVIPnCmx%jatRH$&Q;
zJI|m`yLMkV(Q*TGP@S)P7K)n5;{c=wR>0n8Sjj46$WOg(hayjH&3KhCuSpZ_{q}^&
zU`+?FZyk7|^uCh4QB}p}_E3UDNrSId`}L@`0si!8-zIvyeFf1Rvi=x;{k5vq-vul^
z1S#DO-UlgCu^JAwwQJ{&U6F>o5wyNYm|@oN@a-~1{JZHt_Xe+mrc%F5WqwXMmOo4f
ztJ^HX!WdaJJ2ixxcnFuy%fG~g_!14{jHJyxmm9M8#&=oYQzEy#>k8t1{^xnmT-z_N
z*aHmzF6n1ahQX0Qsn<{UU=kWxz-qA`&H(E#Jp^nS9@<wtO;uLGy?pLo6TTX?Qz1`{
zwh<8{I>pFQPTDuo`L8JpV%aO@<>QlM!7aL#<?~LyTV4?bePL}*`LfUVf4n;N&AW|J
zpe9G@n9^L?0%JhRwm?R`gr7%{B7cxZLW=C-G1^2)mNq1AsuPi13=WWYGJ<{hlZ;P7
za%|Ao-z|KtW49JovA^ovq3Pj1=0Q)Oz&%}XZ@hQi+xmTo+l~GcuBn1hlp+wMlVh2D
Y=!qs_i+AJ6kC_KBH?=mYLb=BM7v+~S&;S4c
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/firebug/twisty-closed-firebug.svg
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- See license.txt for terms of usage -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   version="1.1"
+   width="11"
+   height="11"
+   id="svg2">
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient3792">
+      <stop
+         id="stop3795"
+         style="stop-color:#c3baaa;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3797"
+         style="stop-color:#ffffff;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="6.0530181"
+       y1="7.092885"
+       x2="2.8882971"
+       y2="1.7999334"
+       id="linearGradient3799"
+       xlink:href="#linearGradient3792"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0256411,0,0,1.0256411,-0.11538478,1.8846152)" />
+    <linearGradient
+       x1="6.0530181"
+       y1="7.092885"
+       x2="2.8882971"
+       y2="1.7999334"
+       id="linearGradient2992"
+       xlink:href="#linearGradient3792"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0256411,0,0,1.0256411,-0.11538478,1.8846152)" />
+    <linearGradient
+       x1="6.0530181"
+       y1="7.092885"
+       x2="2.8882971"
+       y2="1.7999334"
+       id="linearGradient2996"
+       xlink:href="#linearGradient3792"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0256411,0,0,1.0256411,0.88461522,0.8846152)" />
+  </defs>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <rect
+     width="8"
+     height="8"
+     rx="1"
+     ry="1"
+     x="1.5"
+     y="1.5"
+     id="rect3022"
+     style="fill:url(#linearGradient2996);fill-opacity:1;stroke:#7898b5;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+  <path
+     d="M 5,3 5,5 3,5 3,6 5,6 5,8 6,8 6,6 8,6 8,5 6,5 6,3 5,3 z"
+     id="rect3801"
+     style="fill:#000000;fill-opacity:1;stroke:none" />
+</svg>
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/firebug/twisty-open-firebug.svg
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- See license.txt for terms of usage -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   version="1.1"
+   width="11"
+   height="11"
+   id="svg2">
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient3792">
+      <stop
+         id="stop3795"
+         style="stop-color:#c3baaa;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3797"
+         style="stop-color:#ffffff;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="6.0530181"
+       y1="7.092885"
+       x2="2.8882971"
+       y2="1.7999334"
+       id="linearGradient3799"
+       xlink:href="#linearGradient3792"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0256411,0,0,1.0256411,-0.11538478,1.8846152)" />
+    <linearGradient
+       x1="6.0530181"
+       y1="7.092885"
+       x2="2.8882971"
+       y2="1.7999334"
+       id="linearGradient2992"
+       xlink:href="#linearGradient3792"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0256411,0,0,1.0256411,-0.11538478,1.8846152)" />
+    <linearGradient
+       x1="6.0530181"
+       y1="7.092885"
+       x2="2.8882971"
+       y2="1.7999334"
+       id="linearGradient2996"
+       xlink:href="#linearGradient3792"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0256411,0,0,1.0256411,0.88461522,0.8846152)" />
+  </defs>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <rect
+     width="8"
+     height="8"
+     rx="1"
+     ry="1"
+     x="1.5"
+     y="1.5"
+     id="rect3022"
+     style="fill:url(#linearGradient2996);fill-opacity:1;stroke:#7898b5;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+  <rect
+     width="5"
+     height="1"
+     x="3"
+     y="5"
+     id="rect3801"
+     style="fill:#000000;fill-opacity:1;stroke:none" />
+</svg>
--- a/devtools/client/themes/variables.css
+++ b/devtools/client/themes/variables.css
@@ -90,8 +90,49 @@
   --theme-graphs-purple: #df80ff;
   --theme-graphs-yellow: #d99b28;
   --theme-graphs-orange: #d96629;
   --theme-graphs-red: #eb5368;
   --theme-graphs-grey: #757873;
   --theme-graphs-full-red: #f00;
   --theme-graphs-full-blue: #00f;
 }
+
+:root.theme-firebug {
+  --theme-body-background: white;
+  --theme-sidebar-background: white;
+  --theme-contrast-background: #e6b064;
+
+  --theme-tab-toolbar-background: #fcfcfc;
+  --theme-toolbar-background: #fcfcfc;
+  --theme-selection-background: #4c9ed9;
+  --theme-selection-background-semitransparent: rgba(76, 158, 217, 0.15);
+  --theme-selection-color: #f5f7fa;
+  --theme-splitter-color: #dde1e4;
+  --theme-comment: #696969;
+
+  --theme-body-color: #393f4c;
+  --theme-body-color-alt: #585959;
+  --theme-content-color1: #292e33;
+  --theme-content-color2: #8fa1b2;
+  --theme-content-color3: #667380;
+
+  --theme-highlight-green: #2cbb0f;
+  --theme-highlight-blue: #0088cc;
+  --theme-highlight-bluegrey: #0072ab;
+  --theme-highlight-purple: #5b5fff;
+  --theme-highlight-lightorange: #d97e00;
+  --theme-highlight-orange: #f13c00;
+  --theme-highlight-red: #ed2655;
+  --theme-highlight-pink: #b82ee5;
+
+  /* Colors used in Graphs, like performance tools. Similar colors to Chrome's timeline. */
+  --theme-graphs-green: #85d175;
+  --theme-graphs-blue: #83b7f6;
+  --theme-graphs-bluegrey: #0072ab;
+  --theme-graphs-purple: #b693eb;
+  --theme-graphs-yellow: #efc052;
+  --theme-graphs-orange: #d97e00;
+  --theme-graphs-red: #e57180;
+  --theme-graphs-grey: #cccccc;
+  --theme-graphs-full-red: #f00;
+  --theme-graphs-full-blue: #00f;
+}
--- a/devtools/shared/layout/utils.js
+++ b/devtools/shared/layout/utils.js
@@ -1,31 +1,30 @@
 /* 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 { Ci } = require("chrome");
+const { Ci, Cc } = require("chrome");
 const { memoize } = require("sdk/lang/functional");
 
-loader.lazyRequireGetter(this, "setIgnoreLayoutChanges",
-  "devtools/server/actors/layout", true);
+loader.lazyRequireGetter(this, "setIgnoreLayoutChanges", "devtools/server/actors/layout", true);
 exports.setIgnoreLayoutChanges = (...args) =>
   this.setIgnoreLayoutChanges(...args);
 
 /**
  * Returns the `DOMWindowUtils` for the window given.
  *
  * @param {DOMWindow} win
  * @returns {DOMWindowUtils}
  */
 const utilsFor = memoize(
-  (win) => win.QueryInterface(Ci.nsIInterfaceRequestor)
-              .getInterface(Ci.nsIDOMWindowUtils)
+  win => win.QueryInterface(Ci.nsIInterfaceRequestor)
+            .getInterface(Ci.nsIDOMWindowUtils)
 );
 
 /**
  * like win.top, but goes through mozbrowsers and mozapps iframes.
  *
  * @param {DOMWindow} win
  * @return {DOMWindow}
  */
@@ -33,17 +32,18 @@ function getTopWindow(win) {
   let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIWebNavigation)
                     .QueryInterface(Ci.nsIDocShell);
 
   if (!docShell.isMozBrowserOrApp) {
     return win.top;
   }
 
-  let topDocShell = docShell.getSameTypeRootTreeItemIgnoreBrowserAndAppBoundaries();
+  let topDocShell =
+    docShell.getSameTypeRootTreeItemIgnoreBrowserAndAppBoundaries();
 
   return topDocShell
           ? topDocShell.contentViewer.DOMDocument.defaultView
           : null;
 }
 
 exports.getTopWindow = getTopWindow;
 
@@ -93,17 +93,18 @@ function getParentWindow(win) {
   let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
                  .getInterface(Ci.nsIWebNavigation)
                  .QueryInterface(Ci.nsIDocShell);
 
   if (!docShell.isMozBrowserOrApp) {
     return win.parent;
   }
 
-  let parentDocShell = docShell.getSameTypeParentIgnoreBrowserAndAppBoundaries();
+  let parentDocShell =
+    docShell.getSameTypeParentIgnoreBrowserAndAppBoundaries();
 
   return parentDocShell
           ? parentDocShell.contentViewer.DOMDocument.defaultView
           : null;
 }
 
 exports.getParentWindow = getParentWindow;
 
@@ -135,33 +136,31 @@ function getFrameOffsets(boundaryWindow,
   let xOffset = 0;
   let yOffset = 0;
   let frameWin = node.ownerDocument.defaultView;
   let scale = getCurrentZoom(node);
 
   if (boundaryWindow === null) {
     boundaryWindow = getTopWindow(frameWin);
   } else if (typeof boundaryWindow === "undefined") {
-    throw new Error("No `boundaryWindow` given. Use `null` for the default one.");
+    throw new Error("No boundaryWindow given. Use null for the default one.");
   }
 
   while (frameWin !== boundaryWindow) {
-
     let frameElement = getFrameElement(frameWin);
     if (!frameElement) {
       break;
     }
 
     // We are in an iframe.
     // We take into account the parent iframe position and its
     // offset (borders and padding).
     let frameRect = frameElement.getBoundingClientRect();
 
-    let [offsetTop, offsetLeft] =
-      getIframeContentOffset(frameElement);
+    let [offsetTop, offsetLeft] = getFrameContentOffset(frameElement);
 
     xOffset += frameRect.left + offsetLeft;
     yOffset += frameRect.top + offsetTop;
 
     frameWin = getParentWindow(frameWin);
   }
 
   return [xOffset * scale, yOffset * scale];
@@ -245,81 +244,80 @@ exports.getAdjustedQuads = getAdjustedQu
 
 /**
  * Compute the absolute position and the dimensions of a node, relativalely
  * to the root window.
 
  * @param {DOMWindow} boundaryWindow
  *        The window where to stop to iterate. If `null` is given, the top
  *        window is used.
- * @param {DOMNode} aNode
+ * @param {DOMNode} node
  *        a DOM element to get the bounds for
- * @param {DOMWindow} aContentWindow
+ * @param {DOMWindow} contentWindow
  *        the content window holding the node
  * @return {Object}
  *         A rect object with the {top, left, width, height} properties
  */
-function getRect(boundaryWindow, aNode, aContentWindow) {
-  let frameWin = aNode.ownerDocument.defaultView;
-  let clientRect = aNode.getBoundingClientRect();
+function getRect(boundaryWindow, node, contentWindow) {
+  let frameWin = node.ownerDocument.defaultView;
+  let clientRect = node.getBoundingClientRect();
 
   if (boundaryWindow === null) {
     boundaryWindow = getTopWindow(frameWin);
   } else if (typeof boundaryWindow === "undefined") {
-    throw new Error("No `boundaryWindow` given. Use `null` for the default one.");
+    throw new Error("No boundaryWindow given. Use null for the default one.");
   }
 
   // Go up in the tree of frames to determine the correct rectangle.
   // clientRect is read-only, we need to be able to change properties.
   let rect = {
-    top: clientRect.top + aContentWindow.pageYOffset,
-    left: clientRect.left + aContentWindow.pageXOffset,
+    top: clientRect.top + contentWindow.pageYOffset,
+    left: clientRect.left + contentWindow.pageXOffset,
     width: clientRect.width,
     height: clientRect.height
   };
 
   // We iterate through all the parent windows.
   while (frameWin !== boundaryWindow) {
     let frameElement = getFrameElement(frameWin);
     if (!frameElement) {
       break;
     }
 
     // We are in an iframe.
     // We take into account the parent iframe position and its
     // offset (borders and padding).
     let frameRect = frameElement.getBoundingClientRect();
 
-    let [offsetTop, offsetLeft] =
-      getIframeContentOffset(frameElement);
+    let [offsetTop, offsetLeft] = getFrameContentOffset(frameElement);
 
     rect.top += frameRect.top + offsetTop;
     rect.left += frameRect.left + offsetLeft;
 
     frameWin = getParentWindow(frameWin);
   }
 
   return rect;
-};
+}
 exports.getRect = getRect;
 
 /**
  * Get the 4 bounding points for a node taking iframes into account.
  * Note that for transformed nodes, this will return the untransformed bound.
  *
  * @param {DOMWindow} boundaryWindow
  *        The window where to stop to iterate. If `null` is given, the top
  *        window is used.
  * @param {DOMNode} node
  * @return {Object}
  *         An object with p1,p2,p3,p4 properties being {x,y} objects
  */
 function getNodeBounds(boundaryWindow, node) {
   if (!node) {
-    return;
+    return null;
   }
 
   let scale = getCurrentZoom(node);
 
   // Find out the offset of the node in its current frame
   let offsetLeft = 0;
   let offsetTop = 0;
   let el = node;
@@ -358,79 +356,106 @@ function getNodeBounds(boundaryWindow, n
     p2: {x: xOffset + width, y: yOffset},
     p3: {x: xOffset + width, y: yOffset + height},
     p4: {x: xOffset, y: yOffset + height}
   };
 }
 exports.getNodeBounds = getNodeBounds;
 
 /**
- * Returns iframe content offset (iframe border + padding).
+ * Same as doing iframe.contentWindow but works with all types of container
+ * elements that act like frames (e.g. <embed>), where 'contentWindow' isn't a
+ * property that can be accessed.
+ * This uses the inIDeepTreeWalker instead.
+ * @param {DOMNode} frame
+ * @return {Window}
+ */
+function safelyGetContentWindow(frame) {
+  if (frame.contentWindow) {
+    return frame.contentWindow;
+  }
+
+  let walker = Cc["@mozilla.org/inspector/deep-tree-walker;1"]
+               .createInstance(Ci.inIDeepTreeWalker);
+  walker.showSubDocuments = true;
+  walker.showDocumentsAsNodes = true;
+  walker.init(frame, Ci.nsIDOMNodeFilter.SHOW_ALL);
+  walker.currentNode = frame;
+
+  let document = walker.nextNode();
+  if (!document || !document.defaultView) {
+    throw new Error("Couldn't get the content window inside frame " + frame);
+  }
+
+  return document.defaultView;
+}
+
+/**
+ * Returns a frame's content offset (frame border + padding).
  * Note: this function shouldn't need to exist, had the platform provided a
- * suitable API for determining the offset between the iframe's content and
+ * suitable API for determining the offset between the frame's content and
  * its bounding client rect. Bug 626359 should provide us with such an API.
  *
- * @param {DOMNode} aIframe
- *        The iframe.
+ * @param {DOMNode} frame
+ *        The frame.
  * @return {Array} [offsetTop, offsetLeft]
- *         offsetTop is the distance from the top of the iframe and the top of
+ *         offsetTop is the distance from the top of the frame and the top of
  *         the content document.
- *         offsetLeft is the distance from the left of the iframe and the left
+ *         offsetLeft is the distance from the left of the frame and the left
  *         of the content document.
  */
-function getIframeContentOffset(aIframe) {
-  let style = aIframe.contentWindow.getComputedStyle(aIframe, null);
+function getFrameContentOffset(frame) {
+  let style = safelyGetContentWindow(frame).getComputedStyle(frame, null);
 
   // In some cases, the computed style is null
   if (!style) {
     return [0, 0];
   }
 
-  let paddingTop = parseInt(style.getPropertyValue("padding-top"));
-  let paddingLeft = parseInt(style.getPropertyValue("padding-left"));
+  let paddingTop = parseInt(style.getPropertyValue("padding-top"), 10);
+  let paddingLeft = parseInt(style.getPropertyValue("padding-left"), 10);
 
-  let borderTop = parseInt(style.getPropertyValue("border-top-width"));
-  let borderLeft = parseInt(style.getPropertyValue("border-left-width"));
+  let borderTop = parseInt(style.getPropertyValue("border-top-width"), 10);
+  let borderLeft = parseInt(style.getPropertyValue("border-left-width"), 10);
 
   return [borderTop + paddingTop, borderLeft + paddingLeft];
 }
-exports.getIframeContentOffset = getIframeContentOffset;
 
 /**
  * Find an element from the given coordinates. This method descends through
  * frames to find the element the user clicked inside frames.
  *
- * @param {DOMDocument} aDocument
+ * @param {DOMDocument} document
  *        The document to look into.
- * @param {Number} aX
- * @param {Number} aY
+ * @param {Number} x
+ * @param {Number} y
  * @return {DOMNode}
  *         the element node found at the given coordinates, or null if no node
  *         was found
  */
-function getElementFromPoint(aDocument, aX, aY) {
-  let node = aDocument.elementFromPoint(aX, aY);
+function getElementFromPoint(document, x, y) {
+  let node = document.elementFromPoint(x, y);
   if (node && node.contentDocument) {
     if (node instanceof Ci.nsIDOMHTMLIFrameElement) {
       let rect = node.getBoundingClientRect();
 
-      // Gap between the iframe and its content window.
-      let [offsetTop, offsetLeft] = getIframeContentOffset(node);
+      // Gap between the frame and its content window.
+      let [offsetTop, offsetLeft] = getFrameContentOffset(node);
 
-      aX -= rect.left + offsetLeft;
-      aY -= rect.top + offsetTop;
+      x -= rect.left + offsetLeft;
+      y -= rect.top + offsetTop;
 
-      if (aX < 0 || aY < 0) {
-        // Didn't reach the content document, still over the iframe.
+      if (x < 0 || y < 0) {
+        // Didn't reach the content document, still over the frame.
         return node;
       }
     }
     if (node instanceof Ci.nsIDOMHTMLIFrameElement ||
         node instanceof Ci.nsIDOMHTMLFrameElement) {
-      let subnode = getElementFromPoint(node.contentDocument, aX, aY);
+      let subnode = getElementFromPoint(node.contentDocument, x, y);
       if (subnode) {
         node = subnode;
       }
     }
   }
   return node;
 }
 exports.getElementFromPoint = getElementFromPoint;
@@ -440,35 +465,37 @@ exports.getElementFromPoint = getElement
  *
  * @param {DOMNode} elem
  *        The element that needs to appear in the viewport.
  * @param {Boolean} centered
  *        true if you want it centered, false if you want it to appear on the
  *        top of the viewport. It is true by default, and that is usually what
  *        you want.
  */
-function scrollIntoViewIfNeeded(elem, centered=true) {
+function scrollIntoViewIfNeeded(elem, centered = true) {
   let win = elem.ownerDocument.defaultView;
   let clientRect = elem.getBoundingClientRect();
 
   // The following are always from the {top, bottom}
   // of the viewport, to the {top, …} of the box.
   // Think of them as geometrical vectors, it helps.
   // The origin is at the top left.
 
   let topToBottom = clientRect.bottom;
   let bottomToTop = clientRect.top - win.innerHeight;
-  let yAllowed = true;  // We allow one translation on the y axis.
+  // We allow one translation on the y axis.
+  let yAllowed = true;
 
   // Whatever `centered` is, the behavior is the same if the box is
   // (even partially) visible.
   if ((topToBottom > 0 || !centered) && topToBottom <= elem.offsetHeight) {
     win.scrollBy(0, topToBottom - elem.offsetHeight);
     yAllowed = false;
-  } else if ((bottomToTop < 0 || !centered) && bottomToTop >= -elem.offsetHeight) {
+  } else if ((bottomToTop < 0 || !centered) &&
+             bottomToTop >= -elem.offsetHeight) {
     win.scrollBy(0, bottomToTop + elem.offsetHeight);
     yAllowed = false;
   }
 
   // If we want it centered, and the box is completely hidden,
   // then we center it explicitly.
   if (centered) {
     if (yAllowed && (topToBottom <= 0 || bottomToTop >= 0)) {
@@ -479,25 +506,27 @@ function scrollIntoViewIfNeeded(elem, ce
   }
 }
 exports.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded;
 
 /**
  * Check if a node and its document are still alive
  * and attached to the window.
  *
- * @param {DOMNode} aNode
+ * @param {DOMNode} node
  * @return {Boolean}
  */
-function isNodeConnected(aNode) {
+function isNodeConnected(node) {
+  if (!node.ownerDocument || !node.ownerDocument.defaultView) {
+    return false;
+  }
+
   try {
-    let connected = (aNode.ownerDocument && aNode.ownerDocument.defaultView &&
-                    !(aNode.compareDocumentPosition(aNode.ownerDocument.documentElement) &
-                    aNode.DOCUMENT_POSITION_DISCONNECTED));
-    return connected;
+    return !(node.compareDocumentPosition(node.ownerDocument.documentElement) &
+             node.DOCUMENT_POSITION_DISCONNECTED);
   } catch (e) {
     // "can't access dead object" error
     return false;
   }
 }
 exports.isNodeConnected = isNodeConnected;
 
 /**
@@ -625,18 +654,23 @@ exports.isShadowAnonymous = isShadowAnon
  * nsIDOMWindowUtils instance to avoid querying it every time.
  *
  * @param {DOMNode|DOMWindow}
  *        The node for which the zoom factor should be calculated, or its
  *        owner window.
  * @return {Number}
  */
 function getCurrentZoom(node) {
-  let win = node instanceof Ci.nsIDOMNode ? node.ownerDocument.defaultView :
-            node instanceof Ci.nsIDOMWindow ? node : null;
+  let win = null;
+
+  if (node instanceof Ci.nsIDOMNode) {
+    win = node.ownerDocument.defaultView;
+  } else if (node instanceof Ci.nsIDOMWindow) {
+    win = node;
+  }
 
   if (!win) {
     throw new Error("Unable to get the zoom from the given argument.");
   }
 
   return utilsFor(win).fullZoom;
 }
 exports.getCurrentZoom = getCurrentZoom;
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -1,10 +1,11 @@
 [DEFAULT]
 skip-if = buildapp == 'b2g' # b2g( ReferenceError: MediaSource is not defined)
+subsuite = media
 support-files =
   mediasource.js
   seek.webm seek.webm^headers^
   seek_lowres.webm seek_lowres.webm^headers^
   bipbop/bipbop2s.mp4 bipbop/bipbop2s.mp4^headers^
   bipbop/bipbopinit.mp4 bipbop/bipbop_audioinit.mp4 bipbop/bipbop_videoinit.mp4
   bipbop/bipbop1.m4s bipbop/bipbop_audio1.m4s bipbop/bipbop_video1.m4s
   bipbop/bipbop2.m4s bipbop/bipbop_audio2.m4s bipbop/bipbop_video2.m4s
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -17,16 +17,17 @@
 # throws an error (and does not cause a crash or hang), just add it to
 # gErrorTests in manifest.js.
 
 # To test for a specific bug in handling a specific resource type, make the
 # test first check canPlayType for the type, and if it's not supported, just
 # do ok(true, "Type not supported") and stop the test.
 
 [DEFAULT]
+subsuite = media
 skip-if = buildapp == 'mulet' || android_version == '18'
 support-files =
   16bit_wave_extrametadata.wav
   16bit_wave_extrametadata.wav^headers^
   320x240.ogv
   320x240.ogv^headers^
   448636.ogv
   448636.ogv^headers^
--- a/dom/media/tests/mochitest/identity/mochitest.ini
+++ b/dom/media/tests/mochitest/identity/mochitest.ini
@@ -1,12 +1,13 @@
 [DEFAULT]
 # Android 2.3 - bug 981881
 # won't run on b2g desktop tests - bug 1119993
 # broken HTTPS on b2g emulator - bug 1135339
+subsuite = media
 skip-if = android_version == '10' || android_version == '18' || (buildapp == 'b2g' && toolkit != 'gonk') || (buildapp == 'b2g' && toolkit == 'gonk') || buildapp == 'mulet'
 support-files =
   /.well-known/idp-proxy/idp.js
   identityPcTest.js
 tags = msg
 
 [test_idpproxy.html]
 support-files =
--- a/dom/media/tests/mochitest/ipc/mochitest.ini
+++ b/dom/media/tests/mochitest/ipc/mochitest.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
 tags=msg
+subsuite=media
 support-files =
   ipc.json
 
 skip-if = e10s
 
 [test_ipc.html]
 skip-if =  buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' #bug 910661 # b2g(nested ipc not working) b2g-debug(debug-only failure) b2g-desktop(nested ipc not working)
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -1,11 +1,13 @@
 [DEFAULT]
 # Android 2.3 - bug 981881
 tags = msg webrtc
+<<<<<<< local
+subsuite = media
 skip-if = android_version == '10' || (buildapp == 'mulet') || (toolkit == 'gonk' && debug) # b2g(Either bug 1171118 or bug 1169838, take your pick)
 support-files =
   head.js
   dataChannel.js
   mediaStreamPlayback.js
   network.js
   nonTrickleIce.js
   pc.js
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -1,11 +1,12 @@
 [DEFAULT]
 tags=msg
 tags = webaudio
+subsuite = media
 skip-if = ((buildapp == 'b2g') && (toolkit != 'gonk' || debug)) #b2g-debug,b2g-desktop(bug 916135)
 support-files =
   audio-expected.wav
   audio-mono-expected-2.wav
   audio-mono-expected.wav
   audio-quad.wav
   audio.ogv
   audioBufferSourceNodeDetached_worker.js
--- a/dom/media/webspeech/recognition/test/mochitest.ini
+++ b/dom/media/webspeech/recognition/test/mochitest.ini
@@ -1,11 +1,12 @@
 [DEFAULT]
 tags=msg
 skip-if = buildapp == 'b2g' || (e10s && debug && os == 'win') # Bug 1191270, bug 1037287, bug 967606, bug 1096400, bug 1238542 etc
+subsuite = media
 support-files =
   head.js
   hello.ogg
   hello.ogg^headers^
   silence.ogg
   silence.ogg^headers^
 [test_abort.html]
 skip-if = toolkit == 'android' # bug 1037287
--- a/dom/media/webspeech/synth/test/mochitest.ini
+++ b/dom/media/webspeech/synth/test/mochitest.ini
@@ -1,10 +1,11 @@
 [DEFAULT]
 tags=msg
+subsuite = media
 support-files =
   common.js
   file_bfcache_frame.html
   file_setup.html
   file_speech_queue.html
   file_speech_simple.html
   file_speech_cancel.html
   file_speech_error.html
--- a/layout/base/AccessibleCaretManager.cpp
+++ b/layout/base/AccessibleCaretManager.cpp
@@ -18,16 +18,19 @@
 #include "nsCaret.h"
 #include "nsContainerFrame.h"
 #include "nsContentUtils.h"
 #include "nsFocusManager.h"
 #include "nsFrame.h"
 #include "nsFrameSelection.h"
 #include "nsGenericHTMLElement.h"
 #include "nsIHapticFeedback.h"
+#ifdef MOZ_WIDGET_ANDROID
+#include "nsWindow.h"
+#endif
 
 namespace mozilla {
 
 #undef AC_LOG
 #define AC_LOG(message, ...)                                                   \
   AC_LOG_BASE("AccessibleCaretManager (%p): " message, this, ##__VA_ARGS__);
 
 #undef AC_LOGV
@@ -816,16 +819,25 @@ AccessibleCaretManager::SelectWord(nsIFr
 
 void
 AccessibleCaretManager::SetSelectionDragState(bool aState) const
 {
   RefPtr<nsFrameSelection> fs = GetFrameSelection();
   if (fs) {
     fs->SetDragState(aState);
   }
+
+  // Pin Fennecs DynamicToolbarAnimator in place before/after dragging,
+  // to avoid co-incident screen scrolling.
+  #ifdef MOZ_WIDGET_ANDROID
+    nsIDocument* doc = mPresShell->GetDocument();
+    MOZ_ASSERT(doc);
+    nsIWidget* widget = nsContentUtils::WidgetForDocument(doc);
+    static_cast<nsWindow*>(widget)->SetSelectionDragState(aState);
+  #endif
 }
 
 void
 AccessibleCaretManager::SetSelectionDirection(nsDirection aDir) const
 {
   Selection* selection = GetSelection();
   if (selection) {
     selection->AdjustAnchorFocusForMultiRange(aDir);
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -183,16 +183,21 @@ LOCAL_INCLUDES += [
     '/docshell/base',
     '/dom/base',
     '/dom/html',
     '/dom/svg',
     '/dom/xbl',
     '/view',
 ]
 
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
+    LOCAL_INCLUDES += [
+        '/widget/android',
+    ]
+
 FINAL_LIBRARY = 'xul'
 
 BROWSER_CHROME_MANIFESTS += ['tests/browser.ini']
 MARIONETTE_LAYOUT_MANIFESTS += ['tests/marionette/manifest.ini']
 MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
 MOCHITEST_CHROME_MANIFESTS += ['tests/chrome/chrome.ini']
 
 CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -2812,17 +2812,19 @@ nsDisplayBackgroundImage::ConfigureLayer
   // It's possible (for example, due to downscale-during-decode) that the
   // ImageContainer this ImageLayer is holding has a different size from the
   // intrinsic size of the image. For this reason we compute the transform using
   // the ImageContainer's size rather than the image's intrinsic size.
   // XXX(seth): In reality, since the size of the ImageContainer may change
   // asynchronously, this is not enough. Bug 1183378 will provide a more
   // complete fix, but this solution is safe in more cases than simply relying
   // on the intrinsic size.
-  IntSize containerSize = aLayer->GetContainer()->GetCurrentSize();
+  IntSize containerSize = aLayer->GetContainer()
+                        ? aLayer->GetContainer()->GetCurrentSize()
+                        : IntSize(imageWidth, imageHeight);
 
   const LayoutDevicePoint p = mImageLayerDestRect.TopLeft();
   Matrix transform = Matrix::Translation(p.x, p.y);
   transform.PreScale(mImageLayerDestRect.width / containerSize.width,
                      mImageLayerDestRect.height / containerSize.height);
   aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
 }
 
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1706,17 +1706,19 @@ nsDisplayImage::ConfigureLayer(ImageLaye
   // It's possible (for example, due to downscale-during-decode) that the
   // ImageContainer this ImageLayer is holding has a different size from the
   // intrinsic size of the image. For this reason we compute the transform using
   // the ImageContainer's size rather than the image's intrinsic size.
   // XXX(seth): In reality, since the size of the ImageContainer may change
   // asynchronously, this is not enough. Bug 1183378 will provide a more
   // complete fix, but this solution is safe in more cases than simply relying
   // on the intrinsic size.
-  IntSize containerSize = aLayer->GetContainer()->GetCurrentSize();
+  IntSize containerSize = aLayer->GetContainer()
+                        ? aLayer->GetContainer()->GetCurrentSize()
+                        : IntSize(imageWidth, imageHeight);
 
   const LayoutDevicePoint p = destRect.TopLeft();
   Matrix transform = Matrix::Translation(p.x, p.y);
   transform.PreScale(destRect.Width() / containerSize.width,
                      destRect.Height() / containerSize.height);
   aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
 }
 
--- a/layout/xul/nsImageBoxFrame.cpp
+++ b/layout/xul/nsImageBoxFrame.cpp
@@ -483,17 +483,19 @@ nsDisplayXULImage::ConfigureLayer(ImageL
   // It's possible (for example, due to downscale-during-decode) that the
   // ImageContainer this ImageLayer is holding has a different size from the
   // intrinsic size of the image. For this reason we compute the transform using
   // the ImageContainer's size rather than the image's intrinsic size.
   // XXX(seth): In reality, since the size of the ImageContainer may change
   // asynchronously, this is not enough. Bug 1183378 will provide a more
   // complete fix, but this solution is safe in more cases than simply relying
   // on the intrinsic size.
-  IntSize containerSize = aLayer->GetContainer()->GetCurrentSize();
+  IntSize containerSize = aLayer->GetContainer()
+                        ? aLayer->GetContainer()->GetCurrentSize()
+                        : IntSize(imageWidth, imageHeight);
 
   const LayoutDevicePoint p = destRect.TopLeft();
   Matrix transform = Matrix::Translation(p.x, p.y);
   transform.PreScale(destRect.Width() / containerSize.width,
                      destRect.Height() / containerSize.height);
   aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
 }
 
--- a/mobile/android/app/lint.xml
+++ b/mobile/android/app/lint.xml
@@ -46,16 +46,17 @@
     <issue id="AdapterViewChildren" severity="error" />
     <issue id="AddJavascriptInterface" severity="error" />
     <issue id="AllowBackup" severity="error" />
     <issue id="AlwaysShowAction" severity="error" />
     <issue id="AndroidGradlePluginVersion" severity="error" />
     <issue id="AppCompatMethod" severity="error" />
     <issue id="AppIndexingError" severity="error" />
     <issue id="AppIndexingWarning" severity="error" />
+    <issue id="Assert" severity="error" />
     <issue id="ButtonCase" severity="error" />
     <issue id="ButtonOrder" severity="error" />
     <issue id="ByteOrderMark" severity="error" />
     <issue id="CheckResult" severity="error" />
     <issue id="Correctness" severity="error" />
     <issue id="CutPasteId" severity="error" />
     <issue id="DalvikOverride" severity="error" />
     <issue id="DeviceAdmin" severity="error" />
@@ -175,16 +176,17 @@
     <issue id="Typos" severity="error" />
     <issue id="UniqueConstants" severity="error" />
     <issue id="UniquePermission" severity="error" />
     <issue id="UnknownId" severity="error" />
     <issue id="UnknownIdInLayout" severity="error" />
     <issue id="UnlocalizedSms" severity="error" />
     <issue id="UnusedNamespace" severity="error" />
     <issue id="UnusedQuantity" severity="error" />
+    <issue id="UnusedResources" severity="error" />
     <issue id="Usability" severity="error" />
     <issue id="UseCheckPermission" severity="error" />
     <issue id="UseCompoundDrawables" severity="error" />
     <issue id="UselessLeaf" severity="error" />
     <issue id="UsesMinSdkAttributes" severity="error" />
     <issue id="UsingHttp" severity="error" />
     <issue id="ViewHolder" severity="error" />
     <issue id="ViewTag" severity="error" />
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -6,17 +6,16 @@
 package org.mozilla.gecko;
 
 import android.Manifest;
 import android.support.annotation.NonNull;
 import org.json.JSONArray;
 import org.mozilla.gecko.adjust.AdjustHelperInterface;
 import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.AppConstants.Versions;
-import org.mozilla.gecko.DynamicToolbar.PinReason;
 import org.mozilla.gecko.DynamicToolbar.VisibilityTransition;
 import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
 import org.mozilla.gecko.Tabs.TabEvents;
 import org.mozilla.gecko.animation.PropertyAnimator;
 import org.mozilla.gecko.animation.TransitionsTracker;
 import org.mozilla.gecko.animation.ViewHelper;
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.db.BrowserContract.Combined;
@@ -24,16 +23,17 @@ import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.db.SuggestedSites;
 import org.mozilla.gecko.distribution.Distribution;
 import org.mozilla.gecko.dlc.DownloadContentService;
 import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
 import org.mozilla.gecko.favicons.decoders.IconDirectoryEntry;
 import org.mozilla.gecko.firstrun.FirstrunAnimationContainer;
 import org.mozilla.gecko.gfx.DynamicToolbarAnimator;
+import org.mozilla.gecko.gfx.DynamicToolbarAnimator.PinReason;
 import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.home.BrowserSearch;
 import org.mozilla.gecko.home.HomeBanner;
 import org.mozilla.gecko.home.HomeConfig.PanelType;
 import org.mozilla.gecko.home.HomeConfigPrefsBackend;
 import org.mozilla.gecko.home.HomePager;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenInBackgroundListener;
--- a/mobile/android/base/java/org/mozilla/gecko/DynamicToolbar.java
+++ b/mobile/android/base/java/org/mozilla/gecko/DynamicToolbar.java
@@ -1,13 +1,12 @@
 package org.mozilla.gecko;
 
-import java.util.EnumSet;
-
 import org.mozilla.gecko.PrefsHelper.PrefHandlerBase;
+import org.mozilla.gecko.gfx.DynamicToolbarAnimator.PinReason;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.os.Build;
 import android.os.Bundle;
 import android.util.Log;
 
 public class DynamicToolbar {
@@ -21,27 +20,20 @@ public class DynamicToolbar {
     // the pref from Gecko telling us to turn it on.
     private volatile boolean prefEnabled;
     private boolean accessibilityEnabled;
     // On some device we have to force-disable the dynamic toolbar because of
     // bugs in the Android code. See bug 1231554.
     private final boolean forceDisabled;
 
     private final PrefsHelper.PrefHandler prefObserver;
-    private final EnumSet<PinReason> pinFlags = EnumSet.noneOf(PinReason.class);
     private LayerView layerView;
     private OnEnabledChangedListener enabledChangedListener;
     private boolean temporarilyVisible;
 
-    public enum PinReason {
-        RELAYOUT,
-        ACTION_MODE,
-        FULL_SCREEN
-    }
-
     public enum VisibilityTransition {
         IMMEDIATE,
         ANIMATE
     }
 
     /**
      * Listener for changes to the dynamic toolbar's enabled state.
      */
@@ -131,17 +123,18 @@ public class DynamicToolbar {
     public void setVisible(boolean visible, VisibilityTransition transition) {
         ThreadUtils.assertOnUiThread();
 
         if (layerView == null) {
             return;
         }
 
         // Don't hide the ActionBar/Toolbar, if it's pinned open by TextSelection.
-        if (visible == false && pinFlags.contains(PinReason.ACTION_MODE)) {
+        if (visible == false &&
+            layerView.getDynamicToolbarAnimator().isPinnedBy(PinReason.ACTION_MODE)) {
             return;
         }
 
         final boolean isImmediate = transition == VisibilityTransition.IMMEDIATE;
         if (visible) {
             layerView.getDynamicToolbarAnimator().showToolbar(isImmediate);
         } else {
             layerView.getDynamicToolbarAnimator().hideToolbar(isImmediate);
@@ -175,28 +168,21 @@ public class DynamicToolbar {
         if (temporarilyVisible) {
             temporarilyVisible = false;
             setVisible(true, VisibilityTransition.IMMEDIATE);
         }
     }
 
     public void setPinned(boolean pinned, PinReason reason) {
         ThreadUtils.assertOnUiThread();
-
         if (layerView == null) {
             return;
         }
 
-        if (pinned) {
-            pinFlags.add(reason);
-        } else {
-            pinFlags.remove(reason);
-        }
-
-        layerView.getDynamicToolbarAnimator().setPinned(!pinFlags.isEmpty());
+        layerView.getDynamicToolbarAnimator().setPinned(pinned, reason);
     }
 
     private void triggerEnabledListener() {
         if (enabledChangedListener != null) {
             enabledChangedListener.onEnabledChanged(isEnabled());
         }
     }
 
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
+++ b/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
@@ -11,22 +11,34 @@ import org.mozilla.gecko.util.ThreadUtil
 
 import android.graphics.PointF;
 import android.support.v4.view.ViewCompat;
 import android.util.Log;
 import android.view.animation.DecelerateInterpolator;
 import android.view.MotionEvent;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumSet;
 import java.util.List;
+import java.util.Set;
 
 public class DynamicToolbarAnimator {
     private static final String LOGTAG = "GeckoDynamicToolbarAnimator";
     private static final String PREF_SCROLL_TOOLBAR_THRESHOLD = "browser.ui.scroll-toolbar-threshold";
 
+    public static enum PinReason {
+        RELAYOUT,
+        ACTION_MODE,
+        FULL_SCREEN,
+        CARET_DRAG
+    }
+
+    private final Set<PinReason> pinFlags = Collections.synchronizedSet(EnumSet.noneOf(PinReason.class));
+
     // The duration of the animation in ns
     private static final long ANIMATION_DURATION = 250000000;
 
     private final GeckoLayerClient mTarget;
     private final List<LayerView.DynamicToolbarListener> mListeners;
 
     /* The translation to be applied to the toolbar UI view. This is the
      * distance from the default/initial location (at the top of the screen,
@@ -44,19 +56,16 @@ public class DynamicToolbarAnimator {
      */
     private float mLayerViewTranslation;
 
     /* This stores the maximum translation that can be applied to the toolbar
      * and layerview when scrolling. This is populated with the height of the
      * toolbar. */
     private float mMaxTranslation;
 
-    /* If this boolean is true, scroll changes will not affect translation */
-    private boolean mPinned;
-
     /* This interpolator is used for the above mentioned animation */
     private DecelerateInterpolator mInterpolator;
 
     /* This is the proportion of the viewport rect that needs to be travelled
      * while scrolling before the translation will start taking effect.
      */
     private float SCROLL_TOOLBAR_THRESHOLD = 0.20f;
     /* The ID of the prefs listener for the scroll-toolbar threshold */
@@ -144,22 +153,33 @@ public class DynamicToolbarAnimator {
     public float getMaxTranslation() {
         return mMaxTranslation;
     }
 
     public float getToolbarTranslation() {
         return mToolbarTranslation;
     }
 
-    public void setPinned(boolean pinned) {
-        mPinned = pinned;
+    /**
+     * If true, scroll changes will not affect translation.
+     */
+    public boolean isPinned() {
+        return !pinFlags.isEmpty();
     }
 
-    public boolean isPinned() {
-        return mPinned;
+    public boolean isPinnedBy(PinReason reason) {
+        return pinFlags.contains(reason);
+    }
+
+    public void setPinned(boolean pinned, PinReason reason) {
+        if (pinned) {
+            pinFlags.add(reason);
+        } else {
+            pinFlags.remove(reason);
+        }
     }
 
     public void showToolbar(boolean immediately) {
         animateToolbar(true, immediately);
     }
 
     public void hideToolbar(boolean immediately) {
         animateToolbar(false, immediately);
@@ -340,17 +360,17 @@ public class DynamicToolbarAnimator {
                 return translation;
             }
         }
 
         return 0;
     }
 
     boolean onInterceptTouchEvent(MotionEvent event) {
-        if (mPinned) {
+        if (isPinned()) {
             return false;
         }
 
         // Animations should never co-exist with the user touching the screen.
         if (mAnimationTask != null) {
             mTarget.getView().removeRenderTask(mAnimationTask);
             mAnimationTask = null;
         }
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java
+++ b/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java
@@ -5,16 +5,17 @@
 
 package org.mozilla.gecko.gfx;
 
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.GeckoThread;
 import org.mozilla.gecko.PrefsHelper;
 import org.mozilla.gecko.annotation.WrapForJNI;
+import org.mozilla.gecko.gfx.DynamicToolbarAnimator.PinReason;
 import org.mozilla.gecko.mozglue.JNIObject;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import org.json.JSONObject;
 
 import android.graphics.PointF;
 import android.util.TypedValue;
 import android.view.KeyEvent;
@@ -290,9 +291,18 @@ class NativePanZoomController extends JN
                     public void run() {
                         mOverscroll.setDistance(x, Overscroll.Axis.X);
                         mOverscroll.setDistance(y, Overscroll.Axis.Y);
                     }
                 });
             }
         }
     }
+
+    /**
+     * Active SelectionCaretDrag requires DynamicToolbarAnimator to be pinned
+     * to avoid unwanted scroll interactions.
+     */
+    @WrapForJNI
+    private void onSelectionDragState(boolean state) {
+        mView.getDynamicToolbarAnimator().setPinned(state, PinReason.CARET_DRAG);
+    }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/util/UnusedResourcesUtil.java
+++ b/mobile/android/base/java/org/mozilla/gecko/util/UnusedResourcesUtil.java
@@ -12,16 +12,28 @@ final class UnusedResourcesUtil {
             R.dimen.match_parent,
             R.dimen.wrap_content,
     };
 
     public static final int[] USED_IN_COLOR_PALETTE = {
             R.color.private_browsing_purple, // This will be used eventually, then this item removed.
     };
 
+    public static final int[] USED_IN_CRASH_REPORTER = {
+            R.string.crash_allow_contact2,
+            R.string.crash_close_label,
+            R.string.crash_comment,
+            R.string.crash_email,
+            R.string.crash_include_url2,
+            R.string.crash_message2,
+            R.string.crash_restart_label,
+            R.string.crash_send_report_message3,
+            R.string.crash_sorry,
+    };
+
     public static final int[] USED_IN_JS = {
             R.drawable.ab_search,
             R.drawable.alert_camera,
             R.drawable.alert_download,
             R.drawable.alert_download_animation,
             R.drawable.alert_mic,
             R.drawable.alert_mic_camera,
             R.drawable.casting,
@@ -35,36 +47,45 @@ final class UnusedResourcesUtil {
             R.drawable.reader,
             R.drawable.reader_active,
             R.drawable.sync_promo,
             R.drawable.undo_button_icon,
     };
 
     public static final int[] USED_IN_MANIFEST = {
             R.drawable.search_launcher,
+            R.string.crash_reporter_title,
             R.xml.fxaccount_authenticator,
             R.xml.fxaccount_syncadapter,
             R.xml.search_widget_info,
             R.xml.searchable,
     };
 
     public static final int[] USED_IN_SUGGESTEDSITES = {
             R.drawable.suggestedsites_amazon,
             R.drawable.suggestedsites_facebook,
+            R.drawable.suggestedsites_restricted_fxsupport,
+            R.drawable.suggestedsites_restricted_mozilla,
             R.drawable.suggestedsites_twitter,
+            R.drawable.suggestedsites_webmaker,
             R.drawable.suggestedsites_wikipedia,
             R.drawable.suggestedsites_youtube,
     };
 
     public static final int[] USED_IN_BOOKMARKDEFAULTS = {
             R.raw.bookmarkdefaults_favicon_addons,
             R.raw.bookmarkdefaults_favicon_support,
             R.raw.bookmarkdefaults_favicon_restricted_support,
             R.raw.bookmarkdefaults_favicon_restricted_webmaker,
+            R.string.bookmarkdefaults_title_restricted_support,
+            R.string.bookmarkdefaults_url_restricted_support,
+            R.string.bookmarkdefaults_title_restricted_webmaker,
+            R.string.bookmarkdefaults_url_restricted_webmaker,
     };
 
     public static final int[] USED_IN_PREFS = {
             R.xml.preferences_advanced,
             R.xml.preferences_accessibility,
             R.xml.preferences_home,
             R.xml.preferences_privacy,
+            R.xml.preferences_privacy_clear_tablet,
     };
 }
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -1,16 +1,12 @@
 <!-- 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/. -->
 
-
-<!ENTITY  no_space_to_start_error "There is not enough space available for &brandShortName; to start.">
-<!ENTITY  error_loading_file "An error occurred when trying to load files required to run &brandShortName;">
-
 <!ENTITY firstrun_panel_title_welcome "Welcome">
 <!ENTITY  onboard_start_message3 "Browse with &brandShortName;">
 <!ENTITY  onboard_start_subtext3 "Make your mobile Web browsing experience truly your own.">
 
 <!ENTITY firstrun_urlbar_message "Welcome to &brandShortName;">
 <!ENTITY firstrun_urlbar_subtext "Find things faster with helpful search suggestion shortcuts.">
 <!ENTITY firstrun_bookmarks_title "History">
 <!ENTITY firstrun_bookmarks_message "Your faves, front and center">
@@ -44,22 +40,16 @@
 <!ENTITY  crash_sorry "We\'re sorry">
 <!ENTITY  crash_comment "Add a comment (comments are publicly visible)">
 <!ENTITY  crash_allow_contact2 "Allow &vendorShortName; to contact me about this report">
 <!ENTITY  crash_email "Your email">
 <!ENTITY  crash_closing_alert "Exit without sending a crash report?">
 <!ENTITY  sending_crash_report "Sending crash report\u2026">
 <!ENTITY  crash_close_label "Close">
 <!ENTITY  crash_restart_label "Restart &brandShortName;">
-<!ENTITY  exit_label "Exit">
-
-<!ENTITY  launcher_shortcuts_title "&brandShortName; Web Apps">
-<!ENTITY  launcher_shortcuts_empty "No web apps were found">
-
-<!ENTITY choose_file "Choose File">
 
 <!ENTITY url_bar_default_text2 "Search or enter address">
 
 <!ENTITY bookmark "Bookmark">
 <!ENTITY bookmark_remove "Remove bookmark">
 <!ENTITY bookmark_added "Bookmark added">
 <!-- Localization note (bookmark_already_added) : This string is
      used as a label in a toast. It is the verb "to bookmark", not
@@ -72,20 +62,18 @@
 <!-- Localization note (screenshot_folder_label_in_bookmarks): We save links to screenshots
      the user takes. The folder we store these links in is located in the bookmarks list
      and is labeled by this String. -->
 <!ENTITY screenshot_folder_label_in_bookmarks "Screenshots">
 
 <!ENTITY history_today_section "Today">
 <!ENTITY history_yesterday_section "Yesterday">
 <!ENTITY history_week_section3 "Last 7 days">
-<!ENTITY history_this_month_section "This month">
 <!ENTITY history_older_section3 "Older than 6 months">
 
-<!ENTITY go "Go">
 <!ENTITY search "Search">
 <!ENTITY reload "Reload">
 <!ENTITY forward "Forward">
 <!ENTITY menu "Menu">
 <!ENTITY back "Back">
 <!ENTITY stop "Stop">
 <!ENTITY site_security "Site Security">
 <!ENTITY edit_mode_cancel "Cancel">
@@ -171,34 +159,28 @@
 <!-- Localization note (pref_search_hint) : "TIP" as in "hint", "clue" etc. Displayed as an
      advisory message on the customise search providers settings page explaining how to add new
      search providers.
      The &formatI; in the string will be replaced by a small image of the icon described, and can be moved to wherever
      it is applicable. -->
 <!ENTITY pref_search_hint "TIP: Add any website to your list of search providers by long-pressing on its search field and then tapping the &formatI; icon.">
 <!ENTITY pref_category_advanced "Advanced">
 <!ENTITY pref_category_advanced_summary2 "Restore tabs, plugins, developer tools">
-<!ENTITY pref_developer_remotedebugging "Remote debugging">
 <!ENTITY pref_developer_remotedebugging_usb "Remote debugging via USB">
 <!ENTITY pref_developer_remotedebugging_wifi "Remote debugging via Wi-Fi">
 <!ENTITY pref_developer_remotedebugging_wifi_disabled_summary "Wi-Fi debugging requires your device to have a QR code reader app installed.">
 <!ENTITY pref_remember_signons2 "Remember logins">
 <!ENTITY pref_manage_logins "Manage logins">
 
 <!ENTITY pref_category_home "Home">
 <!ENTITY pref_category_home_summary "Customize your homepage">
 <!ENTITY pref_category_home_panels "Panels">
-<!ENTITY home_add_panel_title "Add new panel">
-<!ENTITY home_add_panel_empty "Sorry, we couldn\'t find any panels for you to add.">
-<!-- Localization note (home_add_panel_installed):
-     The &formatS; will be replaced with the name of the new panel the user just
-     selected to be added to the home page. -->
-<!ENTITY home_add_panel_installed "\'&formatS;\' added to homepage">
 <!ENTITY pref_category_home_content_settings "Content settings">
-<!ENTITY pref_home_updates "Automatic updates">
+<!ENTITY pref_category_home_add_ons "Add-ons">
+<!ENTITY pref_home_updates2 "Content updates">
 <!ENTITY pref_home_updates_enabled "Enabled">
 <!ENTITY pref_home_updates_wifi "Only over Wi-Fi">
 <!ENTITY pref_home_suggested_sites "Show site suggestions">
 <!ENTITY pref_home_suggested_sites_summary "Display shortcuts to sites on your homepage that we think you might find interesting">
 <!ENTITY pref_category_home_homepage "Homepage">
 <!ENTITY home_homepage_title "Set a Homepage">
 <!-- Localization note (home_homepage_radio_user_address): The user will see a series of radio
      buttons to choose the homepage they'd like to start on. When they click the radio
@@ -353,20 +335,22 @@ size. -->
 
 <!ENTITY pref_panels_show "Show">
 <!ENTITY pref_panels_hide "Hide">
 <!ENTITY pref_panels_reorder "Change order">
 <!ENTITY pref_panels_move_up "Move up">
 <!ENTITY pref_panels_move_down "Move down">
 
 <!ENTITY datareporting_notification_title "&brandShortName; stats &amp; data">
+<!ENTITY datareporting_notification_action "Choose what to share">
+<!-- Used in datareporting_notification_ticket_text, but unused in strings.xml. -->
 <!ENTITY datareporting_notification_action_long "Choose what information to share">
-<!ENTITY datareporting_notification_action "Choose what to share">
 <!ENTITY datareporting_notification_summary "To improve your experience, &brandShortName; automatically sends some information to &vendorShortName;.">
-<!ENTITY datareporting_notification_summary_short "To improve your experience, &brandShortName;…">
+<!-- When this item is removed, also remove datareporting_notification_action_long:
+     it is unused in strings.xml. -->
 <!ENTITY datareporting_notification_ticker_text "&datareporting_notification_title;: &datareporting_notification_action_long;">
 
 <!-- Localization note (datareporting_fhr_title, datareporting_fhr_summary2,
      reporting_telemetry_title, datareporting_telemetry_summary,
      datareporting_crashreporter_summary) : These match the strings in
      en-US/chrome/browser/preferences/advanced.dtd (healthReportSection.label,
      healthReportDesc.label, telemetrySection.label, telemetryDesc.label,
      crashReporterDesc.label). -->
@@ -409,17 +393,16 @@ size. -->
 <!ENTITY page "Page">
 <!ENTITY tools "Tools">
 <!ENTITY new_tab "New Tab">
 <!ENTITY new_private_tab "New Private Tab">
 <!ENTITY close_all_tabs "Close All Tabs">
 <!ENTITY close_private_tabs "Close Private Tabs">
 <!ENTITY tabs_normal "Tabs">
 <!ENTITY tabs_private "Private">
-<!ENTITY tabs_synced "Synced">
 <!ENTITY set_image_fail "Unable to set image">
 <!ENTITY set_image_path_fail "Unable to save image">
 <!ENTITY set_image_chooser_title "Set Image As">
 
 <!-- Localization note (find_text, find_prev, find_next, find_close) : These strings are used
      as alternate text for accessibility. They are not visible in the UI. -->
 <!ENTITY find_text "Find in Page">
 <!ENTITY find_prev "Previous">
@@ -499,22 +482,16 @@ size. -->
      as possible. -->
 <!ENTITY reading_list_added3 "Added to Reading List">
 <!ENTITY reading_list_removed "Page removed from your Reading List">
 <!-- Localization note (reading_list_remove) : Used to remove the currently open page from
      the user's reading list. The opposite of overlay_share_reading_list_btn_label. -->
 <!ENTITY reading_list_remove "Remove from Reading List">
 <!ENTITY reading_list_duplicate "Page already in your Reading List">
 
-<!-- Localization note (reading_list_time_minutes2) : This string is used in the "Reading List"
-     panel on the home page to give the user an estimate of how many minutes it will take to
-     read an article. The word "minute" should be abbreviated if possible. -->
-<!ENTITY reading_list_time_minutes2 "&formatD; min">
-<!ENTITY reading_list_time_over_an_hour "Over an hour">
-
 <!-- Localization note : These strings are used as alternate text for accessibility.
      They are not visible in the UI. -->
 <!ENTITY page_action_dropmarker_description "Additional Actions">
 
 <!ENTITY masterpassword_create_title "Create Master Password">
 <!ENTITY masterpassword_remove_title "Remove Master Password">
 <!ENTITY masterpassword_password "Password">
 <!ENTITY masterpassword_confirm "Confirm password">
@@ -622,17 +599,16 @@ just addresses the organization to follo
 <!ENTITY doorhanger_tracking_state_disabled "Disabled">
 <!ENTITY doorhanger_tracking_message_enabled1 "Attempts to track your online behavior have been blocked.">
 <!ENTITY doorhanger_tracking_message_disabled2 "This page includes elements that may track your browsing.">
 
 <!-- Common mixed and tracking content strings in site identity popup -->
 <!ENTITY learn_more "Learn More">
 <!ENTITY enable_protection "Enable protection">
 <!ENTITY disable_protection "Disable protection">
-<!ENTITY keep_blocking "Keep blocking">
 
 <!ENTITY private_data_success "Private data cleared">
 <!ENTITY private_data_fail "Some private data could not be cleared">
 
 <!ENTITY bookmarkhistory_button_import "Import">
 <!ENTITY bookmarkhistory_import_both "Importing bookmarks and history
                                       from Android">
 <!ENTITY bookmarkhistory_import_bookmarks "Importing bookmarks
@@ -718,17 +694,16 @@ just addresses the organization to follo
      time the tabs were last synced relative to the current time; examples
      include "42 minutes ago", "4 days ago", "last week", etc. The subject of
      "Last synced" is one of the user's other Sync clients, typically Firefox on
      their desktop or laptop.-->
 <!ENTITY remote_tabs_last_synced "Last synced: &formatS;">
 <!-- Localization note: Used when the sync has not happend yet, showed in place of a date -->
 <!ENTITY remote_tabs_never_synced "Last synced: never">
 
-<!ENTITY intent_uri_cannot_open "Cannot open link">
 <!-- LOCALIZATION NOTE (intent_uri_private_browsing_prompt): This string will
      appear in an alert when a user, who is currently in private browsing,
      clicks a link that will open an external Android application. "&formatS;"
      will be replaced with the name of the application that will be opened. -->
 <!ENTITY intent_uri_private_browsing_prompt "This link will open in &formatS;. Are you sure you want to exit Private Browsing?">
 <!-- LOCALIZATION NOTE (intent_uri_private_browsing_multiple_match_title): This
      string will appear as the title of an alert when a user, who is currently
      in private browsing, clicks a link that will open an external Android
--- a/mobile/android/base/locales/en-US/search_strings.dtd
+++ b/mobile/android/base/locales/en-US/search_strings.dtd
@@ -17,14 +17,12 @@
 
 <!ENTITY search_pref_title 'Settings'>
 <!ENTITY search_pref_button_content_description 'Settings'>
 
 <!ENTITY pref_clearHistory_confirmation 'History cleared'>
 <!ENTITY pref_clearHistory_dialogMessage 'Delete all search history from this device?'>
 <!ENTITY pref_clearHistory_title 'Clear search history'>
 
-<!ENTITY pref_searchProvider_title 'Search engine'>
-
 <!ENTITY search_widget_button_label 'Search'>
 
 <!ENTITY network_error_title 'No internet connection'>
 <!ENTITY network_error_message 'Tap here to check your network settings'>
--- a/mobile/android/base/locales/en-US/sync_strings.dtd
+++ b/mobile/android/base/locales/en-US/sync_strings.dtd
@@ -31,110 +31,39 @@
 <!-- Bookmark folder strings -->
 <!ENTITY bookmarks.folder.menu.label 'Bookmarks Menu'>
 <!ENTITY bookmarks.folder.places.label ''>
 <!ENTITY bookmarks.folder.tags.label 'Tags'>
 <!ENTITY bookmarks.folder.toolbar.label 'Bookmarks Toolbar'>
 <!ENTITY bookmarks.folder.unfiled.label 'Unsorted Bookmarks'>
 <!ENTITY bookmarks.folder.desktop.label 'Desktop Bookmarks'>
 <!ENTITY bookmarks.folder.mobile.label 'Mobile Bookmarks'>
-<!ENTITY bookmarks.folder.readinglist.label 'Reading List'>
 <!-- Pinned sites on about:home. This folder should never be shown to the user, but we have to give it a string name -->
 <!ENTITY bookmarks.folder.pinned.label 'Pinned'>
 
-<!-- Send tab to device. -->
-<!ENTITY sync.title.redirect.to.set.up.sync.label 'Set up &syncBrand.shortName.label; to send tabs'>
-<!ENTITY sync.text.redirect.to.set.up.sync.label 'Set up &syncBrand.fullName.label; on your device to send tabs to other devices.'>
-<!ENTITY sync.text.tab.sent.label 'Your tab was sent!'>
-<!ENTITY sync.text.tab.not.sent.label 'There was a problem sending your tab.'>
-
 <!-- Firefox Account strings. -->
 
-<!ENTITY fxaccount_full_label 'Firefox Accounts'>
-
-<!ENTITY fxaccount_custom_server_account_title 'Using account on server'>
-<!ENTITY fxaccount_custom_server_sync_title 'Storing Sync data on server'>
-
-<!-- Localization note: these are shown in all screens that query the
-     user for an email address and password. Hide and show are button
-     labels. -->
-<!ENTITY fxaccount_email_hint 'Email'>
-<!ENTITY fxaccount_password_hint 'Password'>
-<!ENTITY fxaccount_password_hide 'Hide'>
-<!ENTITY fxaccount_password_show 'Show'>
-
 <!-- Localization note: these are shown in screens after the user has
      created or signed in to an account, and take the user back to
      Firefox. -->
 <!ENTITY fxaccount_back_to_browsing 'Back to browsing'>
 
-<!-- Localization note: the following two strings are interpolated
-     into fxaccount_create_account_policy_text2; see note for that
-     string as well.  Compare fxaccount_status_{linktos,linkprivacy}:
-     these strings are separated to accommodate languages that decline
-     the two uses differently. -->
-<!ENTITY fxaccount_policy_linktos 'Terms of Service'>
-<!ENTITY fxaccount_policy_linkprivacy 'Privacy Notice'>
-
 <!ENTITY fxaccount_getting_started_welcome_to_sync 'Welcome to &syncBrand.shortName.label;'>
 <!ENTITY fxaccount_getting_started_description2 'Sign in to sync your tabs, bookmarks, logins &amp; more.'>
 <!ENTITY fxaccount_getting_started_get_started 'Get started'>
 <!ENTITY fxaccount_getting_started_old_firefox 'Using an older version of &syncBrand.shortName.label;?'>
 
-<!-- Localization note: the Firefox below should not change with the
-     particular version of Firefox installed (Release, Beta, Aurora,
-     etc).  The account remains a "Firefox Account". -->
-<!ENTITY fxaccount_create_account_header2 'Create a Firefox Account'>
-<!ENTITY fxaccount_create_account_password_length_restriction 'Must be at least 8 characters'>
-<!ENTITY fxaccount_create_account_day_of_birth 'Day'>
-<!ENTITY fxaccount_create_account_month_of_birth 'Month'>
-<!ENTITY fxaccount_create_account_year_of_birth 'Year of birth'>
-<!-- Localization note: &formatS1; is fxaccount_policy_linktos, &formatS2; is fxaccount_policy_linkprivacy, both hyperlinked. -->
-<!ENTITY fxaccount_create_account_policy_text2 'By proceeding, I agree to the &formatS1; and &formatS2; of Firefox cloud services.'>
-<!ENTITY fxaccount_create_account_button 'Next'>
-<!ENTITY fxaccount_create_account_choose_what_to_sync 'Choose what to sync'>
-<!ENTITY fxaccount_create_account_sign_in_instead 'Already have an account? Sign in'>
-<!ENTITY fxaccount_create_account_1990_or_earlier '1990 or earlier'>
-<!ENTITY fxaccount_create_account_unknown_error 'Could not create account'>
-
-<!ENTITY fxaccount_account_create_not_allowed 'Cannot create account'>
-<!ENTITY fxaccount_account_create_not_allowed_you_must_meet_certain_age_requirements 'You must meet certain age requirements to create an account.'>
-<!ENTITY fxaccount_account_create_not_allowed_learn_more 'Learn more'>
+<!ENTITY fxaccount_confirm_account_header 'Confirm your account'>
+<!ENTITY fxaccount_confirm_account_resend_email 'Resend email'>
 
-<!ENTITY fxaccount_confirm_account_header 'Confirm your account'>
-<!-- Localization note: &formatS; is the Firefox Account's email address. -->
-<!ENTITY fxaccount_confirm_account_verification_link 'A verification link has been sent to &formatS;'>
-<!ENTITY fxaccount_confirm_account_resend_email 'Resend email'>
-<!ENTITY fxaccount_confirm_account_change_email 'Forget this email address?'>
-<!ENTITY fxaccount_confirm_account_verification_link_sent2 'Verification email sent'>
-<!ENTITY fxaccount_confirm_account_verification_link_not_sent2 'Couldn\&apos;t send verification email'>
-<!ENTITY fxaccount_resend_unlock_code_button_label 'Resend unlock email'>
-<!ENTITY fxaccount_unlock_code_sent 'Account unlock email sent'>
-<!ENTITY fxaccount_unlock_code_not_sent 'Couldn\&apos;t send account unlock email'>
-
-<!ENTITY fxaccount_sign_in_sub_header 'Sign in'>
 <!ENTITY fxaccount_sign_in_button_label 'Sign in'>
-<!ENTITY fxaccount_sign_in_forgot_password 'Forgot password?'>
-<!ENTITY fxaccount_sign_in_create_account_instead 'Create an account'>
-<!ENTITY fxaccount_sign_in_unknown_error 'Could not sign in'>
-
-<!ENTITY fxaccount_account_verified_sub_header 'Account verified'>
-<!ENTITY fxaccount_account_verified_description2 'Your data will begin syncing momentarily.'>
-
-<!ENTITY fxaccount_migration_finished_header 'Upgrade finished'>
-
-<!ENTITY fxaccount_update_credentials_header 'Sign in'>
-<!ENTITY fxaccount_update_credentials_button_label 'Sign in'>
-<!ENTITY fxaccount_update_credentials_unknown_error 'Could not sign in'>
 
 <!ENTITY fxaccount_finish_migrating_header 'Sign in to finish upgrading'>
 <!ENTITY fxaccount_finish_migrating_button_label 'Finish upgrading'>
-<!ENTITY fxaccount_finish_migrating_description 'Upgrading can transfer a lot of data. It\&apos;s best to be on a Wi-Fi network.'>
 
-<!ENTITY fxaccount_status_header2 'Firefox Account'>
 <!ENTITY fxaccount_status_signed_in_as 'Signed in as'>
 <!ENTITY fxaccount_status_manage_account 'Manage account'>
 <!ENTITY fxaccount_status_auth_server 'Account server'>
 <!ENTITY fxaccount_status_sync_now 'Sync now'>
 <!ENTITY fxaccount_status_syncing2 'Syncing…'>
 <!ENTITY fxaccount_status_device_name 'Device name'>
 <!ENTITY fxaccount_status_sync_server 'Sync server'>
 <!ENTITY fxaccount_status_sync '&syncBrand.shortName.label;'>
@@ -187,25 +116,19 @@
 <!-- Localization note: the format string will be fxaccount_sign_in_button_label, linkified. -->
 <!ENTITY fxaccount_remote_error_ATTEMPT_TO_CREATE_AN_ACCOUNT_THAT_ALREADY_EXISTS_2 'Account already exists. &formatS1;'>
 <!ENTITY fxaccount_remote_error_ATTEMPT_TO_ACCESS_AN_ACCOUNT_THAT_DOES_NOT_EXIST 'Invalid email or password'>
 <!ENTITY fxaccount_remote_error_INCORRECT_PASSWORD 'Invalid email or password'>
 <!ENTITY fxaccount_remote_error_ATTEMPT_TO_OPERATE_ON_AN_UNVERIFIED_ACCOUNT 'Account is not verified'>
 <!ENTITY fxaccount_remote_error_CLIENT_HAS_SENT_TOO_MANY_REQUESTS 'Server busy, try again soon'>
 <!ENTITY fxaccount_remote_error_SERVICE_TEMPORARILY_UNAVAILABLE_TO_DUE_HIGH_LOAD 'Server busy, try again soon'>
 <!ENTITY fxaccount_remote_error_UNKNOWN_ERROR 'There was a problem'>
-<!ENTITY fxaccount_remote_error_COULD_NOT_CONNECT 'Cannot connect to network'>
 <!ENTITY fxaccount_remote_error_ACCOUNT_LOCKED 'Account is locked. &formatS1;'>
 
 <!ENTITY fxaccount_sync_sign_in_error_notification_title2 '&syncBrand.shortName.label; is not connected'>
 <!-- Localization note: the format string below will be replaced
      with the Firefox Account's email address. -->
 <!ENTITY fxaccount_sync_sign_in_error_notification_text2 'Tap to sign in as &formatS;'>
 
 <!ENTITY fxaccount_sync_finish_migrating_notification_title 'Finish upgrading &syncBrand.shortName.label;?'>
 <!-- Localization note: the format string below will be replaced
      with the Firefox Account's email address. -->
 <!ENTITY fxaccount_sync_finish_migrating_notification_text 'Tap to sign in as &formatS;'>
-
-<!-- Localization note: the following strings are used in a
-     notification and should be kept as short as possible. -->
-<!ENTITY old_sync_deprecated_notification_title 'Sign in to continue syncing'>
-<!ENTITY old_sync_deprecated_notification_content 'Your account is no longer supported'>
deleted file mode 100644
--- a/mobile/android/base/resources/anim/grow_fade_in_center.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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/. -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-     android:duration="250">
-
-    <scale android:fromXScale="0.5"
-           android:toXScale="1.0"
-           android:fromYScale="0.5"
-           android:toYScale="1.0"
-           android:pivotX="50%"
-           android:pivotY="50%"/>
-
-    <alpha android:fromAlpha="0.0"
-           android:toAlpha="1.0"/>
-
-</set>
deleted file mode 100644
--- a/mobile/android/base/resources/color/floating_hint_text.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <!-- Holo blue for focus -->
-    <item android:state_focused="true" android:color="@color/text_color_hint_floating_focused" />
-
-    <!-- Default gray for all other states -->
-    <item android:color="@color/text_color_hint"/>
-</selector>
deleted file mode 100644
index 6affe57a90f7ce373060d9030695a3ca8bb4064d..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 57840c7bf894dd3acc3cd895e46483bf8e8c7b49..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index ce74be7d0a6eb3205fbbfc5e229ba36303ce4130..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/mobile/android/base/resources/drawable/divider_horizontal.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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/. -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="rectangle">
-
-    <solid android:color="@color/toolbar_divider_grey"/>
-    <size android:height="1dp" />
-
-</shape>
deleted file mode 100644
--- a/mobile/android/base/resources/drawable/ic_menu_quit.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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/. -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="rectangle">
-
-     <solid android:color="@android:color/transparent"/>
-
-</shape>
--- a/mobile/android/base/resources/layout/home_suggestion_prompt.xml
+++ b/mobile/android/base/resources/layout/home_suggestion_prompt.xml
@@ -39,17 +39,17 @@
                   android:focusable="true"
                   android:text="@string/button_no"/>
 
         <TextView android:id="@+id/suggestions_prompt_yes"
                   android:layout_height="32dp"
                   android:minWidth="72dp"
                   android:layout_width="wrap_content"
                   android:gravity="center"
-                  android:textColor="@color/swipe_refresh_white"
+                  android:textColor="@android:color/white"
                   android:background="@drawable/search_suggestion_prompt_yes"
                   android:focusable="true"
                   android:text="@string/button_yes"/>
 
 
 
     </LinearLayout>
 
deleted file mode 100644
--- a/mobile/android/base/resources/layout/notification_icon_text.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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/. -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent"
-              android:orientation="vertical"
-              android:paddingLeft="5dp">
-
-    <LinearLayout android:layout_width="match_parent"
-                  android:layout_height="wrap_content"
-                  android:orientation="horizontal">
-
-        <ImageView android:id="@+id/notification_image"
-                   android:layout_width="25dp"
-                   android:layout_height="25dp"
-                   android:scaleType="fitCenter"/>
-
-        <TextView android:id="@+id/notification_title"
-                  android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title"
-                  android:layout_width="0dp"
-                  android:layout_height="wrap_content"
-                  android:layout_weight="1"
-                  android:singleLine="true"
-                  android:ellipsize="marquee"
-                  android:fadingEdge="horizontal"
-                  android:paddingLeft="4dp"/>
-
-    </LinearLayout>
-
-    <TextView android:id="@+id/notification_text"
-              android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:paddingLeft="4dp"/>
-
-</LinearLayout>
deleted file mode 100644
--- a/mobile/android/base/resources/layout/notification_progress.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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/. -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent"
-              android:orientation="vertical"
-              android:paddingTop="7dp"
-              android:paddingLeft="5dp">
-
-    <LinearLayout android:layout_width="match_parent"
-                  android:layout_height="wrap_content"
-                  android:orientation="horizontal">
-
-        <ImageView android:id="@+id/notification_image"
-                   android:layout_width="25dp"
-                   android:layout_height="25dp"
-                   android:scaleType="fitCenter"/>
-
-        <TextView android:id="@+id/notification_title"
-                  android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title"
-                  android:layout_width="0dp"
-                  android:layout_height="wrap_content"
-                  android:layout_weight="1"
-                  android:singleLine="true"
-                  android:ellipsize="marquee"
-                  android:fadingEdge="horizontal"
-                  android:paddingLeft="10dp"/>
-
-    </LinearLayout>
-
-    <LinearLayout android:layout_width="match_parent"
-                  android:layout_height="wrap_content"
-                  android:orientation="horizontal">
-        <TextView android:id="@+id/notification_text"
-                  android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent"
-                  android:layout_width="wrap_content"
-                  android:layout_height="wrap_content"
-                  android:paddingLeft="3dp"/>
-        <ProgressBar android:id="@+id/notification_progressbar"
-                     style="?android:attr/progressBarStyleHorizontal"
-                     android:layout_width="match_parent"
-                     android:layout_height="wrap_content"
-                     android:layout_marginTop="1dip"
-                     android:layout_marginBottom="1dip"
-                     android:layout_marginLeft="4dip"
-                     android:layout_marginRight="10dip" />
-    </LinearLayout>
-
-</LinearLayout>
deleted file mode 100644
--- a/mobile/android/base/resources/layout/notification_progress_text.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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/. -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent"
-              android:orientation="vertical"
-              android:paddingLeft="5dp">
-
-    <LinearLayout android:layout_width="match_parent"
-                  android:layout_height="wrap_content"
-                  android:orientation="horizontal">
-
-        <ImageView android:id="@+id/notification_image"
-                   android:layout_width="25dp"
-                   android:layout_height="25dp"
-                   android:scaleType="fitCenter"/>
-
-        <TextView android:id="@+id/notification_title"
-                  android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title"
-                  android:layout_width="0dp"
-                  android:layout_height="wrap_content"
-                  android:layout_weight="1"
-                  android:singleLine="true"
-                  android:ellipsize="marquee"
-                  android:fadingEdge="horizontal"
-                  android:paddingLeft="4dp"/>
-
-    </LinearLayout>
-
-    <ProgressBar android:id="@+id/notification_progressbar"
-                 style="?android:attr/progressBarStyleHorizontal"
-                 android:layout_width="match_parent"
-                 android:layout_height="16dip"
-                 android:layout_marginTop="1dip"
-                 android:layout_marginBottom="1dip"
-                 android:layout_marginLeft="10dip"
-                 android:layout_marginRight="10dip" />
-
-    <TextView android:id="@+id/notification_text"
-              android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:paddingLeft="4dp"/>
-
-</LinearLayout>
deleted file mode 100644
--- a/mobile/android/base/resources/layout/overlay_share_toast.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!-- 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/. -->
-
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:background="@drawable/dropshadow"
-    android:padding="3dp">
-
-    <TextView
-        android:id="@+id/overlay_toast_message"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center"
-        android:padding="20dp"
-        android:background="@drawable/toolbar_grey_round"
-        android:textColor="@color/text_and_tabs_tray_grey"
-        android:textSize="14sp"
-        android:drawablePadding="15dp"
-        android:drawableLeft="@drawable/overlay_check"/>
-
-</FrameLayout>
deleted file mode 100644
--- a/mobile/android/base/resources/layout/simple_dropdown_item_1line.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-  
-          http://www.apache.org/licenses/LICENSE-2.0
-  
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@android:id/text1"
-    style="?android:attr/dropDownItemStyle"
-    android:textAppearance="@style/TextAppearance.Widget.TextView"
-    android:singleLine="true"
-    android:layout_width="match_parent"
-    android:layout_height="?android:attr/listPreferredItemHeightSmall"
-    android:ellipsize="marquee"/>
--- a/mobile/android/base/resources/values/arrays.xml
+++ b/mobile/android/base/resources/values/arrays.xml
@@ -72,20 +72,16 @@
     <string-array name="pref_import_android_defaults">
         <item>true</item>
         <item>true</item>
     </string-array>
     <string-array name="pref_import_android_values">
         <item>android_import.data.bookmarks</item>
         <item>android_import.data.history</item>
     </string-array>
-    <string-array name="pref_import_android_keys">
-        <item>android_import.data.bookmarks</item>
-        <item>android_import.data.history</item>
-    </string-array>
     <string-array name="pref_private_data_entries">
         <item>@string/pref_private_data_history2</item>
         <item>@string/pref_private_data_searchHistory</item>
         <item>@string/pref_private_data_downloadFiles2</item>
         <item>@string/pref_private_data_formdata2</item>
         <item>@string/pref_private_data_cookies2</item>
         <item>@string/pref_private_data_cache</item>
         <item>@string/pref_private_data_offlineApps</item>
--- a/mobile/android/base/resources/values/colors.xml
+++ b/mobile/android/base/resources/values/colors.xml
@@ -35,17 +35,16 @@
   <color name="restricted_profile_background_gold">#ffffcb51</color>
   <color name="restricted_profile_background_green">#1aaa86</color>
 
   <!-- Non-palette colors -->
 
   <!-- Synced w/ toolbar_grey -->
   <color name="background_normal_lwt">#DDEBEBF0</color>
 
-  <color name="background_tabs">#FF363B40</color>
   <color name="highlight">#33000000</color>
   <color name="highlight_focused">#1A000000</color>
   <color name="highlight_dark">#33FFFFFF</color>
   <color name="highlight_dark_focused">#1AFFFFFF</color>
 
   <!-- Synced w/ toolbar_grey_pressed -->
   <color name="tablet_highlight_lwt">#AAD7D7DC</color>
 
@@ -83,17 +82,16 @@
   <color name="text_color_secondary_inverse">#DDDDDD</color>
 
   <!-- Disabled colors -->
   <color name="text_color_primary_disable_only">#999999</color>
 
   <!-- Hint colors -->
   <color name="text_color_hint">#666666</color>
   <color name="text_color_hint_inverse">#7F828A</color>
-  <color name="text_color_hint_floating_focused">#33b5e5</color>
 
   <!-- Highlight colors -->
   <color name="text_color_highlight_inverse">#D06BFF</color>
 
   <!-- Link colors -->
   <color name="text_color_link">#22629E</color>
 
   <!-- Divider colors -->
@@ -111,26 +109,20 @@
   <color name="textbox_stroke_disabled">#666</color>
 
   <color name="url_bar_urltext">#A6A6A6</color>
   <color name="url_bar_domaintext">#000</color>
   <color name="url_bar_domaintext_private">#FFF</color>
   <color name="url_bar_blockedtext">#b14646</color>
   <color name="url_bar_shadow">#12000000</color>
 
-  <color name="home_button_bar_bg">#FFF5F7F9</color>
-
   <color name="panel_image_item_background">#D1D9E1</color>
   <color name="panel_icon_item_title_background">#32000000</color>
   <color name="panel_tab_text_normal">#FFBFBFBF</color>
 
-  <!-- Swipe to refresh colors for dynamic panel -->
-  <color name="swipe_refresh_orange">#FFFFC26C</color>
-  <color name="swipe_refresh_white">#FFFFFFFF</color>
-
   <!-- Remote tabs setup -->
   <color name="remote_tabs_setup_button_background_hit">#D95300</color>
 
   <!-- Button toast colors. -->
   <color name="toast_background">#DD222222</color>
   <color name="toast_button_background">#00000000</color>
   <color name="toast_button_pressed">#DD2C3136</color>
   <color name="toast_button_text">#FFFFFFFF</color>
--- a/mobile/android/base/resources/values/dimens.xml
+++ b/mobile/android/base/resources/values/dimens.xml
@@ -148,20 +148,16 @@
     <dimen name="tab_thumbnail_width">121dp</dimen>
     <dimen name="tab_thumbnail_height">90dp</dimen>
     <dimen name="tab_panel_column_width">129dp</dimen>
     <dimen name="tab_panel_grid_padding">20dp</dimen>
     <dimen name="tab_panel_grid_vspacing">20dp</dimen>
     <dimen name="tab_panel_grid_padding_top">19dp</dimen>
 
     <dimen name="tab_highlight_stroke_width">4dp</dimen>
-    <dimen name="tab_title_height">22dp</dimen>
-    <dimen name="tab_vertical_padding">6dp</dimen>
-    <dimen name="tab_thumbnail_padding">4dp</dimen>
-    <dimen name="tab_thumbnail_margin">6dp</dimen>
 
     <!-- PageActionButtons dimensions -->
     <dimen name="page_action_button_width">32dp</dimen>
 
     <!-- Banner -->
     <dimen name="home_banner_height">72dp</dimen>
     <dimen name="home_banner_close_width">42dp</dimen>
     <dimen name="home_banner_icon_height">48dip</dimen>
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/values/xml.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<resources>
+    <!-- These items are v11+ resources but are referenced in code shipped with
+         API 9 builds. Since v11+ resources don't ship on API 9 builds, in order
+         for the resource ID to be found (and thus compilation to succeed), we
+         provide dummy values below. -->
+    <item type="xml" name="preferences_privacy_clear_tablet">@null</item>
+</resources>
--- a/mobile/android/base/resources/xml/preferences_home.xml
+++ b/mobile/android/base/resources/xml/preferences_home.xml
@@ -23,16 +23,20 @@
 
     <PreferenceCategory android:title="@string/pref_category_home_content_settings">
 
         <CheckBoxPreference android:key="android.not_a_preference.home_suggested_sites"
                             android:title="@string/pref_home_suggested_sites"
                             android:summary="@string/pref_home_suggested_sites_summary"
                             android:defaultValue="true" />
 
+    </PreferenceCategory>
+
+    <PreferenceCategory android:title="@string/pref_category_home_add_ons">
+
         <ListPreference android:key="home.sync.updateMode"
                         android:title="@string/pref_home_updates"
                         android:entries="@array/pref_home_updates_entries"
                         android:entryValues="@array/pref_home_updates_values"
                         android:persistent="false" />
 
     </PreferenceCategory>
 
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -18,28 +18,22 @@
 <!ENTITY formatS3 "&#037;3&#036;s">
 <!ENTITY formatD "&#037;d">
 ]>
 
 <resources>
   <string name="moz_app_displayname">@MOZ_APP_DISPLAYNAME@</string>
   <string name="android_package_name">@ANDROID_PACKAGE_NAME@</string>
   <string name="content_authority_db_browser">@ANDROID_PACKAGE_NAME@.db.browser</string>
-  <string name="content_authority_db_formhistory">@ANDROID_PACKAGE_NAME@.db.formhistory</string>
-  <string name="content_authority_db_passwords">@ANDROID_PACKAGE_NAME@.db.passwords</string>
-  <string name="content_authority_db_tabs">@ANDROID_PACKAGE_NAME@.db.tabs</string>
-  <string name="content_authority_db_readinglist">@ANDROID_PACKAGE_NAME@.db.readinglist</string>
   <string name="moz_android_shared_fxaccount_type">@ANDROID_PACKAGE_NAME@_fxaccount</string>
   <string name="android_package_name_for_ui">@ANDROID_PACKAGE_NAME@</string>
 
 #include ../search/strings/search_strings.xml.in
 
 #include ../services/strings.xml.in
-  <string name="no_space_to_start_error">&no_space_to_start_error;</string>
-  <string name="error_loading_file">&error_loading_file;</string>
 
   <string name="firstrun_panel_title_welcome">&firstrun_panel_title_welcome;</string>
   <string name="firstrun_welcome_message">&onboard_start_message3;</string>
   <string name="firstrun_welcome_subtext">&onboard_start_subtext3;</string>
 
   <string name="firstrun_urlbar_message">&firstrun_urlbar_message;</string>
   <string name="firstrun_urlbar_subtext">&firstrun_urlbar_subtext;</string>
   <string name="firstrun_bookmarks_title">&firstrun_bookmarks_title;</string>
@@ -50,17 +44,16 @@
   <string name="firstrun_data_subtext">&firstrun_data_subtext2;</string>
   <string name="firstrun_sync_title">&firstrun_sync_title;</string>
   <string name="firstrun_sync_message">&firstrun_sync_message;</string>
   <string name="firstrun_sync_subtext">&firstrun_sync_subtext;</string>
   <string name="firstrun_signin_message">&firstrun_signin_message;</string>
   <string name="firstrun_signin_button">&firstrun_signin_button;</string>
   <string name="firstrun_welcome_button_browser">&onboard_start_button_browser;</string>
   <string name="firstrun_button_next">&firstrun_button_next;</string>
-  <string name="firstrun_empty_contentDescription"></string>
 
   <string name="firstrun_welcome_restricted">&onboard_start_restricted1;</string>
 
   <string name="bookmarks_title">&bookmarks_title;</string>
   <string name="history_title">&history_title;</string>
   <string name="reading_list_title">&reading_list_title;</string>
   <string name="recent_tabs_title">&recent_tabs_title;</string>
 
@@ -73,22 +66,16 @@
   <string name="crash_sorry">&crash_sorry;</string>
   <string name="crash_comment">&crash_comment;</string>
   <string name="crash_allow_contact2">&crash_allow_contact2;</string>
   <string name="crash_email">&crash_email;</string>
   <string name="crash_closing_alert">&crash_closing_alert;</string>
   <string name="sending_crash_report">&sending_crash_report;</string>
   <string name="crash_close_label">&crash_close_label;</string>
   <string name="crash_restart_label">&crash_restart_label;</string>
-  <string name="exit_label">&exit_label;</string>
-
-  <string name="launcher_shortcuts_title">&launcher_shortcuts_title;</string>
-  <string name="launcher_shortcuts_empty">&launcher_shortcuts_empty;</string>
-
-  <string name="choose_file">&choose_file;</string>
 
   <string name="url_bar_default_text">&url_bar_default_text2;</string>
 
   <!-- https://input.mozilla.org/feedback/android/%VERSION%/%CHANNEL%/?utm_source=feedback-settings
        This should be kept in sync with the "app.feedbackURL" pref defined in mobile.js -->
   <string name="feedback_link">https://input.mozilla.org/feedback/android/&formatS1;/&formatS2;/?utm_source=feedback-settings</string>
 
   <!-- https://support.mozilla.org/1/mobile/%VERSION%/%OS%/%LOCALE%/faq -->
@@ -107,17 +94,16 @@
   <string name="bookmark_updated">&bookmark_updated;</string>
   <string name="bookmark_options">&bookmark_options;</string>
   <string name="screenshot_added_to_bookmarks">&screenshot_added_to_bookmarks;</string>
   <string name="screenshot_folder_label_in_bookmarks">&screenshot_folder_label_in_bookmarks;</string>
 
   <string name="history_today_section">&history_today_section;</string>
   <string name="history_yesterday_section">&history_yesterday_section;</string>
   <string name="history_week_section">&history_week_section3;</string>
-  <string name="history_this_month_section">&history_this_month_section;</string>
   <string name="history_older_section">&history_older_section3;</string>
 
   <string name="share">&share;</string>
   <string name="share_title">&share_title;</string>
   <string name="share_image_failed">&share_image_failed;</string>
   <string name="save_as_pdf">&save_as_pdf;</string>
   <string name="print">&print;</string>
   <string name="find_in_page">&find_in_page;</string>
@@ -169,33 +155,30 @@
 
   <string name="pref_category_language">&pref_category_language;</string>
   <string name="pref_category_language_summary">&pref_category_language_summary;</string>
   <string name="pref_browser_locale">&pref_browser_locale;</string>
   <string name="locale_system_default">&locale_system_default;</string>
 
   <string name="pref_category_advanced">&pref_category_advanced;</string>
   <string name="pref_category_advanced_summary">&pref_category_advanced_summary2;</string>
-  <string name="pref_developer_remotedebugging">&pref_developer_remotedebugging;</string>
   <string name="pref_developer_remotedebugging_usb">&pref_developer_remotedebugging_usb;</string>
   <string name="pref_developer_remotedebugging_wifi">&pref_developer_remotedebugging_wifi;</string>
   <string name="pref_developer_remotedebugging_wifi_disabled_summary">&pref_developer_remotedebugging_wifi_disabled_summary;</string>
 
   <string name="pref_category_home">&pref_category_home;</string>
   <string name="pref_category_home_summary">&pref_category_home_summary;</string>
   <string name="pref_category_home_panels">&pref_category_home_panels;</string>
-  <string name="home_add_panel_title">&home_add_panel_title;</string>
-  <string name="home_add_panel_empty">&home_add_panel_empty;</string>
-  <string name="home_add_panel_installed">&home_add_panel_installed;</string>
   <string name="pref_category_home_content_settings">&pref_category_home_content_settings;</string>
-  <string name="pref_home_updates">&pref_home_updates;</string>
-  <string name="pref_home_updates_enabled">&pref_home_updates_enabled;</string>
   <string name="pref_home_updates_wifi">&pref_home_updates_wifi;</string>
   <string name="pref_home_suggested_sites">&pref_home_suggested_sites;</string>
   <string name="pref_home_suggested_sites_summary">&pref_home_suggested_sites_summary;</string>
+  <string name="pref_category_home_add_ons">&pref_category_home_add_ons;</string>
+  <string name="pref_home_updates">&pref_home_updates2;</string>
+  <string name="pref_home_updates_enabled">&pref_home_updates_enabled;</string>
   <string name="pref_category_home_homepage">&pref_category_home_homepage;</string>
   <string name="home_homepage_title">&home_homepage_title;</string>
   <string name="home_homepage_radio_default">&home_homepage_radio_default;</string>
   <string name="home_homepage_radio_user_address">&home_homepage_radio_user_address;</string>
   <string name="home_homepage_hint_user_address">&home_homepage_hint_user_address;</string>
 
   <string name="pref_header_general">&pref_header_general;</string>
   <string name="pref_header_search">&pref_header_search;</string>
@@ -317,33 +300,30 @@
 
   <string name="pref_panels_show">&pref_panels_show;</string>
   <string name=&qu