Merge m-c to graphics
authorKartikaya Gupta <kgupta@mozilla.com>
Fri, 24 Feb 2017 09:11:52 -0500
changeset 345862 d4583ab7644efc567dbc412e38c32282a9cb067a
parent 345861 b545b4883b898a78f6071c9ef4cd627037b2fc08 (current diff)
parent 344696 be661bae6cb9a53935c5b87744bf68879d9ebcc5 (diff)
child 345863 6237f9ee107b9c4fafd2aa495d7a47f51d0221d3
push id38337
push userkwierso@gmail.com
push dateSat, 04 Mar 2017 01:30:14 +0000
treeherderautoland@b691557cb7a3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge 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
zc${^+1CTDyk~jQ}XU^ERZQHhOow043XKdTHZJTFod*=P${bJ*Odt)~{vMaK>GP^S}
zqboC_ehShcpeO(U01`k1rXf`c0vq}W0|00u0094*l@(PHq?MEtqqnj1w6wN1q;qsK
z+*bGCSn}FrkCJp<e-Rwx>aCBOkb91<zVw1KE5Qs3s)vzmu|~wT8j+(U#GJ^Xkak^n
zObm}CmUb^cQMvK>asTnGIqAsx@y*@s_U_&N=(w5n@c|$;qF%O{L<>%#gU>PA^F?UF
z6EIo@2ojLs+T)1P4nqPxC@2Ft^JfdZk-W?Ki)@3(6QI5!FhIhD2GG3~Q&@q(@OKF~
zhY2R&?j{OU<uRz$q5c|&xWxg|Qd2{gscWsRh1S6i8Z@$}``*D|K>`}X(LB=_{%-l5
zk=8%$fZ-OnnE--wbQSU}=GCu=*9Nd-hbKW2zyTA`kT>+>7~kvV9&c+O>uBpVo^#ji
zy;(P5<e)DCCU`lF92LYVc)cmVWx}A*JMa8?agXUO++y(c2atm(Va6vZ0{~z}1^1+K
z;GFHk6hHKk!Ni(CDK~DVUmm+ZU#@WeSCBZyrVRZEbKi737H!R~;sl9jbr!q7ffqkr
zKX(;bstjyoizo>0uOh6yHQ=*y+420+&};W^0$zTeGyzaFpH^3bODYIRX%XkQ{IqGH
z;EJ?f5mBU|ru&?QvnmhS^(nQ+jO8q;X&oj63aON*v@sQMW`S#6Sa>eK#<H<qw{w&o
zRdZW{)7caPlh%f98VyEKJ?<UC%F4$Bp{%f32Dc9=TVN7WUEhaI(F&1$&_09MAV^Wa
z*t~#XRWpJtXFEZ3k2Zw;MoG9X$}9?evawbgL31~_4Owx!#z@2u8O|NV*WMcY4fRr$
z)7ENB&#$f!qN78G%;3?$DM{^w!P9L)Rjjn-z}BA)b7;@_M^YsNSLxvnr9j4rl(LiZ
zY#u}cQ#Mvgr}~Q<y~+K@E|23yRzprdnOMH%K`xSrKDe2J0!pLx<f2t!C)*soPs{Zp
zRgh-9hZIvhrF}axM_YJLJzK|t5933&DL*5lUk1bG?@n$YTO1mVQ<|SG_usc5uv&3Z
zHQ!eqXhm4J>A-uQjA9JKG|ZR7uc<R#g$D=lQ_ro2rrj1wXg3R!KPeX&A~+^Zs&+7<
zG&w8Ul3RIhe|>QIbWB>9j7&n-1mU6SC+oV|D#U0w8l92N5T0VFI<H?2yk*`lJA*YG
zRi`tmo(+6dueQnCuVA%~!>id=u`Tt9*K9<E+dNr3N}X=%p;g3XeiL)WwU!2?y11A$
z@r{cBaF^#fF}5h`6*Pm=wm@^^T&#%rlEYZy-iUuqQgT!Mbw^(popmu><CTY|W&jld
zdmnWcip2sMBo#sM2-)GyNl>ifTJZLk<=L9M?*3Go788=MhL5x2@>6V*jb-wvy{;Oq
z8lI*s$9Yk58N)s)YrH4{)&SbKS16N%87TD2DjYR_#(WnY61fdEvoXI`w%lx&ITQ%K
zXo8n5a=U6rMT9dX^)HuGoj#Gos0ml|l8*K95<h$r<~IRu`obICBo9tS1X|buvSc{`
z4yh1E>hsA=?+$Hlro50;B9Qo_mXNHFCN`F#g@<c?rl~gHpbl0;lICB^SpiNqDCTkk
zm}=wxVxC2ZiKCxtr*1xFl5KuyVxXxb<A#W3pn%INX->XkAf-tYwL0oim($^HQ`6Ap
z+gnXAtNr_iG}pN9d$9D!zt@;ZNdY0|PJ0ep$eNi|HIw%x1+K4%Z)ZnLyIe&K{%~bS
zlvdHdW(P~2C^d)3agtmv(^?YDn~lPSr?!@v&`^&&6Y0GKl6B9L{&T!Fty@Cw;(E@a
zGNQ_Y%kE>$)lT14%)2YJxSQ(@C~+d5wf<44Aj4-{y#tSH71QHQn-;NkPA8m7__um;
zUasPFHRfda@tZ;}jYGAV*tA+orDJ9W)hgEY@3wa_*-Y`uQGD+7%(W}LZHyY9MJdw6
zLMy<#kqsF}I8-X}KyLXa&59VUA+_Ta&*sSU2^d|MJ#$l>U`*j$%Z6vCy_-ixSvZ$J
z=q8xN9Q&>%&VUTqceC^<JZILcU9r8O{||SF(;frHkMKguTgZg&C&O-;I%2#y)#Nz!
z1mzg6;tit8EBa@xCgTr&Q0wzV-0EF+S|Mt_iBo67Zhp<(-XbZVX;qWQ5r#MHh0Vcf
z35l1lBE=zowug%LTc%`tBPFT*UCa2kT6quRzWe()cBWQ)l)i4mGIPH(<AhOY^-KAZ
zTRBKPD;|>4N3kwOg6=8G1;$qrcglqjaE%O{n;0;1(tQvt-<_Kyw&u~T+qNy;L;sep
zQW%5_xlhlI4rB#fQqKpR>E)kL*G4iu-#lTD6EMXw;;uw#QI-bNP!25nk865Enz0L`
zTX)R}jhSC}0Ws6;i|V~i^4Tx$Um`+54g3}i4umpTo81)>%hbh*2jvj_P@LKmCa{uE
zTAk!bI?x^NG+gepQ)~id{0FlVLqh_>@+mgC;wFVJLu)w#?$o=GsU9?ye+(X757P;M
zhPPg1<tFe=mDYqTico?`V{P^{M8{O6nltO$>i03Md&M*MZRE+%OpSgShu=<~x&3*s
z#C&vEEOpaEvJ)%!P}}Nu|Jm0^34$4lO?X2kKI&s*J(;s)&#7sIy47w-3#WFK`$c-L
zVde92U+T=_sYy%>V_$udO%{(OT;KHm8SGE0GRh&ilxt~`&#mJ_$2U{Z<C~?lGPmQC
z(I*AX6eG3XNsQ{>S{$AN%j9zZ1Q)vFc?g<XHK7oRP5CY5__|e~-A9Ap^}R1(h*I$Y
z#z!a<B!jr5dp(x~By6BI;U+<#>JO>}dJ3PJn2Ra}D<F^u;(rIpi0dnuA7%^#!Vz0{
zidX_+VaTGyfWZ312>Exj?2cK8Gtc@vU(WikhvRuBgm2wK<);)^R$#q=$ytIfC{{YW
z$gxNruZ5@Gg+5~#Tw<60U0Aq4d8V_OkD!T(;;Ua8k^}()0X+9Ljxiu|eF0c7-w21C
z{^P?~o*3cB4GYnh4w5~qmS$z}27l<;=1VoE1!0top8z#*pu>(ubH+>tGrPfOo6ZE)
zAufpWZI8XR+E)<Y6fx=zF?@$Jb<_M+EzquOnL-x~-~Kr#YQ>j<LVcifrquo28$qPB
zL#ueesLV{I#5hG4#7Rkf>}G50fT!`-A?Gz-^+%S?O)+%7&h)^lhU<veQgOqSub+lK
zm~7W(<|pTIr5%N6qP_vP=>}F`<$WY(AQTewH0udHrjPOanGxPW>%ISaM(-tFcI+gk
zrKx!4Bq-fqmEC~t1V?6KR2N6tc;C2!<rmh?n0?xgej%o_{;HO)_}TO!Z=6|VE9qW-
zSvo}7_YUdVsxnu>)|EcXLf$ITb6OfJJfbWdWd-!b{8f#@R|s-v@}2p?Mk8R-`YsJo
z_)TL2Te+@o2TgI$gAp&A_p-RW`%v(??o06lb2E=Ov=d4z9{e#jT)6I4tFs*DhNu!#
zO5J(K=EKY8viy!Llt2SY*n*#-243oip1Mae&X;~xe8mhGxmV+!D%lGo#{?ET2|qXj
zhH7<ApPD=_!gZHz>y}A{!cyUq{5lzir`O>ydAXMv6>1+&JqE$(`I(?D<?W5^xMJUY
zG=v=L1u2e6N89`Ll~<`9D9Z!=8<Q$fJU5P}S~oXP52r%fRy{FUME8ea|Fko4b3;-E
zwqZdd;i#$mD_C*`U7va_rH(V+O6odB5-205G9!gBM~LvtA3IGl{=aMTUnw^L`!5+#
z!EdS)Ku<2(Z<0m+rkQ-en_&nMd^@rI2H~|Za%Zt>dMMa2St=O8lJ7ZwCA1W<GVOEp
zN#99nA9H?g;YQ3Vs!%y)W~#G}M}|FPgmI)%d4LVo_@%>e#$wJz1a`)fw>9>R@FV_J
zExJc^U0;*LaSQ2u-6(@M_2}P<x%4h83_7BXk#TQr4ppkZPX?8m@`XR87v#UK-ZhSM
zX8A>_UyR(H4%L5sspJlhPzwZ9%ze_4c6j-PvZ`L8>~XndV+cViCqN~dPU*B%x^F8V
zuMF0u&r_MF9#9oD_}Fg@+{WN&x#$@Qs1u<wXXyLaAAM@Q6|lO>rE3<}8ccPhsSQ6U
zyB`QgEbWLojReU+4KY&}Oy<P=;)v@?91Z|oI6vvdfOgog3S{o`77bDza3!Y{@e$Sq
z6VofuFg0%>iPEi5)|88S2#<U^ETLxT>?l%H>ZGAZp}JZ7k?H+e;QkX>sizexb};?~
zJ<=2Vp15j`cF8YN%J1GZ{VEkG9m^nMfP!Imz3jN1*u-N%r~@l#8l>;0<)0gZxq!gU
zbAb&OZCGz}@YFlrQf0zOM9WbeA^AzF$D_7($qU&`v%BTH@f~q|435!oKZsxC+vqiE
zr|g;ro^ls!uJ&FM!3g1kx{0#5p@DoU<|_Zmif{Pst{>CEk>lK}q15*sn|Mqd`K6(z
z^4mtAT=rzAz9(~SeJXSOLfG#a^aui-#0Q1`rw=;XG55d$e?P>s`v+g)pDyXwe|1T?
zA?i}7PnIq-|44Q~008Viqm7}hrJ1RdGu>||J6o67>EDA4FrzmfzY2|^;AjiOa-M;`
z0u>|8D<jf+>tsgN?yr-Q1X}x@6Tk8Koo|u@4Ty9TE{;M&C`CESg?FPZfG2=3r3q}&
zDyalDbi%Bmk2=`Cp?Kr#Rw3pyEF7OrWUU%F+`x!8UYctDe$VA#%Z5FQ?qFw6K>k*H
zkRs|N{2Tx9y3`@N#2HI)kn$0bG(@fPFfvD-7H&nAYZG%n3#%W$9!+xOEBK9<_IuhJ
z3p^!wlycmd4oui#jkaxi=M4USCgT9_xjnPf?tePot^K!Hm>vM&qyN_o0AOisXG>>q
zYYxOm-#-_jASVv{3+rF2VI?I*l>Qk7{>6~s|J*Aq9d-bKUyPQ*!U~eY!UPJAc4n5=
zrT~C@R(6V;it;*I*lag@w?ZO9Wb&Mqg%Yp?Y_~)L9Tg)Kf>d8TfQU^*#a=`)p#+kN
zV$Z)&IH8^>G6YDm{sI(JWXZuE1v^F3q#AR#%j0G`a~J=`*DAMrcH8{MXQcxW{}}`}
zAfo~050kHfvVSrh9usA5009JZ3jnbO?AbRq3ZcD<0jC3duSHv@Bfp||cfb+6eNZj(
zNB0AMa+BLNi5LM2Y0)S5uj@LP=;Iu`>RF_%^5bbOEV45UQZq!FflO*iPWLq0-p}c#
z@SG1!!919gPBRClw|}?D4`VoydZH5yFwsvSU)z~_$c%%WB|`;9?Ecncddsl^C=$2m
zj-8ZEtZ9uCDVPFj4?O^T665-}3-lUOv&fV1!!uJ_bVwwn;h1DP%Aqky$Wi;Vv$7{q
z!&3}!>@ma#jBDwbnmGH80JGUoO%x9s%myOvH4aV=1{+$N9*QMD-OtR#7uOWMV83fj
z5&YM|XLa9nSU_EgY4iGfn2jYWK=%Sjq4O(j+JGJq+0f$x=i`9a;jO1MbU<THUAeWT
zBMp>mMM-~p^PUUmC#>&1tJJf7b7biLki?Pb%qXnKZfoXQA~G~Kb721lbDSIYiVyMa
zf`^9qNzxNepDZdeSt3IqlhUj`h8zx-E)**8nw#+oKloj=-kuV%@rpQ?q5)h?h$Y~Q
z2cV!rB!KcVp`N?(fgTYDd7F-!PfyGKm<{~)QE!v^9z!GXi!~M(VQNECoVm>S)B<K9
z0Eh~JoC{!|f*|*Sw)7#k0pRyQfCVYSK^+Vj_X1JkfkX(<QT&_pFp>kS4am#DUGflF
zg4paaoCA#aSU4c)0wxVWw}6xw;Bx#aZ+}e*AOweuA)qRSITHwv;WUJ3#AE*j0TV`4
zz)6NA6&8{JMiscoQ!2x$2tCg?JJmj=aK`EYzzVnKDOw=Cqk2LRGeDK}Yt<oIgEbn+
zb3n!hqxX;BGI8M31-AD6u?O)+xDJ*dOx;7&_{ASkis+Ak$W;)<fQU9ACY0b#0)<Fe
z8IM#1Y$>o2k5`1>9x0JfI;M>RE-K2XAd?I|fsYz;P>_<Zmd{pLp$MUnUB<Vhx<v8&
z*At?wm{q~_{LCr9nT889JBm(BJuh<(_q6d8@sZ;l6+9^4IGQmQ!%qf>47Cxs5wj6{
zF_tp~b0pfZq+X+nYX#>b5_Z_J&d8dv<(C(N7m^nkcdSnk@qx2_3x~cAk6bwQkm{b6
zJ-NNQJ#rgn7t#*GO*H)Q!|mjq>8qGGye}kwY%U=Max_#;u!InmA#Hx*oEV36o3y(i
z3DQ%z+>m{Ne_N=AB##J>JPv6OsiK4#2^(bq384+)6?rz%Hc>Z0(zuujyd#gM%#M&P
zSuWu&d9Pv~l?D|k#Vv{&^%8|Mm5!o&Nw_+T%A<--Ra&h^zPpHn#9cX(-nc|W^^l@g
zu}Yz{%30__6O-z?7^e!Sut%d~)IHddI10^h_<``gVkE@}g*k<>N^pg|sdD?0%aYi+
z<~awfFmnmmoRVd6lbuCS6D4Y7WVTe3RE|{4gaaxiZa(%r$m!W5)4Soj%R9F!t*X3g
z&pFvL+cI@U?ecM%N7>!7)Y8VX*wRip^ODcf;!<n5TlwyyfMuFxu+`t+m-8Hzcg}D&
zv6DF0S&5usCtEAQtkW#SEb125i#T&xD+??4ixUeK3tWXyDw#D``PmiS!XLrUMR2O4
zvf_D-!Y4&D#-YCklM-giOdc6aoLODITu3{fI)XZwJx4qhJwrUNJYOE6-f6x_zHr~W
z-<83+zzM;@z^lPqVedj|22loeL%%|6W1?e>qS-K9FnO8OnBf>^*lk&Rtc2Jc*fq@-
z3>>FXj3=06nYV2`*J0Qi*`*mP8L^qdrg5jhjytErXy?%6(1fDEqp)R=7L(EktxaRK
z7HETLp=mp5ay2nEhP7=P&s*OuZ0xdaw=H}|LyeFvlr0vGA1bTUX;)iDo;P&vb#~h;
zPh4kxY|U*)TQ`l}7IS{%8};`0tbPXt6$XvvPa7`G_c6}buiSx0jr=mUG0S!n;`ZRI
zcI7&LTv_key!2jsLq{)>&XA6pEc6(8czwV+iD^G?chtGiEw+Ve6>06ZRdUmHi}ecn
z0D<>`&k;`=4K6J=uQ(B$HP>6wBdUd~U9~oUmU^bN8`_-h9P*kAJ3u~CzH-`CpJiWf
zU3Qz8-LVVVrSxm^i~nK!!3NR-+VVg4FBcf?`xD?JSd<4QxGV@CI2|b3&r4`8VKDX_
z-}wN00)65Z5;*5Sca{G#5IvX@;)5Rx1rJ>d<%eO2tcWn*>)&B89+(1FOVmv?NpvHU
zE+SdrS%_MQT##9)Kk7c3JSt&aZKOV;7r7XYJmfNJd{}sxc~FY}PP5f+&(qJB&$0hb
zbSUZ(MMtxuk=671iZo1`MJkRoAHf(w8*#&#p9MbixZq#hEM3}WeNA_boC9G8VV8I_
zL4P7o(oXh=oL9<Kb|;4?{UApv;a;T=O$9oq^u2VYxJM3Jb~E>xl9#NT7bG<y%|S&1
zu>|UashRnKDXej-F|x5}GAs2;hmG_X60523dQ?N_Fw6SvlK+rMgC0}HVTy9n0~tHl
zi$soN4uhRR`})1?B>BD1L$6+dR=@7NcAj=!i-B|T$;7+rTt#uEr}{-LofdD;h~0ER
zb8xezl^;bD9SyyAJDr)Xe1h71MnV0P?}c_0YNY5eJ6oG|*=G0##TrMO<#kDgEWWfm
zDY|sEG_};O-LY-Y>z`@$Kl(l!iA|<;Q~B1jYEx?aD;Jwvt+*8GbhGsMn|KY~P7uc8
z<FTpSsh3>}hU$u~QFYP{(ps20>$;{2&CTaNE>cetG3v5j)n->E8>^ijowb*DYl~{7
zs_I%VwzUQ~)1JNWGvI}xkv?fYoF8~>V(ia;)9)Sal7;0>XM?``zGng9AU@Nv)6=vW
zw5HYel|?T11_S+*yY7CA+auPMcBQSUc)(#KVR(L-uUAilf?-~9b+Ju8=C8Uo5Y2@}
z$mcpcoXdX}NUvfq@c6LYn_5SUOAoy!Z(i^hdDWbBj@;Lw1|kZObLEb6v-tkFoS&Wj
zT{P~N_Poi8;B~5|@3OnKo?Cx7@t(~-d%5KI*m?S_hn_)?)icq(?D%V6?$qa-cL?!;
z&jpSP&K-sm)<Ms$dySdH?tKvX@n=!JyEdm2&Hid(>@#8iLUGk@N1~0ed%W}QRPDX`
zq}9oGc1zO<-KX_$&O&$ON5%`bFUwcG?l}Q3-#IVWtwDbfxA9gzIlt-8qi@$K;hW)b
z@Qla<@uv7FnGZQVzuv3+r{qdfdg(Af^^eZS)ZcZtUiVKX22^g=Z=J`_#oA2$vfj0y
zg=^Cr=e5T#b<A2bz0@v6Kc_v~3!9(LG&f}C_M7?}YW_6e(>ILAyX(?u`CMO+m*9tw
zqA49t0DxpnQbbV2J!{#^!~4&2^K;|#M~^ul`2$X(0MY<Lya$>ff(RmQ0V-;3%W_5a
z%7RvP%f^b<=Em=<Cr|C_YT<e+I8jlA1dm_L0RoT#@g6hd>7{NrPhSqeT9r!`FTT62
z_g?b8<{Rd<=C$3S=C$ar`UtWRh(LMr?hl8xRzJ5hKczzcas-(`79+hMsxbgw&OZM9
zhoi|<ZZ4o90Eh%W5RLXPpCJ*M-CI7mS`WCdd<u<U48Q<T0K9)P5U-bDJb;e;Xo*Z~
z0UZ^_uJ>yjiy4C%QP^>AmI=&oF^`!8FDchyLO&6K0K*^GWHsQzPH<zZ7~|ZPkl#?0
z2q*Yvncw(C8#`WM+22GUOCF&SY6{Q-HD?*{Xa_$tgT?R7cJ$YjozKT!G_R#FZiNNG
zDv)rQlUxAQC}GB)0pavRW^tt#W_z};tO0PLbbHHY@Tt<aXft;8L|DOTu-gjj^K_9=
zmjeh%^GBQ_E!24nqMxdUdAlTZN}+~ZgZ-1v$b>cjB*IX#APzSPedFwbiUkR?!Xisi
zoR-dS3r_->VZ_GlxR}aajn)`(bVG?n$aZ+rgnMUtT8r&3>R&<!Yz8m<J$J$Ih|Vu)
zT!%fK&S`IYB>X21l<5IzkoC5h5RhttSSUixE){c^Lc<7lHsa2VLvAL;U|nQtPZ;OG
zAWsFN5l=o`=q!8!uP9F>pFxJQMni%1T_|g{^8I{XtLKC+w17Z^@iQ>dn+660V>1%a
zL^DSL>_jSN9w#U&4<e)*!%>Vcu$jPn3e92A%0K`xV^Dzq&C9<>%a^_#jDuH%gklV4
zv;dksry?_g+`bN00~HWi$9vOOI4v?fH_?feiCy_gOYRy<tk@4=nU)k9GCj_VyrJ*C
zxDJEE<*Z_}+gv8nQJ^yVmPYMC00bssOy$C-p_K*6{iVYQFtVdAC`>WvN5rql%n4#Y
z_FuUa(4K?OgYXAez$Uya5keQoYD_Ik9wN$6m<wKDEl(Cpd`k1twr}b5n`@3qqP~&7
z;h#H1Pvy0Z>-sWKf4Ix0ah^6yCM#gXzn6N*ZM#U}zQ$o~V6ko7pskl^OUI>?EJ3o)
zIB~?m2NnQ<2bP3zk>fnPiVDa=%sY_caHk3_ju|OSgEzs5kH8o*KqpJxf!77zR`na6
zI8O>61*ie?08)mhaBP}TW@rZ4B%Wii%IITiHd}wxcCa;gcq^ZtZY+k8vu%D8p0L`@
zOw-X8?xbMo;9;rkQMMiC+<foNG^vzsHQ2=6h6FOOYy%VBn%m!*1MLMsK=m1cjDu?2
zrLTW80|)??{)}i^E3$2C*8-9h(f*)C;PE5`c;XsS{aQb06&aO?Q8u9U00JO76ANZm
zlW503!-rEt2opxHHXle)r%iqqw=G>C)ea4=v3Rvo&v-ox2Ug0CJP8eL3!?JL%v*mv
zXY4OlcDJ4E?!pm+iv1C!Yr0gH@$CM#?zmr3gb5)iE%a(hXG0o*^H2(Sf|oU+^ZbT#
zAKneP))PR;<Kv42)&MvoSTyu`Ke4dsei-XmYwRl`l}Ln&u+h{7bImWhVmt+~1EB&X
z0SO=;#Tv-HtT|Gs!w7-;i(;7RtkR^EQP5P>oNjj;cn_qL(FBy;qE0mo(Jo63ea?l;
zz~xG@hQfZQPcv6=?A@@j+g&Wd<MM_Ux{$~JNtErLpF`xZ-t))jDBSA>HW&dlI54R*
zg<vp-oPbWrJ6sNeye<cZ1}clw4g_8U3^6L$V0!L)<5bqRfke4LM7KqdJc6#EmaPK7
zU>KnMWdV2u2y=`DJiM8I)^o!N)57aU6J?=LTR4@;*`4M?pV(PlcPbUZh2fw4S)&xl
z3_PXi4=UAhoJ2kxf114E;&9k?ApA)kfnHohns#}fcE1eu-Tu^_D)e!_@B>bz0(5L|
zznm0x2oz4+@Wv*@_j{Dr(zPS~FEA)@K9hN-Kve))-5@F~uwOfJkLD?a@~y&g$VDa?
z&p0Nq(}Gkp!ul6p1PgsQX0ccD^Kjpzrx^_n&z0SZz3aXtpNm=bcT3Up^LF4L)+{EF
zj+@#$f?HiEEy1~$K2NvBF+=4JyVI_0H~rN5)9L~4BA;&|f9f==DJH1ZSjB%M|2!=n
z?y85DLIuFV`tz)w=QjIK)&QUTK+0l=ZP_E3H9;g;jpPq(1)KuGZVNt9rm);@!GM%N
z)Nv!UYV>`m8OztfSNBkCnLZ?--UJFTC;A2@!3jM*CpxN5UWZC!+3Bs_PdWLTt4?<4
zS~)#HY#MFL@!T!Hm~0Q(9Ax@=Wnd96Dv%Wr6dRC%#@*YFa9?;A4C0H2lB0eLgv)1n
zLaBbR?V={-n^PhzVSGlEGe7}D8ps`Aaxo)7gFyC2e<VnxDWHL$6!$wVI>TsngKX34
z#S`3?ib9}*d9(VT2|sb~_Rnm{o=#`LEG>bQ4Bd5n3G6ERvoK7Oq(ufdls2whbKkp0
zvR-!@|Fg?-$C~OA@c6XA)ChF&#+k)vO5GH%8lT;~6mqG-MJ)rf;Fi>DlG%2Dp`o0h
zgD?WfOl-2{bGw~03OgJ5HAQ_6bSTf(`F{iDpb*S~>#%}xrl?RzW5D$F9AW(4i8)+r
zSqXs39ZY=x+OyFPIIk@jHv@bllwD;14aE1sLwOcV0BDfP3NQ#;Vr8bcPwe2G>L@4G
zpPW&<&2G0F>^LnSJILoK{*tw5@^M2gk<W-;hDX`U5NF^2dOv&}$vb@g5`F<ff^-JT
z2RO~4Bg*$amd;#Xm$>C)PvNNIllhOEgaG>blX;oKs?s}tpFaY1GcF7o-u7ed8M0&D
zS8<%V5M5nEGk97d%iWv-oWUhwt#gRWiKO&=`N#NgD}|)F<opiPn+X6i1Pi2jEQ1B`
zjwU=^{2T8><#Hb*9oNb2FH<|LZ#CesYF=D05c3e&t^3=}nYXtfF9ySI=0+3h*CI!2
zlpOA-pc~j;|AgGr>z%Yn*w5j}kAGGPw^Tz10t(rA1dJ>RZKbNu0^XdqmWJ8;ukA<Y
zKhOE6JJKaWJHyn8`9=`NXb!H4!`vTYUB`}X1&|HO0hzEUF}@9kjO6rK3_NB6+3Nuy
zm}mk0>+4l+uB2wBCB{KsbvXPK-s`A`m#O^E0Y}3%@yrR9cae4_E)hu*#DKQPg7!48
z;is|bnPJ>roltW>PY`cGWY%U@4y{H1hAqX*mLjW*=bqg?hti+^RqVO-OEeg=I*9Qq
zvLOY3v!E2_29Cv)1m58Ls@Mr^;BqYZi^bWhyb>S?S<)@?fIvfr&5xvq)L$%SFz##i
zx8t4`tgP_~W8`5g^3H8C1c3%KCULjXI4TL8rAgL6kryAk6>eYqe^Ed1K$<?DSxX~`
z+!Q8N{Y%6Rr3|LZq;x#qirx1Tc8!jkBGOnd0viWIKMk$7+r!ce3huHCeM;q*LG!$1
zI2!WWf92nk1zji!tned#cyWAe5K@}*XR)xZA(+59$)K2(hQhV7`H^U%*so6_D*LWQ
z^U>1s*647Jr40O@($KK`U^Bzz3tTb(2`BD!4M^+%Nyhe$j3=0wrP;IiJzub^Vo_=4
z9#<$R9np(HXF-*XM&)ZigLl}TEewQImxQkES+YOB+YJ}?XKrOD<nW1{(Pse^0W=L$
ziScr#Q3d(s(fur6pMSpVX0Km%R4!|6X%$r`0>3*)@Z%yv-gizwJQw|7Lq?UyQOM!E
zXsg~#CqCf_Z5rNgT?P=JZPWK#zL+94?hIaOu|agyGb-{u-U6IVCw$=3Q6~>mWo~T%
zLeC5H#_txWtn#AKo&}Uh%u}pqQ8O!beq(u&g{AOy$2<!*PUeo4dOBDmNeS}HaYR&D
zyZ+9Ea0S1SFu@I%Og<-6n!W?TR*5<XV4hH@A^-(~soHEq43d^o;A+DN&1beGpu}7j
zk3DGbt+9Aj79XCmKfX-0SjN36@Swg!)%(M0)ZjE$08J0M#M*ieRa#u>rH>8NS9E82
z`xJvzXf{_PD3@T#)tQ^gUgO$LWMvk)t(K5^c(ZZ|wT>%raJ1UQuHHj-n2igP|EO$0
zK*Z2EHaVl1tjl3&dwhhCb8qW$uJJiUEZrE^-A3Z=t?1FFt_BLc!S>hRZkeH^43>Y(
zn-kCD((d%^TvRI(f-V(WNeU7vKGLxMciWG7@5nc=m*PL0$M*$0e2|2IU_;HB6f-2C
zsoH9HKwaIr_WJh2_F!YiwyB3K3)M&KeBhxvo0}`}cQlmkZ&F-9EI!o2vJ-rT<zlt`
z@1Cub5tNtj$E)*t^(*V~rnOlsY8h^8UagAJirTikV}q7al#q}>?hTC(23CwQdp;tN
zSq`ju7HtLGXY6VSzwOUVFLl%&-(k6n@8&`y%m}nh<+w?<snheJk=9>#yN6kA&!Jv>
zuIu{^XhKq8q?n*U#TaZ+u_Gm}?moU<t!AV|-x%RgFrfr7eRD$n6zAA-l@IiK!5`S)
z-e+zt(K-h-lb)OgTwVDk#8i*>R#47sP*1?8QH=<|2s5DZhvH&gZrZ(hslY%=lk|KF
zoGKy{A4S)7J1uqtBl|10+wb3Qz6}qjkN6}M@UC&nMDmcG+p-uGJU{vGCZVE>YdL31
zB|B)^_!>?x5FXmdt2mlm)-&a#ZdVQMPg5enIr5m`xr?FEh1HN~-@t`4mBE-v;lr{L
z<Ky84i6J4Bs9Uz=JulVT1cxxP?0tRfvl7aJtE^qG_ZJ1V{K3+6S;1Q(t1zo+rl}^=
zdOY-|LkGZLOGdE1Mp<x-pAmmk?w^7B$qaT4;q<ugPCI(>sW>lh>i1iB*M^eh*5+HN
z<+I)PZU6XfYoEcY?|dUO{w;X|^T;vE@C>1%q}!{SvJRzP-YdEyhX-<WjOVr1kkY&x
zLJd+j`y%IbIq3+DxzNXaZqd@<pUiT0q;$a``!&t+tjVNc?sUj++XH0VioIY0S<t90
zxtJIRro_Z!%RV6;Wtc%FDV4?83WNZYf0$yr+Fx*ym5>nO47%7xpTG62cw!pY>zR^p
z!XiLA&XfuoOqo>gyNqUVWBI6QV`lK(r0l2J>=(|yV28-x{+rw!X9@+E)-}O2XD@xx
zj;(uDF2^|bW1jmkj@3_}I{mg}GAGfNwoJwZ^)DO;7;x9ulF}+pEHbJNPTJSi?h8-%
zci`~@`9~F@XU)BbkHra2QyM!F^F}oPNcQRbxG}}Jj3`Yq$05G}Xxc*X?2UX43ZR^%
zU>-C)Q9Adp_#2JJU7Lu9B~%t0bpR+0&hnL1qt@XzZZ$Q&FZsgfDuq2u`bI-w4U|92
zT*3NKlzD^sQ%&SBtGKfCxlvB&0*(Gg(E%Kff(OndJNh8k3CegOn$ON0>HW=Qi~4it
zqyBobx`uB1M%{763sFE;Z^nMw?!{AL%;Db&N_39|!BD2Ps^-;&*XR1PppQuhu4>H8
zNw&ON%IKK}A{wt_OC2s3nyIpMne?>$<iKsi{_pPPcqPO4zjxumNo3OIj-KEcSxp>#
zAixMQBZkdb;X?yeQ^~=yA>Fh$-7TD34ddbiLRC{h=MV^yKyla@i4f6KgOIJH8@y%I
zjE5KdNrx><$4;_tT<fv}j&A5&FDL?&af7Xl`kh7gd>>{p2-W4v#dfDzlWq6s<i?gq
z{7d2Mw(`i)N|=yKqb)7fzPyce^6K4tw3eH%F#~^Yy}s;MIOCnnscL-pQZ+bSXe59a
z{x*7LpaOuJ++4|MfxTCjX2tM{PCI}sC*XA%$XkD%4Ly}&QD8hZc>&jN7(8Tt<x$0`
zURK=;G~kpt`Y(QPn2I3A&>f9{#4Vdzx7YW2m>JgQYJ<pa;LeOxC92U}wCsf>S&m^{
ziQZ_xv3MQ0bafbXuh>y06Mh&JnWp$rH@mzXeg(LfrbEiqQe=8>lcLY&=qsiNaJ~_{
zc;y7N@y05bW+)&{kCHyrfB>rW8WDq{APk4XnSwtZEQCl<u{73Jp5fxf;-vyb5{Hok
z5u%3o;XxG?l4HX8`%4l5kZP5~BtVC=wboY-+`Ves+Pwkmb;#ok%ohMY8f)iae?+i}
zP=d-9ysyw2FbH%|ef@bJ=ZTmZEaIlk{*flgZbjuttI^eh4->={NbZGF=K?Qd#F*JO
z>`u=g=?C}<1y6>>;gW}yi3ro6>u#^NRkWE*VY#rYT*u)3kRyP<1__Y=zAJaV)vRyY
zg2++buOVY3Aq10XM#D>w*Y#T8Fi~i_M7B)Sc?%gxLW%?hhN?jI?hZ9hT)3Y74pOcN
z6W6ts9djPnUE=Hyco`2Pdk@0{f4s=MI`5J}$;x*Mc@gu|nh^AW5qVH!{g$5dH5yCY
zg@?<Mf{IK};81H6ORWN(0{ZR1VdV3>P2%A-H9V0>C5s&%Jl+J%GRhC2zY(~f0{x2b
z{kZx;i)X=1hGa-kv}ZXpb%f@>9YYa-s{m2LAentDx?YBjS`4lxq%xHKQ}%7NubUIC
zD$6~>2jcuXu~RDK1o_}{38KC9f!aMz@Kg46<_oX&r<a$}wZ1oBZrq)5yytSgRW%%5
zZ?8GmHst5$KL_L<X^CM-_(btMEoOaZ;7m+VOS4P(vj%R8*YUY=49{Bxa3yx|s7@jn
zO9@89bC$emELF(896T6ap<ad;&IPWAPp8&#hpFsYao8!oA*R4o3E|L$O(g8opOnMM
zLGdg$0DE^D_m6B?dydOAW-BV-e>x{hHkZ5(HJMD#kNSGlpvIc^qm~P)iLl6=7PzJB
z%}uA<y`75VoNwjouoqtBJ&|5Okt4?jLj%fMA)-B^)dqBY19EP1oe!{+970A-b}E~g
zp>@CAPsaO>oA^+HqimIol_bafd7bguQF9r^-9di#po4`szFZB(P^<YDf`0Ftln5~t
z0w|>9B$rpTTg`-_f1Lq%noL*YGssjfGc;SJDmW40RpMlg=fm9mc2txxpNSYF<Kz(q
z|J4`+_8_m<cK=v;N&w@K|BXR;lgTh(m;wA0=P4p7FOG-+RFEl3u-!w-<Va=4Rj0>C
zV(mtY<CPK%J8C{-NTJIFPPBV40AFS|s5Y7_=B5TWTH_gs=V`At@y;X9D2R(3`%4}G
z08oR4O$Pcv#0pTUC6UDzbY6RuJw%N9^7S7&G=Fq|B~wXkXb^FkQoVop^wnz>1PBH6
zXBFv&Cs7N$st+;vTw-!!_z#O|h2o-WD&zT8?B{b=F1V6Th<^^H;otPni?HFXju-@K
zhDV9B6YN@#knV0oGZx+>z@`%*{S0;bm=OxZ3y@%qL@2{bwPGB6wx@biTlXt(trsOJ
zkugUBMWW%?+@GK){8C^Ppz}9laE}Ft8!fbsptzRHYWp}b{;u=&T?G@XJNI}<ztwqE
zz~PUo9eij?#MRbn!;4~cb;dIygaQ_n<ioDKv(u{rEVAsoa<1RNN9gmsYZ#Z&zXH2R
z1tvT86`%x=2FJ@hh9pA(d@=xWtNvG@Fw(;3)x-%{n<AzBr!NsZ{?Y8>tKP*vWk=ci
z_?=gQ1-i4hJyV5Uv|o^yVsYV*t47C<^9Beq%7Wp4EkDvFeZI#NrpBjN;PJQ~>DCtl
zajfJVKS79@ZsEyUL4iQPISs<#zXLvR8^1cfQ==5HQtEcC`=%f!w2dL@B~BK>jtn}{
zXeQl4C=%b#S9h>|-_^^xQ*;!!xP@B7jsYPHTM2J^etMFI;D&kAN6_I)k^@1gL8yaQ
z4*e%rd;|MxL`o+Zy8ihF0G&W1oFPJ_6Gwg_Z<0TtkIFzACGHGL8R6t8CN=)XTLvb*
zE1@;2yM6J|EPU_@Ilj!_*A=wFZ1~pq=4iu2@$gF;KWRz&T!kDyXGu~*h^J1h+6?5V
zuI;q=c*`07VxWU{odF#1)^G9(x<P?OFe>)tE;{E57fV@qKiH$2in0RueoAXWN<X&+
z-%89}WN`wP9B?gw0hK-(cC)Rm0A6`-{|@|FVGjy{I>dImA_Z)oe6pf}1KWBxzmr>*
zeW`kXSqg?=ED3w}y)sQm#&`?w28v>XdW^a?C{?eDdaoZK_m9su%XL)=`B?*_FZ_4(
znY46&Kv13m5iIg4lFrye6SvuEvMv()0+$<E%Tw#VyvW=d%5nOYkc2E)BsAt|9LT%~
zlK%x$g1EyP%`e7b1vP;e@jV-61k;FkLn5OzYr?uniH15!HOoZbD~X2!J)hZAHGT#P
za!nOZ9B`HZl3OkWc#s4On2wVD!-Ar~K}$h-eR|)YQ~kNc)v$s<V2vxZE&B^y=h=HZ
zWf?Nh{B|HzPyzFo=3o+lN_aj-K>2T&nOp;?3sS>wUD?I39DSRf*OnPd6k+_lzBy+B
z@{v8GJoiB4lr7L$>o{VhFupOkmpe$$2Nn(7Uo#B(FK@rCSuvSPVcsPA0zpYINJyAb
zm`5pr2whrTnby|lVO3BN`#Amyk1(tF{sUNiLQbS6b4h`_Js$8EltM$4mBA4vTqziN
zrre=CeOI(cqq)&_$YR}hP_~4;?9uq)s0I5@PZ#~^g}3;cAdU&CgfLdAkf0$|i9LRo
zPV6{F-1=}3qnQy#K$_vNnLop%=*E_&L1X*_ThXw_fsk>Ur^klM53g&4A)2c&gE^^g
z#L6y)U*H80EReu!N1<QZgN!b^@LJzH{)dIX0z%LYcM#CUM2sP=U?9QFpcZ5oGD+dK
z>r^i^+CJ%*U$z{&3ybT+KHj~?%^C|EJE3WDuhtr>e+^DZJB&`V6Cz2%Tzd4)vAzcK
zRk>fu>Np*lj!jMqkys3%Uye%G)*RISjIwrl?Gm=Tk0;(hbo2J2i)0+Mbzwj=V51`q
zX4n!?v%`nHwNudC6W1^YGYK-}2cw=HGj6z+CBU=R%zE{HJQBc{Yv*VI8a5E2DMW;6
z%REZ4YHz|7$NeSIfZEnUjB$l0VF|T(FrRDX2Ta*;*(4Z<>_d#=*+S>%qLg>J40Ri;
zHv<y>Eb6V_K2KDFIiJ~MY}D{VFg8`eoN3aL7?E9jovs>;yFccCAi@j@h}3DZSg&<}
z42JzFq~J&ISAY?M<iG)c#^DI$t4%$b<;M5(@?qWf{Loz;y}Cmc)@rnDqnZB3`*Ef-
zaFD7~1;!C$0kV)1iU$=8i1#PLm6HZWfiEb%2v=Kfy8ng(v*Dx*Ek`MJ&*JE)#On2_
zR?7mn$Ld1^<~<QF;PXE<{-vQAvQ}n(`%YhRd*9f3dtZW9<%X0@W>{q-UY*V`mK;2y
zOfKj4^lrfDe0>Eguu5ycaRupa%wXJC$lnXk=Lg7aRqsD*0+xXcEx=Gz4Ct!T0Tp(D
z%m*R>4RVmd(TB?1mWqkF((pVgKHtyfzZECWmw(J8LmqwCn*Ym=*dT%cNjYSM2D#E-
zBs}0B2nR+Tj+qas?5{kY#LZ`O=PgylZ7?p^mvG#-QxU}BTmV--Dw$>ZLJ00B&?iUo
zmN@;Zf)D0xfNiMtQpiY^T<-H3pqBefsqnl=ZuxlMeT!>9Aq>C6Si+$u@t3E#I{N*C
zqd*3DJ`{;bqI($yy>MJBypS=83^H3L?0R~7IOzd_pu&{oaVq3|-N5a=V^dR0Lb}3d
zwH=L<9^X%5suqnn6Ov`l5aZ<rhA@Lcn3<^rBm;}Vo)QT9ARzcpgpGCa;n#&8_4nqD
zUdfaE6=7{H?q;ry|BY@)-;Up9;Q~1DUjR1PoB(gXKrDEMVW273P>z4d1}>h>{Ab9f
zTF>K21Qq!5)&`mD)pk^6uOp_BBJ;XJ9w1YA@czR$^r+Pjc{s!)9_jS#Nr>H<<MJ73
z!i!)^k%@&%SFP)9h6@Fjs&5F2ShkkEj9$z2FzREarhQ+m1IqMxj&PCD+!w<EnE`SR
ze89-@4*a{(f7%se({0s2fBE-G=29-c>9aY~OEv(B5qyaM8Opb*SctH^ZI1*ao6May
zV4|DL`&uU5>|yI|Y!*MO%*tc{sJpxw%$SNA2rNxW$QZYyCVv+_c=K$}xVgE@bW`rH
zndyK^WQkmch`G?_deUbAC@rEo26F|HzX%7)q9nybidBuStKG#}>W;N`tE-9nbh#4N
zM)L3*sYBXb(G{cxNK7rL8AwMl1TXn+Uy`b;MFG9DRs^z0bS%jMh|FBCb+QTwIwFG6
zTt7`*;BIE)=k?9DwxZS)`GvM92a+HWT|#y?+%$qj^CseHTu?Z|uVA3cY@QI&A{<)|
zCZ##&4j^ncyO9B~2LjSu+KFNn`}QgWvn&qPxaJG?{Mm~TLyRJ9tdkQstsRwCze?C=
z2I3M>5=8LGx&RzOX-7nuc>BQxtuU^~Lij*X6vm+8^duC(k60-MGeI!IZHS7k;QA`P
zE(5*SQRdCW_qRV;u@DQ^aOjnG-!BYaM-DXo1^^qigV5sH-%3B;j`QJC?Ly0S$}^w*
z*Uiy5o=rZprO%LuV_jhasL+2@r7}=I;Bfns<oV$T=jtu`d8V?MFms&400MD8`|Hrx
zK6c7vN!BK6YJn>L<tQkOFuH<NK)Q&a&;YC=34)X<oY~_ae5-`Fs(;GEELSx-a;Yfr
zI4W$Vfuo?Pgac>eJjh^r3_)ot?iJftK&$Uh*z5d@t@#8+-gwV-MyDKm*9KhcsBHPW
z?tA>kMJBP)%Xo)Xi7HGGKS~JsaID+zW^~M^+ummDR`}ha7!gv0;bsjL&YJpYsy-h?
zGkGljGON5*I$PvHz5iTZFm9-B7zoTSMiyzD3B0gqASp>ulEL7>_od%gyC4MlAin5A
z3I4lh_JPQI!B7Gcpu~hlZM+zooS?+iVFoWbzj<D7?dl#|HVs<W!C(e$*_CV%7b}#;
znT}_6%A9UyDo&#n2d(8y2UD(8Pp@7M=EMBL-M5Brer|E#Zfu2WTtMO2)G8g2#ZN2M
zdq3Rr*QYu}vP}_7HjyB~u;=ER&_6P08MItDltPV-+94>1NhzK#hrtsKAR)`-4MX|3
z0U~=A{(Fpb3(M_4B43mh6xcGXqvy91m3D&6P#wafw+|BzcU91gmIoGF9v8H2SN{|s
z=0akcmQqw^*^+OP`M_E?UKcI7Qd_=<;Ugtts4!u}4OX>%*FF%pfBkwQ_s`G`)}t@#
zJZGuzKVEce?be!_+pkP6lrPnqR<y}yN7rJuPtTwU5w+u95<;v2koW^T&v$7SHlnk0
z7zcWMo1mZL_jZ~9Vaa_0=VsR0_lwC|{?ExU-}vVVYP<4DDLO<_u;J75i;)&x?%TQ8
z(Ynu0_J?Fk<Kd~qz`i;+HJtcZXKYdlMo7kT5ybX#d-cJ5AD2=wg<N<i`n1?D2&>x@
z^3}tR@fZL6*O&_Pzg@MlOYk@qay&1e(E6>IGuLIUs%!hn5Q>{UXjc1yNgMOA;8|vX
z<N#+8#OSBJ>A?_+;J^WLrv;FU_Zt8JK>`vM%wqv{zUS?_qV*dA=54CwiuYgyOdl~4
zunpv0XNeD2IoNi%Ir$pHnyK=6FOA<ba%_1T$6bZQ)<PZTfK@~6HlEVMDZkx&(W$O)
z*VqBG<?%9@oQ^R`sTnvJ>uNJmC3UIQS^uCO$L}EBXX68ni;Jc5Df4o&-jVZ9#r7&L
zlYLo790<86PBk7k*(+(ppdlI2!iIQi`FVIijs7`+etY&Vq07^L%-QU)OQ-47rt0$e
zLJ>D<`CF(kqr-E$*b+)DPzHf#t{Il9?Tp^&=h&vEWw5sEZX`5RAuimK#J1y&iYf;i
zJ;)48OD{CR{q%$XeUskrVNV7#wpUbF7t7?*`l1p%W|m^cpe%qNSWo~!5KF*h@RKVS
z=OafHMF1&uhjAncOY?Qyoci7NH~<}n?Er=ffny78CYKXEs`g16e?Jzd@6M3Hu?7Nf
zxlqE~*(IMbO<$Ymi#Si^ULu8^t(Nu$ZD+cXw-n71b<8^0%o^5OZMF7!>7IEq*E8{J
zZig2O`7h_=>(+fc5CMb1EOL2yM}>u*oI{a`-aZz)VgFtO;V@#k$y4l=cww`Pe9Ve2
zYcDerH0;?p`<fWFiua4udY2b7!99bRE~yf&TLskO1;S%6VF-j}37prB^ub)d>bXJ_
zW@#G1QmMxsJ(d25jT07&Y>8xq5P-}e0#E_Xa7Drp?xF)xOuL{&k3rQS_C0^;Zb|Fq
z^#fNh4>-vmd&SlD$|r~dtE&r!8xQqiIGL=S_p|l><Lb#OO7ipMLz64rvuEQ3N+cm6
zsYkR=YQ!)M;hltLHryTF2>O?6y>nj(Eyjy;P9H1~4%aj-pi@EVuOTh%5EGi{)DnRR
z!u}3_UPVVHtA2x`zz4n}-s=D$3zk7<Dv|GTMggQ56{rlz9ypqA+2tM-(vX42J`ap9
z7(qHq;4%$r1$vKWj$nxDk~7aZuob-7xrXF;GX7^B+ll|47={2fkD(9BJ1u=G1a%P*
zoDepyp~LdaQq;|8@R<t7AO7nE#!!D{>$_GWF&xoeP!S8z2PA|hFB<#JCpY2ysv-0p
zNA>el@L5Yg_5&n?NkBd4b({as-y9$*DkoASWDxk@2kim>b<iI0|0AS-*v-_4*3{jZ
z*3i+}(#+7<*@<4^f8e$gXnx1{{%bAJf8w_P8=7Ea7Bj;QAIN|by5HSf(1|!KqKE|E
zM4`tJ0M2OpM}X5vyO?CJSN+h`-jRF!g=`ioEtqG~#Vu78E4iuO@)2=^DPbK$i8Lna
zZ#ck@?K-{k*SU}dHiSud`Ax>DgR_2TjH(&q&iQ7WO^uZ=j6uDoi_oax?7jA%6*BT+
zM`vA8mb7FMD2KeTxvb%uhy39W`op3>pPS}j%Q-HT;jvt*c09QvmRan*-!dM6ATzF*
zL*9oZQ`t@0*VZDK$uK{N|LJfY{lVGqe<Y8882Nt#symrM&&bS5(@@e%EyGIFQq4@w
zHYzbJG4I{GFfO}{%gEEx%|FRbP0D~URiP{dEj;-|O>{FMWj8lY2I*`u-zVLJ*x0lO
z^5VcU{=xQa3*q{JNbYR+Kr!X=z-}{5$AU0xPY>n5Tyt~)0w_oW0i%HY-zfV3CVwEn
z|ADFhFAISG8)5QqB7^-8yvcv6{wGG`--HkMZ^XvGwf^^n`~T>l?+1kQ{_pAcKVAOM
t1pK$lZM^?`I{v4H|BUFrEtK>9uklro2KyJX1OWce<@(QSMfm?E{Xc0E5RCu;
--- 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
zc${@tXEYpI7agNSZ&5}Ni9UkSYcOhL^cp=z86{>!uM<R#P7tFDBLs;Wj201z-kA`+
zMhl4+5An(S)>mHEd*`n6<J@)6S$p5J|7?9NLLvqL06+@(SR)QI6-BllPyzrZv;e@b
zTOD;{Wud#e8W5y=po^OuT-ehKK6@`fCXIqwI<BWpq6QMK1<&;34d)NypGrt`=wm{F
z<iX+*Dk(w^6ou7K(fm^?li8K2Ja|Kasao`SYeUfP@yBEQaq!T7Ye;MRvEZ)ky6n1N
z9%1rSyzN0MTn@g~U!c06EtV|@MHrEUL!C83uWQ`u0ut<xjsoJ9(xDiK5=5x>XQJer
z0T@~lQp%_>;c-fV3}AnVYZ&M_L9=o`7y3{i#1=2EyieG2C6s^~&qXBDjGMk8L8|5l
z9Qn|P3B6MU*dPRRS)I|8W#tjPOV(^$Li!biig@_GmtSV5<2_R2aFP#_xCUhhK^H#G
zTo|VwofUE;ow)41R>=u=!OKUT*_2^lVls$DLZUQjmKgTW7JH4d7QTWHRWG7jH#tmw
ziKL;)aV$bK1tWYSr%4rO>cR+01c$&Gbr@8F&Gv#$zH`X`B2S+0tp|X*19M0^=MQ0Z
zy8W(yYL0mTcfW2|GOP@Jan51t{je(PngxjVk37yR4GpJ^tdJlRNRZy^b}UhH2WLp7
zd`t$33Q6tgc8C+;X~&JMXroH;Swe&amVz+clV+X>7O%5`a;@E-q)Wj2S&mboNL}i#
z%q;gkOG>srGp=s#v9D2)Fr2Qn{1P3L4ynPs6T>NrlNUBxUqjqdjQ#BIHe|eMnV|U8
z##_$ZK@M)~+a=fo>Ed7awC<cJ!y%Baz=<yn{h$H+bWU?kk5t~T;A;CEab=rx7-&mv
zB<pnpVI1qamtM=bbXbu_K+ix_x%<5R{S)w1nIU=*>(=^}ErqSpchh@5=$;oikYwnF
zq!k=#kiK_v(rD{51$|9kjPc%n9)eYJuVFNU3)hX@5C!T4`6T;Sdej+7ze-!u%az;O
zKXLJPSd<LNT_1s5vssc{cM&T1Ha$jW7Y3ZPD*X9^B%A7!Z)^o#CUQT4Dpv5^O6sXA
zBX-5b2bH3%kk;Aufun06xM3=73FNZMAtRv%j!u%>Fu~OvpsvR~N_{U?GQ;HGHx_8{
z^nGozT5ru<kE=d=)c92yyOW@jQAh!{S0q#aDj5r|ugTO%-bu3Bvvt#+s$(;MvS|a^
zev|X8F*fxyaveEkG>Hn0k}@7JXDlx_^)@8PuXobNGJM_AVwU%{HyI4`nSlYcBc^N4
z81eFhZP&<LnW&qbzQ;vSi=sG<A5W2qnNviPcNbyH#{##{ba_16_RpC;n3~ZHg%ce?
zU+Cnp)0o;rThbVkgQT7aHi;-hjJn++w_SJa4(W@x-XhOXllW?}_E+~ba{LK#jE`%+
zJeF4HzHxgRUEX=0Y?6m&K3w=3xSu$RCQd3Vr~QYgOLf}Z6)NTz=k)a5D+R8fA24%O
zv^&tYl=hT<;<8Ek?z7Iw>n*rq%vR9`5pwMpM)hv@gGZECBfg3~s7Jt>3@xwk1W5?H
zDt?=UjrDr=zTasjD%EnJF(P%{sDk03`0CTSTZ_j$s^FlM?qZgXZgGh&iC`UTv~b%4
z3o7|xNeun?;L{(Q5)9F~nNA^y(((*?C9!QQEZfmqT+`xC>6<+{N!=#o#~xw_mHh(y
zgTcAwtkZSVmmH|ThoCyO`fC>|>(9?`7Cz9yg&Wo@Fs%NpJ`<PXn;VeWor}IB%J7=o
zFbKn6_@kp4D8A^GDeEI0@=8o145)I$b&GiAVOYVW-Y&0;w$I(&lSYUmm030hRMO(s
z7HmU@=3Ac(myMn7>NF%?f(_LHNBPlg*<BjQC$G&a=45+6q+@EI@KV#*CE~B<HEF)p
zlgVsBGECq45_R5tkG`cZfD)nq=7rDM?H*a~{9<%;bD*Gc*x<K=2|7u$`+^-0>!DT5
ze!xFOIq<wLTfGgq(U7N>8WHr#WlG2xN&k7@e3t@RKHTi5LT|+Ex8xE)JZYO?3m^-z
zGpRY;_axI+Vd?5j=S{+7xFvu^?mYb6O*e^!c3-kF#gHrK_ckm^X@4ls=coZY9^_YH
zFI1MLP!YRzam0y5tD+yPLFWAgJJ+F8^NSY)fhT&Db2I4Ry4Z_7?r?s|$dAH)gWNfI
zaT`1Ew`H%761h8Dvs5|k<Dh7tDPY*5UwwMyy~FF4TSjOnqeEC0)`t3tnpkUFb4i|J
zGxDjY7+F7v7xL&$Q_k87QzZ?q-$un%x;^&>ju=hI0T0YV;sgbq`&=;!E39Zfaj4VX
zf`mKF&v=LPrp*>q_%TXP_O=@L4^5X<^}qQz^yKu&ew1m+HBUB*<n<Y(<IG+>zqNM#
zWg-`NuZB8l>+9_f89#G2IxrVqhq~-^dySOBBd^xg0hZGSJs#t-W<oi84SdR1_WUY|
zSwr$_YKxfrH+oMh5XnJ_`C96P0?N$PktExo%u~{1;-O@H`*X?Mo;AqMU7taFF8RmO
zStrqrR9p)(g~J0lqi~zr4GJpL@Ar%CWq}*TRRr|WrZEpkW;mOA>q(co%SDbAPaA8?
zs7<?Rl&b~9I9r#!BB%4|F{q?lkG7~_qz0Y^X;%CpHL!`hYUnY^gW>dJ^eYE6awf6C
zT9(}7eiZFC9L|&NH#)0*I=bu>?`_ees@hL_Xo(C=uBlz}n7|k)?(<d_&Dq&hgzw6u
za&qEZkXF_TFriYH4gG^C@z4WRJHHxi@a=&zJ7U5L<k>7C{;QtlP3}1QRTPcFG_U0&
zq+D*7?`_)v4?SPd%cexo)k4*e<hehGU-D2zy?yw7%3+u+l=2-=`&0)Eh#m#f(mK=F
z%0eqn-Sxlx1a$%i2?c#aF#r-!E(4{SRuC!hM9~!}sISW(jhDQYI8!zgG{%UkI?D!n
zW?8Aw0l*hfVR9ZvU~!#oVr&$#7o?YaPOm2CJJeWYo*HAEtIoMP^Mk_($U+26?EF&M
z=?#UmZr<QG&e><?gFyV$88+u%&^bDV*Kpx_8$UOnhkt5&-Tr*Bq#y+!=WO1-#qP(d
z%|}1~<+^ytXU=#E;Z=u(sb<%P5xx~pi0f@hq7lRDy9!+wq6=T$U!LT@4YHaQD7?l<
zn0dR+@lnMRi&6~2qZ-)dU*%?o^I3`!SY+0LOQ-itfu)F<81r-wJzuR;bVuud)kt}G
z<X)7^hfSqA%Qt3LF4Dr;w?!eFZHi#zq^PVf(x-divtE*$Kb~y8U?pFn(%XVqcSEhw
z+u{aqTn)<o<&mW)q;vYy$7l-=DM8EL!O)zs4+<JbH1J_x$xr0VeNBD3lUv-uuA8!X
zM_GH05~^nb!6*Kxyid71LcCHHsUsUO_VV<MEgi%5j?@=7I7Q*+Ch$rNAIDEutcKlF
zp6#&n+w;<MAmTD@<@?<dM#^sKW_4FLQ(V)&c^{hez!i!G*Wc08+50x6YoN3*yq4!)
zzujii^Qp^IO;Ff;P<3|larx)Q_zxSpH)0j$lU$yly;K4h;g3{Mf$EKMpjk2I=Xpv2
z1KS2D>QqP-go>68FTi%MM<6k_+FB~pS*d(_O2B%*3i{>xc_g16WAi*?U*(un0&%2^
z19VN-%}qyr_=x{xMw1zNWQw>KaBgRRVE+N`Fua)~p<C59QRCQDG%<v^;*J|({r({v
zq6#8qeQgr8sw`VYs4S^7%3*k~0%|gml=k)g*0ZJCwp`9)D`!86?Q23d0mW?>&m6z6
zgOB!o`|5}qL*yP8Tm<$KoGJ0tIZw9F%91k^jN}{g2j<H^#`Ci8y-^XE=`CB`YY_Kl
zel^DRvl>_6AfiKV<lKQ}p8Zr|?zw+hz_e|qQ4ln1Yk~+<#{hR<{sfRrm$V^NxBZzo
zfNVRwzPTu`ap!}Ar(J~t0(V4&Xj<)1aWCJfd2eknA1H!vQjV#$Cn!pvVcxJTN(u1h
zIR=d5EOz$LE7)$W!!~nvGww+mmNCtU2U|1So7&YaFe7Z@*cwYdiPN$3fHP5zl!&Mj
ztlc6W*_Mq0o69Y;F``|BP#x`ae*{pWA7u=b9sP(O#Mt+F${uB1%l5V-NJ;*TB~arD
zX^`sc_!%+Xy<nvEpc@`y*;UZ|ZF+s4%(Sjl)GX?O3?3zqNVRJ1B5w`OE^P^uKflAp
z_<hEU@A*fL%&y=BgN#GX83v(>fIsLMIks3SkV%113HX~MO(D{k7QIH<l#j0?f~DxV
z`5VS?<DB&kg;oIv2Uy?9FzYk%CL3biH?DGiMjS13k15F8Wnb^Ua_)KF8nmf$NTL%P
z|1kR@wJP(VcH?TNG<%58<C1O5Q>vjA1BKPkS-FVXAb}&~!RwYm2065*6i<u3Qyy!C
zL$3c>nV5#=L^Y!dqgKGj#3GxB%9(J+=B*mGGUHb<T`*21rIgZb3z}D>A<gM+FW55B
zzBU=tSYXHYnjm1uy|J*;^1lCAN~C=M8b(^j$*Z%1DJb4pW$Gm3+u95JIXrX8$wid%
z@t#BZ1%>{9U6P55K5P`g2g6)eo0mxiU;`lGC>MmIm$$I1mpjVDm<&K@z*u7TD}DSa
z00cyb1OULF?K<(Z<nKf%BX^G(c<}b!!s_pjX;X%~SSDX#H;CAS{Kj;~vy=C8^P9v-
zZYKtF_294YEQ-9FtMd`nI4B0zWL2dmzUNI0&^l*n7pNQ1H4g(7*~jq+ZT9!39XUkr
zrIR<_st5aKmMNs`xCHxGdwq%+@65715dQ2|ykfpm8yF?8MZG~*Pr&-_E%&NIffpVu
zEBg*>@lyI1KXSmY_~0oH1N>jmU#6d<osgryw-DUZ+XVr)_x6J5|HU;HZz2oBFRN#O
z-(BN>J_d={*Xi%m))EpPq0$l(80;UaH53^aUs(FC)vr@;*c}PmbYxs#_h@n`31^U7
zD_&=I%y3v?vIPIYWeN}l$z1a!V8PJ_+=^ric~M-vkeuVWU&?mg(p?bqase;|wX_sY
zhBdyl>wNA8NwRV$G}6PzjMBI7EmigAh;#;DRM=Sx3j_WNy8DTul<Q>`aG6*C&gyz0
zC<e8Mv;@IIu!+Bb{_Z0>;nSn7cS+{(7b`-s3Wiv5dU37W@!{GEx4!p_8OFam+!yK}
zMIxIXE3i?Crt_6x(n@63n3w`j<>W0QFlm7$nZ;@*SPaPNv!@5aB{Tvc1K~gW@c%?8
z0pK58`L7Ar3I9o>pg+5RH5Z5RQUQKfEb+e;`<q(-1O@nKS^PUL5dui4|6U}2CH1>Z
p{zpoR`tL>a`-I<j_P+^R)PK7{eJx^=Uk`{ckJ%-!D4Ji@zW`%asn!4h
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 sGettersDecodeURL