Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 24 Feb 2017 11:54:01 +0100
changeset 373727 87ca04c38b8c470b77c389f82f417f33ce69d2fb
parent 373726 41a156cf149e667f172f93c7492b466cfbb20565 (current diff)
parent 373706 be661bae6cb9a53935c5b87744bf68879d9ebcc5 (diff)
child 373728 6a1d6726edd03e6b050db304e07a2c8abbc5997c
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone54.0a1
Merge mozilla-central to autoland
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
modules/libpref/init/all.js
toolkit/components/telemetry/Histograms.json
toolkit/content/aboutSupport.js
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") "unexpected end")
-(assert_malformed (module "\00asm\0d") "unexpected end")
-(assert_malformed (module "\00asm\0d\00\00") "unexpected end")
+(assert_malformed (module "\00asm\01") "unexpected end")
+(assert_malformed (module "\00asm\01\00\00") "unexpected end")
 (assert_malformed (module "\00asm\0e\00\00\00") "unknown binary version")
 (assert_malformed (module "\00asm\00\00\00\0d") "unknown binary version")
--- a/js/src/jit-test/tests/wasm/spec/custom_section.wast
+++ b/js/src/jit-test/tests/wasm/spec/custom_section.wast
@@ -1,20 +1,20 @@
 (module
-  "\00asm" "\0d\00\00\00"
+  "\00asm" "\01\00\00\00"
   "\00\24\10" "a custom section" "this is the payload"
   "\00\20\10" "a custom section" "this is payload"
   "\00\11\10" "a custom section" ""
   "\00\10\00" "" "this is payload"
   "\00\01\00" "" ""
   "\00\24\10" "\00\00custom sectio\00" "this is the payload"
 )
 
 (module
-  "\00asm" "\0d\00\00\00"
+  "\00asm" "\01\00\00\00"
   "\00\0e\06" "custom" "payload"
   "\00\0e\06" "custom" "payload"
   "\01\01\00"  ;; type section
   "\00\0e\06" "custom" "payload"
   "\00\0e\06" "custom" "payload"
   "\02\01\00"  ;; import section
   "\00\0e\06" "custom" "payload"