merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 24 Feb 2017 11:46:38 +0100
changeset 373706 be661bae6cb9a53935c5b87744bf68879d9ebcc5
parent 373600 c7935d540027158ef1bf8e1c0e69af6b2eb9df93 (current diff)
parent 373705 6ef5dd99b45cf4b556e7204ba29c6446f550702a (diff)
child 373707 1c0d63ba05fe4ddb21b7afb2ba89b202368f7676
child 373727 87ca04c38b8c470b77c389f82f417f33ce69d2fb
child 373803 c5c7698949ab8d48fef750166df27b61579f22f8
child 374804 d4583ab7644efc567dbc412e38c32282a9cb067a
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone54.0a1
merge mozilla-inbound to mozilla-central a=merge
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/themes/deprecated-boxmodel.css
dom/base/nsDocument.cpp
layout/painting/nsCSSRendering.cpp
layout/painting/nsDisplayList.cpp
python/mozbuild/mozbuild/backend/test_manifest.py
python/mozbuild/mozbuild/test/backend/test_test_manifest.py
toolkit/components/telemetry/Histograms.json
tools/profiler/core/ProfileEntry.cpp
tools/profiler/core/ProfileEntry.h
--- 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/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/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
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/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/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/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/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -280,16 +280,17 @@ bool nsContentUtils::sIsFullScreenApiEna
 bool nsContentUtils::sIsUnprefixedFullscreenApiEnabled = false;
 bool nsContentUtils::sTrustedFullScreenOnly = true;
 bool nsContentUtils::sIsCutCopyAllowed = true;
 bool nsContentUtils::sIsFrameTimingPrefEnabled = false;
 bool nsContentUtils::sIsPerformanceTimingEnabled = false;
 bool nsContentUtils::sIsResourceTimingEnabled = false;
 bool nsContentUtils::sIsUserTimingLoggingEnabled = false;
 bool nsContentUtils::sIsExperimentalAutocompleteEnabled = false;
+bool nsContentUtils::sIsWebComponentsEnabled = false;
 bool nsContentUtils::sEncodeDecodeURLHash = false;
 bool nsContentUtils::sGettersDecodeURLHash = false;
 bool nsContentUtils::sPrivacyResistFingerprinting = false;
 bool nsContentUtils::sSendPerformanceTimingNotifications = false;
 bool nsContentUtils::sUseActivityCursor = false;
 
 uint32_t nsContentUtils::sHandlingInputTimeout = 1000;
 
@@ -507,20 +508,21 @@ nsContentUtils::Init()
   sSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
   if(!sSecurityManager)
     return NS_ERROR_FAILURE;
   NS_ADDREF(sSecurityManager);
 
   sSecurityManager->GetSystemPrincipal(&sSystemPrincipal);
   MOZ_ASSERT(sSystemPrincipal);
 
-  // We use the constructor here because we want infallible initialization; we
-  // apparently don't care whether sNullSubjectPrincipal has a sane URI or not.
-  RefPtr<nsNullPrincipal> nullPrincipal = new nsNullPrincipal();
-  nullPrincipal->Init();
+  RefPtr<nsNullPrincipal> nullPrincipal = nsNullPrincipal::Create();
+  if (!nullPrincipal) {
+    return NS_ERROR_FAILURE;
+  }
+
   nullPrincipal.forget(&sNullSubjectPrincipal);
 
   nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
   if (NS_FAILED(rv)) {
     // This makes life easier, but we can live without it.
 
     sIOService = nullptr;
   }
@@ -577,16 +579,19 @@ nsContentUtils::Init()
                                "dom.performance.enable_user_timing_logging", false);
 
   Preferences::AddBoolVarCache(&sIsFrameTimingPrefEnabled,
                                "dom.enable_frame_timing", false);
 
   Preferences::AddBoolVarCache(&sIsExperimentalAutocompleteEnabled,
                                "dom.forms.autocomplete.experimental", false);
 
+  Preferences::AddBoolVarCache(&sIsWebComponentsEnabled,
+                               "dom.webcomponents.enabled", false);
+
   Preferences::AddBoolVarCache(&sEncodeDecodeURLHash,
                                "dom.url.encode_decode_hash", false);
 
   Preferences::AddBoolVarCache(&sGettersDecodeURLHash,
                                "dom.url.getters_decode_hash", false);
 
   Preferences::AddBoolVarCache(&sPrivacyResistFingerprinting,
                                "privacy.resistFingerprinting", false);
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2801,16 +2801,19 @@ public:
                                         nsIPrincipal** aLoadingPrincipal,
                                         nsContentPolicyType& aContentPolicyType);
 
   static nsresult
   CreateJSValueFromSequenceOfObject(JSContext* aCx,
                                     const mozilla::dom::Sequence<JSObject*>& aTransfer,
                                     JS::MutableHandle<JS::Value> aValue);
 
+  static bool
+  IsWebComponentsEnabled() { return sIsWebComponentsEnabled; }
+
 private:
   static bool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static bool CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
                                 nsIPrincipal* aPrincipal);
 
@@ -2907,16 +2910,17 @@ private:
   static bool sTrustedFullScreenOnly;
   static bool sIsCutCopyAllowed;
   static uint32_t sHandlingInputTimeout;
   static bool sIsPerformanceTimingEnabled;
   static bool sIsResourceTimingEnabled;
   static bool sIsUserTimingLoggingEnabled;
   static bool sIsFrameTimingPrefEnabled;
   static bool sIsExperimentalAutocompleteEnabled;
+  static bool sIsWebComponentsEnabled;
   static bool sEncodeDecodeURLHash;
   static bool sGettersDecodeURLHash;
   static bool sPrivacyResistFingerprinting;
   static bool sSendPerformanceTimingNotifications;
   static bool sUseActivityCursor;
   static uint32_t sCookiesLifetimePolicy;
   static uint32_t sCookiesBehavior;
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -99,16 +99,17 @@
 #include "nsNetUtil.h"     // for NS_NewURI
 #include "nsIInputStreamChannel.h"
 #include "nsIAuthPrompt.h"
 #include "nsIAuthPrompt2.h"
 
 #include "nsIScriptSecurityManager.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
+#include "nsNullPrincipal.h"
 
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDOMElement.h"
 #include "nsFocusManager.h"
 
 // for radio group stuff
 #include "nsIDOMHTMLInputElement.h"
@@ -2731,17 +2732,17 @@ nsDocument::InitCSP(nsIChannel* aChannel
   rv = csp->GetCSPSandboxFlags(&cspSandboxFlags);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mSandboxFlags |= cspSandboxFlags;
 
   if (cspSandboxFlags & SANDBOXED_ORIGIN) {
     // If the new CSP sandbox flags do not have the allow-same-origin flag
     // reset the document principal to a null principal
-    principal = do_CreateInstance("@mozilla.org/nullprincipal;1");
+    principal = nsNullPrincipal::Create();
     SetPrincipal(principal);
   }
 
   // ----- Enforce frame-ancestor policy on any applied policies
   nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
   if (docShell) {
     bool safeAncestry = false;
 
@@ -5936,33 +5937,33 @@ nsDocument::CustomElementConstructor(JSC
   return true;
 }
 
 bool
 nsDocument::IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject)
 {
   JS::Rooted<JSObject*> obj(aCx, aObject);
 
-  if (Preferences::GetBool("dom.webcomponents.enabled")) {
+  if (nsContentUtils::IsWebComponentsEnabled()) {
     return true;
   }
 
   // Check for the webcomponents permission. See Bug 1181555.
   JSAutoCompartment ac(aCx, obj);
   JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, obj));
   nsCOMPtr<nsPIDOMWindowInner> window =
     do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(global));
 
   return IsWebComponentsEnabled(window);
 }
 
 bool
 nsDocument::IsWebComponentsEnabled(dom::NodeInfo* aNodeInfo)
 {
-  if (Preferences::GetBool("dom.webcomponents.enabled")) {
+  if (nsContentUtils::IsWebComponentsEnabled()) {
     return true;
   }
 
   nsIDocument* doc = aNodeInfo->GetDocument();
   // Use GetScopeObject() here so that data documents work the same way as the
   // main document they're associated with.
   nsCOMPtr<nsPIDOMWindowInner> window =
     do_QueryInterface(doc->GetScopeObject());
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -9401,31 +9401,26 @@ public:
       JS::Rooted<JSObject*> obj(cx, currentInner->FastGetGlobalJSObject());
       if (obj && !js::IsSystemCompartment(js::GetObjectCompartment(obj))) {
         JSCompartment* cpt = js::GetObjectCompartment(obj);
         nsCOMPtr<nsIPrincipal> pc = nsJSPrincipals::get(JS_GetCompartmentPrincipals(cpt));
 
         nsAutoString addonId;
         if (NS_SUCCEEDED(pc->GetAddonId(addonId)) && !addonId.IsEmpty()) {
           // We want to nuke all references to the add-on compartment.
-          js::NukeCrossCompartmentWrappers(cx, js::AllCompartments(),
-                                           js::SingleCompartment(cpt),
-                                           win->IsInnerWindow() ? js::DontNukeWindowReferences
-                                                                : js::NukeWindowReferences);
-
-          // Now mark the compartment as nuked and non-scriptable.
-          auto compartmentPrivate = xpc::CompartmentPrivate::Get(cpt);
-          compartmentPrivate->wasNuked = true;
-          compartmentPrivate->scriptability.Block();
+          xpc::NukeAllWrappersForCompartment(cx, cpt,
+                                             win->IsInnerWindow() ? js::DontNukeWindowReferences
+                                                                  : js::NukeWindowReferences);
         } else {
           // We only want to nuke wrappers for the chrome->content case
           js::NukeCrossCompartmentWrappers(cx, BrowserCompartmentMatcher(),
                                            js::SingleCompartment(cpt),
                                            win->IsInnerWindow() ? js::DontNukeWindowReferences
-                                                                : js::NukeWindowReferences);
+                                                                : js::NukeWindowReferences,
+                                           js::NukeIncomingReferences);
         }
       }
     }
 
     return NS_OK;
   }
 
 private:
--- a/dom/base/test/test_postMessages.html
+++ b/dom/base/test/test_postMessages.html
@@ -6,18 +6,17 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 
 <body>
 <input id="fileList" type="file"></input>
 <script type="application/javascript;version=1.7">
 
 function setup_tests() {
-  SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true],
-                                     ["javascript.options.wasm", true]]}, next);
+  SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true]]}, next);
 }
 
 function getType(a) {
   if (a === null || a === undefined)
     return 'null';
 
   if (Array.isArray(a))
     return 'array';
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1901,21 +1901,17 @@ int32_t
 CanvasRenderingContext2D::GetHeight() const
 {
   return mHeight;
 }
 
 NS_IMETHODIMP
 CanvasRenderingContext2D::SetDimensions(int32_t aWidth, int32_t aHeight)
 {
-  bool retainBuffer = false;
-  if (aWidth == mWidth && aHeight == mHeight) {
-    retainBuffer = true;
-  }
-  ClearTarget(retainBuffer);
+  ClearTarget();
 
   // Zero sized surfaces can cause problems.
   mZero = false;
   if (aHeight == 0) {
     aHeight = 1;
     mZero = true;
   }
   if (aWidth == 0) {
@@ -1924,33 +1920,20 @@ CanvasRenderingContext2D::SetDimensions(
   }
   mWidth = aWidth;
   mHeight = aHeight;
 
   return NS_OK;
 }
 
 void
-CanvasRenderingContext2D::ClearTarget(bool aRetainBuffer)
-{
-  RefPtr<PersistentBufferProvider> provider = mBufferProvider;
-  if (aRetainBuffer && provider) {
-    // We should reset the buffer data before reusing the buffer.
-    if (mTarget) {
-      mTarget->SetTransform(Matrix());
-    }
-    ClearRect(0, 0, mWidth, mHeight);
-  }
-
+CanvasRenderingContext2D::ClearTarget()
+{
   Reset();
 
-  if (aRetainBuffer) {
-    mBufferProvider = provider;
-  }
-
   mResetLayer = true;
 
   SetInitialState();
 
   // For vertical writing-mode, unless text-orientation is sideways,
   // we'll modify the initial value of textBaseline to 'middle'.
   RefPtr<nsStyleContext> canvasStyle;
   if (mCanvasElement && mCanvasElement->IsInUncomposedDoc()) {
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -677,17 +677,17 @@ protected:
   /**
    * Cf. OnStableState.
    */
   void ScheduleStableStateCallback();
 
   /**
    * Disposes an old target and prepares to lazily create a new target.
    */
-  void ClearTarget(bool aRetainBuffer = false);
+  void ClearTarget();
 
   /*
    * Returns the target to the buffer provider. i.e. this will queue a frame for
    * rendering.
    */
   void ReturnTarget(bool aForceReset = false);
 
   /**
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -539,19 +539,18 @@ GetDOMFileOrDirectoryPath(const OwningFi
 }
 
 } // namespace
 
 /* static */
 bool
 HTMLInputElement::ValueAsDateEnabled(JSContext* cx, JSObject* obj)
 {
-  return Preferences::GetBool("dom.experimental_forms", false) ||
-    Preferences::GetBool("dom.forms.datepicker", false) ||
-    Preferences::GetBool("dom.forms.datetime", false);
+  return IsExperimentalFormsEnabled() || IsDatePickerEnabled() ||
+         IsInputDateTimeEnabled();
 }
 
 NS_IMETHODIMP
 HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult)
 {
   mInput->PickerClosed();
 
   if (aResult == nsIFilePicker::returnCancel) {
@@ -624,17 +623,17 @@ HTMLInputElement::nsFilePickerShownCallb
   // The text control frame (if there is one) isn't going to send a change
   // event because it will think this is done by a script.
   // So, we can safely send one by ourself.
   mInput->SetFilesOrDirectories(newFilesOrDirectories, true);
 
   RefPtr<DispatchChangeEventCallback> dispatchChangeEventCallback =
     new DispatchChangeEventCallback(mInput);
 
-  if (Preferences::GetBool("dom.webkitBlink.dirPicker.enabled", false) &&
+  if (IsWebkitDirPickerEnabled() &&
       mInput->HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory)) {
     ErrorResult error;
     GetFilesHelper* helper = mInput->GetOrCreateGetFilesHelper(true, error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
 
     helper->AddCallback(dispatchChangeEventCallback);
@@ -825,17 +824,17 @@ HTMLInputElement::IsPopupBlocked() const
   uint32_t permission;
   pm->TestPermission(OwnerDoc()->NodePrincipal(), &permission);
   return permission == nsIPopupWindowManager::DENY_POPUP;
 }
 
 nsresult
 HTMLInputElement::InitDatePicker()
 {
-  if (!Preferences::GetBool("dom.forms.datepicker", false)) {
+  if (!IsDatePickerEnabled()) {
     return NS_OK;
   }
 
   if (mPickerRunning) {
     NS_WARNING("Just one nsIDatePicker is allowed");
     return NS_ERROR_FAILURE;
   }
 
@@ -2563,20 +2562,18 @@ HTMLInputElement::ApplyStep(int32_t aSte
   return rv;
 }
 
 /* static */
 bool
 HTMLInputElement::IsExperimentalMobileType(uint8_t aType)
 {
   return (aType == NS_FORM_INPUT_DATE &&
-    !Preferences::GetBool("dom.forms.datetime", false) &&
-    !Preferences::GetBool("dom.forms.datepicker", false)) ||
-    (aType == NS_FORM_INPUT_TIME &&
-     !Preferences::GetBool("dom.forms.datetime", false));
+    !IsInputDateTimeEnabled() && !IsDatePickerEnabled()) ||
+    (aType == NS_FORM_INPUT_TIME && !IsInputDateTimeEnabled());
 }
 
 bool
 HTMLInputElement::IsDateTimeInputType(uint8_t aType)
 {
   return aType == NS_FORM_INPUT_DATE ||
          aType == NS_FORM_INPUT_TIME ||
          aType == NS_FORM_INPUT_MONTH ||
@@ -3015,18 +3012,18 @@ HTMLInputElement::GetDisplayFileName(nsA
   if (mFilesOrDirectories.Length() == 1) {
     GetDOMFileOrDirectoryName(mFilesOrDirectories[0], aValue);
     return;
   }
 
   nsXPIDLString value;
 
   if (mFilesOrDirectories.IsEmpty()) {
-    if ((Preferences::GetBool("dom.input.dirpicker", false) && Allowdirs()) ||
-        (Preferences::GetBool("dom.webkitBlink.dirPicker.enabled", false) &&
+    if ((IsDirPickerEnabled() && Allowdirs()) ||
+        (IsWebkitDirPickerEnabled() &&
          HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory))) {
       nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
                                          "NoDirSelected", value);
     } else if (HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) {
       nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
                                          "NoFilesSelected", value);
     } else {
       nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
@@ -3045,17 +3042,17 @@ HTMLInputElement::GetDisplayFileName(nsA
 }
 
 void
 HTMLInputElement::SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories,
                                         bool aSetValueChanged)
 {
   ClearGetFilesHelpers();
 
-  if (Preferences::GetBool("dom.webkitBlink.filesystem.enabled", false)) {
+  if (IsWebkitFileSystemEnabled()) {
     HTMLInputElementBinding::ClearCachedWebkitEntriesValue(this);
     mEntries.Clear();
   }
 
   mFilesOrDirectories.Clear();
   mFilesOrDirectories.AppendElements(aFilesOrDirectories);
 
   AfterSetFilesOrDirectories(aSetValueChanged);
@@ -3064,17 +3061,17 @@ HTMLInputElement::SetFilesOrDirectories(
 void
 HTMLInputElement::SetFiles(nsIDOMFileList* aFiles,
                            bool aSetValueChanged)
 {
   RefPtr<FileList> files = static_cast<FileList*>(aFiles);
   mFilesOrDirectories.Clear();
   ClearGetFilesHelpers();
 
-  if (Preferences::GetBool("dom.webkitBlink.filesystem.enabled", false)) {
+  if (IsWebkitFileSystemEnabled()) {
     HTMLInputElementBinding::ClearCachedWebkitEntriesValue(this);
     mEntries.Clear();
   }
 
   if (aFiles) {
     uint32_t listLength;
     aFiles->GetLength(&listLength);
     for (uint32_t i = 0; i < listLength; i++) {
@@ -3087,24 +3084,24 @@ HTMLInputElement::SetFiles(nsIDOMFileLis
 }
 
 // This method is used for testing only.
 void
 HTMLInputElement::MozSetDndFilesAndDirectories(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories)
 {
   SetFilesOrDirectories(aFilesOrDirectories, true);
 
-  if (Preferences::GetBool("dom.webkitBlink.filesystem.enabled", false)) {
+  if (IsWebkitFileSystemEnabled()) {
     UpdateEntries(aFilesOrDirectories);
   }
 
   RefPtr<DispatchChangeEventCallback> dispatchChangeEventCallback =
     new DispatchChangeEventCallback(this);
 
-  if (Preferences::GetBool("dom.webkitBlink.dirPicker.enabled", false) &&
+  if (IsWebkitDirPickerEnabled() &&
       HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory)) {
     ErrorResult rv;
     GetFilesHelper* helper = GetOrCreateGetFilesHelper(true /* recursionFlag */,
                                                        rv);
     if (NS_WARN_IF(rv.Failed())) {
       rv.SuppressException();
       return;
     }
@@ -3174,18 +3171,18 @@ HTMLInputElement::FireChangeEventIfNeede
 
 FileList*
 HTMLInputElement::GetFiles()
 {
   if (mType != NS_FORM_INPUT_FILE) {
     return nullptr;
   }
 
-  if (Preferences::GetBool("dom.input.dirpicker", false) && Allowdirs() &&
-      (!Preferences::GetBool("dom.webkitBlink.dirPicker.enabled", false) ||
+  if (IsDirPickerEnabled() && Allowdirs() &&
+      (!IsWebkitDirPickerEnabled() ||
        !HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory))) {
     return nullptr;
   }
 
   if (!mFileList) {
     mFileList = new FileList(static_cast<nsIContent*>(this));
     UpdateFileList();
   }
@@ -4387,18 +4384,18 @@ HTMLInputElement::MaybeInitPickers(Event
   if (mType == NS_FORM_INPUT_FILE) {
     // If the user clicked on the "Choose folder..." button we open the
     // directory picker, else we open the file picker.
     FilePickerType type = FILE_PICKER_FILE;
     nsCOMPtr<nsIContent> target =
       do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
     if (target &&
         target->FindFirstNonChromeOnlyAccessContent() == this &&
-        ((Preferences::GetBool("dom.input.dirpicker", false) && Allowdirs()) ||
-         (Preferences::GetBool("dom.webkitBlink.dirPicker.enabled", false) &&
+        ((IsDirPickerEnabled() && Allowdirs()) ||
+         (IsWebkitDirPickerEnabled() &&
           HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory)))) {
       type = FILE_PICKER_DIRECTORY;
     }
     return InitFilePicker(type);
   }
   if (mType == NS_FORM_INPUT_COLOR) {
     return InitColorPicker();
   }
@@ -5793,30 +5790,143 @@ HTMLInputElement::ParseTime(const nsAStr
                // NOTE: there is 10.0 instead of 10 and static_cast<int> because
                // some old [and stupid] compilers can't just do the right thing.
                fractionsSeconds * pow(10.0, static_cast<int>(3 - (aValue.Length() - 9)));
   }
 
   return true;
 }
 
-static bool
-IsDateTimeEnabled(int32_t aNewType)
-{
-  return (aNewType == NS_FORM_INPUT_DATE &&
-          (Preferences::GetBool("dom.forms.datetime", false) ||
-           Preferences::GetBool("dom.experimental_forms", false) ||
-           Preferences::GetBool("dom.forms.datepicker", false))) ||
-         (aNewType == NS_FORM_INPUT_TIME &&
-          (Preferences::GetBool("dom.forms.datetime", false) ||
-           Preferences::GetBool("dom.experimental_forms", false))) ||
-         ((aNewType == NS_FORM_INPUT_MONTH ||
-           aNewType == NS_FORM_INPUT_WEEK ||
-           aNewType == NS_FORM_INPUT_DATETIME_LOCAL) &&
-          Preferences::GetBool("dom.forms.datetime", false));
+/* static */ bool
+HTMLInputElement::IsDateTimeTypeSupported(uint8_t aDateTimeInputType)
+{
+  return (aDateTimeInputType == NS_FORM_INPUT_DATE &&
+          (IsInputDateTimeEnabled() || IsExperimentalFormsEnabled() ||
+           IsDatePickerEnabled())) ||
+         (aDateTimeInputType == NS_FORM_INPUT_TIME &&
+          (IsInputDateTimeEnabled() || IsExperimentalFormsEnabled())) ||
+         ((aDateTimeInputType == NS_FORM_INPUT_MONTH ||
+           aDateTimeInputType == NS_FORM_INPUT_WEEK ||
+           aDateTimeInputType == NS_FORM_INPUT_DATETIME_LOCAL) &&
+          IsInputDateTimeEnabled());
+}
+
+/* static */ bool
+HTMLInputElement::IsWebkitDirPickerEnabled()
+{
+  static bool sWebkitDirPickerEnabled = false;
+  static bool sWebkitDirPickerPrefCached = false;
+  if (!sWebkitDirPickerPrefCached) {
+    sWebkitDirPickerPrefCached = true;
+    Preferences::AddBoolVarCache(&sWebkitDirPickerEnabled,
+                                 "dom.webkitBlink.dirPicker.enabled",
+                                 false);
+  }
+
+  return sWebkitDirPickerEnabled;
+}
+
+/* static */ bool
+HTMLInputElement::IsWebkitFileSystemEnabled()
+{
+  static bool sWebkitFileSystemEnabled = false;
+  static bool sWebkitFileSystemPrefCached = false;
+  if (!sWebkitFileSystemPrefCached) {
+    sWebkitFileSystemPrefCached = true;
+    Preferences::AddBoolVarCache(&sWebkitFileSystemEnabled,
+                                 "dom.webkitBlink.filesystem.enabled",
+                                 false);
+  }
+
+  return sWebkitFileSystemEnabled;
+}
+
+/* static */ bool
+HTMLInputElement::IsDirPickerEnabled()
+{
+  static bool sDirPickerEnabled = false;
+  static bool sDirPickerPrefCached = false;
+  if (!sDirPickerPrefCached) {
+    sDirPickerPrefCached = true;
+    Preferences::AddBoolVarCache(&sDirPickerEnabled, "dom.input.dirpicker",
+                                 false);
+  }
+
+  return sDirPickerEnabled;
+}
+
+/* static */ bool
+HTMLInputElement::IsDatePickerEnabled()
+{
+  static bool sDatePickerEnabled = false;
+  static bool sDatePickerPrefCached = false;
+  if (!sDatePickerPrefCached) {
+    sDatePickerPrefCached = true;
+    Preferences::AddBoolVarCache(&sDatePickerEnabled, "dom.forms.datepicker",
+                                 false);
+  }
+
+  return sDatePickerEnabled;
+}
+
+/* static */ bool
+HTMLInputElement::IsExperimentalFormsEnabled()
+{
+  static bool sExperimentalFormsEnabled = false;
+  static bool sExperimentalFormsPrefCached = false;
+  if (!sExperimentalFormsPrefCached) {
+    sExperimentalFormsPrefCached = true;
+    Preferences::AddBoolVarCache(&sExperimentalFormsEnabled,
+                                 "dom.experimental_forms",
+                                 false);
+  }
+
+  return sExperimentalFormsEnabled;
+}
+
+/* static */ bool
+HTMLInputElement::IsInputDateTimeEnabled()
+{
+  static bool sDateTimeEnabled = false;
+  static bool sDateTimePrefCached = false;
+  if (!sDateTimePrefCached) {
+    sDateTimePrefCached = true;
+    Preferences::AddBoolVarCache(&sDateTimeEnabled, "dom.forms.datetime",
+                                 false);
+  }
+
+  return sDateTimeEnabled;
+}
+
+/* static */ bool
+HTMLInputElement::IsInputNumberEnabled()
+{
+  static bool sInputNumberEnabled = false;
+  static bool sInputNumberPrefCached = false;
+  if (!sInputNumberPrefCached) {
+    sInputNumberPrefCached = true;
+    Preferences::AddBoolVarCache(&sInputNumberEnabled, "dom.forms.number",
+                                 false);
+  }
+
+  return sInputNumberEnabled;
+}
+
+/* static */ bool
+HTMLInputElement::IsInputColorEnabled()
+{
+  static bool sInputColorEnabled = false;
+  static bool sInputColorPrefCached = false;
+  if (!sInputColorPrefCached) {
+    sInputColorPrefCached = true;
+    Preferences::AddBoolVarCache(&sInputColorEnabled, "dom.forms.color",
+                                 false);
+  }
+
+  return sInputColorEnabled;
 }
 
 bool
 HTMLInputElement::ParseAttribute(int32_t aNamespaceID,
                                  nsIAtom* aAttribute,
                                  const nsAString& aValue,
                                  nsAttrValue& aResult)
 {
@@ -5824,22 +5934,21 @@ HTMLInputElement::ParseAttribute(int32_t
     if (aAttribute == nsGkAtoms::type) {
       // XXX ARG!! This is major evilness. ParseAttribute
       // shouldn't set members. Override SetAttr instead
       int32_t newType;
       bool success = aResult.ParseEnumValue(aValue, kInputTypeTable, false);
       if (success) {
         newType = aResult.GetEnumValue();
         if ((IsExperimentalMobileType(newType) &&
-             !Preferences::GetBool("dom.experimental_forms", false)) ||
-            (newType == NS_FORM_INPUT_NUMBER &&
-             !Preferences::GetBool("dom.forms.number", false)) ||
-            (newType == NS_FORM_INPUT_COLOR &&
-             !Preferences::GetBool("dom.forms.color", false)) ||
-            (IsDateTimeInputType(newType) && !IsDateTimeEnabled(newType))) {
+             !IsExperimentalFormsEnabled()) ||
+            (newType == NS_FORM_INPUT_NUMBER && !IsInputNumberEnabled()) ||
+            (newType == NS_FORM_INPUT_COLOR && !IsInputColorEnabled()) ||
+            (IsDateTimeInputType(newType) &&
+             !IsDateTimeTypeSupported(newType))) {
           newType = kInputDefaultType->value;
           aResult.SetTo(newType, &aValue);
         }
       } else {
         newType = kInputDefaultType->value;
       }
 
       if (newType != mType) {
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -1645,16 +1645,79 @@ private:
 
   static bool MayFireChangeOnBlur(uint8_t aType) {
     return IsSingleLineTextControl(false, aType) ||
            aType == NS_FORM_INPUT_RANGE ||
            aType == NS_FORM_INPUT_NUMBER ||
            aType == NS_FORM_INPUT_TIME;
   }
 
+  /**
+   * Checks if aDateTimeInputType should be supported based on "dom.forms.datetime",
+   * "dom.forms.datepicker" and "dom.experimental_forms".
+   */
+  static bool
+  IsDateTimeTypeSupported(uint8_t aDateTimeInputType);
+
+  /**
+   * Checks preference "dom.webkitBlink.dirPicker.enabled" to determine if
+   * webkitdirectory should be supported.
+   */
+  static bool
+  IsWebkitDirPickerEnabled();
+
+  /**
+   * Checks preference "dom.webkitBlink.filesystem.enabled" to determine if
+   * webkitEntries should be supported.
+   */
+  static bool
+  IsWebkitFileSystemEnabled();
+
+  /**
+   * Checks preference "dom.input.dirpicker" to determine if file and directory
+   * entries API should be supported.
+   */
+  static bool
+  IsDirPickerEnabled();
+
+  /**
+   * Checks preference "dom.forms.datepicker" to determine if date picker should
+   * be supported.
+   */
+  static bool
+  IsDatePickerEnabled();
+
+  /**
+   * Checks preference "dom.experimental_forms" to determine if experimental
+   * implementation of input element should be enabled.
+   */
+  static bool
+  IsExperimentalFormsEnabled();
+
+  /**
+   * Checks preference "dom.forms.datetime" to determine if input date/time
+   * related types should be supported.
+   */
+  static bool
+  IsInputDateTimeEnabled();
+
+  /**
+   * Checks preference "dom.forms.number" to determine if input type=number
+   * should be supported.
+   */
+  static bool
+  IsInputNumberEnabled();
+
+  /**
+   * Checks preference "dom.forms.color" to determine if date/time related
+   * types should be supported.
+   */
+  static bool
+  IsInputColorEnabled();
+
   struct nsFilePickerFilter {
     nsFilePickerFilter()
       : mFilterMask(0) {}
 
     explicit nsFilePickerFilter(int32_t aFilterMask)
       : mFilterMask(aFilterMask) {}
 
     nsFilePickerFilter(const nsString& aTitle,
--- a/dom/indexedDB/test/helpers.js
+++ b/dom/indexedDB/test/helpers.js
@@ -70,18 +70,17 @@ function* testHarnessSteps() {
 
   info("Pushing preferences");
 
   SpecialPowers.pushPrefEnv(
     {
       "set": [
         ["dom.indexedDB.testing", true],
         ["dom.indexedDB.experimental", true],
-        ["dom.archivereader.enabled", true],
-        ["javascript.options.wasm", true]
+        ["dom.archivereader.enabled", true]
       ]
     },
     nextTestHarnessStep
   );
   yield undefined;
 
   info("Pushing permissions");
 
index 50ca3ef898de2706d42e0019c923838502302225..8f9d67868e5db71b52e0fa3a996afac5dc5f1be7
GIT binary patch
literal 5446
zc$}?Q2{@G7A09-q)g>iavKNCarIZ#Xk?gy~42H4HV9YStmAw>$Y|#~y$Xa%$ER|)#
zpc%xa&}t1?GR%LZTikALJ^#LW&i9=2edl}M_iVrUy?R;<o3;Z00A_$_fScwy^y`cl
zbN~S7CIEm9zz48JLLn%qwVjQOlZUa<765}H8ECdr&=^(#-KMt;0Kn&)pCJL!yMJ5t
zh*ar=U>5qKipsa?*+Ih{nSl&5Jj7uOd&wA}U^c0S+Aju`Kw3#?5|Vmejz-=z0Sr6^
zbX3-kcygWGtaqn)`ybpOR`*Mj3VF)X5QtB@wpyI_6+~;r%-&o6Cma|1lZj>nNYlcc
z6AQ;@uHRI;c>&#g_x(vsIM1LM?~Jg;R%^nX{4LhpWdq|8JG*D(vjmdJfjCjY+Hipg
z1wxirz^S(rzT;!4uol0>m>N1q)49ExwcgeJvHpMfvoujc!Y{T8yj0@wICNa<@!`a0
z=t3zoJkzXZ*jX#_H@R76X6MxoG8U56!&;<!nr1zq#O{3>>O!gM&5zHM9p&t!SmD8f
z-CIWT&hXO{l$h*u?~Z)RgrRsK#^B-6cs@FgCn*%wMEuOPTaD^YD#=RD1bZcpsDsjb
z2XjF+J($Er3?;VlXavlu=u}BL3|MQfQ(4|4!mA}R4Yhf7LF~$VqM!)Y8Ly{knaM6J
z`iiea6XRUqe(p&H-<6}UgGI$cPrS8r8-c;@T(^aH4CV103d;x;v{=xfNGnX32e5qf
zG50%zzULSVmM}Irf|}$p*qJib`=Ui57;n~vrDT$saoz3RZE0nJCSIe9IvR^wuL=@I
zK1CMYU1rdu?GhHi%2n_0rU_VcG1GR52g(%*ae&Hx-XZ=ZpxF+tf`9IiA0u$CL2!d2
z;SdBAf%=BE^H;ET{VSF&4CxAABai1-Ao(|fbhNYi2Ftq<EFU6u9jxkq!73=q$txW=
zbl@<`1M1`<Lpy~oUXBnq4>;V_+7>Be@8|;cSmnM0gPQUE%8;~v3%8Ri8i|0oczm0H
z;C~mf8eaZ1F+F3`kqi;1wf57B+HVmd_nF*<M(pxExvd7U6>0|tgIye9FfjNmt%jh#
z&(qC&Qjx(_BC(h4^?oG78i_0O<inY0XhQq8gX?!ml>a(EE9D;&$4s?gTqk+bGT*7S
zj{`4;?-XRi5X=4Uz+^p(!7S35vSXde2eaW(+8PGZ7D8}IGbvusAH^Yo7lkjXFh?4(
zmFh(cun2&3h5yJl+G}?7t&o4L-I%RyE|D*I=|%6t9QDo3iIkpgo|l=0D!?bLmLE`j
z-P-!=C`-YdHWzki*T@gcwu*}?dvD>Az9b~~)TJKkFGqizz2k9eV<{s}S}c(#vBi<q
zf}ZQ}5U!1cYYxl5jGHg<FKV2B>L(&(XjruT%sX~@Pri8JehZ49Dwc^YJZnettxkoH
zNx?9j_~NOoz#L_&uD5Im@uukU6cb>DjfyP`{=OrTppl-C%A{h*f<L$GZb3qls_SXq
znR^CrRSx7ou<~TVi_p4grq7WJk@ypK2+axpdThXU?IlWwx5Td34fFa|^>;6D7YuXv
zd5^-hjiDi=4kfMqWgu)rH(vy!Zg&`FGF-dik`It?YiJMbXnC#bhZiiYPcG5DKE0<2
z|5Uo^n5QZ>^{Hf&rwOnr3aE>{+hd|BV{3%fK6@X|b_?$^$uwQB(?iVd?hw=&KWW5U
z9%c8An8g{9^C0_u%G#S_of7wh>`CnqS4m&Pmnkd*M)SAD?cb$r%Q8M^IzU8KCv{CJ
zTea=Rh`0_mT$pYw_wG+#ir0CPCKgeFc=z(HX%$QT&@?EFgI^i-mYA;A5D;QGsJdm#
zOB^+LpP%yX+7d83hJ)>rb&YCu9Ql%0ZPyDX-{$Q>bdy5!?0r|-%1kEZ=b0#U0-;^P
zzSHnl3*xqdUY%hw*q04)dQchmh*w}z#4MuU6dbhg*h48fEA63vM&T2q_n4CxAMR0-
zE5$zYV0a4d4;qwCDNNk`;mG9Dl)9nl6rI|xgBA!PIkzz?NEZ97u5qlcA}Q**g#fbt
zg$CAwxdMIY&BtBOc~9l<td7oneHyWYlAL)k%u*!Huye{GB>7YCn3WZwE;*vFMYKdu
zI3X67*A5y7W1kP6b00qw)%Eg&q=QRZ>_J>JY0MFgiAQ!-aAbAfF1Ju_fy3>62%5N_
zH<O>L&s?h_^^UtKb61TNceF#jW)6*tg>Bb2s;pA>eMmdR{0FOg;M0dHeeKH{LZJAn
zBlzK+>yNZ_azEb)EujI_{QqIs!`;OZ1>NvO_qFNv+VgC7ONl-dWI!x#JTH~G(-;P7
z{xesC47b?B0n$p9;h|ehZxofyJ0bxo>WoUe9w*@03z>rl)Nm}ak_pR!=gWqQ7d_2n
zRSLVVa#5tBmrt<GEbXNuOiVxU?|<W`?ZjQQtbbBrUk_Z-IY$$LH0*0+o}&}uYdF>I
zbK{77ZGT2<5jxGGz_n^%bEMlerIh3@j2)JtURWUGjz0+ys7js(VK^71X0>Os+66@(
z-V+a`UYk9~RacVIgmHN8;^Z~a@jCx@C(^kfC%Jf3xx@e~9|?*x@JAa<#zgPMHtoxW
z4(X6tfX$w4P25_b=G@#E1NT^$d`<ZnT+4#^UwMia=-BGhh-j$e0omAGqiC~-mf4u9
z-dM&2(ObsmXN#g!yK>6HMICy36M~(SMn1Hbdh|SL^|O#80J%ymamp%Z8cBl$e>Xt~
zP91p{&U|8Yg;_)oj+n#LRKQhnXWNAgJ-64zt)b@n;f7iLnQ!{=9iG0;Y3C&55N%`a
z2915yj|C8j1{uApGYSD8F5J^_R<W*R%9k@}Rlde<<&cLU-d^x6nVfZZib?3gVUbf5
zYnQ7gpzgk7I?OimO=YJH|7y=B+2b30njEgP8qDoTw5>WWD}_Cfa0EvgJL{O@yBD6N
zNnL(dGRt|Uq-^kFc~Onm^<wXMe^QRKb#XT?ZwSFJa7Xz_x{_qh_>HZPt{hGpewAB%
zDs)#}4tUR1oU5QSNzvU)(}(=ZqjEwq>&Cf${t=nk45^EIWPp>`2%dk@cd_2kz)14K
z(nRnSdMHX>m=H2}ZzMCe40148u{y=oewps5;r-_w<7oEn!1&VHTXEL$_?CSJSY)oz
zRAlkER&!+HePQ48e%YJl*>`uy82p*R|CB6Qkjt&KeMF$ip?BiRCU<n`#M$00d{w4b
zd56;cJO`PMJWl=SZM4n7e!d|TfB&is3MGn}?U3pQT`FyC@)<p>r0<s9M#z6(eONNQ
zt6+MwrjNPh()lpCwz~2HInX|}_Y(u5>Pma0UditcvC^a7KM_+Jr&u(X>B`rJ4c3{P
zx8Sg3_u;3+e*y}JU%pJCLz_02U}_cG3o9!UQrJFC9<Inmm#B|*V6@Kn8@Bi?Pmc={
zgw#kDmD%l;J$+jpQR6}5!Z3Ancv94Ils<4r=9SS%{|Kithtr$fJ+Duk-SmFsm;k?7
zXDzANgZw^TJg+@^>&)_#Dmjq3Vd~xLJvzG6n=htjmB@M-Wl&WHn+m)<TMra^rT09H
zFRr9UZiRIcc|Y1Jkc{i?%5-t0oOtahc7K~f3)7EFVW>u!15C{aV_7&MUL@EbwnYCW
zrFzrB<1-!m(0!Eg?+1^o3un}YcU((+wx3{~qn_878Ltl4?R-b^%Rm<N+|idwZXcj7
zhfS1A%*uY8j-?{Me!5fh;x*(4(Xwpb10xGJQ<O)7zr1*%hz_7qy{_W_ZuccciCGOj
z^1AK6-~);A{WQyc_3~5(m2XN_dG_N=$i&2gc^h?}8nD_u@G^TF^=}TrG%DR{Rp!`H
zD#L2k(w96y6-@GN);^Z%>q)No(z)jD_Lm}-!t!+wZlJ}#JahZB>I%MvSrtj$fgPU{
zW&l8VqcAH2hoT^M5ENviFlq}%KP^fbsDE5jQ*-@>e%#HJ$Vc@5$kpO_-;8n7T9ULP
z_EU_~o*QOkUr7U*y0Hq8QyzvfX8+*KLDbdAuS@`K<vG4_{=^>xE5}R|P}5k0Als}B
z)ZcFpwgKDOx#{WAvQlucO&{PP5ELR{D=HiQ7^FA0mj|$tJk!w&F#O1!^L$>1l|>8;
zTB)t~>Wr&i{kwF01@T8tj^|5l8({3DIBk62d>!NJsD1&Ba~&FN4cc0d_v^U3*5MZa
zEACgn_v`Td>)<ytYyiLN{r;Qcd*`>7!s@>$e$odruKK`V7^>Df9nXJ@jy0Zeqlx*E
zBjouit~khg)9(-@_gTolc*C_qzW06Kgk%QMGyV=C@;@^p-)F09g^-y4KOujIsGrlZ
zKAf-Bag<{N9czO7&+)7e;%j;Ge~RaGNdGyi^|5;`mGD-;cai)@n2by-->}nuakQOr
IW9v%mfA@t6lmGw#
--- a/dom/indexedDB/test/unit/xpcshell-head-parent-process.js
+++ b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js
@@ -45,30 +45,28 @@ if (!this.runTest) {
   this.runTest = function()
   {
     if (SpecialPowers.isMainProcess()) {
       // XPCShell does not get a profile by default.
       do_get_profile();
 
       enableTesting();
       enableExperimental();
-      enableWasm();
     }
 
     Cu.importGlobalProperties(["indexedDB", "Blob", "File", "FileReader"]);
 
     do_test_pending();
     testGenerator.next();
   }
 }
 
 function finishTest()
 {
   if (SpecialPowers.isMainProcess()) {
-    resetWasm();
     resetExperimental();
     resetTesting();
 
     SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher",
                                                  "free");
   }
 
   SpecialPowers.removeFiles();
@@ -209,26 +207,16 @@ function enableTesting()
   SpecialPowers.setBoolPref("dom.indexedDB.testing", true);
 }
 
 function resetTesting()
 {
   SpecialPowers.clearUserPref("dom.indexedDB.testing");
 }
 
-function enableWasm()
-{
-  SpecialPowers.setBoolPref("javascript.options.wasm", true);
-}
-
-function resetWasm()
-{
-  SpecialPowers.clearUserPref("javascript.options.wasm");
-}
-
 function gc()
 {
   Cu.forceGC();
   Cu.forceCC();
 }
 
 function scheduleGC()
 {
--- a/dom/ipc/ContentPrefs.cpp
+++ b/dom/ipc/ContentPrefs.cpp
@@ -1,16 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=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/. */
 
 #include "ContentPrefs.h"
 
+/************************************************************
+ *    DO NOT ADD PREFS TO THIS LIST WITHOUT DOM PEER REVIEW *
+ ************************************************************/
+
 const char* mozilla::dom::ContentPrefs::gInitPrefs[] = {
   "accessibility.monoaudio.enable",
   "accessibility.mouse_focuses_formcontrol",
   "accessibility.tabfocus_applies_to_xul",
   "app.update.channel",
   "browser.dom.window.dump.enabled",
   "browser.sessionhistory.max_entries",
   "browser.sessionhistory.max_total_viewers",
@@ -47,16 +51,17 @@ const char* mozilla::dom::ContentPrefs::
   "dom.performance.enable_user_timing_logging",
   "dom.storage.testing",
   "dom.url.encode_decode_hash",
   "dom.url.getters_decode_hash",
   "dom.use_watchdog",
   "dom.vibrator.enabled",
   "dom.vibrator.max_vibrate_list_len",
   "dom.vibrator.max_vibrate_ms",
+  "dom.webcomponents.enabled",
   "focusmanager.testmode",
   "font.size.inflation.disabledInMasterProcess",
   "font.size.inflation.emPerLine",
   "font.size.inflation.forceEnabled",
   "font.size.inflation.lineThreshold",
   "font.size.inflation.mappingIntercept",
   "font.size.inflation.maxRatio",
   "font.size.inflation.minTwips",
--- a/dom/media/webaudio/test/test_audioContextSuspendResumeClose.html
+++ b/dom/media/webaudio/test/test_audioContextSuspendResumeClose.html
@@ -43,20 +43,22 @@ function tryToCreateNodeOnClosedContext(
 
       expectException(function() {
         ctx[e.name].apply(ctx, e.args);
       }, DOMException.INVALID_STATE_ERR);
     });
 }
 
 function loadFile(url, callback) {
+  todo(false, "loadFile: " + url);
   var xhr = new XMLHttpRequest();
   xhr.open("GET", url, true);
   xhr.responseType = "arraybuffer";
   xhr.onload = function() {
+    todo(false, "loadFile: " + url + " calling callback...");
     callback(xhr.response);
   };
   xhr.send();
 }
 
 // createBuffer, createPeriodicWave and decodeAudioData should work on a context
 // that has `state` == "closed"
 function tryLegalOpeerationsOnClosedContext(ctx) {
--- a/dom/network/UDPSocketParent.cpp
+++ b/dom/network/UDPSocketParent.cpp
@@ -239,17 +239,17 @@ static void CheckSTSThread()
 // Proxy the Connect() request to the STS thread, since it may block and
 // should be done there.
 mozilla::ipc::IPCResult
 UDPSocketParent::RecvConnect(const UDPAddressInfo& aAddressInfo)
 {
   nsCOMPtr<nsIEventTarget> thread(NS_GetCurrentThread());
   Unused <<
     NS_WARN_IF(NS_FAILED(GetSTSThread()->Dispatch(WrapRunnable(
-                                                    this,
+                                                    RefPtr<UDPSocketParent>(this),
                                                     &UDPSocketParent::DoConnect,
                                                     mSocket,
                                                     thread,
                                                     aAddressInfo),
                                                   NS_DISPATCH_NORMAL)));
   return IPC_OK();
 }
 
@@ -261,17 +261,17 @@ UDPSocketParent::DoSendConnectResponse(c
 }
 
 void
 UDPSocketParent::SendConnectResponse(nsIEventTarget *aThread,
                                      const UDPAddressInfo& aAddressInfo)
 {
   Unused <<
     NS_WARN_IF(NS_FAILED(aThread->Dispatch(WrapRunnable(
-                                             this,
+                                             RefPtr<UDPSocketParent>(this),
                                              &UDPSocketParent::DoSendConnectResponse,
                                              aAddressInfo),
                                            NS_DISPATCH_NORMAL)));
 }
 
 // Runs on STS thread
 void
 UDPSocketParent::DoConnect(nsCOMPtr<nsIUDPSocket>& aSocket,
@@ -581,16 +581,16 @@ UDPSocketParent::FireInternalError(uint3
 
 void
 UDPSocketParent::SendInternalError(nsIEventTarget *aThread,
                                    uint32_t aLineNo)
 {
   UDPSOCKET_LOG(("SendInternalError: %u", aLineNo));
   Unused <<
     NS_WARN_IF(NS_FAILED(aThread->Dispatch(WrapRunnable(
-                                             this,
+                                             RefPtr<UDPSocketParent>(this),
                                              &UDPSocketParent::FireInternalError,
                                              aLineNo),
                                            NS_DISPATCH_NORMAL)));
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/promise/tests/test_webassembly_compile.html
+++ b/dom/promise/tests/test_webassembly_compile.html
@@ -162,13 +162,13 @@ function runTest() {
     return;
   }
 
   var test = tests.shift();
   test();
 }
 
 SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv({"set": [["javascript.options.wasm", true]]}, runTest);
+runTest();
 </script>
 </body>
 </html>
 
--- a/dom/tests/mochitest/general/test_interfaces.js
+++ b/dom/tests/mochitest/general/test_interfaces.js
@@ -69,16 +69,17 @@ var ecmaGlobals =
     "TypeError",
     "Uint16Array",
     "Uint32Array",
     "Uint8Array",
     "Uint8ClampedArray",
     "URIError",
     "WeakMap",
     "WeakSet",
+    {name: "WebAssembly", disabled: !SpecialPowers.Cu.getJSTestingFunctions().wasmIsSupported()}
   ];
 // IMPORTANT: Do not change the list above without review from
 //            a JavaScript Engine peer!
 
 // IMPORTANT: Do not change the list below without review from a DOM peer,
 //            except to remove items from it!
 //
 // This is a list of interfaces that were prefixed with 'moz' instead of 'Moz'.
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -471,16 +471,17 @@ private:
   bool mIsWorkerScript;
   bool mFailed;
   nsCOMPtr<nsIInputStreamPump> mPump;
   nsCOMPtr<nsIURI> mBaseURI;
   mozilla::dom::ChannelInfo mChannelInfo;
   UniquePtr<PrincipalInfo> mPrincipalInfo;
   nsCString mCSPHeaderValue;
   nsCString mCSPReportOnlyHeaderValue;
+  nsCString mReferrerPolicyHeaderValue;
 };
 
 NS_IMPL_ISUPPORTS(CacheScriptLoader, nsIStreamLoaderObserver)
 
 class CachePromiseHandler final : public PromiseNativeHandler
 {
 public:
   NS_DECL_ISUPPORTS
@@ -617,16 +618,20 @@ private:
     MOZ_ASSERT(aIndex < mLoadInfos.Length());
     ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
 
     loadInfo.mLoadResult = aRv;
 
     MOZ_ASSERT(!loadInfo.mLoadingFinished);
     loadInfo.mLoadingFinished = true;
 
+    if (IsMainWorkerScript() && NS_SUCCEEDED(aRv)) {
+      MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate->PrincipalURIMatchesScriptURL());
+    }
+
     MaybeExecuteFinishedScripts(aIndex);
   }
 
   void
   MaybeExecuteFinishedScripts(uint32_t aIndex)
   {
     AssertIsOnMainThread();
     MOZ_ASSERT(aIndex < mLoadInfos.Length());
@@ -1132,52 +1137,56 @@ private:
     // worker's primary script.
     if (IsMainWorkerScript()) {
       // Take care of the base URI first.
       mWorkerPrivate->SetBaseURI(finalURI);
 
       // Store the channel info if needed.
       mWorkerPrivate->InitChannelInfo(channel);
 
+      // Our final channel principal should match the original principal
+      // in terms of the origin.
       MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate->FinalChannelPrincipalIsValid(channel));
 
+      // However, we must still override the principal since the nsIPrincipal
+      // URL may be different due to same-origin redirects.  Unfortunately this
+      // URL must exactly match the final worker script URL in order to
+      // properly set the referrer header on fetch/xhr requests.  If bug 1340694
+      // is ever fixed this can be removed.
+      rv = mWorkerPrivate->SetPrincipalFromChannel(channel);
+      NS_ENSURE_SUCCESS(rv, rv);
+
       // We did inherit CSP in bug 1223647. If we do not already have a CSP, we
       // should get it from the HTTP headers on the worker script.
       if (!mWorkerPrivate->GetCSP() && CSPService::sCSPEnabled) {
         rv = mWorkerPrivate->SetCSPFromHeaderValues(tCspHeaderValue,
                                                     tCspROHeaderValue);
         NS_ENSURE_SUCCESS(rv, rv);
       }
+
+      mWorkerPrivate->SetReferrerPolicyFromHeaderValue(tRPHeaderCValue);
+
       WorkerPrivate* parent = mWorkerPrivate->GetParent();
       if (parent) {
         // XHR Params Allowed
         mWorkerPrivate->SetXHRParamsAllowed(parent->XHRParamsAllowed());
       }
     }
 
-    NS_ConvertUTF8toUTF16 tRPHeaderValue(tRPHeaderCValue);
-    // If there's a Referrer-Policy header, apply it.
-    if (!tRPHeaderValue.IsEmpty()) {
-      net::ReferrerPolicy policy =
-        nsContentUtils::GetReferrerPolicyFromHeader(tRPHeaderValue);
-      if (policy != net::RP_Unset) {
-        mWorkerPrivate->SetReferrerPolicy(policy);
-      }
-    }
-
     return NS_OK;
   }
 
   void
   DataReceivedFromCache(uint32_t aIndex, const uint8_t* aString,
                         uint32_t aStringLen,
                         const mozilla::dom::ChannelInfo& aChannelInfo,
                         UniquePtr<PrincipalInfo> aPrincipalInfo,
                         const nsACString& aCSPHeaderValue,
-                        const nsACString& aCSPReportOnlyHeaderValue)
+                        const nsACString& aCSPReportOnlyHeaderValue,
+                        const nsACString& aReferrerPolicyHeaderValue)
   {
     AssertIsOnMainThread();
     MOZ_ASSERT(aIndex < mLoadInfos.Length());
     ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
     MOZ_ASSERT(loadInfo.mCacheStatus == ScriptLoadInfo::Cached);
 
     nsCOMPtr<nsIPrincipal> responsePrincipal =
       PrincipalInfoToPrincipal(*aPrincipalInfo);
@@ -1225,23 +1234,26 @@ private:
       mWorkerPrivate->InitChannelInfo(aChannelInfo);
 
       nsILoadGroup* loadGroup = mWorkerPrivate->GetLoadGroup();
       MOZ_DIAGNOSTIC_ASSERT(loadGroup);
 
       // Override the principal on the WorkerPrivate.  This is only necessary
       // in order to get a principal with exactly the correct URL.  The fetch
       // referrer logic depends on the WorkerPrivate principal having a URL
-      // that matches the worker script URL.
+      // that matches the worker script URL.  If bug 1340694 is ever fixed
+      // this can be removed.
       rv = mWorkerPrivate->SetPrincipalOnMainThread(responsePrincipal, loadGroup);
       MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
 
       rv = mWorkerPrivate->SetCSPFromHeaderValues(aCSPHeaderValue,
                                                   aCSPReportOnlyHeaderValue);
       MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
+
+      mWorkerPrivate->SetReferrerPolicyFromHeaderValue(aReferrerPolicyHeaderValue);
     }
 
     if (NS_SUCCEEDED(rv)) {
       DataReceived();
     }
 
     LoadingFinished(aIndex, rv);
   }
@@ -1647,30 +1659,33 @@ CacheScriptLoader::ResolvedCallback(JSCo
 
   InternalHeaders* headers = response->GetInternalHeaders();
 
   IgnoredErrorResult ignored;
   headers->Get(NS_LITERAL_CSTRING("content-security-policy"),
                mCSPHeaderValue, ignored);
   headers->Get(NS_LITERAL_CSTRING("content-security-policy-report-only"),
                mCSPReportOnlyHeaderValue, ignored);
+  headers->Get(NS_LITERAL_CSTRING("referrer-policy"),
+               mReferrerPolicyHeaderValue, ignored);
 
   nsCOMPtr<nsIInputStream> inputStream;
   response->GetBody(getter_AddRefs(inputStream));
   mChannelInfo = response->GetChannelInfo();
   const UniquePtr<PrincipalInfo>& pInfo = response->GetPrincipalInfo();
   if (pInfo) {
     mPrincipalInfo = mozilla::MakeUnique<PrincipalInfo>(*pInfo);
   }
 
   if (!inputStream) {
     mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
     mRunnable->DataReceivedFromCache(mIndex, (uint8_t*)"", 0, mChannelInfo,
                                      Move(mPrincipalInfo), mCSPHeaderValue,
-                                     mCSPReportOnlyHeaderValue);
+                                     mCSPReportOnlyHeaderValue,
+                                     mReferrerPolicyHeaderValue);
     return;
   }
 
   MOZ_ASSERT(!mPump);
   rv = NS_NewInputStreamPump(getter_AddRefs(mPump), inputStream);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     Fail(rv);
     return;
@@ -1721,17 +1736,18 @@ CacheScriptLoader::OnStreamComplete(nsIS
   }
 
   MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::ReadingFromCache);
   mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
 
   MOZ_ASSERT(mPrincipalInfo);
   mRunnable->DataReceivedFromCache(mIndex, aString, aStringLen, mChannelInfo,
                                    Move(mPrincipalInfo), mCSPHeaderValue,
-                                   mCSPReportOnlyHeaderValue);
+                                   mCSPReportOnlyHeaderValue,
+                                   mReferrerPolicyHeaderValue);
   return NS_OK;
 }
 
 class ChannelGetterRunnable final : public WorkerMainThreadRunnable
 {
   const nsAString& mScriptURL;
   WorkerLoadInfo& mLoadInfo;
   nsresult mResult;
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1980,16 +1980,67 @@ WorkerLoadInfo::FinalChannelPrincipalIsV
 
 bool
 WorkerLoadInfo::PrincipalIsValid() const
 {
   return mPrincipal && mPrincipalInfo &&
          mPrincipalInfo->type() != PrincipalInfo::T__None &&
          mPrincipalInfo->type() <= PrincipalInfo::T__Last;
 }
+
+bool
+WorkerLoadInfo::PrincipalURIMatchesScriptURL()
+{
+  AssertIsOnMainThread();
+
+  nsAutoCString scheme;
+  nsresult rv = mBaseURI->GetScheme(scheme);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  // A system principal must either be a blob URL or a resource JSM.
+  if (mPrincipal->GetIsSystemPrincipal()) {
+    if (scheme == NS_LITERAL_CSTRING("blob")) {
+      return true;
+    }
+
+    bool isResource = false;
+    nsresult rv = NS_URIChainHasFlags(mBaseURI,
+                                      nsIProtocolHandler::URI_IS_UI_RESOURCE,
+                                      &isResource);
+    NS_ENSURE_SUCCESS(rv, false);
+
+    return isResource;
+  }
+
+  // A null principal can occur for a data URL worker script or a blob URL
+  // worker script from a sandboxed iframe.
+  if (mPrincipal->GetIsNullPrincipal()) {
+    return scheme == NS_LITERAL_CSTRING("data") ||
+           scheme == NS_LITERAL_CSTRING("blob");
+  }
+
+  // The principal for a blob: URL worker script does not have a matching URL.
+  // This is likely a bug in our referer setting logic, but exempt it for now.
+  // This is another reason we should fix bug 1340694 so that referer does not
+  // depend on the principal URI.
+  if (scheme == NS_LITERAL_CSTRING("blob")) {
+    return true;
+  }
+
+  nsCOMPtr<nsIURI> principalURI;
+  rv = mPrincipal->GetURI(getter_AddRefs(principalURI));
+  NS_ENSURE_SUCCESS(rv, false);
+  NS_ENSURE_TRUE(principalURI, false);
+
+  bool equal = false;
+  rv = principalURI->Equals(mBaseURI, &equal);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  return equal;
+}
 #endif // defined(DEBUG) || !defined(RELEASE_OR_BETA)
 
 bool
 WorkerLoadInfo::ProxyReleaseMainThreadObjects(WorkerPrivate* aWorkerPrivate)
 {
   nsCOMPtr<nsILoadGroup> nullLoadGroup;
   return ProxyReleaseMainThreadObjects(aWorkerPrivate, nullLoadGroup);
 }
@@ -2635,16 +2686,36 @@ WorkerPrivateParent<Derived>::SetCSPFrom
 
   if (hasReferrerPolicy) {
     mLoadInfo.mReferrerPolicy = static_cast<net::ReferrerPolicy>(rp);
   }
 
   return NS_OK;
 }
 
+template <class Derived>
+void
+WorkerPrivateParent<Derived>::SetReferrerPolicyFromHeaderValue(
+                                  const nsACString& aReferrerPolicyHeaderValue)
+{
+  NS_ConvertUTF8toUTF16 headerValue(aReferrerPolicyHeaderValue);
+
+  if (headerValue.IsEmpty()) {
+    return;
+  }
+
+  net::ReferrerPolicy policy =
+    nsContentUtils::GetReferrerPolicyFromHeader(headerValue);
+  if (policy == net::RP_Unset) {
+    return;
+  }
+
+  SetReferrerPolicy(policy);
+}
+
 
 // Can't use NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerPrivateParent) because of the
 // templates.
 template <class Derived>
 typename WorkerPrivateParent<Derived>::cycleCollection
   WorkerPrivateParent<Derived>::_cycleCollectorGlobal =
     WorkerPrivateParent<Derived>::cycleCollection();
 
@@ -3864,16 +3935,23 @@ WorkerPrivateParent<Derived>::SetPrincip
 
 #if defined(DEBUG) || !defined(RELEASE_OR_BETA)
 template <class Derived>
 bool
 WorkerPrivateParent<Derived>::FinalChannelPrincipalIsValid(nsIChannel* aChannel)
 {
   return mLoadInfo.FinalChannelPrincipalIsValid(aChannel);
 }
+
+template <class Derived>
+bool
+WorkerPrivateParent<Derived>::PrincipalURIMatchesScriptURL()
+{
+  return mLoadInfo.PrincipalURIMatchesScriptURL();
+}
 #endif
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup)
 {
   AssertIsOnMainThread();
 
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -650,16 +650,19 @@ public:
   SetPrincipalOnMainThread(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup);
 
   nsresult
   SetPrincipalFromChannel(nsIChannel* aChannel);
 
 #if defined(DEBUG) || !defined(RELEASE_OR_BETA)
   bool
   FinalChannelPrincipalIsValid(nsIChannel* aChannel);
+
+  bool
+  PrincipalURIMatchesScriptURL();
 #endif
 
   bool
   UsesSystemPrincipal() const
   {
     return mLoadInfo.mPrincipalIsSystem;
   }
 
@@ -698,16 +701,19 @@ public:
     AssertIsOnMainThread();
     mLoadInfo.mCSP = aCSP;
   }
 
   nsresult
   SetCSPFromHeaderValues(const nsACString& aCSPHeaderValue,
                          const nsACString& aCSPReportOnlyHeaderValue);
 
+  void
+  SetReferrerPolicyFromHeaderValue(const nsACString& aReferrerPolicyHeaderValue);
+
   net::ReferrerPolicy
   GetReferrerPolicy() const
   {
     return mLoadInfo.mReferrerPolicy;
   }
 
   void
   SetReferrerPolicy(net::ReferrerPolicy aReferrerPolicy)
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -291,16 +291,19 @@ struct WorkerLoadInfo
   SetPrincipalFromChannel(nsIChannel* aChannel);
 
 #if defined(DEBUG) || !defined(RELEASE_OR_BETA)
   bool
   FinalChannelPrincipalIsValid(nsIChannel* aChannel);
 
   bool
   PrincipalIsValid() const;
+
+  bool
+  PrincipalURIMatchesScriptURL();
 #endif
 
   bool
   ProxyReleaseMainThreadObjects(WorkerPrivate* aWorkerPrivate);
 
   bool
   ProxyReleaseMainThreadObjects(WorkerPrivate* aWorkerPrivate,
                                 nsCOMPtr<nsILoadGroup>& aLoadGroupToCancel);
--- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
+++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
@@ -64,16 +64,17 @@ var ecmaGlobals =
     "TypeError",
     "Uint16Array",
     "Uint32Array",
     "Uint8Array",
     "Uint8ClampedArray",
     "URIError",
     "WeakMap",
     "WeakSet",
+    {name: "WebAssembly", optional: true}
   ];
 // IMPORTANT: Do not change the list above without review from
 //            a JavaScript Engine peer!
 
 // IMPORTANT: Do not change the list below without review from a DOM peer!
 var interfaceNamesInGlobalScope =
   [
 // IMPORTANT: Do not change this list without review from a DOM peer!
@@ -247,16 +248,18 @@ function createInterfaceMap(version, use
         ok(!("pref" in entry), "Bogus pref annotation for " + entry.name);
         if ((entry.nightly === !isNightly) ||
             (entry.nightlyAndroid === !(isAndroid && isNightly) && isAndroid) ||
             (entry.nonReleaseAndroid === !(isAndroid && !isRelease) && isAndroid) ||
             (entry.desktop === !isDesktop) ||
             (entry.android === !isAndroid && !entry.nonReleaseAndroid && !entry.nightlyAndroid) ||
             (entry.release === !isRelease)) {
           interfaceMap[entry.name] = false;
+        } else if (entry.optional) {
+          interfaceMap[entry.name] = "optional";
         } else {
           interfaceMap[entry.name] = true;
         }
       }
     }
   }
 
   addInterfaces(ecmaGlobals);
@@ -267,27 +270,31 @@ function createInterfaceMap(version, use
 
 function runTest(version, userAgent) {
   var interfaceMap = createInterfaceMap(version, userAgent);
   for (var name of Object.getOwnPropertyNames(self)) {
     // An interface name should start with an upper case character.
     if (!/^[A-Z]/.test(name)) {
       continue;
     }
-    ok(interfaceMap[name],
+    ok(interfaceMap[name] === "optional" || interfaceMap[name],
        "If this is failing: DANGER, are you sure you want to expose the new interface " + name +
        " to all webpages as a property on the service worker? Do not make a change to this file without a " +
        " review from a DOM peer for that specific change!!! (or a JS peer for changes to ecmaGlobals)");
     delete interfaceMap[name];
   }
   for (var name of Object.keys(interfaceMap)) {
-    ok(name in self === interfaceMap[name],
-       name + " should " + (interfaceMap[name] ? "" : " NOT") + " be defined on the global scope");
-    if (!interfaceMap[name]) {
+    if (interfaceMap[name] === "optional") {
       delete interfaceMap[name];
+    } else {
+      ok(name in self === interfaceMap[name],
+         name + " should " + (interfaceMap[name] ? "" : " NOT") + " be defined on the global scope");
+      if (!interfaceMap[name]) {
+        delete interfaceMap[name];
+      }
     }
   }
   is(Object.keys(interfaceMap).length, 0,
      "The following interface(s) are not enumerated: " + Object.keys(interfaceMap).join(", "));
 }
 
 workerTestGetVersion(function(version) {
   workerTestGetUserAgent(function(userAgent) {
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -64,16 +64,17 @@ var ecmaGlobals =
     "TypeError",
     "Uint16Array",
     "Uint32Array",
     "Uint8Array",
     "Uint8ClampedArray",
     "URIError",
     "WeakMap",
     "WeakSet",
+    {name: "WebAssembly", optional: true}
   ];
 // IMPORTANT: Do not change the list above without review from
 //            a JavaScript Engine peer!
 
 // IMPORTANT: Do not change the list below without review from a DOM peer!
 var interfaceNamesInGlobalScope =
   [
 // IMPORTANT: Do not change this list without review from a DOM peer!
@@ -261,16 +262,18 @@ function createInterfaceMap(version, use
         ok(!("pref" in entry), "Bogus pref annotation for " + entry.name);
         if ((entry.nightly === !isNightly) ||
             (entry.nightlyAndroid === !(isAndroid && isNightly) && isAndroid) ||
             (entry.desktop === !isDesktop) ||
             (entry.android === !isAndroid && !entry.nightlyAndroid) ||
             (entry.release === !isRelease) ||
             entry.disabled) {
           interfaceMap[entry.name] = false;
+        } else if (entry.optional) {
+          interfaceMap[entry.name] = "optional";
         } else {
           interfaceMap[entry.name] = true;
         }
       }
     }
   }
 
   addInterfaces(ecmaGlobals);
@@ -281,27 +284,31 @@ function createInterfaceMap(version, use
 
 function runTest(version, userAgent) {
   var interfaceMap = createInterfaceMap(version, userAgent);
   for (var name of Object.getOwnPropertyNames(self)) {
     // An interface name should start with an upper case character.
     if (!/^[A-Z]/.test(name)) {
       continue;
     }
-    ok(interfaceMap[name],
+    ok(interfaceMap[name] === "optional" || interfaceMap[name],
        "If this is failing: DANGER, are you sure you want to expose the new interface " + name +
        " to all webpages as a property on the worker? Do not make a change to this file without a " +
        " review from a DOM peer for that specific change!!! (or a JS peer for changes to ecmaGlobals)");
     delete interfaceMap[name];
   }
   for (var name of Object.keys(interfaceMap)) {
-    ok(name in self === interfaceMap[name],
-       name + " should " + (interfaceMap[name] ? "" : " NOT") + " be defined on the global scope");
-    if (!interfaceMap[name]) {
+    if (interfaceMap[name] === "optional") {
       delete interfaceMap[name];
+    } else {
+      ok(name in self === interfaceMap[name],
+         name + " should " + (interfaceMap[name] ? "" : " NOT") + " be defined on the global scope");
+      if (!interfaceMap[name]) {
+        delete interfaceMap[name];
+      }
     }
   }
   is(Object.keys(interfaceMap).length, 0,
      "The following interface(s) are not enumerated: " + Object.keys(interfaceMap).join(", "));
 }
 
 workerTestGetVersion(function(version) {
   workerTestGetUserAgent(function(userAgent) {
--- a/gfx/2d/SourceSurfaceSkia.cpp
+++ b/gfx/2d/SourceSurfaceSkia.cpp
@@ -36,39 +36,50 @@ SourceSurfaceSkia::GetSize() const
 
 SurfaceFormat
 SourceSurfaceSkia::GetFormat() const
 {
   return mFormat;
 }
 
 static sk_sp<SkData>
-MakeSkData(unsigned char* aData, const IntSize& aSize, int32_t aStride)
+MakeSkData(void* aData, int32_t aHeight, size_t aStride)
 {
   CheckedInt<size_t> size = aStride;
-  size *= aSize.height;
+  size *= aHeight;
   if (size.isValid()) {
     void* mem = sk_malloc_flags(size.value(), 0);
     if (mem) {
       if (aData) {
         memcpy(mem, aData, size.value());
       }
       return SkData::MakeFromMalloc(mem, size.value());
     }
   }
   return nullptr;
 }
 
+static sk_sp<SkImage>
+ReadSkImage(const sk_sp<SkImage>& aImage, const SkImageInfo& aInfo, size_t aStride)
+{
+  if (sk_sp<SkData> data = MakeSkData(nullptr, aInfo.height(), aStride)) {
+    if (aImage->readPixels(aInfo, data->writable_data(), aStride, 0, 0, SkImage::kDisallow_CachingHint)) {
+      return SkImage::MakeRasterData(aInfo, data, aStride);
+    }
+  }
+  return nullptr;
+}
+
 bool
 SourceSurfaceSkia::InitFromData(unsigned char* aData,
                                 const IntSize &aSize,
                                 int32_t aStride,
                                 SurfaceFormat aFormat)
 {
-  sk_sp<SkData> data = MakeSkData(aData, aSize, aStride);
+  sk_sp<SkData> data = MakeSkData(aData, aSize.height, aStride);
   if (!data) {
     return false;
   }
 
   SkImageInfo info = MakeSkiaImageInfo(aSize, aFormat);
   mImage = SkImage::MakeRasterData(info, data, aStride);
   if (!mImage) {
     return false;
@@ -120,26 +131,22 @@ SourceSurfaceSkia::InitFromImage(const s
   }
 
   return true;
 }
 
 uint8_t*
 SourceSurfaceSkia::GetData()
 {
+  if (!mImage) {
+    return nullptr;
+  }
 #ifdef USE_SKIA_GPU
   if (mImage->isTextureBacked()) {
-    sk_sp<SkImage> raster;
-    if (sk_sp<SkData> data = MakeSkData(nullptr, mSize, mStride)) {
-      SkImageInfo info = MakeSkiaImageInfo(mSize, mFormat);
-      if (mImage->readPixels(info, data->writable_data(), mStride, 0, 0, SkImage::kDisallow_CachingHint)) {
-        raster = SkImage::MakeRasterData(info, data, mStride);
-      }
-    }
-    if (raster) {
+    if (sk_sp<SkImage> raster = ReadSkImage(mImage, MakeSkiaImageInfo(mSize, mFormat), mStride)) {
       mImage = raster;
     } else {
       gfxCriticalError() << "Failed making Skia raster image for GPU surface";
     }
   }
 #endif
   SkPixmap pixmap;
   if (!mImage->peekPixels(&pixmap)) {
@@ -154,17 +161,17 @@ SourceSurfaceSkia::DrawTargetWillChange(
   if (mDrawTarget) {
     // Raster snapshots do not use Skia's internal copy-on-write mechanism,
     // so we need to do an explicit copy here.
     // GPU snapshots, for which peekPixels is false, will already be dealt
     // with automatically via the internal copy-on-write mechanism, so we
     // don't need to do anything for them here.
     SkPixmap pixmap;
     if (mImage->peekPixels(&pixmap)) {
-      mImage = SkImage::MakeRasterCopy(pixmap);
+      mImage = ReadSkImage(mImage, pixmap.info(), pixmap.rowBytes());
       if (!mImage) {
         gfxCriticalError() << "Failed copying Skia raster snapshot";
       }
     }
     mDrawTarget = nullptr;
   }
 }
 
--- a/gfx/layers/SourceSurfaceSharedData.h
+++ b/gfx/layers/SourceSurfaceSharedData.h
@@ -99,25 +99,37 @@ public:
    *   NS_ERROR_NOT_AVAILABLE -- handle was closed, need to reallocate.
    *   NS_ERROR_FAILURE -- failed to create a handle to share.
    */
   nsresult ShareToProcess(base::ProcessId aPid,
                           SharedMemoryBasic::Handle& aHandle);
 
   /**
    * Indicates the buffer is not expected to be shared with any more processes.
-   * May release the handle if possible (see CloseHandleInternal). */
+   * May release the handle if possible (see CloseHandleInternal).
+   */
   void FinishedSharing()
   {
     MutexAutoLock lock(mMutex);
     mShared = true;
     CloseHandleInternal();
   }
 
   /**
+   * Indicates whether or not the buffer can be shared with another process
+   * without reallocating. Note that this is racy and should only be used for
+   * informational/reporting purposes.
+   */
+  bool CanShare() const
+  {
+    MutexAutoLock lock(mMutex);
+    return !mClosed;
+  }
+
+  /**
    * Allocate a new shared memory buffer so that we can get a new handle for
    * sharing to new processes. ShareToProcess must have failed with
    * NS_ERROR_NOT_AVAILABLE in order for this to be safe to call. Returns true
    * if the operation succeeds. If it fails, there is no state change.
    */
   bool ReallocHandle();
 
   /**
@@ -145,17 +157,17 @@ private:
   }
 
   /**
    * Attempt to close the handle. Only if the buffer has been both finalized
    * and we have completed sharing will it be released.
    */
   void CloseHandleInternal();
 
-  Mutex mMutex;
+  mutable Mutex mMutex;
   int32_t mStride;
   int32_t mMapCount;
   IntSize mSize;
   RefPtr<SharedMemoryBasic> mBuf;
   RefPtr<SharedMemoryBasic> mOldBuf;
   SurfaceFormat mFormat;
   bool mClosed : 1;
   bool mFinalized : 1;
--- a/gfx/ots/README.mozilla
+++ b/gfx/ots/README.mozilla
@@ -1,11 +1,11 @@
 This is the Sanitiser for OpenType project, from http://code.google.com/p/ots/.
 
 Our reference repository is https://github.com/khaledhosny/ots/.
 
-Current revision: ba8417620956a920ed1f05a2f666fb6317fb10cb
+Current revision: ba8417620956a920ed1f05a2f666fb6317fb10cb (5.1.0)
 
 Upstream files included: LICENSE, src/, include/
 
 Additional files: README.mozilla, src/moz.build
 
 Additional patch: ots-visibility.patch (bug 711079).
--- a/gfx/ots/sync.sh
+++ b/gfx/ots/sync.sh
@@ -17,13 +17,14 @@ cp -r $1/src/* .
 cd ..
 
 echo "Updating include..."
 rm -rf include/
 cp -r $1/include .
 
 echo "Updating README.mozilla..."
 REVISION=`cd $1; git log | head -1 | sed "s/commit //"`
-sed -e "s/\(Current revision: \).*/\1$REVISION/" README.mozilla > README.tmp
+VERSION=`cd $1; git describe | cut -d '-' -f 1 | sed 's/v//'`
+sed -e "s/\(Current revision: \).*/\1$REVISION \($VERSION\)/" README.mozilla > README.tmp
 mv README.tmp README.mozilla
 
 echo "Applying ots-visibility.patch..."
 patch -p3 < ots-visibility.patch
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -456,16 +456,17 @@ private:
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.print-histogram",  FPSPrintHistogram, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.write-to-file", WriteFPSToFile, bool, false);
   DECL_GFX_PREF(Once, "layers.acceleration.force-enabled",     LayersAccelerationForceEnabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Live, "layers.advanced.border-layers",         LayersAllowBorderLayers, bool, false);
   DECL_GFX_PREF(Live, "layers.advanced.text-layers",           LayersAllowTextLayers, bool, false);
   DECL_GFX_PREF(Live, "layers.advanced.bullet-layers",         LayersAllowBulletLayers, bool, false);
   DECL_GFX_PREF(Live, "layers.advanced.caret-layers",          LayersAllowCaretLayers, bool, false);
   DECL_GFX_PREF(Live, "layers.advanced.boxshadow-outer-layers", LayersAllowOuterBoxShadow, bool, false);
+  DECL_GFX_PREF(Live, "layers.advanced.outline-layers",        LayersAllowOutlineLayers, bool, false);
   DECL_GFX_PREF(Once, "layers.allow-d3d9-fallback",            LayersAllowD3D9Fallback, bool, false);
   DECL_GFX_PREF(Once, "layers.amd-switchable-gfx.enabled",     LayersAMDSwitchableGfxEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.async-pan-zoom.enabled",         AsyncPanZoomEnabledDoNotUseDirectly, bool, true);
   DECL_GFX_PREF(Once, "layers.async-pan-zoom.separate-event-thread", AsyncPanZoomSeparateEventThread, bool, false);
   DECL_GFX_PREF(Live, "layers.bench.enabled",                  LayersBenchEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.bufferrotation.enabled",         BufferRotationEnabled, bool, true);
   DECL_GFX_PREF(Live, "layers.child-process-shutdown",         ChildProcessShutdown, bool, true);
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
--- a/image/AnimationSurfaceProvider.cpp
+++ b/image/AnimationSurfaceProvider.cpp
@@ -113,25 +113,27 @@ AnimationSurfaceProvider::LogicalSizeInB
   // once bug 1289954 is complete.
   IntSize size = GetSurfaceKey().Size();
   return 3 * size.width * size.height * sizeof(uint32_t);
 }
 
 void
 AnimationSurfaceProvider::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
                                                  size_t& aHeapSizeOut,
-                                                 size_t& aNonHeapSizeOut)
+                                                 size_t& aNonHeapSizeOut,
+                                                 size_t& aSharedHandlesOut)
 {
   // Note that the surface cache lock is already held here, and then we acquire
   // mFramesMutex. For this method, this ordering is unavoidable, which means
   // that we must be careful to always use the same ordering elsewhere.
   MutexAutoLock lock(mFramesMutex);
 
   for (const RawAccessFrameRef& frame : mFrames) {
-    frame->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut, aNonHeapSizeOut);
+    frame->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut,
+                                  aNonHeapSizeOut, aSharedHandlesOut);
   }
 }
 
 void
 AnimationSurfaceProvider::Run()
 {
   MutexAutoLock lock(mDecodingMutex);
 
--- a/image/AnimationSurfaceProvider.h
+++ b/image/AnimationSurfaceProvider.h
@@ -42,17 +42,18 @@ public:
   // We use the ISurfaceProvider constructor of DrawableSurface to indicate that
   // our surfaces are computed lazily.
   DrawableSurface Surface() override { return DrawableSurface(WrapNotNull(this)); }
 
   bool IsFinished() const override;
   size_t LogicalSizeInBytes() const override;
   void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
                               size_t& aHeapSizeOut,
-                              size_t& aNonHeapSizeOut) override;
+                              size_t& aNonHeapSizeOut,
+                              size_t& aSharedHandlesOut) override;
 
 protected:
   DrawableFrameRef DrawableRef(size_t aFrame) override;
 
   // Animation frames are always locked. This is because we only want to release
   // their memory atomically (due to the surface cache discarding them). If they
   // were unlocked, the OS could end up releasing the memory of random frames
   // from the middle of the animation, which is not worth the complexity of
--- a/image/Decoder.cpp
+++ b/image/Decoder.cpp
@@ -330,17 +330,18 @@ Decoder::AllocateFrameInternal(uint32_t 
       aFrameRect.width <= 0 || aFrameRect.height <= 0) {
     NS_WARNING("Trying to add frame with zero or negative size");
     return RawAccessFrameRef();
   }
 
   NotNull<RefPtr<imgFrame>> frame = WrapNotNull(new imgFrame());
   bool nonPremult = bool(mSurfaceFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
   if (NS_FAILED(frame->InitForDecoder(aOutputSize, aFrameRect, aFormat,
-                                      aPaletteDepth, nonPremult))) {
+                                      aPaletteDepth, nonPremult,
+                                      aFrameNum > 0))) {
     NS_WARNING("imgFrame::Init should succeed");
     return RawAccessFrameRef();
   }
 
   RawAccessFrameRef ref = frame->RawAccessRef();
   if (!ref) {
     frame->Abort();
     return RawAccessFrameRef();
--- a/image/FrameAnimator.cpp
+++ b/image/FrameAnimator.cpp
@@ -348,20 +348,21 @@ DoCollectSizeOfCompositingSurfaces(const
   SurfaceKey key = RasterSurfaceKey(aSurface->GetImageSize(),
                                     DefaultSurfaceFlags(),
                                     PlaybackType::eStatic);
 
   // Create a counter for this surface.
   SurfaceMemoryCounter counter(key, /* aIsLocked = */ true, aType);
 
   // Extract the surface's memory usage information.
-  size_t heap = 0, nonHeap = 0;
-  aSurface->AddSizeOfExcludingThis(aMallocSizeOf, heap, nonHeap);
+  size_t heap = 0, nonHeap = 0, handles = 0;
+  aSurface->AddSizeOfExcludingThis(aMallocSizeOf, heap, nonHeap, handles);
   counter.Values().SetDecodedHeap(heap);
   counter.Values().SetDecodedNonHeap(nonHeap);
+  counter.Values().SetSharedHandles(handles);
 
   // Record it.
   aCounters.AppendElement(counter);
 }
 
 void
 FrameAnimator::CollectSizeOfCompositingSurfaces(
     nsTArray<SurfaceMemoryCounter>& aCounters,
--- a/image/ISurfaceProvider.h
+++ b/image/ISurfaceProvider.h
@@ -59,24 +59,26 @@ public:
   /// important that it be constant over the lifetime of this object.
   virtual size_t LogicalSizeInBytes() const = 0;
 
   /// @return the actual number of bytes of memory this ISurfaceProvider is
   /// using. May vary over the lifetime of the ISurfaceProvider. The default
   /// implementation is appropriate for static ISurfaceProviders.
   virtual void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
                                       size_t& aHeapSizeOut,
-                                      size_t& aNonHeapSizeOut)
+                                      size_t& aNonHeapSizeOut,
+                                      size_t& aSharedHandlesOut)
   {
     DrawableFrameRef ref = DrawableRef(/* aFrame = */ 0);
     if (!ref) {
       return;
     }
 
-    ref->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut, aNonHeapSizeOut);
+    ref->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut,
+                                aNonHeapSizeOut, aSharedHandlesOut);
   }
 
   /// @return the availability state of this ISurfaceProvider, which indicates
   /// whether DrawableRef() could successfully return a surface. Should only be
   /// called from SurfaceCache code as it relies on SurfaceCache for
   /// synchronization.
   AvailabilityState& Availability() { return mAvailability; }
   const AvailabilityState& Availability() const { return mAvailability; }
--- a/image/Image.h
+++ b/image/Image.h
@@ -28,37 +28,42 @@ class Image;
 ///////////////////////////////////////////////////////////////////////////////
 
 struct MemoryCounter
 {
   MemoryCounter()
     : mSource(0)
     , mDecodedHeap(0)
     , mDecodedNonHeap(0)
+    , mSharedHandles(0)
   { }
 
   void SetSource(size_t aCount) { mSource = aCount; }
   size_t Source() const { return mSource; }
   void SetDecodedHeap(size_t aCount) { mDecodedHeap = aCount; }
   size_t DecodedHeap() const { return mDecodedHeap; }
   void SetDecodedNonHeap(size_t aCount) { mDecodedNonHeap = aCount; }
   size_t DecodedNonHeap() const { return mDecodedNonHeap; }
+  void SetSharedHandles(size_t aCount) { mSharedHandles = aCount; }
+  size_t SharedHandles() const { return mSharedHandles; }
 
   MemoryCounter& operator+=(const MemoryCounter& aOther)
   {
     mSource += aOther.mSource;
     mDecodedHeap += aOther.mDecodedHeap;
     mDecodedNonHeap += aOther.mDecodedNonHeap;
+    mSharedHandles += aOther.mSharedHandles;
     return *this;
   }
 
 private:
   size_t mSource;
   size_t mDecodedHeap;
   size_t mDecodedNonHeap;
+  size_t mSharedHandles;
 };
 
 enum class SurfaceMemoryCounterType
 {
   NORMAL,
   COMPOSITING,
   COMPOSITING_PREV
 };
--- a/image/SurfaceCache.cpp
+++ b/image/SurfaceCache.cpp
@@ -192,20 +192,22 @@ public:
       }
 
       // Record the memory used by the ISurfaceProvider. This may not have a
       // straightforward relationship to the size of the surface that
       // DrawableRef() returns if the surface is generated dynamically. (i.e.,
       // for surfaces with PlaybackType::eAnimated.)
       size_t heap = 0;
       size_t nonHeap = 0;
+      size_t handles = 0;
       aCachedSurface->mProvider
-        ->AddSizeOfExcludingThis(mMallocSizeOf, heap, nonHeap);
+        ->AddSizeOfExcludingThis(mMallocSizeOf, heap, nonHeap, handles);
       counter.Values().SetDecodedHeap(heap);
       counter.Values().SetDecodedNonHeap(nonHeap);
+      counter.Values().SetSharedHandles(handles);
 
       mCounters.AppendElement(counter);
     }
 
   private:
     nsTArray<SurfaceMemoryCounter>& mCounters;
     MallocSizeOf                    mMallocSizeOf;
   };
--- a/image/imgFrame.cpp
+++ b/image/imgFrame.cpp
@@ -927,30 +927,39 @@ imgFrame::SetCompositingFailed(bool val)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mCompositingFailed = val;
 }
 
 void
 imgFrame::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
                                  size_t& aHeapSizeOut,
-                                 size_t& aNonHeapSizeOut) const
+                                 size_t& aNonHeapSizeOut,
+                                 size_t& aSharedHandlesOut) const
 {
   MonitorAutoLock lock(mMonitor);
 
   if (mPalettedImageData) {
     aHeapSizeOut += aMallocSizeOf(mPalettedImageData);
   }
   if (mLockedSurface) {
     aHeapSizeOut += aMallocSizeOf(mLockedSurface);
   }
   if (mOptSurface) {
     aHeapSizeOut += aMallocSizeOf(mOptSurface);
   }
   if (mRawSurface) {
     aHeapSizeOut += aMallocSizeOf(mRawSurface);
     mRawSurface->AddSizeOfExcludingThis(aMallocSizeOf, aHeapSizeOut,
                                         aNonHeapSizeOut);
+
+    if (mRawSurface->GetType() == SurfaceType::DATA_SHARED) {
+      auto sharedSurface =
+        static_cast<SourceSurfaceSharedData*>(mRawSurface.get());
+      if (sharedSurface->CanShare()) {
+        ++aSharedHandlesOut;
+      }
+    }
   }
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/imgFrame.h
+++ b/image/imgFrame.h
@@ -343,17 +343,18 @@ public:
   void SetCompositingFailed(bool val);
 
   void SetOptimizable();
 
   void FinalizeSurface();
   already_AddRefed<SourceSurface> GetSourceSurface();
 
   void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, size_t& aHeapSizeOut,
-                              size_t& aNonHeapSizeOut) const;
+                              size_t& aNonHeapSizeOut,
+                              size_t& aSharedHandlesOut) const;
 
 private: // methods
 
   ~imgFrame();
 
   nsresult LockImageData();
   nsresult UnlockImageData();
   nsresult Optimize(gfx::DrawTarget* aTarget);
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -275,16 +275,21 @@ private:
     for (const SurfaceMemoryCounter& counter : aCounter.Surfaces()) {
       nsAutoCString surfacePathPrefix(aPathPrefix);
       surfacePathPrefix.Append(counter.IsLocked() ? "locked/" : "unlocked/");
       surfacePathPrefix.Append("surface(");
       surfacePathPrefix.AppendInt(counter.Key().Size().width);
       surfacePathPrefix.Append("x");
       surfacePathPrefix.AppendInt(counter.Key().Size().height);
 
+      if (counter.Values().SharedHandles() > 0) {
+        surfacePathPrefix.Append(", shared:");
+        surfacePathPrefix.AppendInt(uint32_t(counter.Values().SharedHandles()));
+      }
+
       if (counter.Type() == SurfaceMemoryCounterType::NORMAL) {
         PlaybackType playback = counter.Key().Playback();
         surfacePathPrefix.Append(playback == PlaybackType::eAnimated
                                  ? " (animation)"
                                  : "");
 
         if (counter.Key().Flags() != DefaultSurfaceFlags()) {
           surfacePathPrefix.Append(", flags:");
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -79,18 +79,18 @@ ReportBadArrayType(JSContext* cx)
 {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ATOMICS_BAD_ARRAY);
     return false;
 }
 
 static bool
 ReportOutOfRange(JSContext* cx)
 {
-    // Use JSMSG_BAD_INDEX here even if it is generic, since that is
-    // the message used by NonStandardToIndex for its initial range checking.
+    // Use JSMSG_BAD_INDEX here, it is what ToIndex uses for some cases that it
+    // reports directly.
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
     return false;
 }
 
 static bool
 ReportCannotWait(JSContext* cx)
 {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ATOMICS_WAIT_NOT_ALLOWED);
@@ -110,17 +110,17 @@ GetSharedTypedArray(JSContext* cx, Handl
         return ReportBadArrayType(cx);
     return true;
 }
 
 static bool
 GetTypedArrayIndex(JSContext* cx, HandleValue v, Handle<TypedArrayObject*> view, uint32_t* offset)
 {
     uint64_t index;
-    if (!NonStandardToIndex(cx, v, &index))
+    if (!ToIndex(cx, v, &index))
         return false;
     if (index >= view->length())
         return ReportOutOfRange(cx);
     *offset = uint32_t(index);
     return true;
 }
 
 static int32_t
--- a/js/src/builtin/Module.js
+++ b/js/src/builtin/Module.js
@@ -60,22 +60,22 @@ function ModuleGetExportedNames(exportSt
             if (n !== "default" && !callFunction(ArrayIncludes, exportedNames, n))
                 _DefineDataProperty(exportedNames, namesCount++, n);
         }
     }
 
     return exportedNames;
 }
 
-// 15.2.1.16.3 ResolveExport(exportName, resolveSet, exportStarSet)
-function ModuleResolveExport(exportName, resolveSet = [], exportStarSet = [])
+// 15.2.1.16.3 ResolveExport(exportName, resolveSet)
+function ModuleResolveExport(exportName, resolveSet = [])
 {
     if (!IsObject(this) || !IsModule(this)) {
         return callFunction(CallModuleMethodIfWrapped, this, exportName, resolveSet,
-                            exportStarSet, "ModuleResolveExport");
+                            "ModuleResolveExport");
     }
 
     // Step 1
     let module = this;
 
     // Step 2
     for (let i = 0; i < resolveSet.length; i++) {
         let r = resolveSet[i];
@@ -95,64 +95,56 @@ function ModuleResolveExport(exportName,
     }
 
     // Step 5
     let indirectExportEntries = module.indirectExportEntries;
     for (let i = 0; i < indirectExportEntries.length; i++) {
         let e = indirectExportEntries[i];
         if (exportName === e.exportName) {
             let importedModule = CallModuleResolveHook(module, e.moduleRequest,
-                                                       MODULE_STATE_INSTANTIATED);
-            let indirectResolution = callFunction(importedModule.resolveExport, importedModule,
-                                                  e.importName, resolveSet, exportStarSet);
-            if (indirectResolution !== null)
-                return indirectResolution;
+                                                       MODULE_STATE_PARSED);
+            return callFunction(importedModule.resolveExport, importedModule, e.importName,
+                                resolveSet);
         }
     }
 
     // Step 6
     if (exportName === "default") {
         // A default export cannot be provided by an export *.
-        ThrowSyntaxError(JSMSG_BAD_DEFAULT_EXPORT);
+        return null;
     }
 
     // Step 7
-    if (callFunction(ArrayIncludes, exportStarSet, module))
-        return null;
+    let starResolution = null;
 
     // Step 8
-    _DefineDataProperty(exportStarSet, exportStarSet.length, module);
-
-    // Step 9
-    let starResolution = null;
-
-    // Step 10
     let starExportEntries = module.starExportEntries;
     for (let i = 0; i < starExportEntries.length; i++) {
         let e = starExportEntries[i];
         let importedModule = CallModuleResolveHook(module, e.moduleRequest,
-                                                   MODULE_STATE_INSTANTIATED);
+                                                   MODULE_STATE_PARSED);
         let resolution = callFunction(importedModule.resolveExport, importedModule,
-                                      exportName, resolveSet, exportStarSet);
+                                      exportName, resolveSet);
         if (resolution === "ambiguous")
             return resolution;
 
         if (resolution !== null) {
             if (starResolution === null) {
                 starResolution = resolution;
             } else {
                 if (resolution.module !== starResolution.module ||
                     resolution.exportName !== starResolution.exportName)
                 {
                     return "ambiguous";
                 }
             }
         }
     }
 
+    // Step 9
     return starResolution;
 }
 
 // 15.2.1.18 GetModuleNamespace(module)
 function GetModuleNamespace(module)
 {
     // Step 2
     let namespace = module.namespace;
@@ -208,18 +200,18 @@ function GetModuleEnvironment(module)
     assert(env === undefined || IsModuleEnvironment(env),
            "Module environment slot contains unexpected value");
 
     return env;
 }
 
 function RecordInstantationFailure(module)
 {
-    // Set the module's environment slot to 'null' to indicate a failed module
-    // instantiation.
+    // Set the module's state to 'failed' to indicate a failed module
+    // instantiation and reset the environment slot to 'undefined'.
     assert(IsModule(module), "Non-module passed to RecordInstantationFailure");
     SetModuleState(module, MODULE_STATE_FAILED);
     UnsafeSetReservedSlot(module, MODULE_OBJECT_ENVIRONMENT_SLOT, undefined);
 }
 
 // 15.2.1.16.4 ModuleDeclarationInstantiation()
 function ModuleDeclarationInstantiation()
 {
@@ -270,21 +262,23 @@ function ModuleDeclarationInstantiation(
                 CreateNamespaceBinding(env, imp.localName, namespace);
             } else {
                 let resolution = callFunction(importedModule.resolveExport, importedModule,
                                               imp.importName);
                 if (resolution === null)
                     ThrowSyntaxError(JSMSG_MISSING_IMPORT, imp.importName);
                 if (resolution === "ambiguous")
                     ThrowSyntaxError(JSMSG_AMBIGUOUS_IMPORT, imp.importName);
+                if (resolution.module.state < MODULE_STATE_INSTANTIATED)
+                    ThrowInternalError(JSMSG_BAD_MODULE_STATE);
                 CreateImportBinding(env, imp.localName, resolution.module, resolution.bindingName);
             }
         }
 
-        // Step 16.iv
+        // Step 17.a.iii
         InstantiateModuleFunctionDeclarations(module);
     } catch (e) {
         RecordInstantationFailure(module);
         throw e;
     }
 }
 _SetCanonicalName(ModuleDeclarationInstantiation, "ModuleDeclarationInstantiation");
 
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -989,17 +989,17 @@ GlobalObject::initModuleProto(JSContext*
         JS_PSG("localExportEntries", ModuleObject_localExportEntriesGetter, 0),
         JS_PSG("indirectExportEntries", ModuleObject_indirectExportEntriesGetter, 0),
         JS_PSG("starExportEntries", ModuleObject_starExportEntriesGetter, 0),
         JS_PS_END
     };
 
     static const JSFunctionSpec protoFunctions[] = {
         JS_SELF_HOSTED_FN("getExportedNames", "ModuleGetExportedNames", 1, 0),
-        JS_SELF_HOSTED_FN("resolveExport", "ModuleResolveExport", 3, 0),
+        JS_SELF_HOSTED_FN("resolveExport", "ModuleResolveExport", 2, 0),
         JS_SELF_HOSTED_FN("declarationInstantiation", "ModuleDeclarationInstantiation", 0, 0),
         JS_SELF_HOSTED_FN("evaluation", "ModuleEvaluation", 0, 0),
         JS_FS_END
     };
 
     RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
     if (!proto)
         return false;
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -155,16 +155,17 @@ function isSuppressedVirtualMethod(csu, 
 {
     return csu == "nsISupports" && (method == "AddRef" || method == "Release");
 }
 
 // Ignore calls of these functions (so ignore any stack containing these)
 var ignoreFunctions = {
     "ptio.c:pt_MapError" : true,
     "je_malloc_printf" : true,
+    "malloc_usable_size" : true,
     "vprintf_stderr" : true,
     "PR_ExplodeTime" : true,
     "PR_ErrorInstallTable" : true,
     "PR_SetThreadPrivate" : true,
     "JSObject* js::GetWeakmapKeyDelegate(JSObject*)" : true, // FIXME: mark with AutoSuppressGCAnalysis instead
     "uint8 NS_IsMainThread()" : true,
 
     // Has an indirect call under it by the name "__f", which seemed too
new file mode 100644
--- /dev/null
+++ b/js/src/ds/Bitmap.cpp
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ds/Bitmap.h"
+
+using namespace js;
+
+SparseBitmap::~SparseBitmap()
+{
+    if (data.initialized()) {
+        for (Data::Range r(data.all()); !r.empty(); r.popFront())
+            js_delete(r.front().value());
+    }
+}
+
+size_t
+SparseBitmap::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
+{
+    size_t size = data.sizeOfExcludingThis(mallocSizeOf);
+    for (Data::Range r(data.all()); !r.empty(); r.popFront())
+        size += mallocSizeOf(r.front().value());
+    return size;
+}
+
+SparseBitmap::BitBlock&
+SparseBitmap::getOrCreateBlock(size_t blockId)
+{
+    Data::AddPtr p = data.lookupForAdd(blockId);
+    if (!p) {
+        AutoEnterOOMUnsafeRegion oomUnsafe;
+        BitBlock* block = js_new<BitBlock>();
+        if (!block || !data.add(p, blockId, block))
+            oomUnsafe.crash("Bitmap OOM");
+        PodZero(block);
+    }
+    return *p->value();
+}
+
+void
+SparseBitmap::setBit(size_t bit)
+{
+    size_t word = bit / JS_BITS_PER_WORD;
+    size_t blockWord = blockStartWord(word);
+    BitBlock& block = getOrCreateBlock(blockWord / WordsInBlock);
+    block[word - blockWord] |= uintptr_t(1) << (bit % JS_BITS_PER_WORD);
+}
+
+SparseBitmap::BitBlock*
+SparseBitmap::getBlock(size_t blockId) const
+{
+    Data::Ptr p = data.lookup(blockId);
+    return p ? p->value() : nullptr;
+}
+
+bool
+SparseBitmap::getBit(size_t bit) const
+{
+    size_t word = bit / JS_BITS_PER_WORD;
+    size_t blockWord = blockStartWord(word);
+
+    BitBlock* block = getBlock(blockWord / WordsInBlock);
+    if (block)
+        return (*block)[word - blockWord] & (uintptr_t(1) << (bit % JS_BITS_PER_WORD));
+    return false;
+}
+
+void
+SparseBitmap::bitwiseAndWith(const DenseBitmap& other)
+{
+    for (Data::Enum e(data); !e.empty(); e.popFront()) {
+        BitBlock& block = *e.front().value();
+        size_t blockWord = e.front().key() * WordsInBlock;
+        bool anySet = false;
+        size_t numWords = wordIntersectCount(blockWord, other);
+        for (size_t i = 0; i < numWords; i++) {
+            block[i] &= other.word(blockWord + i);
+            anySet |= !!block[i];
+        }
+        if (!anySet) {
+            js_delete(&block);
+            e.removeFront();
+        }
+    }
+}
+
+void
+SparseBitmap::bitwiseOrWith(const SparseBitmap& other)
+{
+    for (Data::Range r(other.data.all()); !r.empty(); r.popFront()) {
+        const BitBlock& otherBlock = *r.front().value();
+        BitBlock& block = getOrCreateBlock(r.front().key());
+        for (size_t i = 0; i < WordsInBlock; i++)
+            block[i] |= otherBlock[i];
+    }
+}
+
+void
+SparseBitmap::bitwiseOrInto(DenseBitmap& other) const
+{
+    for (Data::Range r(data.all()); !r.empty(); r.popFront()) {
+        BitBlock& block = *r.front().value();
+        size_t blockWord = r.front().key() * WordsInBlock;
+        size_t numWords = wordIntersectCount(blockWord, other);
+#ifdef DEBUG
+        // Any words out of range in other should be zero in this bitmap.
+        for (size_t i = numWords; i < WordsInBlock; i++)
+            MOZ_ASSERT(!block[i]);
+#endif
+        for (size_t i = 0; i < numWords; i++)
+            other.word(blockWord + i) |= block[i];
+    }
+}
+
+void
+SparseBitmap::bitwiseOrRangeInto(size_t wordStart, size_t numWords, uintptr_t* target) const
+{
+    size_t blockWord = blockStartWord(wordStart);
+
+    // We only support using a single bit block in this API.
+    MOZ_ASSERT(numWords && (blockWord == blockStartWord(wordStart + numWords - 1)));
+
+    BitBlock* block = getBlock(blockWord / WordsInBlock);
+    if (block) {
+        for (size_t i = 0; i < numWords; i++)
+            target[i] |= (*block)[wordStart - blockWord + i];
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/src/ds/Bitmap.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/. */
+
+#ifndef ds_Bitmap_h
+#define ds_Bitmap_h
+
+#include <algorithm>
+
+#include "jsalloc.h"
+
+#include "js/HashTable.h"
+#include "js/Vector.h"
+
+// This file provides two classes for representing bitmaps.
+//
+// DenseBitmap is an array of words of bits, with size linear in the maximum
+// bit which has been set on it.
+//
+// SparseBitmap provides a reasonably simple, reasonably efficient (in time and
+// space) implementation of a sparse bitmap. The basic representation is a hash
+// table whose entries are fixed length malloc'ed blocks of bits.
+
+namespace js {
+
+class DenseBitmap
+{
+    typedef Vector<uintptr_t, 0, SystemAllocPolicy> Data;
+    Data data;
+
+  public:
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
+        return data.sizeOfExcludingThis(mallocSizeOf);
+    }
+
+    bool ensureSpace(size_t numWords) {
+        MOZ_ASSERT(data.empty());
+        return data.appendN(0, numWords);
+    }
+
+    size_t numWords() const { return data.length(); }
+    uintptr_t word(size_t i) const { return data[i]; }
+    uintptr_t& word(size_t i) { return data[i]; }
+
+    void copyBitsFrom(size_t wordStart, size_t numWords, uintptr_t* source) {
+        MOZ_ASSERT(wordStart + numWords <= data.length());
+        mozilla::PodCopy(&data[wordStart], source, numWords);
+    }
+
+    void bitwiseOrRangeInto(size_t wordStart, size_t numWords, uintptr_t* target) const {
+        for (size_t i = 0; i < numWords; i++)
+            target[i] |= data[wordStart + i];
+    }
+};
+
+class SparseBitmap
+{
+    // The number of words of bits to use for each block mainly affects the
+    // memory usage of the bitmap. To minimize overhead, bitmaps which are
+    // expected to be fairly dense should have a large block size, and bitmaps
+    // which are expected to be very sparse should have a small block size.
+    static const size_t WordsInBlock = 4096 / sizeof(uintptr_t);
+
+    typedef mozilla::Array<uintptr_t, WordsInBlock> BitBlock;
+    typedef HashMap<size_t, BitBlock*, DefaultHasher<size_t>, SystemAllocPolicy> Data;
+    Data data;
+
+    static size_t blockStartWord(size_t word) {
+        return word & ~(WordsInBlock - 1);
+    }
+
+    // Return the number of words in a BitBlock starting at |blockWord| which
+    // are in |other|.
+    static size_t wordIntersectCount(size_t blockWord, const DenseBitmap& other) {
+        long count = other.numWords() - blockWord;
+        return std::min<size_t>((size_t)WordsInBlock, std::max<long>(count, 0));
+    }
+
+    BitBlock* getBlock(size_t blockId) const;
+    BitBlock& getOrCreateBlock(size_t blockId);
+
+  public:
+    bool init() { return data.init(); }
+    ~SparseBitmap();
+
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
+
+    void setBit(size_t bit);
+    bool getBit(size_t bit) const;
+
+    void bitwiseAndWith(const DenseBitmap& other);
+    void bitwiseOrWith(const SparseBitmap& other);
+    void bitwiseOrInto(DenseBitmap& other) const;
+
+    // Currently, this API only supports a range of words that is in a single bit block.
+    void bitwiseOrRangeInto(size_t wordStart, size_t numWords, uintptr_t* target) const;
+};
+
+} // namespace js
+
+#endif // ds_Bitmap_h
--- a/js/src/ds/PageProtectingVector.h
+++ b/js/src/ds/PageProtectingVector.h
@@ -3,23 +3,37 @@
  * 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/. */
 
 #ifndef ds_PageProtectingVector_h
 #define ds_PageProtectingVector_h
 
 #include "mozilla/Atomics.h"
+#include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/PodOperations.h"
+#include "mozilla/Types.h"
 #include "mozilla/Vector.h"
 
+#ifdef MALLOC_H
+# include MALLOC_H
+#endif
+
 #include "ds/MemoryProtectionExceptionHandler.h"
 #include "gc/Memory.h"
 #include "js/Utility.h"
 
+#ifdef MOZ_MEMORY
+# ifdef XP_DARWIN
+#  define malloc_usable_size malloc_size
+# else
+extern "C" MFBT_API size_t malloc_usable_size(MALLOC_USABLE_SIZE_CONST_PTR void* p);
+# endif
+#endif
+
 namespace js {
 
 /*
  * PageProtectingVector is a vector that can only grow or be cleared, restricts
  * access to memory pages that haven't been used yet, and marks all of its fully
  * used memory pages as read-only. It can be used to detect heap corruption in
  * important buffers, since anything that tries to write into its protected
  * pages will crash. On Nightly and Aurora, these crashes will additionally be
@@ -450,62 +464,144 @@ PageProtectingVector<T, A, B, C, D, E, F
 {
     if (MOZ_LIKELY(length() + size <= capacity()))
         return appendNewPage(values, size);
     return appendNewBuffer(values, size);
 }
 
 class ProtectedReallocPolicy
 {
+    uintptr_t currAddr;
+    size_t currSize;
+    uintptr_t prevAddr;
+    size_t prevSize;
+
+    template <typename T> void update(T* newAddr, size_t newSize) {
+        prevAddr = currAddr;
+        prevSize = currSize;
+        currAddr = uintptr_t(newAddr);
+        currSize = newSize * sizeof(T);
+    }
+
+    template <typename T> void updateIfValid(T* newAddr, size_t newSize) {
+        if (newAddr)
+            update<T>(newAddr, newSize);
+    }
+
+    template <typename T> T* reallocUpdate(T* oldAddr, size_t oldSize, size_t newSize) {
+        T* newAddr = js_pod_realloc<T>(oldAddr, oldSize, newSize);
+        updateIfValid<T>(newAddr, newSize);
+        return newAddr;
+    }
+
   public:
-    template <typename T> T* maybe_pod_malloc(size_t numElems) {
-        return js_pod_malloc<T>(numElems);
+    ProtectedReallocPolicy() : currAddr(0), currSize(0), prevAddr(0), prevSize(0) {}
+
+    ~ProtectedReallocPolicy() {
+        MOZ_RELEASE_ASSERT(!currSize && !currAddr);
     }
+
+    template <typename T> T* maybe_pod_malloc(size_t numElems) {
+        MOZ_RELEASE_ASSERT(!currSize && !currAddr);
+        T* addr = js_pod_malloc<T>(numElems);
+        updateIfValid<T>(addr, numElems);
+        return addr;
+    }
+
     template <typename T> T* maybe_pod_calloc(size_t numElems) {
-        return js_pod_calloc<T>(numElems);
+        MOZ_RELEASE_ASSERT(!currSize && !currAddr);
+        T* addr = js_pod_calloc<T>(numElems);
+        updateIfValid<T>(addr, numElems);
+        return addr;
     }
+
     template <typename T> T* maybe_pod_realloc(T* oldAddr, size_t oldSize, size_t newSize) {
+        if (uintptr_t(oldAddr) != currAddr) {
+            MOZ_CRASH_UNSAFE_PRINTF("maybe_pod_realloc: oldAddr and currAddr don't match "
+                                    "(0x%" PRIx64 " != 0x%" PRIx64 ", %" PRIu64 ")!",
+                                    uint64_t(oldAddr), uint64_t(currAddr), uint64_t(currSize));
+        }
+        if (oldSize * sizeof(T) != currSize) {
+            MOZ_CRASH_UNSAFE_PRINTF("maybe_pod_realloc: oldSize and currSize don't match "
+                                    "(%" PRIu64 " != %" PRIu64 ", 0x%" PRIx64 ")!",
+                                    uint64_t(oldSize * sizeof(T)), uint64_t(currSize),
+                                    uint64_t(currAddr));
+        }
+
         MOZ_ASSERT_IF(oldAddr, oldSize);
         if (MOZ_UNLIKELY(!newSize))
             return nullptr;
         if (MOZ_UNLIKELY(!oldAddr))
-            return js_pod_malloc<T>(newSize);
+            return maybe_pod_malloc<T>(newSize);
+
+#ifdef MOZ_MEMORY
+        size_t usableSize = malloc_usable_size(oldAddr);
+        if (usableSize < currSize) {
+            MOZ_CRASH_UNSAFE_PRINTF("maybe_pod_realloc: usableSize < currSize "
+                                    "(%" PRIu64 " < %" PRIu64 ", %" PRIu64 ", %s)!",
+                                    uint64_t(usableSize), uint64_t(currSize),
+                                    uint64_t(prevSize), prevAddr == currAddr ? "true" : "false");
+        }
+#endif
 
         T* tmpAddr = js_pod_malloc<T>(newSize);
         if (MOZ_UNLIKELY(!tmpAddr))
-            return js_pod_realloc<T>(oldAddr, oldSize, newSize);
+            return reallocUpdate<T>(oldAddr, oldSize, newSize);
 
         size_t bytes = (newSize >= oldSize ? oldSize : newSize) * sizeof(T);
         memcpy(tmpAddr, oldAddr, bytes);
 
         T* newAddr = js_pod_realloc<T>(oldAddr, oldSize, newSize);
         if (MOZ_UNLIKELY(!newAddr)) {
             js_free(tmpAddr);
-            return js_pod_realloc<T>(oldAddr, oldSize, newSize);
+            return reallocUpdate<T>(oldAddr, oldSize, newSize);
         }
 
         const uint8_t* newAddrBytes = reinterpret_cast<const uint8_t*>(newAddr);
         const uint8_t* tmpAddrBytes = reinterpret_cast<const uint8_t*>(tmpAddr);
         if (!mozilla::PodEqual(tmpAddrBytes, newAddrBytes, bytes)) {
-            if (oldAddr == newAddr)
-                MOZ_CRASH("New buffer doesn't match the old buffer (newAddr == oldAddr)!");
-            else
-                MOZ_CRASH("New buffer doesn't match the old buffer (newAddr != oldAddr)!");
+#ifdef MOZ_MEMORY
+            MOZ_CRASH_UNSAFE_PRINTF("maybe_pod_realloc: buffers don't match "
+                                    "(%" PRIu64 " >= %" PRIu64 ", %" PRIu64 ", %s)!",
+                                    uint64_t(usableSize), uint64_t(currSize),
+                                    uint64_t(prevSize), prevAddr == currAddr ? "true" : "false");
+#else
+            MOZ_CRASH_UNSAFE_PRINTF("maybe_pod_realloc: buffers don't match "
+                                    "(%" PRIu64 ", %" PRIu64 ", %s)!",
+                                    uint64_t(currSize), uint64_t(prevSize),
+                                    prevAddr == currAddr ? "true" : "false");
+#endif
         }
 
         js_free(tmpAddr);
+        update<T>(newAddr, newSize);
         return newAddr;
     }
 
     template <typename T> T* pod_malloc(size_t numElems) { return maybe_pod_malloc<T>(numElems); }
     template <typename T> T* pod_calloc(size_t numElems) { return maybe_pod_calloc<T>(numElems); }
     template <typename T> T* pod_realloc(T* p, size_t oldSize, size_t newSize) {
         return maybe_pod_realloc<T>(p, oldSize, newSize);
     }
-    void free_(void* p) { js_free(p); }
+
+    void free_(void* p) {
+        MOZ_RELEASE_ASSERT(uintptr_t(p) == currAddr);
+#ifdef MOZ_MEMORY
+        size_t usableSize = malloc_usable_size(p);
+        if (usableSize < currSize) {
+            MOZ_CRASH_UNSAFE_PRINTF("free_: usableSize < currSize "
+                                    "(%" PRIu64 " < %" PRIu64 ", %" PRIu64 ", %s)!",
+                                    uint64_t(usableSize), uint64_t(currSize),
+                                    uint64_t(prevSize), prevAddr == currAddr ? "true" : "false");
+        }
+#endif
+        js_free(p);
+        update<uint8_t>(0, 0);
+    }
+
     void reportAllocOverflow() const {}
     bool checkSimulatedOOM() const {
         return !js::oom::ShouldFailWithOOM();
     }
 };
 
 } /* namespace js */
 
--- a/js/src/gc/AtomMarking.cpp
+++ b/js/src/gc/AtomMarking.cpp
@@ -39,39 +39,16 @@ namespace gc {
 //
 // Each arena in the atoms zone has an atomBitmapStart() value indicating the
 // word index into the bitmap of the first thing in the arena. Each arena uses
 // ArenaBitmapWords of data to store its bitmap, which uses the same
 // representation as chunk mark bitmaps: one bit is allocated per Cell, with
 // bits for space between things being unused when things are larger than a
 // single Cell.
 
-static inline void
-SetBit(uintptr_t* bitmap, size_t bit)
-{
-    bitmap[bit / JS_BITS_PER_WORD] |= uintptr_t(1) << (bit % JS_BITS_PER_WORD);
-}
-
-static inline bool
-GetBit(uintptr_t* bitmap, size_t bit)
-{
-    return bitmap[bit / JS_BITS_PER_WORD] & (uintptr_t(1) << (bit % JS_BITS_PER_WORD));
-}
-
-static inline bool
-EnsureBitmapLength(AtomMarkingRuntime::Bitmap& bitmap, size_t nwords)
-{
-    if (nwords > bitmap.length()) {
-        size_t needed = nwords - bitmap.length();
-        if (needed)
-            return bitmap.appendN(0, needed);
-    }
-    return true;
-}
-
 void
 AtomMarkingRuntime::registerArena(Arena* arena)
 {
     MOZ_ASSERT(arena->getThingSize() != 0);
     MOZ_ASSERT(arena->getThingSize() % CellSize == 0);
     MOZ_ASSERT(arena->zone->isAtomsZone());
     MOZ_ASSERT(arena->zone->runtimeFromAnyThread()->currentThreadHasExclusiveAccess());
 
@@ -93,101 +70,83 @@ AtomMarkingRuntime::unregisterArena(Aren
 {
     MOZ_ASSERT(arena->zone->isAtomsZone());
 
     // Leak these atom bits if we run out of memory.
     mozilla::Unused << freeArenaIndexes.ref().emplaceBack(arena->atomBitmapStart());
 }
 
 bool
-AtomMarkingRuntime::computeBitmapFromChunkMarkBits(JSRuntime* runtime, Bitmap& bitmap)
+AtomMarkingRuntime::computeBitmapFromChunkMarkBits(JSRuntime* runtime, DenseBitmap& bitmap)
 {
     MOZ_ASSERT(runtime->currentThreadHasExclusiveAccess());
 
-    MOZ_ASSERT(bitmap.empty());
-    if (!EnsureBitmapLength(bitmap, allocatedWords))
+    if (!bitmap.ensureSpace(allocatedWords))
         return false;
 
     Zone* atomsZone = runtime->unsafeAtomsCompartment()->zone();
     for (auto thingKind : AllAllocKinds()) {
         for (ArenaIter aiter(atomsZone, thingKind); !aiter.done(); aiter.next()) {
             Arena* arena = aiter.get();
             uintptr_t* chunkWords = arena->chunk()->bitmap.arenaBits(arena);
-            uintptr_t* bitmapWords = &bitmap[arena->atomBitmapStart()];
-            mozilla::PodCopy(bitmapWords, chunkWords, ArenaBitmapWords);
+            bitmap.copyBitsFrom(arena->atomBitmapStart(), ArenaBitmapWords, chunkWords);
         }
     }
 
     return true;
 }
 
 void
-AtomMarkingRuntime::updateZoneBitmap(Zone* zone, const Bitmap& bitmap)
+AtomMarkingRuntime::updateZoneBitmap(Zone* zone, const DenseBitmap& bitmap)
 {
     if (zone->isAtomsZone())
         return;
 
-    // |bitmap| was produced by computeBitmapFromChunkMarkBits, so it should
-    // have the maximum possible size.
-    MOZ_ASSERT(zone->markedAtoms().length() <= bitmap.length());
-
     // Take the bitwise and between the two mark bitmaps to get the best new
     // overapproximation we can. |bitmap| might include bits that are not in
     // the zone's mark bitmap, if additional zones were collected by the GC.
-    for (size_t i = 0; i < zone->markedAtoms().length(); i++)
-        zone->markedAtoms()[i] &= bitmap[i];
+    zone->markedAtoms().bitwiseAndWith(bitmap);
 }
 
 // Set any bits in the chunk mark bitmaps for atoms which are marked in bitmap.
+template <typename Bitmap>
 static void
-AddBitmapToChunkMarkBits(JSRuntime* runtime, AtomMarkingRuntime::Bitmap& bitmap)
+AddBitmapToChunkMarkBits(JSRuntime* runtime, Bitmap& bitmap)
 {
     // Make sure that by copying the mark bits for one arena in word sizes we
     // do not affect the mark bits for other arenas.
     static_assert(ArenaBitmapBits == ArenaBitmapWords * JS_BITS_PER_WORD,
                   "ArenaBitmapWords must evenly divide ArenaBitmapBits");
 
     Zone* atomsZone = runtime->unsafeAtomsCompartment()->zone();
     for (auto thingKind : AllAllocKinds()) {
         for (ArenaIter aiter(atomsZone, thingKind); !aiter.done(); aiter.next()) {
             Arena* arena = aiter.get();
             uintptr_t* chunkWords = arena->chunk()->bitmap.arenaBits(arena);
-
-            // The bitmap might not be long enough, in which case remaining
-            // bits are implicitly zero.
-            if (bitmap.length() <= arena->atomBitmapStart())
-                continue;
-            MOZ_ASSERT(bitmap.length() >= arena->atomBitmapStart() + ArenaBitmapWords);
-
-            uintptr_t* bitmapWords = &bitmap[arena->atomBitmapStart()];
-            for (size_t i = 0; i < ArenaBitmapWords; i++)
-                chunkWords[i] |= bitmapWords[i];
+            bitmap.bitwiseOrRangeInto(arena->atomBitmapStart(), ArenaBitmapWords, chunkWords);
         }
     }
 }
 
 void
 AtomMarkingRuntime::updateChunkMarkBits(JSRuntime* runtime)
 {
     MOZ_ASSERT(runtime->currentThreadHasExclusiveAccess());
 
     // Try to compute a simple union of the zone atom bitmaps before updating
     // the chunk mark bitmaps. If this allocation fails then fall back to
     // updating the chunk mark bitmaps separately for each zone.
-    Bitmap markedUnion;
-    if (EnsureBitmapLength(markedUnion, allocatedWords)) {
+    DenseBitmap markedUnion;
+    if (markedUnion.ensureSpace(allocatedWords)) {
         for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
             // We only need to update the chunk mark bits for zones which were
             // not collected in the current GC. Atoms which are referenced by
             // collected zones have already been marked.
-            if (!zone->isCollectingFromAnyThread()) {
-                MOZ_ASSERT(zone->markedAtoms().length() <= allocatedWords);
-                for (size_t i = 0; i < zone->markedAtoms().length(); i++)
-                    markedUnion[i] |= zone->markedAtoms()[i];
-            }
+            if (!zone->isCollectingFromAnyThread())
+                zone->markedAtoms().bitwiseOrInto(markedUnion);
         }
         AddBitmapToChunkMarkBits(runtime, markedUnion);
     } else {
         for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
             if (!zone->isCollectingFromAnyThread())
                 AddBitmapToChunkMarkBits(runtime, zone->markedAtoms());
         }
     }
@@ -220,24 +179,19 @@ AtomMarkingRuntime::markAtom(JSContext* 
     if (!thing || !cx->zone())
         return;
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
 
     if (ThingIsPermanent(thing) || !thing->zoneFromAnyThread()->isAtomsZone())
         return;
 
     size_t bit = GetAtomBit(thing);
+    MOZ_ASSERT(bit / JS_BITS_PER_WORD < allocatedWords);
 
-    {
-        AutoEnterOOMUnsafeRegion oomUnsafe;
-        if (!EnsureBitmapLength(cx->zone()->markedAtoms(), allocatedWords))
-            oomUnsafe.crash("Atom bitmap OOM");
-    }
-
-    SetBit(cx->zone()->markedAtoms().begin(), bit);
+    cx->zone()->markedAtoms().setBit(bit);
 
     if (!cx->helperThread()) {
         // Trigger a read barrier on the atom, in case there is an incremental
         // GC in progress. This is necessary if the atom is being marked
         // because a reference to it was obtained from another zone which is
         // not being collected by the incremental GC.
         TenuredCell::readBarrier(thing);
     }
@@ -267,28 +221,17 @@ AtomMarkingRuntime::markAtomValue(JSCont
             markAtom(cx, &thing->asTenured());
     }
 }
 
 void
 AtomMarkingRuntime::adoptMarkedAtoms(Zone* target, Zone* source)
 {
     MOZ_ASSERT(target->runtimeFromAnyThread()->currentThreadHasExclusiveAccess());
-
-    Bitmap* targetBitmap = &target->markedAtoms();
-    Bitmap* sourceBitmap = &source->markedAtoms();
-    if (targetBitmap->length() < sourceBitmap->length())
-        std::swap(targetBitmap, sourceBitmap);
-    for (size_t i = 0; i < sourceBitmap->length(); i++)
-        (*targetBitmap)[i] |= (*sourceBitmap)[i];
-
-    if (targetBitmap != &target->markedAtoms())
-        target->markedAtoms() = Move(source->markedAtoms());
-    else
-        source->markedAtoms().clear();
+    target->markedAtoms().bitwiseOrWith(source->markedAtoms());
 }
 
 #ifdef DEBUG
 
 bool
 AtomMarkingRuntime::atomIsMarked(Zone* zone, Cell* thingArg)
 {
     if (!thingArg || IsInsideNursery(thingArg))
@@ -304,19 +247,17 @@ AtomMarkingRuntime::atomIsMarked(Zone* z
     JS::TraceKind kind = thing->getTraceKind();
     if (kind == JS::TraceKind::String) {
         JSAtom* atom = static_cast<JSAtom*>(thing);
         if (AtomIsPinnedInRuntime(zone->runtimeFromAnyThread(), atom))
             return true;
     }
 
     size_t bit = GetAtomBit(thing);
-    if (bit >= zone->markedAtoms().length() * JS_BITS_PER_WORD)
-        return false;
-    return GetBit(zone->markedAtoms().begin(), bit);
+    return zone->markedAtoms().getBit(bit);
 }
 
 bool
 AtomMarkingRuntime::idIsMarked(Zone* zone, jsid id)
 {
     if (JSID_IS_GCTHING(id))
         return atomIsMarked(zone, JSID_TO_GCTHING(id).asCell());
     return true;
--- a/js/src/gc/AtomMarking.h
+++ b/js/src/gc/AtomMarking.h
@@ -3,54 +3,53 @@
  * 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/. */
 
 #ifndef gc_AtomMarking_h
 #define gc_AtomMarking_h
 
 #include "NamespaceImports.h"
+#include "ds/Bitmap.h"
 #include "gc/Heap.h"
 #include "threading/ProtectedData.h"
 
 namespace js {
 namespace gc {
 
 // This class manages state used for marking atoms during GCs.
 // See AtomMarking.cpp for details.
 class AtomMarkingRuntime
 {
     // Unused arena atom bitmap indexes. Protected by the GC lock.
     js::ExclusiveAccessLockOrGCTaskData<Vector<size_t, 0, SystemAllocPolicy>> freeArenaIndexes;
 
+  public:
     // The extent of all allocated and free words in atom mark bitmaps.
     // This monotonically increases and may be read from without locking.
     mozilla::Atomic<size_t> allocatedWords;
 
-  public:
-    typedef Vector<uintptr_t, 0, SystemAllocPolicy> Bitmap;
-
     AtomMarkingRuntime()
       : allocatedWords(0)
     {}
 
     // Mark an arena as holding things in the atoms zone.
     void registerArena(Arena* arena);
 
     // Mark an arena as no longer holding things in the atoms zone.
     void unregisterArena(Arena* arena);
 
     // Fill |bitmap| with an atom marking bitmap based on the things that are
     // currently marked in the chunks used by atoms zone arenas. This returns
     // false on an allocation failure (but does not report an exception).
-    bool computeBitmapFromChunkMarkBits(JSRuntime* runtime, Bitmap& bitmap);
+    bool computeBitmapFromChunkMarkBits(JSRuntime* runtime, DenseBitmap& bitmap);
 
     // Update the atom marking bitmap in |zone| according to another
     // overapproximation of the reachable atoms in |bitmap|.
-    void updateZoneBitmap(Zone* zone, const Bitmap& bitmap);
+    void updateZoneBitmap(Zone* zone, const DenseBitmap& bitmap);
 
     // Set any bits in the chunk mark bitmaps for atoms which are marked in any
     // zone in the runtime.
     void updateChunkMarkBits(JSRuntime* runtime);
 
     // Mark an atom or id as being newly reachable by the context's zone.
     void markAtom(JSContext* cx, TenuredCell* thing);
     void markId(JSContext* cx, jsid id);
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -591,16 +591,66 @@ class ChainedIter
     operator T() const { return get(); }
     T operator->() const { return get(); }
 };
 
 typedef HashMap<Value*, const char*, DefaultHasher<Value*>, SystemAllocPolicy> RootedValueMap;
 
 using AllocKinds = mozilla::EnumSet<AllocKind>;
 
+template <typename T>
+class MemoryCounter
+{
+    // Bytes counter to measure memory pressure for GC scheduling. It runs
+    // from maxBytes down to zero.
+    mozilla::Atomic<ptrdiff_t, mozilla::ReleaseAcquire> bytes_;
+
+    // GC trigger threshold for memory allocations.
+    js::ActiveThreadData<size_t> maxBytes_;
+
+    // Whether a GC has been triggered as a result of bytes falling below
+    // zero.
+    //
+    // This should be a bool, but Atomic only supports 32-bit and pointer-sized
+    // types.
+    mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> triggered_;
+
+  public:
+    MemoryCounter()
+      : bytes_(0),
+        maxBytes_(0),
+        triggered_(false)
+    { }
+
+    void reset() {
+        bytes_ = maxBytes_;
+        triggered_ = false;
+    }
+
+    void setMax(size_t newMax) {
+        // For compatibility treat any value that exceeds PTRDIFF_T_MAX to
+        // mean that value.
+        maxBytes_ = (ptrdiff_t(newMax) >= 0) ? newMax : size_t(-1) >> 1;
+        reset();
+    }
+
+    bool update(T* owner, size_t bytes) {
+        bytes_ -= ptrdiff_t(bytes);
+        if (MOZ_UNLIKELY(isTooMuchMalloc())) {
+            if (!triggered_)
+                triggered_ = owner->triggerGCForTooMuchMalloc();
+        }
+        return triggered_;
+    }
+
+    ptrdiff_t bytes() const { return bytes_; }
+    size_t maxBytes() const { return maxBytes_; }
+    bool isTooMuchMalloc() const { return bytes_ <= 0; }
+};
+
 class GCRuntime
 {
   public:
     explicit GCRuntime(JSRuntime* rt);
     MOZ_MUST_USE bool init(uint32_t maxbytes, uint32_t maxNurseryBytes);
     void finishRoots();
     void finish();
 
@@ -662,18 +712,16 @@ class GCRuntime
     void setNextScheduled(uint32_t count);
     void verifyPreBarriers();
     void maybeVerifyPreBarriers(bool always);
     bool selectForMarking(JSObject* object);
     void clearSelectedForMarking();
     void setDeterministic(bool enable);
 #endif
 
-    size_t maxMallocBytesAllocated() { return maxMallocBytes; }
-
     uint64_t nextCellUniqueId() {
         MOZ_ASSERT(nextCellUniqueId_ > 0);
         uint64_t uid = ++nextCellUniqueId_;
         return uid;
     }
 
 #ifdef DEBUG
     bool shutdownCollectedEverything() const {
@@ -720,22 +768,23 @@ class GCRuntime
     bool isIncrementalGCInProgress() const { return state() != State::NotActive; }
 
     bool isCompactingGCEnabled() const;
 
     void setGrayRootsTracer(JSTraceDataOp traceOp, void* data);
     MOZ_MUST_USE bool addBlackRootsTracer(JSTraceDataOp traceOp, void* data);
     void removeBlackRootsTracer(JSTraceDataOp traceOp, void* data);
 
+    bool triggerGCForTooMuchMalloc() { return triggerGC(JS::gcreason::TOO_MUCH_MALLOC); }
+    int32_t getMallocBytes() const { return mallocCounter.bytes(); }
+    size_t maxMallocBytesAllocated() const { return mallocCounter.maxBytes(); }
+    bool isTooMuchMalloc() const { return mallocCounter.isTooMuchMalloc(); }
+    void resetMallocBytes() { mallocCounter.reset(); }
     void setMaxMallocBytes(size_t value);
-    int32_t getMallocBytes() const { return mallocBytesUntilGC; }
-    void resetMallocBytes();
-    bool isTooMuchMalloc() const { return mallocBytesUntilGC <= 0; }
     void updateMallocCounter(JS::Zone* zone, size_t nbytes);
-    void onTooMuchMalloc();
 
     void setGCCallback(JSGCCallback callback, void* data);
     void callGCCallback(JSGCStatus status) const;
     void setObjectsTenuredCallback(JSObjectsTenuredCallback callback,
                                    void* data);
     void callObjectsTenuredCallback();
     MOZ_MUST_USE bool addFinalizeCallback(JSFinalizeCallback callback, void* data);
     void removeFinalizeCallback(JSFinalizeCallback func);
@@ -1026,18 +1075,16 @@ class GCRuntime
     UnprotectedData<ChunkPool> availableChunks_;
 
     // When all arenas in a chunk are used, it is moved to the fullChunks pool
     // so as to reduce the cost of operations on the available lists.
     UnprotectedData<ChunkPool> fullChunks_;
 
     ActiveThreadData<RootedValueMap> rootsHash;
 
-    ActiveThreadData<size_t> maxMallocBytes;
-
     // An incrementing id used to assign unique ids to cells that require one.
     mozilla::Atomic<uint64_t, mozilla::ReleaseAcquire> nextCellUniqueId_;
 
     /*
      * Number of the committed arenas in all GC chunks including empty chunks.
      */
     mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> numArenasFreeCommitted;
     ActiveThreadData<VerifyPreTracer*> verifyPreData;
@@ -1243,27 +1290,17 @@ class GCRuntime
 
     Callback<JSGCCallback> gcCallback;
     Callback<JS::DoCycleCollectionCallback> gcDoCycleCollectionCallback;
     Callback<JSObjectsTenuredCallback> tenuredCallback;
     CallbackVector<JSFinalizeCallback> finalizeCallbacks;
     CallbackVector<JSWeakPointerZoneGroupCallback> updateWeakPointerZoneGroupCallbacks;
     CallbackVector<JSWeakPointerCompartmentCallback> updateWeakPointerCompartmentCallbacks;
 
-    /*
-     * Malloc counter to measure memory pressure for GC scheduling. It runs
-     * from maxMallocBytes down to zero.
-     */
-    mozilla::Atomic<ptrdiff_t, mozilla::ReleaseAcquire> mallocBytesUntilGC;
-
-    /*
-     * Whether a GC has been triggered as a result of mallocBytesUntilGC
-     * falling below zero.
-     */
-    mozilla::Atomic<bool, mozilla::ReleaseAcquire> mallocGCTriggered;
+    MemoryCounter<GCRuntime> mallocCounter;
 
     /*
      * The trace operations to trace embedding-specific GC roots. One is for
      * tracing through black roots and the other is for tracing through gray
      * roots. The black/gray distinction is only relevant to the cycle
      * collector.
      */
     CallbackVector<JSTraceDataOp> blackRootTracers;
--- a/js/src/gc/Iteration.cpp
+++ b/js/src/gc/Iteration.cpp
@@ -15,65 +15,65 @@
 
 #include "jscntxtinlines.h"
 #include "jsgcinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
 static void
-IterateCompartmentsArenasCells(JSContext* cx, Zone* zone, void* data,
-                               JSIterateCompartmentCallback compartmentCallback,
-                               IterateArenaCallback arenaCallback,
-                               IterateCellCallback cellCallback)
+IterateCompartmentsArenasCellsUnbarriered(JSContext* cx, Zone* zone, void* data,
+                                          JSIterateCompartmentCallback compartmentCallback,
+                                          IterateArenaCallback arenaCallback,
+                                          IterateCellCallback cellCallback)
 {
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
         (*compartmentCallback)(cx, data, comp);
 
     for (auto thingKind : AllAllocKinds()) {
         JS::TraceKind traceKind = MapAllocToTraceKind(thingKind);
         size_t thingSize = Arena::thingSize(thingKind);
 
         for (ArenaIter aiter(zone, thingKind); !aiter.done(); aiter.next()) {
             Arena* arena = aiter.get();
             (*arenaCallback)(cx->runtime(), data, arena, traceKind, thingSize);
-            for (ArenaCellIter iter(arena); !iter.done(); iter.next())
+            for (ArenaCellIterUnbarriered iter(arena); !iter.done(); iter.next())
                 (*cellCallback)(cx->runtime(), data, iter.getCell(), traceKind, thingSize);
         }
     }
 }
 
 void
-js::IterateZonesCompartmentsArenasCells(JSContext* cx, void* data,
-                                        IterateZoneCallback zoneCallback,
-                                        JSIterateCompartmentCallback compartmentCallback,
-                                        IterateArenaCallback arenaCallback,
-                                        IterateCellCallback cellCallback)
+js::IterateHeapUnbarriered(JSContext* cx, void* data,
+                           IterateZoneCallback zoneCallback,
+                           JSIterateCompartmentCallback compartmentCallback,
+                           IterateArenaCallback arenaCallback,
+                           IterateCellCallback cellCallback)
 {
     AutoPrepareForTracing prop(cx, WithAtoms);
 
     for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
         (*zoneCallback)(cx->runtime(), data, zone);
-        IterateCompartmentsArenasCells(cx, zone, data,
-                                       compartmentCallback, arenaCallback, cellCallback);
+        IterateCompartmentsArenasCellsUnbarriered(cx, zone, data,
+                                                  compartmentCallback, arenaCallback, cellCallback);
     }
 }
 
 void
-js::IterateZoneCompartmentsArenasCells(JSContext* cx, Zone* zone, void* data,
-                                       IterateZoneCallback zoneCallback,
-                                       JSIterateCompartmentCallback compartmentCallback,
-                                       IterateArenaCallback arenaCallback,
-                                       IterateCellCallback cellCallback)
+js::IterateHeapUnbarrieredForZone(JSContext* cx, Zone* zone, void* data,
+                                  IterateZoneCallback zoneCallback,
+                                  JSIterateCompartmentCallback compartmentCallback,
+                                  IterateArenaCallback arenaCallback,
+                                  IterateCellCallback cellCallback)
 {
     AutoPrepareForTracing prop(cx, WithAtoms);
 
     (*zoneCallback)(cx->runtime(), data, zone);
-    IterateCompartmentsArenasCells(cx, zone, data,
-                                   compartmentCallback, arenaCallback, cellCallback);
+    IterateCompartmentsArenasCellsUnbarriered(cx, zone, data,
+                                              compartmentCallback, arenaCallback, cellCallback);
 }
 
 void
 js::IterateChunks(JSContext* cx, void* data, IterateChunkCallback chunkCallback)
 {
     AutoPrepareForTracing prep(cx, SkipAtoms);
 
     for (auto chunk = cx->runtime()->gc.allNonEmptyChunks(); !chunk.done(); chunk.next())
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifdef MOZ_VALGRIND
 # include <valgrind/memcheck.h>
 #endif
 
+#include "mozilla/DebugOnly.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Sprintf.h"
 
 #include "jscntxt.h"
 #include "jsgc.h"
 #include "jsprf.h"
 
 #include "gc/GCInternals.h"
@@ -297,17 +298,17 @@ CheckEdgeTracer::onChild(const JS::GCCel
             return;
         }
     }
 }
 
 void
 js::gc::AssertSafeToSkipBarrier(TenuredCell* thing)
 {
-    Zone* zone = thing->zoneFromAnyThread();
+    mozilla::DebugOnly<Zone*> zone = thing->zoneFromAnyThread();
     MOZ_ASSERT(!zone->needsIncrementalBarrier() || zone->isAtomsZone());
 }
 
 static bool
 IsMarkedOrAllocated(const EdgeValue& edge)
 {
     if (!edge.thing || IsMarkedOrAllocated(TenuredCell::fromPointer(edge.thing)))
         return true;
@@ -526,16 +527,20 @@ CheckHeapTracer::onChild(const JS::GCCel
             fprintf(stderr, "  from %s %p %s edge\n",
                     GCTraceKindToAscii(cell->getTraceKind()), cell, name);
             name = parent.name;
         }
         fprintf(stderr, "  from root %s\n", name);
         return;
     }
 
+    // Don't trace into GC things owned by another runtime.
+    if (cell->runtimeFromAnyThread() != rt)
+        return;
+
     WorkItem item(thing, contextName(), parentIndex);
     if (!stack.append(item))
         oom = true;
 }
 
 void
 CheckHeapTracer::check(AutoLockForExclusiveAccess& lock)
 {
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -34,19 +34,16 @@ JS::Zone::Zone(JSRuntime* rt, ZoneGroup*
     gcWeakMapList_(group),
     compartments_(),
     gcGrayRoots_(group),
     gcWeakRefs_(group),
     weakCaches_(group),
     gcWeakKeys_(group, SystemAllocPolicy(), rt->randomHashCodeScrambler()),
     gcZoneGroupEdges_(group),
     typeDescrObjects_(group, this, SystemAllocPolicy()),
-    gcMallocBytes(0),
-    gcMaxMallocBytes(0),
-    gcMallocGCTriggered(false),
     markedAtoms_(group),
     usage(&rt->gc.usage),
     threshold(),
     gcDelayBytes(0),
     propertyTree_(group, this),
     baseShapes_(group, this, BaseShapeSet()),
     initialShapes_(group, this, InitialShapeSet()),
     data(group, nullptr),
@@ -64,16 +61,17 @@ JS::Zone::Zone(JSRuntime* rt, ZoneGroup*
 {
     /* Ensure that there are no vtables to mess us up here. */
     MOZ_ASSERT(reinterpret_cast<JS::shadow::Zone*>(this) ==
                static_cast<JS::shadow::Zone*>(this));
 
     AutoLockGC lock(rt);
     threshold.updateAfterGC(8192, GC_NORMAL, rt->gc.tunables, rt->gc.schedulingState, lock);
     setGCMaxMallocBytes(rt->gc.maxMallocBytesAllocated() * 0.9);
+    jitCodeCounter.setMax(jit::MaxCodeBytesPerProcess * 0.8);
 }
 
 Zone::~Zone()
 {
     JSRuntime* rt = runtimeFromAnyThread();
     if (this == rt->gc.systemZone)
         rt->gc.systemZone = nullptr;
 
@@ -88,17 +86,18 @@ Zone::~Zone()
 }
 
 bool Zone::init(bool isSystemArg)
 {
     isSystem = isSystemArg;
     return uniqueIds().init() &&
            gcZoneGroupEdges().init() &&
            gcWeakKeys().init() &&
-           typeDescrObjects().init();
+           typeDescrObjects().init() &&
+           markedAtoms().init();
 }
 
 void
 Zone::setNeedsIncrementalBarrier(bool needs, ShouldUpdateJit updateJit)
 {
     if (updateJit == UpdateJit && needs != jitUsingBarriers_) {
         jit::ToggleBarriers(this, needs);
         jitUsingBarriers_ = needs;
@@ -106,43 +105,16 @@ Zone::setNeedsIncrementalBarrier(bool ne
 
     MOZ_ASSERT_IF(needs && isAtomsZone(),
                   !runtimeFromActiveCooperatingThread()->hasHelperThreadZones());
     MOZ_ASSERT_IF(needs, canCollect());
     needsIncrementalBarrier_ = needs;
 }
 
 void
-Zone::resetGCMallocBytes()
-{
-    gcMallocBytes = ptrdiff_t(gcMaxMallocBytes);
-    gcMallocGCTriggered = false;
-}
-
-void
-Zone::setGCMaxMallocBytes(size_t value)
-{
-    /*
-     * For compatibility treat any value that exceeds PTRDIFF_T_MAX to
-     * mean that value.
-     */
-    gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1;
-    resetGCMallocBytes();
-}
-
-void
-Zone::onTooMuchMalloc()
-{
-    if (!gcMallocGCTriggered) {
-        GCRuntime& gc = runtimeFromAnyThread()->gc;
-        gcMallocGCTriggered = gc.triggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC);
-    }
-}
-
-void
 Zone::beginSweepTypes(FreeOp* fop, bool releaseTypes)
 {
     AutoClearTypeInferenceStateOnOOM oom(this);
     types.beginSweep(fop, releaseTypes, oom);
 }
 
 Zone::DebuggerVector*
 Zone::getOrCreateDebuggers(JSContext* cx)
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -161,38 +161,25 @@ struct Zone : public JS::shadow::Zone,
 
     void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
                                 size_t* typePool,
                                 size_t* baselineStubsOptimized,
                                 size_t* uniqueIdMap,
                                 size_t* shapeTables,
                                 size_t* atomsMarkBitmaps);
 
-    void resetGCMallocBytes();
-    void setGCMaxMallocBytes(size_t value);
-    void updateMallocCounter(size_t nbytes) {
-        // Note: this code may be run from worker threads. We tolerate any
-        // thread races when updating gcMallocBytes.
-        gcMallocBytes -= ptrdiff_t(nbytes);
-        if (MOZ_UNLIKELY(isTooMuchMalloc()))
-            onTooMuchMalloc();
-    }
-
     // Iterate over all cells in the zone. See the definition of ZoneCellIter
     // in jsgcinlines.h for the possible arguments and documentation.
     template <typename T, typename... Args>
     js::gc::ZoneCellIter<T> cellIter(Args&&... args) {
         return js::gc::ZoneCellIter<T>(const_cast<Zone*>(this), mozilla::Forward<Args>(args)...);
     }
 
-    bool isTooMuchMalloc() const { return gcMallocBytes <= 0; }
-    void onTooMuchMalloc();
-
     MOZ_MUST_USE void* onOutOfMemory(js::AllocFunction allocFunc, size_t nbytes,
-                                               void* reallocPtr = nullptr) {
+                                     void* reallocPtr = nullptr) {
         if (!js::CurrentThreadCanAccessRuntime(runtime_))
             return nullptr;
         return runtimeFromActiveCooperatingThread()->onOutOfMemory(allocFunc, nbytes, reallocPtr);
     }
     void reportAllocationOverflow() { js::ReportAllocationOverflow(nullptr); }
 
     void beginSweepTypes(js::FreeOp* fop, bool releaseTypes);
 
@@ -394,41 +381,58 @@ struct Zone : public JS::shadow::Zone,
     // There are no barriers here - the set contains only tenured objects so no
     // post-barrier is required, and these are weak references so no pre-barrier
     // is required.
     using TypeDescrObjectSet = js::GCHashSet<JSObject*,
                                              js::MovableCellHasher<JSObject*>,
                                              js::SystemAllocPolicy>;
   private:
     js::ZoneGroupData<JS::WeakCache<TypeDescrObjectSet>> typeDescrObjects_;
+
+    // Malloc counter to measure memory pressure for GC scheduling. This
+    // counter should be used only when it's not possible to know the size of
+    // a free.
+    js::gc::MemoryCounter<Zone> gcMallocCounter;
+
+    // Counter of JIT code executable memory for GC scheduling. Also imprecise,
+    // since wasm can generate code that outlives a zone.
+    js::gc::MemoryCounter<Zone> jitCodeCounter;
+
   public:
     JS::WeakCache<TypeDescrObjectSet>& typeDescrObjects() { return typeDescrObjects_.ref(); }
 
     bool addTypeDescrObject(JSContext* cx, HandleObject obj);
 
-    // Malloc counter to measure memory pressure for GC scheduling. It runs from
-    // gcMaxMallocBytes down to zero. This counter should be used only when it's
-    // not possible to know the size of a free.
-    mozilla::Atomic<ptrdiff_t, mozilla::ReleaseAcquire> gcMallocBytes;
+    bool triggerGCForTooMuchMalloc() {
+        return runtimeFromAnyThread()->gc.triggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC);
+    }
+
+    void resetGCMallocBytes() { gcMallocCounter.reset(); }
+    void setGCMaxMallocBytes(size_t value) { gcMallocCounter.setMax(value); }
+    void updateMallocCounter(size_t nbytes) { gcMallocCounter.update(this, nbytes); }
+    size_t GCMaxMallocBytes() const { return gcMallocCounter.maxBytes(); }
+    size_t GCMallocBytes() const { return gcMallocCounter.bytes(); }
 
-    // GC trigger threshold for allocations on the C heap.
-    js::ActiveThreadData<size_t> gcMaxMallocBytes;
+    void updateJitCodeMallocBytes(size_t size) { jitCodeCounter.update(this, size); }
 
-    // Whether a GC has been triggered as a result of gcMallocBytes falling
-    // below zero.
-    //
-    // This should be a bool, but Atomic only supports 32-bit and pointer-sized
-    // types.
-    mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> gcMallocGCTriggered;
+    // Resets all the memory counters.
+    void resetAllMallocBytes() {
+        resetGCMallocBytes();
+        jitCodeCounter.reset();
+    }
+    bool isTooMuchMalloc() const {
+        return gcMallocCounter.isTooMuchMalloc() ||
+               jitCodeCounter.isTooMuchMalloc();
+    }
 
   private:
     // Bitmap of atoms marked by this zone.
-    js::ZoneGroupOrGCTaskData<js::gc::AtomMarkingRuntime::Bitmap> markedAtoms_;
+    js::ZoneGroupOrGCTaskData<js::SparseBitmap> markedAtoms_;
   public:
-    js::gc::AtomMarkingRuntime::Bitmap& markedAtoms() { return markedAtoms_.ref(); }
+    js::SparseBitmap& markedAtoms() { return markedAtoms_.ref(); }
 
     // Track heap usage under this Zone.
     js::gc::HeapUsage usage;
 
     // Thresholds used to trigger GC.
     js::gc::ZoneHeapThreshold threshold;
 
     // Amount of data to allocate before triggering a new incremental slice for
--- a/js/src/gdb/tests/test-ExecutableAllocator.cpp
+++ b/js/src/gdb/tests/test-ExecutableAllocator.cpp
@@ -13,32 +13,32 @@ FRAGMENT(ExecutableAllocator, empty) {
 
     (void) execAlloc;
 }
 
 FRAGMENT(ExecutableAllocator, onepool) {
     using namespace js::jit;
     ExecutablePool* pool = nullptr;
     ExecutableAllocator execAlloc(cx->runtime());
-    execAlloc.alloc(16 * 1024, &pool, BASELINE_CODE);
+    execAlloc.alloc(cx, 16 * 1024, &pool, BASELINE_CODE);
 
     breakpoint();
 
     (void) pool;
     (void) execAlloc;
 }
 
 FRAGMENT(ExecutableAllocator, twopools) {
     using namespace js::jit;
     ExecutablePool* init = nullptr;
     ExecutablePool* pool = nullptr;
     ExecutableAllocator execAlloc(cx->runtime());
 
-    execAlloc.alloc(16 * 1024, &init, BASELINE_CODE);
+    execAlloc.alloc(cx, 16 * 1024, &init, BASELINE_CODE);
 
     do { // Keep allocating until we get a second pool.
-        execAlloc.alloc(32 * 1024, &pool, ION_CODE);
+        execAlloc.alloc(cx, 32 * 1024, &pool, ION_CODE);
     } while (pool == init);
 
     breakpoint();
 
     (void) execAlloc;
 }
--- a/js/src/jit-test/lib/wasm-binary.js
+++ b/js/src/jit-test/lib/wasm-binary.js
@@ -66,16 +66,17 @@ const I32Store16       = 0x3b;
 const I64Store8        = 0x3c;
 const I64Store16       = 0x3d;
 const I64Store32       = 0x3e;
 const GrowMemoryCode   = 0x40;
 const I32ConstCode     = 0x41;
 const I64ConstCode     = 0x42;
 const F32ConstCode     = 0x43;
 const F64ConstCode     = 0x44;
+const I32AddCode       = 0x6a;
 const I32DivSCode      = 0x6d;
 const I32DivUCode      = 0x6e;
 const I32RemSCode      = 0x6f;
 const I32RemUCode      = 0x70;
 const I32TruncSF32Code = 0xa8;
 const I32TruncUF32Code = 0xa9;
 const I32TruncSF64Code = 0xaa;
 const I32TruncUF64Code = 0xab;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/modules/empty.js
@@ -0,0 +1,1 @@
+// Intentionally empty.
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/modules/export-circular-nonexisting-binding-1.js
@@ -0,0 +1,4 @@
+import "export-circular-nonexisting-binding-2.js";
+
+export* from "empty.js";
+export {x} from "empty.js";
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/modules/export-circular-nonexisting-binding-2.js
@@ -0,0 +1,1 @@
+export {x} from "export-circular-nonexisting-binding-1.js";
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/modules/export-star-circular-1.js
@@ -0,0 +1,1 @@
+export* from "export-star-circular-2.js";
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/modules/export-star-circular-2.js
@@ -0,0 +1,3 @@
+export {y as x} from "export-star-circular-1.js";
+
+export var y = "pass";
--- a/js/src/jit-test/tests/atomics/basic-tests.js
+++ b/js/src/jit-test/tests/atomics/basic-tests.js
@@ -207,18 +207,20 @@ var globlength = 0;		// Will be set late
 function testRangeCAS(a) {
     dprint("Range: " + a.constructor.name);
 
     var msg = /out-of-range index/; // A generic message
 
     assertErrorMessage(() => Atomics.compareExchange(a, -1, 0, 1), RangeError, msg);
     assertEq(a[0], 0);
 
-    assertErrorMessage(() => Atomics.compareExchange(a, "hi", 0, 1), RangeError, msg);
-    assertEq(a[0], 0);
+    // Converted to 0
+    assertEq(Atomics.compareExchange(a, "hi", 0, 33), 0);
+    assertEq(a[0], 33);
+    a[0] = 0;
 
     assertErrorMessage(() => Atomics.compareExchange(a, a.length + 5, 0, 1), RangeError, msg);
     assertEq(a[0], 0);
 
     assertErrorMessage(() => Atomics.compareExchange(a, globlength, 0, 1), RangeError, msg);
     assertEq(a[0], 0);
 }
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/nukedCCW.js
@@ -0,0 +1,23 @@
+var wrapper = evaluate("({a: 15, b: {c: 42}})", {global: newGlobal({sameZoneAs: this})});
+
+function test() {
+    var i, error;
+    try {
+        for (i = 0; i < 150; i++) {
+            assertEq(wrapper.b.c, 42);
+            assertEq(wrapper.a, 15);
+
+            if (i == 142) {
+                // Next access to wrapper.b should throw.
+                nukeCCW(wrapper);
+            }
+        }
+    } catch (e) {
+        error = e;
+    }
+
+    assertEq(error.message.includes("dead object"), true);
+    assertEq(i, 143);
+}
+
+test();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1337414.js
@@ -0,0 +1,46 @@
+var lfLogBuffer = `
+gczeal(15,10);
+try {
+    a = []
+    gczeal(2, 2)()
+} catch (e) {}
+a.every(function() {})
+//corefuzz-dcd-endofdata
+//corefuzz-dcd-selectmode 5
+`;
+lfLogBuffer = lfLogBuffer.split('\n');
+lfPreamble = `
+`;
+var lfCodeBuffer = "";
+var lfRunTypeLimit = 7;
+var lfOffThreadGlobal = newGlobal();
+try {} catch (lfVare5) {}
+var lfAccumulatedCode = lfPreamble;
+while (true) {
+    var line = lfLogBuffer.shift();
+    if (line == null) {
+        break;
+    } else if (line == "//corefuzz-dcd-endofdata") {
+        loadFile(lfCodeBuffer);
+    } else if (line.indexOf("//corefuzz-dcd-selectmode ") === 0) {
+        loadFile(line);
+    } else {
+        lfCodeBuffer += line + "\n";
+    }
+}
+if (lfCodeBuffer) loadFile(lfCodeBuffer);
+function loadFile(lfVarx) {
+    try {
+        if (lfVarx.indexOf("//corefuzz-dcd-selectmode ") === 0) {
+            lfRunTypeId = parseInt(lfVarx.split(" ")[1]) % lfRunTypeLimit;
+        } else {
+            switch (lfRunTypeId) {
+                case 5:
+                    evalInWorker(lfAccumulatedCode);
+                    evaluate(lfVarx);
+            }
+        }
+    } catch (lfVare) {
+        lfAccumulatedCode += "try { evaluate(`\n" + lfVarx + "\n`); } catch(exc) {}\n";
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/modules/export-circular-nonexisting-binding.js
@@ -0,0 +1,3 @@
+// |jit-test| module; error:SyntaxError
+
+import "export-circular-nonexisting-binding-1.js";
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/modules/export-star-cannot-rescue-missing-export.js
@@ -0,0 +1,4 @@
+// |jit-test| module; error:SyntaxError
+
+export { a } from "empty.js";
+export* from "module1.js";
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/modules/export-star-circular-dependencies.js
@@ -0,0 +1,6 @@
+// |jit-test| module
+
+import { x, y } from "export-star-circular-1.js";
+
+assertEq(x, "pass");
+assertEq(y, "pass");
--- a/js/src/jit-test/tests/wasm/binary.js
+++ b/js/src/jit-test/tests/wasm/binary.js
@@ -59,19 +59,16 @@ assertErrorMessage(() => wasmEval(toU8([
 assertErrorMessage(() => wasmEval(toU8([42])), CompileError, magicError);
 assertErrorMessage(() => wasmEval(toU8([magic0, magic1, magic2])), CompileError, magicError);
 assertErrorMessage(() => wasmEval(toU8([1,2,3,4])), CompileError, magicError);
 assertErrorMessage(() => wasmEval(toU8([magic0, magic1, magic2, magic3])), CompileError, versionError(0x6d736100));
 assertErrorMessage(() => wasmEval(toU8([magic0, magic1, magic2, magic3, 1])), CompileError, versionError(0x6d736100));
 assertErrorMessage(() => wasmEval(toU8([magic0, magic1, magic2, magic3, ver0])), CompileError, versionError(0x6d736100));
 assertErrorMessage(() => wasmEval(toU8([magic0, magic1, magic2, magic3, ver0, ver1, ver2])), CompileError, versionError(0x6d736100));
 
-// This test should be removed shortly.
-assertEq(WebAssembly.validate(toU8([magic0, magic1, magic2, magic3, 0xd, 0x0, 0x0, 0x0])), true);
-
 function moduleHeaderThen(...rest) {
     return [magic0, magic1, magic2, magic3, ver0, ver1, ver2, ver3, ...rest];
 }
 
 var o = wasmEval(toU8(moduleHeaderThen()));
 assertEq(Object.getOwnPropertyNames(o).length, 0);
 
 // unfinished known sections
--- a/js/src/jit-test/tests/wasm/errors.js
+++ b/js/src/jit-test/tests/wasm/errors.js
@@ -1,13 +1,14 @@
 load(libdir + "wasm.js");
 load(libdir + "wasm-binary.js");
 
 const Module = WebAssembly.Module;
 const Instance = WebAssembly.Instance;
+const CompileError = WebAssembly.CompileError;
 const RuntimeError = WebAssembly.RuntimeError;
 
 function isWasmFunction(name) {
     return /^wasm-function\[\d*\]$/.test(name)
 }
 
 function parseStack(stack) {
     var frames = stack.split('\n');
@@ -148,8 +149,26 @@ assertEq(stack.length > N, true);
 var lastLine = stack[0].line;
 for (var i = 1; i < N; i++) {
     assertEq(isWasmFunction(stack[i].name), true);
     assertEq(stack[i].line > lastLine, true);
     lastLine = stack[i].line;
     assertEq(binary[stack[i].line], i == 3 ? CallIndirectCode : CallCode);
     assertEq(stack[i].column, 1);
 }
+
+function testCompileError(opcode, text) {
+    var binary = new Uint8Array(wasmTextToBinary(text));
+    var exn;
+    try {
+        new Instance(new Module(binary));
+    } catch (e) {
+        exn = e;
+    }
+
+    assertEq(exn instanceof CompileError, true);
+    var offset = Number(exn.message.match(/at offset (\d*)/)[1]);
+    assertEq(binary[offset], opcode);
+}
+
+testCompileError(CallCode, '(module (func $f (param i32)) (func $g call $f))');
+testCompileError(I32AddCode, '(module (func (i32.add (i32.const 1) (f32.const 1))))');
+testCompileError(EndCode, '(module (func (block i32)))');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/regress/current-memory-tls.js
@@ -0,0 +1,97 @@
+load(libdir + "wasm.js");
+
+// Bug 1341650:
+// - when compiled with Ion, pass the TLS register to current_memory;
+// - when compiled with Baseline, don't clobber the last stack slot when
+// calling into current_memory/grow_memory;
+
+// This toy module starts with an empty memory, then tries to set values at different
+// indexes, automatically growing memory when that would trigger an out of
+// bounds access, for the exact right amount of pages. Note it's not made for
+// efficiency, but optimizing it doesn't trigger the bugs mentioned above.
+
+let i = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(`
+(module
+    (memory $mem (export "mem") 0 65535)
+
+    (func (export "cur_mem") (result i32) (current_memory))
+
+    (func $maybeGrow (param $i i32) (local $smem i32)
+     ;; get current_memory in number of bytes, not pages.
+     current_memory
+     i64.extend_u/i32
+     i64.const 65536
+     i64.mul
+
+     ;; get the last byte index accessed by an int32 access.
+     get_local $i
+     i32.const 3
+     i32.add
+     tee_local $i
+     i64.extend_u/i32
+
+     ;; if the memory is too small, grow it.
+     i64.le_u
+     if
+         ;; get the floor of the accessed *page* index.
+         get_local $i
+         i64.extend_u/i32
+         i64.const 65536
+         i64.div_u
+
+         ;; subtract to that the size of the current memory in pages;
+         ;; that's the amount of pages we want to grow, minus one.
+         current_memory
+         i64.extend_u/i32
+
+         i64.sub
+
+         ;; add 1 to that amount.
+         i64.const 1
+         i64.add
+
+         ;; get back to i32 and grow memory.
+         i32.wrap/i64
+         grow_memory
+         drop
+     end
+    )
+
+    (func (export "set") (param $i i32) (param $v i32)
+     get_local $i
+     call $maybeGrow
+     get_local $i
+     get_local $v
+     i32.store
+    )
+
+    (func (export "get") (param $i i32) (result i32)
+     get_local $i
+     i32.load
+    )
+)
+`))).exports;
+
+assertEq(i.cur_mem(), 0);
+
+i.set(0, 1);
+assertEq(i.get(0), 1);
+
+assertEq(i.cur_mem(), 1);
+
+i.set(1234, 1);
+assertEq(i.get(1234), 1);
+
+assertEq(i.cur_mem(), 1);
+
+i.set(65532, 1);
+assertEq(i.get(65532), 1);
+
+assertErrorMessage(() => i.get(65533), WebAssembly.RuntimeError, /index out of bounds/);
+
+assertEq(i.cur_mem(), 1);
+
+i.set(65533, 1);
+assertEq(i.get(65533), 1);
+
+assertEq(i.cur_mem(), 2);
--- a/js/src/jit-test/tests/wasm/spec/binary.wast
+++ b/js/src/jit-test/tests/wasm/spec/binary.wast
@@ -1,18 +1,18 @@
-(module "\00asm\0d\00\00\00")
-(module "\00asm" "\0d\00\00\00")
-(module $M1 "\00asm\0d\00\00\00")
-(module $M2 "\00asm" "\0d\00\00\00")
+(module "\00asm\01\00\00\00")
+(module "\00asm" "\01\00\00\00")
+(module $M1 "\00asm\01\00\00\00")
+(module $M2 "\00asm" "\01\00\00\00")
 
 (assert_malformed (module "") "unexpected end")
 (assert_malformed (module "\01") "unexpected end")
 (assert_malformed (module "\00as") "unexpected end")
 (assert_malformed (module "asm\00") "magic header not detected")
 (assert_malformed (module "msa\00") "magic header not detected")
-(assert_malformed (module "msa\00\0d\00\00\00") "magic header not detected")
+(assert_malformed (module "msa\00\01\00\00\00") "magic header not detected")
 (assert_malformed (module "msa\00\00\00\00\0d") "magic header not detected")
 
 (assert_malformed (module "\00asm&qu