merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 04 Feb 2016 12:02:05 +0100
changeset 283011 f53533d9eb771f3251921949ab2c888def70f41f
parent 282849 03297f8c28a08d2b39a252c7b368524d9e69da69 (current diff)
parent 283010 751e2bc571abba9fca0f9c94f9ac88240b7fd2cb (diff)
child 283030 ced4e3f6c8bcf408df4a4f35b69f1c4d40ee7cb2
child 283085 593b8d0596a5493dd9ec8972c73194ebc54953b1
push idunknown
push userunknown
push dateunknown
reviewersmerge
milestone47.0a1
merge mozilla-inbound to mozilla-central a=merge
configure.in
devtools/client/preferences/devtools.js
devtools/client/webconsole/webconsole.js
layout/reftests/w3c-css/submitted/masking/blue-100x50-transparent-100X50.png
layout/reftests/w3c-css/submitted/masking/blue-100x50-transparent-100X50.svg
layout/reftests/w3c-css/submitted/masking/transparent-100x50-blue-100X50.png
layout/reftests/w3c-css/submitted/masking/transparent-100x50-blue-100X50.svg
testing/mochitest/b2g_start_script.js
testing/web-platform/tests/workers/interfaces/WorkerUtils/importScripts/001.html
testing/web-platform/tests/workers/interfaces/WorkerUtils/importScripts/002.html
--- a/.eslintignore
+++ b/.eslintignore
@@ -34,17 +34,16 @@ modules/**
 mozglue/**
 netwerk/**
 nsprpub/**
 other-licenses/**
 parser/**
 probes/**
 python/**
 rdf/**
-services/**
 startupcache/**
 testing/**
 tools/**
 uriloader/**
 view/**
 webapprt/**
 widget/**
 xpcom/**
@@ -165,16 +164,21 @@ mobile/android/modules/ContactService.js
 mobile/android/modules/WebappManager.jsm
 
 # Non-standard `(catch ex if ...)`
 mobile/android/components/Snippets.js
 
 # Bug 1178739: Ignore this file as a quick fix for "Illegal yield expression"
 mobile/android/modules/HomeProvider.jsm
 
+# services/ exclusions
+
+# Uses `#filter substitution`
+services/sync/modules/constants.js
+
 # toolkit/ exclusions
 
 # Not part of the default build
 toolkit/components/help/**
 
 # Intentionally invalid JS
 toolkit/components/workerloader/tests/moduleF-syntax-error.js
 
--- a/accessible/xpcom/xpcAccessibleGeneric.h
+++ b/accessible/xpcom/xpcAccessibleGeneric.h
@@ -34,17 +34,17 @@ public:
       mSupportedIfaces |= eSelectable;
     if (aInternal->HasNumericValue())
       mSupportedIfaces |= eValue;
     if (aInternal->IsLink())
       mSupportedIfaces |= eHyperLink;
   }
 
   xpcAccessibleGeneric(ProxyAccessible* aProxy, uint32_t aInterfaces) :
-    mIntl(aProxy)
+    mIntl(aProxy), mSupportedIfaces(0)
   {
     if (aInterfaces & Interfaces::SELECTION) {
       mSupportedIfaces |= eSelectable;
     }
       if (aInterfaces & Interfaces::VALUE) {
         mSupportedIfaces |= eValue;
       }
       if (aInterfaces & Interfaces::HYPERLINK) {
--- a/b2g/components/PersistentDataBlock.jsm
+++ b/b2g/components/PersistentDataBlock.jsm
@@ -61,17 +61,17 @@ function debug(str) {
 
 function toHexString(data) {
   function toHexChar(charCode) {
     return ("0" + charCode.toString(16).slice(-2));
   }
   let hexString = "";
   if (typeof data === "string") {
     hexString = Array.from(data, (c, i) => toHexChar(data.charCodeAt(i))).join("");
-  } else if (typeof data === "array") {
+  } else if (data instanceof Array) {
     hexString = data.map(toHexChar).join("");
   }
   return hexString;
 }
 
 function arr2bstr(arr) {
   let bstr = "";
   for (let i = 0; i < arr.length; i++) {
--- a/b2g/components/test/unit/file_persistentdatablock.js
+++ b/b2g/components/test/unit/file_persistentdatablock.js
@@ -28,17 +28,17 @@ function log(str) {
 
 function toHexString(data) {
   function toHexChar(charCode) {
     return ("0" + charCode.toString(16).slice(-2));
   }
   let hexString = "";
   if (typeof data === "string") {
     hexString = Array.from(data, (c, i) => toHexChar(data.charCodeAt(i))).join("");
-  } else if (typeof data === "array") {
+  } else if (data instanceof Array) {
     hexString = data.map(toHexChar).join("");
   }
   return hexString;
 }
 
 function _prepareConfig(_args) {
   let args = _args || {};
   // This digest has been previously calculated given the data to be written later, and setting the OEM Unlocked Enabled byte
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -538,8 +538,10 @@ support-files =
 [browser_domFullscreen_fullscreenMode.js]
 [browser_menuButtonBadgeManager.js]
 [browser_aboutTabCrashed.js]
 skip-if = !e10s || !crashreporter
 [browser_aboutTabCrashed_clearEmail.js]
 skip-if = !e10s || !crashreporter
 [browser_aboutTabCrashed_showForm.js]
 skip-if = !e10s || !crashreporter
+[browser_aboutTabCrashed_withoutDump.js]
+skip-if = !e10s
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_aboutTabCrashed_withoutDump.js
@@ -0,0 +1,44 @@
+"use strict";
+
+const PAGE = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page.";
+
+/**
+ * Monkey patches TabCrashHandler.getDumpID to return null in order to test
+ * about:tabcrashed when a dump is not available.
+ */
+add_task(function* setup() {
+  let originalGetDumpID = TabCrashHandler.getDumpID;
+  TabCrashHandler.getDumpID = function(browser) { return null; };
+  registerCleanupFunction(() => {
+    TabCrashHandler.getDumpID = originalGetDumpID;
+  });
+});
+
+/**
+ * Tests tab crash page when a dump is not available.
+ */
+add_task(function* test_without_dump() {
+  return BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: PAGE,
+  }, function*(browser) {
+    let tab = gBrowser.getTabForBrowser(browser);
+    yield BrowserTestUtils.crashBrowser(browser);
+
+    let tabRemovedPromise = BrowserTestUtils.removeTab(tab, { dontRemove: true });
+
+    yield ContentTask.spawn(browser, null, function*() {
+      let doc = content.document;
+      ok(!doc.documentElement.classList.contains("crashDumpAvailable"),
+         "doesn't have crash dump");
+
+      let container = doc.getElementById("crash-reporter-container");
+      ok(container, "has crash-reporter-container");
+      ok(container.hidden, "crash-reporter-container is hidden");
+
+      doc.getElementById("closeTab").click();
+    });
+
+    yield tabRemovedPromise;
+  });
+});
--- a/browser/extensions/shumway/content/ShumwayStreamConverter.jsm
+++ b/browser/extensions/shumway/content/ShumwayStreamConverter.jsm
@@ -27,16 +27,17 @@ const SHUMWAY_CONTENT_TYPE = 'applicatio
 const EXPECTED_PLAYPREVIEW_URI_PREFIX = 'data:application/x-moz-playpreview;,' +
                                         SHUMWAY_CONTENT_TYPE;
 
 const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}';
 const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}';
 
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
+Cu.import('resource://gre/modules/NetUtil.jsm');
 
 XPCOMUtils.defineLazyModuleGetter(this, 'PrivateBrowsingUtils',
   'resource://gre/modules/PrivateBrowsingUtils.jsm');
 
 XPCOMUtils.defineLazyModuleGetter(this, 'ShumwayTelemetry',
   'resource://shumway/ShumwayTelemetry.jsm');
 
 Components.utils.import('chrome://shumway/content/StartupInfo.jsm');
@@ -299,27 +300,20 @@ ShumwayStreamConverterBase.prototype = {
 
     // TODO For now suspending request, however we can continue fetching data
     aRequest.suspend();
 
     var originalURI = aRequest.URI;
 
     // Create a new channel that loads the viewer as a chrome resource.
     var viewerUrl = 'chrome://shumway/content/viewer.wrapper.html';
-    // TODO use only newChannel2 after FF37 is released.
-    var channel = Services.io.newChannel2 ?
-                  Services.io.newChannel2(viewerUrl,
-                                          null,
-                                          null,
-                                          null, // aLoadingNode
-                                          Services.scriptSecurityManager.getSystemPrincipal(),
-                                          null, // aTriggeringPrincipal
-                                          Ci.nsILoadInfo.SEC_NORMAL,
-                                          Ci.nsIContentPolicy.TYPE_OTHER) :
-                  Services.io.newChannel(viewerUrl, null, null);
+    var channel = NetUtil.newChannel({
+      uri: viewerUrl,
+      loadUsingSystemPrincipal: true
+    });
 
     var converter = this;
     var listener = this.listener;
     // Proxy all the request observer calls, when it gets to onStopRequest
     // we can get the dom window.
     var proxy = {
       onStartRequest: function(request, context) {
         listener.onStartRequest(aRequest, context);
@@ -365,17 +359,17 @@ ShumwayStreamConverterBase.prototype = {
     channel.loadGroup = aRequest.loadGroup;
 
     // We can use all powerful principal: we are opening chrome:// web page,
     // which will need lots of permission.
     var securityManager = Cc['@mozilla.org/scriptsecuritymanager;1']
                           .getService(Ci.nsIScriptSecurityManager);
     var resourcePrincipal = securityManager.getSystemPrincipal();
     aRequest.owner = resourcePrincipal;
-    channel.asyncOpen(proxy, aContext);
+    channel.asyncOpen2(proxy);
   },
 
   // nsIRequestObserver::onStopRequest
   onStopRequest: function(aRequest, aContext, aStatusCode) {
     // Do nothing.
   }
 };
 
--- a/browser/modules/ContentCrashHandlers.jsm
+++ b/browser/modules/ContentCrashHandlers.jsm
@@ -223,17 +223,17 @@ this.TabCrashHandler = {
     this.pageListener.sendAsyncMessage("UpdateCount", {
       count: this._crashedTabCount,
     });
 
     let browser = message.target.browser;
 
     let dumpID = this.getDumpID(browser);
     if (!dumpID) {
-      message.target.sendAsyncMessge("SetCrashReportAvailable", {
+      message.target.sendAsyncMessage("SetCrashReportAvailable", {
         hasReport: false,
       });
       return;
     }
 
     let sendReport = this.prefs.getBoolPref("sendReport");
     let includeURL = this.prefs.getBoolPref("includeURL");
     let emailMe = this.prefs.getBoolPref("emailMe");
--- a/config/config.mk
+++ b/config/config.mk
@@ -184,42 +184,33 @@ else
 ASFLAGS += $(_DEBUG_ASFLAGS)
 endif
 OS_CFLAGS += $(_DEBUG_CFLAGS)
 OS_CXXFLAGS += $(_DEBUG_CFLAGS)
 OS_LDFLAGS += $(_DEBUG_LDFLAGS)
 
 # XXX: What does this? Bug 482434 filed for better explanation.
 ifeq ($(OS_ARCH)_$(GNU_CC),WINNT_)
-ifdef MOZ_DEBUG
-ifneq (,$(MOZ_BROWSE_INFO)$(MOZ_BSCFILE))
-OS_CFLAGS += -FR
-OS_CXXFLAGS += -FR
-endif
-else # ! MOZ_DEBUG
+ifndef MOZ_DEBUG
 
 # MOZ_DEBUG_SYMBOLS generates debug symbols in separate PDB files.
 # Used for generating an optimized build with debugging symbols.
 # Used in the Windows nightlies to generate symbols for crash reporting.
 ifdef MOZ_DEBUG_SYMBOLS
-OS_CXXFLAGS += -UDEBUG -DNDEBUG
-OS_CFLAGS += -UDEBUG -DNDEBUG
 ifdef HAVE_64BIT_BUILD
 OS_LDFLAGS += -DEBUG -OPT:REF,ICF
 else
 OS_LDFLAGS += -DEBUG -OPT:REF
 endif
 endif
 
 #
 # Handle DMD in optimized builds.
-# No opt to give sane callstacks.
 #
 ifdef MOZ_DMD
-MOZ_OPTIMIZE_FLAGS=-Zi -Od -UDEBUG -DNDEBUG
 ifdef HAVE_64BIT_BUILD
 OS_LDFLAGS = -DEBUG -OPT:REF,ICF
 else
 OS_LDFLAGS = -DEBUG -OPT:REF
 endif
 endif # MOZ_DMD
 
 endif # MOZ_DEBUG
--- a/configure.in
+++ b/configure.in
@@ -8743,17 +8743,16 @@ dnl policy alert), set MOZ_DATA_REPORTIN
 dnl We need SUBST for build system and DEFINE for xul preprocessor.
 if test -n "$MOZ_TELEMETRY_REPORTING" || test -n "$MOZ_SERVICES_HEALTHREPORT" || test -n "$MOZ_CRASHREPORTER"; then
   MOZ_DATA_REPORTING=1
   AC_DEFINE(MOZ_DATA_REPORTING)
   AC_SUBST(MOZ_DATA_REPORTING)
 fi
 
 dnl win32 options
-AC_SUBST(MOZ_BROWSE_INFO)
 AC_SUBST(WIN32_REDIST_DIR)
 AC_SUBST(MAKENSISU)
 
 dnl Echo the CFLAGS to remove extra whitespace.
 CFLAGS=`echo \
     $_WARNINGS_CFLAGS \
     $CFLAGS`
 
--- a/devtools/client/preferences/devtools.js
+++ b/devtools/client/preferences/devtools.js
@@ -286,16 +286,20 @@ pref("devtools.webconsole.inputHistoryCo
 // be cleared each time page navigation happens.
 pref("devtools.webconsole.persistlog", false);
 
 // Web Console timestamp: |true| if you want the logs and instructions
 // in the Web Console to display a timestamp, or |false| to not display
 // any timestamps.
 pref("devtools.webconsole.timestampMessages", false);
 
+// Web Console automatic multiline mode: |true| if you want incomplete statements
+// to automatically trigger multiline editing (equivalent to shift + enter).
+pref("devtools.webconsole.autoMultiline", true);
+
 // The number of lines that are displayed in the web console for the Net,
 // CSS, JS and Web Developer categories. These defaults should be kept in sync
 // with DEFAULT_LOG_LIMIT in the webconsole frontend.
 pref("devtools.hud.loglimit.network", 1000);
 pref("devtools.hud.loglimit.cssparser", 1000);
 pref("devtools.hud.loglimit.exception", 1000);
 pref("devtools.hud.loglimit.console", 1000);
 
--- a/devtools/client/webconsole/test/browser.ini
+++ b/devtools/client/webconsole/test/browser.ini
@@ -324,16 +324,17 @@ skip-if = e10s # Bug 1042253 - webconsol
 [browser_webconsole_input_field_focus_on_panel_select.js]
 [browser_webconsole_inspect-parsed-documents.js]
 [browser_webconsole_js_input_expansion.js]
 [browser_webconsole_jsterm.js]
 skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
 [browser_webconsole_live_filtering_of_message_types.js]
 [browser_webconsole_live_filtering_on_search_strings.js]
 [browser_webconsole_message_node_id.js]
+[browser_webconsole_multiline_input.js]
 [browser_webconsole_netlogging.js]
 [browser_webconsole_netlogging_basic.js]
 [browser_webconsole_netlogging_panel.js]
 [browser_webconsole_netlogging_reset_filter.js]
 [browser_webconsole_notifications.js]
 [browser_webconsole_open-links-without-callback.js]
 [browser_webconsole_promise.js]
 [browser_webconsole_output_copy_newlines.js]
--- a/devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js
@@ -2,32 +2,37 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const TEST_URI = "data:text/html;charset=utf-8,<p>bug 585991 - autocomplete " +
                  "popup keyboard usage test";
+
+// We should turn off auto-multiline editing during these tests
+const PREF_AUTO_MULTILINE = "devtools.webconsole.autoMultiline";
 var HUD, popup, jsterm, inputNode, completeNode;
 
 add_task(function*() {
+  Services.prefs.setBoolPref(PREF_AUTO_MULTILINE, false);
   yield loadTab(TEST_URI);
   let hud = yield openConsole();
 
   yield consoleOpened(hud);
   yield popupHideAfterTab();
   yield testReturnKey();
   yield dontShowArrayNumbers();
   yield testReturnWithNoSelection();
   yield popupHideAfterReturnWithNoSelection();
   yield testCompletionInText();
   yield popupHideAfterCompletionInText();
 
   HUD = popup = jsterm = inputNode = completeNode = null;
+  Services.prefs.setBoolPref(PREF_AUTO_MULTILINE, true);
 });
 
 var consoleOpened = Task.async(function*(aHud) {
   let deferred = promise.defer();
   HUD = aHud;
   info("web console opened");
 
   jsterm = HUD.jsterm;
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_multiline_input.js
@@ -0,0 +1,70 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+// Tests that the console waits for more input instead of evaluating
+// when valid, but incomplete, statements are present upon pressing enter
+// -or- when the user ends a line with shift + enter.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+                 "test/test-console.html";
+
+let SHOULD_ENTER_MULTILINE = [
+  {input: "function foo() {" },
+  {input: "var a = 1," },
+  {input: "var a = 1;", shiftKey: true },
+  {input: "function foo() { }", shiftKey: true },
+  {input: "function" },
+  {input: "(x) =>" },
+  {input: "let b = {" },
+  {input: "let a = [" },
+  {input: "{" },
+  {input: "{ bob: 3343," },
+  {input: "function x(y=" },
+  {input: "Array.from(" },
+  // shift + enter creates a new line despite parse errors
+  {input: "{2,}", shiftKey: true },
+];
+let SHOULD_EXECUTE = [
+  {input: "function foo() { }" },
+  {input: "var a = 1;" },
+  {input: "function foo() { var a = 1; }" },
+  {input: '"asdf"' },
+  {input: '99 + 3' },
+  {input: '1, 2, 3' },
+  // errors
+  {input: 'function f(x) { let y = 1, }' },
+  {input: 'function f(x=,) {' },
+  {input: '{2,}' },
+];
+
+add_task(function* () {
+  let { tab, browser } = yield loadTab(TEST_URI);
+  let hud = yield openConsole();
+  let inputNode = hud.jsterm.inputNode;
+
+  for (let test of SHOULD_ENTER_MULTILINE) {
+    hud.jsterm.setInputValue(test.input);
+    EventUtils.synthesizeKey("VK_RETURN", { shiftKey: test.shiftKey });
+    let inputValue = hud.jsterm.getInputValue();
+    is(inputNode.selectionStart, inputNode.selectionEnd,
+       "selection is collapsed");
+    is(inputNode.selectionStart, inputValue.length,
+       "caret at end of multiline input");
+    let inputWithNewline = test.input + "\n";
+    is(inputValue, inputWithNewline, "Input value is correct");
+  }
+
+  for (let test of SHOULD_EXECUTE) {
+    hud.jsterm.setInputValue(test.input);
+    EventUtils.synthesizeKey("VK_RETURN", { shiftKey: test.shiftKey });
+    let inputValue = hud.jsterm.getInputValue();
+    is(inputNode.selectionStart, 0, "selection starts/ends at 0");
+    is(inputNode.selectionEnd, 0, "selection starts/ends at 0");
+    is(inputValue, "", "Input value is cleared");
+  }
+
+});
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -6,16 +6,17 @@
 
 "use strict";
 
 const {Cc, Ci, Cu} = require("chrome");
 
 const {Utils: WebConsoleUtils, CONSOLE_WORKER_IDS} =
   require("devtools/shared/webconsole/utils");
 const promise = require("promise");
+const Debugger = require("Debugger");
 
 loader.lazyServiceGetter(this, "clipboardHelper",
                          "@mozilla.org/widget/clipboardhelper;1",
                          "nsIClipboardHelper");
 loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 loader.lazyRequireGetter(this, "AutocompletePopup", "devtools/client/shared/autocomplete-popup", true);
 loader.lazyRequireGetter(this, "ToolSidebar", "devtools/client/framework/sidebar", true);
@@ -193,16 +194,17 @@ const THROTTLE_UPDATES = 1000;
 const FILTER_PREFS_PREFIX = "devtools.webconsole.filter.";
 
 // The minimum font size.
 const MIN_FONT_SIZE = 10;
 
 const PREF_CONNECTION_TIMEOUT = "devtools.debugger.remote-timeout";
 const PREF_PERSISTLOG = "devtools.webconsole.persistlog";
 const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
+const PREF_AUTO_MULTILINE = "devtools.webconsole.autoMultiline";
 const PREF_INPUT_HISTORY_COUNT = "devtools.webconsole.inputHistoryCount";
 
 /**
  * A WebConsoleFrame instance is an interactive console initialized *per target*
  * that displays console log data as well as provides an interactive terminal to
  * manipulate the target's document content.
  *
  * The WebConsoleFrame is responsible for the actual Web Console UI
@@ -3875,21 +3877,23 @@ JSTerm.prototype = {
             inputNode.focus();
           }
           this.clearCompletion();
           break;
         default:
           break;
       }
       return;
-    } else if (event.shiftKey &&
-        event.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_RETURN) {
-      // shift return
-      // TODO: expand the inputNode height by one line
-      return;
+    } else if (event.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_RETURN) {
+      let autoMultiline = Services.prefs.getBoolPref(PREF_AUTO_MULTILINE);
+      if (event.shiftKey ||
+          (!Debugger.isCompilableUnit(inputNode.value) && autoMultiline)) {
+        // shift return or incomplete statement
+        return;
+      }
     }
 
     switch (event.keyCode) {
       case Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE:
         if (this.autocompletePopup.isOpen) {
           this.clearCompletion();
           event.preventDefault();
           event.stopPropagation();
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -2267,17 +2267,17 @@ nsDocument::ResetToURI(nsIURI *aURI, nsI
       if (NS_SUCCEEDED(rv)) {
         SetPrincipal(principal);
       }
     }
   }
 
   // Refresh the principal on the compartment.
   if (nsPIDOMWindowInner* win = GetInnerWindow()) {
-    win->RefreshCompartmentPrincipal();
+    nsGlobalWindow::Cast(win)->RefreshCompartmentPrincipal();
   }
 }
 
 void
 nsDocument::RemoveDocStyleSheetsFromStyleSets()
 {
   // The stylesheets should forget us
   for (CSSStyleSheet* sheet : Reversed(mStyleSheets)) {
@@ -13408,10 +13408,10 @@ nsIDocument::ReportHasScrollLinkedEffect
   if (mHasScrollLinkedEffect) {
     // We already did this once for this document, don't do it again.
     return;
   }
   mHasScrollLinkedEffect = true;
   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                   NS_LITERAL_CSTRING("Async Pan/Zoom"),
                                   this, nsContentUtils::eLAYOUT_PROPERTIES,
-                                  "ScrollLinkedEffectFound");
-}
+                                  "ScrollLinkedEffectFound2");
+}
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -475,17 +475,17 @@ public:
 
   virtual void MaybeUpdateTouchState() override;
 
   // Outer windows only.
   virtual bool DispatchCustomEvent(const nsAString& aEventName) override;
   bool DispatchResizeEvent(const mozilla::CSSIntSize& aSize);
 
   // Inner windows only.
-  virtual void RefreshCompartmentPrincipal() override;
+  void RefreshCompartmentPrincipal();
 
   // For accessing protected field mFullScreen
   friend class FullscreenTransitionTask;
 
   // Outer windows only.
   virtual nsresult SetFullscreenInternal(
     FullscreenReason aReason, bool aIsFullscreen,
     mozilla::gfx::VRDeviceProxy *aHMD = nullptr) override final;
@@ -1422,33 +1422,27 @@ private:
                         nsPIDOMWindowOuter **aReturn);
 
 public:
   // Timeout Functions
   // Language agnostic timeout function (all args passed).
   // |interval| is in milliseconds.
   nsresult SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler,
                                 int32_t interval,
-                                bool aIsInterval, int32_t* aReturn) override;
+                                bool aIsInterval, int32_t* aReturn);
   int32_t SetTimeoutOrInterval(JSContext* aCx,
                                mozilla::dom::Function& aFunction,
                                int32_t aTimeout,
                                const mozilla::dom::Sequence<JS::Value>& aArguments,
                                bool aIsInterval, mozilla::ErrorResult& aError);
   int32_t SetTimeoutOrInterval(JSContext* aCx, const nsAString& aHandler,
                                int32_t aTimeout, bool aIsInterval,
                                mozilla::ErrorResult& aError);
   void ClearTimeoutOrInterval(int32_t aTimerID,
-                                  mozilla::ErrorResult& aError);
-  nsresult ClearTimeoutOrInterval(int32_t aTimerID) override
-  {
-    mozilla::ErrorResult rv;
-    ClearTimeoutOrInterval(aTimerID, rv);
-    return rv.StealNSResult();
-  }
+                              mozilla::ErrorResult& aError);
 
   // JS specific timeout functions (JS args grabbed from context).
   nsresult ResetTimersForNonBackgroundWindow();
 
   // The timeout implementation functions.
   void RunTimeout(nsTimeout *aTimeout);
   void RunTimeout() { RunTimeout(nullptr); }
   // Return true if |aTimeout| was cleared while its handler ran.
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -146,42 +146,16 @@ public:
   mozilla::dom::EventTarget* GetParentTarget()
   {
     if (!mParentTarget) {
       UpdateParentTarget();
     }
     return mParentTarget;
   }
 
-  bool HasMutationListeners(uint32_t aMutationEventType) const
-  {
-    MOZ_ASSERT(IsInnerWindow());
-
-    if (!mOuterWindow) {
-      NS_ERROR("HasMutationListeners() called on orphan inner window!");
-
-      return false;
-    }
-
-    return (mMutationBits & aMutationEventType) != 0;
-  }
-
-  void SetMutationListeners(uint32_t aType)
-  {
-    MOZ_ASSERT(IsInnerWindow());
-
-    if (!mOuterWindow) {
-      NS_ERROR("HasMutationListeners() called on orphan inner window!");
-
-      return;
-    }
-
-    mMutationBits |= aType;
-  }
-
   virtual void MaybeUpdateTouchState() {}
 
   nsIDocument* GetExtantDoc() const
   {
     return mDoc;
   }
   nsIURI* GetDocumentURI() const;
   nsIURI* GetDocBaseURI() const;
@@ -237,24 +211,16 @@ public:
   virtual uint32_t TimeoutSuspendCount() = 0;
 
   // Fire any DOM notification events related to things that happened while
   // the window was frozen.
   virtual nsresult FireDelayedDOMEvents() = 0;
 
   virtual bool IsFrozen() const = 0;
 
-  // Add a timeout to this window.
-  virtual nsresult SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler,
-                                        int32_t interval,
-                                        bool aIsInterval, int32_t *aReturn) = 0;
-
-  // Clear a timeout from this window.
-  virtual nsresult ClearTimeoutOrInterval(int32_t aTimerID) = 0;
-
   nsPIDOMWindowOuter* GetOuterWindow()
   {
     return mIsInnerWindow ? mOuterWindow.get() : AsOuter();
   }
 
   bool IsInnerWindow() const
   {
     return mIsInnerWindow;
@@ -378,53 +344,16 @@ public:
    * finishes its change to or from fullscreen.
    *
    * @param aIsFullscreen indicates whether the widget is in fullscreen.
    *
    * Outer windows only.
    */
   virtual void FinishFullscreenChange(bool aIsFullscreen) = 0;
 
-  /**
-   * Call this to check whether some node (this window, its document,
-   * or content in that document) has a mouseenter/leave event listener.
-   */
-  bool HasMouseEnterLeaveEventListeners()
-  {
-    return mMayHaveMouseEnterLeaveEventListener;
-  }
-
-  /**
-   * Call this to indicate that some node (this window, its document,
-   * or content in that document) has a mouseenter/leave event listener.
-   */
-  void SetHasMouseEnterLeaveEventListeners()
-  {
-    mMayHaveMouseEnterLeaveEventListener = true;
-  }
-
-  /**
-   * Call this to check whether some node (this window, its document,
-   * or content in that document) has a Pointerenter/leave event listener.
-   */
-  bool HasPointerEnterLeaveEventListeners()
-  {
-    return mMayHavePointerEnterLeaveEventListener;
-  }
-
-  /**
-   * Call this to indicate that some node (this window, its document,
-   * or content in that document) has a Pointerenter/leave event listener.
-   */
-  void SetHasPointerEnterLeaveEventListeners()
-  {
-    mMayHavePointerEnterLeaveEventListener = true;
-  }
-
-
   virtual JSObject* GetCachedXBLPrototypeHandler(nsXBLPrototypeHandler* aKey) = 0;
   virtual void CacheXBLPrototypeHandler(nsXBLPrototypeHandler* aKey,
                                         JS::Handle<JSObject*> aHandler) = 0;
 
   /*
    * Get and set the currently focused element within the document. If
    * aNeedsFocus is true, then set mNeedsFocus to true to indicate that a
    * document focus event is needed.
@@ -574,24 +503,16 @@ public:
    * Dispatch a custom event with name aEventName targeted at this window.
    * Returns whether the default action should be performed.
    *
    * Outer windows only.
    */
   virtual bool DispatchCustomEvent(const nsAString& aEventName) = 0;
 
   /**
-   * Call when the document principal may have changed and the compartment
-   * principal needs to be updated.
-   *
-   * Inner windows only.
-   */
-  virtual void RefreshCompartmentPrincipal() = 0;
-
-  /**
    * Like nsIDOMWindow::Open, except that we don't navigate to the given URL.
    *
    * Outer windows only.
    */
   virtual nsresult
   OpenNoNavigate(const nsAString& aUrl, const nsAString& aName,
                  const nsAString& aOptions, nsPIDOMWindowOuter **_retval) = 0;
 
@@ -793,16 +714,75 @@ public:
   nsresult SetAudioCapture(bool aCapture);
 
   already_AddRefed<mozilla::dom::ServiceWorkerRegistrationMainThread>
     GetServiceWorkerRegistration(const nsAString& aScope);
   void InvalidateServiceWorkerRegistration(const nsAString& aScope);
 
   nsPerformance* GetPerformance();
 
+  bool HasMutationListeners(uint32_t aMutationEventType) const
+  {
+    if (!mOuterWindow) {
+      NS_ERROR("HasMutationListeners() called on orphan inner window!");
+
+      return false;
+    }
+
+    return (mMutationBits & aMutationEventType) != 0;
+  }
+
+  void SetMutationListeners(uint32_t aType)
+  {
+    if (!mOuterWindow) {
+      NS_ERROR("HasMutationListeners() called on orphan inner window!");
+
+      return;
+    }
+
+    mMutationBits |= aType;
+  }
+
+  /**
+   * Call this to check whether some node (this window, its document,
+   * or content in that document) has a mouseenter/leave event listener.
+   */
+  bool HasMouseEnterLeaveEventListeners()
+  {
+    return mMayHaveMouseEnterLeaveEventListener;
+  }
+
+  /**
+   * Call this to indicate that some node (this window, its document,
+   * or content in that document) has a mouseenter/leave event listener.
+   */
+  void SetHasMouseEnterLeaveEventListeners()
+  {
+    mMayHaveMouseEnterLeaveEventListener = true;
+  }
+
+  /**
+   * Call this to check whether some node (this window, its document,
+   * or content in that document) has a Pointerenter/leave event listener.
+   */
+  bool HasPointerEnterLeaveEventListeners()
+  {
+    return mMayHavePointerEnterLeaveEventListener;
+  }
+
+  /**
+   * Call this to indicate that some node (this window, its document,
+   * or content in that document) has a Pointerenter/leave event listener.
+   */
+  void SetHasPointerEnterLeaveEventListeners()
+  {
+    mMayHavePointerEnterLeaveEventListener = true;
+  }
+
+
 protected:
   void CreatePerformanceObjectIfNeeded();
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID)
 
 // NB: It's very very important that these two classes have identical vtables
 // and memory layout!
--- a/dom/base/nsXHTMLContentSerializer.cpp
+++ b/dom/base/nsXHTMLContentSerializer.cpp
@@ -506,17 +506,17 @@ nsXHTMLContentSerializer::CheckElementSt
   if (aContent->IsHTMLElement(nsGkAtoms::body)) {
     ++mInBody;
   }
 
   return true;
 }
 
 bool
-nsXHTMLContentSerializer::CheckElementEnd(Element* aElement,
+nsXHTMLContentSerializer::CheckElementEnd(mozilla::dom::Element* aElement,
                                           bool& aForceFormat,
                                           nsAString& aStr)
 {
   NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
 
   aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
                  aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
 
--- a/dom/bindings/GenerateCSS2PropertiesWebIDL.py
+++ b/dom/bindings/GenerateCSS2PropertiesWebIDL.py
@@ -1,51 +1,64 @@
 # 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/.
 
 import sys
 import string
 
+# Generates a line of WebIDL with the given spelling of the property name
+# (whether camelCase, _underscorePrefixed, etc.) and the given array of
+# extended attributes.
+def generateLine(propName, extendedAttrs):
+    return "  [%s] attribute DOMString %s;\n" % (", ".join(extendedAttrs),
+                                                 propName)
 propList = eval(sys.stdin.read())
 props = ""
 for [name, prop, id, flags, pref, proptype] in propList:
     if "CSS_PROPERTY_INTERNAL" in flags:
         continue
+    # Unfortunately, even some of the getters here are fallible
+    # (e.g. on nsComputedDOMStyle).
     extendedAttrs = ["Throws", "TreatNullAs=EmptyString"]
     if pref is not "":
         extendedAttrs.append('Pref="%s"' % pref)
+
+    # webkit properties get a capitalized "WebkitFoo" accessor (added here)
+    # as well as a camelcase "webkitFoo" accessor (added next).
+    if (prop.startswith("Webkit")):
+        props += generateLine(prop, extendedAttrs)
+
+    # Generate a line with camelCase spelling of property-name (or capitalized,
+    # for Moz-prefixed properties):
     if not prop.startswith("Moz"):
         prop = prop[0].lower() + prop[1:]
-    # Unfortunately, even some of the getters here are fallible
-    # (e.g. on nsComputedDOMStyle).
-    props += "  [%s] attribute DOMString %s;\n" % (", ".join(extendedAttrs),
-                                                   prop)
+    props += generateLine(prop, extendedAttrs)
+
     # Per spec, what's actually supposed to happen here is that we're supposed
     # to have properties for:
     #
     # 1) Each supported CSS property name, camelCased.
     # 2) Each supported name that contains dashes but doesn't start with a
     #    dash, without any changes to the name.
     # 3) cssFloat
     #
     # Note that "float" will cause a property called "float" to exist due to (1)
     # in that list.
     #
-    # In practice, cssFloat is the only case in which "name" doesn't contain "-"
-    # but also doesn't match "prop".  So the stuff we did with "prop" covers (3)
-    # and all of (1) except "float".   If we now output attributes for all the
-    # cases where "name" doesn't match "prop" and "name" doesn't start with "-",
-    # that will cover "float" and (2).
+    # In practice, cssFloat is the only case in which "name" doesn't contain
+    # "-" but also doesn't match "prop".  So the above generatePropLine() call
+    # covered (3) and all of (1) except "float".  If we now output attributes
+    # for all the cases where "name" doesn't match "prop" and "name" doesn't
+    # start with "-", that will cover "float" and (2).
     if prop != name and name[0] != "-":
         extendedAttrs.append('BinaryName="%s"' % prop)
         # Throw in a '_' before the attribute name, because some of these
         # property names collide with IDL reserved words.
-        props += "  [%s] attribute DOMString _%s;\n" % (
-            ", ".join(extendedAttrs),
-            name)
+        props += generateLine("_" + name, extendedAttrs)
+
 
 idlFile = open(sys.argv[1], "r")
 idlTemplate = idlFile.read()
 idlFile.close()
 
 print ("/* THIS IS AN AUTOGENERATED FILE.  DO NOT EDIT */\n\n" +
        string.Template(idlTemplate).substitute({"props": props}))
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -1299,30 +1299,29 @@ WebGLContext::MozGetUnderlyingParamStrin
 void
 WebGLContext::ClearScreen()
 {
     MakeContextCurrent();
     ScopedBindFramebuffer autoFB(gl, 0);
 
     const bool changeDrawBuffers = (mDefaultFB_DrawBuffer0 != LOCAL_GL_BACK);
     if (changeDrawBuffers) {
-        const GLenum back = LOCAL_GL_BACK;
-        gl->fDrawBuffers(1, &back);
+        gl->Screen()->SetDrawBuffer(LOCAL_GL_BACK);
     }
 
     GLbitfield bufferBits = LOCAL_GL_COLOR_BUFFER_BIT;
     if (mOptions.depth)
         bufferBits |= LOCAL_GL_DEPTH_BUFFER_BIT;
     if (mOptions.stencil)
         bufferBits |= LOCAL_GL_STENCIL_BUFFER_BIT;
 
     ForceClearFramebufferWithDefaultValues(bufferBits, mNeedsFakeNoAlpha);
 
     if (changeDrawBuffers) {
-        gl->fDrawBuffers(1, &mDefaultFB_DrawBuffer0);
+        gl->Screen()->SetDrawBuffer(mDefaultFB_DrawBuffer0);
     }
 }
 
 void
 WebGLContext::ForceClearFramebufferWithDefaultValues(GLbitfield clearBits,
                                                      bool fakeNoAlpha)
 {
     MakeContextCurrent();
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -822,17 +822,18 @@ protected:
     WebGLRefPtr<WebGLQuery> mActiveOcclusionQuery;
     WebGLRefPtr<WebGLQuery> mActiveTransformFeedbackQuery;
 
 // -----------------------------------------------------------------------------
 // State and State Requests (WebGLContextState.cpp)
 public:
     void Disable(GLenum cap);
     void Enable(GLenum cap);
-    bool GetStencilBits(GLint* out_stencilBits);
+    bool GetStencilBits(GLint* const out_stencilBits);
+    bool GetChannelBits(const char* funcName, GLenum pname, GLint* const out_val);
     virtual JS::Value GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv);
 
     void GetParameter(JSContext* cx, GLenum pname,
                       JS::MutableHandle<JS::Value> retval, ErrorResult& rv)
     {
         retval.set(GetParameter(cx, pname, rv));
     }
 
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -989,16 +989,20 @@ WebGLContext::Hint(GLenum target, GLenum
 {
     if (IsContextLost())
         return;
 
     bool isValid = false;
 
     switch (target) {
     case LOCAL_GL_GENERATE_MIPMAP_HINT:
+        // Deprecated and removed in desktop GL Core profiles.
+        if (gl->IsCoreProfile())
+            return;
+
         isValid = true;
         break;
 
     case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
         if (IsWebGL2() ||
             IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives))
         {
             isValid = true;
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -68,17 +68,17 @@ StringValue(JSContext* cx, const nsAStri
         rv.Throw(NS_ERROR_OUT_OF_MEMORY);
         return JS::NullValue();
     }
 
     return JS::StringValue(jsStr);
 }
 
 bool
-WebGLContext::GetStencilBits(GLint* out_stencilBits)
+WebGLContext::GetStencilBits(GLint* const out_stencilBits)
 {
     *out_stencilBits = 0;
     if (mBoundDrawFramebuffer) {
         if (mBoundDrawFramebuffer->StencilAttachment().IsDefined() &&
             mBoundDrawFramebuffer->DepthStencilAttachment().IsDefined())
         {
             // Error, we don't know which stencil buffer's bits to use
             ErrorInvalidFramebufferOperation("getParameter: framebuffer has two stencil buffers bound");
@@ -92,19 +92,117 @@ WebGLContext::GetStencilBits(GLint* out_
         }
     } else if (mOptions.stencil) {
         *out_stencilBits = 8;
     }
 
     return true;
 }
 
+bool
+WebGLContext::GetChannelBits(const char* funcName, GLenum pname, GLint* const out_val)
+{
+    if (mBoundDrawFramebuffer) {
+        if (!mBoundDrawFramebuffer->ValidateAndInitAttachments(funcName))
+            return false;
+    }
+
+    if (!mBoundDrawFramebuffer) {
+        switch (pname) {
+        case LOCAL_GL_RED_BITS:
+        case LOCAL_GL_GREEN_BITS:
+        case LOCAL_GL_BLUE_BITS:
+            *out_val = 8;
+            break;
+
+        case LOCAL_GL_ALPHA_BITS:
+            *out_val = (mOptions.alpha ? 8 : 0);
+            break;
+
+        case LOCAL_GL_DEPTH_BITS:
+            if (mOptions.depth) {
+                const auto& glFormats = gl->GetGLFormats();
+
+                GLenum depthFormat = glFormats.depth;
+                if (mOptions.stencil && glFormats.depthStencil) {
+                    depthFormat = glFormats.depthStencil;
+                }
+
+                if (depthFormat == LOCAL_GL_DEPTH_COMPONENT16) {
+                    *out_val = 16;
+                } else {
+                    *out_val = 24;
+                }
+            } else {
+                *out_val = 0;
+            }
+            break;
+
+        case LOCAL_GL_STENCIL_BITS:
+            *out_val = (mOptions.stencil ? 8 : 0);
+            break;
+
+        default:
+            MOZ_CRASH("bad pname");
+        }
+        return true;
+    }
+
+    if (!gl->IsCoreProfile()) {
+        gl->fGetIntegerv(pname, out_val);
+        return true;
+    }
+
+    GLenum fbAttachment = 0;
+    GLenum fbPName = 0;
+    switch (pname) {
+    case LOCAL_GL_RED_BITS:
+        fbAttachment = LOCAL_GL_COLOR_ATTACHMENT0;
+        fbPName = LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE;
+        break;
+
+    case LOCAL_GL_GREEN_BITS:
+        fbAttachment = LOCAL_GL_COLOR_ATTACHMENT0;
+        fbPName = LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE;
+        break;
+
+    case LOCAL_GL_BLUE_BITS:
+        fbAttachment = LOCAL_GL_COLOR_ATTACHMENT0;
+        fbPName = LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE;
+        break;
+
+    case LOCAL_GL_ALPHA_BITS:
+        fbAttachment = LOCAL_GL_COLOR_ATTACHMENT0;
+        fbPName = LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE;
+        break;
+
+    case LOCAL_GL_DEPTH_BITS:
+        fbAttachment = LOCAL_GL_DEPTH_ATTACHMENT;
+        fbPName = LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE;
+        break;
+
+    case LOCAL_GL_STENCIL_BITS:
+        fbAttachment = LOCAL_GL_STENCIL_ATTACHMENT;
+        fbPName = LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE;
+        break;
+
+    default:
+        MOZ_CRASH("bad pname");
+    }
+
+    gl->fGetFramebufferAttachmentParameteriv(LOCAL_GL_DRAW_FRAMEBUFFER, fbAttachment,
+                                             fbPName, out_val);
+    return true;
+}
+
 JS::Value
 WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
 {
+    const char funcName[] = "getParameter";
+
     if (IsContextLost())
         return JS::NullValue();
 
     MakeContextCurrent();
 
     if (MinCapabilityMode()) {
         switch(pname) {
             ////////////////////////////
@@ -351,52 +449,46 @@ WebGLContext::GetParameter(JSContext* cx
             // Assuming stencils have 8 bits
             const GLint stencilMask = (1 << stencilBits) - 1;
 
             GLint refValue = 0;
             gl->fGetIntegerv(pname, &refValue);
 
             return JS::Int32Value(refValue & stencilMask);
         }
-        case LOCAL_GL_STENCIL_BITS: {
-            GLint stencilBits = 0;
-            GetStencilBits(&stencilBits);
-            return JS::Int32Value(stencilBits);
-        }
+
         case LOCAL_GL_STENCIL_CLEAR_VALUE:
         case LOCAL_GL_UNPACK_ALIGNMENT:
         case LOCAL_GL_PACK_ALIGNMENT:
         case LOCAL_GL_SUBPIXEL_BITS:
         case LOCAL_GL_SAMPLE_BUFFERS:
         case LOCAL_GL_SAMPLES:
         case LOCAL_GL_MAX_VERTEX_ATTRIBS:
         case LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
         case LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
-        case LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS:
-        case LOCAL_GL_RED_BITS:
-        case LOCAL_GL_GREEN_BITS:
-        case LOCAL_GL_BLUE_BITS: {
+        case LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS: {
             GLint i = 0;
             gl->fGetIntegerv(pname, &i);
             return JS::Int32Value(i);
         }
-        case LOCAL_GL_DEPTH_BITS: {
-            GLint i = 0;
-            if (!mNeedsFakeNoDepth) {
-                gl->fGetIntegerv(pname, &i);
-            }
-            return JS::Int32Value(i);
+
+        case LOCAL_GL_RED_BITS:
+        case LOCAL_GL_GREEN_BITS:
+        case LOCAL_GL_BLUE_BITS:
+        case LOCAL_GL_ALPHA_BITS:
+        case LOCAL_GL_DEPTH_BITS:
+        case LOCAL_GL_STENCIL_BITS: {
+            // Deprecated and removed in GL Core profiles, so special handling required.
+            GLint val;
+            if (!GetChannelBits(funcName, pname, &val))
+                return JS::NullValue();
+
+            return JS::Int32Value(val);
         }
-        case LOCAL_GL_ALPHA_BITS: {
-            GLint i = 0;
-            if (!mNeedsFakeNoAlpha) {
-                gl->fGetIntegerv(pname, &i);
-            }
-            return JS::Int32Value(i);
-        }
+
         case LOCAL_GL_MAX_TEXTURE_SIZE:
             return JS::Int32Value(mImplMaxTextureSize);
 
         case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE:
             return JS::Int32Value(mImplMaxCubeMapTextureSize);
 
         case LOCAL_GL_MAX_RENDERBUFFER_SIZE:
             return JS::Int32Value(mImplMaxRenderbufferSize);
--- a/dom/canvas/WebGLExtensionTextureFloat.cpp
+++ b/dom/canvas/WebGLExtensionTextureFloat.cpp
@@ -27,89 +27,96 @@ WebGLExtensionTextureFloat::WebGLExtensi
     {
         auto usage = fua->EditUsage(effFormat);
         usage->textureSwizzleRGBA = swizzle;
         fua->AddTexUnpack(usage, pi, dui);
 
         fua->AllowUnsizedTexFormat(pi, usage);
     };
 
-    const bool needSizedInternal = !gl->IsGLES();
-    MOZ_ASSERT_IF(needSizedInternal, gl->IsSupported(gl::GLFeature::texture_swizzle));
+    const bool needsSwizzle = gl->IsCoreProfile();
+    MOZ_ASSERT_IF(needsSwizzle, gl->IsSupported(gl::GLFeature::texture_swizzle));
+
+    const bool needsSizedFormat = !gl->IsGLES();
 
     ////////////////
 
     pi = {LOCAL_GL_RGBA, LOCAL_GL_FLOAT};
     dui = {pi.format, pi.format, pi.type};
     swizzle = nullptr;
-    if (needSizedInternal) {
+    if (needsSizedFormat) {
         dui.internalFormat = LOCAL_GL_RGBA32F;
     }
     fnAdd(webgl::EffectiveFormat::RGBA32F);
 
     //////
 
     pi = {LOCAL_GL_RGB, LOCAL_GL_FLOAT};
     dui = {pi.format, pi.format, pi.type};
     swizzle = nullptr;
-    if (needSizedInternal) {
+    if (needsSizedFormat) {
         dui.internalFormat = LOCAL_GL_RGB32F;
     }
     fnAdd(webgl::EffectiveFormat::RGB32F);
 
     //////
 
     pi = {LOCAL_GL_LUMINANCE, LOCAL_GL_FLOAT};
     dui = {pi.format, pi.format, pi.type};
     swizzle = nullptr;
-    if (needSizedInternal) {
+    if (needsSwizzle) {
         dui = {LOCAL_GL_R32F, LOCAL_GL_RED, LOCAL_GL_FLOAT};
         swizzle = webgl::FormatUsageInfo::kLuminanceSwizzleRGBA;
+    } else if (needsSizedFormat) {
+        dui.internalFormat = LOCAL_GL_LUMINANCE32F_ARB;
     }
     fnAdd(webgl::EffectiveFormat::Luminance32F);
 
     //////
 
     pi = {LOCAL_GL_ALPHA, LOCAL_GL_FLOAT};
     dui = {pi.format, pi.format, pi.type};
     swizzle = nullptr;
-    if (needSizedInternal) {
+    if (needsSwizzle) {
         dui = {LOCAL_GL_R32F, LOCAL_GL_RED, LOCAL_GL_FLOAT};
         swizzle = webgl::FormatUsageInfo::kAlphaSwizzleRGBA;
+    } else if (needsSizedFormat) {
+        dui.internalFormat = LOCAL_GL_ALPHA32F_ARB;
     }
     fnAdd(webgl::EffectiveFormat::Alpha32F);
 
     //////
 
     pi = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_FLOAT};
     dui = {pi.format, pi.format, pi.type};
     swizzle = nullptr;
-    if (needSizedInternal) {
+    if (needsSwizzle) {
         dui = {LOCAL_GL_RG32F, LOCAL_GL_RG, LOCAL_GL_FLOAT};
         swizzle = webgl::FormatUsageInfo::kLumAlphaSwizzleRGBA;
+    } else if (needsSizedFormat) {
+        dui.internalFormat = LOCAL_GL_LUMINANCE_ALPHA32F_ARB;
     }
     fnAdd(webgl::EffectiveFormat::Luminance32FAlpha32F);
 }
 
 WebGLExtensionTextureFloat::~WebGLExtensionTextureFloat()
 {
 }
 
 bool
 WebGLExtensionTextureFloat::IsSupported(const WebGLContext* webgl)
 {
     gl::GLContext* gl = webgl->GL();
 
     if (!gl->IsSupported(gl::GLFeature::texture_float))
         return false;
 
-    const bool needSizedInternal = !gl->IsGLES();
+    const bool needsSwizzle = gl->IsCoreProfile();
     const bool hasSwizzle = gl->IsSupported(gl::GLFeature::texture_swizzle);
-
-    if (needSizedInternal && !hasSwizzle)
+    if (needsSwizzle && !hasSwizzle)
         return false;
 
     return true;
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionTextureFloat, OES_texture_float)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLExtensionTextureHalfFloat.cpp
+++ b/dom/canvas/WebGLExtensionTextureHalfFloat.cpp
@@ -25,75 +25,83 @@ WebGLExtensionTextureHalfFloat::WebGLExt
     {
         auto usage = fua->EditUsage(effFormat);
         usage->textureSwizzleRGBA = swizzle;
         fua->AddTexUnpack(usage, pi, dui);
 
         fua->AllowUnsizedTexFormat(pi, usage);
     };
 
-    const bool needSizedInternal = !gl->IsGLES();
-    MOZ_ASSERT_IF(needSizedInternal, gl->IsSupported(gl::GLFeature::texture_swizzle));
+    const bool needsSwizzle = gl->IsCoreProfile();
+    MOZ_ASSERT_IF(needsSwizzle, gl->IsSupported(gl::GLFeature::texture_swizzle));
+
+    const bool needsSizedFormat = !gl->IsGLES();
 
     GLenum driverUnpackType = LOCAL_GL_HALF_FLOAT;
     if (!gl->IsSupported(gl::GLFeature::texture_half_float)) {
         MOZ_ASSERT(gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float));
         driverUnpackType = LOCAL_GL_HALF_FLOAT_OES;
     }
 
     ////////////////
 
     pi = {LOCAL_GL_RGBA, LOCAL_GL_HALF_FLOAT_OES};
     dui = {pi.format, pi.format, driverUnpackType};
     swizzle = nullptr;
-    if (needSizedInternal) {
+    if (needsSizedFormat) {
         dui.internalFormat = LOCAL_GL_RGBA16F;
     }
     fnAdd(webgl::EffectiveFormat::RGBA16F);
 
     //////
 
     pi = {LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT_OES};
     dui = {pi.format, pi.format, driverUnpackType};
     swizzle = nullptr;
-    if (needSizedInternal) {
+    if (needsSizedFormat) {
         dui.internalFormat = LOCAL_GL_RGB16F;
     }
     fnAdd(webgl::EffectiveFormat::RGB16F);
 
     //////
 
     pi = {LOCAL_GL_LUMINANCE, LOCAL_GL_HALF_FLOAT_OES};
     dui = {pi.format, pi.format, driverUnpackType};
     swizzle = nullptr;
-    if (needSizedInternal) {
+    if (needsSwizzle) {
         dui = {LOCAL_GL_R16F, LOCAL_GL_RED, driverUnpackType};
         swizzle = webgl::FormatUsageInfo::kLuminanceSwizzleRGBA;
+    } else if (needsSizedFormat) {
+        dui.internalFormat = LOCAL_GL_LUMINANCE16F_ARB;
     }
     fnAdd(webgl::EffectiveFormat::Luminance16F);
 
     //////
 
     pi = {LOCAL_GL_ALPHA, LOCAL_GL_HALF_FLOAT_OES};
     dui = {pi.format, pi.format, driverUnpackType};
     swizzle = nullptr;
-    if (needSizedInternal) {
+    if (needsSwizzle) {
         dui = {LOCAL_GL_R16F, LOCAL_GL_RED, driverUnpackType};
         swizzle = webgl::FormatUsageInfo::kAlphaSwizzleRGBA;
+    } else if (needsSizedFormat) {
+        dui.internalFormat = LOCAL_GL_ALPHA16F_ARB;
     }
     fnAdd(webgl::EffectiveFormat::Alpha16F);
 
     //////
 
     pi = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_HALF_FLOAT_OES};
     dui = {pi.format, pi.format, driverUnpackType};
     swizzle = nullptr;
-    if (needSizedInternal) {
+    if (needsSwizzle) {
         dui = {LOCAL_GL_RG16F, LOCAL_GL_RG, driverUnpackType};
         swizzle = webgl::FormatUsageInfo::kLumAlphaSwizzleRGBA;
+    } else if (needsSizedFormat) {
+        dui.internalFormat = LOCAL_GL_LUMINANCE_ALPHA16F_ARB;
     }
     fnAdd(webgl::EffectiveFormat::Luminance16FAlpha16F);
 }
 
 WebGLExtensionTextureHalfFloat::~WebGLExtensionTextureHalfFloat()
 {
 }
 
@@ -103,20 +111,19 @@ WebGLExtensionTextureHalfFloat::IsSuppor
     gl::GLContext* gl = webgl->GL();
 
     if (!gl->IsSupported(gl::GLFeature::texture_half_float) &&
         !gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float))
     {
         return false;
     }
 
-    const bool needSizedInternal = !gl->IsGLES();
+    const bool needsSwizzle = gl->IsCoreProfile();
     const bool hasSwizzle = gl->IsSupported(gl::GLFeature::texture_swizzle);
-
-    if (needSizedInternal && !hasSwizzle)
+    if (needsSwizzle && !hasSwizzle)
         return false;
 
     return true;
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionTextureHalfFloat, OES_texture_half_float)
 
 } // namespace mozilla
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -2,17 +2,17 @@
 # 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/.
 
 TEST_DIRS += ['compiledtest']
 
 # Number changes to this file to avoid bug 1081323 (clobber after changing a manifest):
-# 3
+# 5
 
 MOCHITEST_MANIFESTS += [
     'test/crossorigin/mochitest.ini',
     'test/mochitest-subsuite-webgl.ini',
     'test/mochitest.ini',
 ]
 
 MOCHITEST_CHROME_MANIFESTS += ['test/chrome/chrome.ini']
--- a/dom/canvas/test/webgl-mochitest.ini
+++ b/dom/canvas/test/webgl-mochitest.ini
@@ -1,17 +1,58 @@
 [DEFAULT]
 subsuite = webgl
 skip-if = ((os == 'linux') && (buildapp == 'b2g'))
 
 support-files =
+  webgl-mochitest/ensure-exts/ensure-ext.js
   webgl-mochitest/driver-info.js
   webgl-mochitest/es3-data.js
   webgl-mochitest/webgl-util.js
 
+[webgl-mochitest/ensure-exts/test_ANGLE_instanced_arrays.html]
+fail-if = (os == 'android') || (os == 'mac' && os_version == '10.6')
+[webgl-mochitest/ensure-exts/test_EXT_blend_minmax.html]
+fail-if = (os == 'android')
+[webgl-mochitest/ensure-exts/test_EXT_color_buffer_half_float.html]
+fail-if = (os == 'android')
+[webgl-mochitest/ensure-exts/test_EXT_disjoint_timer_query.html]
+fail-if = (os == 'android') || (os == 'linux') || (os == 'mac') || (os == 'win')
+[webgl-mochitest/ensure-exts/test_EXT_frag_depth.html]
+fail-if = (os == 'android')
+[webgl-mochitest/ensure-exts/test_EXT_sRGB.html]
+fail-if = (os == 'android') || (os == 'linux') || (os == 'mac' && os_version == '10.6') || (os == 'win')
+[webgl-mochitest/ensure-exts/test_EXT_shader_texture_lod.html]
+fail-if = (os == 'android') || (os == 'linux') || (os == 'mac')
+[webgl-mochitest/ensure-exts/test_EXT_texture_filter_anisotropic.html]
+fail-if = (os == 'android') || (os == 'linux')
+[webgl-mochitest/ensure-exts/test_OES_standard_derivatives.html]
+fail-if = (os == 'android')
+[webgl-mochitest/ensure-exts/test_WEBGL_color_buffer_float.html]
+fail-if = (os == 'android')
+[webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_atc.html]
+fail-if = (os == 'android') || (os == 'linux') || (os == 'mac') || (os == 'win')
+[webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_es3.html]
+fail-if = (os == 'android') || (os == 'linux') || (os == 'mac') || (os == 'win')
+[webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_etc1.html]
+# Win7 is 6.1
+fail-if = (os == 'linux') || (os == 'mac') || (os == 'win' && (os_version == '5.1' || os_version == '6.1'))
+[webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_pvrtc.html]
+fail-if = (os == 'android') || (os == 'linux') || (os == 'mac') || (os == 'win')
+[webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_s3tc.html]
+fail-if = (os == 'android') || (os == 'linux')
+[webgl-mochitest/ensure-exts/test_WEBGL_depth_texture.html]
+fail-if = (os == 'mac' && os_version == '10.6')
+[webgl-mochitest/ensure-exts/test_WEBGL_draw_buffers.html]
+# Win7 is 6.1
+fail-if = (os == 'android') || (os == 'win' && (os_version == '5.1' || os_version == '6.1'))
+
+[webgl-mochitest/ensure-exts/test_common.html]
+
+
 [webgl-mochitest/test_backbuffer_channels.html]
 fail-if = (os == 'b2g')
 [webgl-mochitest/test_depth_readpixels.html]
 [webgl-mochitest/test_capture.html]
 support-files = captureStream_common.js
 [webgl-mochitest/test_cubemap_must_be_square.html]
 [webgl-mochitest/test_depth_tex_lazy_clear.html]
 [webgl-mochitest/test_draw.html]
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/ensure-ext.js
@@ -0,0 +1,32 @@
+'use strict';
+
+function EnsureExt(name, shouldBe = true) {
+    var c = document.createElement('canvas');
+    var gl = c.getContext('experimental-webgl');
+
+    if (shouldBe) {
+        ok(gl.getExtension(name), 'Should have extension ' + name + '.');
+    } else {
+        ok(!gl.getExtension(name), 'Should not have extension ' + name + '.');
+    }
+}
+
+function EnsureDraftExt(name, shouldBe = true) {
+    SimpleTest.waitForExplicitFinish();
+
+    var fnEnsure = function() {
+        EnsureExt(name, shouldBe);
+        SimpleTest.finish();
+    };
+
+    if ('SpecialPowers' in window) {
+        var prefStateList = [
+            ['webgl.enable-draft-extensions', true],
+        ];
+        var prefEnv = {'set': prefStateList};
+        SpecialPowers.pushPrefEnv(prefEnv, fnEnsure);
+    } else {
+        console.log('Couldn\'t use SpecialPowers to enable draft extensions.');
+        fnEnsure();
+    }
+}
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_ANGLE_instanced_arrays.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <script src='/tests/SimpleTest/SimpleTest.js'></script>
+    <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+    <script src='ensure-ext.js'></script>
+  </head>
+  <body>
+    <script>
+
+'use strict';
+EnsureExt('ANGLE_instanced_arrays');
+
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_blend_minmax.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <script src='/tests/SimpleTest/SimpleTest.js'></script>
+    <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+    <script src='ensure-ext.js'></script>
+  </head>
+  <body>
+    <script>
+
+'use strict';
+EnsureExt('EXT_blend_minmax');
+
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_color_buffer_half_float.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <script src='/tests/SimpleTest/SimpleTest.js'></script>
+    <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+    <script src='ensure-ext.js'></script>
+  </head>
+  <body>
+    <script>
+
+'use strict';
+EnsureExt('EXT_color_buffer_half_float');
+
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_disjoint_timer_query.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <script src='/tests/SimpleTest/SimpleTest.js'></script>
+    <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+    <script src='ensure-ext.js'></script>
+  </head>
+  <body>
+    <script>
+
+'use strict';
+EnsureDraftExt('EXT_disjoint_timer_query');
+
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_frag_depth.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <script src='/tests/SimpleTest/SimpleTest.js'></script>
+    <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+    <script src='ensure-ext.js'></script>
+  </head>
+  <body>
+    <script>
+
+'use strict';
+EnsureExt('EXT_frag_depth');
+
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_sRGB.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <script src='/tests/SimpleTest/SimpleTest.js'></script>
+    <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+    <script src='ensure-ext.js'></script>
+  </head>
+  <body>
+    <script>
+
+'use strict';
+EnsureExt('EXT_sRGB');
+
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_shader_texture_lod.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <script src='/tests/SimpleTest/SimpleTest.js'></script>
+    <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+    <script src='ensure-ext.js'></script>
+  </head>
+  <body>
+    <script>
+
+'use strict';
+EnsureExt('EXT_shader_texture_lod');
+
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_texture_filter_anisotropic.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <script src='/tests/SimpleTest/SimpleTest.js'></script>
+    <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+    <script src='ensure-ext.js'></script>
+  </head>
+  <body>
+    <script>
+
+'use strict';
+EnsureExt('EXT_texture_filter_anisotropic');
+
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_OES_standard_derivatives.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <script src='/tests/SimpleTest/SimpleTest.js'></script>
+    <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+    <script src='ensure-ext.js'></script>
+  </head>
+  <body>
+    <script>
+
+'use strict';
+EnsureExt('OES_standard_derivatives');
+
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_color_buffer_float.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <script src='/tests/SimpleTest/SimpleTest.js'></script>
+    <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+    <script src='ensure-ext.js'></script>
+  </head>
+  <body>
+    <script>
+
+'use strict';
+EnsureExt('WEBGL_color_buffer_float');
+
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_atc.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <script src='/tests/SimpleTest/SimpleTest.js'></script>
+    <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+    <script src='ensure-ext.js'></script>
+  </head>
+  <body>
+    <script>
+
+'use strict';
+EnsureExt('WEBGL_compressed_texture_atc');
+
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_es3.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <script src='/tests/SimpleTest/SimpleTest.js'></script>
+    <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+    <script src='ensure-ext.js'></script>
+  </head>
+  <body>
+    <script>
+
+'use strict';
+EnsureDraftExt('WEBGL_compressed_texture_es3');
+
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_etc1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <script src='/tests/SimpleTest/SimpleTest.js'></script>
+    <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+    <script src='ensure-ext.js'></script>
+  </head>
+  <body>
+    <script>
+
+'use strict';
+EnsureExt('WEBGL_compressed_texture_etc1');
+
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_pvrtc.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <script src='/tests/SimpleTest/SimpleTest.js'></script>
+    <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+    <script src='ensure-ext.js'></script>
+  </head>
+  <body>
+    <script>
+
+'use strict';
+EnsureExt('WEBGL_compressed_texture_pvrtc');
+
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_s3tc.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <script src='/tests/SimpleTest/SimpleTest.js'></script>
+    <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+    <script src='ensure-ext.js'></script>
+  </head>
+  <body>
+    <script>
+
+'use strict';
+EnsureExt('WEBGL_compressed_texture_s3tc');
+
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_depth_texture.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <script src='/tests/SimpleTest/SimpleTest.js'></script>
+    <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+    <script src='ensure-ext.js'></script>
+  </head>
+  <body>
+    <script>
+
+'use strict';
+EnsureExt('WEBGL_depth_texture');
+
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_draw_buffers.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <script src='/tests/SimpleTest/SimpleTest.js'></script>
+    <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+    <script src='ensure-ext.js'></script>
+  </head>
+  <body>
+    <script>
+
+'use strict';
+EnsureExt('WEBGL_draw_buffers');
+
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_common.html
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <script src='/tests/SimpleTest/SimpleTest.js'></script>
+    <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+  </head>
+  <body>
+    <script>
+
+'use strict';
+
+var c = document.createElement('canvas');
+var gl = c.getContext('experimental-webgl');
+
+function ensureExt(name) {
+  ok(gl.getExtension(name), 'Should have extension ' + name + '.');
+}
+
+function ensureNoExt(name) {
+  ok(!gl.getExtension(name), 'Should not have extension ' + name + '.');
+}
+
+do {
+  if (!gl)
+    break;
+
+  // These aren't all guaranteed, but they're common to all our test slaves.
+  // If you're adding a slave config that is missing one of these, comment the line out
+  // and split it into its own test.
+
+  // Implemented. (commented out if not test-slave-universal)
+  //ensureExt('ANGLE_instanced_arrays');
+  //ensureExt('EXT_blend_minmax');
+  //ensureExt('EXT_color_buffer_half_float');
+  //ensureExt('EXT_frag_depth');
+  //ensureExt('EXT_shader_texture_lod');
+  //ensureExt('EXT_sRGB');
+  //ensureExt('EXT_texture_filter_anisotropic');
+  ensureExt('OES_element_index_uint');
+  //ensureExt('OES_standard_derivatives');
+  ensureExt('OES_texture_float');
+  ensureExt('OES_texture_float_linear');
+  ensureExt('OES_texture_half_float');
+  ensureExt('OES_texture_half_float_linear');
+  ensureExt('OES_vertex_array_object');
+  //ensureExt('WEBGL_color_buffer_float');
+  //ensureExt('WEBGL_compressed_texture_atc');
+  //ensureExt('WEBGL_compressed_texture_es3');
+  //ensureExt('WEBGL_compressed_texture_etc1');
+  //ensureExt('WEBGL_compressed_texture_pvrtc');
+  //ensureExt('WEBGL_compressed_texture_s3tc');
+  //ensureExt('WEBGL_depth_texture');
+  //ensureExt('WEBGL_draw_buffers');
+  ensureExt('WEBGL_lose_context');
+
+  // Draft extensions, which should not be exposed by default.
+  ensureNoExt('EXT_disjoint_timer_query');
+  ensureNoExt('WEBGL_compressed_texture_es3');
+
+  // Not implemented.
+  ensureNoExt('EXT_color_buffer_float');
+  ensureNoExt('OES_fbo_render_mipmap');
+  ensureNoExt('WEBGL_compressed_texture_astc');
+  ensureNoExt('WEBGL_security_sensitive_resources');
+  ensureNoExt('WEBGL_shared_resources');
+
+  // Privileged
+  //ensureExt('WEBGL_debug_renderer_info');
+  //ensureExt('WEBGL_debug_shaders');
+} while (false);
+
+    </script>
+  </body>
+</html>
--- a/dom/canvas/test/webgl-mochitest/mochi-to-testcase.py
+++ b/dom/canvas/test/webgl-mochitest/mochi-to-testcase.py
@@ -23,17 +23,18 @@ def ReadLocalFile(include):
 
     try:
         f.close()
     except:
         pass
 
     return data
 
-kSimpleTestReplacement = '''\n
+kSimpleTestReplacement = '''
+
 <script>
 // SimpleTest.js replacement
 
 function debug(text) {
   var elem = document.getElementById('mochi-to-testcase-output');
   elem.innerHTML += '\\n<br/>\\n' + text;
 }
 
@@ -47,20 +48,22 @@ function todo(val, text) {
   var status = val ? 'Test <font color=\\'orange\\'>UNEXPECTED PASS</font>: '
                    : 'Test <font color=\\'blue\\'  >todo</font>: ';
   debug(status + text);
 }
 
 SimpleTest = {
   waitForExplicitFinish: function() {},
   finish: function() {},
+  requestFlakyTimeout: function() {},
 };
 </script>
 <div id='mochi-to-testcase-output'></div>
-\n'''
+
+'''
 
 fin = open(mochiPath, 'rb')
 fout = open(testPath, 'wb')
 includePattern = re.compile('<script\\s*src=[\'"](.*)\\.js[\'"]>\\s*</script>')
 cssPattern = re.compile('<link\\s*rel=[\'"]stylesheet[\'"]\\s*href=[\'"]([^=>]*)[\'"]>')
 for line in fin:
     skipLine = False
     for css in cssPattern.findall(line):
--- a/dom/devicestorage/test/mochitest.ini
+++ b/dom/devicestorage/test/mochitest.ini
@@ -5,17 +5,16 @@ support-files = devicestorage_common.js
 
 [test_823965.html]
 # [test_add.html]
 # man, our mime database sucks hard.  followup bug # 788273
 [test_addCorrectType.html]
 [test_available.html]
 [test_basic.html]
 [test_dirs.html]
-skip-if = e10s # Bug 1063569.
 # [test_diskSpace.html]
 # Possible race between the time we write a file, and the
 # time it takes to be reflected by statfs().  Bug # 791287
 [test_dotdot.html]
 [test_enumerate.html]
 [test_enumerateMultipleContinue.html]
 [test_enumerateOptions.html]
 [test_freeSpace.html]
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -1012,17 +1012,17 @@ HTMLFormElement::NotifySubmitObservers(n
     bool loop = true;
     while (NS_SUCCEEDED(theEnum->HasMoreElements(&loop)) && loop) {
       theEnum->GetNext(getter_AddRefs(inst));
 
       nsCOMPtr<nsIFormSubmitObserver> formSubmitObserver(
                       do_QueryInterface(inst));
       if (formSubmitObserver) {
         rv = formSubmitObserver->Notify(this,
-                                        window->GetCurrentInnerWindow(),
+                                        window ? window->GetCurrentInnerWindow() : nullptr,
                                         aActionURL,
                                         aCancelSubmit);
         NS_ENSURE_SUCCESS(rv, rv);
       }
       if (*aCancelSubmit) {
         return NS_OK;
       }
     }
--- a/dom/locales/en-US/chrome/layout/layout_errors.properties
+++ b/dom/locales/en-US/chrome/layout/layout_errors.properties
@@ -4,9 +4,9 @@
 
 ImageMapRectBoundsError=The "coords" attribute of the <area shape="rect"> tag is not in the "left,top,right,bottom" format.
 ImageMapCircleWrongNumberOfCoords=The "coords" attribute of the <area shape="circle"> tag is not in the "center-x,center-y,radius" format.
 ImageMapCircleNegativeRadius=The "coords" attribute of the <area shape="circle"> tag has a negative radius.
 ImageMapPolyWrongNumberOfCoords=The "coords" attribute of the <area shape="poly"> tag is not in the "x1,y1,x2,y2 …" format.
 ImageMapPolyOddNumberOfCoords=The "coords" attribute of the <area shape="poly"> tag is missing the last "y" coordinate (the correct format is "x1,y1,x2,y2 …").
 
 TablePartRelPosWarning=Relative positioning of table rows and row groups is now supported. This site may need to be updated because it may depend on this feature having no effect.
-ScrollLinkedEffectFound=This site appears to use a scroll-linked positioning effect. This may not work well with asynchronous panning; see https://developers.mozilla.org/docs/Mozilla/Performance/ScrollLinkedEffects for further details and to join the discussion on related tools and features!
+ScrollLinkedEffectFound2=This site appears to use a scroll-linked positioning effect. This may not work well with asynchronous panning; see https://developer.mozilla.org/docs/Mozilla/Performance/ScrollLinkedEffects for further details and to join the discussion on related tools and features!
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -931,24 +931,26 @@ TrackBuffersManager::OnDemuxerInitDone(n
   MediaInfo info;
 
   uint32_t numVideos = mInputDemuxer->GetNumberTracks(TrackInfo::kVideoTrack);
   if (numVideos) {
     // We currently only handle the first video track.
     mVideoTracks.mDemuxer = mInputDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
     MOZ_ASSERT(mVideoTracks.mDemuxer);
     info.mVideo = *mVideoTracks.mDemuxer->GetInfo()->GetAsVideoInfo();
+    info.mVideo.mTrackId = 2;
   }
 
   uint32_t numAudios = mInputDemuxer->GetNumberTracks(TrackInfo::kAudioTrack);
   if (numAudios) {
     // We currently only handle the first audio track.
     mAudioTracks.mDemuxer = mInputDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
     MOZ_ASSERT(mAudioTracks.mDemuxer);
     info.mAudio = *mAudioTracks.mDemuxer->GetInfo()->GetAsAudioInfo();
+    info.mAudio.mTrackId = 1;
   }
 
   int64_t videoDuration = numVideos ? info.mVideo.mDuration : 0;
   int64_t audioDuration = numAudios ? info.mAudio.mDuration : 0;
 
   int64_t duration = std::max(videoDuration, audioDuration);
   // 1. Update the duration attribute if it currently equals NaN.
   // Those steps are performed by the MediaSourceDecoder::SetInitialDuration
--- a/dom/permission/tests/test_permissions_api.html
+++ b/dom/permission/tests/test_permissions_api.html
@@ -26,24 +26,16 @@ const PERMISSIONS = [
   { name: 'push', perm: 'desktop-notification' },
 ];
 
 const UNSUPPORTED_PERMISSIONS = [
   'foobarbaz',  // Not in spec, for testing only.
   'midi'
 ];
 
-function setup() {
-  return new Promise((resolve, reject) => {
-    SpecialPowers.pushPrefEnv({'set': [
-      ['dom.permissions.enabled', true],
-    ]}, resolve);
-  });
-}
-
 function setPermissions(action) {
   let permissions = PERMISSIONS.map(x => {
     return { 'type': x.perm, 'allow': action, 'context': document };
   });
   return new Promise((resolve, reject) => {
     SpecialPowers.popPermissions(() => {
       SpecialPowers.pushPermissions(permissions, resolve);
     });
@@ -106,18 +98,17 @@ function testStatusOnChange() {
 
 function testInvalidQuery() {
   navigator.permissions.query({ name: 'invalid' }).then(
     result => ok(false, 'invalid query should not have resolved'),
     error => ok(true, 'invalid query should have rejected'));
 }
 
 function runTests() {
-  setup()
-    .then(checkUnsupportedPermissions)
+  checkUnsupportedPermissions()
     .then(checkUserVisiblePushPermission)
     .then(() => setPermissions(UNKNOWN_ACTION))
     .then(() => checkPermissions('prompt'))
     .then(() => setPermissions(PROMPT_ACTION))
     .then(() => checkPermissions('prompt'))
     .then(() => setPermissions(ALLOW_ACTION))
     .then(() => checkPermissions('granted'))
     .then(() => setPermissions(DENY_ACTION))
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -1845,17 +1845,18 @@ nsPluginHost::SiteHasData(nsIPluginTag* 
 nsPluginHost::SpecialType
 nsPluginHost::GetSpecialType(const nsACString & aMIMEType)
 {
   if (aMIMEType.LowerCaseEqualsASCII("application/x-test")) {
     return eSpecialType_Test;
   }
 
   if (aMIMEType.LowerCaseEqualsASCII("application/x-shockwave-flash") ||
-      aMIMEType.LowerCaseEqualsASCII("application/futuresplash")) {
+      aMIMEType.LowerCaseEqualsASCII("application/futuresplash") ||
+      aMIMEType.LowerCaseEqualsASCII("application/x-shockwave-flash-test")) {
     return eSpecialType_Flash;
   }
 
   if (aMIMEType.LowerCaseEqualsASCII("application/x-silverlight") ||
       aMIMEType.LowerCaseEqualsASCII("application/x-silverlight-2")) {
     return eSpecialType_Silverlight;
   }
 
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -668,16 +668,17 @@ PluginModuleParent::PluginModuleParent(b
     , mShutdown(false)
     , mHadLocalInstance(false)
     , mClearSiteDataSupported(false)
     , mGetSitesWithDataSupported(false)
     , mNPNIface(nullptr)
     , mNPPIface(nullptr)
     , mPlugin(nullptr)
     , mTaskFactory(this)
+    , mSandboxLevel(0)
     , mIsFlashPlugin(false)
     , mIsStartingAsync(false)
     , mNPInitialized(false)
     , mIsNPShutdownPending(false)
     , mAsyncNewRv(NS_ERROR_NOT_INITIALIZED)
 {
 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
     mIsStartingAsync = aAllowAsyncInit &&
@@ -731,17 +732,16 @@ PluginModuleChromeParent::PluginModuleCh
     , mChromeTaskFactory(this)
     , mHangAnnotationFlags(0)
     , mHangAnnotatorMutex("PluginModuleChromeParent::mHangAnnotatorMutex")
 #ifdef XP_WIN
     , mPluginCpuUsageOnHang()
     , mHangUIParent(nullptr)
     , mHangUIEnabled(true)
     , mIsTimerReset(true)
-    , mSandboxLevel(aSandboxLevel)
 #ifdef MOZ_CRASHREPORTER
     , mCrashReporterMutex("PluginModuleChromeParent::mCrashReporterMutex")
     , mCrashReporter(nullptr)
 #endif
 #endif
 #ifdef MOZ_CRASHREPORTER_INJECTOR
     , mFlashProcess1(0)
     , mFlashProcess2(0)
@@ -749,16 +749,17 @@ PluginModuleChromeParent::PluginModuleCh
 #endif
     , mInitOnAsyncConnect(false)
     , mAsyncInitRv(NS_ERROR_NOT_INITIALIZED)
     , mAsyncInitError(NPERR_NO_ERROR)
     , mContentParent(nullptr)
 {
     NS_ASSERTION(mSubprocess, "Out of memory!");
     sInstantiated = true;
+    mSandboxLevel = aSandboxLevel;
     mRunID = GeckoChildProcessHost::GetUniqueID();
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
     InitPluginProfiling();
 #endif
 
     mozilla::HangMonitor::RegisterAnnotator(*this);
 }
@@ -2664,16 +2665,33 @@ PluginModuleParent::NPP_NewInternal(NPMI
     }
 
     nsDependentCString strPluginType(pluginType);
     PluginInstanceParent* parentInstance =
         new PluginInstanceParent(this, instance, strPluginType, mNPNIface);
 
     if (mIsFlashPlugin) {
         parentInstance->InitMetadata(strPluginType, srcAttribute);
+#ifdef XP_WIN
+        // Force windowless mode (bug 1201904) when sandbox level >= 2
+        if (mSandboxLevel >= 2) {
+           NS_NAMED_LITERAL_CSTRING(wmodeAttributeName, "wmode");
+           NS_NAMED_LITERAL_CSTRING(opaqueAttributeValue, "opaque");
+           auto wmodeAttributeIndex =
+               names.IndexOf(wmodeAttributeName, 0, comparator);
+           if (wmodeAttributeIndex != names.NoIndex) {
+               if (!values[wmodeAttributeIndex].EqualsLiteral("transparent")) {
+                   values[wmodeAttributeIndex].Assign(opaqueAttributeValue);
+               }
+           } else {
+               names.AppendElement(wmodeAttributeName);
+               values.AppendElement(opaqueAttributeValue);
+           }
+        }
+#endif
     }
 
     // Release the surrogate reference that was in pdata
     RefPtr<PluginAsyncSurrogate> surrogate(
         dont_AddRef(PluginAsyncSurrogate::Cast(instance)));
     // Now replace it with the instance
     instance->pdata = static_cast<PluginDataResolver*>(parentInstance);
 
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -320,16 +320,17 @@ protected:
     TaskFactory<PluginModuleParent> mTaskFactory;
     nsString mPluginDumpID;
     nsString mBrowserDumpID;
     nsString mHangID;
     RefPtr<nsIObserver> mProfilerObserver;
     TimeDuration mTimeBlocked;
     nsCString mPluginName;
     nsCString mPluginVersion;
+    int32_t mSandboxLevel;
     bool mIsFlashPlugin;
 
 #ifdef MOZ_X11
     // Dup of plugin's X socket, used to scope its resources to this
     // object instead of the plugin process's lifetime
     ScopedClose mPluginXSocketFdDup;
 #endif
 
@@ -543,17 +544,16 @@ private:
     Atomic<uint32_t> mHangAnnotationFlags;
     mozilla::Mutex mHangAnnotatorMutex;
     InfallibleTArray<mozilla::ipc::IProtocol*> mProtocolCallStack;
 #ifdef XP_WIN
     InfallibleTArray<float> mPluginCpuUsageOnHang;
     PluginHangUIParent *mHangUIParent;
     bool mHangUIEnabled;
     bool mIsTimerReset;
-    int32_t mSandboxLevel;
 #ifdef MOZ_CRASHREPORTER
     /**
      * This mutex protects the crash reporter when the Plugin Hang UI event
      * handler is executing off main thread. It is intended to protect both
      * the mCrashReporter variable in addition to the CrashReporterParent object
      * that mCrashReporter refers to.
      */
     mozilla::Mutex mCrashReporterMutex;
--- a/dom/plugins/test/mochitest/mochitest.ini
+++ b/dom/plugins/test/mochitest/mochitest.ini
@@ -115,15 +115,17 @@ skip-if = toolkit != "cocoa"
 [test_secondPlugin.html]
 [test_src_url_change.html]
 [test_streamNotify.html]
 [test_stringHandling.html]
 [test_streamatclose.html]
 [test_twostreams.html]
 [test_windowed_invalidate.html]
 skip-if = os != "win"
+[test_windowless_flash.html]
+skip-if = !(os == "win" && processor == "x86_64")
 [test_windowless_ime.html]
 skip-if = os != "win" || e10s
 [test_visibility.html]
 skip-if = toolkit == "cocoa"
 [test_zero_opacity.html]
 [test_bug1165981.html]
 skip-if = !(os == "win" && processor == "x86_64")
new file mode 100644
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_windowless_flash.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="plugin-utils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="runTests()">
+  <script type="application/javascript" src="plugin-utils.js"></script>
+  <script type="text/javascript">
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+  function runTests() {
+    var p1 = document.getElementById('plugin1');
+    var p2 = document.getElementById('plugin2');
+    var p3 = document.getElementById('plugin3');
+    is(p1.hasWidget(), false, "Flash is always windowless mode even if wmode=window");
+    is(p2.hasWidget(), false, "Flash is always windowless mode even if wmode=anything");
+    is(p3.hasWidget(), false, "Flash is always windowless mode even if no wmode");
+    SimpleTest.finish();
+  }
+
+  </script>
+  <p id="display"></p>
+
+  <div id="div1">
+    <embed id="plugin1" type="application/x-shockwave-flash-test" width="200" height="200" wmode="window"></embed>
+    <embed id="plugin2" type="application/x-shockwave-flash-test" width="200" height="200" wmode="test"></embed>
+    <embed id="plugin3" type="application/x-shockwave-flash-test" width="200" height="200"></embed>
+  </div>
+</body>
+</html>
--- a/dom/storage/DOMStorageCache.cpp
+++ b/dom/storage/DOMStorageCache.cpp
@@ -559,19 +559,26 @@ DOMStorageCache::Clear(const DOMStorage*
   }
 
   return hadData ? NS_OK : NS_SUCCESS_DOM_NO_OPERATION;
 }
 
 void
 DOMStorageCache::CloneFrom(const DOMStorageCache* aThat)
 {
-  mLoaded = aThat->mLoaded;
+  // This will never be called on anything else than SessionStorage.
+  // This means mData will never be touched on any other thread than
+  // the main thread and it never went through the loading process.
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mPersistent);
+  MOZ_ASSERT(!(bool)aThat->mLoaded);
+
+  mLoaded = false;
   mInitialized = aThat->mInitialized;
-  mPersistent = aThat->mPersistent;
+  mPersistent = false;
   mSessionOnlyDataSetActive = aThat->mSessionOnlyDataSetActive;
 
   for (uint32_t i = 0; i < kDataSetCount; ++i) {
     for (auto it = aThat->mData[i].mKeys.ConstIter(); !it.Done(); it.Next()) {
       mData[i].mKeys.Put(it.Key(), it.UserData());
     }
     ProcessUsageDelta(i, aThat->mData[i].mOriginQuotaUsage);
   }
--- a/dom/storage/DOMStorageCache.h
+++ b/dom/storage/DOMStorageCache.h
@@ -10,16 +10,17 @@
 #include "nsIPrincipal.h"
 #include "nsITimer.h"
 
 #include "nsString.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Telemetry.h"
+#include "mozilla/Atomics.h"
 #include "nsAutoPtr.h"
 
 namespace mozilla {
 namespace dom {
 
 class DOMStorage;
 class DOMStorageUsage;
 class DOMStorageManager;
@@ -215,18 +216,19 @@ private:
   Data mData[kDataSetCount];
 
   // This monitor is used to wait for full load of data.
   mozilla::Monitor mMonitor;
 
   // Flag that is initially false.  When the cache is about to work with
   // the database (i.e. it is persistent) this flags is set to true after
   // all keys and coresponding values are loaded from the database.
-  // This flag never goes from true back to false.
-  bool mLoaded;
+  // This flag never goes from true back to false.  Since this flag is
+  // critical for mData hashtable synchronization, it's made atomic.
+  Atomic<bool, ReleaseAcquire> mLoaded;
 
   // Result of load from the database.  Valid after mLoaded flag has been set.
   nsresult mLoadResult;
 
   // Init() method has been called
   bool mInitialized : 1;
 
   // This cache is about to be bound with the database (i.e. it has
--- a/dom/system/gonk/SystemProperty.cpp
+++ b/dom/system/gonk/SystemProperty.cpp
@@ -1,17 +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 "SystemProperty.h"
+
 #include <dlfcn.h>
+#include <string.h>
 
+#include "nsDebug.h"
 #include "prinit.h"
 
 namespace mozilla {
 namespace system {
 
 namespace {
 
 typedef int (*PropertyGet)(const char*, char*, const char*);
--- a/dom/system/gonk/VolumeCommand.h
+++ b/dom/system/gonk/VolumeCommand.h
@@ -43,23 +43,23 @@ public:
   VolumeResponseCallback()
     : mResponseCode(0), mPending(false) {}
 
   bool Done() const
   {
     // Response codes from the 200, 400, and 500 series all indicated that
     // the command has completed.
 
-    return (mResponseCode >= ResponseCode::CommandOkay)
-        && (mResponseCode < ResponseCode::UnsolicitedInformational);
+    return (mResponseCode >= ::ResponseCode::CommandOkay)
+        && (mResponseCode < ::ResponseCode::UnsolicitedInformational);
   }
 
   bool WasSuccessful() const
   {
-    return mResponseCode == ResponseCode::CommandOkay;
+    return mResponseCode == ::ResponseCode::CommandOkay;
   }
 
   bool              IsPending() const     { return mPending; }
   int               ResponseCode() const  { return mResponseCode; }
   const nsCString  &ResponseStr() const   { return mResponseStr; }
 
 protected:
   virtual void ResponseReceived(const VolumeCommand* aCommand) = 0;
@@ -74,17 +74,17 @@ private:
     mResponseCode = aResponseCode;
 #if ANDROID_VERSION >= 17
     // There's a sequence number here that we don't care about
     // We expect it to be 0. See VolumeCommand::SetCmd
     mResponseStr = Substring(aResponseStr, 2);
 #else
     mResponseStr = aResponseStr;
 #endif
-    if (mResponseCode >= ResponseCode::CommandOkay) {
+    if (mResponseCode >= ::ResponseCode::CommandOkay) {
       // This is a final response.
       mPending = false;
     }
     ResponseReceived(aCommand);
   }
 
   void SetPending(bool aPending) { mPending = aPending; }
 
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -95,17 +95,17 @@ interface NavigatorFeatures {
   [CheckAnyPermissions="feature-detection", Throws]
   Promise<any> getFeature(DOMString name);
 
   [CheckAnyPermissions="feature-detection", Throws]
   Promise<any> hasFeature(DOMString name);
 };
 
 partial interface Navigator {
-  [Throws, Pref="dom.permissions.enabled"]
+  [Throws]
   readonly attribute Permissions permissions;
 };
 
 // Things that definitely need to be in the spec and and are not for some
 // reason.  See https://www.w3.org/Bugs/Public/show_bug.cgi?id=22406
 partial interface Navigator {
   [Throws]
   readonly attribute MimeTypeArray mimeTypes;
--- a/dom/webidl/PermissionStatus.webidl
+++ b/dom/webidl/PermissionStatus.webidl
@@ -8,14 +8,13 @@
  */
 
 enum PermissionState {
   "granted",
   "denied",
   "prompt"
 };
 
-[Exposed=(Window),
- Pref="dom.permissions.enabled"]
+[Exposed=(Window)]
 interface PermissionStatus : EventTarget {
   readonly attribute PermissionState state;
   attribute EventHandler onchange;
 };
--- a/dom/webidl/Permissions.webidl
+++ b/dom/webidl/Permissions.webidl
@@ -17,14 +17,13 @@ enum PermissionName {
 dictionary PermissionDescriptor {
   required PermissionName name;
 };
 
 dictionary PushPermissionDescriptor : PermissionDescriptor {
   boolean userVisible = false;
 };
 
-[Exposed=(Window),
- Pref="dom.permissions.enabled"]
+[Exposed=(Window)]
 interface Permissions {
   [Throws]
   Promise<PermissionStatus> query(object permission);
 };
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -1237,21 +1237,16 @@ public:
 
     mRegistration->mWaitingWorker = mRegistration->mInstallingWorker.forget();
     mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Installed);
     mRegistration->NotifyListenersOnChange();
     swm->StoreRegistration(mPrincipal, mRegistration);
     swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
                                                    WhichServiceWorker::INSTALLING_WORKER | WhichServiceWorker::WAITING_WORKER);
 
-    // "If registration's waiting worker's skip waiting flag is set"
-    if (mRegistration->mWaitingWorker->SkipWaitingFlag()) {
-      mRegistration->PurgeActiveWorker();
-    }
-
     Done(NS_OK);
     // Activate() is invoked out of band of atomic.
     mRegistration->TryToActivate();
   }
 
 private:
   const InstallType mType;
 };
--- a/editor/libeditor/nsEditor.cpp
+++ b/editor/libeditor/nsEditor.cpp
@@ -1475,17 +1475,17 @@ nsEditor::JoinNodes(nsINode& aLeftNode, 
   // Find the number of children of the lefthand node
   uint32_t oldLeftNodeLen = aLeftNode.Length();
 
   for (auto& listener : mActionListeners) {
     listener->WillJoinNodes(aLeftNode.AsDOMNode(), aRightNode.AsDOMNode(),
                             parent->AsDOMNode());
   }
 
-  nsresult result;
+  nsresult result = NS_OK;
   RefPtr<JoinNodeTxn> txn = CreateTxnForJoinNode(aLeftNode, aRightNode);
   if (txn)  {
     result = DoTransaction(txn);
   }
 
   mRangeUpdater.SelAdjJoinNodes(aLeftNode, aRightNode, *parent, offset,
                                 (int32_t)oldLeftNodeLen);
 
--- a/editor/libeditor/nsHTMLEditor.cpp
+++ b/editor/libeditor/nsHTMLEditor.cpp
@@ -542,17 +542,17 @@ nsHTMLEditor::BeginningOfDocument()
   if (!rootElement) {
     NS_WARNING("GetRoot() returned a null pointer (mRootElement is null)");
     return NS_OK;
   }
 
   // Find first editable thingy
   bool done = false;
   nsCOMPtr<nsINode> curNode = rootElement.get(), selNode;
-  int32_t curOffset = 0, selOffset;
+  int32_t curOffset = 0, selOffset = 0;
   while (!done) {
     nsWSRunObject wsObj(this, curNode, curOffset);
     int32_t visOffset = 0;
     WSType visType;
     nsCOMPtr<nsINode> visNode;
     wsObj.NextVisibleNode(curNode, curOffset, address_of(visNode), &visOffset,
                           &visType);
     if (visType == WSType::normalWS || visType == WSType::text) {
--- a/editor/txtsvc/nsTextServicesDocument.cpp
+++ b/editor/txtsvc/nsTextServicesDocument.cpp
@@ -2749,17 +2749,17 @@ nsTextServicesDocument::GetUncollapsedSe
 
   // It is assumed that the calling function has made sure that the
   // selection is not collapsed, and that the input params to this
   // method are initialized to some defaults.
 
   nsCOMPtr<nsIDOMNode> startParent, endParent;
   int32_t startOffset, endOffset;
   int32_t rangeCount, tableCount, i;
-  int32_t e1s1, e1s2, e2s1, e2s2;
+  int32_t e1s1 = 0, e1s2 = 0, e2s1 = 0, e2s2 = 0;
 
   OffsetEntry *eStart, *eEnd;
   int32_t eStartOffset, eEndOffset;
 
   tableCount = mOffsetTable.Length();
 
   // Get pointers to the first and last offset entries
   // in the table.
--- a/gfx/2d/BaseMargin.h
+++ b/gfx/2d/BaseMargin.h
@@ -16,20 +16,20 @@ namespace mozilla {
 struct Sides final {
   Sides() : mBits(0) {}
   explicit Sides(SideBits aSideBits)
   {
     MOZ_ASSERT((aSideBits & ~eSideBitsAll) == 0, "illegal side bits");
     mBits = aSideBits;
   }
   bool IsEmpty() const { return mBits == 0; }
-  bool Top()     const { return mBits & eSideBitsTop; }
-  bool Right()   const { return mBits & eSideBitsRight; }
-  bool Bottom()  const { return mBits & eSideBitsBottom; }
-  bool Left()    const { return mBits & eSideBitsLeft; }
+  bool Top()     const { return (mBits & eSideBitsTop) != 0; }
+  bool Right()   const { return (mBits & eSideBitsRight) != 0; }
+  bool Bottom()  const { return (mBits & eSideBitsBottom) != 0; }
+  bool Left()    const { return (mBits & eSideBitsLeft) != 0; }
   bool Contains(SideBits aSideBits) const
   {
     MOZ_ASSERT((aSideBits & ~eSideBitsAll) == 0, "illegal side bits");
     return (mBits & aSideBits) == aSideBits;
   }
   Sides operator|(Sides aOther) const
   {
     return Sides(SideBits(mBits | aOther.mBits));
--- a/gfx/2d/Matrix.h
+++ b/gfx/2d/Matrix.h
@@ -293,18 +293,18 @@ public:
   }
 
   /**
    * Returns true if the matrix is anything other than a straight
    * translation by integers.
   */
   bool HasNonIntegerTranslation() const {
     return HasNonTranslation() ||
-      !FuzzyEqual(_31, floor(_31 + 0.5)) ||
-      !FuzzyEqual(_32, floor(_32 + 0.5));
+      !FuzzyEqual(_31, floor(_31 + Float(0.5))) ||
+      !FuzzyEqual(_32, floor(_32 + Float(0.5)));
   }
 
   /**
    * Returns true if the matrix only has an integer translation.
    */
   bool HasOnlyIntegerTranslation() const {
     return !HasNonIntegerTranslation();
   }
--- a/gfx/gl/GLScreenBuffer.cpp
+++ b/gfx/gl/GLScreenBuffer.cpp
@@ -841,17 +841,18 @@ DrawBuffer::Create(GLContext* const gl,
         return false;
 
     *out_buffer = Move(ret);
     return true;
 }
 
 DrawBuffer::~DrawBuffer()
 {
-    mGL->MakeCurrent();
+    if (!mGL->MakeCurrent())
+        return;
 
     GLuint fb = mFB;
     GLuint rbs[] = {
         mColorMSRB,
         mDepthRB,
         mStencilRB
     };
 
@@ -918,17 +919,18 @@ ReadBuffer::Create(GLContext* gl,
         ret = nullptr;
     }
 
     return Move(ret);
 }
 
 ReadBuffer::~ReadBuffer()
 {
-    mGL->MakeCurrent();
+    if (!mGL->MakeCurrent())
+        return;
 
     GLuint fb = mFB;
     GLuint rbs[] = {
         mDepthRB,
         mStencilRB
     };
 
     mGL->fDeleteFramebuffers(1, &fb);
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -132,17 +132,17 @@ void
 APZCTreeManager::UpdateHitTestingTree(CompositorParent* aCompositor,
                                       Layer* aRoot,
                                       bool aIsFirstPaint,
                                       uint64_t aOriginatingLayersId,
                                       uint32_t aPaintSequenceNumber)
 {
   APZThreadUtils::AssertOnCompositorThread();
 
-  MonitorAutoLock lock(mTreeLock);
+  MutexAutoLock lock(mTreeLock);
 
   // For testing purposes, we log some data to the APZTestData associated with
   // the layers id that originated this update.
   APZTestData* testData = nullptr;
   if (gfxPrefs::APZTestLoggingEnabled()) {
     if (CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aOriginatingLayersId)) {
       testData = &state->mApzTestData;
       testData->StartNewPaint(aPaintSequenceNumber);
@@ -1181,17 +1181,17 @@ APZCTreeManager::SetTargetAPZC(uint64_t 
   RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aTarget);
   mInputQueue->SetConfirmedTargetApzc(aInputBlockId, apzc);
 }
 
 void
 APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
                                        const Maybe<ZoomConstraints>& aConstraints)
 {
-  MonitorAutoLock lock(mTreeLock);
+  MutexAutoLock lock(mTreeLock);
   RefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, nullptr);
   MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC
 
   // Propagate the zoom constraints down to the subtree, stopping at APZCs
   // which have their own zoom constraints or are in a different layers id.
   if (aConstraints) {
     APZCTM_LOG("Recording constraints %s for guid %s\n",
       Stringify(aConstraints.value()).c_str(), Stringify(aGuid).c_str());
@@ -1240,17 +1240,17 @@ APZCTreeManager::FlushRepaintsToClearScr
   // hits a dispatch-to-content region then that's an ambiguous result and we
   // need to ask Gecko what actually got hit. In order to do this we need to
   // untransform the input event into Gecko space - but to do that we need to
   // know which APZC got hit! This leads to a circular dependency; the only way
   // to get out of it is to make sure that the untransform for all the possible
   // matched APZCs is the same. It is simplest to ensure that by flushing the
   // pending repaint requests, which makes all of the untransforms empty (and
   // therefore equal).
-  MonitorAutoLock lock(mTreeLock);
+  MutexAutoLock lock(mTreeLock);
   mTreeLock.AssertCurrentThreadOwns();
 
   ForEachNode(mRootNode.get(),
       [](HitTestingTreeNode* aNode)
       {
         if (aNode->IsPrimaryHolder()) {
           MOZ_ASSERT(aNode->GetApzc());
           aNode->GetApzc()->FlushRepaintForNewInputBlock();
@@ -1265,33 +1265,33 @@ APZCTreeManager::CancelAnimation(const S
   if (apzc) {
     apzc->CancelAnimation();
   }
 }
 
 void
 APZCTreeManager::AdjustScrollForSurfaceShift(const ScreenPoint& aShift)
 {
-  MonitorAutoLock lock(mTreeLock);
+  MutexAutoLock lock(mTreeLock);
   RefPtr<AsyncPanZoomController> apzc = FindRootContentOrRootApzc();
   if (apzc) {
     apzc->AdjustScrollForSurfaceShift(aShift);
   }
 }
 
 void
 APZCTreeManager::ClearTree()
 {
   // Ensure that no references to APZCs are alive in any lingering input
   // blocks. This breaks cycles from InputBlockState::mTargetApzc back to
   // the InputQueue.
   APZThreadUtils::RunOnControllerThread(NewRunnableMethod(
     mInputQueue.get(), &InputQueue::Clear));
 
-  MonitorAutoLock lock(mTreeLock);
+  MutexAutoLock lock(mTreeLock);
 
   // Collect the nodes into a list, and then destroy each one.
   // We can't destroy them as we collect them, because ForEachNode()
   // does a pre-order traversal of the tree, and Destroy() nulls out
   // the fields needed to reach the children of the node.
   nsTArray<RefPtr<HitTestingTreeNode>> nodesToDestroy;
   ForEachNode(mRootNode.get(),
       [&nodesToDestroy](HitTestingTreeNode* aNode)
@@ -1303,17 +1303,17 @@ APZCTreeManager::ClearTree()
     nodesToDestroy[i]->Destroy();
   }
   mRootNode = nullptr;
 }
 
 RefPtr<HitTestingTreeNode>
 APZCTreeManager::GetRootNode() const
 {
-  MonitorAutoLock lock(mTreeLock);
+  MutexAutoLock lock(mTreeLock);
   return mRootNode;
 }
 
 /**
  * Transform a displacement from the ParentLayer coordinates of a source APZC
  * to the ParentLayer coordinates of a target APZC.
  * @param aTreeManager the tree manager for the APZC tree containing |aSource|
  *                     and |aTarget|
@@ -1495,17 +1495,17 @@ APZCTreeManager::HitTestAPZC(const Scree
 {
   RefPtr<AsyncPanZoomController> target = GetTargetAPZC(aPoint, nullptr);
   return target != nullptr;
 }
 
 already_AddRefed<AsyncPanZoomController>
 APZCTreeManager::GetTargetAPZC(const ScrollableLayerGuid& aGuid)
 {
-  MonitorAutoLock lock(mTreeLock);
+  MutexAutoLock lock(mTreeLock);
   RefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, nullptr);
   MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC
   RefPtr<AsyncPanZoomController> apzc = node ? node->GetApzc() : nullptr;
   return apzc.forget();
 }
 
 already_AddRefed<HitTestingTreeNode>
 APZCTreeManager::GetTargetNode(const ScrollableLayerGuid& aGuid,
@@ -1514,17 +1514,17 @@ APZCTreeManager::GetTargetNode(const Scr
   mTreeLock.AssertCurrentThreadOwns();
   RefPtr<HitTestingTreeNode> target = FindTargetNode(mRootNode, aGuid, aComparator);
   return target.forget();
 }
 
 already_AddRefed<AsyncPanZoomController>
 APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint, HitTestResult* aOutHitResult)
 {
-  MonitorAutoLock lock(mTreeLock);
+  MutexAutoLock lock(mTreeLock);
   HitTestResult hitResult = HitNothing;
   ParentLayerPoint point = ViewAs<ParentLayerPixel>(aPoint,
     PixelCastJustification::ScreenIsParentLayerForRoot);
   RefPtr<AsyncPanZoomController> target = GetAPZCAtPoint(mRootNode, point, &hitResult);
 
   if (aOutHitResult) {
     *aOutHitResult = hitResult;
   }
@@ -1547,17 +1547,17 @@ APZCTreeManager::BuildOverscrollHandoffC
   // something higher up in the tree.
   // It's not sufficient to just find the initial target, however, as
   // overscroll can be handed off to another APZC. Without scroll grabbing,
   // handoff just occurs from child to parent. With scroll grabbing, the
   // handoff order can be different, so we build a chain of APZCs in the
   // order in which scroll will be handed off to them.
 
   // Grab tree lock since we'll be walking the APZC tree.
-  MonitorAutoLock lock(mTreeLock);
+  MutexAutoLock lock(mTreeLock);
 
   // Build the chain. If there is a scroll parent link, we use that. This is
   // needed to deal with scroll info layers, because they participate in handoff
   // but do not follow the expected layer tree structure. If there are no
   // scroll parent links we just walk up the tree to find the scroll parent.
   OverscrollHandoffChain* result = new OverscrollHandoffChain;
   AsyncPanZoomController* apzc = aInitialTarget;
   while (apzc != nullptr) {
@@ -1650,17 +1650,17 @@ APZCTreeManager::FindTargetNode(HitTesti
     }
   }
   return nullptr;
 }
 
 RefPtr<HitTestingTreeNode>
 APZCTreeManager::FindScrollNode(const AsyncDragMetrics& aDragMetrics)
 {
-  MonitorAutoLock lock(mTreeLock);
+  MutexAutoLock lock(mTreeLock);
 
   return DepthFirstSearch(mRootNode.get(),
       [&aDragMetrics](HitTestingTreeNode* aNode) {
         return aNode->MatchesScrollDragMetrics(aDragMetrics);
       });
 }
 
 AsyncPanZoomController*
@@ -1872,17 +1872,17 @@ APZCTreeManager::FindRootContentOrRootAp
 
 /*
  * See the long comment above for a detailed explanation of this function.
  */
 ScreenToParentLayerMatrix4x4
 APZCTreeManager::GetScreenToApzcTransform(const AsyncPanZoomController *aApzc) const
 {
   Matrix4x4 result;
-  MonitorAutoLock lock(mTreeLock);
+  MutexAutoLock lock(mTreeLock);
 
   // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
   // explained in the comment above. This function is called with aApzc at L, and the loop
   // below performs one iteration, where parent is at P. The comments explain what values are stored
   // in the variables at these two levels. All the comments use standard matrix notation where the
   // leftmost matrix in a multiplication is applied first.
 
   // ancestorUntransform is PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
@@ -1913,17 +1913,17 @@ APZCTreeManager::GetScreenToApzcTransfor
 /*
  * See the long comment above GetScreenToApzcTransform() for a detailed
  * explanation of this function.
  */
 ParentLayerToScreenMatrix4x4
 APZCTreeManager::GetApzcToGeckoTransform(const AsyncPanZoomController *aApzc) const
 {
   Matrix4x4 result;
-  MonitorAutoLock lock(mTreeLock);
+  MutexAutoLock lock(mTreeLock);
 
   // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
   // explained in the comment above. This function is called with aApzc at L, and the loop
   // below performs one iteration, where parent is at P. The comments explain what values are stored
   // in the variables at these two levels. All the comments use standard matrix notation where the
   // leftmost matrix in a multiplication is applied first.
 
   // asyncUntransform is LA.Inverse()
@@ -1942,17 +1942,17 @@ APZCTreeManager::GetApzcToGeckoTransform
   }
 
   return ViewAs<ParentLayerToScreenMatrix4x4>(result);
 }
 
 already_AddRefed<AsyncPanZoomController>
 APZCTreeManager::GetMultitouchTarget(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const
 {
-  MonitorAutoLock lock(mTreeLock);
+  MutexAutoLock lock(mTreeLock);
   RefPtr<AsyncPanZoomController> apzc;
   // For now, we only ever want to do pinching on the root-content APZC for
   // a given layers id.
   if (aApzc1 && aApzc2 && aApzc1->GetLayersId() == aApzc2->GetLayersId()) {
     // If the two APZCs have the same layers id, find the root-content APZC
     // for that layers id. Don't call CommonAncestor() because there may not
     // be a common ancestor for the layers id (e.g. if one APZCs is inside a
     // fixed-position element).
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -12,17 +12,17 @@
 #include "FrameMetrics.h"               // for FrameMetrics, etc
 #include "gfxPoint.h"                   // for gfxPoint
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT_HELPER2
 #include "mozilla/EventForwards.h"      // for WidgetInputEvent, nsEventStatus
 #include "mozilla/gfx/Logging.h"        // for gfx::TreeLog
 #include "mozilla/gfx/Matrix.h"         // for Matrix4x4
 #include "mozilla/layers/APZUtils.h"    // for HitTestResult
 #include "mozilla/layers/TouchCounter.h"// for TouchCounter
-#include "mozilla/Monitor.h"            // for Monitor
+#include "mozilla/Mutex.h"              // for Mutex
 #include "mozilla/TimeStamp.h"          // for mozilla::TimeStamp
 #include "mozilla/Vector.h"             // for mozilla::Vector
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsTArrayForwardDeclare.h"     // for nsTArray, nsTArray_Impl, etc
 #include "Units.h"                      // for CSSPoint, CSSRect, etc
 
@@ -529,17 +529,17 @@ protected:
 private:
   /* Whenever walking or mutating the tree rooted at mRootNode, mTreeLock must be held.
    * This lock does not need to be held while manipulating a single APZC instance in
    * isolation (that is, if its tree pointers are not being accessed or mutated). The
    * lock also needs to be held when accessing the mRootNode instance variable, as that
    * is considered part of the APZC tree management state.
    * Finally, the lock needs to be held when accessing mZoomConstraints.
    * IMPORTANT: See the note about lock ordering at the top of this file. */
-  mutable mozilla::Monitor mTreeLock;
+  mutable mozilla::Mutex mTreeLock;
   RefPtr<HitTestingTreeNode> mRootNode;
   /* Holds the zoom constraints for scrollable layers, as determined by the
    * the main-thread gecko code. */
   std::map<ScrollableLayerGuid, ZoomConstraints> mZoomConstraints;
   /* This tracks the APZC that should receive all inputs for the current input event block.
    * This allows touch points to move outside the thing they started on, but still have the
    * touch events delivered to the same initial APZC. This will only ever be touched on the
    * input delivery thread, and so does not require locking.
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -150,16 +150,24 @@ using mozilla::gfx::PointTyped;
  * goes from y=0...1000 and the visible portion is y=250...750 then
  * we're far from checkerboarding. If we get to y=490...990 though then we're
  * only 10 pixels away from showing checkerboarding so we are probably in
  * a state where we can't keep up with scrolling. The danger zone prefs specify
  * how wide this margin is; in the above example a y-axis danger zone of 10
  * pixels would make us drop to low-res at y=490...990.\n
  * This value is in layer pixels.
  *
+ * \li\b apz.displayport_expiry_ms
+ * While a scrollable frame is scrolling async, we set a displayport on it
+ * to make sure it is layerized. However this takes up memory, so once the
+ * scrolling stops we want to remove the displayport. This pref controls how
+ * long after scrolling stops the displayport is removed. A value of 0 will
+ * disable the expiry behavior entirely.
+ * Units: milliseconds
+ *
  * \li\b apz.enlarge_displayport_when_clipped
  * Pref that enables enlarging of the displayport along one axis when the
  * generated displayport's size is beyond that of the scrollable rect on the
  * opposite axis.
  *
  * \li\b apz.fling_accel_interval_ms
  * The time that determines whether a second fling will be treated as
  * accelerated. If two flings are started within this interval, the second one
@@ -2882,16 +2890,17 @@ AsyncPanZoomController::RequestContentRe
   if (fabsf(marginDelta.left) < EPSILON &&
       fabsf(marginDelta.top) < EPSILON &&
       fabsf(marginDelta.right) < EPSILON &&
       fabsf(marginDelta.bottom) < EPSILON &&
       fabsf(mLastPaintRequestMetrics.GetScrollOffset().x -
             aFrameMetrics.GetScrollOffset().x) < EPSILON &&
       fabsf(mLastPaintRequestMetrics.GetScrollOffset().y -
             aFrameMetrics.GetScrollOffset().y) < EPSILON &&
+      aFrameMetrics.GetPresShellResolution() == mLastPaintRequestMetrics.GetPresShellResolution() &&
       aFrameMetrics.GetZoom() == mLastPaintRequestMetrics.GetZoom() &&
       fabsf(aFrameMetrics.GetViewport().width -
             mLastPaintRequestMetrics.GetViewport().width) < EPSILON &&
       fabsf(aFrameMetrics.GetViewport().height -
             mLastPaintRequestMetrics.GetViewport().height) < EPSILON &&
       aFrameMetrics.GetScrollGeneration() == mLastPaintRequestMetrics.GetScrollGeneration()) {
     return;
   }
@@ -3310,16 +3319,19 @@ void AsyncPanZoomController::NotifyLayer
       // the css-driven resolution) may have changed, and we need to update
       // our zoom to reflect that. Note that we can't just take
       // aLayerMetrics.mZoom because the APZ may have additional async zoom
       // since the repaint request.
       gfxSize totalResolutionChange = aLayerMetrics.GetCumulativeResolution()
                                     / mFrameMetrics.GetCumulativeResolution();
       float presShellResolutionChange = aLayerMetrics.GetPresShellResolution()
                                       / mFrameMetrics.GetPresShellResolution();
+      if (presShellResolutionChange != 1.0f) {
+        needContentRepaint = true;
+      }
       mFrameMetrics.ZoomBy(totalResolutionChange / presShellResolutionChange);
     } else {
       // Take the new zoom as either device scale or composition width or
       // viewport size got changed (e.g. due to orientation change, or content
       // changing the meta-viewport tag).
       mFrameMetrics.SetZoom(aLayerMetrics.GetZoom());
       mFrameMetrics.SetDevPixelsPerCSSPixel(aLayerMetrics.GetDevPixelsPerCSSPixel());
     }
--- a/gfx/layers/apz/src/Axis.cpp
+++ b/gfx/layers/apz/src/Axis.cpp
@@ -25,27 +25,35 @@
 #include "gfxPrefs.h"                   // for the preferences
 
 #define AXIS_LOG(...)
 // #define AXIS_LOG(...) printf_stderr("AXIS: " __VA_ARGS__)
 
 namespace mozilla {
 namespace layers {
 
+// When we compute the velocity we do so by taking two input events and
+// dividing the distance delta over the time delta. In some cases the time
+// delta can be really small, which can make the velocity computation very
+// volatile. To avoid this we impose a minimum time delta below which we do
+// not recompute the velocity.
+const uint32_t MIN_VELOCITY_SAMPLE_TIME_MS = 5;
+
 bool FuzzyEqualsCoordinate(float aValue1, float aValue2)
 {
   return FuzzyEqualsAdditive(aValue1, aValue2, COORDINATE_EPSILON)
       || FuzzyEqualsMultiplicative(aValue1, aValue2);
 }
 
 extern StaticAutoPtr<ComputedTimingFunction> gVelocityCurveFunction;
 
 Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController)
   : mPos(0),
-    mPosTimeMs(0),
+    mVelocitySampleTimeMs(0),
+    mVelocitySamplePos(0),
     mVelocity(0.0f),
     mAxisLocked(false),
     mAsyncPanZoomController(aAsyncPanZoomController),
     mOverscroll(0),
     mFirstOverscrollAnimationSample(0),
     mLastOverscrollPeak(0),
     mOverscrollScale(1.0f)
 {
@@ -62,26 +70,30 @@ float Axis::ToLocalVelocity(float aVeloc
       mAsyncPanZoomController->ToParentLayerCoordinates(velocity, panStart);
   return localVelocity.Length();
 }
 
 void Axis::UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos, ParentLayerCoord aAdditionalDelta, uint32_t aTimestampMs) {
   // mVelocityQueue is controller-thread only
   APZThreadUtils::AssertOnControllerThread();
 
-  if (aTimestampMs == mPosTimeMs) {
-    // This could be a duplicate event, or it could be a legitimate event
-    // on some platforms that generate events really fast. As a compromise
-    // update mPos so we don't run into problems like bug 1042734, even though
-    // that means the velocity will be stale. Better than doing a divide-by-zero.
+  if (aTimestampMs <= mVelocitySampleTimeMs + MIN_VELOCITY_SAMPLE_TIME_MS) {
+    // See also the comment on MIN_VELOCITY_SAMPLE_TIME_MS.
+    // We still update mPos so that the positioning is correct (and we don't run
+    // into problems like bug 1042734) but the velocity will remain where it was.
+    // In particular we don't update either mVelocitySampleTimeMs or
+    // mVelocitySamplePos so that eventually when we do get an event with the
+    // required time delta we use the corresponding distance delta as well.
+    AXIS_LOG("%p|%s skipping velocity computation for small time delta %dms\n",
+        mAsyncPanZoomController, Name(), (aTimestampMs - mVelocitySampleTimeMs));
     mPos = aPos;
     return;
   }
 
-  float newVelocity = mAxisLocked ? 0.0f : (float)(mPos - aPos + aAdditionalDelta) / (float)(aTimestampMs - mPosTimeMs);
+  float newVelocity = mAxisLocked ? 0.0f : (float)(mVelocitySamplePos - aPos + aAdditionalDelta) / (float)(aTimestampMs - mVelocitySampleTimeMs);
   if (gfxPrefs::APZMaxVelocity() > 0.0f) {
     bool velocityIsNegative = (newVelocity < 0);
     newVelocity = fabs(newVelocity);
 
     float maxVelocity = ToLocalVelocity(gfxPrefs::APZMaxVelocity());
     newVelocity = std::min(newVelocity, maxVelocity);
 
     if (gfxPrefs::APZCurveThreshold() > 0.0f && gfxPrefs::APZCurveThreshold() < gfxPrefs::APZMaxVelocity()) {
@@ -102,29 +114,31 @@ void Axis::UpdateWithTouchAtDevicePoint(
       newVelocity = -newVelocity;
     }
   }
 
   AXIS_LOG("%p|%s updating velocity to %f with touch\n",
     mAsyncPanZoomController, Name(), newVelocity);
   mVelocity = newVelocity;
   mPos = aPos;
-  mPosTimeMs = aTimestampMs;
+  mVelocitySampleTimeMs = aTimestampMs;
+  mVelocitySamplePos = aPos;
 
   // Limit queue size pased on pref
   mVelocityQueue.AppendElement(std::make_pair(aTimestampMs, mVelocity));
   if (mVelocityQueue.Length() > gfxPrefs::APZMaxVelocityQueueSize()) {
     mVelocityQueue.RemoveElementAt(0);
   }
 }
 
 void Axis::StartTouch(ParentLayerCoord aPos, uint32_t aTimestampMs) {
   mStartPos = aPos;
   mPos = aPos;
-  mPosTimeMs = aTimestampMs;
+  mVelocitySampleTimeMs = aTimestampMs;
+  mVelocitySamplePos = aPos;
   mAxisLocked = false;
 }
 
 bool Axis::AdjustDisplacement(ParentLayerCoord aDisplacement,
                               /* ParentLayerCoord */ float& aDisplacementOut,
                               /* ParentLayerCoord */ float& aOverscrollAmountOut,
                               bool aForceOverscroll /* = false */)
 {
--- a/gfx/layers/apz/src/Axis.h
+++ b/gfx/layers/apz/src/Axis.h
@@ -246,17 +246,24 @@ public:
   virtual CSSToParentLayerScale GetScaleForAxis(const CSSToParentLayerScale2D& aScale) const = 0;
 
   virtual ScreenPoint MakePoint(ScreenCoord aCoord) const = 0;
 
   virtual const char* Name() const = 0;
 
 protected:
   ParentLayerCoord mPos;
-  uint32_t mPosTimeMs;
+
+  // mVelocitySampleTimeMs and mVelocitySamplePos are the time and position
+  // used in the last velocity sampling. They get updated when a new sample is
+  // taken (which may not happen on every input event, if the time delta is too
+  // small).
+  uint32_t mVelocitySampleTimeMs;
+  ParentLayerCoord mVelocitySamplePos;
+
   ParentLayerCoord mStartPos;
   float mVelocity;      // Units: ParentLayerCoords per millisecond
   bool mAxisLocked;     // Whether movement on this axis is locked.
   AsyncPanZoomController* mAsyncPanZoomController;
 
   // mOverscroll is the displacement of an oscillating spring from its resting
   // state. The resting state moves as the overscroll animation progresses.
   ParentLayerCoord mOverscroll;
--- a/gfx/layers/apz/test/mochitest/helper_subframe_style.css
+++ b/gfx/layers/apz/test/mochitest/helper_subframe_style.css
@@ -1,14 +1,14 @@
 body {
   height: 500px;
 }
 
 .inner-frame {
-  margin-top: 25%;
+  margin-top: 50px; /* this should be at least 30px */
   height: 200%;
   width: 75%;
   overflow: scroll;
 }
 .inner-content {
   height: 200%;
   width: 200%;
   background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
--- a/gfx/layers/apz/test/mochitest/test_layerization.html
+++ b/gfx/layers/apz/test/mochitest/test_layerization.html
@@ -67,16 +67,18 @@ function scrollWheelOver(element) {
   synthesizeNativeMouseMoveAndWaitForMoveEvent(element, x, y, function() {
     synthesizeNativeWheelAndWaitForScrollEvent(element, x, y, 0, -10, driveTest);
   });
 }
 
 var gTestContinuation = null;
 var utils;
 
+const DISPLAYPORT_EXPIRY = 100;
+
 // Return whether the element with id |elementId| has been layerized.
 // Assumes |elementId| will be present in the content description for the
 // element, and not in the content descriptions of other elements.
 function isLayerized(elementId) {
   var contentTestData = utils.getContentAPZTestData();
   ok(contentTestData.paints.length > 0, "expected at least one paint");
   var seqno = contentTestData.paints[contentTestData.paints.length - 1].sequenceNumber;
   contentTestData = convertTestData(contentTestData);
@@ -86,16 +88,24 @@ function isLayerized(elementId) {
       if (paint[scrollId]["contentDescription"].includes(elementId)) {
         return true;
       }
     }
   }
   return false;
 }
 
+// Helper function to pass to waitForAllPaints rather than passing driveTest
+// directly. If there are no paints pending waitForAllPaints will invoke the
+// callback synchronously, and if we did waitForAllPaints(driveTest) that might
+// cause reentrancy into driveTest which is bad.
+function callDriveTestAsync() {
+  setTimeout(driveTest, 0);
+}
+
 function* runTest() {
   utils = SpecialPowers.getDOMWindowUtils(window);
 
   // Initially, nothing should be layerized.
   ok(!isLayerized('outer1'), "initially 'outer1' should not be layerized");
   ok(!isLayerized('inner1'), "initially 'inner1' should not be layerized");
   ok(!isLayerized('outer2'), "initially 'outer2' should not be layerized");
   ok(!isLayerized('inner2'), "initially 'inner2' should not be layerized");
@@ -121,16 +131,82 @@ function* runTest() {
   yield scrollWheelOver(document.getElementById('outer3').contentDocument.documentElement);
   ok(isLayerized('outer3'), "scrolling 'outer3' should cause it to be layerized");
   ok(!isLayerized('inner3'), "scrolling 'outer3' should not cause 'inner3' to be layerized");
 
   // Scrolling over outer4 should layerize both outer4 and inner4.
   yield scrollWheelOver(document.getElementById('outer4').contentDocument.getElementById('inner4'));
   ok(isLayerized('inner4'), "scrolling 'inner4' should cause it to be layerized");
   ok(isLayerized('outer4'), "scrolling 'inner4' should also cause 'outer4' to be layerized");
+
+  // Now we enable displayport expiry, and verify that things are still
+  // layerized as they were before.
+  yield SpecialPowers.pushPrefEnv({"set": [["apz.displayport_expiry_ms", DISPLAYPORT_EXPIRY]]}, driveTest);
+  ok(isLayerized('outer1'), "outer1 is still layerized after enabling expiry");
+  ok(!isLayerized('inner1'), "inner1 is still not layerized after enabling expiry");
+  ok(isLayerized('outer2'), "outer2 is still layerized after enabling expiry");
+  ok(isLayerized('inner2'), "inner2 is still layerized after enabling expiry");
+  ok(isLayerized('outer3'), "outer3 is still layerized after enabling expiry");
+  ok(!isLayerized('inner3'), "inner3 is still not layerized after enabling expiry");
+  ok(isLayerized('outer4'), "outer4 is still layerized after enabling expiry");
+  ok(isLayerized('inner4'), "inner4 is still layerized after enabling expiry");
+
+  // Now we trigger a scroll on some of the things still layerized, so that
+  // the displayport expiry gets triggered.
+
+  // Expire displayport with scrolling on outer1
+  yield scrollWheelOver(document.getElementById('outer1'));
+  yield waitForAllPaints(function() {
+    flushApzRepaints(driveTest);
+  });
+  yield setTimeout(driveTest, DISPLAYPORT_EXPIRY);
+  yield waitForAllPaints(callDriveTestAsync);
+  ok(!isLayerized('outer1'), "outer1 is no longer layerized after displayport expiry");
+  ok(!isLayerized('inner1'), "inner1 is still not layerized after displayport expiry");
+
+  // Expire displayport with scrolling on inner2
+  yield scrollWheelOver(document.getElementById('inner2'));
+  yield waitForAllPaints(function() {
+    flushApzRepaints(driveTest);
+  });
+  // Once the expiry elapses, it will trigger expiry on outer2, so we check
+  // both, one at a time.
+  yield setTimeout(driveTest, DISPLAYPORT_EXPIRY);
+  yield waitForAllPaints(callDriveTestAsync);
+  ok(!isLayerized('inner2'), "inner2 is no longer layerized after displayport expiry");
+  yield setTimeout(driveTest, DISPLAYPORT_EXPIRY);
+  yield waitForAllPaints(callDriveTestAsync);
+  ok(!isLayerized('outer2'), "outer2 got de-layerized with inner2");
+
+  // Scroll on inner3. inner3 isn't layerized, and this will cause it to
+  // get layerized, but it will also trigger displayport expiration for inner3
+  // which will eventually trigger displayport expiration on outer3.
+  yield scrollWheelOver(document.getElementById('outer3').contentDocument.getElementById('inner3'));
+  yield waitForAllPaints(function() {
+    flushApzRepaints(driveTest);
+  });
+  ok(isLayerized('inner3'), "inner3 becomes layerized after scroll");
+  yield setTimeout(driveTest, DISPLAYPORT_EXPIRY);
+  yield waitForAllPaints(callDriveTestAsync);
+  ok(!isLayerized('inner3'), "inner3 becomes unlayerized after expiry");
+  yield setTimeout(driveTest, DISPLAYPORT_EXPIRY);
+  yield waitForAllPaints(callDriveTestAsync);
+  ok(!isLayerized('outer3'), "outer3 is no longer layerized after inner3 triggered expiry");
+
+  // Scroll outer4 and wait for the expiry. It should NOT get expired because
+  // inner4 is still layerized
+  yield scrollWheelOver(document.getElementById('outer4').contentDocument.documentElement);
+  yield waitForAllPaints(function() {
+    flushApzRepaints(driveTest);
+  });
+  // Wait for the expiry to elapse
+  yield setTimeout(driveTest, DISPLAYPORT_EXPIRY);
+  yield waitForAllPaints(callDriveTestAsync);
+  ok(isLayerized('inner4'), "inner4 is still layerized because it never expired");
+  ok(isLayerized('outer4'), "outer4 is still layerized because inner4 is still layerized");
 }
 
 function driveTest() {
   if (!gTestContinuation) {
     gTestContinuation = runTest();
   }
   var ret = gTestContinuation.next();
   if (ret.done) {
@@ -148,24 +224,26 @@ function startTest() {
   }
 
   waitForAllPaints(function() {
     flushApzRepaints(driveTest);
   })
 }
 
 SimpleTest.waitForExplicitFinish();
-SimpleTest.testInChaosMode();
+SimpleTest.requestFlakyTimeout("we are testing code that measures an actual timeout");
+SimpleTest.expectAssertions(0, 8); // we get a bunch of "ASSERTION: Bounds computation mismatch" sometimes (bug 1232856)
 
 // Disable smooth scrolling, because it results in long-running scroll
 // animations that can result in a 'scroll' event triggered by an earlier
 // wheel event as corresponding to a later wheel event.
 // Also enable APZ test logging, since we use that data to determine whether
 // a scroll frame was layerized.
 SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false],
+                                   ["apz.displayport_expiry_ms", 0],
                                    ["apz.test.logging_enabled", true]]},
                           function() {
                             SimpleTest.waitForFocus(startTest, window);
                           });
 </script>
 </pre>
 </body>
 </html>
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -173,21 +173,22 @@ ImageClientSingle::UpdateImage(ImageCont
       }
       source.size() = overlayImage->GetSize();
       GetForwarder()->UseOverlaySource(this, source, image->GetPictureRect());
       continue;
     }
 #endif
 
     RefPtr<TextureClient> texture = image->GetTextureClient(this);
+    const bool hasTextureClient = !!texture;
 
     for (int32_t i = mBuffers.Length() - 1; i >= 0; --i) {
       if (mBuffers[i].mImageSerial == image->GetSerial()) {
-        if (texture) {
-          MOZ_ASSERT(texture == mBuffers[i].mTextureClient);
+        if (hasTextureClient) {
+          MOZ_ASSERT(image->GetTextureClient(this) == mBuffers[i].mTextureClient);
         } else {
           texture = mBuffers[i].mTextureClient;
         }
         // Remove this element from mBuffers so mBuffers only contains
         // images that aren't present in 'images'
         mBuffers.RemoveElementAt(i);
       }
     }
--- a/gfx/layers/d3d9/TextureD3D9.cpp
+++ b/gfx/layers/d3d9/TextureD3D9.cpp
@@ -334,22 +334,24 @@ DataTextureSourceD3D9::Update(gfx::DataS
   // It will be ignored. Incremental update with a source offset is only used
   // on Mac so it is not clear that we ever will need to support it for D3D.
   MOZ_ASSERT(!aSrcOffset);
 
   if (!mCompositor || !mCompositor->device()) {
     NS_WARNING("No D3D device to update the texture.");
     return false;
   }
-  mSize = aSurface->GetSize();
 
-  uint32_t bpp = 0;
+  uint32_t bpp = BytesPerPixel(aSurface->GetFormat());
+  DeviceManagerD3D9* deviceManager = gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager();
+
+  mSize = aSurface->GetSize();
+  mFormat = aSurface->GetFormat();
 
   _D3DFORMAT format = D3DFMT_A8R8G8B8;
-  mFormat = aSurface->GetFormat();
   switch (mFormat) {
   case SurfaceFormat::B8G8R8X8:
     format = D3DFMT_X8R8G8B8;
     bpp = 4;
     break;
   case SurfaceFormat::B8G8R8A8:
     format = D3DFMT_A8R8G8B8;
     bpp = 4;
@@ -359,28 +361,110 @@ DataTextureSourceD3D9::Update(gfx::DataS
     bpp = 1;
     break;
   default:
     NS_WARNING("Bad image format");
     return false;
   }
 
   int32_t maxSize = mCompositor->GetMaxTextureSize();
-  DeviceManagerD3D9* deviceManager = gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager();
   if ((mSize.width <= maxSize && mSize.height <= maxSize) ||
-      (mFlags & TextureFlags::DISALLOW_BIGIMAGE)) {
-    mTexture = DataToTexture(deviceManager,
-                             aSurface->GetData(), aSurface->Stride(),
-                             IntSize(mSize), format, bpp);
+    (mFlags & TextureFlags::DISALLOW_BIGIMAGE)) {
+    mIsTiled = false;
+
+    if (mTexture) {
+      D3DSURFACE_DESC currentDesc;
+      mTexture->GetLevelDesc(0, &currentDesc);
+
+      // Make sure there's no size mismatch, if there is, recreate.
+      if (currentDesc.Width != mSize.width || currentDesc.Height != mSize.height ||
+        currentDesc.Format != format) {
+        mTexture = nullptr;
+        // Make sure we upload the whole surface.
+        aDestRegion = nullptr;
+      }
+    }
+
     if (!mTexture) {
-      NS_WARNING("Could not upload texture");
+      // TODO Improve: Reallocating this texture is costly enough
+      //      that it causes us to skip frames on scrolling
+      //      important pages like Facebook.
+      mTexture = deviceManager->CreateTexture(mSize, format, D3DPOOL_DEFAULT, this);
+      mIsTiled = false;
+      if (!mTexture) {
+        Reset();
+        return false;
+      }
+
+      if (mFlags & TextureFlags::COMPONENT_ALPHA) {
+        aDestRegion = nullptr;
+      }
+    }
+
+    DataSourceSurface::MappedSurface map;
+    if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
+      gfxCriticalError() << "Failed to map surface.";
       Reset();
       return false;
     }
-    mIsTiled = false;
+
+    nsIntRegion regionToUpdate = aDestRegion ? *aDestRegion : nsIntRegion(nsIntRect(0, 0, mSize.width, mSize.height));
+
+    RefPtr<IDirect3DTexture9> srcTexture;
+    RefPtr<IDirect3DSurface9> srcSurface;
+
+    if (mFormat == SurfaceFormat::A8) {
+      // A8 doesn't appear to work with CreateOffscreenPlainSurface
+      srcTexture = deviceManager->CreateTexture(mSize, format, D3DPOOL_SYSTEMMEM, this);
+      if (!srcTexture) {
+        aSurface->Unmap();
+        return false;
+      }
+      srcTexture->GetSurfaceLevel(0, getter_AddRefs(srcSurface));
+    } else {
+      HRESULT hr = mCompositor->device()->CreateOffscreenPlainSurface(mSize.width, mSize.height, format, D3DPOOL_SYSTEMMEM, getter_AddRefs(srcSurface), nullptr);
+      if (FAILED(hr)) {
+        aSurface->Unmap();
+        return false;
+      }
+    }
+
+    RefPtr<IDirect3DSurface9> destSurface;
+    mTexture->GetSurfaceLevel(0, getter_AddRefs(destSurface));
+
+    D3DLOCKED_RECT rect;
+    srcSurface->LockRect(&rect, nullptr, 0);
+
+    for (auto iter = regionToUpdate.RectIter(); !iter.Done(); iter.Next()) {
+      const nsIntRect& iterRect = iter.Get();
+      uint8_t* src = map.mData + map.mStride * iterRect.y + BytesPerPixel(aSurface->GetFormat()) * iterRect.x;
+      uint8_t* dest = reinterpret_cast<uint8_t*>(rect.pBits) + rect.Pitch * iterRect.y + BytesPerPixel(aSurface->GetFormat()) * iterRect.x;
+
+      for (int y = 0; y < iterRect.height; y++) {
+        memcpy(dest + rect.Pitch * y,
+          src + map.mStride * y,
+          iterRect.width * bpp);
+      }
+    }
+
+    srcSurface->UnlockRect();
+    aSurface->Unmap();
+
+    for (auto iter = regionToUpdate.RectIter(); !iter.Done(); iter.Next()) {
+      const nsIntRect& iterRect = iter.Get();
+
+      RECT updateRect;
+      updateRect.left = iterRect.x;
+      updateRect.top = iterRect.y;
+      updateRect.right = iterRect.XMost();
+      updateRect.bottom = iterRect.YMost();
+      POINT point = { updateRect.left, updateRect.top };
+
+      mCompositor->device()->UpdateSurface(srcSurface, &updateRect, destSurface, &point);
+    }
   } else {
     mIsTiled = true;
     uint32_t tileCount = GetRequiredTilesD3D9(mSize.width, maxSize) *
                          GetRequiredTilesD3D9(mSize.height, maxSize);
     mTileTextures.resize(tileCount);
     mTexture = nullptr;
 
     for (uint32_t i = 0; i < tileCount; i++) {
--- a/gfx/skia/skia/src/gpu/SkGrPixelRef.cpp
+++ b/gfx/skia/skia/src/gpu/SkGrPixelRef.cpp
@@ -46,17 +46,18 @@ void SkROLockPixelsPixelRef::onUnlockPix
 
 bool SkROLockPixelsPixelRef::onLockPixelsAreWritable() const {
     return false;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 static SkGrPixelRef* copy_to_new_texture_pixelref(GrTexture* texture, SkColorType dstCT,
-                                                  SkColorProfileType dstPT, const SkIRect* subset) {
+                                                  SkAlphaType dstAT, SkColorProfileType dstPT,
+                                                  const SkIRect* subset) {
     if (nullptr == texture || kUnknown_SkColorType == dstCT) {
         return nullptr;
     }
     GrContext* context = texture->getContext();
     if (nullptr == context) {
         return nullptr;
     }
     GrSurfaceDesc desc;
@@ -70,31 +71,30 @@ static SkGrPixelRef* copy_to_new_texture
     } else {
         SkASSERT(SkIRect::MakeWH(texture->width(), texture->height()).contains(*subset));
         // Create a new texture that is the size of subset.
         desc.fWidth = subset->width();
         desc.fHeight = subset->height();
         srcRect = *subset;
     }
     desc.fFlags = kRenderTarget_GrSurfaceFlag;
-    desc.fConfig = SkImageInfo2GrPixelConfig(dstCT, kPremul_SkAlphaType, dstPT);
+    desc.fConfig = SkImageInfo2GrPixelConfig(dstCT, dstAT, dstPT);
 
     GrTexture* dst = context->textureProvider()->createTexture(desc, false, nullptr, 0);
     if (nullptr == dst) {
         return nullptr;
     }
 
     // Blink is relying on the above copy being sent to GL immediately in the case when the source
     // is a WebGL canvas backing store. We could have a TODO to remove this flush flag, but we have
     // a larger TODO to remove SkGrPixelRef entirely.
     context->copySurface(dst->asRenderTarget(), texture, srcRect, SkIPoint::Make(0,0),
                          GrContext::kFlushWrites_PixelOp);
 
-    SkImageInfo info = SkImageInfo::Make(desc.fWidth, desc.fHeight, dstCT, kPremul_SkAlphaType,
-                                         dstPT);
+    SkImageInfo info = SkImageInfo::Make(desc.fWidth, desc.fHeight, dstCT, dstAT, dstPT);
     SkGrPixelRef* pixelRef = new SkGrPixelRef(info, dst);
     SkSafeUnref(dst);
     return pixelRef;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 SkGrPixelRef::SkGrPixelRef(const SkImageInfo& info, GrSurface* surface) : INHERITED(info) {
@@ -137,17 +137,18 @@ SkPixelRef* SkGrPixelRef::deepCopy(SkCol
     }
 
     // Note that when copying a render-target-backed pixel ref, we
     // return a texture-backed pixel ref instead.  This is because
     // render-target pixel refs are usually created in conjunction with
     // a GrTexture owned elsewhere (e.g., SkGpuDevice), and cannot live
     // independently of that texture.  Texture-backed pixel refs, on the other
     // hand, own their GrTextures, and are thus self-contained.
-    return copy_to_new_texture_pixelref(fSurface->asTexture(), dstCT, dstPT, subset);
+    return copy_to_new_texture_pixelref(fSurface->asTexture(), dstCT, this->info().alphaType(),
+                                        dstPT, subset);
 }
 
 static bool tryAllocBitmapPixels(SkBitmap* bitmap) {
     SkBitmap::Allocator* allocator = SkBitmapCache::GetAllocator();
     if (nullptr != allocator) {
         return allocator->allocPixelRef(bitmap, 0);
     } else {
         // DiscardableMemory is not available, fallback to default allocator
--- a/gfx/src/nsFont.cpp
+++ b/gfx/src/nsFont.cpp
@@ -47,39 +47,17 @@ nsFont::Init()
   variantAlternates = 0;
   variantCaps = NS_FONT_VARIANT_CAPS_NORMAL;
   variantEastAsian = 0;
   variantLigatures = 0;
   variantNumeric = 0;
   variantPosition = NS_FONT_VARIANT_POSITION_NORMAL;
 }
 
-nsFont::nsFont(const nsFont& aOther)
-  : fontlist(aOther.fontlist)
-{
-  style = aOther.style;
-  systemFont = aOther.systemFont;
-  weight = aOther.weight;
-  stretch = aOther.stretch;
-  smoothing = aOther.smoothing;
-  size = aOther.size;
-  sizeAdjust = aOther.sizeAdjust;
-  kerning = aOther.kerning;
-  synthesis = aOther.synthesis;
-  fontFeatureSettings = aOther.fontFeatureSettings;
-  languageOverride = aOther.languageOverride;
-  variantAlternates = aOther.variantAlternates;
-  variantCaps = aOther.variantCaps;
-  variantEastAsian = aOther.variantEastAsian;
-  variantLigatures = aOther.variantLigatures;
-  variantNumeric = aOther.variantNumeric;
-  variantPosition = aOther.variantPosition;
-  alternateValues = aOther.alternateValues;
-  featureValueLookup = aOther.featureValueLookup;
-}
+nsFont::nsFont(const nsFont& aOther) = default;
 
 nsFont::nsFont()
 {
 }
 
 nsFont::~nsFont()
 {
 }
@@ -106,40 +84,17 @@ bool nsFont::Equals(const nsFont& aOther
       (alternateValues == aOther.alternateValues) &&
       (featureValueLookup == aOther.featureValueLookup) &&
       (smoothing == aOther.smoothing)) {
     return true;
   }
   return false;
 }
 
-nsFont& nsFont::operator=(const nsFont& aOther)
-{
-  fontlist = aOther.fontlist;
-  style = aOther.style;
-  systemFont = aOther.systemFont;
-  weight = aOther.weight;
-  stretch = aOther.stretch;
-  smoothing = aOther.smoothing;
-  size = aOther.size;
-  sizeAdjust = aOther.sizeAdjust;
-  kerning = aOther.kerning;
-  synthesis = aOther.synthesis;
-  fontFeatureSettings = aOther.fontFeatureSettings;
-  languageOverride = aOther.languageOverride;
-  variantAlternates = aOther.variantAlternates;
-  variantCaps = aOther.variantCaps;
-  variantEastAsian = aOther.variantEastAsian;
-  variantLigatures = aOther.variantLigatures;
-  variantNumeric = aOther.variantNumeric;
-  variantPosition = aOther.variantPosition;
-  alternateValues = aOther.alternateValues;
-  featureValueLookup = aOther.featureValueLookup;
-  return *this;
-}
+nsFont& nsFont::operator=(const nsFont& aOther) = default;
 
 void
 nsFont::CopyAlternates(const nsFont& aOther)
 {
   variantAlternates = aOther.variantAlternates;
   alternateValues = aOther.alternateValues;
   featureValueLookup = aOther.featureValueLookup;
 }
--- a/gfx/src/nsRegion.cpp
+++ b/gfx/src/nsRegion.cpp
@@ -945,21 +945,16 @@ namespace {
 
     SizePair() : mSizeContainingRect(0), mSize(0) {}
 
     static SizePair VeryLargeNegative() {
       SizePair result;
       result.mSize = result.mSizeContainingRect = kVeryLargeNegativeNumber;
       return result;
     }
-    SizePair& operator=(const SizePair& aOther) {
-      mSizeContainingRect = aOther.mSizeContainingRect;
-      mSize = aOther.mSize;
-      return *this;
-    }
     bool operator<(const SizePair& aOther) const {
       if (mSizeContainingRect < aOther.mSizeContainingRect)
         return true;
       if (mSizeContainingRect > aOther.mSizeContainingRect)
         return false;
       return mSize < aOther.mSize;
     }
     bool operator>(const SizePair& aOther) const {
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -143,16 +143,17 @@ private:
   DECL_GFX_PREF(Live, "apz.axis_lock.breakout_angle",          APZAxisBreakoutAngle, float, float(M_PI / 8.0) /* 22.5 degrees */);
   DECL_GFX_PREF(Live, "apz.axis_lock.breakout_threshold",      APZAxisBreakoutThreshold, float, 1.0f / 32.0f);
   DECL_GFX_PREF(Live, "apz.axis_lock.direct_pan_angle",        APZAllowedDirectPanAngle, float, float(M_PI / 3.0) /* 60 degrees */);
   DECL_GFX_PREF(Live, "apz.axis_lock.lock_angle",              APZAxisLockAngle, float, float(M_PI / 6.0) /* 30 degrees */);
   DECL_GFX_PREF(Live, "apz.axis_lock.mode",                    APZAxisLockMode, int32_t, 0);
   DECL_GFX_PREF(Live, "apz.content_response_timeout",          APZContentResponseTimeout, int32_t, 300);
   DECL_GFX_PREF(Live, "apz.danger_zone_x",                     APZDangerZoneX, int32_t, 50);
   DECL_GFX_PREF(Live, "apz.danger_zone_y",                     APZDangerZoneY, int32_t, 100);
+  DECL_GFX_PREF(Live, "apz.displayport_expiry_ms",             APZDisplayPortExpiryTime, uint32_t, 15000);
   DECL_GFX_PREF(Live, "apz.drag.enabled",                      APZDragEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.enlarge_displayport_when_clipped",  APZEnlargeDisplayPortWhenClipped, bool, false);
   DECL_GFX_PREF(Live, "apz.fling_accel_base_mult",             APZFlingAccelBaseMultiplier, float, 1.0f);
   DECL_GFX_PREF(Live, "apz.fling_accel_interval_ms",           APZFlingAccelInterval, int32_t, 500);
   DECL_GFX_PREF(Live, "apz.fling_accel_supplemental_mult",     APZFlingAccelSupplementalMultiplier, float, 1.0f);
   DECL_GFX_PREF(Once, "apz.fling_curve_function_x1",           APZCurveFunctionX1, float, 0.0f);
   DECL_GFX_PREF(Once, "apz.fling_curve_function_x2",           APZCurveFunctionX2, float, 1.0f);
   DECL_GFX_PREF(Once, "apz.fling_curve_function_y1",           APZCurveFunctionY1, float, 0.0f);
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -173,18 +173,25 @@ gfxUserFontEntry::CreateFontInstance(con
 }
 
 class gfxOTSContext : public ots::OTSContext {
 public:
     explicit gfxOTSContext(gfxUserFontEntry* aUserFontEntry)
         : mUserFontEntry(aUserFontEntry) {}
 
     virtual ots::TableAction GetTableAction(uint32_t aTag) override {
-        // preserve Graphite, color glyph and SVG tables
-        if (aTag == TRUETYPE_TAG('S', 'i', 'l', 'f') ||
+        // Preserve Graphite, color glyph and SVG tables
+        if (
+#ifdef RELEASE_BUILD // For Beta/Release, also allow OT Layout tables through
+                     // unchecked, and rely on harfbuzz to handle them safely.
+            aTag == TRUETYPE_TAG('G', 'D', 'E', 'F') ||
+            aTag == TRUETYPE_TAG('G', 'P', 'O', 'S') ||
+            aTag == TRUETYPE_TAG('G', 'S', 'U', 'B') ||
+#endif
+            aTag == TRUETYPE_TAG('S', 'i', 'l', 'f') ||
             aTag == TRUETYPE_TAG('S', 'i', 'l', 'l') ||
             aTag == TRUETYPE_TAG('G', 'l', 'o', 'c') ||
             aTag == TRUETYPE_TAG('G', 'l', 'a', 't') ||
             aTag == TRUETYPE_TAG('F', 'e', 'a', 't') ||
             aTag == TRUETYPE_TAG('S', 'V', 'G', ' ') ||
             aTag == TRUETYPE_TAG('C', 'O', 'L', 'R') ||
             aTag == TRUETYPE_TAG('C', 'P', 'A', 'L')) {
             return ots::TABLE_ACTION_PASSTHRU;
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -2579,22 +2579,25 @@ namespace {
     }
 
 static inline Expr
 SimdToExpr(SimdType type, SimdOperation op)
 {
     switch (type) {
       case SimdType::Int32x4: {
         ENUMERATE(I32x4, FORALL_INT32X4_ASMJS_OP, I32CASE)
+        break;
       }
       case SimdType::Float32x4: {
         ENUMERATE(F32x4, FORALL_FLOAT32X4_ASMJS_OP, F32CASE)
+        break;
       }
       case SimdType::Bool32x4: {
         ENUMERATE(B32x4, FORALL_BOOL_SIMD_OP, B32CASE)
+        break;
       }
       default: break;
     }
     MOZ_CRASH("unexpected SIMD type x operator combination");
 }
 
 #undef CASE
 #undef I32CASE
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -283,17 +283,17 @@ EvalKernel(JSContext* cx, const CallArgs
                                              evalType == DIRECT_EVAL
                                              ? CALLED_FROM_JSOP_EVAL
                                              : NOT_CALLED_FROM_JSOP_EVAL);
 
         const char* introducerFilename = filename;
         if (maybeScript && maybeScript->scriptSource()->introducerFilename())
             introducerFilename = maybeScript->scriptSource()->introducerFilename();
 
-        RootedObject enclosing(cx);
+        Rooted<StaticScope*> enclosing(cx);
         if (evalType == DIRECT_EVAL)
             enclosing = callerScript->innermostStaticScope(pc);
         else
             enclosing = &cx->global()->lexicalScope().staticBlock();
         Rooted<StaticEvalScope*> staticScope(cx, StaticEvalScope::create(cx, enclosing));
         if (!staticScope)
             return false;
 
@@ -369,17 +369,17 @@ js::DirectEvalStringFromIon(JSContext* c
         uint32_t pcOffset;
         DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset,
                                              &mutedErrors, CALLED_FROM_JSOP_EVAL);
 
         const char* introducerFilename = filename;
         if (maybeScript && maybeScript->scriptSource()->introducerFilename())
             introducerFilename = maybeScript->scriptSource()->introducerFilename();
 
-        RootedObject enclosing(cx, callerScript->innermostStaticScope(pc));
+        Rooted<StaticScope*> enclosing(cx, callerScript->innermostStaticScope(pc));
         Rooted<StaticEvalScope*> staticScope(cx, StaticEvalScope::create(cx, enclosing));
         if (!staticScope)
             return false;
 
         CompileOptions options(cx);
         options.setFileAndLine(filename, 1)
                .setIsRunOnce(true)
                .setForEval(true)
@@ -471,17 +471,18 @@ js::ExecuteInGlobalAndReturnScope(JSCont
 
     Rooted<ClonedBlockObject*> globalLexical(cx, &globalRoot->lexicalScope());
     Rooted<ScopeObject*> scope(cx, NonSyntacticVariablesObject::create(cx, globalLexical));
     if (!scope)
         return false;
 
     // Unlike the non-syntactic scope chain API used by the subscript loader,
     // this API creates a fresh block scope each time.
-    RootedObject enclosingStaticScope(cx, script->enclosingStaticScope());
+    Rooted<StaticNonSyntacticScope*> enclosingStaticScope(cx,
+        &script->enclosingStaticScope()->as<StaticNonSyntacticScope>());
     scope = ClonedBlockObject::createNonSyntactic(cx, enclosingStaticScope, scope);
     if (!scope)
         return false;
 
     RootedValue rval(cx);
     if (!ExecuteKernel(cx, script, *scope, UndefinedValue(),
                        NullFramePtr() /* evalInFrame */, rval.address()))
     {
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -568,25 +568,29 @@ DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject,
 
 /* static */ bool
 ModuleObject::isInstance(HandleValue value)
 {
     return value.isObject() && value.toObject().is<ModuleObject>();
 }
 
 /* static */ ModuleObject*
-ModuleObject::create(ExclusiveContext* cx, HandleObject enclosingStaticScope)
+ModuleObject::create(ExclusiveContext* cx, Handle<StaticScope*> enclosingStaticScope)
 {
     RootedObject proto(cx, cx->global()->getModulePrototype());
     RootedObject obj(cx, NewObjectWithGivenProto(cx, &class_, proto));
     if (!obj)
         return nullptr;
 
     RootedModuleObject self(cx, &obj->as<ModuleObject>());
-    self->initReservedSlot(StaticScopeSlot, ObjectOrNullValue(enclosingStaticScope));
+    Rooted<StaticModuleScope*> scope(cx, StaticModuleScope::create(cx, self,
+                                                                   enclosingStaticScope));
+    if (!scope)
+        return nullptr;
+    self->initReservedSlot(StaticScopeSlot, ObjectOrNullValue(scope));
 
     Zone* zone = cx->zone();
     IndirectBindingMap* bindings = zone->new_<IndirectBindingMap>(zone);
     if (!bindings || !bindings->init()) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
@@ -724,20 +728,20 @@ ModuleObject::evaluated() const
 }
 
 ModuleEnvironmentObject&
 ModuleObject::initialEnvironment() const
 {
     return getReservedSlot(InitialEnvironmentSlot).toObject().as<ModuleEnvironmentObject>();
 }
 
-JSObject*
-ModuleObject::enclosingStaticScope() const
+StaticModuleScope*
+ModuleObject::staticScope() const
 {
-    return getReservedSlot(StaticScopeSlot).toObjectOrNull();
+    return &getReservedSlot(StaticScopeSlot).toObject().as<StaticModuleScope>();
 }
 
 /* static */ void
 ModuleObject::trace(JSTracer* trc, JSObject* obj)
 {
     ModuleObject& module = obj->as<ModuleObject>();
     if (module.hasScript()) {
         JSScript* script = module.script();
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -16,16 +16,18 @@
 
 #include "vm/NativeObject.h"
 #include "vm/ProxyObject.h"
 
 namespace js {
 
 class ModuleEnvironmentObject;
 class ModuleObject;
+class StaticScope;
+class StaticModuleScope;
 
 namespace frontend {
 class ParseNode;
 } /* namespace frontend */
 
 typedef Rooted<ModuleObject*> RootedModuleObject;
 typedef Handle<ModuleObject*> HandleModuleObject;
 typedef Rooted<ModuleEnvironmentObject*> RootedModuleEnvironmentObject;
@@ -220,27 +222,27 @@ class ModuleObject : public NativeObject
         FunctionDeclarationsSlot,
         SlotCount
     };
 
     static const Class class_;
 
     static bool isInstance(HandleValue value);
 
-    static ModuleObject* create(ExclusiveContext* cx, HandleObject enclosingStaticScope);
+    static ModuleObject* create(ExclusiveContext* cx, Handle<StaticScope*> enclosingStaticScope);
     void init(HandleScript script);
     void setInitialEnvironment(Handle<ModuleEnvironmentObject*> initialEnvironment);
     void initImportExportData(HandleArrayObject requestedModules,
                               HandleArrayObject importEntries,
                               HandleArrayObject localExportEntries,
                               HandleArrayObject indiretExportEntries,
                               HandleArrayObject starExportEntries);
 
     JSScript* script() const;
-    JSObject* enclosingStaticScope() const;
+    StaticModuleScope* staticScope() const;
     ModuleEnvironmentObject& initialEnvironment() const;
     ModuleEnvironmentObject* environment() const;
     ModuleNamespaceObject* namespace_();
     bool evaluated() const;
     ArrayObject& requestedModules() const;
     ArrayObject& importEntries() const;
     ArrayObject& localExportEntries() const;
     ArrayObject& indirectExportEntries() const;
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -208,16 +208,18 @@ static const JSFunctionSpec SimdTypedObj
 // Simd##Type##_##Operation
 //
 // /!\ Don't forget to keep this list in sync with the SIMD instrinics used in
 // SelfHosting.cpp.
 
 namespace js {
 namespace jit {
 
+static_assert(uint64_t(SimdOperation::Last) <= UINT16_MAX, "SimdOperation must fit in uint16_t");
+
 // See also JitInfo_* in MCallOptimize.cpp. We provide a JSJitInfo for all the
 // named functions here. The default JitInfo_SimdInt32x4 etc structs represent the
 // SimdOperation::Constructor.
 #define DEFN(TYPE, OP) const JSJitInfo JitInfo_Simd##TYPE##_##OP = {                             \
      /* .getter, unused for inlinable natives. */                                                \
     { nullptr },                                                                                 \
     /* .inlinableNative, but we have to init first union member: .protoID. */                    \
     { uint16_t(InlinableNative::Simd##TYPE) },                                                   \
--- a/js/src/builtin/SIMD.h
+++ b/js/src/builtin/SIMD.h
@@ -873,17 +873,21 @@ GetBooleanSimdType(SimdType t)
 }
 
 // Complete set of SIMD operations.
 //
 // No SIMD types implement all of these operations.
 //
 // C++ defines keywords and/or/xor/not, so prepend Fn_ to all named functions to
 // avoid clashes.
-enum class SimdOperation : uint8_t {
+//
+// Note: because of a gcc < v4.8's compiler bug, uint8_t can't be used as the
+// storage class here. See bug 1243810. See also
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64037 .
+enum class SimdOperation {
     // The constructor call. No Fn_ prefix here.
     Constructor,
 
     // All the operations, except for casts.
 #define DEFOP(x) Fn_##x,
     FORALL_SIMD_NONCAST_OP(DEFOP)
 #undef DEFOP
 
@@ -896,16 +900,18 @@ enum class SimdOperation : uint8_t {
     Fn_fromInt8x16Bits,
     Fn_fromInt16x8Bits,
     Fn_fromInt32x4Bits,
     Fn_fromUint8x16Bits,
     Fn_fromUint16x8Bits,
     Fn_fromUint32x4Bits,
     Fn_fromFloat32x4Bits,
     Fn_fromFloat64x2Bits,
+
+    Last = Fn_fromFloat64x2Bits
 };
 
 class SimdObject : public JSObject
 {
   public:
     static const Class class_;
     static bool toString(JSContext* cx, unsigned int argc, Value* vp);
     static bool resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId, bool* resolved);
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -3618,19 +3618,16 @@ AC_SUBST(MOZ_POST_PROGRAM_COMMAND)
 AC_SUBST(MOZ_APP_NAME)
 AC_SUBST(MOZ_APP_DISPLAYNAME)
 AC_SUBST(MOZ_APP_VERSION)
 
 AC_SUBST(MOZ_PKG_SPECIAL)
 
 AC_SUBST(MOZILLA_OFFICIAL)
 
-dnl win32 options
-AC_SUBST(MOZ_BROWSE_INFO)
-
 dnl Echo the CFLAGS to remove extra whitespace.
 CFLAGS=`echo \
 	$_WARNINGS_CFLAGS \
 	$CFLAGS`
 
 CXXFLAGS=`echo \
 	$_WARNINGS_CXXFLAGS \
 	$CXXFLAGS`
--- a/js/src/doc/Debugger/Debugger.md
+++ b/js/src/doc/Debugger/Debugger.md
@@ -531,8 +531,18 @@ other kinds of objects.
 
 <code>makeGlobalObjectReference(<i>global</i>)</code>
 :   Return the [`Debugger.Object`][object] whose referent is the global object
     designated by <i>global</i>, without adding the designated global as a
     debuggee. If <i>global</i> does not designate a global object, throw a
     `TypeError`. Determine which global is designated by <i>global</i>
     using the same rules as [`Debugger.prototype.addDebuggee`][add].
 
+## Static methods of the Debugger Object
+
+The functions described below are not called with a `this` value.
+
+<code id="isCompilableUnit">isCompilableUnit(<i>source</i>)</code>
+:   Given a string of source code, designated by <i>source</i>, return false if
+    the string might become a valid JavaScript statement with the addition of
+    more lines. Otherwise return true. The intent is to support interactive
+    compilation - accumulate lines in a buffer until isCompilableUnit is true,
+    then pass it to the compiler.
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -67,17 +67,17 @@ class MOZ_STACK_CLASS BytecodeCompiler
 
   private:
     bool checkLength();
     bool createScriptSource();
     bool maybeCompressSource();
     bool canLazilyParse();
     bool createParser();
     bool createSourceAndParser();
-    bool createScript(HandleObject staticScope, bool savedCallerFun = false);
+    bool createScript(Handle<StaticScope*> staticScope, bool savedCallerFun = false);
     bool createEmitter(SharedContext* sharedContext, HandleScript evalCaller = nullptr,
                        bool insideNonGlobalEval = false);
     bool isEvalCompilationUnit();
     bool isNonGlobalEvalCompilationUnit();
     bool isNonSyntacticCompilationUnit();
     bool saveCallerFun(HandleScript evalCaller);
     bool handleParseFailure(const Directives& newDirectives);
     bool prepareAndEmitTree(ParseNode** pn);
@@ -250,17 +250,17 @@ bool
 BytecodeCompiler::createSourceAndParser()
 {
     return createScriptSource() &&
            maybeCompressSource() &&
            createParser();
 }
 
 bool
-BytecodeCompiler::createScript(HandleObject staticScope, bool savedCallerFun)
+BytecodeCompiler::createScript(Handle<StaticScope*> staticScope, bool savedCallerFun)
 {
     script = JSScript::Create(cx, staticScope, savedCallerFun, options,
                               sourceObject, /* sourceStart = */ 0, sourceBuffer.length());
 
     return script != nullptr;
 }
 
 bool
@@ -279,21 +279,18 @@ bool
 BytecodeCompiler::isEvalCompilationUnit()
 {
     return enclosingStaticScope->is<StaticEvalScope>();
 }
 
 bool
 BytecodeCompiler::isNonGlobalEvalCompilationUnit()
 {
-    if (!isEvalCompilationUnit())
-        return false;
-    StaticEvalScope& eval = enclosingStaticScope->as<StaticEvalScope>();
-    JSObject* enclosing = eval.enclosingScopeForStaticScopeIter();
-    return !IsStaticGlobalLexicalScope(enclosing);
+    return isEvalCompilationUnit() &&
+           !IsStaticGlobalLexicalScope(enclosingStaticScope->enclosingScope());
 }
 
 bool
 BytecodeCompiler::isNonSyntacticCompilationUnit()
 {
     return enclosingStaticScope->is<StaticNonSyntacticScope>();
 }
 
@@ -557,26 +554,28 @@ BytecodeCompiler::compileScript(HandleOb
 
     if (!maybeCompleteCompressSource())
         return nullptr;
 
     MOZ_ASSERT_IF(cx->isJSContext(), !cx->asJSContext()->isExceptionPending());
     return script;
 }
 
-ModuleObject* BytecodeCompiler::compileModule()
+ModuleObject*
+BytecodeCompiler::compileModule()
 {
     if (!createSourceAndParser())
         return nullptr;
 
     Rooted<ModuleObject*> module(cx, ModuleObject::create(cx, enclosingStaticScope));
     if (!module)
         return nullptr;
 
-    if (!createScript(module))
+    Rooted<StaticModuleScope*> moduleScope(cx, module->staticScope());
+    if (!createScript(moduleScope))
         return nullptr;
 
     module->init(script);
 
     ModuleBuilder builder(cx->asJSContext(), module);
     ParseNode* pn = parser->standaloneModule(module, builder);
     if (!pn)
         return nullptr;
@@ -646,17 +645,18 @@ BytecodeCompiler::compileFunctionBody(Mu
         !maybeSetSourceMap(parser->tokenStream))
     {
         return false;
     }
 
     if (fn->pn_funbox->function()->isInterpreted()) {
         MOZ_ASSERT(fun == fn->pn_funbox->function());
 
-        if (!createScript(enclosingStaticScope))
+        Rooted<StaticScope*> scope(cx, fn->pn_funbox->staticScope());
+        if (!createScript(scope))
             return false;
 
         script->bindings = fn->pn_funbox->bindings;
 
         if (!createEmitter(fn->pn_funbox) ||
             !emitter->emitFunctionScript(fn->pn_body))
         {
             return false;
@@ -798,21 +798,22 @@ frontend::CompileLazyFunction(JSContext*
     MOZ_ASSERT(!lazy->isLegacyGenerator());
     ParseNode* pn = parser.standaloneLazyFunction(fun, lazy->strict(), lazy->generatorKind());
     if (!pn)
         return false;
 
     if (!NameFunctions(cx, pn))
         return false;
 
-    RootedObject enclosingScope(cx, lazy->enclosingScope());
+    Rooted<StaticScope*> staticScope(cx, pn->pn_funbox->staticScope());
+    MOZ_ASSERT(staticScope);
     RootedScriptSource sourceObject(cx, lazy->sourceObject());
     MOZ_ASSERT(sourceObject);
 
-    Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false, options,
+    Rooted<JSScript*> script(cx, JSScript::Create(cx, staticScope, false, options,
                                                   sourceObject, lazy->begin(), lazy->end()));
     if (!script)
         return false;
 
     script->bindings = pn->pn_funbox->bindings;
 
     if (lazy->usesArgumentsApplyAndThis())
         script->setUsesArgumentsApplyAndThis();
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -728,17 +728,17 @@ BytecodeEmitter::pushLoopStatement(LoopS
     if (enclosingLoop) {
         stmt->canIonOsr = (enclosingLoop->canIonOsr &&
                            stmt->stackDepth == enclosingLoop->stackDepth + loopSlots);
     } else {
         stmt->canIonOsr = stmt->stackDepth == loopSlots;
     }
 }
 
-JSObject*
+StaticScope*
 BytecodeEmitter::innermostStaticScope() const
 {
     if (StmtInfoBCE* stmt = innermostScopeStmt())
         return stmt->staticScope;
     return sc->staticScope();
 }
 
 #ifdef DEBUG
@@ -1357,19 +1357,17 @@ bool
 BytecodeEmitter::atBodyLevel(StmtInfoBCE* stmt) const
 {
     // 'eval' and non-syntactic scripts are always under an invisible lexical
     // scope, but since it is not syntactic, it should still be considered at
     // body level.
     if (sc->staticScope()->is<StaticEvalScope>()) {
         bool bl = !stmt->enclosing;
         MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK);
-        MOZ_ASSERT_IF(bl, stmt->staticScope
-                              ->as<StaticBlockScope>()
-                              .enclosingStaticScope() == sc->staticScope());
+        MOZ_ASSERT_IF(bl, stmt->staticScope->enclosingScope() == sc->staticScope());
         return bl;
     }
     return !stmt;
 }
 
 uint32_t
 BytecodeEmitter::computeHops(ParseNode* pn, BytecodeEmitter** bceOfDefOut)
 {
@@ -1462,18 +1460,18 @@ BytecodeEmitter::computeDefinitionIsAlia
 {
     if (dn->isKnownAliased()) {
         *op = UnaliasedVarOpToAliasedVarOp(*op);
     } else if (isAliasedName(bceOfDef, dn)) {
         // Translate the frame slot to a slot on the dynamic scope
         // object. Aliased block bindings do not need adjusting; see
         // computeAliasedSlots.
         uint32_t slot = dn->pn_scopecoord.slot();
-        if (blockScopeOfDef(dn)->is<JSFunction>() ||
-            blockScopeOfDef(dn)->is<ModuleObject>())
+        if (blockScopeOfDef(dn)->is<StaticFunctionScope>() ||
+            blockScopeOfDef(dn)->is<StaticModuleScope>())
         {
             MOZ_ASSERT(IsArgOp(*op) || slot < bceOfDef->script->bindings.numBodyLevelLocals());
             MOZ_ALWAYS_TRUE(bceOfDef->lookupAliasedName(bceOfDef->script, dn->name(), &slot));
         }
         if (!dn->pn_scopecoord.setSlot(parser->tokenStream, slot))
             return false;
 
         *op = UnaliasedVarOpToAliasedVarOp(*op);
@@ -1564,38 +1562,42 @@ BytecodeEmitter::tryConvertFreeName(Pars
             }
 
             if (!ssi.hasSyntacticDynamicScopeObject())
                 continue;
 
             // Look up for name in function and block scopes.
             if (ssi.type() == StaticScopeIter<NoGC>::Function) {
                 RootedScript funScript(cx, ssi.funScript());
-                if (funScript->funHasExtensibleScope() || ssi.fun().atom() == pn->pn_atom)
+                if (funScript->funHasExtensibleScope() ||
+                    ssi.fun().function().atom() == pn->pn_atom)
+                {
                     return false;
+                }
 
                 // Skip the current function, since we're trying to convert a
                 // free name.
                 if (script != funScript) {
                     uint32_t slot_;
                     if (lookupAliasedName(funScript, name, &slot_, pn)) {
                         slot = Some(slot_);
                         break;
                     }
                 }
             } else if (ssi.type() == StaticScopeIter<NoGC>::Module) {
-                RootedScript moduleScript(cx, ssi.moduleScript());
+                RootedScript moduleScript(cx, ssi.module().script());
                 uint32_t slot_;
                 if (lookupAliasedName(moduleScript, name, &slot_, pn)) {
                     slot = Some(slot_);
                     break;
                 }
 
                 // Convert module import accesses to use JSOP_GETIMPORT.
-                RootedModuleEnvironmentObject env(cx, &ssi.module().initialEnvironment());
+                RootedModuleEnvironmentObject env(cx, &ssi.module().moduleObject()
+                                                       .initialEnvironment());
                 RootedPropertyName propName(cx, name);
                 MOZ_ASSERT(env);
                 if (env->hasImportBinding(propName)) {
                     if (pn->getOp() == JSOP_GETNAME) {
                         pn->setOp(JSOP_GETIMPORT);
                         return true;
                     }
                     return false;
@@ -1828,22 +1830,21 @@ BytecodeEmitter::bindNameToSlotHelper(Pa
       case Definition::NAMED_LAMBDA: {
         MOZ_ASSERT(dn->isOp(JSOP_CALLEE));
         MOZ_ASSERT(op != JSOP_CALLEE);
 
         /*
          * Currently, the ALIASEDVAR ops do not support accessing the
          * callee of a DeclEnvObject, so use NAME.
          */
-        JSFunction* fun = sc->asFunctionBox()->function();
-        if (blockScopeOfDef(dn) != fun)
+        if (blockScopeOfDef(dn) != sc->asFunctionBox()->staticScope())
             return true;
 
-        MOZ_ASSERT(fun->isLambda());
-        MOZ_ASSERT(pn->pn_atom == fun->atom());
+        MOZ_ASSERT(sc->asFunctionBox()->function()->isLambda());
+        MOZ_ASSERT(pn->pn_atom == sc->asFunctionBox()->function()->atom());
 
         /*
          * Leave pn->isOp(JSOP_GETNAME) if this->fun needs a CallObject to
          * address two cases: a new binding introduced by eval, and
          * assignment to the name in strict mode.
          *
          *   var fun = (function f(s) { eval(s); return f; });
          *   assertEq(fun("var f = 42"), 42);
@@ -3533,19 +3534,19 @@ BytecodeEmitter::emitSetThis(ParseNode* 
     if (!emit1(JSOP_POP))
         return false;
 
     // Emit the set.
     return emitVarOp(name, setOp);
 }
 
 static bool
-IsModuleOnScopeChain(JSObject* obj)
-{
-    for (StaticScopeIter<NoGC> ssi(obj); !ssi.done(); ssi++) {
+IsModuleOnScopeChain(StaticScope* scope)
+{
+    for (StaticScopeIter<NoGC> ssi(scope); !ssi.done(); ssi++) {
         if (ssi.type() == StaticScopeIter<NoGC>::Module)
             return true;
     }
     return false;
 }
 
 bool
 BytecodeEmitter::emitFunctionScript(ParseNode* body)
@@ -6385,54 +6386,63 @@ BytecodeEmitter::emitFunction(ParseNode*
     if (fun->isInterpreted()) {
         bool singleton = checkRunOnceContext();
         if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
             return false;
 
         SharedContext* outersc = sc;
         if (fun->isInterpretedLazy()) {
             if (!fun->lazyScript()->sourceObject()) {
-                JSObject* scope = innermostStaticScope();
+                // Two cases that can arise during parsing can cause the static
+                // scope chain to be incorrectly linked up: (1) the
+                // transformation of blocks from non-scopeful to scopeful when
+                // the first block-scoped declaration is found; (2) legacy
+                // comprehension expression transplantation. The
+                // setEnclosingScope call below fixes these cases.
+                Rooted<StaticScope*> enclosingScope(cx, innermostStaticScope());
+                fun->lazyScript()->staticScope()->setEnclosingScope(enclosingScope);
+
                 JSObject* source = script->sourceObject();
-                fun->lazyScript()->setParent(scope, &source->as<ScriptSourceObject>());
+                fun->lazyScript()->initSource(&source->as<ScriptSourceObject>());
             }
             if (emittingRunOnceLambda)
                 fun->lazyScript()->setTreatAsRunOnce();
         } else {
-
             if (outersc->isFunctionBox() && outersc->asFunctionBox()->mightAliasLocals())
                 funbox->setMightAliasLocals();      // inherit mightAliasLocals from parent
             MOZ_ASSERT_IF(outersc->strict(), funbox->strictScript);
 
             // Inherit most things (principals, version, etc) from the
             // parent.  Use default values for the rest.
             Rooted<JSScript*> parent(cx, script);
             MOZ_ASSERT(parent->getVersion() == parser->options().version);
             MOZ_ASSERT(parent->mutedErrors() == parser->options().mutedErrors());
             const TransitiveCompileOptions& transitiveOptions = parser->options();
             CompileOptions options(cx, transitiveOptions);
 
-            Rooted<JSObject*> enclosingScope(cx, innermostStaticScope());
+            // See comment above regarding funScope->setEnclosingScope().
+            Rooted<StaticScope*> funScope(cx, funbox->staticScope());
+            Rooted<StaticScope*> enclosingScope(cx, innermostStaticScope());
+            funScope->setEnclosingScope(enclosingScope);
+
             Rooted<JSObject*> sourceObject(cx, script->sourceObject());
-            Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false, options,
+            Rooted<JSScript*> script(cx, JSScript::Create(cx, funScope, false, options,
                                                           sourceObject,
                                                           funbox->bufStart, funbox->bufEnd));
             if (!script)
                 return false;
 
             script->bindings = funbox->bindings;
 
             uint32_t lineNum = parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin);
             BytecodeEmitter bce2(this, parser, funbox, script, /* lazyScript = */ nullptr,
                                  insideEval, evalCaller,
                                  insideNonGlobalEval, lineNum, emitterMode);
             if (!bce2.init())
                 return false;
-
-            /* We measured the max scope depth when we parsed the function. */
             if (!bce2.emitFunctionScript(pn->pn_body))
                 return false;
 
             if (funbox->usesArguments && funbox->usesApply && funbox->usesThis)
                 script->setUsesArgumentsApplyAndThis();
         }
         if (outersc->isFunctionBox())
             outersc->asFunctionBox()->function()->nonLazyScript()->setHasInnerFunctions(true);
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -244,19 +244,19 @@ struct BytecodeEmitter
                     HandleScript script, Handle<LazyScript*> lazyScript,
                     bool insideEval, HandleScript evalCaller,
                     bool insideNonGlobalEval, uint32_t lineNum, EmitterMode emitterMode = Normal);
     bool init();
     bool updateLocalsToFrameSlots();
 
     StmtInfoBCE* innermostStmt() const { return stmtStack.innermost(); }
     StmtInfoBCE* innermostScopeStmt() const { return stmtStack.innermostScopeStmt(); }
-    JSObject* innermostStaticScope() const;
-    JSObject* blockScopeOfDef(Definition* dn) const {
-        return parser->blockScopes[dn->pn_blockid];
+    StaticScope* innermostStaticScope() const;
+    StaticScope* blockScopeOfDef(Definition* dn) const {
+        return &parser->blockScopes[dn->pn_blockid].get()->as<StaticScope>();
     }
 
     bool atBodyLevel(StmtInfoBCE* stmt) const;
     bool atBodyLevel() const {
         return atBodyLevel(innermostStmt());
     }
     uint32_t computeHops(ParseNode* pn, BytecodeEmitter** bceOfDefOut);
     bool isAliasedName(BytecodeEmitter* bceOfDef, ParseNode* pn);
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -1188,18 +1188,18 @@ ObjectBox::trace(JSTracer* trc)
     TraceRoot(trc, &object, "parser.object");
 }
 
 void
 FunctionBox::trace(JSTracer* trc)
 {
     ObjectBox::trace(trc);
     bindings.trace(trc);
-    if (enclosingStaticScope_)
-        TraceRoot(trc, &enclosingStaticScope_, "funbox-enclosingStaticScope");
+    if (staticScope_)
+        TraceRoot(trc, &staticScope_, "funbox-staticScope");
 }
 
 void
 ModuleBox::trace(JSTracer* trc)
 {
     ObjectBox::trace(trc);
     bindings.trace(trc);
 }
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -52,19 +52,19 @@ using JS::AutoGCRooter;
 
 JSFunction::AutoParseUsingFunctionBox::AutoParseUsingFunctionBox(ExclusiveContext* cx,
                                                                  frontend::FunctionBox* funbox)
   : fun_(cx, funbox->function()),
     oldEnv_(cx, fun_->environment())
 {
     fun_->unsetEnvironment();
     fun_->setFunctionBox(funbox);
-    funbox->computeAllowSyntax(fun_);
-    funbox->computeInWith(fun_);
-    funbox->computeThisBinding(fun_);
+    funbox->computeAllowSyntax(funbox->staticScope_);
+    funbox->computeInWith(funbox->staticScope_);
+    funbox->computeThisBinding(funbox->staticScope_);
 }
 
 JSFunction::AutoParseUsingFunctionBox::~AutoParseUsingFunctionBox()
 {
     fun_->unsetFunctionBox();
     fun_->initEnvironment(oldEnv_);
 }
 
@@ -114,74 +114,75 @@ MarkUsesAsHoistedLexical(ParseNode* pn)
     while ((pnu = *pnup) != nullptr && pnu->pn_blockid >= start) {
         MOZ_ASSERT(pnu->isUsed());
         pnu->pn_dflags |= PND_LEXICAL;
         pnup = &pnu->pn_link;
     }
 }
 
 void
-SharedContext::computeAllowSyntax(JSObject* staticScope)
+SharedContext::computeAllowSyntax(StaticScope* staticScope)
 {
     for (StaticScopeIter<CanGC> it(context, staticScope); !it.done(); it++) {
-        if (it.type() == StaticScopeIter<CanGC>::Function && !it.fun().isArrow()) {
+        if (it.type() == StaticScopeIter<CanGC>::Function && !it.fun().function().isArrow()) {
             // Any function supports new.target.
             allowNewTarget_ = true;
-            allowSuperProperty_ = it.fun().allowSuperProperty();
+            allowSuperProperty_ = it.fun().function().allowSuperProperty();
             if (it.maybeFunctionBox()) {
                 superScopeAlreadyNeedsHomeObject_ = it.maybeFunctionBox()->needsHomeObject();
                 allowSuperCall_ = it.maybeFunctionBox()->isDerivedClassConstructor();
             } else {
-                allowSuperCall_ = it.fun().isDerivedClassConstructor();
+                allowSuperCall_ = it.fun().function().isDerivedClassConstructor();
             }
             break;
         }
     }
 }
 
 void
-SharedContext::computeThisBinding(JSObject* staticScope)
+SharedContext::computeThisBinding(StaticScope* staticScope)
 {
     for (StaticScopeIter<CanGC> it(context, staticScope); !it.done(); it++) {
         if (it.type() == StaticScopeIter<CanGC>::Module) {
             thisBinding_ = ThisBinding::Module;
             return;
         }
 
         if (it.type() == StaticScopeIter<CanGC>::Function) {
+            RootedFunction fun(context, &it.fun().function());
             // Arrow functions and generator expression lambdas don't have
             // their own `this` binding.
-            if (it.fun().isArrow())
+            if (fun->isArrow())
                 continue;
             bool isDerived;
             if (it.maybeFunctionBox()) {
                 if (it.maybeFunctionBox()->inGenexpLambda)
                     continue;
                 isDerived = it.maybeFunctionBox()->isDerivedClassConstructor();
             } else {
-                if (it.fun().nonLazyScript()->isGeneratorExp())
+                if (fun->nonLazyScript()->isGeneratorExp())
                     continue;
-                isDerived = it.fun().isDerivedClassConstructor();
+                isDerived = fun->isDerivedClassConstructor();
             }
 
             // Derived class constructors (including nested arrow functions and
             // eval) need TDZ checks when accessing |this|.
             if (isDerived)
                 needsThisTDZChecks_ = true;
 
             thisBinding_ = ThisBinding::Function;
             return;
         }
     }
 
     thisBinding_ = ThisBinding::Global;
 }
 
 void
-SharedContext::computeInWith(JSObject* staticScope)
+SharedContext::computeInWith(StaticScope* staticScope)
 {
     for (StaticScopeIter<CanGC> it(context, staticScope); !it.done(); it++) {
         if (it.type() == StaticScopeIter<CanGC>::With) {
             inWith_ = true;
             break;
         }
     }
 }
@@ -190,26 +191,26 @@ void
 SharedContext::markSuperScopeNeedsHomeObject()
 {
     MOZ_ASSERT(allowSuperProperty());
 
     if (superScopeAlreadyNeedsHomeObject_)
         return;
 
     for (StaticScopeIter<CanGC> it(context, staticScope()); !it.done(); it++) {
-        if (it.type() == StaticScopeIter<CanGC>::Function && !it.fun().isArrow()) {
-            MOZ_ASSERT(it.fun().allowSuperProperty());
+        if (it.type() == StaticScopeIter<CanGC>::Function && !it.fun().function().isArrow()) {
+            MOZ_ASSERT(it.fun().function().allowSuperProperty());
             // If we are still emitting the outer function that needs a home
             // object, mark it as needing one. Otherwise, we must be emitting
             // an eval script, and the outer function must already be marked
             // as needing a home object since it contains an eval.
             if (it.maybeFunctionBox())
                 it.maybeFunctionBox()->setNeedsHomeObject();
             else
-                MOZ_ASSERT(it.fun().nonLazyScript()->needsHomeObject());
+                MOZ_ASSERT(it.funScript()->needsHomeObject());
             superScopeAlreadyNeedsHomeObject_ = true;
             return;
         }
     }
     MOZ_CRASH("Must have found an enclosing function box scope that allows super.property");
 }
 
 // See comment on member function declaration.
@@ -749,22 +750,22 @@ Parser<ParseHandler>::newObjectBox(JSObj
 
     traceListHead = objbox;
 
     return objbox;
 }
 
 template <typename ParseHandler>
 FunctionBox::FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunction* fun,
-                         JSObject* enclosingStaticScope, ParseContext<ParseHandler>* outerpc,
+                         ParseContext<ParseHandler>* outerpc,
                          Directives directives, bool extraWarnings, GeneratorKind generatorKind)
   : ObjectBox(fun, traceListHead),
     SharedContext(cx, directives, extraWarnings),
     bindings(),
-    enclosingStaticScope_(enclosingStaticScope),
+    staticScope_(nullptr),
     bufStart(0),
     bufEnd(0),
     startLine(1),
     startColumn(0),
     length(0),
     generatorKindBits_(GeneratorKindAsBits(generatorKind)),
     inGenexpLambda(false),
     hasDestructuringArgs(false),
@@ -777,44 +778,54 @@ FunctionBox::FunctionBox(ExclusiveContex
     funCxFlags()
 {
     // Functions created at parse time may be set singleton after parsing and
     // baked into JIT code, so they must be allocated tenured. They are held by
     // the JSScript so cannot be collected during a minor GC anyway.
     MOZ_ASSERT(fun->isTenured());
 }
 
+bool
+FunctionBox::initStaticScope(Handle<StaticScope*> enclosingScope)
+{
+    RootedFunction fun(context, function());
+    staticScope_ = StaticFunctionScope::create(context, fun, enclosingScope);
+    return staticScope_ != nullptr;
+}
+
 template <typename ParseHandler>
 FunctionBox*
 Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun,
                                      ParseContext<ParseHandler>* outerpc,
                                      Directives inheritedDirectives,
                                      GeneratorKind generatorKind,
-                                     JSObject* enclosingStaticScope)
+                                     Handle<StaticScope*> enclosingStaticScope)
 {
     MOZ_ASSERT_IF(outerpc, enclosingStaticScope == outerpc->innermostStaticScope());
     MOZ_ASSERT(fun);
 
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
      * arenas containing the entries must be alive until we are done with
      * scanning, parsing and code generation for the whole script or top-level
      * function.
      */
     FunctionBox* funbox =
-        alloc.new_<FunctionBox>(context, traceListHead, fun, enclosingStaticScope, outerpc,
-                                inheritedDirectives, options().extraWarningsOption,
-                                generatorKind);
+        alloc.new_<FunctionBox>(context, traceListHead, fun, outerpc, inheritedDirectives,
+                                options().extraWarningsOption, generatorKind);
     if (!funbox) {
         ReportOutOfMemory(context);
         return nullptr;
     }
-
     traceListHead = funbox;
+
+    if (!funbox->initStaticScope(enclosingStaticScope))
+        return nullptr;
+
     if (fn)
         handler.setFunctionBox(fn, funbox);
 
     return funbox;
 }
 
 template <typename ParseHandler>
 ModuleBox::ModuleBox(ExclusiveContext* cx, ObjectBox* traceListHead, ModuleObject* module,
@@ -1153,17 +1164,17 @@ Parser<SyntaxParseHandler>::defineFuncti
 
 template <>
 ParseNode*
 Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun,
                                                  Handle<PropertyNameVector> formals,
                                                  GeneratorKind generatorKind,
                                                  Directives inheritedDirectives,
                                                  Directives* newDirectives,
-                                                 HandleObject enclosingStaticScope)
+                                                 Handle<StaticScope*> enclosingStaticScope)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     Node fn = handler.newFunctionDefinition();
     if (!fn)
         return null();
 
     ParseNode* argsbody = handler.newList(PNK_ARGSBODY);
@@ -2839,17 +2850,19 @@ Parser<SyntaxParseHandler>::finishFuncti
 
     if (funbox->inWith())
         return abortIfSyntaxParser();
 
     size_t numFreeVariables = pc->lexdeps->count();
     size_t numInnerFunctions = pc->innerFunctions.length();
 
     RootedFunction fun(context, funbox->function());
-    LazyScript* lazy = LazyScript::CreateRaw(context, fun, numFreeVariables, numInnerFunctions,
+    Rooted<StaticFunctionScope*> funScope(context, funbox->staticScope());
+    LazyScript* lazy = LazyScript::CreateRaw(context, fun, funScope,
+                                             numFreeVariables, numInnerFunctions,
                                              versionNumber(), funbox->bufStart, funbox->bufEnd,
                                              funbox->startLine, funbox->startColumn);
     if (!lazy)
         return false;
 
     LazyScript::FreeVariable* freeVariables = lazy->freeVariables();
     size_t i = 0;
     for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront())
@@ -3042,17 +3055,17 @@ Parser<FullParseHandler>::standaloneLazy
     if (!pn)
         return null();
 
     // Our tokenStream has no current token, so pn's position is garbage.
     // Substitute the position of the first token in our source.
     if (!tokenStream.peekTokenPos(&pn->pn_pos))
         return null();
 
-    RootedObject enclosing(context, fun->lazyScript()->enclosingScope());
+    Rooted<StaticScope*> enclosing(context, fun->lazyScript()->enclosingScope());
     Directives directives(/* strict = */ strict);
     FunctionBox* funbox = newFunctionBox(pn, fun, directives, generatorKind, enclosing);
     if (!funbox)
         return null();
     funbox->length = fun->nargs() - fun->hasRest();
 
     if (fun->lazyScript()->isDerivedClassConstructor())
         funbox->setDerivedClassConstructor();
@@ -6496,18 +6509,21 @@ Parser<ParseHandler>::yieldExpression(In
 
     MOZ_CRASH("yieldExpr");
 }
 
 template <>
 ParseNode*
 Parser<FullParseHandler>::withStatement(YieldHandling yieldHandling)
 {
-    // test262/ch12/12.10/12.10-0-1.js fails if we try to parse with-statements
-    // in syntax-parse mode. See bug 892583.
+    // This is intentionally different from other abortIfSyntaxParser()
+    // bailouts: `with` statements rule out syntax-only parsing for the entire
+    // compilation unit. This `return null()` causes us to bail out all the way
+    // to BytecodeCompiler::compileScript(), which retries with syntax parsing
+    // disabled. See bug 892583.
     if (handler.syntaxParser) {
         handler.disableSyntaxParser();
         abortedSyntaxParse = true;
         return null();
     }
 
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH));
     uint32_t begin = pos().begin;
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -285,17 +285,17 @@ struct MOZ_STACK_CLASS ParseContext : pu
 
     bool init(Parser<ParseHandler>& parser);
 
     unsigned blockid() { return stmtStack.innermost() ? stmtStack.innermost()->blockid : bodyid; }
 
     StmtInfoPC* innermostStmt() const { return stmtStack.innermost(); }
     StmtInfoPC* innermostScopeStmt() const { return stmtStack.innermostScopeStmt(); }
     StmtInfoPC* innermostNonLabelStmt() const { return stmtStack.innermostNonLabel(); }
-    JSObject* innermostStaticScope() const {
+    StaticScope* innermostStaticScope() const {
         if (StmtInfoPC* stmt = innermostScopeStmt())
             return stmt->staticScope;
         return sc->staticScope();
     }
 
     // True if we are at the topmost level of a entire script or function body.
     // For example, while parsing this code we would encounter f1 and f2 at
     // body level, but we would not encounter f3 or f4 at body level:
@@ -305,19 +305,17 @@ struct MOZ_STACK_CLASS ParseContext : pu
     //
     bool atBodyLevel(StmtInfoPC* stmt) {
         // 'eval' and non-syntactic scripts are always under an invisible
         // lexical scope, but since it is not syntactic, it should still be
         // considered at body level.
         if (sc->staticScope()->is<StaticEvalScope>()) {
             bool bl = !stmt->enclosing;
             MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK);
-            MOZ_ASSERT_IF(bl, stmt->staticScope
-                                  ->template as<StaticBlockScope>()
-                                  .enclosingStaticScope() == sc->staticScope());
+            MOZ_ASSERT_IF(bl, stmt->staticScope->enclosingScope() == sc->staticScope());
             return bl;
         }
         return !stmt;
     }
 
     bool atBodyLevel() {
         return atBodyLevel(innermostStmt());
     }
@@ -502,44 +500,44 @@ class Parser : private JS::AutoGCRooter,
 
     /*
      * Allocate a new parsed object or function container from
      * cx->tempLifoAlloc.
      */
     ObjectBox* newObjectBox(JSObject* obj);
     FunctionBox* newFunctionBox(Node fn, JSFunction* fun, ParseContext<ParseHandler>* outerpc,
                                 Directives directives, GeneratorKind generatorKind,
-                                JSObject* enclosingStaticScope);
+                                Handle<StaticScope*> enclosingStaticScope);
 
     // Use when the funbox is the outermost.
     FunctionBox* newFunctionBox(Node fn, HandleFunction fun, Directives directives,
-                                GeneratorKind generatorKind, HandleObject enclosingStaticScope)
+                                GeneratorKind generatorKind, Handle<StaticScope*> enclosingStaticScope)
     {
         return newFunctionBox(fn, fun, nullptr, directives, generatorKind,
                               enclosingStaticScope);
     }
 
     // Use when the funbox should be linked to the outerpc's innermost scope.
     FunctionBox* newFunctionBox(Node fn, HandleFunction fun, ParseContext<ParseHandler>* outerpc,
                                 Directives directives, GeneratorKind generatorKind)
     {
-        RootedObject enclosing(context, outerpc->innermostStaticScope());
+        Rooted<StaticScope*> enclosing(context, outerpc->innermostStaticScope());
         return newFunctionBox(fn, fun, outerpc, directives, generatorKind, enclosing);
     }
 
     ModuleBox* newModuleBox(Node pn, HandleModuleObject module, ModuleBuilder& builder);
 
     /*
      * Create a new function object given a name (which is optional if this is
      * a function expression).
      */
     JSFunction* newFunction(HandleAtom atom, FunctionSyntaxKind kind, GeneratorKind generatorKind,
                             HandleObject proto);
 
-    bool generateBlockId(JSObject* staticScope, uint32_t* blockIdOut) {
+    bool generateBlockId(StaticScope* staticScope, uint32_t* blockIdOut) {
         if (blockScopes.length() == StmtInfoPC::BlockIdLimit) {
             tokenStream.reportError(JSMSG_NEED_DIET, "program");
             return false;
         }
         MOZ_ASSERT(blockScopes.length() < StmtInfoPC::BlockIdLimit);
         *blockIdOut = blockScopes.length();
         return blockScopes.append(staticScope);
     }
@@ -595,17 +593,17 @@ class Parser : private JS::AutoGCRooter,
     // Parse a module.
     Node standaloneModule(Handle<ModuleObject*> module, ModuleBuilder& builder);
 
     // Parse a function, given only its body. Used for the Function and
     // Generator constructors.
     Node standaloneFunctionBody(HandleFunction fun, Handle<PropertyNameVector> formals,
                                 GeneratorKind generatorKind,
                                 Directives inheritedDirectives, Directives* newDirectives,
-                                HandleObject enclosingStaticScope);
+                                Handle<StaticScope*> enclosingStaticScope);
 
     // Parse a function, given only its arguments and body. Used for lazily
     // parsed functions.
     Node standaloneLazyFunction(HandleFunction fun, bool strict, GeneratorKind generatorKind);
 
     /*
      * Parse a function body.  Pass StatementListBody if the body is a list of
      * statements; pass ExpressionBody if the body is a single expression.
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -224,20 +224,20 @@ class SharedContext
         superScopeAlreadyNeedsHomeObject_(false)
     { }
 
     // The unfortunate reason that staticScope() is a virtual is because
     // GlobalSharedContext and FunctionBox have different lifetimes.
     // GlobalSharedContexts are stack allocated and thus may use RootedObject
     // for the static scope. FunctionBoxes are LifoAlloc'd and need to
     // manually trace their static scope.
-    virtual JSObject* staticScope() const = 0;
-    void computeAllowSyntax(JSObject* staticScope);
-    void computeInWith(JSObject* staticScope);
-    void computeThisBinding(JSObject* staticScope);
+    virtual StaticScope* staticScope() const = 0;
+    void computeAllowSyntax(StaticScope* staticScope);
+    void computeInWith(StaticScope* staticScope);
+    void computeThisBinding(StaticScope* staticScope);
 
     virtual ObjectBox* toObjectBox() { return nullptr; }
     bool isObjectBox() { return toObjectBox() != nullptr; }
     bool isFunctionBox() { return isObjectBox() && toObjectBox()->isFunctionBox(); }
     inline FunctionBox* asFunctionBox();
     bool isModuleBox() { return isObjectBox() && toObjectBox()->isModuleBox(); }
     inline ModuleBox* asModuleBox();
     bool isGlobalContext() { return !toObjectBox(); }
@@ -295,29 +295,29 @@ class MOZ_STACK_CLASS GlobalSharedContex
     {
         computeAllowSyntax(staticScope);
         computeInWith(staticScope);
 
         // If we're executing a Debugger eval-in-frame, staticScope is always a
         // non-function scope, so we have to compute our ThisBinding based on
         // the actual callee.
         if (maybeEvalCaller)
-            computeThisBinding(maybeEvalCaller);
+            computeThisBinding(maybeEvalCaller->nonLazyScript()->staticScope());
         else
             computeThisBinding(staticScope);
     }
 
-    JSObject* staticScope() const override { return staticScope_; }
+    StaticScope* staticScope() const override { return staticScope_; }
 };
 
 class FunctionBox : public ObjectBox, public SharedContext
 {
   public:
     Bindings        bindings;               /* bindings for this function */
-    JSObject*       enclosingStaticScope_;
+    StaticFunctionScope* staticScope_;
     uint32_t        bufStart;
     uint32_t        bufEnd;
     uint32_t        startLine;
     uint32_t        startColumn;
     uint16_t        length;
 
     uint8_t         generatorKindBits_;     /* The GeneratorKind of this function. */
     bool            inGenexpLambda:1;       /* lambda from generator expression */
@@ -330,23 +330,25 @@ class FunctionBox : public ObjectBox, pu
     bool            usesArguments:1;  /* contains a free use of 'arguments' */
     bool            usesApply:1;      /* contains an f.apply() call */
     bool            usesThis:1;       /* contains 'this' */
 
     FunctionContextFlags funCxFlags;
 
     template <typename ParseHandler>
     FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunction* fun,
-                JSObject* enclosingStaticScope, ParseContext<ParseHandler>* pc,
-                Directives directives, bool extraWarnings, GeneratorKind generatorKind);
+                ParseContext<ParseHandler>* pc, Directives directives, bool extraWarnings,
+                GeneratorKind generatorKind);
+
+    bool initStaticScope(Handle<StaticScope*> enclosingScope);
 
     ObjectBox* toObjectBox() override { return this; }
     JSFunction* function() const { return &object->as<JSFunction>(); }
-    JSObject* staticScope() const override { return function(); }
-    JSObject* enclosingStaticScope() const { return enclosingStaticScope_; }
+    StaticFunctionScope* staticScope() const override { return staticScope_; }
+    StaticScope* enclosingStaticScope() const { return staticScope_->enclosingScope(); }
 
     GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
     bool isGenerator() const { return generatorKind() != NotGenerator; }
     bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
     bool isStarGenerator() const { return generatorKind() == StarGenerator; }
     bool isArrow() const { return function()->isArrow(); }
 
     void setGeneratorKind(GeneratorKind kind) {
@@ -422,17 +424,17 @@ class ModuleBox : public ObjectBox, publ
     ModuleBuilder& builder;
 
     template <typename ParseHandler>
     ModuleBox(ExclusiveContext* cx, ObjectBox* traceListHead, ModuleObject* module,
               ModuleBuilder& builder, ParseContext<ParseHandler>* pc);
 
     ObjectBox* toObjectBox() override { return this; }
     ModuleObject* module() const { return &object->as<ModuleObject>(); }
-    JSObject* staticScope() const override { return module(); }
+    StaticModuleScope* staticScope() const override { return module()->staticScope(); }
 
     void trace(JSTracer* trc) override;
 };
 
 inline FunctionBox*
 SharedContext::asFunctionBox()
 {
     MOZ_ASSERT(isFunctionBox());
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -950,18 +950,18 @@ LazyScript::traceChildren(JSTracer* trc)
         TraceWeakEdge(trc, &script_, "script");
 
     if (function_)
         TraceEdge(trc, &function_, "function");
 
     if (sourceObject_)
         TraceEdge(trc, &sourceObject_, "sourceObject");
 
-    if (enclosingScope_)
-        TraceEdge(trc, &enclosingScope_, "enclosingScope");
+    if (staticScope_)
+        TraceEdge(trc, &staticScope_, "staticScope");
 
     // We rely on the fact that atoms are always tenured.
     FreeVariable* freeVariables = this->freeVariables();
     for (auto i : MakeRange(numFreeVariables())) {
         JSAtom* atom = freeVariables[i].atom();
         TraceManuallyBarrieredEdge(trc, &atom, "lazyScriptFreeVariable");
     }
 
@@ -976,18 +976,18 @@ js::GCMarker::eagerlyMarkChildren(LazySc
         noteWeakEdge(thing->script_.unsafeUnbarrieredForTracing());
 
     if (thing->function_)
         traverseEdge(thing, static_cast<JSObject*>(thing->function_));
 
     if (thing->sourceObject_)
         traverseEdge(thing, static_cast<JSObject*>(thing->sourceObject_));
 
-    if (thing->enclosingScope_)
-        traverseEdge(thing, static_cast<JSObject*>(thing->enclosingScope_));
+    if (thing->staticScope_)
+        traverseEdge(thing, static_cast<JSObject*>(thing->staticScope_));
 
     // We rely on the fact that atoms are always tenured.
     LazyScript::FreeVariable* freeVariables = thing->freeVariables();
     for (auto i : MakeRange(thing->numFreeVariables()))
         traverseEdge(thing, static_cast<JSString*>(freeVariables[i].atom()));
 
     HeapPtrFunction* innerFunctions = thing->innerFunctions();
     for (auto i : MakeRange(thing->numInnerFunctions()))
--- a/js/src/gc/Policy.h
+++ b/js/src/gc/Policy.h
@@ -95,16 +95,18 @@ class JitCode;
     D(js::PlainObject*) \
     D(js::PropertyName*) \
     D(js::RegExpObject*) \
     D(js::SavedFrame*) \
     D(js::ScopeObject*) \
     D(js::ScriptSourceObject*) \
     D(js::Shape*) \
     D(js::SharedArrayBufferObject*) \
+    D(js::StaticFunctionScope*) \
+    D(js::StaticScope*) \
     D(js::StructTypeDescr*) \
     D(js::UnownedBaseShape*) \
     D(js::jit::JitCode*)
 
 // Expand the given macro D for each internal tagged GC pointer type.
 #define FOR_EACH_INTERNAL_TAGGED_GC_POINTER_TYPE(D) \
     D(js::TaggedProto)
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-findObjects-11.js
@@ -0,0 +1,7 @@
+// This shouldn't segfault.
+
+var g = newGlobal();
+g.eval(`function f() { return function() {
+  function g() {}
+}; }`);
+new Debugger(g).findObjects();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-isCompilableUnit.js
@@ -0,0 +1,50 @@
+load(libdir + "asserts.js");
+
+const bad_types = [
+    2112,
+    {geddy: "lee"},
+    () => 1,
+    [],
+    Array
+]
+
+// We only accept strings around here!
+for (var badType of bad_types) {
+    assertThrowsInstanceOf(() => {
+        Debugger.isCompilableUnit(badType);
+    }, TypeError);
+}
+
+const compilable_units = [
+   "wubba-lubba-dub-dub",
+   "'Get Schwifty!'",
+   "1 + 2",
+   "function f(x) {}",
+   "function x(f,) {", // statements with bad syntax are always compilable
+   "let x = 100",
+   ";;;;;;;;",
+   "",
+   " ",
+   "\n",
+   "let x",
+]
+
+const non_compilable_units = [
+    "function f(x) {",
+    "(...d) =>",
+    "{geddy:",
+    "{",
+    "[1, 2",
+    "[",
+    "1 +",
+    "let x =",
+    "3 ==",
+]
+
+for (var code of compilable_units) {
+    assertEq(Debugger.isCompilableUnit(code), true);
+}
+
+for (var code of non_compilable_units) {
+    assertEq(Debugger.isCompilableUnit(code), false);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/withStatementOffThread.js
@@ -0,0 +1,12 @@
+if (helperThreadCount() == 0)
+   quit();
+
+var code = `
+function f() {
+    [function() { { function g() { } } },
+     function() { with ({}) { } }];
+}
+`;
+
+offThreadCompileScript(code);
+runOffThreadScript();
--- a/js/src/jit-test/tests/xdr/scope.js
+++ b/js/src/jit-test/tests/xdr/scope.js
@@ -1,19 +1,63 @@
 load(libdir + 'bytecode-cache.js');
 var test = "";
 
+// code a nested function after calling it
+test = `
+    function f() {
+        function g() {
+            return [f, g];
+        }
+        return g();
+    }
+    f()
+`;
+evalWithCache(test, {
+    assertEqBytecode: true,
+    checkAfter(ctx) {
+        let [f, g] = ctx.global.f();
+        assertEq(f, ctx.global.f);
+        assertEq(f()[0], f);
+        assertEq(f()[1] === g, false); // second call, fresh g closure
+        assertEq(f()[1].toString(), g.toString()); // but the same source code
+        assertEq(g()[0], f);
+        assertEq(g()[1], g);
+    }
+});
+
+// code an unused function that contains an unused nested function
+test = `
+    function f() {
+        function g() {
+            return [f, g];
+        }
+        return g;
+    }
+    f
+`;
+evalWithCache(test, {
+    assertEqBytecode: true,
+    checkAfter(ctx) {
+        let f = ctx.global.f;
+        let g = f();
+        let [f1, g1] = g();
+        assertEq(f1, f);
+        assertEq(g1, g);
+    }
+});
+
 // code a function which has both used and unused inner functions.
 test  = (function () {
   function f() {
     var x = 3;
     (function() {
       with(obj) {
         (function() {
           assertEq(x, 2);
         })();
       }
     })();
   };
 
   return "var obj = { x : 2 };" + f.toSource() + "; f()";
 })();
-evalWithCache(test, { assertEqBytecode: true, assertEqResult : true });
+evalWithCache(test, { assertEqBytecode: true, assertEqResult: true });
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -3690,18 +3690,22 @@ jit::AnalyzeNewScriptDefiniteProperties(
 
     BaselineInspector inspector(script);
     const JitCompileOptions options(cx);
 
     IonBuilder builder(cx, CompileCompartment::get(cx->compartment()), options, &temp, &graph, constraints,
                        &inspector, &info, optimizationInfo, /* baselineFrame = */ nullptr);
 
     if (!builder.build()) {
-        if (cx->isThrowingOverRecursed() || builder.abortReason() == AbortReason_Alloc)
+        if (cx->isThrowingOverRecursed() ||
+            cx->isThrowingOutOfMemory() ||
+            builder.abortReason() == AbortReason_Alloc)
+        {
             return false;
+        }
         MOZ_ASSERT(!cx->isExceptionPending());
         return true;
     }
 
     FinishDefinitePropertiesAnalysis(cx, constraints);
 
     if (!SplitCriticalEdges(graph))
         return false;
--- a/js/src/jit/TypePolicy.h
+++ b/js/src/jit/TypePolicy.h
@@ -520,24 +520,16 @@ class ClampPolicy final : public TypePol
 
 class FilterTypeSetPolicy final : public TypePolicy
 {
   public:
     EMPTY_DATA_;
     virtual bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override;
 };
 
-static inline bool
-CoercesToDouble(MIRType type)
-{
-    if (type == MIRType_Undefined || IsFloatingPointType(type))
-        return true;
-    return false;
-}
-
 #undef SPECIALIZATION_DATA_
 #undef INHERIT_DATA_
 #undef EMPTY_DATA_
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_TypePolicy_h */
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3451,18 +3451,19 @@ CreateNonSyntacticScopeChain(JSContext* 
                              MutableHandle<StaticScope*> staticScopeObj)
 {
     Rooted<ClonedBlockObject*> globalLexical(cx, &cx->global()->lexicalScope());
     if (!js::CreateScopeObjectsForScopeChain(cx, scopeChain, globalLexical, dynamicScopeObj))
         return false;
 
     staticScopeObj.set(&globalLexical->staticBlock());
     if (!scopeChain.empty()) {
-        staticScopeObj.set(StaticNonSyntacticScope::create(cx, staticScopeObj));
-        if (!staticScopeObj)
+        Rooted<StaticNonSyntacticScope*> scope(cx,
+            StaticNonSyntacticScope::create(cx, staticScopeObj));
+        if (!scope)
             return false;
 
         // The XPConnect subscript loader, which may pass in its own dynamic
         // scopes to load scripts in, expects the dynamic scope chain to be
         // the holder of "var" declarations. In SpiderMonkey, such objects are
         // called "qualified varobjs", the "qualified" part meaning the
         // declaration was qualified by "var". There is only sadness.
         //
@@ -3473,51 +3474,52 @@ CreateNonSyntacticScopeChain(JSContext* 
         // Also get a non-syntactic lexical scope to capture 'let' and 'const'
         // bindings. To persist lexical bindings, we have a 1-1 mapping with
         // the final unwrapped dynamic scope object (the scope that stores the
         // 'var' bindings) and the lexical scope.
         //
         // TODOshu: disallow the subscript loader from using non-distinguished
         // objects as dynamic scopes.
         dynamicScopeObj.set(
-            cx->compartment()->getOrCreateNonSyntacticLexicalScope(cx, staticScopeObj,
-                                                                   dynamicScopeObj));
+            cx->compartment()->getOrCreateNonSyntacticLexicalScope(cx, scope, dynamicScopeObj));
         if (!dynamicScopeObj)
             return false;
+
+        staticScopeObj.set(scope);
     }
 
     return true;
 }
 
 static bool
 IsFunctionCloneable(HandleFunction fun)
 {
     if (!fun->isInterpreted())
         return true;
 
     // If a function was compiled to be lexically nested inside some other
     // script, we cannot clone it without breaking the compiler's assumptions.
-    if (JSObject* scope = fun->nonLazyScript()->enclosingStaticScope()) {
+    if (StaticScope* scope = fun->nonLazyScript()->enclosingStaticScope()) {
         // If the script is directly under the global scope, we can clone it.
         if (IsStaticGlobalLexicalScope(scope))
             return true;
 
         // If the script already deals with non-syntactic scopes, we can clone
         // it.
         if (scope->is<StaticNonSyntacticScope>())
             return true;
 
         // 'eval' scopes are always scoped immediately under a non-extensible
         // lexical scope.
         if (scope->is<StaticBlockScope>()) {
             StaticBlockScope& block = scope->as<StaticBlockScope>();
             if (block.needsClone())
                 return false;
 
-            JSObject* enclosing = block.enclosingStaticScope();
+            StaticScope* enclosing = block.enclosingScope();
 
             // If the script is an indirect eval that is immediately scoped
             // under the global, we can clone it.
             if (enclosing->is<StaticEvalScope>())
                 return !enclosing->as<StaticEvalScope>().isNonGlobal();
         }
 
         // Any other enclosing static scope (e.g., function, block) cannot be
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -500,17 +500,17 @@ JSCompartment::wrap(JSContext* cx, Mutab
             return false;
     }
 
     return wrap(cx, desc.value());
 }
 
 ClonedBlockObject*
 JSCompartment::getOrCreateNonSyntacticLexicalScope(JSContext* cx,
-                                                   HandleObject enclosingStatic,
+                                                   Handle<StaticNonSyntacticScope*> enclosingStatic,
                                                    HandleObject enclosingScope)
 {
     if (!nonSyntacticLexicalScopes_) {
         nonSyntacticLexicalScopes_ = cx->new_<ObjectWeakMap>(cx);
         if (!nonSyntacticLexicalScopes_ || !nonSyntacticLexicalScopes_->init())
             return nullptr;
     }
 
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -27,16 +27,17 @@ class JitCompartment;
 } // namespace jit
 
 namespace gc {
 template<class Node> class ComponentFinder;
 } // namespace gc
 
 struct NativeIterator;
 class ClonedBlockObject;
+class StaticNonSyntacticScope;
 
 /*
  * A single-entry cache for some base-10 double-to-string conversions. This
  * helps date-format-xparb.js.  It also avoids skewing the results for
  * v8-splay.js when measured by the SunSpider harness, where the splay tree
  * initialization (which includes many repeated double-to-string conversions)
  * is erroneously included in the measurement; see bug 562553.
  */
@@ -524,19 +525,20 @@ struct JSCompartment
     void removeWrapper(js::WrapperMap::Ptr p) {
         crossCompartmentWrappers.remove(p);
     }
 
     struct WrapperEnum : public js::WrapperMap::Enum {
         explicit WrapperEnum(JSCompartment* c) : js::WrapperMap::Enum(c->crossCompartmentWrappers) {}
     };
 
-    js::ClonedBlockObject* getOrCreateNonSyntacticLexicalScope(JSContext* cx,
-                                                               js::HandleObject enclosingStatic,
-                                                               js::HandleObject enclosingScope);
+    js::ClonedBlockObject* getOrCreateNonSyntacticLexicalScope(
+        JSContext* cx,
+        js::Handle<js::StaticNonSyntacticScope*> enclosingStatic,
+        js::HandleObject enclosingScope);
     js::ClonedBlockObject* getNonSyntacticLexicalScope(JSObject* enclosingScope) const;
 
     /*
      * This method traces data that is live iff we know that this compartment's
      * global is still live.
      */
     void trace(JSTracer* trc);
     /*
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -400,19 +400,19 @@ js::GetOutermostEnclosingFunctionOfScrip
 
     if (iter.done())
         return nullptr;
 
     if (!iter.isFunctionFrame())
         return nullptr;
 
     RootedFunction curr(cx, iter.callee(cx));
-    for (StaticScopeIter<NoGC> i(curr); !i.done(); i++) {
+    for (StaticScopeIter<NoGC> i(curr->nonLazyScript()->staticScope()); !i.done(); i++) {
         if (i.type() == StaticScopeIter<NoGC>::Function)
-            curr = &i.fun();
+            curr = &i.fun().function();
     }
     return curr;
 }
 
 JS_FRIEND_API(JSFunction*)
 js::DefineFunctionWithReserved(JSContext* cx, JSObject* objArg, const char* name, JSNative call,
                                unsigned nargs, unsigned attrs)
 {
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -37,16 +37,17 @@
 #include "jit/InlinableNatives.h"
 #include "jit/Ion.h"
 #include "jit/JitFrameIterator.h"
 #include "js/CallNonGenericMethod.h"
 #include "js/Proxy.h"
 #include "vm/Debugger.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
+#include "vm/ScopeObject.h"
 #include "vm/Shape.h"
 #include "vm/StringBuffer.h"
 #include "vm/WrapperObject.h"
 #include "vm/Xdr.h"
 
 #include "jsscriptinlines.h"
 
 #include "vm/Interpreter-inl.h"
@@ -530,18 +531,18 @@ fun_resolve(JSContext* cx, HandleObject 
         return true;
     }
 
     return true;
 }
 
 template<XDRMode mode>
 bool
-js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enclosingScript,
-                           MutableHandleFunction objp)
+js::XDRInterpretedFunction(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
+                           HandleScript enclosingScript, MutableHandleFunction objp)
 {
     enum FirstWordFlag {
         HasAtom             = 0x1,
         IsStarGenerator     = 0x2,
         IsLazy              = 0x4,
         HasSingletonType    = 0x8
     };
 
@@ -647,20 +648,22 @@ js::XDRInterpretedFunction(XDRState<mode
             return false;
         objp.set(fun);
     }
 
     return true;
 }
 
 template bool
-js::XDRInterpretedFunction(XDRState<XDR_ENCODE>*, HandleObject, HandleScript, MutableHandleFunction);
+js::XDRInterpretedFunction(XDRState<XDR_ENCODE>*, Handle<StaticScope*>, HandleScript,
+                           MutableHandleFunction);
 
 template bool
-js::XDRInterpretedFunction(XDRState<XDR_DECODE>*, HandleObject, HandleScript, MutableHandleFunction);
+js::XDRInterpretedFunction(XDRState<XDR_DECODE>*, Handle<StaticScope*>, HandleScript,
+                           MutableHandleFunction);
 
 /*
  * [[HasInstance]] internal method for Function objects: fetch the .prototype
  * property of its 'this' parameter, and walks the prototype chain of v (only
  * if v is an object) returning true if .prototype is found.
  */
 static bool
 fun_hasInstance(JSContext* cx, HandleObject objArg, MutableHandleValue v, bool* bp)
@@ -746,25 +749,24 @@ CreateFunctionConstructor(JSContext* cx,
     return functionCtor;
 
 }
 
 static JSObject*
 CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
 {
     Rooted<GlobalObject*> self(cx, cx->global());
+    RootedObject objectProto(cx, &self->getPrototype(JSProto_Object).toObject());
 
-    RootedObject objectProto(cx, &self->getPrototype(JSProto_Object).toObject());
-    /*
-     * Bizarrely, |Function.prototype| must be an interpreted function, so
-     * give it the guts to be one.
-     */
+    // Bizarrely, |Function.prototype| must be an interpreted function, so
+    // give it the guts to be one.
+    Rooted<ClonedBlockObject*> globalLexicalEnv(cx, &self->lexicalScope());
     JSObject* functionProto_ =
         NewFunctionWithProto(cx, nullptr, 0, JSFunction::INTERPRETED,
-                             self, nullptr, objectProto, AllocKind::FUNCTION,
+                             globalLexicalEnv, nullptr, objectProto, AllocKind::FUNCTION,
                              SingletonObject);
     if (!functionProto_)
         return nullptr;
 
     RootedFunction functionProto(cx, &functionProto_->as<JSFunction>());
 
     const char* rawSource = "() {\n}";
     size_t sourceLen = strlen(rawSource);
@@ -782,18 +784,22 @@ CreateFunctionPrototype(JSContext* cx, J
     ss->setSource(source, sourceLen);
     CompileOptions options(cx);
     options.setNoScriptRval(true)
            .setVersion(JSVERSION_DEFAULT);
     RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, ss));
     if (!sourceObject || !ScriptSourceObject::initFromOptions(cx, sourceObject, options))
         return nullptr;
 
+    Rooted<StaticScope*> globalScope(cx, &globalLexicalEnv->staticBlock());
+    Rooted<StaticScope*> funScope(cx, StaticFunctionScope::create(cx, functionProto, globalScope));
+    if (!funScope)
+        return nullptr;
     RootedScript script(cx, JSScript::Create(cx,
-                                             /* enclosingScope = */ nullptr,
+                                             funScope,
                                              /* savedCallerFun = */ false,
                                              options,
                                              sourceObject,
                                              0,
                                              ss->length()));
     if (!script || !JSScript::fullyInitTrivial(cx, script))
         return nullptr;
 
@@ -1440,17 +1446,17 @@ JSFunction::createScriptForLazilyInterpr
         // GCs, to avoid resurrecting dead scripts after incremental sweeping
         // has started.
         if (canRelazify && !JS::IsIncrementalGCInProgress(cx->runtime())) {
             LazyScriptCache::Lookup lookup(cx, lazy);
             cx->runtime()->lazyScriptCache.lookup(lookup, script.address());
         }
 
         if (script) {
-            RootedObject enclosingScope(cx, lazy->enclosingScope());
+            Rooted<StaticScope*> enclosingScope(cx, lazy->enclosingScope());
             RootedScript clonedScript(cx, CloneScriptIntoFunction(cx, enclosingScope, fun, script));
             if (!clonedScript)
                 return false;
 
             clonedScript->setSourceObject(lazy->sourceObject());
 
             fun->initAtom(script->functionNonDelazifying()->displayAtom());
 
@@ -2047,17 +2053,17 @@ js::CloneFunctionReuseScript(JSContext* 
      */
     if (fun->getProto() == clone->getProto())
         clone->setGroup(fun->group());
     return clone;
 }
 
 JSFunction*
 js::CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject parent,
-                           HandleObject newStaticScope,
+                           Handle<StaticScope*> newStaticScope,
                            gc::AllocKind allocKind /* = FUNCTION */,
                            HandleObject proto /* = nullptr */)
 {
     MOZ_ASSERT(NewFunctionScopeIsWellFormed(cx, parent));
     MOZ_ASSERT(!fun->isBoundFunction());
 
     JSScript::AutoDelazify funScript(cx);
     if (fun->isInterpreted()) {
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -723,17 +723,17 @@ extern JSFunction*
 CloneFunctionReuseScript(JSContext* cx, HandleFunction fun, HandleObject parent,
                          gc::AllocKind kind = gc::AllocKind::FUNCTION,
                          NewObjectKind newKindArg = GenericObject,
                          HandleObject proto = nullptr);
 
 // Functions whose scripts are cloned are always given singleton types.
 extern JSFunction*
 CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject parent,
-                       HandleObject newStaticScope,
+                       Handle<StaticScope*> newStaticScope,
                        gc::AllocKind kind = gc::AllocKind::FUNCTION,
                        HandleObject proto = nullptr);
 
 extern bool
 FindBody(JSContext* cx, HandleFunction fun, HandleLinearString src, size_t* bodyStart,
          size_t* bodyEnd);
 
 } // namespace js
@@ -784,17 +784,17 @@ JSFunction::getExtendedSlot(size_t which
 }
 
 namespace js {
 
 JSString* FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen);
 
 template<XDRMode mode>
 bool
-XDRInterpretedFunction(XDRState<mode>* xdr, HandleObject enclosingScope,
+XDRInterpretedFunction(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
                        HandleScript enclosingScript, MutableHandleFunction objp);
 
 /*
  * Report an error that call.thisv is not compatible with the specified class,
  * assuming that the method (clasp->name).prototype.<name of callee function>
  * is what was called.
  */
 extern void
--- a/js/src/jsfuninlines.h
+++ b/js/src/jsfuninlines.h
@@ -87,15 +87,15 @@ CloneFunctionObjectIfNotSingleton(JSCont
                          : finalizeKind;
 
     if (CanReuseScriptForClone(cx->compartment(), fun, parent))
         return CloneFunctionReuseScript(cx, fun, parent, kind, newKind, proto);
 
     RootedScript script(cx, fun->getOrCreateScript(cx));
     if (!script)
         return nullptr;
-    RootedObject staticScope(cx, script->enclosingStaticScope());
+    Rooted<StaticScope*> staticScope(cx, script->enclosingStaticScope());
     return CloneFunctionAndScript(cx, fun, parent, staticScope, kind, proto);
 }
 
 } /* namespace js */
 
 #endif /* jsfuninlines_h */
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3405,16 +3405,17 @@ GCRuntime::sweepBackgroundThings(ZoneLis
     // We must finalize thing kinds in the order specified by BackgroundFinalizePhases.
     ArenaHeader* emptyArenas = nullptr;
     FreeOp fop(rt, threadType);
     for (unsigned phase = 0 ; phase < ArrayLength(BackgroundFinalizePhases) ; ++phase) {
         for (Zone* zone = zones.front(); zone; zone = zone->nextZone()) {
             for (unsigned index = 0 ; index < BackgroundFinalizePhases[phase].length ; ++index) {
                 AllocKind kind = BackgroundFinalizePhases[phase].kinds[index];
                 ArenaHeader* arenas = zone->arenas.arenaListsToSweep[kind];
+                MOZ_RELEASE_ASSERT(uintptr_t(arenas) != uintptr_t(-1));
                 if (arenas)
                     ArenaLists::backgroundFinalize(&fop, arenas, &emptyArenas);
             }
         }
     }
 
     AutoLockGC lock(rt);
     ReleaseArenaList(rt, emptyArenas, lock);
@@ -6826,17 +6827,17 @@ gc::MergeCompartments(JSCompartment* sou
     // be in the source zone
     rt->gc.releaseHeldRelocatedArenas();
 
     // Fixup compartment pointers in source to refer to target, and make sure
     // type information generations are in sync.
 
     // Get the static global lexical scope of the target compartment. Static
     // scopes need to be fixed up below.
-    RootedObject targetStaticGlobalLexicalScope(rt);
+    Rooted<StaticScope*> targetStaticGlobalLexicalScope(rt);
     targetStaticGlobalLexicalScope = &target->maybeGlobal()->lexicalScope().staticBlock();
 
     for (ZoneCellIter iter(source->zone(), AllocKind::SCRIPT); !iter.done(); iter.next()) {
         JSScript* script = iter.get<JSScript>();
         MOZ_ASSERT(script->compartment() == source);
         script->compartment_ = target;
         script->setTypesGeneration(target->zone()->types.generation);
 
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -589,17 +589,17 @@ class ArenaLists
      * only for the arena that was not fully allocated.
      */
     AllAllocKindArray<FreeList> freeLists;
 
     AllAllocKindArray<ArenaList> arenaLists;
 
     enum BackgroundFinalizeStateEnum { BFS_DONE, BFS_RUN };
 
-    typedef mozilla::Atomic<BackgroundFinalizeStateEnum, mozilla::ReleaseAcquire>
+    typedef mozilla::Atomic<BackgroundFinalizeStateEnum, mozilla::SequentiallyConsistent>
         BackgroundFinalizeState;
 
     /* The current background finalization state, accessed atomically. */
     AllAllocKindArray<BackgroundFinalizeState> backgroundFinalizeState;
 
     /* For each arena kind, a list of arenas remaining to be swept. */
     AllAllocKindArray<ArenaHeader*> arenaListsToSweep;
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -585,19 +585,17 @@ ToPropertyKey(JSContext* cx, Value argum
  * Return true if this is a compiler-created internal function accessed by
  * its own object. Such a function object must not be accessible to script
  * or embedding code.
  */
 inline bool
 IsInternalFunctionObject(JSObject& funobj)
 {
     JSFunction& fun = funobj.as<JSFunction>();
-    MOZ_ASSERT_IF(fun.isLambda(),
-                  fun.isInterpreted() || fun.isAsmJSNative());
-    return fun.isLambda() && fun.isInterpreted() && !fun.environment();
+    return fun.isInterpreted() && !fun.environment();
 }
 
 /*
  * Make an object with the specified prototype. If parent is null, it will
  * default to the prototype's global if the prototype is non-null.
  */
 JSObject*
 NewObjectWithGivenTaggedProto(ExclusiveContext* cx, const Class* clasp, Handle<TaggedProto> proto,
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -511,17 +511,17 @@ XDRLazyFreeVariables(XDRState<mode>* xdr
 
     return true;
 }
 
 // Code the missing part needed to re-create a LazyScript from a JSScript.
 template<XDRMode mode>
 static bool
 XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript script,
-                      HandleObject enclosingScope, MutableHandle<LazyScript*> lazy)
+                      Handle<StaticFunctionScope*> funScope, MutableHandle<LazyScript*> lazy)
 {
     MOZ_ASSERT_IF(mode == XDR_ENCODE, script->isRelazifiable() && script->maybeLazyScript());
     MOZ_ASSERT_IF(mode == XDR_ENCODE, !lazy->numInnerFunctions());
 
     JSContext* cx = xdr->cx();
 
     uint64_t packedFields;
     {
@@ -541,18 +541,18 @@ XDRRelazificationInfo(XDRState<mode>* xd
             // JSFunction::createScriptForLazilyInterpretedFunction.
             MOZ_ASSERT(lazy->numInnerFunctions() == 0);
         }
 
         if (!xdr->codeUint64(&packedFields))
             return false;
 
         if (mode == XDR_DECODE) {
-            lazy.set(LazyScript::Create(cx, fun, script, enclosingScope, script,
-                                        packedFields, begin, end, lineno, column));
+            lazy.set(LazyScript::Create(cx, fun, script, funScope, script, packedFields,
+                                        begin, end, lineno, column));
 
             // As opposed to XDRLazyScript, we need to restore the runtime bits
             // of the script, as we are trying to match the fact this function
             // has already been parsed and that it would need to be re-lazified.
             lazy->initRuntimeFields(packedFields);
         }
     }
 
@@ -587,18 +587,18 @@ enum XDRClassKind {
     CK_BlockObject = 0,
     CK_WithObject  = 1,
     CK_JSFunction  = 2,
     CK_JSObject    = 3
 };
 
 template<XDRMode mode>
 bool
-js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScopeArg, HandleScript enclosingScript,
-              HandleFunction fun, MutableHandleScript scriptp)
+js::XDRScript(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScopeArg,
+              HandleScript enclosingScript, HandleFunction fun, MutableHandleScript scriptp)
 {
     /* NB: Keep this in sync with CopyScript. */
 
     MOZ_ASSERT(enclosingScopeArg);
 
     enum ScriptBits {
         NoScriptRval,
         SavedCallerFun,
@@ -632,17 +632,17 @@ js::XDRScript(XDRState<mode>* xdr, Handl
     uint32_t nconsts, nobjects, nregexps, ntrynotes, nblockscopes, nyieldoffsets;
     uint32_t prologueLength, version;
     uint32_t funLength = 0;
     uint32_t nTypeSets = 0;
     uint32_t scriptBits = 0;
 
     JSContext* cx = xdr->cx();
     RootedScript script(cx);
-    RootedObject enclosingScope(cx, enclosingScopeArg);
+    Rooted<StaticScope*> enclosingScope(cx, enclosingScopeArg);
     natoms = nsrcnotes = 0;
     nconsts = nobjects = nregexps = ntrynotes = nblockscopes = nyieldoffsets = 0;
 
     /* XDR arguments and vars. */
     uint16_t nargs = 0;
     uint16_t nblocklocals = 0;
     uint16_t nbodylevellexicals = 0;
     uint32_t nvars = 0;
@@ -844,16 +844,22 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         if (scriptBits & (1 << HasNonSyntacticScope) &&
             IsStaticGlobalLexicalScope(enclosingScope))
         {
             enclosingScope = StaticNonSyntacticScope::create(cx, enclosingScope);
             if (!enclosingScope)
                 return false;
         }
 
+        if (fun) {
+            enclosingScope = StaticFunctionScope::create(cx, fun, enclosingScope);
+            if (!enclosingScope)
+                return false;
+        }
+
         script = JSScript::Create(cx, enclosingScope, !!(scriptBits & (1 << SavedCallerFun)),
                                   options, sourceObject, 0, 0);
         if (!script)
             return false;
 
         // Set the script in its function now so that inner scripts to be
         // decoded may iterate the static scope chain.
         if (fun) {
@@ -1040,49 +1046,54 @@ js::XDRScript(XDRState<mode>* xdr, Handl
                     else
                         enclosingStaticScopeIndex = FindScopeObjectIndex(script, *enclosing);
                 } else {
                     enclosingStaticScopeIndex = UINT32_MAX;
                 }
             }
             if (!xdr->codeUint32(&enclosingStaticScopeIndex))
                 return false;
-            Rooted<JSObject*> enclosingStaticScope(cx);
+            Rooted<StaticScope*> enclosingStaticScope(cx);
             if (mode == XDR_DECODE) {
                 if (enclosingStaticScopeIndex != UINT32_MAX) {
                     MOZ_ASSERT(enclosingStaticScopeIndex < i);
-                    enclosingStaticScope = script->objects()->vector[enclosingStaticScopeIndex];
+                    enclosingStaticScope = &script->objects()->vector[enclosingStaticScopeIndex]
+                                                             ->as<StaticScope>();
                 } else {
                     // This is not ternary because MSVC can't typecheck the
                     // ternary.
                     if (fun)
-                        enclosingStaticScope = fun;
+                        enclosingStaticScope = script->staticScope();
                     else
                         enclosingStaticScope = enclosingScope;
                 }
             }
 
             if (classk == CK_BlockObject) {
-                Rooted<StaticBlockScope*> tmp(cx, static_cast<StaticBlockScope*>(objp->get()));
+                Rooted<StaticBlockScope*> tmp(cx);
+                if (mode == XDR_ENCODE)
+                    tmp = &(*objp)->as<StaticBlockScope>();
                 if (!XDRStaticBlockScope(xdr, enclosingStaticScope, &tmp))
                     return false;
                 *objp = tmp;
             } else {
-                Rooted<StaticWithScope*> tmp(cx, static_cast<StaticWithScope*>(objp->get()));
+                Rooted<StaticWithScope*> tmp(cx);
+                if (mode == XDR_ENCODE)
+                    tmp = &(*objp)->as<StaticWithScope>();
                 if (!XDRStaticWithScope(xdr, enclosingStaticScope, &tmp))
                     return false;
                 *objp = tmp;
             }
             break;
           }
 
           case CK_JSFunction: {
             /* Code the nested function's enclosing scope. */
             uint32_t funEnclosingScopeIndex = 0;
-            RootedObject funEnclosingScope(cx);
+            Rooted<StaticScope*> funEnclosingScope(cx);
             if (mode == XDR_ENCODE) {
                 RootedFunction function(cx, &(*objp)->as<JSFunction>());
 
                 if (function->isInterpretedLazy())
                     funEnclosingScope = function->lazyScript()->enclosingScope();
                 else if (function->isInterpreted())
                     funEnclosingScope = function->nonLazyScript()->enclosingStaticScope();
                 else {
@@ -1119,22 +1130,23 @@ js::XDRScript(XDRState<mode>* xdr, Handl
             if (!xdr->codeUint32(&funEnclosingScopeIndex))
                 return false;
 
             if (mode == XDR_DECODE) {
                 if (funEnclosingScopeIndex == UINT32_MAX) {
                     // This is not ternary because MSVC can't typecheck the
                     // ternary.
                     if (fun)
-                        funEnclosingScope = fun;
+                        funEnclosingScope = script->staticScope();
                     else
                         funEnclosingScope = enclosingScope;
                 } else {
                     MOZ_ASSERT(funEnclosingScopeIndex < i);
-                    funEnclosingScope = script->objects()->vector[funEnclosingScopeIndex];
+                    funEnclosingScope = &script->objects()->vector[funEnclosingScopeIndex]
+                                                          .get()->as<StaticScope>();
                 }
             }
 
             // Code nested function and script.
             RootedFunction tmp(cx);
             if (mode == XDR_ENCODE)
                 tmp = &(*objp)->as<JSFunction>();
             if (!XDRInterpretedFunction(xdr, funEnclosingScope, script, &tmp))
@@ -1201,17 +1213,20 @@ js::XDRScript(XDRState<mode>* xdr, Handl
             return false;
     }
 
     if (scriptBits & (1 << HasLazyScript)) {
         Rooted<LazyScript*> lazy(cx);
         if (mode == XDR_ENCODE)
             lazy = script->maybeLazyScript();
 
-        if (!XDRRelazificationInfo(xdr, fun, script, enclosingScope, &lazy))
+        Rooted<StaticFunctionScope*> lazyScope(cx, mode == XDR_DECODE
+                                                   ? &enclosingScope->as<StaticFunctionScope>()
+                                                   : nullptr);
+        if (!XDRRelazificationInfo(xdr, fun, script, lazyScope, &lazy))
             return false;
 
         if (mode == XDR_DECODE)
             script->setLazyScript(lazy);
     }
 
     if (mode == XDR_DECODE) {
         scriptp.set(script);
@@ -1220,27 +1235,28 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         if (!fun)
             Debugger::onNewScript(cx, script);
     }
 
     return true;
 }
 
 template bool
-js::XDRScript(XDRState<XDR_ENCODE>*, HandleObject, HandleScript, HandleFunction,
+js::XDRScript(XDRState<XDR_ENCODE>*, Handle<StaticScope*>, HandleScript, HandleFunction,
               MutableHandleScript);
 
 template bool
-js::XDRScript(XDRState<XDR_DECODE>*, HandleObject, HandleScript, HandleFunction,
+js::XDRScript(XDRState<XDR_DECODE>*, Handle<StaticScope*>, HandleScript, HandleFunction,
               MutableHandleScript);
 
 template<XDRMode mode>
 bool
-js::XDRLazyScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enclosingScript,
-                  HandleFunction fun, MutableHandle<LazyScript*> lazy)
+js::XDRLazyScript(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
+                  HandleScript enclosingScript, HandleFunction fun,
+                  MutableHandle<LazyScript*> lazy)
 {
     JSContext* cx = xdr->cx();
 
     {
         uint32_t begin;
         uint32_t end;
         uint32_t lineno;
         uint32_t column;
@@ -1263,54 +1279,59 @@ js::XDRLazyScript(XDRState<mode>* xdr, H
         if (!xdr->codeUint32(&begin) || !xdr->codeUint32(&end) ||
             !xdr->codeUint32(&lineno) || !xdr->codeUint32(&column) ||
             !xdr->codeUint64(&packedFields))
         {
             return false;
         }
 
         if (mode == XDR_DECODE) {
-            lazy.set(LazyScript::Create(cx, fun, nullptr, enclosingScope, enclosingScript,
+            Rooted<StaticFunctionScope*> funScope(cx,
+                StaticFunctionScope::create(cx, fun, enclosingScope));
+            if (!funScope)
+                return false;
+            lazy.set(LazyScript::Create(cx, fun, nullptr, funScope, enclosingScript,
                                         packedFields, begin, end, lineno, column));
             if (!lazy)
                 return false;
             fun->initLazyScript(lazy);
         }
     }
 
     // Code free variables.
     if (!XDRLazyFreeVariables(xdr, lazy))
         return false;
 
     // Code inner functions.
     {
         RootedFunction func(cx);
+        Rooted<StaticFunctionScope*> funScope(cx, lazy->staticScope());
         HeapPtrFunction* innerFunctions = lazy->innerFunctions();
         size_t numInnerFunctions = lazy->numInnerFunctions();
         for (size_t i = 0; i < numInnerFunctions; i++) {
             if (mode == XDR_ENCODE)
                 func = innerFunctions[i];
 
-            if (!XDRInterpretedFunction(xdr, fun, enclosingScript, &func))
+            if (!XDRInterpretedFunction(xdr, funScope, enclosingScript, &func))
                 return false;
 
             if (mode == XDR_DECODE)
                 innerFunctions[i] = func;
         }
     }
 
     return true;
 }
 
 template bool
-js::XDRLazyScript(XDRState<XDR_ENCODE>*, HandleObject, HandleScript,
+js::XDRLazyScript(XDRState<XDR_ENCODE>*, Handle<StaticScope*>, HandleScript,
                   HandleFunction, MutableHandle<LazyScript*>);
 
 template bool
-js::XDRLazyScript(XDRState<XDR_DECODE>*, HandleObject, HandleScript,
+js::XDRLazyScript(XDRState<XDR_DECODE>*, Handle<StaticScope*>, HandleScript,
                   HandleFunction, MutableHandle<LazyScript*>);
 
 void
 JSScript::setSourceObject(JSObject* object)
 {
     MOZ_ASSERT(compartment() == object->compartment());
     sourceObject_ = object;
 }
@@ -2742,41 +2763,43 @@ ScriptDataSize(uint32_t nbindings, uint3
 
 void
 JSScript::initCompartment(ExclusiveContext* cx)
 {
     compartment_ = cx->compartment_;
 }
 
 /* static */ JSScript*
-JSScript::Create(ExclusiveContext* cx, HandleObject enclosingScope, bool savedCallerFun,
+JSScript::Create(ExclusiveContext* cx, Handle<StaticScope*> staticScope, bool savedCallerFun,
                  const ReadOnlyCompileOptions& options, HandleObject sourceObject,
                  uint32_t bufStart, uint32_t bufEnd)
 {
     MOZ_ASSERT(bufStart <= bufEnd);
 
     RootedScript script(cx, Allocate<JSScript>(cx));
     if (!script)
         return nullptr;
 
     PodZero(script.get());
     new (&script->bindings) Bindings;
 
-    script->enclosingStaticScope_ = enclosingScope;
+    script->staticScope_ = staticScope;
     script->savedCallerFun_ = savedCallerFun;
     script->initCompartment(cx);
 
     script->selfHosted_ = options.selfHostingMode;
     script->noScriptRval_ = options.noScriptRval;
     script->treatAsRunOnce_ = options.isRunOnce;
 
-    // Compute whether this script is under a non-syntactic scope. We don't
-    // need to walk the entire static scope chain if the script is nested in a
-    // function. In that case, we can propagate the cached value from the
-    // outer script.
+    // Compute whether this script is under a non-syntactic scope, passing
+    // staticScope->enclosingScope() in a case where staticScope itself is not
+    // a non-syntactic scope and may not be fully initialized yet.
+    Rooted<StaticScope*> enclosingScope(cx, staticScope);
+    if (staticScope && staticScope->is<StaticFunctionScope>())
+        enclosingScope = staticScope->enclosingScope();
     script->hasNonSyntacticScope_ = HasNonSyntacticStaticScopeChain(enclosingScope);
 
     script->version = options.version;
     MOZ_ASSERT(script->getVersion() == options.version);     // assert that no overflow occurred
 
     script->setSourceObject(sourceObject);
     script->sourceStart_ = bufStart;
     script->sourceEnd_ = bufEnd;
@@ -3118,25 +3141,30 @@ js::GlobalObject&
 JSScript::uninlinedGlobal() const
 {
     return global();
 }
 
 void
 JSScript::fixEnclosingStaticGlobalLexicalScope()
 {
-    MOZ_ASSERT(IsStaticGlobalLexicalScope(enclosingStaticScope_));
-    enclosingStaticScope_ = &global().lexicalScope().staticBlock();
+    if (function_) {
+        MOZ_ASSERT(IsStaticGlobalLexicalScope(staticScope_->enclosingScope()));
+        staticScope_->setEnclosingScope(&global().lexicalScope().staticBlock());
+    } else {
+        MOZ_ASSERT(IsStaticGlobalLexicalScope(staticScope_));
+        staticScope_ = &global().lexicalScope().staticBlock();
+    }
 }
 
 void
 LazyScript::fixEnclosingStaticGlobalLexicalScope()
 {
-    MOZ_ASSERT(IsStaticGlobalLexicalScope(enclosingScope_));
-    enclosingScope_ = &function_->global().lexicalScope().staticBlock();
+    MOZ_ASSERT(IsStaticGlobalLexicalScope(staticScope_->enclosingScope()));
+    staticScope_->setEnclosingScope(&function_->global().lexicalScope().staticBlock());
 }
 
 void
 JSScript::finalize(FreeOp* fop)
 {
     // NOTE: this JSScript may be partially initialized at this point.  E.g. we
     // may have created it and partially initialized it with
     // JSScript::Create(), but not yet finished initializing it with
@@ -3404,17 +3432,18 @@ template <class T>
 static inline T*
 Rebase(JSScript* dst, JSScript* src, T* srcp)
 {
     size_t off = reinterpret_cast<uint8_t*>(srcp) - src->data;
     return reinterpret_cast<T*>(dst->data + off);
 }
 
 static JSObject*
-CloneInnerInterpretedFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction srcFun)
+CloneInnerInterpretedFunction(JSContext* cx, Handle<StaticScope*> enclosingScope,
+                              HandleFunction srcFun)
 {
     /* NB: Keep this in sync with XDRInterpretedFunction. */
     RootedObject cloneProto(cx);
     if (srcFun->isStarGenerator()) {
         cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
         if (!cloneProto)
             return nullptr;
     }
@@ -3444,17 +3473,17 @@ CloneInnerInterpretedFunction(JSContext*
 
     if (!JSFunction::setTypeForScriptedFunction(cx, clone))
         return nullptr;
 
     return clone;
 }
 
 bool
-js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScript src,
+js::detail::CopyScript(JSContext* cx, Handle<StaticScope*> scriptStaticScope, HandleScript src,
                        HandleScript dst)
 {
     if (src->treatAsRunOnce() && !src->functionNonDelazifying()) {
         // Toplevel run-once scripts may not be cloned.
         JS_ReportError(cx, "No cloning toplevel run-once scripts");
         return false;
     }
 
@@ -3491,24 +3520,25 @@ js::detail::CopyScript(JSContext* cx, Ha
     if (nobjects != 0) {
         HeapPtrObject* vector = src->objects()->vector;
         for (unsigned i = 0; i < nobjects; i++) {
             RootedObject obj(cx, vector[i]);
             RootedObject clone(cx);
             if (obj->is<NestedStaticScope>()) {
                 Rooted<NestedStaticScope*> innerBlock(cx, &obj->as<NestedStaticScope>());
 
-                RootedObject enclosingScope(cx);
+                Rooted<StaticScope*> enclosingScope(cx);
                 if (NestedStaticScope* enclosingBlock = innerBlock->enclosingNestedScope()) {
                     if (IsStaticGlobalLexicalScope(enclosingBlock)) {
                         MOZ_ASSERT(IsStaticGlobalLexicalScope(scriptStaticScope) ||
                                    scriptStaticScope->is<StaticNonSyntacticScope>());
                         enclosingScope = scriptStaticScope;
                     } else {
-                        enclosingScope = objects[FindScopeObjectIndex(src, *enclosingBlock)];
+                        enclosingScope = &objects[FindScopeObjectIndex(src, *enclosingBlock)]
+                                                 .get()->as<StaticScope>();
                     }
                 } else {
                     enclosingScope = scriptStaticScope;
                 }
 
                 clone = CloneNestedScopeObject(cx, enclosingScope, innerBlock);
             } else if (obj->is<JSFunction>()) {
                 RootedFunction innerFun(cx, &obj->as<JSFunction>());
@@ -3520,34 +3550,37 @@ js::detail::CopyScript(JSContext* cx, Ha
                     }
                     clone = innerFun;
                 } else {
                     if (innerFun->isInterpretedLazy()) {
                         AutoCompartment ac(cx, innerFun);
                         if (!innerFun->getOrCreateScript(cx))
                             return false;
                     }
-                    RootedObject staticScope(cx, innerFun->nonLazyScript()->enclosingStaticScope());
+                    Rooted<StaticScope*> staticScope(cx, innerFun->nonLazyScript()
+                                                                 ->enclosingStaticScope());
                     StaticScopeIter<CanGC> ssi(cx, staticScope);
-                    RootedObject enclosingScope(cx);
+                    Rooted<StaticScope*> enclosingScope(cx);
                     if (ssi.done() || ssi.type() == StaticScopeIter<CanGC>::NonSyntactic) {
                         enclosingScope = scriptStaticScope;
                     } else if (ssi.type() == StaticScopeIter<CanGC>::Function) {
-                        MOZ_ASSERT(scriptStaticScope->is<JSFunction>());
+                        MOZ_ASSERT(scriptStaticScope->is<StaticFunctionScope>());
                         enclosingScope = scriptStaticScope;
                     } else if (ssi.type() == StaticScopeIter<CanGC>::Block) {
                         if (ssi.block().isGlobal()) {
                             MOZ_ASSERT(IsStaticGlobalLexicalScope(scriptStaticScope) ||
                                        scriptStaticScope->is<StaticNonSyntacticScope>());
                             enclosingScope = scriptStaticScope;
                         } else {
-                            enclosingScope = objects[FindScopeObjectIndex(src, ssi.block())];
+                            enclosingScope = &objects[FindScopeObjectIndex(src, ssi.block())]
+                                                     .get()->as<StaticBlockScope>();
                         }
                     } else {
-                        enclosingScope = objects[FindScopeObjectIndex(src, ssi.staticWith())];
+                        enclosingScope = &objects[FindScopeObjectIndex(src, ssi.staticWith())]
+                                                 .get()->as<StaticWithScope>();
                     }
 
                     clone = CloneInnerInterpretedFunction(cx, enclosingScope, innerFun);
                 }
             } else {
                 clone = DeepCloneObjectLiteral(cx, obj, TenuredObject);
             }
             if (!clone || !objects.append(clone))
@@ -3651,17 +3684,17 @@ js::detail::CopyScript(JSContext* cx, Ha
      * non-syntactic global scope, make sure delazification can deal.
      */
     MOZ_ASSERT_IF(dst->hasNonSyntacticScope(), !dst->maybeLazyScript());
     MOZ_ASSERT_IF(dst->hasNonSyntacticScope(), !dst->isRelazifiable());
     return true;
 }
 
 static JSScript*
-CreateEmptyScriptForClone(JSContext* cx, HandleObject enclosingScope, HandleScript src)
+CreateEmptyScriptForClone(JSContext* cx, Handle<StaticScope*> enclosingScope, HandleScript src)
 {
     /*
      * Wrap the script source object as needed. Self-hosted scripts may be
      * in another runtime, so lazily create a new script source object to
      * use for them.
      */
     RootedObject sourceObject(cx);
     if (cx->runtime()->isSelfHostingCompartment(src->compartment())) {
@@ -3703,44 +3736,49 @@ js::CloneGlobalScript(JSContext* cx, Han
 
     if (!detail::CopyScript(cx, enclosingScope, src, dst))
         return nullptr;
 
     return dst;
 }
 
 JSScript*
-js::CloneScriptIntoFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction fun,
+js::CloneScriptIntoFunction(JSContext* cx, Handle<StaticScope*> enclosingScope, HandleFunction fun,
                             HandleScript src)
 {
     MOZ_ASSERT(fun->isInterpreted());
 
+    Rooted<StaticFunctionScope*> funScope(cx, StaticFunctionScope::create(cx, fun,
+                                                                          enclosingScope));
+    if (!funScope)
+        return nullptr;
+
     // Allocate the destination script up front and set it as the script of
     // |fun|, which is to be its container.
     //
     // This is so that when cloning nested functions, they can walk the static
     // scope chain via fun and correctly compute the presence of a
     // non-syntactic global.
-    RootedScript dst(cx, CreateEmptyScriptForClone(cx, enclosingScope, src));
+    RootedScript dst(cx, CreateEmptyScriptForClone(cx, funScope, src));
     if (!dst)
         return nullptr;
 
     // Save flags in case we need to undo the early mutations.
     const int preservedFlags = fun->flags();
 
     dst->setFunction(fun);
     Rooted<LazyScript*> lazy(cx);
     if (fun->isInterpretedLazy()) {
         lazy = fun->lazyScriptOrNull();
         fun->setUnlazifiedScript(dst);
     } else {
         fun->initScript(dst);
     }
 
-    if (!detail::CopyScript(cx, fun, src, dst)) {
+    if (!detail::CopyScript(cx, funScope, src, dst)) {
         if (lazy)
             fun->initLazyScript(lazy);
         else
             fun->setScript(nullptr);
         fun->setFlags(preservedFlags);
         return nullptr;
     }
 
@@ -3975,18 +4013,18 @@ JSScript::traceChildren(JSTracer* trc)
     }
 
     if (functionNonDelazifying())
         TraceEdge(trc, &function_, "function");
 
     if (module_)
         TraceEdge(trc, &module_, "module");
 
-    if (enclosingStaticScope_)
-        TraceEdge(trc, &enclosingStaticScope_, "enclosingStaticScope");
+    if (staticScope_)
+        TraceEdge(trc, &staticScope_, "staticScope");
 
     if (maybeLazyScript())
         TraceManuallyBarrieredEdge(trc, &lazyScript, "lazyScript");
 
     if (trc->isMarkingTracer()) {
         compartment()->mark();
 
         if (code())
@@ -4078,30 +4116,28 @@ JSScript::getStaticBlockScope(jsbytecode
         } else {
             top = mid;
         }
     }
 
     return blockChain;
 }
 
-JSObject*
+StaticScope*
 JSScript::innermostStaticScopeInScript(jsbytecode* pc)
 {
-    if (JSObject* scope = getStaticBlockScope(pc))
+    if (NestedStaticScope* scope = getStaticBlockScope(pc))
         return scope;
-    if (module())
-        return module();
-    return functionNonDelazifying();
+    return staticScope_;
 }
 
-JSObject*
+StaticScope*
 JSScript::innermostStaticScope(jsbytecode* pc)
 {
-    if (JSObject* scope = innermostStaticScopeInScript(pc))
+    if (StaticScope* scope = innermostStaticScopeInScript(pc))
         return scope;
     return enclosingStaticScope();
 }
 
 void
 JSScript::setArgumentsHasVarBinding()
 {
     argsHasVarBinding_ = true;
@@ -4136,18 +4172,23 @@ js::SetFrameArgumentsObject(JSContext* c
         while (*pc != JSOP_ARGUMENTS)
             pc += GetBytecodeLength(pc);
         pc += JSOP_ARGUMENTS_LENGTH;
         MOZ_ASSERT(*pc == JSOP_SETALIASEDVAR);
 
         // Note that here and below, it is insufficient to only check for
         // JS_OPTIMIZED_ARGUMENTS, as Ion could have optimized out the
         // arguments slot.
-        if (IsOptimizedPlaceholderMagicValue(frame.callObj().as<ScopeObject>().aliasedVar(ScopeCoordinate(pc))))
-            frame.callObj().as<ScopeObject>().setAliasedVar(cx, ScopeCoordinate(pc), cx->names().arguments, ObjectValue(*argsobj));
+        if (IsOptimizedPlaceholderMagicValue(frame.callObj().as<ScopeObject>()
+                                                  .aliasedVar(ScopeCoordinate(pc))))
+        {
+            frame.callObj().as<ScopeObject>().setAliasedVar(cx, ScopeCoordinate(pc),
+                                                            cx->names().arguments,
+                                                            ObjectValue(*argsobj));
+        }
     } else {
         if (IsOptimizedPlaceholderMagicValue(frame.unaliasedLocal(bi.frameIndex())))
             frame.unaliasedLocal(bi.frameIndex()) = ObjectValue(*argsobj);
     }
 }
 
 /* static */ bool
 JSScript::argumentsOptimizationFailed(JSContext* cx, HandleScript script)
@@ -4235,20 +4276,22 @@ JSScript::localIsAliased(unsigned localS
 }
 
 bool
 JSScript::formalLivesInArgumentsObject(unsigned argSlot)
 {
     return argsObjAliasesFormals() && !formalIsAliased(argSlot);
 }
 
-LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column)
+LazyScript::LazyScript(JSFunction* fun, StaticFunctionScope* funScope, void* table,
+                       uint64_t packedFields, uint32_t begin, uint32_t end,
+                       uint32_t lineno, uint32_t column)
   : script_(nullptr),
     function_(fun),
-    enclosingScope_(nullptr),
+    staticScope_(funScope),
     sourceObject_(nullptr),
     table_(table),
     packedFields_(packedFields),
     begin_(begin),
     end_(end),
     lineno_(lineno),
     column_(column)
 {
@@ -4266,23 +4309,20 @@ LazyScript::initScript(JSScript* script)
 void
 LazyScript::resetScript()
 {
     MOZ_ASSERT(script_.unbarrieredGet());
     script_.set(nullptr);
 }
 
 void
-LazyScript::setParent(JSObject* enclosingScope, ScriptSourceObject* sourceObject)
+LazyScript::initSource(ScriptSourceObject* sourceObject)
 {
-    MOZ_ASSERT(!sourceObject_ && !enclosingScope_);
-    MOZ_ASSERT_IF(enclosingScope, function_->compartment() == enclosingScope->compartment());
+    MOZ_ASSERT(!sourceObject_);
     MOZ_ASSERT(function_->compartment() == sourceObject->compartment());
-
-    enclosingScope_ = enclosingScope;
     sourceObject_ = sourceObject;
 }
 
 ScriptSourceObject*
 LazyScript::sourceObject() const
 {
     return sourceObject_ ? &sourceObject_->as<ScriptSourceObject>() : nullptr;
 }
@@ -4290,16 +4330,17 @@ LazyScript::sourceObject() const
 ScriptSource*
 LazyScript::maybeForwardedScriptSource() const
 {
     return UncheckedUnwrap(MaybeForwarded(sourceObject()))->as<ScriptSourceObject>().source();
 }
 
 /* static */ LazyScript*
 LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun,
+                      Handle<StaticFunctionScope*> funScope,
                       uint64_t packedFields, uint32_t begin, uint32_t end,
                       uint32_t lineno, uint32_t column)
 {
     union {
         PackedView p;
         uint64_t packed;
     };
 
@@ -4319,21 +4360,22 @@ LazyScript::CreateRaw(ExclusiveContext* 
     }
 
     LazyScript* res = Allocate<LazyScript>(cx);
     if (!res)
         return nullptr;
 
     cx->compartment()->scheduleDelazificationForDebugger();
 
-    return new (res) LazyScript(fun, table.forget(), packed, begin, end, lineno, column);
+    return new (res) LazyScript(fun, funScope, table.forget(), packed, begin, end, lineno, column);
 }
 
 /* static */ LazyScript*
 LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun,
+                      Handle<StaticFunctionScope*> funScope,
                       uint32_t numFreeVariables, uint32_t numInnerFunctions, JSVersion version,
                       uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column)
 {
     union {
         PackedView p;
         uint64_t packedFields;
     };
 
@@ -4344,36 +4386,38 @@ LazyScript::CreateRaw(ExclusiveContext* 
     p.strict = false;
     p.bindingsAccessedDynamically = false;
     p.hasDebuggerStatement = false;
     p.hasDirectEval = false;
     p.usesArgumentsApplyAndThis = false;
     p.isDerivedClassConstructor = false;
     p.needsHomeObject = false;
 
-    LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column);
+    LazyScript* res = LazyScript::CreateRaw(cx, fun, funScope, packedFields,
+                                            begin, end, lineno, column);
     MOZ_ASSERT_IF(res, res->version() == version);
     return res;
 }
 
 /* static */ LazyScript*
 LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
-                   HandleScript script, HandleObject enclosingScope,
+                   HandleScript script, Handle<StaticFunctionScope*> funScope,
                    HandleScript sourceObjectScript,
                    uint64_t packedFields, uint32_t begin, uint32_t end,
                    uint32_t lineno, uint32_t column)
 {
     // Dummy atom which is not a valid property name.
     RootedAtom dummyAtom(cx, cx->names().comma);
 
     // Dummy function which is not a valid function as this is the one which is
     // holding this lazy script.
     HandleFunction dummyFun = fun;
 
-    LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column);
+    LazyScript* res = LazyScript::CreateRaw(cx, fun, funScope, packedFields,
+                                            begin, end, lineno, column);
     if (!res)
         return nullptr;
 
     // Fill with dummies, to be GC-safe after the initialization of the free
     // variables and inner functions.
     size_t i, num;
     FreeVariable* variables = res->freeVariables();
     for (i = 0, num = res->numFreeVariables(); i < num; i++)
@@ -4381,17 +4425,17 @@ LazyScript::Create(ExclusiveContext* cx,
 
     HeapPtrFunction* functions = res->innerFunctions();
     for (i = 0, num = res->numInnerFunctions(); i < num; i++)
         functions[i].init(dummyFun);
 
     // Set the enclosing scope of the lazy function, this would later be
     // used to define the environment when the function would be used.
     MOZ_ASSERT(!res->sourceObject());
-    res->setParent(enclosingScope, &sourceObjectScript->scriptSourceUnwrap());
+    res->initSource(&sourceObjectScript->scriptSourceUnwrap());
 
     MOZ_ASSERT(!res->hasScript());
     if (script)
         res->initScript(script);
 
     return res;
 }
 
@@ -4414,20 +4458,20 @@ LazyScript::hasUncompiledEnclosingScript
     // It can happen that we created lazy scripts while compiling an enclosing
     // script, but we errored out while compiling that script. When we iterate
     // over lazy script in a compartment, we might see lazy scripts that never
     // escaped to script and should be ignored.
     //
     // If the enclosing scope is a function with a null script or has a script
     // without code, it was not successfully compiled.
 
-    if (!enclosingScope() || !enclosingScope()->is<JSFunction>())
+    if (!enclosingScope() || !enclosingScope()->is<StaticFunctionScope>())
         return false;
 
-    JSFunction& fun = enclosingScope()->as<JSFunction>();
+    JSFunction& fun = enclosingScope()->as<StaticFunctionScope>().function();
     return !fun.hasScript() || fun.hasUncompiledScript() || !fun.nonLazyScript()->code();
 }
 
 void
 JSScript::updateBaselineOrIonRaw(JSContext* maybecx)
 {
     if (hasBaselineScript() && baseline->hasPendingIonBuilder()) {
         MOZ_ASSERT(maybecx);
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -45,33 +45,35 @@ namespace jit {
 
 class BreakpointSite;
 class BindingIter;
 class Debugger;
 class LazyScript;
 class ModuleObject;
 class NestedStaticScope;
 class StaticScope;
+class StaticFunctionScope;
 class RegExpObject;
 struct SourceCompressionTask;
 class Shape;
 
 namespace frontend {
     struct BytecodeEmitter;
     class UpvarCookie;
     class FunctionBox;
     class ModuleBox;
 } // namespace frontend
 
 namespace detail {
 
 // Do not call this directly! It is exposed for the friend declarations in
 // this file.
 bool
-CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScript src, HandleScript dst);
+CopyScript(JSContext* cx, Handle<StaticScope*> scriptStaticScope, HandleScript src,
+           HandleScript dst);
 
 } // namespace detail
 
 } // namespace js
 
 /*
  * Type of try note associated with each catch or finally block, and also with
  * for-in and other kinds of loops. Non-for-in loops do not need these notes
@@ -141,17 +143,17 @@ struct TryNoteArray {
 
 struct BlockScopeArray {
     BlockScopeNote* vector;     // Array of indexed BlockScopeNote records.
     uint32_t        length;     // Count of indexed try notes.
 };
 
 class YieldOffsetArray {
     friend bool
-    detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScript src,
+    detail::CopyScript(JSContext* cx, Handle<StaticScope*> scriptStaticScope, HandleScript src,
                        HandleScript dst);
 
     uint32_t*       vector_;   // Array of bytecode offsets.
     uint32_t        length_;    // Count of bytecode offsets.
 
   public:
     void init(uint32_t* vector, uint32_t length) {
         vector_ = vector;
@@ -915,47 +917,47 @@ GeneratorKindFromBits(unsigned val) {
     return static_cast<GeneratorKind>(val);
 }
 
 /*
  * NB: after a successful XDR_DECODE, XDRScript callers must do any required
  * subsequent set-up of owning function or script object and then call
  * CallNewScriptHook.
  */
-template<XDRMode mode>
+template <XDRMode mode>
 bool
-XDRScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enclosingScript,
-          HandleFunction fun, MutableHandleScript scriptp);
-
-template<XDRMode mode>
+XDRScript(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
+          HandleScript enclosingScript, HandleFunction fun, MutableHandleScript scriptp);
+
+template <XDRMode mode>
 bool
-XDRLazyScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enclosingScript,
-              HandleFunction fun, MutableHandle<LazyScript*> lazy);
+XDRLazyScript(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
+              HandleScript enclosingScript, HandleFunction fun, MutableHandle<LazyScript*> lazy);
 
 /*
  * Code any constant value.
  */
-template<XDRMode mode>
+template <XDRMode mode>
 bool
 XDRScriptConst(XDRState<mode>* xdr, MutableHandleValue vp);
 
 } /* namespace js */
 
 class JSScript : public js::gc::TenuredCell
 {
     template <js::XDRMode mode>
     friend
     bool
-    js::XDRScript(js::XDRState<mode>* xdr, js::HandleObject enclosingScope,
+    js::XDRScript(js::XDRState<mode>* xdr, js::Handle<js::StaticScope*> enclosingScope,
                   js::HandleScript enclosingScript,
                   js::HandleFunction fun, js::MutableHandleScript scriptp);
 
     friend bool
-    js::detail::CopyScript(JSContext* cx, js::HandleObject scriptStaticScope, js::HandleScript src,
-                           js::HandleScript dst);
+    js::detail::CopyScript(JSContext* cx, js::Handle<js::StaticScope*> scriptStaticScope,
+                           js::HandleScript src, js::HandleScript dst);
 
   public:
     //
     // We order fields according to their size in order to avoid wasting space
     // for alignment.
     //
 
     // Larger-than-word-sized fields.
@@ -999,17 +1001,32 @@ class JSScript : public js::gc::TenuredC
     // This script's ScriptSourceObject, or a CCW thereof.
     //
     // (When we clone a JSScript into a new compartment, we don't clone its
     // source object. Instead, the clone refers to a wrapper.)
     js::HeapPtrObject sourceObject_;
 
     js::HeapPtrFunction function_;
     js::HeapPtr<js::ModuleObject*> module_;
-    js::HeapPtrObject   enclosingStaticScope_;
+
+    // The static scope this script runs in.
+    //
+    // Specifically, it depends on the case:
+    //
+    // *   direct eval: staticScope_ is the StaticEvalScope for the eval call.
+    //
+    // *   function script: staticScope_ is function_'s StaticFunctionScope.
+    //
+    // *   module script: staticScope_ is module_'s StaticModuleScope.
+    //
+    // *   plain old global script or indirect eval: staticScope_ is the static
+    //     global lexical scope (regardless of whether the script uses any
+    //     global lexical bindings).
+    //
+    js::HeapPtr<js::StaticScope*> staticScope_;
 
     /*
      * Information attached by Ion. Nexto a valid IonScript this could be
      * ION_DISABLED_SCRIPT, ION_COMPILING_SCRIPT or ION_PENDING_SCRIPT.
      * The later is a ion compilation that is ready, but hasn't been linked
      * yet.
      */
     js::jit::IonScript* ion;
@@ -1040,21 +1057,20 @@ class JSScript : public js::gc::TenuredC
 
     uint32_t        natoms_;    /* length of atoms array */
     uint32_t        nslots_;    /* vars plus maximum stack depth */
 
     /* Range of characters in scriptSource which contains this script's source. */
     uint32_t        sourceStart_;
     uint32_t        sourceEnd_;
 
-    uint32_t        warmUpCount; /* Number of times the script has been called
-                                  * or has had backedges taken. When running in
-                                  * ion, also increased for any inlined scripts.
-                                  * Reset if the script's JIT code is forcibly
-                                  * discarded. */
+    // Number of times the script has been called or has had backedges taken.
+    // When running in ion, also increased for any inlined scripts. Reset if
+    // the script's JIT code is forcibly discarded.
+    mozilla::Atomic<uint32_t, mozilla::Relaxed> warmUpCount;
 
     // 16-bit fields.
 
     uint16_t        warmUpResetCount; /* Number of times the |warmUpCount| was
                                  * forcibly discarded. The counter is reset when
                                  * a script is successfully jit-compiled. */
 
     uint16_t        version;    /* JS version under which script was compiled */
@@ -1209,17 +1225,17 @@ class JSScript : public js::gc::TenuredC
 #endif
 
     //
     // End of fields.  Start methods.
     //
 
   public:
     static JSScript* Create(js::ExclusiveContext* cx,
-                            js::HandleObject enclosingScope, bool savedCallerFun,
+                            js::Handle<js::StaticScope*> staticScope, bool savedCallerFun,
                             const JS::ReadOnlyCompileOptions& options,
                             js::HandleObject sourceObject, uint32_t sourceStart,
                             uint32_t sourceEnd);
 
     void initCompartment(js::ExclusiveContext* cx);
 
     // Three ways ways to initialize a JSScript. Callers of partiallyInit()
     // and fullyInitTrivial() are responsible for notifying the debugger after
@@ -1694,32 +1710,35 @@ class JSScript : public js::gc::TenuredC
 
     inline js::TypeScript* types();
 
     void maybeSweepTypes(js::AutoClearTypeInferenceStateOnOOM* oom);
 
     inline js::GlobalObject& global() const;
     js::GlobalObject& uninlinedGlobal() const;
 
-    /* See StaticScopeIter comment. */
-    JSObject* enclosingStaticScope() const {
-        return enclosingStaticScope_;
-    }
+    js::StaticScope* staticScope() const { return staticScope_; }
+
+    /*
+     * The static scope this script runs in, skipping the StaticFunctionScope
+     * if this is a non-eval function script.
+     */
+    inline js::StaticScope* enclosingStaticScope() const;
 
     // Switch the script over from the off-thread compartment's static
     // global lexical scope to the main thread compartment's.
     void fixEnclosingStaticGlobalLexicalScope();
 
   private:
     bool makeTypes(JSContext* cx);
 
   public:
     uint32_t getWarmUpCount() const { return warmUpCount; }
     uint32_t incWarmUpCounter(uint32_t amount = 1) { return warmUpCount += amount; }
-    uint32_t* addressOfWarmUpCounter() { return &warmUpCount; }
+    uint32_t* addressOfWarmUpCounter() { return reinterpret_cast<uint32_t*>(&warmUpCount); }
     static size_t offsetOfWarmUpCounter() { return offsetof(JSScript, warmUpCount); }
     void resetWarmUpCounter() { incWarmUpResetCounter(); warmUpCount = 0; }
 
     uint16_t getWarmUpResetCount() const { return warmUpResetCount; }
     uint16_t incWarmUpResetCounter(uint16_t amount = 1) { return warmUpResetCount += amount; }
     void resetWarmUpResetCounter() { warmUpResetCount = 0; }
 
   public:
@@ -1868,23 +1887,23 @@ class JSScript : public js::gc::TenuredC
 
     // The following 4 functions find the static scope just before the
     // execution of the instruction pointed to by pc.
 
     js::NestedStaticScope* getStaticBlockScope(jsbytecode* pc);
 
     // Returns the innermost static scope at pc if it falls within the extent
     // of the script. Returns nullptr otherwise.
-    JSObject* innermostStaticScopeInScript(jsbytecode* pc);
+    js::StaticScope* innermostStaticScopeInScript(jsbytecode* pc);
 
     // As innermostStaticScopeInScript, but returns the enclosing static scope
     // if the innermost static scope falls without the extent of the script.
-    JSObject* innermostStaticScope(jsbytecode* pc);
-
-    JSObject* innermostStaticScope() { return innermostStaticScope(main()); }
+    js::StaticScope* innermostStaticScope(jsbytecode* pc);
+
+    js::StaticScope* innermostStaticScope() { return innermostStaticScope(main()); }
 
     /*
      * The isEmpty method tells whether this script has code that computes any
      * result (not return value, result AKA normal completion value) other than
      * JSVAL_VOID, or any other effects.
      */
     bool isEmpty() const {
         if (length() > 3)
@@ -2137,21 +2156,22 @@ class LazyScript : public gc::TenuredCel
     // If non-nullptr, the script has been compiled and this is a forwarding
     // pointer to the result. This is a weak pointer: after relazification, we
     // can collect the script if there are no other pointers to it.
     WeakRef<JSScript*> script_;
 
     // Original function with which the lazy script is associated.
     HeapPtrFunction function_;
 
-    // Function or block chain in which the script is nested, or nullptr.
-    HeapPtrObject enclosingScope_;
-
-    // ScriptSourceObject, or nullptr if the script in which this is nested
-    // has not been compiled yet. This is never a CCW; we don't clone
+    // Static scope of this function. (All lazy scripts are for functions;
+    // global scripts and eval scripts are never lazified.)
+    HeapPtr<StaticFunctionScope*> staticScope_;
+
+    // ScriptSourceObject. We leave this set to nullptr until we generate
+    // bytecode for our immediate parent. This is never a CCW; we don't clone
     // LazyScripts into other compartments.
     HeapPtrObject sourceObject_;
 
     // Heap allocated table with any free variables or inner functions.
     void* table_;
 
     // Add padding so LazyScript is gc::Cell aligned. Make padding protected
     // instead of private to suppress -Wunused-private-field compiler warnings.
@@ -2190,46 +2210,48 @@ class LazyScript : public gc::TenuredCel
     };
 
     // Source location for the script.
     uint32_t begin_;
     uint32_t end_;
     uint32_t lineno_;
     uint32_t column_;
 
-    LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
+    LazyScript(JSFunction* fun, StaticFunctionScope* funScope, void* table, uint64_t packedFields,
                uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column);
 
     // Create a LazyScript without initializing the freeVariables and the
     // innerFunctions. To be GC-safe, the caller must initialize both vectors
     // with valid atoms and functions.
     static LazyScript* CreateRaw(ExclusiveContext* cx, HandleFunction fun,
+                                 Handle<StaticFunctionScope*> funScope,
                                  uint64_t packedData, uint32_t begin, uint32_t end,
                                  uint32_t lineno, uint32_t column);
 
   public:
     // Create a LazyScript without initializing the freeVariables and the
     // innerFunctions. To be GC-safe, the caller must initialize both vectors
     // with valid atoms and functions.
     static LazyScript* CreateRaw(ExclusiveContext* cx, HandleFunction fun,
+                                 Handle<StaticFunctionScope*> funScope,
                                  uint32_t numFreeVariables, uint32_t numInnerFunctions,
                                  JSVersion version, uint32_t begin, uint32_t end,
                                  uint32_t lineno, uint32_t column);
 
     // Create a LazyScript and initialize the freeVariables and the
     // innerFunctions with dummy values to be replaced in a later initialization
     // phase.
     //
     // The "script" argument to this function can be null.  If it's non-null,
     // then this LazyScript should be associated with the given JSScript.
     //
     // The sourceObjectScript argument must be non-null and is the script that
     // should be used to get the sourceObject_ of this lazyScript.
     static LazyScript* Create(ExclusiveContext* cx, HandleFunction fun,
-                              HandleScript script, HandleObject enclosingScope,
+                              HandleScript script, Handle<StaticFunctionScope*> funScope,
                               HandleScript sourceObjectScript,
                               uint64_t packedData, uint32_t begin, uint32_t end,
                               uint32_t lineno, uint32_t column);
 
     void initRuntimeFields(uint64_t packedFields);
 
     inline JSFunction* functionDelazifying(JSContext* cx) const;
     JSFunction* functionNonDelazifying() const {
@@ -2244,19 +2266,18 @@ class LazyScript : public gc::TenuredCel
     }
     const JSScript* maybeScriptUnbarriered() const {
         return script_.unbarrieredGet();
     }
     bool hasScript() const {
         return bool(script_);
     }
 
-    JSObject* enclosingScope() const {
-        return enclosingScope_;
-    }
+    StaticFunctionScope* staticScope() const { return staticScope_; }
+    StaticScope* enclosingScope() const;
 
     // Switch the script over from the off-thread compartment's static
     // global lexical scope to the main thread compartment's.
     void fixEnclosingStaticGlobalLexicalScope();
 
     ScriptSourceObject* sourceObject() const;
     ScriptSource* scriptSource() const {
         return sourceObject()->source();
@@ -2265,17 +2286,17 @@ class LazyScript : public gc::TenuredCel
     bool mutedErrors() const {
         return scriptSource()->mutedErrors();
     }
     JSVersion version() const {
         JS_STATIC_ASSERT(JSVERSION_UNKNOWN == -1);
         return (p_.version == JS_BIT(8) - 1) ? JSVERSION_UNKNOWN : JSVersion(p_.version);
     }
 
-    void setParent(JSObject* enclosingScope, ScriptSourceObject* sourceObject);
+    void initSource(ScriptSourceObject* sourceObject);
 
     uint32_t numFreeVariables() const {
         return p_.numFreeVariables;
     }
     FreeVariable* freeVariables() {
         return (FreeVariable*)table_;
     }
 
@@ -2525,29 +2546,29 @@ enum LineOption {
 
 extern void
 DescribeScriptedCallerForCompilation(JSContext* cx, MutableHandleScript maybeScript,
                                      const char** file, unsigned* linenop,
                                      uint32_t* pcOffset, bool* mutedErrors,
                                      LineOption opt = NOT_CALLED_FROM_JSOP_EVAL);
 
 JSScript*
-CloneScriptIntoFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction fun,
+CloneScriptIntoFunction(JSContext* cx, Handle<StaticScope*> enclosingScope, HandleFunction fun,
                         HandleScript src);
 
 JSScript*
 CloneGlobalScript(JSContext* cx, Handle<StaticScope*> enclosingScope, HandleScript src);
 
 } /* namespace js */
 
 // JS::ubi::Nodes can point to js::LazyScripts; they're js::gc::Cell instances
 // with no associated compartment.
 namespace JS {
 namespace ubi {
-template<>
+template <>
 struct Concrete<js::LazyScript> : TracerConcrete<js::LazyScript> {
     CoarseType coarseType() const final { return CoarseType::Script; }
     Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
     const char* scriptFilename() const final;
 
   protected:
     explicit Concrete(js::LazyScript *ptr) : TracerConcrete<js::LazyScript>(ptr) { }
 
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -87,18 +87,33 @@ SetFrameArgumentsObject(JSContext* cx, A
 inline JSFunction*
 LazyScript::functionDelazifying(JSContext* cx) const
 {
     if (function_ && !function_->getOrCreateScript(cx))
         return nullptr;
     return function_;
 }
 
+inline StaticScope*
+LazyScript::enclosingScope() const
+{
+    return staticScope_->enclosingScope();
+}
+
 } // namespace js
 
+inline js::StaticScope*
+JSScript::enclosingStaticScope() const
+{
+    // The static scope of a function script is the function scope (which
+    // contains arguments and local variables). This method's callers want to
+    // skip that scope.
+    return function_ ? staticScope_->enclosingScope() : staticScope_;
+}
+
 inline JSFunction*
 JSScript::functionDelazifying() const
 {
     if (function_ && function_->isInterpretedLazy()) {
         function_->setUnlazifiedScript(const_cast<JSScript*>(this));
         // If this script has a LazyScript, make sure the LazyScript has a
         // reference to the script when delazifying its canonical function.
         if (lazyScript && !lazyScript->maybeScript())
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -139,16 +139,17 @@ static const double MAX_TIMEOUT_INTERVAL
 // Per-runtime shell state.
 struct ShellRuntime
 {
     ShellRuntime();
 
     bool isWorker;
     double timeoutInterval;
     Atomic<bool> serviceInterrupt;
+    Atomic<bool> haveInterruptFunc;
     JS::PersistentRootedValue interruptFunc;
     bool lastWarningEnabled;
     JS::PersistentRootedValue lastWarning;
 
     /*
      * Watchdog thread state.
      */
     PRLock* watchdogLock;
@@ -285,16 +286,17 @@ extern JS_EXPORT_API(char*) readline(con
 extern JS_EXPORT_API(void)   add_history(char* line);
 } // extern "C"
 #endif
 
 ShellRuntime::ShellRuntime()
   : isWorker(false),
     timeoutInterval(-1.0),
     serviceInterrupt(false),
+    haveInterruptFunc(false),
     lastWarningEnabled(false),
     watchdogLock(nullptr),
     watchdogWakeup(nullptr),
     watchdogThread(nullptr),
     watchdogHasTimeout(false),
     watchdogTimeout(0),
     sleepWakeup(nullptr),
     exitCode(0),
@@ -425,22 +427,21 @@ ShellInterruptCallback(JSContext* cx)
 
     // Reset serviceInterrupt. CancelExecution or InterruptIf will set it to
     // true to distinguish watchdog or user triggered interrupts.
     // Do this first to prevent other interrupts that may occur while the
     // user-supplied callback is executing from re-entering the handler.
     sr->serviceInterrupt = false;
 
     bool result;
-    RootedValue interruptFunc(cx, sr->interruptFunc);
-    if (!interruptFunc.isNull()) {
+    if (sr->haveInterruptFunc) {
         JS::AutoSaveExceptionState savedExc(cx);
-        JSAutoCompartment ac(cx, &interruptFunc.toObject());
+        JSAutoCompartment ac(cx, &sr->interruptFunc.toObject());
         RootedValue rval(cx);
-        if (!JS_CallFunctionValue(cx, nullptr, interruptFunc,
+        if (!JS_CallFunctionValue(cx, nullptr, sr->interruptFunc,
                                   JS::HandleValueArray::empty(), &rval))
         {
             return false;
         }
         if (rval.isBoolean())
             result = rval.toBoolean();
         else
             result = false;
@@ -3121,17 +3122,17 @@ KillWorkerThreads()
 
 static void
 CancelExecution(JSRuntime* rt)
 {
     ShellRuntime* sr = GetShellRuntime(rt);
     sr->serviceInterrupt = true;
     JS_RequestInterruptCallback(rt);
 
-    if (!sr->interruptFunc.isNull()) {
+    if (sr->haveInterruptFunc) {
         static const char msg[] = "Script runs for too long, terminating.\n";
         fputs(msg, stderr);
     }
 }
 
 static bool
 SetTimeoutValue(JSContext* cx, double t)
 {
@@ -3170,16 +3171,17 @@ Timeout(JSContext* cx, unsigned argc, Va
 
     if (args.length() > 1) {
         RootedValue value(cx, args[1]);
         if (!value.isObject() || !value.toObject().is<JSFunction>()) {
             JS_ReportError(cx, "Second argument must be a timeout function");
             return false;
         }
         sr->interruptFunc = value;
+        sr->haveInterruptFunc = true;
     }
 
     args.rval().setUndefined();
     return SetTimeoutValue(cx, t);
 }
 
 static bool
 InterruptIf(JSContext* cx, unsigned argc, Value* vp)
@@ -3241,16 +3243,17 @@ SetInterruptCallback(JSContext* cx, unsi
     }
 
     RootedValue value(cx, args[0]);
     if (!value.isObject() || !value.toObject().is<JSFunction>()) {
         JS_ReportError(cx, "Argument must be a function");
         return false;
     }
     GetShellRuntime(cx)->interruptFunc = value;
+    GetShellRuntime(cx)->haveInterruptFunc = true;
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 EnableLastWarning(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -4661,16 +4664,18 @@ DumpStaticScopeChain(JSContext* cx, unsi
 
     if (obj->is<JSFunction>()) {
         RootedFunction fun(cx, &obj->as<JSFunction>());
         if (!fun->isInterpreted()) {
             ReportUsageError(cx, callee, "Argument must be an interpreted function");
             return false;
         }
         script = fun->getOrCreateScript(cx);
+        if (!script)
+            return false;
     } else {
         script = obj->as<ModuleObject>().script();
     }
 
     js::DumpStaticScopeChain(script);
 
     args.rval().setUndefined();
     return true;
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -14,16 +14,17 @@
 #include "jscompartment.h"
 #include "jsfriendapi.h"
 #include "jshashutil.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jswrapper.h"
 
 #include "frontend/BytecodeCompiler.h"
+#include "frontend/Parser.h"
 #include "gc/Marking.h"
 #include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineJIT.h"
 #include "jit/JSONSpewer.h"
 #include "jit/MIRGraph.h"
 #include "js/GCAPI.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/Vector.h"
@@ -4518,16 +4519,63 @@ Debugger::endTraceLogger(JSContext* cx, 
     TraceLoggerDisable(logger);
 
     args.rval().setUndefined();
 
     return true;
 }
 
 bool
+Debugger::isCompilableUnit(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (!args[0].isString()) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                             JSMSG_NOT_EXPECTED_TYPE, "Debugger.isCompilableUnit",
+                             "string", InformalValueTypeName(args[0]));
+        return false;
+    }
+
+    JSString* str = args[0].toString();
+    size_t length = GetStringLength(str);
+
+    AutoStableStringChars chars(cx);
+    if (!chars.initTwoByte(cx, str))
+        return false;
+
+    bool result = true;
+
+    CompileOptions options(cx);
+    frontend::Parser<frontend::FullParseHandler> parser(cx, &cx->tempLifoAlloc(),
+                                                        options, chars.twoByteChars(),
+                                                        length, /* foldConstants = */ true,
+                                                        nullptr, nullptr);
+    JSErrorReporter older = JS_SetErrorReporter(cx->runtime(), nullptr);
+    if (!parser.checkOptions() || !parser.parse()) {
+        // We ran into an error. If it was because we ran out of memory we report
+        // it in the usual way.
+        if (cx->isThrowingOutOfMemory()) {
+            JS_SetErrorReporter(cx->runtime(), older);
+            return false;
+        }
+
+        // If it was because we ran out of source, we return false so our caller
+        // knows to try to collect more [source].
+        if (parser.isUnexpectedEOF())
+            result = false;
+
+        cx->clearPendingException();
+    }
+    JS_SetErrorReporter(cx->runtime(), older);
+    args.rval().setBoolean(result);
+    return true;
+}
+
+bool
 Debugger::drainTraceLoggerScriptCalls(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGGER(cx, argc, vp, "drainTraceLoggerScriptCalls", args, dbg);
     if (!args.requireAtLeast(cx, "Debugger.drainTraceLoggerScriptCalls", 0))
         return false;
 
     size_t num;
     TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
@@ -4638,17 +4686,21 @@ const JSFunctionSpec Debugger::methods[]
 # ifdef NIGHTLY_BUILD
     JS_FN("setupTraceLogger", Debugger::setupTraceLogger, 1, 0),
     JS_FN("drainTraceLogger", Debugger::drainTraceLogger, 0, 0),
 # endif
 #endif
     JS_FS_END
 };
 
-
+const JSFunctionSpec Debugger::static_methods[] {
+    JS_FN("isCompilableUnit", Debugger::isCompilableUnit, 1, 0),
+    JS_FS_END
+};
+
 /*** Debugger.Script *****************************************************************************/
 
 static inline JSScript*
 GetScriptReferent(JSObject* obj)
 {
     MOZ_ASSERT(obj->getClass() == &DebuggerScript_class);
     return static_cast<JSScript*>(obj->as<NativeObject>().getPrivate());
 }
@@ -8410,18 +8462,18 @@ JS_DefineDebuggerObject(JSContext* cx, H
         objectProto(cx),
         envProto(cx),
         memoryProto(cx);
     objProto = obj->as<GlobalObject>().getOrCreateObjectPrototype(cx);
     if (!objProto)
         return false;
     debugProto = InitClass(cx, obj,
                            objProto, &Debugger::jsclass, Debugger::construct,
-                           1, Debugger::properties, Debugger::methods, nullptr, nullptr,
-                           debugCtor.address());
+                           1, Debugger::properties, Debugger::methods, nullptr,
+                           Debugger::static_methods, debugCtor.address());
     if (!debugProto)
         return false;
 
     frameProto = InitClass(cx, debugCtor, objProto, &DebuggerFrame_class,
                            DebuggerFrame_construct, 0,
                            DebuggerFrame_properties, DebuggerFrame_methods,
                            nullptr, nullptr);
     if (!frameProto)
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -559,23 +559,25 @@ class Debugger : private mozilla::Linked
     static bool findScripts(JSContext* cx, unsigned argc, Value* vp);
     static bool findObjects(JSContext* cx, unsigned argc, Value* vp);
     static bool findAllGlobals(JSContext* cx, unsigned argc, Value* vp);
     static bool makeGlobalObjectReference(JSContext* cx, unsigned argc, Value* vp);
     static bool setupTraceLoggerScriptCalls(JSContext* cx, unsigned argc, Value* vp);
     static bool drainTraceLoggerScriptCalls(JSContext* cx, unsigned argc, Value* vp);
     static bool startTraceLogger(JSContext* cx, unsigned argc, Value* vp);
     static bool endTraceLogger(JSContext* cx, unsigned argc, Value* vp);
+    static bool isCompilableUnit(JSContext* cx, unsigned argc, Value* vp);
 #ifdef NIGHTLY_BUILD
     static bool setupTraceLogger(JSContext* cx, unsigned argc, Value* vp);
     static bool drainTraceLogger(JSContext* cx, unsigned argc, Value* vp);
 #endif
     static bool construct(JSContext* cx, unsigned argc, Value* vp);
     static const JSPropertySpec properties[];
     static const JSFunctionSpec methods[];
+    static const JSFunctionSpec static_methods[];
 
     static void removeFromFrameMapsAndClearBreakpointsIn(JSContext* cx, AbstractFramePtr frame);
     static bool updateExecutionObservabilityOfFrames(JSContext* cx, const ExecutionObservableSet& obs,
                                                      IsObserving observing);
     static bool updateExecutionObservabilityOfScripts(JSContext* cx, const ExecutionObservableSet& obs,
                                                       IsObserving observing);
     static bool updateExecutionObservability(JSContext* cx, ExecutionObservableSet& obs,
                                              IsObserving observing);
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -922,19 +922,18 @@ js::TypeOfValue(const Value& v)
 }
 
 /*
  * Enter the new with scope using an object at sp[-1] and associate the depth
  * of the with block with sp + stackIndex.
  */
 bool
 js::EnterWithOperation(JSContext* cx, AbstractFramePtr frame, HandleValue val,
-                       HandleObject staticWith)
-{
-    MOZ_ASSERT(staticWith->is<StaticWithScope>());
+                       Handle<StaticWithScope*> staticWith)
+{
     RootedObject obj(cx);
     if (val.isObject()) {
         obj = &val.toObject();
     } else {
         obj = ToObject(cx, val);
         if (!obj)
             return false;
     }
@@ -1034,18 +1033,18 @@ ForcedReturn(JSContext* cx, InterpreterR
 }
 
 static void
 SettleOnTryNote(JSContext* cx, JSTryNote* tn, ScopeIter& si, InterpreterRegs& regs)
 {
     // Unwind the scope to the beginning of the JSOP_TRY.
     UnwindScope(cx, si, UnwindScopeToTryPc(regs.fp()->script(), tn));
 
-    // Set pc to the first bytecode after the the try note to point
-    // to the beginning of catch or finally.
+    // Set pc to the first bytecode after the span of the try note, the
+    // beginning of the first catch or finally block.
     regs.pc = regs.fp()->script()->main() + tn->start + tn->length;
     regs.sp = regs.spForStackDepth(tn->stackDepth);
 }
 
 class InterpreterFrameStackDepthOp
 {
     const InterpreterRegs& regs_;
   public:
@@ -1489,16 +1488,19 @@ class ReservedRooted : public ReservedRo
         *savedRoot = js::GCPolicy<T>::initial();
     }
 
     void set(const T& p) const { *savedRoot = p; }
     operator Handle<T>() { return *savedRoot; }
     operator Rooted<T>&() { return *savedRoot; }
     MutableHandle<T> operator&() { return &*savedRoot; }
 
+    template <typename U>
+    Handle<U*> as() { return savedRoot->template as<U>(); }
+
     DECLARE_NONPOINTER_ACCESSOR_METHODS(savedRoot->get())
     DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(savedRoot->get())
     DECLARE_POINTER_CONSTREF_OPS(T)
     DECLARE_POINTER_ASSIGN_OPS(ReservedRooted, T)
 };
 
 template <>
 class ReservedRootedBase<Value> : public ValueOperations<ReservedRooted<Value>>
@@ -1868,17 +1870,17 @@ CASE(JSOP_GETRVAL)
 END_CASE(JSOP_GETRVAL)
 
 CASE(JSOP_ENTERWITH)
 {
     ReservedRooted<Value> val(&rootValue0, REGS.sp[-1]);
     REGS.sp--;
     ReservedRooted<JSObject*> staticWith(&rootObject0, script->getObject(REGS.pc));
 
-    if (!EnterWithOperation(cx, REGS.fp(), val, staticWith))
+    if (!EnterWithOperation(cx, REGS.fp(), val, staticWith.as<StaticWithScope>()))
         goto error;
 }
 END_CASE(JSOP_ENTERWITH)
 
 CASE(JSOP_LEAVEWITH)
     REGS.fp()->popWith(cx);
 END_CASE(JSOP_LEAVEWITH)
 
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -16,16 +16,17 @@
 
 #include "frontend/ParseNode.h"
 
 #include "vm/Stack.h"
 
 namespace js {
 
 class ScopeIter;
+class StaticWithScope;
 
 /*
  * For a given |call|, convert null/undefined |this| into the global object for
  * the callee and replace other primitives with boxed versions. This assumes
  * that call.callee() is not strict mode code. This is the special/slow case of
  * ComputeThis.
  */
 extern bool
@@ -423,18 +424,18 @@ InitGetterSetterOperation(JSContext* cx,
 bool
 InitGetterSetterOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, HandlePropertyName name,
                           HandleObject val);
 
 unsigned
 GetInitDataPropAttrs(JSOp op);
 
 bool
-EnterWithOperation(JSContext* cx, AbstractFramePtr frame, HandleValue val, HandleObject staticWith);
-
+EnterWithOperation(JSContext* cx, AbstractFramePtr frame, HandleValue val,
+                   Handle<StaticWithScope*> staticWith);
 
 bool
 InitGetterSetterOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, HandleValue idval,
                           HandleObject val);
 
 bool
 SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue thisv,
                     HandleValue callee, HandleValue arr, HandleValue newTarget, MutableHandleValue res);
--- a/js/src/vm/ScopeObject-inl.h
+++ b/js/src/vm/ScopeObject-inl.h
@@ -79,180 +79,163 @@ LexicalScopeBase::initAliasedLexicalsToT
 {
     initRemainingSlotsToUninitializedLexicals(script->bindings.aliasedBodyLevelLexicalBegin());
 }
 
 template <AllowGC allowGC>
 inline void
 StaticScopeIter<allowGC>::operator++(int)
 {
-    if (obj->template is<NestedStaticScope>()) {
-        obj = obj->template as<NestedStaticScope>().enclosingScope();
-    } else if (obj->template is<StaticEvalScope>()) {
-        obj = obj->template as<StaticEvalScope>().enclosingScope();
-    } else if (obj->template is<StaticNonSyntacticScope>()) {
-        obj = obj->template as<StaticNonSyntacticScope>().enclosingScope();
-    } else if (obj->template is<ModuleObject>()) {
-        obj = obj->template as<ModuleObject>().enclosingStaticScope();
-    } else if (onNamedLambda || !obj->template as<JSFunction>().isNamedLambda()) {
+    if (!scope->template is<StaticFunctionScope>()) {
+        scope = scope->enclosingScope();
+    } else if (onNamedLambda || !scope->template as<StaticFunctionScope>().isNamedLambda()) {
         onNamedLambda = false;
-        JSFunction& fun = obj->template as<JSFunction>();
-        if (fun.isBeingParsed())
-            obj = fun.functionBox()->enclosingStaticScope();
-        else
-            obj = fun.nonLazyScript()->enclosingStaticScope();
+        scope = scope->enclosingScope();
     } else {
         onNamedLambda = true;
     }
-    MOZ_ASSERT_IF(obj, IsStaticScope(obj));
-    MOZ_ASSERT_IF(onNamedLambda, obj->template is<JSFunction>());
+    MOZ_ASSERT_IF(scope, scope->template is<StaticScope>());
+    MOZ_ASSERT_IF(onNamedLambda, scope->template is<StaticFunctionScope>());
 }
 
 template <AllowGC allowGC>
 inline bool
 StaticScopeIter<allowGC>::hasSyntacticDynamicScopeObject() const
 {
-    if (obj->template is<JSFunction>()) {
-        JSFunction& fun = obj->template as<JSFunction>();
+    if (scope->template is<StaticFunctionScope>()) {
+        JSFunction& fun = scope->template as<StaticFunctionScope>().function();
         if (fun.isBeingParsed())
             return fun.functionBox()->needsCallObject();
         return fun.needsCallObject();
     }
-    if (obj->template is<ModuleObject>())
+    if (scope->template is<StaticModuleScope>())
         return true;
-    if (obj->template is<StaticBlockScope>()) {
-        return obj->template as<StaticBlockScope>().needsClone() ||
-               obj->template as<StaticBlockScope>().isGlobal();
+    if (scope->template is<StaticBlockScope>()) {
+        return scope->template as<StaticBlockScope>().needsClone() ||
+               scope->template as<StaticBlockScope>().isGlobal();
     }
-    if (obj->template is<StaticWithScope>())
+    if (scope->template is<StaticWithScope>())
         return true;
-    if (obj->template is<StaticEvalScope>())
-        return obj->template as<StaticEvalScope>().isStrict();
-    MOZ_ASSERT(obj->template is<StaticNonSyntacticScope>());
+    if (scope->template is<StaticEvalScope>())
+        return scope->template as<StaticEvalScope>().isStrict();
+    MOZ_ASSERT(scope->template is<StaticNonSyntacticScope>());
     return false;
 }
 
 template <AllowGC allowGC>
 inline Shape*
 StaticScopeIter<allowGC>::scopeShape() const
 {
     MOZ_ASSERT(hasSyntacticDynamicScopeObject());
     MOZ_ASSERT(type() != NamedLambda && type() != Eval);
     if (type() == Block)
         return block().lastProperty();
     if (type() == Module)
-        return moduleScript()->callObjShape();
-    return funScript()->callObjShape();
+        return module().environmentShape();
+    return fun().environmentShape();
 }
 
 template <AllowGC allowGC>
 inline typename StaticScopeIter<allowGC>::Type
 StaticScopeIter<allowGC>::type() const
 {
     if (onNamedLambda)
         return NamedLambda;
-    if (obj->template is<StaticBlockScope>())
+    if (scope->template is<StaticBlockScope>())
         return Block;
-    if (obj->template is<StaticWithScope>())
+    if (scope->template is<StaticModuleScope>())
+        return Module;
+    if (scope->template is<StaticWithScope>())
         return With;
-    if (obj->template is<StaticEvalScope>())
+    if (scope->template is<StaticEvalScope>())
         return Eval;
-    if (obj->template is<StaticNonSyntacticScope>())
+    if (scope->template is<StaticNonSyntacticScope>())
         return NonSyntactic;
-    if (obj->template is<ModuleObject>())
-        return Module;
-    MOZ_ASSERT(obj->template is<JSFunction>());
+    MOZ_ASSERT(scope->template is<StaticFunctionScope>());
     return Function;
 }
 
 template <AllowGC allowGC>
 inline StaticBlockScope&
 StaticScopeIter<allowGC>::block() const
 {
     MOZ_ASSERT(type() == Block);
-    return obj->template as<StaticBlockScope>();
+    return scope->template as<StaticBlockScope>();
 }
 
 template <AllowGC allowGC>
 inline StaticWithScope&
 StaticScopeIter<allowGC>::staticWith() const
 {
     MOZ_ASSERT(type() == With);
-    return obj->template as<StaticWithScope>();
+    return scope->template as<StaticWithScope>();
 }
 
 template <AllowGC allowGC>
 inline StaticEvalScope&
 StaticScopeIter<allowGC>::eval() const
 {
     MOZ_ASSERT(type() == Eval);
-    return obj->template as<StaticEvalScope>();
+    return scope->template as<StaticEvalScope>();
 }
 
 template <AllowGC allowGC>
 inline StaticNonSyntacticScope&
 StaticScopeIter<allowGC>::nonSyntactic() const
 {
     MOZ_ASSERT(type() == NonSyntactic);
-    return obj->template as<StaticNonSyntacticScope>();
+    return scope->template as<StaticNonSyntacticScope>();
 }
 
 template <AllowGC allowGC>
 inline JSScript*
 StaticScopeIter<allowGC>::funScript() const
 {
     MOZ_ASSERT(type() == Function);
-    return obj->template as<JSFunction>().nonLazyScript();
+    return scope->template as<StaticFunctionScope>().function().nonLazyScript();
 }
 
 template <AllowGC allowGC>
-inline JSFunction&
+inline StaticFunctionScope&
 StaticScopeIter<allowGC>::fun() const
 {
     MOZ_ASSERT(type() == Function);
-    return obj->template as<JSFunction>();
+    return scope->template as<StaticFunctionScope>();
 }
 
 template <AllowGC allowGC>
 inline frontend::FunctionBox*
 StaticScopeIter<allowGC>::maybeFunctionBox() const
 {
     MOZ_ASSERT(type() == Function);
-    if (fun().isBeingParsed())
-        return fun().functionBox();
+    if (fun().function().isBeingParsed())
+        return fun().function().functionBox();
     return nullptr;
 }
 
 template <AllowGC allowGC>
-inline JSScript*
-StaticScopeIter<allowGC>::moduleScript() const
-{
-    MOZ_ASSERT(type() == Module);
-    return obj->template as<ModuleObject>().script();
-}
-
-template <AllowGC allowGC>
-inline ModuleObject&
+inline StaticModuleScope&
 StaticScopeIter<allowGC>::module() const
 {
     MOZ_ASSERT(type() == Module);
-    return obj->template as<ModuleObject>();
+    return scope->template as<StaticModuleScope>();
 }
 
 }  /* namespace js */
 
 inline JSObject*
 JSObject::enclosingScope()
 {
     if (is<js::ScopeObject>())
         return &as<js::ScopeObject>().enclosingScope();
 
     if (is<js::DebugScopeObject>())
         return &as<js::DebugScopeObject>().enclosingScope();
 
     if (is<js::GlobalObject>())
         return nullptr;
 
-    MOZ_ASSERT_IF(is<JSFunction>(), as<JSFunction>().isInterpreted());
+    MOZ_ASSERT(!is<JSFunction>());
+    MOZ_ASSERT(!is<js::StaticScope>());
     return &global();
 }
 
 #endif /* vm_ScopeObject_inl_h */
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -34,20 +34,20 @@ using mozilla::PodZero;
 
 typedef Rooted<ArgumentsObject*> RootedArgumentsObject;
 typedef MutableHandle<ArgumentsObject*> MutableHandleArgumentsObject;
 
 
 /*** Static scope objects ************************************************************************/
 
 void
-StaticScope::setEnclosingScope(HandleObject obj)
+StaticScope::setEnclosingScope(StaticScope* scope)
 {
-    MOZ_ASSERT_IF(obj->is<StaticBlockScope>(), obj->isDelegate());
-    setFixedSlot(ENCLOSING_SCOPE_SLOT, ObjectValue(*obj));
+    MOZ_ASSERT_IF(scope->is<StaticBlockScope>(), scope->isDelegate());
+    setFixedSlot(ENCLOSING_SCOPE_SLOT, ObjectValue(*scope));
 }
 
 bool
 StaticBlockScope::isExtensible() const
 {
     return nonProxyIsExtensible();
 }
 
@@ -113,31 +113,88 @@ StaticBlockScope::addVar(ExclusiveContex
                                              /* setter = */ nullptr,
                                              slot,
                                              propFlags,
                                              /* attrs = */ 0,
                                              entry,
                                              /* allowDictionary = */ false);
 }
 
+const Class StaticFunctionScope::class_ = {
+    "StaticFunctionScope",
+    JSCLASS_HAS_RESERVED_SLOTS(StaticFunctionScope::RESERVED_SLOTS) |
+    JSCLASS_IS_ANONYMOUS
+};
+
+StaticFunctionScope*
+StaticFunctionScope::create(ExclusiveContext* cx, Handle<JSFunction*> functionObject,
+                            Handle<StaticScope*> enclosingScope)
+{
+    Rooted<TaggedProto> proto(cx, TaggedProto(nullptr));
+    JSObject* obj = NewObjectWithGivenTaggedProto(cx, &class_, proto, TenuredObject,
+                                                  BaseShape::DELEGATE);
+    if (!obj)
+        return nullptr;
+
+    StaticFunctionScope* scope = &obj->as<StaticFunctionScope>();
+    scope->initEnclosingScope(enclosingScope);
+    scope->setReservedSlot(FUNCTION_OBJECT_SLOT, ObjectValue(*functionObject));
+    return scope;
+}
+
+StaticModuleScope*
+StaticModuleScope::create(ExclusiveContext* cx, Handle<ModuleObject*> moduleObject,
+                          Handle<StaticScope*> enclosingScope)
+{
+    Rooted<TaggedProto> nullProto(cx, TaggedProto(nullptr));
+    JSObject* obj = NewObjectWithGivenTaggedProto(cx, &ModuleEnvironmentObject::class_, nullProto,
+                                                  TenuredObject, BaseShape::DELEGATE);
+    if (!obj)
+        return nullptr;
+
+    StaticModuleScope* scope = &obj->as<StaticModuleScope>();
+    scope->initEnclosingScope(enclosingScope);
+    scope->setReservedSlot(MODULE_OBJECT_SLOT, ObjectValue(*moduleObject));
+    return scope;
+}
+
+ModuleObject&
+StaticModuleScope::moduleObject()
+{
+    return getReservedSlot(MODULE_OBJECT_SLOT).toObject().as<ModuleObject>();
+}
+
+JSScript*
+StaticModuleScope::script()
+{
+    return moduleObject().script();
+}
+
+Shape*
+StaticModuleScope::environmentShape()
+{
+    ModuleObject* module = &getReservedSlot(MODULE_OBJECT_SLOT).toObject().as<ModuleObject>();
+    return module->script()->bindings.callObjShape();
+}
+
 const Class StaticWithScope::class_ = {
     "WithTemplate",
     JSCLASS_HAS_RESERVED_SLOTS(StaticWithScope::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS
 };
 
 StaticWithScope*
 StaticWithScope::create(ExclusiveContext* cx)
 {
     return NewObjectWithNullTaggedProto<StaticWithScope>(cx, TenuredObject, BaseShape::DELEGATE);
 }
 
 template<XDRMode mode>
 bool
-js::XDRStaticWithScope(XDRState<mode>* xdr, HandleObject enclosingScope,
+js::XDRStaticWithScope(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
                        MutableHandle<StaticWithScope*> objp)
 {
     if (mode == XDR_DECODE) {
         JSContext* cx = xdr->cx();
         Rooted<StaticWithScope*> obj(cx, StaticWithScope::create(cx));
         if (!obj)
             return false;
         obj->initEnclosingScope(enclosingScope);
@@ -146,20 +203,22 @@ js::XDRStaticWithScope(XDRState<mode>* x
     // For encoding, there is nothing to do.  The only information that is
     // encoded by a StaticWithScope is its presence on the scope chain, and the
     // script XDR handler already takes care of that.
 
     return true;
 }
 
 template bool
-js::XDRStaticWithScope(XDRState<XDR_ENCODE>*, HandleObject, MutableHandle<StaticWithScope*>);
+js::XDRStaticWithScope(XDRState<XDR_ENCODE>*, Handle<StaticScope*>,
+                       MutableHandle<StaticWithScope*>);
 
 template bool
-js::XDRStaticWithScope(XDRState<XDR_DECODE>*, HandleObject, MutableHandle<StaticWithScope*>);
+js::XDRStaticWithScope(XDRState<XDR_DECODE>*, Handle<StaticScope*>,
+                       MutableHandle<StaticWithScope*>);
 
 
 /*****************************************************************************/
 
 Shape*
 js::ScopeCoordinateToStaticScopeShape(JSScript* script, jsbytecode* pc)
 {
     MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPECOORD);
@@ -435,16 +494,19 @@ CallObject::createHollowForDebug(JSConte
 
 const Class CallObject::class_ = {
     "Call",
     JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS)
 };
 
 /*****************************************************************************/
 
+static_assert(StaticModuleScope::RESERVED_SLOTS == ModuleEnvironmentObject::RESERVED_SLOTS,
+              "static module scopes and dynamic module environments share a Class");
+
 const Class ModuleEnvironmentObject::class_ = {
     "ModuleEnvironmentObject",
     JSCLASS_HAS_RESERVED_SLOTS(ModuleEnvironmentObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
     nullptr,        /* addProperty */
     nullptr,        /* delProperty */
     nullptr,        /* getProperty */
     nullptr,        /* setProperty */
@@ -475,17 +537,18 @@ const Class ModuleEnvironmentObject::cla
 
 /* static */ ModuleEnvironmentObject*
 ModuleEnvironmentObject::create(ExclusiveContext* cx, HandleModuleObject module)
 {
     RootedScript script(cx, module->script());
     RootedShape shape(cx, script->bindings.callObjShape());
     MOZ_ASSERT(shape->getObjectClass() == &class_);
 
-    RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr)));
+    Rooted<TaggedProto> proto(cx, module->staticScope());
+    RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, proto));
     if (!group)
         return nullptr;
 
     gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
     MOZ_ASSERT(CanBeFinalizedInBackground(kind, &class_));
     kind = gc::GetBackgroundAllocKind(kind);
 
     JSObject* obj = JSObject::create(cx, kind, TenuredHeap, shape, group);
@@ -520,16 +583,22 @@ ModuleEnvironmentObject::create(Exclusiv
 }
 
 ModuleObject&
 ModuleEnvironmentObject::module()
 {
     return getReservedSlot(MODULE_SLOT).toObject().as<ModuleObject>();
 }
 
+StaticModuleScope&
+ModuleEnvironmentObject::staticScope()
+{
+    return getProto()->as<StaticModuleScope>();
+}
+
 IndirectBindingMap&
 ModuleEnvironmentObject::importBindings()
 {
     return module().importBindings();
 }
 
 bool
 ModuleEnvironmentObject::createImportBinding(JSContext* cx, HandleAtom importName,
@@ -703,34 +772,33 @@ DeclEnvObject::create(JSContext* cx, Han
     if (!obj)
         return nullptr;
 
     obj->setEnclosingScope(enclosing);
     obj->setFixedSlot(lambdaSlot(), ObjectValue(*callee));
     return obj;
 }
 
-static JSObject*
-CloneStaticWithScope(JSContext* cx, HandleObject enclosingScope, Handle<StaticWithScope*> srcWith)
+static StaticWithScope*
+CloneStaticWithScope(JSContext* cx, Handle<StaticScope*> enclosingScope,
+                     Handle<StaticWithScope*> srcWith)
 {
     Rooted<StaticWithScope*> clone(cx, StaticWithScope::create(cx));
     if (!clone)
         return nullptr;
 
     clone->initEnclosingScope(enclosingScope);
 
     return clone;
 }
 
 DynamicWithObject*
 DynamicWithObject::create(JSContext* cx, HandleObject object, HandleObject enclosing,
-                          HandleObject staticWith, WithKind kind)
+                          Handle<StaticWithScope*> staticWith, WithKind kind)
 {
-    MOZ_ASSERT(staticWith->is<StaticWithScope>());
-
     Rooted<TaggedProto> proto(cx, TaggedProto(staticWith));
     Rooted<DynamicWithObject*> obj(cx);
     obj = NewObjectWithGivenTaggedProto<DynamicWithObject>(cx, proto, GenericObject,
                                                            BaseShape::DELEGATE);
     if (!obj)
         return nullptr;
 
     Value thisv = GetThisValue(object);
@@ -843,26 +911,26 @@ const Class DynamicWithObject::class_ = 
         nullptr, nullptr,    /* watch/unwatch */
         nullptr,             /* getElements */
         nullptr,             /* enumerate (native enumeration of target doesn't work) */
         nullptr,
     }
 };
 
 /* static */ StaticEvalScope*
-StaticEvalScope::create(JSContext* cx, HandleObject enclosing)
+StaticEvalScope::create(JSContext* cx, Handle<StaticScope*> enclosing)
 {
-    StaticEvalScope* obj =
+    StaticEvalScope* scope =
         NewObjectWithNullTaggedProto<StaticEvalScope>(cx, TenuredObject, BaseShape::DELEGATE);
-    if (!obj)
+    if (!scope)
         return nullptr;
 
-    obj->setReservedSlot(ENCLOSING_SCOPE_SLOT, ObjectOrNullValue(enclosing));
-    obj->setReservedSlot(STRICT_SLOT, BooleanValue(false));
-    return obj;
+    scope->initEnclosingScope(enclosing);
+    scope->setReservedSlot(STRICT_SLOT, BooleanValue(false));
+    return scope;
 }
 
 const Class StaticEvalScope::class_ = {
     "StaticEval",
     JSCLASS_HAS_RESERVED_SLOTS(StaticEvalScope::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS
 };
 
@@ -976,20 +1044,20 @@ ClonedBlockObject::createGlobal(JSContex
     if (!lexical)
         return nullptr;
     if (!JSObject::setSingleton(cx, lexical))
         return nullptr;
     return lexical;
 }
 
 /* static */ ClonedBlockObject*
-ClonedBlockObject::createNonSyntactic(JSContext* cx, HandleObject enclosingStatic,
+ClonedBlockObject::createNonSyntactic(JSContext* cx,
+                                      Handle<StaticNonSyntacticScope*> enclosingStatic,
                                       HandleObject enclosingScope)
 {
-    MOZ_ASSERT(enclosingStatic->is<StaticNonSyntacticScope>());
     MOZ_ASSERT(!IsSyntacticScope(enclosingScope));
 
     Rooted<StaticBlockScope*> staticLexical(cx, StaticBlockScope::create(cx));
     if (!staticLexical)
         return nullptr;
 
     staticLexical->setLocalOffset(UINT32_MAX);
     staticLexical->initEnclosingScope(enclosingStatic);
@@ -1098,17 +1166,17 @@ const Class ClonedBlockObject::class_ = 
         nullptr,          /* getElements */
         nullptr,          /* enumerate (native enumeration of target doesn't work) */
         nullptr,
     }
 };
 
 template<XDRMode mode>
 bool
-js::XDRStaticBlockScope(XDRState<mode>* xdr, HandleObject enclosingScope,
+js::XDRStaticBlockScope(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
                         MutableHandle<StaticBlockScope*> objp)
 {
     /* NB: Keep this in sync with CloneStaticBlockScope. */
 
     JSContext* cx = xdr->cx();
 
     Rooted<StaticBlockScope*> obj(cx);
     uint32_t count = 0, offset = 0;
@@ -1204,23 +1272,26 @@ js::XDRStaticBlockScope(XDRState<mode>* 
             if (!xdr->codeUint32(&propFlags))
                 return false;
         }
     }
     return true;
 }
 
 template bool
-js::XDRStaticBlockScope(XDRState<XDR_ENCODE>*, HandleObject, MutableHandle<StaticBlockScope*>);
+js::XDRStaticBlockScope(XDRState<XDR_ENCODE>*, Handle<StaticScope*>,
+                        MutableHandle<StaticBlockScope*>);
 
 template bool
-js::XDRStaticBlockScope(XDRState<XDR_DECODE>*, HandleObject, MutableHandle<StaticBlockScope*>);
-
-static JSObject*
-CloneStaticBlockScope(JSContext* cx, HandleObject enclosingScope, Handle<StaticBlockScope*> srcBlock)
+js::XDRStaticBlockScope(XDRState<XDR_DECODE>*, Handle<StaticScope*>,
+                        MutableHandle<StaticBlockScope*>);
+
+static StaticBlockScope*
+CloneStaticBlockScope(JSContext* cx, Handle<StaticScope*> enclosingScope,
+                      Handle<StaticBlockScope*> srcBlock)
 {
     /* NB: Keep this in sync with XDRStaticBlockScope. */
 
     Rooted<StaticBlockScope*> clone(cx, StaticBlockScope::create(cx));
     if (!clone)
         return nullptr;
 
     clone->initEnclosingScope(enclosingScope);
@@ -1251,26 +1322,26 @@ CloneStaticBlockScope(JSContext* cx, Han
     if (!srcBlock->isExtensible()) {
         if (!clone->makeNonExtensible(cx))
             return nullptr;
     }
 
     return clone;
 }
 
-JSObject*
-js::CloneNestedScopeObject(JSContext* cx, HandleObject enclosingScope,
+NestedStaticScope*
+js::CloneNestedScopeObject(JSContext* cx, Handle<StaticScope*> enclosingScope,
                            Handle<NestedStaticScope*> srcBlock)
 {
     if (srcBlock->is<StaticBlockScope>()) {
-        Rooted<StaticBlockScope*> blockScope(cx, &srcBlock->as<StaticBlockScope>());
-        return CloneStaticBlockScope(cx, enclosingScope, blockScope);
+        return CloneStaticBlockScope(cx, enclosingScope,
+                                     HandleObject(srcBlock).as<StaticBlockScope>());
     } else {
-        Rooted<StaticWithScope*> withScope(cx, &srcBlock->as<StaticWithScope>());
-        return CloneStaticWithScope(cx, enclosingScope, withScope);
+        return CloneStaticWithScope(cx, enclosingScope,
+                                    HandleObject(srcBlock).as<StaticWithScope>());
     }
 }
 
 /* static */ RuntimeLexicalErrorObject*
 RuntimeLexicalErrorObject::create(JSContext* cx, HandleObject enclosing, unsigned errorNumber)
 {
     RuntimeLexicalErrorObject* obj =
         NewObjectWithNullTaggedProto<RuntimeLexicalErrorObject>(cx, GenericObject,
@@ -1386,17 +1457,17 @@ ScopeIter::ScopeIter(JSContext* cx, cons
                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
   : ssi_(cx, si.ssi_),
     scope_(cx, si.scope_),
     frame_(si.frame_)
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 }
 
-ScopeIter::ScopeIter(JSContext* cx, JSObject* scope, JSObject* staticScope
+ScopeIter::ScopeIter(JSContext* cx, JSObject* scope, StaticScope* staticScope
                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
   : ssi_(cx, staticScope),
     scope_(cx, scope),
     frame_(NullFramePtr())
 {
     settle();
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 }
@@ -1471,17 +1542,17 @@ ScopeIter::settle()
     // settled on a static scope.
     if (frame_ && (ssi_.done() || maybeStaticScope() == frame_.script()->enclosingStaticScope()))
         frame_ = NullFramePtr();
 
 #ifdef DEBUG
     if (!ssi_.done() && hasAnyScopeObject()) {
         switch (ssi_.type()) {
           case StaticScopeIter<CanGC>::Module:
-            MOZ_ASSERT(scope_->as<ModuleEnvironmentObject>().module() == ssi_.module());
+            MOZ_ASSERT(scope_->as<ModuleEnvironmentObject>().staticScope() == ssi_.module());
             break;
           case StaticScopeIter<CanGC>::Function:
             MOZ_ASSERT(scope_->as<CallObject>().callee().nonLazyScript() == ssi_.funScript());
             break;
           case StaticScopeIter<CanGC>::Block:
             MOZ_ASSERT(scope_->as<ClonedBlockObject>().staticBlock() == staticBlock());
             break;
           case StaticScopeIter<CanGC>::With:
@@ -1542,35 +1613,30 @@ ScopeIter::type() const
 
 ScopeObject&
 ScopeIter::scope() const
 {
     MOZ_ASSERT(hasAnyScopeObject());
     return scope_->as<ScopeObject>();
 }
 
-JSObject*
+StaticScope*
 ScopeIter::maybeStaticScope() const
 {
     if (ssi_.done())
         return nullptr;
 
     switch (ssi_.type()) {
       case StaticScopeIter<CanGC>::Function:
-        return &fun();
       case StaticScopeIter<CanGC>::Module:
-        return &module();
       case StaticScopeIter<CanGC>::Block:
-        return &staticBlock();
       case StaticScopeIter<CanGC>::With:
-        return &staticWith();
       case StaticScopeIter<CanGC>::Eval:
-        return &staticEval();
       case StaticScopeIter<CanGC>::NonSyntactic:
-        return &staticNonSyntactic();
+        return ssi_.staticScope();
       case StaticScopeIter<CanGC>::NamedLambda:
         MOZ_CRASH("named lambda static scopes should have been skipped");
       default:
         MOZ_CRASH("bad SSI type");
     }
 }
 
 /* static */ HashNumber
@@ -2573,17 +2639,17 @@ DebugScopes::hasDebugScope(JSContext* cx
 }
 
 bool
 DebugScopes::addDebugScope(JSContext* cx, const ScopeIter& si, DebugScopeObject& debugScope)
 {
     MOZ_ASSERT(!si.hasSyntacticScopeObject());
     MOZ_ASSERT(cx->compartment() == debugScope.compartment());
     // Generators should always reify their scopes.
-    MOZ_ASSERT_IF(si.type() == ScopeIter::Call, !si.fun().isGenerator());
+    MOZ_ASSERT_IF(si.type() == ScopeIter::Call, !si.fun().function().isGenerator());
 
     if (!CanUseDebugScopeMaps(cx))
         return true;
 
     DebugScopes* scopes = ensureCompartmentData(cx);
     if (!scopes)
         return false;
 
@@ -2931,17 +2997,18 @@ GetDebugScopeForMissing(JSContext* cx, c
      */
     DebugScopeObject* debugScope = nullptr;
     switch (si.type()) {
       case ScopeIter::Module:
           MOZ_CRASH(); // TODO: Implement debug scopes for modules.
           break;
 
       case ScopeIter::Call: {
-        RootedFunction callee(cx, &si.fun());
+        RootedFunction callee(cx, &si.fun().function());
+
         // Generators should always reify their scopes.
         MOZ_ASSERT(!callee->isGenerator());
 
         Rooted<CallObject*> callobj(cx);
         if (si.withinInitialFrame())
             callobj = CallObject::createForFunction(cx, si.initialFrame());
         else
             callobj = CallObject::createHollowForDebug(cx, callee);
@@ -3088,17 +3155,17 @@ js::CreateScopeObjectsForScopeChain(JSCo
         assertSameCompartment(cx, scopeChain[i]);
         MOZ_ASSERT(!scopeChain[i]->is<GlobalObject>());
     }
 #endif
 
     // Construct With object wrappers for the things on this scope
     // chain and use the result as the thing to scope the function to.
     Rooted<StaticWithScope*> staticWith(cx);
-    RootedObject staticEnclosingScope(cx);
+    Rooted<StaticWithScope*> staticEnclosingScope(cx);
     Rooted<DynamicWithObject*> dynamicWith(cx);
     RootedObject dynamicEnclosingScope(cx, dynamicTerminatingScope);
     for (size_t i = scopeChain.length(); i > 0; ) {
         staticWith = StaticWithScope::create(cx);
         if (!staticWith)
             return false;
         staticWith->initEnclosingScope(staticEnclosingScope);
         staticEnclosingScope = staticWith;
@@ -3110,64 +3177,64 @@ js::CreateScopeObjectsForScopeChain(JSCo
         dynamicEnclosingScope = dynamicWith;
     }
 
     dynamicScopeObj.set(dynamicEnclosingScope);
     return true;
 }
 
 bool
-js::HasNonSyntacticStaticScopeChain(JSObject* staticScope)
+js::HasNonSyntacticStaticScopeChain(StaticScope* staticScope)
 {
     for (StaticScopeIter<NoGC> ssi(staticScope); !ssi.done(); ssi++) {
         // If we hit a function scope, we can short circuit the logic, as
         // scripts cache whether they are under a non-syntactic scope.
         if (ssi.type() == StaticScopeIter<NoGC>::Function)
             return ssi.funScript()->hasNonSyntacticScope();
         if (ssi.type() == StaticScopeIter<NoGC>::NonSyntactic)
             return true;
     }
     return false;
 }
 
 uint32_t
-js::StaticScopeChainLength(JSObject* staticScope)
+js::StaticScopeChainLength(StaticScope* staticScope)
 {
     uint32_t length = 0;
     for (StaticScopeIter<NoGC> ssi(staticScope); !ssi.done(); ssi++)
         length++;
     return length;
 }
 
 ModuleEnvironmentObject*
 js::GetModuleEnvironmentForScript(JSScript* script)
 {
     StaticScopeIter<NoGC> ssi(script->enclosingStaticScope());
     while (!ssi.done() && ssi.type() != StaticScopeIter<NoGC>::Module)
         ssi++;
     if (ssi.done())
         return nullptr;
 
-    return ssi.module().environment();
+    return ssi.module().moduleObject().environment();
 }
 
 bool
 js::GetThisValueForDebuggerMaybeOptimizedOut(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
                                              MutableHandleValue res)
 {
     for (ScopeIter si(cx, frame, pc); !si.done(); ++si) {
         if (si.type() == ScopeIter::Module) {
             res.setUndefined();
             return true;
         }
 
-        if (si.type() != ScopeIter::Call || si.fun().hasLexicalThis())
+        if (si.type() != ScopeIter::Call || si.fun().function().hasLexicalThis())
             continue;
 
-        RootedScript script(cx, si.fun().nonLazyScript());
+        RootedScript script(cx, si.fun().function().nonLazyScript());
 
         if (!script->functionHasThisBinding()) {
             MOZ_ASSERT(!script->isDerivedClassConstructor(),
                        "Derived class constructors always have a this-binding");
 
             // If we're still inside `frame`, we can use the this-value passed
             // to it, if it does not require boxing.
             if (si.withinInitialFrame() && (frame.thisArgument().isObject() || script->strict()))
@@ -3341,32 +3408,34 @@ js::CheckEvalDeclarationConflicts(JSCont
     return CheckVarNameConflictsInScope<CallObject>(cx, script, varObj);
 }
 
 #ifdef DEBUG
 
 void
 js::DumpStaticScopeChain(JSScript* script)
 {
-    DumpStaticScopeChain(script->enclosingStaticScope());
+    DumpStaticScopeChain(script->staticScope());
 }
 
 void
-js::DumpStaticScopeChain(JSObject* staticScope)
+js::DumpStaticScopeChain(StaticScope* staticScope)
 {
     for (StaticScopeIter<NoGC> ssi(staticScope); !ssi.done(); ssi++) {
         switch (ssi.type()) {
           case StaticScopeIter<NoGC>::Module:
             fprintf(stdout, "module [%p]", &ssi.module());
             break;
           case StaticScopeIter<NoGC>::Function:
-            if (ssi.fun().isBeingParsed())
-                fprintf(stdout, "funbox [%p fun=%p]", ssi.maybeFunctionBox(), &ssi.fun());
-            else
-                fprintf(stdout, "function [%p]", &ssi.fun());
+            if (ssi.fun().function().isBeingParsed()) {
+                fprintf(stdout, "funbox [%p box=%p fun=%p]",
+                        &ssi.fun(), ssi.maybeFunctionBox(), &ssi.fun().function());
+            } else {
+                fprintf(stdout, "function [%p fun=%p]", &ssi.fun(), &ssi.fun().function());
+            }
             break;
           case StaticScopeIter<NoGC>::Block:
             fprintf(stdout, "block [%p]", &ssi.block());
             break;
           case StaticScopeIter<NoGC>::With:
             fprintf(stdout, "with [%p]", &ssi.staticWith());
             break;
           case StaticScopeIter<NoGC>::NamedLambda:
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -45,21 +45,21 @@ typedef Handle<ModuleObject*> HandleModu
  * divided into scopes that have direct correspondence to program text (i.e.,
  * syntactic) and ones used internally for scope walking (i.e., non-syntactic).
  *
  * The following are syntactic static scopes:
  *
  * StaticBlockScope
  *   Scope for non-function body blocks. e.g., |{ let x; }|
  *
- * JSFunction
+ * StaticFunctionScope
  *   Scope for function bodies. e.g., |function f() { var x; let y; }|
  *
- * ModuleObject
- *   Scope for moddules.
+ * StaticModuleScope
+ *   Scope for modules.
  *
  * StaticWithScope
  *   Scope for |with|. e.g., |with ({}) { ... }|
  *
  * StaticEvalScope
  *   Scope for |eval|. e.g., |eval(...)|
  *
  * The following are non-syntactic static scopes:
@@ -77,26 +77,24 @@ typedef Handle<ModuleObject*> HandleModu
  * |JSScript::enclosingStaticScope()|.
  */
 class StaticScope : public NativeObject
 {
   public:
     static const uint32_t ENCLOSING_SCOPE_SLOT = 0;
     static const unsigned RESERVED_SLOTS = ENCLOSING_SCOPE_SLOT + 1;
 
-    inline JSObject* enclosingScope() const {
-        return getFixedSlot(ENCLOSING_SCOPE_SLOT).toObjectOrNull();
+    inline StaticScope* enclosingScope() const;
+
+    void initEnclosingScope(StaticScope* scope) {
+        MOZ_ASSERT(getReservedSlot(ENCLOSING_SCOPE_SLOT).isUndefined());
+        setReservedSlot(ENCLOSING_SCOPE_SLOT, ObjectOrNullValue(scope));
     }
 
-    void initEnclosingScope(JSObject* obj) {
-        MOZ_ASSERT(getReservedSlot(ENCLOSING_SCOPE_SLOT).isUndefined());
-        setReservedSlot(ENCLOSING_SCOPE_SLOT, ObjectOrNullValue(obj));
-    }
-
-    void setEnclosingScope(HandleObject obj);
+    void setEnclosingScope(StaticScope* obj);
 };
 
 class NestedStaticScope : public StaticScope
 {
   public:
     /*
      * A refinement of enclosingScope that returns nullptr if the enclosing
      * scope is not a NestedScopeObject.
@@ -104,17 +102,17 @@ class NestedStaticScope : public StaticS
     inline NestedStaticScope* enclosingNestedScope() const;
 
     /*
      * Note: in the case of hoisting, this prev-link will not ultimately be
      * the same as enclosingNestedScope; initEnclosingNestedScope must be
      * called separately in the emitter. 'reset' is just for asserting
      * stackiness.
      */
-    void initEnclosingScopeFromParser(JSObject* prev) {
+    void initEnclosingScopeFromParser(StaticScope* prev) {
         setReservedSlot(ENCLOSING_SCOPE_SLOT, ObjectOrNullValue(prev));
     }
 
     void resetEnclosingScopeFromParser() {
         setReservedSlot(ENCLOSING_SCOPE_SLOT, UndefinedValue());
     }
 };
 
@@ -158,34 +156,29 @@ class StaticBlockScope : public NestedSt
     static StaticBlockScope* create(ExclusiveContext* cx);
 
     /*
      * Among block scopes, only the global lexical scope is extensible.
      * (Non-syntactic scopes and some function scopes are also extensible.)
      */
     bool isExtensible() const;
 
-    /* See StaticScopeIter comment. */
-    JSObject* enclosingStaticScope() const {
-        return getFixedSlot(ENCLOSING_SCOPE_SLOT).toObjectOrNull();
-    }
-
     /*
      * Return the index (in the range [0, numVariables()) corresponding to the
      * given shape of a block object.
      */
     uint32_t shapeToIndex(const Shape& shape) {
         uint32_t slot = shape.slot();
         MOZ_ASSERT(slot - RESERVED_SLOTS < numVariables());
         return slot - RESERVED_SLOTS;
     }
 
     /*
      * A refinement of enclosingStaticScope that returns nullptr if the enclosing
-     * static scope is a JSFunction.
+     * static scope is a StaticFunctionScope.
      */
     inline StaticBlockScope* enclosingBlock() const;
 
     uint32_t localOffset() {
         return getReservedSlot(LOCAL_OFFSET_SLOT).toPrivateUint32();
     }
 
     // Return the local corresponding to the 'var'th binding where 'var' is in the
@@ -208,17 +201,17 @@ class StaticBlockScope : public NestedSt
     // in the range [RESERVED_SLOTS, RESERVED_SLOTS + numVariables()).
     uint32_t localIndexToSlot(uint32_t local) {
         MOZ_ASSERT(local >= localOffset());
         return blockIndexToSlot(local - localOffset());
     }
 
     /*
      * A let binding is aliased if accessed lexically by nested functions or
-     * dynamically through dynamic name lookup (eval, with, function::, etc).
+     * dynamically through dynamic name lookup (eval, with, etc).
      */
     bool isAliased(unsigned i) {
         return slotValue(i).isTrue();
     }
 
     // Look up if the block has an aliased binding named |name|.
     Shape* lookupAliasedName(PropertyName* name);
 
@@ -227,17 +220,20 @@ class StaticBlockScope : public NestedSt
      * variable of the block isAliased.
      */
     bool needsClone() {
         return numVariables() > 0 && !getSlot(RESERVED_SLOTS).isFalse();
     }
 
     // Is this the static global lexical scope?
     bool isGlobal() const {
-        return !enclosingStaticScope();
+        // This method is called from js::gc::MergeCompartments() on scopes
+        // that may be otherwise unreachable (!) and whose enclosing scope slot
+        // may be `undefined`.
+        return getFixedSlot(ENCLOSING_SCOPE_SLOT).isNull();
     }
 
     bool isSyntactic() const {
         return !isExtensible() || isGlobal();
     }
 
     /* Frontend-only functions ***********************************************/
 
@@ -295,48 +291,83 @@ class StaticBlockScope : public NestedSt
      * remove INDEX_LIMIT.
      */
     static const unsigned LOCAL_INDEX_LIMIT = JS_BIT(16);
 
     static Shape* addVar(ExclusiveContext* cx, Handle<StaticBlockScope*> block, HandleId id,
                          bool constant, unsigned index, bool* redeclared);
 };
 
+class StaticFunctionScope : public StaticScope
+{
+    static const unsigned FUNCTION_OBJECT_SLOT = StaticScope::RESERVED_SLOTS;
+
+  public:
+    static const unsigned RESERVED_SLOTS = FUNCTION_OBJECT_SLOT + 1;
+    static const Class class_;
+
+    static StaticFunctionScope* create(ExclusiveContext* cx, HandleFunction functionObject,
+                                       Handle<StaticScope*> enclosingScope);
+
+    JSFunction& function() {
+        return getFixedSlot(FUNCTION_OBJECT_SLOT).toObject().as<JSFunction>();
+    }
+
+    bool isNamedLambda() { return function().isNamedLambda(); }
+
+    Shape* environmentShape() {
+        return function().nonLazyScript()->callObjShape();
+    }
+};
+
+// The top-level scope of a module.
+// Shares ModuleEnvironmentObject::class_.
+class StaticModuleScope : public StaticScope
+{
+    static const unsigned MODULE_OBJECT_SLOT = StaticScope::RESERVED_SLOTS;
+
+  public:
+    static const unsigned RESERVED_SLOTS = MODULE_OBJECT_SLOT + 1;
+
+    static StaticModuleScope* create(ExclusiveContext* cx, Handle<ModuleObject*> moduleObject,
+                                     Handle<StaticScope*> enclosingScope);
+
+    ModuleObject& moduleObject();
+    JSScript* script();
+    Shape* environmentShape();
+};
+
 // Represents the lexical scope of a 'with' statement.
 class StaticWithScope : public NestedStaticScope
 {
   public:
     static const Class class_;
 
     static StaticWithScope* create(ExclusiveContext* cx);
 };
 
 template <XDRMode mode>
 bool
-XDRStaticWithScope(XDRState<mode>* xdr, HandleObject enclosingScope,
+XDRStaticWithScope(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
                    MutableHandle<StaticWithScope*> objp);
 
 /*
  * Static eval scope placeholder objects on the static scope chain. Created at
  * the time of compiling the eval script, and set as its static enclosing
  * scope.
  */
 class StaticEvalScope : public StaticScope
 {
     static const uint32_t STRICT_SLOT = StaticScope::RESERVED_SLOTS;
     static const unsigned RESERVED_SLOTS = STRICT_SLOT + 1;
 
   public:
     static const Class class_;
 
-    static StaticEvalScope* create(JSContext* cx, HandleObject enclosing);
-
-    JSObject* enclosingScopeForStaticScopeIter() {
-        return getReservedSlot(ENCLOSING_SCOPE_SLOT).toObjectOrNull();
-    }
+    static StaticEvalScope* create(JSContext* cx, Handle<StaticScope*> enclosing);
 
     void setStrict() {
         setReservedSlot(STRICT_SLOT, BooleanValue(true));
     }
 
     bool isStrict() const {
         return getReservedSlot(STRICT_SLOT).isTrue();
     }
@@ -453,93 +484,79 @@ class StaticEvalScope : public StaticSco
  */
 class StaticNonSyntacticScope : public StaticScope
 {
   public:
     static const unsigned RESERVED_SLOTS = StaticScope::RESERVED_SLOTS;
     static const Class class_;
 
     static StaticNonSyntacticScope* create(JSContext* cx, HandleObject enclosing);
-
-    JSObject* enclosingScopeForStaticScopeIter() {
-        return getReservedSlot(ENCLOSING_SCOPE_SLOT).toObjectOrNull();
-    }
 };
 
 template <AllowGC allowGC>
 class StaticScopeIter
 {
-    typename MaybeRooted<JSObject*, allowGC>::RootType obj;
+    typename MaybeRooted<StaticScope*, allowGC>::RootType scope;
     bool onNamedLambda;
 
-    static bool IsStaticScope(JSObject* obj) {
-        return obj->is<StaticBlockScope>() ||
-               obj->is<StaticWithScope>() ||
-               obj->is<StaticEvalScope>() ||
-               obj->is<StaticNonSyntacticScope>() ||
-               obj->is<JSFunction>() ||
-               obj->is<ModuleObject>();
-    }
-
   public:
-    StaticScopeIter(ExclusiveContext* cx, JSObject* obj)
-      : obj(cx, obj), onNamedLambda(false)
+    StaticScopeIter(ExclusiveContext* cx, StaticScope* scope)
+      : scope(cx, scope), onNamedLambda(false)
     {
         static_assert(allowGC == CanGC,
                       "the context-accepting constructor should only be used "
                       "in CanGC code");
-        MOZ_ASSERT_IF(obj, IsStaticScope(obj));
+        MOZ_ASSERT_IF(scope, scope->is<StaticScope>());
     }
 
     StaticScopeIter(ExclusiveContext* cx, const StaticScopeIter<CanGC>& ssi)
-      : obj(cx, ssi.obj), onNamedLambda(ssi.onNamedLambda)
+      : scope(cx, ssi.scope), onNamedLambda(ssi.onNamedLambda)
     {
         JS_STATIC_ASSERT(allowGC == CanGC);
     }
 
-    explicit StaticScopeIter(JSObject* obj)
-      : obj((ExclusiveContext*) nullptr, obj), onNamedLambda(false)
+    explicit StaticScopeIter(StaticScope* scope)
+      : scope((ExclusiveContext*) nullptr, scope), onNamedLambda(false)
     {
         static_assert(allowGC == NoGC,
                       "the constructor not taking a context should only be "
                       "used in NoGC code");
-        MOZ_ASSERT_IF(obj, IsStaticScope(obj));
+        MOZ_ASSERT_IF(scope, scope->is<StaticScope>());
     }
 
     explicit StaticScopeIter(const StaticScopeIter<NoGC>& ssi)
-      : obj((ExclusiveContext*) nullptr, ssi.obj), onNamedLambda(ssi.onNamedLambda)
+      : scope((ExclusiveContext*) nullptr, ssi.scope), onNamedLambda(ssi.onNamedLambda)
     {
         static_assert(allowGC == NoGC,
                       "the constructor not taking a context should only be "
                       "used in NoGC code");
     }
 
-    bool done() const { return !obj; }
+    bool done() const { return !scope; }
     void operator++(int);
 
-    JSObject* staticScope() const { MOZ_ASSERT(!done()); return obj; }
+    StaticScope* staticScope() const { MOZ_ASSERT(!done()); return scope; }
 
     // Return whether this static scope will have a syntactic scope (i.e. a
     // ScopeObject that isn't a non-syntactic With or
     // NonSyntacticVariablesObject) on the dynamic scope chain.
     bool hasSyntacticDynamicScopeObject() const;
     Shape* scopeShape() const;
 
     enum Type { Module, Function, Block, With, NamedLambda, Eval, NonSyntactic };
     Type type() const;
 
     StaticBlockScope& block() const;
+    StaticModuleScope& module() const;
     StaticWithScope& staticWith() const;
     StaticEvalScope& eval() const;
     StaticNonSyntacticScope& nonSyntactic() const;
+    StaticFunctionScope& fun() const;
     JSScript* funScript() const;
-    JSFunction& fun() const;
     frontend::FunctionBox* maybeFunctionBox() const;
-    JSScript* moduleScript() const;
-    ModuleObject& module() const;
 };
 
 
 /*****************************************************************************/
 
 /*
  * A "scope coordinate" describes how to get from head of the scope chain to a
  * given lexically-enclosing variable. A scope coordinate has two dimensions:
@@ -597,52 +614,56 @@ ScopeCoordinateFunctionScript(JSScript* 
 
 /*** Scope objects *******************************************************************************/
 
 /*
  * Scope objects are technically real JSObjects but only belong on the scope
  * chain (that is, fp->scopeChain() or fun->environment()). The hierarchy of
  * scope objects is:
  *
- *   JSObject                       Generic object
- *     |   |
- *     |  StaticScope               Created at compile time
- *     |   |   |   |
- *     |   |   |  StaticNonSyntacticScope   See "Non-syntactic scopes"
- *     |   |   |
- *     |   |  StaticEvalScope       Placeholder so eval scopes may be iterated through
- *     |   |
- *     |  NestedStaticScope         Enclosing scope is in the same JSScript
- *     |   |   |
- *     |   |  StaticBlockScope      See NB
- *     |   |
- *     |  StaticWithScope           Template for "with" object in static scope chain
+ *    JSObject                          Generic object
  *     |
- *   ScopeObject                    Engine-internal scope
- *     |   |   |
- *     |   |  DeclEnvObject         Holds name of recursive/needsCallObject named lambda
+ *     +--StaticScope                   Created at compile time
+ *     |   |
+ *     |   +--StaticNonSyntacticScope   See "Non-syntactic scopes"
  *     |   |
- *     |  LexicalScopeBase          Shared base for function and modules scopes
- *     |   |   |
- *     |   |  CallObject            Scope of entire function or strict eval
+ *     |   +--StaticEvalScope           Placeholder so eval scopes may be iterated through
+ *     |   |
+ *     |   +--StaticFunctionScope       Scope in a function
+ *     |   |
+ *     |   +--StaticModuleScope         Toplevel scope in a module
  *     |   |
- *     |  ModuleEnvironmentObject   Module top-level scope on run-time scope chain
+ *     |   +--NestedStaticScope         Enclosing scope is in the same JSScript
+ *     |       |
+ *     |       +--StaticBlockScope      See "N.B." below.
+ *     |       |
+ *     |       +--StaticWithScope       Template for "with" object in static scope chain
  *     |
- *   NestedScopeObject              Statement scopes; don't cross script boundaries
- *     |   |
- *     |  DynamicWithObject         Run-time "with" object on scope chain
- *     |
- *   ClonedBlockObject              let, switch, catch, for
+ *     +--ScopeObject                   Engine-internal scope
+ *         |
+ *         +--DeclEnvObject             Holds name of recursive/needsCallObject named lambda
+ *         |
+ *         +--LexicalScopeBase          Shared base for function and modules scopes
+ *         |   |
+ *         |   +--CallObject            Scope of entire function or strict eval
+ *         |   |
+ *         |   +--ModuleEnvironmentObject   Module top-level scope on run-time scope chain
+ *         |
+ *         +--NestedScopeObject         Statement scopes; don't cross script boundaries
+ *             |
+ *             +--ClonedBlockObject     let, switch, catch, for
+ *             |
+ *             +--DynamicWithObject     Run-time "with" object on scope chain
  *
  * This hierarchy represents more than just the interface hierarchy: reserved
  * slots in base classes are fixed for all derived classes. Thus, for example,
  * ScopeObject::enclosingScope() can simply access a fixed slot without further
  * dynamic type information.
  *
- * NB: Static block objects are a special case: these objects are created at
+ * N.B. Static block objects are a special case: these objects are created at
  * compile time to hold the shape/binding information from which block objects
  * are cloned at runtime. These objects should never escape into the wild and
  * support a restricted set of ScopeObject operations.
  *
  * See also "Debug scope objects" below.
  */
 
 class ScopeObject : public NativeObject
@@ -745,33 +766,23 @@ class CallObject : public LexicalScopeBa
 
     static CallObject* createForFunction(JSContext* cx, HandleObject enclosing, HandleFunction callee);
 
     static CallObject* createForFunction(JSContext* cx, AbstractFramePtr frame);
     static CallObject* createForStrictEval(JSContext* cx, AbstractFramePtr frame);
     static CallObject* createHollowForDebug(JSContext* cx, HandleFunction callee);
 
     /* True if this is for a strict mode eval frame. */
-    bool isForEval() const {
-        if (is<ModuleEnvironmentObject>())
-            return false;
-        MOZ_ASSERT(getFixedSlot(CALLEE_SLOT).isObjectOrNull());
-        MOZ_ASSERT_IF(getFixedSlot(CALLEE_SLOT).isObject(),
-                      getFixedSlot(CALLEE_SLOT).toObject().is<JSFunction>());
-        return getFixedSlot(CALLEE_SLOT).isNull();
-    }
+    inline bool isForEval() const;
 
     /*
      * Returns the function for which this CallObject was created. (This may
      * only be called if !isForEval.)
      */
-    JSFunction& callee() const {
-        MOZ_ASSERT(!is<ModuleEnvironmentObject>());
-        return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>();
-    }
+    inline JSFunction& callee() const;
 
     /* For jit access. */
     static size_t offsetOfCallee() {
         return getFixedSlotOffset(CALLEE_SLOT);
     }
 
     static size_t calleeSlot() {
         return CALLEE_SLOT;
@@ -784,16 +795,17 @@ class ModuleEnvironmentObject : public L
 
   public:
     static const Class class_;
 
     static const uint32_t RESERVED_SLOTS = 2;
 
     static ModuleEnvironmentObject* create(ExclusiveContext* cx, HandleModuleObject module);
     ModuleObject& module();
+    StaticModuleScope& staticScope();
     IndirectBindingMap& importBindings();
 
     bool createImportBinding(JSContext* cx, HandleAtom importName, HandleModuleObject module,
                              HandleAtom exportName);
 
     bool hasImportBinding(HandlePropertyName name);
 
     bool lookupImport(jsid name, ModuleEnvironmentObject** envOut, Shape** shapeOut);
@@ -880,18 +892,18 @@ class DynamicWithObject : public NestedS
     static const Class class_;
 
     enum WithKind {
         SyntacticWith,
         NonSyntacticWith
     };
 
     static DynamicWithObject*
-    create(JSContext* cx, HandleObject object, HandleObject enclosing, HandleObject staticWith,
-           WithKind kind = SyntacticWith);
+    create(JSContext* cx, HandleObject object, HandleObject enclosing,
+           Handle<StaticWithScope*> staticWith, WithKind kind = SyntacticWith);
 
     StaticWithScope& staticWith() const {
         return getProto()->as<StaticWithScope>();
     }
 
     /* Return the 'o' in 'with (o)'. */
     JSObject& object() const {
         return getReservedSlot(OBJECT_SLOT).toObject();
@@ -934,17 +946,18 @@ class ClonedBlockObject : public NestedS
                                      HandleObject enclosing);
 
   public:
     static ClonedBlockObject* create(JSContext* cx, Handle<StaticBlockScope*> block,
                                      AbstractFramePtr frame);
 
     static ClonedBlockObject* createGlobal(JSContext* cx, Handle<GlobalObject*> global);
 
-    static ClonedBlockObject* createNonSyntactic(JSContext* cx, HandleObject enclosingStatic,
+    static ClonedBlockObject* createNonSyntactic(JSContext* cx,
+                                                 Handle<StaticNonSyntacticScope*> enclosingStatic,
                                                  HandleObject enclosingScope);
 
     static ClonedBlockObject* createHollowForDebug(JSContext* cx,
                                                    Handle<StaticBlockScope*> block);
 
     /* Return the number of variables associated with this block. */
     uint32_t numVariables() const {
         // TODO: propertyCount() is O(n), use O(1) lastProperty()->slot() instead
@@ -1041,21 +1054,21 @@ class RuntimeLexicalErrorObject : public
 
     unsigned errorNumber() {
         return getReservedSlot(ERROR_SLOT).toInt32();
     }
 };
 
 template<XDRMode mode>
 bool
-XDRStaticBlockScope(XDRState<mode>* xdr, HandleObject enclosingScope,
+XDRStaticBlockScope(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
                     MutableHandle<StaticBlockScope*> objp);
 
-extern JSObject*
-CloneNestedScopeObject(JSContext* cx, HandleObject enclosingScope,
+extern NestedStaticScope*
+CloneNestedScopeObject(JSContext* cx, Handle<StaticScope*> enclosingScope,
                        Handle<NestedStaticScope*> src);
 
 
 /*****************************************************************************/
 
 // A scope iterator describes the active scopes starting from a dynamic scope,
 // static scope pair. This pair may be derived from the current point of
 // execution in a frame. If derived in such a fashion, the ScopeIter tracks
@@ -1076,17 +1089,17 @@ class MOZ_RAII ScopeIter
 
   public:
     // Constructing from a copy of an existing ScopeIter.
     ScopeIter(JSContext* cx, const ScopeIter& si
               MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
 
     // Constructing from a dynamic scope, static scope pair. All scopes are
     // considered not to be withinInitialFrame, since no frame is given.
-    ScopeIter(JSContext* cx, JSObject* scope, JSObject* staticScope
+    ScopeIter(JSContext* cx, JSObject* scope, StaticScope* staticScope
               MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
 
     // Constructing from a frame. Places the ScopeIter on the innermost scope
     // at pc.
     ScopeIter(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc
               MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
 
     inline bool done() const;
@@ -1100,23 +1113,23 @@ class MOZ_RAII ScopeIter
     Type type() const;
 
     inline bool hasNonSyntacticScopeObject() const;
     inline bool hasSyntacticScopeObject() const;
     inline bool hasAnyScopeObject() const;
     inline bool canHaveSyntacticScopeObject() const;
     ScopeObject& scope() const;
 
-    JSObject* maybeStaticScope() const;
+    StaticScope* maybeStaticScope() const;
     StaticBlockScope& staticBlock() const { return ssi_.block(); }
+    StaticModuleScope& module() const { return ssi_.module(); }
     StaticWithScope& staticWith() const { return ssi_.staticWith(); }
     StaticEvalScope& staticEval() const { return ssi_.eval(); }
     StaticNonSyntacticScope& staticNonSyntactic() const { return ssi_.nonSyntactic(); }
-    JSFunction& fun() const { return ssi_.fun(); }
-    ModuleObject& module() const { return ssi_.module(); }
+    StaticFunctionScope& fun() const { return ssi_.fun(); }
 
     bool withinInitialFrame() const { return !!frame_; }
     AbstractFramePtr initialFrame() const { MOZ_ASSERT(withinInitialFrame()); return frame_; }
     AbstractFramePtr maybeInitialFrame() const { return frame_; }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
@@ -1132,28 +1145,28 @@ class MOZ_RAII ScopeIter
 // frame. In other words, the provenance of the scope chain is from allocated
 // closures (i.e., allocation sites) and is irrecoverable from simple stack
 // inspection (i.e., call sites).
 class MissingScopeKey
 {
     friend class LiveScopeVal;
 
     AbstractFramePtr frame_;
-    JSObject* staticScope_;
+    StaticScope* staticScope_;
 
   public:
     explicit MissingScopeKey(const ScopeIter& si)
       : frame_(si.maybeInitialFrame()),
         staticScope_(si.maybeStaticScope())
     { }
 
     AbstractFramePtr frame() const { return frame_; }
-    JSObject* staticScope() const { return staticScope_; }
+    StaticScope* staticScope() const { return staticScope_; }
 
-    void updateStaticScope(JSObject* obj) { staticScope_ = obj; }
+    void updateStaticScope(StaticScope* scope) { staticScope_ = scope; }
     void updateFrame(AbstractFramePtr frame) { frame_ = frame; }
 
     // For use as hash policy.
     typedef MissingScopeKey Lookup;
     static HashNumber hash(MissingScopeKey sk);
     static bool match(MissingScopeKey sk1, MissingScopeKey sk2);
     bool operator!=(const MissingScopeKey& other) const {
         return frame_ != other.frame_ || staticScope_ != other.staticScope_;
@@ -1165,28 +1178,28 @@ class MissingScopeKey
 
 // The value in LiveScopeMap, mapped from by live scope objects.
 class LiveScopeVal
 {
     friend class DebugScopes;
     friend class MissingScopeKey;
 
     AbstractFramePtr frame_;
-    RelocatablePtrObject staticScope_;
+    RelocatablePtr<StaticScope*> staticScope_;
 
     static void staticAsserts();
 
   public:
     explicit LiveScopeVal(const ScopeIter& si)
       : frame_(si.initialFrame()),
         staticScope_(si.maybeStaticScope())
     { }
 
     AbstractFramePtr frame() const { return frame_; }
-    JSObject* staticScope() const { return staticScope_; }
+    StaticScope* staticScope() const { return staticScope_; }
 
     void updateFrame(AbstractFramePtr frame) { frame_ = frame; }
 
     bool needsSweep();
 };
 
 
 /*****************************************************************************/
@@ -1344,33 +1357,49 @@ template<>
 inline bool
 JSObject::is<js::StaticBlockScope>() const
 {
     return hasClass(&js::ClonedBlockObject::class_) && !getProto();
 }
 
 template<>
 inline bool
+JSObject::is<js::StaticModuleScope>() const
+{
+    return hasClass(&js::ModuleEnvironmentObject::class_) && !getProto();
+}
+
+template<>
+inline bool
 JSObject::is<js::NestedStaticScope>() const
 {
     return is<js::StaticBlockScope>() ||
            is<js::StaticWithScope>();
 }
 
 template<>
 inline bool
 JSObject::is<js::StaticScope>() const
 {
     return is<js::NestedStaticScope>() ||
+           is<js::StaticFunctionScope>() ||
+           is<js::StaticModuleScope>() ||
            is<js::StaticEvalScope>() ||
            is<js::StaticNonSyntacticScope>();
 }
 
 template<>
 inline bool
+JSObject::is<js::ModuleEnvironmentObject>() const
+{
+    return hasClass(&js::ModuleEnvironmentObject::class_) && !!getProto();
+}
+
+template<>
+inline bool
 JSObject::is<js::ClonedBlockObject>() const
 {
     return hasClass(&js::ClonedBlockObject::class_) && !!getProto();
 }
 
 template<>
 inline bool
 JSObject::is<js::NestedScopeObject>() const
@@ -1435,16 +1464,23 @@ IsExtensibleLexicalScope(JSObject* scope
 }
 
 inline bool
 IsGlobalLexicalScope(JSObject* scope)
 {
     return scope->is<ClonedBlockObject>() && scope->as<ClonedBlockObject>().isGlobal();
 }
 
+inline js::StaticScope*
+js::StaticScope::enclosingScope() const
+{
+    JSObject *obj = getFixedSlot(ENCLOSING_SCOPE_SLOT).toObjectOrNull();
+    return obj ? &obj->as<StaticScope>() : nullptr;
+}
+
 inline NestedStaticScope*
 NestedStaticScope::enclosingNestedScope() const
 {
     JSObject* obj = getReservedSlot(ENCLOSING_SCOPE_SLOT).toObjectOrNull();
     return obj && obj->is<NestedStaticScope>()
            ? &obj->as<NestedStaticScope>()
            : nullptr;
 }
@@ -1528,23 +1564,41 @@ ScopeIter::enclosingScope() const
     // ScopeObjects and non-ScopeObjects cannot be interleaved on the scope
     // chain; every scope chain must start with zero or more ScopeObjects and
     // terminate with one or more non-ScopeObjects (viz., GlobalObject).
     MOZ_ASSERT(done());
     MOZ_ASSERT(!IsSyntacticScope(scope_));
     return *scope_;
 }
 
+inline bool
+js::CallObject::isForEval() const
+{
+    if (is<ModuleEnvironmentObject>())
+        return false;
+    MOZ_ASSERT(getFixedSlot(CALLEE_SLOT).isObjectOrNull());
+    MOZ_ASSERT_IF(getFixedSlot(CALLEE_SLOT).isObject(),
+                  getFixedSlot(CALLEE_SLOT).toObject().is<JSFunction>());
+    return getFixedSlot(CALLEE_SLOT).isNull();
+}
+
+inline JSFunction&
+js::CallObject::callee() const
+{
+    MOZ_ASSERT(!is<ModuleEnvironmentObject>());
+    return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>();
+}
+
 extern bool
 CreateScopeObjectsForScopeChain(JSContext* cx, AutoObjectVector& scopeChain,
                                 HandleObject dynamicTerminatingScope,
                                 MutableHandleObject dynamicScopeObj);
 
-bool HasNonSyntacticStaticScopeChain(JSObject* staticScope);
-uint32_t StaticScopeChainLength(JSObject* staticScope);
+bool HasNonSyntacticStaticScopeChain(StaticScope* staticScope);
+uint32_t StaticScopeChainLength(StaticScope* staticScope);
 
 ModuleEnvironmentObject* GetModuleEnvironmentForScript(JSScript* script);
 
 bool GetThisValueForDebuggerMaybeOptimizedOut(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
                                               MutableHandleValue res);
 
 bool CheckVarNameConflict(JSContext* cx, Handle<ClonedBlockObject*> lexicalScope,
                           HandlePropertyName name);
@@ -1556,16 +1610,16 @@ bool CheckGlobalDeclarationConflicts(JSC
                                      Handle<ClonedBlockObject*> lexicalScope,
                                      HandleObject varObj);
 
 bool CheckEvalDeclarationConflicts(JSContext* cx, HandleScript script,
                                    HandleObject scopeChain, HandleObject varObj);
 
 #ifdef DEBUG
 void DumpStaticScopeChain(JSScript* script);
-void DumpStaticScopeChain(JSObject* staticScope);
+void DumpStaticScopeChain(StaticScope* staticScope);
 bool
 AnalyzeEntrainedVariables(JSContext* cx, HandleScript script);
 #endif
 
 } // namespace js
 
 #endif /* vm_ScopeObject_h */
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2220,19 +2220,20 @@ CloneObject(JSContext* cx, HandleNativeO
 
         // Arrow functions use the first extended slot for their lexical |this| value.
         MOZ_ASSERT(!selfHostedFunction->isArrow());
         js::gc::AllocKind kind = hasName
                                  ? gc::AllocKind::FUNCTION_EXTENDED
                                  : selfHostedFunction->getAllocKind();
         MOZ_ASSERT(!CanReuseScriptForClone(cx->compartment(), selfHostedFunction, cx->global()));
         Rooted<ClonedBlockObject*> globalLexical(cx, &cx->global()->lexicalScope());
-        RootedObject staticGlobalLexical(cx, &globalLexical->staticBlock());
+        Rooted<StaticScope*> staticGlobalLexical(cx, &globalLexical->staticBlock());
         clone = CloneFunctionAndScript(cx, selfHostedFunction, globalLexical,
                                        staticGlobalLexical, kind);
+
         // To be able to re-lazify the cloned function, its name in the
         // self-hosting compartment has to be stored on the clone.
         if (clone && hasName) {
             clone->as<JSFunction>().setExtendedSlot(LAZY_FUNCTION_NAME_SLOT,
                                                     StringValue(selfHostedFunction->atom()));
         }
     } else if (selfHostedObject->is<RegExpObject>()) {
         RegExpObject& reobj = selfHostedObject->as<RegExpObject>();
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -100,31 +100,31 @@ InterpreterFrame::createRestParameter(JS
                                        ObjectGroup::NewArrayKind::UnknownIndex);
 }
 
 static inline void
 AssertDynamicScopeMatchesStaticScope(JSContext* cx, JSScript* script, JSObject* scope)
 {
 #ifdef DEBUG
     RootedObject originalScope(cx, scope);
-    RootedObject enclosingScope(cx, script->enclosingStaticScope());
+    Rooted<StaticScope*> enclosingScope(cx, script->enclosingStaticScope());
     for (StaticScopeIter<NoGC> i(enclosingScope); !i.done(); i++) {
         if (i.type() == StaticScopeIter<NoGC>::NonSyntactic) {
             while (scope->is<DynamicWithObject>() ||
                    scope->is<NonSyntacticVariablesObject>() ||
                    (scope->is<ClonedBlockObject>() &&
                     !scope->as<ClonedBlockObject>().isSyntactic()))
             {
                 MOZ_ASSERT(!IsSyntacticScope(scope));
                 scope = &scope->as<ScopeObject>().enclosingScope();
             }
         } else if (i.hasSyntacticDynamicScopeObject()) {
             switch (i.type()) {
               case StaticScopeIter<NoGC>::Module:
-                MOZ_ASSERT(scope->as<ModuleEnvironmentObject>().module().script() == i.moduleScript());
+                MOZ_ASSERT(scope->as<ModuleEnvironmentObject>().module().staticScope() == &i.module());
                 scope = &scope->as<ModuleEnvironmentObject>().enclosingScope();
                 break;
               case StaticScopeIter<NoGC>::Function:
                 MOZ_ASSERT(scope->as<CallObject>().callee().nonLazyScript() == i.funScript());
                 scope = &scope->as<CallObject>().enclosingScope();
                 break;
               case StaticScopeIter<NoGC>::Block:
                 MOZ_ASSERT(&i.block() == scope->as<ClonedBlockObject>().staticScope());
--- a/js/src/vm/Xdr.cpp
+++ b/js/src/vm/Xdr.cpp
@@ -113,31 +113,31 @@ bool
 XDRState<mode>::codeFunction(MutableHandleFunction objp)
 {
     if (mode == XDR_DECODE)
         objp.set(nullptr);
 
     if (!VersionCheck(this))
         return false;
 
-    RootedObject staticLexical(cx(), &cx()->global()->lexicalScope().staticBlock());
+    Rooted<StaticScope*> staticLexical(cx(), &cx()->global()->lexicalScope().staticBlock());
     return XDRInterpretedFunction(this, staticLexical, nullptr, objp);
 }
 
 template<XDRMode mode>
 bool
 XDRState<mode>::codeScript(MutableHandleScript scriptp)
 {
     if (mode == XDR_DECODE)
         scriptp.set(nullptr);
 
     if (!VersionCheck(this))
         return false;
 
-    RootedObject staticLexical(cx(), &cx()->global()->lexicalScope().staticBlock());
+    Rooted<StaticScope*> staticLexical(cx(), &cx()->global()->lexicalScope().staticBlock());
     if (!XDRScript(this, staticLexical, nullptr, nullptr, scriptp))
         return false;
 
     return true;
 }
 
 template<XDRMode mode>
 bool
--- a/js/xpconnect/src/XPCInlines.h
+++ b/js/xpconnect/src/XPCInlines.h
@@ -532,19 +532,17 @@ inline bool
 XPCWrappedNative::HasInterfaceNoQI(const nsIID& iid)
 {
     return nullptr != GetSet()->FindInterfaceWithIID(iid);
 }
 
 inline void
 XPCWrappedNative::SweepTearOffs()
 {
-    XPCWrappedNativeTearOffChunk* chunk;
-    for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) {
-        XPCWrappedNativeTearOff* to = &chunk->mTearOff;
+    for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) {
         bool marked = to->IsMarked();
         to->Unmark();
         if (marked)
             continue;
 
         // If this tearoff does not have a live dedicated JSObject,
         // then let's recycle it.
         if (!to->GetJSObjectPreserveColor()) {
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -108,19 +108,17 @@ XPCWrappedNative::Suspect(nsCycleCollect
 void
 XPCWrappedNative::NoteTearoffs(nsCycleCollectionTraversalCallback& cb)
 {
     // Tearoffs hold their native object alive. If their JS object hasn't been
     // finalized yet we'll note the edge between the JS object and the native
     // (see nsXPConnect::Traverse), but if their JS object has been finalized
     // then the tearoff is only reachable through the XPCWrappedNative, so we
     // record an edge here.
-    XPCWrappedNativeTearOffChunk* chunk;
-    for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) {
-        XPCWrappedNativeTearOff* to = &chunk->mTearOff;
+    for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) {
         JSObject* jso = to->GetJSObjectPreserveColor();
         if (!jso) {
             NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "tearoff's mNative");
             cb.NoteXPCOMChild(to->GetNative());
         }
     }
 }
 
@@ -893,19 +891,17 @@ XPCWrappedNative::FlatJSObjectFinalized(
     if (!IsValid())
         return;
 
     // Iterate the tearoffs and null out each of their JSObject's privates.
     // This will keep them from trying to access their pointers to the
     // dying tearoff object. We can safely assume that those remaining
     // JSObjects are about to be finalized too.
 
-    XPCWrappedNativeTearOffChunk* chunk;
-    for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) {
-        XPCWrappedNativeTearOff* to = &chunk->mTearOff;
+    for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) {
         JSObject* jso = to->GetJSObjectPreserveColor();
         if (jso) {
             JS_SetPrivate(jso, nullptr);
 #ifdef DEBUG
             JS_UpdateWeakPointerAfterGCUnbarriered(&jso);
             MOZ_ASSERT(!jso);
 #endif
             to->JSObjectFinalized();
@@ -966,51 +962,43 @@ XPCWrappedNative::SystemIsBeingShutDown(
         return;
 
     // The long standing strategy is to leak some objects still held at shutdown.
     // The general problem is that propagating release out of xpconnect at
     // shutdown time causes a world of problems.
 
     // We leak mIdentity (see above).
 
-    // short circuit future finalization
+    // Short circuit future finalization.
     JS_SetPrivate(mFlatJSObject, nullptr);
     mFlatJSObject = nullptr;
     mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
 
     XPCWrappedNativeProto* proto = GetProto();
 
     if (HasProto())
         proto->SystemIsBeingShutDown();
 
     if (mScriptableInfo &&
         (!HasProto() ||
          (proto && proto->GetScriptableInfo() != mScriptableInfo))) {
         delete mScriptableInfo;
     }
 
-    // cleanup the tearoffs...
-
-    XPCWrappedNativeTearOffChunk* chunk;
-    for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) {
-        XPCWrappedNativeTearOff* to = &chunk->mTearOff;
+    // Cleanup the tearoffs.
+    for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) {
         if (JSObject* jso = to->GetJSObjectPreserveColor()) {
             JS_SetPrivate(jso, nullptr);
             to->SetJSObject(nullptr);
         }
         // We leak the tearoff mNative
         // (for the same reason we leak mIdentity - see above).
         Unused << to->TakeNative().take();
         to->SetInterface(nullptr);
     }
-
-    if (mFirstChunk.mNextChunk) {
-        delete mFirstChunk.mNextChunk;
-        mFirstChunk.mNextChunk = nullptr;
-    }
 }
 
 /***************************************************************************/
 
 // Dynamically ensure that two objects don't end up with the same private.
 class MOZ_STACK_CLASS AutoClonePrivateGuard {
 public:
     AutoClonePrivateGuard(JSContext* cx, JSObject* aOld, JSObject* aNew)
@@ -1053,22 +1041,20 @@ XPCWrappedNative::FindTearOff(XPCNativeI
                               bool needJSObject /* = false */,
                               nsresult* pError /* = nullptr */)
 {
     AutoJSContext cx;
     nsresult rv = NS_OK;
     XPCWrappedNativeTearOff* to;
     XPCWrappedNativeTearOff* firstAvailable = nullptr;
 
-    XPCWrappedNativeTearOffChunk* lastChunk;
-    XPCWrappedNativeTearOffChunk* chunk;
-    for (lastChunk = chunk = &mFirstChunk;
-         chunk;
-         lastChunk = chunk, chunk = chunk->mNextChunk) {
-        to = &chunk->mTearOff;
+    XPCWrappedNativeTearOff* lastTearOff;
+    for (lastTearOff = to = &mFirstTearOff;
+         to;
+         lastTearOff = to, to = to->GetNextTearOff()) {
         if (to->GetInterface() == aInterface) {
             if (needJSObject && !to->GetJSObjectPreserveColor()) {
                 AutoMarkingWrappedNativeTearOffPtr tearoff(cx, to);
                 bool ok = InitTearOffJSObject(to);
                 // During shutdown, we don't sweep tearoffs.  So make sure
                 // to unmark manually in case the auto-marker marked us.
                 // We shouldn't ever be getting here _during_ our
                 // Mark/Sweep cycle, so this should be safe.
@@ -1084,19 +1070,17 @@ XPCWrappedNative::FindTearOff(XPCNativeI
         }
         if (!firstAvailable && to->IsAvailable())
             firstAvailable = to;
     }
 
     to = firstAvailable;
 
     if (!to) {
-        auto newChunk = new XPCWrappedNativeTearOffChunk();
-        lastChunk->mNextChunk = newChunk;
-        to = &newChunk->mTearOff;
+        to = lastTearOff->AddTearOff();
     }
 
     {
         // Scope keeps |tearoff| from leaking across the rest of the function.
         AutoMarkingWrappedNativeTearOffPtr tearoff(cx, to);
         rv = InitTearOff(to, aInterface, needJSObject);
         // During shutdown, we don't sweep tearoffs.  So make sure to unmark
         // manually in case the auto-marker marked us.  We shouldn't ever be
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -80,16 +80,17 @@
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
 
 #include "mozilla/dom/ScriptSettings.h"
 
 #include <math.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -1966,45 +1967,38 @@ public:
     // NOP. This is just here to make the AutoMarkingPtr code compile.
     inline void TraceJS(JSTracer* trc) {}
     inline void AutoTrace(JSTracer* trc) {}
 
     void Mark()       {mJSObject.setFlags(1);}
     void Unmark()     {mJSObject.unsetFlags(1);}
     bool IsMarked() const {return mJSObject.hasFlag(1);}
 
+    XPCWrappedNativeTearOff* AddTearOff()
+    {
+        MOZ_ASSERT(!mNextTearOff);
+        mNextTearOff = mozilla::MakeUnique<XPCWrappedNativeTearOff>();
+        return mNextTearOff.get();
+    }
+
+    XPCWrappedNativeTearOff* GetNextTearOff() {return mNextTearOff.get();}
+
 private:
     XPCWrappedNativeTearOff(const XPCWrappedNativeTearOff& r) = delete;
     XPCWrappedNativeTearOff& operator= (const XPCWrappedNativeTearOff& r) = delete;
 
 private:
     XPCNativeInterface* mInterface;
     // mNative is an nsRefPtr not an nsCOMPtr because it may not be the canonical
     // nsISupports pointer.
     RefPtr<nsISupports> mNative;
     JS::TenuredHeap<JSObject*> mJSObject;
+    mozilla::UniquePtr<XPCWrappedNativeTearOff> mNextTearOff;
 };
 
-/***********************************************/
-// XPCWrappedNativeTearOffChunk is a linked list of XPCWrappedNativeTearOff
-// objects. It lets us allocate a set of XPCWrappedNativeTearOff objects and
-// link the sets - rather than only having the option of linking single
-// XPCWrappedNativeTearOff objects.
-
-class XPCWrappedNativeTearOffChunk
-{
-friend class XPCWrappedNative;
-private:
-    XPCWrappedNativeTearOffChunk() : mNextChunk(nullptr) {}
-    ~XPCWrappedNativeTearOffChunk() {delete mNextChunk;}
-
-private:
-    XPCWrappedNativeTearOff mTearOff;
-    XPCWrappedNativeTearOffChunk* mNextChunk;
-};
 
 /***************************************************************************/
 // XPCWrappedNative the wrapper around one instance of a native xpcom object
 // to be used from JavaScript.
 
 class XPCWrappedNative final : public nsIXPConnectWrappedNative
 {
 public:
@@ -2263,23 +2257,23 @@ public:
     static const XPCNativeScriptableCreateInfo& GatherScriptableCreateInfo(nsISupports* obj,
                                                                            nsIClassInfo* classInfo,
                                                                            XPCNativeScriptableCreateInfo& sciProto,
                                                                            XPCNativeScriptableCreateInfo& sciWrapper);
 
 private:
     union
     {
-        XPCWrappedNativeScope*   mMaybeScope;
-        XPCWrappedNativeProto*   mMaybeProto;
+        XPCWrappedNativeScope* mMaybeScope;
+        XPCWrappedNativeProto* mMaybeProto;
     };
-    XPCNativeSet*                mSet;
-    JS::TenuredHeap<JSObject*>   mFlatJSObject;
-    XPCNativeScriptableInfo*     mScriptableInfo;
-    XPCWrappedNativeTearOffChunk mFirstChunk;
+    XPCNativeSet* mSet;
+    JS::TenuredHeap<JSObject*> mFlatJSObject;
+    XPCNativeScriptableInfo* mScriptableInfo;
+    XPCWrappedNativeTearOff mFirstTearOff;
 };
 
 /***************************************************************************
 ****************************************************************************
 *
 * Core classes for wrapped JSObject for use from native code...
 *
 ****************************************************************************
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1170,24 +1170,26 @@ nsLayoutUtils::SetDisplayPortMargins(nsI
 
   if (aRepaintMode == RepaintMode::Repaint) {
     nsIFrame* frame = aContent->GetPrimaryFrame();
     if (frame) {
       frame->SchedulePaint();
     }
   }
 
-  // Display port margins changing means that the set of visible images may
-  // have drastically changed. Check if we should schedule an update.
   nsIFrame* frame = GetScrollFrameFromContent(aContent);
   nsIScrollableFrame* scrollableFrame = frame ? frame->GetScrollTargetFrame() : nullptr;
   if (!scrollableFrame) {
     return true;
   }
 
+  scrollableFrame->TriggerDisplayPortExpiration();
+
+  // Display port margins changing means that the set of visible images may
+  // have drastically changed. Check if we should schedule an update.
   nsRect oldDisplayPort;
   bool hadDisplayPort =
     scrollableFrame->GetDisplayPortAtLastImageVisibilityUpdate(&oldDisplayPort);
 
   nsRect newDisplayPort;
   Unused << GetDisplayPort(aContent, &newDisplayPort);
 
   bool needImageVisibilityUpdate = !hadDisplayPort;
@@ -1244,16 +1246,23 @@ nsLayoutUtils::GetCriticalDisplayPort(ns
 }
 
 bool
 nsLayoutUtils::HasCriticalDisplayPort(nsIContent* aContent)
 {
   return GetCriticalDisplayPort(aContent, nullptr);
 }
 
+void
+nsLayoutUtils::RemoveDisplayPort(nsIContent* aContent)
+{
+  aContent->DeleteProperty(nsGkAtoms::DisplayPort);
+  aContent->DeleteProperty(nsGkAtoms::DisplayPortMargins);
+}
+
 nsContainerFrame*
 nsLayoutUtils::LastContinuationWithChild(nsContainerFrame* aFrame)
 {
   NS_PRECONDITION(aFrame, "NULL frame pointer");
   nsIFrame* f = aFrame->LastContinuation();
   while (!f->PrincipalChildList().FirstChild() && f->GetPrevContinuation()) {
     f = f->GetPrevContinuation();
   }
@@ -3163,16 +3172,47 @@ nsLayoutUtils::SetZeroMarginDisplayPortO
         !nsLayoutUtils::HasDisplayPort(frame->GetContent())) {
       nsLayoutUtils::SetDisplayPortMargins(
         frame->GetContent(), frame->PresContext()->PresShell(), ScreenMargin(), 0,
         aRepaintMode);
     }
   }
 }
 
+void
+nsLayoutUtils::ExpireDisplayPortOnAsyncScrollableAncestor(nsIFrame* aFrame)
+{
+  nsIFrame* frame = aFrame;
+  while (frame) {
+    frame = nsLayoutUtils::GetCrossDocParentFrame(frame);
+    if (!frame) {
+      break;
+    }
+    nsIScrollableFrame* scrollAncestor = GetAsyncScrollableAncestorFrame(frame);
+    if (!scrollAncestor) {
+      break;
+    }
+    frame = do_QueryFrame(scrollAncestor);
+    MOZ_ASSERT(frame);
+    MOZ_ASSERT(scrollAncestor->WantAsyncScroll() ||
+      frame->PresContext()->PresShell()->GetRootScrollFrame() == frame);
+    if (nsLayoutUtils::AsyncPanZoomEnabled(frame) &&
+        nsLayoutUtils::HasDisplayPort(frame->GetContent())) {
+      scrollAncestor->TriggerDisplayPortExpiration();
+      // Stop after the first trigger. If it failed, there's no point in
+      // continuing because all the rest of the frames we encounter are going
+      // to be ancestors of |scrollAncestor| which will keep its displayport.
+      // If the trigger succeeded, we stop because when the trigger executes
+      // it will call this function again to trigger the next ancestor up the
+      // chain.
+      break;
+    }
+  }
+}
+
 nsresult
 nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFrame,
                           const nsRegion& aDirtyRegion, nscolor aBackstop,
                           uint32_t aFlags)
 {
   PROFILER_LABEL("nsLayoutUtils", "PaintFrame",
     js::ProfileEntry::Category::GRAPHICS);
 
@@ -4537,29 +4577,33 @@ AddIntrinsicSizeOffset(nsRenderingContex
   coordOutsideSize += aOffsets.hMargin;
   pctOutsideSize += aOffsets.hPctMargin;
 
   min += coordOutsideSize;
   result = NSCoordSaturatingAdd(result, coordOutsideSize);
   pctTotal += pctOutsideSize;
 
   nscoord size;
-  if (GetAbsoluteCoord(aStyleSize, size) ||
-      GetIntrinsicCoord(aStyleSize, aRenderingContext, aFrame,
-                        PROP_WIDTH, size)) {
+  if (aType == nsLayoutUtils::MIN_ISIZE &&
+      (((aStyleSize.HasPercent() || aStyleMaxSize.HasPercent()) &&
+        aFrame->IsFrameOfType(nsIFrame::eReplacedSizing)) ||
+       (aStyleSize.HasPercent() &&
+        aFrame->GetType() == nsGkAtoms::textInputFrame))) {
+    // A percentage width or max-width on replaced elements means they
+    // can shrink to 0.
+    // This is also true for percentage widths (but not max-widths) on
+    // text inputs.
+    // Note that if this is max-width, this overrides the fixed-width
+    // rule in the next condition.
+    result = 0; // let |min| handle padding/border/margin
+  } else if (GetAbsoluteCoord(aStyleSize, size) ||
+             GetIntrinsicCoord(aStyleSize, aRenderingContext, aFrame,
+                               PROP_WIDTH, size)) {
     result = nsLayoutUtils::AddPercents(aType, size + coordOutsideSize,
                                         pctOutsideSize);
-  } else if (aType == nsLayoutUtils::MIN_ISIZE &&
-             // The only cases of coord-percent-calc() units that
-             // GetAbsoluteCoord didn't handle are percent and calc()s
-             // containing percent.
-             aStyleSize.IsCoordPercentCalcUnit() &&
-             aFrame->IsFrameOfType(nsIFrame::eReplaced)) {
-    // A percentage width on replaced elements means they can shrink to 0.
-    result = 0; // let |min| handle padding/border/margin
   } else {
     // NOTE: We could really do a lot better for percents and for some
     // cases of calc() containing percent (certainly including any where
     // the coefficient on the percent is positive and there are no max()
     // expressions).  However, doing better for percents wouldn't be
     // backwards compatible.
     result = nsLayoutUtils::AddPercents(aType, result, pctTotal);
   }
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -238,16 +238,21 @@ public:
   static bool GetCriticalDisplayPort(nsIContent* aContent, nsRect* aResult);
 
   /**
    * Check whether the given element has a critical display port.
    */
   static bool HasCriticalDisplayPort(nsIContent* aContent);
 
   /**
+   * Remove the displayport for the given element.
+   */
+  static void RemoveDisplayPort(nsIContent* aContent);
+
+  /**
    * Use heuristics to figure out the child list that
    * aChildFrame is currently in.
    */
   static mozilla::layout::FrameChildListID GetChildListNameFor(nsIFrame* aChildFrame);
 
   /**
    * GetBeforeFrameForContent returns the ::before frame for aContent, if
    * one exists.  This is typically O(1).  The frame passed in must be
@@ -2681,16 +2686,22 @@ public:
   static nsIScrollableFrame* GetAsyncScrollableAncestorFrame(nsIFrame* aTarget);
 
   /**
    * Sets a zero margin display port on all proper ancestors of aFrame that
    * are async scrollable.
    */
   static void SetZeroMarginDisplayPortOnAsyncScrollableAncestors(nsIFrame* aFrame,
                                                                  RepaintMode aRepaintMode);
+  /**
+   * Finds the closest ancestor async scrollable frame from aFrame that has a
+   * displayport and attempts to trigger the displayport expiry on that
+   * ancestor.
+   */
+  static void ExpireDisplayPortOnAsyncScrollableAncestor(nsIFrame* aFrame);
 
   static bool IsOutlineStyleAutoEnabled();
 
   static void SetBSizeFromFontMetrics(const nsIFrame* aFrame,
                                       nsHTMLReflowMetrics& aMetrics,
                                       const mozilla::LogicalMargin& aFramePadding,
                                       mozilla::WritingMode aLineWM,
                                       mozilla::WritingMode aFrameWM);
--- a/layout/base/tests/test_bug976963.html
+++ b/layout/base/tests/test_bug976963.html
@@ -10,17 +10,18 @@ https://bugzilla.mozilla.org/show_bug.cg
     <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     <script type="text/javascript">
       function prepareTest() {
         SimpleTest.waitForExplicitFinish();
         SimpleTest.requestFlakyTimeout("untriaged");
         SpecialPowers.pushPrefEnv({
           "set": [
-            ["dom.w3c_pointer_events.enabled", true]
+            ["dom.w3c_pointer_events.enabled", true],
+            ["layout.reflow.synthMouseMove", false]
           ]
         }, startTest);
       }
       function startTest() {
         var iframe = document.getElementById("testFrame");
         iframe.src = "bug976963_inner.html";
       }
       function finishTest() {
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -7,16 +7,17 @@
 /*
  * rendering object for CSS display:block, inline-block, and list-item
  * boxes, also used for various anonymous boxes
  */
 
 #include "nsBlockFrame.h"
 
 #include "mozilla/DebugOnly.h"
+#include "mozilla/Maybe.h"
 
 #include "nsCOMPtr.h"
 #include "nsAbsoluteContainingBlock.h"
 #include "nsBlockReflowContext.h"
 #include "nsBlockReflowState.h"
 #include "nsBulletFrame.h"
 #include "nsFontMetrics.h"
 #include "nsLineBox.h"
@@ -902,31 +903,54 @@ nsBlockFrame::GetPrefWidthTightBounds(ns
       }
     }
   }
   data.ForceBreak();
 
   return NS_OK;
 }
 
+/**
+ * Return whether aNewAvailableSpace is smaller *on either side*
+ * (inline-start or inline-end) than aOldAvailableSpace, so that we know
+ * if we need to redo layout on an line, replaced block, or block
+ * formatting context, because its height (which we used to compute
+ * aNewAvailableSpace) caused it to intersect additional floats.
+ */
 static bool
 AvailableSpaceShrunk(WritingMode aWM,
                      const LogicalRect& aOldAvailableSpace,
-                     const LogicalRect& aNewAvailableSpace)
+                     const LogicalRect& aNewAvailableSpace,
+                     bool aCanGrow /* debug-only */)
 {
   if (aNewAvailableSpace.ISize(aWM) == 0) {
     // Positions are not significant if the inline size is zero.
     return aOldAvailableSpace.ISize(aWM) != 0;
   }
-  NS_ASSERTION(aOldAvailableSpace.IStart(aWM) <=
-               aNewAvailableSpace.IStart(aWM) &&
-               aOldAvailableSpace.IEnd(aWM) >=
-               aNewAvailableSpace.IEnd(aWM),
-               "available space should never grow");
-  return aOldAvailableSpace.ISize(aWM) != aNewAvailableSpace.ISize(aWM);
+  if (aCanGrow) {
+    NS_ASSERTION(aNewAvailableSpace.IStart(aWM) <=
+                   aOldAvailableSpace.IStart(aWM) ||
+                 aNewAvailableSpace.IEnd(aWM) <= aOldAvailableSpace.IEnd(aWM),
+                 "available space should not shrink on the start side and "
+                 "grow on the end side");
+    NS_ASSERTION(aNewAvailableSpace.IStart(aWM) >=
+                   aOldAvailableSpace.IStart(aWM) ||
+                 aNewAvailableSpace.IEnd(aWM) >= aOldAvailableSpace.IEnd(aWM),
+                 "available space should not grow on the start side and "
+                 "shrink on the end side");
+  } else {
+    NS_ASSERTION(aOldAvailableSpace.IStart(aWM) <=
+                 aNewAvailableSpace.IStart(aWM) &&
+                 aOldAvailableSpace.IEnd(aWM) >=
+                 aNewAvailableSpace.IEnd(aWM),
+                 "available space should never grow");
+  }
+  // Have we shrunk on either side?
+  return aNewAvailableSpace.IStart(aWM) > aOldAvailableSpace.IStart(aWM) ||
+         aNewAvailableSpace.IEnd(aWM) < aOldAvailableSpace.IEnd(aWM);
 }
 
 static LogicalSize
 CalculateContainingBlockSizeForAbsolutes(WritingMode aWM,
                                          const nsHTMLReflowState& aReflowState,
                                          LogicalSize aFrameSize)
 {
   // The issue here is that for a 'height' of 'auto' the reflow state
@@ -3314,20 +3338,21 @@ nsBlockFrame::ReflowBlockFrame(nsBlockRe
     // block-start-margin + clearance.
     aState.mBCoord -= bStartMargin;
     availSpace.BStart(wm) -= bStartMargin;
     if (NS_UNCONSTRAINEDSIZE != availSpace.BSize(wm)) {
       availSpace.BSize(wm) += bStartMargin;
     }
 
     // construct the html reflow state for the block. ReflowBlock
-    // will initialize it
-    nsHTMLReflowState
-      blockHtmlRS(aState.mPresContext, aState.mReflowState, frame,
-                  availSpace.Size(wm).ConvertTo(frame->GetWritingMode(), wm));
+    // will initialize it.
+    Maybe<nsHTMLReflowState> blockHtmlRS;
+    blockHtmlRS.emplace(
+      aState.mPresContext, aState.mReflowState, frame,
+      availSpace.Size(wm).ConvertTo(frame->GetWritingMode(), wm));
 
     nsFloatManager::SavedState floatManagerState;
     nsReflowStatus frameReflowStatus;
     do {
       if (floatAvailableSpace.mHasFloats) {
         // Set if floatAvailableSpace.mHasFloats is true for any
         // iteration of the loop.
         aLine->SetLineIsImpactedByFloat(true);
@@ -3342,26 +3367,26 @@ nsBlockFrame::ReflowBlockFrame(nsBlockRe
         !*aState.mReflowState.mDiscoveredClearance;
 
       // Reflow the block into the available space
       if (mayNeedRetry || replacedBlock) {
         aState.mFloatManager->PushState(&floatManagerState);
       }
 
       if (mayNeedRetry) {
-        blockHtmlRS.mDiscoveredClearance = &clearanceFrame;
+        blockHtmlRS->mDiscoveredClearance = &clearanceFrame;
       } else if (!applyBStartMargin) {
-        blockHtmlRS.mDiscoveredClearance =
+        blockHtmlRS->mDiscoveredClearance =
           aState.mReflowState.mDiscoveredClearance;
       }
 
       frameReflowStatus = NS_FRAME_COMPLETE;
       brc.ReflowBlock(availSpace, applyBStartMargin, aState.mPrevBEndMargin,
                       clearance, aState.IsAdjacentWithTop(),
-                      aLine.get(), blockHtmlRS, frameReflowStatus, aState);
+                      aLine.get(), *blockHtmlRS, frameReflowStatus, aState);
 
       // Now the block has a height.  Using that height, get the
       // available space again and call ComputeBlockAvailSpace again.
       // If ComputeBlockAvailSpace gives a different result, we need to
       // reflow again.
       if (!replacedBlock) {
         break;
       }
@@ -3372,18 +3397,28 @@ nsBlockFrame::ReflowBlockFrame(nsBlockRe
                               brc.GetMetrics().Height(),
                               &floatManagerState);
       NS_ASSERTION(floatAvailableSpace.mRect.BStart(wm) ==
                      oldFloatAvailableSpaceRect.BStart(wm),
                    "yikes");
       // Restore the height to the position of the next band.
       floatAvailableSpace.mRect.BSize(wm) =
         oldFloatAvailableSpaceRect.BSize(wm);
+      // Determine whether the available space shrunk on either side,
+      // because (the first time round) we now know the block's height,
+      // and it may intersect additional floats, or (on later
+      // iterations) because narrowing the width relative to the
+      // previous time may cause the block to become taller.  Note that
+      // since we're reflowing the block, narrowing the width might also
+      // make it shorter, so we must pass aCanGrow as true.
       if (!AvailableSpaceShrunk(wm, oldFloatAvailableSpaceRect,
-                                floatAvailableSpace.mRect)) {
+                                floatAvailableSpace.mRect, true)) {
+        // The size and position we chose before are fine (i.e., they
+        // don't cause intersecting with floats that requires a change
+        // in size or position), so we're done.
         break;
       }
 
       bool advanced = false;
       if (!aState.ReplacedBlockFitsInAvailSpace(replacedBlock,
                                                 floatAvailableSpace)) {
         // Advance to the next band.
         nscoord newBCoord = aState.mBCoord;
@@ -3428,33 +3463,32 @@ nsBlockFrame::ReflowBlockFrame(nsBlockRe
         // This should never cause us to move up since the call to
         // GetFloatAvailableSpaceForBSize above included the margin.
         applyBStartMargin = false;
         bStartMargin = 0;
         treatWithClearance = true; // avoid hitting test above
         clearance = 0;
       }
 
-      blockHtmlRS.~nsHTMLReflowState();
-      new (&blockHtmlRS) nsHTMLReflowState(aState.mPresContext,
-                           aState.mReflowState, frame,
-                           availSpace.Size(wm).ConvertTo(
-                             frame->GetWritingMode(), wm));
+      blockHtmlRS.reset();
+      blockHtmlRS.emplace(
+        aState.mPresContext, aState.mReflowState, frame,
+        availSpace.Size(wm).ConvertTo(frame->GetWritingMode(), wm));
     } while (true);
 
     if (mayNeedRetry && clearanceFrame) {
       aState.mFloatManager->PopState(&floatManagerState);
       aState.mBCoord = startingBCoord;
       aState.mPrevBEndMargin = incomingMargin;
       continue;
     }
 
     aState.mPrevChild = frame;
 
-    if (blockHtmlRS.WillReflowAgainForClearance()) {
+    if (blockHtmlRS->WillReflowAgainForClearance()) {
       // If an ancestor of ours is going to reflow for clearance, we
       // need to avoid calling PlaceBlock, because it unsets dirty bits
       // on the child block (both itself, and through its call to
       // nsFrame::DidReflow), and those dirty bits imply dirtiness for
       // all of the child block, including the lines it didn't reflow.
       NS_ASSERTION(originalPosition == frame->GetPosition(),
                    "we need to call PositionChildViews");
       return;
@@ -3482,17 +3516,17 @@ nsBlockFrame::ReflowBlockFrame(nsBlockRe
       // pushing it to the next page would give it more room.
       // Don't force the block to fit if it's impacted by a float. If it is,
       // then pushing it to the next page would give it more room. Note that
       // isImpacted doesn't include impact from the block's own floats.
       bool forceFit = aState.IsAdjacentWithTop() && clearance <= 0 &&
         !floatAvailableSpace.mHasFloats;
       nsCollapsingMargin collapsedBEndMargin;
       nsOverflowAreas overflowAreas;
-      *aKeepReflowGoing = brc.PlaceBlock(blockHtmlRS, forceFit, aLine.get(),
+      *aKeepReflowGoing = brc.PlaceBlock(*blockHtmlRS, forceFit, aLine.get(),
                                          collapsedBEndMargin,
                                          overflowAreas,
                                          frameReflowStatus);
       if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus) &&
           ShouldAvoidBreakInside(aState.mReflowState)) {
         *aKeepReflowGoing = false;
       }
 
@@ -4411,18 +4445,21 @@ nsBlockFrame::PlaceLine(nsBlockReflowSta
                                           aAvailableSpaceHeight,
                                           aFloatStateBeforeLine).mRect;
   NS_ASSERTION(aFloatAvailableSpace.BStart(wm) ==
                oldFloatAvailableSpace.BStart(wm), "yikes");
   // Restore the height to the position of the next band.
   aFloatAvailableSpace.BSize(wm) = oldFloatAvailableSpace.BSize(wm);
   // If the available space between the floats is smaller now that we
   // know the height, return false (and cause another pass with
-  // LINE_REFLOW_REDO_MORE_FLOATS).
-  if (AvailableSpaceShrunk(wm, oldFloatAvailableSpace, aFloatAvailableSpace)) {
+  // LINE_REFLOW_REDO_MORE_FLOATS).  We ensure aAvailableSpaceHeight
+  // never decreases, which means that we can't reduce the set of floats
+  // we intersect, which means that the available space cannot grow.
+  if (AvailableSpaceShrunk(wm, oldFloatAvailableSpace, aFloatAvailableSpace,
+                           false)) {
     return false;
   }
 
 #ifdef DEBUG
   if (!GetParent()->IsCrazySizeAssertSuppressed()) {
     static nscoord lastHeight = 0;
     if (CRAZY_SIZE(aLine->BStart())) {
       lastHeight = aLine->BStart();
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -1856,16 +1856,17 @@ ScrollFrameHelper::ScrollFrameHelper(nsC
   , mHorizontalOverflow(false)
   , mVerticalOverflow(false)
   , mPostedReflowCallback(false)
   , mMayHaveDirtyFixedChildren(false)
   , mUpdateScrollbarAttributes(false)
   , mHasBeenScrolledRecently(false)
   , mCollapsedResizer(false)
   , mWillBuildScrollableLayer(false)
+  , mIsScrollParent(false)
   , mIsScrollableLayerInRootContainer(false)
   , mHasBeenScrolled(false)
   , mIgnoreMomentumScroll(false)
   , mTransformingByAPZ(false)
   , mZoomableByAPZ(false)
   , mVelocityQueue(aOuter->PresContext())
   , mAsyncScrollEvent(END_DOM)
 {
@@ -1903,16 +1904,20 @@ ScrollFrameHelper::~ScrollFrameHelper()
     delete gScrollFrameActivityTracker;
     gScrollFrameActivityTracker = nullptr;
   }
 
   if (mScrollActivityTimer) {
     mScrollActivityTimer->Cancel();
     mScrollActivityTimer = nullptr;
   }
+  if (mDisplayPortExpiryTimer) {
+    mDisplayPortExpiryTimer->Cancel();
+    mDisplayPortExpiryTimer = nullptr;
+  }
 }
 
 /*
  * Callback function from AsyncSmoothMSDScroll, used in ScrollFrameHelper::ScrollTo
  */
 void
 ScrollFrameHelper::AsyncSmoothMSDScrollCallback(ScrollFrameHelper* aInstance,
                                                 mozilla::TimeDuration aDeltaTime)
@@ -2326,16 +2331,53 @@ bool ScrollFrameHelper::IsAlwaysActive()
 
   // If we're overflow:hidden, then start as inactive until
   // we get scrolled manually.
   ScrollbarStyles styles = GetScrollbarStylesFromFrame();
   return (styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN &&
           styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN);
 }
 
+/*static*/ void
+RemoveDisplayPortCallback(nsITimer* aTimer, void* aClosure)
+{
+  ScrollFrameHelper* helper = static_cast<ScrollFrameHelper*>(aClosure);
+
+  // This function only ever gets called from the expiry timer, so it must
+  // be non-null here. Set it to null here so that we don't keep resetting
+  // it unnecessarily in MarkRecentlyScrolled().
+  MOZ_ASSERT(helper->mDisplayPortExpiryTimer);
+  helper->mDisplayPortExpiryTimer = nullptr;
+
+  if (helper->IsAlwaysActive() || helper->mIsScrollParent) {
+    // If this is a scroll parent for some other scrollable frame, don't
+    // expire the displayport because it would break scroll handoff. Once the
+    // descendant scrollframes have their displayports expired, they will
+    // trigger the displayport expiration on this scrollframe as well, and
+    // mIsScrollParent will presumably be false when that kicks in.
+    return;
+  }
+
+  // Remove the displayport from this scrollframe if it's been a while
+  // since it's scrolled, except if it needs to be always active. Note that
+  // there is one scrollframe that doesn't fall under this general rule, and
+  // that is the one that nsLayoutUtils::MaybeCreateDisplayPort decides to put
+  // a displayport on (i.e. the first scrollframe that WantAsyncScroll()s).
+  // If that scrollframe is this one, we remove the displayport anyway, and
+  // as part of the next paint MaybeCreateDisplayPort will put another
+  // displayport back on it. Although the displayport will "flicker" off and
+  // back on, the layer itself should never disappear, because this all
+  // happens between actual painting. If the displayport is reset to a
+  // different position that's ok; this scrollframe hasn't been scrolled
+  // recently and so the reset should be correct.
+  nsLayoutUtils::RemoveDisplayPort(helper->mOuter->GetContent());
+  nsLayoutUtils::ExpireDisplayPortOnAsyncScrollableAncestor(helper->mOuter);
+  helper->mOuter->SchedulePaint();
+}
+
 void ScrollFrameHelper::MarkNotRecentlyScrolled()
 {
   if (!mHasBeenScrolledRecently)
     return;
 
   mHasBeenScrolledRecently = false;
   mOuter->SchedulePaint();
 }
@@ -2349,16 +2391,46 @@ void ScrollFrameHelper::MarkRecentlyScro
   if (mActivityExpirationState.IsTracked()) {
     gScrollFrameActivityTracker->MarkUsed(this);
   } else {
     if (!gScrollFrameActivityTracker) {
       gScrollFrameActivityTracker = new ScrollFrameActivityTracker();
     }
     gScrollFrameActivityTracker->AddObject(this);
   }
+
+  // If we just scrolled and there's a displayport expiry timer in place,
+  // reset the timer.
+  ResetDisplayPortExpiryTimer();
+}
+
+void ScrollFrameHelper::ResetDisplayPortExpiryTimer()
+{
+  if (mDisplayPortExpiryTimer) {
+    mDisplayPortExpiryTimer->InitWithFuncCallback(
+      RemoveDisplayPortCallback, this,
+      gfxPrefs::APZDisplayPortExpiryTime(), nsITimer::TYPE_ONE_SHOT);
+  }
+}
+
+void ScrollFrameHelper::TriggerDisplayPortExpiration()
+{
+  if (IsAlwaysActive()) {
+    return;
+  }
+
+  if (!gfxPrefs::APZDisplayPortExpiryTime()) {
+    // a zero time disables the expiry
+    return;
+  }
+
+  if (!mDisplayPortExpiryTimer) {
+    mDisplayPortExpiryTimer = do_CreateInstance("@mozilla.org/timer;1");
+  }
+  ResetDisplayPortExpiryTimer();
 }
 
 void ScrollFrameHelper::ScrollVisual()
 {
   // Mark this frame as having been scrolled. If this is the root
   // scroll frame of a content document, then IsAlwaysActive()
   // will return true from now on and MarkNotRecentlyScrolled() won't
   // have any effect.
@@ -3174,16 +3246,19 @@ ScrollFrameHelper::BuildDisplayList(nsDi
         }
       }
       const DisplayItemClip* clipNonCaret = aBuilder->ClipState().GetCurrentCombinedClip(aBuilder);
       const DisplayItemScrollClip* scrollClipNonCaret = aBuilder->ClipState().GetCurrentInnermostScrollClip();
       ClipListsExceptCaret(&scrolledContent, aBuilder, mScrolledFrame,
                            clipNonCaret, scrollClipNonCaret);
     }
 
+    if (aBuilder->IsPaintingToWindow()) {
+      mIsScrollParent = idSetter.ShouldForceLayerForScrollParent();
+    }
     if (idSetter.ShouldForceLayerForScrollParent() &&
         !gfxPrefs::LayoutUseContainersForRootFrames())
     {
       // Note that forcing layerization of scroll parents follows the scroll
       // handoff chain which is subject to the out-of-flow-frames caveat noted
       // above (where the idSetter variable is created).
       //
       // This is not compatible when using containes for root scrollframes.
@@ -3204,17 +3279,17 @@ ScrollFrameHelper::BuildDisplayList(nsDi
         // It's too late to change the dirty rect so pass a copy.
         nsRect copyOfDirtyRect = dirtyRect;
         Unused << DecideScrollableLayer(aBuilder, &copyOfDirtyRect,
                     /* aAllowCreateDisplayPort = */ false);
       }
     }
   }
 
-  if (mWillBuildScrollableLayer && !gfxPrefs::LayoutUseContainersForRootFrames()) {
+  if (mWillBuildScrollableLayer) {
     aBuilder->ForceLayerForScrollParent();
   }
 
   if (couldBuildLayer) {
     // Make sure that APZ will dispatch events back to content so we can create
     // a displayport for this frame. We'll add the item later on.
     nsDisplayLayerEventRegions* inactiveRegionItem = nullptr;
     if (aBuilder->IsPaintingToWindow() &&
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -365,16 +365,19 @@ public:
   bool UsesContainerScrolling() const;
 
   bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
                              nsRect* aDirtyRect,
                              bool aAllowCreateDisplayPort);
   void NotifyImageVisibilityUpdate();
   bool GetDisplayPortAtLastImageVisibilityUpdate(nsRect* aDisplayPort);
 
+  void TriggerDisplayPortExpiration();
+  void ResetDisplayPortExpiryTimer();
+
   void ScheduleSyntheticMouseMove();
   static void ScrollActivityCallback(nsITimer *aTimer, void* anInstance);
 
   void HandleScrollbarStyleSwitching();
 
   nsIAtom* LastScrollOrigin() const { return mLastScrollOrigin; }
   nsIAtom* LastSmoothScrollOrigin() const { return mLastSmoothScrollOrigin; }
   uint32_t CurrentScrollGeneration() const { return mScrollGeneration; }
@@ -463,16 +466,19 @@ public:
   nsPoint mLastUpdateImagesPos;
   bool mHadDisplayPortAtLastImageUpdate;
   nsRect mDisplayPortAtLastImageUpdate;
 
   nsRect mPrevScrolledRect;
 
   FrameMetrics::ViewID mScrollParentID;
 
+  // Timer to remove the displayport some time after scrolling has stopped
+  nsCOMPtr<nsITimer> mDisplayPortExpiryTimer;
+
   bool mNeverHasVerticalScrollbar:1;
   bool mNeverHasHorizontalScrollbar:1;
   bool mHasVerticalScrollbar:1;
   bool mHasHorizontalScrollbar:1;
   bool mFrameIsUpdatingScrollbar:1;
   bool mDidHistoryRestore:1;
   // Is this the scrollframe for the document's viewport?
   bool mIsRoot:1;
@@ -504,16 +510,21 @@ public:
   bool mHasBeenScrolledRecently:1;
   // If true, the resizer is collapsed and not displayed
   bool mCollapsedResizer:1;
 
   // If true, the scroll frame should always be active because we always build
   // a scrollable layer. Used for asynchronous scrolling.
   bool mWillBuildScrollableLayer:1;
 
+  // If true, the scroll frame is an ancestor of other scrolling frames, so
+  // we shouldn't expire the displayport on this scrollframe unless those
+  // descendant scrollframes also have their displayports removed.
+  bool mIsScrollParent:1;
+
   // Whether we are the root scroll frame that is used for containerful
   // scrolling with a display port. If true, the scrollable frame
   // shouldn't attach frame metrics to its layers because the container
   // will already have the necessary frame metrics.
   bool mIsScrollableLayerInRootContainer:1;
 
   // If true, add clipping in ScrollFrameHelper::ComputeFrameMetrics.
   bool mAddClipRectToLayer:1;
@@ -854,16 +865,19 @@ public:
     return mHelper.DecideScrollableLayer(aBuilder, aDirtyRect, aAllowCreateDisplayPort);
   }
   virtual void NotifyImageVisibilityUpdate() override {
     mHelper.NotifyImageVisibilityUpdate();
   }
   virtual bool GetDisplayPortAtLastImageVisibilityUpdate(nsRect* aDisplayPort) override {
     return mHelper.GetDisplayPortAtLastImageVisibilityUpdate(aDisplayPort);
   }
+  void TriggerDisplayPortExpiration() override {
+    mHelper.TriggerDisplayPortExpiration();
+  }
 
   // nsIStatefulFrame
   NS_IMETHOD SaveState(nsPresState** aState) override {
     NS_ENSURE_ARG_POINTER(aState);
     *aState = mHelper.SaveState();
     return NS_OK;
   }
   NS_IMETHOD RestoreState(nsPresState* aState) override {
@@ -1327,16 +1341,19 @@ public:
     return mHelper.DecideScrollableLayer(aBuilder, aDirtyRect, aAllowCreateDisplayPort);
   }
   virtual void NotifyImageVisibilityUpdate() override {
     mHelper.NotifyImageVisibilityUpdate();
   }
   virtual bool GetDisplayPortAtLastImageVisibilityUpdate(nsRect* aDisplayPort) override {
     return mHelper.GetDisplayPortAtLastImageVisibilityUpdate(aDisplayPort);
   }
+  void TriggerDisplayPortExpiration() override {
+    mHelper.TriggerDisplayPortExpiration();
+  }
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
 protected:
   nsXULScrollFrame(nsStyleContext* aContext, bool aIsRoot,
                    bool aClipAllDescendants);
--- a/layout/generic/nsHTMLCanvasFrame.h
+++ b/layout/generic/nsHTMLCanvasFrame.h
@@ -81,17 +81,18 @@ public:
 #ifdef ACCESSIBILITY
   virtual mozilla::a11y::AccType AccessibleType() override;
 #endif
 
   virtual nsIAtom* GetType() const override;
 
   virtual bool IsFrameOfType(uint32_t aFlags) const override
   {
-    return nsSplittableFrame::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced));
+    return nsSplittableFrame::IsFrameOfType(aFlags &
+      ~(nsIFrame::eReplaced | nsIFrame::eReplacedSizing));
   }
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
   // Inserted child content gets its frames parented by our child block
   virtual nsContainerFrame* GetContentInsertionFrame() override {
--- a/layout/generic/nsHTMLReflowMetrics.h
+++ b/layout/generic/nsHTMLReflowMetrics.h
@@ -214,34 +214,39 @@ public:
 
   explicit nsHTMLReflowMetrics(const nsHTMLReflowState& aState, uint32_t aFlags = 0);
 
   // ISize and BSize are logical-coordinate dimensions:
   // ISize is the size in the writing mode's inline direction (which equates to
   // width in horizontal writing modes, height in vertical ones), and BSize is
   // the size in the block-progression direction.
   nscoord ISize(mozilla::WritingMode aWritingMode) const {
-    CHECK_WRITING_MODE(aWritingMode);
+    NS_ASSERTION(!aWritingMode.IsOrthogonalTo(mWritingMode),
+                 "mismatched writing mode");
     return mISize;
   }
   nscoord BSize(mozilla::WritingMode aWritingMode) const {
-    CHECK_WRITING_MODE(aWritingMode);
+    NS_ASSERTION(!aWritingMode.IsOrthogonalTo(mWritingMode),
+                 "mismatched writing mode");
     return mBSize;
   }
   mozilla::LogicalSize Size(mozilla::WritingMode aWritingMode) const {
-    CHECK_WRITING_MODE(aWritingMode);
+    NS_ASSERTION(!aWritingMode.IsOrthogonalTo(mWritingMode),
+                 "mismatched writing mode");
     return mozilla::LogicalSize(aWritingMode, mISize, mBSize);
   }
 
   nscoord& ISize(mozilla::WritingMode aWritingMode) {
-    CHECK_WRITING_MODE(aWritingMode);
+    NS_ASSERTION(!aWritingMode.IsOrthogonalTo(mWritingMode),
+                 "mismatched writing mode");
     return mISize;
   }
   nscoord& BSize(mozilla::WritingMode aWritingMode) {
-    CHECK_WRITING_MODE(aWritingMode);
+    NS_ASSERTION(!aWritingMode.IsOrthogonalTo(mWritingMode),
+                 "mismatched writing mode");
     return mBSize;
   }
 
   // Set inline and block size from a LogicalSize, converting to our
   // writing mode as necessary.
   void SetSize(mozilla::WritingMode aWM, mozilla::LogicalSize aSize)
   {
     mozilla::LogicalSize convertedSize = aSize.ConvertTo(mWritingMode, aWM);
--- a/layout/generic/nsHTMLReflowState.cpp
+++ b/layout/generic/nsHTMLReflowState.cpp
@@ -2920,19 +2920,24 @@ nsHTMLReflowState::ComputeMinMaxValues(c
     ComputedMaxBSize() = ComputedMinBSize();
   }
 }
 
 void
 nsHTMLReflowState::SetTruncated(const nsHTMLReflowMetrics& aMetrics,
                                 nsReflowStatus* aStatus) const
 {
-  if (AvailableHeight() != NS_UNCONSTRAINEDSIZE &&
-      AvailableHeight() < aMetrics.Height() &&
-      !mFlags.mIsTopOfPage) {
+  const WritingMode containerWM = aMetrics.GetWritingMode();
+  if (GetWritingMode().IsOrthogonalTo(containerWM)) {
+    // Orthogonal flows are always reflowed with an unconstrained dimension,
+    // so should never end up truncated (see nsHTMLReflowState::Init()).
+    *aStatus &= ~NS_FRAME_TRUNCATED;
+  } else if (AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
+             AvailableBSize() < aMetrics.BSize(containerWM) &&
+             !mFlags.mIsTopOfPage) {
     *aStatus |= NS_FRAME_TRUNCATED;
   } else {
     *aStatus &= ~NS_FRAME_TRUNCATED;
   }
 }
 
 bool
 nsHTMLReflowState::IsFloating() const
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -2132,16 +2132,21 @@ public:
     eBlockFrame =                       1 << 12,
     eTablePart =                        1 << 13,
     // If this bit is set, the frame doesn't allow ignorable whitespace as
     // children. For example, the whitespace between <table>\n<tr>\n<td>
     // will be excluded during the construction of children. 
     eExcludesIgnorableWhitespace =      1 << 14,
     eSupportsCSSTransforms =            1 << 15,
 
+    // A replaced element that has replaced-element sizing
+    // characteristics (i.e., like images or iframes), as opposed to
+    // inline-block sizing characteristics (like form controls).
+    eReplacedSizing =                   1 << 16,
+
     // These are to allow nsFrame::Init to assert that IsFrameOfType
     // implementations all call the base class method.  They are only
     // meaningful in DEBUG builds.
     eDEBUGAllFrames =                   1 << 30,
     eDEBUGNoFrames =                    1 << 31
   };
 
   /**
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -448,11 +448,18 @@ public:
   virtual void NotifyImageVisibilityUpdate() = 0;
 
   /**
    * Returns true if this scroll frame had a display port at the last image
    * visibility update and fills in aDisplayPort with that displayport. Returns
    * false otherwise, and doesn't touch aDisplayPort.
    */
   virtual bool GetDisplayPortAtLastImageVisibilityUpdate(nsRect* aDisplayPort) = 0;
+
+  /**
+   * This is called when a descendant scrollframe's has its displayport expired.
+   * This function will check to see if this scrollframe may safely expire its
+   * own displayport and schedule a timer to do that if it is safe.
+   */
+  virtual void TriggerDisplayPortExpiration() = 0;
 };
 
 #endif
--- a/layout/generic/nsImageFrame.h
+++ b/layout/generic/nsImageFrame.h
@@ -107,17 +107,18 @@ public:
 #ifdef ACCESSIBILITY
   virtual mozilla::a11y::AccType AccessibleType() override;
 #endif
 
   virtual nsIAtom* GetType() const override;
 
   virtual bool IsFrameOfType(uint32_t aFlags) const override
   {
-    return ImageFrameSuper::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced));
+    return ImageFrameSuper::IsFrameOfType(aFlags &
+      ~(nsIFrame::eReplaced | nsIFrame::eReplacedSizing));
   }
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
   void List(FILE* out = stderr, const char* aPrefix = "", 
             uint32_t aFlags = 0) const override;
 #endif
 
--- a/layout/generic/nsPluginFrame.h
+++ b/layout/generic/nsPluginFrame.h
@@ -86,17 +86,18 @@ public:
   virtual nsresult  HandleEvent(nsPresContext* aPresContext,
                                 mozilla::WidgetGUIEvent* aEvent,
                                 nsEventStatus* aEventStatus) override;
 
   virtual nsIAtom* GetType() const override;
 
   virtual bool IsFrameOfType(uint32_t aFlags) const override
   {
-    return nsPluginFrameSuper::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced));
+    return nsPluginFrameSuper::IsFrameOfType(aFlags &
+      ~(nsIFrame::eReplaced | nsIFrame::eReplacedSizing));
   }
 
   virtual bool NeedsView() override { return true; }
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
--- a/layout/generic/nsSubDocumentFrame.h
+++ b/layout/generic/nsSubDocumentFrame.h
@@ -33,17 +33,19 @@ public:
 
   NS_DECL_QUERYFRAME
 
   virtual nsIAtom* GetType() const override;
 
   virtual bool IsFrameOfType(uint32_t aFlags) const override
   {
     return nsSubDocumentFrameSuper::IsFrameOfType(aFlags &
-      ~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock));
+      ~(nsIFrame::eReplaced |
+        nsIFrame::eReplacedSizing |
+        nsIFrame::eReplacedContainsBlock));
   }
 
   virtual void Init(nsIContent*       aContent,
                     nsContainerFrame* aParent,
                     nsIFrame*         aPrevInFlow) override;
 
   virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
 
--- a/layout/generic/nsVideoFrame.h
+++ b/layout/generic/nsVideoFrame.h
@@ -72,17 +72,18 @@ public:
 #ifdef ACCESSIBILITY
   virtual mozilla::a11y::AccType AccessibleType() override;
 #endif
 
   virtual nsIAtom* GetType() const override;
 
   virtual bool IsFrameOfType(uint32_t aFlags) const override
   {
-    return nsSplittableFrame::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced));
+    return nsSplittableFrame::IsFrameOfType(aFlags &
+      ~(nsIFrame::eReplaced | nsIFrame::eReplacedSizing));
   }
   
   virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override;
   virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
                                         uint32_t aFilters) override;
 
   nsIContent* GetPosterImage() { return mPosterImage; }
 
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -55,23 +55,23 @@ asserts(2) skip-if(!cocoaWidget) HTTP(..
 == 25888-3l-block.html 25888-3l-ref.html
 == 25888-3r-block.html 25888-3r-ref.html
 skip-if(B2G||Mulet) == 28811-1a.html 28811-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 fuzzy-if(gtkWidget,6,26200) == 28811-1b.html 28811-1-ref.html  # Bug 1128229
 skip-if(B2G||Mulet) == 28811-2a.html 28811-2-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 fuzzy-if(gtkWidget,6,26200) == 28811-2b.html 28811-2-ref.html  # Bug 1128229
 == 40596-1a.html 40596-1-ref.html
 != 40596-1b.html 40596-1-ref.html
-== 40596-1c.html 40596-1-ref.html
+!= 40596-1c.html 40596-1-ref.html
 != 40596-1d.html 40596-1-ref.html
-== 40596-1e.html 40596-1-ref.html
+!= 40596-1e.html 40596-1-ref.html
 != 40596-1f.html 40596-1-ref.html
-== 40596-1g.html 40596-1-ref.html
+!= 40596-1g.html 40596-1-ref.html
 != 40596-1h.html 40596-1-ref.html
-== 40596-1i.html 40596-1-ref.html
+!= 40596-1i.html 40596-1-ref.html
 != 40596-1j.html 40596-1-ref.html
 == 50630-1a.html 50630-1-ref.html
 == 50630-1b.html 50630-1-ref.html
 == 50630-1c.html 50630-1-ref.html
 == 50630-2.html 50630-2-ref.html
 == 50630-3.html 50630-3-ref.html
 == 50630-4.html 50630-4-ref.html
 == 50630-4.html 50630-4-ref2.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-sizing/min-intrinsic-with-percents-across-elements-ref.html
@@ -0,0 +1,126 @@
+<!DOCTYPE HTML>
+<title>References for bug 823483</title>
+<style>
+
+body, input { font-size: 10px }
+input { padding: 0 1px; border: 1px solid maroon; font-family: monospace }
+
+td:first-child {
+   background: aqua;
+   border: thin solid;
+   padding: 1px 0;
+}
+
+td:nth-child(2) {
+  width: 100%;
+}
+
+td:nth-child(1) > * { vertical-align: bottom }
+
+canvas {
+  background: blue
+}
+
+</style>
+
+<table><tr>
+  <td><img></td>
+  <td>img, unstyled</td>
+</tr></table>
+
+<table><tr>
+  <td><img style="width: 0; height: 0"></td>
+  <td>img, width: 50%</td>
+</tr></table>
+
+<table><tr>
+  <td><img style="width: 0; height: 0;"></td>
+  <td>img, max-width: 50%</td>
+</tr></table>
+
+<table><tr>
+  <td><canvas height="10" width="10"></canvas></td>
+  <td>canvas, unstyled</td>
+</tr></table>
+
+<table><tr>
+  <td><canvas style="width: 0; height: 0" height="10" width="10"></canvas></td>
+  <td>canvas, width: 50%</td>
+</tr></table>
+
+<table><tr>
+  <td><canvas style="width: 0; height: 0" height="10" width="10"></canvas></td>
+  <td>canvas, max-width: 50%</td>
+</tr></table>
+
+<table><tr>
+  <td><iframe height="10" width="10"></iframe></td>
+  <td>iframe, almost unstyled</td>
+</tr></table>
+
+<table><tr>
+  <td><div style="width: 4px"><iframe style="vertical-align: bottom" height="10" width="2"></iframe></div></td>
+  <td>iframe, width: 50%</td>
+</tr></table>
+
+<table><tr>
+  <td><div style="width: 4px"><iframe style="vertical-align: bottom" height="10" width="2"></iframe></div></td>
+  <td>iframe, max-width: 50%</td>
+</tr></table>
+
+<table><tr>
+  <td><input type="text"></td>
+  <td>input type="text", unstyled</td>
+</tr></table>
+
+<table><tr>
+  <td><div style="width:4px"><input type="text" style="width: 2px"></div></td>
+  <td>input type="text&