merge autoland to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 20 Feb 2017 14:03:26 +0100
changeset 372792 66739e3ff97463f071b578a6d05871c88da28cc7
parent 372663 c3cbadc5d2fa0adde48cbe48ed46cd0a5f7ec2bb (current diff)
parent 372791 9806145e3dddd9641d67ee1d7c9b86b51b022f37 (diff)
child 372875 24931c1b6d9e5c0b1e496a2428f2012428715956
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone54.0a1
merge autoland to mozilla-central a=merge
devtools/client/netmonitor/events.js
devtools/client/netmonitor/filter-predicates.js
devtools/client/netmonitor/l10n.js
devtools/client/netmonitor/prefs.js
devtools/client/netmonitor/request-utils.js
devtools/client/netmonitor/sort-predicates.js
devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.css
devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js
dom/presentation/provider/DisplayDeviceProvider.cpp
dom/presentation/provider/DisplayDeviceProvider.h
servo/support/android/apk/jni/main.c
testing/talos/talos/profiler/sps.py
testing/talos/talos/sps_profile.py
third_party/rust/clang-sys/.gitattributes
third_party/rust/error-chain/.gitattributes
third_party/rust/strsim/.gitattributes
--- a/accessible/tests/mochitest/attributes.js
+++ b/accessible/tests/mochitest/attributes.js
@@ -264,17 +264,17 @@ function fontFamily(aComputedStyle)
  * @param aFontSize    [in] font size
  * @param aFontWeight  [in, optional] kBoldFontWeight or kNormalFontWeight,
  *                      default value is kNormalFontWeight
  */
 function buildDefaultTextAttrs(aID, aFontSize, aFontWeight, aFontFamily)
 {
   var elm = getNode(aID);
   var computedStyle = document.defaultView.getComputedStyle(elm);
-  var bgColor = computedStyle.backgroundColor == "transparent" ?
+  var bgColor = computedStyle.backgroundColor == "rgba(0, 0, 0, 0)" ?
     "rgb(255, 255, 255)" : computedStyle.backgroundColor;
 
   var defAttrs = {
     "font-style": computedStyle.fontStyle,
     "font-size": aFontSize,
     "background-color": bgColor,
     "font-weight": aFontWeight ? aFontWeight : kNormalFontWeight,
     "color": computedStyle.color,
--- a/browser/base/content/test/general/browser_selectpopup.js
+++ b/browser/base/content/test/general/browser_selectpopup.js
@@ -85,30 +85,30 @@ const PAGECONTENT_COLORS =
   "  .green { color: #800080; background-color: green; }" +
   "  .defaultColor { color: -moz-ComboboxText; }" +
   "  .defaultBackground { background-color: -moz-Combobox; }" +
   "</style>" +
   "<body><select id='one'>" +
   '  <option value="One" style="color: #fff; background-color: #f00;">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgb(255, 0, 0)"}</option>' +
   '  <option value="Two" class="blue">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgb(0, 0, 255)"}</option>' +
   '  <option value="Three" class="green">{"color": "rgb(128, 0, 128)", "backgroundColor": "rgb(0, 128, 0)"}</option>' +
-  '  <option value="Four" class="defaultColor defaultBackground">{"color": "-moz-ComboboxText", "backgroundColor": "transparent", "unstyled": "true"}</option>' +
-  '  <option value="Five" class="defaultColor">{"color": "-moz-ComboboxText", "backgroundColor": "transparent", "unstyled": "true"}</option>' +
-  '  <option value="Six" class="defaultBackground">{"color": "-moz-ComboboxText", "backgroundColor": "transparent", "unstyled": "true"}</option>' +
+  '  <option value="Four" class="defaultColor defaultBackground">{"color": "-moz-ComboboxText", "backgroundColor": "rgba(0, 0, 0, 0)", "unstyled": "true"}</option>' +
+  '  <option value="Five" class="defaultColor">{"color": "-moz-ComboboxText", "backgroundColor": "rgba(0, 0, 0, 0)", "unstyled": "true"}</option>' +
+  '  <option value="Six" class="defaultBackground">{"color": "-moz-ComboboxText", "backgroundColor": "rgba(0, 0, 0, 0)", "unstyled": "true"}</option>' +
   '  <option value="Seven" selected="true">{"unstyled": "true"}</option>' +
   "</select></body></html>";
 
 const PAGECONTENT_COLORS_ON_SELECT =
   "<html><head><style>" +
   "  #one { background-color: #7E3A3A; color: #fff }" +
   "</style>" +
   "<body><select id='one'>" +
-  '  <option value="One">{"color": "rgb(255, 255, 255)", "backgroundColor": "transparent"}</option>' +
-  '  <option value="Two">{"color": "rgb(255, 255, 255)", "backgroundColor": "transparent"}</option>' +
-  '  <option value="Three">{"color": "rgb(255, 255, 255)", "backgroundColor": "transparent"}</option>' +
+  '  <option value="One">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  '  <option value="Two">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  '  <option value="Three">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
   '  <option value="Four" selected="true">{"end": "true"}</option>' +
   "</select></body></html>";
 
 const TRANSPARENT_SELECT =
   "<html><head><style>" +
   "  #one { background-color: transparent; }" +
   "</style>" +
   "<body><select id='one'>" +
--- a/browser/base/content/test/general/browser_windowactivation.js
+++ b/browser/base/content/test/general/browser_windowactivation.js
@@ -43,42 +43,42 @@ function reallyRunTests() {
   // while another window is focused. The third check is done after that window
   // is closed and the main window focused again. The fourth check is done after
   // switching to the second tab.
   window.messageManager.addMessageListener("Test:BackgroundColorChanged", function(message) {
     colorChangeNotifications++;
 
     switch (colorChangeNotifications) {
       case 1:
-        is(message.data.color, "transparent", "first window initial");
+        is(message.data.color, "rgba(0, 0, 0, 0)", "first window initial");
         break;
       case 2:
-        is(message.data.color, "transparent", "second window initial");
+        is(message.data.color, "rgba(0, 0, 0, 0)", "second window initial");
         runOtherWindowTests();
         break;
       case 3:
         is(message.data.color, "rgb(255, 0, 0)", "first window lowered");
         break;
       case 4:
         is(message.data.color, "rgb(255, 0, 0)", "second window lowered");
         sendGetBackgroundRequest(true);
         otherWindow.close();
         break;
       case 5:
-        is(message.data.color, "transparent", "first window raised");
+        is(message.data.color, "rgba(0, 0, 0, 0)", "first window raised");
         break;
       case 6:
-        is(message.data.color, "transparent", "second window raised");
+        is(message.data.color, "rgba(0, 0, 0, 0)", "second window raised");
         gBrowser.selectedTab = tab2;
         break;
       case 7:
-        is(message.data.color, "transparent", "first window after tab switch");
+        is(message.data.color, "rgba(0, 0, 0, 0)", "first window after tab switch");
         break;
       case 8:
-        is(message.data.color, "transparent", "second window after tab switch");
+        is(message.data.color, "rgba(0, 0, 0, 0)", "second window after tab switch");
         finishTest();
         break;
       case 9:
         ok(false, "too many color change notifications");
         break;
     }
   });
 
--- a/browser/components/extensions/ext-c-omnibox.js
+++ b/browser/components/extensions/ext-c-omnibox.js
@@ -9,18 +9,17 @@ var {
 } = ExtensionUtils;
 
 extensions.registerSchemaAPI("omnibox", "addon_child", context => {
   return {
     omnibox: {
       onInputChanged: new SingletonEventManager(context, "omnibox.onInputChanged", fire => {
         let listener = (text, id) => {
           fire.asyncWithoutClone(text, suggestions => {
-            // TODO: Switch to using callParentFunctionNoReturn once bug 1314903 is fixed.
-            context.childManager.callParentAsyncFunction("omnibox_internal.addSuggestions", [
+            context.childManager.callParentFunctionNoReturn("omnibox_internal.addSuggestions", [
               id,
               suggestions,
             ]);
           });
         };
         context.childManager.getParentEvent("omnibox_internal.onInputChanged").addListener(listener);
         return () => {
           context.childManager.getParentEvent("omnibox_internal.onInputChanged").removeListener(listener);
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js
+++ b/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js
@@ -17,17 +17,17 @@ add_task(function* testPageActionPopup()
       },
     },
 
     files: {
       "popup-a.html": scriptPage("popup-a.js"),
       "popup-a.js": function() {
         window.onload = () => {
           let background = window.getComputedStyle(document.body).backgroundColor;
-          browser.test.assertEq("transparent", background);
+          browser.test.assertEq("rgba(0, 0, 0, 0)", background);
           browser.runtime.sendMessage("from-popup-a");
         };
         browser.runtime.onMessage.addListener(msg => {
           if (msg == "close-popup") {
             window.close();
           }
         });
       },
--- a/browser/components/extensions/test/browser/browser_ext_tabs_insertCSS.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_insertCSS.js
@@ -8,17 +8,17 @@ add_task(function* testExecuteScript() {
   let messageManagersSize = MessageChannel.messageManagers.size;
   let responseManagersSize = MessageChannel.responseManagers.size;
 
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/", true);
 
   async function background() {
     let tasks = [
       {
-        background: "transparent",
+        background: "rgba(0, 0, 0, 0)",
         foreground: "rgb(0, 113, 4)",
         promise: () => {
           return browser.tabs.insertCSS({
             file: "file2.css",
           });
         },
       },
       {
--- a/browser/components/extensions/test/browser/browser_ext_tabs_removeCSS.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_removeCSS.js
@@ -4,17 +4,17 @@
 
 add_task(function* testExecuteScript() {
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/", true);
 
   async function background() {
     let tasks = [
       // Insert CSS file.
       {
-        background: "transparent",
+        background: "rgba(0, 0, 0, 0)",
         foreground: "rgb(0, 113, 4)",
         promise: () => {
           return browser.tabs.insertCSS({
             file: "file2.css",
           });
         },
       },
       // Insert CSS code.
@@ -24,27 +24,27 @@ add_task(function* testExecuteScript() {
         promise: () => {
           return browser.tabs.insertCSS({
             code: "* { background: rgb(42, 42, 42) }",
           });
         },
       },
       // Remove CSS code again.
       {
-        background: "transparent",
+        background: "rgba(0, 0, 0, 0)",
         foreground: "rgb(0, 113, 4)",
         promise: () => {
           return browser.tabs.removeCSS({
             code: "* { background: rgb(42, 42, 42) }",
           });
         },
       },
       // Remove CSS file again.
       {
-        background: "transparent",
+        background: "rgba(0, 0, 0, 0)",
         foreground: "rgb(0, 0, 0)",
         promise: () => {
           return browser.tabs.removeCSS({
             file: "file2.css",
           });
         },
       },
       // Insert CSS code.
@@ -55,17 +55,17 @@ add_task(function* testExecuteScript() {
           return browser.tabs.insertCSS({
             code: "* { background: rgb(42, 42, 42) }",
             cssOrigin: "user",
           });
         },
       },
       // Remove CSS code again.
       {
-        background: "transparent",
+        background: "rgba(0, 0, 0, 0)",
         foreground: "rgb(0, 0, 0)",
         promise: () => {
           return browser.tabs.removeCSS({
             code: "* { background: rgb(42, 42, 42) }",
             cssOrigin: "user",
           });
         },
       },
--- a/browser/components/migration/MigrationUtils.jsm
+++ b/browser/components/migration/MigrationUtils.jsm
@@ -23,16 +23,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "BookmarkHTMLUtils",
                                   "resource://gre/modules/BookmarkHTMLUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
                                   "resource://gre/modules/LoginHelper.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
                                   "resource://gre/modules/PromiseUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ResponsivenessMonitor",
+                                  "resource://gre/modules/ResponsivenessMonitor.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
                                   "resource://gre/modules/Sqlite.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
                                   "resource://gre/modules/TelemetryStopwatch.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
                                   "resource://gre/modules/WindowsRegistry.jsm");
 
 var gMigrators = null;
@@ -207,17 +209,17 @@ this.MigratorPrototype = {
     let resources = this._getMaybeCachedResources(aProfile);
     if (!resources) {
       return [];
     }
     let types = resources.map(r => r.type);
     return types.reduce((a, b) => { a |= b; return a }, 0);
   },
 
-  getKey: function MP_getKey() {
+  getBrowserKey: function MP_getBrowserKey() {
     return this.contractID.match(/\=([^\=]+)$/)[1];
   },
 
   /**
    * DO NOT OVERRIDE - After deCOMing migration, the UI will just call
    * migrate for each resource.
    *
    * @see nsIBrowserProfileMigrator
@@ -232,50 +234,74 @@ this.MigratorPrototype = {
 
     // Used to periodically give back control to the main-thread loop.
     let unblockMainThread = function() {
       return new Promise(resolve => {
         Services.tm.mainThread.dispatch(resolve, Ci.nsIThread.DISPATCH_NORMAL);
       });
     };
 
-    let getHistogramForResourceType = resourceType => {
+    let getHistogramIdForResourceType = (resourceType, template) => {
       if (resourceType == MigrationUtils.resourceTypes.HISTORY) {
-        return "FX_MIGRATION_HISTORY_IMPORT_MS";
+        return template.replace("*", "HISTORY");
       }
       if (resourceType == MigrationUtils.resourceTypes.BOOKMARKS) {
-        return "FX_MIGRATION_BOOKMARKS_IMPORT_MS";
+        return template.replace("*", "BOOKMARKS");
       }
       if (resourceType == MigrationUtils.resourceTypes.PASSWORDS) {
-        return "FX_MIGRATION_LOGINS_IMPORT_MS";
+        return template.replace("*", "LOGINS");
       }
       return null;
     };
-    let maybeStartTelemetryStopwatch = (resourceType) => {
-      let histogram = getHistogramForResourceType(resourceType);
-      if (histogram) {
-        TelemetryStopwatch.startKeyed(histogram, this.getKey());
+
+    let browserKey = this.getBrowserKey();
+
+    let maybeStartTelemetryStopwatch = resourceType => {
+      let histogramId = getHistogramIdForResourceType(resourceType, "FX_MIGRATION_*_IMPORT_MS");
+      if (histogramId) {
+        TelemetryStopwatch.startKeyed(histogramId, browserKey);
       }
+      return histogramId;
     };
-    let maybeStopTelemetryStopwatch = (resourceType) => {
-      let histogram = getHistogramForResourceType(resourceType);
-      if (histogram) {
-        TelemetryStopwatch.finishKeyed(histogram, this.getKey());
+
+    let maybeStartResponsivenessMonitor = resourceType => {
+      let responsivenessMonitor;
+      let responsivenessHistogramId =
+        getHistogramIdForResourceType(resourceType, "FX_MIGRATION_*_JANK_MS");
+      if (responsivenessHistogramId) {
+        responsivenessMonitor = new ResponsivenessMonitor();
+      }
+      return {responsivenessMonitor, responsivenessHistogramId};
+    };
+
+    let maybeFinishResponsivenessMonitor = (responsivenessMonitor, histogramId) => {
+      if (responsivenessMonitor) {
+        let accumulatedDelay = responsivenessMonitor.finish();
+        if (histogramId) {
+          try {
+            Services.telemetry.getKeyedHistogramById(histogramId)
+                    .add(browserKey, accumulatedDelay);
+          } catch (ex) {
+            Cu.reportError(histogramId + ": " + ex);
+          }
+        }
       }
     };
 
     let collectQuantityTelemetry = () => {
-      try {
-        for (let resourceType of Object.keys(MigrationUtils._importQuantities)) {
-          let histogramId =
-            "FX_MIGRATION_" + resourceType.toUpperCase() + "_QUANTITY";
-          let histogram = Services.telemetry.getKeyedHistogramById(histogramId);
-          histogram.add(this.getKey(), MigrationUtils._importQuantities[resourceType]);
+      for (let resourceType of Object.keys(MigrationUtils._importQuantities)) {
+        let histogramId =
+          "FX_MIGRATION_" + resourceType.toUpperCase() + "_QUANTITY";
+        try {
+          Services.telemetry.getKeyedHistogramById(histogramId)
+                  .add(browserKey, MigrationUtils._importQuantities[resourceType]);
+        } catch (ex) {
+          Cu.reportError(histogramId + ": " + ex);
         }
-      } catch (ex) { /* Telemetry is exception-happy */ }
+      }
     };
 
     // Called either directly or through the bookmarks import callback.
     let doMigrate = Task.async(function*() {
       let resourcesGroupedByItems = new Map();
       resources.forEach(function(resource) {
         if (!resourcesGroupedByItems.has(resource.type)) {
           resourcesGroupedByItems.set(resource.type, new Set());
@@ -292,31 +318,38 @@ this.MigratorPrototype = {
 
       for (let resourceType of Object.keys(MigrationUtils._importQuantities)) {
         MigrationUtils._importQuantities[resourceType] = 0;
       }
       notify("Migration:Started");
       for (let [migrationType, itemResources] of resourcesGroupedByItems) {
         notify("Migration:ItemBeforeMigrate", migrationType);
 
-        maybeStartTelemetryStopwatch(migrationType);
+        let stopwatchHistogramId = maybeStartTelemetryStopwatch(migrationType);
+
+        let {responsivenessMonitor, responsivenessHistogramId} =
+          maybeStartResponsivenessMonitor(migrationType);
 
         let itemSuccess = false;
         for (let res of itemResources) {
           let completeDeferred = PromiseUtils.defer();
           let resourceDone = function(aSuccess) {
             itemResources.delete(res);
             itemSuccess |= aSuccess;
             if (itemResources.size == 0) {
               notify(itemSuccess ?
                      "Migration:ItemAfterMigrate" : "Migration:ItemError",
                      migrationType);
               resourcesGroupedByItems.delete(migrationType);
 
-              maybeStopTelemetryStopwatch(migrationType);
+              if (stopwatchHistogramId) {
+                TelemetryStopwatch.finishKeyed(stopwatchHistogramId, browserKey);
+              }
+
+              maybeFinishResponsivenessMonitor(responsivenessMonitor, responsivenessHistogramId);
 
               if (resourcesGroupedByItems.size == 0) {
                 collectQuantityTelemetry();
                 notify("Migration:Ended");
               }
             }
             completeDeferred.resolve();
           };
--- a/browser/components/originattributes/test/browser/browser.ini
+++ b/browser/components/originattributes/test/browser/browser.ini
@@ -27,16 +27,18 @@ support-files =
   file_thirdPartyChild.sharedworker.js
   file_thirdPartyChild.track.vtt
   file_thirdPartyChild.video.ogv
   file_thirdPartyChild.worker.fetch.html
   file_thirdPartyChild.worker.js
   file_thirdPartyChild.worker.request.html
   file_thirdPartyChild.worker.xhr.html
   file_thirdPartyChild.xhr.html
+  file_windowOpenerRestriction.html
+  file_windowOpenerRestrictionTarget.html
   head.js
   test.js
   test.js^headers^
   test.html
   test2.html
   test2.js
   test2.js^headers^
   test_firstParty.html
@@ -66,8 +68,9 @@ support-files =
 [browser_blobURLIsolation.js]
 [browser_imageCacheIsolation.js]
 [browser_sharedworker.js]
 [browser_httpauth.js]
 [browser_clientAuth.js]
 [browser_cacheAPI.js]
 [browser_permissions.js]
 [browser_sanitize.js]
+[browser_windowOpenerRestriction.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_windowOpenerRestriction.js
@@ -0,0 +1,98 @@
+/**
+ * Bug 1339336 - A test case for testing pref 'privacy.firstparty.isolate.restrict_opener_access'
+ */
+
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
+
+const FIRST_PARTY_OPENER = "example.com";
+const FIRST_PARTY_TARGET = "example.org";
+const OPENER_PAGE = "http://" + FIRST_PARTY_OPENER + "/browser/browser/components/" +
+                    "originattributes/test/browser/file_windowOpenerRestriction.html";
+const TARGET_PAGE = "http://" + FIRST_PARTY_TARGET + "/browser/browser/components/" +
+                    "originattributes/test/browser/file_windowOpenerRestrictionTarget.html";
+
+function* testPref(aIsPrefEnabled) {
+  // Use a random key so we don't access it in later tests.
+  let cookieStr = "key" + Math.random().toString() + "=" + Math.random().toString();
+
+  // Open the tab for the opener page.
+  let tab = gBrowser.addTab(OPENER_PAGE);
+
+  // Select this tab and make sure its browser is loaded and focused.
+  gBrowser.selectedTab = tab;
+  tab.ownerGlobal.focus();
+
+  let browser = gBrowser.getBrowserForTab(tab);
+  yield BrowserTestUtils.browserLoaded(browser);
+
+  yield ContentTask.spawn(browser, {cookieStr,
+                                    page: TARGET_PAGE,
+                                    isPrefEnabled: aIsPrefEnabled}, function* (obj) {
+    // Acquire the iframe element.
+    let childFrame = content.document.getElementById("child");
+
+    // Insert a cookie into this iframe.
+    childFrame.contentDocument.cookie = obj.cookieStr;
+
+    // Open the tab here and focus on it.
+    let openedPath = obj.page;
+    if (!obj.isPrefEnabled) {
+      // If the pref is not enabled, we pass the cookie value through the query string
+      // to tell the target page that it should check the cookie value.
+      openedPath += "?" + obj.cookieStr;
+    }
+
+    // Issue the opener page to open the target page and focus on it.
+    this.openedWindow = content.open(openedPath);
+    this.openedWindow.focus();
+  });
+
+  // Wait until the target page is loaded.
+  let targetBrowser = gBrowser.getBrowserForTab(gBrowser.selectedTab);
+  yield BrowserTestUtils.browserLoaded(targetBrowser);
+
+  // The target page will do the check and show the result through its title.
+  is(targetBrowser.contentTitle, "pass", "The behavior of window.opener is correct.");
+
+  // Close Tabs.
+  yield ContentTask.spawn(browser, null, function* () {
+    this.openedWindow.close();
+  });
+  yield BrowserTestUtils.removeTab(tab);
+
+  // Reset cookies
+  Services.cookies.removeAll();
+}
+
+add_task(function* runTests() {
+  let tests = [true, false];
+
+  // First, we test the scenario that the first party isolation is enabled.
+  yield SpecialPowers.pushPrefEnv({"set":
+    [["privacy.firstparty.isolate", true]]
+  });
+
+  for (let enabled of tests) {
+    yield SpecialPowers.pushPrefEnv({"set":
+      [["privacy.firstparty.isolate.restrict_opener_access", enabled]]
+    });
+
+    yield testPref(enabled);
+  }
+
+  // Second, we test the scenario that the first party isolation is disabled.
+  yield SpecialPowers.pushPrefEnv({"set":
+    [["privacy.firstparty.isolate", false]]
+  });
+
+  for (let enabled of tests) {
+    yield SpecialPowers.pushPrefEnv({"set":
+      [["privacy.firstparty.isolate.restrict_opener_access", enabled]]
+    });
+
+    // When first party isolation is disabled, this pref will not affect the behavior of
+    // window.opener. And the correct behavior here is to allow access since the iframe in
+    // the opener page has the same origin with the target page.
+    yield testPref(false);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_windowOpenerRestriction.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8">
+  <title>Test page for window.opener accessibility</title>
+</head>
+<body>
+  <iframe id="child" name="child" src="http://example.org/browser/browser/components/originattributes/test/browser/file_firstPartyBasic.html"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_windowOpenerRestrictionTarget.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8">
+  <title>title not set</title>
+  <script>
+    // If the query string is given, we are expecting the window.opener can be accessed
+    // across different first party domains, so we will match the cookie value.
+    // Otherwise, the access of window.opener should be treated as cross-origin.
+    // Therefore, it should fail at this setting.
+    let openerRestriction = true;
+    let cookieValue;
+    if (window.location.search.length > 0) {
+      cookieValue = window.location.search.substr(1);
+      openerRestriction = false;
+    }
+
+    try {
+      let openerFrame = window.opener.frames["child"];
+      let result = openerFrame.document.cookie === cookieValue;
+      if (result && !openerRestriction) {
+        document.title = "pass";
+      }
+    } catch (e) {
+      if (openerRestriction) {
+        document.title = "pass";
+      }
+    }
+  </script>
+</head>
+<body>
+</body>
+</html>
--- a/browser/modules/ExtensionsUI.jsm
+++ b/browser/modules/ExtensionsUI.jsm
@@ -133,16 +133,17 @@ this.ExtensionsUI = {
         reply(true);
       } else {
         info.permissions = perms;
         let strings = this._buildStrings(info);
         this.showPermissionsPrompt(target, strings, info.icon).then(reply);
       }
     } else if (topic == "webextension-update-permissions") {
       let info = subject.wrappedJSObject;
+      info.type = "update";
       let strings = this._buildStrings(info);
 
       // If we don't prompt for any new permissions, just apply it
       if (strings.msgs.length == 0) {
         info.resolve();
         return;
       }
 
--- a/build/moz.configure/rust.configure
+++ b/build/moz.configure/rust.configure
@@ -38,34 +38,16 @@ def cargo_info(cargo):
         if not m:
             die('Could not determine cargo version from output: %s', out)
         version = m.group(1)
 
     return namespace(
         version=Version(version),
     )
 
-@depends_if(cargo)
-@checking('cargo support for --frozen')
-@imports('subprocess')
-@imports('os')
-def cargo_supports_frozen(cargo):
-    try:
-        lines = subprocess.check_output(
-            [cargo, 'help', 'build']
-        ).splitlines()
-        supported = any('    --frozen' in l for l in lines)
-        if 'MOZ_AUTOMATION' in os.environ and not supported:
-            die('cargo in automation must support --frozen')
-        return supported
-    except subprocess.CalledProcessError as e:
-        die('Failed to call cargo: %s', e.message)
-
-set_config('MOZ_CARGO_SUPPORTS_FROZEN', cargo_supports_frozen)
-
 @depends(rustc, cargo, rustc_info)
 @imports(_from='textwrap', _import='dedent')
 def rust_compiler(rustc, cargo, rustc_info):
     if not rustc:
         die(dedent('''\
         Rust compiler not found.
         To compile rust language sources, you must have 'rustc' in your path.
         See https//www.rust-lang.org/ for more information.
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -894,21 +894,19 @@ endif
 
 ifdef MOZ_RUST
 cargo_host_flag := --target=$(RUST_HOST_TARGET)
 cargo_target_flag := --target=$(RUST_TARGET)
 
 # Permit users to pass flags to cargo from their mozconfigs (e.g. --color=always).
 cargo_build_flags = $(CARGOFLAGS)
 ifndef MOZ_DEBUG
-cargo_build_flags = --release
+cargo_build_flags += --release
 endif
-ifdef MOZ_CARGO_SUPPORTS_FROZEN
 cargo_build_flags += --frozen
-endif
 
 cargo_build_flags += --manifest-path $(CARGO_FILE)
 ifdef BUILD_VERBOSE_LOG
 cargo_build_flags += --verbose
 endif
 
 # Enable color output if original stdout was a TTY and color settings
 # aren't already present. This essentially restores the default behavior
--- a/devtools/client/dom/content/components/dom-tree.js
+++ b/devtools/client/dom/content/components/dom-tree.js
@@ -7,17 +7,17 @@
 
 // React & Redux
 const React = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 const TreeView = React.createFactory(require("devtools/client/shared/components/tree/tree-view"));
 
 // Reps
-const { REPS, MODE } = require("devtools/client/shared/components/reps/load-reps");
+const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
 const Rep = React.createFactory(REPS.Rep);
 const Grip = REPS.Grip;
 
 // DOM Panel
 const { GripProvider } = require("../grip-provider");
 const { DomDecorator } = require("../dom-decorator");
 
 // Shortcuts
--- a/devtools/client/dom/content/components/main-toolbar.js
+++ b/devtools/client/dom/content/components/main-toolbar.js
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // React
 const React = require("devtools/client/shared/vendor/react");
 const { l10n } = require("../utils");
 
 // Reps
-const { createFactories } = require("devtools/client/shared/components/reps/load-reps");
+const { createFactories } = require("devtools/client/shared/components/reps/reps");
 const { Toolbar, ToolbarButton } = createFactories(require("devtools/client/jsonview/components/reps/toolbar"));
 
 // DOM Panel
 const SearchBox = React.createFactory(require("devtools/client/shared/components/search-box"));
 
 // Actions
 const { fetchProperties } = require("../actions/grips");
 const { setVisibilityFilter } = require("../actions/filter");
--- a/devtools/client/inspector/rules/test/browser_rules_edit-property-order.js
+++ b/devtools/client/inspector/rules/test/browser_rules_edit-property-order.js
@@ -51,17 +51,17 @@ add_task(function* () {
   yield togglePropStatus(view, secondProp);
 
   is((yield getValue("#testid", "background-color")), "rgb(0, 128, 0)",
      "After disabling second property, first value should be used");
 
   info("Disabling the first property too and checking the applied style");
   yield togglePropStatus(view, firstProp);
 
-  is((yield getValue("#testid", "background-color")), "transparent",
+  is((yield getValue("#testid", "background-color")), "rgba(0, 0, 0, 0)",
      "After disabling both properties, value should be empty.");
 
   info("Re-enabling the second propertyt and checking the applied style");
   yield togglePropStatus(view, secondProp);
 
   is((yield getValue("#testid", "background-color")), "rgb(0, 0, 255)",
      "Value should be set correctly after re-enabling");
 
--- a/devtools/client/jsonview/components/headers-panel.js
+++ b/devtools/client/jsonview/components/headers-panel.js
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 define(function (require, exports, module) {
   const { DOM: dom, createFactory, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
 
-  const { createFactories } = require("devtools/client/shared/components/reps/load-reps");
+  const { createFactories } = require("devtools/client/shared/components/reps/reps");
 
   const { Headers } = createFactories(require("./headers"));
   const { Toolbar, ToolbarButton } = createFactories(require("./reps/toolbar"));
 
   const { div } = dom;
 
   /**
    * This template represents the 'Headers' panel
--- a/devtools/client/jsonview/components/json-panel.js
+++ b/devtools/client/jsonview/components/json-panel.js
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 define(function (require, exports, module) {
   const { DOM: dom, createFactory, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
   const TreeView = createFactory(require("devtools/client/shared/components/tree/tree-view"));
 
-  const { REPS, createFactories, MODE } = require("devtools/client/shared/components/reps/load-reps");
+  const { REPS, createFactories, MODE } = require("devtools/client/shared/components/reps/reps");
   const Rep = createFactory(REPS.Rep);
 
   const { SearchBox } = createFactories(require("./search-box"));
   const { Toolbar, ToolbarButton } = createFactories(require("./reps/toolbar"));
 
   const { div } = dom;
   const AUTO_EXPAND_MAX_SIZE = 100 * 1024;
   const AUTO_EXPAND_MAX_LEVEL = 7;
--- a/devtools/client/jsonview/components/main-tabbed-area.js
+++ b/devtools/client/jsonview/components/main-tabbed-area.js
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 define(function (require, exports, module) {
   const { createClass, PropTypes } = require("devtools/client/shared/vendor/react");
 
-  const { createFactories } = require("devtools/client/shared/components/reps/load-reps");
+  const { createFactories } = require("devtools/client/shared/components/reps/reps");
   const { JsonPanel } = createFactories(require("./json-panel"));
   const { TextPanel } = createFactories(require("./text-panel"));
   const { HeadersPanel } = createFactories(require("./headers-panel"));
   const { Tabs, TabPanel } = createFactories(require("devtools/client/shared/components/tabs/tabs"));
 
   /**
    * This object represents the root application template
    * responsible for rendering the basic tab layout.
--- a/devtools/client/jsonview/components/text-panel.js
+++ b/devtools/client/jsonview/components/text-panel.js
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 define(function (require, exports, module) {
   const { DOM: dom, createFactory, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
 
   // we'll need to make load-reps define friendly aka UMD
-  const { createFactories } = require("devtools/client/shared/components/reps/load-reps");
+  const { createFactories } = require("devtools/client/shared/components/reps/reps");
   const { Toolbar, ToolbarButton } = createFactories(require("./reps/toolbar"));
   const { div, pre } = dom;
 
   /**
    * This template represents the 'Raw Data' panel displaying
    * JSON as a text received from the server.
    */
   let TextPanel = createClass({
--- a/devtools/client/jsonview/json-viewer.js
+++ b/devtools/client/jsonview/json-viewer.js
@@ -3,17 +3,17 @@
 /* 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";
 
 define(function (require, exports, module) {
   const { render } = require("devtools/client/shared/vendor/react-dom");
-  const { createFactories } = require("devtools/client/shared/components/reps/load-reps");
+  const { createFactories } = require("devtools/client/shared/components/reps/reps");
   const { MainTabbedArea } = createFactories(require("./components/main-tabbed-area"));
 
   const json = document.getElementById("json");
   const headers = document.getElementById("headers");
 
   let jsonData;
 
   try {
--- a/devtools/client/netmonitor/components/monitor-panel.js
+++ b/devtools/client/netmonitor/components/monitor-panel.js
@@ -8,18 +8,18 @@ const {
   createClass,
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
 const Actions = require("../actions/index");
-const { Prefs } = require("../prefs");
-const { getFormDataSections } = require("../request-utils");
+const { Prefs } = require("../utils/prefs");
+const { getFormDataSections } = require("../utils/request-utils");
 const { getSelectedRequest } = require("../selectors/index");
 
 // Components
 const SplitBox = createFactory(require("devtools/client/shared/components/splitter/split-box"));
 const NetworkDetailsPanel = createFactory(require("../shared/components/network-details-panel"));
 const RequestList = createFactory(require("./request-list"));
 const Toolbar = createFactory(require("./toolbar"));
 
--- a/devtools/client/netmonitor/components/request-list-empty.js
+++ b/devtools/client/netmonitor/components/request-list-empty.js
@@ -7,17 +7,17 @@
 const {
   createClass,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const Actions = require("../actions/index");
 const { ACTIVITY_TYPE } = require("../constants");
-const { L10N } = require("../l10n");
+const { L10N } = require("../utils/l10n");
 
 const { button, div, span } = DOM;
 
 /**
  * UI displayed when the request list is empty. Contains instructions on reloading
  * the page and on triggering performance analysis of the page.
  */
 const RequestListEmptyNotice = createClass({
--- a/devtools/client/netmonitor/components/request-list-header.js
+++ b/devtools/client/netmonitor/components/request-list-header.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { createClass, PropTypes, DOM } = require("devtools/client/shared/vendor/react");
 const { div, button } = DOM;
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const { setNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
-const { L10N } = require("../l10n");
+const { L10N } = require("../utils/l10n");
 const { getWaterfallScale } = require("../selectors/index");
 const Actions = require("../actions/index");
 const WaterfallBackground = require("../waterfall-background");
 const { getFormattedTime } = require("../utils/format-utils");
 
 // ms
 const REQUESTS_WATERFALL_HEADER_TICKS_MULTIPLE = 5;
 // px
--- a/devtools/client/netmonitor/components/request-list-item.js
+++ b/devtools/client/netmonitor/components/request-list-item.js
@@ -5,18 +5,18 @@
 "use strict";
 
 const {
   createClass,
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
-const { L10N } = require("../l10n");
-const { getAbbreviatedMimeType } = require("../request-utils");
+const { L10N } = require("../utils/l10n");
+const { getAbbreviatedMimeType } = require("../utils/request-utils");
 const { getFormattedSize } = require("../utils/format-utils");
 
 const { div, img, span } = DOM;
 
 /**
  * Compare two objects on a subset of their properties
  */
 function propertiesEqual(props, item1, item2) {
--- a/devtools/client/netmonitor/components/statistics-panel.js
+++ b/devtools/client/netmonitor/components/statistics-panel.js
@@ -8,18 +8,18 @@ const {
   createClass,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const { Chart } = require("devtools/client/shared/widgets/Chart");
 const { PluralForm } = require("devtools/shared/plural-form");
 const Actions = require("../actions/index");
-const { Filters } = require("../filter-predicates");
-const { L10N } = require("../l10n");
+const { Filters } = require("../utils/filter-predicates");
+const { L10N } = require("../utils/l10n");
 const {
   getSizeWithDecimals,
   getTimeWithDecimals
 } = require("../utils/format-utils");
 
 const { button, div } = DOM;
 const MediaQueryList = window.matchMedia("(min-width: 700px)");
 
--- a/devtools/client/netmonitor/components/toolbar.js
+++ b/devtools/client/netmonitor/components/toolbar.js
@@ -8,18 +8,18 @@ const {
   createClass,
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const { PluralForm } = require("devtools/shared/plural-form");
 const Actions = require("../actions/index");
-const { L10N } = require("../l10n");
-const { Prefs } = require("../prefs");
+const { L10N } = require("../utils/l10n");
+const { Prefs } = require("../utils/prefs");
 const {
   getDisplayedRequestsSummary,
   getRequestFilterTypes,
   isNetworkDetailsToggleButtonDisabled,
 } = require("../selectors/index");
 const {
   getSizeWithDecimals,
   getTimeWithDecimals,
--- a/devtools/client/netmonitor/constants.js
+++ b/devtools/client/netmonitor/constants.js
@@ -1,20 +1,14 @@
 /* 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 general = {
-  FILTER_SEARCH_DELAY: 200,
-  // 100 KB in bytes
-  SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE: 102400,
-};
-
 const actionTypes = {
   ADD_REQUEST: "ADD_REQUEST",
   ADD_TIMING_MARKER: "ADD_TIMING_MARKER",
   BATCH_ACTIONS: "BATCH_ACTIONS",
   BATCH_ENABLE: "BATCH_ENABLE",
   CLEAR_REQUESTS: "CLEAR_REQUESTS",
   CLEAR_TIMING_MARKERS: "CLEAR_TIMING_MARKERS",
   CLONE_SELECTED_REQUEST: "CLONE_SELECTED_REQUEST",
@@ -44,9 +38,83 @@ const ACTIVITY_TYPE = {
     WITH_CACHE_DEFAULT: 3
   },
 
   // Enabling or disabling the cache without triggering a reload.
   ENABLE_CACHE: 3,
   DISABLE_CACHE: 4
 };
 
-module.exports = Object.assign({ ACTIVITY_TYPE }, general, actionTypes);
+// 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.
+  // See https://developer.mozilla.org/docs/Tools/Web_Console/remoting for
+  // more information about what each packet is supposed to deliver.
+  NETWORK_EVENT: "NetMonitor:NetworkEvent",
+  TIMELINE_EVENT: "NetMonitor:TimelineEvent",
+
+  // When a network event is added to the view
+  REQUEST_ADDED: "NetMonitor:RequestAdded",
+
+  // When request headers begin and finish receiving.
+  UPDATING_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdating:RequestHeaders",
+  RECEIVED_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdated:RequestHeaders",
+
+  // When request cookies begin and finish receiving.
+  UPDATING_REQUEST_COOKIES: "NetMonitor:NetworkEventUpdating:RequestCookies",
+  RECEIVED_REQUEST_COOKIES: "NetMonitor:NetworkEventUpdated:RequestCookies",
+
+  // When request post data begins and finishes receiving.
+  UPDATING_REQUEST_POST_DATA: "NetMonitor:NetworkEventUpdating:RequestPostData",
+  RECEIVED_REQUEST_POST_DATA: "NetMonitor:NetworkEventUpdated:RequestPostData",
+
+  // When security information begins and finishes receiving.
+  UPDATING_SECURITY_INFO: "NetMonitor::NetworkEventUpdating:SecurityInfo",
+  RECEIVED_SECURITY_INFO: "NetMonitor::NetworkEventUpdated:SecurityInfo",
+
+  // When response headers begin and finish receiving.
+  UPDATING_RESPONSE_HEADERS: "NetMonitor:NetworkEventUpdating:ResponseHeaders",
+  RECEIVED_RESPONSE_HEADERS: "NetMonitor:NetworkEventUpdated:ResponseHeaders",
+
+  // When response cookies begin and finish receiving.
+  UPDATING_RESPONSE_COOKIES: "NetMonitor:NetworkEventUpdating:ResponseCookies",
+  RECEIVED_RESPONSE_COOKIES: "NetMonitor:NetworkEventUpdated:ResponseCookies",
+
+  // When event timings begin and finish receiving.
+  UPDATING_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdating:EventTimings",
+  RECEIVED_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdated:EventTimings",
+
+  // When response content begins, updates and finishes receiving.
+  STARTED_RECEIVING_RESPONSE: "NetMonitor:NetworkEventUpdating:ResponseStart",
+  UPDATING_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdating:ResponseContent",
+  RECEIVED_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdated:ResponseContent",
+
+  // When the request post params are displayed in the UI.
+  REQUEST_POST_PARAMS_DISPLAYED: "NetMonitor:RequestPostParamsAvailable",
+
+  // When the image response thumbnail is displayed in the UI.
+  RESPONSE_IMAGE_THUMBNAIL_DISPLAYED:
+    "NetMonitor:ResponseImageThumbnailAvailable",
+
+  // Fired when charts have been displayed in the PerformanceStatisticsView.
+  PLACEHOLDER_CHARTS_DISPLAYED: "NetMonitor:PlaceholderChartsDisplayed",
+  PRIMED_CACHE_CHART_DISPLAYED: "NetMonitor:PrimedChartsDisplayed",
+  EMPTY_CACHE_CHART_DISPLAYED: "NetMonitor:EmptyChartsDisplayed",
+
+  // Fired once the NetMonitorController establishes a connection to the debug
+  // target.
+  CONNECTED: "connected",
+};
+
+const general = {
+  ACTIVITY_TYPE,
+  EVENTS,
+  FILTER_SEARCH_DELAY: 200,
+  // 100 KB in bytes
+  SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE: 102400,
+};
+
+// flatten constants
+module.exports = Object.assign({}, general, actionTypes);
deleted file mode 100644
--- a/devtools/client/netmonitor/events.js
+++ /dev/null
@@ -1,72 +0,0 @@
-/* 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";
-
-// 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.
-  // See https://developer.mozilla.org/docs/Tools/Web_Console/remoting for
-  // more information about what each packet is supposed to deliver.
-  NETWORK_EVENT: "NetMonitor:NetworkEvent",
-  TIMELINE_EVENT: "NetMonitor:TimelineEvent",
-
-  // When a network event is added to the view
-  REQUEST_ADDED: "NetMonitor:RequestAdded",
-
-  // When request headers begin and finish receiving.
-  UPDATING_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdating:RequestHeaders",
-  RECEIVED_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdated:RequestHeaders",
-
-  // When request cookies begin and finish receiving.
-  UPDATING_REQUEST_COOKIES: "NetMonitor:NetworkEventUpdating:RequestCookies",
-  RECEIVED_REQUEST_COOKIES: "NetMonitor:NetworkEventUpdated:RequestCookies",
-
-  // When request post data begins and finishes receiving.
-  UPDATING_REQUEST_POST_DATA: "NetMonitor:NetworkEventUpdating:RequestPostData",
-  RECEIVED_REQUEST_POST_DATA: "NetMonitor:NetworkEventUpdated:RequestPostData",
-
-  // When security information begins and finishes receiving.
-  UPDATING_SECURITY_INFO: "NetMonitor::NetworkEventUpdating:SecurityInfo",
-  RECEIVED_SECURITY_INFO: "NetMonitor::NetworkEventUpdated:SecurityInfo",
-
-  // When response headers begin and finish receiving.
-  UPDATING_RESPONSE_HEADERS: "NetMonitor:NetworkEventUpdating:ResponseHeaders",
-  RECEIVED_RESPONSE_HEADERS: "NetMonitor:NetworkEventUpdated:ResponseHeaders",
-
-  // When response cookies begin and finish receiving.
-  UPDATING_RESPONSE_COOKIES: "NetMonitor:NetworkEventUpdating:ResponseCookies",
-  RECEIVED_RESPONSE_COOKIES: "NetMonitor:NetworkEventUpdated:ResponseCookies",
-
-  // When event timings begin and finish receiving.
-  UPDATING_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdating:EventTimings",
-  RECEIVED_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdated:EventTimings",
-
-  // When response content begins, updates and finishes receiving.
-  STARTED_RECEIVING_RESPONSE: "NetMonitor:NetworkEventUpdating:ResponseStart",
-  UPDATING_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdating:ResponseContent",
-  RECEIVED_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdated:ResponseContent",
-
-  // When the request post params are displayed in the UI.
-  REQUEST_POST_PARAMS_DISPLAYED: "NetMonitor:RequestPostParamsAvailable",
-
-  // When the image response thumbnail is displayed in the UI.
-  RESPONSE_IMAGE_THUMBNAIL_DISPLAYED:
-    "NetMonitor:ResponseImageThumbnailAvailable",
-
-  // Fired when charts have been displayed in the PerformanceStatisticsView.
-  PLACEHOLDER_CHARTS_DISPLAYED: "NetMonitor:PlaceholderChartsDisplayed",
-  PRIMED_CACHE_CHART_DISPLAYED: "NetMonitor:PrimedChartsDisplayed",
-  EMPTY_CACHE_CHART_DISPLAYED: "NetMonitor:EmptyChartsDisplayed",
-
-  // Fired once the NetMonitorController establishes a connection to the debug
-  // target.
-  CONNECTED: "connected",
-};
-
-exports.EVENTS = EVENTS;
--- a/devtools/client/netmonitor/har/har-builder.js
+++ b/devtools/client/netmonitor/har/har-builder.js
@@ -7,17 +7,17 @@
 const Services = require("Services");
 const appInfo = Services.appinfo;
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const { CurlUtils } = require("devtools/client/shared/curl");
 const {
   getFormDataSections,
   getUrlQuery,
   parseQueryString,
-} = require("devtools/client/netmonitor/request-utils");
+} = require("devtools/client/netmonitor/utils/request-utils");
 
 const L10N = new LocalizationHelper("devtools/client/locales/har.properties");
 const HAR_VERSION = "1.1";
 
 /**
  * This object is responsible for building HAR file. See HAR spec:
  * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html
  * http://www.softwareishard.com/blog/har-12-spec/
--- a/devtools/client/netmonitor/middleware/prefs.js
+++ b/devtools/client/netmonitor/middleware/prefs.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {
   ENABLE_REQUEST_FILTER_TYPE_ONLY,
   TOGGLE_REQUEST_FILTER_TYPE,
 } = require("../constants");
-const { Prefs } = require("../prefs");
+const { Prefs } = require("../utils/prefs");
 const { getRequestFilterTypes } = require("../selectors/index");
 
 /**
   * Whenever the User clicks on a filter in the network monitor, save the new
   * filters for future tabs
   */
 function prefsMiddleware(store) {
   return next => action => {
--- a/devtools/client/netmonitor/moz.build
+++ b/devtools/client/netmonitor/moz.build
@@ -10,26 +10,20 @@ DIRS += [
     'reducers',
     'selectors',
     'shared',
     'utils',
 ]
 
 DevToolsModules(
     'constants.js',
-    'events.js',
-    'filter-predicates.js',
-    'l10n.js',
     'netmonitor-controller.js',
     'panel.js',
-    'prefs.js',
     'request-list-context-menu.js',
     'request-list-tooltip.js',
-    'request-utils.js',
-    'sort-predicates.js',
     'store.js',
     'waterfall-background.js',
 )
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
 
 with Files('**'):
     BUG_COMPONENT = ('Firefox', 'Developer Tools: Netmonitor')
--- a/devtools/client/netmonitor/netmonitor-controller.js
+++ b/devtools/client/netmonitor/netmonitor-controller.js
@@ -3,24 +3,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
 const EventEmitter = require("devtools/shared/event-emitter");
 const { TimelineFront } = require("devtools/shared/fronts/timeline");
 const { CurlUtils } = require("devtools/client/shared/curl");
-const { ACTIVITY_TYPE } = require("./constants");
-const { EVENTS } = require("./events");
+const { ACTIVITY_TYPE, EVENTS } = require("./constants");
 const { configureStore } = require("./store");
 const Actions = require("./actions/index");
 const {
   fetchHeaders,
   formDataURI,
-} = require("./request-utils");
+} = require("./utils/request-utils");
 const {
   getRequestById,
   getDisplayedRequestById,
 } = require("./selectors/index");
 
 const gStore = window.gStore = configureStore();
 
 /**
--- a/devtools/client/netmonitor/reducers/requests.js
+++ b/devtools/client/netmonitor/reducers/requests.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 I = require("devtools/client/shared/vendor/immutable");
-const { getUrlDetails } = require("../request-utils");
+const { getUrlDetails } = require("../utils/request-utils");
 const {
   ADD_REQUEST,
   CLEAR_REQUESTS,
   CLONE_SELECTED_REQUEST,
   OPEN_NETWORK_DETAILS,
   REMOVE_SELECTED_CUSTOM_REQUEST,
   SELECT_REQUEST,
   SEND_CUSTOM_REQUEST,
--- a/devtools/client/netmonitor/request-list-context-menu.js
+++ b/devtools/client/netmonitor/request-list-context-menu.js
@@ -6,23 +6,23 @@
 
 const Services = require("Services");
 const { Curl } = require("devtools/client/shared/curl");
 const { gDevTools } = require("devtools/client/framework/devtools");
 const Menu = require("devtools/client/framework/menu");
 const MenuItem = require("devtools/client/framework/menu-item");
 const clipboardHelper = require("devtools/shared/platform/clipboard");
 const { HarExporter } = require("./har/har-exporter");
-const { L10N } = require("./l10n");
+const { L10N } = require("./utils/l10n");
 const {
   formDataURI,
   getFormDataSections,
   getUrlQuery,
   parseQueryString,
-} = require("./request-utils");
+} = require("./utils/request-utils");
 const {
   getSelectedRequest,
   getSortedRequests,
 } = require("./selectors/index");
 
 function RequestListContextMenu({
   cloneSelectedRequest,
   openStatistics,
--- a/devtools/client/netmonitor/request-list-tooltip.js
+++ b/devtools/client/netmonitor/request-list-tooltip.js
@@ -3,18 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {
   setImageTooltip,
   getImageDimensions,
 } = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
-const { WEBCONSOLE_L10N } = require("./l10n");
-const { formDataURI } = require("./request-utils");
+const { WEBCONSOLE_L10N } = require("./utils/l10n");
+const { formDataURI } = require("./utils/request-utils");
 
 // px
 const REQUESTS_TOOLTIP_IMAGE_MAX_DIM = 400;
 // px
 const REQUESTS_TOOLTIP_STACK_TRACE_WIDTH = 600;
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 
--- a/devtools/client/netmonitor/selectors/requests.js
+++ b/devtools/client/netmonitor/selectors/requests.js
@@ -1,17 +1,17 @@
 /* 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 { createSelector } = require("devtools/client/shared/vendor/reselect");
-const { Filters, isFreetextMatch } = require("../filter-predicates");
-const { Sorters } = require("../sort-predicates");
+const { Filters, isFreetextMatch } = require("../utils/filter-predicates");
+const { Sorters } = require("../utils/sort-predicates");
 
 /**
  * Take clones into account when sorting.
  * If a request is a clone, use the original request for comparison.
  * If one of the compared request is a clone of the other, sort them next to each other.
  */
 function sortWithClones(requests, sorter, a, b) {
   const aId = a.id, bId = b.id;
--- a/devtools/client/netmonitor/shared/components/cookies-panel.js
+++ b/devtools/client/netmonitor/shared/components/cookies-panel.js
@@ -4,17 +4,17 @@
 
 "use strict";
 
 const {
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
-const { L10N } = require("../../l10n");
+const { L10N } = require("../../utils/l10n");
 
 // Component
 const PropertiesView = createFactory(require("./properties-view"));
 
 const { div } = DOM;
 
 const COOKIES_EMPTY_TEXT = L10N.getStr("cookiesEmptyText");
 const COOKIES_FILTER_TEXT = L10N.getStr("cookiesFilterText");
--- a/devtools/client/netmonitor/shared/components/custom-request-panel.js
+++ b/devtools/client/netmonitor/shared/components/custom-request-panel.js
@@ -4,24 +4,24 @@
 
 "use strict";
 
 const {
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
-const { L10N } = require("../../l10n");
+const { L10N } = require("../../utils/l10n");
 const Actions = require("../../actions/index");
 const { getSelectedRequest } = require("../../selectors/index");
 const {
   getUrlQuery,
   parseQueryString,
   writeHeaderText,
-} = require("../../request-utils");
+} = require("../../utils/request-utils");
 
 const {
   button,
   div,
   input,
   textarea,
 } = DOM;
 
--- a/devtools/client/netmonitor/shared/components/headers-panel.js
+++ b/devtools/client/netmonitor/shared/components/headers-panel.js
@@ -5,21 +5,21 @@
 "use strict";
 
 const {
   createClass,
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
-const { L10N } = require("../../l10n");
-const { writeHeaderText } = require("../../request-utils");
+const { L10N } = require("../../utils/l10n");
+const { writeHeaderText } = require("../../utils/request-utils");
 const { getHeadersURL } = require("../../utils/mdn-utils");
 const { getFormattedSize } = require("../../utils/format-utils");
-const { REPS, MODE } = require("devtools/client/shared/components/reps/load-reps");
+const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
 const Rep = createFactory(REPS.Rep);
 
 // Components
 const MDNLink = createFactory(require("./mdn-link"));
 const PropertiesView = createFactory(require("./properties-view"));
 
 const { button, div, input, textarea } = DOM;
 
--- a/devtools/client/netmonitor/shared/components/mdn-link.js
+++ b/devtools/client/netmonitor/shared/components/mdn-link.js
@@ -5,17 +5,17 @@
 "use strict";
 
 const Services = require("Services");
 const {
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { gDevTools } = require("devtools/client/framework/devtools");
-const { L10N } = require("../../l10n");
+const { L10N } = require("../../utils/l10n");
 
 const { a } = DOM;
 
 const LEARN_MORE = L10N.getStr("netmonitor.headers.learnMore");
 
 function MDNLink({ url }) {
   return (
     a({
--- a/devtools/client/netmonitor/shared/components/params-panel.js
+++ b/devtools/client/netmonitor/shared/components/params-panel.js
@@ -4,18 +4,18 @@
 
 "use strict";
 
 const {
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
-const { L10N } = require("../../l10n");
-const { getUrlQuery, parseQueryString } = require("../../request-utils");
+const { L10N } = require("../../utils/l10n");
+const { getUrlQuery, parseQueryString } = require("../../utils/request-utils");
 
 // Components
 const PropertiesView = createFactory(require("./properties-view"));
 
 const { div } = DOM;
 
 const JSON_SCOPE_NAME = L10N.getStr("jsonScopeName");
 const PARAMS_EMPTY_TEXT = L10N.getStr("paramsEmptyText");
--- a/devtools/client/netmonitor/shared/components/properties-view.js
+++ b/devtools/client/netmonitor/shared/components/properties-view.js
@@ -8,17 +8,17 @@
 
 const {
   createClass,
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 
-const { REPS, MODE } = require("devtools/client/shared/components/reps/load-reps");
+const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
 const Rep = createFactory(REPS.Rep);
 
 const { FILTER_SEARCH_DELAY } = require("../../constants");
 
 // Components
 const Editor = createFactory(require("devtools/client/netmonitor/shared/components/editor"));
 const SearchBox = createFactory(require("devtools/client/shared/components/search-box"));
 const TreeView = createFactory(require("devtools/client/shared/components/tree/tree-view"));
--- a/devtools/client/netmonitor/shared/components/response-panel.js
+++ b/devtools/client/netmonitor/shared/components/response-panel.js
@@ -5,18 +5,18 @@
 "use strict";
 
 const {
   createClass,
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
-const { L10N } = require("../../l10n");
-const { formDataURI, getUrlBaseName } = require("../../request-utils");
+const { L10N } = require("../../utils/l10n");
+const { formDataURI, getUrlBaseName } = require("../../utils/request-utils");
 
 // Components
 const PropertiesView = createFactory(require("./properties-view"));
 
 const { div, img } = DOM;
 const JSON_SCOPE_NAME = L10N.getStr("jsonScopeName");
 const JSON_FILTER_TEXT = L10N.getStr("jsonFilterText");
 const RESPONSE_IMG_NAME = L10N.getStr("netmonitor.response.name");
--- a/devtools/client/netmonitor/shared/components/security-panel.js
+++ b/devtools/client/netmonitor/shared/components/security-panel.js
@@ -4,18 +4,18 @@
 
 "use strict";
 
 const {
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
-const { L10N } = require("../../l10n");
-const { getUrlHost } = require("../../request-utils");
+const { L10N } = require("../../utils/l10n");
+const { getUrlHost } = require("../../utils/request-utils");
 
 // Components
 const PropertiesView = createFactory(require("./properties-view"));
 
 const { div, input, span } = DOM;
 
 /*
  * Security panel component
--- a/devtools/client/netmonitor/shared/components/tabbox-panel.js
+++ b/devtools/client/netmonitor/shared/components/tabbox-panel.js
@@ -5,18 +5,18 @@
 "use strict";
 
 const {
   createFactory,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const Actions = require("../../actions/index");
-const { Filters } = require("../../filter-predicates");
-const { L10N } = require("../../l10n");
+const { Filters } = require("../../utils/filter-predicates");
+const { L10N } = require("../../utils/l10n");
 const { getSelectedRequest } = require("../../selectors/index");
 
 // Components
 const Tabbar = createFactory(require("devtools/client/shared/components/tabs/tabbar"));
 const TabPanel = createFactory(require("devtools/client/shared/components/tabs/tabs").TabPanel);
 const CookiesPanel = createFactory(require("./cookies-panel"));
 const HeadersPanel = createFactory(require("./headers-panel"));
 const ParamsPanel = createFactory(require("./params-panel"));
--- a/devtools/client/netmonitor/shared/components/timings-panel.js
+++ b/devtools/client/netmonitor/shared/components/timings-panel.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, PropTypes } = require("devtools/client/shared/vendor/react");
-const { L10N } = require("../../l10n");
+const { L10N } = require("../../utils/l10n");
 
 const { div, span } = DOM;
 const types = ["blocked", "dns", "connect", "send", "wait", "receive"];
 const TIMINGS_END_PADDING = "80px";
 
 /*
  * Timings panel component
  * Display timeline bars that shows the total wait time for various stages
--- a/devtools/client/netmonitor/test/browser_net_brotli.js
+++ b/devtools/client/netmonitor/test/browser_net_brotli.js
@@ -6,17 +6,17 @@
 const BROTLI_URL = HTTPS_EXAMPLE_URL + "html_brotli-test-page.html";
 const BROTLI_REQUESTS = 1;
 
 /**
  * Test brotli encoded response is handled correctly on HTTPS.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(BROTLI_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
   let {
     getDisplayedRequests,
--- a/devtools/client/netmonitor/test/browser_net_charts-02.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-02.js
@@ -4,17 +4,17 @@
 "use strict";
 
 /**
  * Makes sure Pie Charts have the right internal structure when
  * initialized with empty data.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, windowRequire } = monitor.panelWin;
   let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
   let pie = Chart.Pie(document, {
--- a/devtools/client/netmonitor/test/browser_net_charts-03.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-03.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 /**
  * Makes sure Table Charts have the right internal structure.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, windowRequire } = monitor.panelWin;
   let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
   let table = Chart.Table(document, {
--- a/devtools/client/netmonitor/test/browser_net_charts-04.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-04.js
@@ -4,17 +4,17 @@
 "use strict";
 
 /**
  * Makes sure Pie Charts have the right internal structure when
  * initialized with empty data.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, windowRequire } = monitor.panelWin;
   let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
   let table = Chart.Table(document, {
--- a/devtools/client/netmonitor/test/browser_net_charts-05.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-05.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 /**
  * Makes sure Pie+Table Charts have the right internal structure.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, windowRequire } = monitor.panelWin;
   let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
   let chart = Chart.PieTable(document, {
--- a/devtools/client/netmonitor/test/browser_net_charts-06.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-06.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 /**
  * Makes sure Pie Charts correctly handle empty source data.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, windowRequire } = monitor.panelWin;
   let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
   let pie = Chart.Pie(document, {
--- a/devtools/client/netmonitor/test/browser_net_charts-07.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-07.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 /**
  * Makes sure Table Charts correctly handle empty source data.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, windowRequire } = monitor.panelWin;
   let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
   let table = Chart.Table(document, {
--- a/devtools/client/netmonitor/test/browser_net_clear.js
+++ b/devtools/client/netmonitor/test/browser_net_clear.js
@@ -8,17 +8,17 @@
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
-  let { EVENTS } = windowRequire("devtools/client/netmonitor/events");
+  let { EVENTS } = windowRequire("devtools/client/netmonitor/constants");
   let detailsPane = document.querySelector("#details-pane");
   let detailsPanelToggleButton = document.querySelector(".network-details-panel-toggle");
   let clearButton = document.querySelector("#requests-menu-clear-button");
 
   gStore.dispatch(Actions.batchEnable(false));
 
   // Make sure we start in a sane state
   assertNoRequestState();
--- a/devtools/client/netmonitor/test/browser_net_complex-params.js
+++ b/devtools/client/netmonitor/test/browser_net_complex-params.js
@@ -9,17 +9,17 @@
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(PARAMS_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
-  let { L10N } = windowRequire("devtools/client/netmonitor/l10n");
+  let { L10N } = windowRequire("devtools/client/netmonitor/utils/l10n");
 
   gStore.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1, 6);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
--- a/devtools/client/netmonitor/test/browser_net_content-type.js
+++ b/devtools/client/netmonitor/test/browser_net_content-type.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 /**
  * Tests if different response content types are handled correctly.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
   let {
     getDisplayedRequests,
--- a/devtools/client/netmonitor/test/browser_net_filter-04.js
+++ b/devtools/client/netmonitor/test/browser_net_filter-04.js
@@ -32,17 +32,17 @@ const REQUESTS_WITH_MEDIA_AND_FLASH_AND_
 add_task(function* () {
   Services.prefs.setCharPref("devtools.netmonitor.filters", '["bogus", "js", "alsobogus"]');
 
   let { monitor } = yield initNetMonitor(FILTERING_URL);
   info("Starting test... ");
 
   let { gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
-  let { Prefs } = windowRequire("devtools/client/netmonitor/prefs");
+  let { Prefs } = windowRequire("devtools/client/netmonitor/utils/prefs");
 
   gStore.dispatch(Actions.batchEnable(false));
 
   is(Prefs.filters.length, 1,
     "Only the valid filter types should be loaded, the others should be ignored");
   is(Prefs.filters[0], "js",
     "The only filter type is correct.");
 
--- a/devtools/client/netmonitor/test/browser_net_footer-summary.js
+++ b/devtools/client/netmonitor/test/browser_net_footer-summary.js
@@ -12,17 +12,17 @@ add_task(function* () {
 
   let { tab, monitor } = yield initNetMonitor(FILTERING_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
   let { getDisplayedRequestsSummary } =
     windowRequire("devtools/client/netmonitor/selectors/index");
-  let { L10N } = windowRequire("devtools/client/netmonitor/l10n");
+  let { L10N } = windowRequire("devtools/client/netmonitor/utils/l10n");
   let { PluralForm } = windowRequire("devtools/shared/plural-form");
 
   gStore.dispatch(Actions.batchEnable(false));
   testStatus();
 
   for (let i = 0; i < 2; i++) {
     info(`Performing requests in batch #${i}`);
     let wait = waitForNetworkEvents(monitor, 8);
--- a/devtools/client/netmonitor/test/browser_net_icon-preview.js
+++ b/devtools/client/netmonitor/test/browser_net_icon-preview.js
@@ -9,18 +9,17 @@
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire, NetMonitorController } =
     monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
-  let { ACTIVITY_TYPE } = windowRequire("devtools/client/netmonitor/constants");
-  let { EVENTS } = windowRequire("devtools/client/netmonitor/events");
+  let { ACTIVITY_TYPE, EVENTS } = windowRequire("devtools/client/netmonitor/constants");
 
   gStore.dispatch(Actions.batchEnable(false));
 
   let wait = waitForEvents();
   yield performRequests();
   yield wait;
 
   info("Checking the image thumbnail when all items are shown.");
--- a/devtools/client/netmonitor/test/browser_net_image-tooltip.js
+++ b/devtools/client/netmonitor/test/browser_net_image-tooltip.js
@@ -10,18 +10,17 @@ const IMAGE_TOOLTIP_REQUESTS = 1;
  * Tests if image responses show a popup in the requests menu when hovered.
  */
 add_task(function* test() {
   let { tab, monitor } = yield initNetMonitor(IMAGE_TOOLTIP_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire, NetMonitorController } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
-  let { ACTIVITY_TYPE } = windowRequire("devtools/client/netmonitor/constants");
-  let { EVENTS } = windowRequire("devtools/client/netmonitor/events");
+  let { ACTIVITY_TYPE, EVENTS } = windowRequire("devtools/client/netmonitor/constants");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/selectors/index");
   let toolboxDoc = monitor.toolbox.doc;
 
   gStore.dispatch(Actions.batchEnable(false));
 
--- a/devtools/client/netmonitor/test/browser_net_json-b64.js
+++ b/devtools/client/netmonitor/test/browser_net_json-b64.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 /**
  * Tests if JSON responses encoded in base64 are handled correctly.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
   let { tab, monitor } = yield initNetMonitor(JSON_B64_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
 
   gStore.dispatch(Actions.batchEnable(false));
 
--- a/devtools/client/netmonitor/test/browser_net_json-long.js
+++ b/devtools/client/netmonitor/test/browser_net_json-long.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 /**
  * Tests if very long JSON responses are handled correctly.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(JSON_LONG_URL);
   info("Starting test... ");
 
   // This is receiving over 80 KB of json and will populate over 6000 items
   // in a variables view instance. Debug builds are slow.
   requestLongerTimeout(4);
 
--- a/devtools/client/netmonitor/test/browser_net_json-malformed.js
+++ b/devtools/client/netmonitor/test/browser_net_json-malformed.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 /**
  * Tests if malformed JSON responses are handled correctly.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
   let { tab, monitor } = yield initNetMonitor(JSON_MALFORMED_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
--- a/devtools/client/netmonitor/test/browser_net_json-null.js
+++ b/devtools/client/netmonitor/test/browser_net_json-null.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-const { L10N } = require("devtools/client/netmonitor/l10n");
+const { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
 /**
  * Tests if JSON responses containing null values are properly displayed.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(JSON_BASIC_URL + "?name=null");
   info("Starting test... ");
--- a/devtools/client/netmonitor/test/browser_net_json_custom_mime.js
+++ b/devtools/client/netmonitor/test/browser_net_json_custom_mime.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 /**
  * Tests if JSON responses with unusal/custom MIME types are handled correctly.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(JSON_CUSTOM_MIME_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
   let {
     getDisplayedRequests,
--- a/devtools/client/netmonitor/test/browser_net_json_text_mime.js
+++ b/devtools/client/netmonitor/test/browser_net_json_text_mime.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 /**
  * Tests if JSON responses with unusal/custom MIME types are handled correctly.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(JSON_TEXT_MIME_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
   let {
     getDisplayedRequests,
--- a/devtools/client/netmonitor/test/browser_net_jsonp.js
+++ b/devtools/client/netmonitor/test/browser_net_jsonp.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 /**
  * Tests if JSONP responses are handled correctly.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(JSONP_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
   let {
     getDisplayedRequests,
--- a/devtools/client/netmonitor/test/browser_net_page-nav.js
+++ b/devtools/client/netmonitor/test/browser_net_page-nav.js
@@ -8,17 +8,17 @@
  * action in the network monitor.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { windowRequire } = monitor.panelWin;
-  let { EVENTS } = windowRequire("devtools/client/netmonitor/events");
+  let { EVENTS } = windowRequire("devtools/client/netmonitor/constants");
 
   yield testNavigate();
   yield testNavigateBack();
   yield testClose();
 
   function* testNavigate() {
     info("Navigating forward...");
 
--- a/devtools/client/netmonitor/test/browser_net_pane-collapse.js
+++ b/devtools/client/netmonitor/test/browser_net_pane-collapse.js
@@ -7,17 +7,17 @@
  * Tests if the network monitor panes collapse properly.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, windowRequire } = monitor.panelWin;
-  let { Prefs } = windowRequire("devtools/client/netmonitor/prefs");
+  let { Prefs } = windowRequire("devtools/client/netmonitor/utils/prefs");
   let detailsPaneToggleButton = document.querySelector(".network-details-panel-toggle");
 
   let wait = waitForNetworkEvents(monitor, 1);
   tab.linkedBrowser.reload();
   yield wait;
 
   ok(!document.querySelector(".network-details-panel") &&
      detailsPaneToggleButton.classList.contains("pane-collapsed"),
--- a/devtools/client/netmonitor/test/browser_net_pane-toggle.js
+++ b/devtools/client/netmonitor/test/browser_net_pane-toggle.js
@@ -8,17 +8,17 @@
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
-  let { EVENTS } = windowRequire("devtools/client/netmonitor/events");
+  let { EVENTS } = windowRequire("devtools/client/netmonitor/constants");
   let {
     getSelectedRequest,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/selectors/index");
 
   gStore.dispatch(Actions.batchEnable(false));
 
   let toggleButton = document.querySelector(".network-details-panel-toggle");
--- a/devtools/client/netmonitor/test/browser_net_post-data-01.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-01.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 /**
  * Tests if the POST requests display the correct information in the UI.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   // Set a higher panel height in order to get full CodeMirror content
   Services.prefs.setIntPref("devtools.toolbox.footer.height", 400);
 
   let { tab, monitor } = yield initNetMonitor(POST_DATA_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
--- a/devtools/client/netmonitor/test/browser_net_post-data-02.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-02.js
@@ -4,17 +4,17 @@
 "use strict";
 
 /**
  * Tests if the POST requests display the correct information in the UI,
  * for raw payloads with attached content-type headers.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(POST_RAW_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
   let {
     getDisplayedRequests,
--- a/devtools/client/netmonitor/test/browser_net_post-data-03.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-03.js
@@ -4,17 +4,17 @@
 "use strict";
 
 /**
  * Tests if the POST requests display the correct information in the UI,
  * for raw payloads with content-type headers attached to the upload stream.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(POST_RAW_WITH_HEADERS_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
 
   gStore.dispatch(Actions.batchEnable(false));
--- a/devtools/client/netmonitor/test/browser_net_post-data-04.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-04.js
@@ -4,17 +4,17 @@
 "use strict";
 
 /**
  * Tests if the POST requests display the correct information in the UI,
  * for JSON payloads.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(POST_JSON_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
 
   gStore.dispatch(Actions.batchEnable(false));
--- a/devtools/client/netmonitor/test/browser_net_prefs-and-l10n.js
+++ b/devtools/client/netmonitor/test/browser_net_prefs-and-l10n.js
@@ -3,23 +3,23 @@
 
 "use strict";
 
 /**
  * Tests if the preferences and localization objects work correctly.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { windowRequire } = monitor.panelWin;
-  let { Prefs } = windowRequire("devtools/client/netmonitor/prefs");
+  let { Prefs } = windowRequire("devtools/client/netmonitor/utils/prefs");
 
   testL10N();
   testPrefs();
 
   return teardown(monitor);
 
   function testL10N() {
     is(typeof L10N.getStr("netmonitor.security.enabled"), "string",
--- a/devtools/client/netmonitor/test/browser_net_prefs-reload.js
+++ b/devtools/client/netmonitor/test/browser_net_prefs-reload.js
@@ -17,17 +17,17 @@ add_task(function* () {
   // This test reopens the network monitor a bunch of times, for different
   // hosts (bottom, side, window). This seems to be slow on debug builds.
   requestLongerTimeout(3);
 
   // Use these getters instead of caching instances inside the panel win,
   // since the tool is reopened a bunch of times during this test
   // and the instances will differ.
   let getDoc = () => monitor.panelWin.document;
-  let getPrefs = () => monitor.panelWin.windowRequire("devtools/client/netmonitor/prefs").Prefs;
+  let getPrefs = () => monitor.panelWin.windowRequire("devtools/client/netmonitor/utils/prefs").Prefs;
   let getStore = () => monitor.panelWin.gStore;
   let getState = () => getStore().getState();
 
   let prefsToCheck = {
     filters: {
       // A custom new value to be used for the verified preference.
       newValue: ["html", "css"],
       // Getter used to retrieve the current value from the frontend, in order
--- a/devtools/client/netmonitor/test/browser_net_reload-markers.js
+++ b/devtools/client/netmonitor/test/browser_net_reload-markers.js
@@ -7,17 +7,17 @@
  * Tests if the empty-requests reload button works.
  */
 
 add_task(function* () {
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, windowRequire } = monitor.panelWin;
-  let { EVENTS } = windowRequire("devtools/client/netmonitor/events");
+  let { EVENTS } = windowRequire("devtools/client/netmonitor/constants");
   let button = document.querySelector("#requests-menu-reload-notice-button");
   button.click();
 
   let markers = [];
 
   monitor.panelWin.on(EVENTS.TIMELINE_EVENT, (_, marker) => {
     markers.push(marker);
   });
--- a/devtools/client/netmonitor/test/browser_net_req-resp-bodies.js
+++ b/devtools/client/netmonitor/test/browser_net_req-resp-bodies.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 /**
  * Test if request and response body logging stays on after opening the console.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(JSON_LONG_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
   let {
     getDisplayedRequests,
--- a/devtools/client/netmonitor/test/browser_net_security-error.js
+++ b/devtools/client/netmonitor/test/browser_net_security-error.js
@@ -6,17 +6,17 @@
 /**
  * Test that Security details tab shows an error message with broken connections.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
-  let { EVENTS } = windowRequire("devtools/client/netmonitor/events");
+  let { EVENTS } = windowRequire("devtools/client/netmonitor/constants");
 
   gStore.dispatch(Actions.batchEnable(false));
 
   info("Requesting a resource that has a certificate problem.");
 
   let wait = waitForSecurityBrokenNetworkEvent();
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests(1, "https://nocert.example.com");
--- a/devtools/client/netmonitor/test/browser_net_simple-request-data.js
+++ b/devtools/client/netmonitor/test/browser_net_simple-request-data.js
@@ -3,24 +3,24 @@
 
 "use strict";
 
 /**
  * Tests if requests render correct information in the menu UI.
  */
 
 function test() {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   initNetMonitor(SIMPLE_SJS).then(({ tab, monitor }) => {
     info("Starting test... ");
 
     let { document, gStore, windowRequire } = monitor.panelWin;
     let Actions = windowRequire("devtools/client/netmonitor/actions/index");
-    let { EVENTS } = windowRequire("devtools/client/netmonitor/events");
+    let { EVENTS } = windowRequire("devtools/client/netmonitor/constants");
     let {
       getDisplayedRequests,
       getSelectedRequest,
       getSortedRequests,
     } = windowRequire("devtools/client/netmonitor/selectors/index");
 
     gStore.dispatch(Actions.batchEnable(false));
 
--- a/devtools/client/netmonitor/test/browser_net_simple-request-details.js
+++ b/devtools/client/netmonitor/test/browser_net_simple-request-details.js
@@ -3,24 +3,24 @@
 
 "use strict";
 
 /**
  * Tests if requests render correct information in the details UI.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(SIMPLE_SJS);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
-  let { EVENTS } = windowRequire("devtools/client/netmonitor/events");
+  let { EVENTS } = windowRequire("devtools/client/netmonitor/constants");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/selectors/index");
 
   gStore.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
--- a/devtools/client/netmonitor/test/browser_net_simple-request.js
+++ b/devtools/client/netmonitor/test/browser_net_simple-request.js
@@ -13,17 +13,17 @@
  * 4) Number of requests displayed
  */
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
-  let { EVENTS } = windowRequire("devtools/client/netmonitor/events");
+  let { EVENTS } = windowRequire("devtools/client/netmonitor/constants");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/selectors/index");
 
   gStore.dispatch(Actions.batchEnable(false));
 
   is(document.querySelector(".network-details-panel-toggle").hasAttribute("disabled"),
--- a/devtools/client/netmonitor/test/browser_net_sort-01.js
+++ b/devtools/client/netmonitor/test/browser_net_sort-01.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 /**
  * Test if sorting columns in the network table works correctly with new requests.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { monitor } = yield initNetMonitor(SORTING_URL);
   info("Starting test... ");
 
   // It seems that this test may be slow on debug builds. This could be because
   // of the heavy dom manipulation associated with sorting.
   requestLongerTimeout(2);
 
--- a/devtools/client/netmonitor/test/browser_net_sort-02.js
+++ b/devtools/client/netmonitor/test/browser_net_sort-02.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 /**
  * Test if sorting columns in the network table works correctly.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { monitor } = yield initNetMonitor(SORTING_URL);
   info("Starting test... ");
 
   // It seems that this test may be slow on debug builds. This could be because
   // of the heavy dom manipulation associated with sorting.
   requestLongerTimeout(2);
 
--- a/devtools/client/netmonitor/test/browser_net_status-codes.js
+++ b/devtools/client/netmonitor/test/browser_net_status-codes.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 /**
  * Tests if requests display the correct status code and text in the UI.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(STATUS_CODES_URL);
 
   info("Starting test... ");
 
   let { document, gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
   let {
--- a/devtools/client/netmonitor/test/browser_net_throttle.js
+++ b/devtools/client/netmonitor/test/browser_net_throttle.js
@@ -12,17 +12,17 @@ add_task(function* () {
 
 function* throttleTest(actuallyThrottle) {
   requestLongerTimeout(2);
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   let { document, gStore, windowRequire, NetMonitorController } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
   let { ACTIVITY_TYPE } = windowRequire("devtools/client/netmonitor/constants");
-  let { EVENTS } = windowRequire("devtools/client/netmonitor/events");
+  let { EVENTS } = windowRequire("devtools/client/netmonitor/constants");
   let {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/selectors/index");
 
   info("Starting test... (actuallyThrottle = " + actuallyThrottle + ")");
 
   // When throttling, must be smaller than the length of the content
   // of SIMPLE_URL in bytes.
--- a/devtools/client/netmonitor/test/browser_net_timeline_ticks.js
+++ b/devtools/client/netmonitor/test/browser_net_timeline_ticks.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 /**
  * Tests if timeline correctly displays interval divisions.
  */
 
 add_task(function* () {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { $, $all, NetMonitorView, NetMonitorController } = monitor.panelWin;
   let { RequestsMenu } = NetMonitorView;
 
   // Disable transferred size column support for this test.
--- a/devtools/client/netmonitor/test/browser_net_truncate.js
+++ b/devtools/client/netmonitor/test/browser_net_truncate.js
@@ -3,30 +3,30 @@
 
 "use strict";
 
 /**
  * Verifies that truncated response bodies still have the correct reported size.
  */
 
 function test() {
-  let { L10N } = require("devtools/client/netmonitor/l10n");
+  let { L10N } = require("devtools/client/netmonitor/utils/l10n");
   const { RESPONSE_BODY_LIMIT } = require("devtools/shared/webconsole/network-monitor");
 
   const URL = EXAMPLE_URL + "sjs_truncate-test-server.sjs?limit=" + RESPONSE_BODY_LIMIT;
 
   // Another slow test on Linux debug.
   requestLongerTimeout(2);
 
   initNetMonitor(URL).then(({ tab, monitor }) => {
     info("Starting test... ");
 
     let { document, gStore, windowRequire } = monitor.panelWin;
     let Actions = windowRequire("devtools/client/netmonitor/actions/index");
-    let { EVENTS } = windowRequire("devtools/client/netmonitor/events");
+    let { EVENTS } = windowRequire("devtools/client/netmonitor/constants");
     let {
       getDisplayedRequests,
       getSortedRequests,
     } = windowRequire("devtools/client/netmonitor/selectors/index");
 
     gStore.dispatch(Actions.batchEnable(false));
 
     waitForNetworkEvents(monitor, 1)
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -5,24 +5,24 @@
 
 "use strict";
 
 // shared-head.js handles imports, constants, and utility functions
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
   this);
 
-const { EVENTS } = require("devtools/client/netmonitor/events");
+const { EVENTS } = require("devtools/client/netmonitor/constants");
 var { Toolbox } = require("devtools/client/framework/toolbox");
 const {
   decodeUnicodeUrl,
   getUrlBaseName,
   getUrlQuery,
   getUrlHost,
-} = require("devtools/client/netmonitor/request-utils");
+} = require("devtools/client/netmonitor/utils/request-utils");
 
 const EXAMPLE_URL = "http://example.com/browser/devtools/client/netmonitor/test/";
 const HTTPS_EXAMPLE_URL = "https://example.com/browser/devtools/client/netmonitor/test/";
 
 const API_CALLS_URL = EXAMPLE_URL + "html_api-calls-test-page.html";
 const SIMPLE_URL = EXAMPLE_URL + "html_simple-test-page.html";
 const NAVIGATE_URL = EXAMPLE_URL + "html_navigate-test-page.html";
 const CONTENT_TYPE_URL = EXAMPLE_URL + "html_content-type-test-page.html";
rename from devtools/client/netmonitor/filter-predicates.js
rename to devtools/client/netmonitor/utils/filter-predicates.js
--- a/devtools/client/netmonitor/utils/format-utils.js
+++ b/devtools/client/netmonitor/utils/format-utils.js
@@ -1,15 +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 { L10N } = require("../l10n");
+const { L10N } = require("./l10n");
 
 // Constants for formatting bytes.
 const BYTES_IN_KB = 1024;
 const BYTES_IN_MB = Math.pow(BYTES_IN_KB, 2);
 const BYTES_IN_GB = Math.pow(BYTES_IN_KB, 3);
 const MAX_BYTES_SIZE = 1000;
 const MAX_KB_SIZE = 1000 * BYTES_IN_KB;
 const MAX_MB_SIZE = 1000 * BYTES_IN_MB;
rename from devtools/client/netmonitor/l10n.js
rename to devtools/client/netmonitor/utils/l10n.js
--- a/devtools/client/netmonitor/utils/moz.build
+++ b/devtools/client/netmonitor/utils/moz.build
@@ -1,9 +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(
+    'filter-predicates.js',
     'format-utils.js',
+    'l10n.js',
     'mdn-utils.js',
+    'prefs.js',
+    'request-utils.js',
+    'sort-predicates.js',
 )
rename from devtools/client/netmonitor/prefs.js
rename to devtools/client/netmonitor/utils/prefs.js
rename from devtools/client/netmonitor/request-utils.js
rename to devtools/client/netmonitor/utils/request-utils.js
rename from devtools/client/netmonitor/sort-predicates.js
rename to devtools/client/netmonitor/utils/sort-predicates.js
--- a/devtools/client/shared/components/reps/load-reps.js
+++ b/devtools/client/shared/components/reps/load-reps.js
@@ -9,35 +9,38 @@
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   let REPS;
   let MODE;
   let createFactories;
   let parseURLEncodedText;
   let parseURLParams;
+  let getSelectableInInspectorGrips;
 
   // useRepsBundle hardcoded to true to use the bundle from github. Switch back to rely
   // on individual reps files by flipping it to false.
   let useRepsBundle = true;
   if (useRepsBundle) {
     const bundle = require("devtools/client/shared/components/reps/reps");
     REPS = bundle.REPS;
     MODE = bundle.MODE;
     createFactories = bundle.createFactories;
     parseURLEncodedText = bundle.parseURLEncodedText;
     parseURLParams = bundle.parseURLParams;
+    getSelectableInInspectorGrips = bundle.getSelectableInInspectorGrips;
   } else {
     // Commenting out all requires, otherwise requirejs will agressively load all
     // dependencies when loading load-reps.js, which will fail because files have been
     // removed.
     // ({ createFactories, parseURLEncodedText, parseURLParams } =
     //   require("devtools/client/shared/components/reps/rep-utils"));
     // REPS = require("devtools/client/shared/components/reps/rep").REPS;
     // MODE = require("devtools/client/shared/components/reps/constants").MODE;
   }
 
   exports.REPS = REPS;
   exports.MODE = MODE;
   exports.createFactories = createFactories;
   exports.parseURLEncodedText = parseURLEncodedText;
   exports.parseURLParams = parseURLParams;
+  exports.getSelectableInInspectorGrips = getSelectableInInspectorGrips;
 });
--- a/devtools/client/shared/components/reps/reps.css
+++ b/devtools/client/shared/components/reps/reps.css
@@ -176,8 +176,26 @@
   font-weight: normal;
   white-space: pre-wrap;
 }
 
 .theme-dark .caption,
 .theme-light .caption {
   font-weight: normal;
 }
+
+/******************************************************************************/
+/* Open DOMNode in inspector button */
+
+.open-inspector svg {
+  fill: rgb(215, 215, 215);
+  height: 16px;
+  width: 16px;
+  margin-left: .25em;
+  cursor: pointer;
+  vertical-align: middle;
+}
+
+.objectBox-node:hover .open-inspector svg,
+.objectBox-textNode:hover .open-inspector svg,
+.open-inspector svg:hover {
+  fill: rgb(65, 175, 230);
+}
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -2,17 +2,17 @@
 	if(typeof exports === 'object' && typeof module === 'object')
 		module.exports = factory(require("devtools/client/shared/vendor/react"));
 	else if(typeof define === 'function' && define.amd)
 		define(["devtools/client/shared/vendor/react"], factory);
 	else {
 		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react")) : factory(root["devtools/client/shared/vendor/react"]);
 		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
 	}
-})(this, function(__WEBPACK_EXTERNAL_MODULE_1__) {
+})(this, function(__WEBPACK_EXTERNAL_MODULE_3__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
 /******/
 /******/ 	// The require function
 /******/ 	function __webpack_require__(moduleId) {
 /******/
 /******/ 		// Check if module is in cache
@@ -49,88 +49,86 @@ return /******/ (function(modules) { // 
 /******/ 	// Load entry module and return exports
 /******/ 	return __webpack_require__(0);
 /******/ })
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ function(module, exports, __webpack_require__) {
 
-	const React = __webpack_require__(1);
-	
-	const { MODE } = __webpack_require__(2);
-	const { REPS } = __webpack_require__(3);
-	const { createFactories, parseURLEncodedText, parseURLParams } = __webpack_require__(4);
+	const { MODE } = __webpack_require__(1);
+	const { REPS } = __webpack_require__(2);
+	const {
+	  createFactories,
+	  parseURLEncodedText,
+	  parseURLParams,
+	  getSelectableInInspectorGrips
+	} = __webpack_require__(4);
 	
 	module.exports = {
 	  REPS,
 	  MODE,
 	  createFactories,
 	  parseURLEncodedText,
-	  parseURLParams
+	  parseURLParams,
+	  getSelectableInInspectorGrips
 	};
 
 /***/ },
 /* 1 */
 /***/ function(module, exports) {
 
-	module.exports = __WEBPACK_EXTERNAL_MODULE_1__;
-
-/***/ },
-/* 2 */
-/***/ function(module, exports) {
-
 	module.exports = {
 	  MODE: {
 	    TINY: Symbol("TINY"),
 	    SHORT: Symbol("SHORT"),
 	    LONG: Symbol("LONG")
 	  }
 	};
 
 /***/ },
-/* 3 */
+/* 2 */
 /***/ function(module, exports, __webpack_require__) {
 
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	const { isGrip } = __webpack_require__(4);
-	const { MODE } = __webpack_require__(2);
+	const { MODE } = __webpack_require__(1);
 	
 	// Load all existing rep templates
-	const Undefined = __webpack_require__(5);
-	const Null = __webpack_require__(6);
-	const StringRep = __webpack_require__(7);
-	const LongStringRep = __webpack_require__(8);
-	const Number = __webpack_require__(9);
-	const ArrayRep = __webpack_require__(10);
-	const Obj = __webpack_require__(12);
-	const SymbolRep = __webpack_require__(15);
-	const InfinityRep = __webpack_require__(16);
-	const NaNRep = __webpack_require__(17);
+	const Undefined = __webpack_require__(6);
+	const Null = __webpack_require__(7);
+	const StringRep = __webpack_require__(8);
+	const LongStringRep = __webpack_require__(9);
+	const Number = __webpack_require__(10);
+	const ArrayRep = __webpack_require__(11);
+	const Obj = __webpack_require__(13);
+	const SymbolRep = __webpack_require__(16);
+	const InfinityRep = __webpack_require__(17);
+	const NaNRep = __webpack_require__(18);
 	
 	// DOM types (grips)
-	const Attribute = __webpack_require__(18);
-	const DateTime = __webpack_require__(19);
-	const Document = __webpack_require__(20);
-	const Event = __webpack_require__(21);
-	const Func = __webpack_require__(22);
-	const PromiseRep = __webpack_require__(23);
-	const RegExp = __webpack_require__(24);
-	const StyleSheet = __webpack_require__(25);
-	const CommentNode = __webpack_require__(26);
+	const Attribute = __webpack_require__(19);
+	const DateTime = __webpack_require__(20);
+	const Document = __webpack_require__(21);
+	const Event = __webpack_require__(22);
+	const Func = __webpack_require__(23);
+	const PromiseRep = __webpack_require__(24);
+	const RegExp = __webpack_require__(25);
+	const StyleSheet = __webpack_require__(26);
+	const CommentNode = __webpack_require__(27);
 	const ElementNode = __webpack_require__(28);
-	const TextNode = __webpack_require__(29);
-	const ErrorRep = __webpack_require__(30);
-	const Window = __webpack_require__(31);
-	const ObjectWithText = __webpack_require__(32);
-	const ObjectWithURL = __webpack_require__(33);
-	const GripArray = __webpack_require__(34);
-	const GripMap = __webpack_require__(35);
-	const Grip = __webpack_require__(14);
+	const TextNode = __webpack_require__(32);
+	const ErrorRep = __webpack_require__(33);
+	const Window = __webpack_require__(34);
+	const ObjectWithText = __webpack_require__(35);
+	const ObjectWithURL = __webpack_require__(36);
+	const GripArray = __webpack_require__(37);
+	const GripMap = __webpack_require__(38);
+	const Grip = __webpack_require__(15);
 	
 	// List of all registered template.
 	// XXX there should be a way for extensions to register a new
 	// or modify an existing rep.
 	let reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, LongStringRep, Func, PromiseRep, ArrayRep, Document, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep];
 	
 	/**
 	 * Generic rep that is using for rendering native JS types or an object.
@@ -227,21 +225,30 @@ return /******/ (function(modules) { // 
 	    SymbolRep,
 	    TextNode,
 	    Undefined,
 	    Window
 	  }
 	};
 
 /***/ },
+/* 3 */
+/***/ function(module, exports) {
+
+	module.exports = __WEBPACK_EXTERNAL_MODULE_3__;
+
+/***/ },
 /* 4 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// Dependencies
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
+	
+	// Utils
+	const nodeConstants = __webpack_require__(5);
 	
 	/**
 	 * Create React factories for given arguments.
 	 * Example:
 	 *   const { Rep } = createFactories(require("./rep"));
 	 */
 	function createFactories(args) {
 	  let result = {};
@@ -386,35 +393,153 @@ return /******/ (function(modules) { // 
 	        title: "This object could not be rendered, " + "please file a bug on bugzilla.mozilla.org"
 	      },
 	      /* Labels have to be hardcoded for reps, see Bug 1317038. */
 	      "Invalid object");
 	    }
 	  };
 	}
 	
+	/**
+	 * Get an array of all the items from the grip in parameter (including the grip itself)
+	 * which can be selected in the inspector.
+	 *
+	 * @param {Object} Grip
+	 * @return {Array} Flat array of the grips which can be selected in the inspector
+	 */
+	function getSelectableInInspectorGrips(grip) {
+	  let grips = new Set(getFlattenedGrips([grip]));
+	  return [...grips].filter(isGripSelectableInInspector);
+	}
+	
+	/**
+	 * Indicate if a Grip can be selected in the inspector,
+	 * i.e. if it represents a node element.
+	 *
+	 * @param {Object} Grip
+	 * @return {Boolean}
+	 */
+	function isGripSelectableInInspector(grip) {
+	  return grip && typeof grip === "object" && grip.preview && [nodeConstants.TEXT_NODE, nodeConstants.ELEMENT_NODE].includes(grip.preview.nodeType);
+	}
+	
+	/**
+	 * Get a flat array of all the grips and their preview items.
+	 *
+	 * @param {Array} Grips
+	 * @return {Array} Flat array of the grips and their preview items
+	 */
+	function getFlattenedGrips(grips) {
+	  return grips.reduce((res, grip) => {
+	    let previewItems = getGripPreviewItems(grip);
+	    let flatPreviewItems = previewItems.length > 0 ? getFlattenedGrips(previewItems) : [];
+	
+	    return [...res, grip, ...flatPreviewItems];
+	  }, []);
+	}
+	
+	/**
+	 * Get preview items from a Grip.
+	 *
+	 * @param {Object} Grip from which we want the preview items
+	 * @return {Array} Array of the preview items of the grip, or an empty array
+	 *                 if the grip does not have preview items
+	 */
+	function getGripPreviewItems(grip) {
+	  if (!grip) {
+	    return [];
+	  }
+	
+	  // Promise resolved value Grip
+	  if (grip.promiseState && grip.promiseState.value) {
+	    return [grip.promiseState.value];
+	  }
+	
+	  // Array Grip
+	  if (grip.preview && grip.preview.items) {
+	    return grip.preview.items;
+	  }
+	
+	  // Node Grip
+	  if (grip.preview && grip.preview.childNodes) {
+	    return grip.preview.childNodes;
+	  }
+	
+	  // Set or Map Grip
+	  if (grip.preview && grip.preview.entries) {
+	    return grip.preview.entries.reduce((res, entry) => res.concat(entry), []);
+	  }
+	
+	  // Event Grip
+	  if (grip.preview && grip.preview.target) {
+	    return [grip.preview.target];
+	  }
+	
+	  // Generic Grip
+	  if (grip.preview && grip.preview.ownProperties) {
+	    let propertiesValues = Object.values(grip.preview.ownProperties).map(property => property.value || property);
+	
+	    // ArrayBuffer Grip
+	    if (grip.preview.safeGetterValues) {
+	      propertiesValues = propertiesValues.concat(Object.values(grip.preview.safeGetterValues).map(property => property.getterValue || property));
+	    }
+	
+	    return propertiesValues;
+	  }
+	
+	  return [];
+	}
+	
 	module.exports = {
 	  createFactories,
 	  isGrip,
 	  cropString,
 	  sanitizeString,
 	  wrapRender,
 	  cropMultipleLines,
 	  parseURLParams,
 	  parseURLEncodedText,
 	  getFileName,
-	  getURLDisplayString
+	  getURLDisplayString,
+	  getSelectableInInspectorGrips
 	};
 
 /***/ },
 /* 5 */
+/***/ function(module, exports) {
+
+	module.exports = {
+	  ELEMENT_NODE: 1,
+	  ATTRIBUTE_NODE: 2,
+	  TEXT_NODE: 3,
+	  CDATA_SECTION_NODE: 4,
+	  ENTITY_REFERENCE_NODE: 5,
+	  ENTITY_NODE: 6,
+	  PROCESSING_INSTRUCTION_NODE: 7,
+	  COMMENT_NODE: 8,
+	  DOCUMENT_NODE: 9,
+	  DOCUMENT_TYPE_NODE: 10,
+	  DOCUMENT_FRAGMENT_NODE: 11,
+	  NOTATION_NODE: 12,
+	
+	  // DocumentPosition
+	  DOCUMENT_POSITION_DISCONNECTED: 0x01,
+	  DOCUMENT_POSITION_PRECEDING: 0x02,
+	  DOCUMENT_POSITION_FOLLOWING: 0x04,
+	  DOCUMENT_POSITION_CONTAINS: 0x08,
+	  DOCUMENT_POSITION_CONTAINED_BY: 0x10,
+	  DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 0x20
+	};
+
+/***/ },
+/* 6 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// Dependencies
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	const { wrapRender } = __webpack_require__(4);
 	
 	// Shortcuts
 	const { span } = React.DOM;
 	
 	/**
 	 * Renders undefined value
@@ -438,21 +563,21 @@ return /******/ (function(modules) { // 
 	// Exports from this module
 	
 	module.exports = {
 	  rep: Undefined,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 6 */
+/* 7 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// Dependencies
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	const { wrapRender } = __webpack_require__(4);
 	
 	// Shortcuts
 	const { span } = React.DOM;
 	
 	/**
 	 * Renders null value
@@ -476,21 +601,21 @@ return /******/ (function(modules) { // 
 	// Exports from this module
 	
 	module.exports = {
 	  rep: Null,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 7 */
+/* 8 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// Dependencies
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	const {
 	  cropString,
 	  wrapRender
 	} = __webpack_require__(4);
 	
 	// Shortcuts
 	const { span } = React.DOM;
@@ -544,21 +669,21 @@ return /******/ (function(modules) { // 
 	// Exports from this module
 	
 	module.exports = {
 	  rep: StringRep,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 8 */
+/* 9 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// Dependencies
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	const {
 	  sanitizeString,
 	  isGrip,
 	  wrapRender
 	} = __webpack_require__(4);
 	// Shortcuts
 	const { span } = React.DOM;
 	
@@ -616,21 +741,21 @@ return /******/ (function(modules) { // 
 	
 	// Exports from this module
 	module.exports = {
 	  rep: LongStringRep,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 9 */
+/* 10 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// Dependencies
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	const { wrapRender } = __webpack_require__(4);
 	
 	// Shortcuts
 	const { span } = React.DOM;
 	
 	/**
 	 * Renders a number
@@ -662,27 +787,27 @@ return /******/ (function(modules) { // 
 	// Exports from this module
 	
 	module.exports = {
 	  rep: Number,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 10 */
+/* 11 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// Dependencies
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	const {
 	  createFactories,
 	  wrapRender
 	} = __webpack_require__(4);
-	const Caption = React.createFactory(__webpack_require__(11));
-	const { MODE } = __webpack_require__(2);
+	const Caption = React.createFactory(__webpack_require__(12));
+	const { MODE } = __webpack_require__(1);
 	
 	const ModePropType = React.PropTypes.oneOf(
 	// @TODO Change this to Object.values once it's supported in Node's version of V8
 	Object.keys(MODE).map(key => MODE[key]));
 	
 	// Shortcuts
 	const DOM = React.DOM;
 	
@@ -829,17 +954,17 @@ return /******/ (function(modules) { // 
 	
 	  propTypes: {
 	    object: React.PropTypes.any.isRequired,
 	    delim: React.PropTypes.string.isRequired,
 	    mode: ModePropType
 	  },
 	
 	  render: wrapRender(function () {
-	    const { Rep } = createFactories(__webpack_require__(3));
+	    const { Rep } = createFactories(__webpack_require__(2));
 	
 	    let object = this.props.object;
 	    let delim = this.props.delim;
 	    let mode = this.props.mode;
 	    return DOM.span({}, Rep({ object: object, mode: mode }), delim);
 	  })
 	}));
 	
@@ -849,21 +974,21 @@ return /******/ (function(modules) { // 
 	
 	// Exports from this module
 	module.exports = {
 	  rep: ArrayRep,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 11 */
+/* 12 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// Dependencies
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	const DOM = React.DOM;
 	
 	const { wrapRender } = __webpack_require__(4);
 	
 	/**
 	 * Renders a caption. This template is used by other components
 	 * that needs to distinguish between a simple text/value and a label.
 	 */
@@ -878,27 +1003,27 @@ return /******/ (function(modules) { // 
 	    return DOM.span({ "className": "caption" }, this.props.object);
 	  })
 	});
 	
 	// Exports from this module
 	module.exports = Caption;
 
 /***/ },
-/* 12 */
+/* 13 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// Dependencies
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	const {
 	  wrapRender
 	} = __webpack_require__(4);
-	const Caption = React.createFactory(__webpack_require__(11));
-	const PropRep = React.createFactory(__webpack_require__(13));
-	const { MODE } = __webpack_require__(2);
+	const Caption = React.createFactory(__webpack_require__(12));
+	const PropRep = React.createFactory(__webpack_require__(14));
+	const { MODE } = __webpack_require__(1);
 	// Shortcuts
 	const { span } = React.DOM;
 	/**
 	 * Renders an object. An object is represented by a list of its
 	 * properties enclosed in curly brackets.
 	 */
 	const Obj = React.createClass({
 	  displayName: "Obj",
@@ -1038,26 +1163,26 @@ return /******/ (function(modules) { // 
 	
 	// Exports from this module
 	module.exports = {
 	  rep: Obj,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 13 */
+/* 14 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// Dependencies
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	const {
 	  createFactories,
 	  wrapRender
 	} = __webpack_require__(4);
-	const { MODE } = __webpack_require__(2);
+	const { MODE } = __webpack_require__(1);
 	// Shortcuts
 	const { span } = React.DOM;
 	
 	/**
 	 * Property for Obj (local JS objects), Grip (remote JS objects)
 	 * and GripMap (remote JS maps and weakmaps) reps.
 	 * It's used to render object properties.
 	 */
@@ -1068,63 +1193,72 @@ return /******/ (function(modules) { // 
 	    // Property name.
 	    name: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.object]).isRequired,
 	    // Equal character rendered between property name and value.
 	    equal: React.PropTypes.string,
 	    // Delimiter character used to separate individual properties.
 	    delim: React.PropTypes.string,
 	    // @TODO Change this to Object.values once it's supported in Node's version of V8
 	    mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
-	    objectLink: React.PropTypes.func
+	    objectLink: React.PropTypes.func,
+	    attachedActorIds: React.PropTypes.array,
+	    onDOMNodeMouseOver: React.PropTypes.func,
+	    onDOMNodeMouseOut: React.PropTypes.func,
+	    onInspectIconClick: React.PropTypes.func
 	  },
 	
 	  render: wrapRender(function () {
-	    const Grip = __webpack_require__(14);
-	    let { Rep } = createFactories(__webpack_require__(3));
+	    const Grip = __webpack_require__(15);
+	    let { Rep } = createFactories(__webpack_require__(2));
+	    let {
+	      name,
+	      mode,
+	      equal,
+	      delim
+	    } = this.props;
 	
 	    let key;
 	    // The key can be a simple string, for plain objects,
 	    // or another object for maps and weakmaps.
 	    if (typeof this.props.name === "string") {
 	      key = span({ "className": "nodeName" }, this.props.name);
 	    } else {
-	      key = Rep({
-	        object: this.props.name,
-	        mode: this.props.mode || MODE.TINY,
-	        defaultRep: Grip,
-	        objectLink: this.props.objectLink
-	      });
+	      key = Rep(Object.assign({}, this.props, {
+	        object: name,
+	        mode: mode || MODE.TINY,
+	        defaultRep: Grip
+	      }));
 	    }
 	
 	    return span({}, key, span({
 	      "className": "objectEqual"
-	    }, this.props.equal), Rep(this.props), span({
+	    }, equal), Rep(Object.assign({}, this.props)), span({
 	      "className": "objectComma"
-	    }, this.props.delim));
+	    }, delim));
 	  })
 	});
 	
 	// Exports from this module
 	module.exports = PropRep;
 
 /***/ },
-/* 14 */
+/* 15 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// ReactJS
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	// Dependencies
 	const {
 	  createFactories,
 	  isGrip,
 	  wrapRender
 	} = __webpack_require__(4);
-	const Caption = React.createFactory(__webpack_require__(11));
-	const PropRep = React.createFactory(__webpack_require__(13));
-	const { MODE } = __webpack_require__(2);
+	const Caption = React.createFactory(__webpack_require__(12));
+	const PropRep = React.createFactory(__webpack_require__(14));
+	const { MODE } = __webpack_require__(1);
 	// Shortcuts
 	const { span } = React.DOM;
 	
 	/**
 	 * Renders generic grip. Grip is client representation
 	 * of remote JS object and is used as an input object
 	 * for this rep component.
 	 */
@@ -1132,17 +1266,21 @@ return /******/ (function(modules) { // 
 	  displayName: "Grip",
 	
 	  propTypes: {
 	    object: React.PropTypes.object.isRequired,
 	    // @TODO Change this to Object.values once it's supported in Node's version of V8
 	    mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
 	    isInterestingProp: React.PropTypes.func,
 	    title: React.PropTypes.string,
-	    objectLink: React.PropTypes.func
+	    objectLink: React.PropTypes.func,
+	    attachedActorIds: React.PropTypes.array,
+	    onDOMNodeMouseOver: React.PropTypes.func,
+	    onDOMNodeMouseOut: React.PropTypes.func,
+	    onInspectIconClick: React.PropTypes.func
 	  },
 	
 	  getTitle: function (object) {
 	    let title = this.props.title || object.class || "Object";
 	    if (this.props.objectLink) {
 	      return this.props.objectLink({
 	        object: object
 	      }, title);
@@ -1157,17 +1295,17 @@ return /******/ (function(modules) { // 
 	    } catch (err) {
 	      console.error(err);
 	    }
 	    return [];
 	  },
 	
 	  propIterator: function (object, max) {
 	    if (object.preview && Object.keys(object.preview).includes("wrappedValue")) {
-	      const { Rep } = createFactories(__webpack_require__(3));
+	      const { Rep } = createFactories(__webpack_require__(2));
 	
 	      return [Rep({
 	        object: object.preview.wrappedValue,
 	        mode: this.props.mode || MODE.TINY,
 	        defaultRep: Grip
 	      })];
 	    }
 	
@@ -1229,17 +1367,19 @@ return /******/ (function(modules) { // 
 	      let value = this.getPropValue(properties[name]);
 	
 	      props.push(PropRep(Object.assign({}, this.props, {
 	        mode: MODE.TINY,
 	        name: name,
 	        object: value,
 	        equal: ": ",
 	        delim: i !== indexes.length - 1 || truncate ? ", " : "",
-	        defaultRep: Grip
+	        defaultRep: Grip,
+	        // Do not propagate title to properties reps
+	        title: undefined
 	      })));
 	    });
 	
 	    return props;
 	  },
 	
 	  /**
 	   * Get the indexes of props in the object.
@@ -1330,21 +1470,21 @@ return /******/ (function(modules) { // 
 	  rep: GripRep,
 	  supportsObject: supportsObject
 	};
 	
 	// Exports from this module
 	module.exports = Grip;
 
 /***/ },
-/* 15 */
+/* 16 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// Dependencies
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	const { wrapRender } = __webpack_require__(4);
 	
 	// Shortcuts
 	const { span } = React.DOM;
 	
 	/**
 	 * Renders a symbol.
@@ -1370,21 +1510,21 @@ return /******/ (function(modules) { // 
 	
 	// Exports from this module
 	module.exports = {
 	  rep: SymbolRep,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 16 */
+/* 17 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// Dependencies
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	const { wrapRender } = __webpack_require__(4);
 	
 	// Shortcuts
 	const { span } = React.DOM;
 	
 	/**
 	 * Renders a Infinity object
@@ -1407,21 +1547,21 @@ return /******/ (function(modules) { // 
 	
 	// Exports from this module
 	module.exports = {
 	  rep: InfinityRep,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 17 */
+/* 18 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// Dependencies
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	const { wrapRender } = __webpack_require__(4);
 	
 	// Shortcuts
 	const { span } = React.DOM;
 	
 	/**
 	 * Renders a NaN object
@@ -1440,29 +1580,29 @@ return /******/ (function(modules) { // 
 	
 	// Exports from this module
 	module.exports = {
 	  rep: NaNRep,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 18 */
+/* 19 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// ReactJS
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	// Reps
 	const {
 	  createFactories,
 	  isGrip,
 	  wrapRender
 	} = __webpack_require__(4);
-	const StringRep = __webpack_require__(7);
+	const StringRep = __webpack_require__(8);
 	
 	// Shortcuts
 	const { span } = React.DOM;
 	const { rep: StringRepFactory } = createFactories(StringRep);
 	
 	/**
 	 * Renders DOM attribute
 	 */
@@ -1498,21 +1638,21 @@ return /******/ (function(modules) { // 
 	}
 	
 	module.exports = {
 	  rep: Attribute,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 19 */
+/* 20 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// ReactJS
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	// Reps
 	const {
 	  isGrip,
 	  wrapRender
 	} = __webpack_require__(4);
 	
 	// Shortcuts
@@ -1563,21 +1703,21 @@ return /******/ (function(modules) { // 
 	
 	// Exports from this module
 	module.exports = {
 	  rep: DateTime,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 20 */
+/* 21 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// ReactJS
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	// Reps
 	const {
 	  isGrip,
 	  getURLDisplayString,
 	  wrapRender
 	} = __webpack_require__(4);
 	
@@ -1632,95 +1772,104 @@ return /******/ (function(modules) { // 
 	
 	// Exports from this module
 	module.exports = {
 	  rep: Document,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 21 */
+/* 22 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// ReactJS
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	// Reps
 	const {
 	  createFactories,
 	  isGrip,
 	  wrapRender
 	} = __webpack_require__(4);
-	const { rep } = createFactories(__webpack_require__(14));
+	
+	const { rep } = createFactories(__webpack_require__(15));
+	const { MODE } = __webpack_require__(1);
 	
 	/**
 	 * Renders DOM event objects.
 	 */
 	let Event = React.createClass({
 	  displayName: "event",
 	
 	  propTypes: {
-	    object: React.PropTypes.object.isRequired
+	    object: React.PropTypes.object.isRequired,
+	    objectLink: React.PropTypes.func,
+	    // @TODO Change this to Object.values once it's supported in Node's version of V8
+	    mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+	    attachedActorIds: React.PropTypes.array,
+	    onDOMNodeMouseOver: React.PropTypes.func,
+	    onDOMNodeMouseOut: React.PropTypes.func,
+	    onInspectIconClick: React.PropTypes.func
 	  },
 	
 	  getTitle: function (props) {
 	    let preview = props.object.preview;
 	    let title = preview.type;
 	
 	    if (preview.eventKind == "key" && preview.modifiers && preview.modifiers.length) {
 	      title = `${title} ${preview.modifiers.join("-")}`;
 	    }
 	    return title;
 	  },
 	
 	  render: wrapRender(function () {
 	    // Use `Object.assign` to keep `this.props` without changes because:
 	    // 1. JSON.stringify/JSON.parse is slow.
 	    // 2. Immutable.js is planned for the future.
-	    let props = Object.assign({
+	    let gripProps = Object.assign({}, this.props, {
 	      title: this.getTitle(this.props)
-	    }, this.props);
-	    props.object = Object.assign({}, this.props.object);
-	    props.object.preview = Object.assign({}, this.props.object.preview);
-	
-	    props.object.preview.ownProperties = {};
-	    if (props.object.preview.target) {
-	      Object.assign(props.object.preview.ownProperties, {
-	        target: props.object.preview.target
+	    });
+	    gripProps.object = Object.assign({}, this.props.object);
+	    gripProps.object.preview = Object.assign({}, this.props.object.preview);
+	
+	    gripProps.object.preview.ownProperties = {};
+	    if (gripProps.object.preview.target) {
+	      Object.assign(gripProps.object.preview.ownProperties, {
+	        target: gripProps.object.preview.target
 	      });
 	    }
-	    Object.assign(props.object.preview.ownProperties, props.object.preview.properties);
-	
-	    delete props.object.preview.properties;
-	    props.object.ownPropertyLength = Object.keys(props.object.preview.ownProperties).length;
-	
-	    switch (props.object.class) {
+	    Object.assign(gripProps.object.preview.ownProperties, gripProps.object.preview.properties);
+	
+	    delete gripProps.object.preview.properties;
+	    gripProps.object.ownPropertyLength = Object.keys(gripProps.object.preview.ownProperties).length;
+	
+	    switch (gripProps.object.class) {
 	      case "MouseEvent":
-	        props.isInterestingProp = (type, value, name) => {
+	        gripProps.isInterestingProp = (type, value, name) => {
 	          return ["target", "clientX", "clientY", "layerX", "layerY"].includes(name);
 	        };
 	        break;
 	      case "KeyboardEvent":
-	        props.isInterestingProp = (type, value, name) => {
+	        gripProps.isInterestingProp = (type, value, name) => {
 	          return ["target", "key", "charCode", "keyCode"].includes(name);
 	        };
 	        break;
 	      case "MessageEvent":
-	        props.isInterestingProp = (type, value, name) => {
+	        gripProps.isInterestingProp = (type, value, name) => {
 	          return ["target", "isTrusted", "data"].includes(name);
 	        };
 	        break;
 	      default:
-	        props.isInterestingProp = (type, value, name) => {
+	        gripProps.isInterestingProp = (type, value, name) => {
 	          // We want to show the properties in the order they are declared.
-	          return Object.keys(props.object.preview.ownProperties).includes(name);
+	          return Object.keys(gripProps.object.preview.ownProperties).includes(name);
 	        };
 	    }
 	
-	    return rep(props);
+	    return rep(gripProps);
 	  })
 	});
 	
 	// Registration
 	
 	function supportsObject(grip, type) {
 	  if (!isGrip(grip)) {
 	    return false;
@@ -1731,21 +1880,21 @@ return /******/ (function(modules) { // 
 	
 	// Exports from this module
 	module.exports = {
 	  rep: Event,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 22 */
+/* 23 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// ReactJS
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	// Reps
 	const {
 	  isGrip,
 	  cropString,
 	  wrapRender
 	} = __webpack_require__(4);
 	
@@ -1810,44 +1959,48 @@ return /******/ (function(modules) { // 
 	// Exports from this module
 	
 	module.exports = {
 	  rep: Func,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 23 */
+/* 24 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// ReactJS
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	// Dependencies
 	const {
 	  createFactories,
 	  isGrip,
 	  wrapRender
 	} = __webpack_require__(4);
 	
-	const PropRep = React.createFactory(__webpack_require__(13));
-	const { MODE } = __webpack_require__(2);
+	const PropRep = React.createFactory(__webpack_require__(14));
+	const { MODE } = __webpack_require__(1);
 	// Shortcuts
 	const { span } = React.DOM;
 	
 	/**
 	 * Renders a DOM Promise object.
 	 */
 	const PromiseRep = React.createClass({
 	  displayName: "Promise",
 	
 	  propTypes: {
 	    object: React.PropTypes.object.isRequired,
 	    // @TODO Change this to Object.values once it's supported in Node's version of V8
 	    mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
-	    objectLink: React.PropTypes.func
+	    objectLink: React.PropTypes.func,
+	    attachedActorIds: React.PropTypes.array,
+	    onDOMNodeMouseOver: React.PropTypes.func,
+	    onDOMNodeMouseOut: React.PropTypes.func,
+	    onInspectIconClick: React.PropTypes.func
 	  },
 	
 	  getTitle: function (object) {
 	    const title = object.class;
 	    if (this.props.objectLink) {
 	      return this.props.objectLink({
 	        object: object
 	      }, title);
@@ -1857,33 +2010,34 @@ return /******/ (function(modules) { // 
 	
 	  getProps: function (promiseState) {
 	    const keys = ["state"];
 	    if (Object.keys(promiseState).includes("value")) {
 	      keys.push("value");
 	    }
 	
 	    return keys.map((key, i) => {
+	      let object = promiseState[key];
 	      return PropRep(Object.assign({}, this.props, {
 	        mode: MODE.TINY,
 	        name: `<${key}>`,
-	        object: promiseState[key],
+	        object,
 	        equal: ": ",
 	        delim: i < keys.length - 1 ? ", " : ""
 	      }));
 	    });
 	  },
 	
 	  render: wrapRender(function () {
 	    const object = this.props.object;
 	    const { promiseState } = object;
 	    let objectLink = this.props.objectLink || span;
 	
 	    if (this.props.mode === MODE.TINY) {
-	      let { Rep } = createFactories(__webpack_require__(3));
+	      let { Rep } = createFactories(__webpack_require__(2));
 	
 	      return span({ className: "objectBox objectBox-object" }, this.getTitle(object), objectLink({
 	        className: "objectLeftBrace",
 	        object: object
 	      }, " { "), Rep({ object: promiseState.state }), objectLink({
 	        className: "objectRightBrace",
 	        object: object
 	      }, " }"));
@@ -1910,21 +2064,21 @@ return /******/ (function(modules) { // 
 	
 	// Exports from this module
 	module.exports = {
 	  rep: PromiseRep,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 24 */
+/* 25 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// ReactJS
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	// Reps
 	const {
 	  isGrip,
 	  wrapRender
 	} = __webpack_require__(4);
 	
 	// Shortcuts
@@ -1968,21 +2122,21 @@ return /******/ (function(modules) { // 
 	
 	// Exports from this module
 	module.exports = {
 	  rep: RegExp,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 25 */
+/* 26 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// ReactJS
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	// Reps
 	const {
 	  isGrip,
 	  getURLDisplayString,
 	  wrapRender
 	} = __webpack_require__(4);
 	
@@ -2036,29 +2190,29 @@ return /******/ (function(modules) { // 
 	// Exports from this module
 	
 	module.exports = {
 	  rep: StyleSheet,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 26 */
+/* 27 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// Dependencies
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	const {
 	  isGrip,
 	  cropString,
 	  cropMultipleLines,
 	  wrapRender
 	} = __webpack_require__(4);
-	const { MODE } = __webpack_require__(2);
-	const nodeConstants = __webpack_require__(27);
+	const { MODE } = __webpack_require__(1);
+	const nodeConstants = __webpack_require__(5);
 	
 	// Shortcuts
 	const { span } = React.DOM;
 	
 	/**
 	 * Renders DOM comment node.
 	 */
 	const CommentNode = React.createClass({
@@ -2097,72 +2251,48 @@ return /******/ (function(modules) { // 
 	
 	// Exports from this module
 	module.exports = {
 	  rep: CommentNode,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 27 */
-/***/ function(module, exports) {
-
-	module.exports = {
-	  ELEMENT_NODE: 1,
-	  ATTRIBUTE_NODE: 2,
-	  TEXT_NODE: 3,
-	  CDATA_SECTION_NODE: 4,
-	  ENTITY_REFERENCE_NODE: 5,
-	  ENTITY_NODE: 6,
-	  PROCESSING_INSTRUCTION_NODE: 7,
-	  COMMENT_NODE: 8,
-	  DOCUMENT_NODE: 9,
-	  DOCUMENT_TYPE_NODE: 10,
-	  DOCUMENT_FRAGMENT_NODE: 11,
-	  NOTATION_NODE: 12,
-	
-	  // DocumentPosition
-	  DOCUMENT_POSITION_DISCONNECTED: 0x01,
-	  DOCUMENT_POSITION_PRECEDING: 0x02,
-	  DOCUMENT_POSITION_FOLLOWING: 0x04,
-	  DOCUMENT_POSITION_CONTAINS: 0x08,
-	  DOCUMENT_POSITION_CONTAINED_BY: 0x10,
-	  DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 0x20
-	};
-
-/***/ },
 /* 28 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// ReactJS
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	// Utils
 	const {
 	  isGrip,
 	  wrapRender
 	} = __webpack_require__(4);
-	const { MODE } = __webpack_require__(2);
-	const nodeConstants = __webpack_require__(27);
+	const { MODE } = __webpack_require__(1);
+	const nodeConstants = __webpack_require__(5);
+	const Svg = __webpack_require__(29);
 	
 	// Shortcuts
 	const { span } = React.DOM;
 	
 	/**
 	 * Renders DOM element node.
 	 */
 	const ElementNode = React.createClass({
 	  displayName: "ElementNode",
 	
 	  propTypes: {
 	    object: React.PropTypes.object.isRequired,
 	    // @TODO Change this to Object.values once it's supported in Node's version of V8
 	    mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+	    attachedActorIds: React.PropTypes.array,
 	    onDOMNodeMouseOver: React.PropTypes.func,
 	    onDOMNodeMouseOut: React.PropTypes.func,
+	    onInspectIconClick: React.PropTypes.func,
 	    objectLink: React.PropTypes.func
 	  },
 	
 	  getElements: function (grip, mode) {
 	    let { attributes, nodeName } = grip.preview;
 	    const nodeNameElement = span({
 	      className: "tag-name theme-fg-color3"
 	    }, nodeName);
@@ -2197,36 +2327,53 @@ return /******/ (function(modules) { // 
 	
 	    return ["<", nodeNameElement, ...attributeElements, ">"];
 	  },
 	
 	  render: wrapRender(function () {
 	    let {
 	      object,
 	      mode,
+	      attachedActorIds,
 	      onDOMNodeMouseOver,
-	      onDOMNodeMouseOut
+	      onDOMNodeMouseOut,
+	      onInspectIconClick
 	    } = this.props;
 	    let elements = this.getElements(object, mode);
 	    let objectLink = this.props.objectLink || span;
 	
+	    let isInTree = attachedActorIds ? attachedActorIds.includes(object.actor) : true;
+	
 	    let baseConfig = { className: "objectBox objectBox-node" };
-	    if (onDOMNodeMouseOver) {
-	      Object.assign(baseConfig, {
-	        onMouseOver: _ => onDOMNodeMouseOver(object)
-	      });
+	    let inspectIcon;
+	    if (isInTree) {
+	      if (onDOMNodeMouseOver) {
+	        Object.assign(baseConfig, {
+	          onMouseOver: _ => onDOMNodeMouseOver(object)
+	        });
+	      }
+	
+	      if (onDOMNodeMouseOut) {
+	        Object.assign(baseConfig, {
+	          onMouseOut: onDOMNodeMouseOut
+	        });
+	      }
+	
+	      if (onInspectIconClick) {
+	        inspectIcon = Svg("open-inspector", {
+	          element: "a",
+	          draggable: false,
+	          // TODO: Localize this with "openNodeInInspector" when Bug 1317038 lands
+	          title: "Click to select the node in the inspector",
+	          onClick: () => onInspectIconClick(object)
+	        });
+	      }
 	    }
 	
-	    if (onDOMNodeMouseOut) {
-	      Object.assign(baseConfig, {
-	        onMouseOut: onDOMNodeMouseOut
-	      });
-	    }
-	
-	    return objectLink({ object }, span(baseConfig, ...elements));
+	    return span(baseConfig, objectLink({ object }, ...elements), inspectIcon);
 	  })
 	});
 	
 	// Registration
 	function supportsObject(object, type) {
 	  if (!isGrip(object)) {
 	    return false;
 	  }
@@ -2238,43 +2385,231 @@ return /******/ (function(modules) { // 
 	  rep: ElementNode,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
 /* 29 */
 /***/ function(module, exports, __webpack_require__) {
 
+	const React = __webpack_require__(3);
+	const InlineSVG = __webpack_require__(30);
+	
+	const svg = {
+	  "open-inspector": __webpack_require__(31)
+	};
+	
+	module.exports = function (name, props) {
+	  // eslint-disable-line
+	  if (!svg[name]) {
+	    throw new Error("Unknown SVG: " + name);
+	  }
+	  let className = name;
+	  if (props && props.className) {
+	    className = `${name} ${props.className}`;
+	  }
+	  if (name === "subSettings") {
+	    className = "";
+	  }
+	  props = Object.assign({}, props, { className, src: svg[name] });
+	  return React.createElement(InlineSVG, props);
+	};
+
+/***/ },
+/* 30 */
+/***/ function(module, exports, __webpack_require__) {
+
+	'use strict';
+	
+	Object.defineProperty(exports, '__esModule', {
+	    value: true
+	});
+	
+	var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+	
+	var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
+	
+	var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
+	
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+	
+	function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+	
+	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
+	
+	function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+	
+	var _react = __webpack_require__(3);
+	
+	var _react2 = _interopRequireDefault(_react);
+	
+	var DOMParser = typeof window !== 'undefined' && window.DOMParser;
+	var process = process || {};
+	process.env = process.env || {};
+	var parserAvailable = typeof DOMParser !== 'undefined' && DOMParser.prototype != null && DOMParser.prototype.parseFromString != null;
+	
+	function isParsable(src) {
+	    // kinda naive but meh, ain't gonna use full-blown parser for this
+	    return parserAvailable && typeof src === 'string' && src.trim().substr(0, 4) === '<svg';
+	}
+	
+	// parse SVG string using `DOMParser`
+	function parseFromSVGString(src) {
+	    var parser = new DOMParser();
+	    return parser.parseFromString(src, "image/svg+xml");
+	}
+	
+	// Transform DOM prop/attr names applicable to `<svg>` element but react-limited
+	function switchSVGAttrToReactProp(propName) {
+	    switch (propName) {
+	        case 'class':
+	            return 'className';
+	        default:
+	            return propName;
+	    }
+	}
+	
+	var InlineSVG = (function (_React$Component) {
+	    _inherits(InlineSVG, _React$Component);
+	
+	    _createClass(InlineSVG, null, [{
+	        key: 'defaultProps',
+	        value: {
+	            element: 'i',
+	            raw: false,
+	            src: ''
+	        },
+	        enumerable: true
+	    }, {
+	        key: 'propTypes',
+	        value: {
+	            src: _react2['default'].PropTypes.string.isRequired,
+	            element: _react2['default'].PropTypes.string,
+	            raw: _react2['default'].PropTypes.bool
+	        },
+	        enumerable: true
+	    }]);
+	
+	    function InlineSVG(props) {
+	        _classCallCheck(this, InlineSVG);
+	
+	        _get(Object.getPrototypeOf(InlineSVG.prototype), 'constructor', this).call(this, props);
+	        this._extractSVGProps = this._extractSVGProps.bind(this);
+	    }
+	
+	    // Serialize `Attr` objects in `NamedNodeMap`
+	
+	    _createClass(InlineSVG, [{
+	        key: '_serializeAttrs',
+	        value: function _serializeAttrs(map) {
+	            var ret = {};
+	            var prop = undefined;
+	            for (var i = 0; i < map.length; i++) {
+	                prop = switchSVGAttrToReactProp(map[i].name);
+	                ret[prop] = map[i].value;
+	            }
+	            return ret;
+	        }
+	
+	        // get <svg /> element props
+	    }, {
+	        key: '_extractSVGProps',
+	        value: function _extractSVGProps(src) {
+	            var map = parseFromSVGString(src).documentElement.attributes;
+	            return map.length > 0 ? this._serializeAttrs(map) : null;
+	        }
+	
+	        // get content inside <svg> element.
+	    }, {
+	        key: '_stripSVG',
+	        value: function _stripSVG(src) {
+	            return parseFromSVGString(src).documentElement.innerHTML;
+	        }
+	    }, {
+	        key: 'componentWillReceiveProps',
+	        value: function componentWillReceiveProps(_ref) {
+	            var children = _ref.children;
+	
+	            if ("production" !== process.env.NODE_ENV && children != null) {
+	                console.info('<InlineSVG />: `children` prop will be ignored.');
+	            }
+	        }
+	    }, {
+	        key: 'render',
+	        value: function render() {
+	            var Element = undefined,
+	                __html = undefined,
+	                svgProps = undefined;
+	            var _props = this.props;
+	            var element = _props.element;
+	            var raw = _props.raw;
+	            var src = _props.src;
+	
+	            var otherProps = _objectWithoutProperties(_props, ['element', 'raw', 'src']);
+	
+	            if (raw === true && isParsable(src)) {
+	                Element = 'svg';
+	                svgProps = this._extractSVGProps(src);
+	                __html = this._stripSVG(src);
+	            }
+	            __html = __html || src;
+	            Element = Element || element;
+	            svgProps = svgProps || {};
+	
+	            return _react2['default'].createElement(Element, _extends({}, svgProps, otherProps, { src: null, children: null,
+	                dangerouslySetInnerHTML: { __html: __html } }));
+	        }
+	    }]);
+	
+	    return InlineSVG;
+	})(_react2['default'].Component);
+	
+	exports['default'] = InlineSVG;
+	module.exports = exports['default'];
+
+/***/ },
+/* 31 */
+/***/ function(module, exports) {
+
+	module.exports = "<!-- 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/. --><svg , viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8,3L12,3L12,7L14,7L14,8L12,8L12,12L8,12L8,14L7,14L7,12L3,12L3,8L1,8L1,7L3,7L3,3L7,3L7,1L8,1L8,3ZM10,10L10,5L5,5L5,10L10,10Z\"></path></svg>"
+
+/***/ },
+/* 32 */
+/***/ function(module, exports, __webpack_require__) {
+
 	// ReactJS
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	// Reps
 	const {
 	  isGrip,
 	  cropString,
 	  wrapRender
 	} = __webpack_require__(4);
-	const { MODE } = __webpack_require__(2);
+	const { MODE } = __webpack_require__(1);
+	const Svg = __webpack_require__(29);
 	
 	// Shortcuts
 	const DOM = React.DOM;
 	
 	/**
 	 * Renders DOM #text node.
 	 */
 	let TextNode = React.createClass({
 	  displayName: "TextNode",
 	
 	  propTypes: {
 	    object: React.PropTypes.object.isRequired,
 	    // @TODO Change this to Object.values once it's supported in Node's version of V8
 	    mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
 	    objectLink: React.PropTypes.func,
+	    attachedActorIds: React.PropTypes.array,
 	    onDOMNodeMouseOver: React.PropTypes.func,
-	    onDOMNodeMouseOut: React.PropTypes.func
+	    onDOMNodeMouseOut: React.PropTypes.func,
+	    onInspectIconClick: React.PropTypes.func
 	  },
 	
 	  getTextContent: function (grip) {
 	    return cropString(grip.preview.textContent);
 	  },
 	
 	  getTitle: function (grip) {
 	    const title = "#text";
@@ -2284,37 +2619,56 @@ return /******/ (function(modules) { // 
 	      }, title);
 	    }
 	    return title;
 	  },
 	
 	  render: wrapRender(function () {
 	    let {
 	      object: grip,
-	      mode = MODE.SHORT
+	      mode = MODE.SHORT,
+	      attachedActorIds,
+	      onDOMNodeMouseOver,
+	      onDOMNodeMouseOut,
+	      onInspectIconClick
 	    } = this.props;
 	
 	    let baseConfig = { className: "objectBox objectBox-textNode" };
-	    if (this.props.onDOMNodeMouseOver) {
-	      Object.assign(baseConfig, {
-	        onMouseOver: _ => this.props.onDOMNodeMouseOver(grip)
-	      });
-	    }
-	
-	    if (this.props.onDOMNodeMouseOut) {
-	      Object.assign(baseConfig, {
-	        onMouseOut: this.props.onDOMNodeMouseOut
-	      });
+	    let inspectIcon;
+	    let isInTree = attachedActorIds ? attachedActorIds.includes(grip.actor) : true;
+	
+	    if (isInTree) {
+	      if (onDOMNodeMouseOver) {
+	        Object.assign(baseConfig, {
+	          onMouseOver: _ => onDOMNodeMouseOver(grip)
+	        });
+	      }
+	
+	      if (onDOMNodeMouseOut) {
+	        Object.assign(baseConfig, {
+	          onMouseOut: onDOMNodeMouseOut
+	        });
+	      }
+	
+	      if (onInspectIconClick) {
+	        inspectIcon = Svg("open-inspector", {
+	          element: "a",
+	          draggable: false,
+	          // TODO: Localize this with "openNodeInInspector" when Bug 1317038 lands
+	          title: "Click to select the node in the inspector",
+	          onClick: () => onInspectIconClick(grip)
+	        });
+	      }
 	    }
 	
 	    if (mode === MODE.TINY) {
-	      return DOM.span(baseConfig, this.getTitle(grip));
+	      return DOM.span(baseConfig, this.getTitle(grip), inspectIcon);
 	    }
 	
-	    return DOM.span(baseConfig, this.getTitle(grip), DOM.span({ className: "nodeValue" }, " ", `"${this.getTextContent(grip)}"`));
+	    return DOM.span(baseConfig, this.getTitle(grip), DOM.span({ className: "nodeValue" }, " ", `"${this.getTextContent(grip)}"`), inspectIcon);
 	  })
 	});
 	
 	// Registration
 	
 	function supportsObject(grip, type) {
 	  if (!isGrip(grip)) {
 	    return false;
@@ -2325,27 +2679,27 @@ return /******/ (function(modules) { // 
 	
 	// Exports from this module
 	module.exports = {
 	  rep: TextNode,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 30 */
+/* 33 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// ReactJS
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	// Utils
 	const {
 	  isGrip,
 	  wrapRender
 	} = __webpack_require__(4);
-	const { MODE } = __webpack_require__(2);
+	const { MODE } = __webpack_require__(1);
 	// Shortcuts
 	const { span } = React.DOM;
 	
 	/**
 	 * Renders Error objects.
 	 */
 	const ErrorRep = React.createClass({
 	  displayName: "Error",
@@ -2388,60 +2742,72 @@ return /******/ (function(modules) { // 
 	
 	// Exports from this module
 	module.exports = {
 	  rep: ErrorRep,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 31 */
+/* 34 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// ReactJS
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	// Reps
 	const {
 	  isGrip,
 	  getURLDisplayString,
 	  wrapRender
 	} = __webpack_require__(4);
 	
+	const { MODE } = __webpack_require__(1);
+	
 	// Shortcuts
 	const DOM = React.DOM;
 	
 	/**
 	 * Renders a grip representing a window.
 	 */
 	let Window = React.createClass({
 	  displayName: "Window",
 	
 	  propTypes: {
+	    // @TODO Change this to Object.values once it's supported in Node's version of V8
+	    mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
 	    object: React.PropTypes.object.isRequired,
 	    objectLink: React.PropTypes.func
 	  },
 	
-	  getTitle: function (grip) {
+	  getTitle: function (object) {
+	    let title = object.displayClass || object.class || "Window";
 	    if (this.props.objectLink) {
 	      return DOM.span({ className: "objectBox" }, this.props.objectLink({
-	        object: grip
-	      }, grip.class + " "));
+	        object
+	      }, title));
 	    }
-	    return "";
+	    return title;
 	  },
 	
-	  getLocation: function (grip) {
-	    return getURLDisplayString(grip.preview.url);
+	  getLocation: function (object) {
+	    return getURLDisplayString(object.preview.url);
 	  },
 	
 	  render: wrapRender(function () {
-	    let grip = this.props.object;
-	
-	    return DOM.span({ className: "objectBox objectBox-Window" }, this.getTitle(grip), DOM.span({ className: "objectPropValue" }, this.getLocation(grip)));
+	    let {
+	      mode,
+	      object
+	    } = this.props;
+	
+	    if (mode === MODE.TINY) {
+	      return DOM.span({ className: "objectBox objectBox-Window" }, this.getTitle(object));
+	    }
+	
+	    return DOM.span({ className: "objectBox objectBox-Window" }, this.getTitle(object), " ", DOM.span({ className: "objectPropValue" }, this.getLocation(object)));
 	  })
 	});
 	
 	// Registration
 	
 	function supportsObject(object, type) {
 	  if (!isGrip(object)) {
 	    return false;
@@ -2452,21 +2818,21 @@ return /******/ (function(modules) { // 
 	
 	// Exports from this module
 	module.exports = {
 	  rep: Window,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 32 */
+/* 35 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// ReactJS
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	// Reps
 	const {
 	  isGrip,
 	  wrapRender
 	} = __webpack_require__(4);
 	
 	// Shortcuts
@@ -2518,21 +2884,21 @@ return /******/ (function(modules) { // 
 	
 	// Exports from this module
 	module.exports = {
 	  rep: ObjectWithText,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 33 */
+/* 36 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// ReactJS
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	
 	// Reps
 	const {
 	  isGrip,
 	  getURLDisplayString,
 	  wrapRender
 	} = __webpack_require__(4);
 	
@@ -2585,61 +2951,66 @@ return /******/ (function(modules) { // 
 	
 	// Exports from this module
 	module.exports = {
 	  rep: ObjectWithURL,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 34 */
+/* 37 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// Dependencies
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	const {
 	  createFactories,
 	  isGrip,
 	  wrapRender
 	} = __webpack_require__(4);
-	const Caption = React.createFactory(__webpack_require__(11));
-	const { MODE } = __webpack_require__(2);
+	const Caption = React.createFactory(__webpack_require__(12));
+	const { MODE } = __webpack_require__(1);
 	
 	// Shortcuts
 	const { span } = React.DOM;
 	
 	/**
 	 * Renders an array. The array is enclosed by left and right bracket
 	 * and the max number of rendered items depends on the current mode.
 	 */
 	let GripArray = React.createClass({
 	  displayName: "GripArray",
 	
 	  propTypes: {
 	    object: React.PropTypes.object.isRequired,
 	    // @TODO Change this to Object.values once it's supported in Node's version of V8
 	    mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
 	    provider: React.PropTypes.object,
-	    objectLink: React.PropTypes.func
+	    objectLink: React.PropTypes.func,
+	    attachedActorIds: React.PropTypes.array,
+	    onDOMNodeMouseOver: React.PropTypes.func,
+	    onDOMNodeMouseOut: React.PropTypes.func,
+	    onInspectIconClick: React.PropTypes.func
 	  },
 	
 	  getLength: function (grip) {
 	    if (!grip.preview) {
 	      return 0;
 	    }
 	
 	    return grip.preview.length || grip.preview.childNodesLength || 0;
 	  },
 	
 	  getTitle: function (object, context) {
 	    let objectLink = this.props.objectLink || span;
 	    if (this.props.mode !== MODE.TINY) {
+	      let title = this.props.title || object.class || "Array";
 	      return objectLink({
 	        object: object
-	      }, object.class + " ");
+	      }, title, " ");
 	    }
 	    return "";
 	  },
 	
 	  getPreviewItems: function (grip) {
 	    if (!grip.preview) {
 	      return null;
 	    }
@@ -2670,22 +3041,26 @@ return /******/ (function(modules) { // 
 	      try {
 	        let itemGrip = previewItems[i];
 	        let value = provider ? provider.getValue(itemGrip) : itemGrip;
 	
 	        delim = i == delimMax ? "" : ", ";
 	
 	        items.push(GripArrayItem(Object.assign({}, this.props, {
 	          object: value,
-	          delim: delim
+	          delim: delim,
+	          // Do not propagate title to array items reps
+	          title: undefined
 	        })));
 	      } catch (exc) {
 	        items.push(GripArrayItem(Object.assign({}, this.props, {
 	          object: exc,
-	          delim: delim
+	          delim: delim,
+	          // Do not propagate title to array items reps
+	          title: undefined
 	        })));
 	      }
 	    }
 	    if (previewItems.length > max || gripLength > previewItems.length) {
 	      let objectLink = this.props.objectLink || span;
 	      let leftItemNum = gripLength - max > 0 ? gripLength - max : gripLength - previewItems.length;
 	      items.push(Caption({
 	        object: objectLink({
@@ -2739,21 +3114,30 @@ return /******/ (function(modules) { // 
 	/**
 	 * Renders array item. Individual values are separated by
 	 * a delimiter (a comma by default).
 	 */
 	let GripArrayItem = React.createFactory(React.createClass({
 	  displayName: "GripArrayItem",
 	
 	  propTypes: {
-	    delim: React.PropTypes.string
+	    delim: React.PropTypes.string,
+	    object: React.PropTypes.object.isRequired,
+	    objectLink: React.PropTypes.func,
+	    // @TODO Change this to Object.values once it's supported in Node's version of V8
+	    mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+	    provider: React.PropTypes.object,
+	    attachedActorIds: React.PropTypes.array,
+	    onDOMNodeMouseOver: React.PropTypes.func,
+	    onDOMNodeMouseOut: React.PropTypes.func,
+	    onInspectIconClick: React.PropTypes.func
 	  },
 	
 	  render: function () {
-	    let { Rep } = createFactories(__webpack_require__(3));
+	    let { Rep } = createFactories(__webpack_require__(2));
 	
 	    return span({}, Rep(Object.assign({}, this.props, {
 	      mode: MODE.TINY
 	    })), this.props.delim);
 	  }
 	}));
 	
 	function supportsObject(grip, type) {
@@ -2766,47 +3150,52 @@ return /******/ (function(modules) { // 
 	
 	// Exports from this module
 	module.exports = {
 	  rep: GripArray,
 	  supportsObject: supportsObject
 	};
 
 /***/ },
-/* 35 */
+/* 38 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// Dependencies
-	const React = __webpack_require__(1);
+	const React = __webpack_require__(3);
 	const {
 	  isGrip,
 	  wrapRender
 	} = __webpack_require__(4);
-	const Caption = React.createFactory(__webpack_require__(11));
-	const PropRep = React.createFactory(__webpack_require__(13));
-	const { MODE } = __webpack_require__(2);
+	const Caption = React.createFactory(__webpack_require__(12));
+	const PropRep = React.createFactory(__webpack_require__(14));
+	const { MODE } = __webpack_require__(1);
 	// Shortcuts
 	const { span } = React.DOM;
 	/**
 	 * Renders an map. A map is represented by a list of its
 	 * entries enclosed in curly brackets.
 	 */
 	const GripMap = React.createClass({
 	  displayName: "GripMap",
 	
 	  propTypes: {
 	    object: React.PropTypes.object,
 	    // @TODO Change this to Object.values once it's supported in Node's version of V8
 	    mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
 	    objectLink: React.PropTypes.func,
-	    isInterestingEntry: React.PropTypes.func
+	    isInterestingEntry: React.PropTypes.func,
+	    attachedActorIds: React.PropTypes.array,
+	    onDOMNodeMouseOver: React.PropTypes.func,
+	    onDOMNodeMouseOut: React.PropTypes.func,
+	    onInspectIconClick: React.PropTypes.func,
+	    title: React.PropTypes.string
 	  },
 	
 	  getTitle: function (object) {
-	    let title = object && object.class ? object.class : "Map";
+	    let title = this.props.title || (object && object.class ? object.class : "Map");
 	    if (this.props.objectLink) {
 	      return this.props.objectLink({
 	        object: object
 	      }, title);
 	    }
 	    return title;
 	  },
 	
@@ -2855,16 +3244,24 @@ return /******/ (function(modules) { // 
 	  /**
 	   * Get entries ordered by index.
 	   *
 	   * @param {Array} entries Entries array.
 	   * @param {Array} indexes Indexes of entries.
 	   * @return {Array} Array of PropRep.
 	   */
 	  getEntries: function (entries, indexes) {
+	    let {
+	      objectLink,
+	      attachedActorIds,
+	      onDOMNodeMouseOver,
+	      onDOMNodeMouseOut,
+	      onInspectIconClick
+	    } = this.props;
+	
 	    // Make indexes ordered by ascending.
 	    indexes.sort(function (a, b) {
 	      return a - b;
 	    });
 	
 	    return indexes.map((index, i) => {
 	      let [key, entryValue] = entries[index];
 	      let value = entryValue.value !== undefined ? entryValue.value : entryValue;
@@ -2873,17 +3270,21 @@ return /******/ (function(modules) { // 
 	        // key,
 	        name: key,
 	        equal: ": ",
 	        object: value,
 	        // Do not add a trailing comma on the last entry
 	        // if there won't be a "more..." item.
 	        delim: i < indexes.length - 1 || indexes.length < entries.length ? ", " : "",
 	        mode: MODE.TINY,
-	        objectLink: this.props.objectLink
+	        objectLink,
+	        attachedActorIds,
+	        onDOMNodeMouseOver,
+	        onDOMNodeMouseOut,
+	        onInspectIconClick
 	      });
 	    });
 	  },
 	
 	  /**
 	   * Get the indexes of entries in the map.
 	   *
 	   * @param {Array} entries Entries array.
--- a/devtools/client/shared/components/reps/test/mochitest/head.js
+++ b/devtools/client/shared/components/reps/test/mochitest/head.js
@@ -1,13 +1,22 @@
 /* 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/. */
-/* eslint no-unused-vars: [2, {"vars": "local"}] */
 
+/* Entirely disable no-unused-vars, because the second line here
+   doesn't seem to work with eslint 3.15.0 -- it doesn't suppress
+   other no-unused-vars errors.  */
+/* eslint-disable no-unused-vars */
+/* eslint no-unused-vars: ["error", {"vars": "local"}] */
+
+/* globals is */
+
+/* Not really a module.  */
+/* eslint-disable strict */
 "use strict";
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 var { Assert } = require("resource://testing-common/Assert.jsm");
 var { gDevTools } = require("devtools/client/framework/devtools");
 var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
@@ -42,21 +51,25 @@ function shallowRenderComponent(componen
   return renderer.getRenderOutput();
 }
 
 /**
  * Test that a rep renders correctly across different modes.
  */
 function testRepRenderModes(modeTests, testName, componentUnderTest, gripStub,
   props = {}) {
-  modeTests.forEach(({mode, expectedOutput, message}) => {
+  modeTests.forEach(({mode, expectedOutput, message, title}) => {
     const modeString = typeof mode === "undefined" ? "no mode" : mode.toString();
     if (!message) {
       message = `${testName}: ${modeString} renders correctly.`;
     }
 
     const rendered = renderComponent(
       componentUnderTest.rep,
-      Object.assign({}, { object: gripStub, mode }, props)
+      Object.assign({}, { object: gripStub, mode, title }, props)
     );
     is(rendered.textContent, expectedOutput, message);
   });
 }
+
+function getStubAttachedActorIds(gripStubs) {
+  return gripStubs.map(gripStub => gripStub.actor);
+}
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_element-node.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_element-node.html
@@ -14,31 +14,36 @@ Test Element node rep
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 "use strict";
 
 window.onload = Task.async(function* () {
-  const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  const {
+    REPS,
+    MODE,
+    getSelectableInInspectorGrips,
+  } = browserRequire("devtools/client/shared/components/reps/load-reps");
   let { Rep, ElementNode } = REPS;
 
   try {
     yield testBodyNode();
     yield testDocumentElement();
     yield testNode();
     yield testNodeWithLeadingAndTrailingSpacesClassName();
     yield testNodeWithoutAttributes();
     yield testLotsOfAttributes();
     yield testSvgNode();
     yield testSvgNodeInXHTML();
 
     yield testOnMouseOver();
     yield testOnMouseOut();
+    yield testOnInspectIconClick();
   } catch (e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
   function testBodyNode() {
     const stub = getGripStub("testBodyNode");
@@ -171,47 +176,102 @@ window.onload = Task.async(function* () 
     const tinyRenderedComponent = renderComponent(
       ElementNode.rep, { object: stub, mode: MODE.TINY });
     is(tinyRenderedComponent.textContent, `svg:circle.svg-element`,
       "Element node rep has expected text content for XHTML SVG element in tiny mode");
   }
 
   function testOnMouseOver() {
     const stub = getGripStub("testNode");
+  debugger;
+    const grips = getSelectableInInspectorGrips(stub);
+    is(grips.length, 1, "the stub has one node grip");
+
+    const attachedActorIds = getStubAttachedActorIds(grips);
 
     let mouseOverValue;
     let onDOMNodeMouseOver = (object) => {
       mouseOverValue = object;
     };
-    const renderedComponent = renderComponent(
-      ElementNode.rep, {object: stub, onDOMNodeMouseOver});
+    const renderedComponent = renderComponent(ElementNode.rep, {
+      object: stub,
+      onDOMNodeMouseOver,
+      attachedActorIds,
+    });
 
-    const node = renderedComponent.querySelector(".objectBox-node");
-    TestUtils.Simulate.mouseOver(node);
+    TestUtils.Simulate.mouseOver(renderedComponent);
 
-    is(mouseOverValue, stub, "onDOMNodeMouseOver is called with the expected argument " +
-      "when mouseover is fired on the Rep");
+    is(mouseOverValue, grips[0], "onDOMNodeMouseOver is called " +
+      "with the expected argument when mouseover is fired on the Rep");
   }
 
   function testOnMouseOut() {
     const stub = getGripStub("testNode");
+    const grips = getSelectableInInspectorGrips(stub);
+    is(grips.length, 1, "the stub has one node grip");
+
+    const attachedActorIds = getStubAttachedActorIds(grips);
 
     let called = false;
     let onDOMNodeMouseOut = (object) => {
       called = true;
     };
-    const renderedComponent = renderComponent(
-      ElementNode.rep, {object: stub, onDOMNodeMouseOut});
+    const renderedComponent = renderComponent(ElementNode.rep, {
+      object: stub,
+      onDOMNodeMouseOut,
+      attachedActorIds,
+    });
 
-    const node = renderedComponent.querySelector(".objectBox-node");
-    TestUtils.Simulate.mouseOut(node);
+    TestUtils.Simulate.mouseOut(renderedComponent);
 
     is(called, true, "onDOMNodeMouseOut is called when mouseout is fired on the Rep");
   }
 
+  function testOnInspectIconClick() {
+    const stub = getGripStub("testNode");
+    const grips = getSelectableInInspectorGrips(stub);
+    is(grips.length, 1, "the stub has one node grip");
+
+    const attachedActorIds = getStubAttachedActorIds(grips);
+
+    let inspectIconClickedValue = null;
+    let onInspectIconClick = (object) => {
+      inspectIconClickedValue = object;
+    };
+
+    const renderedComponentWithoutInspectIcon = renderComponent(ElementNode.rep, {
+      object: stub,
+      onInspectIconClick,
+      attachedActorIds: ["someOtherId"]
+    });
+    is(renderedComponentWithoutInspectIcon.querySelector(".open-inspector"), null,
+      "There isn't an inspect icon when actor is not in attachedActorIds");
+
+    let renderedComponent = renderComponent(ElementNode.rep, {
+      object: stub,
+      onInspectIconClick,
+    });
+    let inspectIconNode = renderedComponent.querySelector(".open-inspector");
+    ok(inspectIconNode !== null,
+      "There is an inspect icon when attachedActorIds is not specified");
+
+    renderedComponent = renderComponent(ElementNode.rep, {
+      object: stub,
+      onInspectIconClick,
+      attachedActorIds,
+    });
+
+    inspectIconNode = renderedComponent.querySelector(".open-inspector");
+    ok(inspectIconNode !== null, "There is an inspect icon as expected");
+    TestUtils.Simulate.click(inspectIconNode);
+
+    is(inspectIconClickedValue, grips[0],
+      "onInspectIconClick is called with expected value when inspect icon is clicked");
+  }
+
   function getGripStub(name) {
     switch (name) {
       case "testBodyNode":
         return {
           "type": "object",
           "actor": "server1.conn1.child1/obj30",
           "class": "HTMLBodyElement",
           "ownPropertyLength": 0,
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_event.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_event.html
@@ -12,45 +12,50 @@ Test Event rep
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
-  const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  const {
+    REPS,
+    MODE,
+    getSelectableInInspectorGrips,
+  } = browserRequire("devtools/client/shared/components/reps/load-reps");
   let { Rep, Event } = REPS;
 
   try {
     // Test that correct rep is chosen
     const renderedRep = shallowRenderComponent(Rep, { object: getGripStub("testEvent") });
     is(renderedRep.type, Event.rep, `Rep correctly selects ${Event.rep.displayName}`);
 
     yield testEvent();
     yield testMouseEvent();
     yield testKeyboardEvent();
     yield testKeyboardEventWithModifiers();
     yield testMessageEvent();
 
-    yield testOnMouseOver();
-    yield testOnMouseOut();
-  } catch(e) {
+    yield testOnDomNodeMouseOver();
+    yield testOnDomNodeMouseOut();
+    yield testOnDomNodeInspectIconClick();
+  } catch (e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
   function testEvent() {
     const renderedComponent = renderComponent(Event.rep, {
       object: getGripStub("testEvent"),
     });
     is(renderedComponent.textContent,
-      "beforeprint { target: http://example.com, isTrusted: true, " +
-      "currentTarget: http://example.com, 8 more… }",
+      "beforeprint { target: Window, isTrusted: true, " +
+      "currentTarget: Window, 8 more… }",
       "Event rep has expected text content for an event");
   }
 
   function testMouseEvent() {
     const renderedComponent = renderComponent(Event.rep, {
       object: getGripStub("testMouseEvent")
     });
     is(renderedComponent.textContent,
@@ -96,67 +101,120 @@ window.onload = Task.async(function* () 
        "in long mode");
   }
 
   function testMessageEvent() {
     const renderedComponent = renderComponent(Event.rep, {
       object: getGripStub("testMessageEvent")
     });
     is(renderedComponent.textContent,
-       `message { target: http://example.com, isTrusted: false, data: "test data", ` +
+       `message { target: Window, isTrusted: false, data: "test data", ` +
        "8 more… }",
        "Event rep has expected text content for a message event");
 
     const longRenderedComponent = renderComponent(Event.rep, {
       object: getGripStub("testMessageEvent"),
       mode: MODE.LONG,
     });
     is(longRenderedComponent.textContent,
-       `message { target: http://example.com, isTrusted: false, data: "test data", ` +
-       `origin: "null", lastEventId: "", source: , ports: message, currentTarget: , ` +
-       `eventPhase: 2, bubbles: false, 1 more… }`,
+       `message { target: Window, isTrusted: false, data: "test data", ` +
+       `origin: "null", lastEventId: "", source: Window, ports: Array, ` +
+       `currentTarget: Window, eventPhase: 2, bubbles: false, 1 more… }`,
        "Event rep has expected text content for a message event in long mode");
   }
 
-  function testOnMouseOver() {
+  function testOnDomNodeMouseOver() {
     const stub = getGripStub("testMouseEvent");
+    const grips = getSelectableInInspectorGrips(stub);
+
+    is(grips.length, 1, "the stub has one node grip");
+
+    const attachedActorIds = getStubAttachedActorIds(grips);
 
     let mouseOverValue;
     let onDOMNodeMouseOver = (object) => {
       mouseOverValue = object;
     };
     const renderedComponent = renderComponent(Event.rep, {
-      object: stub, onDOMNodeMouseOver
+      object: stub,
+      onDOMNodeMouseOver,
+      attachedActorIds,
     });
 
     const node = renderedComponent.querySelector(".objectBox-node");
     TestUtils.Simulate.mouseOver(node);
 
-    is(mouseOverValue, stub.preview.target, "onDOMNodeMouseOver is called with " +
+    is(mouseOverValue, grips[0], "onDOMNodeMouseOver is called with " +
       "the expected argument when mouseover is fired on the Rep");
   }
 
-  function testOnMouseOut() {
+  function testOnDomNodeMouseOut() {
     const stub = getGripStub("testMouseEvent");
+    const grips = getSelectableInInspectorGrips(stub);
+    is(grips.length, 1, "the stub has one node grip");
+
+    const attachedActorIds = getStubAttachedActorIds(grips);
 
     let called = false;
     let onDOMNodeMouseOut = (object) => {
       called = true;
     };
     const renderedComponent = renderComponent(Event.rep, {
       object: stub,
-      onDOMNodeMouseOut
+      onDOMNodeMouseOut,
+      attachedActorIds
     });
 
     const node = renderedComponent.querySelector(".objectBox-node");
     TestUtils.Simulate.mouseOut(node);
 
     is(called, true, "onDOMNodeMouseOut is called when mouseout is fired on the Rep");
   }
 
+  function testOnDomNodeInspectIconClick() {
+    const stub = getGripStub("testMouseEvent");
+    const grips = getSelectableInInspectorGrips(stub);
+    is(grips.length, 1, "the stub has one node grip");
+
+    const attachedActorIds = getStubAttachedActorIds(grips);
+
+    let inspectIconClickedValue = null;
+    let onInspectIconClick = (object) => {
+      inspectIconClickedValue = object;
+    };
+
+    let renderedComponentWithoutInspectIcon = renderComponent(Event.rep, {
+      object: stub,
+      onInspectIconClick,
+      attachedActorIds: ["someOtherId"]
+    });
+    is(renderedComponentWithoutInspectIcon.querySelector(".open-inspector"), null,
+      "There isn't an inspect icon when the actor is not in attachedActorIds");
+
+    is(renderedComponentWithoutInspectIcon.querySelector(".open-inspector"), null,
+      "There isn't an inspect icon when attachedActorIds does not have keys " +
+      "matching grip event's target item");
+
+    const renderedComponent = renderComponent(Event.rep, {
+      object: stub,
+      onInspectIconClick,
+      attachedActorIds
+    });
+
+    const icon = renderedComponent.querySelector(".open-inspector");
+    ok(icon !== null, "There is an icon as expected when passing a matching " +
+      "attachedActorIds item");
+
+    TestUtils.Simulate.click(icon);
+
+    is(inspectIconClickedValue, grips[0],
+      "onInspectIconClick is called with the expected argument " +
+      "when the inspect icon is clicked");
+  }
+
   function getGripStub(name) {
     switch (name) {
       case "testEvent":
         return {
           "type": "object",
           "class": "Event",
           "actor": "server1.conn23.obj35",
           "extensible": true,
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_grip-array.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_grip-array.html
@@ -12,17 +12,21 @@ Test GripArray rep
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
-  const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  const {
+    REPS,
+    MODE,
+    getSelectableInInspectorGrips,
+  } = browserRequire("devtools/client/shared/components/reps/load-reps");
   let { Rep, GripArray } = REPS;
 
   let componentUnderTest = GripArray;
   const maxLength = {
     short: 3,
     long: 10
   };
 
@@ -34,18 +38,19 @@ window.onload = Task.async(function* () 
     yield testMoreThanShortMaxProps();
     yield testMoreThanLongMaxProps();
     yield testRecursiveArray();
     yield testPreviewLimit();
     yield testNamedNodeMap();
     yield testNodeList();
     yield testDocumentFragment();
 
-    yield testOnMouseOver();
-    yield testOnMouseOut();
+    yield testOnDomNodeMouseOver();
+    yield testOnDomNodeMouseOut();
+    yield testOnDomNodeInspectIconClick();
   } catch (e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
   function testBasic() {
     // Test array: `[]`
@@ -98,16 +103,23 @@ window.onload = Task.async(function* () 
       },
       {
         mode: MODE.SHORT,
         expectedOutput: defaultOutput,
       },
       {
         mode: MODE.LONG,
         expectedOutput: defaultOutput,
+      },
+      {
+        // Check the custom title with nested objects to make sure nested objects are not
+        // displayed with their parent's title.
+        mode: MODE.LONG,
+        title: "CustomTitle",
+        expectedOutput: `CustomTitle [ 1, "foo", Object ]`,
       }
     ];
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
   function testMoreThanShortMaxProps() {
     // Test array = `["test string"…] //4 items`
@@ -303,51 +315,108 @@ window.onload = Task.async(function* () 
         mode: MODE.LONG,
         expectedOutput: longOutput,
       }
     ];
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
-
+  function testOnDomNodeMouseOver() {
+    const stub = getGripStub("testNodeList");
+    const grips = getSelectableInInspectorGrips(stub);
 
-  function testOnMouseOver() {
-    const stub = getGripStub("testNodeList");
+    is(grips.length, 3, "the stub has three node grips");
+
+    const attachedActorIds = getStubAttachedActorIds(grips);
 
     let mouseOverValue;
     let onDOMNodeMouseOver = (object) => {
       mouseOverValue = object;
     };
-    const renderedComponent = renderComponent(
-      GripArray.rep, {object: stub, onDOMNodeMouseOver});
+    const renderedComponent = renderComponent(GripArray.rep, {
+      object: stub,
+      onDOMNodeMouseOver,
+      attachedActorIds,
+    });
 
     const nodes = renderedComponent.querySelectorAll(".objectBox-node");
+    is(nodes.length, 3, "There are three node elements");
     nodes.forEach((node, index) => {
       TestUtils.Simulate.mouseOver(node);
 
-      is(mouseOverValue, stub.preview.items[index], "onDOMNodeMouseOver is called with " +
-        "the expected argument when mouseover is fired on the Rep");
+      is(mouseOverValue, grips[index],
+        "onDOMNodeMouseOver is called with the expected argument " +
+        "when mouseover is fired on the Rep");
     });
   }
 
-  function testOnMouseOut() {
+  function testOnDomNodeMouseOut() {
     const stub = getGripStub("testNodeList");
+    const grips = getSelectableInInspectorGrips(stub);
+
+    is(grips.length, 3, "the stub has three node grips");
 
-    let called = false;
+    const attachedActorIds = getStubAttachedActorIds(grips);
+
+    let called = 0;
     let onDOMNodeMouseOut = (object) => {
-      called = true;
+      called++;
     };
-    const renderedComponent = renderComponent(
-      GripArray.rep, {object: stub, onDOMNodeMouseOut});
+    const renderedComponent = renderComponent(GripArray.rep, {
+      object: stub,
+      onDOMNodeMouseOut,
+      attachedActorIds,
+    });
+
+    const nodes = renderedComponent.querySelectorAll(".objectBox-node");
+    info("Simulating mouseout on each node");
+    Array.from(nodes).forEach(node => TestUtils.Simulate.mouseOut(node));
+
+    is(called, 3, "onDOMNodeMouseOut is called when mouseout is fired on each NodeRep");
+  }
+
+  function testOnDomNodeInspectIconClick() {
+    const stub = getGripStub("testNodeList");
+    const grips = getSelectableInInspectorGrips(stub);
+
+    is(grips.length, 3, "the stub has three node grips");
+
+    const attachedActorIds = getStubAttachedActorIds(grips);
+
+    let inspectIconClickedValue = null;
+    let onInspectIconClick = (object) => {
+      inspectIconClickedValue = object;
+    };
 
-    const node = renderedComponent.querySelector(".objectBox-node");
-    TestUtils.Simulate.mouseOut(node);
+    let renderedComponentWithoutInspectIcon = renderComponent(GripArray.rep, {
+      object: stub,
+      onInspectIconClick,
+      attachedActorIds: ["someOtherId"],
+    });
+    is(renderedComponentWithoutInspectIcon.querySelector(".open-inspector"), null,
+      "There isn't an inspect icon when the actor is not in attachedActorIds");
 
-    is(called, true, "onDOMNodeMouseOut is called when mouseout is fired on the Rep");
+    const renderedComponent = renderComponent(GripArray.rep, {
+      object: stub,
+      onInspectIconClick,
+      attachedActorIds,
+    });
+
+    const icons = renderedComponent.querySelectorAll(".open-inspector");
+    is(icons.length, grips.length,
+      "There is an icon for each grip array item with a matching attachedNodeFront");
+
+    icons.forEach((icon, index) => {
+      TestUtils.Simulate.click(icon);
+
+      is(inspectIconClickedValue, grips[index],
+        "onInspectIconClick is called with the expected argument " +
+        "when the inspect icon is clicked");
+    });
   }
 
   function getGripStub(functionName) {
     switch (functionName) {
       case "testBasic":
         return {
           "type": "object",
           "class": "Array",
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_grip-map.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_grip-map.html
@@ -14,30 +14,38 @@ Test GripMap rep
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 "use strict";
 
 window.onload = Task.async(function* () {
-  const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  const {
+    REPS,
+    MODE,
+    getSelectableInInspectorGrips,
+  } = browserRequire("devtools/client/shared/components/reps/load-reps");
   let { Rep, GripMap } = REPS;
 
   const componentUnderTest = GripMap;
 
   try {
     yield testEmptyMap();
     yield testSymbolKeyedMap();
     yield testWeakMap();
 
     // // Test entries iterator
     yield testMaxEntries();
     yield testMoreThanMaxEntries();
     yield testUninterestingEntries();
+
+    yield testOnDomNodeMouseOver();
+    yield testOnDomNodeMouseOut();
+    yield testOnDomNodeInspectIconClick();
   } catch (e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
   function testEmptyMap() {
     // Test object: `new Map()`
@@ -125,16 +133,23 @@ window.onload = Task.async(function* () 
       },
       {
         mode: MODE.SHORT,
         expectedOutput: defaultOutput,
       },
       {
         mode: MODE.LONG,
         expectedOutput: defaultOutput,
+      },
+      {
+        // Check the custom title with nested objects to make sure nested objects are not
+        // displayed with their parent's title.
+        mode: MODE.LONG,
+        title: "CustomTitle",
+        expectedOutput: `CustomTitle { Object: "value-a" }`,
       }
     ];
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
   function testMaxEntries() {
     // Test object:
@@ -226,16 +241,170 @@ window.onload = Task.async(function* () 
         mode: MODE.LONG,
         expectedOutput: longOutput,
       }
     ];
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
+  function testOnDomNodeMouseOver() {
+    const nodeValuedStub = getGripStub("testNodeValuedMap");
+    const nodeKeyedStub = getGripStub("testNodeKeyedMap");
+
+    const valuesGrips = getSelectableInInspectorGrips(nodeValuedStub);
+    is(valuesGrips.length, 3, "the stub has three node grips");
+    const valuesattachedActorIds = getStubAttachedActorIds(valuesGrips);
+
+    const keysGrips = getSelectableInInspectorGrips(nodeKeyedStub);
+    is(keysGrips.length, 3, "the stub has three node grips");
+    const keysAttachedActorIds = getStubAttachedActorIds(keysGrips);
+
+    let mouseOverValue;
+    let onDOMNodeMouseOver = (object) => {
+      mouseOverValue = object;
+    };
+
+    info("Testing onDOMNodeMouseOver on node valued Map");
+    const nodeValuedRenderedComponent = renderComponent(GripMap.rep, {
+      object: nodeValuedStub,
+      onDOMNodeMouseOver,
+      attachedActorIds: valuesattachedActorIds,
+    });
+
+    let nodes = nodeValuedRenderedComponent.querySelectorAll(".objectBox-node");
+    nodes.forEach((node, index) => {
+      TestUtils.Simulate.mouseOver(node);
+      is(mouseOverValue, valuesGrips[index],
+        "onDOMNodeMouseOver is called with the expected argument " +
+        "when mouseover is fired on the Rep");
+    });
+
+    info("Testing onDOMNodeMouseOver on node keyed Map");
+    const nodeKeyedRenderedComponent = renderComponent(GripMap.rep, {
+      object: nodeKeyedStub,
+      onDOMNodeMouseOver,
+      attachedActorIds: keysAttachedActorIds,
+    });
+
+    nodes = nodeKeyedRenderedComponent.querySelectorAll(".objectBox-node");
+    nodes.forEach((node, index) => {
+      TestUtils.Simulate.mouseOver(node);
+      is(mouseOverValue, keysGrips[index],
+        "onDOMNodeMouseOver is called with the expected argument " +
+        "when mouseover is fired on the Rep");
+    });
+  }
+
+  function testOnDomNodeMouseOut() {
+    const nodeValuedStub = getGripStub("testNodeValuedMap");
+    const nodeKeyedStub = getGripStub("testNodeKeyedMap");
+
+    const valuesGrips = getSelectableInInspectorGrips(nodeValuedStub);
+    is(valuesGrips.length, 3, "the stub has three node grips");
+    const valuesattachedActorIds = getStubAttachedActorIds(valuesGrips);
+
+    const keysGrips = getSelectableInInspectorGrips(nodeKeyedStub);
+    is(keysGrips.length, 3, "the stub has three node grips");
+    const keysAttachedActorIds = getStubAttachedActorIds(keysGrips);
+
+    let called = 0;
+    let onDOMNodeMouseOut = (object) => {
+      called++;
+    };
+
+    info("Testing onDOMNodeMouseOut on node valued Map");
+    const nodeValuedRenderedComponent = renderComponent(GripMap.rep, {
+      object: nodeValuedStub,
+      onDOMNodeMouseOut,
+      attachedActorIds: valuesattachedActorIds,
+    });
+
+    let nodes = nodeValuedRenderedComponent.querySelectorAll(".objectBox-node");
+    info("Simulating mouseout on each value node");
+    nodes.forEach((node, index) => TestUtils.Simulate.mouseOut(node));
+    is(called, 3,
+      "onDOMNodeMouseOut is called when mouseout is fired on each value NodeRep");
+
+    info("Testing onDOMNodeMouseOut on node keyed Map");
+    const nodeKeyedRenderedComponent = renderComponent(GripMap.rep, {
+      object: nodeKeyedStub,
+      onDOMNodeMouseOut,
+      attachedActorIds: keysAttachedActorIds,
+    });
+
+    nodes = nodeKeyedRenderedComponent.querySelectorAll(".objectBox-node");
+    // Resets counter
+    called = 0;
+    info("Simulating mouseout on each key node");
+    nodes.forEach((node, index) => TestUtils.Simulate.mouseOut(node));
+    is(called, 3,
+      "onDOMNodeMouseOut is called when mouseout is fired on each key NodeRep");
+  }
+
+  function testOnDomNodeInspectIconClick() {
+    const nodeValuedStub = getGripStub("testNodeValuedMap");
+    const nodeKeyedStub = getGripStub("testNodeKeyedMap");
+
+    const valuesGrips = getSelectableInInspectorGrips(nodeValuedStub);
+    is(valuesGrips.length, 3, "the stub has three node grips");
+    const valuesattachedActorIds = getStubAttachedActorIds(valuesGrips);
+
+    const keysGrips = getSelectableInInspectorGrips(nodeKeyedStub);
+    is(keysGrips.length, 3, "the stub has three node grips");
+    const keysAttachedActorIds = getStubAttachedActorIds(keysGrips);
+
+    let inspectIconClickedValue = null;
+    let onInspectIconClick = (object) => {
+      inspectIconClickedValue = object;
+    };
+
+    const renderedComponentWithoutInspectIcon = renderComponent(GripMap.rep, {
+      object: nodeValuedStub,
+      onInspectIconClick,
+      attachedActorIds: [],
+    });
+    is(renderedComponentWithoutInspectIcon.querySelector(".open-inspector"), null,
+      "There isn't an inspect icon when the actor is not in attachedActorIds");
+
+    info("Testing onInspectIconClick on node valued Map");
+    const nodeValuedRenderedComponent = renderComponent(GripMap.rep, {
+      object: nodeValuedStub,
+      onInspectIconClick,
+      attachedActorIds: valuesattachedActorIds,
+    });
+
+    let icons = nodeValuedRenderedComponent.querySelectorAll(".open-inspector");
+    is(icons.length, valuesGrips.length,
+      "There is an icon for each map value with a matching attachedNodeFront");
+
+    icons.forEach((icon, index) => {
+      TestUtils.Simulate.click(icon);
+      is(inspectIconClickedValue, valuesGrips[index], "onInspectIconClick is called " +
+        "with the expected argument when the inspect icon is clicked");
+    });
+
+    info("Testing onInspectIconClick on node keyed Map");
+    const nodeKeyedRenderedComponent = renderComponent(GripMap.rep, {
+      object: nodeKeyedStub,
+      onInspectIconClick,
+      attachedActorIds: keysAttachedActorIds,
+    });
+
+    icons = nodeKeyedRenderedComponent.querySelectorAll(".open-inspector");
+    is(icons.length, keysGrips.length,
+      "There is an icon for each map key with a matching attachedNodeFront");
+
+    icons.forEach((icon, index) => {
+      TestUtils.Simulate.click(icon);
+      is(inspectIconClickedValue, keysGrips[index], "onInspectIconClick is called " +
+        "with the expected argument when the inspect icon is clicked");
+    });
+  }
+
   function getGripStub(functionName) {
     switch (functionName) {
       case "testEmptyMap":
         return {
           "type": "object",
           "actor": "server1.conn1.child1/obj97",
           "class": "Map",
           "extensible": true,
@@ -391,15 +560,181 @@ window.onload = Task.async(function* () 
               ],
               [
                 "key-d",
                 4
               ]
             ]
           }
         };
+
+      case "testNodeValuedMap":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj213",
+          "class": "Map",
+          "ownPropertyLength": 0,
+          "preview": {
+            "kind": "MapLike",
+            "size": 3,
+            "entries": [
+              [
+                "item-0",
+                {
+                  "type": "object",
+                  "actor": "server1.conn1.child1/obj214",
+                  "class": "HTMLButtonElement",
+                  "extensible": true,
+                  "frozen": false,
+                  "sealed": false,
+                  "ownPropertyLength": 0,
+                  "preview": {
+                    "kind": "DOMNode",
+                    "nodeType": 1,
+                    "nodeName": "button",
+                    "attributes": {
+                      "id": "btn-1",
+                      "class": "btn btn-log",
+                      "type": "button"
+                    },
+                    "attributesLength": 3
+                  }
+                }
+              ],
+              [
+                "item-1",
+                {
+                  "type": "object",
+                  "actor": "server1.conn1.child1/obj215",
+                  "class": "HTMLButtonElement",
+                  "extensible": true,
+                  "frozen": false,
+                  "sealed": false,
+                  "ownPropertyLength": 0,
+                  "preview": {
+                    "kind": "DOMNode",
+                    "nodeType": 1,
+                    "nodeName": "button",
+                    "attributes": {
+                      "id": "btn-2",
+                      "class": "btn btn-err",
+                      "type": "button"
+                    },
+                    "attributesLength": 3
+                  }
+                }
+              ],
+              [
+                "item-2",
+                {
+                  "type": "object",
+                  "actor": "server1.conn1.child1/obj216",
+                  "class": "HTMLButtonElement",
+                  "extensible": true,
+                  "frozen": false,
+                  "sealed": false,
+                  "ownPropertyLength": 0,
+                  "preview": {
+                    "kind": "DOMNode",
+                    "nodeType": 1,
+                    "nodeName": "button",
+                    "attributes": {
+                      "id": "btn-3",
+                      "class": "btn btn-count",
+                      "type": "button"
+                    },
+                    "attributesLength": 3
+                  }
+                }
+              ]
+            ]
+          }
+        };
+
+      case "testNodeKeyedMap":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj223",
+          "class": "WeakMap",
+          "ownPropertyLength": 0,
+          "preview": {
+            "kind": "MapLike",
+            "size": 3,
+            "entries": [
+              [
+                {
+                  "type": "object",
+                  "actor": "server1.conn1.child1/obj224",
+                  "class": "HTMLButtonElement",
+                  "extensible": true,
+                  "frozen": false,
+                  "sealed": false,
+                  "ownPropertyLength": 0,
+                  "preview": {
+                    "kind": "DOMNode",
+                    "nodeType": 1,
+                    "nodeName": "button",
+                    "attributes": {
+                      "id": "btn-1",
+                      "class": "btn btn-log",
+                      "type": "button"
+                    },
+                    "attributesLength": 3
+                  }
+                },
+                "item-0"
+              ],
+              [
+                {
+                  "type": "object",
+                  "actor": "server1.conn1.child1/obj225",
+                  "class": "HTMLButtonElement",
+                  "extensible": true,
+                  "frozen": false,
+                  "sealed": false,
+                  "ownPropertyLength": 0,
+                  "preview": {
+                    "kind": "DOMNode",
+                    "nodeType": 1,
+                    "nodeName": "button",
+                    "attributes": {
+                      "id": "btn-3",
+                      "class": "btn btn-count",
+                      "type": "button"
+                    },
+                    "attributesLength": 3
+                  }
+                },
+                "item-2"
+              ],
+              [
+                {
+                  "type": "object",
+                  "actor": "server1.conn1.child1/obj226",
+                  "class": "HTMLButtonElement",
+                  "extensible": true,
+                  "frozen": false,
+                  "sealed": false,
+                  "ownPropertyLength": 0,
+                  "preview": {
+                    "kind": "DOMNode",
+                    "nodeType": 1,
+                    "nodeName": "button",
+                    "attributes": {
+                      "id": "btn-2",
+                      "class": "btn btn-err",
+                      "type": "button"
+                    },
+                    "attributesLength": 3
+                  }
+                },
+                "item-1"
+              ]
+            ]
+          }
+        };
     }
   }
 });
 </script>
 </pre>
 </body>
 </html>
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_grip.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_grip.html
@@ -12,17 +12,21 @@ Test grip rep
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
-  const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  const {
+    REPS,
+    MODE,
+    getSelectableInInspectorGrips,
+  } = browserRequire("devtools/client/shared/components/reps/load-reps");
   let { Rep, Grip } = REPS;
 
   const componentUnderTest = Grip;
 
   try {
     yield testBasic();
     yield testBooleanObject();
     yield testNumberObject();
@@ -39,16 +43,20 @@ window.onload = Task.async(function* () 
     yield testNonEnumerableProps();
 
     // Test that properties are rendered as expected by PropRep
     yield testNestedObject();
     yield testNestedArray();
 
     // Test that 'more' property doesn't clobber the caption.
     yield testMoreProp();
+
+    yield testOnDomNodeMouseOver();
+    yield testOnDomNodeMouseOut();
+    yield testOnDomNodeInspectIconClick();
   } catch(e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
   function testBasic() {
     // Test object: `{}`
@@ -447,16 +455,23 @@ window.onload = Task.async(function* () 
       },
       {
         mode: MODE.SHORT,
         expectedOutput: defaultOutput,
       },
       {
         mode: MODE.LONG,
         expectedOutput: defaultOutput,
+      },
+      {
+        // Check the custom title with nested objects to make sure nested objects are not
+        // displayed with their parent's title.
+        mode: MODE.LONG,
+        title: "CustomTitle",
+        expectedOutput: `CustomTitle { objProp: Object, strProp: "test string" }`,
       }
     ];
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
   function testNestedArray() {
     // Test object: `{arrProp: ["foo", "bar", "baz"]}`
@@ -510,16 +525,114 @@ window.onload = Task.async(function* () 
         mode: MODE.LONG,
         expectedOutput: longOutput,
       }
     ];
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
+  function testOnDomNodeMouseOver() {
+    const stub = getGripStub("testObjectWithNodes");
+
+    const grips = getSelectableInInspectorGrips(stub);
+    is(grips.length, 2, "the stub has two node grips");
+    const attachedActorIds = getStubAttachedActorIds(grips);
+
+    let mouseOverValue;
+    let called = 0;
+    let onDOMNodeMouseOver = (object) => {
+      mouseOverValue = object;
+      called++;
+    };
+
+    const renderedComponent = renderComponent(Grip.rep, {
+      object: stub,
+      onDOMNodeMouseOver,
+      attachedActorIds,
+    });
+
+    const nodes = renderedComponent.querySelectorAll(".objectBox-node");
+    nodes.forEach((node, index) => {
+      TestUtils.Simulate.mouseOver(node);
+      is(mouseOverValue, grips[index],
+        "onDOMNodeMouseOver is called with the expected argument " +
+        "when mouseover is fired on the Rep");
+    });
+    is(called, 2,
+      "onDOMNodeMouseOver is called when mouseOverValue is fired on each NodeRep");
+  }
+
+  function testOnDomNodeMouseOut() {
+    const stub = getGripStub("testObjectWithNodes");
+
+    const grips = getSelectableInInspectorGrips(stub);
+    is(grips.length, 2, "the stub has two node grips");
+    const attachedActorIds = getStubAttachedActorIds(grips);
+
+    let called = 0;
+    let onDOMNodeMouseOut = (object) => {
+      called++;
+    };
+
+    const renderedComponent = renderComponent(Grip.rep, {
+      object: stub,
+      onDOMNodeMouseOut,
+      attachedActorIds,
+    });
+
+    const nodes = renderedComponent.querySelectorAll(".objectBox-node");
+    info("Simulating mouseout on each node");
+    Array.from(nodes).forEach(node => TestUtils.Simulate.mouseOut(node));
+
+    is(called, 2, "onDOMNodeMouseOut is called when mouseout is fired on each NodeRep");
+  }
+
+  function testOnDomNodeInspectIconClick() {
+    const stub = getGripStub("testObjectWithNodes");
+
+    const grips = getSelectableInInspectorGrips(stub);
+    is(grips.length, 2, "the stub has two node grips");
+    const attachedActorIds = getStubAttachedActorIds(grips);
+
+    let inspectIconClickedValue = null;
+    let onInspectIconClick = (object) => {
+      inspectIconClickedValue = object;
+    };
+
+    let renderedComponentWithoutInspectIcon = renderComponent(Grip.rep, {
+      object: stub,
+      onInspectIconClick,
+      attachedActorIds: ["someOtherId"],
+    });
+    is(renderedComponentWithoutInspectIcon.querySelector(".open-inspector"), null,
+      "There isn't an inspect icon when the actor is not in attachedActorIds");
+
+    is(renderedComponentWithoutInspectIcon.querySelector(".open-inspector"), null,
+      "There isn't an inspect icon when attachedActorIds does not have keys " +
+      "matching grip properties");
+
+    const renderedComponent = renderComponent(Grip.rep, {
+      object: stub,
+      onInspectIconClick,
+      attachedActorIds,
+    });
+
+    const icons = renderedComponent.querySelectorAll(".open-inspector");
+    is(icons.length, 2,
+      "There is an icon for each grip property matching an attachedNodeFront");
+
+    icons.forEach((icon, index) => {
+      TestUtils.Simulate.click(icon);
+      is(inspectIconClickedValue, grips[index],
+        "onInspectIconClick is called with the expected argument " +
+        "when the inspect icon is clicked");
+    });
+  }
+
   function getGripStub(functionName) {
     switch (functionName) {
       case "testBasic":
         return {
           "type": "object",
           "class": "Object",
           "actor": "server1.conn0.obj304",
           "extensible": true,
@@ -1012,15 +1125,79 @@ window.onload = Task.async(function* () 
                 },
                 "getterPrototypeLevel": 1,
                 "enumerable": true,
                 "writable": true
               }
             }
           }
         };
+      case "testObjectWithNodes":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj214",
+          "class": "Object",
+          "ownPropertyLength": 2,
+          "preview": {
+            "kind": "Object",
+            "ownProperties": {
+              "foo": {
+                "configurable": true,
+                "enumerable": true,
+                "writable": true,
+                "value": {
+                  "type": "object",
+                  "actor": "server1.conn1.child1/obj215",
+                  "class": "HTMLButtonElement",
+                  "extensible": true,
+                  "frozen": false,
+                  "sealed": false,
+                  "ownPropertyLength": 0,
+                  "preview": {
+                    "kind": "DOMNode",
+                    "nodeType": 1,
+                    "nodeName": "button",
+                    "attributes": {
+                      "id": "btn-1",
+                      "class": "btn btn-log",
+                      "type": "button"
+                    },
+                    "attributesLength": 3
+                  }
+                }
+              },
+              "bar": {
+                "configurable": true,
+                "enumerable": true,
+                "writable": true,
+                "value": {
+                  "type": "object",
+                  "actor": "server1.conn1.child1/obj216",
+                  "class": "HTMLButtonElement",
+                  "extensible": true,
+                  "frozen": false,
+                  "sealed": false,
+                  "ownPropertyLength": 0,
+                  "preview": {
+                    "kind": "DOMNode",
+                    "nodeType": 1,
+                    "nodeName": "button",
+                    "attributes": {
+                      "id": "btn-2",
+                      "class": "btn btn-err",
+                      "type": "button"
+                    },
+                    "attributesLength": 3
+                  }
+                }
+              }
+            },
+            "ownPropertiesLength": 2,
+            "safeGetterValues": {}
+          }
+        };
     }
   }
 });
 </script>
 </pre>
 </body>
 </html>
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_promise.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_promise.html
@@ -14,27 +14,35 @@ Test Promise rep
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 "use strict";
 
 window.onload = Task.async(function* () {
-  const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  const {
+    REPS,
+    MODE,
+    getSelectableInInspectorGrips,
+  } = browserRequire("devtools/client/shared/components/reps/load-reps");
   let { Rep, PromiseRep } = REPS;
 
   const componentUnderTest = PromiseRep;
 
   try {
     yield testPending();
     yield testFulfilledWithNumber();
     yield testFulfilledWithString();
     yield testFulfilledWithObject();
     yield testFulfilledWithArray();
+
+    yield testOnDomNodeMouseOver();
+    yield testOnDomNodeMouseOut();
+    yield testOnDomNodeInspectIconClick();
   } catch (e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
   function testPending() {
     // Test object = `new Promise((resolve, reject) => true)`
@@ -203,16 +211,100 @@ window.onload = Task.async(function* () 
         mode: MODE.LONG,
         expectedOutput: defaultOutput,
       }
     ];
 
     testRepRenderModes(modeTests, "testFulfilledWithArray", componentUnderTest, stub);
   }
 
+  function testOnDomNodeMouseOver() {
+    const stub = getGripStub("testFulfilledWithNode");
+
+    const grips = getSelectableInInspectorGrips(stub);
+    is(grips.length, 1, "the stub has one node grip");
+    const attachedActorIds = getStubAttachedActorIds(grips);
+
+    let mouseOverValue;
+    let onDOMNodeMouseOver = (object) => {
+      mouseOverValue = object;
+    };
+
+    const renderedComponent = renderComponent(PromiseRep.rep, {
+      object: stub,
+      onDOMNodeMouseOver,
+      attachedActorIds,
+    });
+
+    const node = renderedComponent.querySelector(".objectBox-node");
+    TestUtils.Simulate.mouseOver(node);
+
+    is(mouseOverValue, grips[0], "onDOMNodeMouseOver is called with " +
+      "the expected argument when mouseover is fired on the node element");
+  }
+
+  function testOnDomNodeMouseOut() {
+    const stub = getGripStub("testFulfilledWithNode");
+
+    const grips = getSelectableInInspectorGrips(stub);
+    is(grips.length, 1, "the stub has one node grip");
+    const attachedActorIds = getStubAttachedActorIds(grips);
+
+    let called = false;
+    let onDOMNodeMouseOut = (object) => {
+      called = true;
+    };
+    const renderedComponent = renderComponent(PromiseRep.rep, {
+      object: stub,
+      onDOMNodeMouseOut,
+      attachedActorIds,
+    });
+
+    const node = renderedComponent.querySelector(".objectBox-node");
+    TestUtils.Simulate.mouseOut(node);
+
+    is(called, true,
+      "onDOMNodeMouseOut is called when mouseout is fired on the node element");
+  }
+
+  function testOnDomNodeInspectIconClick() {
+    const stub = getGripStub("testFulfilledWithNode");
+
+    const grips = getSelectableInInspectorGrips(stub);
+    is(grips.length, 1, "the stub has one node grip");
+    const attachedActorIds = getStubAttachedActorIds(grips);
+
+    let inspectIconClickedValues = null;
+    let onInspectIconClick = (object) => {
+      inspectIconClickedValues = object;
+    };
+
+    let renderedComponentWithoutInspectIcon = renderComponent(PromiseRep.rep, {
+      object: stub,
+      onInspectIconClick,
+      attachedActorIds: ["someOtherId"],
+    });
+    is(renderedComponentWithoutInspectIcon.querySelector(".open-inspector"), null,
+      "There isn't an inspect icon when the actor is not in attachedActorIds");
+
+    const renderedComponent = renderComponent(PromiseRep.rep, {
+      object: stub,
+      onInspectIconClick,
+      attachedActorIds,
+    });
+
+    const icon = renderedComponent.querySelector(".open-inspector");
+    ok(icon !== null, "There is an inspect icon as expected");
+
+    TestUtils.Simulate.click(icon);
+    is(inspectIconClickedValues, grips[0],
+      "onInspectIconClick is called with the expected argument " +
+      "when the inspect icon is clicked");
+  }
+
   function getGripStub(name) {
     switch (name) {
       case "testPending":
         return {
           "type": "object",
           "actor": "server1.conn1.child1/obj54",
           "class": "Promise",
           "promiseState": {
@@ -318,16 +410,54 @@ window.onload = Task.async(function* () 
           "ownPropertyLength": 0,
           "preview": {
             "kind": "Object",
             "ownProperties": {},
             "ownPropertiesLength": 0,
             "safeGetterValues": {}
           }
         };
+      case "testFulfilledWithNode":
+        return {
+          "type": "object",
+          "actor": "server1.conn1.child1/obj217",
+          "class": "Promise",
+          "promiseState": {
+            "state": "fulfilled",
+            "value": {
+              "type": "object",
+              "actor": "server1.conn1.child1/obj218",
+              "class": "HTMLButtonElement",
+              "extensible": true,
+              "frozen": false,
+              "sealed": false,
+              "ownPropertyLength": 0,
+              "preview": {
+                "kind": "DOMNode",
+                "nodeType": 1,
+                "nodeName": "button",
+                "attributes": {
+                  "id": "btn-1",
+                  "class": "btn btn-log",
+                  "type": "button"
+                },
+                "attributesLength": 3
+              }
+            },
+            "creationTimestamp": 1480423091620.3716,
+            "timeToSettle": 0.02842400000372436
+          },
+          "ownPropertyLength": 0,
+          "preview": {
+            "kind": "Object",
+            "ownProperties": {},
+            "ownPropertiesLength": 0,
+            "safeGetterValues": {}
+          }
+        };
     }
     return null;
   }
 });
 </script>
 </pre>
 </body>
 </html>
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_text-node.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_text-node.html
@@ -14,31 +14,41 @@ Test text-node rep
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 "use strict";
 
 window.onload = Task.async(function* () {
-  const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  const {
+    REPS,
+    MODE,
+    getSelectableInInspectorGrips,
+  } = browserRequire("devtools/client/shared/components/reps/load-reps");
   let { Rep, TextNode } = REPS;
 
   let gripStubs = new Map();
   gripStubs.set("testRendering", {
     "class": "Text",
     "actor": "server1.conn1.child1/obj50",
     "preview": {
+      "kind": "DOMNode",
+      "nodeType": 3,
+      "nodeName": "#text",
       "textContent": "hello world"
     }
   });
   gripStubs.set("testRenderingWithEOL", {
     "class": "Text",
     "actor": "server1.conn1.child1/obj50",
     "preview": {
+      "kind": "DOMNode",
+      "nodeType": 3,
+      "nodeName": "#text",
       "textContent": "hello\nworld"
     }
   });
 
   try {
     // Test that correct rep is chosen
     const renderedRep = shallowRenderComponent(Rep, {
       object: gripStubs.get("testRendering")
@@ -47,16 +57,17 @@ window.onload = Task.async(function* () 
     is(renderedRep.type, TextNode.rep,
       `Rep correctly selects ${TextNode.rep.displayName}`);
 
     yield testRendering();
     yield testRenderingWithEOL();
 
     yield testOnMouseOver();
     yield testOnMouseOut();
+    yield testOnInspectIconClick();
   } catch (e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
   function testRendering() {
     const stub = gripStubs.get("testRendering");
@@ -108,38 +119,86 @@ window.onload = Task.async(function* () 
     ];
 
     testRepRenderModes(modeTests, "testRenderingWithEOL", TextNode, stub);
   }
 
   function testOnMouseOver() {
     const stub = gripStubs.get("testRendering");
 
+    const grips = getSelectableInInspectorGrips(stub);
+    is(grips.length, 1, "the stub has one text node grip");
+    const attachedActorIds = getStubAttachedActorIds(grips);
+
     let mouseOverValue;
     let onDOMNodeMouseOver = (object) => {
       mouseOverValue = object;
     };
-    const renderedComponent = renderComponent(
-      TextNode.rep, {object: stub, onDOMNodeMouseOver});
+    const renderedComponent = renderComponent(TextNode.rep, {
+      object: stub,
+      onDOMNodeMouseOver,
+      attachedActorIds,
+    });
 
     TestUtils.Simulate.mouseOver(renderedComponent);
-    is(mouseOverValue, stub, "onDOMNodeMouseOver is called with the expected argument " +
-      "when mouseover is fired on the Rep");
+    is(mouseOverValue, grips[0], "onDOMNodeMouseOver is called " +
+      "with the expected argument when mouseover is fired on the Rep");
   }
 
   function testOnMouseOut() {
     const stub = gripStubs.get("testRendering");
 
+    const grips = getSelectableInInspectorGrips(stub);
+    is(grips.length, 1, "the stub has one text node grip");
+    const attachedActorIds = getStubAttachedActorIds(grips);
+
     let called = false;
     let onDOMNodeMouseOut = (object) => {
       called = true;
     };
-    const renderedComponent = renderComponent(
-      TextNode.rep, {object: stub, onDOMNodeMouseOut});
+    const renderedComponent = renderComponent(TextNode.rep, {
+      object: stub,
+      onDOMNodeMouseOut,
+      attachedActorIds,
+    });
 
     TestUtils.Simulate.mouseOut(renderedComponent);
     is(called, true, "onDOMNodeMouseOut is called when mouseout is fired on the Rep");
   }
+
+  function testOnInspectIconClick() {
+    const stub = gripStubs.get("testRendering");
+
+    const grips = getSelectableInInspectorGrips(stub);
+    is(grips.length, 1, "the stub has one text node grip");
+    const attachedActorIds = getStubAttachedActorIds(grips);
+
+    let inspectIconClickedValue = null;
+    let onInspectIconClick = (object) => {
+      inspectIconClickedValue = object;
+    };
+
+    const renderedComponentWithoutInspectIcon = renderComponent(TextNode.rep, {
+      object: stub,
+      onInspectIconClick,
+      attachedActorIds: ["someOtherId"]
+    });
+    is(renderedComponentWithoutInspectIcon.querySelector(".open-inspector"), null,
+      "There isn't an inspect icon when the actor is not in attachedActorIds");
+
+    const renderedComponent = renderComponent(TextNode.rep, {
+      object: stub,
+      onInspectIconClick,
+      attachedActorIds,
+    });
+
+    const inspectIconNode = renderedComponent.querySelector(".open-inspector");
+    ok(inspectIconNode !== null, "There is an inspect icon as expected");
+    TestUtils.Simulate.click(inspectIconNode);
+
+    is(inspectIconClickedValue, grips[0],
+      "onInspectIconClick is called with expected value when inspect icon is clicked");
+  }
 });
 </script>
 </pre>
 </body>
 </html>
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_window.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_window.html
@@ -16,17 +16,17 @@ Test window rep
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
   try {
     let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
     let React = browserRequire("devtools/client/shared/vendor/react");
 
-    const { REPS } = browserRequire("devtools/client/shared/components/reps/load-reps");
+    const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/load-reps");
     let { Rep, Window } = REPS;
 
     let gripStub = {
       "type": "object",
       "class": "Window",
       "actor": "server1.conn3.obj198",
       "extensible": true,
       "frozen": false,
@@ -40,18 +40,48 @@ window.onload = Task.async(function* () 
 
     // Test that correct rep is chosen
     const renderedRep = shallowRenderComponent(Rep, { object: gripStub });
     is(renderedRep.type, Window.rep, `Rep correctly selects ${Window.rep.displayName}`);
 
     // Test rendering
     const renderedComponent = renderComponent(Window.rep, { object: gripStub });
     ok(renderedComponent.className.includes("objectBox-Window"), "Window rep has expected class name");
+    is(renderedComponent.textContent, "Window about:newtab", "Window rep has expected text content");
     const innerNode = renderedComponent.querySelector(".objectPropValue");
     is(innerNode.textContent, "about:newtab", "Window rep has expected inner HTML structure and text content");
+
+    const tinyRenderedComponent = renderComponent(Window.rep, {
+      object: gripStub,
+      mode: MODE.TINY,
+    });
+    is(tinyRenderedComponent.textContent, "Window",
+       "Window rep has expected text content in TINY mode");
+
+    const longRenderedComponent = renderComponent(Window.rep, {
+      object: gripStub,
+      mode: MODE.LONG,
+    });
+    is(longRenderedComponent.textContent, "Window about:newtab",
+       "Window rep has expected text content in LONG mode");
+
+    const displayClassRenderedComponent = renderComponent(Window.rep, {
+      object: Object.assign({}, gripStub, {displayClass : "Custom"}),
+      mode: MODE.TINY,
+    });
+    is(displayClassRenderedComponent.textContent, "Custom",
+       "Window rep has expected text content in TINY mode with Custom display class");
+
+    const displayClassLongRenderedComponent = renderComponent(Window.rep, {
+      object: Object.assign({}, gripStub, {displayClass : "Custom"}),
+      mode: MODE.LONG,
+      title: "Custom"
+    });
+    is(displayClassLongRenderedComponent.textContent, "Custom about:newtab",
+       "Window rep has expected text content in LONG mode with Custom display class");
   } catch(e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 });
 </script>
 </pre>
--- a/devtools/client/themes/common.css
+++ b/devtools/client/themes/common.css
@@ -330,17 +330,17 @@ checkbox:-moz-focusring {
   padding: 0 5px;
 }
 
 .devtools-toolbarbutton:not([label]):hover,
 .devtools-button:empty:not(:disabled):hover {
   background: var(--toolbarbutton-background);
 }
 
-.devtools-button:not(:empty):not(:disabled):hover,
+.devtools-button:not(:empty):not(:disabled):not(.checked):hover,
 .devtools-toolbarbutton[label]:not(:-moz-any([checked=true],[disabled])):hover,
 .devtools-button:empty:not(:disabled):-moz-any(:hover:active,.checked),
 .devtools-toolbarbutton:not([label]):-moz-any([checked],[open],:hover:active) {
   background: var(--toolbarbutton-hover-background);
   border-color: var(--toolbarbutton-hover-border-color);
 }
 
 .devtools-button:not(:empty):not(.checked):not(:disabled):hover:active,
--- a/devtools/client/webconsole/net/components/net-info-body.js
+++ b/devtools/client/webconsole/net/components/net-info-body.js
@@ -1,15 +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 React = require("devtools/client/shared/vendor/react");
-const { createFactories } = require("devtools/client/shared/components/reps/load-reps");
+const { createFactories } = require("devtools/client/shared/components/reps/reps");
 const { Tabs, TabPanel } = createFactories(require("devtools/client/shared/components/tabs/tabs"));
 
 // Network
 const HeadersTab = React.createFactory(require("./headers-tab"));
 const ResponseTab = React.createFactory(require("./response-tab"));
 const ParamsTab = React.createFactory(require("./params-tab"));
 const CookiesTab = React.createFactory(require("./cookies-tab"));
 const PostTab = React.createFactory(require("./post-tab"));
--- a/devtools/client/webconsole/net/components/post-tab.js
+++ b/devtools/client/webconsole/net/components/post-tab.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const React = require("devtools/client/shared/vendor/react");
 
 const TreeView = React.createFactory(require("devtools/client/shared/components/tree/tree-view"));
 
-const { REPS, MODE, parseURLEncodedText } = require("devtools/client/shared/components/reps/load-reps");
+const { REPS, MODE, parseURLEncodedText } = require("devtools/client/shared/components/reps/reps");
 const Rep = React.createFactory(REPS.Rep);
 
 // Network
 const NetInfoParams = React.createFactory(require("./net-info-params"));
 const NetInfoGroupList = React.createFactory(require("./net-info-group-list"));
 const Spinner = React.createFactory(require("./spinner"));
 const SizeLimit = React.createFactory(require("./size-limit"));
 const NetUtils = require("../utils/net");
--- a/devtools/client/webconsole/net/components/response-tab.js
+++ b/devtools/client/webconsole/net/components/response-tab.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const React = require("devtools/client/shared/vendor/react");
 
 // Reps
 const TreeView = React.createFactory(require("devtools/client/shared/components/tree/tree-view"));
-const { REPS, MODE } = require("devtools/client/shared/components/reps/load-reps");
+const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
 const Rep = React.createFactory(REPS.Rep);
 
 // Network
 const SizeLimit = React.createFactory(require("./size-limit"));
 const NetInfoGroupList = React.createFactory(require("./net-info-group-list"));
 const Spinner = React.createFactory(require("./spinner"));
 const Json = require("../utils/json");
 const NetUtils = require("../utils/net");
--- a/devtools/client/webconsole/net/net-request.js
+++ b/devtools/client/webconsole/net/net-request.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // React
 const React = require("devtools/client/shared/vendor/react");
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 
 // Reps
-const { parseURLParams } = require("devtools/client/shared/components/reps/load-reps");
+const { parseURLParams } = require("devtools/client/shared/components/reps/reps");
 
 // Network
 const { cancelEvent, isLeftClick } = require("./utils/events");
 const NetInfoBody = React.createFactory(require("./components/net-info-body"));
 const DataProvider = require("./data-provider");
 
 // Constants
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
--- a/devtools/client/webconsole/new-console-output/components/console-table.js
+++ b/devtools/client/webconsole/new-console-output/components/console-table.js
@@ -7,17 +7,17 @@ const {
   createClass,
   createFactory,
   DOM: dom,
   PropTypes
 } = require("devtools/client/shared/vendor/react");
 const { ObjectClient } = require("devtools/shared/client/main");
 const actions = require("devtools/client/webconsole/new-console-output/actions/messages");
 const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
-const { MODE } = require("devtools/client/shared/components/reps/load-reps");
+const { MODE } = require("devtools/client/shared/components/reps/reps");
 const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body"));
 
 const TABLE_ROW_MAX_ITEMS = 1000;
 const TABLE_COLUMN_MAX_ITEMS = 10;
 
 const ConsoleTable = createClass({
 
   displayName: "ConsoleTable",
--- a/devtools/client/webconsole/new-console-output/components/grip-message-body.js
+++ b/devtools/client/webconsole/new-console-output/components/grip-message-body.js
@@ -15,17 +15,17 @@ if (typeof define === "undefined") {
 // React
 const {
   createFactory,
   PropTypes
 } = require("devtools/client/shared/vendor/react");
 
 const VariablesViewLink = createFactory(require("devtools/client/webconsole/new-console-output/components/variables-view-link"));
 
-const { REPS, MODE, createFactories } = require("devtools/client/shared/components/reps/load-reps");
+const { REPS, MODE, createFactories } = require("devtools/client/shared/components/reps/reps");
 const Rep = createFactory(REPS.Rep);
 const Grip = REPS.Grip;
 const StringRep = createFactories(REPS.StringRep).rep;
 
 GripMessageBody.displayName = "GripMessageBody";
 
 GripMessageBody.propTypes = {
   grip: PropTypes.oneOfType([
--- a/devtools/client/webconsole/new-console-output/test/components/console-api-call.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/console-api-call.test.js
@@ -19,34 +19,32 @@ const {
   MESSAGE_CLOSE,
 } = require("devtools/client/webconsole/new-console-output/constants");
 const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/components/message-indent");
 
 // Test fakes.
 const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
 const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
 
-const tempfilePath = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js";
-
 describe("ConsoleAPICall component:", () => {
   describe("console.log", () => {
     it("renders string grips", () => {
       const message = stubPreparedMessages.get("console.log('foobar', 'test')");
       const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
 
       expect(wrapper.find(".message-body").text()).toBe("foobar test");
       expect(wrapper.find(".objectBox-string").length).toBe(2);
       let selector = "div.message.cm-s-mozilla span span.message-flex-body " +
         "span.message-body.devtools-monospace";
       expect(wrapper.find(selector).length).toBe(1);
 
       // There should be the location
       const locationLink = wrapper.find(`.message-location`);
       expect(locationLink.length).toBe(1);
-      expect(locationLink.text()).toBe("test-tempfile.js:1:27");
+      expect(locationLink.text()).toBe("test-console-api.html:1:27");
     });
 
     it("renders string grips with custom style", () => {
       const message = stubPreparedMessages.get("console.log(%cfoobar)");
       const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
 
       const elements = wrapper.find(".objectBox-string");
       expect(elements.text()).toBe("foobar");
@@ -142,22 +140,24 @@ describe("ConsoleAPICall component:", ()
       expect(wrapper.find(".message-body").text()).toMatch(/^bar: \d+(\.\d+)?ms$/);
     });
   });
 
   describe("console.trace", () => {
     it("renders", () => {
       const message = stubPreparedMessages.get("console.trace()");
       const wrapper = render(ConsoleApiCall({ message, serviceContainer, open: true }));
-      const filepath = `${tempfilePath}`;
+      const filepath = "http://example.com/browser/devtools/client/webconsole/" +
+                       "new-console-output/test/fixtures/stub-generators/" +
+                       "test-console-api.html";
 
       expect(wrapper.find(".message-body").text()).toBe("console.trace()");
 
       const frameLinks = wrapper.find(
-        `.stack-trace span.frame-link[data-url='${filepath}']`);
+        `.stack-trace span.frame-link[data-url]`);
       expect(frameLinks.length).toBe(3);
 
       expect(frameLinks.eq(0).find(".frame-link-function-display-name").text())
         .toBe("testStacktraceFiltering");
       expect(frameLinks.eq(0).find(".frame-link-filename").text())
         .toBe(filepath);
 
       expect(frameLinks.eq(1).find(".frame-link-function-display-name").text())
--- a/devtools/client/webconsole/new-console-output/test/components/page-error.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/page-error.test.js
@@ -39,17 +39,17 @@ describe("PageError component:", () => {
     // The stacktrace should be closed by default.
     const frameLinks = wrapper.find(`.stack-trace`);
     expect(frameLinks.length).toBe(0);
 
     // There should be the location.
     const locationLink = wrapper.find(`.message-location`);
     expect(locationLink.length).toBe(1);
     // @TODO Will likely change. See bug 1307952
-    expect(locationLink.text()).toBe("test-tempfile.js:3:5");
+    expect(locationLink.text()).toBe("test-console-api.html:3:5");
   });
 
   it("displays a [Learn more] link", () => {
     const store = setupStore([]);
 
     const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
 
     serviceContainer.openLink = sinon.spy();
@@ -71,19 +71,19 @@ describe("PageError component:", () => {
 
   it("has a stacktrace which can be openned", () => {
     const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
     const wrapper = render(PageError({ message, serviceContainer, open: true }));
 
     // There should be a collapse button.
     expect(wrapper.find(".theme-twisty.open").length).toBe(1);
 
-    // There should be three stacktrace items.
+    // There should be five stacktrace items.
     const frameLinks = wrapper.find(`.stack-trace span.frame-link`);
-    expect(frameLinks.length).toBe(3);
+    expect(frameLinks.length).toBe(5);
   });
 
   it("toggle the stacktrace when the collapse button is clicked", () => {
     const store = setupStore([]);
     store.dispatch = sinon.spy();
     const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
 
     let wrapper = mount(Provider({store},
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser.ini
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser.ini
@@ -1,20 +1,24 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
   head.js
   !/devtools/client/framework/test/shared-head.js
+  ../stubs/*
   test-console-api.html
   test-css-message.html
   test-network-event.html
-  test-tempfile.css
-  test-tempfile.js
 
+[browser_webconsole_check_stubs_console_api.js]
+[browser_webconsole_check_stubs_css_message.js]
+[browser_webconsole_check_stubs_evaluation_result.js]
+[browser_webconsole_check_stubs_network_event.js]
+[browser_webconsole_check_stubs_page_error.js]
 [browser_webconsole_update_stubs_console_api.js]
 skip-if=true # This is only used to update stubs. It is not an actual test.
 [browser_webconsole_update_stubs_css_message.js]
 skip-if=true # This is only used to update stubs. It is not an actual test.
 [browser_webconsole_update_stubs_evaluation_result.js]
 skip-if=true # This is only used to update stubs. It is not an actual test.
 [browser_webconsole_update_stubs_network_event.js]
 skip-if=true # This is only used to update stubs. It is not an actual test.
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_console_api.js
@@ -0,0 +1,23 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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";
+
+Cu.import("resource://gre/modules/osfile.jsm", {});
+
+add_task(function* () {
+  let generatedStubs = yield generateConsoleApiStubs();
+
+  let repoStubFilePath = getTestFilePath("../stubs/consoleApi.js");
+  let repoStubFileContent = yield OS.File.read(repoStubFilePath, { encoding: "utf-8" });
+
+  if (generatedStubs != repoStubFileContent) {
+    ok(false, "The consoleApi stubs file needs to be updated by running " +
+      "`mach test devtools/client/webconsole/new-console-output/test/fixtures/" +
+      "stub-generators/browser_webconsole_update_stubs_console_api.js`");
+  } else {
+    ok(true, "The consoleApi stubs file is up to date");
+  }
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_css_message.js
@@ -0,0 +1,23 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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";
+
+Cu.import("resource://gre/modules/osfile.jsm", {});
+
+add_task(function* () {
+  let generatedStubs = yield generateCssMessageStubs();
+
+  let repoStubFilePath = getTestFilePath("../stubs/cssMessage.js");
+  let repoStubFileContent = yield OS.File.read(repoStubFilePath, { encoding: "utf-8" });
+
+  if (generatedStubs != repoStubFileContent) {
+    ok(false, "The cssMessage stubs file needs to be updated by running " +
+      "`mach test devtools/client/webconsole/new-console-output/test/fixtures/" +
+      "stub-generators/browser_webconsole_update_stubs_css_message.js`");
+  } else {
+    ok(true, "The cssMessage stubs file is up to date");
+  }
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_evaluation_result.js
@@ -0,0 +1,23 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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";
+
+Cu.import("resource://gre/modules/osfile.jsm", {});
+
+add_task(function* () {
+  let generatedStubs = yield generateEvaluationResultStubs();
+
+  let repoStubFilePath = getTestFilePath("../stubs/evaluationResult.js");
+  let repoStubFileContent = yield OS.File.read(repoStubFilePath, { encoding: "utf-8" });
+
+  if (generatedStubs != repoStubFileContent) {
+    ok(false, "The evaluationResult stubs file needs to be updated by running " +
+      "`mach test devtools/client/webconsole/new-console-output/test/fixtures/" +
+      "stub-generators/browser_webconsole_update_stubs_evaluation_result.js`");
+  } else {
+    ok(true, "The evaluationResult stubs file is up to date");
+  }
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_network_event.js
@@ -0,0 +1,23 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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";
+
+Cu.import("resource://gre/modules/osfile.jsm", {});
+
+add_task(function* () {
+  let generatedStubs = yield generateNetworkEventStubs();
+
+  let repoStubFilePath = getTestFilePath("../stubs/networkEvent.js");
+  let repoStubFileContent = yield OS.File.read(repoStubFilePath, { encoding: "utf-8" });
+
+  if (generatedStubs != repoStubFileContent) {
+    ok(false, "The networkEvent stubs file needs to be updated by running " +
+      "`mach test devtools/client/webconsole/new-console-output/test/fixtures/" +
+      "stub-generators/browser_webconsole_update_stubs_network_event.js`");
+  } else {
+    ok(true, "The networkEvent stubs file is up to date");
+  }
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_check_stubs_page_error.js
@@ -0,0 +1,29 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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";
+
+Cu.import("resource://gre/modules/osfile.jsm", {});
+
+add_task(function* () {
+  // On e10s, the exception is triggered in child process
+  // and is ignored by test harness
+  if (!Services.appinfo.browserTabsRemoteAutostart) {
+    expectUncaughtException();
+  }
+
+  let generatedStubs = yield generatePageErrorStubs();
+
+  let repoStubFilePath = getTestFilePath("../stubs/pageError.js");
+  let repoStubFileContent = yield OS.File.read(repoStubFilePath, { encoding: "utf-8" });
+
+  if (generatedStubs != repoStubFileContent) {
+    ok(false, "The pageError stubs file needs to be updated by running " +
+      "`mach test devtools/client/webconsole/new-console-output/test/fixtures/" +
+      "stub-generators/browser_webconsole_update_stubs_page_error.js`");
+  } else {
+    ok(true, "The pageError stubs file is up to date");
+  }
+});
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_console_api.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_console_api.js
@@ -1,58 +1,15 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* 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";
-requestLongerTimeout(2);
 
 Cu.import("resource://gre/modules/osfile.jsm", {});
-const { consoleApi: snippets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js");
-
-const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html";
-
-let stubs = {
-  preparedMessages: [],
-  packets: [],
-};
 
 add_task(function* () {
-  for (let [key, {keys, code}] of snippets) {
-    yield OS.File.writeAtomic(TEMP_FILE_PATH, `function triggerPacket() {${code}}`);
-
-    let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
-    let {ui} = toolbox.getCurrentPanel().hud;
-
-    ok(ui.jsterm, "jsterm exists");
-    ok(ui.newConsoleOutput, "newConsoleOutput exists");
-
-    let received = new Promise(resolve => {
-      let i = 0;
-      let listener = (type, res) => {
-        stubs.packets.push(formatPacket(keys[i], res));
-        stubs.preparedMessages.push(formatStub(keys[i], res));
-        if (++i === keys.length) {
-          toolbox.target.client.removeListener("consoleAPICall", listener);
-          resolve();
-        }
-      };
-      toolbox.target.client.addListener("consoleAPICall", listener);
-    });
-
-    yield ContentTask.spawn(gBrowser.selectedBrowser, key, function (subKey) {
-      let script = content.document.createElement("script");
-      script.src = "test-tempfile.js?key=" + encodeURIComponent(subKey);
-      script.onload = function () {
-        content.wrappedJSObject.triggerPacket();
-      };
-      content.document.body.appendChild(script);
-    });
-
-    yield received;
-
-    yield closeTabAndToolbox();
-  }
+  let fileContent = yield generateConsoleApiStubs();
   let filePath = OS.Path.join(`${BASE_PATH}/stubs`, "consoleApi.js");
-  OS.File.writeAtomic(filePath, formatFile(stubs, "ConsoleMessage"));
-  OS.File.writeAtomic(TEMP_FILE_PATH, "");
+  yield OS.File.writeAtomic(filePath, fileContent);
+  ok(true, "Make the test not fail");
 });
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_css_message.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_css_message.js
@@ -1,48 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 Cu.import("resource://gre/modules/osfile.jsm", {});
-const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html";
-
-const { cssMessage: snippets} = require("devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js");
-
-let stubs = {
-  preparedMessages: [],
-  packets: [],
-};
 
 add_task(function* () {
-  let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
-  ok(true, "make the test not fail");
-
-  for (let [key, code] of snippets) {
-    OS.File.writeAtomic(TEMP_CSS_FILE_PATH, code);
-    let received = new Promise(resolve => {
-      /* CSS errors are considered as pageError on the server */
-      toolbox.target.client.addListener("pageError", function onPacket(e, packet) {
-        toolbox.target.client.removeListener("pageError", onPacket);
-        info("Received css message:" + e + " " + JSON.stringify(packet, null, "\t"));
-
-        let message = prepareMessage(packet, {getNextId: () => 1});
-        stubs.packets.push(formatPacket(message.messageText, packet));
-        stubs.preparedMessages.push(formatStub(message.messageText, packet));
-        resolve();
-      });
-    });
-
-    yield ContentTask.spawn(gBrowser.selectedBrowser, key, function (snippetKey) {
-      let stylesheet = content.document.createElement("link");
-      stylesheet.rel = "stylesheet";
-      stylesheet.href = "test-tempfile.css?key=" + encodeURIComponent(snippetKey);
-      content.document.body.appendChild(stylesheet);
-    });
-
-    yield received;
-  }
-
+  let fileContent = yield generateCssMessageStubs();
   let filePath = OS.Path.join(`${BASE_PATH}/stubs`, "cssMessage.js");
-  OS.File.writeAtomic(filePath, formatFile(stubs, "ConsoleMessage"));
-  OS.File.writeAtomic(TEMP_CSS_FILE_PATH, "");
+  yield OS.File.writeAtomic(filePath, fileContent);
+  ok(true, "Make the test not fail");
 });
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_evaluation_result.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_evaluation_result.js
@@ -1,32 +1,15 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* 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";
 
 Cu.import("resource://gre/modules/osfile.jsm", {});
-const TEST_URI = "data:text/html;charset=utf-8,stub generation";
-
-const { evaluationResult: snippets} = require("devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js");
-
-let stubs = {
-  preparedMessages: [],
-  packets: [],
-};
 
 add_task(function* () {
-  let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
-  ok(true, "make the test not fail");
-
-  for (let [code, key] of snippets) {
-    const packet = yield new Promise(resolve => {
-      toolbox.target.activeConsole.evaluateJS(code, resolve);
-    });
-    stubs.packets.push(formatPacket(key, packet));
-    stubs.preparedMessages.push(formatStub(key, packet));
-  }
-
+  let fileContent = yield generateEvaluationResultStubs();
   let filePath = OS.Path.join(`${BASE_PATH}/stubs`, "evaluationResult.js");
-  OS.File.writeAtomic(filePath, formatFile(stubs, "ConsoleMessage"));
+  yield OS.File.writeAtomic(filePath, fileContent);
+  ok(true, "Make the test not fail");
 });
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_network_event.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_network_event.js
@@ -1,59 +1,15 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* 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";
 
 Cu.import("resource://gre/modules/osfile.jsm", {});
-const TARGET = "networkEvent";
-const { [TARGET]: snippets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js");
-const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html";
-
-let stubs = {
-  preparedMessages: [],
-  packets: [],
-};
 
 add_task(function* () {
-  for (let {keys, code} of snippets.values()) {
-    OS.File.writeAtomic(TEMP_FILE_PATH, `function triggerPacket() {${code}}`);
-    let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
-    let {ui} = toolbox.getCurrentPanel().hud;
-
-    ok(ui.jsterm, "jsterm exists");
-    ok(ui.newConsoleOutput, "newConsoleOutput exists");
-
-    let networkEvent = new Promise(resolve => {
-      let i = 0;
-      toolbox.target.activeConsole.on(TARGET, (type, res) => {
-        stubs.packets.push(formatPacket(keys[i], res));
-        stubs.preparedMessages.push(formatNetworkEventStub(keys[i], res));
-        if (++i === keys.length) {
-          resolve();
-        }
-      });
-    });
-
-    let networkEventUpdate = new Promise(resolve => {
-      let i = 0;
-      ui.jsterm.hud.on("network-message-updated", function onNetworkUpdated(event, res) {
-        ui.jsterm.hud.off("network-message-updated", onNetworkUpdated);
-        stubs.preparedMessages.push(
-          formatNetworkEventStub(`${keys[i++]} ${res.packet.updateType}`, res));
-        if (i === keys.length) {
-          resolve();
-        }
-      });
-    });
-
-    yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
-      content.wrappedJSObject.triggerPacket();
-    });
-
-    yield Promise.all([networkEvent, networkEventUpdate]);
-  }
-  let filePath = OS.Path.join(`${BASE_PATH}/stubs/${TARGET}.js`);
-  OS.File.writeAtomic(filePath, formatFile(stubs, "NetworkEventMessage"));
-  OS.File.writeAtomic(TEMP_FILE_PATH, "");
+  let fileContent = yield generateNetworkEventStubs();
+  let filePath = OS.Path.join(`${BASE_PATH}/stubs/networkEvent.js`);
+  yield OS.File.writeAtomic(filePath, fileContent);
+  ok(true, "Make the test not fail");
 });
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_page_error.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/browser_webconsole_update_stubs_page_error.js
@@ -1,48 +1,15 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* 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";
 
 Cu.import("resource://gre/modules/osfile.jsm", {});
-const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html";
-
-const { pageError: snippets} = require("devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js");
-
-let stubs = {
-  preparedMessages: [],
-  packets: [],
-};
 
 add_task(function* () {
-  let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
-  ok(true, "make the test not fail");
-
-  for (let [key, code] of snippets) {
-    OS.File.writeAtomic(TEMP_FILE_PATH, `${code}`);
-    let received = new Promise(resolve => {
-      toolbox.target.client.addListener("pageError", function onPacket(e, packet) {
-        toolbox.target.client.removeListener("pageError", onPacket);
-        info("Received page error:" + e + " " + JSON.stringify(packet, null, "\t"));
-
-        let message = prepareMessage(packet, {getNextId: () => 1});
-        stubs.packets.push(formatPacket(message.messageText, packet));
-        stubs.preparedMessages.push(formatStub(message.messageText, packet));
-        resolve();
-      });
-    });
-
-    yield ContentTask.spawn(gBrowser.selectedBrowser, key, function (subKey) {
-      let script = content.document.createElement("script");
-      script.src = "test-tempfile.js?key=" + encodeURIComponent(subKey);
-      content.document.body.appendChild(script);
-    });
-
-    yield received;
-  }
-
+  let fileContent = yield generatePageErrorStubs();
   let filePath = OS.Path.join(`${BASE_PATH}/stubs`, "pageError.js");
-  OS.File.writeAtomic(filePath, formatFile(stubs, "ConsoleMessage"));
-  OS.File.writeAtomic(TEMP_FILE_PATH, "");
+  yield OS.File.writeAtomic(filePath, fileContent);
+  ok(true, "Make the test not fail");
 });
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/head.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/head.js
@@ -1,36 +1,41 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* 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/ */
 /* import-globals-from ../../../../../framework/test/shared-head.js */
-/* exported TEMP_FILE_PATH, TEMP_CSS_FILE_PATH, formatPacket, formatStub,
-            formatNetworkEventStub, formatFile */
+/* exported generateConsoleApiStubs, generateCssMessageStubs,
+            generateEvaluationResultStubs, generateNetworkEventStubs,
+            generatePageErrorStubs, BASE_PATH */
 "use strict";
 
 // shared-head.js handles imports, constants, and utility functions
 // Load the shared-head file first.
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
   this);
 
 Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", true);
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
 });
 
 const { prepareMessage } = require("devtools/client/webconsole/new-console-output/utils/messages");
 const { stubPackets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index.js");
+const {
+  consoleApi,
+  cssMessage,
+  evaluationResult,
+  networkEvent,
+  pageError,
+} = require("devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js");
 
 const BASE_PATH =
   "../../../../devtools/client/webconsole/new-console-output/test/fixtures";
-const TEMP_FILE_PATH = OS.Path.join(`${BASE_PATH}/stub-generators`, "test-tempfile.js");
-const TEMP_CSS_FILE_PATH = OS.Path.join(`${BASE_PATH}/stub-generators`,
-                                        "test-tempfile.css");
 
 let cachedPackets = {};
 
 function getCleanedPacket(key, packet) {
   if (Object.keys(cachedPackets).includes(key)) {
     return cachedPackets[key];
   }
 
@@ -51,33 +56,53 @@ function getCleanedPacket(key, packet) {
       from: existingPacket.from
     });
 
     // Clean root timestamp.
     if (res.timestamp) {
       res.timestamp = existingPacket.timestamp;
     }
 
+    if (res.timeStamp) {
+      res.timeStamp = existingPacket.timeStamp;
+    }
+
+    if (res.startedDateTime) {
+      res.startedDateTime = existingPacket.startedDateTime;
+    }
+
+    if (res.actor) {
+      res.actor = existingPacket.actor;
+    }
+
     if (res.message) {
       // Clean timeStamp on the message prop.
       res.message.timeStamp = existingPacket.message.timeStamp;
       if (res.message.timer) {
         // Clean timer properties on the message.
         // Those properties are found on console.time and console.timeEnd calls,
         // and those time can vary, which is why we need to clean them.
         if (res.message.timer.duration) {
           res.message.timer.duration = existingPacket.message.timer.duration;
         }
       }
 
       if (Array.isArray(res.message.arguments)) {
-        // Clean actor ids on each message.arguments item.
         res.message.arguments.forEach((argument, i) => {
+          let existingArgument = existingPacket.message.arguments[i];
+
+          // Clean actor ids on each message.arguments item.
           if (argument && argument.actor) {
-            argument.actor = existingPacket.message.arguments[i].actor;
+            argument.actor = existingArgument.actor;
+          }
+
+          // `window`'s properties count can vary from OS to OS, so we
+          // clean the `ownPropertyLength` property from the grip.
+          if (argument && argument.class === "Window") {
+            argument.ownPropertyLength = existingArgument.ownPropertyLength;
           }
         });
       }
     }
 
     if (res.result) {
       // Clean actor ids on evaluation result messages.
       res.result.actor = existingPacket.result.actor;
@@ -106,16 +131,53 @@ function getCleanedPacket(key, packet) {
       res.eventActor.startedDateTime = existingPacket.eventActor.startedDateTime;
       res.eventActor.timeStamp = existingPacket.eventActor.timeStamp;
     }
 
     if (res.pageError) {
       // Clean timeStamp on pageError messages.
       res.pageError.timeStamp = existingPacket.pageError.timeStamp;
     }
+
+    if (res.packet) {
+      if (res.packet.totalTime) {
+        // res.packet.totalTime is read-only so we use assign to override it.
+        res.packet = Object.assign({}, existingPacket.packet, {
+          totalTime: existingPacket.packet.totalTime
+        });
+      }
+    }
+
+    if (res.networkInfo) {
+      if (res.networkInfo.timeStamp) {
+        res.networkInfo.timeStamp = existingPacket.networkInfo.timeStamp;
+      }
+
+      if (res.networkInfo.startedDateTime) {
+        res.networkInfo.startedDateTime = existingPacket.networkInfo.startedDateTime;
+      }
+
+      if (res.networkInfo.totalTime) {
+        res.networkInfo.totalTime = existingPacket.networkInfo.totalTime;
+      }
+
+      if (res.networkInfo.actor) {
+        res.networkInfo.actor = existingPacket.networkInfo.actor;
+      }
+
+      if (res.networkInfo.request && res.networkInfo.request.headersSize) {
+        res.networkInfo.request.headersSize =
+          existingPacket.networkInfo.request.headersSize;
+      }
+
+      if (res.networkInfo.response && res.networkInfo.response.headersSize) {
+        res.networkInfo.response.headersSize =
+          existingPacket.networkInfo.response.headersSize;
+      }
+    }
   } else {
     res = packet;
   }
 
   cachedPackets[key] = res;
   return res;
 }
 
@@ -129,18 +191,23 @@ function formatStub(key, packet) {
     getCleanedPacket(key, packet),
     {getNextId: () => "1"}
   );
   let stringifiedMessage = JSON.stringify(prepared, null, 2);
   return `stubPreparedMessages.set("${key}", new ConsoleMessage(${stringifiedMessage}));`;
 }
 
 function formatNetworkEventStub(key, packet) {
-  let networkInfo = packet.actor ? packet : packet.networkInfo;
-  let prepared = prepareMessage(networkInfo, {getNextId: () => networkInfo.actor});
+  let cleanedPacket = getCleanedPacket(key, packet);
+  let networkInfo = cleanedPacket.networkInfo ? cleanedPacket.networkInfo : cleanedPacket;
+
+  let prepared = prepareMessage(
+    networkInfo,
+    {getNextId: () => "1"}
+  );
   let stringifiedMessage = JSON.stringify(prepared, null, 2);
   return `stubPreparedMessages.set("${key}", ` +
     `new NetworkEventMessage(${stringifiedMessage}));`;
 }
 
 function formatFile(stubs, type) {
   return `/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
@@ -162,8 +229,213 @@ let stubPackets = new Map();
 ${stubs.packets.join("\n\n")}
 
 module.exports = {
   stubPreparedMessages,
   stubPackets,
 };
 `;
 }
+
+function* generateConsoleApiStubs() {
+  const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html";
+
+  let stubs = {
+    preparedMessages: [],
+    packets: [],
+  };
+
+  let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
+  let {ui} = toolbox.getCurrentPanel().hud;
+  ok(ui.jsterm, "jsterm exists");
+  ok(ui.newConsoleOutput, "newConsoleOutput exists");
+
+  for (let [key, {keys, code}] of consoleApi) {
+    let received = new Promise(resolve => {
+      let i = 0;
+      let listener = (type, res) => {
+        stubs.packets.push(formatPacket(keys[i], res));
+        stubs.preparedMessages.push(formatStub(keys[i], res));
+        if (++i === keys.length) {
+          toolbox.target.client.removeListener("consoleAPICall", listener);
+          resolve();
+        }
+      };
+      toolbox.target.client.addListener("consoleAPICall", listener);
+    });
+
+    yield ContentTask.spawn(
+      gBrowser.selectedBrowser,
+      [key, code],
+      function ([subKey, subCode]) {
+        let script = content.document.createElement("script");
+        script.innerHTML = `function triggerPacket() {${subCode}}`;
+        content.document.body.appendChild(script);
+        content.wrappedJSObject.triggerPacket();
+        script.remove();
+      }
+    );
+
+    yield received;
+  }
+
+  yield closeTabAndToolbox();
+  return formatFile(stubs, "ConsoleMessage");
+}
+
+function* generateCssMessageStubs() {
+  const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html";
+
+  let stubs = {
+    preparedMessages: [],
+    packets: [],
+  };
+
+  let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
+
+  for (let [key, code] of cssMessage) {
+    let received = new Promise(resolve => {
+      /* CSS errors are considered as pageError on the server */
+      toolbox.target.client.addListener("pageError", function onPacket(e, packet) {
+        toolbox.target.client.removeListener("pageError", onPacket);
+        info("Received css message:" + e + " " + JSON.stringify(packet, null, "\t"));
+
+        let message = prepareMessage(packet, {getNextId: () => 1});
+        stubs.packets.push(formatPacket(message.messageText, packet));
+        stubs.preparedMessages.push(formatStub(message.messageText, packet));
+        resolve();
+      });
+    });
+
+    yield ContentTask.spawn(
+      gBrowser.selectedBrowser,
+      [key, code],
+      function ([subKey, subCode]) {
+        let style = content.document.createElement("style");
+        style.innerHTML = subCode;
+        content.document.body.appendChild(style);
+      }
+    );
+
+    yield received;
+  }
+
+  yield closeTabAndToolbox();
+  return formatFile(stubs, "ConsoleMessage");
+}
+
+function* generateEvaluationResultStubs() {
+  const TEST_URI = "data:text/html;charset=utf-8,stub generation";
+
+  let stubs = {
+    preparedMessages: [],
+    packets: [],
+  };
+
+  let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
+
+  for (let [code, key] of evaluationResult) {
+    const packet = yield new Promise(resolve => {
+      toolbox.target.activeConsole.evaluateJS(code, resolve);
+    });
+    stubs.packets.push(formatPacket(key, packet));
+    stubs.preparedMessages.push(formatStub(key, packet));
+  }
+
+  yield closeTabAndToolbox();
+  return formatFile(stubs, "ConsoleMessage");
+}
+
+function* generateNetworkEventStubs() {
+  const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html";
+
+  let stubs = {
+    preparedMessages: [],
+    packets: [],
+  };
+
+  let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
+  let {ui} = toolbox.getCurrentPanel().hud;
+
+  for (let [key, {keys, code}] of networkEvent) {
+    let onNetwork = new Promise(resolve => {
+      let i = 0;
+      toolbox.target.activeConsole.on("networkEvent", function onNetworkEvent(type, res) {
+        stubs.packets.push(formatPacket(keys[i], res));
+        stubs.preparedMessages.push(formatNetworkEventStub(keys[i], res));
+        if (++i === keys.length) {
+          toolbox.target.activeConsole.off("networkEvent", onNetworkEvent);
+          resolve();
+        }
+      });
+    });
+
+    let onNetworkUpdate = new Promise(resolve => {
+      let i = 0;
+      ui.jsterm.hud.on("network-message-updated", function onNetworkUpdated(event, res) {
+        let updateKey = `${keys[i++]} ${res.packet.updateType}`;
+        stubs.packets.push(formatPacket(updateKey, res));
+        stubs.preparedMessages.push(formatNetworkEventStub(updateKey, res));
+        if (i === keys.length) {
+          ui.jsterm.hud.off("network-message-updated", onNetworkUpdated);
+          resolve();
+        }
+      });
+    });
+
+    yield ContentTask.spawn(
+      gBrowser.selectedBrowser,
+      [key, code],
+      function ([subKey, subCode]) {
+        let script = content.document.createElement("script");
+        script.innerHTML = `function triggerPacket() {${subCode}}`;
+        content.document.body.appendChild(script);
+        content.wrappedJSObject.triggerPacket();
+        script.remove();
+      }
+    );
+
+    yield Promise.all([onNetwork, onNetworkUpdate]);
+  }
+
+  yield closeTabAndToolbox();
+  return formatFile(stubs, "NetworkEventMessage");
+}
+
+function* generatePageErrorStubs() {
+  const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html";
+
+  let stubs = {
+    preparedMessages: [],
+    packets: [],
+  };
+
+  let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
+
+  for (let [key, code] of pageError) {
+    let received = new Promise(resolve => {
+      toolbox.target.client.addListener("pageError", function onPacket(e, packet) {
+        let message = prepareMessage(packet, {getNextId: () => 1});
+        stubs.packets.push(formatPacket(message.messageText, packet));
+        stubs.preparedMessages.push(formatStub(message.messageText, packet));
+        resolve();
+      }, {
+        once: true
+      });
+    });
+
+    yield ContentTask.spawn(
+      gBrowser.selectedBrowser,
+      [key, code],
+      function ([subKey, subCode]) {
+        let script = content.document.createElement("script");
+        script.innerHTML = subCode;
+        content.document.body.appendChild(script);
+        script.remove();
+      }
+    );
+
+    yield received;
+  }
+
+  yield closeTabAndToolbox();
+  return formatFile(stubs, "ConsoleMessage");
+}
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/moz.build
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/moz.build
@@ -1,8 +1,8 @@
 # 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(
-    'stub-snippets.js',
+    'stub-snippets.js'
 )
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html
@@ -1,11 +1,10 @@
 <!DOCTYPE html>
 <html lang="en">
   <head>
     <meta charset="utf-8">
     <title>Stub generator</title>
   </head>
   <body>
     <p>Stub generator</p>
-    <script src="test-tempfile.js"></script>
   </body>
 </html>
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html
@@ -1,11 +1,10 @@
 <!DOCTYPE html>
 <html lang="en">
   <head>
     <meta charset="utf-8">
     <title>Stub generator</title>
   </head>
   <body>
     <p>Stub generator</p>
-    <link rel="stylesheet" href="test-tempfile.css"/>
   </body>
 </html>
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html
@@ -1,11 +1,10 @@
 <!DOCTYPE html>
 <html lang="en">
   <head>
     <meta charset="utf-8">
     <title>Stub generator for network event</title>
   </head>
   <body>
     <p>Stub generator for network event</p>
-    <script src="test-tempfile.js"></script>
   </body>
 </html>
deleted file mode 100644
deleted file mode 100644
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
@@ -21,20 +21,20 @@ stubPreparedMessages.set("console.log('f
   "type": "log",
   "level": "log",
   "messageText": null,
   "parameters": [
     "foobar",
     "test"
   ],
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159894798,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foobar\",\"test\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(%27foobar%27%2C%20%27test%27)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159894798,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foobar\",\"test\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(%27foobar%27%2C%20%27test%27)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -48,20 +48,20 @@ stubPreparedMessages.set("console.log(un
   "level": "log",
   "messageText": null,
   "parameters": [
     {
       "type": "undefined"
     }
   ],
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159896036,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"undefined\"}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(undefined)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159896036,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"undefined\"}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(undefined)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -73,20 +73,20 @@ stubPreparedMessages.set("console.warn('
   "timeStamp": 1479159897333,
   "type": "warn",
   "level": "warn",
   "messageText": null,
   "parameters": [
     "danger, will robinson!"
   ],
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159897333,\"type\":\"warn\",\"level\":\"warn\",\"messageText\":null,\"parameters\":[\"danger, will robinson!\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.warn(%27danger%2C%20will%20robinson!%27)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159897333,\"type\":\"warn\",\"level\":\"warn\",\"messageText\":null,\"parameters\":[\"danger, will robinson!\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.warn(%27danger%2C%20will%20robinson!%27)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -100,20 +100,20 @@ stubPreparedMessages.set("console.log(Na
   "level": "log",
   "messageText": null,
   "parameters": [
     {
       "type": "NaN"
     }
   ],
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159898667,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"NaN\"}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(NaN)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159898667,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"NaN\"}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(NaN)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -127,20 +127,20 @@ stubPreparedMessages.set("console.log(nu
   "level": "log",
   "messageText": null,
   "parameters": [
     {
       "type": "null"
     }
   ],
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159900151,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"null\"}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(null)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159900151,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"null\"}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(null)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -152,20 +152,20 @@ stubPreparedMessages.set("console.log('鼬')", new ConsoleMessage({
   "timeStamp": 1479159901470,
   "type": "log",
   "level": "log",
   "messageText": null,
   "parameters": [
     "鼬"
   ],
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159901470,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"鼬\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(%27%E9%BC%AC%27)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159901470,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"鼬\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(%27%E9%BC%AC%27)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -177,20 +177,20 @@ stubPreparedMessages.set("console.clear(
   "timeStamp": 1479159902721,
   "type": "clear",
   "level": "log",
   "messageText": null,
   "parameters": [
     "Console was cleared."
   ],
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159902721,\"type\":\"clear\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"Console was cleared.\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.clear()\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159902721,\"type\":\"clear\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"Console was cleared.\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.clear()",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -200,20 +200,20 @@ stubPreparedMessages.set("console.count(
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1479159903982,
   "type": "log",
   "level": "debug",
   "messageText": "bar: 1",
   "parameters": null,
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159903982,\"type\":\"log\",\"level\":\"debug\",\"messageText\":\"bar: 1\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.count(%27bar%27)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159903982,\"type\":\"log\",\"level\":\"debug\",\"messageText\":\"bar: 1\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.count(%27bar%27)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -246,28 +246,28 @@ stubPreparedMessages.set("console.assert
           }
         },
         "ownPropertiesLength": 1,
         "safeGetterValues": {}
       }
     }
   ],
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159905182,\"type\":\"assert\",\"level\":\"error\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn8.child1/obj31\",\"class\":\"Object\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":1,\"preview\":{\"kind\":\"Object\",\"ownProperties\":{\"message\":{\"configurable\":true,\"enumerable\":true,\"writable\":true,\"value\":\"foobar\"}},\"ownPropertiesLength\":1,\"safeGetterValues\":{}}}],\"repeatId\":null,\"stacktrace\":[{\"columnNumber\":27,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.assert(false%2C%20%7Bmessage%3A%20%27foobar%27%7D)\",\"functionName\":\"triggerPacket\",\"language\":2,\"lineNumber\":1}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.assert(false%2C%20%7Bmessage%3A%20%27foobar%27%7D)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159905182,\"type\":\"assert\",\"level\":\"error\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn8.child1/obj31\",\"class\":\"Object\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":1,\"preview\":{\"kind\":\"Object\",\"ownProperties\":{\"message\":{\"configurable\":true,\"enumerable\":true,\"writable\":true,\"value\":\"foobar\"}},\"ownPropertiesLength\":1,\"safeGetterValues\":{}}}],\"repeatId\":null,\"stacktrace\":[{\"columnNumber\":27,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"functionName\":\"triggerPacket\",\"language\":2,\"lineNumber\":1}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": [
     {
       "columnNumber": 27,
-      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.assert(false%2C%20%7Bmessage%3A%20%27foobar%27%7D)",
+      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
       "functionName": "triggerPacket",
       "language": 2,
       "lineNumber": 1
     }
   ],
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.assert(false%2C%20%7Bmessage%3A%20%27foobar%27%7D)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -279,20 +279,20 @@ stubPreparedMessages.set("console.log('h
   "timeStamp": 1479159906444,
   "type": "log",
   "level": "log",
   "messageText": null,
   "parameters": [
     "hello \nfrom \rthe \"string world!"
   ],
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159906444,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"hello \\nfrom \\rthe \\\"string world!\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(%27hello%20%5Cnfrom%20%5Crthe%20%5C%22string%20world!%27)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159906444,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"hello \\nfrom \\rthe \\\"string world!\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(%27hello%20%5Cnfrom%20%5Crthe%20%5C%22string%20world!%27)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -304,20 +304,20 @@ stubPreparedMessages.set("console.log('úṇĩçödê țĕșť')", new ConsoleMessage({
   "timeStamp": 1479159907704,
   "type": "log",
   "level": "log",
   "messageText": null,
   "parameters": [
     "úṇĩçödê țĕșť"
   ],
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159907704,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"úṇĩçödê țĕșť\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(%27%C3%BA%E1%B9%87%C4%A9%C3%A7%C3%B6d%C3%AA%20%C8%9B%C4%95%C8%99%C5%A5%27)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159907704,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"úṇĩçödê țĕșť\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(%27%C3%BA%E1%B9%87%C4%A9%C3%A7%C3%B6d%C3%AA%20%C8%9B%C4%95%C8%99%C5%A5%27)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -341,20 +341,20 @@ stubPreparedMessages.set("console.dirxml
       "ownPropertyLength": 815,
       "preview": {
         "kind": "ObjectWithURL",
         "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html"
       }
     }
   ],
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159908948,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn11.child1/obj31\",\"class\":\"Window\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":815,\"preview\":{\"kind\":\"ObjectWithURL\",\"url\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\"}}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.dirxml(window)\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159908948,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn11.child1/obj31\",\"class\":\"Window\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":815,\"preview\":{\"kind\":\"ObjectWithURL\",\"url\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\"}}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":1,\"column\":27},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.dirxml(window)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 1,
     "column": 27
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -364,42 +364,42 @@ stubPreparedMessages.set("console.trace(
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1479159910198,
   "type": "trace",
   "level": "log",
   "messageText": null,
   "parameters": [],
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159910198,\"type\":\"trace\",\"level\":\"log\",\"messageText\":null,\"parameters\":[],\"repeatId\":null,\"stacktrace\":[{\"columnNumber\":3,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()\",\"functionName\":\"testStacktraceFiltering\",\"language\":2,\"lineNumber\":3},{\"columnNumber\":3,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()\",\"functionName\":\"foo\",\"language\":2,\"lineNumber\":6},{\"columnNumber\":1,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()\",\"functionName\":\"triggerPacket\",\"language\":2,\"lineNumber\":9}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()\",\"line\":3,\"column\":3},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159910198,\"type\":\"trace\",\"level\":\"log\",\"messageText\":null,\"parameters\":[],\"repeatId\":null,\"stacktrace\":[{\"columnNumber\":3,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"functionName\":\"testStacktraceFiltering\",\"language\":2,\"lineNumber\":3},{\"columnNumber\":3,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"functionName\":\"foo\",\"language\":2,\"lineNumber\":6},{\"columnNumber\":1,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"functionName\":\"triggerPacket\",\"language\":2,\"lineNumber\":9}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":3},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": [
     {
       "columnNumber": 3,
-      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()",
+      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
       "functionName": "testStacktraceFiltering",
       "language": 2,
       "lineNumber": 3
     },
     {
       "columnNumber": 3,
-      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()",
+      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
       "functionName": "foo",
       "language": 2,
       "lineNumber": 6
     },
     {
       "columnNumber": 1,
-      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()",
+      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
       "functionName": "triggerPacket",
       "language": 2,
       "lineNumber": 9
     }
   ],
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 3,
     "column": 3
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -409,20 +409,20 @@ stubPreparedMessages.set("console.time('
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1479159911476,
   "type": "nullMessage",
   "level": "log",
   "messageText": null,
   "parameters": null,
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159911476,\"type\":\"nullMessage\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.time(%27bar%27)\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159911476,\"type\":\"nullMessage\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.time(%27bar%27)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 2,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -432,20 +432,20 @@ stubPreparedMessages.set("console.timeEn
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1479159911478,
   "type": "timeEnd",
   "level": "log",
   "messageText": "bar: 1.36ms",
   "parameters": null,
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159911478,\"type\":\"timeEnd\",\"level\":\"log\",\"messageText\":\"bar: 1.36ms\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.time(%27bar%27)\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159911478,\"type\":\"timeEnd\",\"level\":\"log\",\"messageText\":\"bar: 1.36ms\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.time(%27bar%27)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 3,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -457,20 +457,20 @@ stubPreparedMessages.set("console.table(
   "timeStamp": 1479159912655,
   "type": "log",
   "level": "log",
   "messageText": null,
   "parameters": [
     "bar"
   ],
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159912655,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"bar\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.table(%27bar%27)\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159912655,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"bar\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.table(%27bar%27)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 2,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -499,20 +499,20 @@ stubPreparedMessages.set("console.table(
           "a",
           "b",
           "c"
         ]
       }
     }
   ],
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159913807,\"type\":\"table\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn15.child1/obj31\",\"class\":\"Array\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":4,\"preview\":{\"kind\":\"ArrayLike\",\"length\":3,\"items\":[\"a\",\"b\",\"c\"]}}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.table(%5B%27a%27%2C%20%27b%27%2C%20%27c%27%5D)\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159913807,\"type\":\"table\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn15.child1/obj31\",\"class\":\"Array\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":4,\"preview\":{\"kind\":\"ArrayLike\",\"length\":3,\"items\":[\"a\",\"b\",\"c\"]}}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.table(%5B%27a%27%2C%20%27b%27%2C%20%27c%27%5D)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 2,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -522,20 +522,20 @@ stubPreparedMessages.set("console.group(
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1479159914984,
   "type": "startGroup",
   "level": "log",
   "messageText": "bar",
   "parameters": null,
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159914984,\"type\":\"startGroup\",\"level\":\"log\",\"messageText\":\"bar\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group(%27bar%27)\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159914984,\"type\":\"startGroup\",\"level\":\"log\",\"messageText\":\"bar\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group(%27bar%27)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 2,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -545,20 +545,20 @@ stubPreparedMessages.set("console.groupE
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1479159914987,
   "type": "endGroup",
   "level": "log",
   "messageText": null,
   "parameters": null,
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159914987,\"type\":\"endGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group(%27bar%27)\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159914987,\"type\":\"endGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group(%27bar%27)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 3,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -568,20 +568,20 @@ stubPreparedMessages.set("console.groupC
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1479159916153,
   "type": "startGroupCollapsed",
   "level": "log",
   "messageText": "foo",
   "parameters": null,
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159916153,\"type\":\"startGroupCollapsed\",\"level\":\"log\",\"messageText\":\"foo\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.groupCollapsed(%27foo%27)\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159916153,\"type\":\"startGroupCollapsed\",\"level\":\"log\",\"messageText\":\"foo\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.groupCollapsed(%27foo%27)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 2,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -591,20 +591,20 @@ stubPreparedMessages.set("console.groupE
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1479159916155,
   "type": "endGroup",
   "level": "log",
   "messageText": null,
   "parameters": null,
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159916155,\"type\":\"endGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.groupCollapsed(%27foo%27)\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159916155,\"type\":\"endGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.groupCollapsed(%27foo%27)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 3,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -614,20 +614,20 @@ stubPreparedMessages.set("console.group(
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1479159917524,
   "type": "startGroup",
   "level": "log",
   "messageText": "<no group label>",
   "parameters": null,
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159917524,\"type\":\"startGroup\",\"level\":\"log\",\"messageText\":\"<no group label>\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group()\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159917524,\"type\":\"startGroup\",\"level\":\"log\",\"messageText\":\"<no group label>\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group()",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 2,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -637,20 +637,20 @@ stubPreparedMessages.set("console.groupE
   "allowRepeating": true,
   "source": "console-api",
   "timeStamp": 1479159917526,
   "type": "endGroup",
   "level": "log",
   "messageText": null,
   "parameters": null,
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159917526,\"type\":\"endGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group()\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159917526,\"type\":\"endGroup\",\"level\":\"log\",\"messageText\":null,\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group()",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 3,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [],
   "notes": null
 }));
@@ -663,20 +663,20 @@ stubPreparedMessages.set("console.log(%c
   "type": "log",
   "level": "log",
   "messageText": null,
   "parameters": [
     "foo",
     "bar"
   ],
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159919144,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foo\",\"bar\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(%25cfoobar)\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[\"color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px\",\"color:red;background:url('http://example.com/test')\"],\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"timeStamp\":1479159919144,\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foo\",\"bar\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":1},\"groupId\":null,\"exceptionDocURL\":null,\"userProvidedStyles\":[\"color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px\",\"color:red;background:url('http://example.com/test')\"],\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(%25cfoobar)",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 2,
     "column": 1
   },
   "groupId": null,
   "exceptionDocURL": null,
   "userProvidedStyles": [
     "color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px",
     "color:red;background:url('http://example.com/test')"
@@ -689,17 +689,17 @@ stubPackets.set("console.log('foobar', '
   "type": "consoleAPICall",
   "message": {
     "arguments": [
       "foobar",
       "test"
     ],
     "columnNumber": 27,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(%27foobar%27%2C%20%27test%27)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 1,
     "private": false,
     "styles": [],
     "timeStamp": 1479159894798,
     "timer": null,
@@ -714,17 +714,17 @@ stubPackets.set("console.log(undefined)"
   "message": {
     "arguments": [
       {
         "type": "undefined"
       }
     ],
     "columnNumber": 27,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(undefined)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 1,
     "private": false,
     "styles": [],
     "timeStamp": 1479159896036,
     "timer": null,
@@ -737,17 +737,17 @@ stubPackets.set("console.warn('danger, w
   "from": "server1.conn2.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
     "arguments": [
       "danger, will robinson!"
     ],
     "columnNumber": 27,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.warn(%27danger%2C%20will%20robinson!%27)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "warn",
     "lineNumber": 1,
     "private": false,
     "styles": [],
     "timeStamp": 1479159897333,
     "timer": null,
@@ -762,17 +762,17 @@ stubPackets.set("console.log(NaN)", {
   "message": {
     "arguments": [
       {
         "type": "NaN"
       }
     ],
     "columnNumber": 27,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(NaN)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 1,
     "private": false,
     "styles": [],
     "timeStamp": 1479159898667,
     "timer": null,
@@ -787,17 +787,17 @@ stubPackets.set("console.log(null)", {
   "message": {
     "arguments": [
       {
         "type": "null"
       }
     ],
     "columnNumber": 27,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(null)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 1,
     "private": false,
     "styles": [],
     "timeStamp": 1479159900151,
     "timer": null,
@@ -810,17 +810,17 @@ stubPackets.set("console.log('鼬')", {
   "from": "server1.conn5.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
     "arguments": [
       "鼬"
     ],
     "columnNumber": 27,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(%27%E9%BC%AC%27)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 1,
     "private": false,
     "styles": [],
     "timeStamp": 1479159901470,
     "timer": null,
@@ -831,17 +831,17 @@ stubPackets.set("console.log('鼬')", {
 
 stubPackets.set("console.clear()", {
   "from": "server1.conn6.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
     "arguments": [],
     "columnNumber": 27,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.clear()",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "clear",
     "lineNumber": 1,
     "private": false,
     "timeStamp": 1479159902721,
     "timer": null,
     "workerType": "none",
@@ -857,17 +857,17 @@ stubPackets.set("console.count('bar')", 
     "arguments": [
       "bar"
     ],
     "columnNumber": 27,
     "counter": {
       "count": 1,
       "label": "bar"
     },
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.count(%27bar%27)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "count",
     "lineNumber": 1,
     "private": false,
     "timeStamp": 1479159903982,
     "timer": null,
     "workerType": "none",
@@ -901,29 +901,29 @@ stubPackets.set("console.assert(false, {
           },
           "ownPropertiesLength": 1,
           "safeGetterValues": {}
         }
       }
     ],
     "columnNumber": 27,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.assert(false%2C%20%7Bmessage%3A%20%27foobar%27%7D)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "assert",
     "lineNumber": 1,
     "private": false,
     "styles": [],
     "timeStamp": 1479159905182,
     "timer": null,
     "stacktrace": [
       {
         "columnNumber": 27,
-        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.assert(false%2C%20%7Bmessage%3A%20%27foobar%27%7D)",
+        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
         "functionName": "triggerPacket",
         "language": 2,
         "lineNumber": 1
       }
     ],
     "workerType": "none",
     "category": "webdev"
   }
@@ -933,17 +933,17 @@ stubPackets.set("console.log('hello \nfr
   "from": "server1.conn9.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
     "arguments": [
       "hello \nfrom \rthe \"string world!"
     ],
     "columnNumber": 27,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(%27hello%20%5Cnfrom%20%5Crthe%20%5C%22string%20world!%27)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 1,
     "private": false,
     "styles": [],
     "timeStamp": 1479159906444,
     "timer": null,
@@ -956,17 +956,17 @@ stubPackets.set("console.log('úṇĩçödê țĕșť')", {
   "from": "server1.conn10.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
     "arguments": [
       "úṇĩçödê țĕșť"
     ],
     "columnNumber": 27,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(%27%C3%BA%E1%B9%87%C4%A9%C3%A7%C3%B6d%C3%AA%20%C8%9B%C4%95%C8%99%C5%A5%27)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 1,
     "private": false,
     "styles": [],
     "timeStamp": 1479159907704,
     "timer": null,
@@ -991,17 +991,17 @@ stubPackets.set("console.dirxml(window)"
         "preview": {
           "kind": "ObjectWithURL",
           "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html"
         }
       }
     ],
     "columnNumber": 27,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.dirxml(window)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "dirxml",
     "lineNumber": 1,
     "private": false,
     "timeStamp": 1479159908948,
     "timer": null,
     "workerType": "none",
@@ -1012,42 +1012,42 @@ stubPackets.set("console.dirxml(window)"
 
 stubPackets.set("console.trace()", {
   "from": "server1.conn12.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
     "arguments": [],
     "columnNumber": 3,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "testStacktraceFiltering",
     "groupName": "",
     "level": "trace",
     "lineNumber": 3,
     "private": false,
     "timeStamp": 1479159910198,
     "timer": null,
     "stacktrace": [
       {
         "columnNumber": 3,
-        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()",
+        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
         "functionName": "testStacktraceFiltering",
         "language": 2,
         "lineNumber": 3
       },
       {
         "columnNumber": 3,
-        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()",
+        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
         "functionName": "foo",
         "language": 2,
         "lineNumber": 6
       },
       {
         "columnNumber": 1,
-        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()",
+        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
         "functionName": "triggerPacket",
         "language": 2,
         "lineNumber": 9
       }
     ],
     "workerType": "none",
     "styles": [],
     "category": "webdev"
@@ -1058,17 +1058,17 @@ stubPackets.set("console.time('bar')", {
   "from": "server1.conn13.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
     "arguments": [
       "bar"
     ],
     "columnNumber": 1,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.time(%27bar%27)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "time",
     "lineNumber": 2,
     "private": false,
     "timeStamp": 1479159911476,
     "timer": {
       "name": "bar"
@@ -1083,17 +1083,17 @@ stubPackets.set("console.timeEnd('bar')"
   "from": "server1.conn13.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
     "arguments": [
       "bar"
     ],
     "columnNumber": 1,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.time(%27bar%27)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "timeEnd",
     "lineNumber": 3,
     "private": false,
     "timeStamp": 1479159911478,
     "timer": {
       "duration": 1.3550000000000182,
@@ -1109,17 +1109,17 @@ stubPackets.set("console.table('bar')", 
   "from": "server1.conn14.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
     "arguments": [
       "bar"
     ],
     "columnNumber": 1,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.table(%27bar%27)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "table",
     "lineNumber": 2,
     "private": false,
     "timeStamp": 1479159912655,
     "timer": null,
     "workerType": "none",
@@ -1149,17 +1149,17 @@ stubPackets.set("console.table(['a', 'b'
             "b",
             "c"
           ]
         }
       }
     ],
     "columnNumber": 1,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.table(%5B%27a%27%2C%20%27b%27%2C%20%27c%27%5D)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "table",
     "lineNumber": 2,
     "private": false,
     "timeStamp": 1479159913807,
     "timer": null,
     "workerType": "none",
@@ -1172,17 +1172,17 @@ stubPackets.set("console.group('bar')", 
   "from": "server1.conn16.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
     "arguments": [
       "bar"
     ],
     "columnNumber": 1,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group(%27bar%27)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "bar",
     "level": "group",
     "lineNumber": 2,
     "private": false,
     "timeStamp": 1479159914984,
     "timer": null,
     "workerType": "none",
@@ -1195,17 +1195,17 @@ stubPackets.set("console.groupEnd('bar')
   "from": "server1.conn16.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
     "arguments": [
       "bar"
     ],
     "columnNumber": 1,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group(%27bar%27)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "bar",
     "level": "groupEnd",
     "lineNumber": 3,
     "private": false,
     "timeStamp": 1479159914987,
     "timer": null,
     "workerType": "none",
@@ -1218,17 +1218,17 @@ stubPackets.set("console.groupCollapsed(
   "from": "server1.conn17.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
     "arguments": [
       "foo"
     ],
     "columnNumber": 1,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.groupCollapsed(%27foo%27)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "foo",
     "level": "groupCollapsed",
     "lineNumber": 2,
     "private": false,
     "timeStamp": 1479159916153,
     "timer": null,
     "workerType": "none",
@@ -1241,17 +1241,17 @@ stubPackets.set("console.groupEnd('foo')
   "from": "server1.conn17.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
     "arguments": [
       "foo"
     ],
     "columnNumber": 1,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.groupCollapsed(%27foo%27)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "foo",
     "level": "groupEnd",
     "lineNumber": 3,
     "private": false,
     "timeStamp": 1479159916155,
     "timer": null,
     "workerType": "none",
@@ -1262,17 +1262,17 @@ stubPackets.set("console.groupEnd('foo')
 
 stubPackets.set("console.group()", {
   "from": "server1.conn18.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
     "arguments": [],
     "columnNumber": 1,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group()",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "group",
     "lineNumber": 2,
     "private": false,
     "timeStamp": 1479159917524,
     "timer": null,
     "workerType": "none",
@@ -1283,17 +1283,17 @@ stubPackets.set("console.group()", {
 
 stubPackets.set("console.groupEnd()", {
   "from": "server1.conn18.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
     "arguments": [],
     "columnNumber": 1,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.group()",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "groupEnd",
     "lineNumber": 3,
     "private": false,
     "timeStamp": 1479159917526,
     "timer": null,
     "workerType": "none",
@@ -1307,17 +1307,17 @@ stubPackets.set("console.log(%cfoobar)",
   "type": "consoleAPICall",
   "message": {
     "arguments": [
       "foo",
       "bar"
     ],
     "columnNumber": 1,
     "counter": null,
-    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.log(%25cfoobar)",
+    "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "log",
     "lineNumber": 2,
     "private": false,
     "styles": [
       "color:blue;font-size:1.3em;background:url('http://example.com/test');position:absolute;top:10px",
       "color:red;background:url('http://example.com/test')"
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/cssMessage.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/cssMessage.js
@@ -18,88 +18,92 @@ stubPreparedMessages.set("Unknown property ‘such-unknown-property’.  Declaration dropped.", new ConsoleMessage({
   "allowRepeating": true,
   "source": "css",
   "timeStamp": 1479159920406,
   "type": "log",
   "level": "warn",
   "messageText": "Unknown property ‘such-unknown-property’.  Declaration dropped.",
   "parameters": null,
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"css\",\"timeStamp\":1479159920406,\"type\":\"log\",\"level\":\"warn\",\"messageText\":\"Unknown property ‘such-unknown-property’.  Declaration dropped.\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.css?key=Unknown%20property\",\"line\":3,\"column\":23},\"groupId\":null,\"userProvidedStyles\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"css\",\"timeStamp\":1479159920406,\"type\":\"log\",\"level\":\"warn\",\"messageText\":\"Unknown property ‘such-unknown-property’.  Declaration dropped.\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html\",\"line\":3,\"column\":23},\"groupId\":null,\"userProvidedStyles\":null,\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.css?key=Unknown%20property",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html",
     "line": 3,
     "column": 23
   },
   "groupId": null,
-  "userProvidedStyles": null
+  "userProvidedStyles": null,
+  "notes": null
 }));
 
 stubPreparedMessages.set("Error in parsing value for ‘padding-top’.  Declaration dropped.", new ConsoleMessage({
   "id": "1",
   "allowRepeating": true,
   "source": "css",
   "timeStamp": 1479159920465,
   "type": "log",
   "level": "warn",
   "messageText": "Error in parsing value for ‘padding-top’.  Declaration dropped.",
   "parameters": null,
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"css\",\"timeStamp\":1479159920465,\"type\":\"log\",\"level\":\"warn\",\"messageText\":\"Error in parsing value for ‘padding-top’.  Declaration dropped.\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.css?key=Invalid%20property%20value\",\"line\":3,\"column\":15},\"groupId\":null,\"userProvidedStyles\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"css\",\"timeStamp\":1479159920465,\"type\":\"log\",\"level\":\"warn\",\"messageText\":\"Error in parsing value for ‘padding-top’.  Declaration dropped.\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html\",\"line\":3,\"column\":15},\"groupId\":null,\"userProvidedStyles\":null,\"notes\":null}",
   "stacktrace": null,
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.css?key=Invalid%20property%20value",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html",
     "line": 3,
     "column": 15
   },
   "groupId": null,
-  "userProvidedStyles": null
+  "userProvidedStyles": null,
+  "notes": null
 }));
 
 stubPackets.set("Unknown property ‘such-unknown-property’.  Declaration dropped.", {
   "from": "server1.conn0.child1/consoleActor2",
   "type": "pageError",
   "pageError": {
     "errorMessage": "Unknown property ‘such-unknown-property’.  Declaration dropped.",
     "errorMessageName": "",
-    "sourceName": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.css?key=Unknown%20property",
+    "sourceName": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html",
     "lineText": "  such-unknown-property: wow;",
     "lineNumber": 3,
     "columnNumber": 23,
     "category": "CSS Parser",
     "timeStamp": 1479159920406,
     "warning": true,
     "error": false,
     "exception": false,
     "strict": false,
     "info": false,
     "private": false,
-    "stacktrace": null
+    "stacktrace": null,
+    "notes": null
   }
 });
 
 stubPackets.set("Error in parsing value for ‘padding-top’.  Declaration dropped.", {
   "from": "server1.conn0.child1/consoleActor2",
   "type": "pageError",
   "pageError": {
     "errorMessage": "Error in parsing value for ‘padding-top’.  Declaration dropped.",
     "errorMessageName": "",
-    "sourceName": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.css?key=Invalid%20property%20value",
+    "sourceName": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-css-message.html",
     "lineText": "  padding-top: invalid value;",
     "lineNumber": 3,
     "columnNumber": 15,
     "category": "CSS Parser",
     "timeStamp": 1479159920465,
     "warning": true,
     "error": false,
     "exception": false,
     "strict": false,
     "info": false,
     "private": false,
-    "stacktrace": null
+    "stacktrace": null,
+    "notes": null
   }
 });
 
 module.exports = {
   stubPreparedMessages,
   stubPackets,
 };
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/networkEvent.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/networkEvent.js
@@ -9,33 +9,33 @@
  */
 
 const { NetworkEventMessage } =
   require("devtools/client/webconsole/new-console-output/types");
 
 let stubPreparedMessages = new Map();
 let stubPackets = new Map();
 stubPreparedMessages.set("GET request", new NetworkEventMessage({
-  "id": "server1.conn0.child1/netEvent30",
+  "id": "1",
   "actor": "server1.conn0.child1/netEvent30",
   "level": "log",
   "isXHR": false,
   "request": {
     "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
     "method": "GET"
   },
   "response": {},
   "source": "network",
   "type": "log",
   "groupId": null,
   "timeStamp": 1487022056850
 }));
 
 stubPreparedMessages.set("GET request eventTimings", new NetworkEventMessage({
-  "id": "server1.conn0.child1/netEvent30",
+  "id": "1",
   "actor": "server1.conn0.child1/netEvent30",
   "level": "log",
   "isXHR": false,
   "request": {
     "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
     "method": "GET",
     "headersSize": 489
   },
@@ -45,38 +45,38 @@ stubPreparedMessages.set("GET request ev
     "statusText": "Not Found",
     "headersSize": 160,
     "remoteAddress": "127.0.0.1",
     "remotePort": 8888
   },
   "source": "network",
   "type": "log",
   "groupId": null,
-  "timeStamp": 1487022056850,
-  "totalTime": 7
+  "timeStamp": 1486927175277,
+  "totalTime": 23
 }));
 
 stubPreparedMessages.set("XHR GET request", new NetworkEventMessage({
-  "id": "server1.conn1.child1/netEvent30",
+  "id": "1",
   "actor": "server1.conn1.child1/netEvent30",
   "level": "log",
   "isXHR": true,
   "request": {
     "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
     "method": "GET"
   },
   "response": {},
   "source": "network",
   "type": "log",
   "groupId": null,
   "timeStamp": 1487022057746
 }));
 
 stubPreparedMessages.set("XHR GET request eventTimings", new NetworkEventMessage({
-  "id": "server1.conn1.child1/netEvent30",
+  "id": "1",
   "actor": "server1.conn1.child1/netEvent30",
   "level": "log",
   "isXHR": true,
   "request": {
     "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
     "method": "GET",
     "headersSize": 489
   },
@@ -86,38 +86,38 @@ stubPreparedMessages.set("XHR GET reques
     "statusText": "Not Found",
     "headersSize": 160,
     "remoteAddress": "127.0.0.1",
     "remotePort": 8888
   },
   "source": "network",
   "type": "log",
   "groupId": null,
-  "timeStamp": 1487022057746,
-  "totalTime": 7
+  "timeStamp": 1486927179824,
+  "totalTime": 51
 }));
 
 stubPreparedMessages.set("XHR POST request", new NetworkEventMessage({
-  "id": "server1.conn2.child1/netEvent30",
+  "id": "1",
   "actor": "server1.conn2.child1/netEvent30",
   "level": "log",
   "isXHR": true,
   "request": {
     "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
     "method": "POST"
   },
   "response": {},
   "source": "network",
   "type": "log",
   "groupId": null,
   "timeStamp": 1487022058414
 }));
 
 stubPreparedMessages.set("XHR POST request eventTimings", new NetworkEventMessage({
-  "id": "server1.conn2.child1/netEvent30",
+  "id": "1",
   "actor": "server1.conn2.child1/netEvent30",
   "level": "log",
   "isXHR": true,
   "request": {
     "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
     "method": "POST",
     "headersSize": 509
   },
@@ -127,18 +127,18 @@ stubPreparedMessages.set("XHR POST reque
     "statusText": "Not Found",
     "headersSize": 160,
     "remoteAddress": "127.0.0.1",
     "remotePort": 8888
   },
   "source": "network",
   "type": "log",
   "groupId": null,
-  "timeStamp": 1487022058414,
-  "totalTime": 11
+  "timeStamp": 1486927183220,
+  "totalTime": 12
 }));
 
 stubPackets.set("GET request", {
   "_type": "NetworkEvent",
   "timeStamp": 1487022056850,
   "node": null,
   "actor": "server1.conn0.child1/netEvent30",
   "discardRequestBody": true,
@@ -149,26 +149,26 @@ stubPackets.set("GET request", {
     "method": "GET"
   },
   "isXHR": false,
   "cause": {
     "type": "img",
     "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
     "stacktrace": [
       {
-        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
+        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
         "lineNumber": 3,
         "columnNumber": 1,
         "functionName": "triggerPacket",
         "asyncCause": null
       },
       {
         "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
-        "lineNumber": 4,
-        "columnNumber": 7,
+        "lineNumber": 7,
+        "columnNumber": 9,
         "functionName": null,
         "asyncCause": null
       },
       {
         "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
         "lineNumber": 53,
         "columnNumber": 20,
         "functionName": null,
@@ -178,16 +178,88 @@ stubPackets.set("GET request", {
   },
   "response": {},
   "timings": {},
   "updates": [],
   "private": false,
   "from": "server1.conn0.child1/consoleActor2"
 });
 
+stubPackets.set("GET request eventTimings", {
+  "packet": {
+    "from": "server1.conn0.child1/netEvent30",
+    "type": "networkEventUpdate",
+    "updateType": "eventTimings",
+    "totalTime": 23
+  },
+  "networkInfo": {
+    "_type": "NetworkEvent",
+    "timeStamp": 1486927175277,
+    "node": null,
+    "actor": "server1.conn0.child1/netEvent30",
+    "discardRequestBody": true,
+    "discardResponseBody": false,
+    "startedDateTime": "2017-02-12T19:19:35.277Z",
+    "request": {
+      "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
+      "method": "GET",
+      "headersSize": 489
+    },
+    "isXHR": false,
+    "cause": {
+      "type": "img",
+      "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
+      "stacktrace": [
+        {
+          "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
+          "lineNumber": 3,
+          "columnNumber": 1,
+          "functionName": "triggerPacket",
+          "asyncCause": null
+        },
+        {
+          "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
+          "lineNumber": 7,
+          "columnNumber": 9,
+          "functionName": null,
+          "asyncCause": null
+        },
+        {
+          "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
+          "lineNumber": 53,
+          "columnNumber": 20,
+          "functionName": null,
+          "asyncCause": null
+        }
+      ]
+    },
+    "response": {
+      "httpVersion": "HTTP/1.1",
+      "status": "404",
+      "statusText": "Not Found",
+      "headersSize": 160,
+      "remoteAddress": "127.0.0.1",
+      "remotePort": 8888
+    },
+    "timings": {},
+    "updates": [
+      "requestHeaders",
+      "requestCookies",
+      "responseStart",
+      "securityInfo",
+      "responseHeaders",
+      "responseCookies",
+      "eventTimings"
+    ],
+    "private": false,
+    "securityInfo": "insecure",
+    "totalTime": 23
+  }
+});
+
 stubPackets.set("XHR GET request", {
   "_type": "NetworkEvent",
   "timeStamp": 1487022057746,
   "node": null,
   "actor": "server1.conn1.child1/netEvent30",
   "discardRequestBody": true,
   "discardResponseBody": true,
   "startedDateTime": "2017-02-13T21:40:57.746Z",
@@ -196,26 +268,26 @@ stubPackets.set("XHR GET request", {
     "method": "GET"
   },
   "isXHR": true,
   "cause": {
     "type": "xhr",
     "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
     "stacktrace": [
       {
-        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
+        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
         "lineNumber": 4,
         "columnNumber": 1,
         "functionName": "triggerPacket",
         "asyncCause": null
       },
       {
         "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
-        "lineNumber": 4,
-        "columnNumber": 7,
+        "lineNumber": 7,
+        "columnNumber": 9,
         "functionName": null,
         "asyncCause": null
       },
       {
         "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
         "lineNumber": 53,
         "columnNumber": 20,
         "functionName": null,
@@ -225,16 +297,88 @@ stubPackets.set("XHR GET request", {
   },
   "response": {},
   "timings": {},
   "updates": [],
   "private": false,
   "from": "server1.conn1.child1/consoleActor2"
 });
 
+stubPackets.set("XHR GET request eventTimings", {
+  "packet": {
+    "from": "server1.conn1.child1/netEvent30",
+    "type": "networkEventUpdate",
+    "updateType": "eventTimings",
+    "totalTime": 51
+  },
+  "networkInfo": {
+    "_type": "NetworkEvent",
+    "timeStamp": 1486927179824,
+    "node": null,
+    "actor": "server1.conn1.child1/netEvent30",
+    "discardRequestBody": true,
+    "discardResponseBody": false,
+    "startedDateTime": "2017-02-12T19:19:39.824Z",
+    "request": {
+      "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
+      "method": "GET",
+      "headersSize": 489
+    },
+    "isXHR": true,
+    "cause": {
+      "type": "xhr",
+      "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
+      "stacktrace": [
+        {
+          "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
+          "lineNumber": 4,
+          "columnNumber": 1,
+          "functionName": "triggerPacket",
+          "asyncCause": null
+        },
+        {
+          "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
+          "lineNumber": 7,
+          "columnNumber": 9,
+          "functionName": null,
+          "asyncCause": null
+        },
+        {
+          "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
+          "lineNumber": 53,
+          "columnNumber": 20,
+          "functionName": null,
+          "asyncCause": null
+        }
+      ]
+    },
+    "response": {
+      "httpVersion": "HTTP/1.1",
+      "status": "404",
+      "statusText": "Not Found",
+      "headersSize": 160,
+      "remoteAddress": "127.0.0.1",
+      "remotePort": 8888
+    },
+    "timings": {},
+    "updates": [
+      "requestHeaders",
+      "requestCookies",
+      "responseStart",
+      "securityInfo",
+      "responseHeaders",
+      "responseCookies",
+      "eventTimings"
+    ],
+    "private": false,
+    "securityInfo": "insecure",
+    "totalTime": 51
+  }
+});
+
 stubPackets.set("XHR POST request", {
   "_type": "NetworkEvent",
   "timeStamp": 1487022058414,
   "node": null,
   "actor": "server1.conn2.child1/netEvent30",
   "discardRequestBody": true,
   "discardResponseBody": true,
   "startedDateTime": "2017-02-13T21:40:58.414Z",
@@ -243,26 +387,26 @@ stubPackets.set("XHR POST request", {
     "method": "POST"
   },
   "isXHR": true,
   "cause": {
     "type": "xhr",
     "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
     "stacktrace": [
       {
-        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
+        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
         "lineNumber": 4,
         "columnNumber": 1,
         "functionName": "triggerPacket",
         "asyncCause": null
       },
       {
         "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
-        "lineNumber": 4,
-        "columnNumber": 7,
+        "lineNumber": 7,
+        "columnNumber": 9,
         "functionName": null,
         "asyncCause": null
       },
       {
         "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
         "lineNumber": 53,
         "columnNumber": 20,
         "functionName": null,
@@ -272,12 +416,84 @@ stubPackets.set("XHR POST request", {
   },
   "response": {},
   "timings": {},
   "updates": [],
   "private": false,
   "from": "server1.conn2.child1/consoleActor2"
 });
 
+stubPackets.set("XHR POST request eventTimings", {
+  "packet": {
+    "from": "server1.conn2.child1/netEvent30",
+    "type": "networkEventUpdate",
+    "updateType": "eventTimings",
+    "totalTime": 12
+  },
+  "networkInfo": {
+    "_type": "NetworkEvent",
+    "timeStamp": 1486927183220,
+    "node": null,
+    "actor": "server1.conn2.child1/netEvent30",
+    "discardRequestBody": true,
+    "discardResponseBody": false,
+    "startedDateTime": "2017-02-12T19:19:43.220Z",
+    "request": {
+      "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
+      "method": "POST",
+      "headersSize": 509
+    },
+    "isXHR": true,
+    "cause": {
+      "type": "xhr",
+      "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
+      "stacktrace": [
+        {
+          "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
+          "lineNumber": 4,
+          "columnNumber": 1,
+          "functionName": "triggerPacket",
+          "asyncCause": null
+        },
+        {
+          "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
+          "lineNumber": 7,
+          "columnNumber": 9,
+          "functionName": null,
+          "asyncCause": null
+        },
+        {
+          "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
+          "lineNumber": 53,
+          "columnNumber": 20,
+          "functionName": null,
+          "asyncCause": null
+        }
+      ]
+    },
+    "response": {
+      "httpVersion": "HTTP/1.1",
+      "status": "404",
+      "statusText": "Not Found",
+      "headersSize": 160,
+      "remoteAddress": "127.0.0.1",
+      "remotePort": 8888
+    },
+    "timings": {},
+    "updates": [
+      "requestHeaders",
+      "requestCookies",
+      "responseStart",
+      "securityInfo",
+      "responseHeaders",
+      "responseCookies",
+      "eventTimings"
+    ],
+    "private": false,
+    "securityInfo": "insecure",
+    "totalTime": 12
+  }
+});
+
 module.exports = {
   stubPreparedMessages,
   stubPackets,
 };
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/pageError.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/pageError.js
@@ -18,85 +18,109 @@ stubPreparedMessages.set("ReferenceError
   "allowRepeating": true,
   "source": "javascript",
   "timeStamp": 1476573167137,
   "type": "log",
   "level": "error",
   "messageText": "ReferenceError: asdf is not defined",
   "parameters": null,
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":1476573167137,\"type\":\"log\",\"level\":\"error\",\"messageText\":\"ReferenceError: asdf is not defined\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":[{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error\",\"lineNumber\":3,\"columnNumber\":5,\"functionName\":\"bar\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error\",\"lineNumber\":6,\"columnNumber\":5,\"functionName\":\"foo\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error\",\"lineNumber\":9,\"columnNumber\":3,\"functionName\":null}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error\",\"line\":3,\"column\":5},\"groupId\":null,\"exceptionDocURL\":\"https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default\",\"userProvidedStyles\":null,\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":1476573167137,\"type\":\"log\",\"level\":\"error\",\"messageText\":\"ReferenceError: asdf is not defined\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":[{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"lineNumber\":3,\"columnNumber\":5,\"functionName\":\"bar\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"lineNumber\":6,\"columnNumber\":5,\"functionName\":\"foo\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"lineNumber\":9,\"columnNumber\":3,\"functionName\":null},{\"filename\":\"chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval\",\"lineNumber\":6,\"columnNumber\":9,\"functionName\":null},{\"filename\":\"chrome://mochikit/content/tests/BrowserTestUtils/content-task.js\",\"lineNumber\":53,\"columnNumber\":20,\"functionName\":null}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":5},\"groupId\":null,\"exceptionDocURL\":\"https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default\",\"userProvidedStyles\":null,\"notes\":null}",
   "stacktrace": [
     {
-      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error",
+      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
       "lineNumber": 3,
       "columnNumber": 5,
       "functionName": "bar"
     },
     {
-      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error",
+      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
       "lineNumber": 6,
       "columnNumber": 5,
       "functionName": "foo"
     },
     {
-      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error",
+      "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
       "lineNumber": 9,
       "columnNumber": 3,
       "functionName": null
+    },
+    {
+      "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
+      "lineNumber": 6,
+      "columnNumber": 9,
+      "functionName": null
+    },
+    {
+      "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
+      "lineNumber": 53,
+      "columnNumber": 20,
+      "functionName": null
     }
   ],
   "frame": {
-    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error",
+    "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 3,
     "column": 5
   },
   "groupId": null,
   "exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
   "userProvidedStyles": null,
   "notes": null
 }));
 
 stubPackets.set("ReferenceError: asdf is not defined", {
   "from": "server1.conn0.child1/consoleActor2",
   "type": "pageError",
   "pageError": {
     "errorMessage": "ReferenceError: asdf is not defined",
     "errorMessageName": "JSMSG_NOT_DEFINED",
     "exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
-    "sourceName": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error",
+    "sourceName": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "lineText": "",
     "lineNumber": 3,
     "columnNumber": 5,
     "category": "content javascript",
     "timeStamp": 1476573167137,
     "warning": false,
     "error": false,
     "exception": true,
     "strict": false,
     "info": false,
     "private": false,
     "stacktrace": [
       {
-        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error",
+        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
         "lineNumber": 3,
         "columnNumber": 5,
         "functionName": "bar"
       },
       {
-        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error",
+        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
         "lineNumber": 6,
         "columnNumber": 5,
         "functionName": "foo"
       },
       {
-        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=Reference%20Error",
+        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
         "lineNumber": 9,
         "columnNumber": 3,
         "functionName": null
+      },
+      {
+        "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
+        "lineNumber": 6,
+        "columnNumber": 9,
+        "functionName": null
+      },
+      {
+        "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
+        "lineNumber": 53,
+        "columnNumber": 20,
+        "functionName": null
       }
     ],
     "notes": null
   }
 });
 
 module.exports = {
   stubPreparedMessages,
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -119,16 +119,17 @@
 #include "xpcpublic.h"
 #include "nsIScriptError.h"
 #include "mozilla/Telemetry.h"
 
 #include "mozilla/CORSMode.h"
 
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/HTMLTemplateElement.h"
+#include "mozilla/dom/SVGUseElement.h"
 
 #include "nsStyledElement.h"
 #include "nsIContentInlines.h"
 #include "nsChildContentList.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
@@ -337,16 +338,24 @@ nsIContent::LookupNamespaceURIInternal(c
       return NS_OK;
   } while ((content = content->GetParent()));
   return NS_ERROR_FAILURE;
 }
 
 already_AddRefed<nsIURI>
 nsIContent::GetBaseURI(bool aTryUseXHRDocBaseURI) const
 {
+  if (IsInAnonymousSubtree() && IsAnonymousContentInSVGUseSubtree()) {
+    nsIContent* bindingParent = GetBindingParent();
+    MOZ_ASSERT(bindingParent);
+    SVGUseElement* useElement = static_cast<SVGUseElement*>(bindingParent);
+    // XXX Ignore xml:base as we are removing it.
+    return do_AddRef(useElement->GetContentBaseURI());
+  }
+
   nsIDocument* doc = OwnerDoc();
   // Start with document base
   nsCOMPtr<nsIURI> base = doc->GetBaseURI(aTryUseXHRDocBaseURI);
 
   // Collect array of xml:base attribute values up the parent chain. This
   // is slightly slower for the case when there are xml:base attributes, but
   // faster for the far more common case of there not being any such
   // attributes.
@@ -366,22 +375,16 @@ nsIContent::GetBaseURI(bool aTryUseXHRDo
           // document for the base URI.
           // XXX Will fail with xml:base
           base = binding->PrototypeBinding()->DocURI();
           break;
         }
       }
     }
 
-    nsIURI* explicitBaseURI = elem->GetExplicitBaseURI();
-    if (explicitBaseURI) {
-      base = explicitBaseURI;
-      break;
-    }
-
     // Otherwise check for xml:base attribute
     elem->GetAttr(kNameSpaceID_XML, nsGkAtoms::base, attr);
     if (!attr.IsEmpty()) {
       baseAttrs.AppendElement(attr);
     }
     elem = elem->GetParent();
   } while(elem);
 
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -2143,17 +2143,16 @@ GK_ATOM(animationEffectsForBeforePropert
 GK_ATOM(animationEffectsForAfterProperty, "AnimationsEffectsForAfterProperty") // EffectSet*
 GK_ATOM(cssPseudoElementBeforeProperty, "CSSPseudoElementBeforeProperty") // CSSPseudoElement*
 GK_ATOM(cssPseudoElementAfterProperty, "CSSPseudoElementAfterProperty") // CSSPseudoElement*
 GK_ATOM(transitionsProperty, "TransitionsProperty")        // FrameTransitions*
 GK_ATOM(transitionsOfBeforeProperty, "TransitionsOfBeforeProperty") // FrameTransitions*
 GK_ATOM(transitionsOfAfterProperty, "TransitionsOfAfterProperty") // FrameTransitions*
 GK_ATOM(genConInitializerProperty, "QuoteNodeProperty")
 GK_ATOM(labelMouseDownPtProperty, "LabelMouseDownPtProperty")
-GK_ATOM(baseURIProperty, "baseURIProperty")
 GK_ATOM(lockedStyleStates, "lockedStyleStates")
 GK_ATOM(apzCallbackTransform, "apzCallbackTransform")
 GK_ATOM(restylableAnonymousNode, "restylableAnonymousNode")
 GK_ATOM(paintRequestTime, "PaintRequestTime")
 GK_ATOM(pseudoProperty, "PseudoProperty")  // CSSPseudoElementType
 
 // Languages for lang-specific transforms
 GK_ATOM(Japanese, "ja")
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -10439,18 +10439,20 @@ nsGlobalWindow::EnableVRUpdates()
     mVREventObserver = new VREventObserver(this);
   }
 }
 
 void
 nsGlobalWindow::DisableVRUpdates()
 {
   MOZ_ASSERT(IsInnerWindow());
-
-  mVREventObserver = nullptr;
+  if (mVREventObserver) {
+    mVREventObserver->DisconnectFromOwner();
+    mVREventObserver = nullptr;
+  }
 }
 
 void
 nsGlobalWindow::SetChromeEventHandler(EventTarget* aChromeEventHandler)
 {
   MOZ_ASSERT(IsOuterWindow());
 
   SetChromeEventHandlerInternal(aChromeEventHandler);
@@ -13467,65 +13469,158 @@ nsGlobalWindow::NotifyActiveVRDisplaysCh
     mNavigator->NotifyActiveVRDisplaysChanged();
   }
 }
 
 void
 nsGlobalWindow::DispatchVRDisplayActivate(uint32_t aDisplayID,
                                           mozilla::dom::VRDisplayEventReason aReason)
 {
+  // Search for the display identified with aDisplayID and fire the
+  // event if found.
   for (auto display : mVRDisplays) {
     if (display->DisplayId() == aDisplayID
         && !display->IsAnyPresenting()) {
       // We only want to trigger this event if nobody is presenting to the
       // display already.
 
       VRDisplayEventInit init;
-      init.mBubbles = true;
+      init.mBubbles = false;
       init.mCancelable = false;
       init.mDisplay = display;
       init.mReason.Construct(aReason);
 
       RefPtr<VRDisplayEvent> event =
         VRDisplayEvent::Constructor(this,
                                     NS_LITERAL_STRING("vrdisplayactivate"),
                                     init);
       // vrdisplayactivate is a trusted event, allowing VRDisplay.requestPresent
       // to be used in response to link traversal, user request (chrome UX), and
       // HMD mounting detection sensors.
       event->SetTrusted(true);
       bool defaultActionEnabled;
       Unused << DispatchEvent(event, &defaultActionEnabled);
-      break;
+      // Once we dispatch the event, we must not access any members as an event
+      // listener can do anything, including closing windows.
+      return;
     }
   }
 }
 
 void
 nsGlobalWindow::DispatchVRDisplayDeactivate(uint32_t aDisplayID,
                                             mozilla::dom::VRDisplayEventReason aReason)
 {
+  // Search for the display identified with aDisplayID and fire the
+  // event if found.
   for (auto display : mVRDisplays) {
     if (display->DisplayId() == aDisplayID && display->IsPresenting()) {
       // We only want to trigger this event to content that is presenting to
       // the display already.
 
       VRDisplayEventInit init;
-      init.mBubbles = true;
+      init.mBubbles = false;
       init.mCancelable = false;
       init.mDisplay = display;
       init.mReason.Construct(aReason);
 
       RefPtr<VRDisplayEvent> event =
         VRDisplayEvent::Constructor(this,
                                     NS_LITERAL_STRING("vrdisplaydeactivate"),
                                     init);
+      event->SetTrusted(true);
+      bool defaultActionEnabled;
+      Unused << DispatchEvent(event, &defaultActionEnabled);
+      // Once we dispatch the event, we must not access any members as an event
+      // listener can do anything, including closing windows.
+      return;
+    }
+  }
+}
+
+void
+nsGlobalWindow::DispatchVRDisplayConnect(uint32_t aDisplayID)
+{
+  // Search for the display identified with aDisplayID and fire the
+  // event if found.
+  for (auto display : mVRDisplays) {
+    if (display->DisplayId() == aDisplayID) {
+      // Fire event even if not presenting to the display.
+      VRDisplayEventInit init;
+      init.mBubbles = false;
+      init.mCancelable = false;
+      init.mDisplay = display;
+      // VRDisplayEvent.reason is not set for vrdisplayconnect
+
+      RefPtr<VRDisplayEvent> event =
+        VRDisplayEvent::Constructor(this,
+                                    NS_LITERAL_STRING("vrdisplayconnect"),
+                                    init);
+      event->SetTrusted(true);
       bool defaultActionEnabled;
       Unused << DispatchEvent(event, &defaultActionEnabled);
-      break;
+      // Once we dispatch the event, we must not access any members as an event
+      // listener can do anything, including closing windows.
+      return;
+    }
+  }
+}
+
+void
+nsGlobalWindow::DispatchVRDisplayDisconnect(uint32_t aDisplayID)
+{
+  // Search for the display identified with aDisplayID and fire the
+  // event if found.
+  for (auto display : mVRDisplays) {
+    if (display->DisplayId() == aDisplayID) {
+      // Fire event even if not presenting to the display.
+      VRDisplayEventInit init;
+      init.mBubbles = false;
+      init.mCancelable = false;
+      init.mDisplay = display;
+      // VRDisplayEvent.reason is not set for vrdisplaydisconnect
+
+      RefPtr<VRDisplayEvent> event =
+        VRDisplayEvent::Constructor(this,
+                                    NS_LITERAL_STRING("vrdisplaydisconnect"),
+                                    init);
+      event->SetTrusted(true);
+      bool defaultActionEnabled;
+      Unused << DispatchEvent(event, &defaultActionEnabled);
+      // Once we dispatch the event, we must not access any members as an event
+      // listener can do anything, including closing windows.
+      return;
+    }
+  }
+}
+
+void
+nsGlobalWindow::DispatchVRDisplayPresentChange(uint32_t aDisplayID)
+{
+  // Search for the display identified with aDisplayID and fire the
+  // event if found.
+  for (auto display : mVRDisplays) {
+    if (display->DisplayId() == aDisplayID) {
+      // Fire event even if not presenting to the display.
+      VRDisplayEventInit init;
+      init.mBubbles = false;
+      init.mCancelable = false;
+      init.mDisplay = display;
+      // VRDisplayEvent.reason is not set for vrdisplaypresentchange
+
+      RefPtr<VRDisplayEvent> event =
+        VRDisplayEvent::Constructor(this,
+                                    NS_LITERAL_STRING("vrdisplaypresentchange"),
+                                    init);
+      event->SetTrusted(true);
+      bool defaultActionEnabled;
+      Unused << DispatchEvent(event, &defaultActionEnabled);
+      // Once we dispatch the event, we must not access any members as an event
+      // listener can do anything, including closing windows.
+      return;
     }
   }
 }
 
 // nsGlobalChromeWindow implementation
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalChromeWindow)
 
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -763,16 +763,19 @@ public:
   // Inner windows only.
   // Called to inform that the set of active VR displays has changed.
   void NotifyActiveVRDisplaysChanged();
 
   void DispatchVRDisplayActivate(uint32_t aDisplayID,
                                  mozilla::dom::VRDisplayEventReason aReason);
   void DispatchVRDisplayDeactivate(uint32_t aDisplayID,
                                    mozilla::dom::VRDisplayEventReason aReason);
+  void DispatchVRDisplayConnect(uint32_t aDisplayID);
+  void DispatchVRDisplayDisconnect(uint32_t aDisplayID);
+  void DispatchVRDisplayPresentChange(uint32_t aDisplayID);
 
 #define EVENT(name_, id_, type_, struct_)                                     \
   mozilla::dom::EventHandlerNonNull* GetOn##name_()                           \
   {                                                                           \
     mozilla::EventListenerManager* elm = GetExistingListenerManager();        \
     return elm ? elm->GetEventHandler(nsGkAtoms::on##name_, EmptyString())    \
                : nullptr;                                                     \
   }                                                                           \
@@ -2022,17 +2025,17 @@ protected:
 #endif
 
   // This is the CC generation the last time we called CanSkip.
   uint32_t mCanSkipCCGeneration;
 
   // The VR Displays for this window
   nsTArray<RefPtr<mozilla::dom::VRDisplay>> mVRDisplays;
 
-  nsAutoPtr<mozilla::dom::VREventObserver> mVREventObserver;
+  RefPtr<mozilla::dom::VREventObserver> mVREventObserver;
 
   friend class nsDOMScriptableHelper;
   friend class nsDOMWindowUtils;
   friend class mozilla::dom::PostMessageEvent;
   friend class DesktopNotification;
   friend class mozilla::dom::TimeoutManager;
   friend class IdleRequestExecutor;
 
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -1508,36 +1508,16 @@ nsINode::Unlink(nsINode* tmp)
   }
 
   if (tmp->HasProperties()) {
     nsNodeUtils::UnlinkUserData(tmp);
     tmp->DeleteProperty(nsGkAtoms::keepobjectsalive);
   }
 }
 
-static void
-ReleaseURI(void*, /* aObject*/
-           nsIAtom*, /* aPropertyName */
-           void* aPropertyValue,
-           void* /* aData */)
-{
-  nsIURI* uri = static_cast<nsIURI*>(aPropertyValue);
-  NS_RELEASE(uri);
-}
-
-nsresult
-nsINode::SetExplicitBaseURI(nsIURI* aURI)
-{
-  nsresult rv = SetProperty(nsGkAtoms::baseURIProperty, aURI, ReleaseURI);
-  if (NS_SUCCEEDED(rv)) {
-    NS_ADDREF(aURI);
-  }
-  return rv;
-}
-
 static nsresult
 AdoptNodeIntoOwnerDoc(nsINode *aParent, nsINode *aNode)
 {
   NS_ASSERTION(!aNode->GetParentNode(),
                "Should have removed from parent already");
 
   nsIDocument *doc = aParent->OwnerDoc();
 
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -1286,24 +1286,16 @@ public:
    * supports setting a per-node base URI.
    *
    * @return the base URI
    */
   virtual already_AddRefed<nsIURI> GetBaseURI(bool aTryUseXHRDocBaseURI = false) const = 0;
   already_AddRefed<nsIURI> GetBaseURIObject() const;
 
   /**
-   * Facility for explicitly setting a base URI on a node.
-   */
-  nsresult SetExplicitBaseURI(nsIURI* aURI);
-  /**
-   * The explicit base URI, if set, otherwise null
-   */
-
-  /**
    * Return true if the node may be apz aware. There are two cases. One is that
    * the node is apz aware (such as HTMLInputElement with number type). The
    * other is that the node has apz aware listeners. This is a non-virtual
    * function which calls IsNodeApzAwareInternal only when the MayBeApzAware is
    * set. We check the details in IsNodeApzAwareInternal which may be overriden
    * by child classes
    */
   bool IsNodeApzAware() const
@@ -1324,26 +1316,16 @@ public:
   // HTML elements named <shadow> may or may not be HTMLShadowElement.  This is
   // a way to ask an element whether it's an HTMLShadowElement.
   virtual bool IsHTMLShadowElement() const { return false; }
 
   // Elements named <content> may or may not be HTMLContentElement.  This is a
   // way to ask an element whether it's an HTMLContentElement.
   virtual bool IsHTMLContentElement() const { return false; }
 
-protected:
-  nsIURI* GetExplicitBaseURI() const {
-    if (!HasProperties()) {
-      return nullptr;
-    }
-
-    return static_cast<nsIURI*>(GetProperty(nsGkAtoms::baseURIProperty));
-  }
-
-public:
   void GetTextContent(nsAString& aTextContent,
                       mozilla::OOMReporter& aError)
   {
     GetTextContentInternal(aTextContent, aError);
   }
   void SetTextContent(const nsAString& aTextContent,
                       mozilla::ErrorResult& aError)
   {
--- a/dom/base/nsMappedAttributes.cpp
+++ b/dom/base/nsMappedAttributes.cpp
@@ -12,16 +12,17 @@
 #include "nsMappedAttributes.h"
 #include "nsHTMLStyleSheet.h"
 #include "nsRuleData.h"
 #include "nsRuleWalker.h"
 #include "mozilla/GenericSpecifiedValues.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ServoDeclarationBlock.h"
+#include "mozilla/ServoSpecifiedValues.h"
 
 using namespace mozilla;
 
 nsMappedAttributes::nsMappedAttributes(nsHTMLStyleSheet* aSheet,
                                        nsMapRuleToAttributesFunc aMapRuleFunc)
   : mAttrCount(0),
     mSheet(aSheet),
     mRuleMapper(aMapRuleFunc),
@@ -284,24 +285,19 @@ nsMappedAttributes::SizeOfIncludingThis(
   size_t n = aMallocSizeOf(this);
   for (uint16_t i = 0; i < mAttrCount; ++i) {
     n += Attrs()[i].mValue.SizeOfExcludingThis(aMallocSizeOf);
   }
   return n;
 }
 
 void
-nsMappedAttributes::LazilyResolveServoDeclaration(nsRuleData* aRuleData,
-                                                  nsCSSPropertyID* aIndexToIdMapping,
-                                                  size_t aRuleDataSize)
+nsMappedAttributes::LazilyResolveServoDeclaration(nsPresContext* aContext)
 {
-  MapRuleInfoInto(aRuleData);
 
   MOZ_ASSERT(!mServoStyle,
              "LazilyResolveServoDeclaration should not be called if mServoStyle is already set");
-  mServoStyle = Servo_DeclarationBlock_CreateEmpty().Consume();
-  for (size_t i = 0; i < aRuleDataSize; i++) {
-    nsCSSValue& val = aRuleData->mValueStorage[i];
-    if (val.GetUnit() != eCSSUnit_Null) {
-      Servo_DeclarationBlock_AddPresValue(mServoStyle.get(), aIndexToIdMapping[i], &val);
-    }
+  if (mRuleMapper) {
+    mServoStyle = Servo_DeclarationBlock_CreateEmpty().Consume();
+    ServoSpecifiedValues servo = ServoSpecifiedValues(aContext, mServoStyle.get());
+    (*mRuleMapper)(this, &servo);
   }
 }
--- a/dom/base/nsMappedAttributes.h
+++ b/dom/base/nsMappedAttributes.h
@@ -67,26 +67,19 @@ public:
     return &Attrs()[aPos].mValue;
   }
   // Remove the attr at position aPos.  The value of the attr is placed in
   // aValue; any value that was already in aValue is destroyed.
   void RemoveAttrAt(uint32_t aPos, nsAttrValue& aValue);
   const nsAttrName* GetExistingAttrNameFromQName(const nsAString& aName) const;
   int32_t IndexOfAttr(nsIAtom* aLocalName) const;
 
-  // Apply the contained mapper to an empty nsRuleData object
-  // that is able to contain all properties. Set contained servo declaration block
-  // to result of this computation.
-  // aIndexToIdMapping is
-  // a table that maps from an index in the rule data to the corresponding
-  // property