Merge m-c to graphics
authorKartikaya Gupta <kgupta@mozilla.com>
Fri, 24 Feb 2017 09:11:52 -0500
changeset 374804 d4583ab7644efc567dbc412e38c32282a9cb067a
parent 374803 b545b4883b898a78f6071c9ef4cd627037b2fc08 (current diff)
parent 373706 be661bae6cb9a53935c5b87744bf68879d9ebcc5 (diff)
child 374805 6237f9ee107b9c4fafd2aa495d7a47f51d0221d3
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone54.0a1
Merge m-c to graphics MozReview-Commit-ID: D9NUw9xhxzv
browser/themes/linux/browser-lightweightTheme.css
browser/themes/osx/browser-lightweightTheme.css
browser/themes/windows/browser-lightweightTheme.css
devtools/client/inspector/components/deprecated-box-model.js
devtools/client/inspector/components/test/.eslintrc.js
devtools/client/inspector/components/test/browser.ini
devtools/client/inspector/components/test/browser_boxmodel.js
devtools/client/inspector/components/test/browser_boxmodel_editablemodel.js
devtools/client/inspector/components/test/browser_boxmodel_editablemodel_allproperties.js
devtools/client/inspector/components/test/browser_boxmodel_editablemodel_bluronclick.js
devtools/client/inspector/components/test/browser_boxmodel_editablemodel_border.js
devtools/client/inspector/components/test/browser_boxmodel_editablemodel_stylerules.js
devtools/client/inspector/components/test/browser_boxmodel_guides.js
devtools/client/inspector/components/test/browser_boxmodel_navigation.js
devtools/client/inspector/components/test/browser_boxmodel_rotate-labels-on-sides.js
devtools/client/inspector/components/test/browser_boxmodel_sync.js
devtools/client/inspector/components/test/browser_boxmodel_tooltips.js
devtools/client/inspector/components/test/browser_boxmodel_update-after-navigation.js
devtools/client/inspector/components/test/browser_boxmodel_update-after-reload.js
devtools/client/inspector/components/test/browser_boxmodel_update-in-iframes.js
devtools/client/inspector/components/test/doc_boxmodel_iframe1.html
devtools/client/inspector/components/test/doc_boxmodel_iframe2.html
devtools/client/inspector/components/test/head.js
devtools/client/inspector/layout/actions/box-model.js
devtools/client/inspector/layout/components/BoxModel.js
devtools/client/inspector/layout/components/BoxModelEditable.js
devtools/client/inspector/layout/components/BoxModelInfo.js
devtools/client/inspector/layout/components/BoxModelMain.js
devtools/client/inspector/layout/components/BoxModelProperties.js
devtools/client/inspector/layout/components/ComputedProperty.js
devtools/client/inspector/layout/reducers/box-model.js
devtools/client/inspector/layout/utils/editing-session.js
devtools/client/shared/components/reps/load-reps.js
devtools/client/themes/deprecated-boxmodel.css
gfx/layers/ipc/CompositorBridgeParent.cpp
layout/painting/nsDisplayList.cpp
tools/profiler/core/ProfileEntry.cpp
tools/profiler/core/ProfileEntry.h
--- a/.eslintignore
+++ b/.eslintignore
@@ -77,17 +77,18 @@ browser/locales/**
 browser/extensions/mortar/**
 
 # devtools/ exclusions
 devtools/client/canvasdebugger/**
 devtools/client/commandline/**
 devtools/client/debugger/**
 devtools/client/framework/**
 !devtools/client/framework/selection.js
-!devtools/client/framework/toolbox.js
+!devtools/client/framework/target*
+!devtools/client/framework/toolbox*
 devtools/client/netmonitor/test/**
 devtools/client/netmonitor/har/test/**
 devtools/client/projecteditor/**
 devtools/client/responsivedesign/**
 devtools/client/scratchpad/**
 devtools/client/shadereditor/**
 devtools/client/shared/*.jsm
 devtools/client/shared/components/reps/reps.js
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -5075,16 +5075,17 @@ nsBrowserAccess.prototype = {
     let tab = win.gBrowser.loadOneTab(aURI ? aURI.spec : "about:blank", {
                                       triggeringPrincipal: aTriggeringPrincipal,
                                       referrerURI: aReferrer,
                                       referrerPolicy: aReferrerPolicy,
                                       userContextId: aUserContextId,
                                       fromExternal: aIsExternal,
                                       inBackground: loadInBackground,
                                       forceNotRemote: aForceNotRemote,
+                                      forceBrowserInsertion: true,
                                       opener: aOpener,
                                       });
     let browser = win.gBrowser.getBrowserForTab(tab);
 
     if (needToFocusWin || (!loadInBackground && aIsExternal))
       win.focus();
 
     return browser;
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1509,16 +1509,17 @@
             var aSkipAnimation;
             var aForceNotRemote;
             var aPreferredRemoteType;
             var aNoReferrer;
             var aUserContextId;
             var aSameProcessAsFrameLoader;
             var aOriginPrincipal;
             var aOpener;
+            var aForceBrowserInsertion;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aTriggeringPrincipal      = params.triggeringPrincipal
               aReferrerURI              = params.referrerURI;
               aReferrerPolicy           = params.referrerPolicy;
               aCharset                  = params.charset;
@@ -1532,16 +1533,17 @@
               aForceNotRemote           = params.forceNotRemote;
               aPreferredRemoteType      = params.preferredRemoteType;
               aNoReferrer               = params.noReferrer;
               aUserContextId            = params.userContextId;
               aSameProcessAsFrameLoader = params.sameProcessAsFrameLoader;
               aOriginPrincipal          = params.originPrincipal;
               aOpener                   = params.opener;
               aIsPrerendered            = params.isPrerendered;
+              aForceBrowserInsertion    = params.forceBrowserInsertion;
             }
 
             var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
                          Services.prefs.getBoolPref("browser.tabs.loadInBackground");
             var owner = bgLoad ? null : this.selectedTab;
 
             var tab = this.addTab(aURI, {
                                   triggeringPrincipal: aTriggeringPrincipal,
@@ -1551,16 +1553,17 @@
                                   postData: aPostData,
                                   ownerTab: owner,
                                   allowThirdPartyFixup: aAllowThirdPartyFixup,
                                   fromExternal: aFromExternal,
                                   relatedToCurrent: aRelatedToCurrent,
                                   skipAnimation: aSkipAnimation,
                                   allowMixedContent: aAllowMixedContent,
                                   forceNotRemote: aForceNotRemote,
+                                  forceBrowserInsertion: aForceBrowserInsertion,
                                   preferredRemoteType: aPreferredRemoteType,
                                   noReferrer: aNoReferrer,
                                   userContextId: aUserContextId,
                                   originPrincipal: aOriginPrincipal,
                                   sameProcessAsFrameLoader: aSameProcessAsFrameLoader,
                                   opener: aOpener,
                                   isPrerendered: aIsPrerendered });
             if (!bgLoad)
@@ -1705,16 +1708,19 @@
             // Abort if we're not going to change anything
             let currentRemoteType = aBrowser.getAttribute("remoteType");
             if (isRemote == aShouldBeRemote && !aOptions.newFrameloader &&
                 (!isRemote || currentRemoteType == aOptions.remoteType)) {
               return false;
             }
 
             let tab = this.getTabForBrowser(aBrowser);
+            // aBrowser needs to be inserted now if it hasn't been already.
+            this._insertBrowser(tab);
+
             let evt = document.createEvent("Events");
             evt.initEvent("BeforeTabRemotenessChange", true, false);
             tab.dispatchEvent(evt);
 
             let wasActive = document.activeElement == aBrowser;
 
             // Unmap the old outerWindowID.
             this._outerWindowIDBrowserMap.delete(aBrowser.outerWindowID);
@@ -1837,29 +1843,31 @@
             aOptions = aOptions || {};
 
             if (!gMultiProcessBrowser)
               return this.updateBrowserRemoteness(aBrowser, false);
 
             // If we're in a LargeAllocation process, we prefer switching back
             // into a normal content process, as that way we can clean up the
             // L-A process.
-            let preferredRemoteType = aBrowser.remoteType;
-            if (aBrowser.remoteType == E10SUtils.LARGE_ALLOCATION_REMOTE_TYPE) {
+            let currentRemoteType = aBrowser.getAttribute("remoteType") || null;
+            let preferredRemoteType = currentRemoteType;
+
+            if (currentRemoteType == E10SUtils.LARGE_ALLOCATION_REMOTE_TYPE) {
               preferredRemoteType = E10SUtils.DEFAULT_REMOTE_TYPE;
             }
             aOptions.remoteType =
               E10SUtils.getRemoteTypeForURI(aURL,
                                             gMultiProcessBrowser,
                                             preferredRemoteType,
                                             aOptions.freshProcess);
 
             // If this URL can't load in the current browser then flip it to the
             // correct type.
-            if (aBrowser.remoteType != aOptions.remoteType ||
+            if (currentRemoteType != aOptions.remoteType ||
                 aOptions.newFrameloader) {
               let remote = aOptions.remoteType != E10SUtils.NOT_REMOTE;
               return this.updateBrowserRemoteness(aBrowser, remote, aOptions);
             }
 
             return false;
           ]]>
         </body>
@@ -1938,22 +1946,22 @@
       </method>
 
       <method name="_createBrowser">
         <parameter name="aParams"/>
         <body>
           <![CDATA[
             // Supported parameters:
             // userContextId, remote, remoteType, isPreloadBrowser,
-            // uriIsAboutBlank, permanentKey, isPrerendered
+            // uriIsAboutBlank, sameProcessAsFrameLoader, isPrerendered
 
             const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
             let b = document.createElementNS(NS_XUL, "browser");
-            b.permanentKey = aParams.permanentKey || {};
+            b.permanentKey = {};
             b.setAttribute("type", "content");
             b.setAttribute("message", "true");
             b.setAttribute("messagemanagergroup", "browsers");
             b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
             b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
 
             if (aParams.isPrerendered) {
               b.setAttribute("prerendered", "true");
@@ -2029,69 +2037,102 @@
               b.setAttribute("nodefaultsrc", "true");
             }
 
             return b;
           ]]>
         </body>
       </method>
 
-      <method name="_linkBrowserToTab">
+      <!--
+        `_createLazyBrowser` will define properties on the unbound lazy browser
+        which correspond to properties defined in XBL which will be bound to
+        the browser when it is inserted into the document.  If any of these
+        properties are accessed by consumers, `_insertBrowser` is called and
+        the browser is inserted to ensure that things don't break.  This list
+        provides the names of properties that may be called while the browser
+        is in its unbound (lazy) state.
+      -->
+      <field name="_browserBindingProperties">[
+        "canGoBack", "canGoForward", "goBack", "goForward", "permitUnload",
+        "reload", "reloadWithFlags", "stop", "loadURI", "loadURIWithFlags",
+        "goHome", "homePage", "gotoIndex", "currentURI", "documentURI",
+        "preferences", "imageDocument", "isRemoteBrowser", "messageManager",
+        "getTabBrowser", "finder", "fastFind", "sessionHistory", "contentTitle",
+        "characterSet", "fullZoom", "textZoom", "webProgress",
+        "addProgressListener", "removeProgressListener", "audioPlaybackStarted",
+        "audioPlaybackStopped", "adjustPriority", "pauseMedia", "stopMedia",
+        "blockMedia", "resumeMedia", "audioMuted", "mute", "unmute",
+        "blockedPopups", "mIconURL", "lastURI", "userTypedValue",
+        "purgeSessionHistory", "stopScroll", "startScroll"
+      ]</field>
+
+      <method name="_createLazyBrowser">
         <parameter name="aTab"/>
-        <parameter name="aURI"/>
-        <parameter name="aParams"/>
+        <body>
+          <![CDATA[
+            let browser = aTab.linkedBrowser;
+
+            let names = this._browserBindingProperties;
+
+            for (let i = 0; i < names.length; i++) {
+              let name = names[i];
+              let getter;
+              let setter;
+              switch (name) {
+                default:
+                  getter = () => {
+                    this._insertBrowser(aTab);
+                    return browser[name];
+                  };
+                  setter = (value) => {
+                    this._insertBrowser(aTab);
+                    return browser[name] = value;
+                  };
+              }
+              Object.defineProperty(browser, name, {
+                get: getter,
+                set: setter,
+                configurable: true,
+                enumerable: true
+              });
+            }
+          ]]>
+        </body>
+      </method>
+
+      <method name="_insertBrowser">
+        <parameter name="aTab"/>
         <body>
           <![CDATA[
             "use strict";
 
-            // Supported parameters:
-            // forceNotRemote, preferredRemoteType, userContextId, isPrerendered
-
-            let uriIsAboutBlank = !aURI || aURI == "about:blank";
-
-            let remoteType =
-              aParams.forceNotRemote ? E10SUtils.NOT_REMOTE
-              : E10SUtils.getRemoteTypeForURI(aURI, gMultiProcessBrowser,
-                                              aParams.preferredRemoteType);
-
-            let browser;
-            let usingPreloadedContent = false;
-
-            // If we open a new tab with the newtab URL in the default
-            // userContext, check if there is a preloaded browser ready.
-            // Private windows are not included because both the label and the
-            // icon for the tab would be set incorrectly (see bug 1195981).
-            if (aURI == BROWSER_NEW_TAB_URL &&
-                !aParams.userContextId &&
-                !PrivateBrowsingUtils.isWindowPrivate(window)) {
-              browser = this._getPreloadedBrowser();
-              if (browser) {
-                usingPreloadedContent = true;
-                aTab.permanentKey = browser.permanentKey;
+            // If browser is already inserted, or aTab doesn't have a
+            // browser, don't do anything.
+            if (aTab.linkedPanel || !aTab.linkedBrowser) {
+              return;
+            }
+
+            let browser = aTab.linkedBrowser;
+
+            // If browser is a lazy browser, delete the substitute properties.
+            if (this._browserBindingProperties[0] in browser) {
+              for (let name of this._browserBindingProperties) {
+                delete browser[name];
               }
             }
 
-            if (!browser) {
-              // No preloaded browser found, create one.
-              browser = this._createBrowser({permanentKey: aTab.permanentKey,
-                                             remoteType,
-                                             uriIsAboutBlank,
-                                             userContextId: aParams.userContextId,
-                                             sameProcessAsFrameLoader: aParams.sameProcessAsFrameLoader,
-                                             opener: aParams.opener,
-                                             isPrerendered: aParams.isPrerendered});
-            }
+            let { uriIsAboutBlank, remoteType, usingPreloadedContent } =
+                    aTab._browserParams;
+            delete aTab._browserParams;
 
             let notificationbox = this.getNotificationBox(browser);
             let uniqueId = this._generateUniquePanelID();
             notificationbox.id = uniqueId;
             aTab.linkedPanel = uniqueId;
-            aTab.linkedBrowser = browser;
-            aTab.hasBrowser = true;
-            this._tabForBrowser.set(browser, aTab);
 
             // Inject the <browser> into the DOM if necessary.
             if (!notificationbox.parentNode) {
               // NB: this appendChild call causes us to run constructors for the
               // browser element, which fires off a bunch of notifications. Some
               // of those notifications can cause code to run that inspects our
               // state, so it is important that the tab element is fully
               // initialized by this point.
@@ -2119,18 +2160,16 @@
             // into the DOM. We thus have to register the new outerWindowID
             // for non-remote browsers after we have called browser.loadURI().
             if (remoteType == E10SUtils.NOT_REMOTE) {
               this._outerWindowIDBrowserMap.set(browser.outerWindowID, browser);
             }
 
             var evt = new CustomEvent("TabBrowserInserted", { bubbles: true, detail: {} });
             aTab.dispatchEvent(evt);
-
-            return { usingPreloadedContent };
           ]]>
         </body>
       </method>
 
       <method name="addTab">
         <parameter name="aURI"/>
         <parameter name="aReferrerURI"/>
         <parameter name="aCharset"/>
@@ -2153,16 +2192,17 @@
             var aPreferredRemoteType;
             var aNoReferrer;
             var aUserContextId;
             var aEventDetail;
             var aSameProcessAsFrameLoader;
             var aOriginPrincipal;
             var aDisallowInheritPrincipal;
             var aOpener;
+            var aForceBrowserInsertion;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aTriggeringPrincipal      = params.triggeringPrincipal;
               aReferrerURI              = params.referrerURI;
               aReferrerPolicy           = params.referrerPolicy;
               aCharset                  = params.charset;
@@ -2178,16 +2218,17 @@
               aNoReferrer               = params.noReferrer;
               aUserContextId            = params.userContextId;
               aEventDetail              = params.eventDetail;
               aSameProcessAsFrameLoader = params.sameProcessAsFrameLoader;
               aOriginPrincipal          = params.originPrincipal;
               aDisallowInheritPrincipal = params.disallowInheritPrincipal;
               aOpener                   = params.opener;
               aIsPrerendered            = params.isPrerendered;
+              aForceBrowserInsertion    = params.forceBrowserInsertion;
             }
 
             // if we're adding tabs, we're past interrupt mode, ditch the owner
             if (this.mCurrentTab.owner)
               this.mCurrentTab.owner = null;
 
             var t = document.createElementNS(NS_XUL, "tab");
 
@@ -2240,47 +2281,74 @@
             this.tabContainer.appendChild(t);
 
             // If this new tab is owned by another, assert that relationship
             if (aOwner)
               t.owner = aOwner;
 
             var position = this.tabs.length - 1;
             t._tPos = position;
-            t.permanentKey = {};
             this.tabContainer._setPositionalAttributes();
 
             this.tabContainer.updateVisibility();
 
             // If URI is about:blank and we don't have a preferred remote type,
             // then we need to use the referrer, if we have one, to get the
             // correct remote type for the new tab.
             if (uriIsAboutBlank && !aPreferredRemoteType && aReferrerURI) {
               aPreferredRemoteType =
                 E10SUtils.getRemoteTypeForURI(aReferrerURI.spec,
                                               gMultiProcessBrowser);
             }
 
-            // Currently in this incarnation of bug 906076, we are forcing the
-            // browser to immediately be linked.  In future incarnations of this
-            // bug this will be removed so we can leave the tab in its "lazy"
-            // state to be exploited for startup optimization.  Note that for
-            // now this must occur before "TabOpen" event is fired, as that will
-            // trigger SessionStore.jsm to run code that expects the existence
-            // of tab.linkedBrowser.
-            let browserParams = {
-              forceNotRemote: aForceNotRemote,
-              preferredRemoteType: aPreferredRemoteType,
-              userContextId:  aUserContextId,
-              sameProcessAsFrameLoader: aSameProcessAsFrameLoader,
-              opener: aOpener,
-              isPrerendered: aIsPrerendered,
-            };
-            let { usingPreloadedContent } = this._linkBrowserToTab(t, aURI, browserParams);
-            let b = t.linkedBrowser;
+            let remoteType =
+              aForceNotRemote ? E10SUtils.NOT_REMOTE
+              : E10SUtils.getRemoteTypeForURI(aURI, gMultiProcessBrowser,
+                                              aPreferredRemoteType);
+
+            let b;
+            let usingPreloadedContent = false;
+
+            // If we open a new tab with the newtab URL in the default
+            // userContext, check if there is a preloaded browser ready.
+            // Private windows are not included because both the label and the
+            // icon for the tab would be set incorrectly (see bug 1195981).
+            if (aURI == BROWSER_NEW_TAB_URL &&
+                !aUserContextId &&
+                !PrivateBrowsingUtils.isWindowPrivate(window)) {
+              b = this._getPreloadedBrowser();
+              if (b) {
+                usingPreloadedContent = true;
+              }
+            }
+
+            if (!b) {
+              // No preloaded browser found, create one.
+              b = this._createBrowser({ remoteType,
+                                        uriIsAboutBlank,
+                                        userContextId: aUserContextId,
+                                        sameProcessAsFrameLoader: aSameProcessAsFrameLoader,
+                                        opener: aOpener,
+                                        isPrerendered: aIsPrerendered });
+            }
+
+            t.linkedBrowser = b;
+            this._tabForBrowser.set(b, t);
+            t.permanentKey = b.permanentKey;
+            t._browserParams = { uriIsAboutBlank,
+                                 remoteType,
+                                 usingPreloadedContent };
+
+            // If we're creating a blank tab, create a lazy browser.
+            // Otherwise insert the browser into the document now.
+            if (uriIsAboutBlank && !aForceBrowserInsertion) {
+              this._createLazyBrowser(t);
+            } else {
+              this._insertBrowser(t);
+            }
 
             // Dispatch a new tab notification.  We do this once we're
             // entirely done, so that things are in a consistent state
             // even if the event listener opens or closes tabs.
             var detail = aEventDetail || {};
             var evt = new CustomEvent("TabOpen", { bubbles: true, detail });
             t.dispatchEvent(evt);
 
@@ -2490,16 +2558,19 @@
         <body>
           <![CDATA[
             if (aParams) {
               var animate = aParams.animate;
               var byMouse = aParams.byMouse;
               var skipPermitUnload = aParams.skipPermitUnload;
             }
 
+            // Ensure aTab's browser is inserted into the document.
+            this._insertBrowser(aTab);
+
             window.maybeRecordAbandonmentTelemetry(aTab, "tabClosed");
 
             // Handle requests for synchronously removing an already
             // asynchronously closing tab.
             if (!animate &&
                 aTab.closing) {
               this._endRemoveTab(aTab);
               return;
@@ -2989,16 +3060,19 @@
 
       <method name="_swapBrowserDocShells">
         <parameter name="aOurTab"/>
         <parameter name="aOtherBrowser"/>
         <parameter name="aFlags"/>
         <parameter name="aStateFlags"/>
         <body>
           <![CDATA[
+            // aOurTab's browser needs to be inserted now if it hasn't already.
+            this._insertBrowser(aOurTab);
+
             // Unhook our progress listener
             const filter = this._tabFilters.get(aOurTab);
             let tabListener = this._tabListeners.get(aOurTab);
             let ourBrowser = this.getBrowserForTab(aOurTab);
             ourBrowser.webProgress.removeProgressListener(filter);
             filter.removeProgressListener(tabListener);
 
             // Make sure to unregister any open URIs.
@@ -4961,17 +5035,16 @@
           var uniqueId = this._generateUniquePanelID();
           this.mPanelContainer.childNodes[0].id = uniqueId;
           this.mCurrentTab.linkedPanel = uniqueId;
           this.mCurrentTab.permanentKey = this.mCurrentBrowser.permanentKey;
           this.mCurrentTab._tPos = 0;
           this.mCurrentTab._fullyOpen = true;
           this.mCurrentTab.cachePosition = 0;
           this.mCurrentTab.linkedBrowser = this.mCurrentBrowser;
-          this.mCurrentTab.hasBrowser = true;
           this._tabForBrowser.set(this.mCurrentBrowser, this.mCurrentTab);
 
           // set up the shared autoscroll popup
           this._autoScrollPopup = this.mCurrentBrowser._createAutoScrollPopup();
           this._autoScrollPopup.id = "autoscroller";
           this.appendChild(this._autoScrollPopup);
           this.mCurrentBrowser.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
           this.mCurrentBrowser.droppedLinkHandler = handleDroppedLink;
@@ -6277,16 +6350,31 @@
               let preview = this._browserNewtabpageEnabled ? "_PREVIEW" : "";
               Services.telemetry.getHistogramById("FX_TAB_ANIM_OPEN" + preview + "_FRAME_INTERVAL_MS").add(averageInterval);
             }
           }
         ]]>
         </body>
       </method>
 
+      <method name="getRelatedElement">
+        <parameter name="aTab"/>
+        <body>
+        <![CDATA[
+          if (!aTab)
+            return null;
+          // If the tab's browser is lazy, we need to `_insertBrowser` in order
+          // to have a linkedPanel.  This will also serve to bind the browser
+          // and make it ready to use when the tab is selected.
+          this.tabbrowser._insertBrowser(aTab);
+          return document.getElementById(aTab.linkedPanel);
+        ]]>
+        </body>
+      </method>
+
       <!-- Deprecated stuff, implemented for backwards compatibility. -->
       <property name="mAllTabsPopup" readonly="true"
                 onget="return document.getElementById('alltabs-popup');"/>
     </implementation>
 
     <handlers>
       <handler event="TabSelect" action="this._handleTabSelect();"/>
 
--- a/browser/base/content/test/general/browser_extension_permissions.js
+++ b/browser/base/content/test/general/browser_extension_permissions.js
@@ -57,17 +57,17 @@ function checkNotification(panel, filena
   if (filename == PERMS_XPI) {
     // The icon should come from the extension, don't bother with the precise
     // path, just make sure we've got a jar url pointing to the right path
     // inside the jar.
     ok(icon.startsWith("jar:file://"), "Icon is a jar url");
     ok(icon.endsWith("/icon.png"), "Icon is icon.png inside a jar");
 
     is(header.getAttribute("hidden"), "", "Permission list header is visible");
-    is(ul.childElementCount, 4, "Permissions list has 4 entries");
+    is(ul.childElementCount, 5, "Permissions list has 5 entries");
     // Real checking of the contents here is deferred until bug 1316996 lands
   } else if (filename == NO_PERMS_XPI) {
     // This extension has no icon, it should have the default
     ok(isDefaultIcon(icon), "Icon is the default extension icon");
 
     is(header.getAttribute("hidden"), "true", "Permission list header is hidden");
     is(ul.childElementCount, 0, "Permissions list has 0 entries");
   }
--- a/browser/base/content/test/general/browser_extension_sideloading.js
+++ b/browser/base/content/test/general/browser_extension_sideloading.js
@@ -26,16 +26,17 @@ class MockAddon {
   }
 
   get userDisabled() {
     return this._userDisabled;
   }
 
   set userDisabled(val) {
     this._userDisabled = val;
+    AddonManagerPrivate.callAddonListeners(val ? "onDisabled" : "onEnabled", this);
     let fn = setCallbacks.get(this);
     if (fn) {
       setCallbacks.delete(this);
       fn(val);
     }
     return val;
   }
 
@@ -128,17 +129,43 @@ add_task(function* () {
     userDisabled: true,
     seen: false,
     userPermissions: {
       permissions: [],
       hosts: [],
     },
   });
 
-  let provider = new MockProvider(mock1, mock2);
+  const ID3 = "addon3@tests.mozilla.org";
+  let mock3 = new MockAddon({
+    id: ID3,
+    name: "Test 3",
+    isWebExtension: true,
+    userDisabled: true,
+    seen: false,
+    userPermissions: {
+      permissions: [],
+      hosts: ["<all_urls>"],
+    }
+  });
+
+  const ID4 = "addon4@tests.mozilla.org";
+  let mock4 = new MockAddon({
+    id: ID4,
+    name: "Test 4",
+    isWebExtension: true,
+    userDisabled: true,
+    seen: false,
+    userPermissions: {
+      permissions: [],
+      hosts: ["<all_urls>"],
+    }
+  });
+
+  let provider = new MockProvider(mock1, mock2, mock3, mock4);
   AddonManagerPrivate.registerProvider(provider, [{
     id: "extension",
     name: "Extensions",
     uiPriority: 4000,
     flags: AddonManager.TYPE_UI_VIEW_LIST |
            AddonManager.TYPE_SUPPORTS_UNDO_RESTARTLESS_UNINSTALL,
   }]);
   registerCleanupFunction(function*() {
@@ -173,17 +200,17 @@ add_task(function* () {
   // Check for the addons badge on the hamburger menu
   let menuButton = document.getElementById("PanelUI-menu-button");
   is(menuButton.getAttribute("badge-status"), "addon-alert", "Should have addon alert badge");
 
   // Find the menu entries for sideloaded extensions
   yield PanelUI.show();
 
   let addons = document.getElementById("PanelUI-footer-addons");
-  is(addons.children.length, 2, "Have 2 menu entries for sideloaded extensions");
+  is(addons.children.length, 4, "Have 4 menu entries for sideloaded extensions");
 
   // Click the first sideloaded extension
   let popupPromise = promisePopupNotificationShown("addon-webext-permissions");
   addons.children[0].click();
 
   // When we get the permissions prompt, we should be at the extensions
   // list in about:addons
   let panel = yield popupPromise;
@@ -199,50 +226,119 @@ add_task(function* () {
   is(icon, ICON_URL, "Permissions notification has the addon icon");
 
   let disablePromise = promiseSetDisabled(mock1);
   panel.secondaryButton.click();
 
   let value = yield disablePromise;
   is(value, true, "Addon should remain disabled");
 
-  let [addon1, addon2] = yield AddonManager.getAddonsByIDs([ID1, ID2]);
+  let [addon1, addon2, addon3, addon4] = yield AddonManager.getAddonsByIDs([ID1, ID2, ID3, ID4]);
   ok(addon1.seen, "Addon should be marked as seen");
   is(addon1.userDisabled, true, "Addon 1 should still be disabled");
   is(addon2.userDisabled, true, "Addon 2 should still be disabled");
+  is(addon3.userDisabled, true, "Addon 3 should still be disabled");
+  is(addon4.userDisabled, true, "Addon 4 should still be disabled");
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
-  // Should still have 1 entry in the hamburger menu
+  // Should still have 3 entries in the hamburger menu
   yield PanelUI.show();
 
   addons = document.getElementById("PanelUI-footer-addons");
-  is(addons.children.length, 1, "Have 1 menu entry for sideloaded extensions");
+  is(addons.children.length, 3, "Have 3 menu entries for sideloaded extensions");
 
   // Click the second sideloaded extension and wait for the notification
   popupPromise = promisePopupNotificationShown("addon-webext-permissions");
   addons.children[0].click();
   panel = yield popupPromise;
 
-  isnot(menuButton.getAttribute("badge-status"), "addon-alert", "Should no longer have addon alert badge");
-
   // Again we should be at the extentions list in about:addons
   is(gBrowser.currentURI.spec, "about:addons", "Foreground tab is at about:addons");
 
   win = gBrowser.selectedBrowser.contentWindow;
   ok(!win.gViewController.isLoading, "about:addons view is fully loaded");
   is(win.gViewController.currentViewId, VIEW, "about:addons is at extensions list");
 
   // Check the notification contents, this time accept the install
   icon = panel.getAttribute("icon");
   is(icon, DEFAULT_ICON_URL, "Permissions notification has the default icon");
   disablePromise = promiseSetDisabled(mock2);
   panel.button.click();
 
   value = yield disablePromise;
   is(value, false, "Addon should be set to enabled");
 
-  [addon1, addon2] = yield AddonManager.getAddonsByIDs([ID1, ID2]);
+  [addon1, addon2, addon3, addon4] = yield AddonManager.getAddonsByIDs([ID1, ID2, ID3, ID4]);
   is(addon1.userDisabled, true, "Addon 1 should still be disabled");
   is(addon2.userDisabled, false, "Addon 2 should now be enabled");
+  is(addon3.userDisabled, true, "Addon 3 should still be disabled");
+  is(addon4.userDisabled, true, "Addon 4 should still be disabled");
+
+  // Should still have 2 entries in the hamburger menu
+  yield PanelUI.show();
+
+  addons = document.getElementById("PanelUI-footer-addons");
+  is(addons.children.length, 2, "Have 2 menu entries for sideloaded extensions");
+
+  // Close the hamburger menu and go directly to the addons manager
+  yield PanelUI.hide();
+
+  win = yield BrowserOpenAddonsMgr(VIEW);
+
+  let list = win.document.getElementById("addon-list");
+
+  // Make sure XBL bindings are applied
+  list.clientHeight;
+
+  let item = list.children.find(_item => _item.value == ID3);
+  ok(item, "Found entry for sideloaded extension in about:addons");
+  item.scrollIntoView({behavior: "instant"});
+
+  ok(is_visible(item._enableBtn), "Enable button is visible for sideloaded extension");
+  ok(is_hidden(item._disableBtn), "Disable button is not visible for sideloaded extension");
+
+  // When clicking enable we should see the permissions notification
+  popupPromise = promisePopupNotificationShown("addon-webext-permissions");
+  BrowserTestUtils.synthesizeMouseAtCenter(item._enableBtn, {},
+                                           gBrowser.selectedBrowser);
+  panel = yield popupPromise;
+
+  // Accept the permissions
+  disablePromise = promiseSetDisabled(mock3);
+  panel.button.click();
+  value = yield disablePromise;
+  is(value, false, "userDisabled should be set on addon 3");
+
+  addon3 = yield AddonManager.getAddonByID(ID3);
+  is(addon3.userDisabled, false, "Addon 3 should be enabled");
+
+  // Should still have 1 entry in the hamburger menu
+  yield PanelUI.show();
+
+  addons = document.getElementById("PanelUI-footer-addons");
+  is(addons.children.length, 1, "Have 1 menu entry for sideloaded extensions");
+
+  // Close the hamburger menu and go to the detail page for this addon
+  yield PanelUI.hide();
+
+  win = yield BrowserOpenAddonsMgr(`addons://detail/${encodeURIComponent(ID4)}`);
+  let button = win.document.getElementById("detail-enable-btn");
+
+  // When clicking enable we should see the permissions notification
+  popupPromise = promisePopupNotificationShown("addon-webext-permissions");
+  BrowserTestUtils.synthesizeMouseAtCenter(button, {},
+                                           gBrowser.selectedBrowser);
+  panel = yield popupPromise;
+
+  // Accept the permissions
+  disablePromise = promiseSetDisabled(mock4);
+  panel.button.click();
+  value = yield disablePromise;
+  is(value, false, "userDisabled should be set on addon 4");
+
+  addon4 = yield AddonManager.getAddonByID(ID4);
+  is(addon4.userDisabled, false, "Addon 4 should be enabled");
+
+  isnot(menuButton.getAttribute("badge-status"), "addon-alert", "Should no longer have addon alert badge");
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
--- a/browser/base/content/test/general/browser_extension_update_background.js
+++ b/browser/base/content/test/general/browser_extension_update_background.js
@@ -1,13 +1,13 @@
 const {AddonManagerPrivate} = Cu.import("resource://gre/modules/AddonManager.jsm", {});
 
 const URL_BASE = "https://example.com/browser/browser/base/content/test/general";
-const ID = "update@tests.mozilla.org";
-const ID_ICON = "update_icon@tests.mozilla.org";
+const ID = "update2@tests.mozilla.org";
+const ID_ICON = "update_icon2@tests.mozilla.org";
 const ID_PERMS = "update_perms@tests.mozilla.org";
 const ID_LEGACY = "legacy_update@tests.mozilla.org";
 
 registerCleanupFunction(async function() {
   for (let id of [ID, ID_ICON, ID_PERMS, ID_LEGACY]) {
     let addon = await AddonManager.getAddonByID(id);
     if (addon) {
       ok(false, `Addon ${id} was still installed at the end of the test`);
@@ -149,19 +149,27 @@ function* backgroundUpdateTest(url, id, 
   is(tab.linkedBrowser.currentURI.spec, "about:addons", "Browser is at about:addons");
 
   const VIEW = "addons://list/extension";
   yield promiseViewLoaded(tab, VIEW);
   let win = tab.linkedBrowser.contentWindow;
   ok(!win.gViewController.isLoading, "about:addons view is fully loaded");
   is(win.gViewController.currentViewId, VIEW, "about:addons is at extensions list");
 
-  // Wait for the permission prompt, check the contents, then cancel the update
+  // Wait for the permission prompt, check the contents
   let panel = yield popupPromise;
   checkIconFn(panel.getAttribute("icon"));
+
+  // The original extension has 1 promptable permission and the new one
+  // has 2 (history and <all_urls>) plus 1 non-promptable permission (cookies).
+  // So we should only see the 1 new promptable permission in the notification.
+  let list = document.getElementById("addon-webext-perm-list");
+  is(list.childElementCount, 1, "Permissions list contains 1 entry");
+
+  // Cancel the update.
   panel.secondaryButton.click();
 
   addon = yield AddonManager.getAddonByID(id);
   is(addon.version, "1.0", "Should still be running the old version");
 
   yield BrowserTestUtils.removeTab(tab);
 
   // Alert badge and hamburger menu items should be gone
--- a/browser/base/content/test/general/browser_extension_update_interactive.js
+++ b/browser/base/content/test/general/browser_extension_update_interactive.js
@@ -1,12 +1,12 @@
 const {AddonManagerPrivate} = Cu.import("resource://gre/modules/AddonManager.jsm", {});
 
 const URL_BASE = "https://example.com/browser/browser/base/content/test/general";
-const ID = "update@tests.mozilla.org";
+const ID = "update2@tests.mozilla.org";
 const ID_LEGACY = "legacy_update@tests.mozilla.org";
 
 registerCleanupFunction(async function() {
   for (let id of [ID, ID_LEGACY]) {
     let addon = await AddonManager.getAddonByID(id);
     if (addon) {
       ok(false, `Addon ${id} was still installed at the end of the test`);
       addon.uninstall();
index e9402bde6b01c1de759644a8971cf3d3a51d4681..9241ab3159892136e7e77f8d738c36baf47f2930
GIT binary patch
literal 16592
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
--- a/browser/base/content/test/general/browser_webext_update.json
+++ b/browser/base/content/test/general/browser_webext_update.json
@@ -1,25 +1,25 @@
 {
   "addons": {
-    "update@tests.mozilla.org": {
+    "update2@tests.mozilla.org": {
       "updates": [
         {
           "version": "2.0",
           "update_link": "https://example.com/browser/browser/base/content/test/general/browser_webext_update2.xpi",
           "applications": {
             "gecko": {
               "strict_min_version": "1",
               "advisory_max_version": "55.0"
             }
           }
         }
       ]
     },
-    "update_icon@tests.mozilla.org": {
+    "update_icon2@tests.mozilla.org": {
       "updates": [
         {
           "version": "2.0",
           "update_link": "https://example.com/browser/browser/base/content/test/general/browser_webext_update_icon2.xpi",
           "applications": {
             "gecko": {
               "strict_min_version": "1",
               "advisory_max_version": "55.0"
index 8eac9daa01745a2247eb9284b51858546b52f6a1..d4cc2453262d48826337b23f92a7e24642743d52
GIT binary patch
literal 313
zc$^FHW@Zs#U|`^2IGY*Z74;?8t`o?+2*kV$G7Pzid6{Xc#U*-K#rb)mA)E}%Mq<k%
zK)AGmn}Lz#D<cB~Si5h}X)Y#19@p=mMOCvkt-QnkwRcQ0>pQqFW7PuZ|94B=uJ}Hf
zS6uwuQIkXM66?-+@)0wFTJ}nr$GQjyFojIqAkdz}{dCv1e@EVnmQ`u5ah|+cJ-cf5
z(KCly{4(-iO0!OQo&Wuf&(V(;%p=>sD829XOKn<u{mHgcvt-UAe<E!UP2I8VQ_JNG
vZRP>qj7)OOxO^nR00KZqGc0KYvC#a-3h^JB-vYc@*&x~&8G?cI1+XCi!~kcI
index 81c7407ba7f17e486ce553318369dc7aaf1b017b..26565b50b8407c92b9ff50e60a04e207c27de073
GIT binary patch
literal 4340
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
index ccb61e7c5f25ab77cca0934373d7311498e515db..25d6f8ec4a4f4300f46ced58e9f1a86081ba8b57
GIT binary patch
literal 12570
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
F{y&jhwT%D(
index 9f1c5fb34bade3b1445fa0ce2260c1b9207f7a2f..6885ec08e6fa7be509ac575b9f15b24f2d65f083
GIT binary patch
literal 12586
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
V9~#tuLVf`Fzxv<FU0D7b{U2L1xYYmv
--- a/browser/extensions/formautofill/content/formautofill.css
+++ b/browser/extensions/formautofill/content/formautofill.css
@@ -1,7 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-.autocomplete-richlistitem[originaltype="autofill-profile"] {
+#PopupAutoComplete > richlistbox > richlistitem[originaltype="autofill-profile"] {
+  display: block;
+  margin: 0;
+  padding: 0;
+  height: auto;
+  min-height: auto;
+
   -moz-binding: url("chrome://formautofill/content/formautofill.xml#autocomplete-profile-listitem");
 }
+
+#PopupAutoComplete[firstresultstyle="autofill-profile"] {
+  min-width: 150px !important;
+}
--- a/browser/extensions/formautofill/content/formautofill.xml
+++ b/browser/extensions/formautofill/content/formautofill.xml
@@ -5,16 +5,21 @@
 
 <bindings id="formautofillBindings"
           xmlns="http://www.mozilla.org/xbl"
           xmlns:html="http://www.w3.org/1999/xhtml"
           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
           xmlns:xbl="http://www.mozilla.org/xbl">
 
   <binding id="autocomplete-profile-listitem" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
+    <resources>
+      <stylesheet src="chrome://formautofill-shared/skin/autocomplete-item.css"/>
+      <stylesheet src="chrome://formautofill/skin/autocomplete-item.css"/>
+    </resources>
+
     <xbl:content xmlns="http://www.w3.org/1999/xhtml">
       <div anonid="profile-item-box" class="profile-item-box">
         <div class="profile-label-col profile-item-col">
           <span anonid="profile-label" class="profile-label"></span>
         </div>
         <div class="profile-comment-col profile-item-col">
           <span anonid="profile-comment" class="profile-comment"></span>
         </div>
@@ -59,16 +64,19 @@
         <![CDATA[
           let outerBoxRect = this.parentNode.getBoundingClientRect();
           let value = this.getAttribute("ac-value");
           let comment = this.getAttribute("ac-comment");
 
           this._comment.textContent = comment;
           this._label.textContent = value;
 
+          // Make item fit in popup as XUL box could not constrain
+          // item's width
+          this._itemBox.style.width =  outerBoxRect.width + "px";
           // Use two-lines layout when width is smaller than 150px
           if (outerBoxRect.width <= 150) {
             this._itemBox.setAttribute("size", "small");
           } else {
             this._itemBox.removeAttribute("size");
           }
         ]]>
         </body>
--- a/browser/extensions/formautofill/jar.mn
+++ b/browser/extensions/formautofill/jar.mn
@@ -3,8 +3,14 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 [features/formautofill@mozilla.org] chrome.jar:
 % resource formautofill %res/
   res/ (*.jsm)
 
 % content formautofill %content/
   content/ (content/*)
+
+% skin formautofill classic/1.0 %skin/linux/
+% skin formautofill classic/1.0 %skin/osx/ os=Darwin
+% skin formautofill classic/1.0 %skin/windows/ os=WINNT
+% skin formautofill-shared classic/1.0 %skin/shared/
+  skin/  (skin/*)
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/skin/linux/autocomplete-item.css
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+@namespace url("http://www.w3.org/1999/xhtml");
+
+
+.profile-item-box > .profile-item-col > .profile-label {
+  font-size: .9em;
+}
+
+.profile-item-box > .profile-item-col > .profile-comment {
+  font-size: .7em;
+}
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/skin/osx/autocomplete-item.css
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+@namespace url("http://www.w3.org/1999/xhtml");
+
+
+.profile-item-box > .profile-item-col > .profile-label {
+  font-size: 1.18em;
+}
+
+.profile-item-box > .profile-item-col > .profile-comment {
+  font-size: .9em;
+}
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/skin/shared/autocomplete-item.css
@@ -0,0 +1,65 @@
+/* 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/. */
+
+@namespace url("http://www.w3.org/1999/xhtml");
+@namespace xul url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+
+xul|richlistitem[originaltype="autofill-profile"][selected="true"] > .profile-item-box {
+  background-color: #F2F2F2;
+}
+
+.profile-item-box {
+  box-sizing: border-box;
+  border-bottom: 1px solid rgba(38,38,38,.15);
+  padding-top: 6px;
+  padding-bottom: 6px;
+  padding-inline-start: 16px;
+  padding-inline-end: 10px;
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  align-items: center;
+}
+
+.profile-item-box:last-child {
+  border-bottom: 0;
+}
+
+.profile-item-box > .profile-item-col {
+  box-sizing: border-box;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  width: 50%;
+}
+
+.profile-item-box > .profile-label-col {
+  padding-inline-end: 10px;
+  text-align: start;
+}
+
+.profile-item-box > .profile-comment-col {
+  text-align: end;
+  color: GrayText;
+}
+
+.profile-item-box[size="small"] {
+  padding-top: 10px;
+  padding-bottom: 10px;
+  flex-direction: column;
+}
+
+.profile-item-box[size="small"] > .profile-item-col {
+  width: 100%;
+}
+
+.profile-item-box[size="small"] > .profile-label-col {
+  padding-inline-end: 0;
+}
+
+.profile-item-box[size="small"] > .profile-comment-col {
+  margin-top: 7px;
+  text-align: start;
+}
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/skin/windows/autocomplete-item.css
@@ -0,0 +1,21 @@
+/* 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/. */
+
+@namespace url("http://www.w3.org/1999/xhtml");
+@namespace xul url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+
+.profile-item-box > .profile-item-col > .profile-label {
+  font-size: 1.08em;
+}
+
+.profile-item-box > .profile-item-col > .profile-comment {
+  font-size: .83em;
+}
+
+@media (-moz-windows-default-theme: 0) {
+  xul|richlistitem[originaltype="autofill-profile"][selected="true"] > .profile-item-box {
+    background-color: Highlight;
+  }
+}
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,3 +1,3 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 1.7.297
+Current extension version is: 1.7.312
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -216,16 +216,21 @@ var FontType = {
  TYPE0: 9,
  MMTYPE1: 10
 };
 var VERBOSITY_LEVELS = {
  errors: 0,
  warnings: 1,
  infos: 5
 };
+var CMapCompressionType = {
+ NONE: 0,
+ BINARY: 1,
+ STREAM: 2
+};
 var OPS = {
  dependency: 1,
  setLineWidth: 2,
  setLineCap: 3,
  setLineJoin: 4,
  setMiterLimit: 5,
  setDash: 6,
  setRenderingIntent: 7,
@@ -1161,16 +1166,22 @@ function isArray(v) {
  return v instanceof Array;
 }
 function isArrayBuffer(v) {
  return typeof v === 'object' && v !== null && v.byteLength !== undefined;
 }
 function isSpace(ch) {
  return ch === 0x20 || ch === 0x09 || ch === 0x0D || ch === 0x0A;
 }
+function isNodeJS() {
+ if (typeof __pdfjsdev_webpack__ === 'undefined') {
+  return typeof process === 'object' && process + '' === '[object process]';
+ }
+ return false;
+}
 function createPromiseCapability() {
  var capability = {};
  capability.promise = new Promise(function (resolve, reject) {
   capability.resolve = resolve;
   capability.reject = reject;
  });
  return capability;
 }
@@ -1433,16 +1444,17 @@ exports.OPS = OPS;
 exports.VERBOSITY_LEVELS = VERBOSITY_LEVELS;
 exports.UNSUPPORTED_FEATURES = UNSUPPORTED_FEATURES;
 exports.AnnotationBorderStyleType = AnnotationBorderStyleType;
 exports.AnnotationFieldFlag = AnnotationFieldFlag;
 exports.AnnotationFlag = AnnotationFlag;
 exports.AnnotationType = AnnotationType;
 exports.FontType = FontType;
 exports.ImageKind = ImageKind;
+exports.CMapCompressionType = CMapCompressionType;
 exports.InvalidPDFException = InvalidPDFException;
 exports.MessageHandler = MessageHandler;
 exports.MissingDataException = MissingDataException;
 exports.MissingPDFException = MissingPDFException;
 exports.NotImplementedException = NotImplementedException;
 exports.PageViewport = PageViewport;
 exports.PasswordException = PasswordException;
 exports.PasswordResponses = PasswordResponses;
@@ -1469,16 +1481,17 @@ exports.info = info;
 exports.isArray = isArray;
 exports.isArrayBuffer = isArrayBuffer;
 exports.isBool = isBool;
 exports.isEmptyObj = isEmptyObj;
 exports.isInt = isInt;
 exports.isNum = isNum;
 exports.isString = isString;
 exports.isSpace = isSpace;
+exports.isNodeJS = isNodeJS;
 exports.isSameOrigin = isSameOrigin;
 exports.createValidAbsoluteUrl = createValidAbsoluteUrl;
 exports.isLittleEndian = isLittleEndian;
 exports.isEvalSupported = isEvalSupported;
 exports.loadJpegStream = loadJpegStream;
 exports.log2 = log2;
 exports.readInt8 = readInt8;
 exports.readUint16 = readUint16;
@@ -1501,16 +1514,18 @@ exports.warn = warn;
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
 var assert = sharedUtil.assert;
 var removeNullCharacters = sharedUtil.removeNullCharacters;
 var warn = sharedUtil.warn;
 var deprecated = sharedUtil.deprecated;
 var createValidAbsoluteUrl = sharedUtil.createValidAbsoluteUrl;
+var stringToBytes = sharedUtil.stringToBytes;
+var CMapCompressionType = sharedUtil.CMapCompressionType;
 var DEFAULT_LINK_REL = 'noopener noreferrer nofollow';
 function DOMCanvasFactory() {
 }
 DOMCanvasFactory.prototype = {
  create: function DOMCanvasFactory_create(width, height) {
   assert(width > 0 && height > 0, 'invalid canvas size');
   var canvas = document.createElement('canvas');
   var context = canvas.getContext('2d');
@@ -1530,16 +1545,58 @@ DOMCanvasFactory.prototype = {
  destroy: function DOMCanvasFactory_destroy(canvasAndContextPair) {
   assert(canvasAndContextPair.canvas, 'canvas is not specified');
   canvasAndContextPair.canvas.width = 0;
   canvasAndContextPair.canvas.height = 0;
   canvasAndContextPair.canvas = null;
   canvasAndContextPair.context = null;
  }
 };
+var DOMCMapReaderFactory = function DOMCMapReaderFactoryClosure() {
+ function DOMCMapReaderFactory(params) {
+  this.baseUrl = params.baseUrl || null;
+  this.isCompressed = params.isCompressed || false;
+ }
+ DOMCMapReaderFactory.prototype = {
+  fetch: function (params) {
+   if (!params.name) {
+    return Promise.reject(new Error('CMap name must be specified.'));
+   }
+   return new Promise(function (resolve, reject) {
+    var url = this.baseUrl + params.name;
+    var request = new XMLHttpRequest();
+    if (this.isCompressed) {
+     url += '.bcmap';
+     request.responseType = 'arraybuffer';
+    }
+    request.onreadystatechange = function () {
+     if (request.readyState === XMLHttpRequest.DONE && (request.status === 200 || request.status === 0)) {
+      var data;
+      if (this.isCompressed && request.response) {
+       data = new Uint8Array(request.response);
+      } else if (!this.isCompressed && request.responseText) {
+       data = stringToBytes(request.responseText);
+      }
+      if (data) {
+       resolve({
+        cMapData: data,
+        compressionType: this.isCompressed ? CMapCompressionType.BINARY : CMapCompressionType.NONE
+       });
+       return;
+      }
+      reject(new Error('Unable to load ' + (this.isCompressed ? 'binary' : '') + ' CMap at: ' + url));
+     }
+    }.bind(this);
+    request.open('GET', url, true);
+    request.send(null);
+   }.bind(this));
+  }
+ };
+ return DOMCMapReaderFactory;
+}();
 var CustomStyle = function CustomStyleClosure() {
  var prefixes = [
   'ms',
   'Moz',
   'Webkit',
   'O'
  ];
  var _cache = Object.create(null);
@@ -1689,16 +1746,17 @@ exports.addLinkAttributes = addLinkAttri
 exports.isExternalLinkTargetSet = isExternalLinkTargetSet;
 exports.isValidUrl = isValidUrl;
 exports.getFilenameFromUrl = getFilenameFromUrl;
 exports.LinkTarget = LinkTarget;
 exports.hasCanvasTypedArrays = hasCanvasTypedArrays;
 exports.getDefaultSetting = getDefaultSetting;
 exports.DEFAULT_LINK_REL = DEFAULT_LINK_REL;
 exports.DOMCanvasFactory = DOMCanvasFactory;
+exports.DOMCMapReaderFactory = DOMCMapReaderFactory;
 
 /***/ }),
 /* 2 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
@@ -2353,16 +2411,17 @@ var stringToBytes = sharedUtil.stringToB
 var globalScope = sharedUtil.globalScope;
 var warn = sharedUtil.warn;
 var FontFaceObject = displayFontLoader.FontFaceObject;
 var FontLoader = displayFontLoader.FontLoader;
 var CanvasGraphics = displayCanvas.CanvasGraphics;
 var Metadata = displayMetadata.Metadata;
 var getDefaultSetting = displayDOMUtils.getDefaultSetting;
 var DOMCanvasFactory = displayDOMUtils.DOMCanvasFactory;
+var DOMCMapReaderFactory = displayDOMUtils.DOMCMapReaderFactory;
 var DEFAULT_RANGE_CHUNK_SIZE = 65536;
 var isWorkerDisabled = false;
 var workerSrc;
 var isPostMessageTransfersDisabled = false;
 var pdfjsFilePath = null;
 var fakeWorkerFilesLoader = null;
 var useRequireEnsure = false;
 function getDocument(src, pdfDataRangeTransport, passwordCallback, progressCallback) {
@@ -2426,31 +2485,32 @@ function getDocument(src, pdfDataRangeTr
     error('Invalid PDF binary data: either typed array, string or ' + 'array-like object is expected in the data property.');
    }
    continue;
   }
   params[key] = source[key];
  }
  params.rangeChunkSize = params.rangeChunkSize || DEFAULT_RANGE_CHUNK_SIZE;
  params.disableNativeImageDecoder = params.disableNativeImageDecoder === true;
+ var CMapReaderFactory = params.CMapReaderFactory || DOMCMapReaderFactory;
  if (!worker) {
   worker = new PDFWorker();
   task._worker = worker;
  }
  var docId = task.docId;
  worker.promise.then(function () {
   if (task.destroyed) {
    throw new Error('Loading aborted');
   }
   return _fetchDocument(worker, params, rangeTransport, docId).then(function (workerId) {
    if (task.destroyed) {
     throw new Error('Loading aborted');
    }
    var messageHandler = new MessageHandler(docId, workerId, worker.port);
-   var transport = new WorkerTransport(messageHandler, task, rangeTransport);
+   var transport = new WorkerTransport(messageHandler, task, rangeTransport, CMapReaderFactory);
    task._transport = transport;
    messageHandler.send('Ready', null);
   });
  }).catch(task._capability.reject);
  return task;
 }
 function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
  if (worker.destroyed) {
@@ -2463,18 +2523,16 @@ function _fetchDocument(worker, source, 
   source.length = pdfDataRangeTransport.length;
   source.initialData = pdfDataRangeTransport.initialData;
  }
  return worker.messageHandler.sendWithPromise('GetDocRequest', {
   docId: docId,
   source: source,
   disableRange: getDefaultSetting('disableRange'),
   maxImageSize: getDefaultSetting('maxImageSize'),
-  cMapUrl: getDefaultSetting('cMapUrl'),
-  cMapPacked: getDefaultSetting('cMapPacked'),
   disableFontFace: getDefaultSetting('disableFontFace'),
   disableCreateObjectURL: getDefaultSetting('disableCreateObjectURL'),
   postMessageTransfers: getDefaultSetting('postMessageTransfers') && !isPostMessageTransfersDisabled,
   docBaseUrl: source.docBaseUrl,
   disableNativeImageDecoder: source.disableNativeImageDecoder
  }).then(function (workerId) {
   if (worker.destroyed) {
    throw new Error('Worker was destroyed');
@@ -3076,22 +3134,26 @@ var PDFWorker = function PDFWorkerClosur
     this._messageHandler.destroy();
     this._messageHandler = null;
    }
   }
  };
  return PDFWorker;
 }();
 var WorkerTransport = function WorkerTransportClosure() {
- function WorkerTransport(messageHandler, loadingTask, pdfDataRangeTransport) {
+ function WorkerTransport(messageHandler, loadingTask, pdfDataRangeTransport, CMapReaderFactory) {
   this.messageHandler = messageHandler;
   this.loadingTask = loadingTask;
   this.pdfDataRangeTransport = pdfDataRangeTransport;
   this.commonObjs = new PDFObjects();
   this.fontLoader = new FontLoader(loadingTask.docId);
+  this.CMapReaderFactory = new CMapReaderFactory({
+   baseUrl: getDefaultSetting('cMapUrl'),
+   isCompressed: getDefaultSetting('cMapPacked')
+  });
   this.destroyed = false;
   this.destroyCapability = null;
   this._passwordCapability = null;
   this.pageCache = [];
   this.pagePromises = [];
   this.downloadInfoCapability = createPromiseCapability();
   this.setupMessageHandler();
  }
@@ -3366,16 +3428,22 @@ var WorkerTransport = function WorkerTra
       });
      };
      img.onerror = function () {
       reject(new Error('JpegDecode failed to load image'));
      };
      img.src = imageUrl;
     });
    }, this);
+   messageHandler.on('FetchBuiltInCMap', function (data) {
+    if (this.destroyed) {
+     return Promise.reject(new Error('Worker was destroyed'));
+    }
+    return this.CMapReaderFactory.fetch({ name: data.name });
+   }, this);
   },
   getData: function WorkerTransport_getData() {
    return this.messageHandler.sendWithPromise('GetData', null);
   },
   getPage: function WorkerTransport_getPage(pageNumber, capability) {
    if (!isInt(pageNumber) || pageNumber <= 0 || pageNumber > this.numPages) {
     return Promise.reject(new Error('Invalid page request'));
    }
@@ -3627,18 +3695,18 @@ var _UnsupportedManager = function Unsup
   },
   notify: function (featureId) {
    for (var i = 0, ii = listeners.length; i < ii; i++) {
     listeners[i](featureId);
    }
   }
  };
 }();
-exports.version = '1.7.297';
-exports.build = '425ad309';
+exports.version = '1.7.312';
+exports.build = 'cada411a';
 exports.getDocument = getDocument;
 exports.PDFDataRangeTransport = PDFDataRangeTransport;
 exports.PDFWorker = PDFWorker;
 exports.PDFDocumentProxy = PDFDocumentProxy;
 exports.PDFPageProxy = PDFPageProxy;
 exports._UnsupportedManager = _UnsupportedManager;
 
 /***/ }),
@@ -4645,18 +4713,18 @@ var deprecated = sharedUtil.deprecated;
 var warn = sharedUtil.warn;
 var LinkTarget = displayDOMUtils.LinkTarget;
 var DEFAULT_LINK_REL = displayDOMUtils.DEFAULT_LINK_REL;
 var isWorker = typeof window === 'undefined';
 if (!globalScope.PDFJS) {
  globalScope.PDFJS = {};
 }
 var PDFJS = globalScope.PDFJS;
-PDFJS.version = '1.7.297';
-PDFJS.build = '425ad309';
+PDFJS.version = '1.7.312';
+PDFJS.build = 'cada411a';
 PDFJS.pdfBug = false;
 if (PDFJS.verbosity !== undefined) {
  sharedUtil.setVerbosityLevel(PDFJS.verbosity);
 }
 delete PDFJS.verbosity;
 Object.defineProperty(PDFJS, 'verbosity', {
  get: function () {
   return sharedUtil.getVerbosityLevel();
@@ -7138,18 +7206,18 @@ exports.getShadingPatternFromIR = getSha
 exports.TilingPattern = TilingPattern;
 
 /***/ }),
 /* 13 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
-var pdfjsVersion = '1.7.297';
-var pdfjsBuild = '425ad309';
+var pdfjsVersion = '1.7.312';
+var pdfjsBuild = 'cada411a';
 var pdfjsSharedUtil = __w_pdfjs_require__(0);
 var pdfjsDisplayGlobal = __w_pdfjs_require__(8);
 var pdfjsDisplayAPI = __w_pdfjs_require__(3);
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(5);
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(2);
 var pdfjsDisplayDOMUtils = __w_pdfjs_require__(1);
 var pdfjsDisplaySVG = __w_pdfjs_require__(4);
 exports.PDFJS = pdfjsDisplayGlobal.PDFJS;
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -216,16 +216,21 @@ var FontType = {
  TYPE0: 9,
  MMTYPE1: 10
 };
 var VERBOSITY_LEVELS = {
  errors: 0,
  warnings: 1,
  infos: 5
 };
+var CMapCompressionType = {
+ NONE: 0,
+ BINARY: 1,
+ STREAM: 2
+};
 var OPS = {
  dependency: 1,
  setLineWidth: 2,
  setLineCap: 3,
  setLineJoin: 4,
  setMiterLimit: 5,
  setDash: 6,
  setRenderingIntent: 7,
@@ -1161,16 +1166,22 @@ function isArray(v) {
  return v instanceof Array;
 }
 function isArrayBuffer(v) {
  return typeof v === 'object' && v !== null && v.byteLength !== undefined;
 }
 function isSpace(ch) {
  return ch === 0x20 || ch === 0x09 || ch === 0x0D || ch === 0x0A;
 }
+function isNodeJS() {
+ if (typeof __pdfjsdev_webpack__ === 'undefined') {
+  return typeof process === 'object' && process + '' === '[object process]';
+ }
+ return false;
+}
 function createPromiseCapability() {
  var capability = {};
  capability.promise = new Promise(function (resolve, reject) {
   capability.resolve = resolve;
   capability.reject = reject;
  });
  return capability;
 }
@@ -1433,16 +1444,17 @@ exports.OPS = OPS;
 exports.VERBOSITY_LEVELS = VERBOSITY_LEVELS;
 exports.UNSUPPORTED_FEATURES = UNSUPPORTED_FEATURES;
 exports.AnnotationBorderStyleType = AnnotationBorderStyleType;
 exports.AnnotationFieldFlag = AnnotationFieldFlag;
 exports.AnnotationFlag = AnnotationFlag;
 exports.AnnotationType = AnnotationType;
 exports.FontType = FontType;
 exports.ImageKind = ImageKind;
+exports.CMapCompressionType = CMapCompressionType;
 exports.InvalidPDFException = InvalidPDFException;
 exports.MessageHandler = MessageHandler;
 exports.MissingDataException = MissingDataException;
 exports.MissingPDFException = MissingPDFException;
 exports.NotImplementedException = NotImplementedException;
 exports.PageViewport = PageViewport;
 exports.PasswordException = PasswordException;
 exports.PasswordResponses = PasswordResponses;
@@ -1469,16 +1481,17 @@ exports.info = info;
 exports.isArray = isArray;
 exports.isArrayBuffer = isArrayBuffer;
 exports.isBool = isBool;
 exports.isEmptyObj = isEmptyObj;
 exports.isInt = isInt;
 exports.isNum = isNum;
 exports.isString = isString;
 exports.isSpace = isSpace;
+exports.isNodeJS = isNodeJS;
 exports.isSameOrigin = isSameOrigin;
 exports.createValidAbsoluteUrl = createValidAbsoluteUrl;
 exports.isLittleEndian = isLittleEndian;
 exports.isEvalSupported = isEvalSupported;
 exports.loadJpegStream = loadJpegStream;
 exports.log2 = log2;
 exports.readInt8 = readInt8;
 exports.readUint16 = readUint16;
@@ -24782,16 +24795,17 @@ var coreStandardFonts = __w_pdfjs_requir
 var coreUnicode = __w_pdfjs_require__(16);
 var coreGlyphList = __w_pdfjs_require__(7);
 var FONT_IDENTITY_MATRIX = sharedUtil.FONT_IDENTITY_MATRIX;
 var IDENTITY_MATRIX = sharedUtil.IDENTITY_MATRIX;
 var UNSUPPORTED_FEATURES = sharedUtil.UNSUPPORTED_FEATURES;
 var ImageKind = sharedUtil.ImageKind;
 var OPS = sharedUtil.OPS;
 var TextRenderingMode = sharedUtil.TextRenderingMode;
+var CMapCompressionType = sharedUtil.CMapCompressionType;
 var Util = sharedUtil.Util;
 var assert = sharedUtil.assert;
 var createPromiseCapability = sharedUtil.createPromiseCapability;
 var error = sharedUtil.error;
 var info = sharedUtil.info;
 var isArray = sharedUtil.isArray;
 var isNum = sharedUtil.isNum;
 var isString = sharedUtil.isString;
@@ -24840,20 +24854,16 @@ var getNormalizedUnicodes = coreUnicode.
 var reverseIfRtl = coreUnicode.reverseIfRtl;
 var getUnicodeForGlyph = coreUnicode.getUnicodeForGlyph;
 var getGlyphsUnicode = coreGlyphList.getGlyphsUnicode;
 var PartialEvaluator = function PartialEvaluatorClosure() {
  var DefaultPartialEvaluatorOptions = {
   forceDataSchema: false,
   maxImageSize: -1,
   disableFontFace: false,
-  cMapOptions: {
-   url: null,
-   packed: false
-  },
   disableNativeImageDecoder: false
  };
  function NativeImageDecoder(xref, resources, handler, forceDataSchema) {
   this.xref = xref;
   this.resources = resources;
   this.handler = handler;
   this.forceDataSchema = forceDataSchema;
  }
@@ -24887,24 +24897,37 @@ var PartialEvaluator = function PartialE
  NativeImageDecoder.isDecodable = function NativeImageDecoder_isDecodable(image, xref, res) {
   var dict = image.dict;
   if (dict.has('DecodeParms') || dict.has('DP')) {
    return false;
   }
   var cs = ColorSpace.parse(dict.get('ColorSpace', 'CS'), xref, res);
   return (cs.numComps === 1 || cs.numComps === 3) && cs.isDefaultDecode(dict.getArray('Decode', 'D'));
  };
- function PartialEvaluator(pdfManager, xref, handler, pageIndex, idFactory, fontCache, options) {
+ function PartialEvaluator(pdfManager, xref, handler, pageIndex, idFactory, fontCache, builtInCMapCache, options) {
   this.pdfManager = pdfManager;
   this.xref = xref;
   this.handler = handler;
   this.pageIndex = pageIndex;
   this.idFactory = idFactory;
   this.fontCache = fontCache;
+  this.builtInCMapCache = builtInCMapCache;
   this.options = options || DefaultPartialEvaluatorOptions;
+  this.fetchBuiltInCMap = function (name) {
+   var cachedCMap = builtInCMapCache[name];
+   if (cachedCMap) {
+    return Promise.resolve(cachedCMap);
+   }
+   return handler.sendWithPromise('FetchBuiltInCMap', { name: name }).then(function (data) {
+    if (data.compressionType !== CMapCompressionType.NONE) {
+     builtInCMapCache[name] = data;
+    }
+    return data;
+   });
+  };
  }
  var TIME_SLOT_DURATION_MS = 20;
  var CHECK_TIME_EVERY = 100;
  function TimeSlotManager() {
   this.reset();
  }
  TimeSlotManager.prototype = {
   check: function TimeSlotManager_check() {
@@ -26231,17 +26254,21 @@ var PartialEvaluator = function PartialE
      toUnicode[charcode] = String.fromCharCode(glyphsUnicodeMap[glyphName]);
     }
     return Promise.resolve(new ToUnicodeMap(toUnicode));
    }
    if (properties.composite && (properties.cMap.builtInCMap && !(properties.cMap instanceof IdentityCMap) || properties.cidSystemInfo.registry === 'Adobe' && (properties.cidSystemInfo.ordering === 'GB1' || properties.cidSystemInfo.ordering === 'CNS1' || properties.cidSystemInfo.ordering === 'Japan1' || properties.cidSystemInfo.ordering === 'Korea1'))) {
     var registry = properties.cidSystemInfo.registry;
     var ordering = properties.cidSystemInfo.ordering;
     var ucs2CMapName = Name.get(registry + '-' + ordering + '-UCS2');
-    return CMapFactory.create(ucs2CMapName, this.options.cMapOptions, null).then(function (ucs2CMap) {
+    return CMapFactory.create({
+     encoding: ucs2CMapName,
+     fetchBuiltInCMap: this.fetchBuiltInCMap,
+     useCMap: null
+    }).then(function (ucs2CMap) {
      var cMap = properties.cMap;
      toUnicode = [];
      cMap.forEach(function (charcode, cid) {
       assert(cid <= 0xffff, 'Max size of CID is 65,535');
       var ucs2 = ucs2CMap.lookup(cid);
       if (ucs2) {
        toUnicode[charcode] = String.fromCharCode((ucs2.charCodeAt(0) << 8) + ucs2.charCodeAt(1));
       }
@@ -26249,24 +26276,32 @@ var PartialEvaluator = function PartialE
      return new ToUnicodeMap(toUnicode);
     });
    }
    return Promise.resolve(new IdentityToUnicodeMap(properties.firstChar, properties.lastChar));
   },
   readToUnicode: function PartialEvaluator_readToUnicode(toUnicode) {
    var cmapObj = toUnicode;
    if (isName(cmapObj)) {
-    return CMapFactory.create(cmapObj, this.options.cMapOptions, null).then(function (cmap) {
+    return CMapFactory.create({
+     encoding: cmapObj,
+     fetchBuiltInCMap: this.fetchBuiltInCMap,
+     useCMap: null
+    }).then(function (cmap) {
      if (cmap instanceof IdentityCMap) {
       return new IdentityToUnicodeMap(0, 0xFFFF);
      }
      return new ToUnicodeMap(cmap.getMap());
     });
    } else if (isStream(cmapObj)) {
-    return CMapFactory.create(cmapObj, this.options.cMapOptions, null).then(function (cmap) {
+    return CMapFactory.create({
+     encoding: cmapObj,
+     fetchBuiltInCMap: this.fetchBuiltInCMap,
+     useCMap: null
+    }).then(function (cmap) {
      if (cmap instanceof IdentityCMap) {
       return new IdentityToUnicodeMap(0, 0xFFFF);
      }
      var map = new Array(cmap.length);
      cmap.forEach(function (charCode, token) {
       var str = [];
       for (var k = 0; k < token.length; k += 2) {
        var w1 = token.charCodeAt(k) << 8 | token.charCodeAt(k + 1);
@@ -26519,17 +26554,16 @@ var PartialEvaluator = function PartialE
   },
   translateFont: function PartialEvaluator_translateFont(preEvaluatedFont, xref) {
    var baseDict = preEvaluatedFont.baseDict;
    var dict = preEvaluatedFont.dict;
    var composite = preEvaluatedFont.composite;
    var descriptor = preEvaluatedFont.descriptor;
    var type = preEvaluatedFont.type;
    var maxCharIndex = composite ? 0xFFFF : 0xFF;
-   var cMapOptions = this.options.cMapOptions;
    var properties;
    if (!descriptor) {
     if (type === 'Type3') {
      descriptor = new Dict(null);
      descriptor.set('FontName', Name.get(type));
      descriptor.set('FontBBox', dict.getArray('FontBBox'));
     } else {
      var baseFontName = dict.get('BaseFont');
@@ -26614,17 +26648,21 @@ var PartialEvaluator = function PartialE
     coded: false
    };
    var cMapPromise;
    if (composite) {
     var cidEncoding = baseDict.get('Encoding');
     if (isName(cidEncoding)) {
      properties.cidEncoding = cidEncoding.name;
     }
-    cMapPromise = CMapFactory.create(cidEncoding, cMapOptions, null).then(function (cMap) {
+    cMapPromise = CMapFactory.create({
+     encoding: cidEncoding,
+     fetchBuiltInCMap: this.fetchBuiltInCMap,
+     useCMap: null
+    }).then(function (cMap) {
      properties.cMap = cMap;
      properties.vertical = properties.cMap.vertical;
     });
    } else {
     cMapPromise = Promise.resolve(undefined);
    }
    return cMapPromise.then(function () {
     return this.extractDataStructures(dict, baseDict, xref, properties);
@@ -29867,16 +29905,17 @@ var Parser = coreParser.Parser;
 var ChunkedStream = coreChunkedStream.ChunkedStream;
 var ColorSpace = coreColorSpace.ColorSpace;
 var Catalog = function CatalogClosure() {
  function Catalog(pdfManager, xref, pageFactory) {
   this.pdfManager = pdfManager;
   this.xref = xref;
   this.catDict = xref.getCatalogObj();
   this.fontCache = new RefSetCache();
+  this.builtInCMapCache = Object.create(null);
   assert(isDict(this.catDict), 'catalog object is not a dictionary');
   this.pageFactory = pageFactory;
   this.pagePromises = [];
  }
  Catalog.prototype = {
   get metadata() {
    var streamRef = this.catDict.getRaw('Metadata');
    if (!isRef(streamRef)) {
@@ -30181,24 +30220,25 @@ var Catalog = function CatalogClosure() 
     promises.push(promise);
    });
    return Promise.all(promises).then(function (translatedFonts) {
     for (var i = 0, ii = translatedFonts.length; i < ii; i++) {
      var font = translatedFonts[i].dict;
      delete font.translated;
     }
     this.fontCache.clear();
+    this.builtInCMapCache = Object.create(null);
    }.bind(this));
   },
   getPage: function Catalog_getPage(pageIndex) {
    if (!(pageIndex in this.pagePromises)) {
     this.pagePromises[pageIndex] = this.getPageDict(pageIndex).then(function (a) {
      var dict = a[0];
      var ref = a[1];
-     return this.pageFactory.createPage(pageIndex, dict, ref, this.fontCache);
+     return this.pageFactory.createPage(pageIndex, dict, ref, this.fontCache, this.builtInCMapCache);
     }.bind(this));
    }
    return this.pagePromises[pageIndex];
   },
   getPageDict: function Catalog_getPageDict(pageIndex) {
    var capability = createPromiseCapability();
    var nodesToVisit = [this.catDict.getRaw('Pages')];
    var currentPageIndex = 0;
@@ -33921,16 +33961,17 @@ var XRefParseException = sharedUtil.XRef
 var arrayByteLength = sharedUtil.arrayByteLength;
 var arraysToBytes = sharedUtil.arraysToBytes;
 var assert = sharedUtil.assert;
 var createPromiseCapability = sharedUtil.createPromiseCapability;
 var error = sharedUtil.error;
 var info = sharedUtil.info;
 var warn = sharedUtil.warn;
 var setVerbosityLevel = sharedUtil.setVerbosityLevel;
+var isNodeJS = sharedUtil.isNodeJS;
 var Ref = corePrimitives.Ref;
 var LocalPdfManager = corePdfManager.LocalPdfManager;
 var NetworkPdfManager = corePdfManager.NetworkPdfManager;
 var globalScope = sharedUtil.globalScope;
 var WorkerTask = function WorkerTaskClosure() {
  function WorkerTask(name) {
   this.name = name;
   this.terminated = false;
@@ -34398,25 +34439,20 @@ var WorkerMessageHandler = {
      pdfManager.requestLoadedStream();
      pdfManager.onLoadedStream().then(function () {
       ensureNotTerminated();
       loadDocument(true).then(onSuccess, onFailure);
      });
     }, onFailure);
    }
    ensureNotTerminated();
-   var cMapOptions = {
-    url: data.cMapUrl === undefined ? null : data.cMapUrl,
-    packed: data.cMapPacked === true
-   };
    var evaluatorOptions = {
     forceDataSchema: data.disableCreateObjectURL,
     maxImageSize: data.maxImageSize === undefined ? -1 : data.maxImageSize,
     disableFontFace: data.disableFontFace,
-    cMapOptions: cMapOptions,
     disableNativeImageDecoder: data.disableNativeImageDecoder
    };
    getPdfManager(data, evaluatorOptions).then(function (newPdfManager) {
     if (terminated) {
      newPdfManager.terminate();
      throw new Error('Worker was terminated');
     }
     pdfManager = newPdfManager;
@@ -34583,22 +34619,16 @@ var WorkerMessageHandler = {
   return workerHandlerName;
  }
 };
 function initializeWorker() {
  var handler = new MessageHandler('worker', 'main', self);
  WorkerMessageHandler.setup(handler, self);
  handler.send('ready', null);
 }
-function isNodeJS() {
- if (typeof __pdfjsdev_webpack__ === 'undefined') {
-  return typeof process === 'object' && process + '' === '[object process]';
- }
- return false;
-}
 if (typeof window === 'undefined' && !isNodeJS()) {
  initializeWorker();
 }
 exports.setPDFNetworkStreamClass = setPDFNetworkStreamClass;
 exports.WorkerTask = WorkerTask;
 exports.WorkerMessageHandler = WorkerMessageHandler;
 
 /***/ }),
@@ -36575,21 +36605,22 @@ var coreStream = __w_pdfjs_require__(2);
 var coreParser = __w_pdfjs_require__(5);
 var Util = sharedUtil.Util;
 var assert = sharedUtil.assert;
 var warn = sharedUtil.warn;
 var error = sharedUtil.error;
 var isInt = sharedUtil.isInt;
 var isString = sharedUtil.isString;
 var MissingDataException = sharedUtil.MissingDataException;
+var CMapCompressionType = sharedUtil.CMapCompressionType;
 var isEOF = corePrimitives.isEOF;
 var isName = corePrimitives.isName;
 var isCmd = corePrimitives.isCmd;
 var isStream = corePrimitives.isStream;
-var StringStream = coreStream.StringStream;
+var Stream = coreStream.Stream;
 var Lexer = coreParser.Lexer;
 var BUILT_IN_CMAPS = [
  'Adobe-GB1-UCS2',
  'Adobe-CNS1-UCS2',
  'Adobe-Japan1-UCS2',
  'Adobe-Korea1-UCS2',
  '78-EUC-H',
  '78-EUC-V',
@@ -36914,33 +36945,16 @@ var IdentityCMap = function IdentityCMap
   },
   get isIdentityCMap() {
    error('should not access .isIdentityCMap');
   }
  };
  return IdentityCMap;
 }();
 var BinaryCMapReader = function BinaryCMapReaderClosure() {
- function fetchBinaryData(url) {
-  return new Promise(function (resolve, reject) {
-   var request = new XMLHttpRequest();
-   request.open('GET', url, true);
-   request.responseType = 'arraybuffer';
-   request.onreadystatechange = function () {
-    if (request.readyState === XMLHttpRequest.DONE) {
-     if (!request.response || request.status !== 200 && request.status !== 0) {
-      reject(new Error('Unable to get binary cMap at: ' + url));
-     } else {
-      resolve(new Uint8Array(request.response));
-     }
-    }
-   };
-   request.send(null);
-  });
- }
  function hexToInt(a, size) {
   var n = 0;
   for (var i = 0; i <= size; i++) {
    n = n << 8 | a[i];
   }
   return n >>> 0;
  }
  function hexToStr(a, size) {
@@ -37040,18 +37054,18 @@ var BinaryCMapReader = function BinaryCM
    var len = this.readNumber();
    var s = '';
    for (var i = 0; i < len; i++) {
     s += String.fromCharCode(this.readNumber());
    }
    return s;
   }
  };
- function processBinaryCMap(url, cMap, extend) {
-  return fetchBinaryData(url).then(function (data) {
+ function processBinaryCMap(data, cMap, extend) {
+  return new Promise(function (resolve, reject) {
    var stream = new BinaryCMapStream(data);
    var header = stream.readByte();
    cMap.vertical = !!(header & 1);
    var useCMap = null;
    var start = new Uint8Array(MAX_NUM_SIZE);
    var end = new Uint8Array(MAX_NUM_SIZE);
    var char = new Uint8Array(MAX_NUM_SIZE);
    var charCode = new Uint8Array(MAX_NUM_SIZE);
@@ -37172,29 +37186,30 @@ var BinaryCMapReader = function BinaryCM
       }
       stream.readHexNumber(end, ucs2DataSize);
       addHex(end, start, ucs2DataSize);
       stream.readHex(charCode, dataSize);
       cMap.mapBfRange(hexToInt(start, ucs2DataSize), hexToInt(end, ucs2DataSize), hexToStr(charCode, dataSize));
      }
      break;
     default:
-     error('Unknown type: ' + type);
-     break;
+     reject(new Error('processBinaryCMap: Unknown type: ' + type));
+     return;
     }
    }
    if (useCMap) {
-    return extend(useCMap);
-   }
-   return cMap;
+    resolve(extend(useCMap));
+    return;
+   }
+   resolve(cMap);
   });
  }
  function BinaryCMapReader() {
  }
- BinaryCMapReader.prototype = { read: processBinaryCMap };
+ BinaryCMapReader.prototype = { process: processBinaryCMap };
  return BinaryCMapReader;
 }();
 var CMapFactory = function CMapFactoryClosure() {
  function strToInt(str) {
   var a = 0;
   for (var i = 0; i < str.length; i++) {
    a = a << 8 | str.charCodeAt(i);
   }
@@ -37325,17 +37340,17 @@ var CMapFactory = function CMapFactoryCl
   }
  }
  function parseCMapName(cMap, lexer) {
   var obj = lexer.getObj();
   if (isName(obj) && isString(obj.name)) {
    cMap.name = obj.name;
   }
  }
- function parseCMap(cMap, lexer, builtInCMapParams, useCMap) {
+ function parseCMap(cMap, lexer, fetchBuiltInCMap, useCMap) {
   var previous;
   var embededUseCMap;
   objLoop:
    while (true) {
     try {
      var obj = lexer.getObj();
      if (isEOF(obj)) {
       break;
@@ -37379,88 +37394,74 @@ var CMapFactory = function CMapFactoryCl
      warn('Invalid cMap data: ' + ex);
      continue;
     }
    }
   if (!useCMap && embededUseCMap) {
    useCMap = embededUseCMap;
   }
   if (useCMap) {
-   return extendCMap(cMap, builtInCMapParams, useCMap);
+   return extendCMap(cMap, fetchBuiltInCMap, useCMap);
   }
   return Promise.resolve(cMap);
  }
- function extendCMap(cMap, builtInCMapParams, useCMap) {
-  return createBuiltInCMap(useCMap, builtInCMapParams).then(function (newCMap) {
+ function extendCMap(cMap, fetchBuiltInCMap, useCMap) {
+  return createBuiltInCMap(useCMap, fetchBuiltInCMap).then(function (newCMap) {
    cMap.useCMap = newCMap;
    if (cMap.numCodespaceRanges === 0) {
     var useCodespaceRanges = cMap.useCMap.codespaceRanges;
     for (var i = 0; i < useCodespaceRanges.length; i++) {
      cMap.codespaceRanges[i] = useCodespaceRanges[i].slice();
     }
     cMap.numCodespaceRanges = cMap.useCMap.numCodespaceRanges;
    }
    cMap.useCMap.forEach(function (key, value) {
     if (!cMap.contains(key)) {
      cMap.mapOne(key, cMap.useCMap.lookup(key));
     }
    });
    return cMap;
   });
  }
- function parseBinaryCMap(name, builtInCMapParams) {
-  var url = builtInCMapParams.url + name + '.bcmap';
-  var cMap = new CMap(true);
-  return new BinaryCMapReader().read(url, cMap, function (useCMap) {
-   return extendCMap(cMap, builtInCMapParams, useCMap);
-  });
- }
- function createBuiltInCMap(name, builtInCMapParams) {
+ function createBuiltInCMap(name, fetchBuiltInCMap) {
   if (name === 'Identity-H') {
    return Promise.resolve(new IdentityCMap(false, 2));
   } else if (name === 'Identity-V') {
    return Promise.resolve(new IdentityCMap(true, 2));
   }
   if (BUILT_IN_CMAPS.indexOf(name) === -1) {
    return Promise.reject(new Error('Unknown cMap name: ' + name));
   }
-  assert(builtInCMapParams, 'built-in cMap parameters are not provided');
-  if (builtInCMapParams.packed) {
-   return parseBinaryCMap(name, builtInCMapParams);
-  }
-  return new Promise(function (resolve, reject) {
-   var url = builtInCMapParams.url + name;
-   var request = new XMLHttpRequest();
-   request.onreadystatechange = function () {
-    if (request.readyState === XMLHttpRequest.DONE) {
-     if (request.status === 200 || request.status === 0) {
-      var cMap = new CMap(true);
-      var lexer = new Lexer(new StringStream(request.responseText));
-      parseCMap(cMap, lexer, builtInCMapParams, null).then(function (parsedCMap) {
-       resolve(parsedCMap);
-      });
-     } else {
-      reject(new Error('Unable to get cMap at: ' + url));
-     }
-    }
-   };
-   request.open('GET', url, true);
-   request.send(null);
+  assert(fetchBuiltInCMap, 'Built-in CMap parameters are not provided.');
+  return fetchBuiltInCMap(name).then(function (data) {
+   var cMapData = data.cMapData, compressionType = data.compressionType;
+   var cMap = new CMap(true);
+   if (compressionType === CMapCompressionType.BINARY) {
+    return new BinaryCMapReader().process(cMapData, cMap, function (useCMap) {
+     return extendCMap(cMap, fetchBuiltInCMap, useCMap);
+    });
+   }
+   assert(compressionType === CMapCompressionType.NONE, 'TODO: Only BINARY/NONE CMap compression is currently supported.');
+   var lexer = new Lexer(new Stream(cMapData));
+   return parseCMap(cMap, lexer, fetchBuiltInCMap, null);
   });
  }
  return {
-  create: function (encoding, builtInCMapParams, useCMap) {
+  create: function (params) {
+   var encoding = params.encoding;
+   var fetchBuiltInCMap = params.fetchBuiltInCMap;
+   var useCMap = params.useCMap;
    if (isName(encoding)) {
-    return createBuiltInCMap(encoding.name, builtInCMapParams);
+    return createBuiltInCMap(encoding.name, fetchBuiltInCMap);
    } else if (isStream(encoding)) {
     var cMap = new CMap();
     var lexer = new Lexer(encoding);
-    return parseCMap(cMap, lexer, builtInCMapParams, useCMap).then(function (parsedCMap) {
+    return parseCMap(cMap, lexer, fetchBuiltInCMap, useCMap).then(function (parsedCMap) {
      if (parsedCMap.isIdentityCMap) {
-      return createBuiltInCMap(parsedCMap.name, builtInCMapParams);
+      return createBuiltInCMap(parsedCMap.name, fetchBuiltInCMap);
      }
      return parsedCMap;
     });
    }
    return Promise.reject(new Error('Encoding required.'));
   }
  };
 }();
@@ -37515,23 +37516,24 @@ var AnnotationFactory = coreAnnotation.A
 var Page = function PageClosure() {
  var DEFAULT_USER_UNIT = 1.0;
  var LETTER_SIZE_MEDIABOX = [
   0,
   0,
   612,
   792
  ];
- function Page(pdfManager, xref, pageIndex, pageDict, ref, fontCache) {
+ function Page(pdfManager, xref, pageIndex, pageDict, ref, fontCache, builtInCMapCache) {
   this.pdfManager = pdfManager;
   this.pageIndex = pageIndex;
   this.pageDict = pageDict;
   this.xref = xref;
   this.ref = ref;
   this.fontCache = fontCache;
+  this.builtInCMapCache = builtInCMapCache;
   this.evaluatorOptions = pdfManager.evaluatorOptions;
   this.resourcesPromise = null;
   var uniquePrefix = 'p' + this.pageIndex + '_';
   var idCounters = { obj: 0 };
   this.idFactory = {
    createObjId: function () {
     return uniquePrefix + ++idCounters.obj;
    }
@@ -37647,17 +37649,17 @@ var Page = function PageClosure() {
    var resourcesPromise = this.loadResources([
     'ExtGState',
     'ColorSpace',
     'Pattern',
     'Shading',
     'XObject',
     'Font'
    ]);
-   var partialEvaluator = new PartialEvaluator(pdfManager, this.xref, handler, this.pageIndex, this.idFactory, this.fontCache, this.evaluatorOptions);
+   var partialEvaluator = new PartialEvaluator(pdfManager, this.xref, handler, this.pageIndex, this.idFactory, this.fontCache, this.builtInCMapCache, this.evaluatorOptions);
    var dataPromises = Promise.all([
     contentStreamPromise,
     resourcesPromise
    ]);
    var pageListPromise = dataPromises.then(function (data) {
     var contentStream = data[0];
     var opList = new OperatorList(intent, handler, self.pageIndex);
     handler.send('StartRenderPage', {
@@ -37703,17 +37705,17 @@ var Page = function PageClosure() {
     'Font'
    ]);
    var dataPromises = Promise.all([
     contentStreamPromise,
     resourcesPromise
    ]);
    return dataPromises.then(function (data) {
     var contentStream = data[0];
-    var partialEvaluator = new PartialEvaluator(pdfManager, self.xref, handler, self.pageIndex, self.idFactory, self.fontCache, self.evaluatorOptions);
+    var partialEvaluator = new PartialEvaluator(pdfManager, self.xref, handler, self.pageIndex, self.idFactory, self.fontCache, self.builtInCMapCache, self.evaluatorOptions);
     return partialEvaluator.getTextContent(contentStream, task, self.resources, null, normalizeWhitespace, combineTextItems);
    });
   },
   getAnnotationsData: function Page_getAnnotationsData(intent) {
    var annotations = this.annotations;
    var annotationsData = [];
    for (var i = 0, n = annotations.length; i < n; ++i) {
     if (intent) {
@@ -37896,18 +37898,18 @@ var PDFDocument = function PDFDocumentCl
   parseStartXRef: function PDFDocument_parseStartXRef() {
    var startXRef = this.startXRef;
    this.xref.setStartXRef(startXRef);
   },
   setup: function PDFDocument_setup(recoveryMode) {
    this.xref.parse(recoveryMode);
    var self = this;
    var pageFactory = {
-    createPage: function (pageIndex, dict, ref, fontCache) {
-     return new Page(self.pdfManager, self.xref, pageIndex, dict, ref, fontCache);
+    createPage: function (pageIndex, dict, ref, fontCache, builtInCMapCache) {
+     return new Page(self.pdfManager, self.xref, pageIndex, dict, ref, fontCache, builtInCMapCache);
     }
    };
    this.catalog = new Catalog(this.pdfManager, this.xref, pageFactory);
   },
   get numPages() {
    var linearization = this.linearization;
    var num = linearization ? linearization.numPages : this.catalog.numPages;
    return shadow(this, 'numPages', num);
@@ -49169,17 +49171,17 @@ var Type1Parser = function Type1ParserCl
 exports.Type1Parser = Type1Parser;
 
 /***/ }),
 /* 35 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
-var pdfjsVersion = '1.7.297';
-var pdfjsBuild = '425ad309';
+var pdfjsVersion = '1.7.312';
+var pdfjsBuild = 'cada411a';
 var pdfjsCoreWorker = __w_pdfjs_require__(17);
 ;
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ })
 /******/ ]);
 });
\ No newline at end of file
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -316,31 +316,34 @@ function getVisibleElements(scrollEl, vi
   first: first,
   last: last,
   views: visible
  };
 }
 function noContextMenuHandler(e) {
  e.preventDefault();
 }
-function getPDFFileNameFromURL(url) {
- var reURI = /^(?:([^:]+:)?\/\/[^\/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/;
+function getPDFFileNameFromURL(url, defaultFilename) {
+ if (typeof defaultFilename === 'undefined') {
+  defaultFilename = 'document.pdf';
+ }
+ var reURI = /^(?:(?:[^:]+:)?\/\/[^\/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/;
  var reFilename = /[^\/?#=]+\.pdf\b(?!.*\.pdf\b)/i;
  var splitURI = reURI.exec(url);
  var suggestedFilename = reFilename.exec(splitURI[1]) || reFilename.exec(splitURI[2]) || reFilename.exec(splitURI[3]);
  if (suggestedFilename) {
   suggestedFilename = suggestedFilename[0];
   if (suggestedFilename.indexOf('%') !== -1) {
    try {
     suggestedFilename = reFilename.exec(decodeURIComponent(suggestedFilename))[0];
    } catch (e) {
    }
   }
  }
- return suggestedFilename || 'document.pdf';
+ return suggestedFilename || defaultFilename;
 }
 function normalizeWheelEventDelta(evt) {
  var delta = Math.sqrt(evt.deltaX * evt.deltaX + evt.deltaY * evt.deltaY);
  var angle = Math.atan2(evt.deltaY, evt.deltaX);
  if (-0.25 * Math.PI < angle && angle < 0.75 * Math.PI) {
   delta = -delta;
  }
  var MOUSE_DOM_DELTA_PIXEL_MODE = 0;
@@ -1143,21 +1146,25 @@ var PDFViewerApplication = {
    onProgress: function (loaded, total) {
     PDFViewerApplication.progress(loaded / total);
    }
   });
  },
  setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) {
   this.url = url;
   this.baseUrl = url.split('#')[0];
-  try {
-   this.setTitle(decodeURIComponent(pdfjsLib.getFilenameFromUrl(url)) || url);
-  } catch (e) {
-   this.setTitle(url);
-  }
+  var title = getPDFFileNameFromURL(url, '');
+  if (!title) {
+   try {
+    title = decodeURIComponent(pdfjsLib.getFilenameFromUrl(url)) || url;
+   } catch (e) {
+    title = url;
+   }
+  }
+  this.setTitle(title);
  },
  setTitle: function pdfViewSetTitle(title) {
   if (this.isViewerEmbedded) {
    return;
   }
   document.title = title;
  },
  close: function pdfViewClose() {
@@ -1246,17 +1253,17 @@ var PDFViewerApplication = {
    throw new Error(loadingErrorMessage);
   });
  },
  download: function pdfViewDownload() {
   function downloadByUrl() {
    downloadManager.downloadUrl(url, filename);
   }
   var url = this.baseUrl;
-  var filename = getPDFFileNameFromURL(url);
+  var filename = getPDFFileNameFromURL(this.url);
   var downloadManager = this.downloadManager;
   downloadManager.onerror = function (err) {
    PDFViewerApplication.error('PDF failed to download.');
   };
   if (!this.pdfDocument) {
    downloadByUrl();
    return;
   }
@@ -3891,16 +3898,28 @@ var PDFAttachmentViewer = function PDFAt
   },
   _dispatchEvent: function PDFAttachmentViewer_dispatchEvent(attachmentsCount) {
    this.eventBus.dispatch('attachmentsloaded', {
     source: this,
     attachmentsCount: attachmentsCount
    });
    this._renderedCapability.resolve();
   },
+  _bindPdfLink: function PDFAttachmentViewer_bindPdfLink(button, content, filename) {
+   var blobUrl;
+   button.onclick = function () {
+    if (!blobUrl) {
+     blobUrl = pdfjsLib.createObjectURL(content, 'application/pdf', pdfjsLib.PDFJS.disableCreateObjectURL);
+    }
+    var viewerUrl;
+    viewerUrl = blobUrl + '?' + encodeURIComponent(filename);
+    window.open(viewerUrl);
+    return false;
+   };
+  },
   _bindLink: function PDFAttachmentViewer_bindLink(button, content, filename) {
    button.onclick = function downloadFile(e) {
     this.downloadManager.downloadData(content, filename, '');
     return false;
    }.bind(this);
   },
   render: function PDFAttachmentViewer_render(params) {
    params = params || {};
@@ -3917,21 +3936,26 @@ var PDFAttachmentViewer = function PDFAt
    }
    var names = Object.keys(attachments).sort(function (a, b) {
     return a.toLowerCase().localeCompare(b.toLowerCase());
    });
    attachmentsCount = names.length;
    for (var i = 0; i < attachmentsCount; i++) {
     var item = attachments[names[i]];
     var filename = pdfjsLib.getFilenameFromUrl(item.filename);
+    filename = pdfjsLib.removeNullCharacters(filename);
     var div = document.createElement('div');
     div.className = 'attachmentsItem';
     var button = document.createElement('button');
-    this._bindLink(button, item.content, filename);
-    button.textContent = pdfjsLib.removeNullCharacters(filename);
+    button.textContent = filename;
+    if (/\.pdf$/i.test(filename)) {
+     this._bindPdfLink(button, item.content, filename);
+    } else {
+     this._bindLink(button, item.content, filename);
+    }
     div.appendChild(button);
     this.container.appendChild(div);
    }
    this._dispatchEvent(attachmentsCount);
   },
   _appendAttachment: function PDFAttachmentViewer_appendAttachment(item) {
    this._renderedCapability.promise.then(function (id, filename, content) {
     var attachments = this.attachments;
--- a/browser/installer/windows/nsis/maintenanceservice_installer.nsi
+++ b/browser/installer/windows/nsis/maintenanceservice_installer.nsi
@@ -144,24 +144,24 @@ Section "MaintenanceService"
   ; disk as maintenanceservice.exe directly.
   StrCpy $TempMaintServiceName "maintenanceservice.exe"
   IfFileExists "$INSTDIR\maintenanceservice.exe" 0 skipAlreadyExists
     StrCpy $TempMaintServiceName "maintenanceservice_tmp.exe"
   skipAlreadyExists:
 
   ; We always write out a copy and then decide whether to install it or 
   ; not via calling its 'install' cmdline which works by version comparison.
-  CopyFiles "$EXEDIR\maintenanceservice.exe" "$INSTDIR\$TempMaintServiceName"
+  CopyFiles /SILENT "$EXEDIR\maintenanceservice.exe" "$INSTDIR\$TempMaintServiceName"
 
   ; The updater.ini file is only used when performing an install or upgrade,
   ; and only if that install or upgrade is successful.  If an old updater.ini
   ; happened to be copied into the maintenance service installation directory
   ; but the service was not newer, the updater.ini file would be unused.
   ; It is used to fill the description of the service on success.
-  CopyFiles "$EXEDIR\updater.ini" "$INSTDIR\updater.ini"
+  CopyFiles /SILENT "$EXEDIR\updater.ini" "$INSTDIR\updater.ini"
 
   ; Install the application maintenance service.
   ; If a service already exists, the command line parameter will stop the
   ; service and only install itself if it is newer than the already installed
   ; service.  If successful it will remove the old maintenanceservice.exe
   ; and replace it with maintenanceservice_tmp.exe.
   ClearErrors
   ${GetParameters} $0
--- a/browser/modules/ExtensionsUI.jsm
+++ b/browser/modules/ExtensionsUI.jsm
@@ -20,23 +20,24 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyPreferenceGetter(this, "WEBEXT_PERMISSION_PROMPTS",
                                       "extensions.webextPermissionPrompts", false);
 
 const DEFAULT_EXTENSION_ICON = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
 
 const BROWSER_PROPERTIES = "chrome://browser/locale/browser.properties";
-const BRAND_PROPERTIES = "chrome://browser/locale/brand.properties";
+const BRAND_PROPERTIES = "chrome://branding/locale/brand.properties";
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 
 this.ExtensionsUI = {
   sideloaded: new Set(),
   updates: new Set(),
+  sideloadListener: null,
 
   init() {
     Services.obs.addObserver(this, "webextension-permission-prompt", false);
     Services.obs.addObserver(this, "webextension-update-permissions", false);
     Services.obs.addObserver(this, "webextension-install-notify", false);
 
     this._checkForSideloaded();
   },
@@ -48,16 +49,35 @@ this.ExtensionsUI = {
       let sideloaded = addons.filter(
         addon => addon.seen === false && (addon.permissions & AddonManager.PERM_CAN_ENABLE));
 
       if (!sideloaded.length) {
         return;
       }
 
       if (WEBEXT_PERMISSION_PROMPTS) {
+        if (!this.sideloadListener) {
+          this.sideloadListener = {
+            onEnabled: addon => {
+              if (!this.sideloaded.has(addon)) {
+                return;
+              }
+
+              this.sideloaded.delete(addon);
+              this.emit("change");
+
+              if (this.sideloaded.size == 0) {
+                AddonManager.removeAddonListener(this.sideloadListener);
+                this.sideloadListener = null;
+              }
+            },
+          };
+          AddonManager.addAddonListener(this.sideloadListener);
+        }
+
         for (let addon of sideloaded) {
           this.sideloaded.add(addon);
         }
         this.emit("change");
       } else {
         // This and all the accompanying about:newaddon code can eventually
         // be removed.  See bug 1331521.
         let win = RecentWindow.getMostRecentBrowserWindow();
@@ -115,32 +135,24 @@ this.ExtensionsUI = {
       // Dismiss the progress notification.  Note that this is bad if
       // there are multiple simultaneous installs happening, see
       // bug 1329884 for a longer explanation.
       let progressNotification = target.ownerGlobal.PopupNotifications.getNotification("addon-progress", target);
       if (progressNotification) {
         progressNotification.remove();
       }
 
-      let reply = answer => {
+      let strings = this._buildStrings(info);
+      this.showPermissionsPrompt(target, strings, info.icon).then(answer => {
         if (answer) {
           info.resolve();
         } else {
           info.reject();
         }
-      };
-
-      let perms = info.addon.userPermissions;
-      if (!perms) {
-        reply(true);
-      } else {
-        info.permissions = perms;
-        let strings = this._buildStrings(info);
-        this.showPermissionsPrompt(target, strings, info.icon).then(reply);
-      }
+      });
     } else if (topic == "webextension-update-permissions") {
       let info = subject.wrappedJSObject;
       info.type = "update";
       let strings = this._buildStrings(info);
 
       // If we don't prompt for any new permissions, just apply it
       if (strings.msgs.length == 0) {
         info.resolve();
--- a/build/moz.configure/warnings.configure
+++ b/build/moz.configure/warnings.configure
@@ -73,16 +73,19 @@ check_and_add_gcc_warning('-Wimplicit-fa
 # --enable-warnings-as-errors is specified so that no unexpected fatal
 # warnings are produced.
 check_and_add_gcc_warning('-Werror=non-literal-null-conversion',
                           when='--enable-warnings-as-errors')
 
 # catches string literals used in boolean expressions
 check_and_add_gcc_warning('-Wstring-conversion')
 
+# catches inconsistent use of mutexes
+check_and_add_gcc_warning('-Wthread-safety')
+
 # we inline 'new' and 'delete' in mozalloc
 check_and_add_gcc_warning('-Wno-inline-new-delete', cxx_compiler)
 
 # Prevent the following GCC warnings from being treated as errors:
 # too many false positives
 check_and_add_gcc_warning('-Wno-error=maybe-uninitialized')
 
 # we don't want our builds held hostage when a platform-specific API
--- a/build/templates.mozbuild
+++ b/build/templates.mozbuild
@@ -56,25 +56,28 @@ def CppUnitTests(names, ext='.cpp'):
 
 @template
 def Library(name):
     '''Template for libraries.'''
     LIBRARY_NAME = name
 
 
 @template
-def RustLibrary(name, features=None):
+def RustLibrary(name, features=None, target_dir=None):
     '''Template for Rust libraries.'''
     Library(name)
 
     IS_RUST_LIBRARY = True
 
     if features:
         RUST_LIBRARY_FEATURES = features
 
+    if target_dir:
+        RUST_LIBRARY_TARGET_DIR = target_dir
+
 
 @template
 def SharedLibrary(name):
     '''Template for shared libraries.'''
     Library(name)
 
     FORCE_SHARED_LIB = True
 
--- a/caps/nsNullPrincipal.cpp
+++ b/caps/nsNullPrincipal.cpp
@@ -161,30 +161,48 @@ nsNullPrincipal::GetBaseDomain(nsACStrin
  */
 NS_IMETHODIMP
 nsNullPrincipal::Read(nsIObjectInputStream* aStream)
 {
   // Note - nsNullPrincipal use NS_GENERIC_FACTORY_CONSTRUCTOR_INIT, which means
   // that the Init() method has already been invoked by the time we deserialize.
   // This is in contrast to nsPrincipal, which uses NS_GENERIC_FACTORY_CONSTRUCTOR,
   // in which case ::Read needs to invoke Init().
-  nsAutoCString suffix;
-  nsresult rv = aStream->ReadCString(suffix);
+
+  nsAutoCString spec;
+  nsresult rv = aStream->ReadCString(spec);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIURI> uri;
+  rv = NS_NewURI(getter_AddRefs(uri), spec);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  bool ok = mOriginAttributes.PopulateFromSuffix(suffix);
+  nsAutoCString suffix;
+  rv = aStream->ReadCString(suffix);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  OriginAttributes attrs;
+  bool ok = attrs.PopulateFromSuffix(suffix);
   NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
 
-  return NS_OK;
+  return Init(attrs, uri);
 }
 
 NS_IMETHODIMP
 nsNullPrincipal::Write(nsIObjectOutputStream* aStream)
 {
+  NS_ENSURE_STATE(mURI);
+
+  nsAutoCString spec;
+  nsresult rv = mURI->GetSpec(spec);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = aStream->WriteStringZ(spec.get());
+  NS_ENSURE_SUCCESS(rv, rv);
+
   nsAutoCString suffix;
   OriginAttributesRef().CreateSuffix(suffix);
 
-  nsresult rv = aStream->WriteStringZ(suffix.get());
+  rv = aStream->WriteStringZ(suffix.get());
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
-
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -931,17 +931,17 @@ rustflags = -C opt-level=0
 # to explicitly disable them when MOZ_DEBUG is not set.
 ifndef MOZ_DEBUG
 rustflags += -C debug-assertions=no
 endif
 rustflags_override = RUSTFLAGS='$(rustflags)'
 endif
 
 CARGO_BUILD = env $(rustflags_override) \
-	CARGO_TARGET_DIR=. \
+	CARGO_TARGET_DIR=$(CARGO_TARGET_DIR) \
 	RUSTC=$(RUSTC) \
 	MOZ_DIST=$(ABS_DIST) \
 	LIBCLANG_PATH=$(MOZ_LIBCLANG_PATH) \
 	CLANG_PATH=$(MOZ_CLANG_PATH) \
 	PKG_CONFIG_ALLOW_CROSS=1 \
 	$(CARGO) build $(cargo_build_flags)
 
 ifdef RUST_LIBRARY_FILE
--- a/devtools/client/framework/ToolboxProcess.jsm
+++ b/devtools/client/framework/ToolboxProcess.jsm
@@ -25,52 +25,52 @@ const Services = require("Services");
 
 this.EXPORTED_SYMBOLS = ["BrowserToolboxProcess"];
 
 var processes = new Set();
 
 /**
  * Constructor for creating a process that will hold a chrome toolbox.
  *
- * @param function aOnClose [optional]
+ * @param function onClose [optional]
  *        A function called when the process stops running.
- * @param function aOnRun [optional]
+ * @param function onRun [optional]
  *        A function called when the process starts running.
- * @param object aOptions [optional]
+ * @param object options [optional]
  *        An object with properties for configuring BrowserToolboxProcess.
  */
-this.BrowserToolboxProcess = function BrowserToolboxProcess(aOnClose, aOnRun, aOptions) {
+this.BrowserToolboxProcess = function BrowserToolboxProcess(onClose, onRun, options) {
   let emitter = new EventEmitter();
   this.on = emitter.on.bind(emitter);
   this.off = emitter.off.bind(emitter);
   this.once = emitter.once.bind(emitter);
   // Forward any events to the shared emitter.
   this.emit = function (...args) {
     emitter.emit(...args);
     BrowserToolboxProcess.emit(...args);
   };
 
   // If first argument is an object, use those properties instead of
   // all three arguments
-  if (typeof aOnClose === "object") {
-    if (aOnClose.onClose) {
-      this.once("close", aOnClose.onClose);
+  if (typeof onClose === "object") {
+    if (onClose.onClose) {
+      this.once("close", onClose.onClose);
     }
-    if (aOnClose.onRun) {
-      this.once("run", aOnClose.onRun);
+    if (onClose.onRun) {
+      this.once("run", onClose.onRun);
     }
-    this._options = aOnClose;
+    this._options = onClose;
   } else {
-    if (aOnClose) {
-      this.once("close", aOnClose);
+    if (onClose) {
+      this.once("close", onClose);
     }
-    if (aOnRun) {
-      this.once("run", aOnRun);
+    if (onRun) {
+      this.once("run", onRun);
     }
-    this._options = aOptions || {};
+    this._options = options || {};
   }
 
   this._telemetry = new Telemetry();
 
   this.close = this.close.bind(this);
   Services.obs.addObserver(this.close, "quit-application", false);
   this._initServer();
   this._initProfile();
@@ -80,34 +80,34 @@ this.BrowserToolboxProcess = function Br
 };
 
 EventEmitter.decorate(BrowserToolboxProcess);
 
 /**
  * Initializes and starts a chrome toolbox process.
  * @return object
  */
-BrowserToolboxProcess.init = function (aOnClose, aOnRun, aOptions) {
-  return new BrowserToolboxProcess(aOnClose, aOnRun, aOptions);
+BrowserToolboxProcess.init = function (onClose, onRun, options) {
+  return new BrowserToolboxProcess(onClose, onRun, options);
 };
 
 /**
  * Passes a set of options to the BrowserAddonActors for the given ID.
  *
- * @param aId string
+ * @param id string
  *        The ID of the add-on to pass the options to
- * @param aOptions object
+ * @param options object
  *        The options.
  * @return a promise that will be resolved when complete.
  */
-BrowserToolboxProcess.setAddonOptions = function DSC_setAddonOptions(aId, aOptions) {
+BrowserToolboxProcess.setAddonOptions = function (id, options) {
   let promises = [];
 
   for (let process of processes.values()) {
-    promises.push(process.debuggerServer.setAddonOptions(aId, aOptions));
+    promises.push(process.debuggerServer.setAddonOptions(id, options));
   }
 
   return promise.all(promises);
 };
 
 BrowserToolboxProcess.prototype = {
   /**
    * Initializes the debugger server.
@@ -185,50 +185,58 @@ BrowserToolboxProcess.prototype = {
     // clears out the prefs file before re-writing it, and in practice the
     // file is empty when we get here. So just copying doesn't work in that
     // case.
     // We could force a sync pref flush and then copy it... but if we're doing
     // that, we might as well just flush directly to the new profile, which
     // always works:
     Services.prefs.savePrefFile(prefsFile);
 
-    dumpn("Finished creating the chrome toolbox user profile at: " + this._dbgProfilePath);
+    dumpn("Finished creating the chrome toolbox user profile at: " +
+          this._dbgProfilePath);
   },
 
   /**
    * Creates and initializes the profile & process for the remote debugger.
    */
   _create: function () {
     dumpn("Initializing chrome debugging process.");
-    let process = this._dbgProcess = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
+    let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
+    this._dbgProcess = process;
     process.init(Services.dirsvc.get("XREExeF", Ci.nsIFile));
 
     let xulURI = DBG_XUL;
 
     if (this._options.addonID) {
       xulURI += "?addonID=" + this._options.addonID;
     }
 
     dumpn("Running chrome debugging process.");
-    let args = ["-no-remote", "-foreground", "-profile", this._dbgProfilePath, "-chrome", xulURI];
+    let args = [
+      "-no-remote",
+      "-foreground",
+      "-profile", this._dbgProfilePath,
+      "-chrome", xulURI
+    ];
 
     // During local development, incremental builds can trigger the main process
     // to clear its startup cache with the "flag file" .purgecaches, but this
     // file is removed during app startup time, so we aren't able to know if it
     // was present in order to also clear the child profile's startup cache as
     // well.
     //
     // As an approximation of "isLocalBuild", check for an unofficial build.
     if (!Services.appinfo.isOfficial) {
       args.push("-purgecaches");
     }
 
     // Disable safe mode for the new process in case this was opened via the
     // keyboard shortcut.
-    let nsIEnvironment = Components.classes["@mozilla.org/process/environment;1"].getService(Components.interfaces.nsIEnvironment);
+    let nsIEnvironment = Cc["@mozilla.org/process/environment;1"]
+                           .getService(Ci.nsIEnvironment);
     let originalValue = nsIEnvironment.get("MOZ_DISABLE_SAFE_MODE_KEY");
     nsIEnvironment.set("MOZ_DISABLE_SAFE_MODE_KEY", "1");
 
     process.runwAsync(args, args.length, { observe: () => this.close() });
 
     // Now that the process has started, it's safe to reset the env variable.
     nsIEnvironment.set("MOZ_DISABLE_SAFE_MODE_KEY", originalValue);
 
@@ -283,12 +291,14 @@ function dumpn(str) {
   if (wantLogging) {
     dump("DBG-FRONTEND: " + str + "\n");
   }
 }
 
 var wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
 
 Services.prefs.addObserver("devtools.debugger.log", {
-  observe: (...args) => wantLogging = Services.prefs.getBoolPref(args.pop())
+  observe: (...args) => {
+    wantLogging = Services.prefs.getBoolPref(args.pop());
+  }
 }, false);
 
 Services.obs.notifyObservers(null, "ToolboxProcessLoaded", null);
--- a/devtools/client/framework/target-from-url.js
+++ b/devtools/client/framework/target-from-url.js
@@ -57,47 +57,47 @@ exports.targetFromURL = Task.async(funct
 
   let client = yield createClient(params);
 
   yield client.connect();
 
   let form, isTabActor;
   if (type === "tab") {
     // Fetch target for a remote tab
-    id = parseInt(id);
+    id = parseInt(id, 10);
     if (isNaN(id)) {
-      throw new Error("targetFromURL, wrong tab id:'" + id + "', should be a number");
+      throw new Error(`targetFromURL, wrong tab id '${id}', should be a number`);
     }
     try {
       let response = yield client.getTab({ outerWindowID: id });
       form = response.tab;
     } catch (ex) {
       if (ex.error == "noTab") {
-        throw new Error("targetFromURL, tab with outerWindowID:'" + id + "' doesn't exist");
+        throw new Error(`targetFromURL, tab with outerWindowID '${id}' doesn't exist`);
       }
       throw ex;
     }
   } else if (type == "process") {
     // Fetch target for a remote chrome actor
     DebuggerServer.allowChromeProcess = true;
     try {
-      id = parseInt(id);
+      id = parseInt(id, 10);
       if (isNaN(id)) {
         id = 0;
       }
       let response = yield client.getProcess(id);
       form = response.form;
       chrome = true;
       if (id != 0) {
         // Child process are not exposing tab actors and only support debugger+console
         isTabActor = false;
       }
     } catch (ex) {
       if (ex.error == "noProcess") {
-        throw new Error("targetFromURL, process with id:'" + id + "' doesn't exist");
+        throw new Error(`targetFromURL, process with id '${id}' doesn't exist`);
       }
       throw ex;
     }
   } else if (type == "window") {
     // Fetch target for a remote window actor
     DebuggerServer.allowChromeProcess = true;
     try {
       id = parseInt(id, 10);
@@ -106,23 +106,22 @@ exports.targetFromURL = Task.async(funct
       }
       let response = yield client.mainRoot.getWindow({
         outerWindowID: id,
       });
       form = response.window;
       chrome = true;
     } catch (ex) {
       if (ex.error == "notFound") {
-        throw new Error(`targetFromURL, window with id:'${id}' ` +
-                        "doesn't exist");
+        throw new Error(`targetFromURL, window with id '${id}' doesn't exist`);
       }
       throw ex;
     }
   } else {
-    throw new Error("targetFromURL, unsupported type='" + type + "' parameter");
+    throw new Error(`targetFromURL, unsupported type '${type}' parameter`);
   }
 
   return TargetFactory.forRemoteTab({ client, form, chrome, isTabActor });
 });
 
 function* createClient(params) {
   let host = params.get("host");
   let port = params.get("port");
@@ -132,12 +131,12 @@ function* createClient(params) {
   if (port) {
     transport = yield DebuggerClient.socketConnect({ host, port, webSocket });
   } else {
     // Setup a server if we don't have one already running
     if (!DebuggerServer.initialized) {
       DebuggerServer.init();
       DebuggerServer.addBrowserActors();
     }
-    transport = DebuggerServer.connectPipe()
+    transport = DebuggerServer.connectPipe();
   }
   return new DebuggerClient(transport);
 }
--- a/devtools/client/framework/target.js
+++ b/devtools/client/framework/target.js
@@ -1,16 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Ci } = require("chrome");
-const promise = require("promise");
 const defer = require("devtools/shared/defer");
 const EventEmitter = require("devtools/shared/event-emitter");
 const Services = require("Services");
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
 loader.lazyRequireGetter(this, "DebuggerClient",
   "devtools/shared/client/main", true);
@@ -481,53 +480,53 @@ TabTarget.prototype = {
   },
 
   /**
    * Setup listeners for remote debugging, updating existing ones as necessary.
    */
   _setupRemoteListeners: function () {
     this.client.addListener("closed", this.destroy);
 
-    this._onTabDetached = (aType, aPacket) => {
+    this._onTabDetached = (type, packet) => {
       // We have to filter message to ensure that this detach is for this tab
-      if (aPacket.from == this._form.actor) {
+      if (packet.from == this._form.actor) {
         this.destroy();
       }
     };
     this.client.addListener("tabDetached", this._onTabDetached);
 
-    this._onTabNavigated = (aType, aPacket) => {
+    this._onTabNavigated = (type, packet) => {
       let event = Object.create(null);
-      event.url = aPacket.url;
-      event.title = aPacket.title;
-      event.nativeConsoleAPI = aPacket.nativeConsoleAPI;
-      event.isFrameSwitching = aPacket.isFrameSwitching;
+      event.url = packet.url;
+      event.title = packet.title;
+      event.nativeConsoleAPI = packet.nativeConsoleAPI;
+      event.isFrameSwitching = packet.isFrameSwitching;
 
-      if (!aPacket.isFrameSwitching) {
+      if (!packet.isFrameSwitching) {
         // Update the title and url unless this is a frame switch.
-        this._url = aPacket.url;
-        this._title = aPacket.title;
+        this._url = packet.url;
+        this._title = packet.title;
       }
 
       // Send any stored event payload (DOMWindow or nsIRequest) for backwards
       // compatibility with non-remotable tools.
-      if (aPacket.state == "start") {
+      if (packet.state == "start") {
         event._navPayload = this._navRequest;
         this.emit("will-navigate", event);
         this._navRequest = null;
       } else {
         event._navPayload = this._navWindow;
         this.emit("navigate", event);
         this._navWindow = null;
       }
     };
     this.client.addListener("tabNavigated", this._onTabNavigated);
 
-    this._onFrameUpdate = (aType, aPacket) => {
-      this.emit("frame-update", aPacket);
+    this._onFrameUpdate = (type, packet) => {
+      this.emit("frame-update", packet);
     };
     this.client.addListener("frameUpdate", this._onFrameUpdate);
 
     this._onSourceUpdated = (event, packet) => this.emit("source-updated", packet);
     this.client.addListener("newSource", this._onSourceUpdated);
     this.client.addListener("updatedSource", this._onSourceUpdated);
   },
 
@@ -560,19 +559,21 @@ TabTarget.prototype = {
         }
         break;
       case "TabRemotenessChange":
         this.onRemotenessChange();
         break;
     }
   },
 
-  // Automatically respawn the toolbox when the tab changes between being
-  // loaded within the parent process and loaded from a content process.
-  // Process change can go in both ways.
+  /**
+   * Automatically respawn the toolbox when the tab changes between being
+   * loaded within the parent process and loaded from a content process.
+   * Process change can go in both ways.
+   */
   onRemotenessChange: function () {
     // Responsive design do a crazy dance around tabs and triggers
     // remotenesschange events. But we should ignore them as at the end
     // the content doesn't change its remoteness.
     if (this._tab.isResponsiveDesignMode) {
       return;
     }
 
@@ -682,21 +683,21 @@ TabTarget.prototype = {
 
     return deferred.promise;
   },
 };
 
 /**
  * WebProgressListener for TabTarget.
  *
- * @param object aTarget
+ * @param object target
  *        The TabTarget instance to work with.
  */
-function TabWebProgressListener(aTarget) {
-  this.target = aTarget;
+function TabWebProgressListener(target) {
+  this.target = target;
 }
 
 TabWebProgressListener.prototype = {
   target: null,
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
                                          Ci.nsISupportsWeakReference]),
 
--- a/devtools/client/framework/test/browser_target_from_url.js
+++ b/devtools/client/framework/test/browser_target_from_url.js
@@ -28,17 +28,17 @@ add_task(function* () {
   let browser = tab.linkedBrowser;
   let target;
 
   info("Test invalid type");
   try {
     yield targetFromURL(new URL("http://foo?type=x"));
     ok(false, "Shouldn't pass");
   } catch (e) {
-    is(e.message, "targetFromURL, unsupported type='x' parameter");
+    is(e.message, "targetFromURL, unsupported type 'x' parameter");
   }
 
   info("Test browser window");
   let windowId = window.QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIDOMWindowUtils)
                        .outerWindowID;
   target = yield targetFromURL(new URL("http://foo?type=window&id=" + windowId));
   is(target.url, window.location.href);
@@ -56,17 +56,17 @@ add_task(function* () {
   target = yield targetFromURL(new URL("http://foo?type=tab&id=" + windowId + "&chrome"));
   assertIsTabTarget(target, TEST_URI, true);
 
   info("Test invalid tab id");
   try {
     yield targetFromURL(new URL("http://foo?type=tab&id=10000"));
     ok(false, "Shouldn't pass");
   } catch (e) {
-    is(e.message, "targetFromURL, tab with outerWindowID:'10000' doesn't exist");
+    is(e.message, "targetFromURL, tab with outerWindowID '10000' doesn't exist");
   }
 
   info("Test parent process");
   target = yield targetFromURL(new URL("http://foo?type=process"));
   let topWindow = Services.wm.getMostRecentWindow("navigator:browser");
   assertIsTabTarget(target, topWindow.location.href, true);
 
   yield testRemoteTCP();
--- a/devtools/client/framework/toolbox-highlighter-utils.js
+++ b/devtools/client/framework/toolbox-highlighter-utils.js
@@ -1,10 +1,8 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const promise = require("promise");
 const {Task} = require("devtools/shared/task");
@@ -28,17 +26,16 @@ const flags = require("devtools/shared/f
  * scope that would be different for another instance returned by this function.
  *
  * @param {Toolbox} toolbox
  * @return {Object} the highlighterUtils public API
  */
 exports.getHighlighterUtils = function (toolbox) {
   if (!toolbox || !toolbox.target) {
     throw new Error("Missing or invalid toolbox passed to getHighlighterUtils");
-    return;
   }
 
   // Exported API properties will go here
   let exported = {};
 
   // The current toolbox target
   let target = toolbox.target;
 
@@ -92,22 +89,21 @@ exports.getHighlighterUtils = function (
   };
 
   /**
    * Start/stop the element picker on the debuggee target.
    * @param {Boolean} doFocus - Optionally focus the content area once the picker is
    *                            activated.
    * @return A promise that resolves when done
    */
-  let togglePicker = exported.togglePicker = function (doFocus) {
+  exported.togglePicker = function (doFocus) {
     if (isPicking) {
       return cancelPicker();
-    } else {
-      return startPicker(doFocus);
     }
+    return startPicker(doFocus);
   };
 
   /**
    * Start the element picker on the debuggee target.
    * This will request the inspector actor to start listening for mouse events
    * on the target page to highlight the hovered/picked element.
    * Depending on the server-side capabilities, this may fire events when nodes
    * are hovered.
@@ -245,18 +241,17 @@ exports.getHighlighterUtils = function (
 
   /**
    * This is a convenience method in case you don't have a nodeFront but a
    * valueGrip. This is often the case with VariablesView properties.
    * This method will simply translate the grip into a nodeFront and call
    * highlightNodeFront, so it has the same signature.
    * @see highlightNodeFront
    */
-  let highlightDomValueGrip = exported.highlightDomValueGrip = requireInspector(
-  function* (valueGrip, options = {}) {
+  exported.highlightDomValueGrip = requireInspector(function* (valueGrip, options = {}) {
     let nodeFront = yield gripToNodeFront(valueGrip);
     if (nodeFront) {
       yield highlightNodeFront(nodeFront, options);
     } else {
       throw new Error("The ValueGrip passed could not be translated to a NodeFront");
     }
   });
 
@@ -274,23 +269,23 @@ exports.getHighlighterUtils = function (
    * Hide the highlighter.
    * @param {Boolean} forceHide Only really matters in test mode (when
    * flags.testing is true). In test mode, hovering over several nodes
    * in the markup view doesn't hide/show the highlighter to ease testing. The
    * highlighter stays visible at all times, except when the mouse leaves the
    * markup view, which is when this param is passed to true
    * @return a promise that resolves when the highlighter is hidden
    */
-  let unhighlight = exported.unhighlight = Task.async(
-  function* (forceHide = false) {
+  exported.unhighlight = Task.async(function* (forceHide = false) {
     forceHide = forceHide || !flags.testing;
 
     // Note that if isRemoteHighlightable is true, there's no need to hide the
     // highlighter as the walker uses setTimeout to hide it after some time
-    if (isNodeFrontHighlighted && forceHide && toolbox.highlighter && isRemoteHighlightable()) {
+    if (isNodeFrontHighlighted && forceHide && toolbox.highlighter &&
+        isRemoteHighlightable()) {
       isNodeFrontHighlighted = false;
       yield toolbox.highlighter.hideBoxModel();
     }
 
     // unhighlight is called when destroying the toolbox, which means that by
     // now, the toolbox reference might have been nullified already.
     if (toolbox) {
       toolbox.emit("node-unhighlight");
@@ -301,24 +296,22 @@ exports.getHighlighterUtils = function (
    * If the main, box-model, highlighter isn't enough, or if multiple
    * highlighters are needed in parallel, this method can be used to return a
    * new instance of a highlighter actor, given a type.
    * The type of the highlighter passed must be known by the server.
    * The highlighter actor returned will have the show(nodeFront) and hide()
    * methods and needs to be released by the consumer when not needed anymore.
    * @return a promise that resolves to the highlighter
    */
-  let getHighlighterByType = exported.getHighlighterByType = requireInspector(
-  function* (typeName) {
+  exported.getHighlighterByType = requireInspector(function* (typeName) {
     let highlighter = null;
 
     if (supportsCustomHighlighters()) {
       highlighter = yield toolbox.inspector.getHighlighterByType(typeName);
     }
 
     return highlighter || promise.reject("The target doesn't support " +
         `creating highlighters by types or ${typeName} is unknown`);
-
   });
 
   // Return the public API
   return exported;
 };
--- a/devtools/client/framework/toolbox-host-manager.js
+++ b/devtools/client/framework/toolbox-host-manager.js
@@ -1,10 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
 const Services = require("Services");
-const {Ci} = require("chrome");
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const {Task} = require("devtools/shared/task");
 
 loader.lazyRequireGetter(this, "Toolbox", "devtools/client/framework/toolbox", true);
 loader.lazyRequireGetter(this, "Hosts", "devtools/client/framework/toolbox-hosts", true);
 
@@ -62,28 +67,31 @@ ToolboxHostManager.prototype = {
   create: Task.async(function* (toolId) {
     yield this.host.create();
 
     this.host.frame.setAttribute("aria-label", L10N.getStr("toolbox.label"));
     this.host.frame.ownerDocument.defaultView.addEventListener("message", this);
     // We have to listen on capture as no event fires on bubble
     this.host.frame.addEventListener("unload", this, true);
 
-    let toolbox = new Toolbox(this.target, toolId, this.host.type, this.host.frame.contentWindow, this.frameId);
+    let toolbox = new Toolbox(this.target, toolId, this.host.type,
+                              this.host.frame.contentWindow, this.frameId);
 
-    // Prevent reloading the toolbox when loading the tools in a tab (e.g. from about:debugging)
-    if (!this.host.frame.contentWindow.location.href.startsWith("about:devtools-toolbox")) {
+    // Prevent reloading the toolbox when loading the tools in a tab
+    // (e.g. from about:debugging)
+    let location = this.host.frame.contentWindow.location;
+    if (!location.href.startsWith("about:devtools-toolbox")) {
       this.host.frame.setAttribute("src", "about:devtools-toolbox");
     }
 
     return toolbox;
   }),
 
   handleEvent(event) {
-    switch(event.type) {
+    switch (event.type) {
       case "message":
         this.onMessage(event);
         break;
       case "unload":
         // On unload, host iframe already lost its contentWindow attribute, so
         // we can only compare against locations. Here we filter two very
         // different cases: preliminary about:blank document as well as iframes
         // like tool iframes.
--- a/devtools/client/framework/toolbox-init.js
+++ b/devtools/client/framework/toolbox-init.js
@@ -1,13 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* eslint-env browser */
+/* global XPCNativeWrapper */
 
 "use strict";
 
 // URL constructor doesn't support about: scheme
 let href = window.location.href.replace("about:", "http://");
 let url = new window.URL(href);
 
 // Only use this method to attach the toolbox if some query parameters are given
@@ -52,40 +53,40 @@ if (url.search.length > 1) {
     let target;
     if (url.searchParams.has("target")) {
       // Attach toolbox to a given browser iframe (<xul:browser> or <html:iframe
       // mozbrowser>) whose reference is set on the host iframe.
 
       // `iframe` is the targeted document to debug
       let iframe = host.wrappedJSObject ? host.wrappedJSObject.target
                                         : host.target;
+      if (!iframe) {
+        throw new Error("Unable to find the targeted iframe to debug");
+      }
+
       // Need to use a xray and query some interfaces to have
       // attributes and behavior expected by devtools codebase
       iframe = XPCNativeWrapper(iframe);
       iframe.QueryInterface(Ci.nsIFrameLoaderOwner);
 
-      if (iframe) {
-        // Fake a xul:tab object as we don't have one.
-        // linkedBrowser is the only one attribute being queried by client.getTab
-        let tab = { linkedBrowser: iframe };
+      // Fake a xul:tab object as we don't have one.
+      // linkedBrowser is the only one attribute being queried by client.getTab
+      let tab = { linkedBrowser: iframe };
 
-        if (!DebuggerServer.initialized) {
-          DebuggerServer.init();
-          DebuggerServer.addBrowserActors();
-        }
-        let client = new DebuggerClient(DebuggerServer.connectPipe());
+      if (!DebuggerServer.initialized) {
+        DebuggerServer.init();
+        DebuggerServer.addBrowserActors();
+      }
+      let client = new DebuggerClient(DebuggerServer.connectPipe());
 
-        yield client.connect();
-        // Creates a target for a given browser iframe.
-        let response = yield client.getTab({ tab });
-        let form = response.tab;
-        target = yield TargetFactory.forRemoteTab({client, form, chrome: false});
-      } else {
-        alert("Unable to find the targetted iframe to debug");
-      }
+      yield client.connect();
+      // Creates a target for a given browser iframe.
+      let response = yield client.getTab({ tab });
+      let form = response.tab;
+      target = yield TargetFactory.forRemoteTab({client, form, chrome: false});
     } else {
       target = yield targetFromURL(url);
     }
     let options = { customIframe: host };
     yield gDevTools.showToolbox(target, tool, Toolbox.HostType.CUSTOM, options);
   }).catch(error => {
     console.error("Exception while loading the toolbox", error);
   });
--- a/devtools/client/framework/toolbox-options.js
+++ b/devtools/client/framework/toolbox-options.js
@@ -1,10 +1,8 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
 const defer = require("devtools/shared/defer");
@@ -64,18 +62,17 @@ function OptionsPanel(iframeWindow, tool
   this.toolbox = toolbox;
   this.isReady = false;
 
   this._prefChanged = this._prefChanged.bind(this);
   this._themeRegistered = this._themeRegistered.bind(this);
   this._themeUnregistered = this._themeUnregistered.bind(this);
   this._disableJSClicked = this._disableJSClicked.bind(this);
 
-  this.disableJSNode = this.panelDoc.getElementById(
-    "devtools-disable-javascript");
+  this.disableJSNode = this.panelDoc.getElementById("devtools-disable-javascript");
 
   this._addListeners();
 
   const EventEmitter = require("devtools/shared/event-emitter");
   EventEmitter.decorate(this);
 }
 
 OptionsPanel.prototype = {
@@ -111,19 +108,18 @@ OptionsPanel.prototype = {
     Services.prefs.removeObserver("devtools.cache.disabled", this._prefChanged);
     Services.prefs.removeObserver("devtools.theme", this._prefChanged);
     gDevTools.off("theme-registered", this._themeRegistered);
     gDevTools.off("theme-unregistered", this._themeUnregistered);
   },
 
   _prefChanged: function (subject, topic, prefName) {
     if (prefName === "devtools.cache.disabled") {
-      let cacheDisabled = data.newValue;
+      let cacheDisabled = GetPref(prefName);
       let cbx = this.panelDoc.getElementById("devtools-disable-cache");
-
       cbx.checked = cacheDisabled;
     } else if (prefName === "devtools.theme") {
       this.updateCurrentTheme();
     }
   },
 
   _themeRegistered: function (event, themeId) {
     this.setupThemeList();
@@ -354,17 +350,17 @@ OptionsPanel.prototype = {
     };
 
     for (let prefDefinition of prefDefinitions) {
       let parent = this.panelDoc.getElementById(prefDefinition.parentId);
       parent.appendChild(createPreferenceOption(prefDefinition));
     }
   },
 
-  populatePreferences: function () {
+  populatePreferences: Task.async(function* () {
     let prefCheckboxes = this.panelDoc.querySelectorAll(
       "input[type=checkbox][data-pref]");
     for (let prefCheckbox of prefCheckboxes) {
       if (GetPref(prefCheckbox.getAttribute("data-pref"))) {
         prefCheckbox.setAttribute("checked", true);
       }
       prefCheckbox.addEventListener("change", function (e) {
         let checkbox = e.target;
@@ -394,36 +390,36 @@ OptionsPanel.prototype = {
       let options = [...prefSelect.options];
       options.some(function (option) {
         let value = option.value;
         // non strict check to allow int values.
         if (value == pref) {
           prefSelect.selectedIndex = options.indexOf(option);
           return true;
         }
+        return false;
       });
 
       prefSelect.addEventListener("change", function (e) {
         let select = e.target;
         SetPref(select.getAttribute("data-pref"),
           select.options[select.selectedIndex].value);
       });
     }
 
     if (this.target.activeTab) {
-      return this.target.client.attachTab(this.target.activeTab._actor)
-        .then(([response, client]) => {
-          this._origJavascriptEnabled = !response.javascriptEnabled;
-          this.disableJSNode.checked = this._origJavascriptEnabled;
-          this.disableJSNode.addEventListener("click",
-            this._disableJSClicked);
-        });
+      let [ response ] = yield this.target.client.attachTab(this.target.activeTab._actor);
+      this._origJavascriptEnabled = !response.javascriptEnabled;
+      this.disableJSNode.checked = this._origJavascriptEnabled;
+      this.disableJSNode.addEventListener("click", this._disableJSClicked);
+    } else {
+      // Hide the checkbox and label
+      this.disableJSNode.parentNode.style.display = "none";
     }
-    this.disableJSNode.hidden = true;
-  },
+  }),
 
   updateCurrentTheme: function () {
     let currentTheme = GetPref("devtools.theme");
     let themeBox = this.panelDoc.getElementById("devtools-theme-box");
     let themeRadioInput = themeBox.querySelector(`[value=${currentTheme}]`);
 
     if (themeRadioInput) {
       themeRadioInput.checked = true;
rename from devtools/client/inspector/layout/actions/box-model.js
rename to devtools/client/inspector/boxmodel/actions/box-model.js
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/boxmodel/actions/index.js
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { createEnum } = require("devtools/client/shared/enum");
+
+createEnum([
+
+  // Update the layout state with the latest layout properties.
+  "UPDATE_LAYOUT",
+
+], module.exports);
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/boxmodel/actions/moz.build
@@ -0,0 +1,10 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+    'box-model.js',
+    'index.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/boxmodel/box-model.js
@@ -0,0 +1,301 @@
+/* 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 { Task } = require("devtools/shared/task");
+const { getCssProperties } = require("devtools/shared/fronts/css-properties");
+const { ReflowFront } = require("devtools/shared/fronts/reflow");
+
+const { InplaceEditor } = require("devtools/client/shared/inplace-editor");
+
+const { updateLayout } = require("./actions/box-model");
+
+const EditingSession = require("./utils/editing-session");
+
+const NUMERIC = /^-?[\d\.]+$/;
+
+/**
+ * A singleton instance of the box model controllers.
+ *
+ * @param  {Inspector} inspector
+ *         An instance of the Inspector currently loaded in the toolbox.
+ * @param  {Window} window
+ *         The document window of the toolbox.
+ */
+function BoxModel(inspector, window) {
+  this.document = window.document;
+  this.inspector = inspector;
+  this.store = inspector.store;
+
+  this.updateBoxModel = this.updateBoxModel.bind(this);
+
+  this.onHideBoxModelHighlighter = this.onHideBoxModelHighlighter.bind(this);
+  this.onNewSelection = this.onNewSelection.bind(this);
+  this.onShowBoxModelEditor = this.onShowBoxModelEditor.bind(this);
+  this.onShowBoxModelHighlighter = this.onShowBoxModelHighlighter.bind(this);
+  this.onSidebarSelect = this.onSidebarSelect.bind(this);
+
+  this.inspector.selection.on("new-node-front", this.onNewSelection);
+  this.inspector.sidebar.on("select", this.onSidebarSelect);
+}
+
+BoxModel.prototype = {
+
+  /**
+   * Destruction function called when the inspector is destroyed. Removes event listeners
+   * and cleans up references.
+   */
+  destroy() {
+    this.inspector.selection.off("new-node-front", this.onNewSelection);
+    this.inspector.sidebar.off("select", this.onSidebarSelect);
+
+    if (this.reflowFront) {
+      this.untrackReflows();
+      this.reflowFront.destroy();
+      this.reflowFront = null;
+    }
+
+    this.document = null;
+    this.inspector = null;
+    this.walker = null;
+  },
+
+  /**
+   * Returns an object containing the box model's handler functions used in the box
+   * model's React component props.
+   */
+  getComponentProps() {
+    return {
+      onHideBoxModelHighlighter: this.onHideBoxModelHighlighter,
+      onShowBoxModelEditor: this.onShowBoxModelEditor,
+      onShowBoxModelHighlighter: this.onShowBoxModelHighlighter,
+    };
+  },
+
+  /**
+   * Returns true if the computed or layout panel is visible, and false otherwise.
+   */
+  isPanelVisible() {
+    return this.inspector.toolbox.currentToolId === "inspector" &&
+           this.inspector.sidebar &&
+           (this.inspector.sidebar.getCurrentTabID() === "layoutview" ||
+            this.inspector.sidebar.getCurrentTabID() === "computedview");
+  },
+
+  /**
+   * Returns true if the layout panel is visible and the current node is valid to
+   * be displayed in the view.
+   */
+  isPanelVisibleAndNodeValid() {
+    return this.isPanelVisible() &&
+           this.inspector.selection.isConnected() &&
+           this.inspector.selection.isElementNode();
+  },
+
+  /**
+   * Starts listening to reflows in the current tab.
+   */
+  trackReflows() {
+    if (!this.reflowFront) {
+      let { target } = this.inspector;
+      if (target.form.reflowActor) {
+        this.reflowFront = ReflowFront(target.client,
+                                       target.form);
+      } else {
+        return;
+      }
+    }
+
+    this.reflowFront.on("reflows", this.updateBoxModel);
+    this.reflowFront.start();
+  },
+
+  /**
+   * Stops listening to reflows in the current tab.
+   */
+  untrackReflows() {
+    if (!this.reflowFront) {
+      return;
+    }
+
+    this.reflowFront.off("reflows", this.updateBoxModel);
+    this.reflowFront.stop();
+  },
+
+  /**
+   * Updates the box model panel by dispatching the new layout data.
+   *
+   * @param  {String} reason
+   *         Optional string describing the reason why the boxmodel is updated.
+   */
+  updateBoxModel(reason) {
+    this._updateReasons = this._updateReasons || [];
+    if (reason) {
+      this._updateReasons.push(reason);
+    }
+
+    let lastRequest = Task.spawn((function* () {
+      if (!(this.isPanelVisible() &&
+          this.inspector.selection.isConnected() &&
+          this.inspector.selection.isElementNode())) {
+        return null;
+      }
+
+      let node = this.inspector.selection.nodeFront;
+      let layout = yield this.inspector.pageStyle.getLayout(node, {
+        autoMargins: true,
+      });
+      let styleEntries = yield this.inspector.pageStyle.getApplied(node, {});
+      this.elementRules = styleEntries.map(e => e.rule);
+
+      // Update the redux store with the latest layout properties and update the box
+      // model view.
+      this.store.dispatch(updateLayout(layout));
+
+      // If a subsequent request has been made, wait for that one instead.
+      if (this._lastRequest != lastRequest) {
+        return this._lastRequest;
+      }
+
+      this.inspector.emit("boxmodel-view-updated", this._updateReasons);
+
+      this._lastRequest = null;
+      this._updateReasons = [];
+
+      return null;
+    }).bind(this)).catch(console.error);
+
+    this._lastRequest = lastRequest;
+  },
+
+  /**
+   * Selection 'new-node-front' event handler.
+   */
+  onNewSelection: function () {
+    if (!this.isPanelVisibleAndNodeValid()) {
+      return;
+    }
+
+    if (this.inspector.selection.isConnected() &&
+        this.inspector.selection.isElementNode()) {
+      this.trackReflows();
+    }
+
+    this.updateBoxModel("new-selection");
+  },
+
+  /**
+   * Hides the box-model highlighter on the currently selected element.
+   */
+  onHideBoxModelHighlighter() {
+    let toolbox = this.inspector.toolbox;
+    toolbox.highlighterUtils.unhighlight();
+  },
+
+  /**
+   * Shows the inplace editor when a box model editable value is clicked on the
+   * box model panel.
+   *
+   * @param  {DOMNode} element
+   *         The element that was clicked.
+   * @param  {Event} event
+   *         The event object.
+   * @param  {String} property
+   *         The name of the property.
+   */
+  onShowBoxModelEditor(element, event, property) {
+    let session = new EditingSession({
+      inspector: this.inspector,
+      doc: this.document,
+      elementRules: this.elementRules,
+    });
+    let initialValue = session.getProperty(property);
+
+    let editor = new InplaceEditor({
+      element: element,
+      initial: initialValue,
+      contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE,
+      property: {
+        name: property
+      },
+      start: self => {
+        self.elt.parentNode.classList.add("boxmodel-editing");
+      },
+      change: value => {
+        if (NUMERIC.test(value)) {
+          value += "px";
+        }
+
+        let properties = [
+          { name: property, value: value }
+        ];
+
+        if (property.substring(0, 7) == "border-") {
+          let bprop = property.substring(0, property.length - 5) + "style";
+          let style = session.getProperty(bprop);
+          if (!style || style == "none" || style == "hidden") {
+            properties.push({ name: bprop, value: "solid" });
+          }
+        }
+
+        session.setProperties(properties).catch(e => console.error(e));
+      },
+      done: (value, commit) => {
+        editor.elt.parentNode.classList.remove("boxmodel-editing");
+        if (!commit) {
+          session.revert().then(() => {
+            session.destroy();
+          }, e => console.error(e));
+          return;
+        }
+
+        let node = this.inspector.selection.nodeFront;
+        this.inspector.pageStyle.getLayout(node, {
+          autoMargins: true,
+        }).then(layout => {
+          this.store.dispatch(updateLayout(layout));
+        }, e => console.error(e));
+      },
+      contextMenu: this.inspector.onTextBoxContextMenu,
+      cssProperties: getCssProperties(this.inspector.toolbox)
+    }, event);
+  },
+
+  /**
+   * Shows the box-model highlighter on the currently selected element.
+   *
+   * @param  {Object} options
+   *         Options passed to the highlighter actor.
+   */
+  onShowBoxModelHighlighter(options = {}) {
+    let toolbox = this.inspector.toolbox;
+    let nodeFront = this.inspector.selection.nodeFront;
+
+    toolbox.highlighterUtils.highlightNodeFront(nodeFront, options);
+  },
+
+  /**
+   * Handler for the inspector sidebar select event. Starts listening for
+   * "grid-layout-changed" if the layout panel is visible. Otherwise, stop
+   * listening for grid layout changes. Finally, refresh the layout view if
+   * it is visible.
+   */
+  onSidebarSelect() {
+    if (!this.isPanelVisible()) {
+      this.untrackReflows();
+      return;
+    }
+
+    if (this.inspector.selection.isConnected() &&
+        this.inspector.selection.isElementNode()) {
+      this.trackReflows();
+    }
+
+    this.updateBoxModel();
+  },
+
+};
+
+module.exports = BoxModel;
rename from devtools/client/inspector/layout/components/BoxModel.js
rename to devtools/client/inspector/boxmodel/components/BoxModel.js
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/boxmodel/components/BoxModelApp.js
@@ -0,0 +1,51 @@
+/* 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 { addons, createClass, createFactory, PropTypes } =
+  require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+
+const { LocalizationHelper } = require("devtools/shared/l10n");
+
+const Accordion =
+  createFactory(require("devtools/client/inspector/layout/components/Accordion"));
+const BoxModel = createFactory(require("./BoxModel"));
+
+const Types = require("../types");
+
+const BOXMODEL_STRINGS_URI = "devtools/client/locales/boxmodel.properties";
+const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI);
+
+const BoxModelApp = createClass({
+
+  displayName: "BoxModelApp",
+
+  propTypes: {
+    boxModel: PropTypes.shape(Types.boxModel).isRequired,
+    showBoxModelProperties: PropTypes.bool.isRequired,
+    onHideBoxModelHighlighter: PropTypes.func.isRequired,
+    onShowBoxModelEditor: PropTypes.func.isRequired,
+    onShowBoxModelHighlighter: PropTypes.func.isRequired,
+  },
+
+  mixins: [ addons.PureRenderMixin ],
+
+  render() {
+    return Accordion({
+      items: [
+        {
+          header: BOXMODEL_L10N.getStr("boxmodel.title"),
+          component: BoxModel,
+          componentProps: this.props,
+          opened: true,
+        }
+      ]
+    });
+  },
+
+});
+
+module.exports = connect(state => state)(BoxModelApp);
rename from devtools/client/inspector/layout/components/BoxModelEditable.js
rename to devtools/client/inspector/boxmodel/components/BoxModelEditable.js
rename from devtools/client/inspector/layout/components/BoxModelInfo.js
rename to devtools/client/inspector/boxmodel/components/BoxModelInfo.js
rename from devtools/client/inspector/layout/components/BoxModelMain.js
rename to devtools/client/inspector/boxmodel/components/BoxModelMain.js
--- a/devtools/client/inspector/layout/components/BoxModelMain.js
+++ b/devtools/client/inspector/boxmodel/components/BoxModelMain.js
@@ -32,17 +32,33 @@ module.exports = createClass({
 
   mixins: [ addons.PureRenderMixin ],
 
   getBorderOrPaddingValue(property) {
     let { layout } = this.props.boxModel;
     return layout[property] ? parseFloat(layout[property]) : "-";
   },
 
-  getHeightOrWidthValue(property) {
+  getHeightValue(property) {
+    let { layout } = this.props.boxModel;
+
+    if (property == undefined) {
+      return "-";
+    }
+
+    property -= parseFloat(layout["border-top-width"]) +
+                parseFloat(layout["border-bottom-width"]) +
+                parseFloat(layout["padding-top"]) +
+                parseFloat(layout["padding-bottom"]);
+    property = parseFloat(property.toPrecision(6));
+
+    return property;
+  },
+
+  getWidthValue(property) {
     let { layout } = this.props.boxModel;
 
     if (property == undefined) {
       return "-";
     }
 
     property -= parseFloat(layout["border-left-width"]) +
                 parseFloat(layout["border-right-width"]) +
@@ -78,35 +94,35 @@ module.exports = createClass({
       showOnly: region,
       onlyRegionArea: true,
     });
   },
 
   render() {
     let { boxModel, onShowBoxModelEditor } = this.props;
     let { layout } = boxModel;
-    let { width, height } = layout;
+    let { height, width } = layout;
 
     let borderTop = this.getBorderOrPaddingValue("border-top-width");
     let borderRight = this.getBorderOrPaddingValue("border-right-width");
     let borderBottom = this.getBorderOrPaddingValue("border-bottom-width");
     let borderLeft = this.getBorderOrPaddingValue("border-left-width");
 
     let paddingTop = this.getBorderOrPaddingValue("padding-top");
     let paddingRight = this.getBorderOrPaddingValue("padding-right");
     let paddingBottom = this.getBorderOrPaddingValue("padding-bottom");
     let paddingLeft = this.getBorderOrPaddingValue("padding-left");
 
     let marginTop = this.getMarginValue("margin-top", "top");
     let marginRight = this.getMarginValue("margin-right", "right");
     let marginBottom = this.getMarginValue("margin-bottom", "bottom");
     let marginLeft = this.getMarginValue("margin-left", "left");
 
-    width = this.getHeightOrWidthValue(width);
-    height = this.getHeightOrWidthValue(height);
+    height = this.getHeightValue(height);
+    width = this.getWidthValue(width);
 
     return dom.div(
       {
         className: "boxmodel-main",
         onMouseOver: this.onHighlightMouseOver,
         onMouseOut: this.props.onHideBoxModelHighlighter,
       },
       dom.span(
rename from devtools/client/inspector/layout/components/BoxModelProperties.js
rename to devtools/client/inspector/boxmodel/components/BoxModelProperties.js
rename from devtools/client/inspector/layout/components/ComputedProperty.js
rename to devtools/client/inspector/boxmodel/components/ComputedProperty.js
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/boxmodel/components/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+    'BoxModel.js',
+    'BoxModelApp.js',
+    'BoxModelEditable.js',
+    'BoxModelInfo.js',
+    'BoxModelMain.js',
+    'BoxModelProperties.js',
+    'ComputedProperty.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/boxmodel/moz.build
@@ -0,0 +1,19 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += [
+    'actions',
+    'components',
+    'reducers',
+    'utils',
+]
+
+DevToolsModules(
+    'box-model.js',
+    'types.js',
+)
+
+BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
rename from devtools/client/inspector/layout/reducers/box-model.js
rename to devtools/client/inspector/boxmodel/reducers/box-model.js
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/boxmodel/reducers/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+    'box-model.js',
+)
rename from devtools/client/inspector/components/test/.eslintrc.js
rename to devtools/client/inspector/boxmodel/test/.eslintrc.js
rename from devtools/client/inspector/components/test/browser.ini
rename to devtools/client/inspector/boxmodel/test/browser.ini
--- a/devtools/client/inspector/components/test/browser.ini
+++ b/devtools/client/inspector/boxmodel/test/browser.ini
@@ -16,15 +16,17 @@ support-files =
 [browser_boxmodel_editablemodel.js]
 # [browser_boxmodel_editablemodel_allproperties.js]
 # Disabled for too many intermittent failures (bug 1009322)
 [browser_boxmodel_editablemodel_bluronclick.js]
 [browser_boxmodel_editablemodel_border.js]
 [browser_boxmodel_editablemodel_stylerules.js]
 [browser_boxmodel_guides.js]
 [browser_boxmodel_navigation.js]
+skip-if = true # Bug 1336198
 [browser_boxmodel_rotate-labels-on-sides.js]
 [browser_boxmodel_sync.js]
 [browser_boxmodel_tooltips.js]
+skip-if = true # Bug 1336198
 [browser_boxmodel_update-after-navigation.js]
 [browser_boxmodel_update-after-reload.js]
 # [browser_boxmodel_update-in-iframes.js]
 # Bug 1020038 boxmodel-view updates for iframe elements changes
rename from devtools/client/inspector/components/test/browser_boxmodel.js
rename to devtools/client/inspector/boxmodel/test/browser_boxmodel.js
--- a/devtools/client/inspector/components/test/browser_boxmodel.js
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel.js
@@ -5,128 +5,128 @@
 "use strict";
 
 // Test that the box model displays the right values and that it updates when
 // the node's style is changed
 
 // Expected values:
 var res1 = [
   {
-    selector: "#old-boxmodel-element-size",
+    selector: ".boxmodel-element-size",
     value: "160" + "\u00D7" + "160.117"
   },
   {
-    selector: ".old-boxmodel-size > span",
+    selector: ".boxmodel-size > span",
     value: "100" + "\u00D7" + "100.117"
   },
   {
-    selector: ".old-boxmodel-margin.old-boxmodel-top > span",
+    selector: ".boxmodel-margin.boxmodel-top > span",
     value: 30
   },
   {
-    selector: ".old-boxmodel-margin.old-boxmodel-left > span",
+    selector: ".boxmodel-margin.boxmodel-left > span",
     value: "auto"
   },
   {
-    selector: ".old-boxmodel-margin.old-boxmodel-bottom > span",
+    selector: ".boxmodel-margin.boxmodel-bottom > span",
     value: 30
   },
   {
-    selector: ".old-boxmodel-margin.old-boxmodel-right > span",
+    selector: ".boxmodel-margin.boxmodel-right > span",
     value: "auto"
   },
   {
-    selector: ".old-boxmodel-padding.old-boxmodel-top > span",
+    selector: ".boxmodel-padding.boxmodel-top > span",
     value: 20
   },
   {
-    selector: ".old-boxmodel-padding.old-boxmodel-left > span",
+    selector: ".boxmodel-padding.boxmodel-left > span",
     value: 20
   },
   {
-    selector: ".old-boxmodel-padding.old-boxmodel-bottom > span",
+    selector: ".boxmodel-padding.boxmodel-bottom > span",
     value: 20
   },
   {
-    selector: ".old-boxmodel-padding.old-boxmodel-right > span",
+    selector: ".boxmodel-padding.boxmodel-right > span",
     value: 20
   },
   {
-    selector: ".old-boxmodel-border.old-boxmodel-top > span",
+    selector: ".boxmodel-border.boxmodel-top > span",
     value: 10
   },
   {
-    selector: ".old-boxmodel-border.old-boxmodel-left > span",
+    selector: ".boxmodel-border.boxmodel-left > span",
     value: 10
   },
   {
-    selector: ".old-boxmodel-border.old-boxmodel-bottom > span",
+    selector: ".boxmodel-border.boxmodel-bottom > span",
     value: 10
   },
   {
-    selector: ".old-boxmodel-border.old-boxmodel-right > span",
+    selector: ".boxmodel-border.boxmodel-right > span",
     value: 10
   },
 ];
 
 var res2 = [
   {
-    selector: "#old-boxmodel-element-size",
+    selector: ".boxmodel-element-size",
     value: "190" + "\u00D7" + "210"
   },
   {
-    selector: ".old-boxmodel-size > span",
+    selector: ".boxmodel-size > span",
     value: "100" + "\u00D7" + "150"
   },
   {
-    selector: ".old-boxmodel-margin.old-boxmodel-top > span",
+    selector: ".boxmodel-margin.boxmodel-top > span",
     value: 30
   },
   {
-    selector: ".old-boxmodel-margin.old-boxmodel-left > span",
+    selector: ".boxmodel-margin.boxmodel-left > span",
     value: "auto"
   },
   {
-    selector: ".old-boxmodel-margin.old-boxmodel-bottom > span",
+    selector: ".boxmodel-margin.boxmodel-bottom > span",
     value: 30
   },
   {
-    selector: ".old-boxmodel-margin.old-boxmodel-right > span",
+    selector: ".boxmodel-margin.boxmodel-right > span",
     value: "auto"
   },
   {
-    selector: ".old-boxmodel-padding.old-boxmodel-top > span",
+    selector: ".boxmodel-padding.boxmodel-top > span",
     value: 20
   },
   {
-    selector: ".old-boxmodel-padding.old-boxmodel-left > span",
+    selector: ".boxmodel-padding.boxmodel-left > span",
     value: 20
   },
   {
-    selector: ".old-boxmodel-padding.old-boxmodel-bottom > span",
+    selector: ".boxmodel-padding.boxmodel-bottom > span",
     value: 20
   },
   {
-    selector: ".old-boxmodel-padding.old-boxmodel-right > span",
+    selector: ".boxmodel-padding.boxmodel-right > span",
     value: 50
   },
   {
-    selector: ".old-boxmodel-border.old-boxmodel-top > span",
+    selector: ".boxmodel-border.boxmodel-top > span",
     value: 10
   },
   {
-    selector: ".old-boxmodel-border.old-boxmodel-left > span",
+    selector: ".boxmodel-border.boxmodel-left > span",
     value: 10
   },
   {
-    selector: ".old-boxmodel-border.old-boxmodel-bottom > span",
+    selector: ".boxmodel-border.boxmodel-bottom > span",
     value: 10
   },
   {
-    selector: ".old-boxmodel-border.old-boxmodel-right > span",
+    selector: ".boxmodel-border.boxmodel-right > span",
     value: 10
   },
 ];
 
 add_task(function* () {
   let style = "div { position: absolute; top: 42px; left: 42px; " +
               "height: 100.111px; width: 100px; border: 10px solid black; " +
               "padding: 20px; margin: 30px auto;}";
@@ -137,28 +137,28 @@ add_task(function* () {
   yield selectNode("div", inspector);
 
   yield testInitialValues(inspector, view);
   yield testChangingValues(inspector, view, testActor);
 });
 
 function* testInitialValues(inspector, view) {
   info("Test that the initial values of the box model are correct");
-  let viewdoc = view.doc;
+  let viewdoc = view.document;
 
   for (let i = 0; i < res1.length; i++) {
     let elt = viewdoc.querySelector(res1[i].selector);
     is(elt.textContent, res1[i].value,
        res1[i].selector + " has the right value.");
   }
 }
 
 function* testChangingValues(inspector, view, testActor) {
   info("Test that changing the document updates the box model");
-  let viewdoc = view.doc;
+  let viewdoc = view.document;
 
   let onUpdated = waitForUpdate(inspector);
   yield testActor.setAttribute("div", "style",
                                "height:150px;padding-right:50px;");
   yield onUpdated;
 
   for (let i = 0; i < res2.length; i++) {
     let elt = viewdoc.querySelector(res2[i].selector);
rename from devtools/client/inspector/components/test/browser_boxmodel_editablemodel.js
rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel.js
--- a/devtools/client/inspector/components/test/browser_boxmodel_editablemodel.js
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel.js
@@ -31,164 +31,164 @@ add_task(function* () {
 function* testEditingMargins(inspector, view, testActor) {
   info("Test that editing margin dynamically updates the document, pressing " +
        "escape cancels the changes");
 
   is((yield getStyle(testActor, "#div1", "margin-top")), "",
      "Should be no margin-top on the element.");
   yield selectNode("#div1", inspector);
 
-  let span = view.doc.querySelector(".old-boxmodel-margin.old-boxmodel-top > span");
+  let span = view.document.querySelector(".boxmodel-margin.boxmodel-top > span");
   is(span.textContent, 5, "Should have the right value in the box model.");
 
-  EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView);
-  let editor = view.doc.querySelector(".styleinspector-propertyeditor");
+  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+  let editor = view.document.querySelector(".styleinspector-propertyeditor");
   ok(editor, "Should have opened the editor.");
   is(editor.value, "5px", "Should have the right value in the editor.");
 
-  EventUtils.synthesizeKey("3", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("3", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is((yield getStyle(testActor, "#div1", "margin-top")), "3px",
      "Should have updated the margin.");
 
-  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is((yield getStyle(testActor, "#div1", "margin-top")), "",
      "Should be no margin-top on the element.");
   is(span.textContent, 5, "Should have the right value in the box model.");
 }
 
 function* testKeyBindings(inspector, view, testActor) {
   info("Test that arrow keys work correctly and pressing enter commits the " +
        "changes");
 
   is((yield getStyle(testActor, "#div1", "margin-left")), "",
      "Should be no margin-top on the element.");
   yield selectNode("#div1", inspector);
 
-  let span = view.doc.querySelector(".old-boxmodel-margin.old-boxmodel-left > span");
+  let span = view.document.querySelector(".boxmodel-margin.boxmodel-left > span");
   is(span.textContent, 10, "Should have the right value in the box model.");
 
-  EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView);
-  let editor = view.doc.querySelector(".styleinspector-propertyeditor");
+  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+  let editor = view.document.querySelector(".styleinspector-propertyeditor");
   ok(editor, "Should have opened the editor.");
   is(editor.value, "10px", "Should have the right value in the editor.");
 
-  EventUtils.synthesizeKey("VK_UP", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_UP", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is(editor.value, "11px", "Should have the right value in the editor.");
   is((yield getStyle(testActor, "#div1", "margin-left")), "11px",
      "Should have updated the margin.");
 
-  EventUtils.synthesizeKey("VK_DOWN", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_DOWN", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is(editor.value, "10px", "Should have the right value in the editor.");
   is((yield getStyle(testActor, "#div1", "margin-left")), "10px",
      "Should have updated the margin.");
 
-  EventUtils.synthesizeKey("VK_UP", { shiftKey: true }, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_UP", { shiftKey: true }, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is(editor.value, "20px", "Should have the right value in the editor.");
   is((yield getStyle(testActor, "#div1", "margin-left")), "20px",
      "Should have updated the margin.");
-  EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
 
   is((yield getStyle(testActor, "#div1", "margin-left")), "20px",
      "Should be the right margin-top on the element.");
   is(span.textContent, 20, "Should have the right value in the box model.");
 }
 
 function* testEscapeToUndo(inspector, view, testActor) {
   info("Test that deleting the value removes the property but escape undoes " +
        "that");
 
   is((yield getStyle(testActor, "#div1", "margin-left")), "20px",
      "Should be the right margin-top on the element.");
   yield selectNode("#div1", inspector);
 
-  let span = view.doc.querySelector(".old-boxmodel-margin.old-boxmodel-left > span");
+  let span = view.document.querySelector(".boxmodel-margin.boxmodel-left > span");
   is(span.textContent, 20, "Should have the right value in the box model.");
 
-  EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView);
-  let editor = view.doc.querySelector(".styleinspector-propertyeditor");
+  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+  let editor = view.document.querySelector(".styleinspector-propertyeditor");
   ok(editor, "Should have opened the editor.");
   is(editor.value, "20px", "Should have the right value in the editor.");
 
-  EventUtils.synthesizeKey("VK_DELETE", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_DELETE", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is(editor.value, "", "Should have the right value in the editor.");
   is((yield getStyle(testActor, "#div1", "margin-left")), "",
      "Should have updated the margin.");
 
-  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is((yield getStyle(testActor, "#div1", "margin-left")), "20px",
      "Should be the right margin-top on the element.");
   is(span.textContent, 20, "Should have the right value in the box model.");
 }
 
 function* testDeletingValue(inspector, view, testActor) {
   info("Test that deleting the value removes the property");
 
   yield setStyle(testActor, "#div1", "marginRight", "15px");
   yield waitForUpdate(inspector);
 
   yield selectNode("#div1", inspector);
 
-  let span = view.doc.querySelector(".old-boxmodel-margin.old-boxmodel-right > span");
+  let span = view.document.querySelector(".boxmodel-margin.boxmodel-right > span");
   is(span.textContent, 15, "Should have the right value in the box model.");
 
-  EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView);
-  let editor = view.doc.querySelector(".styleinspector-propertyeditor");
+  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+  let editor = view.document.querySelector(".styleinspector-propertyeditor");
   ok(editor, "Should have opened the editor.");
   is(editor.value, "15px", "Should have the right value in the editor.");
 
-  EventUtils.synthesizeKey("VK_DELETE", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_DELETE", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is(editor.value, "", "Should have the right value in the editor.");
   is((yield getStyle(testActor, "#div1", "margin-right")), "",
      "Should have updated the margin.");
 
-  EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
 
   is((yield getStyle(testActor, "#div1", "margin-right")), "",
      "Should be the right margin-top on the element.");
   is(span.textContent, 10, "Should have the right value in the box model.");
 }
 
 function* testRefocusingOnClick(inspector, view, testActor) {
   info("Test that clicking in the editor input does not remove focus");
 
   yield selectNode("#div4", inspector);
 
-  let span = view.doc.querySelector(".old-boxmodel-margin.old-boxmodel-top > span");
+  let span = view.document.querySelector(".boxmodel-margin.boxmodel-top > span");
   is(span.textContent, 1, "Should have the right value in the box model.");
 
-  EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView);
-  let editor = view.doc.querySelector(".styleinspector-propertyeditor");
+  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+  let editor = view.document.querySelector(".styleinspector-propertyeditor");
   ok(editor, "Should have opened the editor.");
 
   info("Click in the already opened editor input");
-  EventUtils.synthesizeMouseAtCenter(editor, {}, view.doc.defaultView);
-  is(editor, view.doc.activeElement,
+  EventUtils.synthesizeMouseAtCenter(editor, {}, view.document.defaultView);
+  is(editor, view.document.activeElement,
     "Inplace editor input should still have focus.");
 
   info("Check the input can still be used as expected");
-  EventUtils.synthesizeKey("VK_UP", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_UP", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is(editor.value, "2px", "Should have the right value in the editor.");
   is((yield getStyle(testActor, "#div4", "margin-top")), "2px",
      "Should have updated the margin.");
-  EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
 
   is((yield getStyle(testActor, "#div4", "margin-top")), "2px",
      "Should be the right margin-top on the element.");
   is(span.textContent, 2, "Should have the right value in the box model.");
 }
rename from devtools/client/inspector/components/test/browser_boxmodel_editablemodel_allproperties.js
rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_allproperties.js
--- a/devtools/client/inspector/components/test/browser_boxmodel_editablemodel_allproperties.js
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_allproperties.js
@@ -27,120 +27,120 @@ add_task(function* () {
 function* testEditing(inspector, view, testActor) {
   info("When all properties are set on the node editing one should work");
 
   yield setStyle(testActor, "#div1", "padding", "5px");
   yield waitForUpdate(inspector);
 
   yield selectNode("#div1", inspector);
 
-  let span = view.doc.querySelector(".old-boxmodel-padding.old-boxmodel-bottom > span");
+  let span = view.document.querySelector(".boxmodel-padding.boxmodel-bottom > span");
   is(span.textContent, 5, "Should have the right value in the box model.");
 
-  EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView);
-  let editor = view.doc.querySelector(".styleinspector-propertyeditor");
+  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+  let editor = view.document.querySelector(".styleinspector-propertyeditor");
   ok(editor, "Should have opened the editor.");
   is(editor.value, "5px", "Should have the right value in the editor.");
 
-  EventUtils.synthesizeKey("7", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("7", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is(editor.value, "7", "Should have the right value in the editor.");
   is((yield getStyle(testActor, "#div1", "padding-bottom")), "7px",
      "Should have updated the padding");
 
-  EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
 
   is((yield getStyle(testActor, "#div1", "padding-bottom")), "7px",
      "Should be the right padding.");
   is(span.textContent, 7, "Should have the right value in the box model.");
 }
 
 function* testEditingAndCanceling(inspector, view, testActor) {
   info("When all properties are set on the node editing one and then " +
        "cancelling with ESCAPE should work");
 
   yield setStyle(testActor, "#div1", "padding", "5px");
   yield waitForUpdate(inspector);
 
   yield selectNode("#div1", inspector);
 
-  let span = view.doc.querySelector(".old-boxmodel-padding.old-boxmodel-left > span");
+  let span = view.document.querySelector(".boxmodel-padding.boxmodel-left > span");
   is(span.textContent, 5, "Should have the right value in the box model.");
 
-  EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView);
-  let editor = view.doc.querySelector(".styleinspector-propertyeditor");
+  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+  let editor = view.document.querySelector(".styleinspector-propertyeditor");
   ok(editor, "Should have opened the editor.");
   is(editor.value, "5px", "Should have the right value in the editor.");
 
-  EventUtils.synthesizeKey("8", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("8", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is(editor.value, "8", "Should have the right value in the editor.");
   is((yield getStyle(testActor, "#div1", "padding-left")), "8px",
      "Should have updated the padding");
 
-  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is((yield getStyle(testActor, "#div1", "padding-left")), "5px",
      "Should be the right padding.");
   is(span.textContent, 5, "Should have the right value in the box model.");
 }
 
 function* testDeleting(inspector, view, testActor) {
   info("When all properties are set on the node deleting one should work");
 
   yield selectNode("#div1", inspector);
 
-  let span = view.doc.querySelector(".old-boxmodel-padding.old-boxmodel-left > span");
+  let span = view.document.querySelector(".boxmodel-padding.boxmodel-left > span");
   is(span.textContent, 5, "Should have the right value in the box model.");
 
-  EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView);
-  let editor = view.doc.querySelector(".styleinspector-propertyeditor");
+  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+  let editor = view.document.querySelector(".styleinspector-propertyeditor");
   ok(editor, "Should have opened the editor.");
   is(editor.value, "5px", "Should have the right value in the editor.");
 
-  EventUtils.synthesizeKey("VK_DELETE", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_DELETE", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is(editor.value, "", "Should have the right value in the editor.");
   is((yield getStyle(testActor, "#div1", "padding-left")), "",
      "Should have updated the padding");
 
-  EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
 
   is((yield getStyle(testActor, "#div1", "padding-left")), "",
      "Should be the right padding.");
   is(span.textContent, 3, "Should have the right value in the box model.");
 }
 
 function* testDeletingAndCanceling(inspector, view, testActor) {
   info("When all properties are set on the node deleting one then cancelling " +
        "should work");
 
   yield setStyle(testActor, "#div1", "padding", "5px");
   yield waitForUpdate(inspector);
 
   yield selectNode("#div1", inspector);
 
-  let span = view.doc.querySelector(".old-boxmodel-padding.old-boxmodel-left > span");
+  let span = view.document.querySelector(".boxmodel-padding.boxmodel-left > span");
   is(span.textContent, 5, "Should have the right value in the box model.");
 
-  EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView);
-  let editor = view.doc.querySelector(".styleinspector-propertyeditor");
+  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+  let editor = view.document.querySelector(".styleinspector-propertyeditor");
   ok(editor, "Should have opened the editor.");
   is(editor.value, "5px", "Should have the right value in the editor.");
 
-  EventUtils.synthesizeKey("VK_DELETE", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_DELETE", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is(editor.value, "", "Should have the right value in the editor.");
   is((yield getStyle(testActor, "#div1", "padding-left")), "",
      "Should have updated the padding");
 
-  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is((yield getStyle(testActor, "#div1", "padding-left")), "5px",
      "Should be the right padding.");
   is(span.textContent, 5, "Should have the right value in the box model.");
 }
rename from devtools/client/inspector/components/test/browser_boxmodel_editablemodel_bluronclick.js
rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_bluronclick.js
--- a/devtools/client/inspector/components/test/browser_boxmodel_editablemodel_bluronclick.js
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_bluronclick.js
@@ -25,50 +25,50 @@ add_task(function* () {
 
   yield selectNode("#div1", inspector);
   yield testClickingOutsideEditor(view);
   yield testClickingBelowContainer(view);
 });
 
 function* testClickingOutsideEditor(view) {
   info("Test that clicking outside the editor blurs it");
-  let span = view.doc.querySelector(".old-boxmodel-margin.old-boxmodel-top > span");
+  let span = view.document.querySelector(".boxmodel-margin.boxmodel-top > span");
   is(span.textContent, 10, "Should have the right value in the box model.");
 
-  EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView);
-  let editor = view.doc.querySelector(".styleinspector-propertyeditor");
+  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+  let editor = view.document.querySelector(".styleinspector-propertyeditor");
   ok(editor, "Should have opened the editor.");
 
   info("Click next to the opened editor input.");
   let onBlur = once(editor, "blur");
   let rect = editor.getBoundingClientRect();
   EventUtils.synthesizeMouse(editor, rect.width + 10, rect.height / 2, {},
-    view.doc.defaultView);
+    view.document.defaultView);
   yield onBlur;
 
-  is(view.doc.querySelector(".styleinspector-propertyeditor"), null,
+  is(view.document.querySelector(".styleinspector-propertyeditor"), null,
     "Inplace editor has been removed.");
 }
 
 function* testClickingBelowContainer(view) {
   info("Test that clicking below the box-model container blurs it");
-  let span = view.doc.querySelector(".old-boxmodel-margin.old-boxmodel-top > span");
+  let span = view.document.querySelector(".boxmodel-margin.boxmodel-top > span");
   is(span.textContent, 10, "Should have the right value in the box model.");
 
-  info("Test that clicking below the old-boxmodel-container blurs the opened editor");
-  EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView);
-  let editor = view.doc.querySelector(".styleinspector-propertyeditor");
+  info("Test that clicking below the boxmodel-container blurs the opened editor");
+  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+  let editor = view.document.querySelector(".styleinspector-propertyeditor");
   ok(editor, "Should have opened the editor.");
 
   let onBlur = once(editor, "blur");
-  let container = view.doc.querySelector("#old-boxmodel-container");
+  let container = view.document.querySelector(".boxmodel-container");
   // Using getBoxQuads here because getBoundingClientRect (and therefore synthesizeMouse)
   // use an erroneous height of ~50px for the boxmodel-container.
   let bounds = container.getBoxQuads({relativeTo: view.doc})[0].bounds;
   EventUtils.synthesizeMouseAtPoint(
     bounds.left + 10,
     bounds.top + bounds.height + 10,
-    {}, view.doc.defaultView);
+    {}, view.document.defaultView);
   yield onBlur;
 
-  is(view.doc.querySelector(".styleinspector-propertyeditor"), null,
+  is(view.document.querySelector(".styleinspector-propertyeditor"), null,
     "Inplace editor has been removed.");
 }
rename from devtools/client/inspector/components/test/browser_boxmodel_editablemodel_border.js
rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_border.js
--- a/devtools/client/inspector/components/test/browser_boxmodel_editablemodel_border.js
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_border.js
@@ -19,34 +19,34 @@ add_task(function* () {
   let {inspector, view, testActor} = yield openBoxModelView();
 
   is((yield getStyle(testActor, "#div1", "border-top-width")), "",
      "Should have the right border");
   is((yield getStyle(testActor, "#div1", "border-top-style")), "",
      "Should have the right border");
   yield selectNode("#div1", inspector);
 
-  let span = view.doc.querySelector(".old-boxmodel-border.old-boxmodel-top > span");
+  let span = view.document.querySelector(".boxmodel-border.boxmodel-top > span");
   is(span.textContent, 0, "Should have the right value in the box model.");
 
-  EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView);
-  let editor = view.doc.querySelector(".styleinspector-propertyeditor");
+  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+  let editor = view.document.querySelector(".styleinspector-propertyeditor");
   ok(editor, "Should have opened the editor.");
   is(editor.value, "0", "Should have the right value in the editor.");
 
-  EventUtils.synthesizeKey("1", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("1", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is(editor.value, "1", "Should have the right value in the editor.");
   is((yield getStyle(testActor, "#div1", "border-top-width")), "1px",
      "Should have the right border");
   is((yield getStyle(testActor, "#div1", "border-top-style")), "solid",
      "Should have the right border");
 
-  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is((yield getStyle(testActor, "#div1", "border-top-width")), "",
      "Should be the right padding.");
   is((yield getStyle(testActor, "#div1", "border-top-style")), "",
      "Should have the right border");
   is(span.textContent, 0, "Should have the right value in the box model.");
 });
rename from devtools/client/inspector/components/test/browser_boxmodel_editablemodel_stylerules.js
rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_stylerules.js
--- a/devtools/client/inspector/components/test/browser_boxmodel_editablemodel_stylerules.js
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_stylerules.js
@@ -26,88 +26,88 @@ add_task(function* () {
 
 function* testUnits(inspector, view, testActor) {
   info("Test that entering units works");
 
   is((yield getStyle(testActor, "#div1", "padding-top")), "",
      "Should have the right padding");
   yield selectNode("#div1", inspector);
 
-  let span = view.doc.querySelector(".old-boxmodel-padding.old-boxmodel-top > span");
+  let span = view.document.querySelector(".boxmodel-padding.boxmodel-top > span");
   is(span.textContent, 3, "Should have the right value in the box model.");
 
-  EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView);
-  let editor = view.doc.querySelector(".styleinspector-propertyeditor");
+  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+  let editor = view.document.querySelector(".styleinspector-propertyeditor");
   ok(editor, "Should have opened the editor.");
   is(editor.value, "3px", "Should have the right value in the editor.");
 
-  EventUtils.synthesizeKey("1", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("1", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
-  EventUtils.synthesizeKey("e", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("e", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is((yield getStyle(testActor, "#div1", "padding-top")), "",
      "An invalid value is handled cleanly");
 
-  EventUtils.synthesizeKey("m", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("m", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is(editor.value, "1em", "Should have the right value in the editor.");
   is((yield getStyle(testActor, "#div1", "padding-top")),
      "1em", "Should have updated the padding.");
 
-  EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
 
   is((yield getStyle(testActor, "#div1", "padding-top")), "1em",
      "Should be the right padding.");
   is(span.textContent, 16, "Should have the right value in the box model.");
 }
 
 function* testValueComesFromStyleRule(inspector, view, testActor) {
   info("Test that we pick up the value from a higher style rule");
 
   is((yield getStyle(testActor, "#div2", "border-bottom-width")), "",
      "Should have the right border-bottom-width");
   yield selectNode("#div2", inspector);
 
-  let span = view.doc.querySelector(".old-boxmodel-border.old-boxmodel-bottom > span");
+  let span = view.document.querySelector(".boxmodel-border.boxmodel-bottom > span");
   is(span.textContent, 16, "Should have the right value in the box model.");
 
-  EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView);
-  let editor = view.doc.querySelector(".styleinspector-propertyeditor");
+  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+  let editor = view.document.querySelector(".styleinspector-propertyeditor");
   ok(editor, "Should have opened the editor.");
   is(editor.value, "1em", "Should have the right value in the editor.");
 
-  EventUtils.synthesizeKey("0", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("0", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
 
   is(editor.value, "0", "Should have the right value in the editor.");
   is((yield getStyle(testActor, "#div2", "border-bottom-width")), "0px",
      "Should have updated the border.");
 
-  EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
 
   is((yield getStyle(testActor, "#div2", "border-bottom-width")), "0px",
      "Should be the right border-bottom-width.");
   is(span.textContent, 0, "Should have the right value in the box model.");
 }
 
 function* testShorthandsAreParsed(inspector, view, testActor) {
   info("Test that shorthand properties are parsed correctly");
 
   is((yield getStyle(testActor, "#div3", "padding-right")), "",
      "Should have the right padding");
   yield selectNode("#div3", inspector);
 
-  let span = view.doc.querySelector(".old-boxmodel-padding.old-boxmodel-right > span");
+  let span = view.document.querySelector(".boxmodel-padding.boxmodel-right > span");
   is(span.textContent, 32, "Should have the right value in the box model.");
 
-  EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView);
-  let editor = view.doc.querySelector(".styleinspector-propertyeditor");
+  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+  let editor = view.document.querySelector(".styleinspector-propertyeditor");
   ok(editor, "Should have opened the editor.");
   is(editor.value, "2em", "Should have the right value in the editor.");
 
-  EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
 
   is((yield getStyle(testActor, "#div3", "padding-right")), "",
      "Should be the right padding.");
   is(span.textContent, 32, "Should have the right value in the box model.");
 }
rename from devtools/client/inspector/components/test/browser_boxmodel_guides.js
rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_guides.js
--- a/devtools/client/inspector/components/test/browser_boxmodel_guides.js
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_guides.js
@@ -23,26 +23,26 @@ add_task(function* () {
   yield selectNode("div", inspector);
 
   // Mock the highlighter by replacing the showBoxModel method.
   toolbox.highlighter.showBoxModel = function (nodeFront, options) {
     highlightedNodeFront = nodeFront;
     highlighterOptions = options;
   };
 
-  let elt = view.doc.getElementById("old-boxmodel-margins");
+  let elt = view.document.querySelector(".boxmodel-margins");
   yield testGuideOnLayoutHover(elt, "margin", inspector, view);
 
-  elt = view.doc.getElementById("old-boxmodel-borders");
+  elt = view.document.querySelector(".boxmodel-borders");
   yield testGuideOnLayoutHover(elt, "border", inspector, view);
 
-  elt = view.doc.getElementById("old-boxmodel-padding");
+  elt = view.document.querySelector(".boxmodel-paddings");
   yield testGuideOnLayoutHover(elt, "padding", inspector, view);
 
-  elt = view.doc.getElementById("old-boxmodel-content");
+  elt = view.document.querySelector(".boxmodel-content");
   yield testGuideOnLayoutHover(elt, "content", inspector, view);
 });
 
 function* testGuideOnLayoutHover(elt, expectedRegion, inspector) {
   info("Synthesizing mouseover on the boxmodel-view");
   EventUtils.synthesizeMouse(elt, 2, 2, {type: "mouseover"},
     elt.ownerDocument.defaultView);
 
rename from devtools/client/inspector/components/test/browser_boxmodel_navigation.js
rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_navigation.js
--- a/devtools/client/inspector/components/test/browser_boxmodel_navigation.js
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_navigation.js
@@ -24,80 +24,80 @@ add_task(function* () {
   yield testChangingLevels(inspector, view);
   yield testTabbingWrapAround(inspector, view);
   yield testChangingLevelsByClicking(inspector, view);
 });
 
 function* testInitialFocus(inspector, view) {
   info("Test that the focus is on margin layout.");
   let viewdoc = view.doc;
-  let boxmodel = viewdoc.getElementById("old-boxmodel-wrapper");
+  let boxmodel = viewdoc.getElementById("boxmodel-wrapper");
   boxmodel.focus();
   EventUtils.synthesizeKey("VK_RETURN", {});
 
-  is(boxmodel.getAttribute("aria-activedescendant"), "old-boxmodel-margins",
+  is(boxmodel.getAttribute("aria-activedescendant"), "boxmodel-margins",
     "Should be set to the margin layout.");
 }
 
 function* testChangingLevels(inspector, view) {
   info("Test that using arrow keys updates level.");
   let viewdoc = view.doc;
-  let boxmodel = viewdoc.getElementById("old-boxmodel-wrapper");
+  let boxmodel = viewdoc.getElementById("boxmodel-wrapper");
   boxmodel.focus();
   EventUtils.synthesizeKey("VK_RETURN", {});
   EventUtils.synthesizeKey("VK_ESCAPE", {});
 
   EventUtils.synthesizeKey("VK_DOWN", {});
-  is(boxmodel.getAttribute("aria-activedescendant"), "old-boxmodel-borders",
+  is(boxmodel.getAttribute("aria-activedescendant"), "boxmodel-borders",
     "Should be set to the border layout.");
 
   EventUtils.synthesizeKey("VK_DOWN", {});
-  is(boxmodel.getAttribute("aria-activedescendant"), "old-boxmodel-padding",
+  is(boxmodel.getAttribute("aria-activedescendant"), "boxmodel-padding",
     "Should be set to the padding layout.");
 
   EventUtils.synthesizeKey("VK_UP", {});
-  is(boxmodel.getAttribute("aria-activedescendant"), "old-boxmodel-borders",
+  is(boxmodel.getAttribute("aria-activedescendant"), "boxmodel-borders",
     "Should be set to the border layout.");
 
   EventUtils.synthesizeKey("VK_UP", {});
-  is(boxmodel.getAttribute("aria-activedescendant"), "old-boxmodel-margins",
+  is(boxmodel.getAttribute("aria-activedescendant"), "boxmodel-margins",
     "Should be set to the margin layout.");
 }
 
 function* testTabbingWrapAround(inspector, view) {
   info("Test that using arrow keys updates level.");
   let viewdoc = view.doc;
-  let boxmodel = viewdoc.getElementById("old-boxmodel-wrapper");
+  let boxmodel = viewdoc.getElementById("boxmodel-wrapper");
   boxmodel.focus();
   EventUtils.synthesizeKey("VK_RETURN", {});
 
   let editLevel = boxmodel.getAttribute("aria-activedescendant");
   let dataLevel = viewdoc.getElementById(editLevel).getAttribute("data-box");
   let editBoxes = [...viewdoc.querySelectorAll(
-    `[data-box="${dataLevel}"].old-boxmodel-editable`)];
+    `[data-box="${dataLevel}"].boxmodel-editable`)];
 
   EventUtils.synthesizeKey("VK_ESCAPE", {});
   editBoxes[3].focus();
   EventUtils.synthesizeKey("VK_TAB", {});
   is(editBoxes[0], viewdoc.activeElement, "Top edit box should have focus.");
 
   editBoxes[0].focus();
   EventUtils.synthesizeKey("VK_TAB", { shiftKey: true });
   is(editBoxes[3], viewdoc.activeElement, "Left edit box should have focus.");
 }
 
 function* testChangingLevelsByClicking(inspector, view) {
   info("Test that clicking on levels updates level.");
   let viewdoc = view.doc;
-  let boxmodel = viewdoc.getElementById("old-boxmodel-wrapper");
+  let boxmodel = viewdoc.getElementById("boxmodel-wrapper");
   boxmodel.focus();
 
-  let marginLayout = viewdoc.getElementById("old-boxmodel-margins");
-  let borderLayout = viewdoc.getElementById("old-boxmodel-borders");
-  let paddingLayout = viewdoc.getElementById("old-boxmodel-padding");
+  let marginLayout = viewdoc.getElementById("boxmodel-margins");
+  let borderLayout = viewdoc.getElementById("boxmodel-borders");
+  let paddingLayout = viewdoc.getElementById("boxmodel-padding");
   let layouts = [paddingLayout, borderLayout, marginLayout];
 
   layouts.forEach(layout => {
     layout.click();
     is(boxmodel.getAttribute("aria-activedescendant"), layout.id,
       "Should be set to" + layout.getAttribute("data-box") + "layout.");
   });
 }
rename from devtools/client/inspector/components/test/browser_boxmodel_rotate-labels-on-sides.js
rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_rotate-labels-on-sides.js
--- a/devtools/client/inspector/components/test/browser_boxmodel_rotate-labels-on-sides.js
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_rotate-labels-on-sides.js
@@ -2,28 +2,28 @@
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test that longer values are rotated on the side
 
 const res1 = [
-  {selector: ".old-boxmodel-margin.old-boxmodel-top > span", value: 30},
-  {selector: ".old-boxmodel-margin.old-boxmodel-left > span", value: "auto"},
-  {selector: ".old-boxmodel-margin.old-boxmodel-bottom > span", value: 30},
-  {selector: ".old-boxmodel-margin.old-boxmodel-right > span", value: "auto"},
-  {selector: ".old-boxmodel-padding.old-boxmodel-top > span", value: 20},
-  {selector: ".old-boxmodel-padding.old-boxmodel-left > span", value: 2000000},
-  {selector: ".old-boxmodel-padding.old-boxmodel-bottom > span", value: 20},
-  {selector: ".old-boxmodel-padding.old-boxmodel-right > span", value: 20},
-  {selector: ".old-boxmodel-border.old-boxmodel-top > span", value: 10},
-  {selector: ".old-boxmodel-border.old-boxmodel-left > span", value: 10},
-  {selector: ".old-boxmodel-border.old-boxmodel-bottom > span", value: 10},
-  {selector: ".old-boxmodel-border.old-boxmodel-right > span", value: 10},
+  {selector: ".boxmodel-margin.boxmodel-top > span", value: 30},
+  {selector: ".boxmodel-margin.boxmodel-left > span", value: "auto"},
+  {selector: ".boxmodel-margin.boxmodel-bottom > span", value: 30},
+  {selector: ".boxmodel-margin.boxmodel-right > span", value: "auto"},
+  {selector: ".boxmodel-padding.boxmodel-top > span", value: 20},
+  {selector: ".boxmodel-padding.boxmodel-left > span", value: 2000000},
+  {selector: ".boxmodel-padding.boxmodel-bottom > span", value: 20},
+  {selector: ".boxmodel-padding.boxmodel-right > span", value: 20},
+  {selector: ".boxmodel-border.boxmodel-top > span", value: 10},
+  {selector: ".boxmodel-border.boxmodel-left > span", value: 10},
+  {selector: ".boxmodel-border.boxmodel-bottom > span", value: 10},
+  {selector: ".boxmodel-border.boxmodel-right > span", value: 10},
 ];
 
 const TEST_URI = encodeURIComponent([
   "<style>",
   "div { border:10px solid black; padding: 20px 20px 20px 2000000px; " +
   "margin: 30px auto; }",
   "</style>",
   "<div></div>"
@@ -31,19 +31,19 @@ const TEST_URI = encodeURIComponent([
 const LONG_TEXT_ROTATE_LIMIT = 3;
 
 add_task(function* () {
   yield addTab("data:text/html," + TEST_URI);
   let {inspector, view} = yield openBoxModelView();
   yield selectNode("div", inspector);
 
   for (let i = 0; i < res1.length; i++) {
-    let elt = view.doc.querySelector(res1[i].selector);
+    let elt = view.document.querySelector(res1[i].selector);
     let isLong = elt.textContent.length > LONG_TEXT_ROTATE_LIMIT;
     let classList = elt.parentNode.classList;
-    let canBeRotated = classList.contains("old-boxmodel-left") ||
-                       classList.contains("old-boxmodel-right");
-    let isRotated = classList.contains("old-boxmodel-rotate");
+    let canBeRotated = classList.contains("boxmodel-left") ||
+                       classList.contains("boxmodel-right");
+    let isRotated = classList.contains("boxmodel-rotate");
 
     is(canBeRotated && isLong,
       isRotated, res1[i].selector + " correctly rotated.");
   }
 });
rename from devtools/client/inspector/components/test/browser_boxmodel_sync.js
rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_sync.js
--- a/devtools/client/inspector/components/test/browser_boxmodel_sync.js
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_sync.js
@@ -12,24 +12,24 @@ add_task(function* () {
   yield addTab("data:text/html," + encodeURIComponent(TEST_URI));
   let {inspector, view} = yield openBoxModelView();
 
   info("When a property is edited, it should sync in the rule view");
 
   yield selectNode("p", inspector);
 
   info("Modify padding-bottom in box model view");
-  let span = view.doc.querySelector(".old-boxmodel-padding.old-boxmodel-bottom > span");
-  EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView);
-  let editor = view.doc.querySelector(".styleinspector-propertyeditor");
+  let span = view.document.querySelector(".boxmodel-padding.boxmodel-bottom > span");
+  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+  let editor = view.document.querySelector(".styleinspector-propertyeditor");
 
-  EventUtils.synthesizeKey("7", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("7", {}, view.document.defaultView);
   yield waitForUpdate(inspector);
   is(editor.value, "7", "Should have the right value in the editor.");
-  EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView);
+  EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
 
   let onRuleViewRefreshed = once(inspector, "rule-view-refreshed");
   let onRuleViewSelected = once(inspector.sidebar, "ruleview-selected");
   info("Select the rule view and check that the property was synced there");
   let ruleView = selectRuleView(inspector);
 
   info("Wait for the rule view to be selected");
   yield onRuleViewSelected;
rename from devtools/client/inspector/components/test/browser_boxmodel_tooltips.js
rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_tooltips.js
--- a/devtools/client/inspector/components/test/browser_boxmodel_tooltips.js
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_tooltips.js
@@ -71,49 +71,49 @@ const VALUES_TEST_DATA = [{
 }];
 
 add_task(function* () {
   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {inspector, view} = yield openBoxModelView();
 
   info("Checking the regions tooltips");
 
-  ok(view.doc.querySelector("#old-boxmodel-margins").hasAttribute("title"),
+  ok(view.document.querySelector(".boxmodel-margins").hasAttribute("title"),
     "The margin region has a tooltip");
-  is(view.doc.querySelector("#old-boxmodel-margins").getAttribute("title"), "margin",
+  is(view.document.querySelector(".boxmodel-margins").getAttribute("title"), "margin",
     "The margin region has the correct tooltip content");
 
-  ok(view.doc.querySelector("#old-boxmodel-borders").hasAttribute("title"),
+  ok(view.document.querySelector(".boxmodel-borders").hasAttribute("title"),
     "The border region has a tooltip");
-  is(view.doc.querySelector("#old-boxmodel-borders").getAttribute("title"), "border",
+  is(view.document.querySelector(".boxmodel-borders").getAttribute("title"), "border",
     "The border region has the correct tooltip content");
 
-  ok(view.doc.querySelector("#old-boxmodel-padding").hasAttribute("title"),
+  ok(view.document.querySelector(".boxmodel-paddings").hasAttribute("title"),
     "The padding region has a tooltip");
-  is(view.doc.querySelector("#old-boxmodel-padding").getAttribute("title"), "padding",
+  is(view.document.querySelector(".boxmodel-paddings").getAttribute("title"), "padding",
     "The padding region has the correct tooltip content");
 
-  ok(view.doc.querySelector("#old-boxmodel-content").hasAttribute("title"),
+  ok(view.document.querySelector(".boxmodel-content").hasAttribute("title"),
     "The content region has a tooltip");
-  is(view.doc.querySelector("#old-boxmodel-content").getAttribute("title"), "content",
+  is(view.document.querySelector(".boxmodel-content").getAttribute("title"), "content",
     "The content region has the correct tooltip content");
 
   for (let {selector, values} of VALUES_TEST_DATA) {
     info("Selecting " + selector + " and checking the values tooltips");
     yield selectNode(selector, inspector);
 
     info("Iterate over all values");
     for (let key in view.map) {
       if (key === "position") {
         continue;
       }
 
       let name = view.map[key].property;
       let expectedTooltipData = values.find(o => o.name === name);
-      let el = view.doc.querySelector(view.map[key].selector);
+      let el = view.document.querySelector(view.map[key].selector);
 
       ok(el.hasAttribute("title"), "The " + name + " value has a tooltip");
 
       if (expectedTooltipData) {
         info("The " + name + " value comes from a css rule");
         let expectedTooltip = name + "\n" + expectedTooltipData.ruleSelector +
                               "\n" + expectedTooltipData.styleSheetLocation;
         is(el.getAttribute("title"), expectedTooltip, "The tooltip is correct");
rename from devtools/client/inspector/components/test/browser_boxmodel_update-after-navigation.js
rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-navigation.js
--- a/devtools/client/inspector/components/test/browser_boxmodel_update-after-navigation.js
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-navigation.js
@@ -12,79 +12,78 @@ const IFRAME2 = URL_ROOT + "doc_boxmodel
 
 add_task(function* () {
   yield addTab(IFRAME1);
   let {inspector, view, testActor} = yield openBoxModelView();
 
   yield testFirstPage(inspector, view, testActor);
 
   info("Navigate to the second page");
+  let onMarkupLoaded = waitForMarkupLoaded(inspector);
   yield testActor.eval(`content.location.href="${IFRAME2}"`);
-  yield inspector.once("markuploaded");
+  yield onMarkupLoaded;
 
   yield testSecondPage(inspector, view, testActor);
 
   info("Go back to the first page");
+  onMarkupLoaded = waitForMarkupLoaded(inspector);
   yield testActor.eval("content.history.back();");
-  yield inspector.once("markuploaded");
+  yield onMarkupLoaded;
 
   yield testBackToFirstPage(inspector, view, testActor);
 });
 
 function* testFirstPage(inspector, view, testActor) {
   info("Test that the box model view works on the first page");
 
-  info("Selecting the test node");
   yield selectNode("p", inspector);
 
   info("Checking that the box model view shows the right value");
-  let paddingElt = view.doc.querySelector(
-    ".old-boxmodel-padding.old-boxmodel-top > span");
+  let paddingElt = view.document.querySelector(
+    ".boxmodel-padding.boxmodel-top > span");
   is(paddingElt.textContent, "50");
 
   info("Listening for box model view changes and modifying the padding");
   let onUpdated = waitForUpdate(inspector);
   yield setStyle(testActor, "p", "padding", "20px");
   yield onUpdated;
   ok(true, "Box model view got updated");
 
   info("Checking that the box model view shows the right value after update");
   is(paddingElt.textContent, "20");
 }
 
 function* testSecondPage(inspector, view, testActor) {
   info("Test that the box model view works on the second page");
 
-  info("Selecting the test node");
   yield selectNode("p", inspector);
 
   info("Checking that the box model view shows the right value");
-  let sizeElt = view.doc.querySelector(".old-boxmodel-size > span");
+  let sizeElt = view.document.querySelector(".boxmodel-size > span");
   is(sizeElt.textContent, "100" + "\u00D7" + "100");
 
   info("Listening for box model view changes and modifying the size");
   let onUpdated = waitForUpdate(inspector);
   yield setStyle(testActor, "p", "width", "200px");
   yield onUpdated;
   ok(true, "Box model view got updated");
 
   info("Checking that the box model view shows the right value after update");
   is(sizeElt.textContent, "200" + "\u00D7" + "100");
 }
 
 function* testBackToFirstPage(inspector, view, testActor) {
   info("Test that the box model view works on the first page after going back");
 
-  info("Selecting the test node");
   yield selectNode("p", inspector);
 
   info("Checking that the box model view shows the right value, which is the" +
     "modified value from step one because of the bfcache");
-  let paddingElt = view.doc.querySelector(
-    ".old-boxmodel-padding.old-boxmodel-top > span");
+  let paddingElt = view.document.querySelector(
+    ".boxmodel-padding.boxmodel-top > span");
   is(paddingElt.textContent, "20");
 
   info("Listening for box model view changes and modifying the padding");
   let onUpdated = waitForUpdate(inspector);
   yield setStyle(testActor, "p", "padding", "100px");
   yield onUpdated;
   ok(true, "Box model view got updated");
 
rename from devtools/client/inspector/components/test/browser_boxmodel_update-after-reload.js
rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-reload.js
--- a/devtools/client/inspector/components/test/browser_boxmodel_update-after-reload.js
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-reload.js
@@ -9,30 +9,30 @@
 add_task(function* () {
   yield addTab(URL_ROOT + "doc_boxmodel_iframe1.html");
   let {inspector, view, testActor} = yield openBoxModelView();
 
   info("Test that the box model view works on the first page");
   yield assertBoxModelView(inspector, view, testActor);
 
   info("Reload the page");
+  let onMarkupLoaded = waitForMarkupLoaded(inspector);
   yield testActor.reload();
-  yield inspector.once("markuploaded");
+  yield onMarkupLoaded;
 
   info("Test that the box model view works on the reloaded page");
   yield assertBoxModelView(inspector, view, testActor);
 });
 
 function* assertBoxModelView(inspector, view, testActor) {
-  info("Selecting the test node");
   yield selectNode("p", inspector);
 
   info("Checking that the box model view shows the right value");
-  let paddingElt = view.doc.querySelector(
-    ".old-boxmodel-padding.old-boxmodel-top > span");
+  let paddingElt = view.document.querySelector(
+    ".boxmodel-padding.boxmodel-top > span");
   is(paddingElt.textContent, "50");
 
   info("Listening for box model view changes and modifying the padding");
   let onUpdated = waitForUpdate(inspector);
   yield setStyle(testActor, "p", "padding", "20px");
   yield onUpdated;
   ok(true, "Box model view got updated");
 
rename from devtools/client/inspector/components/test/browser_boxmodel_update-in-iframes.js
rename to devtools/client/inspector/boxmodel/test/browser_boxmodel_update-in-iframes.js
--- a/devtools/client/inspector/components/test/browser_boxmodel_update-in-iframes.js
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-in-iframes.js
@@ -17,17 +17,17 @@ add_task(function* () {
 
 function* testResizingInIframe(inspector, view, testActor) {
   info("Test that resizing an element in an iframe updates its box model");
 
   info("Selecting the nested test node");
   yield selectNodeInIframe2("div", inspector);
 
   info("Checking that the box model view shows the right value");
-  let sizeElt = view.doc.querySelector(".old-boxmodel-size > span");
+  let sizeElt = view.document.querySelector(".boxmodel-size > span");
   is(sizeElt.textContent, "400\u00D7200");
 
   info("Listening for box model view changes and modifying its size");
   let onUpdated = waitForUpdate(inspector);
   yield setStyleInIframe2(testActor, "div", "width", "200px");
   yield onUpdated;
   ok(true, "Box model view got updated");
 
@@ -35,24 +35,25 @@ function* testResizingInIframe(inspector
   is(sizeElt.textContent, "200\u00D7200");
 }
 
 function* testReflowsAfterIframeDeletion(inspector, view, testActor) {
   info("Test reflows are still sent to the box model view after deleting an " +
        "iframe");
 
   info("Deleting the iframe2");
+  let onInspectorUpdated = inspector.once("inspector-updated");
   yield removeIframe2(testActor);
-  yield inspector.once("inspector-updated");
+  yield onInspectorUpdated;
 
   info("Selecting the test node in iframe1");
   yield selectNodeInIframe1("p", inspector);
 
   info("Checking that the box model view shows the right value");
-  let sizeElt = view.doc.querySelector(".old-boxmodel-size > span");
+  let sizeElt = view.document.querySelector(".boxmodel-size > span");
   is(sizeElt.textContent, "100\u00D7100");
 
   info("Listening for box model view changes and modifying its size");
   let onUpdated = waitForUpdate(inspector);
   yield setStyleInIframe1(testActor, "p", "width", "200px");
   yield onUpdated;
   ok(true, "Box model view got updated");
 
rename from devtools/client/inspector/components/test/doc_boxmodel_iframe1.html
rename to devtools/client/inspector/boxmodel/test/doc_boxmodel_iframe1.html
rename from devtools/client/inspector/components/test/doc_boxmodel_iframe2.html
rename to devtools/client/inspector/boxmodel/test/doc_boxmodel_iframe2.html
rename from devtools/client/inspector/components/test/head.js
rename to devtools/client/inspector/boxmodel/test/head.js
--- a/devtools/client/inspector/components/test/head.js
+++ b/devtools/client/inspector/boxmodel/test/head.js
@@ -14,37 +14,39 @@ Services.scriptloader.loadSubScript(
 Services.prefs.setIntPref("devtools.toolbox.footer.height", 350);
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.toolbox.footer.height");
 });
 
 /**
  * Highlight a node and set the inspector's current selection to the node or
  * the first match of the given css selector.
- * @param {String|NodeFront} selectorOrNodeFront
- *        The selector for the node to be set, or the nodeFront
- * @param {InspectorPanel} inspector
- *        The instance of InspectorPanel currently loaded in the toolbox
- * @return a promise that resolves when the inspector is updated with the new
- * node
+ *
+ * @param  {String|NodeFront} selectorOrNodeFront
+ *         The selector for the node to be set, or the nodeFront.
+ * @param  {InspectorPanel} inspector
+ *         The instance of InspectorPanel currently loaded in the toolbox.
+ * @return {Promise} a promise that resolves when the inspector is updated with the new
+ *         node.
  */
 function* selectAndHighlightNode(selectorOrNodeFront, inspector) {
   info("Highlighting and selecting the node " + selectorOrNodeFront);
 
   let nodeFront = yield getNodeFront(selectorOrNodeFront, inspector);
   let updated = inspector.toolbox.once("highlighter-ready");
   inspector.selection.setNodeFront(nodeFront, "test-highlight");
   yield updated;
 }
 
 /**
  * Open the toolbox, with the inspector tool visible, and the computed view
  * sidebar tab selected to display the box model view.
- * @return a promise that resolves when the inspector is ready and the box model
- * view is visible and ready
+ *
+ * @return {Promise} a promise that resolves when the inspector is ready and the box model
+ *         view is visible and ready.
  */
 function openBoxModelView() {
   return openInspectorSidebarTab("computedview").then(data => {
     // The actual highligher show/hide methods are mocked in box model tests.
     // The highlighter is tested in devtools/inspector/test.
     function mockHighlighter({highlighter}) {
       highlighter.showBoxModel = function () {
         return promise.resolve();
@@ -53,35 +55,74 @@ function openBoxModelView() {
         return promise.resolve();
       };
     }
     mockHighlighter(data.toolbox);
 
     return {
       toolbox: data.toolbox,
       inspector: data.inspector,
-      view: data.inspector.computedview.boxModelView,
+      view: data.inspector.computedview,
       testActor: data.testActor
     };
   });
 }
 
 /**
  * Wait for the boxmodel-view-updated event.
- * @return a promise
+ *
+ * @param  {InspectorPanel} inspector
+ *         The instance of InspectorPanel currently loaded in the toolbox.
+ * @param  {Boolean} waitForSelectionUpdate
+ *         Should the boxmodel-view-updated event come from a new selection.
+ * @return {Promise} a promise
  */
-function waitForUpdate(inspector) {
-  return inspector.once("boxmodel-view-updated");
+function waitForUpdate(inspector, waitForSelectionUpdate) {
+  return new Promise(resolve => {
+    inspector.on("boxmodel-view-updated", function onUpdate(e, reasons) {
+      // Wait for another update event if we are waiting for a selection related event.
+      if (waitForSelectionUpdate && !reasons.includes("new-selection")) {
+        return;
+      }
+
+      inspector.off("boxmodel-view-updated", onUpdate);
+      resolve();
+    });
+  });
+}
+
+/**
+ * Wait for both boxmode-view-updated and markuploaded events.
+ *
+ * @return {Promise} a promise that resolves when both events have been received.
+ */
+function waitForMarkupLoaded(inspector) {
+  return Promise.all([
+    waitForUpdate(inspector),
+    inspector.once("markuploaded"),
+  ]);
 }
 
 function getStyle(testActor, selector, propertyName) {
   return testActor.eval(`
     content.document.querySelector("${selector}")
                     .style.getPropertyValue("${propertyName}");
   `);
 }
 
 function setStyle(testActor, selector, propertyName, value) {
   return testActor.eval(`
     content.document.querySelector("${selector}")
                     .style.${propertyName} = "${value}";
   `);
 }
+
+/**
+ * The box model doesn't participate in the inspector's update mechanism, so simply
+ * calling the default selectNode isn't enough to guarantee that the box model view has
+ * finished updating. We also need to wait for the "boxmodel-view-updated" event.
+ */
+var _selectNode = selectNode;
+selectNode = function* (node, inspector, reason) {
+  let onUpdate = waitForUpdate(inspector, true);
+  yield _selectNode(node, inspector, reason);
+  yield onUpdate;
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/boxmodel/types.js
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { PropTypes } = require("devtools/client/shared/vendor/react");
+
+/**
+ * The box model data for the current selected node.
+ */
+exports.boxModel = {
+
+  // The layout information of the current selected node
+  layout: PropTypes.object,
+
+};
rename from devtools/client/inspector/layout/utils/editing-session.js
rename to devtools/client/inspector/boxmodel/utils/editing-session.js
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/boxmodel/utils/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+    'editing-session.js',
+)
deleted file mode 100644
--- a/devtools/client/inspector/components/deprecated-box-model.js
+++ /dev/null
@@ -1,912 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/**
- * THIS MODULE IS DEPRECATED.
- *
- * To continue any work related to the box model view, see the new react/redux
- * implementation in devtools/client/inspector/layout/.
- */
-
-"use strict";
-
-const {Task} = require("devtools/shared/task");
-const {InplaceEditor, editableItem} =
-      require("devtools/client/shared/inplace-editor");
-const {ReflowFront} = require("devtools/shared/fronts/reflow");
-const {LocalizationHelper} = require("devtools/shared/l10n");
-const {getCssProperties} = require("devtools/shared/fronts/css-properties");
-const {KeyCodes} = require("devtools/client/shared/keycodes");
-const EditingSession = require("devtools/client/inspector/layout/utils/editing-session");
-
-const STRINGS_URI = "devtools/client/locales/shared.properties";
-const STRINGS_INSPECTOR = "devtools/shared/locales/styleinspector.properties";
-const SHARED_L10N = new LocalizationHelper(STRINGS_URI);
-const INSPECTOR_L10N = new LocalizationHelper(STRINGS_INSPECTOR);
-const NUMERIC = /^-?[\d\.]+$/;
-const LONG_TEXT_ROTATE_LIMIT = 3;
-
-/**
- * The box model view
- * @param {InspectorPanel} inspector
- *        An instance of the inspector-panel currently loaded in the toolbox
- * @param {Document} document
- *        The document that will contain the box model view.
- */
-function BoxModelView(inspector, document) {
-  this.inspector = inspector;
-  this.doc = document;
-  this.wrapper = this.doc.getElementById("old-boxmodel-wrapper");
-  this.container = this.doc.getElementById("old-boxmodel-container");
-  this.expander = this.doc.getElementById("old-boxmodel-expander");
-  this.sizeLabel = this.doc.querySelector(".old-boxmodel-size > span");
-  this.sizeHeadingLabel = this.doc.getElementById("old-boxmodel-element-size");
-  this._geometryEditorHighlighter = null;
-  this._cssProperties = getCssProperties(inspector.toolbox);
-
-  this.init();
-}
-
-BoxModelView.prototype = {
-  init: function () {
-    this.update = this.update.bind(this);
-
-    this.onNewSelection = this.onNewSelection.bind(this);
-    this.inspector.selection.on("new-node-front", this.onNewSelection);
-
-    this.onNewNode = this.onNewNode.bind(this);
-    this.inspector.sidebar.on("computedview-selected", this.onNewNode);
-
-    this.onSidebarSelect = this.onSidebarSelect.bind(this);
-    this.inspector.sidebar.on("select", this.onSidebarSelect);
-
-    this.onToggleExpander = this.onToggleExpander.bind(this);
-    this.expander.addEventListener("click", this.onToggleExpander);
-    let header = this.doc.getElementById("old-boxmodel-header");
-    header.addEventListener("dblclick", this.onToggleExpander);
-
-    this.onFilterComputedView = this.onFilterComputedView.bind(this);
-    this.inspector.on("computed-view-filtered",
-      this.onFilterComputedView);
-
-    this.onPickerStarted = this.onPickerStarted.bind(this);
-    this.onMarkupViewLeave = this.onMarkupViewLeave.bind(this);
-    this.onMarkupViewNodeHover = this.onMarkupViewNodeHover.bind(this);
-    this.onWillNavigate = this.onWillNavigate.bind(this);
-    this.onKeyDown = this.onKeyDown.bind(this);
-    this.onLevelClick = this.onLevelClick.bind(this);
-    this.setAriaActive = this.setAriaActive.bind(this);
-    this.getEditBoxes = this.getEditBoxes.bind(this);
-    this.makeFocusable = this.makeFocusable.bind(this);
-    this.makeUnfocasable = this.makeUnfocasable.bind(this);
-    this.moveFocus = this.moveFocus.bind(this);
-    this.onFocus = this.onFocus.bind(this);
-
-    this.borderLayout = this.doc.getElementById("old-boxmodel-borders");
-    this.boxModel = this.doc.getElementById("old-boxmodel-wrapper");
-    this.marginLayout = this.doc.getElementById("old-boxmodel-margins");
-    this.paddingLayout = this.doc.getElementById("old-boxmodel-padding");
-
-    this.layouts = {
-      "margin": new Map([
-        [KeyCodes.DOM_VK_ESCAPE, this.marginLayout],
-        [KeyCodes.DOM_VK_DOWN, this.borderLayout],
-        [KeyCodes.DOM_VK_UP, null],
-        ["click", this.marginLayout]
-      ]),
-      "border": new Map([
-        [KeyCodes.DOM_VK_ESCAPE, this.borderLayout],
-        [KeyCodes.DOM_VK_DOWN, this.paddingLayout],
-        [KeyCodes.DOM_VK_UP, this.marginLayout],
-        ["click", this.borderLayout]
-      ]),
-      "padding": new Map([
-        [KeyCodes.DOM_VK_ESCAPE, this.paddingLayout],
-        [KeyCodes.DOM_VK_DOWN, null],
-        [KeyCodes.DOM_VK_UP, this.borderLayout],
-        ["click", this.paddingLayout]
-      ])
-    };
-
-    this.boxModel.addEventListener("click", this.onLevelClick, true);
-    this.boxModel.addEventListener("focus", this.onFocus, true);
-    this.boxModel.addEventListener("keydown", this.onKeyDown, true);
-
-    this.initBoxModelHighlighter();
-
-    // Store for the different dimensions of the node.
-    // 'selector' refers to the element that holds the value;
-    // 'property' is what we are measuring;
-    // 'value' is the computed dimension, computed in update().
-    this.map = {
-      position: {
-        selector: "#old-boxmodel-element-position",
-        property: "position",
-        value: undefined
-      },
-      marginTop: {
-        selector: ".old-boxmodel-margin.old-boxmodel-top > span",
-        property: "margin-top",
-        value: undefined
-      },
-      marginBottom: {
-        selector: ".old-boxmodel-margin.old-boxmodel-bottom > span",
-        property: "margin-bottom",
-        value: undefined
-      },
-      marginLeft: {
-        selector: ".old-boxmodel-margin.old-boxmodel-left > span",
-        property: "margin-left",
-        value: undefined
-      },
-      marginRight: {
-        selector: ".old-boxmodel-margin.old-boxmodel-right > span",
-        property: "margin-right",
-        value: undefined
-      },
-      paddingTop: {
-        selector: ".old-boxmodel-padding.old-boxmodel-top > span",
-        property: "padding-top",
-        value: undefined
-      },
-      paddingBottom: {
-        selector: ".old-boxmodel-padding.old-boxmodel-bottom > span",
-        property: "padding-bottom",
-        value: undefined
-      },
-      paddingLeft: {
-        selector: ".old-boxmodel-padding.old-boxmodel-left > span",
-        property: "padding-left",
-        value: undefined
-      },
-      paddingRight: {
-        selector: ".old-boxmodel-padding.old-boxmodel-right > span",
-        property: "padding-right",
-        value: undefined
-      },
-      borderTop: {
-        selector: ".old-boxmodel-border.old-boxmodel-top > span",
-        property: "border-top-width",
-        value: undefined
-      },
-      borderBottom: {
-        selector: ".old-boxmodel-border.old-boxmodel-bottom > span",
-        property: "border-bottom-width",
-        value: undefined
-      },
-      borderLeft: {
-        selector: ".old-boxmodel-border.old-boxmodel-left > span",
-        property: "border-left-width",
-        value: undefined
-      },
-      borderRight: {
-        selector: ".old-boxmodel-border.old-boxmodel-right > span",
-        property: "border-right-width",
-        value: undefined
-      }
-    };
-
-    // Make each element the dimensions editable
-    for (let i in this.map) {
-      if (i == "position") {
-        continue;
-      }
-
-      let dimension = this.map[i];
-      editableItem({
-        element: this.doc.querySelector(dimension.selector)
-      }, (element, event) => {
-        this.initEditor(element, event, dimension);
-      });
-    }
-
-    this.onNewNode();
-
-    let nodeGeometry = this.doc.getElementById("old-layout-geometry-editor");
-    this.onGeometryButtonClick = this.onGeometryButtonClick.bind(this);
-    nodeGeometry.addEventListener("click", this.onGeometryButtonClick);
-  },
-
-  initBoxModelHighlighter: function () {
-    let highlightElts = this.doc.querySelectorAll("#old-boxmodel-container *[title]");
-    this.onHighlightMouseOver = this.onHighlightMouseOver.bind(this);
-    this.onHighlightMouseOut = this.onHighlightMouseOut.bind(this);
-
-    for (let element of highlightElts) {
-      element.addEventListener("mouseover", this.onHighlightMouseOver, true);
-      element.addEventListener("mouseout", this.onHighlightMouseOut, true);
-    }
-  },
-
-  /**
-   * Start listening to reflows in the current tab.
-   */
-  trackReflows: function () {
-    if (!this.reflowFront) {
-      let { target } = this.inspector;
-      if (target.form.reflowActor) {
-        this.reflowFront = ReflowFront(target.client,
-                                       target.form);
-      } else {
-        return;
-      }
-    }
-
-    this.reflowFront.on("reflows", this.update);
-    this.reflowFront.start();
-  },
-
-  /**
-   * Stop listening to reflows in the current tab.
-   */
-  untrackReflows: function () {
-    if (!this.reflowFront) {
-      return;
-    }
-
-    this.reflowFront.off("reflows", this.update);
-    this.reflowFront.stop();
-  },
-
-  /**
-   * Called when the user clicks on one of the editable values in the box model view
-   */
-  initEditor: function (element, event, dimension) {
-    let { property } = dimension;
-    let session = new EditingSession(this);
-    let initialValue = session.getProperty(property);
-
-    let editor = new InplaceEditor({
-      element: element,
-      initial: initialValue,
-      contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE,
-      property: {
-        name: dimension.property
-      },
-      start: self => {
-        self.elt.parentNode.classList.add("old-boxmodel-editing");
-      },
-      change: value => {
-        if (NUMERIC.test(value)) {
-          value += "px";
-        }
-
-        let properties = [
-          { name: property, value: value }
-        ];
-
-        if (property.substring(0, 7) == "border-") {
-          let bprop = property.substring(0, property.length - 5) + "style";
-          let style = session.getProperty(bprop);
-          if (!style || style == "none" || style == "hidden") {
-            properties.push({ name: bprop, value: "solid" });
-          }
-        }
-
-        session.setProperties(properties).catch(e => console.error(e));
-      },
-      done: (value, commit) => {
-        editor.elt.parentNode.classList.remove("old-boxmodel-editing");
-        if (!commit) {
-          session.revert().then(() => {
-            session.destroy();
-          }, e => console.error(e));
-        }
-      },
-      contextMenu: this.inspector.onTextBoxContextMenu,
-      cssProperties: this._cssProperties
-    }, event);
-  },
-
-  /**
-   * Is the BoxModelView visible in the sidebar.
-   * @return {Boolean}
-   */
-  isViewVisible: function () {
-    return this.inspector &&
-           this.inspector.sidebar.getCurrentTabID() == "computedview";
-  },
-
-  /**
-   * Is the BoxModelView visible in the sidebar and is the current node valid to
-   * be displayed in the view.
-   * @return {Boolean}
-   */
-  isViewVisibleAndNodeValid: function () {
-    return this.isViewVisible() &&
-           this.inspector.selection.isConnected() &&
-           this.inspector.selection.isElementNode();
-  },
-
-  /**
-   * Destroy the nodes. Remove listeners.
-   */
-  destroy: function () {
-    let highlightElts = this.doc.querySelectorAll("#old-boxmodel-container *[title]");
-
-    for (let element of highlightElts) {
-      element.removeEventListener("mouseover", this.onHighlightMouseOver, true);
-      element.removeEventListener("mouseout", this.onHighlightMouseOut, true);
-    }
-
-    this.expander.removeEventListener("click", this.onToggleExpander);
-    let header = this.doc.getElementById("old-boxmodel-header");
-    header.removeEventListener("dblclick", this.onToggleExpander);
-
-    let nodeGeometry = this.doc.getElementById("old-layout-geometry-editor");
-    nodeGeometry.removeEventListener("click", this.onGeometryButtonClick);
-
-    this.boxModel.removeEventListener("click", this.onLevelClick, true);
-    this.boxModel.removeEventListener("focus", this.onFocus, true);
-    this.boxModel.removeEventListener("keydown", this.onKeyDown, true);
-
-    this.inspector.off("picker-started", this.onPickerStarted);
-
-    // Inspector Panel will destroy `markup` object on "will-navigate" event,
-    // therefore we have to check if it's still available in case BoxModelView
-    // is destroyed immediately after.
-    if (this.inspector.markup) {
-      this.inspector.markup.off("leave", this.onMarkupViewLeave);
-      this.inspector.markup.off("node-hover", this.onMarkupViewNodeHover);
-    }
-
-    this.inspector.sidebar.off("computedview-selected", this.onNewNode);
-    this.inspector.selection.off("new-node-front", this.onNewSelection);
-    this.inspector.sidebar.off("select", this.onSidebarSelect);
-    this.inspector.target.off("will-navigate", this.onWillNavigate);
-    this.inspector.off("computed-view-filtered", this.onFilterComputedView);
-
-    this.inspector = null;
-    this.doc = null;
-    this.wrapper = null;
-    this.container = null;
-    this.expander = null;
-    this.sizeLabel = null;
-    this.sizeHeadingLabel = null;
-
-    this.marginLayout = null;
-    this.borderLayout = null;
-    this.paddingLayout = null;
-    this.boxModel = null;
-    this.layouts = null;
-
-    if (this.reflowFront) {
-      this.untrackReflows();
-      this.reflowFront.destroy();
-      this.reflowFront = null;
-    }
-  },
-
-  /**
-   * Set initial box model focus to the margin layout.
-   */
-  onFocus: function () {
-    let activeDescendant = this.boxModel.getAttribute("aria-activedescendant");
-
-    if (!activeDescendant) {
-      let nextLayout = this.marginLayout;
-      this.setAriaActive(nextLayout);
-    }
-  },
-
-  /**
-   * Active aria-level set to current layout.
-   *
-   * @param {Element} nextLayout
-   *        Element of next layout that user has navigated to
-   * @param {Node} target
-   *        Node to be observed
-   */
-  setAriaActive: function (nextLayout, target) {
-    this.boxModel.setAttribute("aria-activedescendant", nextLayout.id);
-    if (target && target._editable) {
-      target.blur();
-    }
-
-    // Clear all
-    this.marginLayout.classList.remove("layout-active-elm");
-    this.borderLayout.classList.remove("layout-active-elm");
-    this.paddingLayout.classList.remove("layout-active-elm");
-
-    // Set the next level's border outline
-    nextLayout.classList.add("layout-active-elm");
-  },
-
-  /**
-   * Update aria-active on mouse click.
-   *
-   * @param {Event} event
-   *         The event triggered by a mouse click on the box model
-   */
-  onLevelClick: function (event) {
-    let {target} = event;
-    let nextLayout = this.layouts[target.getAttribute("data-box")].get("click");
-
-    this.setAriaActive(nextLayout, target);
-  },
-
-  /**
-   * Handle keyboard navigation and focus for box model layouts.
-   *
-   * Updates active layout on arrow key navigation
-   * Focuses next layout's editboxes on enter key
-   * Unfocuses current layout's editboxes when active layout changes
-   * Controls tabbing between editBoxes
-   *
-   * @param {Event} event
-   *         The event triggered by a keypress on the box model
-   */
-  onKeyDown: function (event) {
-    let {target, keyCode} = event;
-    // If focused on editable value or in editing mode
-    let isEditable = target._editable || target.editor;
-    let level = this.boxModel.getAttribute("aria-activedescendant");
-    let editingMode = target.tagName === "input";
-    let nextLayout;
-
-    switch (keyCode) {
-      case KeyCodes.DOM_VK_RETURN:
-        if (!isEditable) {
-          this.makeFocusable(level);
-        }
-        break;
-      case KeyCodes.DOM_VK_DOWN:
-      case KeyCodes.DOM_VK_UP:
-        if (!editingMode) {
-          event.preventDefault();
-          this.makeUnfocasable(level);
-          let datalevel = this.doc.getElementById(level).getAttribute("data-box");
-          nextLayout = this.layouts[datalevel].get(keyCode);
-          this.boxModel.focus();
-        }
-        break;
-      case KeyCodes.DOM_VK_TAB:
-        if (isEditable) {
-          event.preventDefault();
-          this.moveFocus(event, level);
-        }
-        break;
-      case KeyCodes.DOM_VK_ESCAPE:
-        if (isEditable && target._editable) {
-          event.preventDefault();
-          event.stopPropagation();
-          this.makeUnfocasable(level);
-          this.boxModel.focus();
-        }
-        break;
-      default:
-        break;
-    }
-
-    if (nextLayout) {
-      this.setAriaActive(nextLayout, target);
-    }
-  },
-
-  /**
-   * Make previous layout's elements unfocusable.
-   *
-   * @param {String} editLevel
-   *        The previous layout
-   */
-  makeUnfocasable: function (editLevel) {
-    let editBoxes = this.getEditBoxes(editLevel);
-    editBoxes.forEach(editBox => editBox.setAttribute("tabindex", "-1"));
-  },
-
-  /**
-   * Make current layout's elements focusable.
-   *
-   * @param {String} editLevel
-   *        The current layout
-   */
-  makeFocusable: function (editLevel) {
-    let editBoxes = this.getEditBoxes(editLevel);
-    editBoxes.forEach(editBox => editBox.setAttribute("tabindex", "0"));
-    editBoxes[0].focus();
-  },
-
-  /**
-   * Keyboard navigation of edit boxes wraps around on edge
-   * elements ([layout]-top, [layout]-left).
-   *
-   * @param {Node} target
-   *        Node to be observed
-   * @param {Boolean} shiftKey
-   *        Determines if shiftKey was pressed
-   * @param {String} level
-   *        Current active layout
-   */
-  moveFocus: function ({target, shiftKey}, level) {
-    let editBoxes = this.getEditBoxes(level);
-    let editingMode = target.tagName === "input";
-    // target.nextSibling is input field
-    let position = editingMode ? editBoxes.indexOf(target.nextSibling)
-                               : editBoxes.indexOf(target);
-
-    if (position === editBoxes.length - 1 && !shiftKey) {
-      position = 0;
-    } else if (position === 0 && shiftKey) {
-      position = editBoxes.length - 1;
-    } else {
-      shiftKey ? position-- : position++;
-    }
-
-    let editBox = editBoxes[position];
-    editBox.focus();
-
-    if (editingMode) {
-      editBox.click();
-    }
-  },
-
-  /**
-   * Retrieve edit boxes for current layout.
-   *
-   * @param {String} editLevel
-   *        Current active layout
-   * @return Layout's edit boxes
-   */
-  getEditBoxes: function (editLevel) {
-    let dataLevel = this.doc.getElementById(editLevel).getAttribute("data-box");
-    return [...this.doc.querySelectorAll(
-      `[data-box="${dataLevel}"].old-boxmodel-editable`)];
-  },
-
-  onSidebarSelect: function (e, sidebar) {
-    this.setActive(sidebar === "computedview");
-  },
-
-  /**
-   * Selection 'new-node-front' event handler.
-   */
-  onNewSelection: function () {
-    let done = this.inspector.updating("computed-view");
-    this.onNewNode()
-      .then(() => this.hideGeometryEditor())
-      .then(done, (err) => {
-        console.error(err);
-        done();
-      }).catch(console.error);
-  },
-
-  /**
-   * @return a promise that resolves when the view has been updated
-   */
-  onNewNode: function () {
-    this.setActive(this.isViewVisibleAndNodeValid());
-    return this.update();
-  },
-
-  onHighlightMouseOver: function (e) {
-    let region = e.target.getAttribute("data-box");
-    if (!region) {
-      return;
-    }
-
-    this.showBoxModel({
-      region,
-      showOnly: region,
-      onlyRegionArea: true
-    });
-  },
-
-  onHighlightMouseOut: function () {
-    this.hideBoxModel();
-  },
-
-  onGeometryButtonClick: function ({target}) {
-    if (target.hasAttribute("checked")) {
-      target.removeAttribute("checked");
-      this.hideGeometryEditor();
-    } else {
-      target.setAttribute("checked", "true");
-      this.showGeometryEditor();
-    }
-  },
-
-  onPickerStarted: function () {
-    this.hideGeometryEditor();
-  },
-
-  onToggleExpander: function () {
-    let isOpen = this.expander.hasAttribute("open");
-
-    if (isOpen) {
-      this.container.hidden = true;
-      this.expander.removeAttribute("open");
-    } else {
-      this.container.hidden = false;
-      this.expander.setAttribute("open", "");
-    }
-  },
-
-  onMarkupViewLeave: function () {
-    this.showGeometryEditor(true);
-  },
-
-  onMarkupViewNodeHover: function () {
-    this.hideGeometryEditor(false);
-  },
-
-  onWillNavigate: function () {
-    this._geometryEditorHighlighter.release().catch(console.error);
-    this._geometryEditorHighlighter = null;
-  },
-
-  /**
-   * Event handler that responds to the computed view being filtered
-   * @param {String} reason
-   * @param {Boolean} hidden
-   *        Whether or not to hide the box model wrapper
-   */
-  onFilterComputedView: function (reason, hidden) {
-    this.wrapper.hidden = hidden;
-  },
-
-  /**
-   * Stop tracking reflows and hide all values when no node is selected or the
-   * box model view is hidden, otherwise track reflows and show values.
-   * @param {Boolean} isActive
-   */
-  setActive: function (isActive) {
-    if (isActive === this.isActive) {
-      return;
-    }
-    this.isActive = isActive;
-
-    if (isActive) {
-      this.trackReflows();
-    } else {
-      this.untrackReflows();
-    }
-  },
-
-  /**
-   * Compute the dimensions of the node and update the values in
-   * the inspector.xul document.
-   * @return a promise that will be resolved when complete.
-   */
-  update: function () {
-    let lastRequest = Task.spawn((function* () {
-      if (!this.isViewVisibleAndNodeValid()) {
-        this.wrapper.hidden = true;
-        this.inspector.emit("boxmodel-view-updated");
-        return null;
-      }
-
-      let node = this.inspector.selection.nodeFront;
-      let layout = yield this.inspector.pageStyle.getLayout(node, {
-        autoMargins: this.isActive
-      });
-      let styleEntries = yield this.inspector.pageStyle.getApplied(node, {});
-
-      yield this.updateGeometryButton();
-
-      // If a subsequent request has been made, wait for that one instead.
-      if (this._lastRequest != lastRequest) {
-        return this._lastRequest;
-      }
-
-      this._lastRequest = null;
-      let width = layout.width;
-      let height = layout.height;
-      let newLabel = SHARED_L10N.getFormatStr("dimensions", width, height);
-
-      if (this.sizeHeadingLabel.textContent != newLabel) {
-        this.sizeHeadingLabel.textContent = newLabel;
-      }
-
-      for (let i in this.map) {
-        let property = this.map[i].property;
-        if (!(property in layout)) {
-          // Depending on the actor version, some properties
-          // might be missing.
-          continue;
-        }
-        let parsedValue = parseFloat(layout[property]);
-        if (Number.isNaN(parsedValue)) {
-          // Not a number. We use the raw string.
-          // Useful for "position" for example.
-          this.map[i].value = layout[property];
-        } else {
-          this.map[i].value = parsedValue;
-        }
-      }
-
-      let margins = layout.autoMargins;
-      if ("top" in margins) {
-        this.map.marginTop.value = "auto";
-      }
-      if ("right" in margins) {
-        this.map.marginRight.value = "auto";
-      }
-      if ("bottom" in margins) {
-        this.map.marginBottom.value = "auto";
-      }
-      if ("left" in margins) {
-        this.map.marginLeft.value = "auto";
-      }
-
-      for (let i in this.map) {
-        let selector = this.map[i].selector;
-        let span = this.doc.querySelector(selector);
-        this.updateSourceRuleTooltip(span, this.map[i].property, styleEntries);
-        if (span.textContent.length > 0 &&
-            span.textContent == this.map[i].value) {
-          continue;
-        }
-        span.textContent = this.map[i].value;
-        this.manageOverflowingText(span);
-      }
-
-      width -= this.map.borderLeft.value + this.map.borderRight.value +
-               this.map.paddingLeft.value + this.map.paddingRight.value;
-      width = parseFloat(width.toPrecision(6));
-      height -= this.map.borderTop.value + this.map.borderBottom.value +
-                this.map.paddingTop.value + this.map.paddingBottom.value;
-      height = parseFloat(height.toPrecision(6));
-
-      let newValue = width + "\u00D7" + height;
-      if (this.sizeLabel.textContent != newValue) {
-        this.sizeLabel.textContent = newValue;
-      }
-
-      this.elementRules = styleEntries.map(e => e.rule);
-
-      this.wrapper.hidden = false;
-
-      this.inspector.emit("boxmodel-view-updated");
-      return null;
-    }).bind(this)).catch(console.error);
-
-    this._lastRequest = lastRequest;
-    return this._lastRequest;
-  },
-
-  /**
-   * Update the text in the tooltip shown when hovering over a value to provide
-   * information about the source CSS rule that sets this value.
-   * @param {DOMNode} el The element that will receive the tooltip.
-   * @param {String} property The name of the CSS property for the tooltip.
-   * @param {Array} rules An array of applied rules retrieved by
-   * styleActor.getApplied.
-   */
-  updateSourceRuleTooltip: function (el, property, rules) {
-    // Dummy element used to parse the cssText of applied rules.
-    let dummyEl = this.doc.createElement("div");
-
-    // Rules are in order of priority so iterate until we find the first that
-    // defines a value for the property.
-    let sourceRule, value;
-    for (let {rule} of rules) {
-      dummyEl.style.cssText = rule.cssText;
-      value = dummyEl.style.getPropertyValue(property);
-      if (value !== "") {
-        sourceRule = rule;
-        break;
-      }
-    }
-
-    let title = property;
-    if (sourceRule && sourceRule.selectors) {
-      title += "\n" + sourceRule.selectors.join(", ");
-    }
-    if (sourceRule && sourceRule.parentStyleSheet) {
-      if (sourceRule.parentStyleSheet.href) {
-        title += "\n" + sourceRule.parentStyleSheet.href + ":" + sourceRule.line;
-      } else {
-        title += "\n" + INSPECTOR_L10N.getStr("rule.sourceInline") +
-          ":" + sourceRule.line;
-      }
-    }
-
-    el.setAttribute("title", title);
-  },
-
-  /**
-   * Show the box-model highlighter on the currently selected element
-   * @param {Object} options Options passed to the highlighter actor
-   */
-  showBoxModel: function (options = {}) {
-    let toolbox = this.inspector.toolbox;
-    let nodeFront = this.inspector.selection.nodeFront;
-
-    toolbox.highlighterUtils.highlightNodeFront(nodeFront, options);
-  },
-
-  /**
-   * Hide the box-model highlighter on the currently selected element
-   */
-  hideBoxModel: function () {
-    let toolbox = this.inspector.toolbox;
-
-    toolbox.highlighterUtils.unhighlight();
-  },
-
-  /**
-   * Show the geometry editor highlighter on the currently selected element
-   * @param {Boolean} [showOnlyIfActive=false]
-   *   Indicates if the Geometry Editor should be shown only if it's active but
-   *   hidden.
-   */
-  showGeometryEditor: function (showOnlyIfActive = false) {
-    let toolbox = this.inspector.toolbox;
-    let nodeFront = this.inspector.selection.nodeFront;
-    let nodeGeometry = this.doc.getElementById("old-layout-geometry-editor");
-    let isActive = nodeGeometry.hasAttribute("checked");
-
-    if (showOnlyIfActive && !isActive) {
-      return;
-    }
-
-    if (this._geometryEditorHighlighter) {
-      this._geometryEditorHighlighter.show(nodeFront).catch(console.error);
-      return;
-    }
-
-    // instantiate Geometry Editor highlighter
-    toolbox.highlighterUtils
-      .getHighlighterByType("GeometryEditorHighlighter").then(highlighter => {
-        highlighter.show(nodeFront).catch(console.error);
-        this._geometryEditorHighlighter = highlighter;
-
-        // Hide completely the geometry editor if the picker is clicked
-        toolbox.on("picker-started", this.onPickerStarted);
-
-        // Temporary hide the geometry editor
-        this.inspector.markup.on("leave", this.onMarkupViewLeave);
-        this.inspector.markup.on("node-hover", this.onMarkupViewNodeHover);
-
-        // Release the actor on will-navigate event
-        this.inspector.target.once("will-navigate", this.onWillNavigate);
-      });
-  },
-
-  /**
-   * Hide the geometry editor highlighter on the currently selected element
-   * @param {Boolean} [updateButton=true]
-   *   Indicates if the Geometry Editor's button needs to be unchecked too
-   */
-  hideGeometryEditor: function (updateButton = true) {
-    if (this._geometryEditorHighlighter) {
-      this._geometryEditorHighlighter.hide().catch(console.error);
-    }
-
-    if (updateButton) {
-      let nodeGeometry = this.doc.getElementById("old-layout-geometry-editor");
-      nodeGeometry.removeAttribute("checked");
-    }
-  },
-
-  /**
-   * Update the visibility and the state of the geometry editor button,
-   * based on the selected node.
-   */
-  updateGeometryButton: Task.async(function* () {
-    let node = this.inspector.selection.nodeFront;
-    let isEditable = false;
-
-    if (node) {
-      isEditable = yield this.inspector.pageStyle.isPositionEditable(node);
-    }
-
-    let nodeGeometry = this.doc.getElementById("old-layout-geometry-editor");
-    nodeGeometry.style.visibility = isEditable ? "visible" : "hidden";
-  }),
-
-  manageOverflowingText: function (span) {
-    let classList = span.parentNode.classList;
-
-    if (classList.contains("old-boxmodel-left") ||
-        classList.contains("old-boxmodel-right")) {
-      let force = span.textContent.length > LONG_TEXT_ROTATE_LIMIT;
-      classList.toggle("old-boxmodel-rotate", force);
-    }
-  }
-};
-
-module.exports = BoxModelView;
--- a/devtools/client/inspector/components/moz.build
+++ b/devtools/client/inspector/components/moz.build
@@ -1,13 +1,10 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
-    'deprecated-box-model.js',
     'inspector-tab-panel.css',
     'inspector-tab-panel.js',
 )
-
-BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
--- a/devtools/client/inspector/computed/computed.js
+++ b/devtools/client/inspector/computed/computed.js
@@ -21,19 +21,24 @@ const {
   VIEW_NODE_SELECTOR_TYPE,
   VIEW_NODE_PROPERTY_TYPE,
   VIEW_NODE_VALUE_TYPE,
   VIEW_NODE_IMAGE_URL_TYPE,
 } = require("devtools/client/inspector/shared/node-types");
 const StyleInspectorMenu = require("devtools/client/inspector/shared/style-inspector-menu");
 const TooltipsOverlay = require("devtools/client/inspector/shared/tooltips-overlay");
 const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
-const BoxModelView = require("devtools/client/inspector/components/deprecated-box-model");
 const clipboardHelper = require("devtools/shared/platform/clipboard");
 
+const { createElement, createFactory } = require("devtools/client/shared/vendor/react");
+const ReactDOM = require("devtools/client/shared/vendor/react-dom");
+const { Provider } = require("devtools/client/shared/vendor/react-redux");
+
+const BoxModelApp = createFactory(require("devtools/client/inspector/boxmodel/components/BoxModelApp"));
+
 const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties";
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);
 
 const PREF_ORIG_SOURCES = "devtools.styleeditor.source-maps-enabled";
 
 const FILTER_CHANGED_TIMEOUT = 150;
 const HTML_NS = "http://www.w3.org/1999/xhtml";
@@ -149,16 +154,17 @@ UpdateProcess.prototype = {
  *        The document that will contain the computed view.
  * @param {PageStyleFront} pageStyle
  *        Front for the page style actor that will be providing
  *        the style information.
  */
 function CssComputedView(inspector, document, pageStyle) {
   this.inspector = inspector;
   this.highlighters = inspector.highlighters;
+  this.store = inspector.store;
   this.styleDocument = document;
   this.styleWindow = this.styleDocument.defaultView;
   this.pageStyle = pageStyle;
 
   this.propertyViews = [];
 
   let cssProperties = getCssProperties(inspector.toolbox);
   this._outputParser = new OutputParser(document, cssProperties);
@@ -169,16 +175,17 @@ function CssComputedView(inspector, docu
   this._onClick = this._onClick.bind(this);
   this._onCopy = this._onCopy.bind(this);
   this._onFilterStyles = this._onFilterStyles.bind(this);
   this._onClearSearch = this._onClearSearch.bind(this);
   this._onIncludeBrowserStyles = this._onIncludeBrowserStyles.bind(this);
 
   let doc = this.styleDocument;
   this.element = doc.getElementById("propertyContainer");
+  this.boxModelWrapper = doc.getElementById("boxmodel-wrapper");
   this.searchField = doc.getElementById("computedview-searchbox");
   this.searchClearButton = doc.getElementById("computedview-searchinput-clear");
   this.includeBrowserStylesCheckbox =
     doc.getElementById("browser-style-checkbox");
 
   this.shortcuts = new KeyShortcuts({ window: this.styleWindow });
   this._onShortcut = this._onShortcut.bind(this);
   this.shortcuts.on("CmdOrCtrl+F", this._onShortcut);
@@ -204,16 +211,17 @@ function CssComputedView(inspector, docu
   this._onSourcePrefChanged = this._onSourcePrefChanged.bind(this);
   this._prefObserver = new PrefObserver("devtools.");
   this._prefObserver.on(PREF_ORIG_SOURCES, this._onSourcePrefChanged);
   this._prefObserver.on("devtools.defaultColorUnit", this._handlePrefChange);
 
   // The element that we're inspecting, and the document that it comes from.
   this._viewedElement = null;
 
+  this.createBoxModelView();
   this.createStyleViews();
 
   this._contextmenu = new StyleInspectorMenu(this, { isRuleView: false });
 
   // Add the tooltips and highlightersoverlay
   this.tooltips = new TooltipsOverlay(this);
   this.tooltips.addToView();
 
@@ -547,20 +555,20 @@ CssComputedView.prototype = {
 
     let filterTimeout = (this.searchField.value.length > 0)
       ? FILTER_CHANGED_TIMEOUT : 0;
     this.searchClearButton.hidden = this.searchField.value.length === 0;
 
     this._filterChangedTimeout = setTimeout(() => {
       if (this.searchField.value.length > 0) {
         this.searchField.setAttribute("filled", true);
-        this.inspector.emit("computed-view-filtered", true);
+        this.boxModelWrapper.hidden = true;
       } else {
         this.searchField.removeAttribute("filled");
-        this.inspector.emit("computed-view-filtered", false);
+        this.boxModelWrapper.hidden = false;
       }
 
       this.refreshPanel();
       this._filterChangeTimeout = null;
     }, filterTimeout);
   },
 
   /**
@@ -601,16 +609,39 @@ CssComputedView.prototype = {
     this._handlePrefChange();
     for (let propView of this.propertyViews) {
       propView.updateSourceLinks();
     }
     this.inspector.emit("computed-view-sourcelinks-updated");
   },
 
   /**
+   * Render the box model view.
+   */
+  createBoxModelView: function () {
+    let {
+      onHideBoxModelHighlighter,
+      onShowBoxModelEditor,
+      onShowBoxModelHighlighter,
+    } = this.inspector.boxmodel.getComponentProps();
+
+    let provider = createElement(
+      Provider,
+      { store: this.store },
+      BoxModelApp({
+        showBoxModelProperties: false,
+        onHideBoxModelHighlighter,
+        onShowBoxModelEditor,
+        onShowBoxModelHighlighter,
+      })
+    );
+    ReactDOM.render(provider, this.boxModelWrapper);
+  },
+
+  /**
    * The CSS as displayed by the UI.
    */
   createStyleViews: function () {
     if (CssComputedView.propertyNames) {
       return;
     }
 
     CssComputedView.propertyNames = [];
@@ -778,29 +809,30 @@ CssComputedView.prototype = {
     this.searchField.removeEventListener("contextmenu",
       this.inspector.onTextBoxContextMenu);
     this.searchClearButton.removeEventListener("click", this._onClearSearch);
     this.includeBrowserStylesCheckbox.removeEventListener("input",
       this._onIncludeBrowserStyles);
 
     // Nodes used in templating
     this.element = null;
-    this.panel = null;
+    this.boxModelWrapper = null;
     this.searchField = null;
     this.searchClearButton = null;
     this.includeBrowserStylesCheckbox = null;
 
     // Property views
     for (let propView of this.propertyViews) {
       propView.destroy();
     }
     this.propertyViews = null;
 
     this.inspector = null;
     this.highlighters = null;
+    this.store = null;
     this.styleDocument = null;
     this.styleWindow = null;
 
     this._isDestroyed = true;
   }
 };
 
 function PropertyInfo(tree, name) {
@@ -1411,17 +1443,16 @@ SelectorView.prototype = {
 };
 
 function ComputedViewTool(inspector, window) {
   this.inspector = inspector;
   this.document = window.document;
 
   this.computedView = new CssComputedView(this.inspector, this.document,
     this.inspector.pageStyle);
-  this.boxModelView = new BoxModelView(this.inspector, this.document);
 
   this.onSelected = this.onSelected.bind(this);
   this.refresh = this.refresh.bind(this);
   this.onPanelSelected = this.onPanelSelected.bind(this);
   this.onMutations = this.onMutations.bind(this);
   this.onResized = this.onResized.bind(this);
 
   this.inspector.selection.on("detached-front", this.onSelected);
@@ -1520,17 +1551,16 @@ ComputedViewTool.prototype = {
     this.inspector.selection.off("new-node-front", this.onSelected);
     this.inspector.selection.off("detached-front", this.onSelected);
     this.inspector.sidebar.off("computedview-selected", this.onPanelSelected);
     if (this.inspector.pageStyle) {
       this.inspector.pageStyle.off("stylesheet-updated", this.refresh);
     }
 
     this.computedView.destroy();
-    this.boxModelView.destroy();
 
-    this.computedView = this.boxModelView = this.document = this.inspector = null;
+    this.computedView = this.document = this.inspector = null;
   }
 };
 
 exports.CssComputedView = CssComputedView;
 exports.ComputedViewTool = ComputedViewTool;
 exports.PropertyView = PropertyView;
--- a/devtools/client/inspector/computed/test/browser_computed_search-filter.js
+++ b/devtools/client/inspector/computed/test/browser_computed_search-filter.js
@@ -29,17 +29,17 @@ function* testToggleDefaultStyles(inspec
   let onRefreshed = inspector.once("computed-view-refreshed");
   checkbox.click();
   yield onRefreshed;
 }
 
 function* testAddTextInFilter(inspector, computedView) {
   info("setting filter text to \"color\"");
   let doc = computedView.styleDocument;
-  let boxModelWrapper = doc.querySelector("#old-boxmodel-wrapper");
+  let boxModelWrapper = doc.getElementById("boxmodel-wrapper");
   let searchField = computedView.searchField;
   let onRefreshed = inspector.once("computed-view-refreshed");
   let win = computedView.styleWindow;
 
   // First check to make sure that accel + F doesn't focus search if the
   // container isn't focused
   inspector.panelWin.focus();
   EventUtils.synthesizeKey("f", { accelKey: true });
--- a/devtools/client/inspector/computed/test/browser_computed_search-filter_clear.js
+++ b/devtools/client/inspector/computed/test/browser_computed_search-filter_clear.js
@@ -45,17 +45,17 @@ function* testAddTextInFilter(inspector,
   });
 }
 
 function* testClearSearchFilter(inspector, computedView) {
   info("Clearing the search filter");
 
   let win = computedView.styleWindow;
   let doc = computedView.styleDocument;
-  let boxModelWrapper = doc.querySelector("#old-boxmodel-wrapper");
+  let boxModelWrapper = doc.getElementById("boxmodel-wrapper");
   let propertyViews = computedView.propertyViews;
   let searchField = computedView.searchField;
   let searchClearButton = computedView.searchClearButton;
   let onRefreshed = inspector.once("computed-view-refreshed");
 
   EventUtils.synthesizeMouseAtCenter(searchClearButton, {}, win);
   yield onRefreshed;
 
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -18,17 +18,17 @@ var {Task} = require("devtools/shared/ta
 const {initCssProperties} = require("devtools/shared/fronts/css-properties");
 const nodeConstants = require("devtools/shared/dom-node-constants");
 const Telemetry = require("devtools/client/shared/telemetry");
 
 const Menu = require("devtools/client/framework/menu");
 const MenuItem = require("devtools/client/framework/menu-item");
 
 const {HTMLBreadcrumbs} = require("devtools/client/inspector/breadcrumbs");
-const {ComputedViewTool} = require("devtools/client/inspector/computed/computed");
+const BoxModel = require("devtools/client/inspector/boxmodel/box-model");
 const {FontInspector} = require("devtools/client/inspector/fonts/fonts");
 const {InspectorSearch} = require("devtools/client/inspector/inspector-search");
 const {RuleViewTool} = require("devtools/client/inspector/rules/rules");
 const HighlightersOverlay = require("devtools/client/inspector/shared/highlighters-overlay");
 const {ToolSidebar} = require("devtools/client/inspector/toolsidebar");
 const MarkupView = require("devtools/client/inspector/markup/markup");
 const {CommandUtils} = require("devtools/client/shared/developer-toolbar");
 const {ViewHelpers} = require("devtools/client/shared/widgets/view-helpers");
@@ -73,18 +73,16 @@ const PORTRAIT_MODE_WIDTH = 700;
  *      Fired when the computed rules view updates to a new node
  * - computed-view-property-expanded
  *      Fired when a property is expanded in the computed rules view
  * - computed-view-property-collapsed
  *      Fired when a property is collapsed in the computed rules view
  * - computed-view-sourcelinks-updated
  *      Fired when the stylesheet source links have been updated (when switching
  *      to source-mapped files)
- * - computed-view-filtered
- *      Fired when the computed rules view is filtered
  * - rule-view-refreshed
  *      Fired when the rule view updates to a new node
  * - rule-view-sourcelinks-updated
  *      Fired when the stylesheet source links have been updated (when switching
  *      to source-mapped files)
  */
 function Inspector(toolbox) {
   this._toolbox = toolbox;
@@ -567,16 +565,20 @@ Inspector.prototype = {
       defaultTab == "ruleview");
 
     this.sidebar.addExistingTab(
       "computedview",
       INSPECTOR_L10N.getStr("inspector.sidebar.computedViewTitle"),
       defaultTab == "computedview");
 
     this.ruleview = new RuleViewTool(this, this.panelWin);
+    this.boxmodel = new BoxModel(this, this.panelWin);
+
+    const {ComputedViewTool} =
+      this.browserRequire("devtools/client/inspector/computed/computed");
     this.computedview = new ComputedViewTool(this, this.panelWin);
 
     if (Services.prefs.getBoolPref("devtools.layoutview.enabled")) {
       const LayoutView = this.browserRequire("devtools/client/inspector/layout/layout");
       this.layoutview = new LayoutView(this, this.panelWin);
     }
 
     if (this.target.form.animationsActor) {
--- a/devtools/client/inspector/inspector.xhtml
+++ b/devtools/client/inspector/inspector.xhtml
@@ -9,17 +9,16 @@
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
 
   <link rel="stylesheet" href="chrome://devtools/skin/widgets.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/inspector.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/rules.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/computed.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/fonts.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/boxmodel.css"/>
-  <link rel="stylesheet" href="chrome://devtools/skin/deprecated-boxmodel.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/layout.css"/>
   <link rel="stylesheet" href="chrome://devtools/skin/animationinspector.css"/>
   <link rel="stylesheet" href="resource://devtools/client/shared/components/sidebar-toggle.css"/>
   <link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/tabs.css"/>
   <link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/tabbar.css"/>
   <link rel="stylesheet" href="resource://devtools/client/inspector/components/inspector-tab-panel.css"/>
   <link rel="stylesheet" href="resource://devtools/client/shared/components/splitter/split-box.css"/>
   <link rel="stylesheet" href="resource://devtools/client/inspector/layout/components/Accordion.css"/>
@@ -127,70 +126,17 @@
                       type="checkbox"
                       class="includebrowserstyles"/>
           <label id="browser-style-checkbox-label" for="browser-style-checkbox"
                         data-localization="content=inspector.browserStyles.label"></label>
         </div>
 
         <div id="computedview-container">
           <div id="computedview-container-focusable" tabindex="-1">
-            <div id="old-boxmodel-wrapper" tabindex="0"
-              data-localization-bundle="devtools/client/locales/boxmodel.properties">
-              <div id="old-boxmodel-header">
-                <div id="old-boxmodel-expander" class="expander theme-twisty expandable" open=""></div>
-                <span data-localization="content=boxmodel.title"></span>
-              </div>
-
-              <div id="old-boxmodel-container">
-                <div id="old-boxmodel-main">
-                  <span class="old-boxmodel-legend" data-box="margin" data-localization="content=boxmodel.margin;title=boxmodel.margin"></span>
-                  <div id="old-boxmodel-margins" data-box="margin" data-localization="title=boxmodel.margin">
-                    <span class="old-boxmodel-legend" data-box="border" data-localization="content=boxmodel.border;title=boxmodel.border"></span>
-                    <div id="old-boxmodel-borders" data-box="border" data-localization="title=boxmodel.border">
-                      <span class="old-boxmodel-legend" data-box="padding" data-localization="content=boxmodel.padding;title=boxmodel.padding"></span>
-                      <div id="old-boxmodel-padding" data-box="padding" data-localization="title=boxmodel.padding">
-                        <div id="old-boxmodel-content" data-box="content" data-localization="title=boxmodel.content">
-                        </div>
-                      </div>
-                    </div>
-                  </div>
-
-                  <p class="old-boxmodel-margin old-boxmodel-top"><span data-box="margin" class="old-boxmodel-editable" title="margin-top"></span></p>
-                  <p class="old-boxmodel-margin old-boxmodel-right"><span data-box="margin" class="old-boxmodel-editable" title="margin-right"></span></p>
-                  <p class="old-boxmodel-margin old-boxmodel-bottom"><span data-box="margin" class="old-boxmodel-editable" title="margin-bottom"></span></p>
-                  <p class="old-boxmodel-margin old-boxmodel-left"><span data-box="margin" class="old-boxmodel-editable" title="margin-left"></span></p>
-
-                  <p class="old-boxmodel-border old-boxmodel-top"><span data-box="border" class="old-boxmodel-editable" title="border-top"></span></p>
-                  <p class="old-boxmodel-border old-boxmodel-right"><span data-box="border" class="old-boxmodel-editable" title="border-right"></span></p>
-                  <p class="old-boxmodel-border old-boxmodel-bottom"><span data-box="border" class="old-boxmodel-editable" title="border-bottom"></span></p>
-                  <p class="old-boxmodel-border old-boxmodel-left"><span data-box="border" class="old-boxmodel-editable" title="border-left"></span></p>
-
-                  <p class="old-boxmodel-padding old-boxmodel-top"><span data-box="padding" class="old-boxmodel-editable" title="padding-top"></span></p>
-                  <p class="old-boxmodel-padding old-boxmodel-right"><span data-box="padding" class="old-boxmodel-editable" title="padding-right"></span></p>
-                  <p class="old-boxmodel-padding old-boxmodel-bottom"><span data-box="padding" class="old-boxmodel-editable" title="padding-bottom"></span></p>
-                  <p class="old-boxmodel-padding old-boxmodel-left"><span data-box="padding" class="old-boxmodel-editable" title="padding-left"></span></p>
-
-                  <p class="old-boxmodel-size">
-                    <span data-box="content" data-localization="title=boxmodel.content"></span>
-                  </p>
-                </div>
-
-                <div id="old-boxmodel-info">
-                  <span id="old-boxmodel-element-size"></span>
-                  <section id="old-boxmodel-position-group">
-                    <button class="devtools-button" id="old-layout-geometry-editor"
-                            data-localization="title=boxmodel.geometryButton.tooltip"></button>
-                    <span id="old-boxmodel-element-position"></span>
-                  </section>
-                </div>
-
-                <div style="display: none">
-                  <p id="old-boxmodel-dummy"></p>
-                </div>
-              </div>
+            <div id="boxmodel-wrapper" tabindex="0">
             </div>
 
             <div id="propertyContainer" class="theme-separator" tabindex="0" dir="ltr">
             </div>
 
             <div id="computedview-no-results" hidden="" data-localization="content=inspector.noProperties"></div>
           </div>
         </div>
--- a/devtools/client/inspector/layout/actions/index.js
+++ b/devtools/client/inspector/layout/actions/index.js
@@ -12,18 +12,15 @@ createEnum([
   "UPDATE_GRID_COLOR",
 
   // Update the grid highlighted state.
   "UPDATE_GRID_HIGHLIGHTED",
 
   // Update the entire grids state with the new list of grids.
   "UPDATE_GRIDS",
 
-  // Update the layout state with the latest layout properties.
-  "UPDATE_LAYOUT",
-
   // Update the grid highlighter's show grid line numbers state.
   "UPDATE_SHOW_GRID_LINE_NUMBERS",
 
   // Update the grid highlighter's show infinite lines state.
   "UPDATE_SHOW_INFINITE_LINES",
 
 ], module.exports);
--- a/devtools/client/inspector/layout/actions/moz.build
+++ b/devtools/client/inspector/layout/actions/moz.build
@@ -1,12 +1,11 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
-    'box-model.js',
     'grids.js',
     'highlighter-settings.js',
     'index.js',
 )
--- a/devtools/client/inspector/layout/components/App.js
+++ b/devtools/client/inspector/layout/components/App.js
@@ -6,19 +6,20 @@
 
 const { addons, createClass, createFactory, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 
 const Accordion = createFactory(require("./Accordion"));
-const BoxModel = createFactory(require("./BoxModel"));
 const Grid = createFactory(require("./Grid"));
 
+const BoxModel = createFactory(require("devtools/client/inspector/boxmodel/components/BoxModel"));
+
 const Types = require("../types");
 const { getStr } = require("../utils/l10n");
 
 const BOXMODEL_STRINGS_URI = "devtools/client/locales/boxmodel.properties";
 const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI);
 
 const App = createClass({
 
--- a/devtools/client/inspector/layout/components/moz.build
+++ b/devtools/client/inspector/layout/components/moz.build
@@ -3,19 +3,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/.
 
 DevToolsModules(
     'Accordion.css',
     'Accordion.js',
     'App.js',
-    'BoxModel.js',
-    'BoxModelEditable.js',
-    'BoxModelInfo.js',
-    'BoxModelMain.js',
-    'BoxModelProperties.js',
-    'ComputedProperty.js',
     'Grid.js',
     'GridDisplaySettings.js',
     'GridItem.js',
     'GridList.js',
 )
--- a/devtools/client/inspector/layout/layout.js
+++ b/devtools/client/inspector/layout/layout.js
@@ -1,47 +1,38 @@
 /* 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 { Task } = require("devtools/shared/task");
-const { getCssProperties } = require("devtools/shared/fronts/css-properties");
-const { ReflowFront } = require("devtools/shared/fronts/reflow");
 
-const { InplaceEditor } = require("devtools/client/shared/inplace-editor");
 const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
 const { Provider } = require("devtools/client/shared/vendor/react-redux");
 
 const SwatchColorPickerTooltip = require("devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip");
 
 const {
-  updateLayout,
-} = require("./actions/box-model");
-const {
   updateGridColor,
   updateGridHighlighted,
   updateGrids,
 } = require("./actions/grids");
 const {
   updateShowGridLineNumbers,
   updateShowInfiniteLines,
 } = require("./actions/highlighter-settings");
 
 const App = createFactory(require("./components/App"));
 
-const EditingSession = require("./utils/editing-session");
-
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const INSPECTOR_L10N =
   new LocalizationHelper("devtools/client/locales/inspector.properties");
 
-const NUMERIC = /^-?[\d\.]+$/;
 const SHOW_GRID_LINE_NUMBERS = "devtools.gridinspector.showGridLineNumbers";
 const SHOW_INFINITE_LINES_PREF = "devtools.gridinspector.showInfiniteLines";
 
 // Default grid colors.
 const GRID_COLORS = [
   "#05E4EE",
   "#BB9DFF",
   "#FFB53B",
@@ -54,44 +45,46 @@ const GRID_COLORS = [
 
 function LayoutView(inspector, window) {
   this.document = window.document;
   this.highlighters = inspector.highlighters;
   this.inspector = inspector;
   this.store = inspector.store;
   this.walker = this.inspector.walker;
 
-  this.updateBoxModel = this.updateBoxModel.bind(this);
-
   this.onGridLayoutChange = this.onGridLayoutChange.bind(this);
   this.onHighlighterChange = this.onHighlighterChange.bind(this);
-  this.onNewSelection = this.onNewSelection.bind(this);
   this.onSidebarSelect = this.onSidebarSelect.bind(this);
 
   this.init();
 }
 
 LayoutView.prototype = {
 
   /**
    * Initializes the layout view by fetching the LayoutFront from the walker, creating
    * the redux store and adding the view into the inspector sidebar.
    */
   init: Task.async(function* () {
     if (!this.inspector) {
       return;
     }
 
+    let {
+      onHideBoxModelHighlighter,
+      onShowBoxModelEditor,
+      onShowBoxModelHighlighter,
+    } = this.inspector.boxmodel.getComponentProps();
+
     this.layoutInspector = yield this.inspector.walker.getLayoutInspector();
 
     this.loadHighlighterSettings();
 
     this.highlighters.on("grid-highlighter-hidden", this.onHighlighterChange);
     this.highlighters.on("grid-highlighter-shown", this.onHighlighterChange);
-    this.inspector.selection.on("new-node-front", this.onNewSelection);
     this.inspector.sidebar.on("select", this.onSidebarSelect);
 
     // Create a shared SwatchColorPicker instance to be reused by all GridItem components.
     this.swatchColorPickerTooltip = new SwatchColorPickerTooltip(
       this.inspector.toolbox.doc,
       this.inspector,
       {
         supportsCssColor4ColorFunction: () => false
@@ -107,23 +100,19 @@ LayoutView.prototype = {
       },
 
       /**
        * Shows the box model properties under the box model if true, otherwise, hidden by
        * default.
        */
       showBoxModelProperties: true,
 
-      /**
-       * Hides the box-model highlighter on the currently selected element.
-       */
-      onHideBoxModelHighlighter: () => {
-        let toolbox = this.inspector.toolbox;
-        toolbox.highlighterUtils.unhighlight();
-      },
+      onHideBoxModelHighlighter,
+      onShowBoxModelEditor,
+      onShowBoxModelHighlighter,
 
       /**
        * Handler for a change in the grid overlay color picker for a grid container.
        *
        * @param  {NodeFront} node
        *         The NodeFront of the grid container element for which the grid color is
        *         being updated.
        * @param  {String} color
@@ -139,98 +128,16 @@ LayoutView.prototype = {
           if (grid.nodeFront === node && grid.highlighted) {
             let highlighterSettings = this.getGridHighlighterSettings(node);
             this.highlighters.showGridHighlighter(node, highlighterSettings);
           }
         }
       },
 
       /**
-       * Shows the inplace editor when a box model editable value is clicked on the
-       * box model panel.
-       *
-       * @param  {DOMNode} element
-       *         The element that was clicked.
-       * @param  {Event} event
-       *         The event object.
-       * @param  {String} property
-       *         The name of the property.
-       */
-      onShowBoxModelEditor: (element, event, property) => {
-        let session = new EditingSession({
-          inspector: this.inspector,
-          doc: this.document,
-          elementRules: this.elementRules,
-        });
-        let initialValue = session.getProperty(property);
-
-        let editor = new InplaceEditor({
-          element: element,
-          initial: initialValue,
-          contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE,
-          property: {
-            name: property
-          },
-          start: self => {
-            self.elt.parentNode.classList.add("boxmodel-editing");
-          },
-          change: value => {
-            if (NUMERIC.test(value)) {
-              value += "px";
-            }
-
-            let properties = [
-              { name: property, value: value }
-            ];
-
-            if (property.substring(0, 7) == "border-") {
-              let bprop = property.substring(0, property.length - 5) + "style";
-              let style = session.getProperty(bprop);
-              if (!style || style == "none" || style == "hidden") {
-                properties.push({ name: bprop, value: "solid" });
-              }
-            }
-
-            session.setProperties(properties).catch(e => console.error(e));
-          },
-          done: (value, commit) => {
-            editor.elt.parentNode.classList.remove("boxmodel-editing");
-            if (!commit) {
-              session.revert().then(() => {
-                session.destroy();
-              }, e => console.error(e));
-              return;
-            }
-
-            let node = this.inspector.selection.nodeFront;
-            this.inspector.pageStyle.getLayout(node, {
-              autoMargins: true,
-            }).then(layout => {
-              this.store.dispatch(updateLayout(layout));
-            }, e => console.error(e));
-          },
-          contextMenu: this.inspector.onTextBoxContextMenu,
-          cssProperties: getCssProperties(this.inspector.toolbox)
-        }, event);
-      },
-
-      /**
-       * Shows the box-model highlighter on the currently selected element.
-       *
-       * @param  {Object} options
-       *         Options passed to the highlighter actor.
-       */
-      onShowBoxModelHighlighter: (options = {}) => {
-        let toolbox = this.inspector.toolbox;
-        let nodeFront = this.inspector.selection.nodeFront;
-
-        toolbox.highlighterUtils.highlightNodeFront(nodeFront, options);
-      },
-
-      /**
        * Handler for a change in the input checkboxes in the GridList component.
        * Toggles on/off the grid highlighter for the provided grid container element.
        *
        * @param  {NodeFront} node
        *         The NodeFront of the grid container element for which the grid
        *         highlighter is toggled on/off for.
        */
       onToggleGridHighlighter: node => {
@@ -304,26 +211,19 @@ LayoutView.prototype = {
 
   /**
    * Destruction function called when the inspector is destroyed. Removes event listeners
    * and cleans up references.
    */
   destroy() {
     this.highlighters.off("grid-highlighter-hidden", this.onHighlighterChange);
     this.highlighters.off("grid-highlighter-shown", this.onHighlighterChange);
-    this.inspector.selection.off("new-node-front", this.onNewSelection);
     this.inspector.sidebar.off("select", this.onSidebarSelect);
     this.layoutInspector.off("grid-layout-changed", this.onGridLayoutChange);
 
-    if (this.reflowFront) {
-      this.untrackReflows();
-      this.reflowFront.destroy();
-      this.reflowFront = null;
-    }
-
     this.document = null;
     this.inspector = null;
     this.layoutInspector = null;
     this.store = null;
     this.walker = null;
   },
 
   /**
@@ -368,105 +268,29 @@ LayoutView.prototype = {
    */
   isPanelVisible() {
     return this.inspector.toolbox.currentToolId === "inspector" &&
            this.inspector.sidebar &&
            this.inspector.sidebar.getCurrentTabID() === "layoutview";
   },
 
   /**
-   * Returns true if the layout panel is visible and the current node is valid to
-   * be displayed in the view.
-   */
-  isPanelVisibleAndNodeValid() {
-    return this.isPanelVisible() &&
-           this.inspector.selection.isConnected() &&
-           this.inspector.selection.isElementNode();
-  },
-
-  /**
    * Load the grid highligher display settings into the store from the stored preferences.
    */
   loadHighlighterSettings() {
     let { dispatch } = this.store;
 
     let showGridLineNumbers = Services.prefs.getBoolPref(SHOW_GRID_LINE_NUMBERS);
     let showInfinteLines = Services.prefs.getBoolPref(SHOW_INFINITE_LINES_PREF);
 
     dispatch(updateShowGridLineNumbers(showGridLineNumbers));
     dispatch(updateShowInfiniteLines(showInfinteLines));
   },
 
   /**
-   * Starts listening to reflows in the current tab.
-   */
-  trackReflows() {
-    if (!this.reflowFront) {
-      let { target } = this.inspector;
-      if (target.form.reflowActor) {
-        this.reflowFront = ReflowFront(target.client,
-                                       target.form);
-      } else {
-        return;
-      }
-    }
-
-    this.reflowFront.on("reflows", this.updateBoxModel);
-    this.reflowFront.start();
-  },
-
-  /**
-   * Stops listening to reflows in the current tab.
-   */
-  untrackReflows() {
-    if (!this.reflowFront) {
-      return;
-    }
-
-    this.reflowFront.off("reflows", this.updateBoxModel);
-    this.reflowFront.stop();
-  },
-
-  /**
-   * Updates the box model panel by dispatching the new layout data.
-   */
-  updateBoxModel() {
-    let lastRequest = Task.spawn((function* () {
-      if (!(this.isPanelVisible() &&
-          this.inspector.selection.isConnected() &&
-          this.inspector.selection.isElementNode())) {
-        return null;
-      }
-
-      let node = this.inspector.selection.nodeFront;
-      let layout = yield this.inspector.pageStyle.getLayout(node, {
-        autoMargins: true,
-      });
-      let styleEntries = yield this.inspector.pageStyle.getApplied(node, {});
-      this.elementRules = styleEntries.map(e => e.rule);
-
-      // Update the redux store with the latest layout properties and update the box
-      // model view.
-      this.store.dispatch(updateLayout(layout));
-
-      // If a subsequent request has been made, wait for that one instead.
-      if (this._lastRequest != lastRequest) {
-        return this._lastRequest;
-      }
-
-      this._lastRequest = null;
-
-      this.inspector.emit("boxmodel-view-updated");
-      return null;
-    }).bind(this)).catch(console.error);
-
-    this._lastRequest = lastRequest;
-  },
-
-  /**
    * Updates the grid panel by dispatching the new grid data. This is called when the
    * layout view becomes visible or the view needs to be updated with new grid data.
    *
    * @param {Array|null} gridFronts
    *        Optional array of all GridFront in the current page.
    */
   updateGridPanel: Task.async(function* (gridFronts) {
     // Stop refreshing if the inspector or store is already destroyed.
@@ -522,44 +346,26 @@ LayoutView.prototype = {
    *         is shown for.
    */
   onHighlighterChange(event, nodeFront) {
     let highlighted = event === "grid-highlighter-shown";
     this.store.dispatch(updateGridHighlighted(nodeFront, highlighted));
   },
 
   /**
-   * Selection 'new-node-front' event handler.
-   */
-  onNewSelection: function () {
-    if (!this.isPanelVisibleAndNodeValid()) {
-      return;
-    }
-
-    this.updateBoxModel();
-  },
-
-  /**
    * Handler for the inspector sidebar select event. Starts listening for
    * "grid-layout-changed" if the layout panel is visible. Otherwise, stop
    * listening for grid layout changes. Finally, refresh the layout view if
    * it is visible.
    */
   onSidebarSelect() {
     if (!this.isPanelVisible()) {
       this.layoutInspector.off("grid-layout-changed", this.onGridLayoutChange);
-      this.untrackReflows();
       return;
     }
 
-    if (this.inspector.selection.isConnected() &&
-        this.inspector.selection.isElementNode()) {
-      this.trackReflows();
-    }
-
     this.layoutInspector.on("grid-layout-changed", this.onGridLayoutChange);
-    this.updateBoxModel();
     this.updateGridPanel();
   },
 
 };
 
 module.exports = LayoutView;
--- a/devtools/client/inspector/layout/reducers/moz.build
+++ b/devtools/client/inspector/layout/reducers/moz.build
@@ -1,11 +1,10 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
-    'box-model.js',
     'grids.js',
     'highlighter-settings.js',
 )
--- a/devtools/client/inspector/layout/types.js
+++ b/devtools/client/inspector/layout/types.js
@@ -2,26 +2,16 @@
  * 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 { PropTypes } = require("devtools/client/shared/vendor/react");
 
 /**
- * The box model data for the current selected node.
- */
-exports.boxModel = {
-
-  // The layout information of the current selected node
-  layout: PropTypes.object,
-
-};
-
-/**
  * A single grid container in the document.
  */
 exports.grid = {
 
   // The id of the grid
   id: PropTypes.number,
 
   // The color for the grid overlay highlighter
--- a/devtools/client/inspector/layout/utils/moz.build
+++ b/devtools/client/inspector/layout/utils/moz.build
@@ -1,10 +1,9 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
-    'editing-session.js',
     'l10n.js',
 )
--- a/devtools/client/inspector/moz.build
+++ b/devtools/client/inspector/moz.build
@@ -1,13 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += [
+    'boxmodel',
     'components',
     'computed',
     'fonts',
     'layout',
     'markup',
     'rules',
     'shared'
 ]
--- a/devtools/client/inspector/reducers.js
+++ b/devtools/client/inspector/reducers.js
@@ -2,11 +2,11 @@
  * 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";
 
 // This file exposes the Redux reducers of the box model, grid and grid highlighter
 // settings.
 
-exports.boxModel = require("devtools/client/inspector/layout/reducers/box-model");
+exports.boxModel = require("devtools/client/inspector/boxmodel/reducers/box-model");
 exports.grids = require("devtools/client/inspector/layout/reducers/grids");
 exports.highlighterSettings = require("devtools/client/inspector/layout/reducers/highlighter-settings");
--- a/devtools/client/inspector/test/browser_inspector_textbox-menu.js
+++ b/devtools/client/inspector/test/browser_inspector_textbox-menu.js
@@ -61,17 +61,17 @@ add_task(function* () {
 
   info("Switching to the computed-view");
   let onComputedViewReady = inspector.once("boxmodel-view-updated");
   selectComputedView(inspector);
   yield onComputedViewReady;
 
   info("Testing the box-model region");
   let margin = inspector.panelDoc.querySelector(
-    ".old-boxmodel-margin.old-boxmodel-top > span");
+    ".boxmodel-margin.boxmodel-top > span");
   EventUtils.synthesizeMouseAtCenter(margin, {}, inspector.panelWin);
   yield checkTextBox(inspector.panelDoc.activeElement, toolbox);
 });
 
 function* checkTextBox(textBox, {textBoxContextMenuPopup}) {
   is(textBoxContextMenuPopup.state, "closed", "The menu is closed");
 
   info("Simulating context click on the textbox and expecting the menu to open");
--- a/devtools/client/inspector/test/shared-head.js
+++ b/devtools/client/inspector/test/shared-head.js
@@ -47,17 +47,25 @@ var openInspector = Task.async(function*
  *        The ID of the sidebar tab to be opened
  * @return a promise that resolves when the inspector is ready and the tab is
  * visible and ready
  */
 var openInspectorSidebarTab = Task.async(function* (id) {
   let {toolbox, inspector, testActor} = yield openInspector();
 
   info("Selecting the " + id + " sidebar");
-  inspector.sidebar.select(id);
+
+  if (id === "computedview" || id === "layoutview") {
+    // The layout and computed views should wait until the box-model widget is ready.
+    let onBoxModelViewReady = inspector.once("boxmodel-view-updated");
+    inspector.sidebar.select(id);
+    yield onBoxModelViewReady;
+  } else {
+    inspector.sidebar.select(id);
+  }
 
   return {
     toolbox,
     inspector,
     testActor
   };
 });
 
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -172,17 +172,16 @@ devtools.jar:
     skin/images/item-toggle.svg (themes/images/item-toggle.svg)
     skin/images/item-arrow-dark-rtl.svg (themes/images/item-arrow-dark-rtl.svg)
     skin/images/item-arrow-dark-ltr.svg (themes/images/item-arrow-dark-ltr.svg)
     skin/images/item-arrow-rtl.svg (themes/images/item-arrow-rtl.svg)
     skin/images/item-arrow-ltr.svg (themes/images/item-arrow-ltr.svg)
     skin/images/noise.png (themes/images/noise.png)
     skin/images/dropmarker.svg (themes/images/dropmarker.svg)
     skin/boxmodel.css (themes/boxmodel.css)
-    skin/deprecated-boxmodel.css (themes/deprecated-boxmodel.css)
     skin/images/geometry-editor.svg (themes/images/geometry-editor.svg)
     skin/images/pause.svg (themes/images/pause.svg)
     skin/images/play.svg (themes/images/play.svg)
     skin/images/fast-forward.svg (themes/images/fast-forward.svg)
     skin/images/rewind.svg (themes/images/rewind.svg)
     skin/images/debugger-step-in.svg (themes/images/debugger-step-in.svg)
     skin/images/debugger-step-out.svg (themes/images/debugger-step-out.svg)
     skin/images/debugger-step-over.svg (themes/images/debugger-step-over.svg)
--- a/devtools/client/netmonitor/components/request-list-empty.js
+++ b/devtools/client/netmonitor/components/request-list-empty.js
@@ -26,17 +26,17 @@ const RequestListEmptyNotice = createCla
   propTypes: {
     onReloadClick: PropTypes.func.isRequired,
     onPerfClick: PropTypes.func.isRequired,
   },
 
   render() {
     return div(
       {
-        className: "requests-list-empty-notice",
+        className: "request-list-empty-notice",
       },
       div({ className: "notice-reload-message" },
         span(null, L10N.getStr("netmonitor.reloadNotice1")),
         button(
           {
             className: "devtools-toolbarbutton requests-list-reload-notice-button",
             "data-standalone": true,
             onClick: this.props.onReloadClick,
--- a/devtools/client/netmonitor/test/browser_net_simple-request.js
+++ b/devtools/client/netmonitor/test/browser_net_simple-request.js
@@ -24,53 +24,53 @@ add_task(function* () {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/selectors/index");
 
   gStore.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(".requests-list-empty-notice"),
+  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,
     "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(".requests-list-empty-notice"),
+  ok(!document.querySelector(".request-list-empty-notice"),
     "The empty notice should be hidden after the first request.");
   is(gStore.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(".requests-list-empty-notice"),
+  ok(!document.querySelector(".request-list-empty-notice"),
     "The empty notice should be still hidden after a reload.");
   is(gStore.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());
 
   is(document.querySelector(".network-details-panel-toggle").hasAttribute("disabled"),
     true,
     "The pane toggle button should be disabled when after clear.");
-  ok(document.querySelector(".requests-list-empty-notice"),
+  ok(document.querySelector(".request-list-empty-notice"),
     "An empty notice should be displayed again after clear.");
   is(gStore.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);
 
--- a/devtools/client/shared/browser-loader.js
+++ b/devtools/client/shared/browser-loader.js
@@ -7,16 +7,18 @@ var Cu = Components.utils;
 const loaders = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {});
 const { devtools } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const { joinURI } = devtools.require("devtools/shared/path");
 const { assert } = devtools.require("devtools/shared/DevToolsUtils");
 const Services = devtools.require("Services");
 const { AppConstants } = devtools.require("resource://gre/modules/AppConstants.jsm");
 
 const BROWSER_BASED_DIRS = [
+  "resource://devtools/client/inspector/boxmodel",
+  "resource://devtools/client/inspector/computed",
   "resource://devtools/client/inspector/layout",
   "resource://devtools/client/jsonview",
   "resource://devtools/client/shared/vendor",
   "resource://devtools/client/shared/redux",
 ];
 
 const COMMON_LIBRARY_DIRS = [
   "resource://devtools/client/shared/vendor",
deleted file mode 100644
--- a/devtools/client/shared/components/reps/load-reps.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/* global define */
-/* 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/. */
-
-// Disable eslint strict rule because the eslint env ("es6") is incompatible with eslint
-// validation in MC (https://github.com/devtools-html/devtools-reps/issues/58)
-/* eslint-disable strict */
-
-// Make this available to both AMD and CJS environments
-define(function (require, exports, module) {
-  let REPS;
-  let MODE;
-  let createFactories;
-  let parseURLEncodedText;
-  let parseURLParams;
-  let getSelectableInInspectorGrips;
-
-  // useRepsBundle hardcoded to true to use the bundle from github. Switch back to rely
-  // on individual reps files by flipping it to false.
-  let useRepsBundle = true;
-  if (useRepsBundle) {
-    const bundle = require("devtools/client/shared/components/reps/reps");
-    REPS = bundle.REPS;
-    MODE = bundle.MODE;
-    createFactories = bundle.createFactories;
-    parseURLEncodedText = bundle.parseURLEncodedText;
-    parseURLParams = bundle.parseURLParams;
-    getSelectableInInspectorGrips = bundle.getSelectableInInspectorGrips;
-  } else {
-    // Commenting out all requires, otherwise requirejs will agressively load all
-    // dependencies when loading load-reps.js, which will fail because files have been
-    // removed.
-    // ({ createFactories, parseURLEncodedText, parseURLParams } =
-    //   require("devtools/client/shared/components/reps/rep-utils"));
-    // REPS = require("devtools/client/shared/components/reps/rep").REPS;
-    // MODE = require("devtools/client/shared/components/reps/constants").MODE;
-  }
-
-  exports.REPS = REPS;
-  exports.MODE = MODE;
-  exports.createFactories = createFactories;
-  exports.parseURLEncodedText = parseURLEncodedText;
-  exports.parseURLParams = parseURLParams;
-  exports.getSelectableInInspectorGrips = getSelectableInInspectorGrips;
-});
--- a/devtools/client/shared/components/reps/moz.build
+++ b/devtools/client/shared/components/reps/moz.build
@@ -1,13 +1,12 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
-    'load-reps.js',
     'reps.css',
     'reps.js',
 )
 
 MOCHITEST_CHROME_MANIFESTS += ['test/mochitest/chrome.ini']
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -264,28 +264,110 @@ return /******/ (function(modules) { // 
 	function isGrip(object) {
 	  return object && object.actor;
 	}
 	
 	function escapeNewLines(value) {
 	  return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n");
 	}
 	
+	// Map from character code to the corresponding escape sequence.  \0
+	// isn't here because it would require special treatment in some
+	// situations.  \b, \f, and \v aren't here because they aren't very
+	// common.  \' isn't here because there's no need, we only
+	// double-quote strings.
+	const escapeMap = {
+	  // Tab.
+	  9: "\\t",
+	  // Newline.
+	  0xa: "\\n",
+	  // Carriage return.
+	  0xd: "\\r",
+	  // Quote.
+	  0x22: "\\\"",
+	  // Backslash.
+	  0x5c: "\\\\"
+	};
+	
+	// Regexp that matches any character we might possibly want to escape.
+	// Note that we over-match here, because it's difficult to, say, match
+	// an unpaired surrogate with a regexp.  The details are worked out by
+	// the replacement function; see |escapeString|.
+	const escapeRegexp = new RegExp("[" +
+	// Quote and backslash.
+	"\"\\\\" +
+	// Controls.
+	"\x00-\x1f" +
+	// More controls.
+	"\x7f-\x9f" +
+	// BOM
+	"\ufeff" +
+	// Replacement characters and non-characters.
+	"\ufffc-\uffff" +
+	// Surrogates.
+	"\ud800-\udfff" +
+	// Mathematical invisibles.
+	"\u2061-\u2064" +
+	// Line and paragraph separators.
+	"\u2028-\u2029" +
+	// Private use area.
+	"\ue000-\uf8ff" + "]", "g");
+	
+	/**
+	 * Escape a string so that the result is viewable and valid JS.
+	 * Control characters, other invisibles, invalid characters,
+	 * backslash, and double quotes are escaped.  The resulting string is
+	 * surrounded by double quotes.
+	 *
+	 * @param {String} str
+	 *        the input
+	 * @return {String} the escaped string
+	 */
+	function escapeString(str) {
+	  return "\"" + str.replace(escapeRegexp, (match, offset) => {
+	    let c = match.charCodeAt(0);
+	    if (c in escapeMap) {
+	      return escapeMap[c];
+	    }
+	    if (c >= 0xd800 && c <= 0xdfff) {
+	      // Find the full code point containing the surrogate, with a
+	      // special case for a trailing surrogate at the start of the
+	      // string.
+	      if (c >= 0xdc00 && offset > 0) {
+	        --offset;
+	      }
+	      let codePoint = str.codePointAt(offset);
+	      if (codePoint >= 0xd800 && codePoint <= 0xdfff) {
+	        // Unpaired surrogate.
+	        return "\\u" + codePoint.toString(16);
+	      } else if (codePoint >= 0xf0000 && codePoint <= 0x10fffd) {
+	        // Private use area.  Because we visit each pair of a such a
+	        // character, return the empty string for one half and the
+	        // real result for the other, to avoid duplication.
+	        if (c <= 0xdbff) {
+	          return "\\u{" + codePoint.toString(16) + "}";
+	        }
+	        return "";
+	      }
+	      // Other surrogate characters are passed through.
+	      return match;
+	    }
+	    return "\\u" + ("0000" + c.toString(16)).substr(-4);
+	  }) + "\"";
+	}
+	
 	function cropMultipleLines(text, limit) {
 	  return escapeNewLines(cropString(text, limit));
 	}
 	
-	function cropString(text, limit, alternativeText) {
+	function rawCropString(text, limit, alternativeText) {
 	  if (!alternativeText) {
 	    alternativeText = "\u2026";
 	  }
 	
-	  // Make sure it's a string and sanitize it.
-	  text = sanitizeString(text + "");
-	
 	  // Crop the string only if a limit is actually specified.
 	  if (!limit || limit <= 0) {
 	    return text;
 	  }
 	
 	  // Set the limit at least to the length of the alternative text
 	  // plus one character of the original text.
 	  if (limit <= alternativeText.length) {
@@ -296,22 +378,26 @@ return /******/ (function(modules) { // 
 	
 	  if (text.length > limit) {
 	    return text.substr(0, Math.ceil(halfLimit)) + alternativeText + text.substr(text.length - Math.floor(halfLimit));
 	  }
 	
 	  return text;
 	}
 	
+	function cropString(text, limit, alternativeText) {
+	  return rawCropString(sanitizeString(text + ""), limit, alternativeText);
+	}
+	
 	function sanitizeString(text) {
 	  // Replace all non-printable characters, except of
 	  // (horizontal) tab (HT: \x09) and newline (LF: \x0A, CR: \x0D),
 	  // with unicode replacement character (u+fffd).
 	  // eslint-disable-next-line no-control-regex
-	  let re = new RegExp("[\x00-\x08\x0B\x0C\x0E-\x1F\x80-\x9F]", "g");
+	  let re = new RegExp("[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]", "g");
 	  return text.replace(re, "\ufffd");
 	}
 	
 	function parseURLParams(url) {
 	  url = new URL(url);
 	  return parseURLEncodedText(url.searchParams);
 	}
 	
@@ -487,17 +573,19 @@ return /******/ (function(modules) { // 
 	
 	  return [];
 	}
 	
 	module.exports = {
 	  createFactories,
 	  isGrip,
 	  cropString,
+	  rawCropString,
 	  sanitizeString,
+	  escapeString,
 	  wrapRender,
 	  cropMultipleLines,
 	  parseURLParams,
 	  parseURLEncodedText,
 	  getFileName,
 	  getURLDisplayString,
 	  getSelectableInInspectorGrips
 	};
@@ -608,17 +696,19 @@ return /******/ (function(modules) { // 
 /***/ },
 /* 8 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// Dependencies
 	const React = __webpack_require__(3);
 	
 	const {
-	  cropString,
+	  escapeString,
+	  rawCropString,
+	  sanitizeString,
 	  wrapRender
 	} = __webpack_require__(4);
 	
 	// Shortcuts
 	const { span } = React.DOM;
 	
 	/**
 	 * Renders a string. String value is enclosed within quotes.
@@ -645,25 +735,27 @@ return /******/ (function(modules) { // 
 	    let member = this.props.member;
 	    let style = this.props.style;
 	
 	    let config = { className: "objectBox objectBox-string" };
 	    if (style) {
 	      config.style = style;
 	    }
 	
-	    if (member && member.open) {
-	      return span(config, "\"" + text + "\"");
+	    if (this.props.useQuotes) {
+	      text = escapeString(text);
+	    } else {
+	      text = sanitizeString(text);
 	    }
 	
-	    let croppedString = this.props.cropLimit ? cropString(text, this.props.cropLimit) : cropString(text);
-	
-	    let formattedString = this.props.useQuotes ? "\"" + croppedString + "\"" : croppedString;
-	
-	    return span(config, formattedString);
+	    if ((!member || !member.open) && this.props.cropLimit) {
+	      text = rawCropString(text, this.props.cropLimit);
+	    }
+	
+	    return span(config, text);
 	  })
 	});
 	
 	function supportsObject(object, type) {
 	  return type == "string";
 	}
 	
 	// Exports from this module
@@ -675,16 +767,17 @@ return /******/ (function(modules) { // 
 
 /***/ },
 /* 9 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// Dependencies
 	const React = __webpack_require__(3);
 	const {
+	  escapeString,
 	  sanitizeString,
 	  isGrip,
 	  wrapRender
 	} = __webpack_require__(4);
 	// Shortcuts
 	const { span } = React.DOM;
 	
 	/**
@@ -722,18 +815,18 @@ return /******/ (function(modules) { // 
 	      config.style = style;
 	    }
 	
 	    let string = member && member.open ? fullText || initial : initial.substring(0, cropLimit);
 	
 	    if (string.length < length) {
 	      string += "\u2026";
 	    }
-	    let formattedString = useQuotes ? `"${string}"` : string;
-	    return span(config, sanitizeString(formattedString));
+	    let formattedString = useQuotes ? escapeString(string) : sanitizeString(string);
+	    return span(config, formattedString);
 	  })
 	});
 	
 	function supportsObject(object, type) {
 	  if (!isGrip(object)) {
 	    return false;
 	  }
 	  return object.type === "longString";
@@ -2564,17 +2657,17 @@ return /******/ (function(modules) { // 
 	
 	exports['default'] = InlineSVG;
 	module.exports = exports['default'];
 
 /***/ },
 /* 31 */
 /***/ function(module, exports) {
 
-	module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --><svg , viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8,3L12,3L12,7L14,7L14,8L12,8L12,12L8,12L8,14L7,14L7,12L3,12L3,8L1,8L1,7L3,7L3,3L7,3L7,1L8,1L8,3ZM10,10L10,5L5,5L5,10L10,10Z\"></path></svg>"
+	module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --><svg viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8,3L12,3L12,7L14,7L14,8L12,8L12,12L8,12L8,14L7,14L7,12L3,12L3,8L1,8L1,7L3,7L3,3L7,3L7,1L8,1L8,3ZM10,10L10,5L5,5L5,10L10,10Z\"></path></svg>"
 
 /***/ },
 /* 32 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// ReactJS
 	const React = __webpack_require__(3);
 	
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_array.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_array.html
@@ -15,17 +15,17 @@ Test ArrayRep rep
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 "use strict";
 /* import-globals-from head.js */
 
 window.onload = Task.async(function* () {
-  const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/reps");
   let { Rep, ArrayRep } = REPS;
 
   let componentUnderTest = ArrayRep;
   const maxLength = {
     short: 3,
     long: 10
   };
 
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_attribute.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_attribute.html
@@ -13,17 +13,17 @@ Test Attribute rep
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
   try {
-    const { REPS } = browserRequire("devtools/client/shared/components/reps/load-reps");
+    const { REPS } = browserRequire("devtools/client/shared/components/reps/reps");
     let { Rep, Attribute } = REPS;
 
     let gripStub = {
       "type": "object",
       "class": "Attr",
       "actor": "server1.conn19.obj65",
       "extensible": true,
       "frozen": false,
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_comment-node.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_comment-node.html
@@ -15,17 +15,17 @@ Test comment-node rep
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 "use strict";
 
 window.onload = Task.async(function* () {
   try {
-    const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/load-reps");
+    const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/reps");
     let { Rep, CommentNode } = REPS;
 
     let gripStub = {
       "type": "object",
       "actor": "server1.conn1.child1/obj47",
       "class": "Comment",
       "extensible": true,
       "frozen": false,
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_date-time.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_date-time.html
@@ -12,17 +12,17 @@ Test DateTime rep
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
-  const { REPS } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  const { REPS } = browserRequire("devtools/client/shared/components/reps/reps");
   let { Rep, DateTime } = REPS;
 
   try {
     testValid();
     testInvalid();
   } catch(e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_document.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_document.html
@@ -12,17 +12,17 @@ Test Document rep
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
-  const { REPS } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  const { REPS } = browserRequire("devtools/client/shared/components/reps/reps");
   let { Rep, Document } = REPS;
 
   try {
     let gripStub = {
       "type": "object",
       "class": "HTMLDocument",
       "actor": "server1.conn17.obj115",
       "extensible": true,
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_element-node.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_element-node.html
@@ -18,17 +18,17 @@ Test Element node rep
 <script type="application/javascript;version=1.8">
 "use strict";
 
 window.onload = Task.async(function* () {
   const {
     REPS,
     MODE,
     getSelectableInInspectorGrips,
-  } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  } = browserRequire("devtools/client/shared/components/reps/reps");
   let { Rep, ElementNode } = REPS;
 
   try {
     yield testBodyNode();
     yield testDocumentElement();
     yield testNode();
     yield testNodeWithLeadingAndTrailingSpacesClassName();
     yield testNodeWithoutAttributes();
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_error.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_error.html
@@ -14,17 +14,17 @@ Test Error rep
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 "use strict";
 
 window.onload = Task.async(function* () {
-  const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/reps");
   let { Rep, ErrorRep } = REPS;
 
   try {
     // Test errors with different properties
     yield testSimpleError();
     yield testMultilineStackError();
     yield testErrorWithoutStacktrace();
 
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_event.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_event.html
@@ -16,17 +16,17 @@ Test Event rep
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
   const {
     REPS,
     MODE,
     getSelectableInInspectorGrips,
-  } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  } = browserRequire("devtools/client/shared/components/reps/reps");
   let { Rep, Event } = REPS;
 
   try {
     // Test that correct rep is chosen
     const renderedRep = shallowRenderComponent(Rep, { object: getGripStub("testEvent") });
     is(renderedRep.type, Event.rep, `Rep correctly selects ${Event.rep.displayName}`);
 
     yield testEvent();
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_failure.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_failure.html
@@ -13,17 +13,17 @@ Test fallback for rep rendering when a r
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
   try {
-    const { REPS } = browserRequire("devtools/client/shared/components/reps/load-reps");
+    const { REPS } = browserRequire("devtools/client/shared/components/reps/reps");
     let { Rep, ArrayRep, RegExp } = REPS;
 
     // Force the RegExp rep to crash by creating RegExp grip that throws when accessing
     // the displayString property
     let gripStub = {
       "type": "object",
       "class": "RegExp",
       "actor": "server1.conn22.obj39",
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_function.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_function.html
@@ -12,17 +12,17 @@ Test Func rep
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
-  const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/reps");
   let { Rep, Func } = REPS;
 
   const componentUnderTest = Func;
 
   try {
     // Test that correct rep is chosen
     const gripStub = getGripStub("testNamed");
     const renderedRep = shallowRenderComponent(Rep, { object: gripStub });
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_grip-array.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_grip-array.html
@@ -16,17 +16,17 @@ Test GripArray rep
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
   const {
     REPS,
     MODE,
     getSelectableInInspectorGrips,
-  } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  } = browserRequire("devtools/client/shared/components/reps/reps");
   let { Rep, GripArray } = REPS;
 
   let componentUnderTest = GripArray;
   const maxLength = {
     short: 3,
     long: 10
   };
 
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_grip-map.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_grip-map.html
@@ -18,17 +18,17 @@ Test GripMap rep
 <script type="application/javascript;version=1.8">
 "use strict";
 
 window.onload = Task.async(function* () {
   const {
     REPS,
     MODE,
     getSelectableInInspectorGrips,
-  } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  } = browserRequire("devtools/client/shared/components/reps/reps");
   let { Rep, GripMap } = REPS;
 
   const componentUnderTest = GripMap;
 
   try {
     yield testEmptyMap();
     yield testSymbolKeyedMap();
     yield testWeakMap();
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_grip.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_grip.html
@@ -16,17 +16,17 @@ Test grip rep
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
   const {
     REPS,
     MODE,
     getSelectableInInspectorGrips,
-  } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  } = browserRequire("devtools/client/shared/components/reps/reps");
   let { Rep, Grip } = REPS;
 
   const componentUnderTest = Grip;
 
   try {
     yield testBasic();
     yield testBooleanObject();
     yield testNumberObject();
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_infinity.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_infinity.html
@@ -14,17 +14,17 @@ Test Infinity rep
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 "use strict";
 
 window.onload = Task.async(function* () {
-  const { REPS } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  const { REPS } = browserRequire("devtools/client/shared/components/reps/reps");
   let { Rep, InfinityRep } = REPS;
 
   try {
     yield testInfinity();
     yield testNegativeInfinity();
   } catch (e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_long-string.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_long-string.html
@@ -12,17 +12,17 @@ Test LongString rep
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
-  const { REPS } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  const { REPS } = browserRequire("devtools/client/shared/components/reps/reps");
   let { Rep, LongStringRep } = REPS;
 
   try {
     // Test that correct rep is chosen
     const renderedRep = shallowRenderComponent(Rep, { object: getGripStub("testMultiline") });
     is(renderedRep.type, LongStringRep.rep,
       `Rep correctly selects ${LongStringRep.rep.displayName}`);
 
@@ -33,59 +33,63 @@ window.onload = Task.async(function* () 
     yield testMultilineLimit();
     yield testUseQuotes();
   } catch (e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
+  function quoteNewlines(text) {
+    return text.split("\n").join("\\n");
+  }
+
   function testMultiline() {
     const stub = getGripStub("testMultiline");
     const renderedComponent = renderComponent(
       LongStringRep.rep, { object: stub });
 
-    is(renderedComponent.textContent, `"${stub.initial}…"`,
+    is(renderedComponent.textContent, quoteNewlines(`"${stub.initial}…"`),
       "LongString rep has expected text content for multiline string");
   }
 
   function testMultilineLimit() {
     const renderedComponent = renderComponent(
       LongStringRep.rep, { object: getGripStub("testMultiline"), cropLimit: 20 });
 
     is(
       renderedComponent.textContent,
-      `"a\naaaaaaaaaaaaaaaaaa…"`,
+      `"a\\naaaaaaaaaaaaaaaaaa…"`,
       "LongString rep has expected text content for multiline string " +
       "with specified number of characters");
   }
 
   function testMultilineOpen() {
     const stub = getGripStub("testMultiline");
     const renderedComponent = renderComponent(
       LongStringRep.rep, { object: stub, member: {open: true}, cropLimit: 20 });
 
-    is(renderedComponent.textContent, `"${stub.initial}…"`,
+    is(renderedComponent.textContent, quoteNewlines(`"${stub.initial}…"`),
       "LongString rep has expected text content for multiline string when open");
   }
 
   function testFullText() {
     const stub = getGripStub("testFullText");
     const renderedComponentOpen = renderComponent(
       LongStringRep.rep, { object: stub, member: {open: true}, cropLimit: 20 });
 
-    is(renderedComponentOpen.textContent, `"${stub.fullText}"`,
+    is(renderedComponentOpen.textContent, quoteNewlines(`"${stub.fullText}"`),
       "LongString rep has expected text content when grip has a fullText " +
       "property and is open");
 
     const renderedComponentNotOpen = renderComponent(
       LongStringRep.rep, { object: stub, cropLimit: 20 });
 
     is(renderedComponentNotOpen.textContent,
-      `"a\naaaaaaaaaaaaaaaaaa…"`,
+      `"a\\naaaaaaaaaaaaaaaaaa…"`,
       "LongString rep has expected text content when grip has a fullText " +
       "property and is not open");
   }
 
   function testUseQuotes() {
     const renderedComponent = renderComponent(LongStringRep.rep,
       { object: getGripStub("testMultiline"), cropLimit: 20, useQuotes: false });
 
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_nan.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_nan.html
@@ -14,17 +14,17 @@ Test NaN rep
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 "use strict";
 
 window.onload = Task.async(function* () {
-  const { REPS } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  const { REPS } = browserRequire("devtools/client/shared/components/reps/reps");
   let { Rep, NaNRep } = REPS;
 
   try {
     yield testNaN();
   } catch (e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_null.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_null.html
@@ -13,17 +13,17 @@ Test Null rep
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
   try {
-    const { REPS } = browserRequire("devtools/client/shared/components/reps/load-reps");
+    const { REPS } = browserRequire("devtools/client/shared/components/reps/reps");
     let { Rep, Null } = REPS;
 
     let gripStub = {
       "type": "null"
     };
 
     // Test that correct rep is chosen
     const renderedRep = shallowRenderComponent(Rep, { object: gripStub });
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_number.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_number.html
@@ -12,17 +12,17 @@ Test Number rep
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
-  const { REPS } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  const { REPS } = browserRequire("devtools/client/shared/components/reps/reps");
   let { Rep, Number } = REPS;
 
   try {
     yield testInt();
     yield testBoolean();
     yield testNegativeZero();
     yield testUnsafeInt();
   } catch(e) {
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_object-with-text.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_object-with-text.html
@@ -13,17 +13,17 @@ Test ObjectWithText rep
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
   try {
-    const { REPS } = browserRequire("devtools/client/shared/components/reps/load-reps");
+    const { REPS } = browserRequire("devtools/client/shared/components/reps/reps");
     let { Rep, ObjectWithText } = REPS;
 
     let gripStub = {
       "type": "object",
       "class": "CSSStyleRule",
       "actor": "server1.conn3.obj273",
       "extensible": true,
       "frozen": false,
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_object-with-url.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_object-with-url.html
@@ -16,17 +16,17 @@ Test ObjectWithURL rep
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
   try {
     let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
     let React = browserRequire("devtools/client/shared/vendor/react");
 
-    const { REPS } = browserRequire("devtools/client/shared/components/reps/load-reps");
+    const { REPS } = browserRequire("devtools/client/shared/components/reps/reps");
     let { Rep, ObjectWithURL } = REPS;
 
     let gripStub = {
       "type": "object",
       "class": "Location",
       "actor": "server1.conn2.obj272",
       "extensible": true,
       "frozen": false,
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_object.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_object.html
@@ -12,17 +12,17 @@ Test Obj rep
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
-  const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/reps");
   let { Rep, Obj } = REPS;
 
   const componentUnderTest = Obj;
 
   try {
     yield testBasic();
 
     // Test property iterator
@@ -243,19 +243,19 @@ window.onload = Task.async(function* () 
       },
       {
         mode: MODE.LONG,
         expectedOutput: defaultOutput,
       }
     ];
 
     testRepRenderModes(
-      modeTests, 
-      "testCustomTitle", 
-      componentUnderTest, 
+      modeTests,
+      "testCustomTitle",
+      componentUnderTest,
       stub, {
         title: customTitle
       }
     );
   }
 });
 </script>
 </pre>
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_promise.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_promise.html
@@ -18,17 +18,17 @@ Test Promise rep
 <script type="application/javascript;version=1.8">
 "use strict";
 
 window.onload = Task.async(function* () {
   const {
     REPS,
     MODE,
     getSelectableInInspectorGrips,
-  } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  } = browserRequire("devtools/client/shared/components/reps/reps");
   let { Rep, PromiseRep } = REPS;
 
   const componentUnderTest = PromiseRep;
 
   try {
     yield testPending();
     yield testFulfilledWithNumber();
     yield testFulfilledWithString();
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_regexp.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_regexp.html
@@ -13,17 +13,17 @@ Test RegExp rep
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
   try {
-    const { REPS } = browserRequire("devtools/client/shared/components/reps/load-reps");
+    const { REPS } = browserRequire("devtools/client/shared/components/reps/reps");
     let { Rep, RegExp } = REPS;
 
     let gripStub = {
       "type": "object",
       "class": "RegExp",
       "actor": "server1.conn22.obj39",
       "extensible": true,
       "frozen": false,
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_string.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_string.html
@@ -12,68 +12,88 @@ Test String rep
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
-  const { REPS } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  const { REPS } = browserRequire("devtools/client/shared/components/reps/reps");
   let { Rep, StringRep } = REPS;
 
+  const test_cases = [{
+    name: "testMultiline",
+    props: {
+      object: "aaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbb\ncccccccccccccccc\n",
+    },
+    result: "\"aaaaaaaaaaaaaaaaaaaaa\\nbbbbbbbbbbbbbbbbbbb\\ncccccccccccccccc\\n\""
+  }, {
+    name: "testMultilineLimit",
+    props: {
+      object: "aaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbb\ncccccccccccccccc\n",
+      cropLimit: 20
+    },
+    result: "\"aaaaaaaaa…cccccc\\n\""
+  }, {
+    name: "testMultilineOpen",
+    props: {
+      object: "aaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbb\ncccccccccccccccc\n",
+      member: { open: true }
+    },
+    result: "\"aaaaaaaaaaaaaaaaaaaaa\\nbbbbbbbbbbbbbbbbbbb\\ncccccccccccccccc\\n\""
+  }, {
+    name: "testUseQuotes",
+    props: {
+      object: "abc",
+      useQuotes: false
+    },
+    result: "abc"
+  }, {
+    name: "testNonPrintableCharacters",
+    props: {
+      object: "a\x01b",
+      useQuotes: false
+    },
+    result: "a\ufffdb"
+  }, {
+    name: "testQuoting",
+    props: {
+      object: "\t\n\r\"'\\\x1f\x9f\ufeff\ufffe\ud8000\u2063\ufffc\u2028\ueeee",
+      useQuotes: true
+    },
+    result: "\"\\t\\n\\r\\\"'\\\\\\u001f\\u009f\\ufeff\\ufffe\\ud8000\\u2063\\ufffc\\u2028\\ueeee\""
+  }, {
+    name: "testUnpairedSurrogate",
+    props: {
+      object: "\uDC23",
+      useQuotes: true
+    },
+    result: "\"\\udc23\""
+  }, {
+    name: "testValidSurrogate",
+    props: {
+      object: "\ud83d\udeec",
+      useQuotes: true
+    },
+    result: "\"\ud83d\udeec\""
+  }];
+
   try {
     // Test that correct rep is chosen
-    const renderedRep = shallowRenderComponent(Rep, { object: getGripStub("testMultiline") });
+    const renderedRep = shallowRenderComponent(Rep, test_cases[0].props);
     is(renderedRep.type, StringRep.rep, `Rep correctly selects ${StringRep.rep.displayName}`);
 
     // Test rendering
-    yield testMultiline();
-    yield testMultilineOpen();
-    yield testMultilineLimit();
-    yield testUseQuotes();
-    yield testNonPritableCharacters();
+    for (let test of test_cases) {
+      const renderedComponent = renderComponent(StringRep.rep, test.props);
+      is(renderedComponent.textContent, test.result, "String rep " + test.name);
+    }
   } catch(e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
-
-  function testMultiline() {
-    const renderedComponent = renderComponent(StringRep.rep, { object: getGripStub("testMultiline") });
-    is(renderedComponent.textContent, "\"aaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbb\ncccccccccccccccc\n\"", "String rep has expected text content for multiline string");
-  }
-
-  function testMultilineLimit() {
-    const renderedComponent = renderComponent(StringRep.rep, { object: getGripStub("testMultiline"), cropLimit: 20 });
-    is(renderedComponent.textContent, "\"aaaaaaaaaa…cccccccc\n\"", "String rep has expected text content for multiline string with specified number of characters");
-  }
-
-  function testMultilineOpen() {
-    const renderedComponent = renderComponent(StringRep.rep, { object: getGripStub("testMultiline"), member: {open: true} });
-    is(renderedComponent.textContent, "\"aaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbb\ncccccccccccccccc\n\"", "String rep has expected text content for multiline string when open");
-  }
-
-  function testUseQuotes(){
-     const renderedComponent = renderComponent(StringRep.rep, { object: getGripStub("testUseQuotes"), useQuotes: false });
-     is(renderedComponent.textContent, "abc", "String rep was expected to omit quotes");
-  }
-
-  function testNonPritableCharacters(){
-     const renderedComponent = renderComponent(StringRep.rep, { object: getGripStub("testNonPritableCharacters"), useQuotes: false });
-     is(renderedComponent.textContent, "a\ufffdb", "String rep was expected to omit non printable characters");
-  }
-
-  function getGripStub(name) {
-    switch (name) {
-      case "testMultiline":
-        return "aaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbb\ncccccccccccccccc\n";
-      case "testUseQuotes":
-        return "abc";
-      case "testNonPritableCharacters":
-        return "a\x01b";
-    }
-  }
 });
 </script>
 </pre>
 </body>
 </html>
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_stylesheet.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_stylesheet.html
@@ -13,17 +13,17 @@ Test Stylesheet rep
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
   try {
-    const { REPS } = browserRequire("devtools/client/shared/components/reps/load-reps");
+    const { REPS } = browserRequire("devtools/client/shared/components/reps/reps");
     let { Rep, StyleSheet } = REPS;
 
     let gripStub = {
       "type": "object",
       "class": "CSSStyleSheet",
       "actor": "server1.conn2.obj1067",
       "extensible": true,
       "frozen": false,
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_symbol.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_symbol.html
@@ -15,17 +15,17 @@ Test Symbol rep
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 "use strict";
 /* import-globals-from head.js */
 
 window.onload = Task.async(function* () {
-  const { REPS } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  const { REPS } = browserRequire("devtools/client/shared/components/reps/reps");
   let { Rep, SymbolRep } = REPS;
 
   let gripStubs = new Map();
   gripStubs.set("testSymbolFoo", {
     type: "symbol",
     name: "foo"
   });
   gripStubs.set("testSymbolWithoutIdentifier", {
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_text-node.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_text-node.html
@@ -18,17 +18,17 @@ Test text-node rep
 <script type="application/javascript;version=1.8">
 "use strict";
 
 window.onload = Task.async(function* () {
   const {
     REPS,
     MODE,
     getSelectableInInspectorGrips,
-  } = browserRequire("devtools/client/shared/components/reps/load-reps");
+  } = browserRequire("devtools/client/shared/components/reps/reps");
   let { Rep, TextNode } = REPS;
 
   let gripStubs = new Map();
   gripStubs.set("testRendering", {
     "class": "Text",
     "actor": "server1.conn1.child1/obj50",
     "preview": {
       "kind": "DOMNode",
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_undefined.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_undefined.html
@@ -15,17 +15,17 @@ Test undefined rep
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
   try {
     let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
     let React = browserRequire("devtools/client/shared/vendor/react");
-    const { REPS } = browserRequire("devtools/client/shared/components/reps/load-reps");
+    const { REPS } = browserRequire("devtools/client/shared/components/reps/reps");
     let { Rep, Undefined } = REPS;
 
     let gripStub = {
       "type": "undefined"
     };
 
     // Test that correct rep is chosen
     const renderedRep = shallowRenderComponent(Rep, { object: gripStub });
--- a/devtools/client/shared/components/reps/test/mochitest/test_reps_window.html
+++ b/devtools/client/shared/components/reps/test/mochitest/test_reps_window.html
@@ -16,17 +16,17 @@ Test window rep
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
   try {
     let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
     let React = browserRequire("devtools/client/shared/vendor/react");
 
-    const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/load-reps");
+    const { REPS, MODE } = browserRequire("devtools/client/shared/components/reps/reps");
     let { Rep, Window } = REPS;
 
     let gripStub = {
       "type": "object",
       "class": "Window",
       "actor": "server1.conn3.obj198",
       "extensible": true,
       "frozen": false,
deleted file mode 100644
--- a/devtools/client/themes/deprecated-boxmodel.css
+++ /dev/null
@@ -1,259 +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/ */
-
-/**
- * THIS STYLESHEET IS FOR THE DEPRECATED BOX MODEL MODULE (deprecated-box-model.js) AND
- * SHOULD NO LONGER BE MODIFIED.
- */
-
-#old-boxmodel-wrapper {
-  border-bottom-style: solid;
-  border-bottom-width: 1px;
-  border-color: var(--theme-splitter-color);
-  -moz-user-select: none;
-}
-
-#old-boxmodel-container {
-  /* The view will grow bigger as the window gets resized, until 400px */
-  max-width: 400px;
-  margin: 0px auto;
-  padding: 0;
-}
-
-/* Header */
-
-#old-boxmodel-header,
-#old-boxmodel-info {
-  display: flex;
-  align-items: center;
-  padding: 4px 17px;
-}
-
-#old-layout-geometry-editor {
-  visibility: hidden;
-}
-
-#old-layout-geometry-editor::before {
-  background: url(images/geometry-editor.svg) no-repeat center center / 16px 16px;
-}
-
-/* Main: contains the box-model regions */
-
-#old-boxmodel-main {
-  position: relative;
-  box-sizing: border-box;
-  /* The regions are semi-transparent, so the white background is partly
-     visible */
-  background-color: white;
-  color: var(--theme-selection-color);
-  /* Make sure there is some space between the window's edges and the regions */
-  margin: 0 14px 4px 14px;
-  width: calc(100% - 2 * 14px);
-}
-
-.old-boxmodel-margin,
-.old-boxmodel-size {
-  color: var(--theme-highlight-blue);
-}
-
-/* Regions are 3 nested elements with wide borders and outlines */
-
-#old-boxmodel-content {
-  height: 18px;
-}
-
-#old-boxmodel-margins,
-#old-boxmodel-borders,
-#old-boxmodel-padding {
-  border-color: hsla(210,100%,85%,0.2);
-  border-width: 18px;
-  border-style: solid;
-  outline: dotted 1px hsl(210,100%,85%);
-}
-
-#old-boxmodel-margins {
-  /* This opacity applies to all of the regions, since they are nested */
-  opacity: .8;
-}
-
-/* Regions colors */
-
-#old-boxmodel-margins {
-  border-color: #edff64;
-}
-
-#old-boxmodel-borders {
-  border-color: #444444;
-}
-
-#old-boxmodel-padding {
-  border-color: #6a5acd;
-}
-
-#old-boxmodel-content {
-  background-color: #87ceeb;
-}
-
-.theme-firebug #old-boxmodel-main,
-.theme-firebug #old-boxmodel-borders,
-.theme-firebug #old-boxmodel-content {
-  border-style: solid;
-}
-
-.theme-firebug #old-boxmodel-main,
-.theme-firebug #old-boxmodel-header {
-  font-family: var(--proportional-font-family);
-}
-
-.theme-firebug #old-boxmodel-main {
-  color: var(--theme-body-color);
-  font-size: var(--theme-toolbar-font-size);
-}
-
-.theme-firebug #old-boxmodel-header {
-  font-size: var(--theme-toolbar-font-size);
-}
-
-/* Editable region sizes are contained in absolutely positioned <p> */
-
-#old-boxmodel-main > p {
-  position: absolute;
-  pointer-events: none;
-  margin: 0;
-  text-align: center;
-}
-
-#old-boxmodel-main > p > span,
-#old-boxmodel-main > p > input {
-  vertical-align: middle;
-  pointer-events: auto;
-}
-
-/* Coordinates for the region sizes */
-
-.old-boxmodel-top,
-.old-boxmodel-bottom {
-  width: calc(100% - 2px);
-  text-align: center;
-}
-
-.old-boxmodel-padding.old-boxmodel-top {
-  top: 37px;
-}
-
-.old-boxmodel-padding.old-boxmodel-bottom {
-  bottom: 38px;
-}
-
-.old-boxmodel-border.old-boxmodel-top {
-  top: 19px;
-}
-
-.old-boxmodel-border.old-boxmodel-bottom {
-  bottom: 20px;
-}
-
-.old-boxmodel-margin.old-boxmodel-top {
-  top: 1px;
-}
-
-.old-boxmodel-margin.old-boxmodel-bottom {
-  bottom: 2px;
-}
-
-.old-boxmodel-size,
-.old-boxmodel-margin.old-boxmodel-left,
-.old-boxmodel-margin.old-boxmodel-right,
-.old-boxmodel-border.old-boxmodel-left,
-.old-boxmodel-border.old-boxmodel-right,
-.old-boxmodel-padding.old-boxmodel-left,
-.old-boxmodel-padding.old-boxmodel-right {
-  top: 22px;
-  line-height: 80px;
-}
-
-.old-boxmodel-size {
-  width: calc(100% - 2px);
-}
-
-.old-boxmodel-margin.old-boxmodel-right,
-.old-boxmodel-margin.old-boxmodel-left,
-.old-boxmodel-border.old-boxmodel-left,
-.old-boxmodel-border.old-boxmodel-right,
-.old-boxmodel-padding.old-boxmodel-right,
-.old-boxmodel-padding.old-boxmodel-left {
-  width: 21px;
-}
-
-.old-boxmodel-padding.old-boxmodel-left {
-  left: 35px;
-}
-
-.old-boxmodel-padding.old-boxmodel-right {
-  right: 35px;
-}
-
-.old-boxmodel-border.old-boxmodel-left {
-  left: 16px;
-}
-
-.old-boxmodel-border.old-boxmodel-right {
-  right: 17px;
-}
-
-.old-boxmodel-margin.old-boxmodel-right {
-  right: 0;
-}
-
-.old-boxmodel-margin.old-boxmodel-left {
-  left: 0;
-}
-
-.old-boxmodel-rotate.old-boxmodel-left:not(.old-boxmodel-editing) {
-  transform: rotate(-90deg);
-}
-
-.old-boxmodel-rotate.old-boxmodel-right:not(.old-boxmodel-editing) {
-  transform: rotate(90deg);
-}
-
-/* Legend: displayed inside regions */
-
-.old-boxmodel-legend {
-  position: absolute;
-  margin: 2px 6px;
-  z-index: 1;
-}
-
-.old-boxmodel-legend[data-box="margin"] {
-  color: var(--theme-highlight-blue);
-}
-
-/* Editable fields */
-
-.old-boxmodel-editable {
-  border: 1px dashed transparent;
-  -moz-user-select: none;
-}
-
-.old-boxmodel-editable:hover {
-  border-bottom-color: hsl(0, 0%, 50%);
-}
-
-/* Make sure the content size doesn't appear as editable like the other sizes */
-
-.old-boxmodel-size > span {
-  cursor: default;
-}
-
-/* Box Model Info: contains the position and size of the element */
-
-#old-boxmodel-element-size {
-  flex: 1;
-}
-
-#old-boxmodel-position-group {
-  display: flex;
-  align-items: center;
-}
--- a/devtools/client/webconsole/new-console-output/main.js
+++ b/devtools/client/webconsole/new-console-output/main.js
@@ -1,23 +1,26 @@
 /* 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 BrowserLoader */
 
 "use strict";
 
-var { utils: Cu } = Components;
+var Cu = Components.utils;
 
 const { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
 
-// Initialize module loader and load all modules of the new inline
-// preview feature. The entire code-base doesn't need any extra
-// privileges and runs entirely in content scope.
-const NewConsoleOutputWrapper = BrowserLoader({
-  baseURI: "resource://devtools/client/webconsole/new-console-output/",
-  window}).require("./new-console-output-wrapper");
+this.NewConsoleOutput = function (parentNode, jsterm, toolbox, owner, serviceContainer) {
+  // Initialize module loader and load all modules of the new inline
+  // preview feature. The entire code-base doesn't need any extra
+  // privileges and runs entirely in content scope.
+  let NewConsoleOutputWrapper = BrowserLoader({
+    baseURI: "resource://devtools/client/webconsole/new-console-output/",
+    // The toolbox is not available for the browser console.
+    commonLibRequire: toolbox ? toolbox.browserRequire : null,
+    window
+  }).require("./new-console-output-wrapper");
 
-this.NewConsoleOutput = function (parentNode, jsterm, toolbox, owner, serviceContainer) {
   return new NewConsoleOutputWrapper(
     parentNode, jsterm, toolbox, owner, serviceContainer);
 };
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -219,20 +219,23 @@ function WebConsoleFrame(webConsoleOwner
   this._onPanelSelected = this._onPanelSelected.bind(this);
   this._flushMessageQueue = this._flushMessageQueue.bind(this);
   this._onToolboxPrefChanged = this._onToolboxPrefChanged.bind(this);
   this._onUpdateListeners = this._onUpdateListeners.bind(this);
 
   this._outputTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   this._outputTimerInitialized = false;
 
-  let require = BrowserLoaderModule.BrowserLoader({
+  let toolbox = gDevTools.getToolbox(this.owner.target);
+  let {require} = BrowserLoaderModule.BrowserLoader({
     window: this.window,
-    useOnlyShared: true
-  }).require;
+    useOnlyShared: true,
+    // The toolbox isn't available for the browser console.
+    commonLibRequire: toolbox ? toolbox.browserRequire : null,
+  });
 
   this.React = require("devtools/client/shared/vendor/react");
   this.ReactDOM = require("devtools/client/shared/vendor/react-dom");
   this.FrameView = this.React.createFactory(require("devtools/client/shared/components/frame"));
   this.StackTraceView = this.React.createFactory(require("devtools/client/shared/components/stack-trace"));
 
   this._telemetry = new Telemetry();
 
--- a/devtools/server/tests/unit/test_frameactor_wasm-01.js
+++ b/devtools/server/tests/unit/test_frameactor_wasm-01.js
@@ -3,23 +3,19 @@
 
 /**
  * Verify that wasm frame(s) can be requested from the client.
  */
 
 var gDebuggee;
 var gClient;
 var gThreadClient;
-var gOldPref;
 
 function run_test()
 {
-  gOldPref = Services.prefs.getBoolPref("javascript.options.wasm");
-  Services.prefs.setBoolPref("javascript.options.wasm", true);
-
   if (typeof WebAssembly == "undefined") {
     return; // wasm is not enabled for this platform
   }
 
   initTestDebuggerServer();
   gDebuggee = addTestGlobal("test-stack");
   gClient = new DebuggerClient(DebuggerServer.connectPipe());
   gClient.connect().then(function () {
@@ -44,26 +40,25 @@ function test_pause_frame()
       do_check_eq(wasmFrame.type, "wasmcall");
       do_check_eq(wasmFrame.this, undefined);
 
       let location = wasmFrame.where;
       do_check_eq(location.line > 0, true);
       do_check_eq(location.column > 0, true);
       do_check_eq(location.source.url.endsWith(" > wasm"), true);
 
-      Services.prefs.setBoolPref("javascript.options.wasm", gOldPref);
       finishClient(gClient);
     });
   });
 
   gDebuggee.eval("(" + function () {
     // WebAssembly bytecode was generated by running:
     // js -e 'print(wasmTextToBinary("(module(import \"a\" \"b\")(func(export \"c\")call 0))"))'
     var m = new WebAssembly.Module(new Uint8Array([
-      0,97,115,109,13,0,0,0,1,132,128,128,128,0,1,96,0,0,2,135,128,128,128,0,1,1,97,1,
+      0,97,115,109,1,0,0,0,1,132,128,128,128,0,1,96,0,0,2,135,128,128,128,0,1,1,97,1,
       98,0,0,3,130,128,128,128,0,1,0,6,129,128,128,128,0,0,7,133,128,128,128,0,1,1,99,
       0,1,10,138,128,128,128,0,1,132,128,128,128,0,0,16,0,11
     ]));
     var i = new WebAssembly.Instance(m, {a: {b: () => {
       debugger;
     }}});
     i.exports.c();
   } + ")()");
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -167,17 +167,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 /* static */ bool
 CustomElementRegistry::IsCustomElementEnabled(JSContext* aCx, JSObject* aObject)
 {
   return Preferences::GetBool("dom.webcomponents.customelements.enabled") ||
-         Preferences::GetBool("dom.webcomponents.enabled");
+         nsContentUtils::IsWebComponentsEnabled();
 }
 
 /* static */ already_AddRefed<CustomElementRegistry>
 CustomElementRegistry::Create(nsPIDOMWindowInner* aWindow)
 {
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aWindow->IsInnerWindow());
 
--- a/dom/base/DOMIntersectionObserver.cpp
+++ b/dom/base/DOMIntersectionObserver.cpp
@@ -38,25 +38,27 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMInte
 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMIntersectionObserver)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMIntersectionObserver)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMIntersectionObserver)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+  tmp->Disconnect();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mQueuedEntries)
-  tmp->Disconnect();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMIntersectionObserver)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueuedEntries)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 already_AddRefed<DOMIntersectionObserver>
 DOMIntersectionObserver::Constructor(const mozilla::dom::GlobalObject& aGlobal,
                                      mozilla::dom::IntersectionCallback& aCb,
@@ -184,38 +186,36 @@ DOMIntersectionObserver::UnlinkTarget(El
 void
 DOMIntersectionObserver::Connect()
 {
   if (mConnected) {
     return;
   }
 
   mConnected = true;
-  nsIDocument* document = mOwner->GetExtantDoc();
-  document->AddIntersectionObserver(this);
+  if (mDocument) {
+    mDocument->AddIntersectionObserver(this);
+  }
 }
 
 void
 DOMIntersectionObserver::Disconnect()
 {
   if (!mConnected) {
     return;
   }
 
   mConnected = false;
   for (auto iter = mObservationTargets.Iter(); !iter.Done(); iter.Next()) {
     Element* target = iter.Get()->GetKey();
     target->UnregisterIntersectionObserver(this);
   }
   mObservationTargets.Clear();
-  if (mOwner) {
-    nsIDocument* document = mOwner->GetExtantDoc();
-    if (document) {
-      document->RemoveIntersectionObserver(this);
-    }
+  if (mDocument) {
+    mDocument->RemoveIntersectionObserver(this);
   }
 }
 
 void
 DOMIntersectionObserver::TakeRecords(nsTArray<RefPtr<DOMIntersectionObserverEntry>>& aRetVal)
 {
   aRetVal.SwapElements(mQueuedEntries);
   mQueuedEntries.Clear();
--- a/dom/base/DOMIntersectionObserver.h
+++ b/dom/base/DOMIntersectionObserver.h
@@ -103,17 +103,17 @@ class DOMIntersectionObserver final : pu
 {
   virtual ~DOMIntersectionObserver() {
     Disconnect();
   }
 
 public:
   DOMIntersectionObserver(already_AddRefed<nsPIDOMWindowInner>&& aOwner,
                           mozilla::dom::IntersectionCallback& aCb)
-  : mOwner(aOwner), mCallback(&aCb), mConnected(false)
+  : mOwner(aOwner), mDocument(mOwner->GetExtantDoc()), mCallback(&aCb), mConnected(false)
   {
   }
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMIntersectionObserver)
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_INTERSECTION_OBSERVER_IID)
 
   static already_AddRefed<DOMIntersectionObserver>
   Constructor(const mozilla::dom::GlobalObject& aGlobal,
@@ -161,16 +161,17 @@ protected:
   void QueueIntersectionObserverEntry(Element* aTarget,
                                       DOMHighResTimeStamp time,
                                       const Maybe<nsRect>& aRootRect,
                                       const nsRect& aTargetRect,
                                       const Maybe<nsRect>& aIntersectionRect,
                                       double aIntersectionRatio);
 
   nsCOMPtr<nsPIDOMWindowInner>                    mOwner;
+  RefPtr<nsIDocument>                             mDocument;
   RefPtr<mozilla::dom::IntersectionCallback>      mCallback;
   RefPtr<Element>                                 mRoot;
   nsCSSRect                                       mRootMargin;
   nsTArray<double>                                mThresholds;
   nsTHashtable<nsPtrHashKey<Element>>             mObservationTargets;
   nsTArray<RefPtr<DOMIntersectionObserverEntry>>  mQueuedEntries;
   bool                                            mConnected;
 };
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -3846,46 +3846,46 @@ Element::ClearDataset()
 {
   nsDOMSlots *slots = GetExistingDOMSlots();
 
   MOZ_ASSERT(slots && slots->mDataset,
              "Slots should exist and dataset should not be null.");
   slots->mDataset = nullptr;
 }
 
-nsDataHashtable<nsPtrHashKey<DOMIntersectionObserver>, int32_t>*
+nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>*
 Element::RegisteredIntersectionObservers()
 {
   nsDOMSlots* slots = DOMSlots();
   return &slots->mRegisteredIntersectionObservers;
 }
 
 void
 Element::RegisterIntersectionObserver(DOMIntersectionObserver* aObserver)
 {
-  nsDataHashtable<nsPtrHashKey<DOMIntersectionObserver>, int32_t>* observers =
+  nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>* observers =
     RegisteredIntersectionObservers();
   if (observers->Contains(aObserver)) {
     return;
   }
   RegisteredIntersectionObservers()->Put(aObserver, -1);
 }
 
 void
 Element::UnregisterIntersectionObserver(DOMIntersectionObserver* aObserver)
 {
-  nsDataHashtable<nsPtrHashKey<DOMIntersectionObserver>, int32_t>* observers =
+  nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>* observers =
     RegisteredIntersectionObservers();
   observers->Remove(aObserver);
 }
 
 bool
 Element::UpdateIntersectionObservation(DOMIntersectionObserver* aObserver, int32_t aThreshold)
 {
-  nsDataHashtable<nsPtrHashKey<DOMIntersectionObserver>, int32_t>* observers =
+  nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>* observers =
     RegisteredIntersectionObservers();
   if (!observers->Contains(aObserver)) {
     return false;
   }
   int32_t previousThreshold = observers->Get(aObserver);
   if (previousThreshold != aThreshold) {
     observers->Put(aObserver, aThreshold);
     return true;
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -1461,17 +1461,18 @@ protected:
    * the value of xlink:show, converted to a suitably equivalent named target
    * (e.g. _blank).
    */
   virtual void GetLinkTarget(nsAString& aTarget);
 
   nsDOMTokenList* GetTokenList(nsIAtom* aAtom,
                                const DOMTokenListSupportedTokenArray aSupportedTokens = nullptr);
 
-  nsDataHashtable<nsPtrHashKey<DOMIntersectionObserver>, int32_t>* RegisteredIntersectionObservers();
+  nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>*
+    RegisteredIntersectionObservers();
 
 private:
   /**
    * Hook for implementing GetClasses.  This is guaranteed to only be
    * called if the NODE_MAY_HAVE_CLASS flag is set.
    */
   const nsAttrValue* DoGetClasses() const;
 
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -621,16 +621,22 @@ FragmentOrElement::nsDOMSlots::Traverse(
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mClassList");
   cb.NoteXPCOMChild(mClassList.get());
 
   if (mCustomElementData) {
     for (uint32_t i = 0; i < mCustomElementData->mCallbackQueue.Length(); i++) {
       mCustomElementData->mCallbackQueue[i]->Traverse(cb);
     }
   }
+
+  for (auto iter = mRegisteredIntersectionObservers.Iter(); !iter.Done(); iter.Next()) {
+    DOMIntersectionObserver* observer = iter.Key();
+    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mRegisteredIntersectionObservers[i]");
+    cb.NoteXPCOMChild(observer);
+  }
 }
 
 void
 FragmentOrElement::nsDOMSlots::Unlink(bool aIsXUL)
 {
   mStyle = nullptr;
   mSMILOverrideStyle = nullptr;
   if (mAttributeMap) {
--- a/dom/base/FragmentOrElement.h
+++ b/dom/base/FragmentOrElement.h
@@ -343,17 +343,18 @@ public:
     /**
      * Web components custom element data.
      */
     RefPtr<CustomElementData> mCustomElementData;
 
     /**
      * Registered Intersection Observers on the element.
      */
-    nsDataHashtable<nsPtrHashKey<DOMIntersectionObserver>, int32_t> mRegisteredIntersectionObservers;
+    nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>
+      mRegisteredIntersectionObservers;
   };
 
 protected:
   void GetMarkup(bool aIncludeSelf, nsAString& aMarkup);
   void SetInnerHTMLInternal(const nsAString& aInnerHTML, ErrorResult& aError);
 
   // Override from nsINode
   virtual nsINode::nsSlots* CreateSlots() override;
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -280,16 +280,17 @@ bool nsContentUtils::sIsFullScreenApiEna
 bool nsContentUtils::sIsUnprefixedFullscreenApiEnabled = false;
 bool nsContentUtils::sTrustedFullScreenOnly = true;
 bool nsContentUtils::sIsCutCopyAllowed = true;
 bool nsContentUtils::sIsFrameTimingPrefEnabled = false;
 bool nsContentUtils::sIsPerformanceTimingEnabled = false;
 bool nsContentUtils::sIsResourceTimingEnabled = false;
 bool nsContentUtils::sIsUserTimingLoggingEnabled = false;
 bool nsContentUtils::sIsExperimentalAutocompleteEnabled = false;
+bool nsContentUtils::sIsWebComponentsEnabled = false;
 bool nsContentUtils::sEncodeDecodeURLHash = false;
 bool nsContentUtils::sGettersDecodeURLHash = false;
 bool nsContentUtils::sPrivacyResistFingerprinting = false;
 bool nsContentUtils::sSendPerformanceTimingNotifications = false;
 bool nsContentUtils::sUseActivityCursor = false;
 
 uint32_t nsContentUtils::sHandlingInputTimeout = 1000;
 
@@ -507,20 +508,21 @@ nsContentUtils::Init()
   sSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
   if(!sSecurityManager)
     return NS_ERROR_FAILURE;
   NS_ADDREF(sSecurityManager);
 
   sSecurityManager->GetSystemPrincipal(&sSystemPrincipal);
   MOZ_ASSERT(sSystemPrincipal);
 
-  // We use the constructor here because we want infallible initialization; we
-  // apparently don't care whether sNullSubjectPrincipal has a sane URI or not.
-  RefPtr<nsNullPrincipal> nullPrincipal = new nsNullPrincipal();
-  nullPrincipal->Init();
+  RefPtr<nsNullPrincipal> nullPrincipal = nsNullPrincipal::Create();
+  if (!nullPrincipal) {
+    return NS_ERROR_FAILURE;
+  }
+
   nullPrincipal.forget(&sNullSubjectPrincipal);
 
   nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
   if (NS_FAILED(rv)) {
     // This makes life easier, but we can live without it.
 
     sIOService = nullptr;
   }
@@ -577,16 +579,19 @@ nsContentUtils::Init()
                                "dom.performance.enable_user_timing_logging", false);
 
   Preferences::AddBoolVarCache(&sIsFrameTimingPrefEnabled,
                                "dom.enable_frame_timing", false);
 
   Preferences::AddBoolVarCache(&sIsExperimentalAutocompleteEnabled,
                                "dom.forms.autocomplete.experimental", false);
 
+  Preferences::AddBoolVarCache(&sIsWebComponentsEnabled,
+                               "dom.webcomponents.enabled", false);
+
   Preferences::AddBoolVarCache(&sEncodeDecodeURLHash,
                                "dom.url.encode_decode_hash", false);
 
   Preferences::AddBoolVarCache(&sGettersDecodeURLHash,
                                "dom.url.getters_decode_hash", false);
 
   Preferences::AddBoolVarCache(&sPrivacyResistFingerprinting,
                                "privacy.resistFingerprinting", false);
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2801,16 +2801,19 @@ public:
                                         nsIPrincipal** aLoadingPrincipal,
                                         nsContentPolicyType& aContentPolicyType);
 
   static nsresult
   CreateJSValueFromSequenceOfObject(JSContext* aCx,
                                     const mozilla::dom::Sequence<JSObject*>& aTransfer,
                                     JS::MutableHandle<JS::Value> aValue);
 
+  static bool
+  IsWebComponentsEnabled() { return sIsWebComponentsEnabled; }
+
 private:
   static bool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static bool CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
                                 nsIPrincipal* aPrincipal);
 
@@ -2907,16 +2910,17 @@ private:
   static bool sTrustedFullScreenOnly;
   static bool sIsCutCopyAllowed;
   static uint32_t sHandlingInputTimeout;
   static bool sIsPerformanceTimingEnabled;
   static bool sIsResourceTimingEnabled;
   static bool sIsUserTimingLoggingEnabled;
   static bool sIsFrameTimingPrefEnabled;
   static bool sIsExperimentalAutocompleteEnabled;
+  static bool sIsWebComponentsEnabled;
   static bool sEncodeDecodeURLHash;
   static bool sGettersDecodeURLHash;
   static bool sPrivacyResistFingerprinting;
   static bool sSendPerformanceTimingNotifications;
   static bool sUseActivityCursor;
   static uint32_t sCookiesLifetimePolicy;
   static uint32_t sCookiesBehavior;
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -99,16 +99,17 @@
 #include "nsNetUtil.h"     // for NS_NewURI
 #include "nsIInputStreamChannel.h"
 #include "nsIAuthPrompt.h"
 #include "nsIAuthPrompt2.h"
 
 #include "nsIScriptSecurityManager.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
+#include "nsNullPrincipal.h"
 
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDOMElement.h"
 #include "nsFocusManager.h"
 
 // for radio group stuff
 #include "nsIDOMHTMLInputElement.h"
@@ -1826,18 +1827,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents)
 
   // Traverse all our nsCOMArrays.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnDemandBuiltInUASheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
 
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIntersectionObservers)
-
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSubImportLinks)
 
   for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]");
     cb.NoteXPCOMChild(tmp->mFrameRequestCall