merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 07 Jun 2016 15:23:03 +0200
changeset 300790 7f7c7d24700eb80ce328b05fd260ec58e9725ca4
parent 300651 bc9db3800f3c2bd3c25a98614dc6b40571d25795 (current diff)
parent 300789 d8c954e520e4f30cc5e4d1b9c574f777edba24c4 (diff)
child 300866 0deb0ab8cf3ff074be1542471fd6eb0dbfadd98f
child 300878 5c76c7484b9a9ee6855a57b9b04c46b8c6e75728
push id30321
push usercbook@mozilla.com
push dateTue, 07 Jun 2016 13:29:08 +0000
treeherdermozilla-central@7f7c7d24700e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone50.0a1
first release with
nightly linux32
7f7c7d24700e / 50.0a1 / 20160607071209 / files
nightly linux64
7f7c7d24700e / 50.0a1 / 20160607071209 / files
nightly mac
7f7c7d24700e / 50.0a1 / 20160607071209 / files
nightly win32
7f7c7d24700e / 50.0a1 / 20160607071209 / files
nightly win64
7f7c7d24700e / 50.0a1 / 20160607071209 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
browser/base/content/browser.js
build/macosx/universal/flight.mk
gfx/thebes/gfxQuartzImageSurface.cpp
gfx/thebes/gfxQuartzImageSurface.h
js/src/jit-test/tests/basic/cross-context-stack-1.js
js/src/jit-test/tests/debug/cross-context-1.js
js/src/jit-test/tests/debug/cross-context-2.js
js/src/jit-test/tests/debug/cross-context-3.js
js/src/jit-test/tests/saved-stacks/new-context.js
js/src/jsapi-tests/testContexts.cpp
testing/web-platform/meta/html/semantics/embedded-content/media-elements/track/track-element/track-api-texttracks.html.ini
toolkit/components/prompts/test/test_bug625187.html
toolkit/components/telemetry/Histograms.json
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1299,18 +1299,16 @@ var gBrowserInit = {
       gDataNotificationInfoBar.init();
 
     gBrowserThumbnails.init();
 
     gMenuButtonBadgeManager.init();
 
     gMenuButtonUpdateBadge.init();
 
-    UserContextStyleManager.init();
-
     window.addEventListener("mousemove", MousePosTracker, false);
     window.addEventListener("dragover", MousePosTracker, false);
 
     gNavToolbox.addEventListener("customizationstarting", CustomizationHandler);
     gNavToolbox.addEventListener("customizationchange", CustomizationHandler);
     gNavToolbox.addEventListener("customizationending", CustomizationHandler);
 
     // End startup crash tracking after a delay to catch crashes while restoring
@@ -7950,37 +7948,8 @@ TabModalPromptBox.prototype = {
   get browser() {
     let browser = this._weakBrowserRef.get();
     if (!browser) {
       throw "Stale promptbox! The associated browser is gone.";
     }
     return browser;
   },
 };
-
-let UserContextStyleManager = {
-  init() {
-    for (let styleId in document.styleSheets) {
-      let styleSheet = document.styleSheets[styleId];
-      if (styleSheet.href != "chrome://browser/content/usercontext/usercontext.css") {
-        continue;
-      }
-
-      if (ContextualIdentityService.needsCssRule()) {
-        for (let ruleId in styleSheet.cssRules) {
-          let cssRule = styleSheet.cssRules[ruleId];
-          if (cssRule.selectorText != ":root") {
-            continue;
-          }
-
-          ContextualIdentityService.storeCssRule(cssRule.cssText);
-          break;
-        }
-      }
-
-      ContextualIdentityService.cssRules().forEach(rule => {
-        styleSheet.insertRule(rule, styleSheet.cssRules.length);
-      });
-
-      break;
-    }
-  },
-};
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1962,18 +1962,21 @@
 
             if (!aURI || isBlankPageURL(aURI)) {
               t.setAttribute("label", this.mStringBundle.getString("tabs.emptyTabTitle"));
             } else if (aURI.toLowerCase().startsWith("javascript:")) {
               // This can go away when bug 672618 or bug 55696 are fixed.
               t.setAttribute("label", aURI);
             }
 
-            if (aUserContextId)
+            if (aUserContextId) {
               t.setAttribute("usercontextid", aUserContextId);
+              ContextualIdentityService.setTabStyle(t);
+            }
+
             t.setAttribute("crop", "end");
             t.setAttribute("onerror", "this.removeAttribute('image');");
             t.className = "tabbrowser-tab";
 
             this.tabContainer._unlockTabSizing();
 
             // When overflowing, new tabs are scrolled into view smoothly, which
             // doesn't go well together with the width transition. So we skip the
@@ -2610,16 +2613,17 @@
               modifiedAttrs.push("muted");
             }
             if (aOtherTab.hasAttribute("soundplaying")) {
               aOurTab.setAttribute("soundplaying", "true");
               modifiedAttrs.push("soundplaying");
             }
             if (aOtherTab.hasAttribute("usercontextid")) {
               aOurTab.setAttribute("usercontextid", aOtherTab.getAttribute("usercontextid"));
+              ContextualIdentityService.setTabStyle(aOurTab);
               modifiedAttrs.push("usercontextid");
             }
 
             // If the other tab is pending (i.e. has not been restored, yet)
             // then do not switch docShells but retrieve the other tab's state
             // and apply it to our tab.
             if (isPending) {
               SessionStore.setTabState(aOurTab, SessionStore.getTabState(aOtherTab));
--- a/browser/base/content/test/general/browser_readerMode.js
+++ b/browser/base/content/test/general/browser_readerMode.js
@@ -64,19 +64,22 @@ add_task(function* test_reader_button() 
   yield new Promise((resolve, reject) => {
     waitForClipboard(url, function () {
       gURLBar.focus();
       gURLBar.select();
       goDoCommand("cmd_copy");
     }, resolve, reject);
   });
 
+  info("Got correct URL when copying");
+
   // Switch page back out of reader mode.
+  let promisePageShow = BrowserTestUtils.waitForContentEvent(tab.linkedBrowser, "pageshow");
   readerButton.click();
-  yield BrowserTestUtils.waitForContentEvent(tab.linkedBrowser, "pageshow");
+  yield promisePageShow;
   is(gBrowser.selectedBrowser.currentURI.spec, url,
     "Back to the original page after clicking active reader mode button");
   ok(gBrowser.selectedBrowser.canGoForward,
     "Moved one step back in the session history.");
 
   // Load a new tab that is NOT reader-able.
   let newTab = gBrowser.selectedTab = gBrowser.addTab();
   yield promiseTabLoadEvent(newTab, "about:robots");
--- a/browser/components/contextualidentity/ContextualIdentityService.jsm
+++ b/browser/components/contextualidentity/ContextualIdentityService.jsm
@@ -4,16 +4,18 @@
 
 this.EXPORTED_SYMBOLS = ["ContextualIdentityService"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm")
 
+const DEFAULT_TAB_COLOR = "#909090"
+
 XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
   return Services.strings.createBundle("chrome://browser/locale/browser.properties");
 });
 
 this.ContextualIdentityService = {
   _identities: [
     { userContextId: 1,
       icon: "chrome://browser/skin/usercontext/personal.svg",
@@ -47,38 +49,27 @@ this.ContextualIdentityService = {
     return this._identities.find(info => info.userContextId == userContextId);
   },
 
   getUserContextLabel(userContextId) {
     let identity = this.getIdentityFromId(userContextId);
     return gBrowserBundle.GetStringFromName(identity.label);
   },
 
-  needsCssRule() {
-    return !this._cssRule;
-  },
-
-  storeCssRule(cssRule) {
-    this._cssRule = cssRule;
-  },
-
-  cssRules() {
-    let rules = [];
-
-    if (this.needsCssRule()) {
-      return rules;
+  setTabStyle(tab) {
+    // inline style is only a temporary fix for some bad performances related
+    // to the use of CSS vars. This code will be removed in bug 1278177.
+    if (!tab.hasAttribute("usercontextid")) {
+      tab.style.removeProperty("background-image");
+      tab.style.removeProperty("background-size");
+      tab.style.removeProperty("background-repeat");
+      return;
     }
 
-    /* The CSS Rules for the ContextualIdentity tabs are set up like this:
-     * 1. :root { --usercontext-color-<id>: #color }
-     * 2. the template, replacing 'id' with the userContextId.
-     * 3. tabbrowser-tab[usercontextid="<id>"] { background-image: var(--usercontext-tab-<id>) }
-     */
-    for (let identity of this._identities) {
-      rules.push(":root { --usercontext-color-" + identity.userContextId + ": " + identity.color + " }");
+    let userContextId = tab.getAttribute("usercontextid");
+    let identity = this.getIdentityFromId(userContextId);
 
-      rules.push(this._cssRule.replace(/id/g, identity.userContextId));
-      rules.push(".tabbrowser-tab[usercontextid=\"" + identity.userContextId + "\"] { background-image: var(--usercontext-tab-" + identity.userContextId + ") }");
-    }
-
-    return rules;
+    let color = identity ? identity.color : DEFAULT_TAB_COLOR;
+    tab.style.backgroundImage = "linear-gradient(to right, transparent 20%, " + color + " 30%, " + color + " 70%, transparent 80%)";
+    tab.style.backgroundSize = "auto 2px";
+    tab.style.backgroundRepeat = "no-repeat";
   },
 }
--- a/browser/components/contextualidentity/content/usercontext.css
+++ b/browser/components/contextualidentity/content/usercontext.css
@@ -1,27 +1,13 @@
-:root {
-  /* This is used as a template for ContextualIdentityService. 'id' will be
-   * replaced with the userContextId */
-  --usercontext-tab-id: linear-gradient(to right, transparent 20%, var(--usercontext-color-id) 30%, var(--usercontext-color-id) 70%, transparent 80%);
-}
-
 #userContext-indicator {
   height: 16px;
   width: 16px;
 }
 
 #userContext-label {
   margin-inline-end: 3px;
   color: #909090;
 }
 
 #userContext-icons {
   -moz-box-align: center;
 }
-
-/* User Context UI - change tab decoration depending on userContextId.
-   Defaults to gray for unknown usercontextids. */
-.tabbrowser-tab[usercontextid] {
-  background-image: linear-gradient(to right, transparent 20%, #909090 30%, #909090 70%, transparent 80%);
-  background-size: auto 2px;
-  background-repeat: no-repeat;
-}
--- a/browser/components/sessionstore/SessionHistory.jsm
+++ b/browser/components/sessionstore/SessionHistory.jsm
@@ -140,20 +140,16 @@ var SessionHistoryInternal = {
       entry.referrer = shEntry.referrerURI.spec;
       entry.referrerPolicy = shEntry.referrerPolicy;
     }
 
     if (shEntry.originalURI) {
       entry.originalURI = shEntry.originalURI.spec;
     }
 
-    if (shEntry.loadReplace) {
-      entry.loadReplace = shEntry.loadReplace;
-    }
-
     if (shEntry.srcdocData)
       entry.srcdocData = shEntry.srcdocData;
 
     if (shEntry.isSrcdocEntry)
       entry.isSrcdocEntry = shEntry.isSrcdocEntry;
 
     if (shEntry.baseURI)
       entry.baseURI = shEntry.baseURI.spec;
@@ -310,19 +306,16 @@ var SessionHistoryInternal = {
       shEntry.contentType = entry.contentType;
     if (entry.referrer) {
       shEntry.referrerURI = Utils.makeURI(entry.referrer);
       shEntry.referrerPolicy = entry.referrerPolicy;
     }
     if (entry.originalURI) {
       shEntry.originalURI = Utils.makeURI(entry.originalURI);
     }
-    if (entry.loadReplace) {
-      shEntry.loadReplace = entry.loadReplace;
-    }
     if (entry.isSrcdocEntry)
       shEntry.srcdocData = entry.srcdocData;
     if (entry.baseURI)
       shEntry.baseURI = Utils.makeURI(entry.baseURI);
 
     if (entry.cacheKey) {
       var cacheKey = Cc["@mozilla.org/supports-PRUint32;1"].
                      createInstance(Ci.nsISupportsPRUint32);
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -222,9 +222,10 @@ skip-if = os == "mac"
 [browser_multiple_navigateAndRestore.js]
 run-if = e10s
 [browser_async_window_flushing.js]
 [browser_forget_async_closings.js]
 [browser_newtab_userTypedValue.js]
 [browser_parentProcessRestoreHash.js]
 run-if = e10s
 [browser_sessionStoreContainer.js]
+[browser_windowStateContainer.js]
 [browser_1234021.js]
--- a/browser/components/sessionstore/test/browser_sessionStoreContainer.js
+++ b/browser/components/sessionstore/test/browser_sessionStoreContainer.js
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-requestLongerTimeout(3);
+"use strict";
 
 add_task(function* () {
   for (let i = 0; i < 3; ++i) {
     let tab = gBrowser.addTab("http://example.com/", { userContextId: i });
     let browser = tab.linkedBrowser;
 
     yield promiseBrowserLoaded(browser);
 
@@ -64,123 +64,8 @@ add_task(function* () {
     Assert.equal(docShell.getOriginAttributes().userContextId,
                  args.expectedId,
                  "The docShell has the correct userContextId");
   });
 
   yield promiseRemoveTab(tab2);
 });
 
-add_task(function* () {
-  let win = window.openDialog(location, "_blank", "chrome,all,dialog=no");
-  yield promiseWindowLoaded(win);
-
-  // Create 4 tabs with different userContextId.
-  for (let userContextId = 1; userContextId < 5; userContextId++) {
-    let tab = win.gBrowser.addTab("http://example.com/", {userContextId});
-    yield promiseBrowserLoaded(tab.linkedBrowser);
-    yield TabStateFlusher.flush(tab.linkedBrowser);
-  }
-
-  // Move the default tab of window to the end.
-  // We want the 1st tab to have non-default userContextId, so later when we
-  // restore into win2 we can test restore into an existing tab with different
-  // userContextId.
-  win.gBrowser.moveTabTo(win.gBrowser.tabs[0], win.gBrowser.tabs.length - 1);
-
-  let winState = JSON.parse(ss.getWindowState(win));
-
-  for (let i = 0; i < 4; i++) {
-    Assert.equal(winState.windows[0].tabs[i].userContextId, i + 1,
-                 "1st Window: tabs[" + i + "].userContextId should exist.");
-  }
-
-  let win2 = window.openDialog(location, "_blank", "chrome,all,dialog=no");
-  yield promiseWindowLoaded(win2);
-
-  // Create tabs with different userContextId, but this time we create them with
-  // fewer tabs and with different order with win.
-  for (let userContextId = 3; userContextId > 0; userContextId--) {
-    let tab = win2.gBrowser.addTab("http://example.com/", {userContextId});
-    yield promiseBrowserLoaded(tab.linkedBrowser);
-    yield TabStateFlusher.flush(tab.linkedBrowser);
-  }
-
-  ss.setWindowState(win2, JSON.stringify(winState), true);
-
-  for (let i = 0; i < 4; i++) {
-    let browser = win2.gBrowser.tabs[i].linkedBrowser;
-    yield ContentTask.spawn(browser, { expectedId: i + 1 }, function* (args) {
-      Assert.equal(docShell.getOriginAttributes().userContextId,
-                   args.expectedId,
-                   "The docShell has the correct userContextId");
-
-      Assert.equal(content.document.nodePrincipal.originAttributes.userContextId,
-                   args.expectedId,
-                   "The document has the correct userContextId");
-    });
-  }
-
-  // Test the last tab, which doesn't have userContextId.
-  let browser = win2.gBrowser.tabs[4].linkedBrowser;
-  yield ContentTask.spawn(browser, { expectedId: 0 }, function* (args) {
-    Assert.equal(docShell.getOriginAttributes().userContextId,
-                 args.expectedId,
-                 "The docShell has the correct userContextId");
-
-    Assert.equal(content.document.nodePrincipal.originAttributes.userContextId,
-                 args.expectedId,
-                 "The document has the correct userContextId");
-  });
-
-  yield BrowserTestUtils.closeWindow(win);
-  yield BrowserTestUtils.closeWindow(win2);
-});
-
-add_task(function* () {
-  let win = window.openDialog(location, "_blank", "chrome,all,dialog=no");
-  yield promiseWindowLoaded(win);
-
-  let tab = win.gBrowser.addTab("http://example.com/", { userContextId: 1 });
-  yield promiseBrowserLoaded(tab.linkedBrowser);
-  yield TabStateFlusher.flush(tab.linkedBrowser);
-
-  // win should have 1 default tab, and 1 container tab.
-  Assert.equal(win.gBrowser.tabs.length, 2, "win should have 2 tabs");
-
-  let winState = JSON.parse(ss.getWindowState(win));
-
-  for (let i = 0; i < 2; i++) {
-    Assert.equal(winState.windows[0].tabs[i].userContextId, i,
-                 "1st Window: tabs[" + i + "].userContextId should be " + i);
-  }
-
-  let win2 = window.openDialog(location, "_blank", "chrome,all,dialog=no");
-  yield promiseWindowLoaded(win2);
-
-  let tab2 = win2.gBrowser.addTab("http://example.com/", { userContextId : 1 });
-  yield promiseBrowserLoaded(tab2.linkedBrowser);
-  yield TabStateFlusher.flush(tab2.linkedBrowser);
-
-  // Move the first normal tab to end, so the first tab of win2 will be a
-  // container tab.
-  win2.gBrowser.moveTabTo(win2.gBrowser.tabs[0], win2.gBrowser.tabs.length - 1);
-  yield TabStateFlusher.flush(win2.gBrowser.tabs[0].linkedBrowser);
-
-  ss.setWindowState(win2, JSON.stringify(winState), true);
-
-  for (let i = 0; i < 2; i++) {
-    let browser = win2.gBrowser.tabs[i].linkedBrowser;
-    yield ContentTask.spawn(browser, { expectedId: i }, function* (args) {
-      Assert.equal(docShell.getOriginAttributes().userContextId,
-                   args.expectedId,
-                   "The docShell has the correct userContextId");
-
-      Assert.equal(content.document.nodePrincipal.originAttributes.userContextId,
-                   args.expectedId,
-                   "The document has the correct userContextId");
-    });
-  }
-
-  yield BrowserTestUtils.closeWindow(win);
-  yield BrowserTestUtils.closeWindow(win2);
-});
-
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_windowStateContainer.js
@@ -0,0 +1,119 @@
+"use strict";
+
+requestLongerTimeout(2);
+
+add_task(function* () {
+  let win = window.openDialog(location, "_blank", "chrome,all,dialog=no");
+  yield promiseWindowLoaded(win);
+
+  // Create 4 tabs with different userContextId.
+  for (let userContextId = 1; userContextId < 5; userContextId++) {
+    let tab = win.gBrowser.addTab("http://example.com/", {userContextId});
+    yield promiseBrowserLoaded(tab.linkedBrowser);
+    yield TabStateFlusher.flush(tab.linkedBrowser);
+  }
+
+  // Move the default tab of window to the end.
+  // We want the 1st tab to have non-default userContextId, so later when we
+  // restore into win2 we can test restore into an existing tab with different
+  // userContextId.
+  win.gBrowser.moveTabTo(win.gBrowser.tabs[0], win.gBrowser.tabs.length - 1);
+
+  let winState = JSON.parse(ss.getWindowState(win));
+
+  for (let i = 0; i < 4; i++) {
+    Assert.equal(winState.windows[0].tabs[i].userContextId, i + 1,
+                 "1st Window: tabs[" + i + "].userContextId should exist.");
+  }
+
+  let win2 = window.openDialog(location, "_blank", "chrome,all,dialog=no");
+  yield promiseWindowLoaded(win2);
+
+  // Create tabs with different userContextId, but this time we create them with
+  // fewer tabs and with different order with win.
+  for (let userContextId = 3; userContextId > 0; userContextId--) {
+    let tab = win2.gBrowser.addTab("http://example.com/", {userContextId});
+    yield promiseBrowserLoaded(tab.linkedBrowser);
+    yield TabStateFlusher.flush(tab.linkedBrowser);
+  }
+
+  ss.setWindowState(win2, JSON.stringify(winState), true);
+
+  for (let i = 0; i < 4; i++) {
+    let browser = win2.gBrowser.tabs[i].linkedBrowser;
+    yield ContentTask.spawn(browser, { expectedId: i + 1 }, function* (args) {
+      Assert.equal(docShell.getOriginAttributes().userContextId,
+                   args.expectedId,
+                   "The docShell has the correct userContextId");
+
+      Assert.equal(content.document.nodePrincipal.originAttributes.userContextId,
+                   args.expectedId,
+                   "The document has the correct userContextId");
+    });
+  }
+
+  // Test the last tab, which doesn't have userContextId.
+  let browser = win2.gBrowser.tabs[4].linkedBrowser;
+  yield ContentTask.spawn(browser, { expectedId: 0 }, function* (args) {
+    Assert.equal(docShell.getOriginAttributes().userContextId,
+                 args.expectedId,
+                 "The docShell has the correct userContextId");
+
+    Assert.equal(content.document.nodePrincipal.originAttributes.userContextId,
+                 args.expectedId,
+                 "The document has the correct userContextId");
+  });
+
+  yield BrowserTestUtils.closeWindow(win);
+  yield BrowserTestUtils.closeWindow(win2);
+});
+
+add_task(function* () {
+  let win = window.openDialog(location, "_blank", "chrome,all,dialog=no");
+  yield promiseWindowLoaded(win);
+
+  let tab = win.gBrowser.addTab("http://example.com/", { userContextId: 1 });
+  yield promiseBrowserLoaded(tab.linkedBrowser);
+  yield TabStateFlusher.flush(tab.linkedBrowser);
+
+  // win should have 1 default tab, and 1 container tab.
+  Assert.equal(win.gBrowser.tabs.length, 2, "win should have 2 tabs");
+
+  let winState = JSON.parse(ss.getWindowState(win));
+
+  for (let i = 0; i < 2; i++) {
+    Assert.equal(winState.windows[0].tabs[i].userContextId, i,
+                 "1st Window: tabs[" + i + "].userContextId should be " + i);
+  }
+
+  let win2 = window.openDialog(location, "_blank", "chrome,all,dialog=no");
+  yield promiseWindowLoaded(win2);
+
+  let tab2 = win2.gBrowser.addTab("http://example.com/", { userContextId : 1 });
+  yield promiseBrowserLoaded(tab2.linkedBrowser);
+  yield TabStateFlusher.flush(tab2.linkedBrowser);
+
+  // Move the first normal tab to end, so the first tab of win2 will be a
+  // container tab.
+  win2.gBrowser.moveTabTo(win2.gBrowser.tabs[0], win2.gBrowser.tabs.length - 1);
+  yield TabStateFlusher.flush(win2.gBrowser.tabs[0].linkedBrowser);
+
+  ss.setWindowState(win2, JSON.stringify(winState), true);
+
+  for (let i = 0; i < 2; i++) {
+    let browser = win2.gBrowser.tabs[i].linkedBrowser;
+    yield ContentTask.spawn(browser, { expectedId: i }, function* (args) {
+      Assert.equal(docShell.getOriginAttributes().userContextId,
+                   args.expectedId,
+                   "The docShell has the correct userContextId");
+
+      Assert.equal(content.document.nodePrincipal.originAttributes.userContextId,
+                   args.expectedId,
+                   "The document has the correct userContextId");
+    });
+  }
+
+  yield BrowserTestUtils.closeWindow(win);
+  yield BrowserTestUtils.closeWindow(win2);
+});
+
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -51,10 +51,13 @@ MOZ_APP_STATIC_INI=1
 MOZ_WEBGL_CONFORMANT=1
 # Enable navigator.mozPay
 MOZ_PAY=1
 # Enable activities. These are used for FxOS developers currently.
 MOZ_ACTIVITIES=1
 MOZ_JSDOWNLOADS=1
 MOZ_RUST_MP4PARSE=1
 
+# Enable checking that add-ons are signed by the trusted root
+MOZ_ADDON_SIGNING=1
+
 # Include the DevTools client, not just the server (which is the default)
 MOZ_DEVTOOLS=all
--- a/browser/modules/test/browser_BrowserUITelemetry_defaults.js
+++ b/browser/modules/test/browser_BrowserUITelemetry_defaults.js
@@ -5,22 +5,28 @@
 
 function test() {
   let s = {};
   Cu.import("resource:///modules/CustomizableUI.jsm", s);
   Cu.import("resource:///modules/BrowserUITelemetry.jsm", s);
 
   let { CustomizableUI, BrowserUITelemetry } = s;
 
-  Assert.ok(CustomizableUI.inDefaultState,
-            "No other test should have left CUI in a dirty state.");
+  // Bug 1278176 - DevEdition never has the UI in a default state by default.
+  if (!AppConstants.MOZ_DEV_EDITION) {
+    Assert.ok(CustomizableUI.inDefaultState,
+              "No other test should have left CUI in a dirty state.");
+  }
 
   let result = BrowserUITelemetry._getWindowMeasurements(window, 0);
 
-  Assert.deepEqual(result.defaultMoved, []);
+  // Bug 1278176 - DevEdition always reports the developer-button is moved.
+  if (!AppConstants.MOZ_DEV_EDITION) {
+    Assert.deepEqual(result.defaultMoved, []);
+  }
   Assert.deepEqual(result.nondefaultAdded, []);
   // This one is a bit weird - the "social-share-button" is dynamically added
   // to the toolbar as the feature is first used - but it's listed as being in
   // the toolbar by default so it doesn't end up in nondefaultAdded once it
   // is created. The end result is that it ends up in defaultRemoved before
   // the feature has been activated.
   // Bug 1273358 exists to fix this.
   Assert.deepEqual(result.defaultRemoved, ["social-share-button"]);
--- a/build/annotationProcessors/CodeGenerator.java
+++ b/build/annotationProcessors/CodeGenerator.java
@@ -23,16 +23,17 @@ public class CodeGenerator {
     private final StringBuilder cpp = new StringBuilder();
     private final StringBuilder header = new StringBuilder();
     private final StringBuilder natives = new StringBuilder();
     private final StringBuilder nativesInits = new StringBuilder();
 
     private final Class<?> cls;
     private final String clsName;
     private boolean isMultithreaded;
+    private int numNativesInits;
 
     private final HashSet<String> takenMethodNames = new HashSet<String>();
 
     public CodeGenerator(ClassWithOptions annotatedClass) {
         this.cls = annotatedClass.wrappedClass;
         this.clsName = annotatedClass.generatedName;
 
         final String unqualifiedName = Utils.getUnqualifiedName(clsName);
@@ -335,19 +336,20 @@ public class CodeGenerator {
 
         if (nativesInits.length() > 0) {
             nativesInits.append(',');
         }
 
         nativesInits.append(
                 "\n" +
                 "\n" +
-                "        mozilla::jni::MakeNativeMethod<" + traits + ">(\n" +
-                "                mozilla::jni::NativeStub<" + traits + ", Impl>\n" +
-                "                ::template Wrap<&Impl::" + info.wrapperName + ">)");
+                "    mozilla::jni::MakeNativeMethod<" + traits + ">(\n" +
+                "            mozilla::jni::NativeStub<" + traits + ", Impl>\n" +
+                "            ::template Wrap<&Impl::" + info.wrapperName + ">)");
+        numNativesInits++;
     }
 
     private String getLiteral(Object val, AnnotationInfo info) {
         final Class<?> type = val.getClass();
 
         if (type.equals(char.class) || type.equals(Character.class)) {
             final char c = (char) val;
             if (c >= 0x20 && c < 0x7F) {
@@ -557,18 +559,18 @@ public class CodeGenerator {
      *
      * @return The bytes to be written to the header file.
      */
     public String getNativesFileContents() {
         if (nativesInits.length() == 0) {
             return "";
         }
         natives.append(
-                "    static constexpr JNINativeMethod methods[] = {" + nativesInits + '\n' +
-                "    };\n" +
+                "    static const JNINativeMethod methods[" + numNativesInits + "];\n" +
                 "};\n" +
                 "\n" +
                 "template<class Impl>\n" +
-                "constexpr JNINativeMethod " + clsName + "::Natives<Impl>::methods[];\n" +
+                "const JNINativeMethod " + clsName + "::Natives<Impl>::methods[] = {" + nativesInits + '\n' +
+                "};\n" +
                 "\n");
         return natives.toString();
     }
 }
deleted file mode 100644
--- a/build/macosx/universal/flight.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-# BE CAREFUL!  This makefile handles a postflight_all rule for a
-# multi-project build, so DON'T rely on anything that might differ between
-# the two OBJDIRs.
-
-ifndef OBJDIR
-OBJDIR_ARCH_1 = $(MOZ_OBJDIR)/$(firstword $(MOZ_BUILD_PROJECTS))
-OBJDIR_ARCH_2 = $(MOZ_OBJDIR)/$(word 2,$(MOZ_BUILD_PROJECTS))
-DIST_ARCH_1 = $(OBJDIR_ARCH_1)/dist
-DIST_ARCH_2 = $(OBJDIR_ARCH_2)/dist
-DIST_UNI = $(DIST_ARCH_1)/universal
-OBJDIR = $(OBJDIR_ARCH_1)
-endif
-
-topsrcdir = $(TOPSRCDIR)
-DEPTH = $(OBJDIR)
-include $(OBJDIR)/config/autoconf.mk
-
-DIST = $(OBJDIR)/dist
-
-postflight_all:
-	mkdir -p $(DIST_UNI)/$(MOZ_PKG_APPNAME)
-	rm -f $(DIST_ARCH_2)/universal
-	ln -s $(abspath $(DIST_UNI)) $(DIST_ARCH_2)/universal
-# Stage a package for buildsymbols to be happy. Doing so in OBJDIR_ARCH_1
-# actually does a universal staging with both OBJDIR_ARCH_1 and OBJDIR_ARCH_2.
-	$(MAKE) -C $(OBJDIR_ARCH_1)/$(MOZ_BUILD_APP)/installer \
-	   PKG_SKIP_STRIP=1 stage-package
--- a/build/macosx/universal/mozconfig.common
+++ b/build/macosx/universal/mozconfig.common
@@ -1,16 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 mk_add_options MOZ_UNIFY_BDATE=1
 
-mk_add_options MOZ_POSTFLIGHT_ALL+=build/macosx/universal/flight.mk
-
 DARWIN_VERSION=`uname -r`
 ac_add_app_options i386 --target=i386-apple-darwin$DARWIN_VERSION
 ac_add_app_options x86_64 --target=x86_64-apple-darwin$DARWIN_VERSION
 ac_add_app_options i386 --with-unify-dist=../x86_64/dist
 ac_add_app_options x86_64 --with-unify-dist=../i386/dist
 
 ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.7.sdk
 
--- a/build/moz-automation.mk
+++ b/build/moz-automation.mk
@@ -83,16 +83,22 @@ automation/upload: automation/sdk
 # binaries/libs, and that's what we package/test.
 automation/pretty-package: automation/buildsymbols
 
 # The installer, sdk and packager all run stage-package, and may conflict
 # with each other.
 automation/installer: automation/package
 automation/sdk: automation/installer automation/package
 
+# Universal builds need package staging happening before buildsymbols
+# (bug 834228)
+ifdef UNIVERSAL_BINARY
+automation/buildsymbols: automation/package
+endif
+
 # The 'pretty' versions of targets run before the regular ones to avoid
 # conflicts in writing to the same files.
 automation/installer: automation/pretty-installer
 automation/package: automation/pretty-package
 automation/package-tests: automation/pretty-package-tests
 automation/l10n-check: automation/pretty-l10n-check
 automation/update-packaging: automation/pretty-update-packaging
 
--- a/client.mk
+++ b/client.mk
@@ -427,16 +427,25 @@ ifdef MOZ_POSTFLIGHT
 	done
 endif
 
 endif # MOZ_CURRENT_PROJECT
 
 ####################################
 # Postflight, after building all projects
 
+ifdef MOZ_AUTOMATION
+ifndef MOZ_CURRENT_PROJECT
+$(if $(MOZ_PGO),profiledbuild,realbuild)::
+# Only run the automation/build target for the first project.
+# (i.e. first platform of universal builds)
+	$(MAKE) -f $(TOPSRCDIR)/client.mk automation/build $(addprefix MOZ_CURRENT_PROJECT=,$(firstword $(MOZ_BUILD_PROJECTS)))
+endif
+endif
+
 realbuild postflight_all::
 ifeq (,$(MOZ_CURRENT_PROJECT)$(if $(MOZ_POSTFLIGHT_ALL),,1))
 # Don't run postflight_all for individual projects in multi-project builds
 # (when MOZ_CURRENT_PROJECT is set.)
 ifndef MOZ_BUILD_PROJECTS
 # Building a single project, OBJDIR is usable.
 	set -e; \
 	for mkfile in $(MOZ_POSTFLIGHT_ALL); do \
--- a/config/makefiles/debugmake.mk
+++ b/config/makefiles/debugmake.mk
@@ -69,17 +69,16 @@ showbuild:
 		CPP \
 		LD \
 		AR \
 		IMPLIB \
 		FILTER \
 		MKSHLIB \
 		MKCSHLIB \
 		RC \
-		MC \
 		CFLAGS \
 		OS_CFLAGS \
 		COMPILE_CFLAGS \
 		CXXFLAGS \
 		OS_CXXFLAGS \
 		COMPILE_CXXFLAGS \
 		COMPILE_CMFLAGS \
 		COMPILE_CMMFLAGS \
--- a/config/mozunit.py
+++ b/config/mozunit.py
@@ -68,17 +68,17 @@ class _MozTestResult(_TestResult):
     def addFailure(self, test, err):
         _TestResult.addFailure(self, test, err)
         self.printFail(test,err)
         self.stream.writeln("FAIL: {0}".format(self.getDescription(test)))
         self.stream.writeln(self.failures[-1][1])
 
     def printFail(self, test, err):
         exctype, value, tb = err
-        message = value.message.splitlines()[0]
+        message = value.message.splitlines()[0] if value.message else 'NO MESSAGE'
         # Skip test runner traceback levels
         while tb and self._is_relevant_tb_level(tb):
             tb = tb.tb_next
         if tb:
             _, ln, _ = inspect.getframeinfo(tb)[:3]
             message = 'line {0}: {1}'.format(ln, message)
         self.printStatus("TEST-UNEXPECTED-FAIL", test, message)
 
--- a/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
+++ b/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
@@ -43,19 +43,19 @@ struct DevTools : public ::testing::Test
 
   virtual void SetUp() {
     MOZ_ASSERT(!_initialized);
 
     rt = getRuntime();
     if (!rt)
       return;
 
-    cx = createContext();
-    if (!cx)
-      return;
+    MOZ_RELEASE_ASSERT(!cx);
+    MOZ_RELEASE_ASSERT(JS_ContextIterator(rt, &cx));
+
     JS_BeginRequest(cx);
 
     global.init(rt, createGlobal());
     if (!global)
       return;
     JS_EnterCompartment(cx, global);
 
     compartment = js::GetContextCompartment(cx);
@@ -88,20 +88,16 @@ struct DevTools : public ::testing::Test
 
   static void reportError(JSContext* cx, const char* message, JSErrorReport* report) {
     fprintf(stderr, "%s:%u:%s\n",
             report->filename ? report->filename : "<no filename>",
             (unsigned int) report->lineno,
             message);
   }
 
-  JSContext* createContext() {
-    return JS_NewContext(rt, 8192);
-  }
-
   static const JSClass* getGlobalClass() {
     static const JSClassOps globalClassOps = {
       nullptr, nullptr, nullptr, nullptr,
       nullptr, nullptr, nullptr, nullptr,
       nullptr, nullptr, nullptr,
       JS_GlobalObjectTraceHook
     };
     static const JSClass globalClass = {
@@ -134,21 +130,18 @@ struct DevTools : public ::testing::Test
 
   virtual void TearDown() {
     _initialized = false;
 
     if (global) {
       JS_LeaveCompartment(cx, nullptr);
       global = nullptr;
     }
-    if (cx) {
+    if (cx)
       JS_EndRequest(cx);
-      JS_DestroyContext(cx);
-      cx = nullptr;
-    }
   }
 };
 
 
 // Helper to define a test and ensure that the fixture is initialized properly.
 #define DEF_TEST(name, body)                    \
   TEST_F(DevTools, name) {                      \
     ASSERT_TRUE(_initialized);                  \
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1249,17 +1249,16 @@ nsDocShell::LoadURI(nsIURI* aURI,
   }
 
   if (DoAppRedirectIfNeeded(aURI, aLoadInfo, aFirstParty)) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIURI> referrer;
   nsCOMPtr<nsIURI> originalURI;
-  bool loadReplace = false;
   nsCOMPtr<nsIInputStream> postStream;
   nsCOMPtr<nsIInputStream> headersStream;
   nsCOMPtr<nsISupports> owner;
   bool inheritOwner = false;
   bool ownerIsExplicit = false;
   bool sendReferrer = true;
   uint32_t referrerPolicy = mozilla::net::RP_Default;
   bool isSrcdoc = false;
@@ -1277,17 +1276,16 @@ nsDocShell::LoadURI(nsIURI* aURI,
       mItemType == typeContent && !NS_IsAboutBlank(aURI)) {
     StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI);
   }
 
   // Extract the info from the DocShellLoadInfo struct...
   if (aLoadInfo) {
     aLoadInfo->GetReferrer(getter_AddRefs(referrer));
     aLoadInfo->GetOriginalURI(getter_AddRefs(originalURI));
-    aLoadInfo->GetLoadReplace(&loadReplace);
     nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
     aLoadInfo->GetLoadType(&lt);
     // Get the appropriate loadType from nsIDocShellLoadInfo type
     loadType = ConvertDocShellLoadInfoToLoadType(lt);
 
     aLoadInfo->GetOwner(getter_AddRefs(owner));
     aLoadInfo->GetInheritOwner(&inheritOwner);
     aLoadInfo->GetOwnerIsExplicit(&ownerIsExplicit);
@@ -1537,17 +1535,16 @@ nsDocShell::LoadURI(nsIURI* aURI,
   }
 
   if (isSrcdoc) {
     flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
   }
 
   return InternalLoad(aURI,
                       originalURI,
-                      loadReplace,
                       referrer,
                       referrerPolicy,
                       owner,
                       flags,
                       target.get(),
                       nullptr,      // No type hint
                       NullString(), // No forced download
                       postStream,
@@ -5294,17 +5291,17 @@ nsDocShell::LoadErrorPage(nsIURI* aURI, 
   // end of the URL, so append it last.
   errorPageUrl.AppendLiteral("&d=");
   errorPageUrl.AppendASCII(escapedDescription.get());
 
   nsCOMPtr<nsIURI> errorPageURI;
   rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return InternalLoad(errorPageURI, nullptr, false, nullptr,
+  return InternalLoad(errorPageURI, nullptr, nullptr,
                       mozilla::net::RP_Default,
                       nullptr, INTERNAL_LOAD_FLAGS_INHERIT_OWNER, nullptr,
                       nullptr, NullString(), nullptr, nullptr, LOAD_ERROR_PAGE,
                       nullptr, true, NullString(), this, nullptr, nullptr,
                       nullptr);
 }
 
 NS_IMETHODIMP
@@ -5346,41 +5343,36 @@ nsDocShell::Reload(uint32_t aReloadFlags
 
     // Do not inherit owner from document
     uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
     nsAutoString srcdoc;
     nsIPrincipal* principal = nullptr;
     nsAutoString contentTypeHint;
     nsCOMPtr<nsIURI> baseURI;
     nsCOMPtr<nsIURI> originalURI;
-    bool loadReplace = false;
     if (doc) {
       principal = doc->NodePrincipal();
       doc->GetContentType(contentTypeHint);
 
       if (doc->IsSrcdocDocument()) {
         doc->GetSrcdocData(srcdoc);
         flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
         baseURI = doc->GetBaseURI();
       }
       nsCOMPtr<nsIChannel> chan = doc->GetChannel();
       if (chan) {
-        uint32_t loadFlags;
-        chan->GetLoadFlags(&loadFlags);
-        loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
         nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
         if (httpChan) {
           httpChan->GetOriginalURI(getter_AddRefs(originalURI));
         }
       }
     }
 
     rv = InternalLoad(mCurrentURI,
                       originalURI,
-                      loadReplace,
                       mReferrerURI,
                       mReferrerPolicy,
                       principal,
                       flags,
                       nullptr,         // No window target
                       NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
                       NullString(),    // No forced download
                       nullptr,         // No post data
@@ -9514,29 +9506,28 @@ nsDocShell::CopyFavicon(nsIURI* aOldURI,
   }
 #endif
 }
 
 class InternalLoadEvent : public Runnable
 {
 public:
   InternalLoadEvent(nsDocShell* aDocShell, nsIURI* aURI,
-                    nsIURI* aOriginalURI, bool aLoadReplace,
+                    nsIURI* aOriginalURI,
                     nsIURI* aReferrer, uint32_t aReferrerPolicy,
                     nsISupports* aOwner, uint32_t aFlags,
                     const char* aTypeHint, nsIInputStream* aPostData,
                     nsIInputStream* aHeadersData, uint32_t aLoadType,
                     nsISHEntry* aSHEntry, bool aFirstParty,
                     const nsAString& aSrcdoc, nsIDocShell* aSourceDocShell,
                     nsIURI* aBaseURI)
     : mSrcdoc(aSrcdoc)
     , mDocShell(aDocShell)
     , mURI(aURI)
     , mOriginalURI(aOriginalURI)
-    , mLoadReplace(aLoadReplace)
     , mReferrer(aReferrer)
     , mReferrerPolicy(aReferrerPolicy)
     , mOwner(aOwner)
     , mPostData(aPostData)
     , mHeadersData(aHeadersData)
     , mSHEntry(aSHEntry)
     , mFlags(aFlags)
     , mLoadType(aLoadType)
@@ -9549,17 +9540,16 @@ public:
       mTypeHint = aTypeHint;
     }
   }
 
   NS_IMETHOD
   Run()
   {
     return mDocShell->InternalLoad(mURI, mOriginalURI,
-                                   mLoadReplace,
                                    mReferrer,
                                    mReferrerPolicy,
                                    mOwner, mFlags,
                                    nullptr, mTypeHint.get(),
                                    NullString(), mPostData, mHeadersData,
                                    mLoadType, mSHEntry, mFirstParty,
                                    mSrcdoc, mSourceDocShell, mBaseURI,
                                    nullptr, nullptr);
@@ -9569,17 +9559,16 @@ private:
   // Use IDL strings so .get() returns null by default
   nsXPIDLString mWindowTarget;
   nsXPIDLCString mTypeHint;
   nsString mSrcdoc;
 
   RefPtr<nsDocShell> mDocShell;
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIURI> mOriginalURI;
-  bool mLoadReplace;
   nsCOMPtr<nsIURI> mReferrer;
   uint32_t mReferrerPolicy;
   nsCOMPtr<nsISupports> mOwner;
   nsCOMPtr<nsIInputStream> mPostData;
   nsCOMPtr<nsIInputStream> mHeadersData;
   nsCOMPtr<nsISHEntry> mSHEntry;
   uint32_t mFlags;
   uint32_t mLoadType;
@@ -9636,17 +9625,16 @@ nsDocShell::IsAboutNewtab(nsIURI* aURI)
     return false;
   }
   return module.Equals("newtab");
 }
 
 NS_IMETHODIMP
 nsDocShell::InternalLoad(nsIURI* aURI,
                          nsIURI* aOriginalURI,
-                         bool aLoadReplace,
                          nsIURI* aReferrer,
                          uint32_t aReferrerPolicy,
                          nsISupports* aOwner,
                          uint32_t aFlags,
                          const char16_t* aWindowTarget,
                          const char* aTypeHint,
                          const nsAString& aFileName,
                          nsIInputStream* aPostData,
@@ -9900,17 +9888,16 @@ nsDocShell::InternalLoad(nsIURI* aURI,
 
     //
     // Transfer the load to the target DocShell...  Pass nullptr as the
     // window target name from to prevent recursive retargeting!
     //
     if (NS_SUCCEEDED(rv) && targetDocShell) {
       rv = targetDocShell->InternalLoad(aURI,
                                         aOriginalURI,
-                                        aLoadReplace,
                                         aReferrer,
                                         aReferrerPolicy,
                                         owner,
                                         aFlags,
                                         nullptr,         // No window target
                                         aTypeHint,
                                         NullString(),    // No forced download
                                         aPostData,
@@ -9981,17 +9968,17 @@ nsDocShell::InternalLoad(nsIURI* aURI,
       // the unload event also a replace load, so we don't
       // create extra history entries.
       if (LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
         mLoadType = LOAD_NORMAL_REPLACE;
       }
 
       // Do this asynchronously
       nsCOMPtr<nsIRunnable> ev =
-        new InternalLoadEvent(this, aURI, aOriginalURI, aLoadReplace,
+        new InternalLoadEvent(this, aURI, aOriginalURI,
                               aReferrer, aReferrerPolicy, aOwner, aFlags,
                               aTypeHint, aPostData, aHeadersData,
                               aLoadType, aSHEntry, aFirstParty, aSrcdoc,
                               aSourceDocShell, aBaseURI);
       return NS_DispatchToCurrentThread(ev);
     }
 
     // Just ignore this load attempt
@@ -10498,17 +10485,20 @@ nsDocShell::InternalLoad(nsIURI* aURI,
   }
 
   net::PredictorLearn(aURI, nullptr,
                       nsINetworkPredictor::LEARN_LOAD_TOPLEVEL, this);
   net::PredictorPredict(aURI, nullptr,
                         nsINetworkPredictor::PREDICT_LOAD, this, nullptr);
 
   nsCOMPtr<nsIRequest> req;
-  rv = DoURILoad(aURI, aOriginalURI, aLoadReplace, aReferrer,
+  // At this point we will open a new channel to load data. If aOriginalURI
+  // is present, we load aOriginalURI instead of aURI because we want to load
+  // all redirects again.
+  rv = DoURILoad(aOriginalURI ? aOriginalURI : aURI, aReferrer,
                  !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
                  aReferrerPolicy,
                  owner, aTypeHint, aFileName, aPostData, aHeadersData,
                  aFirstParty, aDocShell, getter_AddRefs(req),
                  (aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0,
                  (aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0,
                  (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES) != 0,
                  srcdoc, aBaseURI, contentType);
@@ -10575,18 +10565,16 @@ nsDocShell::GetInheritedPrincipal(bool a
     return docPrincipal;
   }
 
   return nullptr;
 }
 
 nsresult
 nsDocShell::DoURILoad(nsIURI* aURI,
-                      nsIURI* aOriginalURI,
-                      bool aLoadReplace,
                       nsIURI* aReferrerURI,
                       bool aSendReferrer,
                       uint32_t aReferrerPolicy,
                       nsISupports* aOwner,
                       const char* aTypeHint,
                       const nsAString& aFileName,
                       nsIInputStream* aPostData,
                       nsIInputStream* aHeadersData,
@@ -10849,27 +10837,17 @@ nsDocShell::DoURILoad(nsIURI* aURI,
   }
 
   // Make sure to give the caller a channel if we managed to create one
   // This is important for correct error page/session history interaction
   if (aRequest) {
     NS_ADDREF(*aRequest = channel);
   }
 
-  if (aOriginalURI) {
-    channel->SetOriginalURI(aOriginalURI);
-    if (aLoadReplace) {
-      uint32_t loadFlags;
-      channel->GetLoadFlags(&loadFlags);
-      NS_ENSURE_SUCCESS(rv, rv);
-      channel->SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
-    }
-  } else {
-    channel->SetOriginalURI(aURI);
-  }
+  channel->SetOriginalURI(aURI);
 
   if (aTypeHint && *aTypeHint) {
     channel->SetContentType(nsDependentCString(aTypeHint));
     mContentTypeHint = aTypeHint;
   } else {
     mContentTypeHint.Truncate();
   }
 
@@ -10996,18 +10974,18 @@ nsDocShell::DoURILoad(nsIURI* aURI,
       rv = AddHeadersToChannel(aHeadersData, httpChannel);
     }
     // Set the referrer explicitly
     if (aReferrerURI && aSendReferrer) {
       // Referrer is currenly only set for link clicks here.
       httpChannel->SetReferrerWithPolicy(aReferrerURI, aReferrerPolicy);
     }
     // set Content-Signature enforcing bit if aOriginalURI == about:newtab
-    if (aOriginalURI && httpChannel) {
-      if (IsAboutNewtab(aOriginalURI)) {
+    if (httpChannel) {
+      if (IsAboutNewtab(aURI)) {
         nsCOMPtr<nsILoadInfo> loadInfo = httpChannel->GetLoadInfo();
         if (loadInfo) {
           loadInfo->SetVerifySignedContent(true);
         }
       }
     }
   }
 
@@ -11861,17 +11839,16 @@ nsDocShell::AddState(JS::Handle<JS::Valu
     // AddToSessionHistory may not modify mOSHE.  In case it doesn't,
     // we'll just set mOSHE here.
     mOSHE = newSHEntry;
 
   } else {
     newSHEntry = mOSHE;
     newSHEntry->SetURI(newURI);
     newSHEntry->SetOriginalURI(newURI);
-    newSHEntry->SetLoadReplace(false);
   }
 
   // Step 4: Modify new/original session history entry and clear its POST
   // data, if there is any.
   newSHEntry->SetStateData(scContainer);
   newSHEntry->SetPostData(nullptr);
 
   // If this push/replaceState changed the document's current URI and the new
@@ -12072,17 +12049,16 @@ nsDocShell::AddToSessionHistory(nsIURI* 
     if (!entry) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
   // Get the post data & referrer
   nsCOMPtr<nsIInputStream> inputStream;
   nsCOMPtr<nsIURI> originalURI;
-  bool loadReplace = false;
   nsCOMPtr<nsIURI> referrerURI;
   uint32_t referrerPolicy = mozilla::net::RP_Default;
   nsCOMPtr<nsISupports> cacheKey;
   nsCOMPtr<nsISupports> owner = aOwner;
   bool expired = false;
   bool discardLayoutState = false;
   nsCOMPtr<nsICacheInfoChannel> cacheChannel;
   if (aChannel) {
@@ -12103,17 +12079,16 @@ nsDocShell::AddToSessionHistory(nsIURI* 
     if (httpChannel) {
       nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
       if (uploadChannel) {
         uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
       }
       httpChannel->GetOriginalURI(getter_AddRefs(originalURI));
       uint32_t loadFlags;
       aChannel->GetLoadFlags(&loadFlags);
-      loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
       httpChannel->GetReferrer(getter_AddRefs(referrerURI));
       httpChannel->GetReferrerPolicy(&referrerPolicy);
 
       discardLayoutState = ShouldDiscardLayoutState(httpChannel);
     }
     aChannel->GetOwner(getter_AddRefs(owner));
     if (!owner) {
       nsCOMPtr<nsILoadInfo> loadInfo;
@@ -12147,17 +12122,16 @@ nsDocShell::AddToSessionHistory(nsIURI* 
                 nullptr,           // LayoutHistory state
                 cacheKey,          // CacheKey
                 mContentTypeHint,  // Content-type
                 owner,             // Channel or provided owner
                 mHistoryID,
                 mDynamicallyCreated);
 
   entry->SetOriginalURI(originalURI);
-  entry->SetLoadReplace(loadReplace);
   entry->SetReferrerURI(referrerURI);
   entry->SetReferrerPolicy(referrerPolicy);
   nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
   if (inStrmChan) {
     bool isSrcdocChannel;
     inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
     if (isSrcdocChannel) {
       nsAutoString srcdoc;
@@ -12247,30 +12221,27 @@ nsresult
 nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType)
 {
   if (!IsNavigationAllowed()) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIURI> uri;
   nsCOMPtr<nsIURI> originalURI;
-  bool loadReplace = false;
   nsCOMPtr<nsIInputStream> postData;
   nsCOMPtr<nsIURI> referrerURI;
   uint32_t referrerPolicy;
   nsAutoCString contentType;
   nsCOMPtr<nsISupports> owner;
 
   NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
 
   NS_ENSURE_SUCCESS(aEntry->GetURI(getter_AddRefs(uri)), NS_ERROR_FAILURE);
   NS_ENSURE_SUCCESS(aEntry->GetOriginalURI(getter_AddRefs(originalURI)),
                     NS_ERROR_FAILURE);
-  NS_ENSURE_SUCCESS(aEntry->GetLoadReplace(&loadReplace),
-                    NS_ERROR_FAILURE);
   NS_ENSURE_SUCCESS(aEntry->GetReferrerURI(getter_AddRefs(referrerURI)),
                     NS_ERROR_FAILURE);
   NS_ENSURE_SUCCESS(aEntry->GetReferrerPolicy(&referrerPolicy),
                     NS_ERROR_FAILURE);
   NS_ENSURE_SUCCESS(aEntry->GetPostData(getter_AddRefs(postData)),
                     NS_ERROR_FAILURE);
   NS_ENSURE_SUCCESS(aEntry->GetContentType(contentType), NS_ERROR_FAILURE);
   NS_ENSURE_SUCCESS(aEntry->GetOwner(getter_AddRefs(owner)), NS_ERROR_FAILURE);
@@ -12341,17 +12312,16 @@ nsDocShell::LoadHistoryEntry(nsISHEntry*
   }
 
   // Passing nullptr as aSourceDocShell gives the same behaviour as before
   // aSourceDocShell was introduced. According to spec we should be passing
   // the source browsing context that was used when the history entry was
   // first created. bug 947716 has been created to address this issue.
   rv = InternalLoad(uri,
                     originalURI,
-                    loadReplace,
                     referrerURI,
                     referrerPolicy,
                     owner,
                     flags,
                     nullptr,            // No window target
                     contentType.get(),  // Type hint
                     NullString(),       // No forced file download
                     postData,           // Post data stream
@@ -13843,17 +13813,16 @@ nsDocShell::OnLinkClickSync(nsIContent* 
   nsCOMPtr<nsIURI> clonedURI;
   aURI->Clone(getter_AddRefs(clonedURI));
   if (!clonedURI) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   nsresult rv = InternalLoad(clonedURI,                 // New URI
                              nullptr,                   // Original URI
-                             false,                     // LoadReplace
                              referer,                   // Referer URI
                              refererPolicy,             // Referer policy
                              aContent->NodePrincipal(), // Owner is our node's
                                                         // principal
                              flags,
                              target.get(),              // Window target
                              NS_LossyConvertUTF16toASCII(typeHint).get(),
                              aFileName,                 // Download as file
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -348,22 +348,17 @@ protected:
   // at the parent.
   nsIPrincipal* GetInheritedPrincipal(bool aConsiderCurrentDocument);
 
   // Actually open a channel and perform a URI load. Note: whatever owner is
   // passed to this function will be set on the channel. Callers who wish to
   // not have an owner on the channel should just pass null.
   // If aSrcdoc is not void, the load will be considered as a srcdoc load,
   // and the contents of aSrcdoc will be loaded instead of aURI.
-  // aOriginalURI will be set as the originalURI on the channel that does the
-  // load. If aOriginalURI is null, aURI will be set as the originalURI.
-  // If aLoadReplace is true, OLOAD_REPLACE flag will be set to the nsIChannel.
   nsresult DoURILoad(nsIURI* aURI,
-                     nsIURI* aOriginalURI,
-                     bool aLoadReplace,
                      nsIURI* aReferrer,
                      bool aSendReferrer,
                      uint32_t aReferrerPolicy,
                      nsISupports* aOwner,
                      const char* aTypeHint,
                      const nsAString& aFileName,
                      nsIInputStream* aPostData,
                      nsIInputStream* aHeadersData,
--- a/docshell/base/nsDocShellLoadInfo.cpp
+++ b/docshell/base/nsDocShellLoadInfo.cpp
@@ -7,18 +7,17 @@
 #include "nsDocShellLoadInfo.h"
 #include "nsISHEntry.h"
 #include "nsIInputStream.h"
 #include "nsIURI.h"
 #include "nsIDocShell.h"
 #include "mozilla/net/ReferrerPolicy.h"
 
 nsDocShellLoadInfo::nsDocShellLoadInfo()
-  : mLoadReplace(false)
-  , mInheritOwner(false)
+  : mInheritOwner(false)
   , mOwnerIsExplicit(false)
   , mSendReferrer(true)
   , mReferrerPolicy(mozilla::net::RP_Default)
   , mLoadType(nsIDocShellLoadInfo::loadNormal)
   , mIsSrcdocLoad(false)
 {
 }
 
@@ -64,30 +63,16 @@ nsDocShellLoadInfo::GetOriginalURI(nsIUR
 NS_IMETHODIMP
 nsDocShellLoadInfo::SetOriginalURI(nsIURI* aOriginalURI)
 {
   mOriginalURI = aOriginalURI;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocShellLoadInfo::GetLoadReplace(bool* aLoadReplace)
-{
-  *aLoadReplace = mLoadReplace;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDocShellLoadInfo::SetLoadReplace(bool aLoadReplace)
-{
-  mLoadReplace = aLoadReplace;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsDocShellLoadInfo::GetOwner(nsISupports** aOwner)
 {
   NS_ENSURE_ARG_POINTER(aOwner);
 
   *aOwner = mOwner;
   NS_IF_ADDREF(*aOwner);
   return NS_OK;
 }
--- a/docshell/base/nsDocShellLoadInfo.h
+++ b/docshell/base/nsDocShellLoadInfo.h
@@ -29,17 +29,16 @@ public:
 
 protected:
   virtual ~nsDocShellLoadInfo();
 
 protected:
   nsCOMPtr<nsIURI> mReferrer;
   nsCOMPtr<nsIURI> mOriginalURI;
   nsCOMPtr<nsISupports> mOwner;
-  bool mLoadReplace;
   bool mInheritOwner;
   bool mOwnerIsExplicit;
   bool mSendReferrer;
   nsDocShellInfoReferrerPolicy mReferrerPolicy;
   nsDocShellInfoLoadType mLoadType;
   nsCOMPtr<nsISHEntry> mSHEntry;
   nsString mTarget;
   nsCOMPtr<nsIInputStream> mPostDataStream;
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -120,22 +120,21 @@ interface nsIDocShell : nsIDocShellTreeI
   // NB: 0x80 is available.
 
   /**
    * Loads the given URI.  This method is identical to loadURI(...) except
    * that its parameter list is broken out instead of being packaged inside
    * of an nsIDocShellLoadInfo object...
    *
    * @param aURI            - The URI to load.
-   * @param aOriginalURI    - The URI to set as the originalURI on the channel
-   *                          that does the load. If null, aURI will be set as
-   *                          the originalURI.
-   * @param aLoadReplace    - If set LOAD_REPLACE flag will be set on the
-   *                          channel. aOriginalURI is null, this argument is
-   *                          ignored.
+   * @param aOriginalURI    - If aOriginalURI is present and this function ends
+   *                          up actually loading data from network or cache,
+   *                          but not from bfcache, (e.g. in case of a reload)
+   *                          aOriginalURI will be loaded instead of aURI.
+   *                          Thereby we will load all redirects again.
    * @param aReferrer       - Referring URI
    * @param aReferrerPolicy - Referrer policy
    * @param aOwner          - Owner (security principal)
    * @param aInheritOwner   - Flag indicating whether the owner of the current
    *                          document should be inherited if aOwner is null.
    * @param aStopActiveDoc  - Flag indicating whether loading the current
    *                          document should be stopped.
    * @param aWindowTarget   - Window target for the load.
@@ -153,17 +152,16 @@ interface nsIDocShell : nsIDocShellTreeI
    *                          of aURI.
    * @param aSourceDocShell - The source browsing context for the navigation.
    * @param aBaseURI        - The base URI to be used for the load.  Set in
    *                          srcdoc loads as it cannot otherwise be inferred
    *                          in certain situations such as view-source.
    */
   [noscript]void internalLoad(in nsIURI aURI,
                               in nsIURI aOriginalURI,
-                              in boolean aLoadReplace,
                               in nsIURI aReferrer,
                               in unsigned long aReferrerPolicy,
                               in nsISupports aOwner,
                               in uint32_t aFlags,
                               in wstring aWindowTarget,
                               in string aTypeHint,
                               in AString aFileName,
                               in nsIInputStream aPostDataStream,
--- a/docshell/base/nsIDocShellLoadInfo.idl
+++ b/docshell/base/nsIDocShellLoadInfo.idl
@@ -25,21 +25,16 @@ interface nsIDocShellLoadInfo : nsISuppo
     /** This is the referrer for the load. */
     attribute nsIURI referrer;
 
     /**
      * The originalURI to be passed to nsIDocShell.internalLoad. May be null.
      */
     attribute nsIURI originalURI;
 
-    /**
-     * loadReplace flag to be passed to nsIDocShell.internalLoad.
-     */
-    attribute boolean loadReplace;
-
     /** The owner of the load, that is, the entity responsible for 
      *  causing the load to occur. This should be a nsIPrincipal typically.
      */
     attribute nsISupports owner;
 
     /** If this attribute is true and no owner is specified, copy
      *  the owner from the referring document.
      */
--- a/docshell/shistory/nsISHEntry.idl
+++ b/docshell/shistory/nsISHEntry.idl
@@ -43,21 +43,16 @@ interface nsISHEntry : nsISupports
     /**
      * A readonly property that returns the original URI of the current entry.
      * If an entry is the result of a redirect this attribute holds original
      * URI. The object returned is of type nsIURI
      */
     attribute nsIURI originalURI;
 
     /**
-     *  This flag remembers whether channel has LOAD_REPLACE set.
-     */
-    attribute boolean loadReplace;
-
-    /**
      * A readonly property that returns the title
      * of the current entry.  The object returned
      * is a encoded string
      */
     readonly attribute wstring title;
 
     /**
      * A readonly property that returns a boolean
--- a/docshell/shistory/nsSHEntry.cpp
+++ b/docshell/shistory/nsSHEntry.cpp
@@ -19,34 +19,32 @@
 #include <algorithm>
 
 namespace dom = mozilla::dom;
 
 static uint32_t gEntryID = 0;
 
 nsSHEntry::nsSHEntry()
   : mShared(new nsSHEntryShared())
-  , mLoadReplace(false)
   , mReferrerPolicy(mozilla::net::RP_Default)
   , mLoadType(0)
   , mID(gEntryID++)
   , mScrollPositionX(0)
   , mScrollPositionY(0)
   , mParent(nullptr)
   , mURIWasModified(false)
   , mIsSrcdocEntry(false)
   , mScrollRestorationIsManual(false)
 {
 }
 
 nsSHEntry::nsSHEntry(const nsSHEntry& aOther)
   : mShared(aOther.mShared)
   , mURI(aOther.mURI)
   , mOriginalURI(aOther.mOriginalURI)
-  , mLoadReplace(aOther.mLoadReplace)
   , mReferrerURI(aOther.mReferrerURI)
   , mReferrerPolicy(aOther.mReferrerPolicy)
   , mTitle(aOther.mTitle)
   , mPostData(aOther.mPostData)
   , mLoadType(0)         // XXX why not copy?
   , mID(aOther.mID)
   , mScrollPositionX(0)  // XXX why not copy?
   , mScrollPositionY(0)  // XXX why not copy?
@@ -133,30 +131,16 @@ nsSHEntry::GetOriginalURI(nsIURI** aOrig
 NS_IMETHODIMP
 nsSHEntry::SetOriginalURI(nsIURI* aOriginalURI)
 {
   mOriginalURI = aOriginalURI;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSHEntry::GetLoadReplace(bool* aLoadReplace)
-{
-  *aLoadReplace = mLoadReplace;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsSHEntry::SetLoadReplace(bool aLoadReplace)
-{
-  mLoadReplace = aLoadReplace;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsSHEntry::GetReferrerURI(nsIURI** aReferrerURI)
 {
   *aReferrerURI = mReferrerURI;
   NS_IF_ADDREF(*aReferrerURI);
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/docshell/shistory/nsSHEntry.h
+++ b/docshell/shistory/nsSHEntry.h
@@ -45,17 +45,16 @@ private:
 
   // We share the state in here with other SHEntries which correspond to the
   // same document.
   RefPtr<nsSHEntryShared> mShared;
 
   // See nsSHEntry.idl for comments on these members.
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIURI> mOriginalURI;
-  bool mLoadReplace;
   nsCOMPtr<nsIURI> mReferrerURI;
   uint32_t mReferrerPolicy;
   nsString mTitle;
   nsCOMPtr<nsIInputStream> mPostData;
   uint32_t mLoadType;
   uint32_t mID;
   int32_t mScrollPositionX;
   int32_t mScrollPositionY;
--- a/docshell/shistory/nsSHistory.cpp
+++ b/docshell/shistory/nsSHistory.cpp
@@ -1762,20 +1762,16 @@ nsSHistory::InitiateLoad(nsISHEntry* aFr
 
   loadInfo->SetLoadType(aLoadType);
   loadInfo->SetSHEntry(aFrameEntry);
 
   nsCOMPtr<nsIURI> originalURI;
   aFrameEntry->GetOriginalURI(getter_AddRefs(originalURI));
   loadInfo->SetOriginalURI(originalURI);
 
-  bool loadReplace;
-  aFrameEntry->GetLoadReplace(&loadReplace);
-  loadInfo->SetLoadReplace(loadReplace);
-
   nsCOMPtr<nsIURI> nextURI;
   aFrameEntry->GetURI(getter_AddRefs(nextURI));
   // Time   to initiate a document load
   return aFrameDS->LoadURI(nextURI, loadInfo,
                            nsIWebNavigation::LOAD_FLAGS_NONE, false);
 
 }
 
new file mode 100644
--- /dev/null
+++ b/docshell/test/bug968273_new.html
@@ -0,0 +1,1 @@
+<html><body>This is bug968273_new.html.</body></html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/bug968273_redirect.html
@@ -0,0 +1,1 @@
+<html><body>This document is redirected to bug968273_new.html.</body></html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/bug968273_redirect.html^headers^
@@ -0,0 +1,2 @@
+HTTP 302 Moved Temporarily
+Location: bug968273_new.html
new file mode 100644
--- /dev/null
+++ b/docshell/test/file_bug968273.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=968273
+
+If a load has redirects, reloading the page will load the page starting with the original
+URI and do the redirects again.
+-->
+<head>
+  <title>Test for Bug 968273</title>
+<script>
+  var SimpleTest = opener.SimpleTest;
+  var ok = opener.ok;
+  var is = opener.is;
+  var doOneReload = true;
+
+  function onLoadCheckFrame() {
+    ok(frames[0].performance, 'Window.performance should be defined');
+    ok(frames[0].performance.navigation, 'Window.performance.navigation should be defined');
+    // do this with a timeout to see the visuals of the navigations.
+    setTimeout("checkFrame();", 200);
+  }
+
+  function checkFrame() {
+    is(frames[0].performance.navigation.redirectCount, 1, "Expected rediect");
+    if (doOneReload == true) {
+      doOneReload = false;
+      // do a reload and check that a redirect is preformed again.
+      frames[0].location.reload();
+    } else {
+      SimpleTest.finish();
+    }
+  }
+
+</script>
+</head>
+<body>
+<div id="frames">
+<iframe name="child0" onload="onLoadCheckFrame();" src="bug968273_redirect.html"></iframe>
+</div>
+</body>
+</html>
--- a/docshell/test/mochitest.ini
+++ b/docshell/test/mochitest.ini
@@ -6,16 +6,19 @@ support-files =
   bug404548-subframe.html
   bug413310-post.sjs
   bug413310-subframe.html
   bug529119-window.html
   bug570341_recordevents.html
   bug668513_redirect.html
   bug668513_redirect.html^headers^
   bug691547_frame.html
+  bug968273_new.html
+  bug968273_redirect.html
+  bug968273_redirect.html^headers^
   file_anchor_scroll_after_document_open.html
   file_bug385434_1.html
   file_bug385434_2.html
   file_bug385434_3.html
   file_bug475636.sjs
   file_bug509055.html
   file_bug540462.html
   file_bug580069_1.html
@@ -27,16 +30,17 @@ support-files =
   file_bug653741.html
   file_bug660404
   file_bug660404^headers^
   file_bug662170.html
   file_bug669671.sjs
   file_bug680257.html
   file_bug703855.html
   file_bug728939.html
+  file_bug968273.html
   file_pushState_after_document_open.html
   historyframes.html
 
 [test_anchor_scroll_after_document_open.html]
 [test_bfcache_plus_hash.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug123696.html]
 [test_bug369814.html]
@@ -103,8 +107,10 @@ skip-if = (buildapp == 'b2g' && toolkit 
 [test_framedhistoryframes.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' #Bug 931116, b2g desktop specific, initial triage, and also bug 784321
 support-files = file_framedhistoryframes.html
 [test_pushState_after_document_open.html]
 [test_windowedhistoryframes.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug1121701.html]
 skip-if = (buildapp == 'b2g' || buildapp == 'mulet')
+[test_bug968273.html]
+skip-if = toolkit == 'android' # The same reason test_bug668513.html is disabled. performance.redirectcount check false on load but it is correct on reload. 
new file mode 100644
--- /dev/null
+++ b/docshell/test/test_bug968273.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=968273
+
+If a load has redirects, reloading the page will load the page starting with the original
+URI and do the redirects again.
+-->
+<head>
+  <title>Test for Bug 968273</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=968273">Mozilla Bug 968273</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+
+<script type='application/javascript'>
+if (navigator.platform.startsWith("Linux")) {
+    SimpleTest.expectAssertions(0, 1);
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("We need do this with a timeout to see the visuals of" +
+                               " the performance.navigation");
+window.open("file_bug968273.html");
+</script>
+</pre>
+</body>
+</html>
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1619,16 +1619,23 @@ Navigator::GetDeprecatedBattery(ErrorRes
     mBatteryManager->Init();
   }
 
   nsIDocument* doc = mWindow->GetDoc();
   if (doc) {
     doc->WarnOnceAbout(nsIDocument::eNavigatorBattery);
   }
 
+  // Is this the first time this page has accessed navigator.battery?
+  if (!mBatteryTelemetryReported) {
+    // sample value 0 = navigator.battery
+    Telemetry::Accumulate(Telemetry::BATTERY_STATUS_COUNT, 0);
+    mBatteryTelemetryReported = true;
+  }
+
   return mBatteryManager;
 }
 
 already_AddRefed<Promise>
 Navigator::PublishServer(const nsAString& aName,
                          const FlyWebPublishOptions& aOptions,
                          ErrorResult& aRv)
 {
--- a/dom/base/nsDOMMutationObserver.cpp
+++ b/dom/base/nsDOMMutationObserver.cpp
@@ -18,16 +18,17 @@
 #include "nsError.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsServiceManagerUtils.h"
 #include "nsTextFragment.h"
 #include "nsThreadUtils.h"
 
 using mozilla::Maybe;
+using mozilla::Move;
 using mozilla::NonOwningAnimationTarget;
 using mozilla::dom::TreeOrderComparator;
 using mozilla::dom::Animation;
 using mozilla::dom::Element;
 
 AutoTArray<RefPtr<nsDOMMutationObserver>, 4>*
   nsDOMMutationObserver::sScheduledMutationObservers = nullptr;
 
@@ -680,30 +681,29 @@ nsDOMMutationObserver::Observe(nsINode& 
   if (aOptions.mAttributeFilter.WasPassed()) {
     allAttrs = false;
     const mozilla::dom::Sequence<nsString>& filtersAsString =
       aOptions.mAttributeFilter.Value();
     uint32_t len = filtersAsString.Length();
     filters.SetCapacity(len);
 
     for (uint32_t i = 0; i < len; ++i) {
-      nsCOMPtr<nsIAtom> a = NS_Atomize(filtersAsString[i]);
-      filters.AppendObject(a);
+      filters.AppendElement(NS_Atomize(filtersAsString[i]));
     }
   }
 
   nsMutationReceiver* r = GetReceiverFor(&aTarget, true, animations);
   r->SetChildList(childList);
   r->SetAttributes(attributes);
   r->SetCharacterData(characterData);
   r->SetSubtree(subtree);
   r->SetAttributeOldValue(attributeOldValue);
   r->SetCharacterDataOldValue(characterDataOldValue);
   r->SetNativeAnonymousChildList(nativeAnonymousChildList);
-  r->SetAttributeFilter(filters);
+  r->SetAttributeFilter(Move(filters));
   r->SetAllAttributes(allAttrs);
   r->SetAnimations(animations);
   r->RemoveClones();
 
 #ifdef DEBUG
   for (int32_t i = 0; i < mReceivers.Count(); ++i) {
     NS_WARN_IF_FALSE(mReceivers[i]->Target(),
                      "All the receivers should have a target!");
--- a/dom/base/nsDOMMutationObserver.h
+++ b/dom/base/nsDOMMutationObserver.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsDOMMutationObserver_h
 #define nsDOMMutationObserver_h
 
 #include "mozilla/Attributes.h"
+#include "mozilla/Move.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsPIDOMWindow.h"
 #include "nsIScriptContext.h"
 #include "nsStubAnimationObserver.h"
 #include "nsCOMArray.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "nsIVariant.h"
@@ -213,21 +214,21 @@ public:
   }
   void SetAttributeOldValue(bool aOldValue)
   {
     NS_ASSERTION(!mParent, "Shouldn't have parent");
     mAttributeOldValue = aOldValue;
   }
 
   nsCOMArray<nsIAtom>& AttributeFilter() { return mAttributeFilter; }
-  void SetAttributeFilter(nsCOMArray<nsIAtom>& aFilter)
+  void SetAttributeFilter(nsCOMArray<nsIAtom>&& aFilter)
   {
     NS_ASSERTION(!mParent, "Shouldn't have parent");
     mAttributeFilter.Clear();
-    mAttributeFilter.AppendObjects(aFilter);
+    mAttributeFilter = mozilla::Move(aFilter);
   }
 
   void AddClone(nsMutationReceiverBase* aClone)
   {
     mTransientReceivers.AppendObject(aClone);
   }
 
   void RemoveClone(nsMutationReceiverBase* aClone)
--- a/dom/broadcastchannel/BroadcastChannel.h
+++ b/dom/broadcastchannel/BroadcastChannel.h
@@ -100,21 +100,16 @@ private:
 
   void UpdateMustKeepAlive();
 
   bool IsCertainlyAliveForCC() const override
   {
     return mIsKeptAlive;
   }
 
-  bool IsClosed() const
-  {
-    return mState != StateActive;
-  }
-
   void RemoveDocFromBFCache();
 
   RefPtr<BroadcastChannelChild> mActor;
   nsTArray<RefPtr<BroadcastChannelMessage>> mPendingMessages;
 
   nsAutoPtr<workers::WorkerFeature> mWorkerFeature;
 
   nsAutoPtr<PrincipalInfo> mPrincipalInfo;
--- a/dom/broadcastchannel/BroadcastChannelChild.cpp
+++ b/dom/broadcastchannel/BroadcastChannelChild.cpp
@@ -53,19 +53,18 @@ BroadcastChannelChild::RecvNotify(const 
 
       blobs.AppendElement(impl);
     }
   }
 
   nsCOMPtr<DOMEventTargetHelper> helper = mBC;
   nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(helper);
 
-  // This object has been already closed by content or is going to be deleted
-  // soon. No notify is required.
-  if (!eventTarget || mBC->IsClosed()) {
+  // The object is going to be deleted soon. No notify is required.
+  if (!eventTarget) {
     return true;
   }
 
   // CheckInnerWindowCorrectness can be used also without a window when
   // BroadcastChannel is running in a worker. In this case, it's a NOP.
   if (NS_FAILED(mBC->CheckInnerWindowCorrectness())) {
     return true;
   }
--- a/dom/broadcastchannel/tests/mochitest.ini
+++ b/dom/broadcastchannel/tests/mochitest.ini
@@ -10,16 +10,17 @@ support-files =
   iframe_mozbrowser.html
   iframe_mozbrowser2.html
   server.sjs
   manifest.webapp
 
 [test_broadcastchannel_any.html]
 [test_broadcastchannel_basic.html]
 [test_broadcastchannel_close.html]
+[test_broadcastchannel_close2.html]
 [test_broadcastchannel_self.html]
 [test_broadcastchannel_sharedWorker.html]
 [test_broadcastchannel_worker.html]
 [test_broadcastchannel_worker_alive.html]
 [test_broadcastchannel_mozbrowser.html]
 skip-if = buildapp != 'mulet'
 [test_broadcastchannel_mozbrowser2.html]
 skip-if = buildapp != 'mulet'
new file mode 100644
--- /dev/null
+++ b/dom/broadcastchannel/tests/test_broadcastchannel_close2.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for BroadcastChannel</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<div id="content"></div>
+
+<script type="application/javascript">
+
+function runTest() {
+  var c1 = new BroadcastChannel('foo');
+  var c2 = new BroadcastChannel('foo');
+
+  var status = [];
+  c2.onmessage = function(e) {
+    status.push(e.data);
+    if (status.length == 2) {
+      is (status[0], 'first', "First message has been received");
+      is (status[1], 'second', "Second message has been received");
+      SimpleTest.finish();
+    }
+
+    c2.close();
+  }
+
+  c1.postMessage('first');
+  c1.postMessage('second');
+}
+
+SimpleTest.waitForExplicitFinish();
+runTest();
+
+</script>
+</body>
+</html>
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -582,17 +582,17 @@ BaseCaps(const WebGLContextOptions& opti
 
 static already_AddRefed<gl::GLContext>
 CreateGLWithEGL(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
                 WebGLContext* webgl, nsACString* const out_failReason,
                 nsACString* const out_failureId)
 {
     const gfx::IntSize dummySize(16, 16);
     RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps,
-                                                                     flags);
+                                                                     flags, *out_failureId);
     if (gl && gl->IsANGLE()) {
         gl = nullptr;
     }
 
     if (!gl) {
         if (out_failReason->Length()) {
             out_failReason->AppendLiteral("\n");
         }
@@ -606,17 +606,17 @@ CreateGLWithEGL(const gl::SurfaceCaps& c
 
 static already_AddRefed<GLContext>
 CreateGLWithANGLE(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
                   WebGLContext* webgl, nsACString* const out_failReason,
                   nsACString* const out_failureId)
 {
     const gfx::IntSize dummySize(16, 16);
     RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps,
-                                                                     flags);
+                                                                     flags, *out_failureId);
     if (gl && !gl->IsANGLE()) {
         gl = nullptr;
     }
 
     if (!gl) {
         if (out_failReason->Length()) {
             out_failReason->AppendLiteral("\n");
         }
@@ -642,17 +642,18 @@ CreateGLWithDefault(const gl::SurfaceCap
             out_failReason->AppendASCII("\n");
         }
         out_failReason->AppendASCII("Refused to create native OpenGL context because of"
                                     " blacklisting.");
         return nullptr;
     }
 
     const gfx::IntSize dummySize(16, 16);
-    RefPtr<GLContext> gl = gl::GLContextProvider::CreateOffscreen(dummySize, caps, flags);
+    RefPtr<GLContext> gl = gl::GLContextProvider::CreateOffscreen(dummySize, caps,
+                                                                  flags, *out_failureId);
 
     if (gl && gl->IsANGLE()) {
         gl = nullptr;
     }
 
     if (!gl) {
         if (out_failReason->Length()) {
             out_failReason->AppendASCII("\n");
@@ -932,16 +933,18 @@ WebGLContext::SetDimensions(int32_t sign
         ThrowEvent_WebGLContextCreationError(text);
         return NS_ERROR_FAILURE;
     }
     MOZ_ASSERT(gl);
     MOZ_ASSERT_IF(mOptions.alpha, gl->Caps().alpha);
 
     if (mOptions.failIfMajorPerformanceCaveat) {
         if (gl->IsWARP()) {
+            Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID,
+                                  NS_LITERAL_CSTRING("FEATURE_FAILURE_PERF_WARP"));
             const nsLiteralCString text("failIfMajorPerformanceCaveat: Driver is not"
                                         " hardware-accelerated.");
             ThrowEvent_WebGLContextCreationError(text);
             return NS_ERROR_FAILURE;
         }
     }
 
     if (!ResizeBackbuffer(width, height)) {
--- a/dom/filesystem/tests/test_webkitdirectory.html
+++ b/dom/filesystem/tests/test_webkitdirectory.html
@@ -5,31 +5,33 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 
 <body>
 <input id="inputFileWebkitDirectory" type="file" webkitdirectory></input>
 <input id="inputFileWebkitDirectoryAndDirectory" type="file" webkitdirectory directory></input>
 <input id="inputFileDirectory" type="file" directory></input>
+<input id="inputFileDirectoryChange" type="file" webkitdirectory></input>
 
 <script type="application/javascript;version=1.7">
 
 function populateInputFile(aInputFile) {
   var url = SimpleTest.getTestFileURL("script_fileList.js");
   var script = SpecialPowers.loadChromeScript(url);
 
   var MockFilePicker = SpecialPowers.MockFilePicker;
   MockFilePicker.init(window, "A Mock File Picker", SpecialPowers.Ci.nsIFilePicker.modeOpen);
 
   function onOpened(message) {
     MockFilePicker.useDirectory(message.dir);
 
     var input = document.getElementById(aInputFile);
-    input.addEventListener('change', function() {
+    input.addEventListener('change', function change() {
+      input.removeEventListener('change', change);
       MockFilePicker.cleanup();
       script.destroy();
       next();
     });
 
     input.click();
   }
 
@@ -79,35 +81,101 @@ function test_webkitdirectory_attribute(
   a.webkitdirectory = true;
 
   ok(a.hasAttribute("webkitdirectory"), "Webkitdirectory DOM attribute is set");
   ok(a.webkitdirectory, "Webkitdirectory attribute is set");
 
   next();
 }
 
+function test_changeDataWhileWorking() {
+  var url = SimpleTest.getTestFileURL("script_fileList.js");
+  var script = SpecialPowers.loadChromeScript(url);
+
+  var MockFilePicker = SpecialPowers.MockFilePicker;
+  MockFilePicker.init(window, "A Mock File Picker", SpecialPowers.Ci.nsIFilePicker.modeOpen);
+
+  // Let's start retrieving the root nsIFile object
+  new Promise(function(resolve) {
+    function onOpened(message) {
+      script.removeMessageListener("dir.opened", onOpened);
+      resolve(message.dir);
+    }
+
+    script.addMessageListener("dir.opened", onOpened);
+    script.sendAsyncMessage("dir.open", { path: 'root' });
+  })
+
+  // input.click() pointing to the root dir
+  .then(function(aDir) {
+    MockFilePicker.cleanup();
+    MockFilePicker.init(window, "A Mock File Picker", SpecialPowers.Ci.nsIFilePicker.modeOpen);
+    MockFilePicker.useDirectory(aDir);
+    var input = document.getElementById("inputFileDirectoryChange");
+    input.click();
+  })
+
+  // Before onchange, let's take the 'test' directory
+  .then(function() {
+    return new Promise(function(resolve) {
+      function onOpened(message) {
+        script.removeMessageListener("dir.opened", onOpened);
+        script.destroy();
+        resolve(message.dir);
+      }
+
+      script.addMessageListener("dir.opened", onOpened);
+      script.sendAsyncMessage("dir.open", { path: 'test' });
+    });
+  })
+
+  // Now let's click again and wait for onchange.
+  .then(function(aDir) {
+    return new Promise(function(resolve) {
+      MockFilePicker.cleanup();
+      MockFilePicker.init(window, "A Mock File Picker", SpecialPowers.Ci.nsIFilePicker.modeOpen);
+      MockFilePicker.useDirectory(aDir);
+
+      var input = document.getElementById("inputFileDirectoryChange");
+      input.addEventListener('change', function() {
+        MockFilePicker.cleanup();
+        resolve();
+      });
+
+      input.click();
+    })
+  })
+
+  .then(function() {
+    test_fileList('inputFileWebkitDirectory', testDirData);
+  });
+}
+
 function test_setup() {
   SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true],
                                      ["dom.webkitBlink.dirPicker.enabled", true]]}, next);
 }
 
+var testDirData = [ { name: 'foo.txt', path: '/foo.txt' },
+                    { name: 'bar.txt', path: '/subdir/bar.txt' }];
+
 var tests = [
   test_setup,
 
   function() { populateInputFile('inputFileWebkitDirectory'); },
   function() { populateInputFile('inputFileWebkitDirectoryAndDirectory'); },
   function() { populateInputFile('inputFileDirectory'); },
 
-  function() { test_fileList('inputFileWebkitDirectory', [ { name: 'foo.txt', path: '/foo.txt' },
-                                                           { name: 'bar.txt', path: '/subdir/bar.txt' }]); },
-  function() { test_fileList('inputFileWebkitDirectoryAndDirectory', [ { name: 'foo.txt', path: '/foo.txt' },
-                                                                       { name: 'bar.txt', path: '/subdir/bar.txt' }]); },
+  function() { test_fileList('inputFileWebkitDirectory', testDirData) },
+  function() { test_fileList('inputFileWebkitDirectoryAndDirectory', testDirData) },
   function() { test_fileList('inputFileDirectory', null); },
 
   test_webkitdirectory_attribute,
+
+  test_changeDataWhileWorking,
 ];
 
 function next() {
   if (!tests.length) {
     SimpleTest.finish();
     return;
   }
 
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -323,55 +323,81 @@ public:
   }
 
   // CC methods
   void Unlink()
   {
     mGlobal = nullptr;
     mFiles.Clear();
     mPromises.Clear();
+    mCallbacks.Clear();
+
+    MutexAutoLock lock(mMutex);
+    mCanceled = true;
   }
 
   void Traverse(nsCycleCollectionTraversalCallback &cb)
   {
     GetFilesHelper* tmp = this;
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal);
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFiles);
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromises);
   }
 
 private:
   GetFilesHelper(nsIGlobalObject* aGlobal, bool aRecursiveFlag)
     : mGlobal(aGlobal)
     , mRecursiveFlag(aRecursiveFlag)
     , mListingCompleted(false)
     , mErrorResult(NS_OK)
+    , mMutex("GetFilesHelper::mMutex")
+    , mCanceled(false)
   {
     MOZ_ASSERT(aGlobal);
   }
 
   void
   SetDirectoryPath(const nsAString& aDirectoryPath)
   {
     mDirectoryPath = aDirectoryPath;
   }
 
+  bool
+  IsCanceled()
+  {
+    MutexAutoLock lock(mMutex);
+    return mCanceled;
+  }
+
   NS_IMETHOD
   Run() override
   {
     MOZ_ASSERT(!mDirectoryPath.IsEmpty());
     MOZ_ASSERT(!mListingCompleted);
 
     // First step is to retrieve the list of file paths.
     // This happens in the I/O thread.
     if (!NS_IsMainThread()) {
       RunIO();
+
+      // If this operation has been canceled, we don't have to go back to
+      // main-thread.
+      if (IsCanceled()) {
+        return NS_OK;
+      }
+
       return NS_DispatchToMainThread(this);
     }
 
+    // We are here, but we should not do anything on this thread because, in the
+    // meantime, the operation has been canceled.
+    if (IsCanceled()) {
+      return NS_OK;
+    }
+
     RunMainThread();
 
     // We mark the operation as completed here.
     mListingCompleted = true;
 
     // Let's process the pending promises.
     nsTArray<RefPtr<Promise>> promises;
     promises.SwapElements(mPromises);
@@ -449,16 +475,21 @@ private:
   }
 
   nsresult
   ExploreDirectory(const nsAString& aDOMPath, nsIFile* aFile)
   {
     MOZ_ASSERT(!NS_IsMainThread());
     MOZ_ASSERT(aFile);
 
+    // We check if this operation has to be terminated at each recursion.
+    if (IsCanceled()) {
+      return NS_OK;
+    }
+
     nsCOMPtr<nsISimpleEnumerator> entries;
     nsresult rv = aFile->GetDirectoryEntries(getter_AddRefs(entries));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     for (;;) {
       bool hasMore = false;
@@ -572,16 +603,21 @@ private:
   // This is the real File sequence that we expose via Promises.
   Sequence<RefPtr<File>> mFiles;
 
   // Error code to propagate.
   nsresult mErrorResult;
 
   nsTArray<RefPtr<Promise>> mPromises;
   nsTArray<RefPtr<GetFilesCallback>> mCallbacks;
+
+  Mutex mMutex;
+
+  // This variable is protected by mutex.
+  bool mCanceled;
 };
 
 // An helper class for the dispatching of the 'change' event.
 class DispatchChangeEventCallback final : public GetFilesCallback
 {
 public:
   explicit DispatchChangeEventCallback(HTMLInputElement* aInputElement)
     : mInputElement(aInputElement)
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -4821,17 +4821,17 @@ void HTMLMediaElement::FireTimeUpdate(bo
     mDecoder->SetFragmentEndTime(mFragmentEnd);
   }
 
   // Update the cues displaying on the video.
   // Here mTextTrackManager can be null if the cycle collector has unlinked
   // us before our parent. In that case UnbindFromTree will call us
   // when our parent is unlinked.
   if (mTextTrackManager) {
-    mTextTrackManager->UpdateCueDisplay();
+    mTextTrackManager->TimeMarchesOn();
   }
 }
 
 void HTMLMediaElement::GetCurrentSpec(nsCString& aString)
 {
   if (mLoadingSrc) {
     mLoadingSrc->GetSpec(aString);
   } else {
--- a/dom/html/HTMLTrackElement.cpp
+++ b/dom/html/HTMLTrackElement.cpp
@@ -197,23 +197,16 @@ HTMLTrackElement::LoadResource()
   LOG(LogLevel::Info, ("%p Trying to load from src=%s", this,
       NS_ConvertUTF16toUTF8(src).get()));
 
   if (mChannel) {
     mChannel->Cancel(NS_BINDING_ABORTED);
     mChannel = nullptr;
   }
 
-  // We may already have a TextTrack at this point if GetTrack() has already
-  // been called. This happens, for instance, if script tries to get the
-  // TextTrack before its mTrackElement has been bound to the DOM tree.
-  if (!mTrack) {
-    CreateTextTrack();
-  }
-
   nsCOMPtr<nsIChannel> channel;
   nsCOMPtr<nsILoadGroup> loadGroup = OwnerDoc()->GetDocumentLoadGroup();
   rv = NS_NewChannel(getter_AddRefs(channel),
                      uri,
                      static_cast<Element*>(this),
                      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS,
                      nsIContentPolicy::TYPE_INTERNAL_TRACK,
                      loadGroup,
@@ -254,21 +247,26 @@ HTMLTrackElement::BindToTree(nsIDocument
   if (!aParent || !aParent->IsNodeOfType(nsINode::eMEDIA)) {
     return NS_OK;
   }
 
   // Store our parent so we can look up its frame for display.
   if (!mMediaParent) {
     mMediaParent = static_cast<HTMLMediaElement*>(aParent);
 
-    HTMLMediaElement* media = static_cast<HTMLMediaElement*>(aParent);
     // TODO: separate notification for 'alternate' tracks?
-    media->NotifyAddedSource();
+    mMediaParent->NotifyAddedSource();
     LOG(LogLevel::Debug, ("Track element sent notification to parent."));
 
+    // We may already have a TextTrack at this point if GetTrack() has already
+    // been called. This happens, for instance, if script tries to get the
+    // TextTrack before its mTrackElement has been bound to the DOM tree.
+    if (!mTrack) {
+      CreateTextTrack();
+    }
     RefPtr<Runnable> r = NewRunnableMethod(this, &HTMLTrackElement::LoadResource);
     mMediaParent->RunInStableState(r);
   }
 
   return NS_OK;
 }
 
 void
--- a/dom/html/TextTrackManager.cpp
+++ b/dom/html/TextTrackManager.cpp
@@ -247,20 +247,16 @@ TextTrackManager::UpdateCueDisplay()
 
     nsPIDOMWindowInner* window = mMediaElement->OwnerDoc()->GetInnerWindow();
     if (window) {
       sParserWrapper->ProcessCues(window, jsCues, overlay);
     }
   } else if (overlay->Length() > 0) {
     nsContentUtils::SetNodeTextContent(overlay, EmptyString(), true);
   }
-  // Call TimeMarchesOn() directly instead DispatchTimeMarchesOn()
-  // because we had render the new cue, so we must run
-  // TimeMarchesOn immediately.
-  TimeMarchesOn();
 }
 
 void
 TextTrackManager::AddCue(TextTrackCue& aCue)
 {
   if (mNewCues) {
     mNewCues->AddCue(aCue);
   }
@@ -534,17 +530,17 @@ TextTrackManager::TimeMarchesOn()
   nsISupports* parentObject =
     mMediaElement->OwnerDoc()->GetParentObject();
   if (NS_WARN_IF(!parentObject)) {
     return;
   }
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(parentObject);
 
   if (mMediaElement &&
-      (!(mMediaElement->GetPlayedOrSeeked())|| mMediaElement->Seeking())) {
+      (!(mMediaElement->GetPlayedOrSeeked()) || mMediaElement->Seeking())) {
     return;
   }
 
   // Step 3.
   double currentPlaybackTime = mMediaElement->CurrentTime();
   bool hasNormalPlayback = !mHasSeeked;
   mHasSeeked = false;
 
@@ -706,12 +702,15 @@ TextTrackManager::TimeMarchesOn()
       if (trackElement) {
         trackElement->DispatchTrackRunnable(NS_LITERAL_STRING("cuechange"));
       }
     }
   }
 
   mLastTimeMarchesOnCalled = currentPlaybackTime;
   mLastActiveCues = currentCues;
+
+  // Step 18.
+  UpdateCueDisplay();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/html/TextTrackManager.h
+++ b/dom/html/TextTrackManager.h
@@ -78,38 +78,38 @@ public:
    * This takes the form of <foo.class.subclass>. These classes are then parsed
    * and set as the anonymous content's class attribute.
    *
    * Rules on constructing DOM objects from WebVTT nodes can be found here
    * http://dev.w3.org/html5/webvtt/#webvtt-cue-text-dom-construction-rules.
    * Current rules are taken from revision on April 15, 2013.
    */
 
-  /**
-   * Converts the TextTrackCue's cuetext into a tree of DOM objects and attaches
-   * it to a div on it's owning TrackElement's MediaElement's caption overlay.
-   */
-  void UpdateCueDisplay();
-
   void PopulatePendingList();
 
   void AddListeners();
 
   // The HTMLMediaElement that this TextTrackManager manages the TextTracks of.
   RefPtr<HTMLMediaElement> mMediaElement;
 
   void DispatchTimeMarchesOn();
+  void TimeMarchesOn();
 
   void NotifyShutdown()
   {
     mShutdown = true;
   }
 
 private:
-  void TimeMarchesOn();
+  /**
+   * Converts the TextTrackCue's cuetext into a tree of DOM objects
+   * and attaches it to a div on its owning TrackElement's
+   * MediaElement's caption overlay.
+   */
+  void UpdateCueDisplay();
 
   // List of the TextTrackManager's owning HTMLMediaElement's TextTracks.
   RefPtr<TextTrackList> mTextTracks;
   // List of text track objects awaiting loading.
   RefPtr<TextTrackList> mPendingTextTracks;
   // List of newly introduced Text Track cues.
 
   // Contain all cues for a MediaElement.
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -26,24 +26,16 @@ namespace mozilla {
 #undef LOGW
 
 LazyLogModule gAudioStreamLog("AudioStream");
 // For simple logs
 #define LOG(x, ...) MOZ_LOG(gAudioStreamLog, mozilla::LogLevel::Debug, ("%p " x, this, ##__VA_ARGS__))
 #define LOGW(x, ...) MOZ_LOG(gAudioStreamLog, mozilla::LogLevel::Warning, ("%p " x, this, ##__VA_ARGS__))
 
 /**
- * When MOZ_DUMP_AUDIO is set in the environment (to anything),
- * we'll drop a series of files in the current working directory named
- * dumped-audio-<nnn>.wav, one per AudioStream created, containing
- * the audio for the stream including any skips due to underruns.
- */
-static int gDumpedAudioCount = 0;
-
-/**
  * Keep a list of frames sent to the audio engine in each DataCallback along
  * with the playback rate at the moment. Since the playback rate and number of
  * underrun frames can vary in each callback. We need to keep the whole history
  * in order to calculate the playback position of the audio engine correctly.
  */
 class FrameHistory {
   struct Chunk {
     uint32_t servicedFrames;
@@ -116,18 +108,16 @@ public:
 private:
   AutoTArray<Chunk, 7> mChunks;
   int64_t mBaseOffset;
   double mBasePosition;
 };
 
 AudioStream::AudioStream(DataSource& aSource)
   : mMonitor("AudioStream")
-  , mInRate(0)
-  , mOutRate(0)
   , mChannels(0)
   , mOutChannels(0)
   , mTimeStretcher(nullptr)
   , mDumpFile(nullptr)
   , mState(INITIALIZED)
   , mDataSource(aSource)
 {
 }
@@ -157,17 +147,17 @@ AudioStream::SizeOfIncludingThis(MallocS
   return amount;
 }
 
 nsresult AudioStream::EnsureTimeStretcherInitializedUnlocked()
 {
   mMonitor.AssertCurrentThreadOwns();
   if (!mTimeStretcher) {
     mTimeStretcher = soundtouch::createSoundTouchObj();
-    mTimeStretcher->setSampleRate(mInRate);
+    mTimeStretcher->setSampleRate(mAudioClock.GetInputRate());
     mTimeStretcher->setChannels(mOutChannels);
     mTimeStretcher->setPitch(1.0);
   }
   return NS_OK;
 }
 
 nsresult AudioStream::SetPlaybackRate(double aPlaybackRate)
 {
@@ -183,17 +173,16 @@ nsresult AudioStream::SetPlaybackRate(do
     return NS_OK;
   }
 
   if (EnsureTimeStretcherInitializedUnlocked() != NS_OK) {
     return NS_ERROR_FAILURE;
   }
 
   mAudioClock.SetPlaybackRate(aPlaybackRate);
-  mOutRate = mInRate / aPlaybackRate;
 
   if (mAudioClock.GetPreservesPitch()) {
     mTimeStretcher->setTempo(aPlaybackRate);
     mTimeStretcher->setRate(1.0f);
   } else {
     mTimeStretcher->setTempo(1.0f);
     mTimeStretcher->setRate(aPlaybackRate);
   }
@@ -236,42 +225,49 @@ static void SetUint16LE(uint8_t* aDest, 
 
 static void SetUint32LE(uint8_t* aDest, uint32_t aValue)
 {
   SetUint16LE(aDest, aValue & 0xFFFF);
   SetUint16LE(aDest + 2, aValue >> 16);
 }
 
 static FILE*
-OpenDumpFile(AudioStream* aStream)
+OpenDumpFile(uint32_t aChannels, uint32_t aRate)
 {
+  /**
+   * When MOZ_DUMP_AUDIO is set in the environment (to anything),
+   * we'll drop a series of files in the current working directory named
+   * dumped-audio-<nnn>.wav, one per AudioStream created, containing
+   * the audio for the stream including any skips due to underruns.
+   */
+  static Atomic<int> gDumpedAudioCount(0);
+
   if (!getenv("MOZ_DUMP_AUDIO"))
     return nullptr;
   char buf[100];
-  snprintf_literal(buf, "dumped-audio-%d.wav", gDumpedAudioCount);
+  snprintf_literal(buf, "dumped-audio-%d.wav", ++gDumpedAudioCount);
   FILE* f = fopen(buf, "wb");
   if (!f)
     return nullptr;
-  ++gDumpedAudioCount;
 
   uint8_t header[] = {
     // RIFF header
     0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45,
     // fmt chunk. We always write 16-bit samples.
     0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0xFF, 0xFF,
     0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x10, 0x00,
     // data chunk
     0x64, 0x61, 0x74, 0x61, 0xFE, 0xFF, 0xFF, 0x7F
   };
   static const int CHANNEL_OFFSET = 22;
   static const int SAMPLE_RATE_OFFSET = 24;
   static const int BLOCK_ALIGN_OFFSET = 32;
-  SetUint16LE(header + CHANNEL_OFFSET, aStream->GetChannels());
-  SetUint32LE(header + SAMPLE_RATE_OFFSET, aStream->GetRate());
-  SetUint16LE(header + BLOCK_ALIGN_OFFSET, aStream->GetChannels()*2);
+  SetUint16LE(header + CHANNEL_OFFSET, aChannels);
+  SetUint32LE(header + SAMPLE_RATE_OFFSET, aRate);
+  SetUint16LE(header + BLOCK_ALIGN_OFFSET, aChannels * 2);
   fwrite(header, sizeof(header), 1, f);
 
   return f;
 }
 
 template <typename T>
 typename EnableIf<IsSame<T, int16_t>::value, void>::Type
 WriteDumpFileHelper(T* aInput, size_t aSamples, FILE* aFile) {
@@ -324,21 +320,20 @@ int AudioStream::InvokeCubeb(Function aF
 nsresult
 AudioStream::Init(uint32_t aNumChannels, uint32_t aRate,
                   const dom::AudioChannel aAudioChannel)
 {
   auto startTime = TimeStamp::Now();
   auto isFirst = CubebUtils::GetFirstStream();
 
   LOG("%s channels: %d, rate: %d", __FUNCTION__, aNumChannels, aRate);
-  mInRate = mOutRate = aRate;
   mChannels = aNumChannels;
   mOutChannels = aNumChannels;
 
-  mDumpFile = OpenDumpFile(this);
+  mDumpFile = OpenDumpFile(aNumChannels, aRate);
 
   cubeb_stream_params params;
   params.rate = aRate;
   params.channels = mOutChannels;
 #if defined(__ANDROID__)
 #if defined(MOZ_B2G)
   params.stream_type = CubebUtils::ConvertChannelToCubebType(aAudioChannel);
 #else
@@ -498,18 +493,18 @@ AudioStream::GetPositionInFramesUnlocked
     return -1;
   }
   return std::min<uint64_t>(position, INT64_MAX);
 }
 
 bool
 AudioStream::IsValidAudioFormat(Chunk* aChunk)
 {
-  if (aChunk->Rate() != mInRate) {
-    LOGW("mismatched sample %u, mInRate=%u", aChunk->Rate(), mInRate);
+  if (aChunk->Rate() != mAudioClock.GetInputRate()) {
+    LOGW("mismatched sample %u, mInRate=%u", aChunk->Rate(), mAudioClock.GetInputRate());
     return false;
   }
 
   if (aChunk->Channels() > 8) {
     return false;
   }
 
   return true;
@@ -554,18 +549,18 @@ AudioStream::GetTimeStretched(AudioBuffe
 {
   mMonitor.AssertCurrentThreadOwns();
 
   // We need to call the non-locking version, because we already have the lock.
   if (EnsureTimeStretcherInitializedUnlocked() != NS_OK) {
     return;
   }
 
-  double playbackRate = static_cast<double>(mInRate) / mOutRate;
-  uint32_t toPopFrames = ceil(aWriter.Available() * playbackRate);
+  uint32_t toPopFrames =
+    ceil(aWriter.Available() * mAudioClock.GetPlaybackRate());
 
   while (mTimeStretcher->numSamples() < aWriter.Available()) {
     UniquePtr<Chunk> c = mDataSource.PopFrames(toPopFrames);
     if (c->Frames() == 0) {
       break;
     }
     MOZ_ASSERT(c->Frames() <= toPopFrames);
     if (IsValidAudioFormat(c.get())) {
@@ -600,17 +595,17 @@ AudioStream::DataCallback(void* aBuffer,
     NS_WARNING("data callback fires before cubeb_stream_start() is called");
     mAudioClock.UpdateFrameHistory(0, aFrames);
     return writer.WriteZeros(aFrames);
   }
 
   // NOTE: wasapi (others?) can call us back *after* stop()/Shutdown() (mState == SHUTDOWN)
   // Bug 996162
 
-  if (mInRate == mOutRate) {
+  if (mAudioClock.GetInputRate() == mAudioClock.GetOutputRate()) {
     GetUnprocessed(writer);
   } else {
     GetTimeStretched(writer);
   }
 
   // Always send audible frames first, and silent frames later.
   // Otherwise it will break the assumption of FrameHistory.
   if (!mDataSource.Ended()) {
--- a/dom/media/AudioStream.h
+++ b/dom/media/AudioStream.h
@@ -67,16 +67,19 @@ public:
   double GetPlaybackRate() const;
   // Set if we are preserving the pitch.
   // Called on the audio thread.
   void SetPreservesPitch(bool aPreservesPitch);
   // Get the current pitch preservation state.
   // Called on the audio thread.
   bool GetPreservesPitch() const;
 
+  uint32_t GetInputRate() const { return mInRate; }
+  uint32_t GetOutputRate() const { return mOutRate; }
+
 private:
   // Output rate in Hz (characteristic of the playback rate)
   uint32_t mOutRate;
   // Input rate in Hz (characteristic of the media being played)
   uint32_t mInRate;
   // True if the we are timestretching, false if we are resampling.
   bool mPreservesPitch;
   // The history of frames sent to the audio engine in each DataCallback.
@@ -216,18 +219,17 @@ public:
   // was opened, of the audio hardware.  Thread-safe.
   int64_t GetPositionInFrames();
 
   static uint32_t GetPreferredRate()
   {
     CubebUtils::InitPreferredSampleRate();
     return CubebUtils::PreferredSampleRate();
   }
-  uint32_t GetRate() { return mOutRate; }
-  uint32_t GetChannels() { return mChannels; }
+
   uint32_t GetOutChannels() { return mOutChannels; }
 
   // Set playback rate as a multiple of the intrinsic playback rate. This is to
   // be called only with aPlaybackRate > 0.0.
   nsresult SetPlaybackRate(double aPlaybackRate);
   // Switch between resampling (if false) and time stretching (if true, default).
   nsresult SetPreservesPitch(bool aPreservesPitch);
 
@@ -272,20 +274,16 @@ private:
   void GetTimeStretched(AudioBufferWriter& aWriter);
 
   template <typename Function, typename... Args>
   int InvokeCubeb(Function aFunction, Args&&... aArgs);
 
   // The monitor is held to protect all access to member variables.
   Monitor mMonitor;
 
-  // Input rate in Hz (characteristic of the media being played)
-  uint32_t mInRate;
-  // Output rate in Hz (characteristic of the playback rate)
-  uint32_t mOutRate;
   uint32_t mChannels;
   uint32_t mOutChannels;
   AudioClock mAudioClock;
   soundtouch::SoundTouch* mTimeStretcher;
 
   // Output file for dumping audio
   FILE* mDumpFile;
 
--- a/dom/media/test/test_bug1242594.html
+++ b/dom/media/test/test_bug1242594.html
@@ -25,21 +25,22 @@ video.preload = "auto";
 
 var trackElement = document.createElement("track");
 trackElement.src = "basic.vtt";
 trackElement.kind = "subtitles";
 
 document.getElementById("content").appendChild(video);
 video.appendChild(trackElement);
 
-video.addEventListener("loadedmetadata", function run_tests() {
-  is(video.textTracks.length, 1, "Video should have one TextTrack.");
-  var parent = video.parentNode;
-  parent.removeChild(video);
-  is(video.textTracks.length, 1, "After unbind the video element, should have one TextTrack.");
-  parent.appendChild(video);
-  is(video.textTracks.length, 1, "After bind the video element, should have one TextTrack.");
-  SimpleTest.finish();
-});
+// Bug 1242599, access video.textTracks.length immediately after
+// the track element binds into the media element.
+is(video.textTracks.length, 1, "Video should have one TextTrack.");
+var parent = video.parentNode;
+parent.removeChild(video);
+is(video.textTracks.length, 1, "After unbind the video element, should have one TextTrack.");
+parent.appendChild(video);
+is(video.textTracks.length, 1, "After bind the video element, should have one TextTrack.");
+SimpleTest.finish();
+
 </script>
 </pre>
 </body>
 </html>
\ No newline at end of file
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -87,17 +87,18 @@ private:
   bool mCanceled;
 };
 
 static RefPtr<GLContext> sPluginContext = nullptr;
 
 static bool EnsureGLContext()
 {
   if (!sPluginContext) {
-    sPluginContext = GLContextProvider::CreateHeadless(CreateContextFlags::REQUIRE_COMPAT_PROFILE);
+    nsCString failureId;
+    sPluginContext = GLContextProvider::CreateHeadless(CreateContextFlags::REQUIRE_COMPAT_PROFILE, failureId);
   }
 
   return sPluginContext != nullptr;
 }
 
 static std::map<NPP, nsNPAPIPluginInstance*> sPluginNPPMap;
 
 #endif
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -124,17 +124,16 @@ CreateDrawTargetForSurface(gfxASurface *
   SurfaceFormat format = aSurface->GetSurfaceFormat();
   RefPtr<DrawTarget> drawTarget =
     Factory::CreateDrawTargetForCairoSurface(aSurface->CairoSurface(),
                                              aSurface->GetSize(),
                                              &format);
   if (!drawTarget) {
     NS_RUNTIMEABORT("CreateDrawTargetForSurface failed in plugin");
   }
-  aSurface->SetData(&kDrawTarget, drawTarget, nullptr);
   return drawTarget;
 }
 
 bool PluginInstanceChild::sIsIMEComposing = false;
 
 PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
                                          const nsCString& aMimeType,
                                          const uint16_t& aMode,
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -823,16 +823,21 @@ PluginInstanceParent::SetCurrentImage(Im
     MOZ_ASSERT(IsUsingDirectDrawing());
     ImageContainer::NonOwningImage holder(aImage);
     holder.mFrameID = ++mFrameID;
 
     AutoTArray<ImageContainer::NonOwningImage,1> imageList;
     imageList.AppendElement(holder);
     mImageContainer->SetCurrentImages(imageList);
 
+    // Invalidate our area in the page so the image gets flushed.
+    gfx::IntRect rect = aImage->GetPictureRect();
+    NPRect nprect = {uint16_t(rect.x), uint16_t(rect.y), uint16_t(rect.width), uint16_t(rect.height)};
+    RecvNPN_InvalidateRect(nprect);
+
     RecordDrawingModel();
 }
 
 bool
 PluginInstanceParent::RecvShowDirectDXGISurface(const WindowsHandle& handle,
                                                  const gfx::IntRect& dirty)
 {
 #if defined(XP_WIN)
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -390,48 +390,56 @@ public:
     AssertIsOnMainThread();
     MOZ_ASSERT(aWorkerPrivate);
     MOZ_ASSERT(aKeepAliveToken);
 
     mKeepAliveToken =
       new nsMainThreadPtrHolder<KeepAliveToken>(aKeepAliveToken);
   }
 
-  void
+  bool
   DispatchExtendableEventOnWorkerScope(JSContext* aCx,
                                        WorkerGlobalScope* aWorkerScope,
                                        ExtendableEvent* aEvent,
-                                       Promise** aWaitUntilPromise)
+                                       PromiseNativeHandler* aPromiseHandler)
   {
     MOZ_ASSERT(aWorkerScope);
     MOZ_ASSERT(aEvent);
     nsCOMPtr<nsIGlobalObject> sgo = aWorkerScope;
     WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
 
     ErrorResult result;
     result = aWorkerScope->DispatchDOMEvent(nullptr, aEvent, nullptr, nullptr);
     if (NS_WARN_IF(result.Failed()) || internalEvent->mFlags.mExceptionWasRaised) {
       result.SuppressException();
-      return;
+      return false;
     }
 
     RefPtr<Promise> waitUntilPromise = aEvent->GetPromise();
     if (!waitUntilPromise) {
       waitUntilPromise =
         Promise::Resolve(sgo, aCx, JS::UndefinedHandleValue, result);
       MOZ_RELEASE_ASSERT(!result.Failed());
     }
 
     MOZ_ASSERT(waitUntilPromise);
+
+    // Make sure to append the caller's promise handler before attaching
+    // our keep alive handler.  This can avoid terminating the worker
+    // before a success result is delivered to the caller in cases where
+    // the idle timeout has been set to zero.  This low timeout value is
+    // sometimes set in tests.
+    if (aPromiseHandler) {
+      waitUntilPromise->AppendNativeHandler(aPromiseHandler);
+    }
+
     KeepAliveHandler::CreateAndAttachToPromise(mKeepAliveToken,
                                                waitUntilPromise);
 
-    if (aWaitUntilPromise) {
-      waitUntilPromise.forget(aWaitUntilPromise);
-    }
+    return true;
   }
 };
 
 // Handle functional event
 // 9.9.7 If the time difference in seconds calculated by the current time minus
 // registration's last update check time is greater than 86400, invoke Soft Update
 // algorithm.
 class ExtendableFunctionalEventWorkerRunnable : public ExtendableEventWorkerRunnable
@@ -649,22 +657,18 @@ LifecycleEventWorkerRunnable::DispatchLi
   // is still executing. This can happen with infinite loops, for example.
   RefPtr<LifeCycleEventWatcher> watcher =
     new LifeCycleEventWatcher(aWorkerPrivate, mCallback);
 
   if (!watcher->Init()) {
     return true;
   }
 
-  RefPtr<Promise> waitUntil;
-  DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(),
-                                       event, getter_AddRefs(waitUntil));
-  if (waitUntil) {
-    waitUntil->AppendNativeHandler(watcher);
-  } else {
+  if (!DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(),
+                                       event, watcher)) {
     watcher->ReportResult(false);
   }
 
   return true;
 }
 
 } // anonymous namespace
 
@@ -801,22 +805,18 @@ public:
       PushEvent::Constructor(globalObj, NS_LITERAL_STRING("push"), pei, result);
     if (NS_WARN_IF(result.Failed())) {
       result.SuppressException();
       errorReporter->Report();
       return false;
     }
     event->SetTrusted(true);
 
-    RefPtr<Promise> waitUntil;
-    DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(),
-                                         event, getter_AddRefs(waitUntil));
-    if (waitUntil) {
-      waitUntil->AppendNativeHandler(errorReporter);
-    } else {
+    if (!DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(),
+                                              event, errorReporter)) {
       errorReporter->Report(nsIPushErrorReporter::DELIVERY_UNCAUGHT_EXCEPTION);
     }
 
     return true;
   }
 };
 
 class SendPushSubscriptionChangeEventRunnable final : public ExtendableEventWorkerRunnable
@@ -1149,26 +1149,24 @@ public:
     RefPtr<NotificationEvent> event =
       NotificationEvent::Constructor(target, mEventName,
                                      nei, result);
     if (NS_WARN_IF(result.Failed())) {
       return false;
     }
 
     event->SetTrusted(true);
-    RefPtr<Promise> waitUntil;
     aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
-    DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(),
-                                         event, getter_AddRefs(waitUntil));
-      aWorkerPrivate->GlobalScope()->ConsumeWindowInteraction();
-    if (waitUntil) {
-      RefPtr<AllowWindowInteractionHandler> allowWindowInteraction =
-        new AllowWindowInteractionHandler(aWorkerPrivate);
-      waitUntil->AppendNativeHandler(allowWindowInteraction);
+    RefPtr<AllowWindowInteractionHandler> allowWindowInteraction =
+      new AllowWindowInteractionHandler(aWorkerPrivate);
+    if (!DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(),
+                                              event, allowWindowInteraction)) {
+      allowWindowInteraction->RejectedCallback(aCx, JS::UndefinedHandleValue);
     }
+    aWorkerPrivate->GlobalScope()->ConsumeWindowInteraction();
 
     return true;
   }
 };
 
 } // namespace anonymous
 
 nsresult
--- a/extensions/gio/nsGIOProtocolHandler.cpp
+++ b/extensions/gio/nsGIOProtocolHandler.cpp
@@ -821,21 +821,21 @@ mount_operation_ask_password (GMountOper
     return;
   }
   nsAutoString nsmessage;
 
   if (flags & G_ASK_PASSWORD_NEED_PASSWORD) {
     if (flags & G_ASK_PASSWORD_NEED_USERNAME) {
       if (!realm.IsEmpty()) {
         const char16_t *strings[] = { realm.get(), dispHost.get() };
-        bundle->FormatStringFromName(MOZ_UTF16("EnterLoginForRealm"),
+        bundle->FormatStringFromName(MOZ_UTF16("EnterLoginForRealm2"),
                                      strings, 2, getter_Copies(nsmessage));
       } else {
         const char16_t *strings[] = { dispHost.get() };
-        bundle->FormatStringFromName(MOZ_UTF16("EnterUserPasswordFor"),
+        bundle->FormatStringFromName(MOZ_UTF16("EnterUserPasswordFor2"),
                                      strings, 1, getter_Copies(nsmessage));
       }
     } else {
       NS_ConvertUTF8toUTF16 userName(default_user);
       const char16_t *strings[] = { userName.get(), dispHost.get() };
       bundle->FormatStringFromName(MOZ_UTF16("EnterPasswordFor"),
                                    strings, 2, getter_Copies(nsmessage));
     }
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -1067,16 +1067,24 @@ DrawTargetSkia::FillGlyphs(ScaledFont *a
       // To accomplish this we have to explicitly disable hinting,
       // and disable LCDRenderText.
       paint.mPaint.setHinting(SkPaint::kNo_Hinting);
     } else {
       paint.mPaint.setHinting(SkPaint::kNormal_Hinting);
     }
   }
 
+  if (!shouldLCDRenderText && aFont->GetType() == FontType::GDI) {
+    // If we have non LCD GDI text, Cairo currently always uses cleartype fonts and
+    // converts them to grayscale. Force Skia to do the same, otherwise we use
+    // GDI fonts with the ANTIALIASED_QUALITY which is generally bolder than
+    // Cleartype fonts.
+    paint.mPaint.setFlags(paint.mPaint.getFlags() | SkPaint::kGenA8FromLCD_Flag);
+  }
+
   std::vector<uint16_t> indices;
   std::vector<SkPoint> offsets;
   indices.resize(aBuffer.mNumGlyphs);
   offsets.resize(aBuffer.mNumGlyphs);
 
   for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
     indices[i] = aBuffer.mGlyphs[i].mIndex;
     offsets[i].fX = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.x);
--- a/gfx/gl/GLContextEGL.h
+++ b/gfx/gl/GLContextEGL.h
@@ -18,17 +18,18 @@ class GLContextEGL : public GLContext
     friend class TextureImageEGL;
 
     static already_AddRefed<GLContextEGL>
     CreateGLContext(CreateContextFlags flags,
                     const SurfaceCaps& caps,
                     GLContextEGL *shareContext,
                     bool isOffscreen,
                     EGLConfig config,
-                    EGLSurface surface);
+                    EGLSurface surface,
+                    nsACString& aFailureId);
 
 public:
     MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextEGL, override)
     GLContextEGL(const SurfaceCaps& caps,
                  GLContext* shareContext,
                  bool isOffscreen,
                  EGLConfig config,
                  EGLSurface surface,
@@ -101,17 +102,18 @@ public:
 
     bool BindTex2DOffscreen(GLContext *aOffscreen);
     void UnbindTex2DOffscreen(GLContext *aOffscreen);
     void BindOffscreenFramebuffer();
 
     static already_AddRefed<GLContextEGL>
     CreateEGLPBufferOffscreenContext(CreateContextFlags flags,
                                      const gfx::IntSize& size,
-                                     const SurfaceCaps& minCaps);
+                                     const SurfaceCaps& minCaps,
+                                     nsACString& aFailureId);
 
 protected:
     friend class GLContextProviderEGL;
 
 public:
     const EGLConfig  mConfig;
 protected:
     EGLSurface mSurface;
--- a/gfx/gl/GLContextProviderCGL.mm
+++ b/gfx/gl/GLContextProviderCGL.mm
@@ -316,57 +316,66 @@ CreateOffscreenFBOContext(CreateContextF
 
     if (gfxPrefs::GLMultithreaded()) {
         CGLEnable(glContext->GetCGLContext(), kCGLCEMPEngine);
     }
     return glContext.forget();
 }
 
 already_AddRefed<GLContext>
-GLContextProviderCGL::CreateHeadless(CreateContextFlags flags)
+GLContextProviderCGL::CreateHeadless(CreateContextFlags flags, nsACString& aFailureId)
 {
     RefPtr<GLContextCGL> gl;
     gl = CreateOffscreenFBOContext(flags);
-    if (!gl)
+    if (!gl) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_CGL_FBO");
         return nullptr;
+    }
 
     if (!gl->Init()) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_CGL_INIT");
         NS_WARNING("Failed during Init.");
         return nullptr;
     }
 
     return gl.forget();
 }
 
 already_AddRefed<GLContext>
 GLContextProviderCGL::CreateOffscreen(const IntSize& size,
                                       const SurfaceCaps& minCaps,
-                                      CreateContextFlags flags)
+                                      CreateContextFlags flags,
+                                      nsACString& aFailureId)
 {
-    RefPtr<GLContext> gl = CreateHeadless(flags);
-    if (!gl)
+    RefPtr<GLContext> gl = CreateHeadless(flags, aFailureId);
+    if (!gl) {
         return nullptr;
+    }
 
-    if (!gl->InitOffscreen(size, minCaps))
+    if (!gl->InitOffscreen(size, minCaps)) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_CGL_INIT");
         return nullptr;
+    }
 
     return gl.forget();
 }
 
 static RefPtr<GLContext> gGlobalContext;
 
 GLContext*
 GLContextProviderCGL::GetGlobalContext()
 {
     static bool triedToCreateContext = false;
     if (!triedToCreateContext) {
         triedToCreateContext = true;
 
         MOZ_RELEASE_ASSERT(!gGlobalContext);
-        RefPtr<GLContext> temp = CreateHeadless(CreateContextFlags::NONE);
+        nsCString discardFailureId;
+        RefPtr<GLContext> temp = CreateHeadless(CreateContextFlags::NONE,
+                                                discardFailureId);
         gGlobalContext = temp;
 
         if (!gGlobalContext) {
             NS_WARNING("Couldn't init gGlobalContext.");
         }
     }
 
     return gGlobalContext;
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -450,17 +450,18 @@ GLContextEGL::SwapBuffers()
 void
 GLContextEGL::HoldSurface(gfxASurface *aSurf) {
     mThebesSurface = aSurf;
 }
 
 /* static */ EGLSurface
 GLContextEGL::CreateSurfaceForWindow(nsIWidget* aWidget)
 {
-    if (!sEGLLibrary.EnsureInitialized()) {
+    nsCString discardFailureId;
+    if (!sEGLLibrary.EnsureInitialized(false, discardFailureId)) {
         MOZ_CRASH("GFX: Failed to load EGL library!\n");
         return nullptr;
     }
 
     EGLConfig config;
     if (!CreateConfig(&config, aWidget)) {
         MOZ_CRASH("GFX: Failed to create EGLConfig!\n");
         return nullptr;
@@ -483,19 +484,21 @@ GLContextEGL::DestroySurface(EGLSurface 
 }
 
 already_AddRefed<GLContextEGL>
 GLContextEGL::CreateGLContext(CreateContextFlags flags,
                 const SurfaceCaps& caps,
                 GLContextEGL *shareContext,
                 bool isOffscreen,
                 EGLConfig config,
-                EGLSurface surface)
+                EGLSurface surface,
+                nsACString& aFailureId)
 {
     if (sEGLLibrary.fBindAPI(LOCAL_EGL_OPENGL_ES_API) == LOCAL_EGL_FALSE) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_ES");
         NS_WARNING("Failed to bind API to GLES!");
         return nullptr;
     }
 
     EGLContext eglShareContext = shareContext ? shareContext->mContext
                                               : EGL_NO_CONTEXT;
 
     nsTArray<EGLint> contextAttribs;
@@ -522,29 +525,32 @@ GLContextEGL::CreateGLContext(CreateCont
     if (!context && shareContext) {
         shareContext = nullptr;
         context = sEGLLibrary.fCreateContext(EGL_DISPLAY(),
                                               config,
                                               EGL_NO_CONTEXT,
                                               contextAttribs.Elements());
     }
     if (!context) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_CREATE");
         NS_WARNING("Failed to create EGLContext!");
         return nullptr;
     }
 
     RefPtr<GLContextEGL> glContext = new GLContextEGL(caps,
                                                         shareContext,
                                                         isOffscreen,
                                                         config,
                                                         surface,
                                                         context);
 
-    if (!glContext->Init())
+    if (!glContext->Init()) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_INIT");
         return nullptr;
+    }
 
     return glContext.forget();
 }
 
 EGLSurface
 GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(EGLConfig config,
                                                    EGLenum bindToTextureFormat,
                                                    mozilla::gfx::IntSize& pbsize)
@@ -733,17 +739,18 @@ CreateConfig(EGLConfig* aConfig, nsIWidg
     } else {
         return true;
     }
 }
 
 already_AddRefed<GLContext>
 GLContextProviderEGL::CreateWrappingExisting(void* aContext, void* aSurface)
 {
-    if (!sEGLLibrary.EnsureInitialized()) {
+    nsCString discardFailureId;
+    if (!sEGLLibrary.EnsureInitialized(false, discardFailureId)) {
         MOZ_CRASH("GFX: Failed to load EGL library 2!\n");
         return nullptr;
     }
 
     if (aContext && aSurface) {
         SurfaceCaps caps = SurfaceCaps::Any();
         EGLConfig config = EGL_NO_CONFIG;
         RefPtr<GLContextEGL> glContext =
@@ -758,17 +765,18 @@ GLContextProviderEGL::CreateWrappingExis
     }
 
     return nullptr;
 }
 
 already_AddRefed<GLContext>
 GLContextProviderEGL::CreateForWindow(nsIWidget *aWidget, bool aForceAccelerated)
 {
-    if (!sEGLLibrary.EnsureInitialized()) {
+    nsCString discardFailureId;
+    if (!sEGLLibrary.EnsureInitialized(false, discardFailureId)) {
         MOZ_CRASH("GFX: Failed to load EGL library 3!\n");
         return nullptr;
     }
 
     bool doubleBuffered = true;
 
     EGLConfig config;
     if (!CreateConfig(&config, aWidget)) {
@@ -781,17 +789,17 @@ GLContextProviderEGL::CreateForWindow(ns
         MOZ_CRASH("GFX: Failed to create EGLSurface!\n");
         return nullptr;
     }
 
     SurfaceCaps caps = SurfaceCaps::Any();
     RefPtr<GLContextEGL> glContext =
         GLContextEGL::CreateGLContext(CreateContextFlags::NONE, caps,
                                       nullptr, false,
-                                      config, surface);
+                                      config, surface, discardFailureId);
 
     if (!glContext) {
         MOZ_CRASH("GFX: Failed to create EGLContext!\n");
         mozilla::gl::DestroySurface(surface);
         return nullptr;
     }
 
     glContext->MakeCurrent();
@@ -799,17 +807,18 @@ GLContextProviderEGL::CreateForWindow(ns
 
     return glContext.forget();
 }
 
 #if defined(ANDROID)
 EGLSurface
 GLContextProviderEGL::CreateEGLSurface(void* aWindow)
 {
-    if (!sEGLLibrary.EnsureInitialized()) {
+    nsCString discardFailureId;
+    if (!sEGLLibrary.EnsureInitialized(false, discardFailureId)) {
         MOZ_CRASH("GFX: Failed to load EGL library 4!\n");
     }
 
     EGLConfig config;
     if (!CreateConfig(&config, static_cast<nsIWidget*>(aWindow))) {
         MOZ_CRASH("GFX: Failed to create EGLConfig 2!\n");
     }
 
@@ -822,17 +831,18 @@ GLContextProviderEGL::CreateEGLSurface(v
     }
 
     return surface;
 }
 
 void
 GLContextProviderEGL::DestroyEGLSurface(EGLSurface surface)
 {
-    if (!sEGLLibrary.EnsureInitialized()) {
+    nsCString discardFailureId;
+    if (!sEGLLibrary.EnsureInitialized(false, discardFailureId)) {
         MOZ_CRASH("GFX: Failed to load EGL library 5!\n");
     }
 
     sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surface);
 }
 #endif // defined(ANDROID)
 
 static void
@@ -933,111 +943,124 @@ ChooseConfig(GLLibraryEGL* egl, CreateCo
     out_configCaps->bpp16 = (GetAttrib(egl, config, LOCAL_EGL_RED_SIZE) < 8);
 
     return config;
 }
 
 /*static*/ already_AddRefed<GLContextEGL>
 GLContextEGL::CreateEGLPBufferOffscreenContext(CreateContextFlags flags,
                                                const mozilla::gfx::IntSize& size,
-                                               const SurfaceCaps& minCaps)
+                                               const SurfaceCaps& minCaps,
+                                               nsACString& aFailureId)
 {
     bool forceEnableHardware = bool(flags & CreateContextFlags::FORCE_ENABLE_HARDWARE);
-    if (!sEGLLibrary.EnsureInitialized(forceEnableHardware))
+    if (!sEGLLibrary.EnsureInitialized(forceEnableHardware, aFailureId)) {
         return nullptr;
+    }
 
     SurfaceCaps configCaps;
     EGLConfig config = ChooseConfig(&sEGLLibrary, flags, minCaps, &configCaps);
     if (config == EGL_NO_CONFIG) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_NO_CONFIG");
         NS_WARNING("Failed to find a compatible config.");
         return nullptr;
     }
 
     if (GLContext::ShouldSpew()) {
         sEGLLibrary.DumpEGLConfig(config);
     }
 
     mozilla::gfx::IntSize pbSize(size);
     EGLSurface surface = GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(config,
                                                                             LOCAL_EGL_NONE,
                                                                             pbSize);
     if (!surface) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_POT");
         NS_WARNING("Failed to create PBuffer for context!");
         return nullptr;
     }
 
     RefPtr<GLContextEGL> gl = GLContextEGL::CreateGLContext(flags, configCaps, nullptr, true,
-                                                            config, surface);
+                                                            config, surface, aFailureId);
     if (!gl) {
         NS_WARNING("Failed to create GLContext from PBuffer");
         sEGLLibrary.fDestroySurface(sEGLLibrary.Display(), surface);
         return nullptr;
     }
 
     return gl.forget();
 }
 
 /*static*/ already_AddRefed<GLContext>
-GLContextProviderEGL::CreateHeadless(CreateContextFlags flags)
+GLContextProviderEGL::CreateHeadless(CreateContextFlags flags, nsACString& aFailureId)
 {
     mozilla::gfx::IntSize dummySize = mozilla::gfx::IntSize(16, 16);
     SurfaceCaps dummyCaps = SurfaceCaps::Any();
-    return GLContextEGL::CreateEGLPBufferOffscreenContext(flags, dummySize, dummyCaps);
+    return GLContextEGL::CreateEGLPBufferOffscreenContext(flags, dummySize, dummyCaps,
+                                                          aFailureId);
 }
 
 // Under EGL, on Android, pbuffers are supported fine, though
 // often without the ability to texture from them directly.
 /*static*/ already_AddRefed<GLContext>
 GLContextProviderEGL::CreateOffscreen(const mozilla::gfx::IntSize& size,
                                       const SurfaceCaps& minCaps,
-                                      CreateContextFlags flags)
+                                      CreateContextFlags flags,
+                                      nsACString& aFailureId)
 {
     bool forceEnableHardware = bool(flags & CreateContextFlags::FORCE_ENABLE_HARDWARE);
-    if (!sEGLLibrary.EnsureInitialized(forceEnableHardware)) // Needed for IsANGLE().
+    if (!sEGLLibrary.EnsureInitialized(forceEnableHardware, aFailureId)) { // Needed for IsANGLE().
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_LIB_INIT");
         return nullptr;
+    }
 
     bool canOffscreenUseHeadless = true;
     if (sEGLLibrary.IsANGLE()) {
         // ANGLE needs to use PBuffers.
         canOffscreenUseHeadless = false;
     }
 
     RefPtr<GLContext> gl;
     SurfaceCaps minOffscreenCaps = minCaps;
 
     if (canOffscreenUseHeadless) {
-        gl = CreateHeadless(flags);
-        if (!gl)
+        gl = CreateHeadless(flags, aFailureId);
+        if (!gl) {
             return nullptr;
+        }
     } else {
         SurfaceCaps minBackbufferCaps = minOffscreenCaps;
         if (minOffscreenCaps.antialias) {
             minBackbufferCaps.antialias = false;
             minBackbufferCaps.depth = false;
             minBackbufferCaps.stencil = false;
         }
 
-        gl = GLContextEGL::CreateEGLPBufferOffscreenContext(flags, size, minBackbufferCaps);
-        if (!gl)
+        gl = GLContextEGL::CreateEGLPBufferOffscreenContext(flags, size, minBackbufferCaps,
+                                                            aFailureId);
+        if (!gl) {
             return nullptr;
+        }
 
         // Pull the actual resulting caps to ensure that our offscreen matches our
         // backbuffer.
         minOffscreenCaps.alpha = gl->Caps().alpha;
         if (!minOffscreenCaps.antialias) {
             // Only update these if we don't have AA. If we do have AA, we ignore
             // backbuffer depth/stencil.
             minOffscreenCaps.depth = gl->Caps().depth;
             minOffscreenCaps.stencil = gl->Caps().stencil;
         }
     }
 
     // Init the offscreen with the updated offscreen caps.
-    if (!gl->InitOffscreen(size, minOffscreenCaps))
+    if (!gl->InitOffscreen(size, minOffscreenCaps)) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_OFFSCREEN");
         return nullptr;
+    }
 
     return gl.forget();
 }
 
 // Don't want a global context on Android as 1) share groups across 2 threads fail on many Tegra drivers (bug 759225)
 // and 2) some mobile devices have a very strict limit on global number of GL contexts (bug 754257)
 // and 3) each EGL context eats 750k on B2G (bug 813783)
 /*static*/ GLContext*
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -1218,17 +1218,18 @@ GLContextGLX::FindFBConfigForWindow(Disp
         }
     }
 
     NS_WARNING("[GLX] Couldn't find a FBConfig matching window visual");
     return false;
 }
 
 static already_AddRefed<GLContextGLX>
-CreateOffscreenPixmapContext(const IntSize& size, const SurfaceCaps& minCaps, ContextProfile profile = ContextProfile::OpenGLCompatibility)
+CreateOffscreenPixmapContext(const IntSize& size, const SurfaceCaps& minCaps, nsACString& aFailureId,
+                             ContextProfile profile = ContextProfile::OpenGLCompatibility)
 {
     GLXLibrary* glx = &sGLXLibrary;
     if (!glx->EnsureInitialized())
         return nullptr;
 
     Display* display = DefaultXDisplay();
     int screen = DefaultScreen(display);
 
@@ -1280,64 +1281,68 @@ DONE_CREATING_PIXMAP:
         return nullptr;
 
     GLContextGLX* shareContext = GetGlobalContextGLX();
     return GLContextGLX::CreateGLContext(minCaps, shareContext, true, display, pixmap,
                                          config, true, surface, profile);
 }
 
 /*static*/ already_AddRefed<GLContext>
-GLContextProviderGLX::CreateHeadless(CreateContextFlags)
+GLContextProviderGLX::CreateHeadless(CreateContextFlags, nsACString& aFailureId)
 {
     IntSize dummySize = IntSize(16, 16);
     SurfaceCaps dummyCaps = SurfaceCaps::Any();
-    return CreateOffscreenPixmapContext(dummySize, dummyCaps);
+    return CreateOffscreenPixmapContext(dummySize, dummyCaps, aFailureId);
 }
 
 /*static*/ already_AddRefed<GLContext>
 GLContextProviderGLX::CreateOffscreen(const IntSize& size,
                                       const SurfaceCaps& minCaps,
-                                      CreateContextFlags flags)
+                                      CreateContextFlags flags,
+                                      nsACString& aFailureId)
 {
     SurfaceCaps minBackbufferCaps = minCaps;
     if (minCaps.antialias) {
         minBackbufferCaps.antialias = false;
         minBackbufferCaps.depth = false;
         minBackbufferCaps.stencil = false;
     }
 
     ContextProfile profile = ContextProfile::OpenGLCore;
     if (flags & CreateContextFlags::REQUIRE_COMPAT_PROFILE) {
         profile = ContextProfile::OpenGLCompatibility;
     }
 
     RefPtr<GLContext> gl;
-    gl = CreateOffscreenPixmapContext(size, minBackbufferCaps, profile);
+    gl = CreateOffscreenPixmapContext(size, minBackbufferCaps, aFailureId, profile);
     if (!gl)
         return nullptr;
 
-    if (!gl->InitOffscreen(size, minCaps))
+    if (!gl->InitOffscreen(size, minCaps)) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_GLX_INIT");
         return nullptr;
+    }
 
     return gl.forget();
 }
 
 /*static*/ GLContext*
 GLContextProviderGLX::GetGlobalContext()
 {
     // TODO: get GLX context sharing to work well with multiple threads
     if (gfxEnv::DisableContextSharingGlx())
         return nullptr;
 
     static bool triedToCreateContext = false;
     if (!triedToCreateContext) {
         triedToCreateContext = true;
 
         MOZ_RELEASE_ASSERT(!gGlobalContext);
-        RefPtr<GLContext> temp = CreateHeadless(CreateContextFlags::NONE);
+        nsCString discardFailureId;
+        RefPtr<GLContext> temp = CreateHeadless(CreateContextFlags::NONE, discardFailureId);
         gGlobalContext = temp;
     }
 
     return gGlobalContext;
 }
 
 /*static*/ void
 GLContextProviderGLX::Shutdown()
--- a/gfx/gl/GLContextProviderImpl.h
+++ b/gfx/gl/GLContextProviderImpl.h
@@ -62,21 +62,22 @@ public:
      * @param flags   The set of CreateContextFlags to be used for this
      *                offscreen context.
      *
      * @return Context to use for offscreen rendering
      */
     static already_AddRefed<GLContext>
     CreateOffscreen(const mozilla::gfx::IntSize& size,
                     const SurfaceCaps& minCaps,
-                    CreateContextFlags flags);
+                    CreateContextFlags flags,
+                    nsACString& failureId);
 
     // Just create a context. We'll add offscreen stuff ourselves.
     static already_AddRefed<GLContext>
-    CreateHeadless(CreateContextFlags flags);
+    CreateHeadless(CreateContextFlags flags, nsACString& aFailureId);
 
     /**
      * Create wrapping Gecko GLContext for external gl context.
      *
      * @param aContext External context which will be wrapped by Gecko GLContext.
      * @param aSurface External surface which is used for external context.
      *
      * @return Wrapping Context to use for rendering
--- a/gfx/gl/GLContextProviderNull.cpp
+++ b/gfx/gl/GLContextProviderNull.cpp
@@ -18,18 +18,20 @@ already_AddRefed<GLContext>
 GLContextProviderNull::CreateWrappingExisting(void*, void*)
 {
     return nullptr;
 }
 
 already_AddRefed<GLContext>
 GLContextProviderNull::CreateOffscreen(const gfx::IntSize&,
                                        const SurfaceCaps&,
-                                       CreateContextFlags)
+                                       CreateContextFlags,
+                                       nsACString& aFailureId)
 {
+    aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_NULL");
     return nullptr;
 }
 
 already_AddRefed<GLContext>
 GLContextProviderNull::CreateHeadless(CreateContextFlags)
 {
     return nullptr;
 }
--- a/gfx/gl/GLContextProviderWGL.cpp
+++ b/gfx/gl/GLContextProviderWGL.cpp
@@ -636,17 +636,17 @@ CreateWindowOffscreenContext()
     RefPtr<GLContextWGL> glContext = new GLContextWGL(caps,
                                                         shareContext, true,
                                                         dc, context, win);
 
     return glContext.forget();
 }
 
 /*static*/ already_AddRefed<GLContext>
-GLContextProviderWGL::CreateHeadless(CreateContextFlags)
+GLContextProviderWGL::CreateHeadless(CreateContextFlags, nsACString& aFailureId)
 {
     if (!sWGLLib.EnsureInitialized()) {
         return nullptr;
     }
 
     RefPtr<GLContextWGL> glContext;
 
     // Always try to create a pbuffer context first, because we
@@ -671,39 +671,43 @@ GLContextProviderWGL::CreateHeadless(Cre
 
     RefPtr<GLContext> retGL = glContext.get();
     return retGL.forget();
 }
 
 /*static*/ already_AddRefed<GLContext>
 GLContextProviderWGL::CreateOffscreen(const IntSize& size,
                                       const SurfaceCaps& minCaps,
-                                      CreateContextFlags flags)
+                                      CreateContextFlags flags,
+                                      nsACString& aFailureId)
 {
-    RefPtr<GLContext> gl = CreateHeadless(flags);
+    RefPtr<GLContext> gl = CreateHeadless(flags, aFailureId);
     if (!gl)
         return nullptr;
 
-    if (!gl->InitOffscreen(size, minCaps))
+    if (!gl->InitOffscreen(size, minCaps)) {
+        aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WGL_INIT");
         return nullptr;
+    }
 
     return gl.forget();
 }
 
 static StaticRefPtr<GLContext> gGlobalContext;
 
 /*static*/ GLContext*
 GLContextProviderWGL::GetGlobalContext()
 {
     static bool triedToCreateContext = false;
     if (!triedToCreateContext) {
         triedToCreateContext = true;
 
         MOZ_RELEASE_ASSERT(!gGlobalContext);
-        RefPtr<GLContext> temp = CreateHeadless(CreateContextFlags::NONE);
+        nsCString discardFailureId;
+        RefPtr<GLContext> temp = CreateHeadless(CreateContextFlags::NONE, discardFailureId);
         gGlobalContext = temp;
     }
 
     return static_cast<GLContext*>(gGlobalContext);
 }
 
 /*static*/ void
 GLContextProviderWGL::Shutdown()
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -137,26 +137,29 @@ GetAndInitWARPDisplay(GLLibraryEGL& egl,
 
     if (!egl.fInitialize(display, nullptr, nullptr))
         return EGL_NO_DISPLAY;
 
     return display;
 }
 
 static bool
-IsAccelAngleSupported(const nsCOMPtr<nsIGfxInfo>& gfxInfo)
+IsAccelAngleSupported(const nsCOMPtr<nsIGfxInfo>& gfxInfo, nsACString& aFailureId)
 {
     int32_t angleSupport;
     nsCString failureId;
     gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
                                          nsIGfxInfo::FEATURE_WEBGL_ANGLE,
                                          failureId,
                                          &angleSupport);
     Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_ACCL_FAILURE_ID,
                           failureId);
+    if (failureId.IsEmpty()) {
+      aFailureId = failureId;
+    }
     return (angleSupport == nsIGfxInfo::FEATURE_STATUS_OK);
 }
 
 static EGLDisplay
 GetAndInitDisplay(GLLibraryEGL& egl, void* displayType)
 {
     EGLDisplay display = egl.fGetDisplay(displayType);
     if (display == EGL_NO_DISPLAY)
@@ -196,17 +199,19 @@ GetAndInitDisplayForAccelANGLE(GLLibrary
     return ret;
 }
 
 bool
 GLLibraryEGL::ReadbackEGLImage(EGLImage image, gfx::DataSourceSurface* out_surface)
 {
     StaticMutexAutoUnlock lock(sMutex);
     if (!mReadbackGL) {
-        mReadbackGL = gl::GLContextProvider::CreateHeadless(gl::CreateContextFlags::NONE);
+        nsCString discardFailureId;
+        mReadbackGL = gl::GLContextProvider::CreateHeadless(gl::CreateContextFlags::NONE,
+                                                            discardFailureId);
     }
 
     ScopedTexture destTex(mReadbackGL);
     const GLuint target = LOCAL_GL_TEXTURE_EXTERNAL;
     ScopedBindTexture autoTex(mReadbackGL, destTex.Texture(), target);
     mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
     mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
     mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);
@@ -218,17 +223,17 @@ GLLibraryEGL::ReadbackEGLImage(EGLImage 
     int shaderConfig = config.mFeatures;
     mReadbackGL->ReadTexImageHelper()->ReadTexImage(out_surface, 0, target,
                                                     out_surface->GetSize(), shaderConfig);
 
     return true;
 }
 
 bool
-GLLibraryEGL::EnsureInitialized(bool forceAccel)
+GLLibraryEGL::EnsureInitialized(bool forceAccel, nsACString& aFailureId)
 {
     if (mInitialized) {
         return true;
     }
 
     mozilla::ScopedGfxFeatureReporter reporter("EGL");
 
 #ifdef MOZ_B2G
@@ -384,17 +389,17 @@ GLLibraryEGL::EnsureInitialized(bool for
 
     // Check the ANGLE support the system has
     nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
     mIsANGLE = IsExtensionSupported(ANGLE_platform_angle);
 
     EGLDisplay chosenDisplay = nullptr;
 
     if (IsExtensionSupported(ANGLE_platform_angle_d3d)) {
-        bool accelAngleSupport = IsAccelAngleSupported(gfxInfo);
+        bool accelAngleSupport = IsAccelAngleSupported(gfxInfo, aFailureId);
 
         bool shouldTryAccel = forceAccel || accelAngleSupport;
         bool shouldTryWARP = !shouldTryAccel;
         if (gfxPrefs::WebGLANGLEForceWARP()) {
             shouldTryWARP = true;
             shouldTryAccel = false;
         }
 
@@ -405,28 +410,34 @@ GLLibraryEGL::EnsureInitialized(bool for
                 mIsWARP = true;
             }
         }
 
         if (!chosenDisplay) {
             // If falling back to WARP did not work and we don't want to try
             // using HW accelerated ANGLE, then fail.
             if (!shouldTryAccel) {
+                if (aFailureId.IsEmpty()) {
+                    aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WARP_FALLBACK");
+                }
                 NS_ERROR("Fallback WARP ANGLE context failed to initialize.");
                 return false;
             }
 
             // Hardware accelerated ANGLE path
             chosenDisplay = GetAndInitDisplayForAccelANGLE(*this);
         }
     } else {
         chosenDisplay = GetAndInitDisplay(*this, EGL_DEFAULT_DISPLAY);
     }
 
     if (!chosenDisplay) {
+        if (aFailureId.IsEmpty()) {
+            aFailureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_NO_DISPLAY");
+        }
         NS_WARNING("Failed to initialize a display.");
         return false;
     }
     mEGLDisplay = chosenDisplay;
 
     InitDisplayExtensions();
 
     ////////////////////////////////////
--- a/gfx/gl/GLLibraryEGL.h
+++ b/gfx/gl/GLLibraryEGL.h
@@ -530,17 +530,17 @@ public:
     }
 
     bool HasRobustness() const {
         return IsExtensionSupported(EXT_create_context_robustness);
     }
 
     bool ReadbackEGLImage(EGLImage image, gfx::DataSourceSurface* out_surface);
 
-    bool EnsureInitialized(bool forceAccel = false);
+    bool EnsureInitialized(bool forceAccel, nsACString& aFailureId);
 
     void DumpEGLConfig(EGLConfig cfg);
     void DumpEGLConfigs();
 
     struct {
         typedef EGLDisplay (GLAPIENTRY * pfnGetDisplay)(void *display_id);
         pfnGetDisplay fGetDisplay;
         typedef EGLDisplay(GLAPIENTRY * pfnGetPlatformDisplayEXT)(EGLenum platform, void *native_display, const EGLint *attrib_list);
--- a/gfx/layers/GLImages.cpp
+++ b/gfx/layers/GLImages.cpp
@@ -46,17 +46,19 @@ EGLImageImage::~EGLImageImage()
 }
 
 already_AddRefed<gfx::SourceSurface>
 GLImage::GetAsSourceSurface()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread");
 
   if (!sSnapshotContext) {
-    sSnapshotContext = GLContextProvider::CreateHeadless(CreateContextFlags::NONE);
+    nsCString discardFailureId;
+    sSnapshotContext = GLContextProvider::CreateHeadless(CreateContextFlags::NONE,
+                                                         discardFailureId);
     if (!sSnapshotContext) {
       NS_WARNING("Failed to create snapshot GLContext");
       return nullptr;
     }
   }
 
   sSnapshotContext->MakeCurrent();
   ScopedTexture scopedTex(sSnapshotContext);
--- a/gfx/layers/LayerScope.cpp
+++ b/gfx/layers/LayerScope.cpp
@@ -1472,18 +1472,20 @@ LayerScopeWebSocketManager::SocketHandle
     nsAutoCString guid("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
     nsAutoCString res;
     SHA1Sum sha1;
     nsCString combined(wsKey + guid);
     sha1.update(combined.get(), combined.Length());
     uint8_t digest[SHA1Sum::kHashSize]; // SHA1 digests are 20 bytes long.
     sha1.finish(digest);
     nsCString newString(reinterpret_cast<char*>(digest), SHA1Sum::kHashSize);
-    Base64Encode(newString, res);
-
+    rv = Base64Encode(newString, res);
+    if (NS_FAILED(rv)) {
+        return false;
+    }
     nsCString response("HTTP/1.1 101 Switching Protocols\r\n");
     response.AppendLiteral("Upgrade: websocket\r\n");
     response.AppendLiteral("Connection: Upgrade\r\n");
     response.Append(nsCString("Sec-WebSocket-Accept: ") + res + nsCString("\r\n"));
     response.AppendLiteral("Sec-WebSocket-Protocol: binary\r\n\r\n");
     uint32_t written = 0;
     uint32_t size = response.Length();
     while (written < size) {
--- a/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
+++ b/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
@@ -170,22 +170,56 @@ function synthesizeNativeWheelAndWaitFor
 // aX and aY are relative to the top-left of |aElement|'s containing window.
 function synthesizeNativeMouseMove(aElement, aX, aY) {
   var pt = coordinatesRelativeToScreen(aX, aY, aElement);
   var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
   utils.sendNativeMouseEvent(pt.x, pt.y, nativeMouseMoveEventMsg(), 0, aElement);
   return true;
 }
 
+function equalPoints(aPt1, aPt2) {
+  if (!aPt1 || !aPt2) {
+    return false;
+  }
+  return aPt1.x == aPt2.x &&
+         aPt1.y == aPt2.y;
+}
+
 // Synthesizes a native mouse move event and invokes the callback once the
 // mouse move event is dispatched to |aElement|'s containing window. If the event
 // targets content in a subdocument, |aElement| should be inside the
 // subdocument. See synthesizeNativeMouseMove for details on the other
 // parameters.
 function synthesizeNativeMouseMoveAndWaitForMoveEvent(aElement, aX, aY, aCallback) {
+  // Initialize, if necessary, a place to a store state that will persist
+  // across calls to this function.
+  var func = synthesizeNativeMouseMoveAndWaitForMoveEvent;  // just so it's shorter
+  if (typeof func.persistentState == 'undefined') {
+    // If we're in a subtest, the test driver provides a variable where
+    // we can store state that persists across subtests. Otherwise, just
+    // create a variable that lives as long as this function.
+    if (typeof window.statePersistentAcrossSubtests == 'undefined') {
+      func.persistentState = {}
+    } else {
+      func.persistentState = window.statePersistentAcrossSubtests;
+    }
+  }
+
+  // On Windows, if the mouse is already at the target location, the mouse
+  // move event never gets dispatched. This is a significant potential footgun
+  // (if we try waiting for it when it'll never come); to avoid it, we store
+  // the last location we moved the mouse to, and just call the callback
+  // right away if the new location is the same.
+  var pt = coordinatesRelativeToScreen(aX, aY, aElement);
+  if (equalPoints(func.persistentState.lastMouseMoveLocation, pt)) {
+    setTimeout(aCallback, 0);
+    return true;
+  }
+  func.persistentState.lastMouseMoveLocation = pt;
+
   var targetWindow = aElement.ownerDocument.defaultView;
   targetWindow.addEventListener("mousemove", function mousemoveWaiter(e) {
     targetWindow.removeEventListener("mousemove", mousemoveWaiter);
     setTimeout(aCallback, 0);
   });
   return synthesizeNativeMouseMove(aElement, aX, aY);
 }
 
@@ -236,8 +270,23 @@ function synthesizeNativeMouseEvent(aEle
 function synthesizeNativeClick(aElement, aX, aY, aObserver = null) {
   var pt = coordinatesRelativeToScreen(aX, aY, aElement);
   var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
   utils.sendNativeMouseEvent(pt.x, pt.y, nativeMouseDownEventMsg(), 0, aElement, function() {
     utils.sendNativeMouseEvent(pt.x, pt.y, nativeMouseUpEventMsg(), 0, aElement, aObserver);
   });
   return true;
 }
+
+// Move the mouse to (dx, dy) relative to |element|, and scroll the wheel
+// at that location.
+// Moving the mouse is necessary to avoid wheel events from two consecutive
+// scrollWheelOver() calls on different elements being incorreclty considered
+// as part of t he same wheel transaction.
+// We also wait for the mouse move event to be processed before sending the
+// wheel event, otherwise there is a chance they might get reordered, and
+// we have the transaction problem again.
+function moveMouseAndScrollWheelOver(element, dx, dy, testDriver) {
+  return synthesizeNativeMouseMoveAndWaitForMoveEvent(element, dx, dy, function() {
+    synthesizeNativeWheelAndWaitForScrollEvent(element, dx, dy, 0, -10, testDriver);
+  });
+}
+
--- a/gfx/layers/apz/test/mochitest/apz_test_utils.js
+++ b/gfx/layers/apz/test/mochitest/apz_test_utils.js
@@ -143,16 +143,20 @@ function waitForApzFlushedRepaints(aCall
 // functions provided by SimpleTest are also mapped into the subtest's window.
 // For other things from the parent, the subtest can use window.opener.<whatever>
 // to access objects.
 function runSubtestsSeriallyInFreshWindows(aSubtests) {
   return new Promise(function(resolve, reject) {
     var testIndex = -1;
     var w = null;
 
+    // Some state that persists across subtests. This is made available to
+    // subtests to put things into / read things out of.
+    var statePersistentAcrossSubtests = {};
+
     function advanceSubtestExecution() {
       var test = aSubtests[testIndex];
       if (w) {
         if (typeof test.dp_suppression != 'undefined') {
           // We modified the suppression when starting the test, so now undo that.
           SpecialPowers.getDOMWindowUtils(window).respectDisplayPortSuppression(!test.dp_suppression);
         }
         if (test.prefs) {
@@ -184,16 +188,17 @@ function runSubtestsSeriallyInFreshWindo
         // entire test which is more deterministic.
         SpecialPowers.getDOMWindowUtils(window).respectDisplayPortSuppression(test.dp_suppression);
       }
 
       function spawnTest(aFile) {
         w = window.open('', "_blank");
         w.subtestDone = advanceSubtestExecution;
         w.SimpleTest = SimpleTest;
+        w.statePersistentAcrossSubtests = statePersistentAcrossSubtests;
         w.is = function(a, b, msg) { return is(a, b, aFile + " | " + msg); };
         w.ok = function(cond, name, diag) { return ok(cond, aFile + " | " + name, diag); };
         w.location = location.href.substring(0, location.href.lastIndexOf('/') + 1) + aFile;
         return w;
       }
 
       if (test.prefs) {
         // Got some prefs for this subtest, push them
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_scroll_inactive_perspective.html
@@ -0,0 +1,46 @@
+<head>
+  <meta name="viewport" content="width=device-width; initial-scale=1.0">
+  <title>Wheel-scrolling over inactive subframe with perspective</title>
+  <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+  <script type="application/javascript" src="apz_test_utils.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+  <script type="application/javascript">
+
+function* test(testDriver) {
+  var subframe = document.getElementById('scroll');
+
+  // scroll over the middle of the subframe, to make sure it scrolls,
+  // not the page
+  var scrollPos = subframe.scrollTop;
+  yield moveMouseAndScrollWheelOver(subframe, 100, 100, testDriver);
+  dump("after scroll, subframe.scrollTop = " + subframe.scrollTop + "\n");
+  ok(subframe.scrollTop > scrollPos, "subframe scrolled after wheeling over it");
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+  </script>
+  <style>
+    #scroll {
+      width: 200px;
+      height: 200px;
+      overflow: scroll;
+      perspective: 400px;
+    }
+    #scrolled {
+      width: 200px;
+      height: 1000px; /* so the subframe has room to scroll */
+      background: linear-gradient(red, blue); /* so you can see it scroll */
+      transform: translateZ(0px); /* so the perspective makes it to the display list */
+    }
+  </style>
+</head>
+<body>
+  <div id="scroll">
+    <div id="scrolled"></div>
+  </div>
+  <div style="height: 5000px;"></div><!-- So the page is scrollable as well -->
+</body>
+</head>
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_scroll_inactive_zindex.html
@@ -0,0 +1,47 @@
+<head>
+  <meta name="viewport" content="width=device-width; initial-scale=1.0">
+  <title>Wheel-scrolling over inactive subframe with z-index</title>
+  <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+  <script type="application/javascript" src="apz_test_utils.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+  <script type="application/javascript">
+
+function* test(testDriver) {
+  var subframe = document.getElementById('scroll');
+
+  // scroll over the middle of the subframe, and make sure that it scrolls,
+  // not the page
+  var scrollPos = subframe.scrollTop;
+  yield moveMouseAndScrollWheelOver(subframe, 100, 100, testDriver);
+  dump("after scroll, subframe.scrollTop = " + subframe.scrollTop + "\n");
+  ok(subframe.scrollTop > scrollPos, "subframe scrolled after wheeling over it");
+}
+
+waitUntilApzStable()
+.then(runContinuation(test))
+.then(subtestDone);
+
+  </script>
+  <style>
+    #scroll {
+      width: 200px;
+      height: 200px;
+      overflow: scroll;
+    }
+    #scrolled {
+      width: 200px;
+      height: 1000px; /* so the subframe has room to scroll */
+      z-index: 2;
+      background: linear-gradient(red, blue); /* so you can see it scroll */
+      transform: translateZ(0px); /* to force active layers */ 
+      will-change: transform; /* to force active layers */
+    }
+  </style>
+</head>
+<body>
+  <div id="scroll">
+    <div id="scrolled"></div>
+  </div>
+  <div style="height: 5000px;"></div><!-- So the page is scrollable as well -->
+</body>
+</head>
--- a/gfx/layers/apz/test/mochitest/helper_scroll_on_position_fixed.html
+++ b/gfx/layers/apz/test/mochitest/helper_scroll_on_position_fixed.html
@@ -1,68 +1,51 @@
 <head>
   <meta name="viewport" content="width=device-width; initial-scale=1.0">
   <title>Wheel-scrolling over position:fixed and position:sticky elements, in the top-level document as well as iframes</title>
   <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
   <script type="application/javascript" src="apz_test_utils.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
   <script type="application/javascript">
 
-// Scroll the mouse wheel at (dx, dy) relative to |element|.
-function scrollWheelOver(element, dx, dy, testDriver) {
-  // Move the mouse to the desired wheel location.
-  // Not doing so can result in the wheel events from two consecutive
-  // scrollWheelOver() calls on different elements being incorrectly considered
-  // as part of the same wheel transaction.
-  // We also wait for the mouse move event to be processed before sending the
-  // wheel event, otherwise there is a chance they might get reordered, and
-  // we have the transaction problem again.
-  return synthesizeNativeMouseMoveAndWaitForMoveEvent(element, dx, dy, function() {
-    synthesizeNativeWheelAndWaitForScrollEvent(element, dx, dy, 0, -10, testDriver);
-  });
-}
-
 function* test(testDriver) {
   var iframeWin = document.getElementById('iframe').contentWindow;
 
   // scroll over the middle of the iframe's position:sticky element, check
   // that it scrolls the iframe
   var scrollPos = iframeWin.scrollY;
-  yield scrollWheelOver(iframeWin.document.body, 50, 150, testDriver);
+  yield moveMouseAndScrollWheelOver(iframeWin.document.body, 50, 150, testDriver);
   ok(iframeWin.scrollY > scrollPos, "iframe scrolled after wheeling over the position:sticky element");
 
   // same, but using the iframe's position:fixed element
   scrollPos = iframeWin.scrollY;
-  yield scrollWheelOver(iframeWin.document.body, 250, 150, testDriver);
+  yield moveMouseAndScrollWheelOver(iframeWin.document.body, 250, 150, testDriver);
   ok(iframeWin.scrollY > scrollPos, "iframe scrolled after wheeling over the position:fixed element");
 
   // same, but scrolling the scrollable frame *inside* the position:fixed item
   var fpos = document.getElementById('fpos_scrollable');
   scrollPos = fpos.scrollTop;
-  yield scrollWheelOver(fpos, 50, 150, testDriver);
+  yield moveMouseAndScrollWheelOver(fpos, 50, 150, testDriver);
   ok(fpos.scrollTop > scrollPos, "scrollable item inside fixed-pos element scrolled");
   // wait for it to layerize fully and then try again
   yield waitForAllPaints(function() {
     flushApzRepaints(testDriver);
   });
   scrollPos = fpos.scrollTop;
-  // The mouse is already at the right position. If we call scrollWheelOver it
-  // hangs on windows waiting for the mouse-move, so instead we just synthesize
-  // the wheel directly.
-  yield synthesizeNativeWheelAndWaitForScrollEvent(fpos, 50, 150, 0, -10, testDriver);
+  yield moveMouseAndScrollWheelOver(fpos, 50, 150, testDriver);
   ok(fpos.scrollTop > scrollPos, "scrollable item inside fixed-pos element scrolled after layerization");
 
   // same, but using the top-level window's position:sticky element
   scrollPos = window.scrollY;
-  yield scrollWheelOver(document.body, 50, 150, testDriver);
+  yield moveMouseAndScrollWheelOver(document.body, 50, 150, testDriver);
   ok(window.scrollY > scrollPos, "top-level document scrolled after wheeling over the position:sticky element");
 
   // same, but using the top-level window's position:fixed element
   scrollPos = window.scrollY;
-  yield scrollWheelOver(document.body, 250, 150, testDriver);
+  yield moveMouseAndScrollWheelOver(document.body, 250, 150, testDriver);
   ok(window.scrollY > scrollPos, "top-level document scrolled after wheeling over the position:fixed element");
 }
 
 waitUntilApzStable()
 .then(runContinuation(test))
 .then(subtestDone);
 
   </script>
--- a/gfx/layers/apz/test/mochitest/mochitest.ini
+++ b/gfx/layers/apz/test/mochitest/mochitest.ini
@@ -15,16 +15,18 @@ support-files =
   helper_long_tap.html
   helper_scroll_on_position_fixed.html
   helper_tap_passive.html
   helper_click.html
   helper_drag_click.html
   helper_bug1271432.html
   helper_touch_action.html
   helper_touch_action_regions.html
+  helper_scroll_inactive_perspective.html
+  helper_scroll_inactive_zindex.html
 tags = apz
 [test_bug982141.html]
 [test_bug1151663.html]
 [test_wheel_scroll.html]
 skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
 [test_wheel_transactions.html]
 skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
 [test_bug1151667.html]
--- a/gfx/layers/apz/test/mochitest/test_group_wheelevents.html
+++ b/gfx/layers/apz/test/mochitest/test_group_wheelevents.html
@@ -3,31 +3,32 @@
 <head>
   <meta charset="utf-8">
   <title>Various wheel-scrolling tests that spawn in new windows</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="apz_test_utils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
+var prefs = [
+  // turn off smooth scrolling so that we don't have to wait for
+  // APZ animations to finish before sampling the scroll offset
+  ['general.smoothScroll', false],
+  // ensure that any mouse movement will trigger a new wheel transaction,
+  // because in this test we move the mouse a bunch and want to recalculate
+  // the target APZC after each such movement.
+  ['mousewheel.transaction.ignoremovedelay', 0],
+  ['mousewheel.transaction.timeout', 0]
+]
+
 var subtests = [
-  {'file': 'helper_scroll_on_position_fixed.html', 'prefs': [
-        // turn off smooth scrolling so that we don't have to wait for
-        // APZ animations to finish before sampling the scroll offset
-        ['general.smoothScroll', false],
-        // ensure that any mouse movement will trigger a new wheel transaction,
-        // because in this test we move the mouse a bunch and want to recalculate
-        // the target APZC after each such movement.
-        ['mousewheel.transaction.ignoremovedelay', 0],
-        ['mousewheel.transaction.timeout', 0]]},
-  {'file': 'helper_bug1271432.html', 'prefs': [
-        // same prefs as in the previous test, for the same reasons.
-        ['general.smoothScroll', false],
-        ['mousewheel.transaction.ignoremovedelay', 0],
-        ['mousewheel.transaction.timeout', 0]]}
+  {'file': 'helper_scroll_on_position_fixed.html', 'prefs': prefs},
+  {'file': 'helper_bug1271432.html', 'prefs': prefs},
+  {'file': 'helper_scroll_inactive_perspective.html', 'prefs': prefs},
+  {'file': 'helper_scroll_inactive_zindex.html', 'prefs': prefs}
 ];
 
 if (isApzEnabled()) {
   SimpleTest.waitForExplicitFinish();
   window.onload = function() {
     runSubtestsSeriallyInFreshWindows(subtests)
     .then(SimpleTest.finish);
   };
--- a/gfx/layers/basic/BasicImages.cpp
+++ b/gfx/layers/basic/BasicImages.cpp
@@ -17,19 +17,16 @@
 #include "nsAutoRef.h"                  // for nsCountedRef
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_ERROR, NS_ASSERTION
 #include "nsISupportsImpl.h"            // for Image::Release, etc
 #include "nsThreadUtils.h"              // for NS_IsMainThread
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "gfx2DGlue.h"
 #include "YCbCrUtils.h"                 // for YCbCr conversions
-#ifdef XP_MACOSX
-#include "gfxQuartzImageSurface.h"
-#endif
 
 namespace mozilla {
 namespace layers {
 
 class BasicPlanarYCbCrImage : public RecyclingPlanarYCbCrImage
 {
 public:
   BasicPlanarYCbCrImage(const gfx::IntSize& aScaleHint, gfxImageFormat aOffscreenFormat, BufferRecycleBin *aRecycleBin)
--- a/gfx/layers/basic/GrallocTextureHostBasic.cpp
+++ b/gfx/layers/basic/GrallocTextureHostBasic.cpp
@@ -187,16 +187,22 @@ GrallocTextureHostBasic::SetCompositor(C
   }
 
   mCompositor = compositor;
   if (mTextureSource) {
     mTextureSource->SetCompositor(compositor);
   }
 }
 
+Compositor*
+GrallocTextureHostBasic::GetCompositor()
+{
+  return mCompositor;
+}
+
 gfx::SurfaceFormat
 GrallocTextureHostBasic::GetFormat() const {
   return mFormat;
 }
 
 void
 GrallocTextureHostBasic::WaitAcquireFenceHandleSyncComplete()
 {
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -126,18 +126,20 @@ CompositorOGL::CreateContext()
 #endif
 
   // Allow to create offscreen GL context for main Layer Manager
   if (!context && gfxEnv::LayersPreferOffscreen()) {
     SurfaceCaps caps = SurfaceCaps::ForRGB();
     caps.preserve = false;
     caps.bpp16 = gfxPlatform::GetPlatform()->GetOffscreenFormat() == SurfaceFormat::R5G6B5_UINT16;
 
+    nsCString discardFailureId;
     context = GLContextProvider::CreateOffscreen(mSurfaceSize,
-                                                 caps, CreateContextFlags::REQUIRE_COMPAT_PROFILE);
+                                                 caps, CreateContextFlags::REQUIRE_COMPAT_PROFILE,
+                                                 discardFailureId);
   }
 
   if (!context) {
     context = gl::GLContextProvider::CreateForWindow(mWidget->RealWidget(),
                 gfxPlatform::GetPlatform()->RequiresAcceleratedGLContextForCompositorOGL());
   }
 
   if (!context) {
--- a/gfx/skia/generate_mozbuild.py
+++ b/gfx/skia/generate_mozbuild.py
@@ -68,17 +68,19 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in {
     'gonk',
     'qt',
   }:
     DEFINES['SK_FONTHOST_DOES_NOT_USE_FONTMGR'] = 1
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     DEFINES['UNICODE'] = True
     DEFINES['_UNICODE'] = True
-    DEFINES['SK_FONT_HOST_USE_SYSTEM_SETTINGS'] = 1
+    # These are the usual dwrite default rendering param values
+    DEFINES['SK_GAMMA_EXPONENT'] = 1.8
+    DEFINES['SK_GAMMA_CONTRAST'] = 0.5
     UNIFIED_SOURCES += [
         'skia/src/fonts/SkFontMgr_indirect.cpp',
         'skia/src/fonts/SkRemotableFontMgr.cpp',
     ]
 
 # We should autogenerate these SSE related flags.
 
 if CONFIG['_MSC_VER']:
--- a/gfx/skia/moz.build
+++ b/gfx/skia/moz.build
@@ -604,17 +604,19 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in {
     'gonk',
     'qt',
   }:
     DEFINES['SK_FONTHOST_DOES_NOT_USE_FONTMGR'] = 1
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     DEFINES['UNICODE'] = True
     DEFINES['_UNICODE'] = True
-    DEFINES['SK_FONT_HOST_USE_SYSTEM_SETTINGS'] = 1
+    # These are the usual dwrite default rendering param values
+    DEFINES['SK_GAMMA_EXPONENT'] = 1.8
+    DEFINES['SK_GAMMA_CONTRAST'] = 0.5
     UNIFIED_SOURCES += [
         'skia/src/fonts/SkFontMgr_indirect.cpp',
         'skia/src/fonts/SkRemotableFontMgr.cpp',
     ]
 
 # We should autogenerate these SSE related flags.
 
 if CONFIG['_MSC_VER']:
--- a/gfx/tests/gtest/TestCompositor.cpp
+++ b/gfx/tests/gtest/TestCompositor.cpp
@@ -43,19 +43,21 @@ public:
     return GetClientBounds(aRect);
   }
 
   void* GetNativeData(uint32_t aDataType) override {
     if (aDataType == NS_NATIVE_OPENGL_CONTEXT) {
       mozilla::gl::SurfaceCaps caps = mozilla::gl::SurfaceCaps::ForRGB();
       caps.preserve = false;
       caps.bpp16 = false;
+      nsCString discardFailureId;
       RefPtr<GLContext> context = GLContextProvider::CreateOffscreen(
         IntSize(gCompWidth, gCompHeight), caps,
-        CreateContextFlags::REQUIRE_COMPAT_PROFILE);
+        CreateContextFlags::REQUIRE_COMPAT_PROFILE,
+        discardFailureId);
       return context.forget().take();
     }
     return nullptr;
   }
 
   NS_IMETHOD              Create(nsIWidget* aParent,
                                  nsNativeWidget aNativeParent,
                                  const LayoutDeviceIntRect& aRect,
--- a/gfx/thebes/SoftwareVsyncSource.cpp
+++ b/gfx/thebes/SoftwareVsyncSource.cpp
@@ -15,17 +15,16 @@ SoftwareVsyncSource::SoftwareVsyncSource
 {
   MOZ_ASSERT(NS_IsMainThread());
   mGlobalDisplay = new SoftwareDisplay();
 }
 
 SoftwareVsyncSource::~SoftwareVsyncSource()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  mGlobalDisplay->Shutdown();
   mGlobalDisplay = nullptr;
 }
 
 SoftwareDisplay::SoftwareDisplay()
   : mVsyncEnabled(false)
 {
   // Mimic 60 fps
   MOZ_ASSERT(NS_IsMainThread());
--- a/gfx/thebes/SoftwareVsyncSource.h
+++ b/gfx/thebes/SoftwareVsyncSource.h
@@ -22,17 +22,17 @@ public:
   SoftwareDisplay();
   virtual void EnableVsync() override;
   virtual void DisableVsync() override;
   virtual bool IsVsyncEnabled() override;
   bool IsInSoftwareVsyncThread();
   virtual void NotifyVsync(mozilla::TimeStamp aVsyncTimestamp) override;
   virtual mozilla::TimeDuration GetVsyncRate() override;
   void ScheduleNextVsync(mozilla::TimeStamp aVsyncTimestamp);
-  void Shutdown();
+  void Shutdown() override;
 
 protected:
   ~SoftwareDisplay();
 
 private:
   mozilla::TimeDuration mVsyncRate;
   // Use a chromium thread because nsITimers* fire on the main thread
   base::Thread* mVsyncThread;
--- a/gfx/thebes/VsyncSource.cpp
+++ b/gfx/thebes/VsyncSource.cpp
@@ -138,10 +138,16 @@ VsyncSource::Display::UpdateVsyncStatus(
 }
 
 RefPtr<RefreshTimerVsyncDispatcher>
 VsyncSource::Display::GetRefreshTimerVsyncDispatcher()
 {
   return mRefreshTimerVsyncDispatcher;
 }
 
+void
+VsyncSource::Shutdown()
+{
+  GetGlobalDisplay().Shutdown();
+}
+
 } //namespace gfx
 } //namespace mozilla
--- a/gfx/thebes/VsyncSource.h
+++ b/gfx/thebes/VsyncSource.h
@@ -51,31 +51,33 @@ public:
       void RemoveCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
       void NotifyRefreshTimerVsyncStatus(bool aEnable);
       virtual TimeDuration GetVsyncRate();
 
       // These should all only be called on the main thread
       virtual void EnableVsync() = 0;
       virtual void DisableVsync() = 0;
       virtual bool IsVsyncEnabled() = 0;
+      virtual void Shutdown() = 0;
 
     private:
       void UpdateVsyncStatus();
 
       Mutex mDispatcherLock;
       bool mRefreshTimerNeedsVsync;
       nsTArray<RefPtr<CompositorVsyncDispatcher>> mCompositorVsyncDispatchers;
       RefPtr<RefreshTimerVsyncDispatcher> mRefreshTimerVsyncDispatcher;
   };
 
   void AddCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
   void RemoveCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
 
   RefPtr<RefreshTimerVsyncDispatcher> GetRefreshTimerVsyncDispatcher();
   virtual Display& GetGlobalDisplay() = 0; // Works across all displays
+  void Shutdown();
 
 protected:
   virtual ~VsyncSource() {}
 };
 
 } // namespace gfx
 } // namespace mozilla
 
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -30,17 +30,16 @@
 #endif
 
 #ifdef MOZ_X11
 #include "gfxXlibSurface.h"
 #endif
 
 #ifdef CAIRO_HAS_QUARTZ_SURFACE
 #include "gfxQuartzSurface.h"
-#include "gfxQuartzImageSurface.h"
 #endif
 
 #if defined(CAIRO_HAS_QT_SURFACE) && defined(MOZ_WIDGET_QT)
 #include "gfxQPainterSurface.h"
 #endif
 
 #include <stdio.h>
 #include <limits.h>
@@ -181,19 +180,16 @@ gfxASurface::Wrap (cairo_surface_t *csur
     else if (stype == CAIRO_SURFACE_TYPE_XLIB) {
         result = new gfxXlibSurface(csurf);
     }
 #endif
 #ifdef CAIRO_HAS_QUARTZ_SURFACE
     else if (stype == CAIRO_SURFACE_TYPE_QUARTZ) {
         result = new gfxQuartzSurface(csurf, aSize);
     }
-    else if (stype == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) {
-        result = new gfxQuartzImageSurface(csurf);
-    }
 #endif
 #if defined(CAIRO_HAS_QT_SURFACE) && defined(MOZ_WIDGET_QT)
     else if (stype == CAIRO_SURFACE_TYPE_QT) {
         result = new gfxQPainterSurface(csurf);
     }
 #endif
     else {
         result = new gfxUnknownSurface(csurf, aSize);
--- a/gfx/thebes/gfxAndroidPlatform.cpp
+++ b/gfx/thebes/gfxAndroidPlatform.cpp
@@ -378,16 +378,22 @@ public:
   {
   public:
     GonkDisplay() : mVsyncEnabled(false)
     {
     }
 
     ~GonkDisplay()
     {
+      MOZ_ASSERT(NS_IsMainThread());
+    }
+
+    virtual void Shutdown() override
+    {
+      MOZ_ASSERT(NS_IsMainThread());
       DisableVsync();
     }
 
     virtual void EnableVsync() override
     {
       MOZ_ASSERT(NS_IsMainThread());
       if (IsVsyncEnabled()) {
         return;
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -780,16 +780,18 @@ gfxPlatform::Init()
     }
 }
 
 static bool sLayersIPCIsUp = false;
 
 void
 gfxPlatform::Shutdown()
 {
+    // In some cases, gPlatform may not be created but Shutdown() called,
+    // e.g., during xpcshell tests.
     if (!gPlatform) {
       return;
     }
 
     MOZ_ASSERT(!sLayersIPCIsUp);
 
     // These may be called before the corresponding subsystems have actually
     // started up. That's OK, they can handle it.
@@ -799,39 +801,40 @@ gfxPlatform::Shutdown()
     gfxAlphaBoxBlur::ShutdownBlurCache();
     gfxGraphiteShaper::Shutdown();
     gfxPlatformFontList::Shutdown();
     ShutdownTileCache();
 
     // Free the various non-null transforms and loaded profiles
     ShutdownCMS();
 
-    // In some cases, gPlatform may not be created but Shutdown() called,
-    // e.g., during xpcshell tests.
-    if (gPlatform) {
-        /* Unregister our CMS Override callback. */
-        NS_ASSERTION(gPlatform->mSRGBOverrideObserver, "mSRGBOverrideObserver has alreay gone");
-        Preferences::RemoveObserver(gPlatform->mSRGBOverrideObserver, GFX_PREF_CMS_FORCE_SRGB);
-        gPlatform->mSRGBOverrideObserver = nullptr;
+    /* Unregister our CMS Override callback. */
+    NS_ASSERTION(gPlatform->mSRGBOverrideObserver, "mSRGBOverrideObserver has alreay gone");
+    Preferences::RemoveObserver(gPlatform->mSRGBOverrideObserver, GFX_PREF_CMS_FORCE_SRGB);
+    gPlatform->mSRGBOverrideObserver = nullptr;
+
+    NS_ASSERTION(gPlatform->mFontPrefsObserver, "mFontPrefsObserver has alreay gone");
+    Preferences::RemoveObservers(gPlatform->mFontPrefsObserver, kObservedPrefs);
+    gPlatform->mFontPrefsObserver = nullptr;
 
-        NS_ASSERTION(gPlatform->mFontPrefsObserver, "mFontPrefsObserver has alreay gone");
-        Preferences::RemoveObservers(gPlatform->mFontPrefsObserver, kObservedPrefs);
-        gPlatform->mFontPrefsObserver = nullptr;
+    NS_ASSERTION(gPlatform->mMemoryPressureObserver, "mMemoryPressureObserver has already gone");
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    if (obs) {
+        obs->RemoveObserver(gPlatform->mMemoryPressureObserver, "memory-pressure");
+    }
 
-        NS_ASSERTION(gPlatform->mMemoryPressureObserver, "mMemoryPressureObserver has already gone");
-        nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-        if (obs) {
-            obs->RemoveObserver(gPlatform->mMemoryPressureObserver, "memory-pressure");
-        }
+    gPlatform->mMemoryPressureObserver = nullptr;
+    gPlatform->mSkiaGlue = nullptr;
 
-        gPlatform->mMemoryPressureObserver = nullptr;
-        gPlatform->mSkiaGlue = nullptr;
-        gPlatform->mVsyncSource = nullptr;
+    if (XRE_IsParentProcess()) {
+      gPlatform->mVsyncSource->Shutdown();
     }
 
+    gPlatform->mVsyncSource = nullptr;
+
 #ifdef MOZ_WIDGET_ANDROID
     // Shut down the texture pool
     TexturePoolOGL::Shutdown();
 #endif
 
     // Shut down the default GL context provider.
     GLContextProvider::Shutdown();
 
@@ -941,28 +944,25 @@ gfxPlatform::~gfxPlatform()
 #endif
 
 #if MOZ_TREE_CAIRO
     cairo_debug_reset_static_data();
 #endif
 #endif
 }
 
-cairo_user_data_key_t kDrawTarget;
-
 already_AddRefed<DrawTarget>
 gfxPlatform::CreateDrawTargetForSurface(gfxASurface *aSurface, const IntSize& aSize)
 {
   SurfaceFormat format = aSurface->GetSurfaceFormat();
   RefPtr<DrawTarget> drawTarget = Factory::CreateDrawTargetForCairoSurface(aSurface->CairoSurface(), aSize, &format);
   if (!drawTarget) {
     gfxWarning() << "gfxPlatform::CreateDrawTargetForSurface failed in CreateDrawTargetForCairoSurface";
     return nullptr;
   }
-  aSurface->SetData(&kDrawTarget, drawTarget, nullptr);
   return drawTarget.forget();
 }
 
 cairo_user_data_key_t kSourceSurface;
 
 /**
  * Record the backend that was used to construct the SourceSurface.
  * When getting the cached SourceSurface for a gfxASurface/DrawTarget pair,
@@ -1316,18 +1316,20 @@ gfxPlatform::GetSkiaGLGlue()
 
   if (!mSkiaGlue) {
     /* Dummy context. We always draw into a FBO.
      *
      * FIXME: This should be stored in TLS or something, since there needs to be one for each thread using it. As it
      * stands, this only works on the main thread.
      */
     RefPtr<GLContext> glContext;
+    nsCString discardFailureId;
     glContext = GLContextProvider::CreateHeadless(CreateContextFlags::REQUIRE_COMPAT_PROFILE |
-                                                  CreateContextFlags::ALLOW_OFFLINE_RENDERER);
+                                                  CreateContextFlags::ALLOW_OFFLINE_RENDERER,
+                                                  discardFailureId);
     if (!glContext) {
       printf_stderr("Failed to create GLContext for SkiaGL!\n");
       return nullptr;
     }
     mSkiaGlue = new SkiaGLGlue(glContext);
     MOZ_ASSERT(mSkiaGlue->GetGrContext(), "No GrContext");
     InitializeSkiaCacheLimits();
   }
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -63,18 +63,16 @@ BackendTypeBit(BackendType b)
 
 #define MOZ_PERFORMANCE_WARNING(module, ...) \
   do { \
     if (gfxPlatform::PerfWarnings()) { \
       printf_stderr("[" module "] " __VA_ARGS__); \
     } \
   } while (0)
 
-extern cairo_user_data_key_t kDrawTarget;
-
 enum eCMSMode {
     eCMSMode_Off          = 0,     // No color management
     eCMSMode_All          = 1,     // Color manage everything
     eCMSMode_TaggedOnly   = 2,     // Color manage tagged Images Only
     eCMSMode_AllCount     = 3
 };
 
 enum eGfxLog {
--- a/gfx/thebes/gfxPlatformMac.cpp
+++ b/gfx/thebes/gfxPlatformMac.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * 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 "gfxPlatformMac.h"
 
 #include "gfxQuartzSurface.h"
-#include "gfxQuartzImageSurface.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/MacIOSurface.h"
 
 #include "gfxMacPlatformFontList.h"
 #include "gfxMacFont.h"
 #include "gfxCoreTextShaper.h"
 #include "gfxTextRun.h"
 #include "gfxUserFontSet.h"
@@ -412,19 +411,16 @@ public:
     {
       MOZ_ASSERT(NS_IsMainThread());
       mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
     }
 
     ~OSXDisplay()
     {
       MOZ_ASSERT(NS_IsMainThread());
-      mTimer->Cancel();
-      mTimer = nullptr;
-      DisableVsync();
     }
 
     static void RetryEnableVsync(nsITimer* aTimer, void* aOsxDisplay)
     {
       MOZ_ASSERT(NS_IsMainThread());
       OSXDisplay* osxDisplay = static_cast<OSXDisplay*>(aOsxDisplay);
       MOZ_ASSERT(osxDisplay);
       osxDisplay->EnableVsync();
@@ -510,16 +506,24 @@ public:
       return mDisplayLink != nullptr;
     }
 
     virtual TimeDuration GetVsyncRate() override
     {
       return mVsyncRate;
     }
 
+    virtual void Shutdown() override
+    {
+      MOZ_ASSERT(NS_IsMainThread());
+      mTimer->Cancel();
+      mTimer = nullptr;
+      DisableVsync();
+    }
+
     // The vsync timestamps given by the CVDisplayLinkCallback are
     // in the future for the NEXT frame. Large parts of Gecko, such
     // as animations assume a timestamp at either now or in the past.
     // Normalize the timestamps given to the VsyncDispatchers to the vsync
     // that just occured, not the vsync that is upcoming.
     TimeStamp mPreviousTimestamp;
 
   private:
deleted file mode 100644
--- a/gfx/thebes/gfxQuartzImageSurface.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * 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 "gfxQuartzImageSurface.h"
-#include "gfxImageSurface.h"
-
-#include "cairo-quartz-image.h"
-
-gfxQuartzImageSurface::gfxQuartzImageSurface(gfxImageSurface *imageSurface)
-{
-    if (imageSurface->CairoSurface() == nullptr)
-        return;
-
-    cairo_surface_t *surf = cairo_quartz_image_surface_create (imageSurface->CairoSurface());
-    Init (surf);
-    mSize = ComputeSize();
-}
-
-gfxQuartzImageSurface::gfxQuartzImageSurface(cairo_surface_t *csurf)
-{
-    Init (csurf, true);
-    mSize = ComputeSize();
-}
-
-gfxQuartzImageSurface::~gfxQuartzImageSurface()
-{
-}
-
-mozilla::gfx::IntSize
-gfxQuartzImageSurface::ComputeSize()
-{
-  if (mSurfaceValid) {
-    cairo_surface_t* isurf = cairo_quartz_image_surface_get_image(mSurface);
-    if (isurf) {
-      return mozilla::gfx::IntSize(cairo_image_surface_get_width(isurf),
-                                   cairo_image_surface_get_height(isurf));
-    }
-  }
-
-  // If we reach here then something went wrong. Just use the same default
-  // value as gfxASurface::GetSize.
-  return mozilla::gfx::IntSize(-1, -1);
-}
-
-int32_t
-gfxQuartzImageSurface::KnownMemoryUsed()
-{
-  // This surface doesn't own any memory itself, but we want to report here the
-  // amount of memory that the surface it wraps uses.
-  RefPtr<gfxImageSurface> imgSurface = GetAsImageSurface();
-  if (imgSurface)
-    return imgSurface->KnownMemoryUsed();
-  return 0;
-}
-
-already_AddRefed<gfxImageSurface>
-gfxQuartzImageSurface::GetAsImageSurface()
-{
-    if (!mSurfaceValid)
-        return nullptr;
-
-    cairo_surface_t *isurf = cairo_quartz_image_surface_get_image (CairoSurface());
-    if (!isurf) {
-        NS_WARNING ("Couldn't obtain an image surface from a QuartzImageSurface?!");
-        return nullptr;
-    }
-
-    RefPtr<gfxImageSurface> result = gfxASurface::Wrap(isurf).downcast<gfxImageSurface>();
-    result->SetOpaqueRect(GetOpaqueRect());
-
-    return result.forget();
-}
deleted file mode 100644
--- a/gfx/thebes/gfxQuartzImageSurface.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef GFX_QUARTZIMAGESURFACE_H
-#define GFX_QUARTZIMAGESURFACE_H
-
-#include "gfxASurface.h"
-#include "nsSize.h"
-
-class gfxImageSurface;
-
-class gfxQuartzImageSurface : public gfxASurface {
-public:
-    explicit gfxQuartzImageSurface(gfxImageSurface *imageSurface);
-    explicit gfxQuartzImageSurface(cairo_surface_t *csurf);
-
-    virtual ~gfxQuartzImageSurface();
-
-    already_AddRefed<gfxImageSurface> GetAsImageSurface();
-    virtual int32_t KnownMemoryUsed();
-    virtual const mozilla::gfx::IntSize GetSize() const { return mSize; }
-
-protected:
-    mozilla::gfx::IntSize mSize;
-
-private:
-    mozilla::gfx::IntSize ComputeSize();
-};
-
-#endif /* GFX_QUARTZIMAGESURFACE_H */
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -2727,16 +2727,24 @@ public:
           float rate = ((float) refreshRate.uiDenominator
                        / (float) refreshRate.uiNumerator) * 1000;
           mVsyncRate = TimeDuration::FromMilliseconds(rate);
         } else {
           mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
         }
       }
 
+      virtual void Shutdown() override
+      {
+        MOZ_ASSERT(NS_IsMainThread());
+        DisableVsync();
+        mVsyncThread->Stop();
+        delete mVsyncThread;
+      }
+
       virtual void EnableVsync() override
       {
         MOZ_ASSERT(NS_IsMainThread());
         MOZ_ASSERT(mVsyncThread->IsRunning());
         { // scope lock
           MonitorAutoLock lock(mVsyncEnabledLock);
           if (mVsyncEnabled) {
             return;
@@ -2889,19 +2897,16 @@ public:
                     TimeStamp::Now();
         } // end for
       }
 
     private:
       virtual ~D3DVsyncDisplay()
       {
         MOZ_ASSERT(NS_IsMainThread());
-        DisableVsync();
-        mVsyncThread->Stop();
-        delete mVsyncThread;
       }
 
       bool IsInVsyncThread()
       {
         return mVsyncThread->thread_id() == PlatformThread::CurrentId();
       }
 
       TimeDuration mSoftwareVsyncRate;
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -80,25 +80,23 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'go
         'gfxFT2FontList.cpp',
         'gfxFT2Fonts.cpp',
         'gfxFT2Utils.cpp',
         'gfxPDFSurface.cpp',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     EXPORTS += [
         'gfxPlatformMac.h',
-        'gfxQuartzImageSurface.h',
         'gfxQuartzNativeDrawing.h',
         'gfxQuartzSurface.h',
     ]
     SOURCES += [
         'gfxCoreTextShaper.cpp',
         'gfxMacFont.cpp',
         'gfxPlatformMac.cpp',
-        'gfxQuartzImageSurface.cpp',
         'gfxQuartzNativeDrawing.cpp',
         'gfxQuartzSurface.cpp',
     ]
 elif 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
     EXPORTS += [
         'gfxFontconfigFonts.h',
         'gfxFT2FontBase.h',
         'gfxGdkNativeRenderer.h',
--- a/image/encoders/bmp/nsBMPEncoder.cpp
+++ b/image/encoders/bmp/nsBMPEncoder.cpp
@@ -122,17 +122,17 @@ nsBMPEncoder::StartImageEncode(uint32_t 
       aInputFormat != INPUT_FORMAT_RGBA &&
       aInputFormat != INPUT_FORMAT_HOSTARGB) {
     return NS_ERROR_INVALID_ARG;
   }
 
   // parse and check any provided output options
   Version version;
   uint32_t bpp;
-  nsresult rv = ParseOptions(aOutputOptions, &version, &bpp);
+  nsresult rv = ParseOptions(aOutputOptions, version, bpp);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   InitFileHeader(version, bpp, aWidth, aHeight);
   InitInfoHeader(version, bpp, aWidth, aHeight);
 
   mImageBufferSize = mBMPFileHeader.filesize;
@@ -251,25 +251,21 @@ nsBMPEncoder::EndImageEncode()
 
   return NS_OK;
 }
 
 
 // Parses the encoder options and sets the bits per pixel to use
 // See InitFromData for a description of the parse options
 nsresult
-nsBMPEncoder::ParseOptions(const nsAString& aOptions, Version* version,
-                           uint32_t* bpp)
+nsBMPEncoder::ParseOptions(const nsAString& aOptions, Version& aVersionOut,
+                           uint32_t& aBppOut)
 {
-  if (version) {
-    *version = VERSION_3;
-  }
-  if (bpp) {
-    *bpp = 24;
-  }
+  aVersionOut = VERSION_3;
+  aBppOut = 24;
 
   // Parse the input string into a set of name/value pairs.
   // From a format like: name=value;bpp=<bpp_value>;name=value
   // to format: [0] = name=value, [1] = bpp=<bpp_value>, [2] = name=value
   nsTArray<nsCString> nameValuePairs;
   if (!ParseString(NS_ConvertUTF16toUTF8(aOptions), ';', nameValuePairs)) {
     return NS_ERROR_INVALID_ARG;
   }
@@ -286,30 +282,30 @@ nsBMPEncoder::ParseOptions(const nsAStri
       return NS_ERROR_INVALID_ARG;
     }
 
     // Parse the bpp portion of the string name=value;version=<version_value>;
     // name=value
     if (nameValuePair[0].Equals("version",
                                 nsCaseInsensitiveCStringComparator())) {
       if (nameValuePair[1].EqualsLiteral("3")) {
-        *version = VERSION_3;
+        aVersionOut = VERSION_3;
       } else if (nameValuePair[1].EqualsLiteral("5")) {
-        *version = VERSION_5;
+        aVersionOut = VERSION_5;
       } else {
         return NS_ERROR_INVALID_ARG;
       }
     }
 
     // Parse the bpp portion of the string name=value;bpp=<bpp_value>;name=value
     if (nameValuePair[0].Equals("bpp", nsCaseInsensitiveCStringComparator())) {
       if (nameValuePair[1].EqualsLiteral("24")) {
-        *bpp = 24;
+        aBppOut = 24;
       } else if (nameValuePair[1].EqualsLiteral("32")) {
-        *bpp = 32;
+        aBppOut = 32;
       } else {
         return NS_ERROR_INVALID_ARG;
       }
     }
   }
 
   return NS_OK;
 }
--- a/image/encoders/bmp/nsBMPEncoder.h
+++ b/image/encoders/bmp/nsBMPEncoder.h
@@ -98,18 +98,18 @@ protected:
 
   enum Version
   {
       VERSION_3 = 3,
       VERSION_5 = 5
   };
 
   // See InitData in the cpp for valid parse options
-  nsresult ParseOptions(const nsAString& aOptions, Version* version,
-                        uint32_t* bpp);
+  nsresult ParseOptions(const nsAString& aOptions, Version& aVersionOut,
+                        uint32_t& aBppOut);
   // Obtains data with no alpha in machine-independent byte order
   void ConvertHostARGBRow(const uint8_t* aSrc,
                           const mozilla::UniquePtr<uint8_t[]>& aDest,
                           uint32_t aPixelWidth);
   // Thread safe notify listener
   void NotifyListener();
 
   // Initializes the bitmap file header member mBMPFileHeader
--- a/image/encoders/ico/nsICOEncoder.cpp
+++ b/image/encoders/ico/nsICOEncoder.cpp
@@ -226,17 +226,17 @@ nsICOEncoder::StartImageEncode(uint32_t 
   // Icons are only 1 byte, so make sure our bitmap is in range
   if (aWidth > 256 || aHeight > 256) {
     return NS_ERROR_INVALID_ARG;
   }
 
   // parse and check any provided output options
   uint32_t bpp = 24;
   bool usePNG = true;
-  nsresult rv = ParseOptions(aOutputOptions, &bpp, &usePNG);
+  nsresult rv = ParseOptions(aOutputOptions, bpp, usePNG);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mUsePNG = usePNG;
 
   InitFileHeader();
   // The width and height are stored as 0 when we have a value of 256
   InitInfoHeader(bpp, aWidth == 256 ? 0 : (uint8_t)aWidth,
                  aHeight == 256 ? 0 : (uint8_t)aHeight);
@@ -261,27 +261,23 @@ nsICOEncoder::EndImageEncode()
   }
 
   return NS_OK;
 }
 
 // Parses the encoder options and sets the bits per pixel to use and PNG or BMP
 // See InitFromData for a description of the parse options
 nsresult
-nsICOEncoder::ParseOptions(const nsAString& aOptions, uint32_t* bpp,
-                           bool* usePNG)
+nsICOEncoder::ParseOptions(const nsAString& aOptions, uint32_t& aBppOut,
+                           bool& aUsePNGOut)
 {
   // If no parsing options just use the default of 24BPP and PNG yes
   if (aOptions.Length() == 0) {
-    if (usePNG) {
-      *usePNG = true;
-    }
-    if (bpp) {
-      *bpp = 24;
-    }
+    aUsePNGOut = true;
+    aBppOut = 24;
   }
 
   // Parse the input string into a set of name/value pairs.
   // From format: format=<png|bmp>;bpp=<bpp_value>
   // to format: [0] = format=<png|bmp>, [1] = bpp=<bpp_value>
   nsTArray<nsCString> nameValuePairs;
   if (!ParseString(NS_ConvertUTF16toUTF8(aOptions), ';', nameValuePairs)) {
     return NS_ERROR_INVALID_ARG;
@@ -299,34 +295,34 @@ nsICOEncoder::ParseOptions(const nsAStri
       return NS_ERROR_INVALID_ARG;
     }
 
     // Parse the format portion of the string format=<png|bmp>;bpp=<bpp_value>
     if (nameValuePair[0].Equals("format",
                                 nsCaseInsensitiveCStringComparator())) {
       if (nameValuePair[1].Equals("png",
                                   nsCaseInsensitiveCStringComparator())) {
-        *usePNG = true;
+        aUsePNGOut = true;
       }
       else if (nameValuePair[1].Equals("bmp",
                                        nsCaseInsensitiveCStringComparator())) {
-        *usePNG = false;
+        aUsePNGOut = false;
       }
       else {
         return NS_ERROR_INVALID_ARG;
       }
     }
 
     // Parse the bpp portion of the string format=<png|bmp>;bpp=<bpp_value>
     if (nameValuePair[0].Equals("bpp", nsCaseInsensitiveCStringComparator())) {
       if (nameValuePair[1].EqualsLiteral("24")) {
-        *bpp = 24;
+        aBppOut = 24;
       }
       else if (nameValuePair[1].EqualsLiteral("32")) {
-        *bpp = 32;
+        aBppOut = 32;
       }
       else {
         return NS_ERROR_INVALID_ARG;
       }
     }
   }
 
   return NS_OK;
--- a/image/encoders/ico/nsICOEncoder.h
+++ b/image/encoders/ico/nsICOEncoder.h
@@ -45,18 +45,18 @@ public:
   uint32_t GetRealHeight() const
   {
     return mICODirEntry.mHeight == 0 ? 256 : mICODirEntry.mHeight;
   }
 
 protected:
   ~nsICOEncoder();
 
-  nsresult ParseOptions(const nsAString& aOptions, uint32_t* bpp,
-                        bool* usePNG);
+  nsresult ParseOptions(const nsAString& aOptions, uint32_t& aBppOut,
+                        bool& aUsePNGOut);
   void NotifyListener();
 
   // Initializes the icon file header mICOFileHeader
   void InitFileHeader();
   // Initializes the icon directory info header mICODirEntry
   void InitInfoHeader(uint32_t aBPP, uint8_t aWidth, uint8_t aHeight);
   // Encodes the icon file header mICOFileHeader
   void EncodeFileHeader();
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -578,25 +578,29 @@ GeckoChildProcessHost::PerformAsyncLaunc
 }
 
 bool
 GeckoChildProcessHost::RunPerformAsyncLaunch(std::vector<std::string> aExtraOpts,
                                              base::ProcessArchitecture aArch)
 {
   InitializeChannel();
 
-  if (PerformAsyncLaunch(aExtraOpts, aArch)) {
-    return true;
-  } else {
+  bool ok = PerformAsyncLaunch(aExtraOpts, aArch);
+  if (!ok) {
+    // WaitUntilConnected might be waiting for us to signal.
+    // If something failed let's set the error state and notify.
+    MonitorAutoLock lock(mMonitor);
+    mProcessState = PROCESS_ERROR;
+    lock.Notify();
     CHROMIUM_LOG(ERROR) << "Failed to launch " <<
       XRE_ChildProcessTypeToString(mProcessType) << " subprocess";
     Telemetry::Accumulate(Telemetry::SUBPROCESS_LAUNCH_FAILURE,
       nsDependentCString(XRE_ChildProcessTypeToString(mProcessType)));
-    return false;
   }
+  return ok;
 }
 
 void
 #if defined(XP_WIN)
 AddAppDirToCommandLine(CommandLine& aCmdLine)
 #else
 AddAppDirToCommandLine(std::vector<std::string>& aCmdLine)
 #endif
@@ -1025,47 +1029,53 @@ GeckoChildProcessHost::PerformAsyncLaunc
   // XXX: Bug 1124167: We should get rid of the process specific logic for
   // sandboxing in this class at some point. Unfortunately it will take a bit
   // of reorganizing so I don't think this patch is the right time.
   switch (mProcessType) {
     case GeckoProcessType_Content:
 #if defined(MOZ_CONTENT_SANDBOX)
       if (mSandboxLevel > 0 &&
           !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
+        // For now we treat every failure as fatal in SetSecurityLevelForContentProcess
+        // and just crash there right away. Should this change in the future then we
+        // should also handle the error here.
         mSandboxBroker.SetSecurityLevelForContentProcess(mSandboxLevel);
         cmdLine.AppendLooseValue(UTF8ToWide("-sandbox"));
         shouldSandboxCurrentProcess = true;
         AddContentSandboxAllowedFiles(mSandboxLevel, mAllowedFilesRead);
       }
 #endif // MOZ_CONTENT_SANDBOX
       break;
     case GeckoProcessType_Plugin:
       if (mSandboxLevel > 0 &&
           !PR_GetEnv("MOZ_DISABLE_NPAPI_SANDBOX")) {
-        mSandboxBroker.SetSecurityLevelForPluginProcess(mSandboxLevel);
+        bool ok = mSandboxBroker.SetSecurityLevelForPluginProcess(mSandboxLevel);
+        if (!ok) {
+          return false;
+        }
         cmdLine.AppendLooseValue(UTF8ToWide("-sandbox"));
         shouldSandboxCurrentProcess = true;
       }
       break;
     case GeckoProcessType_IPDLUnitTest:
       // XXX: We don't sandbox this process type yet
-      // mSandboxBroker.SetSecurityLevelForIPDLUnitTestProcess();
-      // cmdLine.AppendLooseValue(UTF8ToWide("-sandbox"));
-      // shouldSandboxCurrentProcess = true;
       break;
     case GeckoProcessType_GMPlugin:
       if (!PR_GetEnv("MOZ_DISABLE_GMP_SANDBOX")) {
         // The Widevine CDM on Windows can only load at USER_RESTRICTED,
         // not at USER_LOCKDOWN. So look in the command line arguments
         // to see if we're loading the path to the Widevine CDM, and if
         // so use sandbox level USER_RESTRICTED instead of USER_LOCKDOWN.
         bool isWidevine = std::any_of(aExtraOpts.begin(), aExtraOpts.end(),
           [](const std::string arg) { return arg.find("gmp-widevinecdm") != std::string::npos; });
         auto level = isWidevine ? SandboxBroker::Restricted : SandboxBroker::LockDown;
-        mSandboxBroker.SetSecurityLevelForGMPlugin(level);
+        bool ok = mSandboxBroker.SetSecurityLevelForGMPlugin(level);
+        if (!ok) {
+          return false;
+        }
         cmdLine.AppendLooseValue(UTF8ToWide("-sandbox"));
         shouldSandboxCurrentProcess = true;
       }
       break;
     case GeckoProcessType_Default:
     default:
       MOZ_CRASH("Bad process type in GeckoChildProcessHost");
       break;
@@ -1141,19 +1151,16 @@ GeckoChildProcessHost::PerformAsyncLaunc
 #endif
   }
 
 #else
 #  error Sorry
 #endif
 
   if (!process) {
-    MonitorAutoLock lock(mMonitor);
-    mProcessState = PROCESS_ERROR;
-    lock.Notify();
     return false;
   }
   // NB: on OS X, we block much longer than we need to in order to
   // reach this call, waiting for the child process's task_t.  The
   // best way to fix that is to refactor this file, hard.
   SetHandle(process);
 #if defined(MOZ_WIDGET_COCOA)
   mChildTask = child_task;
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -560,17 +560,17 @@ JSVAL_IS_NULL_IMPL(jsval_layout l)
 {
     return l.s.tag == JSVAL_TAG_NULL;
 }
 
 static inline jsval_layout
 PRIVATE_PTR_TO_JSVAL_IMPL(void* ptr)
 {
     jsval_layout l;
-    MOZ_ASSERT(((uint32_t)ptr & 1) == 0);
+    MOZ_ASSERT((uintptr_t(ptr) & 1) == 0);
     l.s.tag = (JSValueTag)0;
     l.s.payload.ptr = ptr;
     MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(l));
     return l;
 }
 
 static inline void*
 JSVAL_TO_PRIVATE_PTR_IMPL(jsval_layout l)
@@ -876,17 +876,17 @@ JSVAL_TRACE_KIND_IMPL(jsval_layout l)
         return (uint32_t)JS::GCThingTraceKind(JSVAL_TO_GCTHING_IMPL(l));
     return (uint32_t)(l.asBits >> JSVAL_TAG_SHIFT) & 0x03;
 }
 
 static inline jsval_layout
 PRIVATE_PTR_TO_JSVAL_IMPL(void* ptr)
 {
     jsval_layout l;
-    uint64_t ptrBits = (uint64_t)ptr;
+    uintptr_t ptrBits = uintptr_t(ptr);
     MOZ_ASSERT((ptrBits & 1) == 0);
     l.asBits = ptrBits >> 1;
     MOZ_ASSERT(JSVAL_IS_DOUBLE_IMPL(l));
     return l;
 }
 
 static inline void*
 JSVAL_TO_PRIVATE_PTR_IMPL(jsval_layout l)
--- a/js/src/Y.js
+++ b/js/src/Y.js
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-// The Y combinator, applied to the factorial function
+// The Y combinator, applied to the factorial function.
 
 // Return the function that is the fixed point of f.
-var Y = f => (x => f(v => x(x)(v)))
-             (x => f(v => x(x)(v)));
+const Y = f => (x => f(v => x(x)(v)))
+               (x => f(v => x(x)(v)));
 
 // The factorial function is the fixed point of this:
-var f = fac => n => (n <= 1) ? 1 : n * fac(n - 1);
+const f = fac => n => (n <= 1) ? 1 : n * fac(n - 1);
 
-print("5! is " + Y(f)(5));
+print(`5! is ${Y(f)(5)}`);
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -291,150 +291,160 @@ class AsmJSExport
     }
     uint32_t endOffsetInModule() const {
         return endOffsetInModule_;
     }
 };
 
 typedef Vector<AsmJSExport, 0, SystemAllocPolicy> AsmJSExportVector;
 
-// Holds the trivially-memcpy()able, serializable portion of AsmJSModuleData.
-struct AsmJSModuleCacheablePod
+enum class CacheResult
+{
+    Hit,
+    Miss
+};
+
+// Holds the immutable guts of an AsmJSModule.
+//
+// AsmJSMetadata is built incrementally by ModuleValidator and then shared
+// immutably between AsmJSModules.
+
+struct AsmJSMetadataCacheablePod
 {
     uint32_t                minHeapLength;
     uint32_t                numFFIs;
     uint32_t                srcLength;
     uint32_t                srcLengthWithRightBrace;
+
+    AsmJSMetadataCacheablePod() { PodZero(this); }
 };
 
-// Holds the immutable guts of an AsmJSModule. This struct is mutably built up
-// by ModuleValidator and then handed over to the AsmJSModule constructor in
-// finish().
-struct AsmJSModuleData : AsmJSModuleCacheablePod
+struct AsmJSMetadata : RefCounted<AsmJSMetadata>, AsmJSMetadataCacheablePod
 {
     AsmJSGlobalVector       globals;
     AsmJSImportVector       imports;
     AsmJSExportVector       exports;
     PropertyName*           globalArgumentName;
     PropertyName*           importArgumentName;
     PropertyName*           bufferArgumentName;
 
+    CacheResult             cacheResult;
+
     // These values are not serialized since they are relative to the
     // containing script which can be different between serialization and
     // deserialization contexts. Thus, they must be set explicitly using the
     // ambient Parser/ScriptSource after deserialization. Cloning, however,
     // preserves the same exact parsing context and can copy these values.
     uint32_t                srcStart;
     uint32_t                srcBodyStart;
     bool                    strict;
     ScriptSourceHolder      scriptSource;
 
-    AsmJSModuleData()
+    AsmJSMetadata()
       : globalArgumentName(nullptr),
         importArgumentName(nullptr),
         bufferArgumentName(nullptr),
+        cacheResult(CacheResult::Miss),
         srcStart(0),
         srcBodyStart(0),
         strict(false)
-    {
-        PodZero(&pod());
-    }
-
-    AsmJSModuleCacheablePod& pod() { return *this; }
-    const AsmJSModuleCacheablePod& pod() const { return *this; }
+    {}
+
+    AsmJSMetadataCacheablePod& pod() { return *this; }
+    const AsmJSMetadataCacheablePod& pod() const { return *this; }
 
     void trace(JSTracer* trc) const {
         for (const AsmJSGlobal& global : globals)
             global.trace(trc);
         TraceNameField(trc, &globalArgumentName, "asm.js global argument name");
         TraceNameField(trc, &importArgumentName, "asm.js import argument name");
         TraceNameField(trc, &bufferArgumentName, "asm.js buffer argument name");
     }
 
-    WASM_DECLARE_SERIALIZABLE(AsmJSModuleData)
+    WASM_DECLARE_SERIALIZABLE(AsmJSMetadata)
 };
 
-typedef UniquePtr<AsmJSModuleData> UniqueAsmJSModuleData;
+typedef RefPtr<AsmJSMetadata> MutableAsmJSMetadata;
+typedef RefPtr<const AsmJSMetadata> SharedAsmJSMetadata;
 
 // An AsmJSModule is-a Module with the extra persistent state necessary to
 // represent a compiled asm.js module.
 class js::AsmJSModule final : public Module
 {
-    typedef UniquePtr<const AsmJSModuleData> UniqueConstAsmJSModuleData;
-    typedef UniquePtr<const StaticLinkData> UniqueConstStaticLinkData;
-
-    const UniqueConstStaticLinkData  link_;
-    const UniqueExportMap            exportMap_;
-    const UniqueConstAsmJSModuleData module_;
+    const SharedStaticLinkData staticLinkData_;
+    const SharedExportMap      exportMap_;
+    const SharedAsmJSMetadata  asmJSMetadata_;
 
   public:
-    AsmJSModule(UniqueModuleData base,
-                UniqueStaticLinkData link,
-                UniqueExportMap exportMap,
-                UniqueAsmJSModuleData module)
-      : Module(Move(base)),
-        link_(Move(link)),
-        exportMap_(Move(exportMap)),
-        module_(Move(module))
+    AsmJSModule(UniqueCodeSegment code,
+                const Metadata& metadata,
+                const StaticLinkData& staticLinkData,
+                const ExportMap& exportMap,
+                const AsmJSMetadata& asmJSMetadata)
+      : Module(Move(code), metadata),
+        staticLinkData_(&staticLinkData),
+        exportMap_(&exportMap),
+        asmJSMetadata_(&asmJSMetadata)
     {}
 
     virtual void trace(JSTracer* trc) override {
         Module::trace(trc);
-        module_->trace(trc);
+        asmJSMetadata_->trace(trc);
     }
     virtual void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data) override {
         Module::addSizeOfMisc(mallocSizeOf, code, data);
-        *data += mallocSizeOf(link_.get()) + link_->sizeOfExcludingThis(mallocSizeOf);
+        *data += mallocSizeOf(staticLinkData_.get()) + staticLinkData_->sizeOfExcludingThis(mallocSizeOf);
         *data += mallocSizeOf(exportMap_.get()) + exportMap_->sizeOfExcludingThis(mallocSizeOf);
-        *data += mallocSizeOf(module_.get()) + module_->sizeOfExcludingThis(mallocSizeOf);
+        *data += mallocSizeOf(asmJSMetadata_.get()) + asmJSMetadata_->sizeOfExcludingThis(mallocSizeOf);
     }
     virtual bool mutedErrors() const override {
         return scriptSource()->mutedErrors();
     }
     virtual const char16_t* displayURL() const override {
         return scriptSource()->hasDisplayURL() ? scriptSource()->displayURL() : nullptr;
     }
     virtual ScriptSource* maybeScriptSource() const override {
         return scriptSource();
     }
 
-    uint32_t minHeapLength() const { return module_->minHeapLength; }
-    uint32_t numFFIs() const { return module_->numFFIs; }
-    bool strict() const { return module_->strict; }
-    ScriptSource* scriptSource() const { return module_->scriptSource.get(); }
-    const AsmJSGlobalVector& asmJSGlobals() const { return module_->globals; }
-    const AsmJSImportVector& asmJSImports() const { return module_->imports; }
-    const AsmJSExportVector& asmJSExports() const { return module_->exports; }
-    PropertyName* globalArgumentName() const { return module_->globalArgumentName; }
-    PropertyName* importArgumentName() const { return module_->importArgumentName; }
-    PropertyName* bufferArgumentName() const { return module_->bufferArgumentName; }
+    uint32_t minHeapLength() const { return asmJSMetadata_->minHeapLength; }
+    uint32_t numFFIs() const { return asmJSMetadata_->numFFIs; }
+    bool strict() const { return asmJSMetadata_->strict; }
+    ScriptSource* scriptSource() const { return asmJSMetadata_->scriptSource.get(); }
+    const AsmJSGlobalVector& asmJSGlobals() const { return asmJSMetadata_->globals; }
+    const AsmJSImportVector& asmJSImports() const { return asmJSMetadata_->imports; }
+    const AsmJSExportVector& asmJSExports() const { return asmJSMetadata_->exports; }
+    PropertyName* globalArgumentName() const { return asmJSMetadata_->globalArgumentName; }
+    PropertyName* importArgumentName() const { return asmJSMetadata_->importArgumentName; }
+    PropertyName* bufferArgumentName() const { return asmJSMetadata_->bufferArgumentName; }
+    bool loadedFromCache() const { return asmJSMetadata_->cacheResult == CacheResult::Hit; }
 
     // srcStart() refers to the offset in the ScriptSource to the beginning of
     // the asm.js module function. If the function has been created with the
     // Function constructor, this will be the first character in the function
     // source. Otherwise, it will be the opening parenthesis of the arguments
     // list.
     uint32_t srcStart() const {
-        return module_->srcStart;
+        return asmJSMetadata_->srcStart;
     }
     uint32_t srcEndBeforeCurly() const {
-        return module_->srcStart + module_->srcLength;
+        return asmJSMetadata_->srcStart + asmJSMetadata_->srcLength;
     }
     uint32_t srcEndAfterCurly() const {
-        return module_->srcStart + module_->srcLengthWithRightBrace;
+        return asmJSMetadata_->srcStart + asmJSMetadata_->srcLengthWithRightBrace;
     }
 
     // srcBodyStart() refers to the offset in the ScriptSource to the end
     // of the 'use asm' string-literal token.
     uint32_t srcBodyStart() const {
-        return module_->srcBodyStart;
+        return asmJSMetadata_->srcBodyStart;
     }
 
     bool staticallyLink(ExclusiveContext* cx) {
-        return Module::staticallyLink(cx, *link_);
+        return Module::staticallyLink(cx, *staticLinkData_);
     }
     bool dynamicallyLink(JSContext* cx,
                          Handle<WasmModuleObject*> moduleObj,
                          Handle<ArrayBufferObjectMaybeShared*> heap,
                          Handle<FunctionVector> imports,
                          MutableHandleObject exportObj) {
         return Module::dynamicallyLink(cx, moduleObj, heap, imports, *exportMap_, exportObj);
     }
@@ -1680,17 +1690,17 @@ class MOZ_STACK_CLASS ModuleValidator
     GlobalMap             globalMap_;
     SigMap                sigMap_;
     ImportMap             importMap_;
     ArrayViewVector       arrayViews_;
     bool                  atomicsPresent_;
 
     // State used to build the AsmJSModule in finish():
     ModuleGenerator       mg_;
-    UniqueAsmJSModuleData module_;
+    MutableAsmJSMetadata  asmJSMetadata_;
 
     // Error reporting:
     UniqueChars           errorString_;
     uint32_t              errorOffset_;
     bool                  errorOverRecursed_;
 
     // Helpers:
     bool addStandardLibraryMathName(const char* name, AsmJSMathBuiltinFunction func) {
@@ -1771,25 +1781,25 @@ class MOZ_STACK_CLASS ModuleValidator
                                            JSMSG_USE_ASM_TYPE_FAIL,
                                            errorString_.get());
         }
         if (errorOverRecursed_)
             ReportOverRecursed(cx_);
     }
 
     bool init() {
-        module_ = cx_->make_unique<AsmJSModuleData>();
-        if (!module_)
-            return false;
-
-        module_->minHeapLength = RoundUpToNextValidAsmJSHeapLength(0);
-        module_->srcStart = moduleFunctionNode_->pn_body->pn_pos.begin;
-        module_->srcBodyStart = parser_.tokenStream.currentToken().pos.end;
-        module_->strict = parser_.pc->sc->strict() && !parser_.pc->sc->hasExplicitUseStrict();
-        module_->scriptSource.reset(parser_.ss);
+        asmJSMetadata_ = cx_->new_<AsmJSMetadata>();
+        if (!asmJSMetadata_)
+            return false;
+
+        asmJSMetadata_->minHeapLength = RoundUpToNextValidAsmJSHeapLength(0);
+        asmJSMetadata_->srcStart = moduleFunctionNode_->pn_body->pn_pos.begin;
+        asmJSMetadata_->srcBodyStart = parser_.tokenStream.currentToken().pos.end;
+        asmJSMetadata_->strict = parser_.pc->sc->strict() && !parser_.pc->sc->hasExplicitUseStrict();
+        asmJSMetadata_->scriptSource.reset(parser_.ss);
 
         if (!globalMap_.init() || !sigMap_.init() || !importMap_.init())
             return false;
 
         if (!standardLibraryMathNames_.init() ||
             !addStandardLibraryMathName("sin", AsmJSMathBuiltin_sin) ||
             !addStandardLibraryMathName("cos", AsmJSMathBuiltin_cos) ||
             !addStandardLibraryMathName("tan", AsmJSMathBuiltin_tan) ||
@@ -1866,49 +1876,49 @@ class MOZ_STACK_CLASS ModuleValidator
             filename = DuplicateString(parser_.ss->filename());
             if (!filename)
                 return false;
         }
 
         if (!mg_.init(Move(genData), Move(filename)))
             return false;
 
-        mg_.bumpMinHeapLength(module_->minHeapLength);
+        mg_.bumpMinHeapLength(asmJSMetadata_->minHeapLength);
 
         return true;
     }
 
     ExclusiveContext* cx() const             { return cx_; }
     PropertyName* moduleFunctionName() const { return moduleFunctionName_; }
-    PropertyName* globalArgumentName() const { return module_->globalArgumentName; }
-    PropertyName* importArgumentName() const { return module_->importArgumentName; }
-    PropertyName* bufferArgumentName() const { return module_->bufferArgumentName; }
+    PropertyName* globalArgumentName() const { return asmJSMetadata_->globalArgumentName; }
+    PropertyName* importArgumentName() const { return asmJSMetadata_->importArgumentName; }
+    PropertyName* bufferArgumentName() const { return asmJSMetadata_->bufferArgumentName; }
     ModuleGenerator& mg()                    { return mg_; }
     AsmJSParser& parser() const              { return parser_; }
     TokenStream& tokenStream() const         { return parser_.tokenStream; }
     RootedFunction& dummyFunction()          { return dummyFunction_; }
     bool supportsSimd() const                { return cx_->jitSupportsSimd(); }
     bool atomicsPresent() const              { return atomicsPresent_; }
-    uint32_t minHeapLength() const           { return module_->minHeapLength; }
+    uint32_t minHeapLength() const           { return asmJSMetadata_->minHeapLength; }
 
     void initModuleFunctionName(PropertyName* name) {
         MOZ_ASSERT(!moduleFunctionName_);
         moduleFunctionName_ = name;
     }
     void initGlobalArgumentName(PropertyName* n) {
         MOZ_ASSERT(n->isTenured());
-        module_->globalArgumentName = n;
+        asmJSMetadata_->globalArgumentName = n;
     }
     void initImportArgumentName(PropertyName* n) {
         MOZ_ASSERT(n->isTenured());
-        module_->importArgumentName = n;
+        asmJSMetadata_->importArgumentName = n;
     }
     void initBufferArgumentName(PropertyName* n) {
         MOZ_ASSERT(n->isTenured());
-        module_->bufferArgumentName = n;
+        asmJSMetadata_->bufferArgumentName = n;
     }
     bool addGlobalVarInit(PropertyName* var, const NumLit& lit, Type type, bool isConst)
     {
         MOZ_ASSERT(type.isGlobalVarType());
         MOZ_ASSERT(type == Type::canonicalize(Type::lit(lit)));
 
         uint32_t index;
         if (!mg_.allocateGlobal(type.canonicalToValType(), isConst, &index))
@@ -1924,17 +1934,17 @@ class MOZ_STACK_CLASS ModuleValidator
             global->u.varOrConst.literalValue_ = lit;
         if (!globalMap_.putNew(var, global))
             return false;
 
         AsmJSGlobal g(AsmJSGlobal::Variable, nullptr);
         g.pod.u.var.initKind_ = AsmJSGlobal::InitConstant;
         g.pod.u.var.u.val_ = lit.value();
         g.pod.u.var.globalDataOffset_ = mg_.global(index).globalDataOffset;
-        return module_->globals.append(g);
+        return asmJSMetadata_->globals.append(g);
     }
     bool addGlobalVarImport(PropertyName* var, PropertyName* field, Type type, bool isConst) {
         MOZ_ASSERT(type.isGlobalVarType());
 
         uint32_t index;
         ValType valType = type.canonicalToValType();
         if (!mg_.allocateGlobal(valType, isConst, &index))
             return false;
@@ -1947,46 +1957,46 @@ class MOZ_STACK_CLASS ModuleValidator
         global->u.varOrConst.type_ = type.which();
         if (!globalMap_.putNew(var, global))
             return false;
 
         AsmJSGlobal g(AsmJSGlobal::Variable, field);
         g.pod.u.var.initKind_ = AsmJSGlobal::InitImport;
         g.pod.u.var.u.importType_ = valType;
         g.pod.u.var.globalDataOffset_ = mg_.global(index).globalDataOffset;
-        return module_->globals.append(g);
+        return asmJSMetadata_->globals.append(g);
     }
     bool addArrayView(PropertyName* var, Scalar::Type vt, PropertyName* maybeField) {
         if (!arrayViews_.append(ArrayView(var, vt)))
             return false;
 
         Global* global = validationLifo_.new_<Global>(Global::ArrayView);
         if (!global)
             return false;
         global->u.viewInfo.viewType_ = vt;
         if (!globalMap_.putNew(var, global))
             return false;
 
         AsmJSGlobal g(AsmJSGlobal::ArrayView, maybeField);
         g.pod.u.viewType_ = vt;
-        return module_->globals.append(g);
+        return asmJSMetadata_->globals.append(g);
     }
     bool addMathBuiltinFunction(PropertyName* var, AsmJSMathBuiltinFunction func,
                                 PropertyName* field)
     {
         Global* global = validationLifo_.new_<Global>(Global::MathBuiltinFunction);
         if (!global)
             return false;
         global->u.mathBuiltinFunc_ = func;
         if (!globalMap_.putNew(var, global))
             return false;
 
         AsmJSGlobal g(AsmJSGlobal::MathBuiltinFunction, field);
         g.pod.u.mathBuiltinFunc_ = func;
-        return module_->globals.append(g);
+        return asmJSMetadata_->globals.append(g);
     }
   private:
     bool addGlobalDoubleConstant(PropertyName* var, double constant) {
         Global* global = validationLifo_.new_<Global>(Global::ConstantLiteral);
         if (!global)
             return false;
         global->u.varOrConst.type_ = Type::Double;
         global->u.varOrConst.literalValue_ = NumLit(NumLit::Double, DoubleValue(constant));
@@ -1995,97 +2005,97 @@ class MOZ_STACK_CLASS ModuleValidator
   public:
     bool addMathBuiltinConstant(PropertyName* var, double constant, PropertyName* field) {
         if (!addGlobalDoubleConstant(var, constant))
             return false;
 
         AsmJSGlobal g(AsmJSGlobal::Constant, field);
         g.pod.u.constant.value_ = constant;
         g.pod.u.constant.kind_ = AsmJSGlobal::MathConstant;
-        return module_->globals.append(g);
+        return asmJSMetadata_->globals.append(g);
     }
     bool addGlobalConstant(PropertyName* var, double constant, PropertyName* field) {
         if (!addGlobalDoubleConstant(var, constant))
             return false;
 
         AsmJSGlobal g(AsmJSGlobal::Constant, field);
         g.pod.u.constant.value_ = constant;
         g.pod.u.constant.kind_ = AsmJSGlobal::GlobalConstant;
-        return module_->globals.append(g);
+        return asmJSMetadata_->globals.append(g);
     }
     bool addAtomicsBuiltinFunction(PropertyName* var, AsmJSAtomicsBuiltinFunction func,
                                    PropertyName* field)
     {
         atomicsPresent_ = true;
 
         Global* global = validationLifo_.new_<Global>(Global::AtomicsBuiltinFunction);
         if (!global)
             return false;
         global->u.atomicsBuiltinFunc_ = func;
         if (!globalMap_.putNew(var, global))
             return false;
 
         AsmJSGlobal g(AsmJSGlobal::AtomicsBuiltinFunction, field);
         g.pod.u.atomicsBuiltinFunc_ = func;
-        return module_->globals.append(g);
+        return asmJSMetadata_->globals.append(g);
     }
     bool addSimdCtor(PropertyName* var, SimdType type, PropertyName* field) {
         Global* global = validationLifo_.new_<Global>(Global::SimdCtor);
         if (!global)
             return false;
         global->u.simdCtorType_ = type;
         if (!globalMap_.putNew(var, global))
             return false;
 
         AsmJSGlobal g(AsmJSGlobal::SimdCtor, field);
         g.pod.u.simdCtorType_ = type;
-        return module_->globals.append(g);
+        return asmJSMetadata_->globals.append(g);
     }
     bool addSimdOperation(PropertyName* var, SimdType type, SimdOperation op, PropertyName* opName)
     {
         Global* global = validationLifo_.new_<Global>(Global::SimdOp);
         if (!global)
             return false;
         global->u.simdOp.type_ = type;
         global->u.simdOp.which_ = op;
         if (!globalMap_.putNew(var, global))
             return false;
 
         AsmJSGlobal g(AsmJSGlobal::SimdOp, opName);
         g.pod.u.simdOp.type_ = type;
         g.pod.u.simdOp.which_ = op;
-        return module_->globals.append(g);
+        return asmJSMetadata_->globals.append(g);
     }
     bool addArrayViewCtor(PropertyName* var, Scalar::Type vt, PropertyName* field) {
         Global* global = validationLifo_.new_<Global>(Global::ArrayViewCtor);
         if (!global)
             return false;
         global->u.viewInfo.viewType_ = vt;
         if (!globalMap_.putNew(var, global))
             return false;
 
         AsmJSGlobal g(AsmJSGlobal::ArrayViewCtor, field);
         g.pod.u.viewType_ = vt;
-        return module_->globals.append(g);
+        return asmJSMetadata_->globals.append(g);
     }
     bool addFFI(PropertyName* var, PropertyName* field) {
-        if (module_->numFFIs == UINT32_MAX)
-            return false;
-        uint32_t ffiIndex = module_->numFFIs++;
+        if (asmJSMetadata_->numFFIs == UINT32_MAX)
+            return false;
+        uint32_t ffiIndex = asmJSMetadata_->numFFIs++;
 
         Global* global = validationLifo_.new_<Global>(Global::FFI);
         if (!global)
             return false;
         global->u.ffiIndex_ = ffiIndex;
         if (!globalMap_.putNew(var, global))
             return false;
 
         AsmJSGlobal g(AsmJSGlobal::FFI, field);
         g.pod.u.ffiIndex_ = ffiIndex;
-        return module_->globals.append(g);
+        return asmJSMetadata_->globals.append(g);
     }
     bool addExportField(ParseNode* pn, const Func& func, PropertyName* maybeFieldName) {
         // Record the field name of this export.
         CacheableChars fieldName;
         if (maybeFieldName)
             fieldName = StringToNewUTF8CharsZ(cx_, *maybeFieldName);
         else
             fieldName = DuplicateString("");
@@ -2095,20 +2105,20 @@ class MOZ_STACK_CLASS ModuleValidator
         // Declare which function is exported which gives us an index into the
         // module ExportVector.
         uint32_t exportIndex;
         if (!mg_.declareExport(Move(fieldName), func.index(), &exportIndex))
             return false;
 
         // The exported function might have already been exported in which case
         // the index will refer into the range of AsmJSExports.
-        MOZ_ASSERT(exportIndex <= module_->exports.length());
-        return exportIndex < module_->exports.length() ||
-               module_->exports.emplaceBack(func.srcBegin() - module_->srcStart,
-                                            func.srcEnd() - module_->srcStart);
+        MOZ_ASSERT(exportIndex <= asmJSMetadata_->exports.length());
+        return exportIndex < asmJSMetadata_->exports.length() ||
+               asmJSMetadata_->exports.emplaceBack(func.srcBegin() - asmJSMetadata_->srcStart,
+                                                   func.srcEnd() - asmJSMetadata_->srcStart);
     }
     bool addFunction(PropertyName* name, uint32_t firstUse, Sig&& sig, Func** func) {
         uint32_t sigIndex;
         if (!declareSig(Move(sig), &sigIndex))
             return false;
         uint32_t funcIndex = numFunctions();
         if (funcIndex >= MaxFuncs)
             return failCurrentOffset("too many functions");
@@ -2150,37 +2160,37 @@ class MOZ_STACK_CLASS ModuleValidator
         return true;
     }
     bool declareImport(PropertyName* name, Sig&& sig, unsigned ffiIndex, uint32_t* importIndex) {
         ImportMap::AddPtr p = importMap_.lookupForAdd(NamedSig::Lookup(name, sig));
         if (p) {
             *importIndex = p->value();
             return true;
         }
-        *importIndex = module_->imports.length();
+        *importIndex = asmJSMetadata_->imports.length();
         if (*importIndex >= MaxImports)
             return failCurrentOffset("too many imports");
-        if (!module_->imports.emplaceBack(ffiIndex))
+        if (!asmJSMetadata_->imports.emplaceBack(ffiIndex))
             return false;
         uint32_t sigIndex;
         if (!declareSig(Move(sig), &sigIndex))
             return false;
         if (!mg_.initImport(*importIndex, sigIndex))
             return false;
         return importMap_.add(p, NamedSig(name, mg_.sig(sigIndex)), *importIndex);
     }
 
     bool tryConstantAccess(uint64_t start, uint64_t width) {
         MOZ_ASSERT(UINT64_MAX - start > width);
         uint64_t len = start + width;
         if (len > uint64_t(INT32_MAX) + 1)
             return false;
         len = RoundUpToNextValidAsmJSHeapLength(len);
-        if (len > module_->minHeapLength) {
-            module_->minHeapLength = len;
+        if (len > asmJSMetadata_->minHeapLength) {
+            asmJSMetadata_->minHeapLength = len;
             mg_.bumpMinHeapLength(len);
         }
         return true;
     }
 
     // Error handling.
     bool hasAlreadyFailed() const {
         return !!errorString_;
@@ -2315,34 +2325,41 @@ class MOZ_STACK_CLASS ModuleValidator
         CacheableCharsVector funcNames;
         for (const Func* func : functions_) {
             CacheableChars funcName = StringToNewUTF8CharsZ(cx_, *func->name());
             if (!funcName || !funcNames.emplaceBack(Move(funcName)))
                 return false;
         }
 
         uint32_t endBeforeCurly = tokenStream().currentToken().pos.end;
-        module_->srcLength = endBeforeCurly - module_->srcStart;
+        asmJSMetadata_->srcLength = endBeforeCurly - asmJSMetadata_->srcStart;
 
         TokenPos pos;
         JS_ALWAYS_TRUE(tokenStream().peekTokenPos(&pos, TokenStream::Operand));
         uint32_t endAfterCurly = pos.end;
-        module_->srcLengthWithRightBrace = endAfterCurly - module_->srcStart;
-
-        UniqueModuleData base;
-        UniqueStaticLinkData link;
-        UniqueExportMap exportMap;
-        if (!mg_.finish(Move(funcNames), &base, &link, &exportMap, slowFuncs))
+        asmJSMetadata_->srcLengthWithRightBrace = endAfterCurly - asmJSMetadata_->srcStart;
+
+        UniqueCodeSegment code;
+        SharedMetadata metadata;
+        SharedStaticLinkData staticLinkData;
+        SharedExportMap exportMap;
+        if (!mg_.finish(Move(funcNames), &code, &metadata, &staticLinkData, &exportMap, slowFuncs))
             return false;
 
         moduleObj.set(WasmModuleObject::create(cx_));
         if (!moduleObj)
             return false;
 
-        return moduleObj->init(js_new<AsmJSModule>(Move(base), Move(link), Move(exportMap), Move(module_)));
+        auto* module = js_new<AsmJSModule>(Move(code), *metadata, *staticLinkData, *exportMap,
+                                           *asmJSMetadata_);
+        if (!module)
+            return false;
+
+        moduleObj->init(*module);
+        return true;
     }
 };
 
 /*****************************************************************************/
 // Numeric literal utilities
 
 static bool
 IsNumericNonFloatLiteral(ParseNode* pn)
@@ -6565,18 +6582,17 @@ CheckIf(FunctionValidator& f, ParseNode*
         return false;
 
     if (elseStmt) {
         if (!f.switchToElse())
             return false;
 
         if (elseStmt->isKind(PNK_IF)) {
             ifStmt = elseStmt;
-            ++numIfEnd;
-            if (numIfEnd == 0)
+            if (numIfEnd++ == UINT32_MAX)
                 return false;
             goto recurse;
         }
 
         if (!CheckStatement(f, elseStmt))
             return false;
     }
 
@@ -8045,189 +8061,168 @@ AsmJSGlobal::serializedSize() const
 const uint8_t*
 AsmJSGlobal::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
 {
     (cursor = ReadBytes(cursor, &pod, sizeof(pod))) &&
     (cursor = DeserializeName(cx, cursor, &name_));
     return cursor;
 }
 
-bool
-AsmJSGlobal::clone(JSContext* cx, AsmJSGlobal* out) const
-{
-    *out = *this;
-    return true;
-}
-
 size_t
-AsmJSModuleData::serializedSize() const
+AsmJSMetadata::serializedSize() const
 {
     return sizeof(pod()) +
            SerializedVectorSize(globals) +
            SerializedPodVectorSize(imports) +
            SerializedPodVectorSize(exports) +
            SerializedNameSize(globalArgumentName) +
            SerializedNameSize(importArgumentName) +
            SerializedNameSize(bufferArgumentName);
 }
 
 uint8_t*
-AsmJSModuleData::serialize(uint8_t* cursor) const
+AsmJSMetadata::serialize(uint8_t* cursor) const
 {
     cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
     cursor = SerializeVector(cursor, globals);
     cursor = SerializePodVector(cursor, imports);
     cursor = SerializePodVector(cursor, exports);
     cursor = SerializeName(cursor, globalArgumentName);
     cursor = SerializeName(cursor, importArgumentName);
     cursor = SerializeName(cursor, bufferArgumentName);
     return cursor;
 }
 
 const uint8_t*
-AsmJSModuleData::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+AsmJSMetadata::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
 {
     (cursor = ReadBytes(cursor, &pod(), sizeof(pod()))) &&
     (cursor = DeserializeVector(cx, cursor, &globals)) &&
     (cursor = DeserializePodVector(cx, cursor, &imports)) &&
     (cursor = DeserializePodVector(cx, cursor, &exports)) &&
     (cursor = DeserializeName(cx, cursor, &globalArgumentName)) &&
     (cursor = DeserializeName(cx, cursor, &importArgumentName)) &&
     (cursor = DeserializeName(cx, cursor, &bufferArgumentName));
+    cacheResult = CacheResult::Hit;
     return cursor;
 }
 
-bool
-AsmJSModuleData::clone(JSContext* cx, AsmJSModuleData* out) const
-{
-    out->pod() = pod();
-    out->globalArgumentName = globalArgumentName;
-    out->importArgumentName = importArgumentName;
-    out->bufferArgumentName = bufferArgumentName;
-    out->srcStart = srcStart;
-    out->srcBodyStart = srcBodyStart;
-    out->strict = strict;
-    out->scriptSource.reset(scriptSource.get());
-    return CloneVector(cx, globals, &out->globals) &&
-           ClonePodVector(cx, imports, &out->imports) &&
-           ClonePodVector(cx, exports, &out->exports);
-}
-
 size_t
-AsmJSModuleData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
+AsmJSMetadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return globals.sizeOfExcludingThis(mallocSizeOf) +
            imports.sizeOfExcludingThis(mallocSizeOf) +
            exports.sizeOfExcludingThis(mallocSizeOf);
 }
 
 size_t
 AsmJSModule::serializedSize() const
 {
-    return base().serializedSize() +
-           link_->serializedSize() +
+    return codeSegment().serializedSize() +
+           metadata().serializedSize() +
+           staticLinkData_->serializedSize() +
            exportMap_->serializedSize() +
-           module_->serializedSize();
+           asmJSMetadata_->serializedSize();
 }
 
 uint8_t*
 AsmJSModule::serialize(uint8_t* cursor) const
 {
-    cursor = base().serialize(cursor);
-    cursor = link_->serialize(cursor);
+    cursor = codeSegment().serialize(cursor);
+    cursor = metadata().serialize(cursor);
+    cursor = staticLinkData_->serialize(cursor);
     cursor = exportMap_->serialize(cursor);
-    cursor = module_->serialize(cursor);
+    cursor = asmJSMetadata_->serialize(cursor);
     return cursor;
 }
 
 /* static */ const uint8_t*
 AsmJSModule::deserialize(ExclusiveContext* cx, const uint8_t* cursor, AsmJSParser& parser,
                          MutableHandle<WasmModuleObject*> moduleObj)
 {
     moduleObj.set(WasmModuleObject::create(cx));
     if (!moduleObj)
         return nullptr;
 
     // Deserialization GC-allocates a bunch of atoms and stores them in unrooted
     // Vectors so, for simplicity, inhibit GC of the atoms zone.
     AutoKeepAtoms aka(cx->perThreadData);
 
-    UniqueModuleData base = cx->make_unique<ModuleData>();
-    if (!base)
+    UniqueCodeSegment code = MakeUnique<CodeSegment>();
+    if (!code)
         return nullptr;
-    cursor = base->deserialize(cx, cursor);
+    cursor = code->deserialize(cx, cursor);
     if (!cursor)
         return nullptr;
 
-    MOZ_ASSERT(!base->loadedFromCache);
-    base->loadedFromCache = true;
-
-    UniqueStaticLinkData link = cx->make_unique<StaticLinkData>();
-    if (!link)
+    MutableMetadata metadata = js_new<Metadata>();
+    if (!metadata)
         return nullptr;
-    cursor = link->deserialize(cx, cursor);
+    cursor = metadata->deserialize(cx, cursor);
     if (!cursor)
         return nullptr;
 
-    UniqueExportMap exportMap = cx->make_unique<ExportMap>();
+    MutableStaticLinkData staticLinkData = cx->new_<StaticLinkData>();
+    if (!staticLinkData)
+        return nullptr;
+    cursor = staticLinkData->deserialize(cx, cursor);
+    if (!cursor)
+        return nullptr;
+
+    MutableExportMap exportMap = cx->new_<ExportMap>();
     if (!exportMap)
         return nullptr;
     cursor = exportMap->deserialize(cx, cursor);
     if (!cursor)
         return nullptr;
 
-    UniqueAsmJSModuleData module = cx->make_unique<AsmJSModuleData>();
-    if (!module)
+    MutableAsmJSMetadata asmJSMetadata = cx->new_<AsmJSMetadata>();
+    if (!asmJSMetadata)
         return nullptr;
-    cursor = module->deserialize(cx, cursor);
+    cursor = asmJSMetadata->deserialize(cx, cursor);
     if (!cursor)
         return nullptr;
 
-    // See AsmJSModuleData comment as well as ModuleValidator::init().
-    module->srcStart = parser.pc->maybeFunction->pn_body->pn_pos.begin;
-    module->srcBodyStart = parser.tokenStream.currentToken().pos.end;
-    module->strict = parser.pc->sc->strict() && !parser.pc->sc->hasExplicitUseStrict();
-    module->scriptSource.reset(parser.ss);
-
-    if (!moduleObj->init(js_new<AsmJSModule>(Move(base), Move(link), Move(exportMap), Move(module))))
+    // See AsmJSMetadata comment as well as ModuleValidator::init().
+    asmJSMetadata->srcStart = parser.pc->maybeFunction->pn_body->pn_pos.begin;
+    asmJSMetadata->srcBodyStart = parser.tokenStream.currentToken().pos.end;
+    asmJSMetadata->strict = parser.pc->sc->strict() && !parser.pc->sc->hasExplicitUseStrict();
+    asmJSMetadata->scriptSource.reset(parser.ss);
+
+    auto* module = js_new<AsmJSModule>(Move(code), *metadata, *staticLinkData, *exportMap,
+                                       *asmJSMetadata);
+    if (!module)
         return nullptr;
 
+    moduleObj->init(*module);
     return cursor;
 }
 
 bool
 AsmJSModule::clone(JSContext* cx, MutableHandle<WasmModuleObject*> moduleObj) const
 {
     moduleObj.set(WasmModuleObject::create(cx));
     if (!moduleObj)
         return false;
 
     // Prevent any GC that may move the temporarily-unrooted atoms being cloned.
     AutoKeepAtoms aka(cx->perThreadData);
 
-    UniqueModuleData base = cx->make_unique<ModuleData>();
-    if (!base || !this->base().clone(cx, base.get()))
-        return false;
-
-    UniqueStaticLinkData link = cx->make_unique<StaticLinkData>();
-    if (!link || !link_->clone(cx, link.get()))
-        return false;
-
-    UniqueExportMap exportMap = cx->make_unique<ExportMap>();
-    if (!exportMap || !exportMap_->clone(cx, exportMap.get()))
-        return false;
-
-    UniqueAsmJSModuleData module = cx->make_unique<AsmJSModuleData>();
-    if (!module || !module_->clone(cx, module.get()))
-        return false;
-
-    if (!moduleObj->init(js_new<AsmJSModule>(Move(base), Move(link), Move(exportMap), Move(module))))
-        return false;
-
-    return Module::clone(cx, *link_, &moduleObj->module());
+    UniqueCodeSegment code = CodeSegment::clone(cx, codeSegment());
+    if (!code)
+        return false;
+
+    auto* module = js_new<AsmJSModule>(Move(code), metadata(), *staticLinkData_, *exportMap_,
+                                       *asmJSMetadata_);
+    if (!module)
+        return false;
+
+    moduleObj->init(*module);
+
+    return Module::clone(cx, *staticLinkData_, &moduleObj->module());
 }
 
 namespace {
 
 struct PropertyNameWrapper
 {
     PropertyName* name;
 
@@ -8778,17 +8773,17 @@ js::IsAsmJSModuleLoadedFromCache(JSConte
     JSFunction* fun = MaybeWrappedNativeFunction(args.get(0));
     if (!fun || !IsAsmJSModule(fun)) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_USE_ASM_TYPE_FAIL,
                              "argument passed to isAsmJSModuleLoadedFromCache is not a "
                              "validated asm.js module");
         return false;
     }
 
-    bool loadedFromCache = AsmJSModuleToModuleObject(fun)->module().loadedFromCache();
+    bool loadedFromCache = AsmJSModuleToModuleObject(fun)->module().asAsmJS().loadedFromCache();
 
     args.rval().set(BooleanValue(loadedFromCache));
     return true;
 }
 
 /*****************************************************************************/
 // asm.js toString/toSource support
 
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -1097,17 +1097,17 @@ DecodeNameSection(JSContext* cx, Decoder
         return Fail(cx, d, "names section byte size mismatch");
 
     return true;
 }
 
 
 static bool
 DecodeModule(JSContext* cx, UniqueChars file, const uint8_t* bytes, uint32_t length,
-             ImportNameVector* importNames, UniqueExportMap* exportMap,
+             ImportNameVector* importNames, SharedExportMap* exportMap,
              MutableHandle<ArrayBufferObject*> heap, MutableHandle<WasmModuleObject*> moduleObj)
 {
     Decoder d(bytes, bytes + length);
 
     uint32_t u32;
     if (!d.readFixedU32(&u32) || u32 != MagicNumber)
         return Fail(cx, d, "failed to match magic number");
 
@@ -1150,30 +1150,34 @@ DecodeModule(JSContext* cx, UniqueChars 
     if (!DecodeNameSection(cx, d, &funcNames))
         return false;
 
     while (!d.done()) {
         if (!d.skipSection())
             return Fail(cx, d, "failed to skip unknown section at end");
     }
 
-    UniqueModuleData module;
-    UniqueStaticLinkData staticLink;
+    UniqueCodeSegment code;
+    SharedMetadata metadata;
+    SharedStaticLinkData staticLinkData;
     SlowFunctionVector slowFuncs(cx);
-    if (!mg.finish(Move(funcNames), &module, &staticLink, exportMap, &slowFuncs))
+    if (!mg.finish(Move(funcNames), &code, &metadata, &staticLinkData, exportMap, &slowFuncs))
         return false;
 
     moduleObj.set(WasmModuleObject::create(cx));
     if (!moduleObj)
         return false;
 
-    if (!moduleObj->init(cx->new_<Module>(Move(module))))
+    auto module = cx->new_<Module>(Move(code), *metadata);
+    if (!module)
         return false;
 
-    return moduleObj->module().staticallyLink(cx, *staticLink);
+    moduleObj->init(*module);
+
+    return moduleObj->module().staticallyLink(cx, *staticLinkData);
 }
 
 /*****************************************************************************/
 // Top-level functions
 
 bool
 wasm::HasCompilerSupport(ExclusiveContext* cx)
 {
@@ -1290,17 +1294,17 @@ wasm::Eval(JSContext* cx, Handle<TypedAr
     if (!DescribeScriptedCaller(cx, &filename))
         return false;
 
     UniqueChars file = DuplicateString(filename.get());
     if (!file)
         return false;
 
     ImportNameVector importNames;
-    UniqueExportMap exportMap;
+    SharedExportMap exportMap;
     Rooted<ArrayBufferObject*> heap(cx);
     Rooted<WasmModuleObject*> moduleObj(cx);
 
     if (!DecodeModule(cx, Move(file), bytes, length, &importNames, &exportMap, &heap, &moduleObj)) {
         if (!cx->isExceptionPending())
             ReportOutOfMemory(cx);
         return false;
     }
@@ -1313,17 +1317,17 @@ wasm::Eval(JSContext* cx, Handle<TypedAr
 
     RootedObject exportObj(cx);
     if (!module.dynamicallyLink(cx, moduleObj, heap, imports, *exportMap, &exportObj))
         return false;
 
     if (!CreateInstance(cx, exportObj, instance))
         return false;
 
-    if (cx->compartment()->debuggerObservesAsmJS()) {
+    if (cx->compartment()->isDebuggee()) {
         Bytes source;
         if (!source.append(bytes, length))
             return false;
         module.setSource(Move(source));
     }
 
     Debugger::onNewWasmModule(cx, moduleObj);
     return true;
new file mode 100644
--- /dev/null
+++ b/js/src/asmjs/WasmCode.cpp
@@ -0,0 +1,381 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2016 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "asmjs/WasmCode.h"
+
+#include "mozilla/Atomics.h"
+
+#include "asmjs/WasmSerialize.h"
+#include "jit/ExecutableAllocator.h"
+
+using namespace js;
+using namespace js::jit;
+using namespace js::wasm;
+using mozilla::Atomic;
+
+// Limit the number of concurrent wasm code allocations per process. Note that
+// on Linux, the real maximum is ~32k, as each module requires 2 maps (RW/RX),
+// and the kernel's default max_map_count is ~65k.
+//
+// Note: this can be removed once writable/non-executable global data stops
+// being stored in the code segment.
+static Atomic<uint32_t> wasmCodeAllocations(0);
+static const uint32_t MaxWasmCodeAllocations = 16384;
+
+static uint8_t*
+AllocateCodeSegment(ExclusiveContext* cx, uint32_t totalLength)
+{
+    if (wasmCodeAllocations >= MaxWasmCodeAllocations)
+        return nullptr;
+
+    // Allocate RW memory. DynamicallyLinkModule will reprotect the code as RX.
+    unsigned permissions =
+        ExecutableAllocator::initialProtectionFlags(ExecutableAllocator::Writable);
+
+    void* p = AllocateExecutableMemory(nullptr, totalLength, permissions,
+                                       "wasm-code-segment", gc::SystemPageSize());
+    if (!p) {
+        ReportOutOfMemory(cx);
+        return nullptr;
+    }
+
+    wasmCodeAllocations++;
+    return (uint8_t*)p;
+}
+
+/* static */ UniqueCodeSegment
+CodeSegment::allocate(ExclusiveContext* cx, uint32_t codeLength, uint32_t globalDataLength)
+{
+    UniqueCodeSegment code = cx->make_unique<CodeSegment>();
+    if (!code)
+        return nullptr;
+
+    uint8_t* bytes = AllocateCodeSegment(cx, codeLength + globalDataLength);
+    if (!bytes)
+        return nullptr;
+
+    code->bytes_ = bytes;
+    code->codeLength_ = codeLength;
+    code->globalDataLength_ = globalDataLength;
+    return code;
+}
+
+/* static */ UniqueCodeSegment
+CodeSegment::clone(ExclusiveContext* cx, const CodeSegment& src)
+{
+    UniqueCodeSegment dst = allocate(cx, src.codeLength_, src.globalDataLength_);
+    if (!dst)
+        return nullptr;
+
+    memcpy(dst->code(), src.code(), src.codeLength());
+    return dst;
+}
+
+CodeSegment::~CodeSegment()
+{
+    if (!bytes_) {
+        MOZ_ASSERT(!totalLength());
+        return;
+    }
+
+    MOZ_ASSERT(wasmCodeAllocations > 0);
+    wasmCodeAllocations--;
+
+    MOZ_ASSERT(totalLength() > 0);
+    DeallocateExecutableMemory(bytes_, totalLength(), gc::SystemPageSize());
+}
+
+size_t
+CodeSegment::serializedSize() const
+{
+    return sizeof(uint32_t) +
+           sizeof(uint32_t) +
+           codeLength_;
+}
+
+uint8_t*
+CodeSegment::serialize(uint8_t* cursor) const
+{
+    cursor = WriteScalar<uint32_t>(cursor, codeLength_);
+    cursor = WriteScalar<uint32_t>(cursor, globalDataLength_);
+    cursor = WriteBytes(cursor, bytes_, codeLength_);
+    return cursor;
+}
+
+const uint8_t*
+CodeSegment::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+{
+    cursor = ReadScalar<uint32_t>(cursor, &codeLength_);
+    cursor = ReadScalar<uint32_t>(cursor, &globalDataLength_);
+
+    bytes_ = AllocateCodeSegment(cx, codeLength_ + globalDataLength_);
+    if (!bytes_)
+        return nullptr;
+
+    cursor = ReadBytes(cursor, bytes_, codeLength_);
+    return cursor;
+}
+
+static size_t
+SerializedSigSize(const Sig& sig)
+{
+    return sizeof(ExprType) +
+           SerializedPodVectorSize(sig.args());
+}
+
+static uint8_t*
+SerializeSig(uint8_t* cursor, const Sig& sig)
+{
+    cursor = WriteScalar<ExprType>(cursor, sig.ret());
+    cursor = SerializePodVector(cursor, sig.args());
+    return cursor;
+}
+
+static const uint8_t*
+DeserializeSig(ExclusiveContext* cx, const uint8_t* cursor, Sig* sig)
+{
+    ExprType ret;
+    cursor = ReadScalar<ExprType>(cursor, &ret);
+
+    ValTypeVector args;
+    cursor = DeserializePodVector(cx, cursor, &args);
+    if (!cursor)
+        return nullptr;
+
+    *sig = Sig(Move(args), ret);
+    return cursor;
+}
+
+static size_t
+SizeOfSigExcludingThis(const Sig& sig, MallocSizeOf mallocSizeOf)
+{
+    return sig.args().sizeOfExcludingThis(mallocSizeOf);
+}
+
+size_t
+Export::serializedSize() const
+{
+    return SerializedSigSize(sig_) +
+           sizeof(pod);
+}
+
+uint8_t*
+Export::serialize(uint8_t* cursor) const
+{
+    cursor = SerializeSig(cursor, sig_);
+    cursor = WriteBytes(cursor, &pod, sizeof(pod));
+    return cursor;
+}
+
+const uint8_t*
+Export::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+{
+    (cursor = DeserializeSig(cx, cursor, &sig_)) &&
+    (cursor = ReadBytes(cursor, &pod, sizeof(pod)));
+    return cursor;
+}
+
+size_t
+Export::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
+{
+    return SizeOfSigExcludingThis(sig_, mallocSizeOf);
+}
+
+size_t
+Import::serializedSize() const
+{
+    return SerializedSigSize(sig_) +
+           sizeof(pod);
+}
+
+uint8_t*
+Import::serialize(uint8_t* cursor) const
+{
+    cursor = SerializeSig(cursor, sig_);
+    cursor = WriteBytes(cursor, &pod, sizeof(pod));
+    return cursor;
+}
+
+const uint8_t*
+Import::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+{
+    (cursor = DeserializeSig(cx, cursor, &sig_)) &&
+    (cursor = ReadBytes(cursor, &pod, sizeof(pod)));
+    return cursor;
+}
+
+size_t
+Import::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
+{
+    return SizeOfSigExcludingThis(sig_, mallocSizeOf);
+}
+
+CodeRange::CodeRange(Kind kind, Offsets offsets)
+  : begin_(offsets.begin),
+    profilingReturn_(0),
+    end_(offsets.end),
+    funcIndex_(0),
+    funcLineOrBytecode_(0),
+    funcBeginToTableEntry_(0),
+    funcBeginToTableProfilingJump_(0),
+    funcBeginToNonProfilingEntry_(0),
+    funcProfilingJumpToProfilingReturn_(0),
+    funcProfilingEpilogueToProfilingReturn_(0),
+    kind_(kind)
+{
+    MOZ_ASSERT(begin_ <= end_);
+    MOZ_ASSERT(kind_ == Entry || kind_ == Inline || kind_ == CallThunk);
+}
+
+CodeRange::CodeRange(Kind kind, ProfilingOffsets offsets)
+  : begin_(offsets.begin),
+    profilingReturn_(offsets.profilingReturn),
+    end_(offsets.end),
+    funcIndex_(0),
+    funcLineOrBytecode_(0),
+    funcBeginToTableEntry_(0),
+    funcBeginToTableProfilingJump_(0),
+    funcBeginToNonProfilingEntry_(0),
+    funcProfilingJumpToProfilingReturn_(0),
+    funcProfilingEpilogueToProfilingReturn_(0),
+    kind_(kind)
+{
+    MOZ_ASSERT(begin_ < profilingReturn_);
+    MOZ_ASSERT(profilingReturn_ < end_);
+    MOZ_ASSERT(kind_ == ImportJitExit || kind_ == ImportInterpExit);
+}
+
+CodeRange::CodeRange(uint32_t funcIndex, uint32_t funcLineOrBytecode, FuncOffsets offsets)
+  : begin_(offsets.begin),
+    profilingReturn_(offsets.profilingReturn),
+    end_(offsets.end),
+    funcIndex_(funcIndex),
+    funcLineOrBytecode_(funcLineOrBytecode),
+    funcBeginToTableEntry_(offsets.tableEntry - begin_),
+    funcBeginToTableProfilingJump_(offsets.tableProfilingJump - begin_),
+    funcBeginToNonProfilingEntry_(offsets.nonProfilingEntry - begin_),
+    funcProfilingJumpToProfilingReturn_(profilingReturn_ - offsets.profilingJump),
+    funcProfilingEpilogueToProfilingReturn_(profilingReturn_ - offsets.profilingEpilogue),
+    kind_(Function)
+{
+    MOZ_ASSERT(begin_ < profilingReturn_);
+    MOZ_ASSERT(profilingReturn_ < end_);
+    MOZ_ASSERT(funcBeginToTableEntry_ == offsets.tableEntry - begin_);
+    MOZ_ASSERT(funcBeginToTableProfilingJump_ == offsets.tableProfilingJump - begin_);
+    MOZ_ASSERT(funcBeginToNonProfilingEntry_ == offsets.nonProfilingEntry - begin_);
+    MOZ_ASSERT(funcProfilingJumpToProfilingReturn_ == profilingReturn_ - offsets.profilingJump);
+    MOZ_ASSERT(funcProfilingEpilogueToProfilingReturn_ == profilingReturn_ - offsets.profilingEpilogue);
+}
+
+static size_t
+NullableStringLength(const char* chars)
+{
+    return chars ? strlen(chars) : 0;
+}
+
+size_t
+CacheableChars::serializedSize() const
+{
+    return sizeof(uint32_t) + NullableStringLength(get());
+}
+
+uint8_t*
+CacheableChars::serialize(uint8_t* cursor) const
+{
+    uint32_t length = NullableStringLength(get());
+    cursor = WriteBytes(cursor, &length, sizeof(uint32_t));
+    cursor = WriteBytes(cursor, get(), length);
+    return cursor;
+}
+
+const uint8_t*
+CacheableChars::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+{
+    uint32_t length;
+    cursor = ReadBytes(cursor, &length, sizeof(uint32_t));
+
+    reset(cx->pod_calloc<char>(length + 1));
+    if (!get())
+        return nullptr;
+
+    cursor = ReadBytes(cursor, get(), length);
+    return cursor;
+}
+
+size_t
+CacheableChars::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
+{
+    return mallocSizeOf(get());
+}
+
+size_t
+Metadata::serializedSize() const
+{
+    return sizeof(pod()) +
+           SerializedVectorSize(imports) +
+           SerializedVectorSize(exports) +
+           SerializedPodVectorSize(heapAccesses) +
+           SerializedPodVectorSize(codeRanges) +
+           SerializedPodVectorSize(callSites) +
+           SerializedPodVectorSize(callThunks) +
+           SerializedVectorSize(prettyFuncNames) +
+           filename.serializedSize();
+}
+
+uint8_t*
+Metadata::serialize(uint8_t* cursor) const
+{
+    cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
+    cursor = SerializeVector(cursor, imports);
+    cursor = SerializeVector(cursor, exports);
+    cursor = SerializePodVector(cursor, heapAccesses);
+    cursor = SerializePodVector(cursor, codeRanges);
+    cursor = SerializePodVector(cursor, callSites);
+    cursor = SerializePodVector(cursor, callThunks);
+    cursor = SerializeVector(cursor, prettyFuncNames);
+    cursor = filename.serialize(cursor);
+    return cursor;
+}
+
+/* static */ const uint8_t*
+Metadata::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+{
+    (cursor = ReadBytes(cursor, &pod(), sizeof(pod()))) &&
+    (cursor = DeserializeVector(cx, cursor, &imports)) &&
+    (cursor = DeserializeVector(cx, cursor, &exports)) &&
+    (cursor = DeserializePodVector(cx, cursor, &heapAccesses)) &&
+    (cursor = DeserializePodVector(cx, cursor, &codeRanges)) &&
+    (cursor = DeserializePodVector(cx, cursor, &callSites)) &&
+    (cursor = DeserializePodVector(cx, cursor, &callThunks)) &&
+    (cursor = DeserializeVector(cx, cursor, &prettyFuncNames)) &&
+    (cursor = filename.deserialize(cx, cursor));
+    return cursor;
+}
+
+size_t
+Metadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
+{
+    return SizeOfVectorExcludingThis(imports, mallocSizeOf) +
+           SizeOfVectorExcludingThis(exports, mallocSizeOf) +
+           heapAccesses.sizeOfExcludingThis(mallocSizeOf) +
+           codeRanges.sizeOfExcludingThis(mallocSizeOf) +
+           callSites.sizeOfExcludingThis(mallocSizeOf) +
+           callThunks.sizeOfExcludingThis(mallocSizeOf) +
+           SizeOfVectorExcludingThis(prettyFuncNames, mallocSizeOf) +
+           filename.sizeOfExcludingThis(mallocSizeOf);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/asmjs/WasmCode.h
@@ -0,0 +1,342 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2016 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef wasm_code_h
+#define wasm_code_h
+
+#include "asmjs/WasmTypes.h"
+
+namespace js {
+namespace wasm {
+
+// A wasm CodeSegment owns the allocated executable code for a wasm module.
+// CodeSegment passed to the Module constructor must be allocated via allocate.
+
+class CodeSegment;
+typedef UniquePtr<CodeSegment> UniqueCodeSegment;
+
+class CodeSegment
+{
+    uint8_t* bytes_;
+    uint32_t codeLength_;
+    uint32_t globalDataLength_;
+
+    CodeSegment(const CodeSegment&) = delete;
+    void operator=(const CodeSegment&) = delete;
+
+  public:
+    static UniqueCodeSegment allocate(ExclusiveContext* cx, uint32_t codeLength, uint32_t dataLength);
+    static UniqueCodeSegment clone(ExclusiveContext* cx, const CodeSegment& code);
+    CodeSegment() : bytes_(nullptr), codeLength_(0), globalDataLength_(0) {}
+    ~CodeSegment();
+
+    uint8_t* code() const { return bytes_; }
+    uint8_t* globalData() const { return bytes_ + codeLength_; }
+    uint32_t codeLength() const { return codeLength_; }
+    uint32_t globalDataLength() const { return globalDataLength_; }
+    uint32_t totalLength() const { return codeLength_ + globalDataLength_; }
+
+    WASM_DECLARE_SERIALIZABLE(CodeSegment)
+};
+
+// An Export represents a single function inside a wasm Module that has been
+// exported one or more times.
+
+class Export
+{
+    Sig sig_;
+    struct CacheablePod {
+        uint32_t stubOffset_;
+    } pod;
+
+  public:
+    Export() = default;
+    explicit Export(Sig&& sig)
+      : sig_(Move(sig))
+    {
+        pod.stubOffset_ = UINT32_MAX;
+    }
+    void initStubOffset(uint32_t stubOffset) {
+        MOZ_ASSERT(pod.stubOffset_ == UINT32_MAX);
+        pod.stubOffset_ = stubOffset;
+    }
+
+    uint32_t stubOffset() const {
+        return pod.stubOffset_;
+    }
+    const Sig& sig() const {
+        return sig_;
+    }
+
+    WASM_DECLARE_SERIALIZABLE(Export)
+};
+
+typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
+
+// An Import describes a wasm module import. Currently, only functions can be
+// imported in wasm. A function import includes the signature used within the
+// module to call it.
+
+class Import
+{
+    Sig sig_;
+    struct CacheablePod {
+        uint32_t exitGlobalDataOffset_;
+        uint32_t interpExitCodeOffset_;
+        uint32_t jitExitCodeOffset_;
+    } pod;
+
+  public:
+    Import() = default;
+    Import(Sig&& sig, uint32_t exitGlobalDataOffset)
+      : sig_(Move(sig))
+    {
+        pod.exitGlobalDataOffset_ = exitGlobalDataOffset;
+        pod.interpExitCodeOffset_ = 0;
+        pod.jitExitCodeOffset_ = 0;
+    }
+
+    void initInterpExitOffset(uint32_t off) {
+        MOZ_ASSERT(!pod.interpExitCodeOffset_);
+        pod.interpExitCodeOffset_ = off;
+    }
+    void initJitExitOffset(uint32_t off) {
+        MOZ_ASSERT(!pod.jitExitCodeOffset_);
+        pod.jitExitCodeOffset_ = off;
+    }
+
+    const Sig& sig() const {
+        return sig_;
+    }
+    uint32_t exitGlobalDataOffset() const {
+        return pod.exitGlobalDataOffset_;
+    }
+    uint32_t interpExitCodeOffset() const {
+        return pod.interpExitCodeOffset_;
+    }
+    uint32_t jitExitCodeOffset() const {
+        return pod.jitExitCodeOffset_;
+    }
+
+    WASM_DECLARE_SERIALIZABLE(Import)
+};
+
+typedef Vector<Import, 0, SystemAllocPolicy> ImportVector;
+
+// A CodeRange describes a single contiguous range of code within a wasm
+// module's code segment. A CodeRange describes what the code does and, for
+// function bodies, the name and source coordinates of the function.
+
+class CodeRange
+{
+  public:
+    enum Kind { Function, Entry, ImportJitExit, ImportInterpExit, Inline, CallThunk };
+
+  private:
+    // All fields are treated as cacheable POD:
+    uint32_t begin_;
+    uint32_t profilingReturn_;
+    uint32_t end_;
+    uint32_t funcIndex_;
+    uint32_t funcLineOrBytecode_;
+    uint8_t funcBeginToTableEntry_;
+    uint8_t funcBeginToTableProfilingJump_;
+    uint8_t funcBeginToNonProfilingEntry_;
+    uint8_t funcProfilingJumpToProfilingReturn_;
+    uint8_t funcProfilingEpilogueToProfilingReturn_;
+    Kind kind_ : 8;
+
+  public:
+    CodeRange() = default;
+    CodeRange(Kind kind, Offsets offsets);
+    CodeRange(Kind kind, ProfilingOffsets offsets);
+    CodeRange(uint32_t funcIndex, uint32_t lineOrBytecode, FuncOffsets offsets);
+
+    // All CodeRanges have a begin and end.
+
+    uint32_t begin() const {
+        return begin_;
+    }
+    uint32_t end() const {
+        return end_;
+    }
+
+    // Other fields are only available for certain CodeRange::Kinds.
+
+    Kind kind() const {
+        return kind_;
+    }
+
+    bool isFunction() const {
+        return kind() == Function;
+    }
+    bool isImportExit() const {
+        return kind() == ImportJitExit || kind() == ImportInterpExit;
+    }
+    bool isInline() const {
+        return kind() == Inline;
+    }
+
+    // Every CodeRange except entry and inline stubs has a profiling return
+    // which is used for asynchronous profiling to determine the frame pointer.
+
+    uint32_t profilingReturn() const {
+        MOZ_ASSERT(isFunction() || isImportExit());
+        return profilingReturn_;
+    }
+
+    // Functions have offsets which allow patching to selectively execute
+    // profiling prologues/epilogues.
+
+    uint32_t funcProfilingEntry() const {
+        MOZ_ASSERT(isFunction());
+        return begin();
+    }
+    uint32_t funcTableEntry() const {
+        MOZ_ASSERT(isFunction());
+        return begin_ + funcBeginToTableEntry_;
+    }
+    uint32_t funcTableProfilingJump() const {
+        MOZ_ASSERT(isFunction());
+        return begin_ + funcBeginToTableProfilingJump_;
+    }
+    uint32_t funcNonProfilingEntry() const {
+        MOZ_ASSERT(isFunction());
+        return begin_ + funcBeginToNonProfilingEntry_;
+    }
+    uint32_t funcProfilingJump() const {
+        MOZ_ASSERT(isFunction());
+        return profilingReturn_ - funcProfilingJumpToProfilingReturn_;
+    }
+    uint32_t funcProfilingEpilogue() const {
+        MOZ_ASSERT(isFunction());
+        return profilingReturn_ - funcProfilingEpilogueToProfilingReturn_;
+    }
+    uint32_t funcIndex() const {
+        MOZ_ASSERT(isFunction());
+        return funcIndex_;
+    }
+    uint32_t funcLineOrBytecode() const {
+        MOZ_ASSERT(isFunction());
+        return funcLineOrBytecode_;
+    }
+
+    // A sorted array of CodeRanges can be looked up via BinarySearch and PC.
+
+    struct PC {
+        size_t offset;
+        explicit PC(size_t offset) : offset(offset) {}
+        bool operator==(const CodeRange& rhs) const {
+            return offset >= rhs.begin() && offset < rhs.end();
+        }
+        bool operator<(const CodeRange& rhs) const {
+            return offset < rhs.begin();
+        }
+    };
+};
+
+WASM_DECLARE_POD_VECTOR(CodeRange, CodeRangeVector)
+
+// A CallThunk describes the offset and target of thunks so that they may be
+// patched at runtime when profiling is toggled. Thunks are emitted to connect
+// callsites that are too far away from callees to fit in a single call
+// instruction's relative offset.
+
+struct CallThunk
+{
+    uint32_t offset;
+    union {
+        uint32_t funcIndex;
+        uint32_t codeRangeIndex;
+    } u;
+
+    CallThunk(uint32_t offset, uint32_t funcIndex) : offset(offset) { u.funcIndex = funcIndex; }
+    CallThunk() = default;
+};
+
+WASM_DECLARE_POD_VECTOR(CallThunk, CallThunkVector)
+
+// CacheableChars is used to cacheably store UniqueChars.
+
+struct CacheableChars : UniqueChars
+{
+    CacheableChars() = default;
+    explicit CacheableChars(char* ptr) : UniqueChars(ptr) {}
+    MOZ_IMPLICIT CacheableChars(UniqueChars&& rhs) : UniqueChars(Move(rhs)) {}
+    WASM_DECLARE_SERIALIZABLE(CacheableChars)
+};
+
+typedef Vector<CacheableChars, 0, SystemAllocPolicy> CacheableCharsVector;
+
+// A wasm module can either use no heap, a unshared heap (ArrayBuffer) or shared
+// heap (SharedArrayBuffer).
+
+enum class HeapUsage
+{
+    None = false,
+    Unshared = 1,
+    Shared = 2
+};
+
+static inline bool
+UsesHeap(HeapUsage heapUsage)
+{
+    return bool(heapUsage);
+}
+
+// Metadata holds all the data that is needed to describe compiled wasm code
+// at runtime (as opposed to data that is only used to statically link or
+// instantiate a module).
+//
+// Metadata is built incrementally by ModuleGenerator and then shared immutably
+// between modules.
+
+struct MetadataCacheablePod
+{
+    uint32_t              functionLength;
+    ModuleKind            kind;
+    HeapUsage             heapUsage;
+    CompileArgs           compileArgs;
+
+    MetadataCacheablePod() { mozilla::PodZero(this); }
+};
+
+struct Metadata : RefCounted<Metadata>, MetadataCacheablePod
+{
+    MetadataCacheablePod& pod() { return *this; }
+    const MetadataCacheablePod& pod() const { return *this; }
+
+    ImportVector          imports;
+    ExportVector          exports;
+    HeapAccessVector      heapAccesses;
+    CodeRangeVector       codeRanges;
+    CallSiteVector        callSites;
+    CallThunkVector       callThunks;
+    CacheableCharsVector  prettyFuncNames;
+    CacheableChars        filename;
+
+    WASM_DECLARE_SERIALIZABLE(Metadata);
+};
+
+typedef RefPtr<Metadata> MutableMetadata;
+typedef RefPtr<const Metadata> SharedMetadata;
+
+} // namespace wasm
+} // namespace js
+
+#endif // wasm_code_h
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -35,16 +35,17 @@ using mozilla::MakeEnumeratedRange;
 // ModuleGenerator
 
 static const unsigned GENERATOR_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
 static const unsigned COMPILATION_LIFO_DEFAULT_CHUNK_SIZE = 64 * 1024;
 
 ModuleGenerator::ModuleGenerator(ExclusiveContext* cx)
   : cx_(cx),
     jcx_(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread())),
+    globalDataLength_(InitialGlobalDataBytes),
     slowFuncs_(cx),
     numSigs_(0),
     lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE),
     alloc_(&lifo_),
     masm_(MacroAssembler::AsmJSToken(), alloc_),
     funcIndexToExport_(cx),
     lastPatchedCallsite_(0),
     startOfUnpatchedBranches_(0),
@@ -110,56 +111,54 @@ ParallelCompilationEnabled(ExclusiveCont
 }
 
 bool
 ModuleGenerator::init(UniqueModuleGeneratorData shared, UniqueChars filename)
 {
     if (!funcIndexToExport_.init())
         return false;
 
-    module_ = MakeUnique<ModuleData>();
-    if (!module_)
+    metadata_ = js_new<Metadata>();
+    if (!metadata_)
         return false;
 
-    module_->globalBytes = InitialGlobalDataBytes;
-    module_->compileArgs = shared->args;
-    module_->kind = shared->kind;
-    module_->heapUsage = HeapUsage::None;
-    module_->filename = Move(filename);
+    metadata_->compileArgs = shared->args;
+    metadata_->kind = shared->kind;
+    metadata_->heapUsage = HeapUsage::None;
+    metadata_->filename = Move(filename);
 
-    exportMap_ = MakeUnique<ExportMap>();
+    exportMap_ = js_new<ExportMap>();
     if (!exportMap_)
         return false;
 
     shared_ = Move(shared);
 
     // For asm.js, the Vectors in ModuleGeneratorData are max-sized reservations
     // and will be initialized in a linear order via init* functions as the
     // module is generated. For wasm, the Vectors are correctly-sized and
     // already initialized.
 
-    if (module_->kind == ModuleKind::Wasm) {
+    if (metadata_->kind == ModuleKind::Wasm) {
         numSigs_ = shared_->sigs.length();
-        module_->numFuncs = shared_->funcSigs.length();
-        module_->globalBytes = AlignBytes(module_->globalBytes, sizeof(void*));
+        globalDataLength_ = AlignBytes(globalDataLength_, sizeof(void*));
 
         for (ImportModuleGeneratorData& import : shared_->imports) {
             MOZ_ASSERT(!import.globalDataOffset);
-            import.globalDataOffset = module_->globalBytes;
-            module_->globalBytes += Module::SizeOfImportExit;
+            import.globalDataOffset = globalDataLength_;
+            globalDataLength_ += Module::SizeOfImportExit;
             if (!addImport(*import.sig, import.globalDataOffset))
                 return false;
         }
 
-        MOZ_ASSERT(module_->globalBytes % sizeof(void*) == 0);
+        MOZ_ASSERT(globalDataLength_ % sizeof(void*) == 0);
         MOZ_ASSERT(shared_->asmJSSigToTable.empty());
         MOZ_ASSERT(shared_->wasmTable.numElems == shared_->wasmTable.elemFuncIndices.length());
         MOZ_ASSERT(!shared_->wasmTable.globalDataOffset);
-        shared_->wasmTable.globalDataOffset = module_->globalBytes;
-        module_->globalBytes += shared_->wasmTable.numElems * sizeof(void*);
+        shared_->wasmTable.globalDataOffset = globalDataLength_;
+        globalDataLength_ += shared_->wasmTable.numElems * sizeof(void*);
     }
 
     return true;
 }
 
 bool
 ModuleGenerator::finishOutstandingTask()
 {
@@ -195,17 +194,17 @@ ModuleGenerator::funcIsDefined(uint32_t 
     return funcIndex < funcIndexToCodeRange_.length() &&
            funcIndexToCodeRange_[funcIndex] != BadCodeRange;
 }
 
 const CodeRange&
 ModuleGenerator::funcCodeRange(uint32_t funcIndex) const
 {
     MOZ_ASSERT(funcIsDefined(funcIndex));
-    const CodeRange& cr = module_->codeRanges[funcIndexToCodeRange_[funcIndex]];
+    const CodeRange& cr = metadata_->codeRanges[funcIndexToCodeRange_[funcIndex]];
     MOZ_ASSERT(cr.isFunction());
     return cr;
 }
 
 static uint32_t
 JumpRange()
 {
     return Min(JitOptions.jumpThreshold, JumpImmediateRange);
@@ -247,19 +246,19 @@ ModuleGenerator::convertOutOfRangeBranch
         if (!p) {
             Offsets offsets;
             offsets.begin = masm_.currentOffset();
             uint32_t thunkOffset = masm_.thunkWithPatch().offset();
             if (masm_.oom())
                 return false;
             offsets.end = masm_.currentOffset();
 
-            if (!module_->codeRanges.emplaceBack(CodeRange::CallThunk, offsets))
+            if (!metadata_->codeRanges.emplaceBack(CodeRange::CallThunk, offsets))
                 return false;
-            if (!module_->callThunks.emplaceBack(thunkOffset, cs.targetIndex()))
+            if (!metadata_->callThunks.emplaceBack(thunkOffset, cs.targetIndex()))
                 return false;
             if (!alreadyThunked.add(p, cs.targetIndex(), offsets.begin))
                 return false;
         }
 
         masm_.patchCall(callerOffset, p->value());
     }
 
@@ -278,17 +277,17 @@ ModuleGenerator::convertOutOfRangeBranch
 
         Offsets offsets;
         offsets.begin = masm_.currentOffset();
         uint32_t thunkOffset = masm_.thunkWithPatch().offset();
         if (masm_.oom())
             return false;
         offsets.end = masm_.currentOffset();
 
-        if (!module_->codeRanges.emplaceBack(CodeRange::Inline, offsets))
+        if (!metadata_->codeRanges.emplaceBack(CodeRange::Inline, offsets))
             return false;
         if (!jumpThunks_[target].append(thunkOffset))
             return false;
     }
 
     // Unlike callsites, which need to be persisted in the Module, we can simply
     // flush jump sites after each patching pass.
     masm_.clearJumpSites();
@@ -312,18 +311,18 @@ ModuleGenerator::finishTask(IonCompileTa
     }
 
     // Offset the recorded FuncOffsets by the offset of the function in the
     // whole module's code segment.
     uint32_t offsetInWhole = masm_.size();
     results.offsets().offsetBy(offsetInWhole);
 
     // Add the CodeRange for this function.
-    uint32_t funcCodeRangeIndex = module_->codeRanges.length();
-    if (!module_->codeRanges.emplaceBack(func.index(), func.lineOrBytecode(), results.offsets()))
+    uint32_t funcCodeRangeIndex = metadata_->codeRanges.length();
+    if (!metadata_->codeRanges.emplaceBack(func.index(), func.lineOrBytecode(), results.offsets()))
         return false;
 
     // Maintain a mapping from function index to CodeRange index.
     if (func.index() >= funcIndexToCodeRange_.length()) {
         uint32_t n = func.index() - funcIndexToCodeRange_.length() + 1;
         if (!funcIndexToCodeRange_.appendN(BadCodeRange, n))
             return false;
     }
@@ -365,84 +364,84 @@ ModuleGenerator::finishCodegen(StaticLin
     {
         TempAllocator alloc(&lifo_);
         MacroAssembler masm(MacroAssembler::AsmJSToken(), alloc);
 
         if (!entries.resize(numExports()))
             return false;
         for (uint32_t i = 0; i < numExports(); i++) {
             uint32_t target = exportMap_->exportFuncIndices[i];
-            const Sig& sig = module_->exports[i].sig();
+            const Sig& sig = metadata_->exports[i].sig();
             entries[i] = GenerateEntry(masm, target, sig, usesHeap());
         }
 
         if (!interpExits.resize(numImports()))
             return false;
         if (!jitExits.resize(numImports()))
             return false;
         for (uint32_t i = 0; i < numImports(); i++) {
-            interpExits[i] = GenerateInterpExit(masm, module_->imports[i], i);
-            jitExits[i] = GenerateJitExit(masm, module_->imports[i], usesHeap());
+            interpExits[i] = GenerateInterpExit(masm, metadata_->imports[i], i);
+            jitExits[i] = GenerateJitExit(masm, metadata_->imports[i], usesHeap());
         }
 
         for (JumpTarget target : MakeEnumeratedRange(JumpTarget::Limit))
             jumpTargets[target] = GenerateJumpTarget(masm, target);
 
         interruptExit = GenerateInterruptStub(masm);
 
         if (masm.oom() || !masm_.asmMergeWith(masm))
             return false;
     }
 
     // Adjust each of the resulting Offsets (to account for being merged into
     // masm_) and then create code ranges for all the stubs.
 
     for (uint32_t i = 0; i < numExports(); i++) {
         entries[i].offsetBy(offsetInWhole);
-        module_->exports[i].initStubOffset(entries[i].begin);
-        if (!module_->codeRanges.emplaceBack(CodeRange::Entry, entries[i]))
+        metadata_->exports[i].initStubOffset(entries[i].begin);
+        if (!metadata_->codeRanges.emplaceBack(CodeRange::Entry, entries[i]))
             return false;
     }
 
     for (uint32_t i = 0; i < numImports(); i++) {
         interpExits[i].offsetBy(offsetInWhole);
-        module_->imports[i].initInterpExitOffset(interpExits[i].begin);
-        if (!module_->codeRanges.emplaceBack(CodeRange::ImportInterpExit, interpExits[i]))
+        metadata_->imports[i].initInterpExitOffset(interpExits[i].begin);
+        if (!metadata_->codeRanges.emplaceBack(CodeRange::ImportInterpExit, interpExits[i]))
             return false;
 
         jitExits[i].offsetBy(offsetInWhole);
-        module_->imports[i].initJitExitOffset(jitExits[i].begin);
-        if (!module_->codeRanges.emplaceBack(CodeRange::ImportJitExit, jitExits[i]))
+        metadata_->imports[i].initJitExitOffset(jitExits[i].begin);
+        if (!metadata_->codeRanges.emplaceBack(CodeRange::ImportJitExit, jitExits[i]))
             return false;
     }
 
     for (JumpTarget target : MakeEnumeratedRange(JumpTarget::Limit)) {
         jumpTargets[target].offsetBy(offsetInWhole);
-        if (!module_->codeRanges.emplaceBack(CodeRange::Inline, jumpTargets[target]))
+        if (!metadata_->codeRanges.emplaceBack(CodeRange::Inline, jumpTargets[target]))
             return false;
     }
 
     interruptExit.offsetBy(offsetInWhole);
-    if (!module_->codeRanges.emplaceBack(CodeRange::Inline, interruptExit))
+    if (!metadata_->codeRanges.emplaceBack(CodeRange::Inline, interruptExit))
         return false;
 
     // Fill in StaticLinkData with the offsets of these stubs.
 
     link->pod.outOfBoundsOffset = jumpTargets[JumpTarget::OutOfBounds].begin;
     link->pod.interruptOffset = interruptExit.begin;
 
     // Only call convertOutOfRangeBranchesToThunks after all other codegen that may
     // emit new jumps to JumpTargets has finished.
 
     if (!convertOutOfRangeBranchesToThunks())
         return false;
 
     // Now that all thunks have been generated, patch all the thunks.
 
-    for (CallThunk& callThunk : module_->callThunks) {
+    for (CallThunk& callThunk : metadata_->callThunks) {
         uint32_t funcIndex = callThunk.u.funcIndex;
         callThunk.u.codeRangeIndex = funcIndexToCodeRange_[funcIndex];
         masm_.patchThunk(callThunk.offset, funcCodeRange(funcIndex).funcNonProfilingEntry());
     }
 
     for (JumpTarget target : MakeEnumeratedRange(JumpTarget::Limit)) {
         for (uint32_t thunkOffset : jumpThunks_[target])
             masm_.patchThunk(thunkOffset, jumpTargets[target].begin);
@@ -450,17 +449,17 @@ ModuleGenerator::finishCodegen(StaticLin
 
     // Code-generation is complete!
 
     masm_.finish();
     return !masm_.oom();
 }
 
 bool
-ModuleGenerator::finishStaticLinkData(uint8_t* code, uint32_t codeBytes, StaticLinkData* link)
+ModuleGenerator::finishStaticLinkData(uint8_t* code, uint32_t codeLength, StaticLinkData* link)
 {
     // Add links to absolute addresses identified symbolically.
     StaticLinkData::SymbolicLinkArray& symbolicLinks = link->symbolicLinks;
     for (size_t i = 0; i < masm_.numAsmJSAbsoluteAddresses(); i++) {
         AsmJSAbsoluteAddress src = masm_.asmJSAbsoluteAddress(i);
         if (!symbolicLinks[src.target].append(src.patchAt.offset()))
             return false;
     }
@@ -482,26 +481,26 @@ ModuleGenerator::finishStaticLinkData(ui
 #if defined(JS_CODEGEN_X86)
     // Global data accesses in x86 need to be patched with the absolute
     // address of the global. Globals are allocated sequentially after the
     // code section so we can just use an InternalLink.
     for (size_t i = 0; i < masm_.numAsmJSGlobalAccesses(); i++) {
         AsmJSGlobalAccess a = masm_.asmJSGlobalAccess(i);
         StaticLinkData::InternalLink inLink(StaticLinkData::InternalLink::RawPointer);
         inLink.patchAtOffset = masm_.labelToPatchOffset(a.patchAt);
-        inLink.targetOffset = codeBytes + a.globalDataOffset;
+        inLink.targetOffset = codeLength + a.globalDataOffset;
         if (!link->internalLinks.append(inLink))
             return false;
     }
 #endif
 
 #if defined(JS_CODEGEN_X64)
     // Global data accesses on x64 use rip-relative addressing and thus do
     // not need patching after deserialization.
-    uint8_t* globalData = code + codeBytes;
+    uint8_t* globalData = code + codeLength;
     for (size_t i = 0; i < masm_.numAsmJSGlobalAccesses(); i++) {
         AsmJSGlobalAccess a = masm_.asmJSGlobalAccess(i);
         masm_.patchAsmJSGlobalAccess(a.patchAt, code, globalData, a.globalDataOffset);
     }
 #endif
 
     // Function pointer table elements
 
@@ -537,33 +536,30 @@ ModuleGenerator::finishStaticLinkData(ui
 
 bool
 ModuleGenerator::addImport(const Sig& sig, uint32_t globalDataOffset)
 {
     Sig copy;
     if (!copy.clone(sig))
         return false;
 
-    return module_->imports.emplaceBack(Move(copy), globalDataOffset);
+    return metadata_->imports.emplaceBack(Move(copy), globalDataOffset);
 }
 
 bool
 ModuleGenerator::allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset)
 {
-    uint32_t globalBytes = module_->globalBytes;
-
-    uint32_t pad = ComputeByteAlignment(globalBytes, align);
-    if (UINT32_MAX - globalBytes < pad + bytes)
+    uint32_t pad = ComputeByteAlignment(globalDataLength_, align);
+    if (UINT32_MAX - globalDataLength_ < pad + bytes)
         return false;
 
-    globalBytes += pad;
-    *globalDataOffset = globalBytes;
-    globalBytes += bytes;
+    globalDataLength_ += pad;
+    *globalDataOffset = globalDataLength_;
+    globalDataLength_ += bytes;
 
-    module_->globalBytes = globalBytes;
     return true;
 }
 
 bool
 ModuleGenerator::allocateGlobal(ValType type, bool isConst, uint32_t* index)
 {
     MOZ_ASSERT(!startedFuncDefs_);
     unsigned width = 0;
@@ -596,25 +592,25 @@ ModuleGenerator::allocateGlobal(ValType 
 
     *index = shared_->globals.length();
     return shared_->globals.append(GlobalDesc(type, offset, isConst));
 }
 
 void
 ModuleGenerator::initHeapUsage(HeapUsage heapUsage, uint32_t minHeapLength)
 {
-    MOZ_ASSERT(module_->heapUsage == HeapUsage::None);
-    module_->heapUsage = heapUsage;
+    MOZ_ASSERT(metadata_->heapUsage == HeapUsage::None);
+    metadata_->heapUsage = heapUsage;
     shared_->minHeapLength = minHeapLength;
 }
 
 bool
 ModuleGenerator::usesHeap() const
 {
-    return UsesHeap(module_->heapUsage);
+    return UsesHeap(metadata_->heapUsage);
 }
 
 void
 ModuleGenerator::initSig(uint32_t sigIndex, Sig&& sig)
 {
     MOZ_ASSERT(isAsmJS());
     MOZ_ASSERT(sigIndex == numSigs_);
     numSigs_++;
@@ -629,20 +625,18 @@ ModuleGenerator::sig(uint32_t index) con
     MOZ_ASSERT(index < numSigs_);
     return shared_->sigs[index];
 }
 
 void
 ModuleGenerator::initFuncSig(uint32_t funcIndex, uint32_t sigIndex)
 {
     MOZ_ASSERT(isAsmJS());
-    MOZ_ASSERT(funcIndex == module_->numFuncs);
     MOZ_ASSERT(!shared_->funcSigs[funcIndex]);
 
-    module_->numFuncs++;
     shared_->funcSigs[funcIndex] = &shared_->sigs[sigIndex];
 }
 
 void
 ModuleGenerator::bumpMinHeapLength(uint32_t newMinHeapLength)
 {
     MOZ_ASSERT(isAsmJS());
     MOZ_ASSERT(newMinHeapLength >= shared_->minHeapLength);
@@ -661,31 +655,31 @@ bool
 ModuleGenerator::initImport(uint32_t importIndex, uint32_t sigIndex)
 {
     MOZ_ASSERT(isAsmJS());
 
     uint32_t globalDataOffset;
     if (!allocateGlobalBytes(Module::SizeOfImportExit, sizeof(void*), &globalDataOffset))
         return false;
 
-    MOZ_ASSERT(importIndex == module_->imports.length());
+    MOZ_ASSERT(importIndex == metadata_->imports.length());
     if (!addImport(sig(sigIndex), globalDataOffset))
         return false;
 
     ImportModuleGeneratorData& import = shared_->imports[importIndex];
     MOZ_ASSERT(!import.sig);
     import.sig = &shared_->sigs[sigIndex];
     import.globalDataOffset = globalDataOffset;
     return true;
 }
 
 uint32_t
 ModuleGenerator::numImports() const
 {
-    return module_->imports.length();
+    return metadata_->imports.length();
 }
 
 const ImportModuleGeneratorData&
 ModuleGenerator::import(uint32_t index) const
 {
     MOZ_ASSERT(shared_->imports[index].sig);
     return shared_->imports[index];
 }
@@ -698,36 +692,36 @@ ModuleGenerator::declareExport(UniqueCha
 
     FuncIndexMap::AddPtr p = funcIndexToExport_.lookupForAdd(funcIndex);
     if (p) {
         if (exportIndex)
             *exportIndex = p->value();
         return exportMap_->fieldsToExports.append(p->value());
     }
 
-    uint32_t newExportIndex = module_->exports.length();
+    uint32_t newExportIndex = metadata_->exports.length();
     MOZ_ASSERT(newExportIndex < MaxExports);
 
     if (exportIndex)
         *exportIndex = newExportIndex;
 
     Sig copy;
     if (!copy.clone(funcSig(funcIndex)))
         return false;
 
-    return module_->exports.append(Move(copy)) &&
+    return metadata_->exports.append(Move(copy)) &&
            funcIndexToExport_.add(p, funcIndex, newExportIndex) &&
            exportMap_->fieldsToExports.append(newExportIndex) &&
            exportMap_->exportFuncIndices.append(funcIndex);
 }
 
 uint32_t
 ModuleGenerator::numExports() const
 {
-    return module_->exports.length();
+    return metadata_->exports.length();
 }
 
 bool
 ModuleGenerator::addMemoryExport(UniqueChars fieldName)
 {
     return exportMap_->fieldNames.append(Move(fieldName)) &&
            exportMap_->fieldsToExports.append(MemoryExport);
 }
@@ -844,17 +838,17 @@ ModuleGenerator::finishFuncDefs()
     while (outstanding_ > 0) {
         if (!finishOutstandingTask())
             return false;
     }
 
     for (uint32_t funcIndex = 0; funcIndex < funcIndexToCodeRange_.length(); funcIndex++)
         MOZ_ASSERT(funcIsDefined(funcIndex));
 
-    module_->functionBytes = masm_.size();
+    metadata_->functionLength = masm_.size();
     finishedFuncDefs_ = true;
     return true;
 }
 
 bool
 ModuleGenerator::initSigTableLength(uint32_t sigIndex, uint32_t numElems)
 {
     MOZ_ASSERT(isAsmJS());
@@ -882,74 +876,76 @@ ModuleGenerator::initSigTableElems(uint3
     MOZ_ASSERT(table.numElems == elemFuncIndices.length());
 
     MOZ_ASSERT(table.elemFuncIndices.empty());
     table.elemFuncIndices = Move(elemFuncIndices);
 }
 
 bool
 ModuleGenerator::finish(CacheableCharsVector&& prettyFuncNames,
-                        UniqueModuleData* module,
-                        UniqueStaticLinkData* linkData,
-                        UniqueExportMap* exportMap,
+                        UniqueCodeSegment* codeSegment,
+                        SharedMetadata* metadata,
+                        SharedStaticLinkData* staticLinkDataOut,
+                        SharedExportMap* exportMap,
                         SlowFunctionVector* slowFuncs)
 {
     MOZ_ASSERT(!activeFunc_);
     MOZ_ASSERT(finishedFuncDefs_);
 
-    UniqueStaticLinkData link = MakeUnique<StaticLinkData>();
-    if (!link)
+    MutableStaticLinkData staticLinkData = js_new<StaticLinkData>();
+    if (!staticLinkData)
         return false;
 
-    if (!finishCodegen(link.get()))
+    if (!finishCodegen(staticLinkData.get()))
         return false;
 
-    module_->prettyFuncNames = Move(prettyFuncNames);
+    metadata_->prettyFuncNames = Move(prettyFuncNames);
 
     // Start global data on a new page so JIT code may be given independent
     // protection flags. Note assumption that global data starts right after
     // code below.
-    module_->codeBytes = AlignBytes(masm_.bytesNeeded(), gc::SystemPageSize());
+    uint32_t codeLength = AlignBytes(masm_.bytesNeeded(), gc::SystemPageSize());
 
     // Inflate the global bytes up to page size so that the total bytes are a
     // page size (as required by the allocator functions).
-    module_->globalBytes = AlignBytes(module_->globalBytes, gc::SystemPageSize());
+    globalDataLength_ = AlignBytes(globalDataLength_, gc::SystemPageSize());
 
     // Allocate the code (guarded by a UniquePtr until it is given to the Module).
-    module_->code = AllocateCode(cx_, module_->totalBytes());
-    if (!module_->code)
+    UniqueCodeSegment cs = CodeSegment::allocate(cx_, codeLength, globalDataLength_);
+    if (!cs)
         return false;
 
     // Delay flushing until Module::dynamicallyLink. The flush-inhibited range
     // is set by executableCopy.
     AutoFlushICache afc("ModuleGenerator::finish", /* inhibit = */ true);
-    masm_.executableCopy(module_->code.get());
+    masm_.executableCopy(cs->code());
 
     // c.f. JitCode::copyFrom
     MOZ_ASSERT(masm_.jumpRelocationTableBytes() == 0);
     MOZ_ASSERT(masm_.dataRelocationTableBytes() == 0);
     MOZ_ASSERT(masm_.preBarrierTableBytes() == 0);
     MOZ_ASSERT(!masm_.hasSelfReference());
 
     // Convert the CallSiteAndTargetVector (needed during generation) to a
     // CallSiteVector (what is stored in the Module).
-    if (!module_->callSites.appendAll(masm_.callSites()))
+    if (!metadata_->callSites.appendAll(masm_.callSites()))
         return false;
 
     // The MacroAssembler has accumulated all the heap accesses during codegen.
-    module_->heapAccesses = masm_.extractHeapAccesses();
+    metadata_->heapAccesses = masm_.extractHeapAccesses();
 
-    if (!finishStaticLinkData(module_->code.get(), module_->codeBytes, link.get()))
+    if (!finishStaticLinkData(cs->code(), cs->codeLength(), staticLinkData.get()))
         return false;
 
     // These Vectors can get large and the excess capacity can be significant,
     // so realloc them down to size.
-    module_->heapAccesses.podResizeToFit();
-    module_->codeRanges.podResizeToFit();
-    module_->callSites.podResizeToFit();
-    module_->callThunks.podResizeToFit();
+    metadata_->heapAccesses.podResizeToFit();
+    metadata_->codeRanges.podResizeToFit();
+    metadata_->callSites.podResizeToFit();
+    metadata_->callThunks.podResizeToFit();
 
-    *module = Move(module_);
-    *linkData = Move(link);
-    *exportMap = Move(exportMap_);
+    *codeSegment = Move(cs);
+    *metadata = metadata_.forget();
+    *staticLinkDataOut = staticLinkData.forget();
+    *exportMap = exportMap_.forget();
     *slowFuncs = Move(slowFuncs_);
     return true;
 }
--- a/js/src/asmjs/WasmGenerator.h
+++ b/js/src/asmjs/WasmGenerator.h
@@ -114,18 +114,19 @@ typedef UniquePtr<ModuleGeneratorData> U
 class MOZ_STACK_CLASS ModuleGenerator
 {
     typedef HashMap<uint32_t, uint32_t> FuncIndexMap;
 
     ExclusiveContext*               cx_;
     jit::JitContext                 jcx_;
 
     // Data handed back to the caller in finish()
-    UniqueModuleData                module_;
-    UniqueExportMap                 exportMap_;
+    uint32_t                        globalDataLength_;
+    MutableMetadata                 metadata_;
+    MutableExportMap                exportMap_;
     SlowFunctionVector              slowFuncs_;
 
     // Data scoped to the ModuleGenerator's lifetime
     UniqueModuleGeneratorData       shared_;
     uint32_t                        numSigs_;
     LifoAlloc                       lifo_;
     jit::TempAllocator              alloc_;
     jit::MacroAssembler             masm_;
@@ -147,40 +148,40 @@ class MOZ_STACK_CLASS ModuleGenerator
     DebugOnly<bool>                 finishedFuncDefs_;
 
     MOZ_MUST_USE bool finishOutstandingTask();
     bool funcIsDefined(uint32_t funcIndex) const;
     const CodeRange& funcCodeRange(uint32_t funcIndex) const;
     MOZ_MUST_USE bool convertOutOfRangeBranchesToThunks();
     MOZ_MUST_USE bool finishTask(IonCompileTask* task);
     MOZ_MUST_USE bool finishCodegen(StaticLinkData* link);
-    MOZ_MUST_USE bool finishStaticLinkData(uint8_t* code, uint32_t codeBytes, StaticLinkData* link);
+    MOZ_MUST_USE bool finishStaticLinkData(uint8_t* code, uint32_t codeLength, StaticLinkData* link);
     MOZ_MUST_USE bool addImport(const Sig& sig, uint32_t globalDataOffset);
     MOZ_MUST_USE bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOff);
 
   public:
     explicit ModuleGenerator(ExclusiveContext* cx);
     ~ModuleGenerator();
 
     MOZ_MUST_USE bool init(UniqueModuleGeneratorData shared, UniqueChars filename);
 
-    bool isAsmJS() const { return module_->kind == ModuleKind::AsmJS; }
-    CompileArgs args() const { return module_->compileArgs; }
+    bool isAsmJS() const { return metadata_->kind == ModuleKind::AsmJS; }
+    CompileArgs args() const { return metadata_->compileArgs; }
     jit::MacroAssembler& masm() { return masm_; }
 
     // Heap usage:
     void initHeapUsage(HeapUsage heapUsage, uint32_t minHeapLength = 0);
     bool usesHeap() const;
 
     // Signatures:
     uint32_t numSigs() const { return numSigs_; }
     const DeclaredSig& sig(uint32_t sigIndex) const;
 
     // Function declarations:
-    uint32_t numFuncSigs() const { return module_->numFuncs; }
+    uint32_t numFuncSigs() const { return shared_->funcSigs.length(); }
     const DeclaredSig& funcSig(uint32_t funcIndex) const;
 
     // Globals:
     MOZ_MUST_USE bool allocateGlobal(ValType type, bool isConst, uint32_t* index);
     const GlobalDesc& global(unsigned index) const { return shared_->globals[index]; }
 
     // Imports:
     uint32_t numImports() const;
@@ -202,23 +203,24 @@ class MOZ_STACK_CLASS ModuleGenerator
     // asm.js lazy initialization:
     void initSig(uint32_t sigIndex, Sig&& sig);
     void initFuncSig(uint32_t funcIndex, uint32_t sigIndex);
     MOZ_MUST_USE bool initImport(uint32_t importIndex, uint32_t sigIndex);
     MOZ_MUST_USE bool initSigTableLength(uint32_t sigIndex, uint32_t numElems);
     void initSigTableElems(uint32_t sigIndex, Uint32Vector&& elemFuncIndices);
     void bumpMinHeapLength(uint32_t newMinHeapLength);
 
-    // Return a ModuleData object which may be used to construct a Module, the
+    // Return a Metadata object which may be used to construct a Module, the
     // StaticLinkData required to call Module::staticallyLink, and the list of
     // functions that took a long time to compile.
     MOZ_MUST_USE bool finish(CacheableCharsVector&& prettyFuncNames,
-                             UniqueModuleData* module,
-                             UniqueStaticLinkData* staticLinkData,
-                             UniqueExportMap* exportMap,
+                             UniqueCodeSegment* codeSegment,
+                             SharedMetadata* metadata,
+                             SharedStaticLinkData* staticLinkData,
+                             SharedExportMap* exportMap,
                              SlowFunctionVector* slowFuncs);
 };
 
 // A FunctionGenerator encapsulates the generation of a single function body.
 // ModuleGenerator::startFunc must be called after construction and before doing
 // anything else. After the body is complete, ModuleGenerator::finishFunc must
 // be called before the FunctionGenerator is destroyed and the next function is
 // started.
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -29,75 +29,39 @@
 #include "asmjs/WasmBinaryToText.h"
 #include "asmjs/WasmSerialize.h"
 #include "builtin/AtomicsObject.h"
 #include "builtin/SIMD.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "jit/BaselineJIT.h"
-#include "jit/ExecutableAllocator.h"
 #include "jit/JitCommon.h"
 #include "js/MemoryMetrics.h"
 #include "vm/StringBuffer.h"
 #ifdef MOZ_VTUNE
 # include "vtune/VTuneWrapper.h"
 #endif
 
 #include "jsobjinlines.h"
 
 #include "jit/MacroAssembler-inl.h"
 #include "vm/ArrayBufferObject-inl.h"
 #include "vm/TypeInference-inl.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
-using mozilla::Atomic;
 using mozilla::BinarySearch;
 using mozilla::MakeEnumeratedRange;
 using mozilla::PodCopy;
 using mozilla::PodZero;
 using mozilla::Swap;
 using JS::GenericNaN;
 
-// Limit the number of concurrent wasm code allocations per process. Note that
-// on Linux, the real maximum is ~32k, as each module requires 2 maps (RW/RX),
-// and the kernel's default max_map_count is ~65k.
-static Atomic<uint32_t> wasmCodeAllocations(0);
-static const uint32_t MaxWasmCodeAllocations = 16384;
-
-UniqueCodePtr
-wasm::AllocateCode(ExclusiveContext* cx, size_t bytes)
-{
-    // Allocate RW memory. DynamicallyLinkModule will reprotect the code as RX.
-    unsigned permissions =
-        ExecutableAllocator::initialProtectionFlags(ExecutableAllocator::Writable);
-
-    void* p = nullptr;
-    if (wasmCodeAllocations++ < MaxWasmCodeAllocations)
-        p = AllocateExecutableMemory(nullptr, bytes, permissions, "asm-js-code", gc::SystemPageSize());
-    if (!p) {
-        wasmCodeAllocations--;
-        ReportOutOfMemory(cx);
-    }
-
-    return UniqueCodePtr((uint8_t*)p, CodeDeleter(bytes));
-}
-
-void
-CodeDeleter::operator()(uint8_t* p)
-{
-    MOZ_ASSERT(wasmCodeAllocations > 0);
-    wasmCodeAllocations--;
-
-    MOZ_ASSERT(bytes_ != 0);
-    DeallocateExecutableMemory(p, bytes_, gc::SystemPageSize());
-}
-
 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
 // On MIPS, CodeLabels are instruction immediates so InternalLinks only
 // patch instruction immediates.
 StaticLinkData::InternalLink::InternalLink(Kind kind)
 {
     MOZ_ASSERT(kind == CodeLabel || kind == InstructionImmediate);
 }
 
@@ -144,26 +108,16 @@ StaticLinkData::SymbolicLinkArray::deser
     for (Uint32Vector& offsets : *this) {
         cursor = DeserializePodVector(cx, cursor, &offsets);
         if (!cursor)
             return nullptr;
     }
     return cursor;
 }
 
-bool
-StaticLinkData::SymbolicLinkArray::clone(JSContext* cx, SymbolicLinkArray* out) const
-{
-    for (auto imm : MakeEnumeratedRange(SymbolicAddress::Limit)) {
-        if (!ClonePodVector(cx, (*this)[imm], &(*out)[imm]))
-            return false;
-    }
-    return true;
-}
-
 size_t
 StaticLinkData::SymbolicLinkArray::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     size_t size = 0;
     for (const Uint32Vector& offsets : *this)
         size += offsets.sizeOfExcludingThis(mallocSizeOf);
     return size;
 }
@@ -186,23 +140,16 @@ StaticLinkData::FuncPtrTable::serialize(
 const uint8_t*
 StaticLinkData::FuncPtrTable::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
 {
     (cursor = ReadBytes(cursor, &globalDataOffset, sizeof(globalDataOffset))) &&
     (cursor = DeserializePodVector(cx, cursor, &elemOffsets));
     return cursor;
 }
 
-bool
-StaticLinkData::FuncPtrTable::clone(JSContext* cx, FuncPtrTable* out) const
-{
-    out->globalDataOffset = globalDataOffset;
-    return ClonePodVector(cx, elemOffsets, &out->elemOffsets);
-}
-
 size_t
 StaticLinkData::FuncPtrTable::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return elemOffsets.sizeOfExcludingThis(mallocSizeOf);
 }
 
 size_t
 StaticLinkData::serializedSize() const
@@ -228,254 +175,24 @@ StaticLinkData::deserialize(ExclusiveCon
 {
     (cursor = ReadBytes(cursor, &pod, sizeof(pod))) &&
     (cursor = DeserializePodVector(cx, cursor, &internalLinks)) &&
     (cursor = symbolicLinks.deserialize(cx, cursor)) &&
     (cursor = DeserializeVector(cx, cursor, &funcPtrTables));
     return cursor;
 }
 
-bool
-StaticLinkData::clone(JSContext* cx, StaticLinkData* out) const
-{
-    out->pod = pod;
-    return ClonePodVector(cx, internalLinks, &out->internalLinks) &&
-           symbolicLinks.clone(cx, &out->symbolicLinks) &&
-           CloneVector(cx, funcPtrTables, &out->funcPtrTables);
-}
-
 size_t
 StaticLinkData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return internalLinks.sizeOfExcludingThis(mallocSizeOf) +
            symbolicLinks.sizeOfExcludingThis(mallocSizeOf) +
            SizeOfVectorExcludingThis(funcPtrTables, mallocSizeOf);
 }
 
-static size_t
-SerializedSigSize(const Sig& sig)
-{
-    return sizeof(ExprType) +
-           SerializedPodVectorSize(sig.args());
-}
-
-static uint8_t*
-SerializeSig(uint8_t* cursor, const Sig& sig)
-{
-    cursor = WriteScalar<ExprType>(cursor, sig.ret());
-    cursor = SerializePodVector(cursor, sig.args());
-    return cursor;
-}
-
-static const uint8_t*
-DeserializeSig(ExclusiveContext* cx, const uint8_t* cursor, Sig* sig)
-{
-    ExprType ret;
-    cursor = ReadScalar<ExprType>(cursor, &ret);
-
-    ValTypeVector args;
-    cursor = DeserializePodVector(cx, cursor, &args);
-    if (!cursor)
-        return nullptr;
-
-    *sig = Sig(Move(args), ret);
-    return cursor;
-}
-
-static size_t
-SizeOfSigExcludingThis(const Sig& sig, MallocSizeOf mallocSizeOf)
-{
-    return sig.args().sizeOfExcludingThis(mallocSizeOf);
-}
-
-size_t
-Export::serializedSize() const
-{
-    return SerializedSigSize(sig_) +
-           sizeof(pod);
-}
-
-uint8_t*
-Export::serialize(uint8_t* cursor) const
-{
-    cursor = SerializeSig(cursor, sig_);
-    cursor = WriteBytes(cursor, &pod, sizeof(pod));
-    return cursor;
-}
-
-const uint8_t*
-Export::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
-{
-    (cursor = DeserializeSig(cx, cursor, &sig_)) &&
-    (cursor = ReadBytes(cursor, &pod, sizeof(pod)));
-    return cursor;
-}
-
-bool
-Export::clone(JSContext* cx, Export* out) const
-{
-    out->pod = pod;
-    return out->sig_.clone(sig_);
-}
-
-size_t
-Export::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
-{
-    return SizeOfSigExcludingThis(sig_, mallocSizeOf);
-}
-
-size_t
-Import::serializedSize() const
-{
-    return SerializedSigSize(sig_) +
-           sizeof(pod);
-}
-
-uint8_t*
-Import::serialize(uint8_t* cursor) const
-{
-    cursor = SerializeSig(cursor, sig_);
-    cursor = WriteBytes(cursor, &pod, sizeof(pod));
-    return cursor;
-}
-
-const uint8_t*
-Import::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
-{
-    (cursor = DeserializeSig(cx, cursor, &sig_)) &&
-    (cursor = ReadBytes(cursor, &pod, sizeof(pod)));
-    return cursor;
-}
-
-bool
-Import::clone(JSContext* cx, Import* out) const
-{
-    out->pod = pod;
-    return out->sig_.clone(sig_);
-}
-
-size_t
-Import::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
-{
-    return SizeOfSigExcludingThis(sig_, mallocSizeOf);
-}
-
-CodeRange::CodeRange(Kind kind, Offsets offsets)
-  : begin_(offsets.begin),
-    profilingReturn_(0),
-    end_(offsets.end),
-    funcIndex_(0),
-    funcLineOrBytecode_(0),
-    funcBeginToTableEntry_(0),
-    funcBeginToTableProfilingJump_(0),
-    funcBeginToNonProfilingEntry_(0),
-    funcProfilingJumpToProfilingReturn_(0),
-    funcProfilingEpilogueToProfilingReturn_(0),
-    kind_(kind)
-{
-    MOZ_ASSERT(begin_ <= end_);
-    MOZ_ASSERT(kind_ == Entry || kind_ == Inline || kind_ == CallThunk);
-}
-
-CodeRange::CodeRange(Kind kind, ProfilingOffsets offsets)
-  : begin_(offsets.begin),
-    profilingReturn_(offsets.profilingReturn),
-    end_(offsets.end),
-    funcIndex_(0),
-    funcLineOrBytecode_(0),
-    funcBeginToTableEntry_(0),
-    funcBeginToTableProfilingJump_(0),
-    funcBeginToNonProfilingEntry_(0),
-    funcProfilingJumpToProfilingReturn_(0),
-    funcProfilingEpilogueToProfilingReturn_(0),
-    kind_(kind)
-{
-    MOZ_ASSERT(begin_ < profilingReturn_);
-    MOZ_ASSERT(profilingReturn_ < end_);
-    MOZ_ASSERT(kind_ == ImportJitExit || kind_ == ImportInterpExit);
-}
-
-CodeRange::CodeRange(uint32_t funcIndex, uint32_t funcLineOrBytecode, FuncOffsets offsets)
-  : begin_(offsets.begin),
-    profilingReturn_(offsets.profilingReturn),
-    end_(offsets.end),
-    funcIndex_(funcIndex),
-    funcLineOrBytecode_(funcLineOrBytecode),
-    funcBeginToTableEntry_(offsets.tableEntry - begin_),
-    funcBeginToTableProfilingJump_(offsets.tableProfilingJump - begin_),
-    funcBeginToNonProfilingEntry_(offsets.nonProfilingEntry - begin_),
-    funcProfilingJumpToProfilingReturn_(profilingReturn_ - offsets.profilingJump),
-    funcProfilingEpilogueToProfilingReturn_(profilingReturn_ - offsets.profilingEpilogue),
-    kind_(Function)
-{
-    MOZ_ASSERT(begin_ < profilingReturn_);
-    MOZ_ASSERT(profilingReturn_ < end_);
-    MOZ_ASSERT(funcBeginToTableEntry_ == offsets.tableEntry - begin_);
-    MOZ_ASSERT(funcBeginToTableProfilingJump_ == offsets.tableProfilingJump - begin_);
-    MOZ_ASSERT(funcBeginToNonProfilingEntry_ == offsets.nonProfilingEntry - begin_);
-    MOZ_ASSERT(funcProfilingJumpToProfilingReturn_ == profilingReturn_ - offsets.profilingJump);
-    MOZ_ASSERT(funcProfilingEpilogueToProfilingReturn_ == profilingReturn_ - offsets.profilingEpilogue);
-}
-
-static size_t
-NullableStringLength(const char* chars)
-{
-    return chars ? strlen(chars) : 0;
-}
-
-size_t
-CacheableChars::serializedSize() const
-{
-    return sizeof(uint32_t) + NullableStringLength(get());
-}
-
-uint8_t*
-CacheableChars::serialize(uint8_t* cursor) const
-{
-    uint32_t length = NullableStringLength(get());
-    cursor = WriteBytes(cursor, &length, sizeof(uint32_t));
-    cursor = WriteBytes(cursor, get(), length);
-    return cursor;
-}
-
-const uint8_t*
-CacheableChars::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
-{
-    uint32_t length;
-    cursor = ReadBytes(cursor, &length, sizeof(uint32_t));
-
-    reset(cx->pod_calloc<char>(length + 1));
-    if (!get())
-        return nullptr;
-
-    cursor = ReadBytes(cursor, get(), length);
-    return cursor;
-}
-
-bool
-CacheableChars::clone(JSContext* cx, CacheableChars* out) const
-{
-    uint32_t length = NullableStringLength(get());
-
-    UniqueChars chars(cx->pod_calloc<char>(length + 1));
-    if (!chars)
-        return false;
-
-    PodCopy(chars.get(), get(), length);
-
-    *out = Move(chars);
-    return true;
-}
-
-size_t
-CacheableChars::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
-{
-    return mallocSizeOf(get());
-}
-
 size_t
 ExportMap::serializedSize() const
 {
     return SerializedVectorSize(fieldNames) +
            SerializedPodVectorSize(fieldsToExports) +
            SerializedPodVectorSize(exportFuncIndices);
 }
 
@@ -492,118 +209,24 @@ const uint8_t*
 ExportMap::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
 {
     (cursor = DeserializeVector(cx, cursor, &fieldNames)) &&
     (cursor = DeserializePodVector(cx, cursor, &fieldsToExports)) &&
     (cursor = DeserializePodVector(cx, cursor, &exportFuncIndices));
     return cursor;
 }
 
-bool
-ExportMap::clone(JSContext* cx, ExportMap* map) const
-{
-    return CloneVector(cx, fieldNames, &map->fieldNames) &&
-           ClonePodVector(cx, fieldsToExports, &map->fieldsToExports) &&
-           ClonePodVector(cx, exportFuncIndices, &map->exportFuncIndices);
-}
-
 size_t
 ExportMap::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return SizeOfVectorExcludingThis(fieldNames, mallocSizeOf) &&
            fieldsToExports.sizeOfExcludingThis(mallocSizeOf) &&
            exportFuncIndices.sizeOfExcludingThis(mallocSizeOf);
 }
 
-size_t
-ModuleData::serializedSize() const
-{
-    return sizeof(pod()) +
-           codeBytes +
-           SerializedVectorSize(imports) +
-           SerializedVectorSize(exports) +
-           SerializedPodVectorSize(heapAccesses) +
-           SerializedPodVectorSize(codeRanges) +
-           SerializedPodVectorSize(callSites) +
-           SerializedPodVectorSize(callThunks) +
-           SerializedVectorSize(prettyFuncNames) +
-           filename.serializedSize();
-}
-
-uint8_t*
-ModuleData::serialize(uint8_t* cursor) const
-{
-    cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
-    cursor = WriteBytes(cursor, code.get(), codeBytes);
-    cursor = SerializeVector(cursor, imports);
-    cursor = SerializeVector(cursor, exports);
-    cursor = SerializePodVector(cursor, heapAccesses);
-    cursor = SerializePodVector(cursor, codeRanges);
-    cursor = SerializePodVector(cursor, callSites);
-    cursor = SerializePodVector(cursor, callThunks);
-    cursor = SerializeVector(cursor, prettyFuncNames);
-    cursor = filename.serialize(cursor);
-    return cursor;
-}
-
-/* static */ const uint8_t*
-ModuleData::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
-{
-    cursor = ReadBytes(cursor, &pod(), sizeof(pod()));
-
-    code = AllocateCode(cx, totalBytes());
-    if (!code)
-        return nullptr;
-    cursor = ReadBytes(cursor, code.get(), codeBytes);
-
-    (cursor = DeserializeVector(cx, cursor, &imports)) &&
-    (cursor = DeserializeVector(cx, cursor, &exports)) &&
-    (cursor = DeserializePodVector(cx, cursor, &heapAccesses)) &&
-    (cursor = DeserializePodVector(cx, cursor, &codeRanges)) &&
-    (cursor = DeserializePodVector(cx, cursor, &callSites)) &&
-    (cursor = DeserializePodVector(cx, cursor, &callThunks)) &&
-    (cursor = DeserializeVector(cx, cursor, &prettyFuncNames)) &&
-    (cursor = filename.deserialize(cx, cursor));
-    return cursor;
-}
-
-bool
-ModuleData::clone(JSContext* cx, ModuleData* out) const
-{
-    out->pod() = pod();
-
-    out->code = AllocateCode(cx, totalBytes());
-    if (!out->code)
-        return false;
-    memcpy(out->code.get(), code.get(), codeBytes);
-
-    return CloneVector(cx, imports, &out->imports) &&
-           CloneVector(cx, exports, &out->exports) &&
-           ClonePodVector(cx, heapAccesses, &out->heapAccesses) &&
-           ClonePodVector(cx, codeRanges, &out->codeRanges) &&
-           ClonePodVector(cx, callSites, &out->callSites) &&
-           ClonePodVector(cx, callThunks, &out->callThunks) &&
-           CloneVector(cx, prettyFuncNames, &out->prettyFuncNames) &&
-           filename.clone(cx, &out->filename);
-}
-
-size_t
-ModuleData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
-{
-    // Module::addSizeOfMisc takes care of code and global memory.
-    return SizeOfVectorExcludingThis(imports, mallocSizeOf) +
-           SizeOfVectorExcludingThis(exports, mallocSizeOf) +
-           heapAccesses.sizeOfExcludingThis(mallocSizeOf) +
-           codeRanges.sizeOfExcludingThis(mallocSizeOf) +
-           callSites.sizeOfExcludingThis(mallocSizeOf) +
-           callThunks.sizeOfExcludingThis(mallocSizeOf) +
-           SizeOfVectorExcludingThis(prettyFuncNames, mallocSizeOf) +
-           filename.sizeOfExcludingThis(mallocSizeOf);
-}
-
 uint8_t*
 Module::rawHeapPtr() const
 {
     return const_cast<Module*>(this)->rawHeapPtr();
 }
 
 uint8_t*&
 Module::rawHeapPtr()
@@ -629,40 +252,40 @@ Module::specializeToHeap(ArrayBufferObje
     uint8_t* ptrBase = heap->dataPointerEither().unwrap(/*safe - protected by Module methods*/);
     uint32_t heapLength = heap->byteLength();
 #if defined(JS_CODEGEN_X86)
     // An access is out-of-bounds iff
     //      ptr + offset + data-type-byte-size > heapLength
     // i.e. ptr > heapLength - data-type-byte-size - offset. data-type-byte-size
     // and offset are already included in the addend so we
     // just have to add the heap length here.
-    for (const HeapAccess& access : module_->heapAccesses) {
+    for (const HeapAccess& access : metadata_->heapAccesses) {
         if (access.hasLengthCheck())
             X86Encoding::AddInt32(access.patchLengthAt(code()), heapLength);
         void* addr = access.patchHeapPtrImmAt(code());
         uint32_t disp = reinterpret_cast<uint32_t>(X86Encoding::GetPointer(addr));
         MOZ_ASSERT(disp <= INT32_MAX);
         X86Encoding::SetPointer(addr, (void*)(ptrBase + disp));
     }
 #elif defined(JS_CODEGEN_X64)
     // Even with signal handling being used for most bounds checks, there may be
     // atomic operations that depend on explicit checks.
     //
     // If we have any explicit bounds checks, we need to patch the heap length
     // checks at the right places. All accesses that have been recorded are the
     // only ones that need bound checks (see also
     // CodeGeneratorX64::visitAsmJS{Load,Store,CompareExchange,Exchange,AtomicBinop}Heap)
-    for (const HeapAccess& access : module_->heapAccesses) {
+    for (const HeapAccess& access : metadata_->heapAccesses) {
         // See comment above for x86 codegen.
         if (access.hasLengthCheck())
             X86Encoding::AddInt32(access.patchLengthAt(code()), heapLength);
     }
 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
       defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
-    for (const HeapAccess& access : module_->heapAccesses)
+    for (const HeapAccess& access : metadata_->heapAccesses)
         Assembler::UpdateBoundsCheck(heapLength, (Instruction*)(access.insnOffset() + code()));
 #endif
 
     heap_ = heap;
     rawHeapPtr() = ptrBase;
 }
 
 void
@@ -672,29 +295,29 @@ Module::despecializeFromHeap(ArrayBuffer
     // another dynamically-linked module which we are despecializing from that
     // module's heap.
     MOZ_ASSERT_IF(heap_, heap_ == heap);
     MOZ_ASSERT_IF(rawHeapPtr(), rawHeapPtr() == heap->dataPointerEither().unwrap());
 
 #if defined(JS_CODEGEN_X86)
     uint32_t heapLength = heap->byteLength();
     uint8_t* ptrBase = heap->dataPointerEither().unwrap(/*safe - used for value*/);
-    for (unsigned i = 0; i < module_->heapAccesses.length(); i++) {
-        const HeapAccess& access = module_->heapAccesses[i];
+    for (unsigned i = 0; i < metadata_->heapAccesses.length(); i++) {
+        const HeapAccess& access = metadata_->heapAccesses[i];
         if (access.hasLengthCheck())
             X86Encoding::AddInt32(access.patchLengthAt(code()), -heapLength);
         void* addr = access.patchHeapPtrImmAt(code());
         uint8_t* ptr = reinterpret_cast<uint8_t*>(X86Encoding::GetPointer(addr));
         MOZ_ASSERT(ptr >= ptrBase);
         X86Encoding::SetPointer(addr, reinterpret_cast<void*>(ptr - ptrBase));
     }
 #elif defined(JS_CODEGEN_X64)
     uint32_t heapLength = heap->byteLength();
-    for (unsigned i = 0; i < module_->heapAccesses.length(); i++) {
-        const HeapAccess& access = module_->heapAccesses[i];
+    for (unsigned i = 0; i < metadata_->heapAccesses.length(); i++) {
+        const HeapAccess& access = metadata_->heapAccesses[i];
         if (access.hasLengthCheck())
             X86Encoding::AddInt32(access.patchLengthAt(code()), -heapLength);
     }
 #endif
 
     heap_ = nullptr;
     rawHeapPtr() = nullptr;
 }
@@ -707,17 +330,17 @@ Module::sendCodeRangesToProfiler(JSConte
     enabled |= PerfFuncEnabled();
 #endif
 #ifdef MOZ_VTUNE
     enabled |= IsVTuneProfilingActive();
 #endif
     if (!enabled)
         return true;
 
-    for (const CodeRange& codeRange : module_->codeRanges) {
+    for (const CodeRange& codeRange : metadata_->codeRanges) {
         if (!codeRange.isFunction())
             continue;
 
         uintptr_t start = uintptr_t(code() + codeRange.begin());
         uintptr_t end = uintptr_t(code() + codeRange.end());
         uintptr_t size = end - start;
 
         UniqueChars owner;
@@ -727,17 +350,17 @@ Module::sendCodeRangesToProfiler(JSConte
 
         // Avoid "unused" warnings
         (void)start;
         (void)size;
         (void)name;
 
 #ifdef JS_ION_PERF
         if (PerfFuncEnabled()) {
-            const char* file = module_->filename.get();
+            const char* file = metadata_->filename.get();
             unsigned line = codeRange.funcLineOrBytecode();
             unsigned column = 0;
             writePerfSpewerAsmJSFunctionMap(start, size, file, line, column, name);
         }
 #endif
 #ifdef MOZ_VTUNE
         if (IsVTuneProfilingActive()) {
             unsigned method_id = iJIT_GetNewMethodID();
@@ -770,57 +393,57 @@ Module::setProfilingEnabled(JSContext* c
     if (profilingEnabled_ == enabled)
         return true;
 
     // When enabled, generate profiling labels for every name in funcNames_
     // that is the name of some Function CodeRange. This involves malloc() so
     // do it now since, once we start sampling, we'll be in a signal-handing
     // context where we cannot malloc.
     if (enabled) {
-        if (!funcLabels_.resize(module_->numFuncs)) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
-        for (const CodeRange& codeRange : module_->codeRanges) {
+        for (const CodeRange& codeRange : metadata_->codeRanges) {
             if (!codeRange.isFunction())
                 continue;
 
             UniqueChars owner;
             const char* funcName = getFuncName(cx, codeRange.funcIndex(), &owner);
             if (!funcName)
                 return false;
 
             UniqueChars label(JS_smprintf("%s (%s:%u)",
                                           funcName,
-                                          module_->filename.get(),
+                                          metadata_->filename.get(),
                                           codeRange.funcLineOrBytecode()));
             if (!label) {
                 ReportOutOfMemory(cx);
                 return false;
             }
 
+            if (codeRange.funcIndex() >= funcLabels_.length()) {
+                if (!funcLabels_.resize(codeRange.funcIndex() + 1))
+                    return false;
+            }
             funcLabels_[codeRange.funcIndex()] = Move(label);
         }
     } else {
         funcLabels_.clear();
     }
 
     // Patch callsites and returns to execute profiling prologues/epilogues.
     {
-        AutoWritableJitCode awjc(cx->runtime(), code(), codeBytes());
+        AutoWritableJitCode awjc(cx->runtime(), code(), codeLength());
         AutoFlushICache afc("Module::setProfilingEnabled");
-        AutoFlushICache::setRange(uintptr_t(code()), codeBytes());
+        AutoFlushICache::setRange(uintptr_t(code()), codeLength());
 
-        for (const CallSite& callSite : module_->callSites)
+        for (const CallSite& callSite : metadata_->callSites)
             ToggleProfiling(*this, callSite, enabled);
 
-        for (const CallThunk& callThunk : module_->callThunks)
+        for (const CallThunk& callThunk : metadata_->callThunks)
             ToggleProfiling(*this, callThunk, enabled);
 
-        for (const CodeRange& codeRange : module_->codeRanges)
+        for (const CodeRange& codeRange : metadata_->codeRanges)
             ToggleProfiling(*this, codeRange, enabled);
     }
 
     // In asm.js, table elements point directly to the prologue and must be
     // updated to reflect the profiling mode. In wasm, table elements point to
     // the (one) table entry which checks signature before jumping to the
     // appropriate prologue (which is patched by ToggleProfiling).
     if (isAsmJS()) {
@@ -848,62 +471,65 @@ Module::importToExit(const Import& impor
     return *reinterpret_cast<ImportExit*>(globalData() + import.exitGlobalDataOffset());
 }
 
 bool
 Module::clone(JSContext* cx, const StaticLinkData& link, Module* out) const
 {
     MOZ_ASSERT(dynamicallyLinked_);
 
-    // The out->module_ field was already cloned and initialized when 'out' was
+    // The out->metadata_ field was already cloned and initialized when 'out' was
     // constructed. This function should clone the rest.
-    MOZ_ASSERT(out->module_);
+    MOZ_ASSERT(out->metadata_);
 
+    // Copy the profiling state over too since the cloned machine code
+    // implicitly brings the profiling mode.
     out->profilingEnabled_ = profilingEnabled_;
-
-    if (!CloneVector(cx, funcLabels_, &out->funcLabels_))
-        return false;
+    for (const CacheableChars& label : funcLabels_) {
+        if (!out->funcLabels_.emplaceBack(DuplicateString(label.get())))
+            return false;
+    }
 
 #ifdef DEBUG
     // Put the symbolic links back to -1 so PatchDataWithValueCheck assertions
     // in Module::staticallyLink are valid.
     for (auto imm : MakeEnumeratedRange(SymbolicAddress::Limit)) {
         void* callee = AddressOf(imm, cx);
         const Uint32Vector& offsets = link.symbolicLinks[imm];
         for (uint32_t offset : offsets) {
-            jit::Assembler::PatchDataWithValueCheck(jit::CodeLocationLabel(out->code() + offset),
-                                                    jit::PatchedImmPtr((void*)-1),
-                                                    jit::PatchedImmPtr(callee));
+            Assembler::PatchDataWithValueCheck(CodeLocationLabel(out->code() + offset),
+                                               PatchedImmPtr((void*)-1),
+                                               PatchedImmPtr(callee));
         }
     }
 #endif
 
     // If the copied machine code has been specialized to the heap, it must be
     // unspecialized in the copy.
     if (usesHeap())
         out->despecializeFromHeap(heap_);
 
     return true;
 }
 
-
-Module::Module(UniqueModuleData module)
-  : module_(Move(module)),
+Module::Module(UniqueCodeSegment codeSegment, const Metadata& metadata)
+  : codeSegment_(Move(codeSegment)),
+    metadata_(&metadata),
     staticallyLinked_(false),
     interrupt_(nullptr),
     outOfBounds_(nullptr),
     dynamicallyLinked_(false),
     profilingEnabled_(false)
 {
     *(double*)(globalData() + NaN64GlobalDataOffset) = GenericNaN();
     *(float*)(globalData() + NaN32GlobalDataOffset) = GenericNaN();
 
 #ifdef DEBUG
     uint32_t lastEnd = 0;
-    for (const CodeRange& cr : module_->codeRanges) {
+    for (const CodeRange& cr : metadata_->codeRanges) {
         MOZ_ASSERT(cr.begin() >= lastEnd);
         lastEnd = cr.end();
     }
 #endif
 }
 
 Module::~Module()
 {
@@ -930,21 +556,21 @@ Module::trace(JSTracer* trc)
 Module::readBarrier()
 {
     InternalBarrierMethods<JSObject*>::readBarrier(owner());
 }
 
 /* virtual */ void
 Module::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data)
 {
-    *code += codeBytes();
+    *code += codeSegment_->codeLength();
     *data += mallocSizeOf(this) +
-             globalBytes() +
-             mallocSizeOf(module_.get()) +
-             module_->sizeOfExcludingThis(mallocSizeOf) +
+             codeSegment_->globalDataLength() +
+             mallocSizeOf(metadata_.get()) +
+             metadata_->sizeOfExcludingThis(mallocSizeOf) +
              source_.sizeOfExcludingThis(mallocSizeOf) +
              funcPtrTables_.sizeOfExcludingThis(mallocSizeOf) +
              SizeOfVectorExcludingThis(funcLabels_, mallocSizeOf);
 }
 
 /* virtual */ bool
 Module::mutedErrors() const
 {
@@ -959,60 +585,60 @@ Module::displayURL() const
 {
     // WebAssembly code does not have `//# sourceURL`.
     return nullptr;
 }
 
 bool
 Module::containsFunctionPC(void* pc) const
 {
-    return pc >= code() && pc < (code() + module_->functionBytes);
+    return pc >= code() && pc < (code() + metadata_->functionLength);
 }
 
 bool
 Module::containsCodePC(void* pc) const
 {
-    return pc >= code() && pc < (code() + codeBytes());
+    return pc >= code() && pc < (code() + codeLength());
 }
 
 struct CallSiteRetAddrOffset
 {
     const CallSiteVector& callSites;
     explicit CallSiteRetAddrOffset(const CallSiteVector& callSites) : callSites(callSites) {}
     uint32_t operator[](size_t index) const {
         return callSites[index].returnAddressOffset();
     }
 };
 
 const CallSite*
 Module::lookupCallSite(void* returnAddress) const
 {
     uint32_t target = ((uint8_t*)returnAddress) - code();
     size_t lowerBound = 0;
-    size_t upperBound = module_->callSites.length();
+    size_t upperBound = metadata_->callSites.length();
 
     size_t match;
-    if (!BinarySearch(CallSiteRetAddrOffset(module_->callSites), lowerBound, upperBound, target, &match))
+    if (!BinarySearch(CallSiteRetAddrOffset(metadata_->callSites), lowerBound, upperBound, target, &match))
         return nullptr;
 
-    return &module_->callSites[match];
+    return &metadata_->callSites[match];
 }
 
 const CodeRange*
 Module::lookupCodeRange(void* pc) const
 {
     CodeRange::PC target((uint8_t*)pc - code());
     size_t lowerBound = 0;
-    size_t upperBound = module_->codeRanges.length();
+    size_t upperBound = metadata_->codeRanges.length();
 
     size_t match;
-    if (!BinarySearch(module_->codeRanges, lowerBound, upperBound, target, &match))
+    if (!BinarySearch(metadata_->codeRanges, lowerBound, upperBound, target, &match))
         return nullptr;
 
-    return &module_->codeRanges[match];
+    return &metadata_->codeRanges[match];
 }
 
 struct HeapAccessOffset
 {
     const HeapAccessVector& accesses;
     explicit HeapAccessOffset(const HeapAccessVector& accesses) : accesses(accesses) {}
     uintptr_t operator[](size_t index) const {
         return accesses[index].insnOffset();
@@ -1021,38 +647,38 @@ struct HeapAccessOffset
 
 const HeapAccess*
 Module::lookupHeapAccess(void* pc) const
 {
     MOZ_ASSERT(containsFunctionPC(pc));
 
     uint32_t target = ((uint8_t*)pc) - code();
     size_t lowerBound = 0;
-    size_t upperBound = module_->heapAccesses.length();
+    size_t upperBound = metadata_->heapAccesses.length();
 
     size_t match;
-    if (!BinarySearch(HeapAccessOffset(module_->heapAccesses), lowerBound, upperBound, target, &match))
+    if (!BinarySearch(HeapAccessOffset(metadata_->heapAccesses), lowerBound, upperBound, target, &match))
         return nullptr;
 
-    return &module_->heapAccesses[match];
+    return &metadata_->heapAccesses[match];
 }
 
 bool
 Module::staticallyLink(ExclusiveContext* cx, const StaticLinkData& linkData)
 {
     MOZ_ASSERT(!dynamicallyLinked_);
     MOZ_ASSERT(!staticallyLinked_);
     staticallyLinked_ = true;
 
     // Push a JitContext for benefit of IsCompilingAsmJS and delay flushing
     // until Module::dynamicallyLink.
     JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread()));
     MOZ_ASSERT(IsCompilingAsmJS());
     AutoFlushICache afc("Module::staticallyLink", /* inhibit = */ true);
-    AutoFlushICache::setRange(uintptr_t(code()), codeBytes());
+    AutoFlushICache::setRange(uintptr_t(code()), codeLength());
 
     interrupt_ = code() + linkData.pod.interruptOffset;
     outOfBounds_ = code() + linkData.pod.outOfBoundsOffset;
 
     for (StaticLinkData::InternalLink link : linkData.internalLinks) {
         uint8_t* patchAt = code() + link.patchAtOffset;
         void* target = code() + link.targetOffset;
 
@@ -1221,34 +847,34 @@ Module::dynamicallyLink(JSContext* cx,
     MOZ_ASSERT(!dynamicallyLinked_);
     dynamicallyLinked_ = true;
 
     // Push a JitContext for benefit of IsCompilingAsmJS and flush the ICache.
     // We've been inhibiting flushing up to this point so flush it all now.
     JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread()));
     MOZ_ASSERT(IsCompilingAsmJS());
     AutoFlushICache afc("Module::dynamicallyLink");
-    AutoFlushICache::setRange(uintptr_t(code()), codeBytes());
+    AutoFlushICache::setRange(uintptr_t(code()), codeLength());
 
     // Initialize imports with actual imported values.
     MOZ_ASSERT(importArgs.length() == imports().length());
     for (size_t i = 0; i < imports().length(); i++) {
         const Import& import = imports()[i];
         ImportExit& exit = importToExit(import);
         exit.code = code() + import.interpExitCodeOffset();
         exit.fun = importArgs[i];
         exit.baselineScript = nullptr;
     }
 
     // Specialize code to the actual heap.
     if (usesHeap())
         specializeToHeap(heap);
 
-    // See AllocateCode comment above.
-    if (!ExecutableAllocator::makeExecutable(code(), codeBytes())) {
+    // See CodeSegment::allocate comment above.
+    if (!ExecutableAllocator::makeExecutable(code(), codeLength())) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     if (!sendCodeRangesToProfiler(cx))
         return false;
 
     return CreateExportObject(cx, moduleObj, heap, exportMap, exports(), exportObj);
@@ -1278,16 +904,43 @@ Module::deoptimizeImportExit(uint32_t im
 {
     MOZ_ASSERT(dynamicallyLinked_);
     const Import& import = imports()[importIndex];
     ImportExit& exit = importToExit(import);
     exit.code = code() + import.interpExitCodeOffset();
     exit.baselineScript = nullptr;
 }
 
+static bool
+ReadI64Object(JSContext* cx, HandleValue v, int64_t* i64)
+{
+    if (!v.isObject()) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL,
+                             "i64 JS value must be an object");
+        return false;
+    }
+
+    RootedObject obj(cx, &v.toObject());
+
+    int32_t* i32 = (int32_t*)i64;
+
+    RootedValue val(cx);
+    if (!JS_GetProperty(cx, obj, "low", &val))
+        return false;
+    if (!ToInt32(cx, val, &i32[0]))
+        return false;
+
+    if (!JS_GetProperty(cx, obj, "high", &val))
+        return false;
+    if (!ToInt32(cx, val, &i32[1]))
+        return false;
+
+    return true;
+}
+
 static JSObject*
 CreateI64Object(JSContext* cx, int64_t i64)
 {
     RootedObject result(cx, JS_NewPlainObject(cx));
     if (!result)
         return nullptr;
 
     RootedValue val(cx, Int32Value(uint32_t(i64)));
@@ -1617,22 +1270,71 @@ Module::callImport(JSContext* cx, uint32
     if (!script->baselineScript()->addDependentWasmModule(cx, *this, importIndex))
         return false;
 
     exit.code = jitExitCode;
     exit.baselineScript = script->baselineScript();
     return true;
 }
 
+/* static */ int32_t
+Module::callImport_void(int32_t importIndex, int32_t argc, uint64_t* argv)
+{
+    WasmActivation* activation = JSRuntime::innermostWasmActivation();
+    JSContext* cx = activation->cx();
+
+    RootedValue rval(cx);
+    return activation->module().callImport(cx, importIndex, argc, argv, &rval);
+}
+
+/* static */ int32_t
+Module::callImport_i32(int32_t importIndex, int32_t argc, uint64_t* argv)
+{
+    WasmActivation* activation = JSRuntime::innermostWasmActivation();
+    JSContext* cx = activation->cx();
+
+    RootedValue rval(cx);
+    if (!activation->module().callImport(cx, importIndex, argc, argv, &rval))
+        return false;
+
+    return ToInt32(cx, rval, (int32_t*)argv);
+}
+
+/* static */ int32_t
+Module::callImport_i64(int32_t importIndex, int32_t argc, uint64_t* argv)
+{
+    WasmActivation* activation = JSRuntime::innermostWasmActivation();
+    JSContext* cx = activation->cx();
+
+    RootedValue rval(cx);
+    if (!activation->module().callImport(cx, importIndex, argc, argv, &rval))
+        return false;
+
+    return ReadI64Object(cx, rval, (int64_t*)argv);
+}
+
+/* static */ int32_t
+Module::callImport_f64(int32_t importIndex, int32_t argc, uint64_t* argv)
+{
+    WasmActivation* activation = JSRuntime::innermostWasmActivation();
+    JSContext* cx = activation->cx();
+
+    RootedValue rval(cx);
+    if (!activation->module().callImport(cx, importIndex, argc, argv, &rval))
+        return false;
+
+    return ToNumber(cx, rval, (double*)argv);
+}
+
 const char*
 Module::maybePrettyFuncName(uint32_t funcIndex) const
 {
-    if (funcIndex >= module_->prettyFuncNames.length())
+    if (funcIndex >= metadata_->prettyFuncNames.length())
         return nullptr;
-    return module_->prettyFuncNames[funcIndex].get();
+    return metadata_->prettyFuncNames[funcIndex].get();
 }
 
 const char*
 Module::getFuncName(JSContext* cx, uint32_t funcIndex, UniqueChars* owner) const
 {
     if (const char* prettyName = maybePrettyFuncName(funcIndex))
         return prettyName;
 
@@ -1670,17 +1372,17 @@ const char experimentalWarning[] =
     "| (_,_) \\ |  |   _.-`   | (_,_). '. |  _( )_/ |  |\n"
     "|  |/    \\|  |.'   _    |.---.  \\  :| (_ o _) |  |\n"
     "|  '  /\\  `  ||  _( )_  |\\    `-'  ||  (_,_)  |  |\n"
     "|    /  \\    |\\ (_ o _) / \\       / |  |      |  |\n"
     "`---'    `---` '.(_,_).'   `-...-'  '--'      '--'\n"
     "text support (Work In Progress):\n\n";
 
 const char enabledMessage[] =
-    "Restart with debugger open to view WebAssembly source";
+    "Restart with developer tools open to view WebAssembly source";
 
 JSString*
 Module::createText(JSContext* cx)
 {
     StringBuffer buffer(cx);
     if (!source_.empty()) {
         if (!buffer.append(experimentalWarning))
             return nullptr;
@@ -1752,26 +1454,23 @@ WasmModuleObject::create(ExclusiveContex
     AutoSetNewObjectMetadata metadata(cx);
     JSObject* obj = NewObjectWithGivenProto(cx, &WasmModuleObject::class_, nullptr);
     if (!obj)
         return nullptr;
 
     return &obj->as<WasmModuleObject>();
 }
 
-bool
-WasmModuleObject::init(Module* module)
+void
+WasmModuleObject::init(Module& module)
 {
     MOZ_ASSERT(is<WasmModuleObject>());
     MOZ_ASSERT(!hasModule());
-    if (!module)
-        return false;
-    module->setOwner(this);
-    setReservedSlot(MODULE_SLOT, PrivateValue(module));
-    return true;
+    module.setOwner(this);
+    setReservedSlot(MODULE_SLOT, PrivateValue(&module));
 }
 
 Module&
 WasmModuleObject::module() const
 {
     MOZ_ASSERT(is<WasmModuleObject>());
     MOZ_ASSERT(hasModule());
     return *(Module*)getReservedSlot(MODULE_SLOT).toPrivate();
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -16,47 +16,37 @@
  * limitations under the License.
  */
 
 #ifndef wasm_module_h
 #define wasm_module_h
 
 #include "mozilla/LinkedList.h"
 
-#include "asmjs/WasmTypes.h"
+#include "asmjs/WasmCode.h"
 #include "gc/Barrier.h"
 #include "vm/MallocProvider.h"
 #include "vm/NativeObject.h"
 
 namespace js {
 
 class AsmJSModule;
 class WasmActivation;
 class WasmModuleObject;
 namespace jit { struct BaselineScript; }
 
 namespace wasm {
 
-// A wasm Module and everything it contains must support serialization,
-// deserialization and cloning. Some data can be simply copied as raw bytes and,
-// as a convention, is stored in an inline CacheablePod struct. Everything else
-// should implement the below methods which are called recusively by the
-// containing Module. See comments for these methods in wasm::Module.
-
-#define WASM_DECLARE_SERIALIZABLE(Type)                                         \
-    size_t serializedSize() const;                                              \
-    uint8_t* serialize(uint8_t* cursor) const;                                  \
-    const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);    \
-    MOZ_MUST_USE bool clone(JSContext* cx, Type* out) const;                    \
-    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
-
 // The StaticLinkData contains all the metadata necessary to perform
 // Module::staticallyLink but is not necessary afterwards.
+//
+// StaticLinkData is built incrementing by ModuleGenerator and then shared
+// immutably between modules.
 
-struct StaticLinkData
+struct StaticLinkData : RefCounted<StaticLinkData>
 {
     struct InternalLink {
         enum Kind {
             RawPointer,
             CodeLabel,
             InstructionImmediate
         };
         uint32_t patchAtOffset;
@@ -92,367 +82,45 @@ struct StaticLinkData
     } pod;
     InternalLinkVector  internalLinks;
     SymbolicLinkArray   symbolicLinks;
     FuncPtrTableVector  funcPtrTables;
 
     WASM_DECLARE_SERIALIZABLE(StaticLinkData)
 };
 
-typedef UniquePtr<StaticLinkData> UniqueStaticLinkData;
-
-// An Export represents a single function inside a wasm Module that has been
-// exported one or more times.
-
-class Export
-{
-    Sig sig_;
-    struct CacheablePod {
-        uint32_t stubOffset_;
-    } pod;
-
-  public:
-    Export() = default;
-    explicit Export(Sig&& sig)
-      : sig_(Move(sig))
-    {
-        pod.stubOffset_ = UINT32_MAX;
-    }
-    Export(Export&& rhs)
-      : sig_(Move(rhs.sig_)),
-        pod(rhs.pod)
-    {}
-
-    void initStubOffset(uint32_t stubOffset) {
-        MOZ_ASSERT(pod.stubOffset_ == UINT32_MAX);
-        pod.stubOffset_ = stubOffset;
-    }
-
-    uint32_t stubOffset() const {
-        return pod.stubOffset_;
-    }
-    const Sig& sig() const {
-        return sig_;
-    }
-
-    WASM_DECLARE_SERIALIZABLE(Export)
-};
-
-typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
-
-// An Import describes a wasm module import. Currently, only functions can be
-// imported in wasm. A function import includes the signature used within the
-// module to call it.
-
-class Import
-{
-    Sig sig_;
-    struct CacheablePod {
-        uint32_t exitGlobalDataOffset_;
-        uint32_t interpExitCodeOffset_;
-        uint32_t jitExitCodeOffset_;
-    } pod;
-
-  public:
-    Import() {}
-    Import(Import&& rhs) : sig_(Move(rhs.sig_)), pod(rhs.pod) {}
-    Import(Sig&& sig, uint32_t exitGlobalDataOffset)
-      : sig_(Move(sig))
-    {
-        pod.exitGlobalDataOffset_ = exitGlobalDataOffset;
-        pod.interpExitCodeOffset_ = 0;
-        pod.jitExitCodeOffset_ = 0;
-    }
-
-    void initInterpExitOffset(uint32_t off) {
-        MOZ_ASSERT(!pod.interpExitCodeOffset_);
-        pod.interpExitCodeOffset_ = off;
-    }
-    void initJitExitOffset(uint32_t off) {
-        MOZ_ASSERT(!pod.jitExitCodeOffset_);
-        pod.jitExitCodeOffset_ = off;
-    }
-
-    const Sig& sig() const {
-        return sig_;
-    }
-    uint32_t exitGlobalDataOffset() const {
-        return pod.exitGlobalDataOffset_;
-    }
-    uint32_t interpExitCodeOffset() const {
-        return pod.interpExitCodeOffset_;
-    }
-    uint32_t jitExitCodeOffset() const {
-        return pod.jitExitCodeOffset_;
-    }
-
-    WASM_DECLARE_SERIALIZABLE(Import)
-};
-
-typedef Vector<Import, 0, SystemAllocPolicy> ImportVector;
-
-// A CodeRange describes a single contiguous range of code within a wasm
-// module's code segment. A CodeRange describes what the code does and, for
-// function bodies, the name and source coordinates of the function.
-
-class CodeRange
-{
-  public:
-    enum Kind { Function, Entry, ImportJitExit, ImportInterpExit, Inline, CallThunk };
-
-  private:
-    // All fields are treated as cacheable POD:
-    uint32_t begin_;
-    uint32_t profilingReturn_;
-    uint32_t end_;
-    uint32_t funcIndex_;
-    uint32_t funcLineOrBytecode_;
-    uint8_t funcBeginToTableEntry_;
-    uint8_t funcBeginToTableProfilingJump_;
-    uint8_t funcBeginToNonProfilingEntry_;
-    uint8_t funcProfilingJumpToProfilingReturn_;
-    uint8_t funcProfilingEpilogueToProfilingReturn_;
-    Kind kind_ : 8;
-
-  public:
-    CodeRange() = default;
-    CodeRange(Kind kind, Offsets offsets);
-    CodeRange(Kind kind, ProfilingOffsets offsets);
-    CodeRange(uint32_t funcIndex, uint32_t lineOrBytecode, FuncOffsets offsets);
-
-    // All CodeRanges have a begin and end.
-
-    uint32_t begin() const {
-        return begin_;
-    }
-    uint32_t end() const {
-        return end_;
-    }
-
-    // Other fields are only available for certain CodeRange::Kinds.
-
-    Kind kind() const {
-        return kind_;
-    }
-
-    bool isFunction() const {
-        return kind() == Function;
-    }
-    bool isImportExit() const {
-        return kind() == ImportJitExit || kind() == ImportInterpExit;
-    }
-    bool isInline() const {
-        return kind() == Inline;
-    }
-
-    // Every CodeRange except entry and inline stubs has a profiling return
-    // which is used for asynchronous profiling to determine the frame pointer.
-
-    uint32_t profilingReturn() const {
-        MOZ_ASSERT(isFunction() || isImportExit());
-        return profilingReturn_;
-    }
-
-    // Functions have offsets which allow patching to selectively execute
-    // profiling prologues/epilogues.
-
-    uint32_t funcProfilingEntry() const {
-        MOZ_ASSERT(isFunction());
-        return begin();
-    }
-    uint32_t funcTableEntry() const {
-        MOZ_ASSERT(isFunction());
-        return begin_ + funcBeginToTableEntry_;
-    }
-    uint32_t funcTableProfilingJump() const {
-        MOZ_ASSERT(isFunction());
-        return begin_ + funcBeginToTableProfilingJump_;
-    }
-    uint32_t funcNonProfilingEntry() const {
-        MOZ_ASSERT(isFunction());
-        return begin_ + funcBeginToNonProfilingEntry_;
-    }
-    uint32_t funcProfilingJump() const {
-        MOZ_ASSERT(isFunction());
-        return profilingReturn_ - funcProfilingJumpToProfilingReturn_;
-    }
-    uint32_t funcProfilingEpilogue() const {
-        MOZ_ASSERT(isFunction());
-        return profilingReturn_ - funcProfilingEpilogueToProfilingReturn_;
-    }
-    uint32_t funcIndex() const {
-        MOZ_ASSERT(isFunction());
-        return funcIndex_;
-    }
-    uint32_t funcLineOrBytecode() const {
-        MOZ_ASSERT(isFunction());
-        return funcLineOrBytecode_;
-    }
-
-    // A sorted array of CodeRanges can be looked up via BinarySearch and PC.
-
-    struct PC {
-        size_t offset;
-        explicit PC(size_t offset) : offset(offset) {}
-        bool operator==(const CodeRange& rhs) const {
-            return offset >= rhs.begin() && offset < rhs.end();
-        }
-        bool operator<(const CodeRange& rhs) const {
-            return offset < rhs.begin();
-        }
-    };
-};
-
-} // namespace wasm
-} // namespace js
-namespace mozilla {
-template <> struct IsPod<js::wasm::CodeRange> : TrueType {};
-}
-namespace js {
-namespace wasm {
-
-typedef Vector<CodeRange, 0, SystemAllocPolicy> CodeRangeVector;
-
-// A CallThunk describes the offset and target of thunks so that they may be
-// patched at runtime when profiling is toggled. Thunks are emitted to connect
-// callsites that are too far away from callees to fit in a single call
-// instruction's relative offset.
-
-struct CallThunk
-{
-    uint32_t offset;
-    union {
-        uint32_t funcIndex;
-        uint32_t codeRangeIndex;
-    } u;
-
-    CallThunk(uint32_t offset, uint32_t funcIndex) : offset(offset) { u.funcIndex = funcIndex; }
-    CallThunk() = default;
-};
-
-typedef Vector<CallThunk, 0, SystemAllocPolicy> CallThunkVector;
-
-} // namespace wasm
-} // namespace js
-namespace mozilla {
-template <> struct IsPod<js::wasm::CallThunk> : TrueType {};
-}
-namespace js {
-namespace wasm {
-
-// CacheableChars is used to cacheably store UniqueChars.
-
-struct CacheableChars : UniqueChars
-{
-    CacheableChars() = default;
-    explicit CacheableChars(char* ptr) : UniqueChars(ptr) {}
-    MOZ_IMPLICIT CacheableChars(UniqueChars&& rhs) : UniqueChars(Move(rhs)) {}
-    CacheableChars(CacheableChars&& rhs) : UniqueChars(Move(rhs)) {}
-    void operator=(CacheableChars&& rhs) { UniqueChars::operator=(Move(rhs)); }
-    WASM_DECLARE_SERIALIZABLE(CacheableChars)
-};
-
-typedef Vector<CacheableChars, 0, SystemAllocPolicy> CacheableCharsVector;
+typedef RefPtr<StaticLinkData> MutableStaticLinkData;
+typedef RefPtr<const StaticLinkData> SharedStaticLinkData;
 
 // The ExportMap describes how Exports are mapped to the fields of the export
 // object. This allows a single Export to be used in multiple fields.
 // The 'fieldNames' vector provides the list of names of the module's exports.
 // For each field in fieldNames, 'fieldsToExports' provides either:
 //  - the sentinel value MemoryExport indicating an export of linear memory; or
 //  - the index of an export (both into the module's ExportVector and the
 //    ExportMap's exportFuncIndices vector).
 // Lastly, the 'exportFuncIndices' vector provides, for each exported function,
 // the internal index of the function.
+//
+// The ExportMap is built incrementally by ModuleGenerator and then shared
+// immutably between modules.
 
 static const uint32_t MemoryExport = UINT32_MAX;
 
-struct ExportMap
+struct ExportMap : RefCounted<ExportMap>
 {
     CacheableCharsVector fieldNames;
     Uint32Vector fieldsToExports;
     Uint32Vector exportFuncIndices;
 
     WASM_DECLARE_SERIALIZABLE(ExportMap)
 };
 
-typedef UniquePtr<ExportMap> UniqueExportMap;
-
-// A UniqueCodePtr owns allocated executable code. Code passed to the Module
-// constructor must be allocated via AllocateCode.
-
-class CodeDeleter
-{
-    uint32_t bytes_;
-  public:
-    CodeDeleter() : bytes_(0) {}
-    explicit CodeDeleter(uint32_t bytes) : bytes_(bytes) {}
-    void operator()(uint8_t* p);
-};
-typedef UniquePtr<uint8_t, CodeDeleter> UniqueCodePtr;
-
-UniqueCodePtr
-AllocateCode(ExclusiveContext* cx, size_t bytes);
-
-// A wasm module can either use no heap, a unshared heap (ArrayBuffer) or shared
-// heap (SharedArrayBuffer).
-
-enum class HeapUsage
-{
-    None = false,
-    Unshared = 1,
-    Shared = 2
-};
-
-static inline bool
-UsesHeap(HeapUsage heapUsage)
-{
-    return bool(heapUsage);
-}
-
-// ModuleCacheablePod holds the trivially-memcpy()able serializable portion of
-// ModuleData.
-
-struct ModuleCacheablePod
-{
-    uint32_t              functionBytes;
-    uint32_t              codeBytes;
-    uint32_t              globalBytes;
-    uint32_t              numFuncs;
-    ModuleKind            kind;
-    HeapUsage             heapUsage;
-    CompileArgs           compileArgs;
-
-    uint32_t totalBytes() const { return codeBytes + globalBytes; }
-};
-
-// ModuleData holds the guts of a Module. ModuleData is mutably built up by
-// ModuleGenerator and then handed over to the Module constructor in finish(),
-// where it is stored immutably.
-
-struct ModuleData : ModuleCacheablePod
-{
-    ModuleData() : loadedFromCache(false) { mozilla::PodZero(&pod()); }
-    ModuleCacheablePod& pod() { return *this; }
-    const ModuleCacheablePod& pod() const { return *this; }
-
-    UniqueCodePtr         code;
-    ImportVector          imports;
-    ExportVector          exports;
-    HeapAccessVector      heapAccesses;
-    CodeRangeVector       codeRanges;
-    CallSiteVector        callSites;
-    CallThunkVector       callThunks;
-    CacheableCharsVector  prettyFuncNames;
-    CacheableChars        filename;
-    bool                  loadedFromCache;
-
-    WASM_DECLARE_SERIALIZABLE(ModuleData);
-};
-
-typedef UniquePtr<ModuleData> UniqueModuleData;
+typedef RefPtr<ExportMap> MutableExportMap;
+typedef RefPtr<const ExportMap> SharedExportMap;
 
 // Module represents a compiled WebAssembly module which lives until the last
 // reference to any exported functions is dropped. Modules must be wrapped by a
 // rooted JSObject immediately after creation so that Module::trace() is called
 // during GC. Modules are created after compilation completes and start in a
 // a fully unlinked state. After creation, a module must be first statically
 // linked and then dynamically linked:
 //
@@ -466,17 +134,16 @@ typedef UniquePtr<ModuleData> UniqueModu
 //    once. However, a dynamically-linked module may be cloned so that the clone
 //    can be independently dynamically linked.
 //
 // Once fully dynamically linked, a Module can have its exports invoked via
 // callExport().
 
 class Module : public mozilla::LinkedListElement<Module>
 {
-    typedef UniquePtr<const ModuleData> UniqueConstModuleData;
     struct ImportExit {
         void* code;
         jit::BaselineScript* baselineScript;
         GCPtrFunction fun;
         static_assert(sizeof(GCPtrFunction) == sizeof(void*), "for JIT access");
     };
     struct EntryArg {
         uint64_t lo;
@@ -492,17 +159,18 @@ class Module : public mozilla::LinkedLis
         {}
     };
     typedef Vector<FuncPtrTable, 0, SystemAllocPolicy> FuncPtrTableVector;
     typedef Vector<CacheableChars, 0, SystemAllocPolicy> FuncLabelVector;
     typedef HeapPtr<ArrayBufferObjectMaybeShared*> BufferPtr;
     typedef GCPtr<WasmModuleObject*> ModuleObjectPtr;
 
     // Initialized when constructed:
-    const UniqueConstModuleData  module_;
+    const UniqueCodeSegment      codeSegment_;
+    const SharedMetadata         metadata_;
 
     // Initialized during staticallyLink:
     bool                         staticallyLinked_;
     uint8_t*                     interrupt_;
     uint8_t*                     outOfBounds_;
     FuncPtrTableVector           funcPtrTables_;
 
     // Initialized during dynamicallyLink:
@@ -523,67 +191,75 @@ class Module : public mozilla::LinkedLis
     uint8_t*& rawHeapPtr();
     WasmActivation*& activation();
     void specializeToHeap(ArrayBufferObjectMaybeShared* heap);
     void despecializeFromHeap(ArrayBufferObjectMaybeShared* heap);
     MOZ_MUST_USE bool sendCodeRangesToProfiler(JSContext* cx);
     MOZ_MUST_USE bool setProfilingEnabled(JSContext* cx, bool enabled);
     ImportExit& importToExit(const Import& import);
 
+    bool callImport(JSContext* cx, uint32_t importIndex, unsigned argc, const uint64_t* argv,
+                    MutableHandleValue rval);
+    static int32_t callImport_void(int32_t importIndex, int32_t argc, uint64_t* argv);
+    static int32_t callImport_i32(int32_t importIndex, int32_t argc, uint64_t* argv);
+    static int32_t callImport_i64(int32_t importIndex, int32_t argc, uint64_t* argv);
+    static int32_t callImport_f64(int32_t importIndex, int32_t argc, uint64_t* argv);
+
     friend class js::WasmActivation;
+    friend void* wasm::AddressOf(SymbolicAddress, ExclusiveContext*);
 
   protected:
-    const ModuleData& base() const { return *module_; }
+    const CodeSegment& codeSegment() const { return *codeSegment_; }
+    const Metadata& metadata() const { return *metadata_; }
     MOZ_MUST_USE bool clone(JSContext* cx, const StaticLinkData& link, Module* clone) const;
 
   public:
     static const unsigned SizeOfImportExit = sizeof(ImportExit);
     static const unsigned OffsetOfImportExitFun = offsetof(ImportExit, fun);
     static const unsigned SizeOfEntryArg = sizeof(EntryArg);
 
-    explicit Module(UniqueModuleData module);
+    explicit Module(UniqueCodeSegment codeSegment, const Metadata& metadata);
     virtual ~Module();
     virtual void trace(JSTracer* trc);
     virtual void readBarrier();
     virtual void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data);
 
     void setOwner(WasmModuleObject* owner) { MOZ_ASSERT(!ownerObject_); ownerObject_ = owner; }
     inline const GCPtr<WasmModuleObject*>& owner() const;
 
     void setSource(Bytes&& source) { source_ = Move(source); }
 
-    uint8_t* code() const { return module_->code.get(); }
-    uint32_t codeBytes() const { return module_->codeBytes; }
-    uint8_t* globalData() const { return code() + module_->codeBytes; }
-    uint32_t globalBytes() const { return module_->globalBytes; }
-    HeapUsage heapUsage() const { return module_->heapUsage; }
-    bool usesHeap() const { return UsesHeap(module_->heapUsage); }
-    bool hasSharedHeap() const { return module_->heapUsage == HeapUsage::Shared; }
-    CompileArgs compileArgs() const { return module_->compileArgs; }
-    const ImportVector& imports() const { return module_->imports; }
-    const ExportVector& exports() const { return module_->exports; }
-    const CodeRangeVector& codeRanges() const { return module_->codeRanges; }
-    const char* filename() const { return module_->filename.get(); }
-    bool loadedFromCache() const { return module_->loadedFromCache; }
+    uint8_t* code() const { return codeSegment_->code(); }
+    uint32_t codeLength() const { return codeSegment_->codeLength(); }
+    uint8_t* globalData() const { return codeSegment_->globalData(); }
+    uint32_t globalDataLength() const { return codeSegment_->globalDataLength(); }
+    HeapUsage heapUsage() const { return metadata_->heapUsage; }
+    bool usesHeap() const { return UsesHeap(metadata_->heapUsage); }
+    bool hasSharedHeap() const { return metadata_->heapUsage == HeapUsage::Shared; }
+    CompileArgs compileArgs() const { return metadata_->compileArgs; }
+    const ImportVector& imports() const { return metadata_->imports; }
+    const ExportVector& exports() const { return metadata_->exports; }
+    const CodeRangeVector& codeRanges() const { return metadata_->codeRanges; }
+    const char* filename() const { return metadata_->filename.get(); }
     bool staticallyLinked() const { return staticallyLinked_; }
     bool dynamicallyLinked() const { return dynamicallyLinked_; }
 
     // Some wasm::Module's have the most-derived type AsmJSModule. The
     // AsmJSModule stores the extra metadata necessary to implement asm.js (JS)
     // semantics. The asAsmJS() member may be used as a checked downcast when
     // isAsmJS() is true.
 
-    bool isAsmJS() const { return module_->kind == ModuleKind::AsmJS; }
+    bool isAsmJS() const { return metadata_->kind == ModuleKind::AsmJS; }
     AsmJSModule& asAsmJS() { MOZ_ASSERT(isAsmJS()); return *(AsmJSModule*)this; }
     const AsmJSModule& asAsmJS() const { MOZ_ASSERT(isAsmJS()); return *(const AsmJSModule*)this; }
     virtual bool mutedErrors() const;
     virtual const char16_t* displayURL() const;
     virtual ScriptSource* maybeScriptSource() const { return nullptr; }
 
-    // The range [0, functionBytes) is a subrange of [0, codeBytes) that
+    // The range [0, functionLength) is a subrange of [0, codeLength) that
     // contains only function body code, not the stub code. This distinction is
     // used by the async interrupt handler to only interrupt when the pc is in
     // function code which, in turn, simplifies reasoning about how stubs
     // enter/exit.
 
     bool containsFunctionPC(void* pc) const;
     bool containsCodePC(void* pc) const;
     const CallSite* lookupCallSite(void* returnAddress) const;
@@ -620,18 +296,16 @@ class Module : public mozilla::LinkedLis
     MOZ_MUST_USE bool callExport(JSContext* cx, uint32_t exportIndex, CallArgs args);
 
     // Initially, calls to imports in wasm code call out through the generic
     // callImport method. If the imported callee gets JIT compiled and the types
     // match up, callImport will patch the code to instead call through a thunk
     // directly into the JIT code. If the JIT code is released, the Module must
     // be notified so it can go back to the generic callImport.
 
-    MOZ_MUST_USE bool callImport(JSContext* cx, uint32_t importIndex, unsigned argc,
-                                 const uint64_t* argv, MutableHandleValue rval);
     void deoptimizeImportExit(uint32_t importIndex);
 
     // At runtime, when $pc is in wasm function code (containsFunctionPC($pc)),
     // $pc may be moved abruptly to interrupt() or outOfBounds() by a signal
     // handler or SetContext() from another thread.
 
     uint8_t* interrupt() const { MOZ_ASSERT(staticallyLinked_); return interrupt_; }
     uint8_t* outOfBounds() const { MOZ_ASSERT(staticallyLinked_); return outOfBounds_; }
@@ -685,17 +359,17 @@ class WasmModuleObject : public NativeOb
     static const ClassOps classOps_;
 
     bool hasModule() const;
     static void finalize(FreeOp* fop, JSObject* obj);
     static void trace(JSTracer* trc, JSObject* obj);
   public:
     static const unsigned RESERVED_SLOTS = 1;
     static WasmModuleObject* create(ExclusiveContext* cx);
-    MOZ_MUST_USE bool init(wasm::Module* module);
+    void init(wasm::Module& module);
     wasm::Module& module() const;
     void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data);
     static const Class class_;
 };
 
 inline const GCPtr<WasmModuleObject*>&
 wasm::Module::owner() const {
     MOZ_ASSERT(&ownerObject_->module() == this);
--- a/js/src/asmjs/WasmSerialize.h
+++ b/js/src/asmjs/WasmSerialize.h
@@ -160,30 +160,16 @@ DeserializeVector(ExclusiveContext* cx, 
     for (size_t i = 0; i < vec->length(); i++) {
         if (!(cursor = (*vec)[i].deserialize(cx, cursor)))
             return nullptr;
     }
     return cursor;
 }
 
 template <class T, size_t N>
-static inline MOZ_MUST_USE bool
-CloneVector(JSContext* cx, const mozilla::Vector<T, N, SystemAllocPolicy>& in,
-            mozilla::Vector<T, N, SystemAllocPolicy>* out)
-{
-    if (!out->resize(in.length()))
-        return false;
-    for (size_t i = 0; i < in.length(); i++) {
-        if (!in[i].clone(cx, &(*out)[i]))
-            return false;
-    }
-    return true;
-}
-
-template <class T, size_t N>
 static inline size_t
 SizeOfVectorExcludingThis(const mozilla::Vector<T, N, SystemAllocPolicy>& vec,
                           MallocSizeOf mallocSizeOf)
 {
     size_t size = vec.sizeOfExcludingThis(mallocSizeOf);
     for (const T& t : vec)
         size += t.sizeOfExcludingThis(mallocSizeOf);
     return size;
@@ -214,27 +200,16 @@ DeserializePodVector(ExclusiveContext* c
     uint32_t length;
     cursor = ReadScalar<uint32_t>(cursor, &length);
     if (!vec->resize(length))
         return nullptr;
     cursor = ReadBytes(cursor, vec->begin(), length * sizeof(T));
     return cursor;
 }
 
-template <class T, size_t N>
-static inline MOZ_MUST_USE bool
-ClonePodVector(JSContext* cx, const mozilla::Vector<T, N, SystemAllocPolicy>& in,
-               mozilla::Vector<T, N, SystemAllocPolicy>* out)
-{
-    if (!out->resize(in.length()))
-        return false;
-    mozilla::PodCopy(out->begin(), in.begin(), in.length());
-    return true;
-}
-
 static inline MOZ_MUST_USE bool
 GetCPUID(uint32_t* cpuId)
 {
     enum Arch {
         X86 = 0x1,
         X64 = 0x2,
         ARM = 0x3,
         MIPS = 0x4,
--- a/js/src/asmjs/WasmStubs.cpp
+++ b/js/src/asmjs/WasmStubs.cpp
@@ -390,26 +390,26 @@ FillArgumentArray(MacroAssembler& masm, 
                 masm.storeDouble(ScratchDoubleReg, dstAddr);
             }
             break;
         }
     }
 }
 
 // Generate a stub that is called via the internal ABI derived from the
-// signature of the import and calls into an appropriate InvokeImport C++
+// signature of the import and calls into an appropriate callImport C++
 // function, having boxed all the ABI arguments into a homogeneous Value array.
 ProfilingOffsets
 wasm::GenerateInterpExit(MacroAssembler& masm, const Import& import, uint32_t importIndex)
 {
     const Sig& sig = import.sig();
 
     masm.setFramePushed(0);
 
-    // Argument types for InvokeImport_*:
+    // Argument types for Module::callImport_*:
     static const MIRType typeArray[] = { MIRType::Pointer,   // ImportExit
                                          MIRType::Int32,     // argc
                                          MIRType::Pointer }; // argv
     MIRTypeVector invokeArgTypes;
     MOZ_ALWAYS_TRUE(invokeArgTypes.append(typeArray, ArrayLength(typeArray)));
 
     // At the point of the call, the stack layout shall be (sp grows to the left):
     //   | stack args | padding | Value argv[] | padding | retaddr | caller stack args |
@@ -422,17 +422,17 @@ wasm::GenerateInterpExit(MacroAssembler&
     ProfilingOffsets offsets;
     GenerateExitPrologue(masm, framePushed, ExitReason::ImportInterp, &offsets);
 
     // Fill the argument array.
     unsigned offsetToCallerStackArgs = sizeof(AsmJSFrame) + masm.framePushed();
     Register scratch = ABINonArgReturnReg0;
     FillArgumentArray(masm, sig.args(), argOffset, offsetToCallerStackArgs, scratch, ToValue(false));
 
-    // Prepare the arguments for the call to InvokeImport_*.
+    // Prepare the arguments for the call to Module::callImport_*.
     ABIArgMIRTypeIter i(invokeArgTypes);
 
     // argument 0: importIndex
     if (i->kind() == ABIArg::GPR)
         masm.mov(ImmWord(importIndex), i->gpr());
     else
         masm.store32(Imm32(importIndex), Address(masm.getStackPointer(), i->offsetFromArgBase()));
     i++;
@@ -455,38 +455,38 @@ wasm::GenerateInterpExit(MacroAssembler&
     }
     i++;
     MOZ_ASSERT(i.done());
 
     // Make the call, test whether it succeeded, and extract the return value.
     AssertStackAlignment(masm, ABIStackAlignment);
     switch (sig.ret()) {
       case ExprType::Void:
-        masm.call(SymbolicAddress::InvokeImport_Void);
+        masm.call(SymbolicAddress::CallImport_Void);
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw);
         break;
       case ExprType::I32:
-        masm.call(SymbolicAddress::InvokeImport_I32);
+        masm.call(SymbolicAddress::CallImport_I32);
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw);
         masm.load32(argv, ReturnReg);
         break;
       case ExprType::I64:
         MOZ_ASSERT(JitOptions.wasmTestMode);
-        masm.call(SymbolicAddress::InvokeImport_I64);
+        masm.call(SymbolicAddress::CallImport_I64);
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw);
         masm.load64(argv, ReturnReg64);
         break;
       case ExprType::F32:
-        masm.call(SymbolicAddress::InvokeImport_F64);
+        masm.call(SymbolicAddress::CallImport_F64);
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw);
         masm.loadDouble(argv, ReturnDoubleReg);
         masm.convertDoubleToFloat32(ReturnDoubleReg, ReturnFloat32Reg);
         break;
       case ExprType::F64:
-        masm.call(SymbolicAddress::InvokeImport_F64);
+        masm.call(SymbolicAddress::CallImport_F64);
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw);
         masm.loadDouble(argv, ReturnDoubleReg);
         break;
       case ExprType::I8x16:
       case ExprType::I16x8:
       case ExprType::I32x4:
       case ExprType::F32x4:
       case ExprType::B8x16:
--- a/js/src/asmjs/WasmTypes.cpp
+++ b/js/src/asmjs/WasmTypes.cpp
@@ -126,111 +126,16 @@ CoerceInPlace_ToNumber(MutableHandleValu
     double dbl;
     if (!ToNumber(cx, val, &dbl))
         return false;
     val.set(DoubleValue(dbl));
 
     return true;
 }
 
-// Use an int32_t return type instead of bool since bool does not have a
-// specified width and the caller is assuming a word-sized return.
-static int32_t
-InvokeImport_Void(int32_t importIndex, int32_t argc, uint64_t* argv)
-{
-    WasmActivation* activation = JSRuntime::innermostWasmActivation();
-    JSContext* cx = activation->cx();
-
-    RootedValue rval(cx);
-    return activation->module().callImport(cx, importIndex, argc, argv, &rval);
-}
-
-// Use an int32_t return type instead of bool since bool does not have a
-// specified width and the caller is assuming a word-sized return.
-static int32_t
-InvokeImport_I32(int32_t importIndex, int32_t argc, uint64_t* argv)
-{
-    WasmActivation* activation = JSRuntime::innermostWasmActivation();
-    JSContext* cx = activation->cx();
-
-    RootedValue rval(cx);
-    if (!activation->module().callImport(cx, importIndex, argc, argv, &rval))
-        return false;
-
-    int32_t i32;
-    if (!ToInt32(cx, rval, &i32))
-        return false;
-
-    argv[0] = i32;
-    return true;
-}
-
-bool
-js::wasm::ReadI64Object(JSContext* cx, HandleValue v, int64_t* i64)
-{
-    if (!v.isObject()) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL,
-                             "i64 JS value must be an object");
-        return false;
-    }
-
-    RootedObject obj(cx, &v.toObject());
-
-    int32_t* i32 = (int32_t*)i64;
-
-    RootedValue val(cx);
-    if (!JS_GetProperty(cx, obj, "low", &val))
-        return false;
-    if (!ToInt32(cx, val, &i32[0]))
-        return false;
-
-    if (!JS_GetProperty(cx, obj, "high", &val))
-        return false;
-    if (!ToInt32(cx, val, &i32[1]))
-        return false;
-
-    return true;
-}
-
-static int32_t
-InvokeImport_I64(int32_t importIndex, int32_t argc, uint64_t* argv)
-{
-    WasmActivation* activation = JSRuntime::innermostWasmActivation();
-    JSContext* cx = activation->cx();
-
-    RootedValue rval(cx);
-    if (!activation->module().callImport(cx, importIndex, argc, argv, &rval))
-        return false;
-
-    if (!ReadI64Object(cx, rval, (int64_t*)argv))
-        return false;
-
-    return true;
-}
-
-// Use an int32_t return type instead of bool since bool does not have a
-// specified width and the caller is assuming a word-sized return.
-static int32_t
-InvokeImport_F64(int32_t importIndex, int32_t argc, uint64_t* argv)
-{
-    WasmActivation* activation = JSRuntime::innermostWasmActivation();
-    JSContext* cx = activation->cx();
-
-    RootedValue rval(cx);
-    if (!activation->module().callImport(cx, importIndex, argc, argv, &rval))
-        return false;
-
-    double dbl;
-    if (!ToNumber(cx, rval, &dbl))
-        return false;
-
-    ((double*)argv)[0] = dbl;
-    return true;
-}
-
 template <class F>
 static inline void*
 FuncCast(F* pf, ABIFunctionType type)
 {
     void *pv = JS_FUNC_TO_DATA_PTR(void*, pf);
 #ifdef JS_SIMULATOR
     pv = Simulator::RedirectNativeFunction(pv, type);
 #endif
@@ -248,24 +153,24 @@ wasm::AddressOf(SymbolicAddress imm, Exc
       case SymbolicAddress::StackLimit:
         return cx->stackLimitAddressForJitCode(StackForUntrustedScript);
       case SymbolicAddress::ReportOverRecursed:
         return FuncCast(WasmReportOverRecursed, Args_General0);
       case SymbolicAddress::HandleExecutionInterrupt:
         return FuncCast(WasmHandleExecutionInterrupt, Args_General0);
       case SymbolicAddress::HandleTrap:
         return FuncCast(HandleTrap, Args_General1);
-      case SymbolicAddress::InvokeImport_Void:
-        return FuncCast(InvokeImport_Void, Args_General3);
-      case SymbolicAddress::InvokeImport_I32:
-        return FuncCast(InvokeImport_I32, Args_General3);
-      case SymbolicAddress::InvokeImport_I64:
-        return FuncCast(InvokeImport_I64, Args_General3);
-      case SymbolicAddress::InvokeImport_F64:
-        return FuncCast(InvokeImport_F64, Args_General3);
+      case SymbolicAddress::CallImport_Void:
+        return FuncCast(Module::callImport_void, Args_General3);
+      case SymbolicAddress::CallImport_I32:
+        return FuncCast(Module::callImport_i32, Args_General3);
+      case SymbolicAddress::CallImport_I64:
+        return FuncCast(Module::callImport_i64, Args_General3);
+      case SymbolicAddress::CallImport_F64:
+        return FuncCast(Module::callImport_f64, Args_General3);
       case SymbolicAddress::CoerceInPlace_ToInt32:
         return FuncCast(CoerceInPlace_ToInt32, Args_General1);
       case SymbolicAddress::CoerceInPlace_ToNumber:
         return FuncCast(CoerceInPlace_ToNumber, Args_General1);
       case SymbolicAddress::ToInt32:
         return FuncCast<int32_t (double)>(JS::ToInt32, Args_Int_Double);
 #if defined(JS_CODEGEN_ARM)
       case SymbolicAddress::aeabi_idivmod:
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -18,16 +18,18 @@
 
 #ifndef wasm_types_h
 #define wasm_types_h
 
 #include "mozilla/EnumeratedArray.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Move.h"
+#include "mozilla/RefCounted.h"
+#include "mozilla/RefPtr.h"
 
 #include "NamespaceImports.h"
 
 #include "asmjs/WasmBinary.h"
 #include "ds/LifoAlloc.h"
 #include "jit/IonTypes.h"
 #include "js/UniquePtr.h"
 #include "js/Utility.h"
@@ -39,19 +41,42 @@ class PropertyName;
 
 namespace wasm {
 
 using mozilla::DebugOnly;
 using mozilla::EnumeratedArray;
 using mozilla::Maybe;
 using mozilla::Move;
 using mozilla::MallocSizeOf;
+using mozilla::RefCounted;
 
 typedef Vector<uint32_t, 0, SystemAllocPolicy> Uint32Vector;
 
+// To call Vector::podResizeToFit, a type must specialize mozilla::IsPod
+// which is pretty verbose to do within js::wasm, so factor that process out
+// into a macro.
+
+#define WASM_DECLARE_POD_VECTOR(Type, VectorName)                               \
+} } namespace mozilla {                                                         \
+template <> struct IsPod<js::wasm::Type> : TrueType {};                         \
+} namespace js { namespace wasm {                                               \
+typedef Vector<Type, 0, SystemAllocPolicy> VectorName;
+
+// A wasm Module and everything it contains must support serialization and
+// deserialization. Some data can be simply copied as raw bytes and,
+// as a convention, is stored in an inline CacheablePod struct. Everything else
+// should implement the below methods which are called recusively by the
+// containing Module.
+
+#define WASM_DECLARE_SERIALIZABLE(Type)                                         \
+    size_t serializedSize() const;                                              \
+    uint8_t* serialize(uint8_t* cursor) const;                                  \
+    const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);    \
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
 // ValType/ExprType utilities
 
 // ExprType::Limit is an out-of-band value and has no wasm-semantic meaning. For
 // the purpose of recursive validation, we use this value to represent the type
 // of branch/return instructions that don't actually return to the parent
 // expression and can thus be used in any context.
 const ExprType AnyType = ExprType::Limit;
 
@@ -508,41 +533,33 @@ class CallSite : public CallSiteDesc
 
     // The stackDepth measures the amount of stack space pushed since the
     // function was called. In particular, this includes the pushed return
     // address on all archs (whether or not the call instruction pushes the
     // return address (x86/x64) or the prologue does (ARM/MIPS)).
     uint32_t stackDepth() const { return stackDepth_; }
 };
 
+WASM_DECLARE_POD_VECTOR(CallSite, CallSiteVector)
+
 class CallSiteAndTarget : public CallSite
 {
     uint32_t targetIndex_;
 
   public:
     CallSiteAndTarget(CallSite cs, uint32_t targetIndex)
       : CallSite(cs), targetIndex_(targetIndex)
     { }
 
     static const uint32_t NOT_INTERNAL = UINT32_MAX;
 
     bool isInternal() const { return targetIndex_ != NOT_INTERNAL; }
     uint32_t targetIndex() const { MOZ_ASSERT(isInternal()); return targetIndex_; }
 };
 
-} // namespace wasm
-} // namespace js
-namespace mozilla {
-template <> struct IsPod<js::wasm::CallSite>          : TrueType {};
-template <> struct IsPod<js::wasm::CallSiteAndTarget> : TrueType {};
-}
-namespace js {
-namespace wasm {
-
-typedef Vector<CallSite, 0, SystemAllocPolicy> CallSiteVector;
 typedef Vector<CallSiteAndTarget, 0, SystemAllocPolicy> CallSiteAndTargetVector;
 
 // Summarizes a heap access made by wasm code that needs to be patched later
 // and/or looked up by the wasm signal handlers. Different architectures need
 // to know different things (x64: offset and length, ARM: where to patch in
 // heap length, x86: where to patch in heap length and base).
 
 #if defined(JS_CODEGEN_X86)
@@ -634,25 +651,17 @@ class HeapAccess
 #elif defined(JS_CODEGEN_NONE)
 class HeapAccess {
   public:
     void offsetInsnOffsetBy(uint32_t) { MOZ_CRASH(); }
     uint32_t insnOffset() const { MOZ_CRASH(); }
 };
 #endif
 
-} // namespace wasm
-} // namespace js
-namespace mozilla {
-template <> struct IsPod<js::wasm::HeapAccess> : TrueType {};
-}
-namespace js {
-namespace wasm {
-
-typedef Vector<HeapAccess, 0, SystemAllocPolicy> HeapAccessVector;
+WASM_DECLARE_POD_VECTOR(HeapAccess, HeapAccessVector)
 
 // A wasm::SymbolicAddress represents a pointer to a well-known function or
 // object that is embedded in wasm code. Since wasm code is serialized and
 // later deserialized into a different address space, symbolic addresses must be
 // used for *all* pointers into the address space. The MacroAssembler records a
 // list of all SymbolicAddresses and the offsets of their use in the code for
 // later patching during static linking.
 
@@ -690,32 +699,28 @@ enum class SymbolicAddress
     PowD,
     ATan2D,
     Runtime,
     RuntimeInterruptUint32,
     StackLimit,
     ReportOverRecursed,
     HandleExecutionInterrupt,
     HandleTrap,
-    InvokeImport_Void,
-    InvokeImport_I32,
-    InvokeImport_I64,
-    InvokeImport_F64,
+    CallImport_Void,
+    CallImport_I32,
+    CallImport_I64,
+    CallImport_F64,
     CoerceInPlace_ToInt32,
     CoerceInPlace_ToNumber,
     Limit
 };
 
 void*
 AddressOf(SymbolicAddress imm, ExclusiveContext* cx);
 
-// Extracts low and high from an int64 object {low: int32, high: int32}, for
-// testing purposes mainly.
-MOZ_MUST_USE bool ReadI64Object(JSContext* cx, HandleValue v, int64_t* val);
-
 // A wasm::Trap is a reason for why we reached a trap in executed code. Each
 // different trap is mapped to a different error message.
 
 enum class Trap
 {
     // The Unreachable opcode has been executed.
     Unreachable,
     // An integer arithmetic operation led to an overflow.
--- a/js/src/jit-test/tests/SIMD/inline-missing-arguments.js
+++ b/js/src/jit-test/tests/SIMD/inline-missing-arguments.js
@@ -5,27 +5,77 @@ setJitCompilerOption("ion.warmup.trigger
 function test(i) {
     assertEqX4(SIMD.Int32x4(),              [0, 0, 0, 0]);
     assertEqX4(SIMD.Int32x4(i),             [i, 0, 0, 0]);
     assertEqX4(SIMD.Int32x4(i, 1),          [i, 1, 0, 0]);
     assertEqX4(SIMD.Int32x4(i, 1, 2),       [i, 1, 2, 0]);
     assertEqX4(SIMD.Int32x4(i, 1, 2, 3),    [i, 1, 2, 3]);
     assertEqX4(SIMD.Int32x4(i, 1, 2, 3, 4), [i, 1, 2, 3]);
 
+    assertEqVecArr(SIMD.Int16x8(),              [0, 0, 0, 0, 0, 0, 0, 0]);
+    assertEqVecArr(SIMD.Int16x8(i),             [i, 0, 0, 0, 0, 0, 0, 0]);
+    assertEqVecArr(SIMD.Int16x8(i, 1),          [i, 1, 0, 0, 0, 0, 0, 0]);
+    assertEqVecArr(SIMD.Int16x8(i, 1, 2),       [i, 1, 2, 0, 0, 0, 0, 0]);
+    assertEqVecArr(SIMD.Int16x8(i, 1, 2, 3),    [i, 1, 2, 3, 0, 0, 0, 0]);
+    assertEqVecArr(SIMD.Int16x8(i, 1, 2, 3, 4), [i, 1, 2, 3, 4, 0, 0, 0]);
+    assertEqVecArr(SIMD.Int16x8(i, 1, 2, 3, 4, 5, 6),
+                               [i, 1, 2, 3, 4, 5, 6, 0]);
+    j = i & 32
+    assertEqVecArr(SIMD.Int8x16(),              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+    assertEqVecArr(SIMD.Int8x16(j),             [j, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+    assertEqVecArr(SIMD.Int8x16(j, 1),          [j, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+    assertEqVecArr(SIMD.Int8x16(j, 1, 2),       [j, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+    assertEqVecArr(SIMD.Int8x16(j, 1, 2, 3),    [j, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+    assertEqVecArr(SIMD.Int8x16(j, 1, 2, 3, 4), [j, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+    assertEqVecArr(SIMD.Int8x16(j, 1, 2, 3, 4, 5, 6),
+                               [j, 1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+    assertEqVecArr(SIMD.Int8x16(j, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12),
+                               [j, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0]);
+
     assertEqX4(SIMD.Float32x4(),                [NaN, NaN, NaN, NaN]);
     assertEqX4(SIMD.Float32x4(i),               [i,   NaN, NaN, NaN]);
     assertEqX4(SIMD.Float32x4(i, 1),            [i,   1,   NaN, NaN]);
     assertEqX4(SIMD.Float32x4(i, 1, 2),         [i,   1,   2,   NaN]);
     assertEqX4(SIMD.Float32x4(i, 1, 2, 3),      [i,   1,   2,   3  ]);
     assertEqX4(SIMD.Float32x4(i, 1, 2, 3, 4),   [i,   1,   2,   3  ]);
 
     var b = i % 2 > 0 ;
     assertEqX4(SIMD.Bool32x4(),                           [false, false, false, false]);
     assertEqX4(SIMD.Bool32x4(b),                          [b,     false, false, false]);
     assertEqX4(SIMD.Bool32x4(b, true),                    [b,     true,  false, false]);
     assertEqX4(SIMD.Bool32x4(b, false, true),             [b,     false, true,  false]);
     assertEqX4(SIMD.Bool32x4(b, false, true, true),       [b,     false, true,  true ]);
     assertEqX4(SIMD.Bool32x4(b, false, true, true, true), [b,     false, true,  true ]);
+
+    assertEqVecArr(SIMD.Bool16x8(),
+                                [false, false, false, false, false, false, false, false]);
+    assertEqVecArr(SIMD.Bool16x8(b),
+                                [b,     false, false, false, false, false, false, false]);
+    assertEqVecArr(SIMD.Bool16x8(b,     true),
+                                [b,     true,  false, false, false, false, false, false]);
+    assertEqVecArr(SIMD.Bool16x8(b,     false, true),
+                                [b,     false, true,  false, false, false, false, false]);
+    assertEqVecArr(SIMD.Bool16x8(b,     false, true,  true),
+                                [b,     false, true,  true,  false, false, false, false]);
+    assertEqVecArr(SIMD.Bool16x8(b,     false, true,  true,  true),
+                                [b,     false, true,  true,  true,  false, false, false]);
+    assertEqVecArr(SIMD.Bool16x8(b,     false, true,  true,  true,  true),
+                                [b,     false, true,  true,  true,  true,  false, false]);
+
+    assertEqVecArr(SIMD.Bool8x16(),
+                                [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]);
+    assertEqVecArr(SIMD.Bool8x16(b),
+                                [b,     false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]);
+    assertEqVecArr(SIMD.Bool8x16(b,     true),
+                                [b,     true,  false, false, false, false, false, false, false, false, false, false, false, false, false, false]);
+    assertEqVecArr(SIMD.Bool8x16(b,     false, true),
+                                [b,     false, true,  false, false, false, false, false, false, false, false, false, false, false, false, false]);
+    assertEqVecArr(SIMD.Bool8x16(b,     false, true,  true),
+                                [b,     false, true,  true,  false, false, false, false, false, false, false, false, false, false, false, false]);
+    assertEqVecArr(SIMD.Bool8x16(b,     false, true,  true,  true),
+                                [b,     false, true,  true,  true,  false, false, false, false, false, false, false, false, false, false, false]);
+    assertEqVecArr(SIMD.Bool8x16(b,     false, true,  true,  true,  true,  false, true,  true,  true),
+                                [b,     false, true,  true,  true,  true,  false, true,  true,  true,  false, false, false, false, false, false]);
 }
 
 for(var i=0; i<300; i++) {
     test(i);
 }
--- a/js/src/jit-test/tests/auto-regress/bug909441.js
+++ b/js/src/jit-test/tests/auto-regress/bug909441.js
@@ -4,17 +4,16 @@ function f(code) {
         g = Function(code)
     } catch (e) {}
     g()
 }
 f("\
     Object.defineProperty(this,\"x\",{\
         get: function(){\
             evaluate(\"Array(x)\",{\
-                newContext:true,\
                 catchTermination:(function(){})\
             })\
         }\
     })\
 ");
 f("x");
 f(")");
 f("x");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1276882.js
@@ -0,0 +1,5 @@
+// |jit-test| error: sleep interval is not a number
+sleep(0.001);
+1;
+sleep(0.1);
+sleep(this);
--- a/js/src/jit-test/tests/basic/bug863084.js
+++ b/js/src/jit-test/tests/basic/bug863084.js
@@ -5,13 +5,13 @@ function f(code) {
     } catch (e) {}
     eval(code)
 }
 f("\
     z=evalcx('');\
     gc();\
     z.toString=(function(){\
         v=evaluate(\"(Math.atan2(\\\"\\\",this))\",{\
-            global:z,newContext:7,catchTermination:this\
+            global:z,catchTermination:this\
         });\
     });\
     String(z)\
 ")
--- a/js/src/jit-test/tests/basic/bug876226.js
+++ b/js/src/jit-test/tests/basic/bug876226.js
@@ -1,8 +1,6 @@
 // |jit-test| error: SyntaxError
 try {
-    evaluate("throw 3", {
-	newContext: new Set,
-    });
+    evaluate("throw 3");
 } catch(e) {}
 
 evaluate("()", {});
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/cross-context-stack-1.js
+++ /dev/null
@@ -1,21 +0,0 @@
-// Error().stack (ScriptFrameIter) is no longer context-bound.
-function beta() {
-    evaluate("function gamma() {\nstack = Error().stack;\n };\n gamma();", {newContext: true});
-}
-function alpha() {
-    beta();
-}
-alpha();
-assertEq(/alpha@/.test(stack), true);
-assertEq(/beta@/.test(stack), true);
-assertEq(/gamma@/.test(stack), true);
-assertEq(/delta@/.test(stack), false);
-
-function delta() {
-    evaluate("stack = Error().stack", {newContext: true});
-}
-delta();
-assertEq(/alpha@/.test(stack), false);
-assertEq(/beta@/.test(stack), false);
-assertEq(/gamma@/.test(stack), false);
-assertEq(/delta@/.test(stack), true);
--- a/js/src/jit-test/tests/basic/testBug895774.js
+++ b/js/src/jit-test/tests/basic/testBug895774.js
@@ -1,11 +1,11 @@
 var g1 = newGlobal();
 var g2 = newGlobal();
-g1.eval("function f1() { debugger; evaluate('debugger', {newContext:true}) }");
+g1.eval("function f1() { debugger; evaluate('debugger') }");
 g2.eval("function f2() { f1(); assertEq(Number(this), 42) }");
 g2.f1 = g1.f1;
 
 var dbg = new Debugger(g1,g2);
 dbg.onDebuggerStatement = function(frame) {
     var target = frame.older;
     dbg.onDebuggerStatement = function(frame) {
         assertEq(Number(target.this.unsafeDereference()), 42);
--- a/js/src/jit-test/tests/debug/Object-boundTargetFunction-02.js
+++ b/js/src/jit-test/tests/debug/Object-boundTargetFunction-02.js
@@ -10,16 +10,16 @@ var fw = gw.executeInGlobal("function f(
 assertEq(fw.isBoundFunction, false);
 assertEq(fw.boundThis, undefined);
 assertEq(fw.boundArguments, undefined);
 assertEq(fw.boundTargetFunction, undefined);
 
 var nw = gw.executeInGlobal("var n = Math.max; n").return;
 assertEq(nw.isBoundFunction, false);
 assertEq(nw.boundThis, undefined);
-assertEq(nw.boundArguments, undefined);
+assertEq(fw.boundArguments, undefined);
 assertEq(nw.boundTargetFunction, undefined);
 
 var ow = gw.executeInGlobal("var o = {}; o").return;
 assertEq(ow.isBoundFunction, false);
 assertEq(ow.boundThis, undefined);
-assertEq(ow.boundArguments, undefined);
+assertEq(fw.boundArguments, undefined);
 assertEq(ow.boundTargetFunction, undefined);
--- a/js/src/jit-test/tests/debug/bug1161332.js
+++ b/js/src/jit-test/tests/debug/bug1161332.js
@@ -1,12 +1,12 @@
 // |jit-test| error: Error
 
 var g = newGlobal();
-g.eval('function f(a) { if (a == 1) debugger; evaluate("f(" + a + " - 1);", {newContext: true}); }');
+g.eval('function f(a) { if (a == 1) debugger; evaluate("f(" + a + " - 1);"); }');
 var N = 2;
 var dbg = new Debugger(g);
 var frames = [];
 dbg.onEnterFrame = function (frame) {
    frames.push(frame);
    frame.onPop = function () { assertEq(frame.onPop, frame.onPop); };
 };
 dbg.onDebuggerStatement = function (frame) {
deleted file mode 100644
--- a/js/src/jit-test/tests/debug/cross-context-1.js
+++ /dev/null
@@ -1,20 +0,0 @@
-// frame.eval can evaluate code in a frame pushed in another context. Bug 749697.
-
-// In other words, the debugger can see all frames on the stack, even though
-// each frame is attached to a particular JSContext and multiple JSContexts may
-// have frames on the stack.
-
-var g = newGlobal();
-g.eval('function f(a) { debugger; evaluate("debugger;", {newContext: true}); }');
-
-var dbg = new Debugger(g);
-var hits = 0;
-dbg.onDebuggerStatement = function (frame1) {
-    dbg.onDebuggerStatement = function (frame2) {
-        assertEq(frame1.eval("a").return, 31);
-        hits++;
-    };
-};
-
-g.f(31);
-assertEq(hits, 1);
deleted file mode 100644
--- a/js/src/jit-test/tests/debug/cross-context-2.js
+++ /dev/null
@@ -1,32 +0,0 @@
-// The debugger can eval in any frame in the stack even if every frame was
-// pushed in a different context.
-
-var g = newGlobal();
-g.eval('function f(a) {\n' +
-       '    if (a == 1)\n' +
-       '        debugger;\n' +
-       '    else\n' +
-       '        evaluate("f(" + a + " - 1);", {newContext: true});\n' +
-       '}\n');
-var N = 9;
-
-var dbg = new Debugger(g);
-var frames = [];
-var hits = 0;
-dbg.onEnterFrame = function (frame) {
-    if (frame.type == "call" && frame.callee.name == "f") {
-        frames.push(frame);
-        frame.onPop = function () { assertEq(frames.pop(), frame); };
-    }
-};
-dbg.onDebuggerStatement = function (frame) {
-    assertEq(frames.length, N);
-    var i = N;
-    for (var f of frames)
-        assertEq(f.eval('a').return, i--);
-    hits++;
-};
-
-g.f(N);
-assertEq(hits, 1);
-assertEq(frames.length, 0);
deleted file mode 100644
--- a/js/src/jit-test/tests/debug/cross-context-3.js
+++ /dev/null
@@ -1,14 +0,0 @@
-var g = newGlobal();
-g.eval('function f() { debugger; evaluate("debugger;", {newContext: true}); }');
-
-var dbg = new Debugger(g);
-var hits = 0;
-dbg.onDebuggerStatement = function (frame1) {
-    dbg.onDebuggerStatement = function (frame2) {
-        assertEq(frame1.eval("throw 'ponies'").throw, 'ponies');
-        hits++;
-    };
-};
-
-g.f();
-assertEq(hits, 1);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1254197.js
@@ -0,0 +1,16 @@
+setJitCompilerOption("ion.warmup.trigger", 1);
+function f(x) {
+    var w = [];
+    var i = 0;
+    for (var count = 0; count < 3; count++) {
+        for (var j = 0; j < 60; j++) {
+            if (j < 1) {
+                w[0] = x[i];
+            } else {
+                w[0][0];
+            }
+        }
+        i = 1;
+    }
+}
+f([NaN, 0]);
--- a/js/src/jit-test/tests/jaeger/bug768313.js
+++ b/js/src/jit-test/tests/jaeger/bug768313.js
@@ -1,6 +1,6 @@
 // |jit-test| --dump-bytecode
 
 function f() { }
-evaluate('function g() { f(); }', {newContext: true});
+evaluate('function g() { f(); }');
 for (var i = 0; i < 2; i++)
     g(0);
deleted file mode 100644
--- a/js/src/jit-test/tests/saved-stacks/new-context.js
+++ /dev/null
@@ -1,24 +0,0 @@
-// Test that we can save stacks that cross contexts.
-
-const stack = (function iife() {
-  return evaluate("(" + function evalFrame() {
-    return saveStack();
-  } + "())", {
-    newContext: true,
-    fileName: "evaluated"
-  });
-}());
-
-print(stack);
-
-assertEq(stack.functionDisplayName, "evalFrame");
-assertEq(stack.source, "evaluated");
-
-assertEq(stack.parent.source, "evaluated");
-
-assertEq(stack.parent.parent.functionDisplayName, "iife");
-assertEq(/new-context\.js$/.test(stack.parent.parent.source), true);
-
-assertEq(/new-context\.js$/.test(stack.parent.parent.parent.source), true);
-
-assertEq(stack.parent.parent.parent.parent, null);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -7758,16 +7758,30 @@ ICIteratorClose_Fallback::Compiler::gene
 static bool
 TryAttachInstanceOfStub(JSContext* cx, BaselineFrame* frame, ICInstanceOf_Fallback* stub,
                         HandleFunction fun, bool* attached)
 {
     MOZ_ASSERT(!*attached);
     if (fun->isBoundFunction())
         return true;
 
+    // If the user has supplied their own @@hasInstance method we shouldn't
+    // clobber it.
+    if (!js::FunctionHasDefaultHasInstance(fun, cx->wellKnownSymbols()))
+        return true;
+
+    // Refuse to optimize any function whose [[Prototype]] isn't
+    // Function.prototype.
+    if (!fun->hasStaticPrototype() || fun->hasUncacheableProto())
+        return true;
+
+    Value funProto = cx->global()->getPrototype(JSProto_Function);
+    if (funProto.isObject() && fun->staticPrototype() != &funProto.toObject())
+        return true;
+
     Shape* shape = fun->lookupPure(cx->names().prototype);
     if (!shape || !shape->hasSlot() || !shape->hasDefaultGetter())
         return true;
 
     uint32_t slot = shape->slot();
     MOZ_ASSERT(fun->numFixedSlots() == 0, "Stub code relies on this");
 
     if (!fun->getSlot(slot).isObject())
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3473,32 +3473,52 @@ CodeGenerator::visitOutOfLineCallPostWri
     saveLiveVolatile(ool->lir());
     const LAllocation* obj = ool->object();
     emitPostWriteBarrier(obj);
     restoreLiveVolatile(ool->lir());
 
     masm.jump(ool->rejoin());
 }
 
+void
+CodeGenerator::maybeEmitGlobalBarrierCheck(const LAllocation* maybeGlobal, OutOfLineCode* ool)
+{
+    // Check whether an object is a global that we have already barriered before
+    // calling into the VM.
+
+    if (!maybeGlobal->isConstant())
+        return;
+
+    JSObject* obj = &maybeGlobal->toConstant()->toObject();
+    if (!obj->is<GlobalObject>())
+        return;
+
+    JSCompartment* comp = obj->compartment();
+    auto addr = AbsoluteAddress(&comp->globalWriteBarriered);
+    masm.branch32(Assembler::NotEqual, addr, Imm32(0), ool->rejoin());
+}
+
 template <class LPostBarrierType>
 void
 CodeGenerator::visitPostWriteBarrierCommonO(LPostBarrierType* lir, OutOfLineCode* ool)
 {
     addOutOfLineCode(ool, lir->mir());
 
     Register temp = ToTempRegisterOrInvalid(lir->temp());
 
     if (lir->object()->isConstant()) {
         // Constant nursery objects cannot appear here, see LIRGenerator::visitPostWriteElementBarrier.
         MOZ_ASSERT(!IsInsideNursery(&lir->object()->toConstant()->toObject()));
     } else {
         masm.branchPtrInNurseryRange(Assembler::Equal, ToRegister(lir->object()), temp,
                                      ool->rejoin());
     }
 
+    maybeEmitGlobalBarrierCheck(lir->object(), ool);
+
     masm.branchPtrInNurseryRange(Assembler::Equal, ToRegister(lir->value()), temp, ool->entry());
 
     masm.bind(ool->rejoin());
 }
 
 template <class LPostBarrierType>
 void
 CodeGenerator::visitPostWriteBarrierCommonV(LPostBarrierType* lir, OutOfLineCode* ool)
@@ -3510,16 +3530,18 @@ CodeGenerator::visitPostWriteBarrierComm
     if (lir->object()->isConstant()) {
         // Constant nursery objects cannot appear here, see LIRGenerator::visitPostWriteElementBarrier.
         MOZ_ASSERT(!IsInsideNursery(&lir->object()->toConstant()->toObject()));
     } else {
         masm.branchPtrInNurseryRange(Assembler::Equal, ToRegister(lir->object()), temp,
                                      ool->rejoin());
     }
 
+    maybeEmitGlobalBarrierCheck(lir->object(), ool);
+
     ValueOperand value = ToValue(lir, LPostBarrierType::Input);
     masm.branchValueIsNurseryObject(Assembler::Equal, value, temp, ool->entry());
 
     masm.bind(ool->rejoin());
 }
 
 void
 CodeGenerator::visitPostWriteBarrierO(LPostWriteBarrierO* lir)
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -514,16 +514,18 @@ class CodeGenerator : public CodeGenerat
 
     // Bailout if an element about to be written to is a hole.
     void emitStoreHoleCheck(Register elements, const LAllocation* index, int32_t offsetAdjustment,
                             LSnapshot* snapshot);
 
     void emitAssertRangeI(const Range* r, Register input);
     void emitAssertRangeD(const Range* r, FloatRegister input, FloatRegister temp);
 
+    void maybeEmitGlobalBarrierCheck(const LAllocation* maybeGlobal, OutOfLineCode* ool);
+
     Vector<CodeOffset, 0, JitAllocPolicy> ionScriptLabels_;
 
     struct SharedStub {
         ICStub::Kind kind;
         IonICEntry entry;
         CodeOffset label;
 
         SharedStub(ICStub::Kind kind, IonICEntry entry, CodeOffset label)
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -9808,17 +9808,18 @@ IonBuilder::jsop_getelem_dense(MDefiniti
 
         // If maybeUndefined was true, the typeset must have undefined, and
         // then either additional types or a barrier. This means we should
         // never have a typed version of LoadElementHole.
         MOZ_ASSERT(knownType == MIRType::Value);
     }
 
     if (knownType != MIRType::Value) {
-        load->setResultType(knownType);
+        if (unboxedType == JSVAL_TYPE_MAGIC)
+            load->setResultType(knownType);
         load->setResultTypeSet(types);
     }
 
     current->push(load);
     return pushTypeBarrier(load, types, barrier);
 }
 
 MInstruction*
@@ -13881,20 +13882,46 @@ IonBuilder::jsop_instanceof()
     // If this is an 'x instanceof function' operation and we can determine the
     // exact function and prototype object being tested for, use a typed path.
     do {
         TemporaryTypeSet* rhsTypes = rhs->resultTypeSet();
         JSObject* rhsObject = rhsTypes ? rhsTypes->maybeSingleton() : nullptr;
         if (!rhsObject || !rhsObject->is<JSFunction>() || rhsObject->isBoundFunction())
             break;
 
+        // Refuse to optimize anything whose [[Prototype]] isn't Function.prototype
+        // since we can't guarantee that it uses the default @@hasInstance method.
+        if (rhsObject->hasUncacheableProto() || !rhsObject->hasStaticPrototype())
+            break;
+
+        Value funProto = script()->global().getPrototype(JSProto_Function);
+        if (!funProto.isObject() || rhsObject->staticPrototype() != &funProto.toObject())
+            break;
+
+        // If the user has supplied their own @@hasInstance method we shouldn't
+        // clobber it.
+        JSFunction* fun = &rhsObject->as<JSFunction>();
+        const WellKnownSymbols* symbols = &compartment->runtime()->wellKnownSymbols();
+        if (!js::FunctionHasDefaultHasInstance(fun, *symbols))
+            break;
+
</