Merge autoland to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Fri, 05 May 2017 13:22:33 -0700
changeset 356827 e7bf9443be2c4a5187c37440e35f3526148d7fa8
parent 356826 23fe0b76a018a5077a0f7234cff91c41e4b6af64 (current diff)
parent 356824 65f5ee1f5db80261b2e7125c88e28124664209f4 (diff)
child 356828 2fe93c1cb07077c489e969e9f11545155687afd9
push id89970
push userkwierso@gmail.com
push dateFri, 05 May 2017 21:20:56 +0000
treeherdermozilla-inbound@c3d254b2070d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to central, a=merge MozReview-Commit-ID: 8Byspu8RLyg
browser/base/content/gcli_sec_bad.svg
browser/base/content/gcli_sec_good.svg
browser/base/content/gcli_sec_moderate.svg
devtools/client/netmonitor/src/netmonitor-controller.js
devtools/client/netmonitor/src/utils/client.js
devtools/client/netmonitor/test/browser_net_page-nav.js
devtools/client/netmonitor/test/browser_net_simple-init.js
devtools/server/content-globals.js
devtools/shared/tests/mochitest/test_css-logic.html
devtools/shared/tests/mochitest/test_devtools_extensions.html
--- a/addon-sdk/source/lib/sdk/loader/sandbox.js
+++ b/addon-sdk/source/lib/sdk/loader/sandbox.js
@@ -10,39 +10,28 @@ module.metadata = {
 const { Cc, Ci, CC, Cu } = require('chrome');
 const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
 const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1'].
                      getService(Ci.mozIJSSubScriptLoader);
 const self = require('sdk/self');
 const { getTabId } = require('../tabs/utils');
 const { getInnerId } = require('../window/utils');
 
-const { devtools } = Cu.import("resource://devtools/shared/Loader.jsm", {});
-const { require: devtoolsRequire } = devtools;
-const { addContentGlobal, removeContentGlobal } = devtoolsRequire("devtools/server/content-globals");
-
 /**
  * Make a new sandbox that inherits given `source`'s principals. Source can be
  * URI string, DOMWindow or `null` for system principals.
  */
 function sandbox(target, options) {
   options = options || {};
   options.metadata = options.metadata ? options.metadata : {};
   options.metadata.addonID = options.metadata.addonID ?
     options.metadata.addonID : self.id;
 
   let sandbox = Cu.Sandbox(target || systemPrincipal, options);
   Cu.setSandboxMetadata(sandbox, options.metadata);
-  let innerWindowID = options.metadata['inner-window-id']
-  if (innerWindowID) {
-    addContentGlobal({
-      global: sandbox,
-      'inner-window-id': innerWindowID
-    });
-  }
   return sandbox;
 }
 exports.sandbox = sandbox;
 
 /**
  * Evaluates given `source` in a given `sandbox` and returns result.
  */
 function evaluate(sandbox, code, uri, line, version) {
--- a/addon-sdk/source/test/test-page-mod-debug.js
+++ b/addon-sdk/source/test/test-page-mod-debug.js
@@ -5,20 +5,16 @@
 
 const { Cc, Ci, Cu } = require("chrome");
 const { PageMod } = require("sdk/page-mod");
 const { testPageMod, handleReadyState, openNewTab,
         contentScriptWhenServer, createLoader } = require("./page-mod/helpers");
 const { cleanUI, after } = require("sdk/test/utils");
 const { open, getFrames, getMostRecentBrowserWindow, getInnerId } = require("sdk/window/utils");
 
-const { devtools } = Cu.import("resource://devtools/shared/Loader.jsm", {});
-const { require: devtoolsRequire } = devtools;
-const contentGlobals = devtoolsRequire("devtools/server/content-globals");
-
 // The following adds Debugger constructor to the global namespace.
 const { addDebuggerToGlobal } = require('resource://gre/modules/jsdebugger.jsm');
 addDebuggerToGlobal(this);
 
 exports.testDebugMetadata = function(assert, done) {
   let dbg = new Debugger;
   let globalDebuggees = [];
   dbg.onNewGlobalObject = function(global) {
@@ -41,26 +37,14 @@ exports.testDebugMetadata = function(ass
           return false;
         }
       }), "one of the globals is a content script");
       done();
     }
   );
 };
 
-exports.testDevToolsExtensionsGetContentGlobals = function(assert, done) {
-  let mods = testPageMod(assert, done, "about:", [{
-      include: "about:",
-      contentScriptWhen: "start",
-      contentScript: "null;",
-    }], function(win, done) {
-      assert.equal(contentGlobals.getContentGlobals({ 'inner-window-id': getInnerId(win) }).length, 1);
-      done();
-    }
-  );
-};
-
 after(exports, function*(name, assert) {
   assert.pass("cleaning ui.");
   yield cleanUI();
 });
 
 require('sdk/test').run(exports);
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -46,16 +46,18 @@ XPCOMUtils.defineLazyGetter(this, "PageM
   let tmp = {};
   Cu.import("resource://gre/modules/PageMenu.jsm", tmp);
   return new tmp.PageMenuChild();
 });
 XPCOMUtils.defineLazyModuleGetter(this, "WebNavigationFrames",
   "resource://gre/modules/WebNavigationFrames.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Feeds",
   "resource:///modules/Feeds.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "findCssSelector",
+  "resource://gre/modules/css-selector.js");
 
 Cu.importGlobalProperties(["URL"]);
 
 // TabChildGlobal
 var global = this;
 
 // Load the form validation popup handler
 var formSubmitObserver = new FormSubmitObserver(content, this);
@@ -159,16 +161,17 @@ var handleContentContextMenu = function(
       } catch (e) {}
     } catch (e) {}
   }
 
   let selectionInfo = BrowserUtils.getSelectionDetails(content);
 
   let loadContext = docShell.QueryInterface(Ci.nsILoadContext);
   let userContextId = loadContext.originAttributes.userContextId;
+  let popupNodeSelectors = getNodeSelectors(event.target);
 
   if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
     let editFlags = SpellCheckHelper.isEditable(event.target, content);
     let spellInfo;
     if (editFlags &
         (SpellCheckHelper.EDITABLE | SpellCheckHelper.CONTENTEDITABLE)) {
       spellInfo =
         InlineSpellCheckerContent.initContextMenu(event, editFlags, this);
@@ -178,31 +181,37 @@ var handleContentContextMenu = function(
     // determine what was context-clicked on. Then, update the state of the
     // commands on the context menu.
     docShell.contentViewer.QueryInterface(Ci.nsIContentViewerEdit)
             .setCommandNode(event.target);
     event.target.ownerGlobal.updateCommands("contentcontextmenu");
 
     let customMenuItems = PageMenuChild.build(event.target);
     let principal = doc.nodePrincipal;
+
     sendRpcMessage("contextmenu",
                    { editFlags, spellInfo, customMenuItems, addonInfo,
                      principal, docLocation, charSet, baseURI, referrer,
                      referrerPolicy, contentType, contentDisposition,
                      frameOuterWindowID, selectionInfo, disableSetDesktopBg,
-                     loginFillInfo, parentAllowsMixedContent, userContextId },
-                   { event, popupNode: event.target });
+                     loginFillInfo, parentAllowsMixedContent, userContextId,
+                     popupNodeSelectors,
+                   }, {
+                     event,
+                     popupNode: event.target,
+                   });
   } else {
     // Break out to the parent window and pass the add-on info along
     let browser = docShell.chromeEventHandler;
     let mainWin = browser.ownerGlobal;
     mainWin.gContextMenuContentData = {
       isRemote: false,
       event,
       popupNode: event.target,
+      popupNodeSelectors,
       browser,
       addonInfo,
       documentURIObject: doc.documentURIObject,
       docLocation,
       charSet,
       referrer,
       referrerPolicy,
       contentType,
@@ -240,16 +249,38 @@ const MOZILLA_PKIX_ERROR_NOT_YET_VALID_I
 const PREF_BLOCKLIST_CLOCK_SKEW_SECONDS = "services.blocklist.clock_skew_seconds";
 
 const PREF_SSL_IMPACT_ROOTS = ["security.tls.version.", "security.ssl3."];
 
 const PREF_SSL_IMPACT = PREF_SSL_IMPACT_ROOTS.reduce((prefs, root) => {
   return prefs.concat(Services.prefs.getChildList(root));
 }, []);
 
+/**
+ * Retrieve the array of CSS selectors corresponding to the provided node. The first item
+ * of the array is the selector of the node in its owner document. Additional items are
+ * used if the node is inside a frame, each representing the CSS selector for finding the
+ * frame element in its parent document.
+ *
+ * This format is expected by DevTools in order to handle the Inspect Node context menu
+ * item.
+ *
+ * @param  {Node}
+ *         The node for which the CSS selectors should be computed
+ * @return {Array} array of css selectors (strings).
+ */
+function getNodeSelectors(node) {
+  let selectors = [];
+  while (node) {
+    selectors.push(findCssSelector(node));
+    node = node.ownerGlobal.frameElement;
+  }
+
+  return selectors;
+}
 
 function getSerializedSecurityInfo(docShell) {
   let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
                     .getService(Ci.nsISerializationHelper);
 
   let securityInfo = docShell.failedChannel && docShell.failedChannel.securityInfo;
   if (!securityInfo) {
     return "";
--- a/browser/base/content/moz.build
+++ b/browser/base/content/moz.build
@@ -149,19 +149,16 @@ with Files("browser-tabPreviews.xml"):
     BUG_COMPONENT = ("Firefox", "Tabbed Browser")
 
 with Files("contentSearch*"):
     BUG_COMPONENT = ("Firefox", "Search")
 
 with Files("*.svg"):
     BUG_COMPONENT = ("Firefox", "Theme")
 
-with Files("gcli*"):
-    BUG_COMPONENT = ("Core", "DOM: Security")
-
 with Files("hiddenWindow.xul"):
     BUG_COMPONENT = ("Firefox", "Device Permissions")
 
 with Files("macBrowserOverlay.xul"):
     BUG_COMPONENT = ("Firefox", "Shell Integration")
 
 with Files("report-phishing-overlay.xul"):
     BUG_COMPONENT = ("Toolkit", "Safe Browsing")
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -28,16 +28,17 @@ function openContextMenu(aMessage) {
   if (spellInfo)
     spellInfo.target = aMessage.target.messageManager;
   let documentURIObject = makeURI(data.docLocation,
                                   data.charSet,
                                   makeURI(data.baseURI));
   gContextMenuContentData = { isRemote: true,
                               event: aMessage.objects.event,
                               popupNode: aMessage.objects.popupNode,
+                              popupNodeSelectors: data.popupNodeSelectors,
                               browser,
                               editFlags: data.editFlags,
                               spellInfo,
                               principal: data.principal,
                               customMenuItems: data.customMenuItems,
                               addonInfo: data.addonInfo,
                               documentURIObject,
                               docLocation: data.docLocation,
@@ -615,17 +616,17 @@ nsContextMenu.prototype = {
   openPasswordManager() {
     LoginHelper.openPasswordManager(window, gContextMenuContentData.documentURIObject.host);
   },
 
   inspectNode() {
     let gBrowser = this.browser.ownerGlobal.gBrowser;
     let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
     let { gDevToolsBrowser } = require("devtools/client/framework/devtools-browser");
-    return gDevToolsBrowser.inspectNode(gBrowser.selectedTab, this.target);
+    return gDevToolsBrowser.inspectNode(gBrowser.selectedTab, this.targetSelectors);
   },
 
   /**
    * Set various context menu attributes based on the state of the world.
    * Note: If the context menu is on a remote process the supplied parameters
    * will be overwritten with data from gContextMenuContentData.
    *
    * @param {Object} aNode The node that this menu is being opened on.
@@ -697,16 +698,22 @@ nsContextMenu.prototype = {
     }
 
     this.textSelected      = this.selectionInfo.text;
     this.isTextSelected    = this.textSelected.length != 0;
 
     // Remember the node that was clicked.
     this.target = aNode;
 
+    // Remember the CSS selectors corresponding to clicked node. gContextMenuContentData
+    // can be null if the menu was triggered by tests in which case use an empty array.
+    this.targetSelectors = gContextMenuContentData
+                              ? gContextMenuContentData.popupNodeSelectors
+                              : [];
+
     let ownerDoc = this.target.ownerDocument;
     this.ownerDoc = ownerDoc;
 
     let editFlags;
 
     // If this is a remote context menu event, use the information from
     // gContextMenuContentData instead.
     if (this.isRemote) {
--- a/browser/base/content/test/general/browser_tab_detach_restore.js
+++ b/browser/base/content/test/general/browser_tab_detach_restore.js
@@ -20,15 +20,15 @@ add_task(function*() {
 
   is(win.gBrowser.selectedBrowser.permanentKey, key, "Should have properly copied the permanentKey");
   yield BrowserTestUtils.closeWindow(win);
 
   is(SessionStore.getClosedWindowCount(), 1, "Should have restore data for the closed window");
 
   win = SessionStore.undoCloseWindow(0);
   yield BrowserTestUtils.waitForEvent(win, "load");
-  yield BrowserTestUtils.waitForEvent(win.gBrowser.tabs[0], "SSTabRestored");
+  yield BrowserTestUtils.waitForEvent(win.gBrowser.tabContainer, "SSTabRestored");
 
   is(win.gBrowser.tabs.length, 1, "Should have restored one tab");
   is(win.gBrowser.selectedBrowser.currentURI.spec, uri, "Should have restored the right page");
 
   yield promiseWindowClosed(win);
 });
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -112,19 +112,16 @@ browser.jar:
         content/browser/defaultthemes/4.preview.png   (content/defaultthemes/4.preview.png)
         content/browser/defaultthemes/5.footer.png    (content/defaultthemes/5.footer.png)
         content/browser/defaultthemes/5.header.png    (content/defaultthemes/5.header.png)
         content/browser/defaultthemes/5.icon.jpg      (content/defaultthemes/5.icon.jpg)
         content/browser/defaultthemes/5.preview.jpg   (content/defaultthemes/5.preview.jpg)
         content/browser/defaultthemes/compact.header.png    (content/defaultthemes/compact.header.png)
         content/browser/defaultthemes/compactdark.icon.svg  (content/defaultthemes/compactdark.icon.svg)
         content/browser/defaultthemes/compactlight.icon.svg (content/defaultthemes/compactlight.icon.svg)
-        content/browser/gcli_sec_bad.svg              (content/gcli_sec_bad.svg)
-        content/browser/gcli_sec_good.svg             (content/gcli_sec_good.svg)
-        content/browser/gcli_sec_moderate.svg         (content/gcli_sec_moderate.svg)
         content/browser/newtab/newTab.xhtml           (content/newtab/newTab.xhtml)
 *       content/browser/newtab/newTab.js              (content/newtab/newTab.js)
         content/browser/newtab/newTab.css             (content/newtab/newTab.css)
         content/browser/newtab/newTab.inadjacent.json         (content/newtab/newTab.inadjacent.json)
         content/browser/newtab/alternativeDefaultSites.json   (content/newtab/alternativeDefaultSites.json)
 *       content/browser/pageinfo/pageInfo.xul         (content/pageinfo/pageInfo.xul)
         content/browser/pageinfo/pageInfo.js          (content/pageinfo/pageInfo.js)
         content/browser/pageinfo/pageInfo.css         (content/pageinfo/pageInfo.css)
--- a/browser/components/places/content/editBookmarkOverlay.js
+++ b/browser/components/places/content/editBookmarkOverlay.js
@@ -133,35 +133,42 @@ var gEditItemOverlay = {
                         PlacesUIUtils.getItemDescription(this._paneInfo.itemId));
   },
 
   _initKeywordField: Task.async(function* (newKeyword = "") {
     if (!this._paneInfo.isBookmark) {
       throw new Error("_initKeywordField called unexpectedly");
     }
 
+    // Reset the field status synchronously now, eventually we'll reinit it
+    // later if we find an existing keyword. This way we can ensure to be in a
+    // consistent status when reusing the panel across different bookmarks.
+    this._keyword = newKeyword;
+    this._initTextField(this._keywordField, newKeyword);
+
     if (!newKeyword) {
       let entries = [];
       yield PlacesUtils.keywords.fetch({ url: this._paneInfo.uri.spec },
                                        e => entries.push(e));
       if (entries.length > 0) {
         // We show an existing keyword if either POST data was not provided, or
         // if the POST data is the same.
         let existingKeyword = entries[0].keyword;
         let postData = this._paneInfo.postData;
         if (postData) {
           let sameEntry = entries.find(e => e.postData === postData);
           existingKeyword = sameEntry ? sameEntry.keyword : "";
         }
         if (existingKeyword) {
-          this._keyword = newKeyword = existingKeyword;
+          this._keyword = existingKeyword;
+          // Update the text field to the existing keyword.
+          this._initTextField(this._keywordField, this._keyword);
         }
       }
     }
-    this._initTextField(this._keywordField, newKeyword);
   }),
 
   _initLoadInSidebar: Task.async(function* () {
     if (!this._paneInfo.isBookmark)
       throw new Error("_initLoadInSidebar called unexpectedly");
 
     this._loadInSidebarCheckbox.checked =
       PlacesUtils.annotations.itemHasAnnotation(
--- a/browser/components/places/tests/chrome/chrome.ini
+++ b/browser/components/places/tests/chrome/chrome.ini
@@ -4,11 +4,12 @@ support-files = head.js
 [test_0_bug510634.xul]
 [test_0_multiple_left_pane.xul]
 [test_bug1163447_selectItems_through_shortcut.xul]
 [test_bug427633_no_newfolder_if_noip.xul]
 [test_bug485100-change-case-loses-tag.xul]
 [test_bug549192.xul]
 [test_bug549491.xul]
 [test_bug631374_tags_selector_scroll.xul]
+[test_editBookmarkOverlay_keywords.xul]
 [test_editBookmarkOverlay_tags_liveUpdate.xul]
 [test_selectItems_on_nested_tree.xul]
 [test_treeview_date.xul]
new file mode 100644
--- /dev/null
+++ b/browser/components/places/tests/chrome/test_editBookmarkOverlay_keywords.xul
@@ -0,0 +1,99 @@
+<?xml version="1.0"?>
+
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+
+<?xml-stylesheet href="chrome://browser/skin/places/editBookmarkOverlay.css"?>
+<?xml-stylesheet href="chrome://browser/content/places/places.css"?>
+<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
+
+<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
+<?xul-overlay href="chrome://browser/content/places/editBookmarkOverlay.xul"?>
+
+<!DOCTYPE window [
+  <!ENTITY % editBookmarkOverlayDTD SYSTEM "chrome://browser/locale/places/editBookmarkOverlay.dtd">
+  %editBookmarkOverlayDTD;
+]>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="Bug 1343256 - Bookmark keywords disappear from one bookmark when adding a keyword to another bookmark"
+        onload="runTest();">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+  <script type="application/javascript"
+          src="chrome://browser/content/places/editBookmarkOverlay.js"/>
+
+  <body xmlns="http://www.w3.org/1999/xhtml" />
+
+  <vbox id="editBookmarkPanelContent"/>
+
+  <script type="application/javascript">
+  <![CDATA[
+    function runTest() {
+      SimpleTest.waitForExplicitFinish();
+      Task.spawn(test.bind(this))
+          .catch(ex => ok(false, ex))
+          .then(() => PlacesUtils.bookmarks.eraseEverything())
+          .then(SimpleTest.finish);
+    }
+
+    function promiseOnItemChanged() {
+      return new Promise(resolve => {
+        PlacesUtils.bookmarks.addObserver({
+          onBeginUpdateBatch() {},
+          onEndUpdateBatch() {},
+          onItemAdded() {},
+          onItemRemoved() {},
+          onItemVisited() {},
+          onItemMoved() {},
+          onItemChanged(id, property, isAnno, value) {
+            PlacesUtils.bookmarks.removeObserver(this);
+            resolve({ property, value });
+          },
+          QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
+        });
+      });
+    }
+
+    function* test() {
+      ok(gEditItemOverlay, "Sanity check: gEditItemOverlay is in context");
+      let keywordField = document.getElementById("editBMPanel_keywordField");
+
+      for (let i = 0; i < 2; ++i) {
+        let bm = yield PlacesUtils.bookmarks.insert({
+          url: `http://www.test${i}.me/`,
+          parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+        });
+        info(`Init panel on bookmark #${i+1}`);
+        let node = yield PlacesUIUtils.promiseNodeLikeFromFetchInfo(bm);
+        gEditItemOverlay.initPanel({ node });
+        is(document.getElementById("editBMPanel_keywordField").value, "",
+          "The keyword field should be empty");
+        info("Add a keyword to the bookmark");
+        let promise = promiseOnItemChanged();
+        keywordField.focus();
+        keywordField.value = "kw";
+        synthesizeKey(i.toString(), {});
+        synthesizeKey("VK_RETURN", {});
+        keywordField.blur();
+        let {property, value} = yield promise;
+        is(property, "keyword", "The keyword should have been changed");
+        is(value, `kw${i}`, "The new keyword value is correct");
+      }
+
+      for (let i = 0; i < 2; ++i) {
+        let entry = yield PlacesUtils.keywords.fetch({ url: `http://www.test${i}.me/` });
+        is(entry.keyword, `kw${i}`, `The keyword for http://www.test${i}.me/ is correct`);
+      }
+    };
+  ]]>
+  </script>
+
+</window>
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -3223,19 +3223,16 @@ var SessionStoreInternal = {
    *        {firstWindow: true} if this is the first non-private window we're
    *                            restoring in this session, that might open an
    *                            external link as well
    */
   restoreWindow: function ssi_restoreWindow(aWindow, winData, aOptions = {}) {
     let overwriteTabs = aOptions && aOptions.overwriteTabs;
     let isFollowUp = aOptions && aOptions.isFollowUp;
     let firstWindow = aOptions && aOptions.firstWindow;
-    // See SessionStoreInternal.restoreTabs for a description of what
-    // selectTab represents.
-    let selectTab = (overwriteTabs ? parseInt(winData.selected || 1, 10) : 0);
 
     if (isFollowUp) {
       this.windowToFocus = aWindow;
     }
 
     // initialize window if necessary
     if (aWindow && (!aWindow.__SSi || !this._windows[aWindow.__SSi]))
       this.onLoad(aWindow);
@@ -3250,120 +3247,77 @@ var SessionStoreInternal = {
       winData.tabs = [];
     // don't restore a single blank tab when we've had an external
     // URL passed in for loading at startup (cf. bug 357419)
     } else if (firstWindow && !overwriteTabs && winData.tabs.length == 1 &&
              (!winData.tabs[0].entries || winData.tabs[0].entries.length == 0)) {
       winData.tabs = [];
     }
 
-    var tabbrowser = aWindow.gBrowser;
-    var openTabCount = overwriteTabs ? tabbrowser.browsers.length : -1;
-    var newTabCount = winData.tabs.length;
+    // See SessionStoreInternal.restoreTabs for a description of what
+    // selectTab represents.
+    let selectTab = 0;
+    if (overwriteTabs) {
+      selectTab = parseInt(winData.selected || 1, 10);
+      selectTab = Math.max(selectTab, 1);
+      selectTab = Math.min(selectTab, winData.tabs.length);
+    }
+
+    let tabbrowser = aWindow.gBrowser;
+    let tabsToRemove = overwriteTabs ? tabbrowser.browsers.length : 0;
+    let newTabCount = winData.tabs.length;
     var tabs = [];
 
     // disable smooth scrolling while adding, moving, removing and selecting tabs
-    var tabstrip = tabbrowser.tabContainer.mTabstrip;
-    var smoothScroll = tabstrip.smoothScroll;
+    let tabstrip = tabbrowser.tabContainer.mTabstrip;
+    let smoothScroll = tabstrip.smoothScroll;
     tabstrip.smoothScroll = false;
 
-    // unpin all tabs to ensure they are not reordered in the next loop
-    if (overwriteTabs) {
-      for (let t = tabbrowser._numPinnedTabs - 1; t > -1; t--)
-        tabbrowser.unpinTab(tabbrowser.tabs[t]);
-    }
-
     // We need to keep track of the initially open tabs so that they
     // can be moved to the end of the restored tabs.
     let initialTabs = [];
     if (!overwriteTabs && firstWindow) {
       initialTabs = Array.slice(tabbrowser.tabs);
     }
 
-    // make sure that the selected tab won't be closed in order to
-    // prevent unnecessary flickering
-    if (overwriteTabs && tabbrowser.selectedTab._tPos >= newTabCount)
-      tabbrowser.moveTabTo(tabbrowser.selectedTab, newTabCount - 1);
-
     let numVisibleTabs = 0;
 
     let restoreTabsLazily = this._prefBranch.getBoolPref("sessionstore.restore_tabs_lazily") &&
       this._prefBranch.getBoolPref("sessionstore.restore_on_demand");
 
     for (var t = 0; t < newTabCount; t++) {
-      // When trying to restore into existing tab, we also take the userContextId
-      // into account if present.
       let userContextId = winData.tabs[t].userContextId;
-      let createLazyBrowser = restoreTabsLazily && !winData.tabs[t].pinned;
-      let reuseExisting = t < openTabCount &&
-                          (tabbrowser.tabs[t].getAttribute("usercontextid") == (userContextId || ""));
-      let tab = reuseExisting ? this._maybeUpdateBrowserRemoteness(tabbrowser.tabs[t])
-                              : tabbrowser.addTab("about:blank",
-                                                  { createLazyBrowser,
-                                                    skipAnimation: true,
-                                                    userContextId,
-                                                    skipBackgroundNotify: true });
-
-      // If we inserted a new tab because the userContextId didn't match with the
-      // open tab, even though `t < openTabCount`, we need to remove that open tab
-      // and put the newly added tab in its place.
-      if (!reuseExisting && t < openTabCount) {
-        tabbrowser.removeTab(tabbrowser.tabs[t]);
-        tabbrowser.moveTabTo(tab, t);
+      let select = t == selectTab - 1;
+      let createLazyBrowser = restoreTabsLazily && !select && !winData.tabs[t].pinned;
+      let tab = tabbrowser.addTab("about:blank",
+                                  { createLazyBrowser,
+                                    skipAnimation: true,
+                                    userContextId,
+                                    skipBackgroundNotify: true });
+
+      if (select) {
+        // Select a new tab first to prevent the removeTab loop from changing
+        // the selected tab over and over again.
+        tabbrowser.selectedTab = tab;
+
+        // Remove superfluous tabs.
+        for (let i = 0; i < tabsToRemove; i++) {
+          tabbrowser.removeTab(tabbrowser.tabs[0]);
+        }
+        tabsToRemove = 0;
       }
 
       tabs.push(tab);
 
       if (winData.tabs[t].hidden) {
         tabbrowser.hideTab(tabs[t]);
       } else {
         tabbrowser.showTab(tabs[t]);
         numVisibleTabs++;
       }
-
-      if (!!winData.tabs[t].muted != tabs[t].linkedBrowser.audioMuted) {
-        tabs[t].toggleMuteAudio(winData.tabs[t].muteReason);
-      }
-    }
-
-    if (selectTab > 0 && selectTab <= tabs.length) {
-      // The state we're restoring wants to select a particular tab. This
-      // implies that we're overwriting tabs.
-      let currentIndex = tabbrowser.tabContainer.selectedIndex;
-      let targetIndex = selectTab - 1;
-
-      if (currentIndex != targetIndex) {
-        // We need to change the selected tab. There are two ways of doing this:
-        //
-        // 1) The fast path: swap the currently selected tab with the one in the
-        //    position of the selected tab in the restored state. Note that this
-        //    can only work if the user contexts between the two tabs being swapped
-        //    match. This should be the common case.
-        //
-        // 2) The slow path: switch to the selected tab.
-        //
-        // We'll try to do (1), and then fallback to (2).
-
-        let selectedTab = tabbrowser.selectedTab;
-        let tabAtTargetIndex = tabs[targetIndex];
-        let userContextsMatch = selectedTab.userContextId == tabAtTargetIndex.userContextId;
-
-        if (userContextsMatch) {
-          tabbrowser.moveTabTo(selectedTab, targetIndex);
-          tabbrowser.moveTabTo(tabAtTargetIndex, currentIndex);
-          // We also have to do a similar "move" in the aTabs Array to
-          // make sure that the restored content shows up in the right
-          // order.
-          tabs[targetIndex] = tabs[currentIndex];
-          tabs[currentIndex] = tabAtTargetIndex;
-        } else {
-          // Otherwise, go the slow path, and switch to the target tab.
-          tabbrowser.selectedTab = tabs[targetIndex];
-        }
-      }
     }
 
     for (let i = 0; i < newTabCount; ++i) {
       if (winData.tabs[i].pinned) {
         tabbrowser.pinTab(tabs[i]);
       } else {
         // Pinned tabs are clustered at the start of the tab strip. As
         // soon as we reach a tab that isn't pinned, we know there aren't
@@ -3371,51 +3325,34 @@ var SessionStoreInternal = {
         break;
       }
     }
 
     if (!overwriteTabs && firstWindow) {
       // Move the originally open tabs to the end
       let endPosition = tabbrowser.tabs.length - 1;
       for (let i = 0; i < initialTabs.length; i++) {
+        tabbrowser.unpinTab(initialTabs[i]);
         tabbrowser.moveTabTo(initialTabs[i], endPosition);
       }
     }
 
     // if all tabs to be restored are hidden, make the first one visible
     if (!numVisibleTabs && winData.tabs.length) {
       winData.tabs[0].hidden = false;
       tabbrowser.showTab(tabs[0]);
     }
 
-    // If overwriting tabs, we want to reset each tab's "restoring" state. Since
-    // we're overwriting those tabs, they should no longer be restoring. The
-    // tabs will be rebuilt and marked if they need to be restored after loading
-    // state (in restoreTabs).
-    if (overwriteTabs) {
-      for (let i = 0; i < tabbrowser.tabs.length; i++) {
-        let tab = tabbrowser.tabs[i];
-        if (tabbrowser.browsers[i].__SS_restoreState)
-          this._resetTabRestoringState(tab);
-      }
-    }
-
     // We want to correlate the window with data from the last session, so
     // assign another id if we have one. Otherwise clear so we don't do
     // anything with it.
     delete aWindow.__SS_lastSessionWindowID;
     if (winData.__lastSessionWindowID)
       aWindow.__SS_lastSessionWindowID = winData.__lastSessionWindowID;
 
-    // when overwriting tabs, remove all superflous ones
-    if (overwriteTabs && newTabCount < openTabCount) {
-      Array.slice(tabbrowser.tabs, newTabCount, openTabCount)
-           .forEach(tabbrowser.removeTab, tabbrowser);
-    }
-
     if (overwriteTabs) {
       this.restoreWindowFeatures(aWindow, winData);
       delete this._windows[aWindow.__SSi].extData;
     }
 
     // Restore cookies from legacy sessions, i.e. before bug 912717.
     SessionCookies.restore(winData.cookies || []);
 
@@ -3824,17 +3761,16 @@ var SessionStoreInternal = {
       let epoch = this.startNextEpoch(browser);
 
       this._sendRestoreHistory(browser, {
         tabData,
         epoch,
         loadArguments: aLoadArguments,
         isRemotenessUpdate,
       });
-
     }
 
     // If the restored browser wants to show view source content, start up a
     // view source browser that will load the required frame script.
     if (uri && ViewSourceBrowser.isViewSource(uri)) {
       new ViewSourceBrowser(browser);
     }
 
@@ -4073,37 +4009,16 @@ var SessionStoreInternal = {
     }
     this._closedObjectsChanged = false;
     setTimeout(() => {
       Services.obs.notifyObservers(null, NOTIFY_CLOSED_OBJECTS_CHANGED);
     }, 0);
   },
 
   /**
-   * Determines whether or not a tab that is being restored needs
-   * to have its remoteness flipped first.
-   *
-   * @param tab (<xul:tab>):
-   *        The tab being restored.
-   *
-   * @returns tab (<xul:tab>)
-   *        The tab that was passed.
-   */
-  _maybeUpdateBrowserRemoteness(tab) {
-    let win = tab.ownerGlobal;
-    let tabbrowser = win.gBrowser;
-    let browser = tab.linkedBrowser;
-    if (win.gMultiProcessBrowser && !browser.isRemoteBrowser) {
-      tabbrowser.updateBrowserRemoteness(browser, true);
-    }
-
-    return tab;
-  },
-
-  /**
    * Update the session start time and send a telemetry measurement
    * for the number of days elapsed since the session was started.
    *
    * @param state
    *        The session state.
    */
   _updateSessionStartTime: function ssi_updateSessionStartTime(state) {
     // Attempt to load the session start time from the session state
--- a/browser/components/sessionstore/test/browser_423132.js
+++ b/browser/components/sessionstore/test/browser_423132.js
@@ -47,10 +47,10 @@ add_task(function*() {
       break;
   }
   is(cookie.name, cookie2.name, "cookie name successfully restored");
   is(cookie.value, cookie2.value, "cookie value successfully restored");
   is(cookie.path, cookie2.path, "cookie path successfully restored");
 
   // clean up
   Services.cookies.removeAll();
-  yield promiseRemoveTab(tab);
+  yield promiseRemoveTab(gBrowser.tabs[1]);
 });
--- a/browser/components/sessionstore/test/browser_586147.js
+++ b/browser/components/sessionstore/test/browser_586147.js
@@ -17,36 +17,36 @@ function test() {
   let [origTab] = gBrowser.visibleTabs;
   let hiddenTab = gBrowser.addTab();
 
   is(gBrowser.visibleTabs.length, 2, "should have 2 tabs before hiding");
   gBrowser.showOnlyTheseTabs([origTab]);
   is(gBrowser.visibleTabs.length, 1, "only 1 after hiding");
   ok(hiddenTab.hidden, "sanity check that it's hidden");
 
-  let extraTab = gBrowser.addTab();
+  gBrowser.addTab();
   let state = ss.getBrowserState();
   let stateObj = JSON.parse(state);
   let tabs = stateObj.windows[0].tabs;
   is(tabs.length, 3, "just checking that browser state is correct");
   ok(!tabs[0].hidden, "first tab is visible");
   ok(tabs[1].hidden, "second is hidden");
   ok(!tabs[2].hidden, "third is visible");
 
   // Make the third tab hidden and then restore the modified state object
   tabs[2].hidden = true;
 
   observeOneRestore(function() {
-    let testWindow = Services.wm.getEnumerator("navigator:browser").getNext();
-    is(testWindow.gBrowser.visibleTabs.length, 1, "only restored 1 visible tab");
-    let restoredTabs = testWindow.gBrowser.tabs;
+    is(gBrowser.visibleTabs.length, 1, "only restored 1 visible tab");
+    let restoredTabs = gBrowser.tabs;
+
     ok(!restoredTabs[0].hidden, "first is still visible");
     ok(restoredTabs[1].hidden, "second tab is still hidden");
     ok(restoredTabs[2].hidden, "third tab is now hidden");
 
     // Restore the original state and clean up now that we're done
-    gBrowser.removeTab(hiddenTab);
-    gBrowser.removeTab(extraTab);
+    gBrowser.removeTab(gBrowser.tabs[1]);
+    gBrowser.removeTab(gBrowser.tabs[1]);
 
     finish();
   });
   ss.setBrowserState(JSON.stringify(stateObj));
 }
--- a/browser/components/sessionstore/test/browser_590563.js
+++ b/browser/components/sessionstore/test/browser_590563.js
@@ -51,21 +51,24 @@ function middleClickTest(win) {
      "The total number of visible tabs should be 3 after restoring 2 tabs by middle click");
 }
 
 function newWindowWithState(state, callback) {
   let opts = "chrome,all,dialog=no,height=800,width=800";
   let win = window.openDialog(getBrowserURL(), "_blank", opts);
 
   win.addEventListener("load", function() {
-    let tab = win.gBrowser.selectedTab;
-
     // The form data will be restored before SSTabRestored, so we want to listen
-    // for that on the currently selected tab (it will be reused)
-    tab.addEventListener("SSTabRestored", function() {
-      callback(win);
-    }, {capture: true, once: true});
+    // for that on the currently selected tab
+    let onSSTabRestored = event => {
+      let tab = event.target;
+      if (tab.selected) {
+        win.gBrowser.tabContainer.removeEventListener("SSTabRestored", onSSTabRestored, true);
+        callback(win);
+      }
+    };
+    win.gBrowser.tabContainer.addEventListener("SSTabRestored", onSSTabRestored, true);
 
     executeSoon(function() {
       ss.setWindowState(win, JSON.stringify(state), true);
     });
   }, {once: true});
 }
--- a/browser/components/sessionstore/test/browser_624727.js
+++ b/browser/components/sessionstore/test/browser_624727.js
@@ -16,20 +16,18 @@ add_task(function* () {
   assertNumberOfTabs(1, "we start off with one tab");
   assertNumberOfPinnedTabs(0, "no pinned tabs so far");
 
   // setup
   gBrowser.addTab("about:blank");
   assertNumberOfTabs(2, "there are two tabs, now");
 
   let [tab1, tab2] = gBrowser.tabs;
-  let linkedBrowser = tab1.linkedBrowser;
   gBrowser.pinTab(tab1);
   gBrowser.pinTab(tab2);
   assertNumberOfPinnedTabs(2, "both tabs are now pinned");
 
   // run the test
   yield promiseBrowserState(TEST_STATE);
 
   assertNumberOfTabs(1, "one tab left after setBrowserState()");
   assertNumberOfPinnedTabs(0, "there are no pinned tabs");
-  is(gBrowser.tabs[0].linkedBrowser, linkedBrowser, "first tab's browser got re-used");
 });
--- a/browser/components/sessionstore/test/browser_remoteness_flip_on_restore.js
+++ b/browser/components/sessionstore/test/browser_remoteness_flip_on_restore.js
@@ -81,40 +81,28 @@ const PINNED_STATE = {
  * stateToRestore:
  *   A JS Object for the state to send down to the window.
  *
  * selectedTab:
  *   The 1-based index of the tab that we want to select for the
  *   restored window. Leave this at 0 if you don't want to change
  *   the selection from the initial window state.
  *
- * expectedFlips:
- *   an Array that represents the window that we end up with after
- *   restoring state. Each bool in the Array represents the window tabs,
- *   in order. A "true" indicates that the tab should have flipped
- *   its remoteness once. "false" indicates that the tab should never
- *   have flipped remoteness. Note that any tab that flips its remoteness
- *   more than once will cause a test failure.
- *
  * expectedRemoteness:
  *   an Array that represents the window that we end up with after
  *   restoring state. Each bool in the Array represents the window
  *   tabs in order. A "true" indicates that the tab be remote, and
  *   a "false" indicates that the tab should be "non-remote". We
  *   need this Array in order to test pinned tabs which will also
  *   be loaded by default, and therefore should end up remote.
  *
  */
 function* runScenarios(scenarios) {
   for (let [scenarioIndex, scenario] of scenarios.entries()) {
     info("Running scenario " + scenarioIndex);
-    // Let's make sure our scenario is sane first.
-    Assert.equal(scenario.expectedFlips.length,
-                 scenario.expectedRemoteness.length,
-                 "All expected flips and remoteness needs to be supplied");
     Assert.ok(scenario.initialSelectedTab > 0,
               "You must define an initially selected tab");
 
     // First, we need to create the initial conditions, so we
     // open a new window to put into our starting state...
     let win = yield BrowserTestUtils.openNewBrowserWindow();
     let tabbrowser = win.gBrowser;
     Assert.ok(tabbrowser.selectedBrowser.isRemoteBrowser,
@@ -137,86 +125,38 @@ function* runScenarios(scenarios) {
     }
 
     // And select the requested tab.
     let tabToSelect = tabbrowser.tabs[scenario.initialSelectedTab - 1];
     if (tabbrowser.selectedTab != tabToSelect) {
       yield BrowserTestUtils.switchTab(tabbrowser, tabToSelect);
     }
 
-    // Hook up an event listener to make sure that the right
-    // tabs flip remoteness, and only once.
-    let flipListener = {
-      seenBeforeTabs: new Set(),
-      seenAfterTabs: new Set(),
-      handleEvent(e) {
-        let index = Array.from(tabbrowser.tabs).indexOf(e.target);
-        switch (e.type) {
-          case "BeforeTabRemotenessChange":
-            info(`Saw tab at index ${index} before remoteness flip`);
-            if (this.seenBeforeTabs.has(e.target)) {
-              Assert.ok(false, "Saw tab before remoteness flip more than once");
-            }
-            this.seenBeforeTabs.add(e.target);
-            break;
-          case "TabRemotenessChange":
-            info(`Saw tab at index ${index} after remoteness flip`);
-            if (this.seenAfterTabs.has(e.target)) {
-              Assert.ok(false, "Saw tab after remoteness flip more than once");
-            }
-            this.seenAfterTabs.add(e.target);
-            break;
-        }
-      },
-    };
-
-    win.addEventListener("BeforeTabRemotenessChange", flipListener);
-    win.addEventListener("TabRemotenessChange", flipListener);
-
     // Okay, time to test!
     let state = prepareState(scenario.stateToRestore,
                              scenario.selectedTab);
 
     SessionStore.setWindowState(win, state, true);
 
-    win.removeEventListener("BeforeTabRemotenessChange", flipListener);
-    win.removeEventListener("TabRemotenessChange", flipListener);
-
-    // Because we know that scenario.expectedFlips and
-    // scenario.expectedRemoteness have the same length, we
-    // can check that we satisfied both with the same loop.
-    for (let i = 0; i < scenario.expectedFlips.length; ++i) {
-      let expectedToFlip = scenario.expectedFlips[i];
+    for (let i = 0; i < scenario.expectedRemoteness.length; ++i) {
       let expectedRemoteness = scenario.expectedRemoteness[i];
       let tab = tabbrowser.tabs[i];
-      if (expectedToFlip) {
-        Assert.ok(flipListener.seenBeforeTabs.has(tab),
-                  `We should have seen tab at index ${i} before remoteness flip`);
-        Assert.ok(flipListener.seenAfterTabs.has(tab),
-                  `We should have seen tab at index ${i} after remoteness flip`);
-      } else {
-        Assert.ok(!flipListener.seenBeforeTabs.has(tab),
-                  `We should not have seen tab at index ${i} before remoteness flip`);
-        Assert.ok(!flipListener.seenAfterTabs.has(tab),
-                  `We should not have seen tab at index ${i} after remoteness flip`);
-      }
 
       Assert.equal(tab.linkedBrowser.isRemoteBrowser, expectedRemoteness,
                    "Should have gotten the expected remoteness " +
                    `for the tab at index ${i}`);
     }
 
     yield BrowserTestUtils.closeWindow(win);
   }
 }
 
 /**
  * Tests that if we restore state to browser windows with
- * a variety of initial remoteness states, that we only flip
- * the remoteness on the necessary tabs. For this particular
+ * a variety of initial remoteness states. For this particular
  * set of tests, we assume that tabs are restoring on demand.
  */
 add_task(function*() {
   // This test opens and closes windows, which might bog down
   // a debug build long enough to time out the test, so we
   // extend the tolerance on timeouts.
   requestLongerTimeout(5);
 
@@ -228,132 +168,83 @@ add_task(function*() {
     // Only one tab in the new window, and it's remote. This
     // is the common case, since this is how restoration occurs
     // when the restored window is being opened.
     {
       initialRemoteness: [true],
       initialSelectedTab: 1,
       stateToRestore: SIMPLE_STATE,
       selectedTab: 3,
-      // The initial tab is remote and should go into
-      // the background state. The second and third tabs
-      // are new and should initialize remotely as well.
-      // There should therefore be no remoteness flips.
-      expectedFlips: [false, false, false],
       // All tabs should now be remote.
       expectedRemoteness: [true, true, true],
     },
 
     // A single remote tab, and this is the one that's going
     // to be selected once state is restored.
     {
       initialRemoteness: [true],
       initialSelectedTab: 1,
       stateToRestore: SIMPLE_STATE,
       selectedTab: 1,
-      // The initial tab is remote and selected, so it should
-      // not flip remoteness. The other two new tabs should
-      // initialize as remote unrestored background tabs.
-      expectedFlips: [false, false, false],
       // All tabs should now be remote.
       expectedRemoteness: [true, true, true],
     },
 
     // A single remote tab which starts selected. We set the
     // selectedTab to 0 which is equivalent to "don't change
     // the tab selection in the window".
     {
       initialRemoteness: [true],
       initialSelectedTab: 1,
       stateToRestore: SIMPLE_STATE,
       selectedTab: 0,
-      // The initial tab is remote and selected, so it should
-      // not flip remoteness. The other two new tabs should
-      // initialize as remote unrestored background tabs.
-      expectedFlips: [false, false, false],
       // All tabs should now be remote.
       expectedRemoteness: [true, true, true],
     },
 
     // An initially remote tab, but we're going to load
     // some pinned tabs now, and the pinned tabs should load
     // right away.
     {
       initialRemoteness: [true],
       initialSelectedTab: 1,
       stateToRestore: PINNED_STATE,
       selectedTab: 3,
-      // The initial tab is pinned and will load right away,
-      // so it should stay remote. The second tab is new
-      // and pinned, so it should start remote and not flip.
-      // The third tab is not pinned, but it is selected,
-      // so it will start remote.
-      expectedFlips: [false, false, false],
       // Both pinned tabs and the selected tabs should all
       // end up being remote.
       expectedRemoteness: [true, true, true],
     },
 
     // A single non-remote tab.
     {
       initialRemoteness: [false],
       initialSelectedTab: 1,
       stateToRestore: SIMPLE_STATE,
       selectedTab: 2,
-      // The initial tab is non-remote and will flip. The two tabs that
-      // are added after should be initialized as remote. Since the selected
-      // tab is changing, SessionStore should swap the initial tab with the
-      // tab thats in the selectedTab slot. That'll result in a remoteness
-      // state like this:
-      //
-      // [true, false, true]
-      expectedFlips: [false, true, false],
       // All tabs should now be remote.
       expectedRemoteness: [true, true, true],
     },
 
     // A mixture of remote and non-remote tabs.
     {
       initialRemoteness: [true, false, true],
       initialSelectedTab: 1,
       stateToRestore: SIMPLE_STATE,
       selectedTab: 3,
-      // The initial tab is remote and should stay that way, even
-      // when put into the background. The initial tab is going to be
-      // swapped into the selectedTab slot, and both of those tabs are
-      // remote already. That will result in the tabs remoteness being
-      // in this order still:
-      //
-      // [true, false, true]
-      //
-      // This means that we'll only need to flip the second tab.
-      expectedFlips: [false, true, false],
       // All tabs should now be remote.
       expectedRemoteness: [true, true, true],
     },
 
     // An initially non-remote tab, but we're going to load
     // some pinned tabs now, and the pinned tabs should load
     // right away.
     {
       initialRemoteness: [false],
       initialSelectedTab: 1,
       stateToRestore: PINNED_STATE,
       selectedTab: 3,
-      // The initial tab is going to swap into the selected slot,
-      // and so after the other two tabs from the PINNED_STATE are
-      // inserted, we'll have a remoteness state like this:
-      //
-      // [true, true, false]
-      //
-      // The two pinned tabs at the start of the PINNED_STATE should
-      // load right away, but should not flip remoteness.
-      //
-      // The third tab is selected, and should load right away, so
-      // it should flip remoteness.
-      expectedFlips: [false, false, true],
       // All tabs should now be remote.
       expectedRemoteness: [true, true, true],
     },
   ];
 
   yield* runScenarios(TEST_SCENARIOS);
 });
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -89,21 +89,27 @@ FormAutofillParent.prototype = {
     this._setStatus(this._getStatus());
     this._updateSavedFieldNames();
   },
 
   observe(subject, topic, data) {
     log.debug("observe:", topic, "with data:", data);
     switch (topic) {
       case "advanced-pane-loaded": {
-        let formAutofillPreferences = new FormAutofillPreferences();
+        let useOldOrganization = Services.prefs.getBoolPref("browser.preferences.useOldOrganization",
+                                                            false);
+        let formAutofillPreferences = new FormAutofillPreferences({useOldOrganization});
         let document = subject.document;
         let prefGroup = formAutofillPreferences.init(document);
-        let parentNode = document.getElementById("mainPrefPane");
-        let insertBeforeNode = document.getElementById("locationBarGroup");
+        let parentNode = useOldOrganization ?
+                         document.getElementById("mainPrefPane") :
+                         document.getElementById("passwordsGroup");
+        let insertBeforeNode = useOldOrganization ?
+                               document.getElementById("locationBarGroup") :
+                               null;
         parentNode.insertBefore(prefGroup, insertBeforeNode);
         break;
       }
 
       case "nsPref:changed": {
         // Observe pref changes and update _enabled cache if status is changed.
         let currentStatus = this._getStatus();
         if (currentStatus !== this._enabled) {
--- a/browser/extensions/formautofill/FormAutofillPreferences.jsm
+++ b/browser/extensions/formautofill/FormAutofillPreferences.jsm
@@ -9,48 +9,41 @@
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["FormAutofillPreferences"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 const PREF_AUTOFILL_ENABLED = "browser.formautofill.enabled";
 const BUNDLE_URI = "chrome://formautofill/locale/formautofill.properties";
 const MANAGE_PROFILES_URL = "chrome://formautofill/content/manageProfiles.xhtml";
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://formautofill/FormAutofillUtils.jsm");
 
 this.log = null;
 FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
 
-function FormAutofillPreferences() {
+function FormAutofillPreferences({useOldOrganization}) {
+  this.useOldOrganization = useOldOrganization;
   this.bundle = Services.strings.createBundle(BUNDLE_URI);
 }
 
 FormAutofillPreferences.prototype = {
   /**
    * Check if Form Autofill feature is enabled.
    *
    * @returns {boolean}
    */
   get isAutofillEnabled() {
     return Services.prefs.getBoolPref(PREF_AUTOFILL_ENABLED);
   },
 
   /**
-   * Check if the current page is Preferences/Privacy.
-   *
-   * @returns {boolean}
-   */
-  get isPrivacyPane() {
-    return this.refs.document.location.href == "about:preferences#privacy";
-  },
-
-  /**
    * Create the Form Autofill preference group.
    *
    * @param   {XULDocument} document
    * @returns {XULElement}
    */
   init(document) {
     this.createPreferenceGroup(document);
     this.attachEventListeners();
@@ -67,68 +60,72 @@ FormAutofillPreferences.prototype = {
   },
 
   /**
    * Create Form Autofill preference group
    *
    * @param  {XULDocument} document
    */
   createPreferenceGroup(document) {
-    const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
-    let formAutofillGroup = document.createElementNS(XUL_NS, "groupbox");
-    let caption = document.createElementNS(XUL_NS, "caption");
-    let captionLabel = document.createElementNS(XUL_NS, "label");
-    let hbox = document.createElementNS(XUL_NS, "hbox");
-    let enabledCheckbox = document.createElementNS(XUL_NS, "checkbox");
+    let formAutofillGroup;
+    let profileAutofill = document.createElementNS(XUL_NS, "hbox");
+    let profileAutofillCheckbox = document.createElementNS(XUL_NS, "checkbox");
     let spacer = document.createElementNS(XUL_NS, "spacer");
     let savedProfilesBtn = document.createElementNS(XUL_NS, "button");
 
+    if (this.useOldOrganization) {
+      let caption = document.createElementNS(XUL_NS, "caption");
+      let captionLabel = document.createElementNS(XUL_NS, "label");
+
+      formAutofillGroup = document.createElementNS(XUL_NS, "groupbox");
+      formAutofillGroup.hidden = document.location.href != "about:preferences#privacy";
+      // Use .setAttribute because HTMLElement.dataset is not available on XUL elements
+      formAutofillGroup.setAttribute("data-category", "panePrivacy");
+      formAutofillGroup.appendChild(caption);
+      caption.appendChild(captionLabel);
+      captionLabel.textContent = this.bundle.GetStringFromName("preferenceGroupTitle");
+    } else {
+      formAutofillGroup = document.createElementNS(XUL_NS, "vbox");
+    }
+
     this.refs = {
-      document,
       formAutofillGroup,
-      enabledCheckbox,
+      profileAutofillCheckbox,
       savedProfilesBtn,
     };
 
     formAutofillGroup.id = "formAutofillGroup";
-    formAutofillGroup.hidden = !this.isPrivacyPane;
-    // Use .setAttribute because HTMLElement.dataset is not available on XUL elements
-    formAutofillGroup.setAttribute("data-category", "panePrivacy");
-
-    captionLabel.textContent = this.bundle.GetStringFromName("preferenceGroupTitle");
+    profileAutofill.id = "profileAutofill";
     savedProfilesBtn.setAttribute("label", this.bundle.GetStringFromName("savedProfiles"));
-    enabledCheckbox.setAttribute("label", this.bundle.GetStringFromName("enableProfileAutofill"));
+    profileAutofillCheckbox.setAttribute("label", this.bundle.GetStringFromName("enableProfileAutofill"));
 
     // Manually set the checked state
     if (this.isAutofillEnabled) {
-      enabledCheckbox.setAttribute("checked", true);
+      profileAutofillCheckbox.setAttribute("checked", true);
     }
 
     spacer.flex = 1;
 
-    formAutofillGroup.appendChild(caption);
-    caption.appendChild(captionLabel);
-    formAutofillGroup.appendChild(hbox);
-    hbox.appendChild(enabledCheckbox);
-    hbox.appendChild(spacer);
-    hbox.appendChild(savedProfilesBtn);
+    formAutofillGroup.appendChild(profileAutofill);
+    profileAutofill.appendChild(profileAutofillCheckbox);
+    profileAutofill.appendChild(spacer);
+    profileAutofill.appendChild(savedProfilesBtn);
   },
 
   /**
    * Handle events
    *
    * @param  {DOMEvent} event
    */
   handleEvent(event) {
     switch (event.type) {
       case "command": {
         let target = event.target;
 
-        if (target == this.refs.enabledCheckbox) {
+        if (target == this.refs.profileAutofillCheckbox) {
           // Set preference directly instead of relying on <Preference>
           Services.prefs.setBoolPref(PREF_AUTOFILL_ENABLED, target.checked);
         } else if (target == this.refs.savedProfilesBtn) {
           target.ownerGlobal.gSubDialog.open(MANAGE_PROFILES_URL);
         }
         break;
       }
     }
--- a/browser/extensions/formautofill/test/browser/browser.ini
+++ b/browser/extensions/formautofill/test/browser/browser.ini
@@ -1,5 +1,7 @@
 [DEFAULT]
+head = head.js
 
 [browser_check_installed.js]
+[browser_editProfileDialog.js]
 [browser_privacyPreferences.js]
 [browser_manageProfilesDialog.js]
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/browser/browser_editProfileDialog.js
@@ -0,0 +1,87 @@
+"use strict";
+
+registerCleanupFunction(function* () {
+  let profiles = yield getProfiles();
+  if (profiles.length) {
+    yield removeProfiles(profiles.map(profile => profile.guid));
+  }
+});
+
+add_task(function* test_cancelEditProfileDialog() {
+  yield new Promise(resolve => {
+    let win = window.openDialog(EDIT_PROFILE_DIALOG_URL, null, null, null);
+    win.addEventListener("load", () => {
+      win.addEventListener("unload", () => {
+        ok(true, "Edit profile dialog is closed");
+        resolve();
+      }, {once: true});
+      win.document.querySelector("#cancel").click();
+    }, {once: true});
+  });
+});
+
+add_task(function* test_saveProfile() {
+  yield new Promise(resolve => {
+    let win = window.openDialog(EDIT_PROFILE_DIALOG_URL, null, null, null);
+    win.addEventListener("load", () => {
+      win.addEventListener("unload", () => {
+        ok(true, "Edit profile dialog is closed");
+        resolve();
+      }, {once: true});
+      EventUtils.synthesizeKey("VK_TAB", {}, win);
+      EventUtils.synthesizeKey(TEST_PROFILE_1.organization, {}, win);
+      EventUtils.synthesizeKey("VK_TAB", {}, win);
+      EventUtils.synthesizeKey(TEST_PROFILE_1["street-address"], {}, win);
+      EventUtils.synthesizeKey("VK_TAB", {}, win);
+      EventUtils.synthesizeKey(TEST_PROFILE_1["address-level2"], {}, win);
+      EventUtils.synthesizeKey("VK_TAB", {}, win);
+      EventUtils.synthesizeKey(TEST_PROFILE_1["address-level1"], {}, win);
+      EventUtils.synthesizeKey("VK_TAB", {}, win);
+      EventUtils.synthesizeKey(TEST_PROFILE_1["postal-code"], {}, win);
+      EventUtils.synthesizeKey("VK_TAB", {}, win);
+      EventUtils.synthesizeKey(TEST_PROFILE_1.country, {}, win);
+      EventUtils.synthesizeKey("VK_TAB", {}, win);
+      EventUtils.synthesizeKey(TEST_PROFILE_1.email, {}, win);
+      EventUtils.synthesizeKey("VK_TAB", {}, win);
+      EventUtils.synthesizeKey(TEST_PROFILE_1.tel, {}, win);
+      EventUtils.synthesizeKey("VK_TAB", {}, win);
+      EventUtils.synthesizeKey("VK_TAB", {}, win);
+      EventUtils.synthesizeKey("VK_RETURN", {}, win);
+    }, {once: true});
+  });
+  let profiles = yield getProfiles();
+
+  is(profiles.length, 1, "only one profile is in storage");
+  is(profiles[0].organization, TEST_PROFILE_1.organization, "match organization");
+  is(profiles[0]["street-address"], TEST_PROFILE_1["street-address"], "match street-address");
+  is(profiles[0]["address-level2"], TEST_PROFILE_1["address-level2"], "match address-level2");
+  is(profiles[0]["address-level1"], TEST_PROFILE_1["address-level1"], "match address-level1");
+  is(profiles[0]["postal-code"], TEST_PROFILE_1["postal-code"], "match postal-code");
+  is(profiles[0].country, TEST_PROFILE_1.country, "match country");
+  is(profiles[0].email, TEST_PROFILE_1.email, "match email");
+  is(profiles[0].tel, TEST_PROFILE_1.tel, "match tel");
+});
+
+add_task(function* test_editProfile() {
+  let profiles = yield getProfiles();
+  yield new Promise(resolve => {
+    let win = window.openDialog(EDIT_PROFILE_DIALOG_URL, null, null, profiles[0]);
+    win.addEventListener("load", () => {
+      win.addEventListener("unload", () => {
+        ok(true, "Edit profile dialog is closed");
+        resolve();
+      }, {once: true});
+      EventUtils.synthesizeKey("VK_TAB", {}, win);
+      EventUtils.synthesizeKey("test", {}, win);
+      win.document.querySelector("#save").click();
+    }, {once: true});
+  });
+  profiles = yield getProfiles();
+
+  is(profiles.length, 1, "only one profile is in storage");
+  is(profiles[0].organization, TEST_PROFILE_1.organization + "test", "organization changed");
+  yield removeProfiles([profiles[0].guid]);
+
+  profiles = yield getProfiles();
+  is(profiles.length, 0, "Profile storage is empty");
+});
--- a/browser/extensions/formautofill/test/browser/browser_manageProfilesDialog.js
+++ b/browser/extensions/formautofill/test/browser/browser_manageProfilesDialog.js
@@ -1,64 +1,29 @@
 "use strict";
 
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-const MANAGE_PROFILES_DIALOG_URL = "chrome://formautofill/content/manageProfiles.xhtml";
 const TEST_SELECTORS = {
   selProfiles: "#profiles",
   btnRemove: "#remove",
   btnAdd: "#add",
   btnEdit: "#edit",
 };
 
-const TEST_PROFILE_1 = {
-  organization: "World Wide Web Consortium",
-  "street-address": "32 Vassar Street\nMIT Room 32-G524",
-  "address-level2": "Cambridge",
-  "address-level1": "MA",
-  "postal-code": "02139",
-  country: "US",
-  tel: "+1 617 253 5702",
-  email: "timbl@w3.org",
-};
-
-const TEST_PROFILE_2 = {
-  "street-address": "Some Address",
-  country: "US",
-};
-
-const TEST_PROFILE_3 = {
-  "street-address": "Other Address",
-  "postal-code": "12345",
-};
-
-function saveProfile(profile) {
-  Services.cpmm.sendAsyncMessage("FormAutofill:SaveProfile", {profile});
-  return TestUtils.topicObserved("formautofill-storage-changed");
-}
-
-function removeProfiles(guids) {
-  Services.cpmm.sendAsyncMessage("FormAutofill:RemoveProfiles", {guids});
-  return TestUtils.topicObserved("formautofill-storage-changed");
-}
-
 function waitForProfiles() {
   return new Promise(resolve => {
     Services.cpmm.addMessageListener("FormAutofill:Profiles", function getResult(result) {
       Services.cpmm.removeMessageListener("FormAutofill:Profiles", getResult);
       // Wait for the next tick for elements to get rendered.
       SimpleTest.executeSoon(resolve.bind(null, result.data));
     });
   });
 }
 
 registerCleanupFunction(function* () {
-  Services.cpmm.sendAsyncMessage("FormAutofill:GetProfiles", {});
-  let profiles = yield waitForProfiles();
+  let profiles = yield getProfiles();
   if (profiles.length) {
     yield removeProfiles(profiles.map(profile => profile.guid));
   }
 });
 
 add_task(function* test_manageProfilesInitialState() {
   yield BrowserTestUtils.withNewTab({gBrowser, url: MANAGE_PROFILES_DIALOG_URL}, function* (browser) {
     yield ContentTask.spawn(browser, TEST_SELECTORS, (args) => {
--- a/browser/extensions/formautofill/test/browser/browser_privacyPreferences.js
+++ b/browser/extensions/formautofill/test/browser/browser_privacyPreferences.js
@@ -1,14 +1,14 @@
 "use strict";
 
 const PAGE_PREFS = "about:preferences";
 const PAGE_PRIVACY = PAGE_PREFS + "#privacy";
-const GROUP_AUTOFILL = "#formAutofillGroup";
-const CHECKBOX_AUTOFILL = GROUP_AUTOFILL + " checkbox";
+const GROUP_AUTOFILL = "#passwordsGroup";
+const CHECKBOX_AUTOFILL = "#profileAutofill checkbox";
 const PREF_AUTOFILL_ENABLED = "browser.formautofill.enabled";
 const TEST_SELECTORS = {
   group: GROUP_AUTOFILL,
   checkbox: CHECKBOX_AUTOFILL,
 };
 
 // Visibility of form autofill group should be hidden when opening
 // preferences page.
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/browser/head.js
@@ -0,0 +1,49 @@
+/* exported MANAGE_PROFILES_DIALOG_URL, EDIT_PROFILE_DIALOG_URL,
+            TEST_PROFILE_1, TEST_PROFILE_2, TEST_PROFILE_3,
+            getProfiles, saveProfile, removeProfiles */
+
+"use strict";
+
+const MANAGE_PROFILES_DIALOG_URL = "chrome://formautofill/content/manageProfiles.xhtml";
+const EDIT_PROFILE_DIALOG_URL = "chrome://formautofill/content/editProfile.xhtml";
+
+const TEST_PROFILE_1 = {
+  organization: "World Wide Web Consortium",
+  "street-address": "32 Vassar Street\nMIT Room 32-G524",
+  "address-level2": "Cambridge",
+  "address-level1": "MA",
+  "postal-code": "02139",
+  country: "US",
+  tel: "+1 617 253 5702",
+  email: "timbl@w3.org",
+};
+
+const TEST_PROFILE_2 = {
+  "street-address": "Some Address",
+  country: "US",
+};
+
+const TEST_PROFILE_3 = {
+  "street-address": "Other Address",
+  "postal-code": "12345",
+};
+
+function getProfiles() {
+  return new Promise(resolve => {
+    Services.cpmm.addMessageListener("FormAutofill:Profiles", function getResult(result) {
+      Services.cpmm.removeMessageListener("FormAutofill:Profiles", getResult);
+      resolve(result.data);
+    });
+    Services.cpmm.sendAsyncMessage("FormAutofill:GetProfiles", {});
+  });
+}
+
+function saveProfile(profile) {
+  Services.cpmm.sendAsyncMessage("FormAutofill:SaveProfile", {profile});
+  return TestUtils.topicObserved("formautofill-storage-changed");
+}
+
+function removeProfiles(guids) {
+  Services.cpmm.sendAsyncMessage("FormAutofill:RemoveProfiles", {guids});
+  return TestUtils.topicObserved("formautofill-storage-changed");
+}
--- a/browser/extensions/webcompat-reporter/skin/shared/lightbulb.css
+++ b/browser/extensions/webcompat-reporter/skin/shared/lightbulb.css
@@ -1,12 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#webcompat-reporter-button[cui-areatype="menu-panel"] {
+#webcompat-reporter-button[cui-areatype="menu-panel"],
+toolbarpaletteitem[place="palette"] > #webcompat-reporter-button {
   list-style-image: url("chrome://webcompat-reporter/skin/lightbulb.svg");
 }
 
 #webcompat-reporter-button[cui-areatype="toolbar"] {
   list-style-image: url("chrome://webcompat-reporter/skin/lightbulb.png");
   -moz-image-region: rect(0, 18px, 18px, 0);
 }
 
--- a/browser/locales/Makefile.in
+++ b/browser/locales/Makefile.in
@@ -185,14 +185,16 @@ endif
 
 # test target, depends on make package
 # try to repack x-test, with just toolkit/defines.inc being there
 l10n-check:: INNER_UNMAKE_PACKAGE=true
 l10n-check::
 	$(RM) -rf x-test
 	$(NSINSTALL) -D x-test/toolkit
 	echo '#define MOZ_LANG_TITLE Just testing' > x-test/toolkit/defines.inc
-	@# ZIP_IN='$(ZIP_IN)' will pass down the *current* value of ZIP_IN, based
-	@# on MOZ_SIMPLE_PACKAGE_NAME not being reset, overwriting the value it
-	@# would get with MOZ_SIMPLE_PACKAGE_NAME reset.
-	$(MAKE) installers-x-test L10NBASEDIR='$(PWD)' LOCALE_MERGEDIR='$(PWD)/mergedir' ZIP_IN='$(ZIP_IN)' MOZ_SIMPLE_PACKAGE_NAME=
+	@# ZIP_IN='$(ZIP_IN)' will pass down the *current* value of ZIP_IN, and
+	@# we do the same for WIN32_INSTALLER_IN, which are based on MOZ_SIMPLE_PACKAGE_NAME
+	@# not being reset, overwriting the value they would get with MOZ_SIMPLE_PACKAGE_NAME
+	@# reset.
+	$(MAKE) installers-x-test L10NBASEDIR='$(PWD)' LOCALE_MERGEDIR='$(PWD)/mergedir' \
+	    ZIP_IN='$(ZIP_IN)' WIN32_INSTALLER_IN='$(WIN32_INSTALLER_IN)' MOZ_SIMPLE_PACKAGE_NAME=
 	$(PYTHON) $(topsrcdir)/toolkit/mozapps/installer/unpack.py $(DIST)/l10n-stage/$(MOZ_PKG_DIR)$(_RESPATH)
 	cd $(DIST)/l10n-stage && test $$(cat $(MOZ_PKG_DIR)$(_RESPATH)/update.locale) = x-test
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -109,20 +109,16 @@
 }
 
 #nav-bar {
   box-shadow: 0 1px 0 @navbarInsetHighlight@ inset;
   padding-top: 2px;
   padding-bottom: 2px;
 }
 
-#nav-bar-overflow-button {
-  -moz-image-region: rect(-5px, 12px, 11px, -4px);
-}
-
 /* This only has an effect when this element is placed on the bookmarks toolbar.
  * It's 30px to make sure buttons with 18px icons fit along with the default 16px
  * icons, without changing the size of the toolbar.
  */
 #personal-bookmarks {
   min-height: 30px;
 }
 
@@ -305,24 +301,16 @@ menuitem.bookmark-item {
 .unified-nav-forward[_moz-menuactive]:-moz-locale-dir(rtl) {
   list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=menu") !important;
 }
 
 %include ../shared/toolbarbuttons.inc.css
 %include ../shared/toolbarbutton-icons.inc.css
 %include ../shared/menupanel.inc.css
 
-#main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-icon,
-#main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-menu-dropmarker,
-#main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-menubutton-dropmarker,
-#main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
-#main-window:not([customizing]) .toolbarbutton-1 > .toolbarbutton-menubutton-button[disabled=true] > .toolbarbutton-icon {
-  opacity: 0.4;
-}
-
 /* Fullscreen window controls */
 #window-controls {
   -moz-box-align: start;
   margin-inline-start: 10px;
 }
 
 #minimize-button {
   list-style-image: url("chrome://global/skin/icons/Minimize.gif");
@@ -1089,62 +1077,38 @@ html|span.ac-emphasize-text-url {
 /* Tabbrowser arrowscrollbox arrows */
 .tabbrowser-arrowscrollbox > .scrollbutton-up > .toolbarbutton-icon,
 .tabbrowser-arrowscrollbox > .scrollbutton-down > .toolbarbutton-icon {
   -moz-appearance: none;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up,
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
-  -moz-appearance: none;
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-arrow-left.png");
-  margin: 0 0 var(--tab-toolbar-navbar-overlap);
 }
 
 #TabsToolbar[brighttext] > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .scrollbutton-up,
 #TabsToolbar[brighttext] > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .scrollbutton-down {
   list-style-image: url(chrome://browser/skin/tabbrowser/tab-arrow-left-inverted.png);
 }
 
-.tabbrowser-arrowscrollbox > .scrollbutton-up[disabled],
-.tabbrowser-arrowscrollbox > .scrollbutton-down[disabled] {
-  opacity: .4;
-}
-
 .tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-locale-dir(rtl),
 .tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-locale-dir(ltr) {
   transform: scaleX(-1);
 }
 
-.tabbrowser-arrowscrollbox > .scrollbutton-down {
-  transition: 1s background-color ease-out;
-}
-
-.tabbrowser-arrowscrollbox > .scrollbutton-down[highlight] {
-  background-color: Highlight;
-  transition: none;
-}
-
-#TabsToolbar .toolbarbutton-1 {
-  margin-bottom: var(--tab-toolbar-navbar-overlap);
-}
-
 #alltabs-button {
   list-style-image: url("chrome://browser/skin/tabbrowser/alltabs.png");
 }
 
 #TabsToolbar[brighttext] > #alltabs-button,
 #TabsToolbar[brighttext] > toolbarpaletteitem > #alltabs-button {
   list-style-image: url("chrome://browser/skin/tabbrowser/alltabs-inverted.png");
 }
 
-#alltabs-button > .toolbarbutton-icon {
-  padding: 9px 6px 6px;
-}
-
 #alltabs-button > .toolbarbutton-menu-dropmarker {
   display: none;
 }
 
 /* All tabs menupopup */
 .alltabs-item > .menu-iconic-left > .menu-iconic-icon {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -39,18 +39,16 @@
   --toolbarbutton-active-background: hsla(0,0%,0%,.02) linear-gradient(hsla(0,0%,0%,.12), transparent) border-box;
   --toolbarbutton-active-bordercolor: hsla(0,0%,0%,.3);
   --toolbarbutton-active-boxshadow: 0 1px 0 hsla(0,0%,100%,.5),
                                     0 1px 0 hsla(0,0%,0%,.05) inset,
                                     0 1px 1px hsla(0,0%,0%,.2) inset;
 
   --toolbarbutton-checkedhover-backgroundcolor: hsla(0,0%,0%,.09);
 
-  --toolbarbutton-disabled-opacity: 0.4;
-
   --urlbar-dropmarker-url: url("chrome://browser/skin/urlbar-history-dropmarker.png");
   --urlbar-dropmarker-region: rect(0, 11px, 14px, 0);
   --urlbar-dropmarker-active-region: rect(0, 22px, 14px, 11px);
   --urlbar-dropmarker-2x-url: url("chrome://browser/skin/urlbar-history-dropmarker@2x.png");
   --urlbar-dropmarker-2x-region: rect(0, 22px, 28px, 0);
   --urlbar-dropmarker-active-2x-region: rect(0, 44px, 28px, 22px);
 
   --panel-separator-color: hsla(210,4%,10%,.14);
@@ -1728,33 +1726,16 @@ toolbarbutton.chevron > .toolbarbutton-m
   #TabsToolbar[brighttext] .tab-close-button.close-icon:not([selected=true]):not(:hover) {
     -moz-image-region: rect(0, 128px, 32px, 96px);
   }
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up,
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   -moz-image-region: rect(0, 13px, 20px, 0);
-  margin: 0 0 var(--tab-toolbar-navbar-overlap);
-  padding: 0 4px;
-  border: none;
-}
-
-.tabbrowser-arrowscrollbox > .scrollbutton-up {
-  border-inline-end: 2px solid transparent;
-}
-
-.tabbrowser-arrowscrollbox > .scrollbutton-down {
-  border-inline-start: 2px solid transparent;
-  transition: 1s background-color ease-out;
-}
-
-.tabbrowser-arrowscrollbox > .scrollbutton-down[highlight] {
-  background-color: Highlight;
-  transition: none;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-locale-dir(ltr),
 .tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-locale-dir(rtl) {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-arrow-left.png");
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-locale-dir(ltr),
@@ -1767,32 +1748,26 @@ toolbarbutton.chevron > .toolbarbutton-m
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-arrow-left-inverted.png");
 }
 
 #TabsToolbar[brighttext] .tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-locale-dir(ltr),
 #TabsToolbar[brighttext] .tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-locale-dir(rtl) {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-arrow-right-inverted.png");
 }
 
-.tabbrowser-arrowscrollbox > .scrollbutton-up:hover,
-.tabbrowser-arrowscrollbox > .scrollbutton-down:hover {
+.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):hover,
+.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):hover {
   -moz-image-region: rect(0, 26px, 20px, 13px);
 }
 
-.tabbrowser-arrowscrollbox > .scrollbutton-up:hover:active,
-.tabbrowser-arrowscrollbox > .scrollbutton-down:hover:active {
+.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):hover:active,
+.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):hover:active {
   -moz-image-region: rect(0, 39px, 20px, 26px);
 }
 
-.tabbrowser-arrowscrollbox > .scrollbutton-up[disabled] > .toolbarbutton-icon,
-.tabbrowser-arrowscrollbox > .scrollbutton-down[disabled] > .toolbarbutton-icon {
-  -moz-image-region: rect(0, 13px, 20px, 0) !important;
-  opacity: .5;
-}
-
 @media (min-resolution: 2dppx) {
   .tabbrowser-arrowscrollbox > .scrollbutton-up,
   .tabbrowser-arrowscrollbox > .scrollbutton-down {
     -moz-image-region: rect(0, 26px, 40px, 0);
   }
 
   .tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-locale-dir(ltr),
   .tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-locale-dir(rtl) {
@@ -1809,87 +1784,36 @@ toolbarbutton.chevron > .toolbarbutton-m
     list-style-image: url("chrome://browser/skin/tabbrowser/tab-arrow-left-inverted@2x.png");
   }
 
   #TabsToolbar[brighttext] .tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-locale-dir(ltr),
   #TabsToolbar[brighttext] .tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-locale-dir(rtl) {
     list-style-image: url("chrome://browser/skin/tabbrowser/tab-arrow-right-inverted@2x.png");
   }
 
-  .tabbrowser-arrowscrollbox > .scrollbutton-up:hover,
-  .tabbrowser-arrowscrollbox > .scrollbutton-down:hover {
+  .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):hover,
+  .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):hover {
     -moz-image-region: rect(0, 52px, 40px, 26px);
   }
 
-  .tabbrowser-arrowscrollbox > .scrollbutton-up:hover:active,
-  .tabbrowser-arrowscrollbox > .scrollbutton-down:hover:active {
+  .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):hover:active,
+  .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):hover:active {
     -moz-image-region: rect(0, 78px, 40px, 52px);
   }
 
-  .tabbrowser-arrowscrollbox > .scrollbutton-up[disabled] > .toolbarbutton-icon,
-  .tabbrowser-arrowscrollbox > .scrollbutton-down[disabled] > .toolbarbutton-icon {
-    -moz-image-region: rect(0, 26px, 40px, 0) !important;
-  }
-
   .tabbrowser-arrowscrollbox > .scrollbutton-up > .toolbarbutton-icon,
   .tabbrowser-arrowscrollbox > .scrollbutton-down > .toolbarbutton-icon {
     width: 13px;
   }
 }
 
 /**
- * Tabstrip & add-on bar toolbar buttons
+ * Tabstrip toolbar buttons
  */
 
-#TabsToolbar .toolbarbutton-1,
-#TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button,
-#TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
-  -moz-appearance: none;
-  margin: 0;
-  /* !important flags needed because of bug 561154: */
-  /* Bug 990390: -moz-any is no longer used in the selector so the !important aren't necessary for that anymore. */
-  padding: 0 !important;
-  border: none !important;
-  border-radius: 0 !important;
-  background: none !important;
-  box-shadow: none !important;
-}
-
-#TabsToolbar .toolbarbutton-1:not([type="menu-button"]),
-#TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button {
-  padding: 0 1px;
-}
-
-#TabsToolbar .toolbarbutton-1 {
-  margin-bottom: var(--tab-toolbar-navbar-overlap);
-}
-
-#TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
-  padding-left: 4px;
-  padding-right: 4px;
-}
-
-.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):hover,
-.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):hover,
-#TabsToolbar .toolbarbutton-1:not([type="menu-button"]):not([disabled=true]):not([open]):hover,
-#TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):hover,
-#TabsToolbar .toolbarbutton-1:not([disabled=true]):not([buttonover]):hover > .toolbarbutton-menubutton-dropmarker {
-  background-image: linear-gradient(transparent, rgba(0,0,0,.15)) !important;
-}
-
-.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):hover:active,
-.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):hover:active,
-#TabsToolbar .toolbarbutton-1:not([type="menu-button"]):not([disabled=true]):hover:active,
-#TabsToolbar .toolbarbutton-1[type="menu"][open],
-#TabsToolbar .toolbarbutton-1[type="panel"][open],
-#TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):hover:active,
-#TabsToolbar .toolbarbutton-1[open]:not([disabled=true]):hover > .toolbarbutton-menubutton-dropmarker {
-  background-image: linear-gradient(transparent, rgba(0,0,0,.3)) !important;
-}
-
 .tabs-newtab-button,
 #TabsToolbar > #new-tab-button,
 #TabsToolbar > toolbarpaletteitem > #new-tab-button {
   list-style-image: url(chrome://browser/skin/tabbrowser/newtab.png);
   -moz-image-region: rect(0, 18px, 20px, 0);
 }
 
 #TabsToolbar[brighttext] .tabs-newtab-button,
@@ -1983,20 +1907,16 @@ toolbarbutton.chevron > .toolbarbutton-m
     width: 17px;
   }
 }
 
 #alltabs-button > .toolbarbutton-menu-dropmarker {
   display: none;
 }
 
-#alltabs-button > .toolbarbutton-icon {
-  margin-inline-end: 2px;
-}
-
 /* All Tabs Menupopup */
 .alltabs-item > .menu-iconic-left > .menu-iconic-icon {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 .alltabs-item[busy] > .menu-iconic-left > .menu-iconic-icon {
   list-style-image: url("chrome://global/skin/icons/loading.png") !important;
 }
--- a/browser/themes/shared/bookmarked-notification.inc.css
+++ b/browser/themes/shared/bookmarked-notification.inc.css
@@ -51,17 +51,17 @@
 
 @media (min-resolution: 2dppx) {
   #bookmarked-notification-anchor[notification="finish"] > #bookmarked-notification {
     background-image: url("chrome://browser/skin/places/bookmarks-notification-finish@2x.png");
   }
 }
 
 #bookmarks-menu-button[notification="finish"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
-  list-style-image: none !important;
+  fill: transparent;
 }
 
 #bookmarked-notification-dropmarker-anchor[notification="finish"] > #bookmarked-notification-dropmarker-icon {
   visibility: visible;
   animation: animation-bookmarkPulse 300ms;
   animation-delay: 600ms;
   animation-timing-function: ease-out;
 }
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -257,17 +257,17 @@
 }
 
 /* Tab Overflow */
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator:not([collapsed]),
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator:not([collapsed]) {
   background-image: url(chrome://browser/skin/tabbrowser/tab-overflow-indicator.png);
   background-size: 100% 100%;
   width: 14px;
-  margin-bottom: var(--navbar-tab-toolbar-highlight-overlap);
+  margin-bottom: calc(var(--navbar-tab-toolbar-highlight-overlap) + var(--tab-toolbar-navbar-overlap));
   pointer-events: none;
   position: relative;
   z-index: 3; /* the selected tab's z-index + 1 */
 }
 
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-start-indicator:-moz-locale-dir(rtl),
 .tabbrowser-arrowscrollbox > .arrowscrollbox-overflow-end-indicator:-moz-locale-dir(ltr) {
   transform: scaleX(-1);
--- a/browser/themes/shared/toolbarbuttons.inc.css
+++ b/browser/themes/shared/toolbarbuttons.inc.css
@@ -2,29 +2,35 @@
  * 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/. */
 
 %filter substitution
 %define toolbarShadowColor hsla(209,67%,12%,0.35)
 %define forwardTransitionLength 150ms
 %define conditionalForwardWithUrlbar window:not([chromehidden~="toolbar"]) #urlbar-wrapper
 
+:root {
+  --toolbarbutton-disabled-opacity: 0.4;
+}
+
 toolbar:-moz-lwtheme {
   --toolbarbutton-hover-background: rgba(255,255,255,.25);
   --toolbarbutton-hover-bordercolor: rgba(0,0,0,.2);
 
   --toolbarbutton-active-background: rgba(70%,70%,70%,.25);
   --toolbarbutton-active-bordercolor: rgba(0,0,0,.3);
   --toolbarbutton-active-boxshadow: 0 0 2px rgba(0,0,0,.6) inset;
 
   --toolbarbutton-checkedhover-backgroundcolor: rgba(85%,85%,85%,.25);
 }
 
 /* ::::: primary toolbar buttons ::::: */
 
+.tabbrowser-arrowscrollbox > .scrollbutton-up[disabled=true],
+.tabbrowser-arrowscrollbox > .scrollbutton-down[disabled=true],
 #main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-icon,
 #main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-menu-dropmarker,
 #main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-menubutton-dropmarker,
 #main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
 #main-window:not([customizing]) .toolbarbutton-1 > .toolbarbutton-menubutton-button[disabled=true] > .toolbarbutton-icon {
   opacity: var(--toolbarbutton-disabled-opacity);
 }
 
@@ -49,16 +55,47 @@ toolbar[brighttext] .toolbarbutton-1 > .
 }
 
 :-moz-any(toolbar, .widget-overflow-list) .toolbarbutton-1:-moz-any(@primaryToolbarButtons@, .toolbarbutton-legacy-addon) > .toolbarbutton-icon,
 :-moz-any(toolbar, .widget-overflow-list) .toolbarbutton-1:-moz-any(@primaryToolbarButtons@, .toolbarbutton-legacy-addon) > :-moz-any(.toolbarbutton-menubutton-button, .toolbarbutton-badge-stack) > .toolbarbutton-icon,
 #bookmarks-menu-button[cui-areatype="toolbar"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
   max-width: 18px;
 }
 
+#TabsToolbar .toolbarbutton-1,
+.tabbrowser-arrowscrollbox > .scrollbutton-up,
+.tabbrowser-arrowscrollbox > .scrollbutton-down {
+  margin: 0 0 calc(var(--navbar-tab-toolbar-highlight-overlap) + var(--tab-toolbar-navbar-overlap));
+}
+
+#TabsToolbar .toolbarbutton-1,
+#TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button,
+.tabbrowser-arrowscrollbox > .scrollbutton-up,
+.tabbrowser-arrowscrollbox > .scrollbutton-down {
+  -moz-appearance: none;
+  padding: 0;
+  min-width: 26px;
+  border: 1px solid transparent;
+  border-bottom-style: none;
+  border-radius: var(--toolbarbutton-border-radius) var(--toolbarbutton-border-radius) 0 0;
+}
+
+#navigator-toolbox:not(:hover) > #TabsToolbar > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .scrollbutton-down:not([highlight]) {
+  transition: 1s background-color ease-out;
+}
+
+.tabbrowser-arrowscrollbox > .scrollbutton-down[highlight] {
+  background-color: Highlight;
+}
+
+.findbar-button {
+  -moz-appearance: none;
+  padding: 0;
+}
+
 #nav-bar .toolbarbutton-1,
 #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button {
   -moz-appearance: none;
   padding: 0;
   margin: 0;
 }
 
 #nav-bar .toolbarbutton-1:not([type=menu-button]),
@@ -199,21 +236,21 @@ toolbar[brighttext] .toolbarbutton-1 > .
   opacity: .2;
 }
 
 #nav-bar[brighttext] .toolbaritem-combined-buttons > separator,
 #nav-bar[brighttext] .toolbarbutton-1:not(:hover):not(:active):not([open]) > .toolbarbutton-menubutton-dropmarker::before {
   opacity: .3;
 }
 
-.findbar-button {
-  -moz-appearance: none;
-  padding: 0;
-}
-
+#TabsToolbar .toolbarbutton-1:not([disabled=true]):hover,
+#TabsToolbar .toolbarbutton-1[open],
+#TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):hover,
+.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled=true]):hover,
+.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled=true]):hover,
 .findbar-button:not(:-moz-any([checked="true"],[disabled="true"])):hover > .toolbarbutton-text,
 #nav-bar .toolbarbutton-1:not([disabled=true]) > .toolbarbutton-menubutton-button[open] + .toolbarbutton-menubutton-dropmarker > .dropmarker-icon,
 #nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any(:hover,[open]) > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
 #nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any(:hover,[open]) > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon,
 #nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-icon,
 #nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-text,
 #nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-badge-stack,
 @conditionalForwardWithUrlbar@ > #forward-button:not([open]):not(:active):not([disabled]):hover > .toolbarbutton-icon,
@@ -235,42 +272,16 @@ toolbar[brighttext] .toolbarbutton-1 > .
   transition-duration: 10ms;
 }
 
 #nav-bar .toolbarbutton-1[checked]:not(:active):hover > .toolbarbutton-icon {
   background-color: var(--toolbarbutton-checkedhover-backgroundcolor);
   transition: background-color .4s;
 }
 
-#TabsToolbar .toolbarbutton-1,
-#TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button,
-.tabbrowser-arrowscrollbox > .scrollbutton-up,
-.tabbrowser-arrowscrollbox > .scrollbutton-down {
-  -moz-appearance: none;
-  border-style: none;
-  padding: 0 3px;
-}
-
-#TabsToolbar .toolbarbutton-1 {
-  margin-bottom: var(--tab-toolbar-navbar-overlap);
-}
-
-#TabsToolbar .toolbarbutton-1:not([disabled=true]):hover,
-#TabsToolbar .toolbarbutton-1[open],
-#TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):hover,
-.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled=true]):hover,
-.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled=true]):hover {
-  background-image: linear-gradient(transparent, rgba(255,255,255,.5)),
-                    linear-gradient(transparent, rgba(0,0,0,.25) 30%),
-                    linear-gradient(transparent, rgba(0,0,0,.25) 30%);
-  background-position: 1px -1px, 0 -1px, 100% -1px;
-  background-size: calc(100% - 2px) 100%, 1px 100%, 1px 100%;
-  background-repeat: no-repeat;
-}
-
 /* unified back/forward button */
 
 :-moz-any(#back-button, #forward-button) > .toolbarbutton-icon {
   border-color: var(--backbutton-border-color) !important;
 }
 
 :-moz-any(#back-button, #forward-button):not(:hover):not(:active):not([open=true]) > .toolbarbutton-icon,
 :-moz-any(#back-button, #forward-button)[disabled=true] > .toolbarbutton-icon {
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -32,17 +32,16 @@
   --toolbarbutton-hover-boxshadow: none;
 
   --toolbarbutton-active-background: rgba(0,0,0,.15);
   --toolbarbutton-active-bordercolor: rgba(0,0,0,.3);
   --toolbarbutton-active-boxshadow: 0 0 0 1px rgba(0,0,0,.15) inset;
 
   --toolbarbutton-checkedhover-backgroundcolor: rgba(0,0,0,.1);
 
-  --toolbarbutton-disabled-opacity: 0.4;
   --backbutton-border-color: var(--urlbar-border-color-hover);
 
   --backbutton-background: rgba(255,255,255,.15);
 
   --urlbar-dropmarker-url: url("chrome://browser/skin/urlbar-history-dropmarker.png");
   --urlbar-dropmarker-region: rect(0px, 11px, 14px, 0px);
   --urlbar-dropmarker-hover-region: rect(0px, 22px, 14px, 11px);
   --urlbar-dropmarker-active-region: rect(0px, 33px, 14px, 22px);
@@ -1514,43 +1513,28 @@ html|span.ac-emphasize-text-url {
   border: none;
 }
 
 /* Tab scrollbox arrow, tabstrip new tab and all-tabs buttons */
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up,
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-arrow-left.svg");
-  margin: 0 0 var(--tab-toolbar-navbar-overlap);
 }
 
 #TabsToolbar[brighttext] > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .scrollbutton-up,
 #TabsToolbar[brighttext] > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .scrollbutton-down {
   list-style-image: url(chrome://browser/skin/tabbrowser/tab-arrow-left-inverted.svg);
 }
 
-.tabbrowser-arrowscrollbox > .scrollbutton-up[disabled],
-.tabbrowser-arrowscrollbox > .scrollbutton-down[disabled] {
-  opacity: .4;
-}
-
 .tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-locale-dir(rtl),
 .tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-locale-dir(ltr) {
   transform: scaleX(-1);
 }
 
-.tabbrowser-arrowscrollbox > .scrollbutton-down {
-  transition: 1s background-color ease-out;
-}
-
-.tabbrowser-arrowscrollbox > .scrollbutton-down[highlight] {
-  background-color: Highlight;
-  transition: none;
-}
-
 .tabs-newtab-button > .toolbarbutton-icon {
   margin-top: -1px;
   margin-bottom: -1px;
 }
 
 .tabs-newtab-button,
 #TabsToolbar > #new-tab-button,
 #TabsToolbar > toolbarpaletteitem > #new-tab-button {
@@ -1578,20 +1562,16 @@ html|span.ac-emphasize-text-url {
   list-style-image: url("chrome://browser/skin/toolbarbutton-dropdown-arrow.png");
 }
 
 #TabsToolbar[brighttext] > #alltabs-button,
 #TabsToolbar[brighttext] > toolbarpaletteitem > #alltabs-button {
   list-style-image: url("chrome://browser/skin/toolbarbutton-dropdown-arrow-inverted.png");
 }
 
-#alltabs-button > .toolbarbutton-icon {
-  margin: 0 2px;
-}
-
 #alltabs-button > .toolbarbutton-menu-dropmarker {
   display: none;
 }
 
 /* All tabs menupopup */
 .alltabs-item > .menu-iconic-left > .menu-iconic-icon {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
--- a/chrome/nsChromeRegistryChrome.cpp
+++ b/chrome/nsChromeRegistryChrome.cpp
@@ -732,17 +732,26 @@ nsChromeRegistryChrome::ManifestLocale(M
 
   if (mDynamicRegistration) {
     ChromePackage chromePackage;
     ChromePackageFromPackageEntry(packageName, entry, &chromePackage,
                                   mSelectedSkin);
     SendManifestEntry(chromePackage);
   }
 
-  if (strcmp(package, "global") == 0) {
+  // We use mainPackage as the package we track for reporting new locales being
+  // registered. For most cases it will be "global", but for Fennec it will be
+  // "browser".
+  nsAutoCString mainPackage;
+  nsresult rv = OverrideLocalePackage(NS_LITERAL_CSTRING("global"), mainPackage);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  if (mainPackage.Equals(package)) {
     // We should refresh the LocaleService, since the available
     // locales changed.
     LocaleService::GetInstance()->OnAvailableLocalesChanged();
   }
 }
 
 void
 nsChromeRegistryChrome::ManifestSkin(ManifestProcessingContext& cx, int lineno,
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -19,17 +19,16 @@ const Telemetry = require("devtools/clie
 const {gDevTools} = require("./devtools");
 
 // Load target and toolbox lazily as they need gDevTools to be fully initialized
 loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
 loader.lazyRequireGetter(this, "Toolbox", "devtools/client/framework/toolbox", true);
 loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
 loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true);
 loader.lazyRequireGetter(this, "BrowserMenus", "devtools/client/framework/browser-menus");
-loader.lazyRequireGetter(this, "findCssSelector", "devtools/shared/inspector/css-logic", true);
 loader.lazyRequireGetter(this, "appendStyleSheet", "devtools/client/shared/stylesheet-utils", true);
 
 loader.lazyImporter(this, "CustomizableUI", "resource:///modules/CustomizableUI.jsm");
 loader.lazyImporter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm");
 loader.lazyImporter(this, "LightweightThemeManager", "resource://gre/modules/LightweightThemeManager.jsm");
 
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
@@ -302,41 +301,34 @@ var gDevToolsBrowser = exports.gDevTools
     let win = Services.wm.getMostRecentWindow("devtools:webide");
     if (win) {
       win.focus();
     } else {
       Services.ww.openWindow(null, "chrome://webide/content/", "webide", "chrome,centerscreen,resizable", null);
     }
   },
 
-  async inspectNode(tab, node) {
+  async inspectNode(tab, nodeSelectors) {
     let target = TargetFactory.forTab(tab);
 
-    // Generate a cross iframes query selector
-    let selectors = [];
-    while (node) {
-      selectors.push(findCssSelector(node));
-      node = node.ownerDocument.defaultView.frameElement;
-    }
-
     let toolbox = await gDevTools.showToolbox(target, "inspector");
     let inspector = toolbox.getCurrentPanel();
 
     // new-node-front tells us when the node has been selected, whether the
     // browser is remote or not.
     let onNewNode = inspector.selection.once("new-node-front");
 
     // Evaluate the cross iframes query selectors
     async function querySelectors(nodeFront) {
-      let selector = selectors.pop();
+      let selector = nodeSelectors.pop();
       if (!selector) {
         return nodeFront;
       }
       nodeFront = await inspector.walker.querySelector(nodeFront, selector);
-      if (selectors.length > 0) {
+      if (nodeSelectors.length > 0) {
         let { nodes } = await inspector.walker.children(nodeFront);
         // This is the NodeFront for the document node inside the iframe
         nodeFront = nodes[0];
       }
       return querySelectors(nodeFront);
     }
     let nodeFront = await inspector.walker.getRootNode();
     nodeFront = await querySelectors(nodeFront);
--- a/devtools/client/framework/test/browser_ignore_toolbox_network_requests.js
+++ b/devtools/client/framework/test/browser_ignore_toolbox_network_requests.js
@@ -18,17 +18,17 @@ add_task(function* () {
   let tab = yield addTab(URL_ROOT + "doc_viewsource.html");
   let target = TargetFactory.forTab(tab);
   let toolbox = yield gDevTools.showToolbox(target, "styleeditor");
   let panel = toolbox.getPanel("styleeditor");
 
   is(panel.UI.editors.length, 1, "correct number of editors opened");
 
   let monitor = yield toolbox.selectTool("netmonitor");
-  let { gStore, windowRequire } = monitor.panelWin;
+  let { store, windowRequire } = monitor.panelWin;
 
-  is(gStore.getState().requests.requests.size, 0, "No network requests appear in the network panel");
+  is(store.getState().requests.requests.size, 0, "No network requests appear in the network panel");
 
   yield gDevTools.closeToolbox(target);
   tab = target = toolbox = panel = null;
   gBrowser.removeCurrentTab();
   flags.testing = isTesting;
 });
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -207,16 +207,19 @@ devtools.jar:
     skin/images/dock-undock.svg (themes/images/dock-undock.svg)
     skin/floating-scrollbars-dark-theme.css (themes/floating-scrollbars-dark-theme.css)
     skin/floating-scrollbars-responsive-design.css (themes/floating-scrollbars-responsive-design.css)
     skin/inspector.css (themes/inspector.css)
     skin/images/profiler-stopwatch.svg (themes/images/profiler-stopwatch.svg)
     skin/images/debugging-addons.svg (themes/images/debugging-addons.svg)
     skin/images/debugging-tabs.svg (themes/images/debugging-tabs.svg)
     skin/images/debugging-workers.svg (themes/images/debugging-workers.svg)
+    skin/images/gcli_sec_bad.svg (themes/images/gcli_sec_bad.svg)
+    skin/images/gcli_sec_good.svg (themes/images/gcli_sec_good.svg)
+    skin/images/gcli_sec_moderate.svg (themes/images/gcli_sec_moderate.svg)
     skin/images/globe.svg (themes/images/globe.svg)
     skin/images/tool-options.svg (themes/images/tool-options.svg)
     skin/images/tool-webconsole.svg (themes/images/tool-webconsole.svg)
     skin/images/tool-canvas.svg (themes/images/tool-canvas.svg)
     skin/images/tool-debugger.svg (themes/images/tool-debugger.svg)
     skin/images/tool-debugger-paused.svg (themes/images/tool-debugger-paused.svg)
     skin/images/tool-inspector.svg (themes/images/tool-inspector.svg)
     skin/images/tool-shadereditor.svg (themes/images/tool-shadereditor.svg)
--- a/devtools/client/netmonitor/index.html
+++ b/devtools/client/netmonitor/index.html
@@ -21,39 +21,42 @@
       }).require;
 
       const EventEmitter = require("devtools/shared/event-emitter");
       const { createFactory } = require("devtools/client/shared/vendor/react");
       const { render, unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom");
       const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
       const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
       const { configureStore } = require("./src/utils/create-store");
-      const store = window.gStore = configureStore();
+      const store = configureStore();
       const actions = bindActionCreators(require("./src/actions/index"), store.dispatch);
-      const { NetMonitorController } = require("./src/netmonitor-controller");
+      const { onFirefoxConnect, onDisconnect } = require("./src/connector/index");
 
       // Inject EventEmitter into global window.
       EventEmitter.decorate(window);
+      // Inject to global window for testing
+      window.store = store;
 
       window.Netmonitor = {
         bootstrap({ toolbox }) {
           this.mount = document.querySelector("#mount");
-          const App = createFactory(require("./src/components/app"));
-          render(Provider({ store }, App()), this.mount);
-          return NetMonitorController.startupNetMonitor({
+          const connection = {
             tabConnection: {
               tabTarget: toolbox.target,
             },
             toolbox,
-          }, actions);
+          };
+          const App = createFactory(require("./src/components/app"));
+          render(Provider({ store }, App()), this.mount);
+          return onFirefoxConnect(connection, actions, store.getState);
         },
 
         destroy() {
           unmountComponentAtNode(this.mount);
-          return NetMonitorController.shutdownNetMonitor();
+          return onDisconnect();
         }
       };
 
       // Implement support for chrome://devtools/content/netmonitor/index.html?type=tab&id=1234 URLs
       // where 1234 is the tab id, you can retrieve from about:debugging#tabs links.
       // Simply copy the id from about:devtools-toolbox?type=tab&id=1234 URLs.
 
       // URL constructor doesn't support chrome: scheme
--- a/devtools/client/netmonitor/index.js
+++ b/devtools/client/netmonitor/index.js
@@ -34,19 +34,22 @@ pref("devtools.netmonitor.har.jsonpCallb
 pref("devtools.netmonitor.har.includeResponseBodies", true);
 pref("devtools.netmonitor.har.compress", false);
 pref("devtools.netmonitor.har.forceExport", false);
 pref("devtools.netmonitor.har.pageLoadedTimeout", 1500);
 pref("devtools.netmonitor.har.enableAutoExportToFile", false);
 pref("devtools.webconsole.persistlog", false);
 
 const App = require("./src/components/app");
-const store = window.gStore = configureStore();
+const store = configureStore();
 const actions = bindActionCreators(require("./src/actions"), store.dispatch);
-const { NetMonitorController } = require("./src/netmonitor-controller");
+const { onConnect } = require("./src/connector");
+
+// Inject to global window for testing
+window.store = store;
 
 /**
  * Stylesheet links in devtools xhtml files are using chrome or resource URLs.
  * Rewrite the href attribute to remove the protocol. web-server.js contains redirects
  * to map CSS urls to the proper file. Supports urls using:
  *   - devtools/client/
  *   - devtools/content/
  *   - skin/
@@ -62,15 +65,15 @@ window.addEventListener("DOMContentLoade
     document.documentElement.setAttribute("platform", "mac");
   } else if (appinfo.OS === "Linux") {
     document.documentElement.setAttribute("platform", "linux");
   } else {
     document.documentElement.setAttribute("platform", "win");
   }
 });
 
-bootstrap(React, ReactDOM).then(connection => {
+bootstrap(React, ReactDOM).then((connection) => {
   if (!connection) {
     return;
   }
   renderRoot(React, ReactDOM, App, store);
-  NetMonitorController.startupNetMonitor(connection, actions);
+  onConnect(connection, actions, store.getState);
 });
--- a/devtools/client/netmonitor/src/actions/requests.js
+++ b/devtools/client/netmonitor/src/actions/requests.js
@@ -1,23 +1,23 @@
 /* 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 { sendHTTPRequest } = require("../connector/index");
 const {
   ADD_REQUEST,
   CLEAR_REQUESTS,
   CLONE_SELECTED_REQUEST,
   REMOVE_SELECTED_CUSTOM_REQUEST,
   SEND_CUSTOM_REQUEST,
   UPDATE_REQUEST,
 } = require("../constants");
-const { NetMonitorController } = require("../netmonitor-controller");
 const { getSelectedRequest } = require("../selectors/index");
 
 function addRequest(id, data, batch) {
   return {
     type: ADD_REQUEST,
     id,
     data,
     meta: { batch },
@@ -42,20 +42,16 @@ function cloneSelectedRequest() {
     type: CLONE_SELECTED_REQUEST
   };
 }
 
 /**
  * Send a new HTTP request using the data in the custom request form.
  */
 function sendCustomRequest() {
-  if (!NetMonitorController.supportsCustomRequest) {
-    return cloneSelectedRequest();
-  }
-
   return (dispatch, getState) => {
     const selected = getSelectedRequest(getState());
 
     if (!selected) {
       return;
     }
 
     // Send a new HTTP request using the data in the custom request form
@@ -66,17 +62,17 @@ function sendCustomRequest() {
     };
     if (selected.requestHeaders) {
       data.headers = selected.requestHeaders.headers;
     }
     if (selected.requestPostData) {
       data.body = selected.requestPostData.postData.text;
     }
 
-    NetMonitorController.webConsoleClient.sendHTTPRequest(data, (response) => {
+    sendHTTPRequest(data, (response) => {
       return dispatch({
         type: SEND_CUSTOM_REQUEST,
         id: response.eventActor.actor,
       });
     });
   };
 }
 
--- a/devtools/client/netmonitor/src/actions/ui.js
+++ b/devtools/client/netmonitor/src/actions/ui.js
@@ -8,17 +8,17 @@ const {
   ACTIVITY_TYPE,
   OPEN_NETWORK_DETAILS,
   OPEN_STATISTICS,
   RESET_COLUMNS,
   SELECT_DETAILS_PANEL_TAB,
   TOGGLE_COLUMN,
   WATERFALL_RESIZE,
 } = require("../constants");
-const { NetMonitorController } = require("../netmonitor-controller");
+const { triggerActivity } = require("../connector/index");
 
 /**
  * Change network details panel.
  *
  * @param {boolean} open - expected network details panel open state
  */
 function openNetworkDetails(open) {
   return {
@@ -29,17 +29,17 @@ function openNetworkDetails(open) {
 
 /**
  * Change performance statistics panel open state.
  *
  * @param {boolean} visible - expected performance statistics panel open state
  */
 function openStatistics(open) {
   if (open) {
-    NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
+    triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
   }
   return {
     type: OPEN_STATISTICS,
     open,
   };
 }
 
 /**
--- a/devtools/client/netmonitor/src/components/headers-panel.js
+++ b/devtools/client/netmonitor/src/components/headers-panel.js
@@ -5,17 +5,16 @@
 "use strict";
 
 const {
   createClass,
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
-const { NetMonitorController } = require("../netmonitor-controller");
 const {
   getFormattedIPAndPort,
   getFormattedSize,
 } = require("../utils/format-utils");
 const { L10N } = require("../utils/l10n");
 const {
   getHeadersURL,
   getHTTPStatusCodeURL,
@@ -196,17 +195,17 @@ const HeadersPanel = createClass({
               + " status-text",
             readOnly: true,
             value: `${status} ${statusText}`,
             size: `${inputWidth}`,
           }),
           statusCodeDocURL ? MDNLink({
             url: statusCodeDocURL,
           }) : null,
-          NetMonitorController.supportsCustomRequest && button({
+          button({
             className: "devtools-button",
             onClick: cloneSelectedRequest,
           }, EDIT_AND_RESEND),
           button({
             className: "devtools-button",
             onClick: this.toggleRawHeaders,
           }, RAW_HEADERS),
         )
--- a/devtools/client/netmonitor/src/components/monitor-panel.js
+++ b/devtools/client/netmonitor/src/components/monitor-panel.js
@@ -9,17 +9,17 @@ 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 { getLongString } = require("../utils/client");
+const { getLongString } = require("../connector/index");
 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("./network-details-panel"));
 const RequestList = createFactory(require("./request-list"));
 const Toolbar = createFactory(require("./toolbar"));
--- a/devtools/client/netmonitor/src/components/request-list-empty-notice.js
+++ b/devtools/client/netmonitor/src/components/request-list-empty-notice.js
@@ -7,18 +7,18 @@
 const {
   createClass,
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const Actions = require("../actions/index");
+const { triggerActivity } = require("../connector/index");
 const { ACTIVITY_TYPE } = require("../constants");
-const { NetMonitorController } = require("../netmonitor-controller");
 const { L10N } = require("../utils/l10n");
 const { getPerformanceAnalysisURL } = require("../utils/mdn-utils");
 
 // Components
 const MDNLink = createFactory(require("./mdn-link"));
 
 const { button, div, span } = DOM;
 
@@ -65,13 +65,11 @@ const RequestListEmptyNotice = createCla
     );
   }
 });
 
 module.exports = connect(
   undefined,
   dispatch => ({
     onPerfClick: () => dispatch(Actions.openStatistics(true)),
-    onReloadClick: () =>
-      NetMonitorController
-        .triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT),
+    onReloadClick: () => triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT),
   })
 )(RequestListEmptyNotice);
--- a/devtools/client/netmonitor/src/components/stack-trace-panel.js
+++ b/devtools/client/netmonitor/src/components/stack-trace-panel.js
@@ -4,32 +4,31 @@
 
 "use strict";
 
 const {
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
+const { viewSourceInDebugger } = require("../connector/index");
 
 const { div } = DOM;
 
 // Components
 const StackTrace = createFactory(require("devtools/client/shared/components/stack-trace"));
 
 function StackTracePanel({ request }) {
   let { stacktrace } = request.cause;
 
   return (
     div({ className: "panel-container" },
       StackTrace({
         stacktrace,
-        onViewSourceInDebugger: (frame) => {
-          window.NetMonitorController.viewSourceInDebugger(frame.url, frame.line);
-        },
+        onViewSourceInDebugger: ({ url, line }) => viewSourceInDebugger(url, line),
       }),
     )
   );
 }
 
 StackTracePanel.displayName = "StackTracePanel";
 
 StackTracePanel.propTypes = {
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/src/connector/firefox-connector.js
@@ -0,0 +1,724 @@
+/* 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 Services = require("Services");
+const { CurlUtils } = require("devtools/client/shared/curl");
+const { TimelineFront } = require("devtools/shared/fronts/timeline");
+const { ACTIVITY_TYPE, EVENTS } = require("../constants");
+const { getDisplayedRequestById } = require("../selectors/index");
+const { fetchHeaders, formDataURI } = require("../utils/request-utils");
+
+class FirefoxConnector {
+  constructor() {
+    this.connect = this.connect.bind(this);
+    this.disconnect = this.disconnect.bind(this);
+    this.willNavigate = this.willNavigate.bind(this);
+    this.displayCachedEvents = this.displayCachedEvents.bind(this);
+    this.onDocLoadingMarker = this.onDocLoadingMarker.bind(this);
+    this.addRequest = this.addRequest.bind(this);
+    this.updateRequest = this.updateRequest.bind(this);
+    this.fetchImage = this.fetchImage.bind(this);
+    this.fetchRequestHeaders = this.fetchRequestHeaders.bind(this);
+    this.fetchResponseHeaders = this.fetchResponseHeaders.bind(this);
+    this.fetchPostData = this.fetchPostData.bind(this);
+    this.fetchResponseCookies = this.fetchResponseCookies.bind(this);
+    this.fetchRequestCookies = this.fetchRequestCookies.bind(this);
+    this.getPayloadFromQueue = this.getPayloadFromQueue.bind(this);
+    this.isQueuePayloadReady = this.isQueuePayloadReady.bind(this);
+    this.pushPayloadToQueue = this.pushPayloadToQueue.bind(this);
+    this.sendHTTPRequest = this.sendHTTPRequest.bind(this);
+    this.setPreferences = this.setPreferences.bind(this);
+    this.triggerActivity = this.triggerActivity.bind(this);
+    this.inspectRequest = this.inspectRequest.bind(this);
+    this.getLongString = this.getLongString.bind(this);
+    this.getNetworkRequest = this.getNetworkRequest.bind(this);
+    this.getTabTarget = this.getTabTarget.bind(this);
+    this.viewSourceInDebugger = this.viewSourceInDebugger.bind(this);
+
+    // Event handlers
+    this.onNetworkEvent = this.onNetworkEvent.bind(this);
+    this.onNetworkEventUpdate = this.onNetworkEventUpdate.bind(this);
+    this.onRequestHeaders = this.onRequestHeaders.bind(this);
+    this.onRequestCookies = this.onRequestCookies.bind(this);
+    this.onRequestPostData = this.onRequestPostData.bind(this);
+    this.onSecurityInfo = this.onSecurityInfo.bind(this);
+    this.onResponseHeaders = this.onResponseHeaders.bind(this);
+    this.onResponseCookies = this.onResponseCookies.bind(this);
+    this.onResponseContent = this.onResponseContent.bind(this);
+    this.onEventTimings = this.onEventTimings.bind(this);
+  }
+
+  async connect(connection, actions, getState) {
+    this.actions = actions;
+    this.getState = getState;
+    this.tabTarget = connection.tabConnection.tabTarget;
+    this.tabClient = this.tabTarget.isTabActor ? this.tabTarget.activeTab : null;
+    this.webConsoleClient = this.tabTarget.activeConsole;
+
+    this.tabTarget.on("will-navigate", this.willNavigate);
+    this.tabTarget.on("close", this.disconnect);
+    this.webConsoleClient.on("networkEvent", this.onNetworkEvent);
+    this.webConsoleClient.on("networkEventUpdate", this.onNetworkEventUpdate);
+
+    // Don't start up waiting for timeline markers if the server isn't
+    // recent enough to emit the markers we're interested in.
+    if (this.tabTarget.getTrait("documentLoadingMarkers")) {
+      this.timelineFront = new TimelineFront(this.tabTarget.client, this.tabTarget.form);
+      this.timelineFront.on("doc-loading", this.onDocLoadingMarker);
+      await this.timelineFront.start({ withDocLoadingEvents: true });
+    }
+
+    this.displayCachedEvents();
+  }
+
+  async disconnect() {
+    // When debugging local or a remote instance, the connection is closed by
+    // the RemoteTarget. The webconsole actor is stopped on disconnect.
+    this.tabClient = null;
+    this.webConsoleClient = null;
+
+    // The timeline front wasn't initialized and started if the server wasn't
+    // recent enough to emit the markers we were interested in.
+    if (this.tabTarget.getTrait("documentLoadingMarkers") && this.timelineFront) {
+      this.timelineFront.off("doc-loading", this.onDocLoadingMarker);
+      await this.timelineFront.destroy();
+      this.timelineFront = null;
+    }
+  }
+
+  willNavigate() {
+    if (!Services.prefs.getBoolPref("devtools.webconsole.persistlog")) {
+      this.actions.batchReset();
+      this.actions.clearRequests();
+    } else {
+      // If the log is persistent, just clear all accumulated timing markers.
+      this.actions.clearTimingMarkers();
+    }
+  }
+
+  /**
+   * Display any network events already in the cache.
+   */
+  displayCachedEvents() {
+    for (let networkInfo of this.webConsoleClient.getNetworkEvents()) {
+      // First add the request to the timeline.
+      this.onNetworkEvent("networkEvent", networkInfo);
+      // Then replay any updates already received.
+      for (let updateType of networkInfo.updates) {
+        this.onNetworkEventUpdate("networkEventUpdate", {
+          packet: { updateType },
+          networkInfo,
+        });
+      }
+    }
+  }
+
+  /**
+   * The "DOMContentLoaded" and "Load" events sent by the timeline actor.
+   *
+   * @param {object} marker
+   */
+  onDocLoadingMarker(marker) {
+    window.emit(EVENTS.TIMELINE_EVENT, marker);
+    this.actions.addTimingMarker(marker);
+  }
+
+  /**
+   * Add a new network request to application state.
+   *
+   * @param {string} id request id
+   * @param {object} data data payload will be added to application state
+   */
+  addRequest(id, data) {
+    let {
+      method,
+      url,
+      isXHR,
+      cause,
+      startedDateTime,
+      fromCache,
+      fromServiceWorker,
+    } = data;
+
+    this.actions.addRequest(
+      id,
+      {
+        // Convert the received date/time string to a unix timestamp.
+        startedMillis: Date.parse(startedDateTime),
+        method,
+        url,
+        isXHR,
+        cause,
+        fromCache,
+        fromServiceWorker,
+      },
+      true,
+    )
+    .then(() => window.emit(EVENTS.REQUEST_ADDED, id));
+  }
+
+  /**
+   * Update a network request if it already exists in application state.
+   *
+   * @param {string} id request id
+   * @param {object} data data payload will be updated to application state
+   */
+  async updateRequest(id, data) {
+    let {
+      mimeType,
+      responseContent,
+      responseCookies,
+      responseHeaders,
+      requestCookies,
+      requestHeaders,
+      requestPostData,
+    } = data;
+
+    // fetch request detail contents in parallel
+    let [
+      imageObj,
+      requestHeadersObj,
+      responseHeadersObj,
+      postDataObj,
+      requestCookiesObj,
+      responseCookiesObj,
+    ] = await Promise.all([
+      this.fetchImage(mimeType, responseContent),
+      this.fetchRequestHeaders(requestHeaders),
+      this.fetchResponseHeaders(responseHeaders),
+      this.fetchPostData(requestPostData),
+      this.fetchRequestCookies(requestCookies),
+      this.fetchResponseCookies(responseCookies),
+    ]);
+
+    let payload = Object.assign({}, data,
+                                    imageObj, requestHeadersObj, responseHeadersObj,
+                                    postDataObj, requestCookiesObj, responseCookiesObj);
+    await this.actions.updateRequest(id, payload, true);
+  }
+
+  async fetchImage(mimeType, responseContent) {
+    let payload = {};
+    if (mimeType && responseContent && responseContent.content) {
+      let { encoding, text } = responseContent.content;
+      let response = await this.getLongString(text);
+
+      if (mimeType.includes("image/")) {
+        payload.responseContentDataUri = formDataURI(mimeType, encoding, response);
+      }
+
+      responseContent.content.text = response;
+      payload.responseContent = responseContent;
+    }
+    return payload;
+  }
+
+  async fetchRequestHeaders(requestHeaders) {
+    let payload = {};
+    if (requestHeaders && requestHeaders.headers && requestHeaders.headers.length) {
+      let headers = await fetchHeaders(requestHeaders, this.getLongString);
+      if (headers) {
+        payload.requestHeaders = headers;
+      }
+    }
+    return payload;
+  }
+
+  async fetchResponseHeaders(responseHeaders) {
+    let payload = {};
+    if (responseHeaders && responseHeaders.headers && responseHeaders.headers.length) {
+      let headers = await fetchHeaders(responseHeaders, this.getLongString);
+      if (headers) {
+        payload.responseHeaders = headers;
+      }
+    }
+    return payload;
+  }
+
+  async fetchPostData(requestPostData) {
+    let payload = {};
+    if (requestPostData && requestPostData.postData) {
+      let { text } = requestPostData.postData;
+      let postData = await this.getLongString(text);
+      const headers = CurlUtils.getHeadersFromMultipartText(postData);
+      const headersSize = headers.reduce((acc, { name, value }) => {
+        return acc + name.length + value.length + 2;
+      }, 0);
+      requestPostData.postData.text = postData;
+      payload.requestPostData = Object.assign({}, requestPostData);
+      payload.requestHeadersFromUploadStream = { headers, headersSize };
+    }
+    return payload;
+  }
+
+  async fetchResponseCookies(responseCookies) {
+    let payload = {};
+    if (responseCookies) {
+      let resCookies = [];
+      // response store cookies in responseCookies or responseCookies.cookies
+      let cookies = responseCookies.cookies ?
+        responseCookies.cookies : responseCookies;
+      // make sure cookies is iterable
+      if (typeof cookies[Symbol.iterator] === "function") {
+        for (let cookie of cookies) {
+          resCookies.push(Object.assign({}, cookie, {
+            value: await this.getLongString(cookie.value),
+          }));
+        }
+        if (resCookies.length) {
+          payload.responseCookies = resCookies;
+        }
+      }
+    }
+    return payload;
+  }
+
+  async fetchRequestCookies(requestCookies) {
+    let payload = {};
+    if (requestCookies) {
+      let reqCookies = [];
+      // request store cookies in requestCookies or requestCookies.cookies
+      let cookies = requestCookies.cookies ?
+        requestCookies.cookies : requestCookies;
+      // make sure cookies is iterable
+      if (typeof cookies[Symbol.iterator] === "function") {
+        for (let cookie of cookies) {
+          reqCookies.push(Object.assign({}, cookie, {
+            value: await this.getLongString(cookie.value),
+          }));
+        }
+        if (reqCookies.length) {
+          payload.requestCookies = reqCookies;
+        }
+      }
+    }
+    return payload;
+  }
+
+  /**
+   * Access a payload item from payload queue.
+   *
+   * @param {string} id request id
+   * @return {boolean} return a queued payload item from queue.
+   */
+  getPayloadFromQueue(id) {
+    return this.payloadQueue.find((item) => item.id === id);
+  }
+
+  /**
+   * Packet order of "networkUpdateEvent" is predictable, as a result we can wait for
+   * the last one "eventTimings" packet arrives to check payload is ready.
+   *
+   * @param {string} id request id
+   * @return {boolean} return whether a specific networkEvent has been updated completely.
+   */
+  isQueuePayloadReady(id) {
+    let queuedPayload = this.getPayloadFromQueue(id);
+    return queuedPayload && queuedPayload.payload.eventTimings;
+  }
+
+  /**
+   * Push a request payload into a queue if request doesn't exist. Otherwise update the
+   * request itself.
+   *
+   * @param {string} id request id
+   * @param {object} payload request data payload
+   */
+  pushPayloadToQueue(id, payload) {
+    let queuedPayload = this.getPayloadFromQueue(id);
+    if (!queuedPayload) {
+      this.payloadQueue.push({ id, payload });
+    } else {
+      // Merge upcoming networkEventUpdate payload into existing one
+      queuedPayload.payload = Object.assign({}, queuedPayload.payload, payload);
+    }
+  }
+
+  /**
+   * Send a HTTP request data payload
+   *
+   * @param {object} data data payload would like to sent to backend
+   * @param {function} callback callback will be invoked after the request finished
+   */
+  sendHTTPRequest(data, callback) {
+    this.webConsoleClient.sendHTTPRequest(data, callback);
+  }
+
+  /**
+   * Set network preferences to control network flow
+   *
+   * @param {object} request request payload would like to sent to backend
+   * @param {function} callback callback will be invoked after the request finished
+   */
+  setPreferences(request, callback) {
+    this.webConsoleClient.setPreferences(request, callback);
+  }
+
+  /**
+   * Triggers a specific "activity" to be performed by the frontend.
+   * This can be, for example, triggering reloads or enabling/disabling cache.
+   *
+   * @param {number} type The activity type. See the ACTIVITY_TYPE const.
+   * @return {object} A promise resolved once the activity finishes and the frontend
+   *                  is back into "standby" mode.
+   */
+  triggerActivity(type) {
+    // Puts the frontend into "standby" (when there's no particular activity).
+    let standBy = () => {
+      this.currentActivity = ACTIVITY_TYPE.NONE;
+    };
+
+    // Waits for a series of "navigation start" and "navigation stop" events.
+    let waitForNavigation = () => {
+      return new Promise((resolve) => {
+        this.tabTarget.once("will-navigate", () => {
+          this.tabTarget.once("navigate", () => {
+            resolve();
+          });
+        });
+      });
+    };
+
+    // Reconfigures the tab, optionally triggering a reload.
+    let reconfigureTab = (options) => {
+      return new Promise((resolve) => {
+        this.tabTarget.activeTab.reconfigure(options, resolve);
+      });
+    };
+
+    // Reconfigures the tab and waits for the target to finish navigating.
+    let reconfigureTabAndWaitForNavigation = (options) => {
+      options.performReload = true;
+      let navigationFinished = waitForNavigation();
+      return reconfigureTab(options).then(() => navigationFinished);
+    };
+    switch (type) {
+      case ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT:
+        return reconfigureTabAndWaitForNavigation({}).then(standBy);
+      case ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED:
+        this.currentActivity = ACTIVITY_TYPE.ENABLE_CACHE;
+        this.tabTarget.once("will-navigate", () => {
+          this.currentActivity = type;
+        });
+        return reconfigureTabAndWaitForNavigation({
+          cacheDisabled: false,
+          performReload: true,
+        }).then(standBy);
+      case ACTIVITY_TYPE.RELOAD.WITH_CACHE_DISABLED:
+        this.currentActivity = ACTIVITY_TYPE.DISABLE_CACHE;
+        this.tabTarget.once("will-navigate", () => {
+          this.currentActivity = type;
+        });
+        return reconfigureTabAndWaitForNavigation({
+          cacheDisabled: true,
+          performReload: true,
+        }).then(standBy);
+      case ACTIVITY_TYPE.ENABLE_CACHE:
+        this.currentActivity = type;
+        return reconfigureTab({
+          cacheDisabled: false,
+          performReload: false,
+        }).then(standBy);
+      case ACTIVITY_TYPE.DISABLE_CACHE:
+        this.currentActivity = type;
+        return reconfigureTab({
+          cacheDisabled: true,
+          performReload: false,
+        }).then(standBy);
+    }
+    this.currentActivity = ACTIVITY_TYPE.NONE;
+    return Promise.reject(new Error("Invalid activity type"));
+  }
+
+  /**
+   * Selects the specified request in the waterfall and opens the details view.
+   *
+   * @param {string} requestId The actor ID of the request to inspect.
+   * @return {object} A promise resolved once the task finishes.
+   */
+  inspectRequest(requestId) {
+    // Look for the request in the existing ones or wait for it to appear, if
+    // the network monitor is still loading.
+    return new Promise((resolve) => {
+      let request = null;
+      let inspector = () => {
+        request = getDisplayedRequestById(this.getState(), requestId);
+        if (!request) {
+          // Reset filters so that the request is visible.
+          this.actions.toggleRequestFilterType("all");
+          request = getDisplayedRequestById(this.getState(), requestId);
+        }
+
+        // If the request was found, select it. Otherwise this function will be
+        // called again once new requests arrive.
+        if (request) {
+          window.off(EVENTS.REQUEST_ADDED, inspector);
+          this.actions.selectRequest(request.id);
+          resolve();
+        }
+      };
+
+      inspector();
+
+      if (!request) {
+        window.on(EVENTS.REQUEST_ADDED, inspector);
+      }
+    });
+  }
+
+  /**
+   * Fetches the network information packet from actor server
+   *
+   * @param {string} id request id
+   * @return {object} networkInfo data packet
+   */
+  getNetworkRequest(id) {
+    return this.webConsoleClient.getNetworkRequest(id);
+  }
+
+  /**
+   * Fetches the full text of a LongString.
+   *
+   * @param {object|string} stringGrip
+   *        The long string grip containing the corresponding actor.
+   *        If you pass in a plain string (by accident or because you're lazy),
+   *        then a promise of the same string is simply returned.
+   * @return {object}
+   *         A promise that is resolved when the full string contents
+   *         are available, or rejected if something goes wrong.
+   */
+  getLongString(stringGrip) {
+    return this.webConsoleClient.getString(stringGrip);
+  }
+
+  /**
+   * Getter that access tab target instance.
+   * @return {object} browser tab target instance
+   */
+  getTabTarget() {
+    return this.tabTarget;
+  }
+
+  /**
+   * Open a given source in Debugger
+   * @param {string} sourceURL source url
+   * @param {number} sourceLine source line number
+   */
+  viewSourceInDebugger(sourceURL, sourceLine) {
+    if (this.toolbox) {
+      this.toolbox.viewSourceInDebugger(sourceURL, sourceLine);
+    }
+  }
+
+  /**
+   * The "networkEvent" message type handler.
+   *
+   * @param {string} type message type
+   * @param {object} networkInfo network request information
+   */
+  onNetworkEvent(type, networkInfo) {
+    let {
+      actor,
+      cause,
+      fromCache,
+      fromServiceWorker,
+      isXHR,
+      request: {
+        method,
+        url,
+      },
+      startedDateTime,
+    } = networkInfo;
+
+    this.addRequest(actor, {
+      cause,
+      fromCache,
+      fromServiceWorker,
+      isXHR,
+      method,
+      startedDateTime,
+      url,
+    });
+
+    window.emit(EVENTS.NETWORK_EVENT, actor);
+  }
+
+  /**
+   * The "networkEventUpdate" message type handler.
+   *
+   * @param {string} type message type
+   * @param {object} packet the message received from the server.
+   * @param {object} networkInfo the network request information.
+   */
+  onNetworkEventUpdate(type, { packet, networkInfo }) {
+    let { actor } = networkInfo;
+
+    switch (packet.updateType) {
+      case "requestHeaders":
+        this.webConsoleClient.getRequestHeaders(actor, this.onRequestHeaders);
+        window.emit(EVENTS.UPDATING_REQUEST_HEADERS, actor);
+        break;
+      case "requestCookies":
+        this.webConsoleClient.getRequestCookies(actor, this.onRequestCookies);
+        window.emit(EVENTS.UPDATING_REQUEST_COOKIES, actor);
+        break;
+      case "requestPostData":
+        this.webConsoleClient.getRequestPostData(actor, this.onRequestPostData);
+        window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, actor);
+        break;
+      case "securityInfo":
+        this.updateRequest(actor, {
+          securityState: networkInfo.securityInfo,
+        }).then(() => {
+          this.webConsoleClient.getSecurityInfo(actor, this.onSecurityInfo);
+          window.emit(EVENTS.UPDATING_SECURITY_INFO, actor);
+        });
+        break;
+      case "responseHeaders":
+        this.webConsoleClient.getResponseHeaders(actor, this.onResponseHeaders);
+        window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, actor);
+        break;
+      case "responseCookies":
+        this.webConsoleClient.getResponseCookies(actor, this.onResponseCookies);
+        window.emit(EVENTS.UPDATING_RESPONSE_COOKIES, actor);
+        break;
+      case "responseStart":
+        this.updateRequest(actor, {
+          httpVersion: networkInfo.response.httpVersion,
+          remoteAddress: networkInfo.response.remoteAddress,
+          remotePort: networkInfo.response.remotePort,
+          status: networkInfo.response.status,
+          statusText: networkInfo.response.statusText,
+          headersSize: networkInfo.response.headersSize
+        }).then(() => {
+          window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
+        });
+        break;
+      case "responseContent":
+        this.webConsoleClient.getResponseContent(actor,
+          this.onResponseContent.bind(this, {
+            contentSize: networkInfo.response.bodySize,
+            transferredSize: networkInfo.response.transferredSize,
+            mimeType: networkInfo.response.content.mimeType
+          }));
+        window.emit(EVENTS.UPDATING_RESPONSE_CONTENT, actor);
+        break;
+      case "eventTimings":
+        this.updateRequest(actor, { totalTime: networkInfo.totalTime })
+          .then(() => {
+            this.webConsoleClient.getEventTimings(actor, this.onEventTimings);
+            window.emit(EVENTS.UPDATING_EVENT_TIMINGS, actor);
+          });
+        break;
+    }
+  }
+
+  /**
+   * Handles additional information received for a "requestHeaders" packet.
+   *
+   * @param {object} response the message received from the server.
+   */
+  onRequestHeaders(response) {
+    this.updateRequest(response.from, {
+      requestHeaders: response
+    }).then(() => {
+      window.emit(EVENTS.RECEIVED_REQUEST_HEADERS, response.from);
+    });
+  }
+
+  /**
+   * Handles additional information received for a "requestCookies" packet.
+   *
+   * @param {object} response the message received from the server.
+   */
+  onRequestCookies(response) {
+    this.updateRequest(response.from, {
+      requestCookies: response
+    }).then(() => {
+      window.emit(EVENTS.RECEIVED_REQUEST_COOKIES, response.from);
+    });
+  }
+
+  /**
+   * Handles additional information received for a "requestPostData" packet.
+   *
+   * @param {object} response the message received from the server.
+   */
+  onRequestPostData(response) {
+    this.updateRequest(response.from, {
+      requestPostData: response
+    }).then(() => {
+      window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA, response.from);
+    });
+  }
+
+  /**
+   * Handles additional information received for a "securityInfo" packet.
+   *
+   * @param {object} response the message received from the server.
+   */
+  onSecurityInfo(response) {
+    this.updateRequest(response.from, {
+      securityInfo: response.securityInfo
+    }).then(() => {
+      window.emit(EVENTS.RECEIVED_SECURITY_INFO, response.from);
+    });
+  }
+
+  /**
+   * Handles additional information received for a "responseHeaders" packet.
+   *
+   * @param {object} response the message received from the server.
+   */
+  onResponseHeaders(response) {
+    this.updateRequest(response.from, {
+      responseHeaders: response
+    }).then(() => {
+      window.emit(EVENTS.RECEIVED_RESPONSE_HEADERS, response.from);
+    });
+  }
+
+  /**
+   * Handles additional information received for a "responseCookies" packet.
+   *
+   * @param {object} response the message received from the server.
+   */
+  onResponseCookies(response) {
+    this.updateRequest(response.from, {
+      responseCookies: response
+    }).then(() => {
+      window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from);
+    });
+  }
+
+  /**
+   * Handles additional information received for a "responseContent" packet.
+   *
+   * @param {object} data the message received from the server event.
+   * @param {object} response the message received from the server.
+   */
+  onResponseContent(data, response) {
+    let payload = Object.assign({ responseContent: response }, data);
+    this.updateRequest(response.from, payload).then(() => {
+      window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, response.from);
+    });
+  }
+
+  /**
+   * Handles additional information received for a "eventTimings" packet.
+   *
+   * @param {object} response the message received from the server.
+   */
+  onEventTimings(response) {
+    this.updateRequest(response.from, {
+      eventTimings: response
+    }).then(() => {
+      window.emit(EVENTS.RECEIVED_EVENT_TIMINGS, response.from);
+    });
+  }
+}
+
+module.exports = new FirefoxConnector();
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/src/connector/index.js
@@ -0,0 +1,85 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+let connector = {};
+
+function onConnect(connection, actions, getState) {
+  if (!connection || !connection.tab) {
+    return;
+  }
+
+  let { clientType } = connection.tab;
+  switch (clientType) {
+    case "chrome":
+      onChromeConnect(connection, actions, getState);
+      break;
+    case "firefox":
+      onFirefoxConnect(connection, actions, getState);
+      break;
+    default:
+      throw Error(`Unknown client type - ${clientType}`);
+  }
+}
+
+function onDisconnect() {
+  connector && connector.disconnect();
+}
+
+function onChromeConnect(connection, actions, getState) {
+  // TODO: support chrome debugging protocol
+}
+
+function onFirefoxConnect(connection, actions, getState) {
+  connector = require("./firefox-connector");
+  connector.connect(connection, actions, getState);
+}
+
+function inspectRequest() {
+  return connector.inspectRequest(...arguments);
+}
+
+function getLongString() {
+  return connector.getLongString(...arguments);
+}
+
+function getNetworkRequest() {
+  return connector.getNetworkRequest(...arguments);
+}
+
+function getTabTarget() {
+  return connector.getTabTarget();
+}
+
+function sendHTTPRequest() {
+  return connector.sendHTTPRequest(...arguments);
+}
+
+function setPreferences() {
+  return connector.setPreferences(...arguments);
+}
+
+function triggerActivity() {
+  return connector.triggerActivity(...arguments);
+}
+
+function viewSourceInDebugger() {
+  return connector.viewSourceInDebugger();
+}
+
+module.exports = {
+  onConnect,
+  onChromeConnect,
+  onFirefoxConnect,
+  onDisconnect,
+  getLongString,
+  getNetworkRequest,
+  getTabTarget,
+  inspectRequest,
+  sendHTTPRequest,
+  setPreferences,
+  triggerActivity,
+  viewSourceInDebugger,
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/src/connector/moz.build
@@ -0,0 +1,8 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+    'firefox-connector.js',
+    'index.js',
+)
--- a/devtools/client/netmonitor/src/constants.js
+++ b/devtools/client/netmonitor/src/constants.js
@@ -42,20 +42,16 @@ const ACTIVITY_TYPE = {
 
   // Enabling or disabling the cache without triggering a reload.
   ENABLE_CACHE: 3,
   DISABLE_CACHE: 4
 };
 
 // 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",
@@ -88,18 +84,17 @@ const EVENTS = {
   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",
 
-  // Fired once the NetMonitorController establishes a connection to the debug
-  // target.
+  // Fired once the connection is established
   CONNECTED: "connected",
 };
 
 const HEADERS = [
   {
     name: "status",
     label: "status3",
     canFilter: true,
--- a/devtools/client/netmonitor/src/har/har-builder.js
+++ b/devtools/client/netmonitor/src/har/har-builder.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
 const appInfo = Services.appinfo;
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const { CurlUtils } = require("devtools/client/shared/curl");
-const { getLongString } = require("../utils/client");
+const { getLongString } = require("../connector/index");
 const {
   getFormDataSections,
   getUrlQuery,
   parseQueryString,
 } = require("../utils/request-utils");
 
 const L10N = new LocalizationHelper("devtools/client/locales/har.properties");
 const HAR_VERSION = "1.1";
--- a/devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js
+++ b/devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js
@@ -10,22 +10,22 @@ add_task(function* () {
   // Disable tcp fast open, because it is setting a response header indicator
   // (bug 1352274). TCP Fast Open is not present on all platforms therefore the
   // number of response headers will vary depending on the platform.
   Services.prefs.setBoolPref("network.tcp.tcp_fastopen_enable", false);
   let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
 
   info("Starting test... ");
 
-  let { gStore, windowRequire } = monitor.panelWin;
+  let { store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let RequestListContextMenu = windowRequire(
     "devtools/client/netmonitor/src/request-list-context-menu");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
   tab.linkedBrowser.reload();
   yield wait;
 
   let contextMenu = new RequestListContextMenu({});
   yield contextMenu.copyAllAsHar();
 
--- a/devtools/client/netmonitor/src/har/test/browser_net_har_post_data.js
+++ b/devtools/client/netmonitor/src/har/test/browser_net_har_post_data.js
@@ -7,22 +7,22 @@
  * Tests for exporting POST data into HAR format.
  */
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(
     HAR_EXAMPLE_URL + "html_har_post-data-test-page.html");
 
   info("Starting test... ");
 
-  let { gStore, windowRequire } = monitor.panelWin;
+  let { store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let RequestListContextMenu = windowRequire(
     "devtools/client/netmonitor/src/request-list-context-menu");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   // Execute one POST request on the page and wait till its done.
   let wait = waitForNetworkEvents(monitor, 0, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.executeTest();
   });
   yield wait;
 
--- a/devtools/client/netmonitor/src/har/test/browser_net_har_throttle_upload.js
+++ b/devtools/client/netmonitor/src/har/test/browser_net_har_throttle_upload.js
@@ -11,43 +11,42 @@ add_task(function* () {
 });
 
 function* throttleUploadTest(actuallyThrottle) {
   let { tab, monitor } = yield initNetMonitor(
     HAR_EXAMPLE_URL + "html_har_post-data-test-page.html");
 
   info("Starting test... (actuallyThrottle = " + actuallyThrottle + ")");
 
-  let { gStore, windowRequire } = monitor.panelWin;
+  let { store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
-  let { NetMonitorController } =
-    windowRequire("devtools/client/netmonitor/src/netmonitor-controller");
+  let { setPreferences } =
+    windowRequire("devtools/client/netmonitor/src/connector/index");
   let RequestListContextMenu = windowRequire(
     "devtools/client/netmonitor/src/request-list-context-menu");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   const size = 4096;
   const uploadSize = actuallyThrottle ? size / 3 : 0;
 
   const request = {
     "NetworkMonitor.throttleData": {
       latencyMean: 0,
       latencyMax: 0,
       downloadBPSMean: 200000,
       downloadBPSMax: 200000,
       uploadBPSMean: uploadSize,
       uploadBPSMax: uploadSize,
     },
   };
-  let client = NetMonitorController.webConsoleClient;
 
   info("sending throttle request");
   yield new Promise((resolve) => {
-    client.setPreferences(request, response => {
+    setPreferences(request, (response) => {
       resolve(response);
     });
   });
 
   // Execute one POST request on the page and wait till its done.
   let wait = waitForNetworkEvents(monitor, 0, 1);
   yield ContentTask.spawn(tab.linkedBrowser, { size }, function* (args) {
     content.wrappedJSObject.executeTest2(args.size);
--- a/devtools/client/netmonitor/src/moz.build
+++ b/devtools/client/netmonitor/src/moz.build
@@ -1,22 +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/.
 
 DIRS += [
     'actions',
     'components',
+    'connector',
     'har',
     'middleware',
     'reducers',
     'selectors',
     'utils',
 ]
 
 DevToolsModules(
     'constants.js',
-    'netmonitor-controller.js',
     'request-list-context-menu.js',
     'request-list-header-context-menu.js',
     'request-list-tooltip.js',
     'waterfall-background.js',
 )
deleted file mode 100644
--- a/devtools/client/netmonitor/src/netmonitor-controller.js
+++ /dev/null
@@ -1,781 +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";
-
-const { TimelineFront } = require("devtools/shared/fronts/timeline");
-const { CurlUtils } = require("devtools/client/shared/curl");
-const { ACTIVITY_TYPE, EVENTS } = require("./constants");
-const { getDisplayedRequestById } = require("./selectors/index");
-const {
-  fetchHeaders,
-  formDataURI,
-} = require("./utils/request-utils");
-const {
-  getLongString,
-  getWebConsoleClient,
-  onFirefoxConnect,
-  onFirefoxDisconnect,
-} = require("./utils/client");
-
-/**
- * Object defining the network monitor controller components.
- */
-var NetMonitorController = {
-  /**
-   * Initializes the view and connects the monitor client.
-   *
-   * @param {Object} connection connection data wrapper
-   * @return {Object} A promise that is resolved when the monitor finishes startup.
-   */
-  startupNetMonitor(connection, actions) {
-    if (this._startup) {
-      return this._startup;
-    }
-    this.actions = actions;
-    this._startup = new Promise(async (resolve) => {
-      await this.connect(connection);
-      resolve();
-    });
-    return this._startup;
-  },
-
-  /**
-   * Destroys the view and disconnects the monitor client from the server.
-   *
-   * @return object
-   *         A promise that is resolved when the monitor finishes shutdown.
-   */
-  shutdownNetMonitor() {
-    if (this._shutdown) {
-      return this._shutdown;
-    }
-    this._shutdown = new Promise(async (resolve) => {
-      this.actions.batchReset();
-      onFirefoxDisconnect(this._target);
-      this._target.off("close", this._onTabDetached);
-      this.NetworkEventsHandler.disconnect();
-      await this.disconnect();
-      resolve();
-    });
-
-    return this._shutdown;
-  },
-
-  /**
-   * Initiates remote or chrome network monitoring based on the current target,
-   * wiring event handlers as necessary. Since the TabTarget will have already
-   * started listening to network requests by now, this is largely
-   * netmonitor-specific initialization.
-   *
-   * @param {Object} connection connection data wrapper
-   * @return {Object} A promise that is resolved when the monitor finishes connecting.
-   */
-  connect(connection) {
-    if (this._connection) {
-      return this._connection;
-    }
-    this._onTabDetached = this.shutdownNetMonitor.bind(this);
-
-    this._connection = new Promise(async (resolve) => {
-      // Some actors like AddonActor or RootActor for chrome debugging
-      // aren't actual tabs.
-      this.toolbox = connection.toolbox;
-      this._target = connection.tabConnection.tabTarget;
-      this.tabClient = this._target.isTabActor ? this._target.activeTab : null;
-
-      let connectTimeline = () => {
-        // Don't start up waiting for timeline markers if the server isn't
-        // recent enough to emit the markers we're interested in.
-        if (this._target.getTrait("documentLoadingMarkers")) {
-          this.timelineFront = new TimelineFront(this._target.client,
-            this._target.form);
-          return this.timelineFront.start({ withDocLoadingEvents: true });
-        }
-        return undefined;
-      };
-      await connectTimeline();
-
-      onFirefoxConnect(this._target);
-      this._target.on("close", this._onTabDetached);
-
-      this.webConsoleClient = getWebConsoleClient();
-      this.NetworkEventsHandler = new NetworkEventsHandler();
-      this.NetworkEventsHandler.connect(this.actions);
-
-      window.emit(EVENTS.CONNECTED);
-
-      resolve();
-      this._connected = true;
-    });
-    return this._connection;
-  },
-
-  /**
-   * Disconnects the debugger client and removes event handlers as necessary.
-   */
-  disconnect() {
-    if (this._disconnection) {
-      return this._disconnection;
-    }
-    this._disconnection = new Promise(async (resolve) => {
-      // Wait for the connection to finish first.
-      if (!this._connected) {
-        await this._connection;
-      }
-
-      // When debugging local or a remote instance, the connection is closed by
-      // the RemoteTarget. The webconsole actor is stopped on disconnect.
-      this.tabClient = null;
-      this.webConsoleClient = null;
-
-      // The timeline front wasn't initialized and started if the server wasn't
-      // recent enough to emit the markers we were interested in.
-      if (this._target.getTrait("documentLoadingMarkers")) {
-        await this.timelineFront.destroy();
-        this.timelineFront = null;
-      }
-
-      resolve();
-      this._connected = false;
-    });
-    return this._disconnection;
-  },
-
-  /**
-   * Triggers a specific "activity" to be performed by the frontend.
-   * This can be, for example, triggering reloads or enabling/disabling cache.
-   *
-   * @param number type
-   *        The activity type. See the ACTIVITY_TYPE const.
-   * @return object
-   *         A promise resolved once the activity finishes and the frontend
-   *         is back into "standby" mode.
-   */
-  triggerActivity: function (type) {
-    // Puts the frontend into "standby" (when there's no particular activity).
-    let standBy = () => {
-      this._currentActivity = ACTIVITY_TYPE.NONE;
-    };
-
-    // Waits for a series of "navigation start" and "navigation stop" events.
-    let waitForNavigation = () => {
-      return new Promise((resolve) => {
-        this._target.once("will-navigate", () => {
-          this._target.once("navigate", () => {
-            resolve();
-          });
-        });
-      });
-    };
-
-    // Reconfigures the tab, optionally triggering a reload.
-    let reconfigureTab = options => {
-      return new Promise((resolve) => {
-        this._target.activeTab.reconfigure(options, resolve);
-      });
-    };
-
-    // Reconfigures the tab and waits for the target to finish navigating.
-    let reconfigureTabAndWaitForNavigation = options => {
-      options.performReload = true;
-      let navigationFinished = waitForNavigation();
-      return reconfigureTab(options).then(() => navigationFinished);
-    };
-    if (type == ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT) {
-      return reconfigureTabAndWaitForNavigation({}).then(standBy);
-    }
-    if (type == ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED) {
-      this._currentActivity = ACTIVITY_TYPE.ENABLE_CACHE;
-      this._target.once("will-navigate", () => {
-        this._currentActivity = type;
-      });
-      return reconfigureTabAndWaitForNavigation({
-        cacheDisabled: false,
-        performReload: true
-      }).then(standBy);
-    }
-    if (type == ACTIVITY_TYPE.RELOAD.WITH_CACHE_DISABLED) {
-      this._currentActivity = ACTIVITY_TYPE.DISABLE_CACHE;
-      this._target.once("will-navigate", () => {
-        this._currentActivity = type;
-      });
-      return reconfigureTabAndWaitForNavigation({
-        cacheDisabled: true,
-        performReload: true
-      }).then(standBy);
-    }
-    if (type == ACTIVITY_TYPE.ENABLE_CACHE) {
-      this._currentActivity = type;
-      return reconfigureTab({
-        cacheDisabled: false,
-        performReload: false
-      }).then(standBy);
-    }
-    if (type == ACTIVITY_TYPE.DISABLE_CACHE) {
-      this._currentActivity = type;
-      return reconfigureTab({
-        cacheDisabled: true,
-        performReload: false
-      }).then(standBy);
-    }
-    this._currentActivity = ACTIVITY_TYPE.NONE;
-    return Promise.reject(new Error("Invalid activity type"));
-  },
-
-  /**
-   * Selects the specified request in the waterfall and opens the details view.
-   *
-   * @param string requestId
-   *        The actor ID of the request to inspect.
-   * @return object
-   *         A promise resolved once the task finishes.
-   */
-  inspectRequest(requestId) {
-    // Look for the request in the existing ones or wait for it to appear, if
-    // the network monitor is still loading.
-    return new Promise((resolve) => {
-      let request = null;
-      let inspector = () => {
-        request = getDisplayedRequestById(window.gStore.getState(), requestId);
-        if (!request) {
-          // Reset filters so that the request is visible.
-          this.actions.toggleRequestFilterType("all");
-          request = getDisplayedRequestById(window.gStore.getState(), requestId);
-        }
-
-        // If the request was found, select it. Otherwise this function will be
-        // called again once new requests arrive.
-        if (request) {
-          window.off(EVENTS.REQUEST_ADDED, inspector);
-          this.actions.selectRequest(request.id);
-          resolve();
-        }
-      };
-
-      inspector();
-      if (!request) {
-        window.on(EVENTS.REQUEST_ADDED, inspector);
-      }
-    });
-  },
-
-  /**
-   * Getter that tells if the server supports sending custom network requests.
-   * @type boolean
-   */
-  get supportsCustomRequest() {
-    return this.webConsoleClient &&
-       (this.webConsoleClient.traits.customNetworkRequest ||
-        !this._target.isApp);
-  },
-
-  /**
-   * Getter that tells if the server can do network performance statistics.
-   * @type boolean
-   */
-  get supportsPerfStats() {
-    return this.tabClient &&
-      (this.tabClient.traits.reconfigure || !this._target.isApp);
-  },
-
-  /**
-   * Open a given source in Debugger
-   */
-  viewSourceInDebugger(sourceURL, sourceLine) {
-    if (this.toolbox) {
-      this.toolbox.viewSourceInDebugger(sourceURL, sourceLine);
-    }
-  },
-};
-
-/**
- * Functions handling target network events.
- */
-function NetworkEventsHandler() {
-  this.payloadQueue = [];
-  this.addRequest = this.addRequest.bind(this);
-  this.updateRequest = this.updateRequest.bind(this);
-  this._onNetworkEvent = this._onNetworkEvent.bind(this);
-  this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
-  this._onDocLoadingMarker = this._onDocLoadingMarker.bind(this);
-  this._onRequestHeaders = this._onRequestHeaders.bind(this);
-  this._onRequestCookies = this._onRequestCookies.bind(this);
-  this._onRequestPostData = this._onRequestPostData.bind(this);
-  this._onResponseHeaders = this._onResponseHeaders.bind(this);
-  this._onResponseCookies = this._onResponseCookies.bind(this);
-  this._onSecurityInfo = this._onSecurityInfo.bind(this);
-  this._onEventTimings = this._onEventTimings.bind(this);
-}
-
-NetworkEventsHandler.prototype = {
-  get client() {
-    return NetMonitorController._target.client;
-  },
-
-  get webConsoleClient() {
-    return NetMonitorController.webConsoleClient;
-  },
-
-  get timelineFront() {
-    return NetMonitorController.timelineFront;
-  },
-
-  /**
-   * Connect to the current target client.
-   */
-  connect(actions) {
-    this.actions = actions;
-    this.webConsoleClient.on("networkEvent", this._onNetworkEvent);
-    this.webConsoleClient.on("networkEventUpdate", this._onNetworkEventUpdate);
-
-    if (this.timelineFront) {
-      this.timelineFront.on("doc-loading", this._onDocLoadingMarker);
-    }
-
-    this._displayCachedEvents();
-  },
-
-  /**
-   * Disconnect from the client.
-   */
-  disconnect() {
-    if (!this.client) {
-      return;
-    }
-    this.webConsoleClient.off("networkEvent", this._onNetworkEvent);
-    this.webConsoleClient.off("networkEventUpdate", this._onNetworkEventUpdate);
-
-    if (this.timelineFront) {
-      this.timelineFront.off("doc-loading", this._onDocLoadingMarker);
-    }
-  },
-
-  /**
-   * Display any network events already in the cache.
-   */
-  _displayCachedEvents: function () {
-    for (let cachedEvent of this.webConsoleClient.getNetworkEvents()) {
-      // First add the request to the timeline.
-      this._onNetworkEvent("networkEvent", cachedEvent);
-      // Then replay any updates already received.
-      for (let update of cachedEvent.updates) {
-        this._onNetworkEventUpdate("networkEventUpdate", {
-          packet: {
-            updateType: update
-          },
-          networkInfo: cachedEvent
-        });
-      }
-    }
-  },
-
-  /**
-   * The "DOMContentLoaded" and "Load" events sent by the timeline actor.
-   * @param object marker
-   */
-  _onDocLoadingMarker: function (marker) {
-    this.actions.addTimingMarker(marker);
-    window.emit(EVENTS.TIMELINE_EVENT, marker);
-  },
-
-  /**
-   * The "networkEvent" message type handler.
-   *
-   * @param string type
-   *        Message type.
-   * @param object networkInfo
-   *        The network request information.
-   */
-  _onNetworkEvent: function (type, networkInfo) {
-    let { actor,
-      startedDateTime,
-      request: { method, url },
-      isXHR,
-      cause,
-      fromCache,
-      fromServiceWorker
-    } = networkInfo;
-
-    this.addRequest(
-      actor, {startedDateTime, method, url, isXHR, cause, fromCache, fromServiceWorker}
-    );
-    window.emit(EVENTS.NETWORK_EVENT, actor);
-  },
-
-  addRequest(id, data) {
-    let { method, url, isXHR, cause, startedDateTime, fromCache,
-          fromServiceWorker } = data;
-
-    this.actions.addRequest(
-      id,
-      {
-        // Convert the received date/time string to a unix timestamp.
-        startedMillis: Date.parse(startedDateTime),
-        method,
-        url,
-        isXHR,
-        cause,
-        fromCache,
-        fromServiceWorker,
-      },
-      true
-    )
-    .then(() => window.emit(EVENTS.REQUEST_ADDED, id));
-  },
-
-  async fetchImage(mimeType, responseContent) {
-    let payload = {};
-    if (mimeType && responseContent && responseContent.content) {
-      let { encoding, text } = responseContent.content;
-      let response = await getLongString(text);
-
-      if (mimeType.includes("image/")) {
-        payload.responseContentDataUri = formDataURI(mimeType, encoding, response);
-      }
-
-      responseContent.content.text = response;
-      payload.responseContent = responseContent;
-    }
-    return payload;
-  },
-
-  async fetchRequestHeaders(requestHeaders) {
-    let payload = {};
-    if (requestHeaders && requestHeaders.headers && requestHeaders.headers.length) {
-      let headers = await fetchHeaders(requestHeaders, getLongString);
-      if (headers) {
-        payload.requestHeaders = headers;
-      }
-    }
-    return payload;
-  },
-
-  async fetchResponseHeaders(responseHeaders) {
-    let payload = {};
-    if (responseHeaders && responseHeaders.headers && responseHeaders.headers.length) {
-      let headers = await fetchHeaders(responseHeaders, getLongString);
-      if (headers) {
-        payload.responseHeaders = headers;
-      }
-    }
-    return payload;
-  },
-
-  // Search the POST data upload stream for request headers and add
-  // them as a separate property, different from the classic headers.
-  async fetchPostData(requestPostData) {
-    let payload = {};
-    if (requestPostData && requestPostData.postData) {
-      let { text } = requestPostData.postData;
-      let postData = await getLongString(text);
-      const headers = CurlUtils.getHeadersFromMultipartText(postData);
-      const headersSize = headers.reduce((acc, { name, value }) => {
-        return acc + name.length + value.length + 2;
-      }, 0);
-      requestPostData.postData.text = postData;
-      payload.requestPostData = Object.assign({}, requestPostData);
-      payload.requestHeadersFromUploadStream = { headers, headersSize };
-    }
-    return payload;
-  },
-
-  async fetchResponseCookies(responseCookies) {
-    let payload = {};
-    if (responseCookies) {
-      let resCookies = [];
-      // response store cookies in responseCookies or responseCookies.cookies
-      let cookies = responseCookies.cookies ?
-        responseCookies.cookies : responseCookies;
-      // make sure cookies is iterable
-      if (typeof cookies[Symbol.iterator] === "function") {
-        for (let cookie of cookies) {
-          resCookies.push(Object.assign({}, cookie, {
-            value: await getLongString(cookie.value),
-          }));
-        }
-        if (resCookies.length) {
-          payload.responseCookies = resCookies;
-        }
-      }
-    }
-    return payload;
-  },
-
-  // Fetch request and response cookies long value.
-  // Actor does not provide full sized cookie value when the value is too long
-  // To display values correctly, we need fetch them in each request.
-  async fetchRequestCookies(requestCookies) {
-    let payload = {};
-    if (requestCookies) {
-      let reqCookies = [];
-      // request store cookies in requestCookies or requestCookies.cookies
-      let cookies = requestCookies.cookies ?
-        requestCookies.cookies : requestCookies;
-      // make sure cookies is iterable
-      if (typeof cookies[Symbol.iterator] === "function") {
-        for (let cookie of cookies) {
-          reqCookies.push(Object.assign({}, cookie, {
-            value: await getLongString(cookie.value),
-          }));
-        }
-        if (reqCookies.length) {
-          payload.requestCookies = reqCookies;
-        }
-      }
-    }
-    return payload;
-  },
-
-  getPayloadFromQueue(id) {
-    return this.payloadQueue.find((item) => item.id === id);
-  },
-
-  // Packet order of "networkUpdateEvent" is predictable, as a result we can wait for
-  // the last one "eventTimings" packet arrives to check payload is ready
-  isQueuePayloadReady(id) {
-    let queuedPayload = this.getPayloadFromQueue(id);
-    return queuedPayload && queuedPayload.payload.eventTimings;
-  },
-
-  pushPayloadToQueue(id, payload) {
-    let queuedPayload = this.getPayloadFromQueue(id);
-    if (!queuedPayload) {
-      this.payloadQueue.push({ id, payload });
-    } else {
-      // Merge upcoming networkEventUpdate payload into existing one
-      queuedPayload.payload = Object.assign({}, queuedPayload.payload, payload);
-    }
-  },
-
-  async updateRequest(id, data) {
-    let {
-      mimeType,
-      responseContent,
-      responseCookies,
-      responseHeaders,
-      requestCookies,
-      requestHeaders,
-      requestPostData,
-    } = data;
-
-    // fetch request detail contents in parallel
-    let [
-      imageObj,
-      requestHeadersObj,
-      responseHeadersObj,
-      postDataObj,
-      requestCookiesObj,
-      responseCookiesObj,
-    ] = await Promise.all([
-      this.fetchImage(mimeType, responseContent),
-      this.fetchRequestHeaders(requestHeaders),
-      this.fetchResponseHeaders(responseHeaders),
-      this.fetchPostData(requestPostData),
-      this.fetchRequestCookies(requestCookies),
-      this.fetchResponseCookies(responseCookies),
-    ]);
-
-    let payload = Object.assign({}, data,
-                                    imageObj, requestHeadersObj, responseHeadersObj,
-                                    postDataObj, requestCookiesObj, responseCookiesObj);
-
-    this.pushPayloadToQueue(id, payload);
-
-    if (this.isQueuePayloadReady(id)) {
-      await this.actions.updateRequest(id, this.getPayloadFromQueue(id).payload, true);
-    }
-  },
-
-  /**
-   * The "networkEventUpdate" message type handler.
-   *
-   * @param string type
-   *        Message type.
-   * @param object packet
-   *        The message received from the server.
-   * @param object networkInfo
-   *        The network request information.
-   */
-  _onNetworkEventUpdate: function (type, { packet, networkInfo }) {
-    let { actor } = networkInfo;
-    switch (packet.updateType) {
-      case "requestHeaders":
-        this.webConsoleClient.getRequestHeaders(actor, this._onRequestHeaders);
-        window.emit(EVENTS.UPDATING_REQUEST_HEADERS, actor);
-        break;
-      case "requestCookies":
-        this.webConsoleClient.getRequestCookies(actor, this._onRequestCookies);
-        window.emit(EVENTS.UPDATING_REQUEST_COOKIES, actor);
-        break;
-      case "requestPostData":
-        this.webConsoleClient.getRequestPostData(actor,
-          this._onRequestPostData);
-        window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, actor);
-        break;
-      case "securityInfo":
-        this.updateRequest(actor, {
-          securityState: networkInfo.securityInfo,
-        }).then(() => {
-          this.webConsoleClient.getSecurityInfo(actor, this._onSecurityInfo);
-          window.emit(EVENTS.UPDATING_SECURITY_INFO, actor);
-        });
-        break;
-      case "responseHeaders":
-        this.webConsoleClient.getResponseHeaders(actor,
-          this._onResponseHeaders);
-        window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, actor);
-        break;
-      case "responseCookies":
-        this.webConsoleClient.getResponseCookies(actor,
-          this._onResponseCookies);
-        window.emit(EVENTS.UPDATING_RESPONSE_COOKIES, actor);
-        break;
-      case "responseStart":
-        this.updateRequest(actor, {
-          httpVersion: networkInfo.response.httpVersion,
-          remoteAddress: networkInfo.response.remoteAddress,
-          remotePort: networkInfo.response.remotePort,
-          status: networkInfo.response.status,
-          statusText: networkInfo.response.statusText,
-          headersSize: networkInfo.response.headersSize
-        }).then(() => {
-          window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
-        });
-        break;
-      case "responseContent":
-        this.webConsoleClient.getResponseContent(actor,
-          this._onResponseContent.bind(this, {
-            contentSize: networkInfo.response.bodySize,
-            transferredSize: networkInfo.response.transferredSize,
-            mimeType: networkInfo.response.content.mimeType
-          }));
-        window.emit(EVENTS.UPDATING_RESPONSE_CONTENT, actor);
-        break;
-      case "eventTimings":
-        this.updateRequest(actor, {
-          totalTime: networkInfo.totalTime
-        }).then(() => {
-          this.webConsoleClient.getEventTimings(actor, this._onEventTimings);
-          window.emit(EVENTS.UPDATING_EVENT_TIMINGS, actor);
-        });
-        break;
-    }
-  },
-
-  /**
-   * Handles additional information received for a "requestHeaders" packet.
-   *
-   * @param object response
-   *        The message received from the server.
-   */
-  _onRequestHeaders: function (response) {
-    this.updateRequest(response.from, {
-      requestHeaders: response
-    }).then(() => {
-      window.emit(EVENTS.RECEIVED_REQUEST_HEADERS, response.from);
-    });
-  },
-
-  /**
-   * Handles additional information received for a "requestCookies" packet.
-   *
-   * @param object response
-   *        The message received from the server.
-   */
-  _onRequestCookies: function (response) {
-    this.updateRequest(response.from, {
-      requestCookies: response
-    }).then(() => {
-      window.emit(EVENTS.RECEIVED_REQUEST_COOKIES, response.from);
-    });
-  },
-
-  /**
-   * Handles additional information received for a "requestPostData" packet.
-   *
-   * @param object response
-   *        The message received from the server.
-   */
-  _onRequestPostData: function (response) {
-    this.updateRequest(response.from, {
-      requestPostData: response
-    }).then(() => {
-      window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA, response.from);
-    });
-  },
-
-  /**
-   * Handles additional information received for a "securityInfo" packet.
-   *
-   * @param object response
-   *        The message received from the server.
-   */
-  _onSecurityInfo: function (response) {
-    this.updateRequest(response.from, {
-      securityInfo: response.securityInfo
-    }).then(() => {
-      window.emit(EVENTS.RECEIVED_SECURITY_INFO, response.from);
-    });
-  },
-
-  /**
-   * Handles additional information received for a "responseHeaders" packet.
-   *
-   * @param object response
-   *        The message received from the server.
-   */
-  _onResponseHeaders: function (response) {
-    this.updateRequest(response.from, {
-      responseHeaders: response
-    }).then(() => {
-      window.emit(EVENTS.RECEIVED_RESPONSE_HEADERS, response.from);
-    });
-  },
-
-  /**
-   * Handles additional information received for a "responseCookies" packet.
-   *
-   * @param object response
-   *        The message received from the server.
-   */
-  _onResponseCookies: function (response) {
-    this.updateRequest(response.from, {
-      responseCookies: response
-    }).then(() => {
-      window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from);
-    });
-  },
-
-  /**
-   * Handles additional information received for a "responseContent" packet.
-   *
-   * @param object data
-   *        The message received from the server event.
-   * @param object response
-   *        The message received from the server.
-   */
-  _onResponseContent: function (data, response) {
-    let payload = Object.assign({ responseContent: response }, data);
-    this.updateRequest(response.from, payload).then(() => {
-      window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, response.from);
-    });
-  },
-
-  /**
-   * Handles additional information received for a "eventTimings" packet.
-   *
-   * @param object response
-   *        The message received from the server.
-   */
-  _onEventTimings: function (response) {
-    this.updateRequest(response.from, {
-      eventTimings: response
-    }).then(() => {
-      window.emit(EVENTS.RECEIVED_EVENT_TIMINGS, response.from);
-    });
-  }
-};
-
-exports.NetMonitorController = NetMonitorController;
--- a/devtools/client/netmonitor/src/request-list-context-menu.js
+++ b/devtools/client/netmonitor/src/request-list-context-menu.js
@@ -5,22 +5,24 @@
 "use strict";
 
 const Services = require("Services");
 const { Curl } = require("devtools/client/shared/curl");
 const { gDevTools } = require("devtools/client/framework/devtools");
 const { saveAs } = require("devtools/client/shared/file-saver");
 const { copyString } = require("devtools/shared/platform/clipboard");
 const { HarExporter } = require("./har/har-exporter");
-const { NetMonitorController } = require("./netmonitor-controller");
+const {
+  getLongString,
+  getTabTarget,
+} = require("./connector/index");
 const {
   getSelectedRequest,
   getSortedRequests,
 } = require("./selectors/index");
-const { getLongString } = require("./utils/client");
 const { L10N } = require("./utils/l10n");
 const { showMenu } = require("./utils/menu");
 const {
   getUrlQuery,
   parseQueryString,
 } = require("./utils/request-utils");
 
 function RequestListContextMenu({
@@ -28,21 +30,25 @@ function RequestListContextMenu({
   openStatistics,
 }) {
   this.cloneSelectedRequest = cloneSelectedRequest;
   this.openStatistics = openStatistics;
 }
 
 RequestListContextMenu.prototype = {
   get selectedRequest() {
-    return getSelectedRequest(window.gStore.getState());
+    // FIXME: Bug 1336382 - Implement RequestListContextMenu React component
+    // Remove window.store
+    return getSelectedRequest(window.store.getState());
   },
 
   get sortedRequests() {
-    return getSortedRequests(window.gStore.getState());
+    // FIXME: Bug 1336382 - Implement RequestListContextMenu React component
+    // Remove window.store
+    return getSortedRequests(window.store.getState());
   },
 
   /**
    * Handle the context menu opening. Hide items if no request is selected.
    * Since visible attribute only accept boolean value but the method call may
    * return undefined, we use !! to force convert any object to boolean
    */
   open(event = {}) {
@@ -159,26 +165,24 @@ RequestListContextMenu.prototype = {
       visible: !!(selectedRequest &&
                selectedRequest.responseContent &&
                selectedRequest.responseContent.content.mimeType.includes("image/")),
       click: () => this.saveImageAs(),
     });
 
     menu.push({
       type: "separator",
-      visible: !!(NetMonitorController.supportsCustomRequest &&
-               selectedRequest && !selectedRequest.isCustom),
+      visible: !!(selectedRequest && !selectedRequest.isCustom),
     });
 
     menu.push({
       id: "request-list-context-resend",
       label: L10N.getStr("netmonitor.context.editAndResend"),
       accesskey: L10N.getStr("netmonitor.context.editAndResend.accesskey"),
-      visible: !!(NetMonitorController.supportsCustomRequest &&
-               selectedRequest && !selectedRequest.isCustom),
+      visible: !!(selectedRequest && !selectedRequest.isCustom),
       click: this.cloneSelectedRequest,
     });
 
     menu.push({
       type: "separator",
       visible: !!selectedRequest,
     });
 
@@ -340,17 +344,17 @@ RequestListContextMenu.prototype = {
     // FIXME: This will not work in launchpad
     // document.execCommand(‘cut’/‘copy’) was denied because it was not called from
     // inside a short running user-generated event handler.
     // https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard
     return HarExporter.save(this.getDefaultHarOptions());
   },
 
   getDefaultHarOptions() {
-    let form = NetMonitorController._target.form;
+    let form = getTabTarget().form;
     let title = form.title || form.url;
 
     return {
       getString: getLongString,
       items: this.sortedRequests,
       title: title
     };
   }
--- a/devtools/client/netmonitor/src/request-list-header-context-menu.js
+++ b/devtools/client/netmonitor/src/request-list-header-context-menu.js
@@ -14,17 +14,19 @@ const stringMap = HEADERS
 
 class RequestListHeaderContextMenu {
   constructor({ toggleColumn, resetColumns }) {
     this.toggleColumn = toggleColumn;
     this.resetColumns = resetColumns;
   }
 
   get columns() {
-    return window.gStore.getState().ui.columns;
+    // FIXME: Bug 1362059 - Implement RequestListHeaderContextMenu React component
+    // Remove window.store
+    return window.store.getState().ui.columns;
   }
 
   get visibleColumns() {
     return [...this.columns].filter(([_, shown]) => shown);
   }
 
   /**
    * Handle the context menu opening.
--- a/devtools/client/netmonitor/src/request-list-tooltip.js
+++ b/devtools/client/netmonitor/src/request-list-tooltip.js
@@ -3,17 +3,17 @@
  * 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 { getLongString } = require("./utils/client");
+const { getLongString } = require("./connector/index");
 const { formDataURI } = require("./utils/request-utils");
 
 const REQUESTS_TOOLTIP_IMAGE_MAX_DIM = 400; // px
 
 async function setTooltipImageContent(tooltip, itemEl, requestItem) {
   let { mimeType, text, encoding } = requestItem.responseContent.content;
 
   if (!mimeType || !mimeType.includes("image/")) {
deleted file mode 100644
--- a/devtools/client/netmonitor/src/utils/client.js
+++ /dev/null
@@ -1,101 +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/. */
-
-/* global gStore */
-
-"use strict";
-
-const Services = require("Services");
-const Actions = require("../actions/index");
-const { EVENTS } = require("../constants");
-
-let activeConsole;
-
-/**
- * Called for each location change in the monitored tab.
- *
- * @param {String} type Packet type.
- * @param {Object} packet Packet received from the server.
- */
-function navigated(type) {
-  window.emit(EVENTS.TARGET_DID_NAVIGATE);
-}
-
-/**
- * Called for each location change in the monitored tab.
- *
- * @param {String} type Packet type.
- * @param {Object} packet Packet received from the server.
- */
-function willNavigate(type) {
-  // Reset UI.
-  if (!Services.prefs.getBoolPref("devtools.webconsole.persistlog")) {
-    gStore.dispatch(Actions.batchReset());
-    gStore.dispatch(Actions.clearRequests());
-  } else {
-    // If the log is persistent, just clear all accumulated timing markers.
-    gStore.dispatch(Actions.clearTimingMarkers());
-  }
-
-  window.emit(EVENTS.TARGET_WILL_NAVIGATE);
-}
-
-/**
- * Process connection events.
- *
- * @param {Object} tabTarget
- */
-function onFirefoxConnect(tabTarget) {
-  activeConsole = tabTarget.activeConsole;
-  tabTarget.on("navigate", navigated);
-  tabTarget.on("will-navigate", willNavigate);
-}
-
-/**
- * Process disconnect events.
- *
- * @param {Object} tabTarget
- */
-function onFirefoxDisconnect(tabTarget) {
-  activeConsole = null;
-  tabTarget.off("navigate", navigated);
-  tabTarget.off("will-navigate", willNavigate);
-}
-
-/**
- * Retrieve webconsole object
- *
- * @returns {Object} webConsole
- */
-function getWebConsoleClient() {
-  return activeConsole;
-}
-
-/**
- * Fetches the full text of a LongString.
- *
- * @param object | string stringGrip
- *        The long string grip containing the corresponding actor.
- *        If you pass in a plain string (by accident or because you're lazy),
- *        then a promise of the same string is simply returned.
- * @return object Promise
- *         A promise that is resolved when the full string contents
- *         are available, or rejected if something goes wrong.
- */
-function getLongString(stringGrip) {
-  // FIXME: this.webConsoleClient will be undefined in mochitest,
-  // so we return string instantly to skip undefined error
-  if (typeof stringGrip === "string") {
-    return Promise.resolve(stringGrip);
-  }
-
-  return activeConsole.getString(stringGrip);
-}
-
-module.exports = {
-  getLongString,
-  getWebConsoleClient,
-  onFirefoxConnect,
-  onFirefoxDisconnect,
-};
--- a/devtools/client/netmonitor/src/utils/moz.build
+++ b/devtools/client/netmonitor/src/utils/moz.build
@@ -1,15 +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(
-    'client.js',
     'create-store.js',
     'filter-predicates.js',
     'filter-text-utils.js',
     'format-utils.js',
     'l10n.js',
     'mdn-utils.js',
     'menu.js',
     'prefs.js',
--- a/devtools/client/netmonitor/test/browser.ini
+++ b/devtools/client/netmonitor/test/browser.ini
@@ -116,17 +116,16 @@ skip-if = (os == 'linux' && debug && bit
 [browser_net_json-long.js]
 [browser_net_json-malformed.js]
 [browser_net_json_custom_mime.js]
 [browser_net_json_text_mime.js]
 [browser_net_jsonp.js]
 [browser_net_large-response.js]
 [browser_net_leak_on_tab_close.js]
 [browser_net_open_request_in_tab.js]
-[browser_net_page-nav.js]
 [browser_net_pane-collapse.js]
 [browser_net_pane-toggle.js]
 [browser_net_post-data-01.js]
 [browser_net_post-data-02.js]
 [browser_net_post-data-03.js]
 [browser_net_post-data-04.js]
 [browser_net_prefs-and-l10n.js]
 [browser_net_prefs-reload.js]
@@ -142,17 +141,16 @@ skip-if = (os == 'linux' && debug && bit
 [browser_net_security-icon-click.js]
 [browser_net_security-redirect.js]
 [browser_net_security-state.js]
 [browser_net_security-tab-deselect.js]
 [browser_net_security-tab-visibility.js]
 [browser_net_security-warnings.js]
 [browser_net_send-beacon.js]
 [browser_net_send-beacon-other-tab.js]
-[browser_net_simple-init.js]
 [browser_net_simple-request-data.js]
 [browser_net_simple-request-details.js]
 skip-if = true # Bug 1258809
 [browser_net_simple-request.js]
 [browser_net_sort-01.js]
 [browser_net_sort-02.js]
 [browser_net_statistics-01.js]
 [browser_net_statistics-02.js]
--- a/devtools/client/netmonitor/test/browser_net_accessibility-01.js
+++ b/devtools/client/netmonitor/test/browser_net_accessibility-01.js
@@ -9,20 +9,20 @@
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
   info("Starting test... ");
 
   // It seems that this test may be slow on Ubuntu builds running on ec2.
   requestLongerTimeout(2);
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let count = 0;
   function check(selectedIndex, panelVisibility) {
     info("Performing check " + (count++) + ".");
 
     let requestItems = Array.from(document.querySelectorAll(".request-list-item"));
     is(requestItems.findIndex((item) => item.matches(".selected")), selectedIndex,
       "The selected item in the requests menu was incorrect.");
@@ -33,51 +33,51 @@ add_task(function* () {
   let wait = waitForNetworkEvents(monitor, 2);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests(2);
   });
   yield wait;
 
   check(-1, false);
 
-  gStore.dispatch(Actions.selectDelta(+Infinity));
+  store.dispatch(Actions.selectDelta(+Infinity));
   check(1, true);
-  gStore.dispatch(Actions.selectDelta(-Infinity));
+  store.dispatch(Actions.selectDelta(-Infinity));
   check(0, true);
 
-  gStore.dispatch(Actions.selectDelta(+1));
+  store.dispatch(Actions.selectDelta(+1));
   check(1, true);
-  gStore.dispatch(Actions.selectDelta(-1));
+  store.dispatch(Actions.selectDelta(-1));
   check(0, true);
 
-  gStore.dispatch(Actions.selectDelta(+10));
+  store.dispatch(Actions.selectDelta(+10));
   check(1, true);
-  gStore.dispatch(Actions.selectDelta(-10));
+  store.dispatch(Actions.selectDelta(-10));
   check(0, true);
 
   wait = waitForNetworkEvents(monitor, 18);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests(18);
   });
   yield wait;
 
-  gStore.dispatch(Actions.selectDelta(+Infinity));
+  store.dispatch(Actions.selectDelta(+Infinity));
   check(19, true);
-  gStore.dispatch(Actions.selectDelta(-Infinity));
+  store.dispatch(Actions.selectDelta(-Infinity));
   check(0, true);
 
-  gStore.dispatch(Actions.selectDelta(+1));
+  store.dispatch(Actions.selectDelta(+1));
   check(1, true);
-  gStore.dispatch(Actions.selectDelta(-1));
+  store.dispatch(Actions.selectDelta(-1));
   check(0, true);
 
-  gStore.dispatch(Actions.selectDelta(+10));
+  store.dispatch(Actions.selectDelta(+10));
   check(10, true);
-  gStore.dispatch(Actions.selectDelta(-10));
+  store.dispatch(Actions.selectDelta(-10));
   check(0, true);
 
-  gStore.dispatch(Actions.selectDelta(+100));
+  store.dispatch(Actions.selectDelta(+100));
   check(19, true);
-  gStore.dispatch(Actions.selectDelta(-100));
+  store.dispatch(Actions.selectDelta(-100));
   check(0, true);
 
   return teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_accessibility-02.js
+++ b/devtools/client/netmonitor/test/browser_net_accessibility-02.js
@@ -9,20 +9,20 @@
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
   info("Starting test... ");
 
   // It seems that this test may be slow on Ubuntu builds running on ec2.
   requestLongerTimeout(2);
 
-  let { window, document, gStore, windowRequire } = monitor.panelWin;
+  let { window, document, windowRequire, store } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let count = 0;
   function check(selectedIndex, panelVisibility) {
     info("Performing check " + (count++) + ".");
 
     let requestItems = Array.from(document.querySelectorAll(".request-list-item"));
     is(requestItems.findIndex((item) => item.matches(".selected")), selectedIndex,
       "The selected item in the requests menu was incorrect.");
--- a/devtools/client/netmonitor/test/browser_net_api-calls.js
+++ b/devtools/client/netmonitor/test/browser_net_api-calls.js
@@ -7,24 +7,24 @@
  * Tests whether API call URLs (without a filename) are correctly displayed
  * (including Unicode)
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(API_CALLS_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   const REQUEST_URIS = [
     "http://example.com/api/fileName.xml",
     "http://example.com/api/file%E2%98%A2.xml",
     "http://example.com/api/ascii/get/",
     "http://example.com/api/unicode/%E2%98%A2/",
     "http://example.com/api/search/?q=search%E2%98%A2"
   ];
@@ -33,17 +33,17 @@ add_task(function* () {
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   REQUEST_URIS.forEach(function (uri, index) {
     verifyRequestItemTarget(
       document,
-      getDisplayedRequests(gStore.getState()),
-      getSortedRequests(gStore.getState()).get(index),
+      getDisplayedRequests(store.getState()),
+      getSortedRequests(store.getState()).get(index),
       "GET",
       uri
      );
   });
 
   yield teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_autoscroll.js
+++ b/devtools/client/netmonitor/test/browser_net_autoscroll.js
@@ -5,19 +5,21 @@
 
 /**
  * Bug 863102 - Automatically scroll down upon new network requests.
  */
 add_task(function* () {
   requestLongerTimeout(4);
 
   let { monitor } = yield initNetMonitor(INFINITE_GET_URL, true);
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, windowRequire, store } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
+  store.dispatch(Actions.batchEnable(false));
+
   // Wait until the first request makes the empty notice disappear
   yield waitForRequestListToAppear();
 
   let requestsContainer = document.querySelector(".requests-list-contents");
   ok(requestsContainer, "Container element exists as expected.");
 
   // (1) Check that the scroll position is maintained at the bottom
   // when the requests overflow the vertical size of the container.
@@ -42,17 +44,17 @@ add_task(function* () {
   requestsContainer.scrollTop = requestsContainer.scrollHeight;
   ok(scrolledToBottom(requestsContainer), "Set scroll position to bottom.");
   yield waitForNetworkEvents(monitor, 8);
   yield waitForScroll();
   ok(true, "Still scrolled to bottom.");
 
   // (4) Now select an item in the list and check that additional requests
   // do not change the scroll position.
-  gStore.dispatch(Actions.selectRequestByIndex(0));
+  store.dispatch(Actions.selectRequestByIndex(0));
   yield waitForNetworkEvents(monitor, 8);
   yield waitSomeTime();
   is(requestsContainer.scrollTop, 0, "Did not scroll.");
 
   // Done: clean up.
   return teardown(monitor);
 
   function waitForRequestListToAppear() {
--- a/devtools/client/netmonitor/test/browser_net_brotli.js
+++ b/devtools/client/netmonitor/test/browser_net_brotli.js
@@ -11,35 +11,35 @@ const BROTLI_REQUESTS = 1;
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(BROTLI_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, BROTLI_REQUESTS);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   verifyRequestItemTarget(
     document,
-    getDisplayedRequests(gStore.getState()),
-    getSortedRequests(gStore.getState()).get(0),
+    getDisplayedRequests(store.getState()),
+    getSortedRequests(store.getState()).get(0),
     "GET", HTTPS_CONTENT_TYPE_SJS + "?fmt=br", {
       status: 200,
       statusText: "Connected",
       type: "plain",
       fullMimeType: "text/plain",
       transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 10),
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 64),
       time: true
--- a/devtools/client/netmonitor/test/browser_net_cached-status.js
+++ b/devtools/client/netmonitor/test/browser_net_cached-status.js
@@ -6,24 +6,24 @@
 /**
  * Tests if cached requests have the correct status code
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(STATUS_CODES_URL, true);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   const REQUEST_DATA = [
     {
       method: "GET",
       uri: STATUS_CODES_SJS + "?sts=ok&cached",
       details: {
         status: 200,
         statusText: "OK",
@@ -91,18 +91,18 @@ add_task(function* () {
   info("Performing requests #2...");
   yield performRequestsAndWait();
 
   let index = 0;
   for (let request of REQUEST_DATA) {
     info("Verifying request #" + index);
     yield verifyRequestItemTarget(
       document,
-      getDisplayedRequests(gStore.getState()),
-      getSortedRequests(gStore.getState()).get(index),
+      getDisplayedRequests(store.getState()),
+      getSortedRequests(store.getState()).get(index),
       request.method,
       request.uri,
       request.details
     );
 
     index++;
   }
 
--- a/devtools/client/netmonitor/test/browser_net_cause.js
+++ b/devtools/client/netmonitor/test/browser_net_cause.js
@@ -84,38 +84,39 @@ add_task(function* () {
   // the initNetMonitor function clears the network request list after the
   // page is loaded. That's why we first load a bogus page from SIMPLE_URL,
   // and only then load the real thing from CAUSE_URL - we want to catch
   // all the requests the page is making, not only the XHRs.
   // We can't use about:blank here, because initNetMonitor checks that the
   // page has actually made at least one request.
   let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
+
   let wait = waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length);
   tab.linkedBrowser.loadURI(CAUSE_URL);
   yield wait;
 
-  is(gStore.getState().requests.requests.size, EXPECTED_REQUESTS.length,
+  is(store.getState().requests.requests.size, EXPECTED_REQUESTS.length,
     "All the page events should be recorded.");
 
   EXPECTED_REQUESTS.forEach((spec, i) => {
     let { method, url, causeType, causeUri, stack } = spec;
 
-    let requestItem = getSortedRequests(gStore.getState()).get(i);
+    let requestItem = getSortedRequests(store.getState()).get(i);
     verifyRequestItemTarget(
       document,
-      getDisplayedRequests(gStore.getState()),
+      getDisplayedRequests(store.getState()),
       requestItem,
       method,
       url,
       { cause: { type: causeType, loadingDocumentUri: causeUri } }
     );
 
     let { stacktrace } = requestItem.cause;
     let stackLen = stacktrace ? stacktrace.length : 0;
@@ -143,14 +144,14 @@ add_task(function* () {
     }
   });
 
   // Sort the requests by cause and check the order
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-cause-button"));
   let expectedOrder = EXPECTED_REQUESTS.map(r => r.causeType).sort();
   expectedOrder.forEach((expectedCause, i) => {
-    const cause = getSortedRequests(gStore.getState()).get(i).cause.type;
+    const cause = getSortedRequests(store.getState()).get(i).cause.type;
     is(cause, expectedCause, `The request #${i} has the expected cause after sorting`);
   });
 
   yield teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_cause_redirect.js
+++ b/devtools/client/netmonitor/test/browser_net_cause_redirect.js
@@ -14,30 +14,30 @@ add_task(function* () {
     { status: 302, hasStack: true },
     // Serves HTTPS, sets the Strict-Transport-Security header, no stack
     { status: 200, hasStack: false },
     // Second request to HTTP redirects to HTTPS internally
     { status: 200, hasStack: true },
   ];
 
   let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
-  let { gStore, windowRequire } = monitor.panelWin;
+  let { store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length);
   yield performRequests(2, HSTS_SJS);
   yield wait;
 
   EXPECTED_REQUESTS.forEach(({status, hasStack}, i) => {
-    let item = getSortedRequests(gStore.getState()).get(i);
+    let item = getSortedRequests(store.getState()).get(i);
 
     is(item.status, status, `Request #${i} has the expected status`);
 
     let { stacktrace } = item.cause;
     let stackLen = stacktrace ? stacktrace.length : 0;
 
     if (hasStack) {
       ok(stacktrace, `Request #${i} has a stacktrace`);
--- a/devtools/client/netmonitor/test/browser_net_clear.js
+++ b/devtools/client/netmonitor/test/browser_net_clear.js
@@ -6,23 +6,23 @@
 /**
  * Tests if the clear button empties the request menu.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let { EVENTS } = windowRequire("devtools/client/netmonitor/src/constants");
   let detailsPanelToggleButton = document.querySelector(".network-details-panel-toggle");
   let clearButton = document.querySelector(".requests-list-clear-button");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   // Make sure we start in a sane state
   assertNoRequestState();
 
   // Load one request and assert it shows up in the list
   let networkEvent = monitor.panelWin.once(EVENTS.NETWORK_EVENT);
   tab.linkedBrowser.reload();
   yield networkEvent;
@@ -55,24 +55,24 @@ add_task(function* () {
     "The details pane should not be visible clicking 'clear'.");
 
   return teardown(monitor);
 
   /**
    * Asserts the state of the network monitor when one request has loaded
    */
   function assertSingleRequestState() {
-    is(gStore.getState().requests.requests.size, 1,
+    is(store.getState().requests.requests.size, 1,
       "The request menu should have one item at this point.");
     is(detailsPanelToggleButton.hasAttribute("disabled"), false,
       "The pane toggle button should be enabled after a request is made.");
   }
 
   /**
    * Asserts the state of the network monitor when no requests have loaded
    */
   function assertNoRequestState() {
-    is(gStore.getState().requests.requests.size, 0,
+    is(store.getState().requests.requests.size, 0,
       "The request menu should be empty at this point.");
     is(detailsPanelToggleButton.hasAttribute("disabled"), true,
       "The pane toggle button should be disabled when the request menu is cleared.");
   }
 });
--- a/devtools/client/netmonitor/test/browser_net_columns_last_column.js
+++ b/devtools/client/netmonitor/test/browser_net_columns_last_column.js
@@ -6,20 +6,20 @@
 /**
  * Tests that last visible column can't be hidden
  */
 
 add_task(function* () {
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, gStore, parent } = monitor.panelWin;
+  let { document, store, parent } = monitor.panelWin;
 
-  for (let [column, shown] of gStore.getState().ui.columns) {
-    let visibleColumns = [...gStore.getState().ui.columns]
+  for (let [column, shown] of store.getState().ui.columns) {
+    let visibleColumns = [...store.getState().ui.columns]
       .filter(([_, visible]) => visible);
 
     if (visibleColumns.length === 1) {
       yield testLastMenuItem(column);
       break;
     }
 
     if (shown) {
--- a/devtools/client/netmonitor/test/browser_net_columns_showhide.js
+++ b/devtools/client/netmonitor/test/browser_net_columns_showhide.js
@@ -6,19 +6,19 @@
 /**
  * Test showing/hiding columns.
  */
 
 add_task(function* () {
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, gStore, parent } = monitor.panelWin;
+  let { document, store, parent } = monitor.panelWin;
 
-  for (let [column, shown] of gStore.getState().ui.columns) {
+  for (let [column, shown] of store.getState().ui.columns) {
     if (shown) {
       yield testVisibleColumnContextMenuItem(column, document, parent);
       yield testHiddenColumnContextMenuItem(column, document, parent);
     } else {
       yield testHiddenColumnContextMenuItem(column, document, parent);
       yield testVisibleColumnContextMenuItem(column, document, parent);
     }
   }
--- a/devtools/client/netmonitor/test/browser_net_complex-params.js
+++ b/devtools/client/netmonitor/test/browser_net_complex-params.js
@@ -7,21 +7,21 @@
  * Tests whether complex request params and payload sent via POST are
  * displayed correctly.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(PARAMS_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let { L10N } = windowRequire("devtools/client/netmonitor/src/utils/l10n");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1, 6);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   wait = waitForDOM(document, "#params-panel .tree-section", 2);
--- a/devtools/client/netmonitor/test/browser_net_content-type.js
+++ b/devtools/client/netmonitor/test/browser_net_content-type.js
@@ -6,127 +6,127 @@
 /**
  * Tests if different response content types are handled correctly.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let { L10N } = windowRequire("devtools/client/netmonitor/src/utils/l10n");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   verifyRequestItemTarget(
     document,
-    getDisplayedRequests(gStore.getState()),
-    getSortedRequests(gStore.getState()).get(0),
+    getDisplayedRequests(store.getState()),
+    getSortedRequests(store.getState()).get(0),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=xml",
     {
       status: 200,
       statusText: "OK",
       type: "xml",
       fullMimeType: "text/xml; charset=utf-8",
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 42),
       time: true
     }
   );
   verifyRequestItemTarget(
     document,
-    getDisplayedRequests(gStore.getState()),
-    getSortedRequests(gStore.getState()).get(1),
+    getDisplayedRequests(store.getState()),
+    getSortedRequests(store.getState()).get(1),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=css",
     {
       status: 200,
       statusText: "OK",
       type: "css",
       fullMimeType: "text/css; charset=utf-8",
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 34),
       time: true
     }
   );
   verifyRequestItemTarget(
     document,
-    getDisplayedRequests(gStore.getState()),
-    getSortedRequests(gStore.getState()).get(2),
+    getDisplayedRequests(store.getState()),
+    getSortedRequests(store.getState()).get(2),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=js",
     {
       status: 200,
       statusText: "OK",
       type: "js",
       fullMimeType: "application/javascript; charset=utf-8",
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 34),
       time: true
     }
   );
   verifyRequestItemTarget(
     document,
-    getDisplayedRequests(gStore.getState()),
-    getSortedRequests(gStore.getState()).get(3),
+    getDisplayedRequests(store.getState()),
+    getSortedRequests(store.getState()).get(3),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=json",
     {
       status: 200,
       statusText: "OK",
       type: "json",
       fullMimeType: "application/json; charset=utf-8",
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29),
       time: true
     }
   );
   verifyRequestItemTarget(
     document,
-    getDisplayedRequests(gStore.getState()),
-    getSortedRequests(gStore.getState()).get(4),
+    getDisplayedRequests(store.getState()),
+    getSortedRequests(store.getState()).get(4),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=bogus",
     {
       status: 404,
       statusText: "Not Found",
       type: "html",
       fullMimeType: "text/html; charset=utf-8",
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 24),
       time: true
     }
   );
   verifyRequestItemTarget(
     document,
-    getDisplayedRequests(gStore.getState()),
-    getSortedRequests(gStore.getState()).get(5),
+    getDisplayedRequests(store.getState()),
+    getSortedRequests(store.getState()).get(5),
     "GET",
     TEST_IMAGE,
     {
       fuzzyUrl: true,
       status: 200,
       statusText: "OK",
       type: "png",
       fullMimeType: "image/png",
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 580),
       time: true
     }
   );
   verifyRequestItemTarget(
     document,
-    getDisplayedRequests(gStore.getState()),
-    getSortedRequests(gStore.getState()).get(6),
+    getDisplayedRequests(store.getState()),
+    getSortedRequests(store.getState()).get(6),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=gzip",
     {
       status: 200,
       statusText: "OK",
       type: "plain",
       fullMimeType: "text/plain",
       transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 73),
@@ -275,20 +275,20 @@ add_task(function* () {
       EventUtils.sendMouseEvent({ type: "mousedown" },
         document.querySelectorAll(".request-list-item")[index]);
     }
   }
 
   function* selectIndexAndWaitForJSONView(index) {
     let tabpanel = document.querySelector("#response-panel");
     let waitDOM = waitForDOM(tabpanel, ".treeTable");
-    gStore.dispatch(Actions.selectRequestByIndex(index));
+    store.dispatch(Actions.selectRequestByIndex(index));
     yield waitDOM;
   }
 
   function* selectIndexAndWaitForImageView(index) {
     let tabpanel = document.querySelector("#response-panel");
     let waitDOM = waitForDOM(tabpanel, ".response-image");
-    gStore.dispatch(Actions.selectRequestByIndex(index));
+    store.dispatch(Actions.selectRequestByIndex(index));
     let [imageNode] = yield waitDOM;
     yield once(imageNode, "load");
   }
 });
--- a/devtools/client/netmonitor/test/browser_net_copy_headers.js
+++ b/devtools/client/netmonitor/test/browser_net_copy_headers.js
@@ -6,29 +6,29 @@
 /**
  * Tests if copying a request's request/response headers works.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
   let wait = waitForNetworkEvents(monitor, 1);
   tab.linkedBrowser.reload();
   yield wait;
 
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[0]);
 
-  let requestItem = getSortedRequests(gStore.getState()).get(0);
+  let requestItem = getSortedRequests(store.getState()).get(0);
   let { method, httpVersion, status, statusText } = requestItem;
 
   EventUtils.sendMouseEvent({ type: "contextmenu" },
     document.querySelectorAll(".request-list-item")[0]);
 
   const EXPECTED_REQUEST_HEADERS = [
     `${method} ${SIMPLE_URL} ${httpVersion}`,
     "Host: example.com",
--- a/devtools/client/netmonitor/test/browser_net_copy_params.js
+++ b/devtools/client/netmonitor/test/browser_net_copy_params.js
@@ -6,20 +6,20 @@
 /**
  * Tests whether copying a request item's parameters works.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(PARAMS_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1, 6);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   yield testCopyUrlParamsHidden(0, false);
@@ -92,17 +92,17 @@ add_task(function* () {
       "The \"Copy POST Data\" context menu item should" + (hidden ? " " : " not ") +
         "be hidden.");
   }
 
   function* testCopyPostData(index, postData) {
     // Wait for formDataSections and requestPostData state are ready in redux store
     // since copyPostData API needs to read these state.
     yield waitUntil(() => {
-      let { requests } = gStore.getState().requests;
+      let { requests } = store.getState().requests;
       let actIDs = Object.keys(requests.toJS());
       let { formDataSections, requestPostData } = requests.get(actIDs[index]).toJS();
       return formDataSections && requestPostData;
     });
     EventUtils.sendMouseEvent({ type: "mousedown" },
       document.querySelectorAll(".request-list-item")[index]);
     EventUtils.sendMouseEvent({ type: "contextmenu" },
       document.querySelectorAll(".request-list-item")[index]);
--- a/devtools/client/netmonitor/test/browser_net_copy_url.js
+++ b/devtools/client/netmonitor/test/browser_net_copy_url.js
@@ -6,33 +6,33 @@
 /**
  * Tests if copying a request's url works.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests(1);
   });
   yield wait;
 
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[0]);
   EventUtils.sendMouseEvent({ type: "contextmenu" },
     document.querySelectorAll(".request-list-item")[0]);
 
-  let requestItem = getSortedRequests(gStore.getState()).get(0);
+  let requestItem = getSortedRequests(store.getState()).get(0);
 
   yield waitForClipboardPromise(function setup() {
     monitor.panelWin.parent.document
       .querySelector("#request-list-context-copy-url").click();
   }, requestItem.url);
 
   yield teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_cors_requests.js
+++ b/devtools/client/netmonitor/test/browser_net_cors_requests.js
@@ -5,41 +5,41 @@
 
 /**
  * Test that CORS preflight requests are displayed by network monitor
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CORS_URL);
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1, 1);
 
   info("Performing a CORS request");
   let requestUrl = "http://test1.example.com" + CORS_SJS_PATH;
   yield ContentTask.spawn(tab.linkedBrowser, requestUrl, function* (url) {
     content.wrappedJSObject.performRequests(url, "triggering/preflight", "post-data");
   });
 
   info("Waiting until the requests appear in netmonitor");
   yield wait;
 
   info("Checking the preflight and flight methods");
   ["OPTIONS", "POST"].forEach((method, index) => {
     verifyRequestItemTarget(
       document,
-      getDisplayedRequests(gStore.getState()),
-      getSortedRequests(gStore.getState()).get(index),
+      getDisplayedRequests(store.getState()),
+      getSortedRequests(store.getState()).get(index),
       method,
       requestUrl
     );
   });
 
   yield teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_curl-utils.js
+++ b/devtools/client/netmonitor/test/browser_net_curl-utils.js
@@ -8,36 +8,36 @@
  */
 
 const { Curl, CurlUtils } = require("devtools/client/shared/curl");
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CURL_UTILS_URL);
   info("Starting test... ");
 
-  let { gStore, windowRequire } = monitor.panelWin;
+  let { store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
-  let { getLongString } = windowRequire("devtools/client/netmonitor/src/utils/client");
+  let { getLongString } = windowRequire("devtools/client/netmonitor/src/connector/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1, 3);
   yield ContentTask.spawn(tab.linkedBrowser, SIMPLE_SJS, function* (url) {
     content.wrappedJSObject.performRequests(url);
   });
   yield wait;
 
   let requests = {
-    get: getSortedRequests(gStore.getState()).get(0),
-    post: getSortedRequests(gStore.getState()).get(1),
-    multipart: getSortedRequests(gStore.getState()).get(2),
-    multipartForm: getSortedRequests(gStore.getState()).get(3),
+    get: getSortedRequests(store.getState()).get(0),
+    post: getSortedRequests(store.getState()).get(1),
+    multipart: getSortedRequests(store.getState()).get(2),
+    multipartForm: getSortedRequests(store.getState()).get(3),
   };
 
   let data = yield createCurlData(requests.get, getLongString);
   testFindHeader(data);
 
   data = yield createCurlData(requests.post, getLongString);
   testIsUrlEncodedRequest(data);
   testWritePostDataTextParams(data);
--- a/devtools/client/netmonitor/test/browser_net_cyrillic-01.js
+++ b/devtools/client/netmonitor/test/browser_net_cyrillic-01.js
@@ -6,35 +6,35 @@
 /**
  * Tests if cyrillic text is rendered correctly in the source editor.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CYRILLIC_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   verifyRequestItemTarget(
     document,
-    getDisplayedRequests(gStore.getState()),
-    getSortedRequests(gStore.getState()).get(0),
+    getDisplayedRequests(store.getState()),
+    getSortedRequests(store.getState()).get(0),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=txt",
     {
       status: 200,
       statusText: "DA DA DA"
     }
   );
 
--- a/devtools/client/netmonitor/test/browser_net_cyrillic-02.js
+++ b/devtools/client/netmonitor/test/browser_net_cyrillic-02.js
@@ -7,30 +7,30 @@
  * Tests if cyrillic text is rendered correctly in the source editor
  * when loaded directly from an HTML page.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CYRILLIC_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
   let wait = waitForNetworkEvents(monitor, 1);
   tab.linkedBrowser.reload();
   yield wait;
 
   verifyRequestItemTarget(
     document,
-    getDisplayedRequests(gStore.getState()),
-    getSortedRequests(gStore.getState()).get(0),
+    getDisplayedRequests(store.getState()),
+    getSortedRequests(store.getState()).get(0),
     "GET",
     CYRILLIC_URL,
     {
       status: 200,
       statusText: "OK"
     });
 
   wait = waitForDOM(document, "#headers-panel");
--- a/devtools/client/netmonitor/test/browser_net_filter-01.js
+++ b/devtools/client/netmonitor/test/browser_net_filter-01.js
@@ -125,43 +125,43 @@ const EXPECTED_REQUESTS = [
       status: 101,
       statusText: "Switching Protocols",
     }
   }
 ];
 
 add_task(function* () {
   let { monitor } = yield initNetMonitor(FILTERING_URL);
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSelectedRequest,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   function setFreetextFilter(value) {
-    gStore.dispatch(Actions.setRequestFilterText(value));
+    store.dispatch(Actions.setRequestFilterText(value));
   }
 
   info("Starting test... ");
 
   let wait = waitForNetworkEvents(monitor, 9);
   loadCommonFrameScript();
   yield performRequestsInContent(REQUESTS_WITH_MEDIA_AND_FLASH_AND_WS);
   yield wait;
 
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[0]);
 
-  isnot(getSelectedRequest(gStore.getState()), null,
+  isnot(getSelectedRequest(store.getState()), null,
     "There should be a selected item in the requests menu.");
-  is(getSelectedIndex(gStore.getState()), 0,
+  is(getSelectedIndex(store.getState()), 0,
     "The first item should be selected in the requests menu.");
   is(!!document.querySelector(".network-details-panel"), true,
     "The network details panel should render correctly.");
 
   // First test with single filters...
   testFilterButtons(monitor, "all");
   testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
@@ -311,23 +311,23 @@ add_task(function* () {
   function getSelectedIndex(state) {
     if (!state.requests.selectedId) {
       return -1;
     }
     return getSortedRequests(state).findIndex(r => r.id === state.requests.selectedId);
   }
 
   function testContents(visibility) {
-    isnot(getSelectedRequest(gStore.getState()), undefined,
+    isnot(getSelectedRequest(store.getState()), undefined,
       "There should still be a selected item after filtering.");
-    is(getSelectedIndex(gStore.getState()), 0,
+    is(getSelectedIndex(store.getState()), 0,
       "The first item should be still selected after filtering.");
 
-    const items = getSortedRequests(gStore.getState());
-    const visibleItems = getDisplayedRequests(gStore.getState());
+    const items = getSortedRequests(store.getState());
+    const visibleItems = getDisplayedRequests(store.getState());
 
     is(items.size, visibility.length,
        "There should be a specific amount of items in the requests menu.");
     is(visibleItems.size, visibility.filter(e => e).length,
        "There should be a specific amount of visible items in the requests menu.");
 
     for (let i = 0; i < visibility.length; i++) {
       let itemId = items.get(i).id;
@@ -336,18 +336,18 @@ add_task(function* () {
 
       is(isThere, shouldBeVisible,
         `The item at index ${i} has visibility=${shouldBeVisible}`);
 
       if (shouldBeVisible) {
         let { method, url, data } = EXPECTED_REQUESTS[i];
         verifyRequestItemTarget(
           document,
-          getDisplayedRequests(gStore.getState()),
-          getSortedRequests(gStore.getState()).get(i),
+          getDisplayedRequests(store.getState()),
+          getSortedRequests(store.getState()).get(i),
           method,
           url,
           data
         );
       }
     }
   }
 });
--- a/devtools/client/netmonitor/test/browser_net_filter-02.js
+++ b/devtools/client/netmonitor/test/browser_net_filter-02.js
@@ -131,37 +131,37 @@ const EXPECTED_REQUESTS = [
 
 add_task(function* () {
   let { monitor } = yield initNetMonitor(FILTERING_URL);
   info("Starting test... ");
 
   // It seems that this test may be slow on Ubuntu builds running on ec2.
   requestLongerTimeout(2);
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSelectedRequest,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 9);
   loadCommonFrameScript();
   yield performRequestsInContent(REQUESTS_WITH_MEDIA_AND_FLASH_AND_WS);
   yield wait;
 
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[0]);
 
-  isnot(getSelectedRequest(gStore.getState()), null,
+  isnot(getSelectedRequest(store.getState()), null,
     "There should be a selected item in the requests menu.");
-  is(getSelectedIndex(gStore.getState()), 0,
+  is(getSelectedIndex(store.getState()), 0,
     "The first item should be selected in the requests menu.");
   is(!!document.querySelector(".network-details-panel"), true,
     "The network details panel should be visible after toggle button was pressed.");
 
   testFilterButtons(monitor, "all");
   testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
   info("Testing html filtering.");
@@ -201,25 +201,25 @@ add_task(function* () {
   function getSelectedIndex(state) {
     if (!state.requests.selectedId) {
       return -1;
     }
     return getSortedRequests(state).findIndex(r => r.id === state.requests.selectedId);
   }
 
   function testContents(visibility) {
-    isnot(getSelectedRequest(gStore.getState()), null,
+    isnot(getSelectedRequest(store.getState()), null,
       "There should still be a selected item after filtering.");
-    is(getSelectedIndex(gStore.getState()), 0,
+    is(getSelectedIndex(store.getState()), 0,
       "The first item should be still selected after filtering.");
     is(!!document.querySelector(".network-details-panel"), true,
       "The network details panel should still be visible after filtering.");
 
-    const items = getSortedRequests(gStore.getState());
-    const visibleItems = getDisplayedRequests(gStore.getState());
+    const items = getSortedRequests(store.getState());
+    const visibleItems = getDisplayedRequests(store.getState());
 
     is(items.size, visibility.length,
       "There should be a specific amount of items in the requests menu.");
     is(visibleItems.size, visibility.filter(e => e).length,
       "There should be a specific amount of visible items in the requests menu.");
 
     for (let i = 0; i < visibility.length; i++) {
       let itemId = items.get(i).id;
@@ -230,18 +230,18 @@ add_task(function* () {
     }
 
     for (let i = 0; i < EXPECTED_REQUESTS.length; i++) {
       let { method, url, data } = EXPECTED_REQUESTS[i];
       for (let j = i; j < visibility.length; j += EXPECTED_REQUESTS.length) {
         if (visibility[j]) {
           verifyRequestItemTarget(
             document,
-            getDisplayedRequests(gStore.getState()),
-            getSortedRequests(gStore.getState()).get(i),
+            getDisplayedRequests(store.getState()),
+            getSortedRequests(store.getState()).get(i),
             method,
             url,
             data
           );
         }
       }
     }
   }
--- a/devtools/client/netmonitor/test/browser_net_filter-03.js
+++ b/devtools/client/netmonitor/test/browser_net_filter-03.js
@@ -22,44 +22,44 @@ const REQUESTS_WITH_MEDIA = BASIC_REQUES
 
 add_task(function* () {
   let { monitor } = yield initNetMonitor(FILTERING_URL);
   info("Starting test... ");
 
   // It seems that this test may be slow on Ubuntu builds running on ec2.
   requestLongerTimeout(2);
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSelectedRequest,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   // The test assumes that the first HTML request here has a longer response
   // body than the other HTML requests performed later during the test.
   let requests = Cu.cloneInto(REQUESTS_WITH_MEDIA, {});
   let newres = "res=<p>" + new Array(10).join(Math.random(10)) + "</p>";
   requests[0].url = requests[0].url.replace("res=undefined", newres);
 
   loadCommonFrameScript();
 
   let wait = waitForNetworkEvents(monitor, 7);
   yield performRequestsInContent(requests);
   yield wait;
 
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[0]);
 
-  isnot(getSelectedRequest(gStore.getState()), null,
+  isnot(getSelectedRequest(store.getState()), null,
     "There should be a selected item in the requests menu.");
-  is(getSelectedIndex(gStore.getState()), 0,
+  is(getSelectedIndex(store.getState()), 0,
     "The first item should be selected in the requests menu.");
   is(!!document.querySelector(".network-details-panel"), true,
     "The network details panel should be visible after toggle button was pressed.");
 
   testFilterButtons(monitor, "all");
   testContents([0, 1, 2, 3, 4, 5, 6], 7, 0);
 
   info("Sorting by size, ascending.");
@@ -106,21 +106,21 @@ add_task(function* () {
   function getSelectedIndex(state) {
     if (!state.requests.selectedId) {
       return -1;
     }
     return getSortedRequests(state).findIndex(r => r.id === state.requests.selectedId);
   }
 
   function testContents(order, visible, selection) {
-    isnot(getSelectedRequest(gStore.getState()), null,
+    isnot(getSelectedRequest(store.getState()), null,
       "There should still be a selected item after filtering.");
-    is(getSelectedIndex(gStore.getState()), selection,
+    is(getSelectedIndex(store.getState()), selection,
       "The first item should be still selected after filtering.");
     is(!!document.querySelector(".network-details-panel"), true,
       "The network details panel should still be visible after filtering.");
 
-    is(getSortedRequests(gStore.getState()).length, order.length,
+    is(getSortedRequests(store.getState()).length, order.length,
       "There should be a specific amount of items in the requests menu.");
-    is(getDisplayedRequests(gStore.getState()).length, visible,
+    is(getDisplayedRequests(store.getState()).length, visible,
       "There should be a specific amount of visible items in the requests menu.");
   }
 });
--- a/devtools/client/netmonitor/test/browser_net_filter-04.js
+++ b/devtools/client/netmonitor/test/browser_net_filter-04.js
@@ -31,21 +31,21 @@ 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 { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let { Prefs } = windowRequire("devtools/client/netmonitor/src/utils/prefs");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   is(Prefs.filters.length, 3,
     "All the filter types should be loaded.");
   is(Prefs.filters[0], "bogus",
     "The first filter type is invalid, but loaded anyway.");
 
   let wait = waitForNetworkEvents(monitor, 9);
   loadCommonFrameScript();
--- a/devtools/client/netmonitor/test/browser_net_filter-flags.js
+++ b/devtools/client/netmonitor/test/browser_net_filter-flags.js
@@ -105,27 +105,27 @@ const EXPECTED_REQUESTS = [
       type: "plain",
       fullMimeType: "text/plain; charset=utf-8"
     }
   },
 ];
 
 add_task(function* () {
   let { monitor } = yield initNetMonitor(FILTERING_URL);
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   function setFreetextFilter(value) {
-    gStore.dispatch(Actions.setRequestFilterText(value));
+    store.dispatch(Actions.setRequestFilterText(value));
   }
 
   info("Starting test... ");
 
   let waitNetwork = waitForNetworkEvents(monitor, 8);
   loadCommonFrameScript();
   yield performRequestsInContent(REQUESTS);
   yield waitNetwork;
@@ -185,18 +185,18 @@ add_task(function* () {
 
   // Test regex filter
   setFreetextFilter("regexp:content.*?Sam");
   testContents([1, 0, 0, 0, 0, 0, 0, 0]);
 
   yield teardown(monitor);
 
   function testContents(visibility) {
-    const items = getSortedRequests(gStore.getState());
-    const visibleItems = getDisplayedRequests(gStore.getState());
+    const items = getSortedRequests(store.getState());
+    const visibleItems = getDisplayedRequests(store.getState());
 
     is(items.size, visibility.length,
       "There should be a specific amount of items in the requests menu.");
     is(visibleItems.size, visibility.filter(e => e).length,
       "There should be a specific amount of visible items in the requests menu.");
 
     for (let i = 0; i < visibility.length; i++) {
       let itemId = items.get(i).id;
@@ -204,18 +204,18 @@ add_task(function* () {
       let isThere = visibleItems.some(r => r.id == itemId);
       is(isThere, shouldBeVisible,
         `The item at index ${i} has visibility=${shouldBeVisible}`);
 
       if (shouldBeVisible) {
         let { method, url, data } = EXPECTED_REQUESTS[i];
         verifyRequestItemTarget(
           document,
-          getDisplayedRequests(gStore.getState()),
-          getSortedRequests(gStore.getState()).get(i),
+          getDisplayedRequests(store.getState()),
+          getSortedRequests(store.getState()).get(i),
           method,
           url,
           data
         );
       }
     }
   }
 });
--- a/devtools/client/netmonitor/test/browser_net_footer-summary.js
+++ b/devtools/client/netmonitor/test/browser_net_footer-summary.js
@@ -13,24 +13,24 @@ add_task(function* () {
     getFormattedTime
   } = require("devtools/client/netmonitor/src/utils/format-utils");
 
   requestLongerTimeout(2);
 
   let { tab, monitor } = yield initNetMonitor(FILTERING_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let { getDisplayedRequestsSummary } =
     windowRequire("devtools/client/netmonitor/src/selectors/index");
   let { L10N } = windowRequire("devtools/client/netmonitor/src/utils/l10n");
   let { PluralForm } = windowRequire("devtools/shared/plural-form");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
   testStatus();
 
   for (let i = 0; i < 2; i++) {
     info(`Performing requests in batch #${i}`);
     let wait = waitForNetworkEvents(monitor, 8);
     yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
       content.wrappedJSObject.performRequests('{ "getMedia": true, "getFlash": true }');
     });
@@ -44,17 +44,17 @@ add_task(function* () {
       EventUtils.sendMouseEvent({ type: "click" }, buttonEl);
       testStatus();
     }
   }
 
   yield teardown(monitor);
 
   function testStatus() {
-    let state = gStore.getState();
+    let state = store.getState();
     let totalRequestsCount = state.requests.requests.size;
     let requestsSummary = getDisplayedRequestsSummary(state);
     info(`Current requests: ${requestsSummary.count} of ${totalRequestsCount}.`);
 
     let valueCount = document.querySelector(".requests-list-network-summary-count")
                         .textContent;
     info("Current summary count: " + valueCount);
     let expectedCount = PluralForm.get(requestsSummary.count,
--- a/devtools/client/netmonitor/test/browser_net_frame.js
+++ b/devtools/client/netmonitor/test/browser_net_frame.js
@@ -153,53 +153,53 @@ add_task(function* () {
   // the initNetMonitor function clears the network request list after the
   // page is loaded. That's why we first load a bogus page from SIMPLE_URL,
   // and only then load the real thing from TOP_URL - we want to catch
   // all the requests the page is making, not only the XHRs.
   // We can't use about:blank here, because initNetMonitor checks that the
   // page has actually made at least one request.
   let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   tab.linkedBrowser.loadURI(TOP_URL, null, null);
 
   yield waitForNetworkEvents(monitor, REQUEST_COUNT);
 
-  is(gStore.getState().requests.requests.size, REQUEST_COUNT,
+  is(store.getState().requests.requests.size, REQUEST_COUNT,
     "All the page events should be recorded.");
 
   // While there is a defined order for requests in each document separately, the requests
   // from different documents may interleave in various ways that change per test run, so
   // there is not a single order when considering all the requests together.
   let currentTop = 0;
   let currentSub = 0;
   for (let i = 0; i < REQUEST_COUNT; i++) {
-    let requestItem = getSortedRequests(gStore.getState()).get(i);
+    let requestItem = getSortedRequests(store.getState()).get(i);
 
     let itemUrl = requestItem.url;
     let itemCauseUri = requestItem.cause.loadingDocumentUri;
     let spec;
     if (itemUrl == SUB_URL || itemCauseUri == SUB_URL) {
       spec = EXPECTED_REQUESTS_SUB[currentSub++];
     } else {
       spec = EXPECTED_REQUESTS_TOP[currentTop++];
     }
     let { method, url, causeType, causeUri, stack } = spec;
 
     verifyRequestItemTarget(
       document,
-      getDisplayedRequests(gStore.getState()),
+      getDisplayedRequests(store.getState()),
       requestItem,
       method,
       url,
       { cause: { type: causeType, loadingDocumentUri: causeUri } }
     );
 
     let { stacktrace } = requestItem.cause;
     let stackLen = stacktrace ? stacktrace.length : 0;
--- a/devtools/client/netmonitor/test/browser_net_header-docs.js
+++ b/devtools/client/netmonitor/test/browser_net_header-docs.js
@@ -6,35 +6,35 @@
 /**
  * Tests if "Learn More" links are correctly displayed
  * next to headers.
  */
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(POST_DATA_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
   let { getHeadersURL } = require("devtools/client/netmonitor/src/utils/mdn-utils");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 0, 2);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelectorAll(".request-list-item")[0]);
 
-  testShowLearnMore(getSortedRequests(gStore.getState()).get(0));
+  testShowLearnMore(getSortedRequests(store.getState()).get(0));
 
   return teardown(monitor);
 
   /*
    * Tests that a "Learn More" button is only shown if
    * and only if a header is documented in MDN.
    */
   function testShowLearnMore(data) {
--- a/devtools/client/netmonitor/test/browser_net_icon-preview.js
+++ b/devtools/client/netmonitor/test/browser_net_icon-preview.js
@@ -7,37 +7,37 @@
  * Tests if image responses show a thumbnail in the requests menu.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL);
   const SELECTOR = ".requests-list-icon[src]";
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
-  let { NetMonitorController } =
-    windowRequire("devtools/client/netmonitor/src/netmonitor-controller");
+  let { triggerActivity } =
+    windowRequire("devtools/client/netmonitor/src/connector/index");
   let { ACTIVITY_TYPE } = windowRequire("devtools/client/netmonitor/src/constants");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS);
   yield performRequests();
   yield wait;
   yield waitUntil(() => !!document.querySelector(SELECTOR));
 
   info("Checking the image thumbnail when all items are shown.");
   checkImageThumbnail();
 
-  gStore.dispatch(Actions.sortBy("contentSize"));
+  store.dispatch(Actions.sortBy("contentSize"));
   info("Checking the image thumbnail when all items are sorted.");
   checkImageThumbnail();
 
-  gStore.dispatch(Actions.toggleRequestFilterType("images"));
+  store.dispatch(Actions.toggleRequestFilterType("images"));
   info("Checking the image thumbnail when only images are shown.");
   checkImageThumbnail();
 
   info("Reloading the debuggee and performing all requests again...");
   wait = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS);
   yield reloadAndPerformRequests();
   yield wait;
   yield waitUntil(() => !!document.querySelector(SELECTOR));
@@ -49,17 +49,17 @@ add_task(function* () {
 
   function performRequests() {
     return ContentTask.spawn(tab.linkedBrowser, {}, function* () {
       content.wrappedJSObject.performRequests();
     });
   }
 
   function* reloadAndPerformRequests() {
-    yield NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
+    yield triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
     yield performRequests();
   }
 
   function checkImageThumbnail() {
     is(document.querySelectorAll(SELECTOR).length, 1,
       "There should be only one image request with a thumbnail displayed.");
     is(document.querySelector(SELECTOR).src, TEST_IMAGE_DATA_URI,
       "The image requests-list-icon thumbnail is displayed correctly.");
--- a/devtools/client/netmonitor/test/browser_net_image-tooltip.js
+++ b/devtools/client/netmonitor/test/browser_net_image-tooltip.js
@@ -9,24 +9,24 @@ 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);
   const SELECTOR = ".requests-list-icon[src]";
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
-  let { NetMonitorController } =
-    windowRequire("devtools/client/netmonitor/src/netmonitor-controller");
+  let { triggerActivity } =
+    windowRequire("devtools/client/netmonitor/src/connector/index");
   let { ACTIVITY_TYPE } = windowRequire("devtools/client/netmonitor/src/constants");
   let toolboxDoc = monitor.panelWin.parent.document;
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let onEvents = waitForNetworkEvents(monitor, IMAGE_TOOLTIP_REQUESTS);
   yield performRequests();
   yield onEvents;
   yield waitUntil(() => !!document.querySelector(SELECTOR));
 
   info("Checking the image thumbnail after a few requests were made...");
   yield showTooltipAndVerify(document.querySelectorAll(".request-list-item")[0]);
@@ -35,17 +35,17 @@ add_task(function* test() {
   // the icon for the request of the next test.
   info("Checking the image thumbnail gets hidden...");
   yield hideTooltipAndVerify(document.querySelectorAll(".request-list-item")[0]);
 
   // +1 extra document reload
   onEvents = waitForNetworkEvents(monitor, IMAGE_TOOLTIP_REQUESTS + 1);
 
   info("Reloading the debuggee and performing all requests again...");
-  yield NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
+  yield triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
   yield performRequests();
   yield onEvents;
   yield waitUntil(() => !!document.querySelector(SELECTOR));
 
   info("Checking the image thumbnail after a reload.");
   yield showTooltipAndVerify(document.querySelectorAll(".request-list-item")[1]);
 
   info("Checking if the image thumbnail is hidden when mouse leaves the menu widget");
--- a/devtools/client/netmonitor/test/browser_net_json-b64.js
+++ b/devtools/client/netmonitor/test/browser_net_json-b64.js
@@ -7,20 +7,20 @@
  * Tests if JSON responses encoded in base64 are handled correctly.
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
   let { tab, monitor } = yield initNetMonitor(JSON_B64_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   wait = waitForDOM(document, "#response-panel");
--- a/devtools/client/netmonitor/test/browser_net_json-long.js
+++ b/devtools/client/netmonitor/test/browser_net_json-long.js
@@ -12,35 +12,35 @@ add_task(function* () {
 
   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);
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   verifyRequestItemTarget(
     document,
-    getDisplayedRequests(gStore.getState()),
-    getSortedRequests(gStore.getState()).get(0),
+    getDisplayedRequests(store.getState()),
+    getSortedRequests(store.getState()).get(0),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=json-long",
     {
       status: 200,
       statusText: "OK",
       type: "json",
       fullMimeType: "text/json; charset=utf-8",
       size: L10N.getFormatStr("networkMenu.sizeKB",
--- a/devtools/client/netmonitor/test/browser_net_json-malformed.js
+++ b/devtools/client/netmonitor/test/browser_net_json-malformed.js
@@ -7,35 +7,35 @@
  * Tests if malformed JSON responses are handled correctly.
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
   let { tab, monitor } = yield initNetMonitor(JSON_MALFORMED_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   verifyRequestItemTarget(
     document,
-    getDisplayedRequests(gStore.getState()),
-    getSortedRequests(gStore.getState()).get(0),
+    getDisplayedRequests(store.getState()),
+    getSortedRequests(store.getState()).get(0),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=json-malformed",
     {
       status: 200,
       statusText: "OK",
       type: "json",
       fullMimeType: "text/json; charset=utf-8"
     });
--- a/devtools/client/netmonitor/test/browser_net_json-null.js
+++ b/devtools/client/netmonitor/test/browser_net_json-null.js
@@ -6,21 +6,21 @@
 /**
  * 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... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let { L10N } = windowRequire("devtools/client/netmonitor/src/utils/l10n");
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   yield openResponsePanel();
--- a/devtools/client/netmonitor/test/browser_net_json_custom_mime.js
+++ b/devtools/client/netmonitor/test/browser_net_json_custom_mime.js
@@ -6,36 +6,36 @@
 /**
  * Tests if JSON responses with unusal/custom MIME types are handled correctly.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(JSON_CUSTOM_MIME_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let { L10N } = windowRequire("devtools/client/netmonitor/src/utils/l10n");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   verifyRequestItemTarget(
     document,
-    getDisplayedRequests(gStore.getState()),
-    getSortedRequests(gStore.getState()).get(0),
+    getDisplayedRequests(store.getState()),
+    getSortedRequests(store.getState()).get(0),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=json-custom-mime",
     {
       status: 200,
       statusText: "OK",
       type: "x-bigcorp-json",
       fullMimeType: "text/x-bigcorp-json; charset=utf-8",
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 41),
--- a/devtools/client/netmonitor/test/browser_net_json_text_mime.js
+++ b/devtools/client/netmonitor/test/browser_net_json_text_mime.js
@@ -8,35 +8,35 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(JSON_TEXT_MIME_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   verifyRequestItemTarget(
     document,
-    getDisplayedRequests(gStore.getState()),
-    getSortedRequests(gStore.getState()).get(0),
+    getDisplayedRequests(store.getState()),
+    getSortedRequests(store.getState()).get(0),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=json-text-mime",
     {
       status: 200,
       statusText: "OK",
       type: "plain",
       fullMimeType: "text/plain; charset=utf-8",
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 41),
--- a/devtools/client/netmonitor/test/browser_net_jsonp.js
+++ b/devtools/client/netmonitor/test/browser_net_jsonp.js
@@ -8,49 +8,49 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(JSONP_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 2);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   verifyRequestItemTarget(
     document,
-    getDisplayedRequests(gStore.getState()),
-    getSortedRequests(gStore.getState()).get(0),
+    getDisplayedRequests(store.getState()),
+    getSortedRequests(store.getState()).get(0),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=jsonp&jsonp=$_0123Fun",
     {
       status: 200,
       statusText: "OK",
       type: "json",
       fullMimeType: "text/json; charset=utf-8",
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 41),
       time: true
     });
   verifyRequestItemTarget(
     document,
-    getDisplayedRequests(gStore.getState()),
-    getSortedRequests(gStore.getState()).get(1),
+    getDisplayedRequests(store.getState()),
+    getSortedRequests(store.getState()).get(1),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=jsonp2&jsonp=$_4567Sad",
     {
       status: 200,
       statusText: "OK",
       type: "json",
       fullMimeType: "text/json; charset=utf-8",
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 54),
--- a/devtools/client/netmonitor/test/browser_net_large-response.js
+++ b/devtools/client/netmonitor/test/browser_net_large-response.js
@@ -12,35 +12,35 @@ const HTML_LONG_URL = CONTENT_TYPE_SJS +
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
   info("Starting test... ");
 
   // This test could potentially be slow because over 100 KB of stuff
   // is going to be requested and displayed in the source editor.
   requestLongerTimeout(2);
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, HTML_LONG_URL, function* (url) {
     content.wrappedJSObject.performRequests(1, url);
   });
   yield wait;
 
   verifyRequestItemTarget(
     document,
-    getDisplayedRequests(gStore.getState()),
-    getSortedRequests(gStore.getState()).get(0),
+    getDisplayedRequests(store.getState()),
+    getSortedRequests(store.getState()).get(0),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=html-long",
     {
       status: 200,
       statusText: "OK"
     });
 
   wait = waitForDOM(document, "#response-panel .CodeMirror-code");
--- a/devtools/client/netmonitor/test/browser_net_open_request_in_tab.js
+++ b/devtools/client/netmonitor/test/browser_net_open_request_in_tab.js
@@ -6,20 +6,20 @@
 /**
  * Tests if Open in new tab works.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
   info("Starting test...");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests(1);
   });
   yield wait;
 
   EventUtils.sendMouseEvent({ type: "mousedown" },
deleted file mode 100644
--- a/devtools/client/netmonitor/test/browser_net_page-nav.js
+++ /dev/null
@@ -1,72 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-/**
- * Tests if page navigation ("close", "navigate", etc.) triggers an appropriate
- * action in the network monitor.
- */
-
-add_task(function* () {
-  let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
-  info("Starting test... ");
-
-  let { windowRequire } = monitor.panelWin;
-  let { NetMonitorController } =
-    windowRequire("devtools/client/netmonitor/src/netmonitor-controller");
-  let { EVENTS } = windowRequire("devtools/client/netmonitor/src/constants");
-
-  yield testNavigate();
-  yield testNavigateBack();
-  yield testClose();
-
-  function* testNavigate() {
-    info("Navigating forward...");
-
-    let onWillNav = monitor.panelWin.once(EVENTS.TARGET_WILL_NAVIGATE);
-    let onDidNav = monitor.panelWin.once(EVENTS.TARGET_DID_NAVIGATE);
-
-    tab.linkedBrowser.loadURI(NAVIGATE_URL);
-    yield onWillNav;
-
-    is(tab.linkedBrowser.currentURI.spec, SIMPLE_URL,
-      "Target started navigating to the correct location.");
-
-    yield onDidNav;
-    is(tab.linkedBrowser.currentURI.spec, NAVIGATE_URL,
-      "Target finished navigating to the correct location.");
-  }
-
-  function* testNavigateBack() {
-    info("Navigating backward...");
-
-    let onWillNav = monitor.panelWin.once(EVENTS.TARGET_WILL_NAVIGATE);
-    let onDidNav = monitor.panelWin.once(EVENTS.TARGET_DID_NAVIGATE);
-
-    tab.linkedBrowser.loadURI(SIMPLE_URL);
-    yield onWillNav;
-
-    is(tab.linkedBrowser.currentURI.spec, NAVIGATE_URL,
-      "Target started navigating back to the previous location.");
-
-    yield onDidNav;
-    is(tab.linkedBrowser.currentURI.spec, SIMPLE_URL,
-      "Target finished navigating back to the previous location.");
-  }
-
-  function* testClose() {
-    info("Closing...");
-
-    let onDestroyed = monitor.once("destroyed");
-    removeTab(tab);
-    yield onDestroyed;
-
-    ok(!NetMonitorController.client,
-      "There shouldn't be a client available after destruction.");
-    ok(!NetMonitorController.tabClient,
-      "There shouldn't be a tabClient available after destruction.");
-    ok(!NetMonitorController.webConsoleClient,
-      "There shouldn't be a webConsoleClient available after destruction.");
-  }
-});
--- a/devtools/client/netmonitor/test/browser_net_pane-toggle.js
+++ b/devtools/client/netmonitor/test/browser_net_pane-toggle.js
@@ -6,76 +6,76 @@
 /**
  * Tests if toggling the details pane works as expected.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let { EVENTS } = windowRequire("devtools/client/netmonitor/src/constants");
   let {
     getSelectedRequest,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let toggleButton = document.querySelector(".network-details-panel-toggle");
 
   is(toggleButton.hasAttribute("disabled"), true,
     "The pane toggle button should be disabled when the frontend is opened.");
   is(toggleButton.classList.contains("pane-collapsed"), true,
     "The pane toggle button should indicate that the details pane is " +
     "collapsed when the frontend is opened.");
   is(!!document.querySelector(".network-details-panel"), false,
     "The details pane should be hidden when the frontend is opened.");
-  is(getSelectedRequest(gStore.getState()), null,
+  is(getSelectedRequest(store.getState()), null,
     "There should be no selected item in the requests menu.");
 
   let networkEvent = monitor.panelWin.once(EVENTS.NETWORK_EVENT);
   tab.linkedBrowser.reload();
   yield networkEvent;
 
   is(toggleButton.hasAttribute("disabled"), false,
     "The pane toggle button should be enabled after the first request.");
   is(toggleButton.classList.contains("pane-collapsed"), true,
     "The pane toggle button should still indicate that the details pane is " +
     "collapsed after the first request.");
   is(!!document.querySelector(".network-details-panel"), false,
     "The details pane should still be hidden after the first request.");
-  is(getSelectedRequest(gStore.getState()), null,
+  is(getSelectedRequest(store.getState()), null,
     "There should still be no selected item in the requests menu.");
 
   EventUtils.sendMouseEvent({ type: "click" }, toggleButton);
 
   is(toggleButton.hasAttribute("disabled"), false,
     "The pane toggle button should still be enabled after being pressed.");
   is(toggleButton.classList.contains("pane-collapsed"), false,
     "The pane toggle button should now indicate that the details pane is " +
     "not collapsed anymore after being pressed.");
   is(!!document.querySelector(".network-details-panel"), true,
     "The details pane should not be hidden after toggle button was pressed.");
-  isnot(getSelectedRequest(gStore.getState()), null,
+  isnot(getSelectedRequest(store.getState()), null,
     "There should be a selected item in the requests menu.");
-  is(getSelectedIndex(gStore.getState()), 0,
+  is(getSelectedIndex(store.getState()), 0,
     "The first item should be selected in the requests menu.");
 
   EventUtils.sendMouseEvent({ type: "click" }, toggleButton);
 
   is(toggleButton.hasAttribute("disabled"), false,
     "The pane toggle button should still be enabled after being pressed again.");
   is(toggleButton.classList.contains("pane-collapsed"), true,
     "The pane toggle button should now indicate that the details pane is " +
     "collapsed after being pressed again.");
   is(!!document.querySelector(".network-details-panel"), false,
     "The details pane should now be hidden after the toggle button was pressed again.");
-  is(getSelectedRequest(gStore.getState()), null,
+  is(getSelectedRequest(store.getState()), null,
     "There should now be no selected item in the requests menu.");
 
   yield teardown(monitor);
 
   function getSelectedIndex(state) {
     if (!state.requests.selectedId) {
       return -1;
     }
--- a/devtools/client/netmonitor/test/browser_net_post-data-01.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-01.js
@@ -11,50 +11,50 @@ add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/src/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;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 0, 2);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   verifyRequestItemTarget(
     document,
-    getDisplayedRequests(gStore.getState()),
-    getSortedRequests(gStore.getState()).get(0),
+    getDisplayedRequests(store.getState()),
+    getSortedRequests(store.getState()).get(0),
     "POST",
     SIMPLE_SJS + "?foo=bar&baz=42&type=urlencoded",
     {
       status: 200,
       statusText: "Och Aye",
       type: "plain",
       fullMimeType: "text/plain; charset=utf-8",
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
       time: true
     }
   );
   verifyRequestItemTarget(
     document,
-    getDisplayedRequests(gStore.getState()),
-    getSortedRequests(gStore.getState()).get(1),
+    getDisplayedRequests(store.getState()),
+    getSortedRequests(store.getState()).get(1),
     "POST",
     SIMPLE_SJS + "?foo=bar&baz=42&type=multipart",
     {
       status: 200,
       statusText: "Och Aye",
       type: "plain",
       fullMimeType: "text/plain; charset=utf-8",
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
--- a/devtools/client/netmonitor/test/browser_net_post-data-02.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-02.js
@@ -9,20 +9,20 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(POST_RAW_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 0, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   // Wait for all tree view updated by react
--- a/devtools/client/netmonitor/test/browser_net_post-data-03.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-03.js
@@ -9,20 +9,20 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(POST_RAW_WITH_HEADERS_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 0, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   // Wait for all tree view updated by react
--- a/devtools/client/netmonitor/test/browser_net_post-data-04.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-04.js
@@ -9,20 +9,20 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(POST_JSON_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 0, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   // Wait for all tree view updated by react
--- a/devtools/client/netmonitor/test/browser_net_prefs-reload.js
+++ b/devtools/client/netmonitor/test/browser_net_prefs-reload.js
@@ -20,17 +20,17 @@ add_task(function* () {
   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/src/utils/prefs").Prefs;
-  let getStore = () => monitor.panelWin.gStore;
+  let getStore = () => monitor.panelWin.store;
   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
       // to verify that the pref was applied properly.
--- a/devtools/client/netmonitor/test/browser_net_raw_headers.js
+++ b/devtools/client/netmonitor/test/browser_net_raw_headers.js
@@ -6,23 +6,23 @@
 /**
  * Tests if showing raw headers works.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(POST_DATA_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 0, 2);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   wait = waitForDOM(document, ".headers-overview");
@@ -30,17 +30,17 @@ add_task(function* () {
     document.querySelectorAll(".request-list-item")[0]);
   yield wait;
 
   wait = waitForDOM(document, ".raw-headers-container textarea", 2);
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelectorAll(".headers-summary .devtools-button")[1]);
   yield wait;
 
-  testShowRawHeaders(getSortedRequests(gStore.getState()).get(0));
+  testShowRawHeaders(getSortedRequests(store.getState()).get(0));
 
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelectorAll(".headers-summary .devtools-button")[1]);
 
   testHideRawHeaders(document);
 
   return teardown(monitor);
 
--- a/devtools/client/netmonitor/test/browser_net_req-resp-bodies.js
+++ b/devtools/client/netmonitor/test/browser_net_req-resp-bodies.js
@@ -8,24 +8,24 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(JSON_LONG_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   // Perform first batch of requests.
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
@@ -55,18 +55,18 @@ add_task(function* () {
 
   verifyRequest(1);
 
   return teardown(monitor);
 
   function verifyRequest(index) {
     verifyRequestItemTarget(
       document,
-      getDisplayedRequests(gStore.getState()),
-      getSortedRequests(gStore.getState()).get(index),
+      getDisplayedRequests(store.getState()),
+      getSortedRequests(store.getState()).get(index),
       "GET",
       CONTENT_TYPE_SJS + "?fmt=json-long",
       {
         status: 200,
         statusText: "OK",
         type: "json",
         fullMimeType: "text/json; charset=utf-8",
         size: L10N.getFormatStr("networkMenu.sizeKB",
--- a/devtools/client/netmonitor/test/browser_net_resend.js
+++ b/devtools/client/netmonitor/test/browser_net_resend.js
@@ -11,55 +11,55 @@ const ADD_QUERY = "t1=t2";
 const ADD_HEADER = "Test-header: true";
 const ADD_UA_HEADER = "User-Agent: Custom-Agent";
 const ADD_POSTDATA = "&t3=t4";
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(POST_DATA_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getSelectedRequest,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 0, 2);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
-  let origItem = getSortedRequests(gStore.getState()).get(0);
+  let origItem = getSortedRequests(store.getState()).get(0);
 
-  gStore.dispatch(Actions.selectRequest(origItem.id));
+  store.dispatch(Actions.selectRequest(origItem.id));
 
   // add a new custom request cloned from selected request
-  gStore.dispatch(Actions.cloneSelectedRequest());
+  store.dispatch(Actions.cloneSelectedRequest());
 
   testCustomForm(origItem);
 
-  let customItem = getSelectedRequest(gStore.getState());
+  let customItem = getSelectedRequest(store.getState());
   testCustomItem(customItem, origItem);
 
   // edit the custom request
   yield editCustomForm();
   // FIXME: reread the customItem, it's been replaced by a new object (immutable!)
-  customItem = getSelectedRequest(gStore.getState());
+  customItem = getSelectedRequest(store.getState());
   testCustomItemChanged(customItem, origItem);
 
   // send the new request
   wait = waitForNetworkEvents(monitor, 0, 1);
-  gStore.dispatch(Actions.sendCustomRequest());
+  store.dispatch(Actions.sendCustomRequest());
   yield wait;
 
-  let sentItem = getSelectedRequest(gStore.getState());
+  let sentItem = getSelectedRequest(store.getState());
   testSentRequest(sentItem, origItem);
 
   return teardown(monitor);
 
   function testCustomItem(item, orig) {
     is(item.method, orig.method, "item is showing the same method as original request");
     is(item.url, orig.url, "item is showing the same URL as original request");
   }
--- a/devtools/client/netmonitor/test/browser_net_resend_cors.js
+++ b/devtools/client/netmonitor/test/browser_net_resend_cors.js
@@ -7,54 +7,54 @@
  * Tests if resending a CORS request avoids the security checks and doesn't send
  * a preflight OPTIONS request (bug 1270096 and friends)
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CORS_URL);
   info("Starting test... ");
 
-  let { gStore, windowRequire } = monitor.panelWin;
+  let { store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let requestUrl = "http://test1.example.com" + CORS_SJS_PATH;
 
   info("Waiting for OPTIONS, then POST");
   let wait = waitForNetworkEvents(monitor, 1, 1);
   yield ContentTask.spawn(tab.linkedBrowser, requestUrl, function* (url) {
     content.wrappedJSObject.performRequests(url, "triggering/preflight", "post-data");
   });
   yield wait;
 
   const METHODS = ["OPTIONS", "POST"];
-  const ITEMS = METHODS.map((val, i) => getSortedRequests(gStore.getState()).get(i));
+  const ITEMS = METHODS.map((val, i) => getSortedRequests(store.getState()).get(i));
 
   // Check the requests that were sent
   ITEMS.forEach((item, i) => {
     is(item.method, METHODS[i], `The ${item.method} request has the right method`);
     is(item.url, requestUrl, `The ${item.method} request has the right URL`);
   });
 
   // Resend both requests without modification. Wait for resent OPTIONS, then POST.
   // POST is supposed to have no preflight OPTIONS request this time (CORS is disabled)
   let onRequests = waitForNetworkEvents(monitor, 1, 0);
   ITEMS.forEach((item) => {
     info(`Selecting the ${item.method} request`);
-    gStore.dispatch(Actions.selectRequest(item.id));
+    store.dispatch(Actions.selectRequest(item.id));
 
     info("Cloning the selected request into a custom clone");
-    gStore.dispatch(Actions.cloneSelectedRequest());
+    store.dispatch(Actions.cloneSelectedRequest());
 
     info("Sending the cloned request (without change)");
-    gStore.dispatch(Actions.sendCustomRequest());
+    store.dispatch(Actions.sendCustomRequest());
   });
 
   info("Waiting for both resent requests");
   yield onRequests;
 
   // Check the resent requests
   ITEMS.forEach((item, i) => {
     is(item.method, METHODS[i], `The ${item.method} request has the right method`);
--- a/devtools/client/netmonitor/test/browser_net_resend_headers.js
+++ b/devtools/client/netmonitor/test/browser_net_resend_headers.js
@@ -6,46 +6,46 @@
 /**
  * Test if custom request headers are not ignored (bug 1270096 and friends)
  */
 
 add_task(function* () {
   let { monitor } = yield initNetMonitor(SIMPLE_SJS);
   info("Starting test... ");
 
-  let { gStore, windowRequire } = monitor.panelWin;
+  let { store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
-  let { NetMonitorController } =
-    windowRequire("devtools/client/netmonitor/src/netmonitor-controller");
+  let { sendHTTPRequest } =
+    windowRequire("devtools/client/netmonitor/src/connector/index");
   let {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let requestUrl = SIMPLE_SJS;
   let requestHeaders = [
     { name: "Host", value: "fakehost.example.com" },
     { name: "User-Agent", value: "Testzilla" },
     { name: "Referer", value: "http://example.com/referrer" },
     { name: "Accept", value: "application/jarda"},
     { name: "Accept-Encoding", value: "compress, identity, funcoding" },
     { name: "Accept-Language", value: "cs-CZ" }
   ];
 
   let wait = waitForNetworkEvents(monitor, 0, 1);
-  NetMonitorController.webConsoleClient.sendHTTPRequest({
+  sendHTTPRequest({
     url: requestUrl,
     method: "POST",
     headers: requestHeaders,
     body: "Hello"
   });
   yield wait;
 
-  let item = getSortedRequests(gStore.getState()).get(0);
+  let item = getSortedRequests(store.getState()).get(0);
   is(item.method, "POST", "The request has the right method");
   is(item.url, requestUrl, "The request has the right URL");
 
   for (let { name, value } of item.requestHeaders.headers) {
     info(`Request header: ${name}: ${value}`);
   }
 
   function hasRequestHeader(name, value) {
--- a/devtools/client/netmonitor/test/browser_net_security-details.js
+++ b/devtools/client/netmonitor/test/browser_net_security-details.js
@@ -4,20 +4,20 @@
 "use strict";
 
 /**
  * Test that Security details tab contains the expected data.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   info("Performing a secure request.");
   const REQUESTS_URL = "https://example.com" + CORS_SJS_PATH;
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, REQUESTS_URL, function* (url) {
     content.wrappedJSObject.performRequests(1, url);
   });
   yield wait;
--- a/devtools/client/netmonitor/test/browser_net_security-error.js
+++ b/devtools/client/netmonitor/test/browser_net_security-error.js
@@ -4,21 +4,21 @@
 "use strict";
 
 /**
  * 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 { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let { EVENTS } = windowRequire("devtools/client/netmonitor/src/constants");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   info("Requesting a resource that has a certificate problem.");
 
   let requestsDone = waitForSecurityBrokenNetworkEvent();
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests(1, "https://nocert.example.com");
   });
   yield requestsDone;
--- a/devtools/client/netmonitor/test/browser_net_security-icon-click.js
+++ b/devtools/client/netmonitor/test/browser_net_security-icon-click.js
@@ -4,26 +4,26 @@
 "use strict";
 
 /**
  * Test that clicking on the security indicator opens the security details tab.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   info("Requesting a resource over HTTPS.");
   yield performRequestAndWait("https://example.com" + CORS_SJS_PATH + "?request_2");
   yield performRequestAndWait("https://example.com" + CORS_SJS_PATH + "?request_1");
 
-  is(gStore.getState().requests.requests.size, 2, "Two events event logged.");
+  is(store.getState().requests.requests.size, 2, "Two events event logged.");
 
   yield clickAndTestSecurityIcon();
 
   info("Selecting headers panel again.");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#headers-tab"));
 
   info("Sorting the items by filename.");
--- a/devtools/client/netmonitor/test/browser_net_security-redirect.js
+++ b/devtools/client/netmonitor/test/browser_net_security-redirect.js
@@ -5,28 +5,28 @@
 
 /**
  * Test a http -> https redirect shows secure icon only for redirected https
  * request.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 2);
   yield ContentTask.spawn(tab.linkedBrowser, HTTPS_REDIRECT_SJS, function* (url) {
     content.wrappedJSObject.performRequests(1, url);
   });
   yield wait;
 
-  is(gStore.getState().requests.requests.size, 2,
+  is(store.getState().requests.requests.size, 2,
      "There were two requests due to redirect.");
 
   let [
     initialSecurityIcon,
     redirectSecurityIcon,
   ] = document.querySelectorAll(".requests-security-state-icon");
 
   ok(initialSecurityIcon.classList.contains("security-state-insecure"),
--- a/devtools/client/netmonitor/test/browser_net_security-state.js
+++ b/devtools/client/netmonitor/test/browser_net_security-state.js
@@ -12,20 +12,20 @@ add_task(function* () {
   const EXPECTED_SECURITY_STATES = {
     "test1.example.com": "security-state-insecure",
     "example.com": "security-state-secure",
     "nocert.example.com": "security-state-broken",
     "localhost": "security-state-local",
   };
 
   let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   yield performRequests();
 
   for (let subitemNode of Array.from(document.querySelectorAll(
     "requests-list-column.requests-list-security-and-domain"))) {
     let domain = subitemNode.querySelector(".requests-list-domain").textContent;
 
     info("Found a request to " + domain);
@@ -81,17 +81,17 @@ add_task(function* () {
     yield done;
 
     done = waitForSecurityBrokenNetworkEvent(true);
     info("Requesting a resource over HTTP to localhost.");
     yield executeRequests(1, "http://localhost" + CORS_SJS_PATH);
     yield done;
 
     const expectedCount = Object.keys(EXPECTED_SECURITY_STATES).length;
-    is(gStore.getState().requests.requests.size,
+    is(store.getState().requests.requests.size,
       expectedCount,
       expectedCount + " events logged.");
   }
 
   /**
    * Returns a promise that's resolved once a request with security issues is
    * completed.
    */
--- a/devtools/client/netmonitor/test/browser_net_security-tab-deselect.js
+++ b/devtools/client/netmonitor/test/browser_net_security-tab-deselect.js
@@ -5,20 +5,20 @@
 
 /**
  * Test that security details tab is no longer selected if an insecure request
  * is selected.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   info("Performing requests.");
   let wait = waitForNetworkEvents(monitor, 2);
   const REQUEST_URLS = [
     "https://example.com" + CORS_SJS_PATH,
     "http://example.com" + CORS_SJS_PATH,
   ];
   yield ContentTask.spawn(tab.linkedBrowser, REQUEST_URLS, function* (urls) {
--- a/devtools/client/netmonitor/test/browser_net_security-tab-visibility.js
+++ b/devtools/client/netmonitor/test/browser_net_security-tab-visibility.js
@@ -27,22 +27,22 @@ add_task(function* () {
       isBroken: true,
       visibleOnNewEvent: false,
       visibleOnSecurityInfo: true,
       visibleOnceComplete: true,
     }
   ];
 
   let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let { getSelectedRequest } =
     windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   for (let testcase of TEST_DATA) {
     info("Testing Security tab visibility for " + testcase.desc);
     let onNewItem = monitor.panelWin.once(EVENTS.NETWORK_EVENT);
     let onSecurityInfo = monitor.panelWin.once(EVENTS.RECEIVED_SECURITY_INFO);
     let onComplete = testcase.isBroken ?
                        waitForSecurityBrokenNetworkEvent() :
                        waitForNetworkEvents(monitor, 1);
@@ -54,41 +54,41 @@ add_task(function* () {
 
     info("Waiting for new network event.");
     yield onNewItem;
 
     info("Selecting the request.");
     EventUtils.sendMouseEvent({ type: "mousedown" },
       document.querySelectorAll(".request-list-item")[0]);
 
-    is(getSelectedRequest(gStore.getState()).securityState, undefined,
+    is(getSelectedRequest(store.getState()).securityState, undefined,
        "Security state has not yet arrived.");
     is(!!document.querySelector("#security-tab"), testcase.visibleOnNewEvent,
       "Security tab is " + (testcase.visibleOnNewEvent ? "visible" : "hidden") +
       " after new request was added to the menu.");
 
     info("Waiting for security information to arrive.");
     yield onSecurityInfo;
 
-    yield waitUntil(() => !!getSelectedRequest(gStore.getState()).securityState);
-    ok(getSelectedRequest(gStore.getState()).securityState,
+    yield waitUntil(() => !!getSelectedRequest(store.getState()).securityState);
+    ok(getSelectedRequest(store.getState()).securityState,
        "Security state arrived.");
     is(!!document.querySelector("#security-tab"), testcase.visibleOnSecurityInfo,
        "Security tab is " + (testcase.visibleOnSecurityInfo ? "visible" : "hidden") +
        " after security information arrived.");
 
     info("Waiting for request to complete.");
     yield onComplete;
 
     is(!!document.querySelector("#security-tab"), testcase.visibleOnceComplete,
        "Security tab is " + (testcase.visibleOnceComplete ? "visible" : "hidden") +
        " after request has been completed.");
 
     info("Clearing requests.");
-    gStore.dispatch(Actions.clearRequests());
+    store.dispatch(Actions.clearRequests());
   }
 
   return teardown(monitor);
 
   /**
    * Returns a promise that's resolved once a request with security issues is
    * completed.
    */
--- a/devtools/client/netmonitor/test/browser_net_security-warnings.js
+++ b/devtools/client/netmonitor/test/browser_net_security-warnings.js
@@ -12,20 +12,20 @@ const TEST_CASES = [
     desc: "no warnings",
     uri: "https://example.com" + CORS_SJS_PATH,
     warnCipher: null,
   },
 ];
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   for (let test of TEST_CASES) {
     info("Testing site with " + test.desc);
 
     info("Performing request to " + test.uri);
     let wait = waitForNetworkEvents(monitor, 1);
     yield ContentTask.spawn(tab.linkedBrowser, test.uri, function* (url) {
       content.wrappedJSObject.performRequests(1, url);
@@ -45,13 +45,13 @@ add_task(function* () {
         document.querySelector("#security-tab"));
       yield wait;
     }
 
     is(document.querySelector("#security-warning-cipher"),
       test.warnCipher,
       "Cipher suite warning is hidden.");
 
-    gStore.dispatch(Actions.clearRequests());
+    store.dispatch(Actions.clearRequests());
   }
 
   return teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_send-beacon-other-tab.js
+++ b/devtools/client/netmonitor/test/browser_net_send-beacon-other-tab.js
@@ -4,36 +4,36 @@
 "use strict";
 
 /**
  * Tests if beacons from other tabs are properly ignored.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
-  let { gStore, windowRequire } = monitor.panelWin;
+  let { store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let beaconTab = yield addTab(SEND_BEACON_URL);
   info("Beacon tab added successfully.");
 
-  is(gStore.getState().requests.requests.size, 0, "The requests menu should be empty.");
+  is(store.getState().requests.requests.size, 0, "The requests menu should be empty.");
 
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(beaconTab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequest();
   });
   tab.linkedBrowser.reload();
   yield wait;
 
-  is(gStore.getState().requests.requests.size, 1, "Only the reload should be recorded.");
-  let request = getSortedRequests(gStore.getState()).get(0);
+  is(store.getState().requests.requests.size, 1, "Only the reload should be recorded.");
+  let request = getSortedRequests(store.getState()).get(0);
   is(request.method, "GET", "The method is correct.");
   is(request.status, "200", "The status is correct.");
 
   yield removeTab(beaconTab);
   return teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_send-beacon.js
+++ b/devtools/client/netmonitor/test/browser_net_send-beacon.js
@@ -4,31 +4,31 @@
 "use strict";
 
 /**
  * Tests if beacons are handled correctly.
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(SEND_BEACON_URL);
-  let { gStore, windowRequire } = monitor.panelWin;
+  let { store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let { getSortedRequests } =
     windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
-  is(gStore.getState().requests.requests.size, 0, "The requests menu should be empty.");
+  is(store.getState().requests.requests.size, 0, "The requests menu should be empty.");
 
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequest();
   });
   yield wait;
 
-  is(gStore.getState().requests.requests.size, 1, "The beacon should be recorded.");
-  let request = getSortedRequests(gStore.getState()).get(0);
+  is(store.getState().requests.requests.size, 1, "The beacon should be recorded.");
+  let request = getSortedRequests(store.getState()).get(0);
   is(request.method, "POST", "The method is correct.");
   ok(request.url.endsWith("beacon_request"), "The URL is correct.");
   is(request.status, "404", "The status is correct.");
 
   return teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_service-worker-status.js
+++ b/devtools/client/netmonitor/test/browser_net_service-worker-status.js
@@ -11,24 +11,24 @@
 const URL = EXAMPLE_URL.replace("http:", "https:");
 
 const TEST_URL = URL + "service-workers/status-codes.html";
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(TEST_URL, true);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   const REQUEST_DATA = [
     {
       method: "GET",
       uri: URL + "service-workers/test/200",
       details: {
         status: 200,
         statusText: "OK (service worker)",
@@ -49,22 +49,22 @@ add_task(function* () {
   let wait = waitForNetworkEvents(monitor, REQUEST_DATA.length);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   let index = 0;
   for (let request of REQUEST_DATA) {
-    let item = getSortedRequests(gStore.getState()).get(index);
+    let item = getSortedRequests(store.getState()).get(index);
 
     info(`Verifying request #${index}`);
     yield verifyRequestItemTarget(
       document,
-      getDisplayedRequests(gStore.getState()),
+      getDisplayedRequests(store.getState()),
       item,
       request.method,
       request.uri,
       request.details
     );
 
     let { stacktrace } = item.cause;
     let stackLen = stacktrace ? stacktrace.length : 0;
deleted file mode 100644
--- a/devtools/client/netmonitor/test/browser_net_simple-init.js
+++ /dev/null
@@ -1,93 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-/**
- * Simple check if the network monitor starts up and shuts down properly.
- */
-
-function test() {
-  // These test suite functions are removed from the global scope inside a
-  // cleanup function. However, we still need them.
-  let gInfo = info;
-  let gOk = ok;
-
-  initNetMonitor(SIMPLE_URL).then(({ tab, monitor }) => {
-    info("Starting test... ");
-
-    let { windowRequire } = monitor.panelWin;
-    let { NetMonitorController } =
-      windowRequire("devtools/client/netmonitor/src/netmonitor-controller");
-
-    is(tab.linkedBrowser.currentURI.spec, SIMPLE_URL,
-      "The current tab's location is the correct one.");
-
-    function checkIfInitialized(tag) {
-      info(`Checking if initialization is ok (${tag}).`);
-
-      ok(NetMonitorController,
-        `The network monitor controller object exists (${tag}).`);
-      ok(NetMonitorController._startup,
-        `The network monitor controller object exists and is initialized (${tag}).`);
-
-      ok(monitor.isReady,
-        `The network monitor panel appears to be ready (${tag}).`);
-
-      ok(NetMonitorController.tabClient,
-        `There should be a tabClient available at this point (${tag}).`);
-      ok(NetMonitorController.webConsoleClient,
-        `There should be a webConsoleClient available at this point (${tag}).`);
-      ok(NetMonitorController.timelineFront,
-        `There should be a timelineFront available at this point (${tag}).`);
-    }
-
-    function checkIfDestroyed(tag) {
-      gInfo("Checking if destruction is ok.");
-
-      gOk(NetMonitorController,
-        `The network monitor controller object still exists (${tag}).`);
-      gOk(NetMonitorController._shutdown,
-        `The network monitor controller object still exists and is destroyed (${tag}).`);
-
-      gOk(!NetMonitorController.tabClient,
-        `There shouldn't be a tabClient available after destruction (${tag}).`);
-      gOk(!NetMonitorController.webConsoleClient,
-        `There shouldn't be a webConsoleClient available after destruction (${tag}).`);
-      gOk(!NetMonitorController.timelineFront,
-        `There shouldn't be a timelineFront available after destruction (${tag}).`);
-    }
-
-    executeSoon(() => {
-      checkIfInitialized(1);
-
-      NetMonitorController.startupNetMonitor()
-        .then(() => {
-          info("Starting up again shouldn't do anything special.");
-          checkIfInitialized(2);
-          return NetMonitorController.connect();
-        })
-        .then(() => {
-          info("Connecting again shouldn't do anything special.");
-          checkIfInitialized(3);
-          return teardown(monitor);
-        })
-        .then(finish);
-    });
-
-    registerCleanupFunction(() => {
-      checkIfDestroyed(1);
-
-      NetMonitorController.shutdownNetMonitor()
-        .then(() => {
-          gInfo("Shutting down again shouldn't do anything special.");
-          checkIfDestroyed(2);
-          return NetMonitorController.disconnect();
-        })
-        .then(() => {
-          gInfo("Disconnecting again shouldn't do anything special.");
-          checkIfDestroyed(3);
-        });
-    });
-  });
-}
--- a/devtools/client/netmonitor/test/browser_net_simple-request-data.js
+++ b/devtools/client/netmonitor/test/browser_net_simple-request-data.js
@@ -13,40 +13,40 @@ function test() {
   // number of response headers will vary depending on the platform.
   Services.prefs.setBoolPref("network.tcp.tcp_fastopen_enable", false);
 
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
 
   initNetMonitor(SIMPLE_SJS).then(({ tab, monitor }) => {
     info("Starting test... ");
 
-    let { document, gStore, windowRequire } = monitor.panelWin;
+    let { document, store, windowRequire } = monitor.panelWin;
     let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
     let { EVENTS } = windowRequire("devtools/client/netmonitor/src/constants");
     let {
       getDisplayedRequests,
       getSelectedRequest,
       getSortedRequests,
     } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-    gStore.dispatch(Actions.batchEnable(false));
+    store.dispatch(Actions.batchEnable(false));
 
     waitForNetworkEvents(monitor, 1)
       .then(() => teardown(monitor))
       .then(finish);
 
     monitor.panelWin.once(EVENTS.NETWORK_EVENT, () => {
-      is(getSelectedRequest(gStore.getState()), null,
+      is(getSelectedRequest(store.getState()), null,
         "There shouldn't be any selected item in the requests menu.");
-      is(gStore.getState().requests.requests.size, 1,
+      is(store.getState().requests.requests.size, 1,
         "The requests menu should not be empty after the first request.");
       is(!!document.querySelector(".network-details-panel"), false,
         "The network details panel should still be hidden after first request.");
 
-      let requestItem = getSortedRequests(gStore.getState()).get(0);
+      let requestItem = getSortedRequests(store.getState()).get(0);
 
       is(typeof requestItem.id, "string",
         "The attached request id is incorrect.");
       isnot(requestItem.id, "",
         "The attached request id should not be empty.");
 
       is(typeof requestItem.startedMillis, "number",
         "The attached startedMillis is incorrect.");
@@ -84,164 +84,164 @@ function test() {
 
       is(requestItem.totalTime, undefined,
         "The totalTime should not yet be set.");
       is(requestItem.eventTimings, undefined,
         "The eventTimings should not yet be set.");
 
       verifyRequestItemTarget(
         document,
-        getDisplayedRequests(gStore.getState()),
+        getDisplayedRequests(store.getState()),
         requestItem,
         "GET",
         SIMPLE_SJS
       );
     });
 
     monitor.panelWin.once(EVENTS.RECEIVED_REQUEST_HEADERS, async () => {
       await waitUntil(() => {
-        let requestItem = getSortedRequests(gStore.getState()).get(0);
+        let requestItem = getSortedRequests(store.getState()).get(0);
         return requestItem.requestHeaders;
       });
 
-      let requestItem = getSortedRequests(gStore.getState()).get(0);
+      let requestItem = getSortedRequests(store.getState()).get(0);
 
       ok(requestItem.requestHeaders,
         "There should be a requestHeaders data available.");
       is(requestItem.requestHeaders.headers.length, 10,
         "The requestHeaders data has an incorrect |headers| property.");
       isnot(requestItem.requestHeaders.headersSize, 0,
         "The requestHeaders data has an incorrect |headersSize| property.");
       // Can't test for the exact request headers size because the value may
       // vary across platforms ("User-Agent" header differs).
 
       verifyRequestItemTarget(
         document,
-        getDisplayedRequests(gStore.getState()),
+        getDisplayedRequests(store.getState()),
         requestItem,
         "GET",
         SIMPLE_SJS
       );
     });
 
     monitor.panelWin.once(EVENTS.RECEIVED_REQUEST_COOKIES, async () => {
       await waitUntil(() => {
-        let requestItem = getSortedRequests(gStore.getState()).get(0);
+        let requestItem = getSortedRequests(store.getState()).get(0);
         return requestItem.requestCookies;
       });
 
-      let requestItem = getSortedRequests(gStore.getState()).get(0);
+      let requestItem = getSortedRequests(store.getState()).get(0);
 
       ok(requestItem.requestCookies,
         "There should be a requestCookies data available.");
       is(requestItem.requestCookies.cookies.length, 2,
         "The requestCookies data has an incorrect |cookies| property.");
 
       verifyRequestItemTarget(
         document,
-        getDisplayedRequests(gStore.getState()),
+        getDisplayedRequests(store.getState()),
         requestItem,
         "GET",
         SIMPLE_SJS
       );
     });
 
     monitor.panelWin.once(EVENTS.RECEIVED_REQUEST_POST_DATA, () => {
       ok(false, "Trap listener: this request doesn't have any post data.");
     });
 
     monitor.panelWin.once(EVENTS.RECEIVED_RESPONSE_HEADERS, async () => {
       await waitUntil(() => {
-        let requestItem = getSortedRequests(gStore.getState()).get(0);
+        let requestItem = getSortedRequests(store.getState()).get(0);
         return requestItem.responseHeaders;
       });
 
-      let requestItem = getSortedRequests(gStore.getState()).get(0);
+      let requestItem = getSortedRequests(store.getState()).get(0);
 
       ok(requestItem.responseHeaders,
         "There should be a responseHeaders data available.");
       is(requestItem.responseHeaders.headers.length, 10,
         "The responseHeaders data has an incorrect |headers| property.");
       is(requestItem.responseHeaders.headersSize, 330,
         "The responseHeaders data has an incorrect |headersSize| property.");
 
       verifyRequestItemTarget(
         document,
-        getDisplayedRequests(gStore.getState()),
+        getDisplayedRequests(store.getState()),
         requestItem,
         "GET",
         SIMPLE_SJS
       );
     });
 
     monitor.panelWin.once(EVENTS.RECEIVED_RESPONSE_COOKIES, async () => {
       await waitUntil(() => {
-        let requestItem = getSortedRequests(gStore.getState()).get(0);
+        let requestItem = getSortedRequests(store.getState()).get(0);
         return requestItem.responseCookies;
       });
 
-      let requestItem = getSortedRequests(gStore.getState()).get(0);
+      let requestItem = getSortedRequests(store.getState()).get(0);
 
       ok(requestItem.responseCookies,
         "There should be a responseCookies data available.");
       is(requestItem.responseCookies.cookies.length, 2,
         "The responseCookies data has an incorrect |cookies| property.");
 
       verifyRequestItemTarget(
         document,
-        getDisplayedRequests(gStore.getState()),
+        getDisplayedRequests(store.getState()),
         requestItem,
         "GET",
         SIMPLE_SJS
       );
     });
 
     monitor.panelWin.once(EVENTS.STARTED_RECEIVING_RESPONSE, async () => {
       await waitUntil(() => {
-        let requestItem = getSortedRequests(gStore.getState()).get(0);
+        let requestItem = getSortedRequests(store.getState()).get(0);
         return requestItem.httpVersion &&
                requestItem.status &&
                requestItem.statusText &&
                requestItem.headersSize;
       });
 
-      let requestItem = getSortedRequests(gStore.getState()).get(0);
+      let requestItem = getSortedRequests(store.getState()).get(0);
 
       is(requestItem.httpVersion, "HTTP/1.1",
         "The httpVersion data has an incorrect value.");
       is(requestItem.status, "200",
         "The status data has an incorrect value.");
       is(requestItem.statusText, "Och Aye",
         "The statusText data has an incorrect value.");
       is(requestItem.headersSize, 330,
         "The headersSize data has an incorrect value.");
 
       verifyRequestItemTarget(
         document,
-        getDisplayedRequests(gStore.getState()),
+        getDisplayedRequests(store.getState()),
         requestItem,
         "GET",
         SIMPLE_SJS,
         {
           status: "200",
           statusText: "Och Aye"
         }
       );
     });
 
     monitor.panelWin.once(EVENTS.RECEIVED_RESPONSE_CONTENT, async () => {
       await waitUntil(() => {
-        let requestItem = getSortedRequests(gStore.getState()).get(0);
+        let requestItem = getSortedRequests(store.getState()).get(0);
         return requestItem.transferredSize &&
                requestItem.contentSize &&
                requestItem.mimeType &&
                requestItem.responseContent;
       });
 
-      let requestItem = getSortedRequests(gStore.getState()).get(0);
+      let requestItem = getSortedRequests(store.getState()).get(0);
 
       is(requestItem.transferredSize, "12",
         "The transferredSize data has an incorrect value.");
       is(requestItem.contentSize, "12",
         "The contentSize data has an incorrect value.");
       is(requestItem.mimeType, "text/plain; charset=utf-8",
         "The mimeType data has an incorrect value.");
 
@@ -257,56 +257,56 @@ function test() {
         "The responseContent data has an incorrect |content.text| property.");
       // eslint-disable-next-line mozilla/no-cpows-in-tests
       is(requestItem.responseContent.content.size,
         12,
         "The responseContent data has an incorrect |content.size| property.");
 
       verifyRequestItemTarget(
         document,
-        getDisplayedRequests(gStore.getState()),
+        getDisplayedRequests(store.getState()),
         requestItem,
         "GET",
         SIMPLE_SJS,
         {
           type: "plain",
           fullMimeType: "text/plain; charset=utf-8",
           transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
           size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
         }
       );
     });
 
     monitor.panelWin.once(EVENTS.UPDATING_EVENT_TIMINGS, async () => {
       await waitUntil(() => {
-        let requestItem = getSortedRequests(gStore.getState()).get(0);
+        let requestItem = getSortedRequests(store.getState()).get(0);
         return requestItem.eventTimings;
       });
 
-      let requestItem = getSortedRequests(gStore.getState()).get(0);
+      let requestItem = getSortedRequests(store.getState()).get(0);
 
       is(typeof requestItem.totalTime, "number",
         "The attached totalTime is incorrect.");
       ok(requestItem.totalTime >= 0,
         "The attached totalTime should be positive.");
 
       verifyRequestItemTarget(
         document,
-        getDisplayedRequests(gStore.getState()),
+        getDisplayedRequests(store.getState()),
         requestItem,
         "GET",
         SIMPLE_SJS,
         {
           time: true
         }
       );
     });
 
     monitor.panelWin.once(EVENTS.RECEIVED_EVENT_TIMINGS, () => {
-      let requestItem = getSortedRequests(gStore.getState()).get(0);
+      let requestItem = getSortedRequests(store.getState()).get(0);
 
       ok(requestItem.eventTimings,
         "There should be a eventTimings data available.");
       is(typeof requestItem.eventTimings.timings.blocked, "number",
         "The eventTimings data has an incorrect |timings.blocked| property.");
       is(typeof requestItem.eventTimings.timings.dns, "number",
         "The eventTimings data has an incorrect |timings.dns| property.");
       is(typeof requestItem.eventTimings.timings.connect, "number",
@@ -317,17 +317,17 @@ function test() {
         "The eventTimings data has an incorrect |timings.wait| property.");
       is(typeof requestItem.eventTimings.timings.receive, "number",
         "The eventTimings data has an incorrect |timings.receive| property.");
       is(typeof requestItem.eventTimings.totalTime, "number",
         "The eventTimings data has an incorrect |totalTime| property.");
 
       verifyRequestItemTarget(
         document,
-        getDisplayedRequests(gStore.getState()),
+        getDisplayedRequests(store.getState()),
         requestItem,
         "GET",
         SIMPLE_SJS,
         {
           time: true
         }
       );
     });
--- a/devtools/client/netmonitor/test/browser_net_simple-request-details.js
+++ b/devtools/client/netmonitor/test/browser_net_simple-request-details.js
@@ -8,44 +8,44 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(SIMPLE_SJS);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire, NetMonitorView } = monitor.panelWin;
+  let { document, store, windowRequire, NetMonitorView } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let { EVENTS } = windowRequire("devtools/client/netmonitor/src/constants");
   let {
     getSelectedRequest,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
   let Editor = require("devtools/client/sourceeditor/editor");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
   tab.linkedBrowser.reload();
   yield wait;
 
-  is(getSelectedRequest(gStore.getState()), undefined,
+  is(getSelectedRequest(store.getState()), undefined,
     "There shouldn't be any selected item in the requests menu.");
-  is(gStore.getState().requests.requests.size, 1,
+  is(store.getState().requests.requests.size, 1,
     "The requests menu should not be empty after the first request.");
   is(!!document.querySelector(".network-details-panel"), false,
     "The network details panel should still be hidden after first request.");
 
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".network-details-panel-toggle"));
 
-  isnot(getSelectedRequest(gStore.getState()), undefined,
+  isnot(getSelectedRequest(store.getState()), undefined,
     "There should be a selected item in the requests menu.");
-  is(getSelectedIndex(gStore.getState()), 0,
+  is(getSelectedIndex(store.getState()), 0,
     "The first item should be selected in the requests menu.");
   is(!!document.querySelector(".network-details-panel"), true,
     "The network details panel should not be hidden after toggle button was pressed.");
 
   testHeadersTab();
   yield testCookiesTab();
   testParamsTab();
   yield testResponseTab();
--- a/devtools/client/netmonitor/test/browser_net_simple-request.js
+++ b/devtools/client/netmonitor/test/browser_net_simple-request.js
@@ -11,63 +11,63 @@
  * 2) Side panel toggle button
  * 3) Empty user message visibility
  * 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 { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   is(document.querySelector(".network-details-panel-toggle").hasAttribute("disabled"),
     true,
     "The pane toggle button should be disabled when the frontend is opened.");
   ok(document.querySelector(".request-list-empty-notice"),
     "An empty notice should be displayed when the frontend is opened.");
-  is(gStore.getState().requests.requests.size, 0,
+  is(store.getState().requests.requests.size, 0,
     "The requests menu should be empty when the frontend is opened.");
   is(!!document.querySelector(".network-details-panel"), false,
     "The network details panel should be hidden when the frontend is opened.");
 
   yield reloadAndWait();
 
   is(document.querySelector(".network-details-panel-toggle").hasAttribute("disabled"),
     false,
     "The pane toggle button should be enabled after the first request.");
   ok(!document.querySelector(".request-list-empty-notice"),
     "The empty notice should be hidden after the first request.");
-  is(gStore.getState().requests.requests.size, 1,
+  is(store.getState().requests.requests.size, 1,
     "The requests menu should not be empty after the first request.");
   is(!!document.querySelector(".network-details-panel"), false,
     "The network details panel should still be hidden after the first request.");
 
   yield reloadAndWait();
 
   is(document.querySelector(".network-details-panel-toggle").hasAttribute("disabled"),
     false,
     "The pane toggle button should be still be enabled after a reload.");
   ok(!document.querySelector(".request-list-empty-notice"),
     "The empty notice should be still hidden after a reload.");
-  is(gStore.getState().requests.requests.size, 1,
+  is(store.getState().requests.requests.size, 1,
     "The requests menu should not be empty after a reload.");
   is(!!document.querySelector(".network-details-panel"), false,
     "The network details panel should still be hidden after a reload.");
 
-  gStore.dispatch(Actions.clearRequests());
+  store.dispatch(Actions.clearRequests());
 
   is(document.querySelector(".network-details-panel-toggle").hasAttribute("disabled"),
     true,
     "The pane toggle button should be disabled when after clear.");
   ok(document.querySelector(".request-list-empty-notice"),
     "An empty notice should be displayed again after clear.");
-  is(gStore.getState().requests.requests.size, 0,
+  is(store.getState().requests.requests.size, 0,
     "The requests menu should be empty after clear.");
   is(!!document.querySelector(".network-details-panel"), false,
     "The network details panel should still be hidden after clear.");
 
   return teardown(monitor);
 
   function* reloadAndWait() {
     let wait = waitForNetworkEvents(monitor, 1);
--- a/devtools/client/netmonitor/test/browser_net_sort-01.js
+++ b/devtools/client/netmonitor/test/browser_net_sort-01.js
@@ -12,25 +12,25 @@ add_task(function* () {
 
   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);
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSelectedRequest,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   // Loading the frame script and preparing the xhr request URLs so we can
   // generate some requests later.
   loadCommonFrameScript();
   let requests = [{
     url: "sjs_sorting-test-server.sjs?index=1&" + Math.random(),
     method: "GET1"
   }, {
@@ -49,19 +49,19 @@ add_task(function* () {
 
   let wait = waitForNetworkEvents(monitor, 5);
   yield performRequestsInContent(requests);
   yield wait;
 
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".network-details-panel-toggle"));
 
-  isnot(getSelectedRequest(gStore.getState()), undefined,
+  isnot(getSelectedRequest(store.getState()), undefined,
     "There should be a selected item in the requests menu.");
-  is(getSelectedIndex(gStore.getState()), 0,
+  is(getSelectedIndex(store.getState()), 0,
     "The first item should be selected in the requests menu.");
   is(!!document.querySelector(".network-details-panel"), true,
     "The network details panel should be visible after toggle button was pressed.");
 
   testHeaders();
   testContents([0, 2, 4, 3, 1], 0);
 
   info("Testing status sort, ascending.");
@@ -133,99 +133,99 @@ add_task(function* () {
   function getSelectedIndex(state) {
     if (!state.requests.selectedId) {
       return -1;
     }
     return getSortedRequests(state).findIndex(r => r.id === state.requests.selectedId);
   }
 
   function testContents(order, selection) {
-    isnot(getSelectedRequest(gStore.getState()), undefined,
+    isnot(getSelectedRequest(store.getState()), undefined,
       "There should still be a selected item after sorting.");
-    is(getSelectedIndex(gStore.getState()), selection,
+    is(getSelectedIndex(store.getState()), selection,
       "The first item should be still selected after sorting.");
     is(!!document.querySelector(".network-details-panel"), true,
       "The network details panel should still be visible after sorting.");
 
-    is(getSortedRequests(gStore.getState()).length, order.length,
+    is(getSortedRequests(store.getState()).length, order.length,
       "There should be a specific number of items in the requests menu.");
-    is(getDisplayedRequests(gStore.getState()).length, order.length,
+    is(getDisplayedRequests(store.getState()).length, order.length,
       "There should be a specific number of visbile items in the requests menu.");
     is(document.querySelectorAll(".request-list-item").length, order.length,
       "The visible items in the requests menu are, in fact, visible!");
 
     for (let i = 0, len = order.length / 5; i < len; i++) {
       verifyRequestItemTarget(
         document,
-        getDisplayedRequests(gStore.getState()),
-        getSortedRequests(gStore.getState()).get(order[i]),
+        getDisplayedRequests(store.getState()),
+        getSortedRequests(store.getState()).get(order[i]),
         "GET1", SORTING_SJS + "?index=1", {
           fuzzyUrl: true,
           status: 101,
           statusText: "Meh",
           type: "1",
           fullMimeType: "text/1",
           transferred: L10N.getStr("networkMenu.sizeUnavailable"),
           size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0),
           time: true
         });
     }
     for (let i = 0, len = order.length / 5; i < len; i++) {
       verifyRequestItemTarget(
         document,
-        getDisplayedRequests(gStore.getState()),
-        getSortedRequests(gStore.getState()).get(order[i + len]),
+        getDisplayedRequests(store.getState()),
+        getSortedRequests(store.getState()).get(order[i + len]),
         "GET2", SORTING_SJS + "?index=2", {
           fuzzyUrl: true,
           status: 200,
           statusText: "Meh",
           type: "2",
           fullMimeType: "text/2",
           transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 19),
           size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 19),
           time: true
         });
     }
     for (let i = 0, len = order.length / 5; i < len; i++) {
       verifyRequestItemTarget(
         document,
-        getDisplayedRequests(gStore.getState()),
-        getSortedRequests(gStore.getState()).get(order[i + len * 2]),
+        getDisplayedRequests(store.getState()),
+        getSortedRequests(store.getState()).get(order[i + len * 2]),
         "GET3", SORTING_SJS + "?index=3", {
           fuzzyUrl: true,
           status: 300,
           statusText: "Meh",
           type: "3",
           fullMimeType: "text/3",
           transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29),
           size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29),
           time: true
         });
     }
     for (let i = 0, len = order.length / 5; i < len; i++) {
       verifyRequestItemTarget(
         document,
-        getDisplayedRequests(gStore.getState()),
-        getSortedRequests(gStore.getState()).get(order[i + len * 3]),
+        getDisplayedRequests(store.getState()),
+        getSortedRequests(store.getState()).get(order[i + len * 3]),
         "GET4", SORTING_SJS + "?index=4", {
           fuzzyUrl: true,
           status: 400,
           statusText: "Meh",
           type: "4",
           fullMimeType: "text/4",
           transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 39),
           size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 39),
           time: true
         });
     }
     for (let i = 0, len = order.length / 5; i < len; i++) {
       verifyRequestItemTarget(
         document,
-        getDisplayedRequests(gStore.getState()),
-        getSortedRequests(gStore.getState()).get(order[i + len * 4]),
+        getDisplayedRequests(store.getState()),
+        getSortedRequests(store.getState()).get(order[i + len * 4]),
         "GET5", SORTING_SJS + "?index=5", {
           fuzzyUrl: true,
           status: 500,
           statusText: "Meh",
           type: "5",
           fullMimeType: "text/5",
           transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 49),
           size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 49),
--- a/devtools/client/netmonitor/test/browser_net_sort-02.js
+++ b/devtools/client/netmonitor/test/browser_net_sort-02.js
@@ -12,25 +12,25 @@ add_task(function* () {
 
   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);
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSelectedRequest,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   // Loading the frame script and preparing the xhr request URLs so we can
   // generate some requests later.
   loadCommonFrameScript();
   let requests = [{
     url: "sjs_sorting-test-server.sjs?index=1&" + Math.random(),
     method: "GET1"
   }, {
@@ -49,19 +49,19 @@ add_task(function* () {
 
   let wait = waitForNetworkEvents(monitor, 5);
   yield performRequestsInContent(requests);
   yield wait;
 
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".network-details-panel-toggle"));
 
-  isnot(getSelectedRequest(gStore.getState()), undefined,
+  isnot(getSelectedRequest(store.getState()), undefined,
     "There should be a selected item in the requests menu.");
-  is(getSelectedIndex(gStore.getState()), 0,
+  is(getSelectedIndex(store.getState()), 0,
     "The first item should be selected in the requests menu.");
   is(!!document.querySelector(".network-details-panel"), true,
     "The network details panel should be visible after toggle button was pressed.");
 
   testHeaders();
   testContents([0, 2, 4, 3, 1]);
 
   info("Testing status sort, ascending.");
@@ -217,90 +217,90 @@ add_task(function* () {
           ? L10N.getStr("networkMenu.sortedAsc")
           : L10N.getStr("networkMenu.sortedDesc"),
           "The " + header.id + " header has a correct 'title' attribute.");
       }
     }
   }
 
   function testContents([a, b, c, d, e]) {
-    isnot(getSelectedRequest(gStore.getState()), undefined,
+    isnot(getSelectedRequest(store.getState()), undefined,
       "There should still be a selected item after sorting.");
-    is(getSelectedIndex(gStore.getState()), a,
+    is(getSelectedIndex(store.getState()), a,
       "The first item should be still selected after sorting.");
     is(!!document.querySelector(".network-details-panel"), true,
       "The network details panel should still be visible after sorting.");
 
-    is(getSortedRequests(gStore.getState()).length, 5,
+    is(getSortedRequests(store.getState()).length, 5,
       "There should be a total of 5 items in the requests menu.");
-    is(getDisplayedRequests(gStore.getState()).length, 5,
+    is(getDisplayedRequests(store.getState()).length, 5,
       "There should be a total of 5 visible items in the requests menu.");
     is(document.querySelectorAll(".request-list-item").length, 5,
       "The visible items in the requests menu are, in fact, visible!");
 
     verifyRequestItemTarget(
       document,
-      getDisplayedRequests(gStore.getState()),
-      getSortedRequests(gStore.getState()).get(a),
+      getDisplayedRequests(store.getState()),
+      getSortedRequests(store.getState()).get(a),
       "GET1", SORTING_SJS + "?index=1", {
         fuzzyUrl: true,
         status: 101,
         statusText: "Meh",
         type: "1",
         fullMimeType: "text/1",
         transferred: L10N.getStr("networkMenu.sizeUnavailable"),
         size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0),
         time: true
       });
     verifyRequestItemTarget(
       document,
-      getDisplayedRequests(gStore.getState()),
-      getSortedRequests(gStore.getState()).get(b),
+      getDisplayedRequests(store.getState()),
+      getSortedRequests(store.getState()).get(b),
       "GET2", SORTING_SJS + "?index=2", {
         fuzzyUrl: true,
         status: 200,
         statusText: "Meh",
         type: "2",
         fullMimeType: "text/2",
         transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 19),
         size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 19),
         time: true
       });
     verifyRequestItemTarget(
       document,
-      getDisplayedRequests(gStore.getState()),
-      getSortedRequests(gStore.getState()).get(c),
+      getDisplayedRequests(store.getState()),
+      getSortedRequests(store.getState()).get(c),
       "GET3", SORTING_SJS + "?index=3", {
         fuzzyUrl: true,
         status: 300,
         statusText: "Meh",
         type: "3",
         fullMimeType: "text/3",
         transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29),
         size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29),
         time: true
       });
     verifyRequestItemTarget(
       document,
-      getDisplayedRequests(gStore.getState()),
-      getSortedRequests(gStore.getState()).get(d),
+      getDisplayedRequests(store.getState()),
+      getSortedRequests(store.getState()).get(d),
       "GET4", SORTING_SJS + "?index=4", {
         fuzzyUrl: true,
         status: 400,
         statusText: "Meh",
         type: "4",
         fullMimeType: "text/4",
         transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 39),
         size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 39),
         time: true
       });
     verifyRequestItemTarget(
       document,
-      getDisplayedRequests(gStore.getState()),
-      getSortedRequests(gStore.getState()).get(e),
+      getDisplayedRequests(store.getState()),
+      getSortedRequests(store.getState()).get(e),
       "GET5", SORTING_SJS + "?index=5", {
         fuzzyUrl: true,
         status: 500,
         statusText: "Meh",
         type: "5",
         fullMimeType: "text/5",
         transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 49),
         size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 49),
--- a/devtools/client/netmonitor/test/browser_net_statistics-01.js
+++ b/devtools/client/netmonitor/test/browser_net_statistics-01.js
@@ -7,24 +7,24 @@
  * Tests if the statistics panel displays correctly.
  */
 
 add_task(function* () {
   let { monitor } = yield initNetMonitor(STATISTICS_URL);
   info("Starting test... ");
 
   let panel = monitor.panelWin;
-  let { document, gStore, windowRequire } = panel;
+  let { document, store, windowRequire } = panel;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
   ok(document.querySelector(".monitor-panel"),
     "The current main panel is correct.");
 
   info("Displaying statistics panel");
-  gStore.dispatch(Actions.openStatistics(true));
+  store.dispatch(Actions.openStatistics(true));
 
   ok(document.querySelector(".statistics-panel"),
     "The current main panel is correct.");
 
   info("Waiting for placeholder to display");
 
   yield waitUntil(
     () => document.querySelectorAll(".pie-chart-container[placeholder=true]")
--- a/devtools/client/netmonitor/test/browser_net_statistics-02.js
+++ b/devtools/client/netmonitor/test/browser_net_statistics-02.js
@@ -8,33 +8,33 @@
  * the performance analysis view.
  */
 
 add_task(function* () {
   let { monitor } = yield initNetMonitor(FILTERING_URL);
   info("Starting test... ");
 
   let panel = monitor.panelWin;
-  let { document, gStore, windowRequire } = panel;
+  let { document, store, windowRequire } = panel;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-html-button"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-css-button"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-js-button"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-ws-button"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-other-button"));
   testFilterButtonsCustom(monitor, [0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1]);
   info("The correct filtering predicates are used before entering perf. analysis mode.");
 
-  gStore.dispatch(Actions.openStatistics(true));
+  store.dispatch(Actions.openStatistics(true));
 
   ok(document.querySelector(".statistics-panel"),
     "The main panel is switched to the statistics panel.");
 
   yield waitUntil(
     () => document.querySelectorAll(".pie-chart-container:not([placeholder=true])")
                   .length == 2);
   ok(true, "Two real pie charts appear to be rendered correctly.");
--- a/devtools/client/netmonitor/test/browser_net_status-codes.js
+++ b/devtools/client/netmonitor/test/browser_net_status-codes.js
@@ -9,24 +9,24 @@
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
 
   let { tab, monitor } = yield initNetMonitor(STATUS_CODES_URL);
 
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let requestItems = [];
 
   const REQUEST_DATA = [
     {
       // request #0
       method: "GET",
       uri: STATUS_CODES_SJS + "?sts=100",
@@ -109,23 +109,23 @@ add_task(function* () {
   /**
    * A helper that verifies all requests show the correct information and caches
    * request list items to requestItems array.
    */
   function* verifyRequests() {
     info("Verifying requests contain correct information.");
     let index = 0;
     for (let request of REQUEST_DATA) {
-      let item = getSortedRequests(gStore.getState()).get(index);
+      let item = getSortedRequests(store.getState()).get(index);
       requestItems[index] = item;
 
       info("Verifying request #" + index);
       yield verifyRequestItemTarget(
         document,
-        getDisplayedRequests(gStore.getState()),
+        getDisplayedRequests(store.getState()),
         item,
         request.method,
         request.uri,
         request.details
       );
 
       index++;
     }
--- a/devtools/client/netmonitor/test/browser_net_streaming-response.js
+++ b/devtools/client/netmonitor/test/browser_net_streaming-response.js
@@ -7,24 +7,24 @@
  * Tests if reponses from streaming content types (MPEG-DASH, HLS) are
  * displayed as XML or plain text
  */
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
 
   info("Starting test... ");
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   const REQUESTS = [
     [ "hls-m3u8", /^#EXTM3U/ ],
     [ "mpeg-dash", /^<\?xml/ ]
   ];
 
   let wait = waitForNetworkEvents(monitor, REQUESTS.length);
   for (let [fmt] of REQUESTS) {
@@ -33,34 +33,34 @@ add_task(function* () {
       content.wrappedJSObject.performRequests(1, args.url);
     });
   }
   yield wait;
 
   REQUESTS.forEach(([ fmt ], i) => {
     verifyRequestItemTarget(
       document,
-      getDisplayedRequests(gStore.getState()),
-      getSortedRequests(gStore.getState()).get(i),
+      getDisplayedRequests(store.getState()),
+      getSortedRequests(store.getState()).get(i),
       "GET",
       CONTENT_TYPE_SJS + "?fmt=" + fmt,
       {
         status: 200,
         statusText: "OK"
       });
   });
 
   wait = waitForDOM(document, "#response-panel");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".network-details-panel-toggle"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#response-tab"));
   yield wait;
 
-  gStore.dispatch(Actions.selectRequest(null));
+  store.dispatch(Actions.selectRequest(null));
 
   yield selectIndexAndWaitForSourceEditor(0);
   // the hls-m3u8 part
   testEditorContent(REQUESTS[0]);
 
   yield selectIndexAndWaitForSourceEditor(1);
   // the mpeg-dash part
   testEditorContent(REQUESTS[1]);
--- a/devtools/client/netmonitor/test/browser_net_throttle.js
+++ b/devtools/client/netmonitor/test/browser_net_throttle.js
@@ -9,21 +9,21 @@ add_task(function* () {
   yield throttleTest(true);
   yield throttleTest(false);
 });
 
 function* throttleTest(actuallyThrottle) {
   requestLongerTimeout(2);
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
-  let { gStore, windowRequire } = monitor.panelWin;
+  let { store, windowRequire } = monitor.panelWin;
   let { ACTIVITY_TYPE } = windowRequire("devtools/client/netmonitor/src/constants");
   let { EVENTS } = windowRequire("devtools/client/netmonitor/src/constants");
-  let { NetMonitorController } =
-    windowRequire("devtools/client/netmonitor/src/netmonitor-controller");
+  let { setPreferences, triggerActivity } =
+    windowRequire("devtools/client/netmonitor/src/connector/index");
   let {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
   info("Starting test... (actuallyThrottle = " + actuallyThrottle + ")");
 
   // When throttling, must be smaller than the length of the content
   // of SIMPLE_URL in bytes.
@@ -34,30 +34,29 @@ function* throttleTest(actuallyThrottle)
       latencyMean: 0,
       latencyMax: 0,
       downloadBPSMean: size,
       downloadBPSMax: size,
       uploadBPSMean: 10000,
       uploadBPSMax: 10000,
     },
   };
-  let client = NetMonitorController.webConsoleClient;
 
   info("sending throttle request");
   yield new Promise((resolve) => {
-    client.setPreferences(request, response => {
+    setPreferences(request, response => {
       resolve(response);
     });
   });
 
   let eventPromise = monitor.panelWin.once(EVENTS.RECEIVED_EVENT_TIMINGS);
-  yield NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_DISABLED);
+  yield triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_DISABLED);
   yield eventPromise;
 
-  let requestItem = getSortedRequests(gStore.getState()).get(0);
+  let requestItem = getSortedRequests(store.getState()).get(0);
   const reportedOneSecond = requestItem.eventTimings.timings.receive > 1000;
   if (actuallyThrottle) {
     ok(reportedOneSecond, "download reported as taking more than one second");
   } else {
     ok(!reportedOneSecond, "download reported as taking less than one second");
   }
 
   yield teardown(monitor);
--- a/devtools/client/netmonitor/test/browser_net_timing-division.js
+++ b/devtools/client/netmonitor/test/browser_net_timing-division.js
@@ -13,23 +13,23 @@ add_task(function* () {
   Services.prefs.setCharPref(
     "devtools.netmonitor.hiddenColumns",
     "[\"file\",\"protocol\",\"remoteip\"]"
   );
 
   let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
   info("Starting test... ");
 
-  let { document, gStore, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 2);
   // Timeout needed for having enough divisions on the time scale.
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests(2, null, 3000);
   });
   yield wait;
 
@@ -43,21 +43,21 @@ add_task(function* () {
   info("Number of millisecond divisions: " + milDivs.length);
   info("Number of second divisions: " + secDivs.length);
   info("Number of minute divisions: " + minDivs.length);
 
   milDivs.forEach(div => info(`Millisecond division: ${div.textContent}`));
   secDivs.forEach(div => info(`Second division: ${div.textContent}`));
   minDivs.forEach(div => info(`Minute division: ${div.textContent}`));
 
-  is(gStore.getState().requests.requests.size, 2,
+  is(store.getState().requests.requests.size, 2,
      "There should be only two requests made.");
 
-  let firstRequest = getSortedRequests(gStore.getState()).get(0);
-  let lastRequest = getSortedRequests(gStore.getState()).get(1);
+  let firstRequest = getSortedRequests(store.getState()).get(0);
+  let lastRequest = getSortedRequests(store.getState()).get(1);
 
   info("First request happened at: " +
        firstRequest.responseHeaders.headers.find(e => e.name == "date").value);
   info("Last request happened at: " +
        lastRequest.responseHeaders.headers.find(e => e.name == "date").value);
 
   ok(secDivs.length,
      "There should be at least one division on the seconds time scale.");
--- a/devtools/client/netmonitor/test/browser_net_truncate.js
+++ b/devtools/client/netmonitor/test/browser_net_truncate.js
@@ -14,35 +14,35 @@ function test() {
   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 { document, store, windowRequire } = monitor.panelWin;
     let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
     let { EVENTS } = windowRequire("devtools/client/netmonitor/src/constants");
     let {
       getDisplayedRequests,
       getSortedRequests,
     } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-    gStore.dispatch(Actions.batchEnable(false));
+    store.dispatch(Actions.batchEnable(false));
 
     waitForNetworkEvents(monitor, 1)
       .then(() => teardown(monitor))
       .then(finish);
 
     monitor.panelWin.once(EVENTS.RECEIVED_RESPONSE_CONTENT, () => {
       verifyRequestItemTarget(
         document,
-        getDisplayedRequests(gStore.getState()),
-        getSortedRequests(gStore.getState()).get(0),
+        getDisplayedRequests(store.getState()),
+        getSortedRequests(store.getState()).get(0),
         "GET", URL,
         {
           type: "plain",
           fullMimeType: "text/plain; charset=utf-8",
           transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeMB", 2),
           size: L10N.getFormatStrWithNumbers("networkMenu.sizeMB", 2),
         }
       );
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -162,32 +162,32 @@ function waitForTimelineMarkers(monitor)
  * - the request start and end times are overlapping. If a new request starts a moment
  *   after the previous one was finished, the wait will be ended in the "interim"
  *   period.
  * @returns a promise that resolves when the wait is done.
  */
 function waitForAllRequestsFinished(monitor) {
   let window = monitor.panelWin;
   let { windowRequire } = window;
-  let { NetMonitorController } =
-    windowRequire("devtools/client/netmonitor/src/netmonitor-controller");
+  let { getNetworkRequest } =
+    windowRequire("devtools/client/netmonitor/src/connector/index");
 
   return new Promise(resolve => {
     // Key is the request id, value is a boolean - is request finished or not?
     let requests = new Map();
 
     function onRequest(_, id) {
-      let networkInfo = NetMonitorController.webConsoleClient.getNetworkRequest(id);
+      let networkInfo = getNetworkRequest(id);
       let { url } = networkInfo.request;
       info(`Request ${id} for ${url} not yet done, keep waiting...`);
       requests.set(id, false);
     }
 
     function onTimings(_, id) {
-      let networkInfo = NetMonitorController.webConsoleClient.getNetworkRequest(id);
+      let networkInfo = getNetworkRequest(id);
       let { url } = networkInfo.request;
       info(`Request ${id} for ${url} done`);
       requests.set(id, true);
       maybeResolve();
     }
 
     function maybeResolve() {
       // Have all the requests in the map finished yet?
@@ -221,32 +221,34 @@ function initNetMonitor(url, enableCache
 
     let toolbox = yield gDevTools.showToolbox(target, "netmonitor");
     info("Network monitor pane shown successfully.");
 
     let monitor = toolbox.getCurrentPanel();
 
     if (!enableCache) {
       let panel = monitor.panelWin;
-      let { gStore, windowRequire } = panel;
+      let { store, windowRequire } = panel;
+      let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
+
       info("Disabling cache and reloading page.");
       let requestsDone = waitForAllRequestsFinished(monitor);
       let markersDone = waitForTimelineMarkers(monitor);
       yield toggleCache(target, true);
       yield Promise.all([requestsDone, markersDone]);
       info("Cache disabled when the current and all future toolboxes are open.");
       // Remove any requests generated by the reload while toggling the cache to
       // avoid interfering with the test.
       isnot([...target.activeConsole.getNetworkEvents()].length, 0,
          "Request to reconfigure the tab was recorded.");
       info("Clearing requests in the console client.");
       target.activeConsole.clearNetworkRequests();
       info("Clearing requests in the UI.");
-      let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
-      gStore.dispatch(Actions.clearRequests());
+
+      store.dispatch(Actions.clearRequests());
     }
 
     return {tab, monitor};
   });
 }
 
 function restartNetMonitor(monitor, newUrl) {
   info("Restarting the specified network monitor.");
@@ -274,18 +276,18 @@ function teardown(monitor) {
     yield onDestroyed;
   });
 }
 
 function waitForNetworkEvents(monitor, getRequests, postRequests = 0) {
   return new Promise((resolve) => {
     let panel = monitor.panelWin;
     let { windowRequire } = panel;
-    let { NetMonitorController } =
-      windowRequire("devtools/client/netmonitor/src/netmonitor-controller");
+    let { getNetworkRequest } =
+      windowRequire("devtools/client/netmonitor/src/connector/index");
     let progress = {};
     let genericEvents = 0;
     let postEvents = 0;
     let awaitedEventsToListeners = [
       ["UPDATING_REQUEST_HEADERS", onGenericEvent],
       ["RECEIVED_REQUEST_HEADERS", onGenericEvent],
       ["UPDATING_REQUEST_COOKIES", onGenericEvent],
       ["RECEIVED_REQUEST_COOKIES", onGenericEvent],
@@ -313,28 +315,28 @@ function waitForNetworkEvents(monitor, g
     }
 
     function updateProgressForURL(url, event) {
       initProgressForURL(url);
       progress[url][Object.keys(EVENTS).find(e => EVENTS[e] == event)] = 1;
     }
 
     function onGenericEvent(event, actor) {
-      let networkInfo = NetMonitorController.webConsoleClient.getNetworkRequest(actor);
+      let networkInfo = getNetworkRequest(actor);
       if (!networkInfo) {
         // Must have been related to reloading document to disable cache.
         // Ignore the event.
         return;
       }
       genericEvents++;
       maybeResolve(event, actor, networkInfo);
     }
 
     function onPostEvent(event, actor) {
-      let networkInfo = NetMonitorController.webConsoleClient.getNetworkRequest(actor);
+      let networkInfo = getNetworkRequest(actor);
       if (!networkInfo) {
         // Must have been related to reloading document to disable cache.
         // Ignore the event.
         return;
       }
       postEvents++;
       maybeResolve(event, actor, networkInfo);
     }
--- a/devtools/client/styleeditor/test/browser_styleeditor_fetch-from-cache.js
+++ b/devtools/client/styleeditor/test/browser_styleeditor_fetch-from-cache.js
@@ -9,36 +9,36 @@
 const TEST_URL = TEST_BASE_HTTP + "doc_uncached.html";
 
 add_task(function* () {
   info("Opening netmonitor");
   let tab = yield addTab("about:blank");
   let target = TargetFactory.forTab(tab);
   let toolbox = yield gDevTools.showToolbox(target, "netmonitor");
   let monitor = toolbox.getPanel("netmonitor");
-  let { gStore, windowRequire } = monitor.panelWin;
+  let { store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
 
   info("Navigating to test page");
   yield navigateTo(TEST_URL);
 
   info("Opening Style Editor");
   let styleeditor = yield toolbox.selectTool("styleeditor");
 
   info("Waiting for the source to be loaded.");
   yield styleeditor.UI.editors[0].getSourceEditor();
 
   info("Checking Netmonitor contents.");
   let items = [];
-  for (let item of getSortedRequests(gStore.getState())) {
+  for (let item of getSortedRequests(store.getState())) {
     if (item.url.endsWith("doc_uncached.css")) {
       items.push(item);
     }
   }
 
   is(items.length, 2,
      "Got two requests for doc_uncached.css after Style Editor was loaded.");
   ok(items[1].fromCache,
--- a/devtools/client/themes/commandline-browser.css
+++ b/devtools/client/themes/commandline-browser.css
@@ -117,17 +117,23 @@ html|*#gcli-output-frame {
   border-width: 0;
   background-color: transparent;
 }
 
 #gcli-output,
 #gcli-tooltip {
   border-width: 0;
   background-color: transparent;
-  -moz-appearance: none; appearance: none;
+  -moz-appearance: none;
+  appearance: none;
+
+  /* We always wanted this to be a percentage of the viewport size but width: x% does not
+   * work. vw is the obvious solution.
+   */
+  width: 80vw;
 }
 
 .gclitoolbar-input-node,
 .gclitoolbar-complete-node {
   margin: 0;
   -moz-box-align: center;
   padding-top: 0;
   padding-bottom: 0;
--- a/devtools/client/themes/commandline.css
+++ b/devtools/client/themes/commandline.css
@@ -1,12 +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/. */
 
+:root {
+  overflow-x: hidden;
+}
 
 /* NOTE: THESE NEED TO STAY IN SYNC WITH LIGHT-THEME.CSS AND DARK-THEME.CSS.
    We are copy/pasting variables from light-theme and dark-theme,
    since they aren't loaded in this context (within commandlineoutput.xhtml
    and commandlinetooltip.xhtml). */
 :root[devtoolstheme="light"] {
   --gcli-background-color: #fcfcfc; /* --theme-tab-toolbar-background */
   --gcli-input-focused-background: #ffffff; /* --theme-sidebar-background */
@@ -38,26 +41,28 @@
   padding: 5px 10px;
   border-bottom-left-radius: 0;
   border-bottom-right-radius: 0;
   border-bottom: 0;
 }
 
 #gcli-tooltip-root {
   padding: 5px 0px;
+  overflow-x: hidden;
 }
 
 #gcli-tooltip-connector {
   margin-top: -1px;
   margin-left: 8px;
-  width: 20px;
-  height: 10px;
-  border-left: 1px solid var(--gcli-border-color);
-  border-right: 1px solid var(--gcli-border-color);
-  background-color: var(--gcli-background-color);
+  width: 0;
+  height: 0;
+  border-left: 10px solid transparent;
+  border-right: 10px solid transparent;
+  background-color: transparent;
+  border-top: 10px solid var(--gcli-background-color);
 }
 
 .gcli-tt-description,
 .gcli-tt-error {
   padding: 0 10px;
 }
 
 .gcli-row-out {
rename from browser/base/content/gcli_sec_bad.svg
rename to devtools/client/themes/images/gcli_sec_bad.svg
rename from browser/base/content/gcli_sec_good.svg
rename to devtools/client/themes/images/gcli_sec_good.svg
rename from browser/base/content/gcli_sec_moderate.svg
rename to devtools/client/themes/images/gcli_sec_moderate.svg
--- a/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
+++ b/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
@@ -83,18 +83,20 @@ NewConsoleOutputWrapper.prototype = {
           frame.url,
           frame.line
         ),
         onViewSourceInStyleEditor: frame => this.toolbox.viewSourceInStyleEditor(
           frame.url,
           frame.line
         ),
         openNetworkPanel: (requestId) => {
-          return this.toolbox.selectTool("netmonitor").then(panel => {
-            return panel.panelWin.NetMonitorController.inspectRequest(requestId);
+          return this.toolbox.selectTool("netmonitor").then((panel) => {
+            let { inspectRequest } = panel.panelWin.windowRequire(
+              "devtools/client/netmonitor/src/connector/index");
+            return inspectRequest(requestId);
           });
         },
         sourceMapService: this.toolbox ? this.toolbox.sourceMapURLService : null,
         highlightDomElement: (grip, options = {}) => {
           return this.toolbox.highlighterUtils
             ? this.toolbox.highlighterUtils.highlightDomValueGrip(grip, options)
             : null;
         },
--- a/devtools/client/webconsole/test/browser_netmonitor_shows_reqs_in_webconsole.js
+++ b/devtools/client/webconsole/test/browser_netmonitor_shows_reqs_in_webconsole.js
@@ -41,37 +41,39 @@ add_task(function* () {
     ]
   });
 
   // Test that the request appears in the network panel.
   let target = TargetFactory.forTab(tab);
   let toolbox = yield gDevTools.showToolbox(target, "netmonitor");
   info("Network panel is open.");
 
-  testNetmonitor(toolbox);
+  yield testNetmonitor(toolbox);
 });
 
 function loadDocument(browser) {
   let deferred = promise.defer();
 
   browser.addEventListener("load", function () {
     deferred.resolve();
   }, {capture: true, once: true});
   BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_PATH);
 
   return deferred.promise;
 }
 
-function testNetmonitor(toolbox) {
+function* testNetmonitor(toolbox) {
   let monitor = toolbox.getCurrentPanel();
 
-  let { gStore, windowRequire } = monitor.panelWin;
+  let { store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let { getSortedRequests } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  gStore.dispatch(Actions.batchEnable(false));
+  store.dispatch(Actions.batchEnable(false));
+
+  yield waitUntil(() => store.getState().requests.requests.size > 0);
 
-  is(gStore.getState().requests.requests.size, 1, "Network request appears in the network panel");
+  is(store.getState().requests.requests.size, 1, "Network request appears in the network panel");
 
-  let item = getSortedRequests(gStore.getState()).get(0);
+  let item = getSortedRequests(store.getState()).get(0);
   is(item.method, "GET", "The attached method is correct.");
   is(item.url, TEST_PATH, "The attached url is correct.");
 }
--- a/devtools/client/webconsole/test/browser_webconsole_netlogging_panel.js
+++ b/devtools/client/webconsole/test/browser_webconsole_netlogging_panel.js
@@ -18,20 +18,17 @@ add_task(function* () {
   const hud = yield loadPageAndGetHud(TEST_NETWORK_REQUEST_URI);
   let request = yield finishedRequest;
 
   yield hud.ui.openNetworkPanel(request.actor);
   let toolbox = gDevTools.getToolbox(hud.target);
   is(toolbox.currentToolId, "netmonitor", "Network panel was opened");
   let panel = toolbox.getCurrentPanel();
 
-  let { gStore, windowRequire } = panel.panelWin;
-  let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
-  let { NetMonitorController } =
-    windowRequire("devtools/client/netmonitor/src/netmonitor-controller");
+  let { store, windowRequire } = panel.panelWin;
   let { getSelectedRequest } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  let selected = getSelectedRequest(gStore.getState());
+  let selected = getSelectedRequest(store.getState());
   is(selected.method, request.request.method,
      "The correct request is selected");
   is(selected.url, request.request.url,
      "The correct request is definitely selected");
 });
--- a/devtools/client/webconsole/test/browser_webconsole_netlogging_reset_filter.js
+++ b/devtools/client/webconsole/test/browser_webconsole_netlogging_reset_filter.js
@@ -33,34 +33,34 @@ add_task(function* () {
   let htmlRequest = requests.find(e => e.request.url.endsWith("html"));
   ok(htmlRequest, "htmlRequest was a html");
 
   yield hud.ui.openNetworkPanel(htmlRequest.actor);
   let toolbox = gDevTools.getToolbox(hud.target);
   is(toolbox.currentToolId, "netmonitor", "Network panel was opened");
 
   let panel = toolbox.getCurrentPanel();
-  let { gStore, windowRequire } = panel.panelWin;
+  let { store, windowRequire } = panel.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let { getSelectedRequest } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  let selected = getSelectedRequest(gStore.getState());
+  let selected = getSelectedRequest(store.getState());
   is(selected.method, htmlRequest.request.method,
      "The correct request is selected");
   is(selected.url, htmlRequest.request.url,
      "The correct request is definitely selected");
 
   // Filter out the HTML request.
-  gStore.dispatch(Actions.toggleRequestFilterType("js"));
+  store.dispatch(Actions.toggleRequestFilterType("js"));
 
   yield toolbox.selectTool("webconsole");
   is(toolbox.currentToolId, "webconsole", "Web console was selected");
   yield hud.ui.openNetworkPanel(htmlRequest.actor);
 
-  selected = getSelectedRequest(gStore.getState());
+  selected = getSelectedRequest(store.getState());
   is(selected.method, htmlRequest.request.method,
      "The correct request is selected");
   is(selected.url, htmlRequest.request.url,
      "The correct request is definitely selected");
 
   // All tests are done. Shutdown.
   HUDService.lastFinishedRequest.callback = null;
   htmlRequest = browser = requests = hud = null;
--- a/devtools/client/webconsole/test/browser_webconsole_shows_reqs_in_netmonitor.js
+++ b/devtools/client/webconsole/test/browser_webconsole_shows_reqs_in_netmonitor.js
@@ -57,19 +57,19 @@ function loadDocument(browser) {
   }, {capture: true, once: true});
   BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_PATH);
 
   return deferred.promise;
 }
 
 function* testNetmonitor(toolbox) {
   let monitor = toolbox.getCurrentPanel();
-  let { gStore, windowRequire } = monitor.panelWin;
+  let { store, windowRequire } = monitor.panelWin;
   let { getSortedRequests } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
-  yield waitUntil(() => gStore.getState().requests.requests.size > 0);
+  yield waitUntil(() => store.getState().requests.requests.size > 0);
 
-  is(gStore.getState().requests.requests.size, 1, "Network request appears in the network panel");
+  is(store.getState().requests.requests.size, 1, "Network request appears in the network panel");
 
-  let item = getSortedRequests(gStore.getState()).get(0);
+  let item = getSortedRequests(store.getState()).get(0);
   is(item.method, "GET", "The request method is correct.");
   is(item.url, TEST_PATH, "The request url is correct.");
 }
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -1932,19 +1932,19 @@ WebConsoleFrame.prototype = {
    */
   openNetworkPanel: function (requestId) {
     let toolbox = gDevTools.getToolbox(this.owner.target);
     // The browser console doesn't have a toolbox.
     if (!toolbox) {
       return;
     }
     return toolbox.selectTool("netmonitor").then(panel => {
-      let { NetMonitorController } = panel.panelWin
-        .windowRequire("devtools/client/netmonitor/src/netmonitor-controller");
-      return NetMonitorController.inspectRequest(requestId);
+      let { inspectRequest } = panel.panelWin.windowRequire(
+        "devtools/client/netmonitor/src/connector/index");
+      return inspectRequest(requestId);
     });
   },
 
   /**
    * Handler for page location changes.
    *
    * @param string uri
    *        New page location.
deleted file mode 100644
--- a/devtools/server/content-globals.js
+++ /dev/null
@@ -1,48 +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";
-
-const { Ci } = require("chrome");
-const Services = require("Services");
-
-var globalsCache = {};
-
-exports.addContentGlobal = function (options) {
-  if (!options || !options.global || !options["inner-window-id"]) {
-    throw Error("Invalid arguments");
-  }
-  let cache = getGlobalCache(options["inner-window-id"]);
-  cache.push(options.global);
-  return undefined;
-};
-
-exports.getContentGlobals = function (options) {
-  if (!options || !options["inner-window-id"]) {
-    throw Error("Invalid arguments");
-  }
-  return Array.slice(globalsCache[options["inner-window-id"]] || []);
-};
-
-exports.removeContentGlobal = function (options) {
-  if (!options || !options.global || !options["inner-window-id"]) {
-    throw Error("Invalid arguments");
-  }
-  let cache = getGlobalCache(options["inner-window-id"]);
-  let index = cache.indexOf(options.global);
-  cache.splice(index, 1);
-  return undefined;
-};
-
-function getGlobalCache(innerWindowID) {
-  globalsCache[innerWindowID] = globalsCache[innerWindowID] || [];
-  return globalsCache[innerWindowID];
-}
-
-// when the window is destroyed, eliminate the associated globals cache
-if (!isWorker) {
-  Services.obs.addObserver(function observer(subject, topic, data) {
-    let id = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
-    delete globalsCache[id];
-  }, "inner-window-destroyed");
-}
--- a/devtools/server/moz.build
+++ b/devtools/server/moz.build
@@ -25,21 +25,20 @@ XPIDL_MODULE = 'jsinspector'
 SOURCES += [
     'nsJSInspector.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 DevToolsModules(
     'child.js',
-    'content-globals.js',
     'content-server.jsm',
     'css-logic.js',
     'event-parsers.js',
     'main.js',
     'primitive.js',
     'service-worker-child.js',
     'websocket-server.js',
     'worker.js'
 )
 
-with Files('**'):
-    BUG_COMPONENT = ('Firefox', 'Developer Tools')
+with Files('**'):
+    BUG_COMPONENT = ('Firefox', 'Developer Tools')
--- a/devtools/shared/gcli/commands/security.js
+++ b/devtools/shared/gcli/commands/security.js
@@ -11,19 +11,19 @@
  *    Provides information about the current referrer policy
  */
 
 "use strict";
 
 const { Ci } = require("chrome");
 const l10n = require("gcli/l10n");
 
-const GOOD_IMG_SRC = "chrome://browser/content/gcli_sec_good.svg";
-const MOD_IMG_SRC = "chrome://browser/content/gcli_sec_moderate.svg";
-const BAD_IMG_SRC = "chrome://browser/content/gcli_sec_bad.svg";
+const GOOD_IMG_SRC = "chrome://devtools/skin/images/gcli_sec_good.svg";
+const MOD_IMG_SRC = "chrome://devtools/skin/images/gcli_sec_moderate.svg";
+const BAD_IMG_SRC = "chrome://devtools/skin/images/gcli_sec_bad.svg";
 
 // special handling within policy
 const POLICY_REPORT_ONLY = "report-only";
 
 // special handling of directives
 const DIR_UPGRADE_INSECURE = "upgrade-insecure-requests";
 const DIR_BLOCK_ALL_MIXED_CONTENT = "block-all-mixed-content";
 
@@ -148,17 +148,17 @@ exports.items = [
     exec: function (cspInfo, context) {
       const url = context.environment.target.url;
 
       if (cspInfo.length == 0) {
         return context.createView({
           html:
             "<table class='gcli-csp-detail' cellspacing='10' valign='top'>" +
             "  <tr>" +
-            "    <td> <img src='chrome://browser/content/gcli_sec_bad.svg' width='20px' /> </td> " +
+            "    <td> <img src='" + BAD_IMG_SRC + "' width='20px' /> </td> " +
             "    <td>" + NO_CSP_ON_PAGE_MSG + " <b>" + url + "</b></td>" +
             "  </tr>" +
             "</table>"});
       }
 
       /* eslint-disable max-len */
       return context.createView({
         html:
--- a/devtools/shared/inspector/css-logic.js
+++ b/devtools/shared/inspector/css-logic.js
@@ -39,16 +39,19 @@ const { getTabPrefs } = require("devtool
  * styling information in the page, and present this to the user in a way that
  * helps them understand:
  * - why their expectations may not have been fulfilled
  * - how browsers process CSS
  * @constructor
  */
 
 const Services = require("Services");
+
+loader.lazyImporter(this, "findCssSelector", "resource://gre/modules/css-selector.js");
+
 const CSSLexer = require("devtools/shared/css/lexer");
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const styleInspectorL10N =
   new LocalizationHelper("devtools/shared/locales/styleinspector.properties");
 
 /**
  * Special values for filter, in addition to an href these values can be used
  */
@@ -340,94 +343,20 @@ function prettifyCSS(text, ruleCount) {
   }
 
   return result;
 }
 
 exports.prettifyCSS = prettifyCSS;
 
 /**
- * Find the position of [element] in [nodeList].
- * @returns an index of the match, or -1 if there is no match
- */
-function positionInNodeList(element, nodeList) {
-  for (let i = 0; i < nodeList.length; i++) {
-    if (element === nodeList[i]) {
-      return i;
-    }
-  }
-  return -1;
-}
-
-/**
  * Find a unique CSS selector for a given element
  * @returns a string such that ele.ownerDocument.querySelector(reply) === ele
  * and ele.ownerDocument.querySelectorAll(reply).length === 1
  */
-function findCssSelector(ele) {
-  ele = getRootBindingParent(ele);
-  let document = ele.ownerDocument;
-  if (!document || !document.contains(ele)) {
-    throw new Error("findCssSelector received element not inside document");
-  }
-
-  // document.querySelectorAll("#id") returns multiple if elements share an ID
-  if (ele.id &&
-      document.querySelectorAll("#" + CSS.escape(ele.id)).length === 1) {
-    return "#" + CSS.escape(ele.id);
-  }
-
-  // Inherently unique by tag name
-  let tagName = ele.localName;
-  if (tagName === "html") {
-    return "html";
-  }
-  if (tagName === "head") {
-    return "head";
-  }
-  if (tagName === "body") {
-    return "body";
-  }
-
-  // We might be able to find a unique class name
-  let selector, index, matches;
-  if (ele.classList.length > 0) {
-    for (let i = 0; i < ele.classList.length; i++) {
-      // Is this className unique by itself?
-      selector = "." + CSS.escape(ele.classList.item(i));
-      matches = document.querySelectorAll(selector);
-      if (matches.length === 1) {
-        return selector;
-      }
-      // Maybe it's unique with a tag name?
-      selector = tagName + selector;
-      matches = document.querySelectorAll(selector);
-      if (matches.length === 1) {
-        return selector;
-      }
-      // Maybe it's unique using a tag name and nth-child
-      index = positionInNodeList(ele, ele.parentNode.children) + 1;
-      selector = selector + ":nth-child(" + index + ")";
-      matches = document.querySelectorAll(selector);
-      if (matches.length === 1) {
-        return selector;
-      }
-    }
-  }
-
-  // Not unique enough yet.  As long as it's not a child of the document,
-  // continue recursing up until it is unique enough.
-  if (ele.parentNode !== document) {
-    index = positionInNodeList(ele, ele.parentNode.children) + 1;
-    selector = findCssSelector(ele.parentNode) + " > " +
-      tagName + ":nth-child(" + index + ")";
-  }
-
-  return selector;
-}
 exports.findCssSelector = findCssSelector;
 
 /**
  * Get the full CSS path for a given element.
  * @returns a string that can be used as a CSS selector for the element. It might not
  * match the element uniquely. It does however, represent the full path from the root
  * node to the element.
  */
--- a/devtools/shared/tests/mochitest/chrome.ini
+++ b/devtools/shared/tests/mochitest/chrome.ini
@@ -1,10 +1,8 @@
 [DEFAULT]
 tags = devtools
 skip-if = os == 'android'
 
 [test_css-logic-getCssPath.html]
-[test_css-logic.html]
-[test_devtools_extensions.html]
 [test_dom_matrix_2d.html]
 [test_eventemitter_basic.html]
 skip-if = os == 'linux' && debug # Bug 1205739
deleted file mode 100644
--- a/devtools/shared/tests/mochitest/test_devtools_extensions.html
+++ /dev/null
@@ -1,117 +0,0 @@
-<!DOCTYPE html>
-<!--
-  Any copyright is dedicated to the Public Domain.
-  http://creativecommons.org/publicdomain/zero/1.0/
--->
-
-<html>
-
-  <head>
-    <meta charset="utf8">
-    <title></title>
-
-    <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">
-
-    <script type="application/javascript">
-      const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-      let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
-      const contentGlobals  = require("devtools/server/content-globals");
-      const Services = require("Services");
-      const tabs = require('sdk/tabs');
-      const { getMostRecentBrowserWindow, getInnerId } = require('sdk/window/utils');
-      const { PageMod } = require('sdk/page-mod');
-
-      var _tests = [];
-      function addTest(test) {
-        _tests.push(test);
-      }
-
-      function runNextTest() {
-        if (_tests.length == 0) {
-          SimpleTest.finish()
-          return;
-        }
-        _tests.shift()();
-      }
-
-      window.onload = function() {
-        SimpleTest.waitForExplicitFinish();
-        runNextTest();
-      }
-
-      addTest(function () {
-        let TEST_URL = 'data:text/html;charset=utf-8,test';
-
-        let mod = PageMod({
-          include: TEST_URL,
-          contentScriptWhen: 'ready',
-          contentScript: 'null;'
-        });
-
-        tabs.open({
-          url: TEST_URL,
-          onLoad: function(tab) {
-            let id = getInnerId(getMostRecentBrowserWindow().gBrowser.selectedBrowser.contentWindow);
-
-            // getting
-            is(contentGlobals.getContentGlobals({
-              'inner-window-id': id
-            }).length, 1, 'found a global for inner-id = ' + id);
-
-            Services.obs.addObserver(function observer(subject, topic, data) {
-              if (id == subject.QueryInterface(Components.interfaces.nsISupportsPRUint64).data) {
-                Services.obs.removeObserver(observer, 'inner-window-destroyed');
-                setTimeout(function() {
-                  // closing the tab window should have removed the global
-                  is(contentGlobals.getContentGlobals({
-                    'inner-window-id': id
-                  }).length, 0, 'did not find a global for inner-id = ' + id);
-
-                  mod.destroy();
-                  runNextTest();
-                })
-              }
-            }, 'inner-window-destroyed');
-
-            tab.close();
-          }
-        });
-      })
-
-      addTest(function testAddRemoveGlobal() {
-        let global = {};
-        let globalDetails = {
-          global: global,
-          'inner-window-id': 5
-        };
-
-        // adding
-        contentGlobals.addContentGlobal(globalDetails);
-
-        // getting
-        is(contentGlobals.getContentGlobals({
-          'inner-window-id': 5
-        }).length, 1, 'found a global for inner-id = 5');
-        is(contentGlobals.getContentGlobals({
-          'inner-window-id': 4
-        }).length, 0, 'did not find a global for inner-id = 4');
-
-        // remove
-        contentGlobals.removeContentGlobal(globalDetails);
-
-        // getting again
-        is(contentGlobals.getContentGlobals({
-          'inner-window-id': 5
-        }).length, 0, 'did not find a global for inner-id = 5');
-
-        runNextTest();
-      });
-
-    </script>
-  </head>
-  <body></body>
-</html>
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -2495,54 +2495,74 @@ nsContentUtils::GetCommonAncestor(nsIDOM
   NS_ENSURE_TRUE(node1 && node2, NS_ERROR_UNEXPECTED);
 
   nsINode* common = GetCommonAncestor(node1, node2);
   NS_ENSURE_TRUE(common, NS_ERROR_NOT_AVAILABLE);
 
   return CallQueryInterface(common, aCommonAncestor);
 }
 
-// static
-nsINode*
-nsContentUtils::GetCommonAncestor(nsINode* aNode1,
-                                  nsINode* aNode2)
+template <typename Node, typename GetParentFunc>
+static Node*
+GetCommonAncestorInternal(Node* aNode1,
+                          Node* aNode2,
+                          GetParentFunc aGetParentFunc)
 {
   if (aNode1 == aNode2) {
     return aNode1;
   }
 
   // Build the chain of parents
-  AutoTArray<nsINode*, 30> parents1, parents2;
+  AutoTArray<Node*, 30> parents1, parents2;
   do {
     parents1.AppendElement(aNode1);
-    aNode1 = aNode1->GetParentNode();
+    aNode1 = aGetParentFunc(aNode1);
   } while (aNode1);
   do {
     parents2.AppendElement(aNode2);
-    aNode2 = aNode2->GetParentNode();
+    aNode2 = aGetParentFunc(aNode2);
   } while (aNode2);
 
   // Find where the parent chain differs
   uint32_t pos1 = parents1.Length();
   uint32_t pos2 = parents2.Length();
-  nsINode* parent = nullptr;
+  Node* parent = nullptr;
   uint32_t len;
   for (len = std::min(pos1, pos2); len > 0; --len) {
-    nsINode* child1 = parents1.ElementAt(--pos1);
-    nsINode* child2 = parents2.ElementAt(--pos2);
+    Node* child1 = parents1.ElementAt(--pos1);
+    Node* child2 = parents2.ElementAt(--pos2);
     if (child1 != child2) {
       break;
     }
     parent = child1;
   }
 
   return parent;
 }
 
 /* static */
+nsINode*
+nsContentUtils::GetCommonAncestor(nsINode* aNode1, nsINode* aNode2)
+{
+  return GetCommonAncestorInternal(aNode1, aNode2, [](nsINode* aNode) {
+    return aNode->GetParentNode();
+  });
+}
+
+/* static */
+nsIContent*
+nsContentUtils::GetCommonFlattenedTreeAncestor(nsIContent* aContent1,
+                                               nsIContent* aContent2)
+{
+  return GetCommonAncestorInternal(aContent1, aContent2, [](nsIContent* aContent) {
+    return aContent->GetFlattenedTreeParent();
+  });
+}
+
+/* static */
 bool
 nsContentUtils::PositionIsBefore(nsINode* aNode1, nsINode* aNode2)
 {
   return (aNode2->CompareDocumentPosition(*aNode1) &
     (nsIDOMNode::DOCUMENT_POSITION_PRECEDING |
      nsIDOMNode::DOCUMENT_POSITION_DISCONNECTED)) ==
     nsIDOMNode::DOCUMENT_POSITION_PRECEDING;
 }
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -361,21 +361,28 @@ public:
    * Returns an error if the two nodes are disconnected and don't have
    * a common ancestor.
    */
   static nsresult GetCommonAncestor(nsIDOMNode *aNode,
                                     nsIDOMNode *aOther,
                                     nsIDOMNode** aCommonAncestor);
 
   /**
-   * Returns the common ancestor, if any, for two nodes. Returns null if the
-   * nodes are disconnected.
+   * Returns the common ancestor, if any, for two nodes.
+   *
+   * Returns null if the nodes are disconnected.
    */
-  static nsINode* GetCommonAncestor(nsINode* aNode1,
-                                    nsINode* aNode2);
+  static nsINode* GetCommonAncestor(nsINode* aNode1, nsINode* aNode2);
+
+  /**
+   * Returns the common flattened tree ancestor, if any, for two given content
+   * nodes.
+   */
+  static nsIContent* GetCommonFlattenedTreeAncestor(nsIContent* aContent1,
+                                                    nsIContent* aContent2);
 
   /**
    * Returns true if aNode1 is before aNode2 in the same connected
    * tree.
    */
   static bool PositionIsBefore(nsINode* aNode1, nsINode* aNode2);
 
   /**
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -532,20 +532,18 @@ nsFocusManager::MoveFocus(mozIDOMWindowP
 
   nsCOMPtr<nsPIDOMWindowOuter> window;
   nsCOMPtr<nsIContent> startContent;
   if (aStartElement) {
     startContent = do_QueryInterface(aStartElement);
     NS_ENSURE_TRUE(startContent, NS_ERROR_INVALID_ARG);
 
     window = GetCurrentWindow(startContent);
-  }
-  else {
+  } else {
     window = aWindow ? nsPIDOMWindowOuter::From(aWindow) : mFocusedWindow.get();
-    NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
   }
 
   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
 
   bool noParentTraversal = aFlags & FLAG_NOPARENTFRAME;
   nsCOMPtr<nsIContent> newFocus;
   nsresult rv = DetermineElementToMoveFocus(window, startContent, aType, noParentTraversal,
                                             getter_AddRefs(newFocus));
@@ -860,17 +858,17 @@ nsFocusManager::ContentRemoved(nsIDocume
             if (limiter == content) {
               editor->FinalizeSelection();
             }
           }
         }
       }
     }
 
-    NotifyFocusStateChange(content, shouldShowFocusRing, false);
+    NotifyFocusStateChange(content, nullptr, shouldShowFocusRing, false);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFocusManager::WindowShown(mozIDOMWindowProxy* aWindow, bool aNeedsFocus)
 {
@@ -966,16 +964,17 @@ nsFocusManager::WindowHidden(mozIDOMWind
 
   nsCOMPtr<nsIContent> oldFocusedContent = mFocusedContent.forget();
 
   nsCOMPtr<nsIDocShell> focusedDocShell = mFocusedWindow->GetDocShell();
   nsCOMPtr<nsIPresShell> presShell = focusedDocShell->GetPresShell();
 
   if (oldFocusedContent && oldFocusedContent->IsInComposedDoc()) {
     NotifyFocusStateChange(oldFocusedContent,
+                           nullptr,
                            mFocusedWindow->ShouldShowFocusRing(),
                            false);
     window->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
 
     if (presShell) {
       SendFocusOrBlurEvent(eBlur, presShell,
                            oldFocusedContent->GetComposedDoc(),
                            oldFocusedContent, 1, false);
@@ -1085,36 +1084,54 @@ nsFocusManager::ParentActivated(mozIDOMW
 
   ActivateOrDeactivate(window, aActive);
   return NS_OK;
 }
 
 /* static */
 void
 nsFocusManager::NotifyFocusStateChange(nsIContent* aContent,
+                                       nsIContent* aContentToFocus,
                                        bool aWindowShouldShowFocusRing,
                                        bool aGettingFocus)
 {
+  MOZ_ASSERT_IF(aContentToFocus, !aGettingFocus);
   if (!aContent->IsElement()) {
     return;
   }
+
+  nsIContent* commonAncestor = nullptr;
+  if (aContentToFocus && aContentToFocus->IsElement()) {
+    commonAncestor =
+      nsContentUtils::GetCommonFlattenedTreeAncestor(aContent, aContentToFocus);
+  }
+
   EventStates eventState = NS_EVENT_STATE_FOCUS;
   if (aWindowShouldShowFocusRing) {
     eventState |= NS_EVENT_STATE_FOCUSRING;
   }
 
   if (aGettingFocus) {
     aContent->AsElement()->AddStates(eventState);
   } else {
     aContent->AsElement()->RemoveStates(eventState);
   }
 
-  for (Element* element = aContent->AsElement(); element;
-       element = element->GetParentElementCrossingShadowRoot()) {
+  for (nsIContent* content = aContent;
+       content && content != commonAncestor;
+       content = content->GetFlattenedTreeParent()) {
+    if (!content->IsElement()) {
+      continue;
+    }
+
+    Element* element = content->AsElement();
     if (aGettingFocus) {
+      if (element->State().HasState(NS_EVENT_STATE_FOCUS_WITHIN)) {
+        break;
+      }
       element->AddStates(NS_EVENT_STATE_FOCUS_WITHIN);
     } else {
       element->RemoveStates(NS_EVENT_STATE_FOCUS_WITHIN);
     }
   }
 }
 
 // static
@@ -1658,17 +1675,20 @@ nsFocusManager::Blur(nsPIDOMWindowOuter*
 
   LOGCONTENT("Element %s has been blurred", content.get());
 
   // Don't fire blur event on the root content which isn't editable.
   bool sendBlurEvent =
     content && content->IsInComposedDoc() && !IsNonFocusableRoot(content);
   if (content) {
     if (sendBlurEvent) {
-      NotifyFocusStateChange(content, shouldShowFocusRing, false);
+      NotifyFocusStateChange(content,
+                             aContentToFocus,
+                             shouldShowFocusRing,
+                             false);
     }
 
     // if an object/plug-in/remote browser is being blurred, move the system focus
     // to the parent window, otherwise events will still get fired at the plugin.
     // But don't do this if we are blurring due to the window being lowered,
     // otherwise, the parent window can get raised again.
     if (mActiveWindow) {
       nsIFrame* contentFrame = content->GetPrimaryFrame();
@@ -1909,17 +1929,20 @@ nsFocusManager::Focus(nsPIDOMWindowOuter
     if (aContent && aFocusChanged) {
       ScrollIntoView(presShell, aContent, aFlags);
     }
 
     bool sendFocusEvent =
       aContent && aContent->IsInComposedDoc() && !IsNonFocusableRoot(aContent);
     nsPresContext* presContext = presShell->GetPresContext();
     if (sendFocusEvent) {
-      NotifyFocusStateChange(aContent, aWindow->ShouldShowFocusRing(), true);
+      NotifyFocusStateChange(aContent,
+                             nullptr,
+                             aWindow->ShouldShowFocusRing(),
+                             true);
 
       // if this is an object/plug-in/remote browser, focus its widget.  Note that we might
       // no longer be in the same document, due to the events we fired above when
       // aIsNewDocument.
       if (presShell->GetDocument() == aContent->GetComposedDoc()) {
         if (aAdjustWidgets && objectFrameWidget && !sTestMode)
           objectFrameWidget->SetFocus(false);
 
--- a/dom/base/nsFocusManager.h
+++ b/dom/base/nsFocusManager.h
@@ -510,16 +510,17 @@ private:
   // we need to pass in whether the window should show a focus ring
   // before the SetFocusedNode call on it happened when losing focus
   // and after the SetFocusedNode call when gaining focus, which is
   // why that information needs to be an explicit argument instead of
   // just passing in the window and asking it whether it should show
   // focus rings: in the losing focus case that information could be
   // wrong..
   static void NotifyFocusStateChange(nsIContent* aContent,
+                                     nsIContent* aContentToFocus,
                                      bool aWindowShouldShowFocusRing,
                                      bool aGettingFocus);
 
   void SetFocusedWindowInternal(nsPIDOMWindowOuter* aWindow);
 
   // Notify the change of content window ID
   // belonging to the top level outer window.
   void NotifyCurrentTopLevelContentWindowChange(nsPIDOMWindowOuter* aWindow);
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -4771,58 +4771,23 @@ GetLabelTarget(nsIContent* aPossibleLabe
   mozilla::dom::HTMLLabelElement* label =
     mozilla::dom::HTMLLabelElement::FromContent(aPossibleLabel);
   if (!label)
     return nullptr;
 
   return label->GetLabeledElement();
 }
 
-static nsIContent* FindCommonAncestor(nsIContent *aNode1, nsIContent *aNode2)
+static nsIContent*
+FindCommonAncestor(nsIContent* aNode1, nsIContent* aNode2)
 {
-  // Find closest common ancestor
-  if (aNode1 && aNode2) {
-    // Find the nearest common ancestor by counting the distance to the
-    // root and then walking up again, in pairs.
-    int32_t offset = 0;
-    nsIContent *anc1 = aNode1;
-    for (;;) {
-      ++offset;
-      nsIContent* parent = anc1->GetFlattenedTreeParent();
-      if (!parent)
-        break;
-      anc1 = parent;
-    }
-    nsIContent *anc2 = aNode2;
-    for (;;) {
-      --offset;
-      nsIContent* parent = anc2->GetFlattenedTreeParent();
-      if (!parent)
-        break;
-      anc2 = parent;
-    }
-    if (anc1 == anc2) {
-      anc1 = aNode1;
-      anc2 = aNode2;
-      while (offset > 0) {
-        anc1 = anc1->GetFlattenedTreeParent();
-        --offset;
-      }
-      while (offset < 0) {
-        anc2 = anc2->GetFlattenedTreeParent();
-        ++offset;
-      }
-      while (anc1 != anc2) {
-        anc1 = anc1->GetFlattenedTreeParent();
-        anc2 = anc2->GetFlattenedTreeParent();
-      }
-      return anc1;
-    }
-  }
-  return nullptr;
+  if (!aNode1 || !aNode2) {
+    return nullptr;
+  }
+  return nsContentUtils::GetCommonFlattenedTreeAncestor(aNode1, aNode2);
 }
 
 /* static */
 void
 EventStateManager::SetFullScreenState(Element* aElement, bool aIsFullScreen)
 {
   DoStateChange(aElement, NS_EVENT_STATE_FULL_SCREEN, aIsFullScreen);
 }
--- a/layout/base/GeckoRestyleManager.cpp
+++ b/layout/base/GeckoRestyleManager.cpp
@@ -242,16 +242,18 @@ GeckoRestyleManager::AttributeWillChange
 // passing the notification to the frame).
 void
 GeckoRestyleManager::AttributeChanged(Element* aElement,
                                       int32_t aNameSpaceID,
                                       nsIAtom* aAttribute,
                                       int32_t aModType,
                                       const nsAttrValue* aOldValue)
 {
+  MOZ_ASSERT(!mInStyleRefresh);
+
   // Hold onto the PresShell to prevent ourselves from being destroyed.
   // XXXbz how, exactly, would this attribute change cause us to be
   // destroyed from inside this function?
   nsCOMPtr<nsIPresShell> shell = PresContext()->GetPresShell();
   mozilla::Unused << shell; // Unused within this function
 
   // Get the frame associated with the content which is the highest in the frame tree
   nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -326,16 +326,17 @@ RestyleManager::ContentRemoved(nsINode* 
  * This is called from both Restyle managers.
  */
 void
 RestyleManager::ContentStateChangedInternal(Element* aElement,
                                             EventStates aStateMask,
                                             nsChangeHint* aOutChangeHint,
                                             nsRestyleHint* aOutRestyleHint)
 {
+  MOZ_ASSERT(!mInStyleRefresh);
   MOZ_ASSERT(aOutChangeHint);
   MOZ_ASSERT(aOutRestyleHint);
 
   StyleSetHandle styleSet = PresContext()->StyleSet();
   NS_ASSERTION(styleSet, "couldn't get style set");
 
   *aOutChangeHint = nsChangeHint(0);
   // Any change to a content state that affects which frames we construct
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -484,16 +484,18 @@ ServoRestyleManager::AttributeWillChange
   }
 }
 
 void
 ServoRestyleManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
                                       nsIAtom* aAttribute, int32_t aModType,
                                       const nsAttrValue* aOldValue)
 {
+  MOZ_ASSERT(!mInStyleRefresh);
+
 #ifdef DEBUG
   ServoElementSnapshot* snapshot = Servo_Element_GetSnapshot(aElement);
   MOZ_ASSERT_IF(snapshot, snapshot->HasAttrs());
 #endif
 
   nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
   if (primaryFrame) {
     primaryFrame->AttributeChanged(aNameSpaceID, aAttribute, aModType);
--- a/layout/mathml/nsMathMLContainerFrame.cpp
+++ b/layout/mathml/nsMathMLContainerFrame.cpp
@@ -11,17 +11,16 @@
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsStyleContext.h"
 #include "nsNameSpaceManager.h"
 #include "nsRenderingContext.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsGkAtoms.h"
 #include "nsDisplayList.h"
-#include "nsIReflowCallback.h"
 #include "mozilla/Likely.h"
 #include "nsIScriptError.h"
 #include "nsContentUtils.h"
 #include "nsMathMLElement.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
@@ -1313,48 +1312,16 @@ nsMathMLContainerFrame::PositionRowChild
     nscoord dx = aOffsetX + child.X();
     nscoord dy = aBaseline - child.Ascent();
     FinishReflowChild(child.Frame(), PresContext(), child.GetReflowOutput(),
                       nullptr, dx, dy, 0);
     ++child;
   }
 }
 
-class ForceReflow : public nsIReflowCallback {
-public:
-  virtual bool ReflowFinished() override {
-    return true;
-  }
-  virtual void ReflowCallbackCanceled() override {}
-};
-
-// We only need one of these so we just make it a static global, no need
-// to dynamically allocate/destroy it.
-static ForceReflow gForceReflow;
-
-void
-nsMathMLContainerFrame::SetIncrementScriptLevel(int32_t aChildIndex, bool aIncrement)
-{
-  nsIFrame* child = PrincipalChildList().FrameAt(aChildIndex);
-  if (!child)
-    return;
-  nsIContent* content = child->GetContent();
-  if (!content->IsMathMLElement())
-    return;
-  nsMathMLElement* element = static_cast<nsMathMLElement*>(content);
-
-  if (element->GetIncrementScriptLevel() == aIncrement)
-    return;
-
-  // XXXroc this does a ContentStatesChanged, is it safe to call here? If
-  // not we should do it in a post-reflow callback.
-  element->SetIncrementScriptLevel(aIncrement, true);
-  PresContext()->PresShell()->PostReflowCallback(&gForceReflow);
-}
-
 // helpers to fix the inter-spacing when <math> is the only parent
 // e.g., it fixes <math> <mi>f</mi> <mo>q</mo> <mi>f</mi> <mo>I</mo> </math>
 
 static nscoord
 GetInterFrameSpacingFor(int32_t         aScriptLevel,
                         nsIFrame*       aParentFrame,
                         nsIFrame*       aChildFrame)
 {
--- a/layout/mathml/nsMathMLContainerFrame.h
+++ b/layout/mathml/nsMathMLContainerFrame.h
@@ -23,17 +23,18 @@
  * to position children in various customized ways.
  */
 
 // Options for the preferred size at which to stretch our stretchy children 
 #define STRETCH_CONSIDER_ACTUAL_SIZE    0x00000001 // just use our current size
 #define STRETCH_CONSIDER_EMBELLISHMENTS 0x00000002 // size calculations include embellishments
 
 class nsMathMLContainerFrame : public nsContainerFrame,
-                               public nsMathMLFrame {
+                               public nsMathMLFrame
+{
   friend class nsMathMLmfencedFrame;
 public:
   nsMathMLContainerFrame(nsStyleContext* aContext)
     : nsContainerFrame(aContext, mozilla::LayoutFrameType::None)
     , mIntrinsicWidth(NS_INTRINSIC_WIDTH_UNKNOWN)
     , mBlockStartAscent(0)
   {}
 
@@ -55,28 +56,16 @@ public:
                                     int32_t         aLastIndex,
                                     uint32_t        aFlagsValues,
                                     uint32_t        aFlagsToUpdate) override
   {
     PropagatePresentationDataFromChildAt(this, aFirstIndex, aLastIndex,
       aFlagsValues, aFlagsToUpdate);
     return NS_OK;
   }
-  
-  // helper to set the "increment script level" flag on the element belonging
-  // to a child frame given by aChildIndex.
-  // When this flag is set, the style system will increment the scriptlevel
-  // for the child element. This is needed for situations where the style system
-  // cannot itself determine the scriptlevel (mfrac, munder, mover, munderover).
-  // This should be called during reflow. We set the flag and if it changed,
-  // we request appropriate restyling and also queue a post-reflow callback
-  // to ensure that restyle and reflow happens immediately after the current
-  // reflow.
-  void
-  SetIncrementScriptLevel(int32_t aChildIndex, bool aIncrement);
 
   // --------------------------------------------------------------------------
   // Overloaded nsContainerFrame methods -- see documentation in nsIFrame.h
 
   virtual bool IsFrameOfType(uint32_t aFlags) const override
   {
     return !(aFlags & nsIFrame::eLineParticipant) &&
       nsContainerFrame::IsFrameOfType(aFlags &
--- a/layout/mathml/nsMathMLmunderoverFrame.cpp
+++ b/layout/mathml/nsMathMLmunderoverFrame.cpp
@@ -2,16 +2,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/. */
 
 #include "nsMathMLmunderoverFrame.h"
 #include "nsPresContext.h"
 #include "nsRenderingContext.h"
 #include "nsMathMLmmultiscriptsFrame.h"
+#include "nsMathMLElement.h"
 #include <algorithm>
 #include "gfxMathTable.h"
 
 //
 // <munderover> -- attach an underscript-overscript pair to a base - implementation
 // <mover> -- attach an overscript to a base - implementation
 // <munder> -- attach an underscript to a base - implementation
 //
@@ -66,16 +67,25 @@ nsMathMLmunderoverFrame::InheritAutomati
   // let the base class get the default from our parent
   nsMathMLContainerFrame::InheritAutomaticData(aParent);
 
   mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
 
   return NS_OK;
 }
 
+void
+nsMathMLmunderoverFrame::DestroyFrom(nsIFrame* aDestroyRoot)
+{
+  if (!mPostReflowIncrementScriptLevelCommands.IsEmpty()) {
+    PresContext()->PresShell()->CancelReflowCallback(this);
+  }
+  nsMathMLContainerFrame::DestroyFrom(aDestroyRoot);
+}
+
 uint8_t
 nsMathMLmunderoverFrame::ScriptIncrement(nsIFrame* aFrame)
 {
   nsIFrame* child = mFrames.FirstChild();
   if (!aFrame || aFrame == child) {
     return 0;
   }
   child = child->GetNextSibling();
@@ -87,16 +97,71 @@ nsMathMLmunderoverFrame::ScriptIncrement
   }
   if (child && aFrame == child->GetNextSibling()) {
     // must be a over frame of munderover
     return mIncrementOver ? 1 : 0;
   }
   return 0;  // frame not found
 }
 
+void
+nsMathMLmunderoverFrame::SetIncrementScriptLevel(uint32_t aChildIndex,
+                                                 bool aIncrement)
+{
+  nsIFrame* child = PrincipalChildList().FrameAt(aChildIndex);
+  if (!child || !child->GetContent()->IsMathMLElement()) {
+    return;
+  }
+
+  auto element = static_cast<nsMathMLElement*>(child->GetContent());
+  if (element->GetIncrementScriptLevel() == aIncrement) {
+    return;
+  }
+
+  if (mPostReflowIncrementScriptLevelCommands.IsEmpty()) {
+    PresContext()->PresShell()->PostReflowCallback(this);
+  }
+
+  mPostReflowIncrementScriptLevelCommands.AppendElement(
+      SetIncrementScriptLevelCommand { aChildIndex, aIncrement });
+}
+
+bool
+nsMathMLmunderoverFrame::ReflowFinished()
+{
+  SetPendingPostReflowIncrementScriptLevel();
+  return true;
+}
+
+void
+nsMathMLmunderoverFrame::ReflowCallbackCanceled()
+{
+  // Do nothing, at this point our work will just be useless.
+  mPostReflowIncrementScriptLevelCommands.Clear();
+}
+
+void
+nsMathMLmunderoverFrame::SetPendingPostReflowIncrementScriptLevel()
+{
+  MOZ_ASSERT(!mPostReflowIncrementScriptLevelCommands.IsEmpty());
+
+  nsTArray<SetIncrementScriptLevelCommand> commands;
+  commands.SwapElements(mPostReflowIncrementScriptLevelCommands);
+
+  for (const auto& command : commands) {
+    nsIFrame* child = PrincipalChildList().FrameAt(command.mChildIndex);
+    if (!child || !child->GetContent()->IsMathMLElement()) {
+      continue;
+    }
+
+    auto element = static_cast<nsMathMLElement*>(child->GetContent());
+    element->SetIncrementScriptLevel(command.mDoIncrement, true);
+  }
+}
+
 NS_IMETHODIMP
 nsMathMLmunderoverFrame::TransmitAutomaticData()
 {
   // At this stage, all our children are in sync and we can fully
   // resolve our own mEmbellishData struct
   //---------------------------------------------------------------------
 
   /* 
@@ -158,17 +223,17 @@ XXX The winner is the outermost setting 
   nsAutoString value;
   if (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder_,
                                       nsGkAtoms::munderover_