merge mozilla-inbound to mozilla-central a=merge
authorIris Hsiao <ihsiao@mozilla.com>
Tue, 25 Apr 2017 11:21:30 +0800
changeset 354691 85932a5027c024900bb0e58cdbc52ecf9a32e1f5
parent 354673 f0621f7f0520476b10eda7b4a59cb976b1e1f2b9 (current diff)
parent 354690 85427dbc69bda7c9ec30cc4f1b829562ddc68f41 (diff)
child 354708 ac529a874366e1f3c3040490fe5eaf8089cd8874
child 354723 ec0146387c72ed8c08a5574e38a8f74fb7cfcc9f
push id31709
push userihsiao@mozilla.com
push dateTue, 25 Apr 2017 03:21:59 +0000
treeherdermozilla-central@85932a5027c0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
js/src/wasm/WasmCode.cpp
js/src/wasm/WasmRuntime.cpp
js/src/wasm/WasmRuntime.h
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -994,16 +994,23 @@ pref("security.sandbox.content.level", 1
 #endif
 
 // This controls the depth of stack trace that is logged when Windows sandbox
 // logging is turned on.  This is only currently available for the content
 // process because the only other sandbox (for GMP) has too strict a policy to
 // allow stack tracing.  This does not require a restart to take effect.
 pref("security.sandbox.windows.log.stackTraceDepth", 0);
 #endif
+
+// This controls the strength of the Windows GPU process sandbox.  Changes
+// will require restart.
+// For information on what the level number means, see
+// SetSecurityLevelForGPUProcess() in
+// security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
+pref("security.sandbox.gpu.level", 1);
 #endif
 
 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) && defined(MOZ_CONTENT_SANDBOX)
 // This pref is discussed in bug 1083344, the naming is inspired from its
 // Windows counterpart, but on Mac it's an integer which means:
 // 0 -> "no sandbox"
 // 1 -> "preliminary content sandboxing enabled: write access to
 //       home directory is prevented"
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -5098,17 +5098,18 @@ var TabsProgressListener = {
 function nsBrowserAccess() { }
 
 nsBrowserAccess.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow, Ci.nsISupports]),
 
   _openURIInNewTab(aURI, aReferrer, aReferrerPolicy, aIsPrivate,
                              aIsExternal, aForceNotRemote = false,
                              aUserContextId = Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID,
-                             aOpener = null, aTriggeringPrincipal = null) {
+                             aOpener = null, aTriggeringPrincipal = null,
+                             aNextTabParentId = 0) {
     let win, needToFocusWin;
 
     // try the current window.  if we're in a popup, fall back on the most recent browser window
     if (window.toolbar.visible)
       win = window;
     else {
       win = RecentWindow.getMostRecentBrowserWindow({private: aIsPrivate});
       needToFocusWin = true;
@@ -5131,16 +5132,17 @@ nsBrowserAccess.prototype = {
                                       triggeringPrincipal: aTriggeringPrincipal,
                                       referrerURI: aReferrer,
                                       referrerPolicy: aReferrerPolicy,
                                       userContextId: aUserContextId,
                                       fromExternal: aIsExternal,
                                       inBackground: loadInBackground,
                                       forceNotRemote: aForceNotRemote,
                                       opener: aOpener,
+                                      nextTabParentId: aNextTabParentId,
                                       });
     let browser = win.gBrowser.getBrowserForTab(tab);
 
     if (needToFocusWin || (!loadInBackground && aIsExternal))
       win.focus();
 
     return browser;
   },
@@ -5233,17 +5235,18 @@ nsBrowserAccess.prototype = {
                                     });
         }
         if (!gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground"))
           window.focus();
     }
     return newWindow;
   },
 
-  openURIInFrame: function browser_openURIInFrame(aURI, aParams, aWhere, aFlags) {
+  openURIInFrame: function browser_openURIInFrame(aURI, aParams, aWhere, aFlags,
+                                                  aNextTabParentId) {
     if (aWhere != Ci.nsIBrowserDOMWindow.OPEN_NEWTAB) {
       dump("Error: openURIInFrame can only open in new tabs");
       return null;
     }
 
     var isExternal = !!(aFlags & Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
 
     var userContextId = aParams.openerOriginAttributes &&
@@ -5252,17 +5255,18 @@ nsBrowserAccess.prototype = {
                           : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID
 
     let referrer = aParams.referrer ? makeURI(aParams.referrer) : null;
     let browser = this._openURIInNewTab(aURI, referrer,
                                         aParams.referrerPolicy,
                                         aParams.isPrivate,
                                         isExternal, false,
                                         userContextId, null,
-                                        aParams.triggeringPrincipal);
+                                        aParams.triggeringPrincipal,
+                                        aNextTabParentId);
     if (browser)
       return browser.QueryInterface(Ci.nsIFrameLoaderOwner);
 
     return null;
   },
 
   isTabContentWindow(aWindow) {
     return gBrowser.browsers.some(browser => browser.contentWindow == aWindow);
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1483,16 +1483,17 @@
             var aForceNotRemote;
             var aPreferredRemoteType;
             var aNoReferrer;
             var aUserContextId;
             var aSameProcessAsFrameLoader;
             var aOriginPrincipal;
             var aOpener;
             var aCreateLazyBrowser;
+            var aNextTabParentId;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aTriggeringPrincipal      = params.triggeringPrincipal
               aReferrerURI              = params.referrerURI;
               aReferrerPolicy           = params.referrerPolicy;
               aCharset                  = params.charset;
@@ -1507,16 +1508,17 @@
               aPreferredRemoteType      = params.preferredRemoteType;
               aNoReferrer               = params.noReferrer;
               aUserContextId            = params.userContextId;
               aSameProcessAsFrameLoader = params.sameProcessAsFrameLoader;
               aOriginPrincipal          = params.originPrincipal;
               aOpener                   = params.opener;
               aIsPrerendered            = params.isPrerendered;
               aCreateLazyBrowser        = params.createLazyBrowser;
+              aNextTabParentId          = params.nextTabParentId;
             }
 
             var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
                          Services.prefs.getBoolPref("browser.tabs.loadInBackground");
             var owner = bgLoad ? null : this.selectedTab;
 
             var tab = this.addTab(aURI, {
                                   triggeringPrincipal: aTriggeringPrincipal,
@@ -1533,17 +1535,18 @@
                                   forceNotRemote: aForceNotRemote,
                                   createLazyBrowser: aCreateLazyBrowser,
                                   preferredRemoteType: aPreferredRemoteType,
                                   noReferrer: aNoReferrer,
                                   userContextId: aUserContextId,
                                   originPrincipal: aOriginPrincipal,
                                   sameProcessAsFrameLoader: aSameProcessAsFrameLoader,
                                   opener: aOpener,
-                                  isPrerendered: aIsPrerendered });
+                                  isPrerendered: aIsPrerendered,
+                                  nextTabParentId: aNextTabParentId });
             if (!bgLoad)
               this.selectedTab = tab;
 
             return tab;
          ]]>
         </body>
       </method>
 
@@ -1969,16 +1972,21 @@
               b.setAttribute("selectmenulist", this.getAttribute("selectmenulist"));
 
             if (this.hasAttribute("datetimepicker")) {
               b.setAttribute("datetimepicker", this.getAttribute("datetimepicker"));
             }
 
             b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
 
+            if (aParams.nextTabParentId) {
+              // Gecko is going to read this attribute and use it.
+              b.setAttribute("nextTabParentId", aParams.nextTabParentId.toString());
+            }
+
             if (aParams.sameProcessAsFrameLoader) {
               b.sameProcessAsFrameLoader = aParams.sameProcessAsFrameLoader;
             }
 
             // Create the browserStack container
             var stack = document.createElementNS(NS_XUL, "stack");
             stack.className = "browserStack";
             stack.appendChild(b);
@@ -2213,16 +2221,17 @@
             var aUserContextId;
             var aEventDetail;
             var aSameProcessAsFrameLoader;
             var aOriginPrincipal;
             var aDisallowInheritPrincipal;
             var aOpener;
             var aCreateLazyBrowser;
             var aSkipBackgroundNotify;
+            var aNextTabParentId;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aTriggeringPrincipal      = params.triggeringPrincipal;
               aReferrerURI              = params.referrerURI;
               aReferrerPolicy           = params.referrerPolicy;
               aCharset                  = params.charset;
@@ -2240,16 +2249,17 @@
               aEventDetail              = params.eventDetail;
               aSameProcessAsFrameLoader = params.sameProcessAsFrameLoader;
               aOriginPrincipal          = params.originPrincipal;
               aDisallowInheritPrincipal = params.disallowInheritPrincipal;
               aOpener                   = params.opener;
               aIsPrerendered            = params.isPrerendered;
               aCreateLazyBrowser        = params.createLazyBrowser;
               aSkipBackgroundNotify     = params.skipBackgroundNotify;
+              aNextTabParentId          = params.nextTabParentId;
             }
 
             // if we're adding tabs, we're past interrupt mode, ditch the owner
             if (this.mCurrentTab.owner)
               this.mCurrentTab.owner = null;
 
             var t = document.createElementNS(NS_XUL, "tab");
 
@@ -2348,17 +2358,18 @@
 
             if (!b) {
               // No preloaded browser found, create one.
               b = this._createBrowser({ remoteType,
                                         uriIsAboutBlank,
                                         userContextId: aUserContextId,
                                         sameProcessAsFrameLoader: aSameProcessAsFrameLoader,
                                         opener: aOpener,
-                                        isPrerendered: aIsPrerendered });
+                                        isPrerendered: aIsPrerendered,
+                                        nextTabParentId: aNextTabParentId });
             }
 
             t.linkedBrowser = b;
             this._tabForBrowser.set(b, t);
             t.permanentKey = b.permanentKey;
             t._browserParams = { uriIsAboutBlank,
                                  remoteType,
                                  usingPreloadedContent };
--- a/browser/locales/searchplugins/rakuten.xml
+++ b/browser/locales/searchplugins/rakuten.xml
@@ -2,15 +2,15 @@
    - 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/. -->
 
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
 <ShortName>楽天市場</ShortName>
 <Description>楽天市場 商品検索</Description>
 <InputEncoding>EUC-JP</InputEncoding>
 <Image width="16" height="16">data:image/x-icon;base64,R0lGODlhEAAQALMOAOefn9psbMswMMMQEPjj4/PPz++/v+OPj99/f9+AgMcgIM9AQOuvr////wAAAL8AACH5BAEAAA4ALAAAAAAQABAAAARf0EnJ0FqIze2YemCoaBsQniEwMeIloOQHJk1NvKDSnTRQNIZThddYPAiNk4UYCDQIpwux1ghEEUTGc6BkhWiLA1DokD3Ag5/1odvlFlzFAkdymFAn1caDH3FWFhh1EhEAOw==</Image>
-<Url type="text/html" method="GET" template="http://pt.afl.rakuten.co.jp/c/013ca98b.cd7c5f0c/" resultdomain="rakuten.co.jp">
+<Url type="text/html" method="GET" template="https://pt.afl.rakuten.co.jp/c/013ca98b.cd7c5f0c/" resultdomain="rakuten.co.jp">
   <Param name="sitem" value="{searchTerms}"/>
   <Param name="sv" value="2"/>
   <Param name="p" value="0"/>
 </Url>
 <SearchForm>http://www.rakuten.co.jp/</SearchForm>
 </SearchPlugin>
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -2952,19 +2952,36 @@ nsFrameLoader::TryRemoteBrowser()
 
   PROFILER_LABEL("nsFrameLoader", "CreateRemoteBrowser",
     js::ProfileEntry::Category::OTHER);
 
   MutableTabContext context;
   nsresult rv = GetNewTabContext(&context);
   NS_ENSURE_SUCCESS(rv, false);
 
+  uint64_t nextTabParentId = 0;
+  if (mOwnerContent) {
+    nsAutoString nextTabParentIdAttr;
+    mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::nextTabParentId,
+                           nextTabParentIdAttr);
+    nextTabParentId = strtoull(NS_ConvertUTF16toUTF8(nextTabParentIdAttr).get(),
+                               nullptr, 10);
+
+    // We may be in a window that was just opened, so try the
+    // nsIBrowserDOMWindow API as a backup.
+    if (!nextTabParentId && window) {
+      Unused << window->GetNextTabParentId(&nextTabParentId);
+    }
+  }
+
   nsCOMPtr<Element> ownerElement = mOwnerContent;
   mRemoteBrowser = ContentParent::CreateBrowser(context, ownerElement,
-                                                openerContentParent, sameTabGroupAs);
+                                                openerContentParent,
+                                                sameTabGroupAs,
+                                                nextTabParentId);
   if (!mRemoteBrowser) {
     return false;
   }
   // Now that mRemoteBrowser is set, we can initialize the RenderFrameParent
   mRemoteBrowser->InitRenderFrame();
 
   MaybeUpdatePrimaryTabParent(eTabParentChanged);
 
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -647,16 +647,17 @@ GK_ATOM(namespaceUri, "namespace-uri")
 GK_ATOM(NaN, "NaN")
 GK_ATOM(nativeAnonymousChildList, "nativeAnonymousChildList")
 GK_ATOM(nav, "nav")
 GK_ATOM(negate, "negate")
 GK_ATOM(never, "never")
 GK_ATOM(_new, "new")
 GK_ATOM(newline, "newline")
 GK_ATOM(nextBidi, "NextBidi")
+GK_ATOM(nextTabParentId, "nextTabParentId")
 GK_ATOM(no, "no")
 GK_ATOM(noautofocus, "noautofocus")
 GK_ATOM(noautohide, "noautohide")
 GK_ATOM(norolluponanchor, "norolluponanchor")
 GK_ATOM(nobr, "nobr")
 GK_ATOM(node, "node")
 GK_ATOM(nodefaultsrc, "nodefaultsrc")
 GK_ATOM(nodeSet, "node-set")
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -11558,34 +11558,40 @@ nsGlobalWindow::ShowSlowScriptDialog()
   // If our document is not active, just kill the script: we've been unloaded
   if (!AsInner()->HasActiveDocument()) {
     return KillSlowScript;
   }
 
   // Check if we should offer the option to debug
   JS::AutoFilename filename;
   unsigned lineno;
-  bool hasFrame = JS::DescribeScriptedCaller(cx, &filename, &lineno);
+  // Computing the line number can be very expensive (see bug 1330231 for
+  // example), and we don't use the line number anywhere except than in the
+  // parent process, so we avoid computing it elsewhere.  This gives us most of
+  // the wins we are interested in, since the source of the slowness here is
+  // minified scripts which is more common in Web content that is loaded in the
+  // content process.
+  unsigned* linenop = XRE_IsParentProcess() ? &lineno : nullptr;
+  bool hasFrame = JS::DescribeScriptedCaller(cx, &filename, linenop);
 
   // Record the slow script event if we haven't done so already for this inner window
   // (which represents a particular page to the user).
   if (!mHasHadSlowScript) {
     Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_PAGE_COUNT, 1);
   }
   mHasHadSlowScript = true;
 
   if (XRE_IsContentProcess() &&
       ProcessHangMonitor::Get()) {
     ProcessHangMonitor::SlowScriptAction action;
     RefPtr<ProcessHangMonitor> monitor = ProcessHangMonitor::Get();
     nsIDocShell* docShell = GetDocShell();
     nsCOMPtr<nsITabChild> child = docShell ? docShell->GetTabChild() : nullptr;
     action = monitor->NotifySlowScript(child,
-                                       filename.get(),
-                                       lineno);
+                                       filename.get());
     if (action == ProcessHangMonitor::Terminate) {
       return KillSlowScript;
     }
 
     if (action == ProcessHangMonitor::StartDebugger) {
       // Spin a nested event loop so that the debugger in the parent can fetch
       // any information it needs. Once the debugger has started, return to the
       // script.
--- a/dom/interfaces/base/nsIBrowserDOMWindow.idl
+++ b/dom/interfaces/base/nsIBrowserDOMWindow.idl
@@ -104,17 +104,18 @@ interface nsIBrowserDOMWindow : nsISuppo
           in short aWhere, in long aFlags);
 
   /**
    * As above, but return the nsIFrameLoaderOwner for the new window.
    // XXXbz is this the right API?
    // See bug 537428
    */
   nsIFrameLoaderOwner openURIInFrame(in nsIURI aURI, in nsIOpenURIInFrameParams params,
-                                     in short aWhere, in long aFlags);
+                                     in short aWhere, in long aFlags,
+                                     in unsigned long long aNextTabParentId);
 
   /**
    * @param  aWindow the window to test.
    * @return whether the window is the main content window for any
    *         currently open tab in this toplevel browser window.
    */
   boolean isTabContentWindow(in nsIDOMWindow aWindow);
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -530,16 +530,18 @@ ScriptableCPInfo::GetMessageManager(nsIM
 
 } // anonymous namespace
 
 nsTArray<ContentParent*>* ContentParent::sPrivateContent;
 StaticAutoPtr<LinkedList<ContentParent> > ContentParent::sContentParents;
 #if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
 UniquePtr<SandboxBrokerPolicyFactory> ContentParent::sSandboxBrokerPolicyFactory;
 #endif
+uint64_t ContentParent::sNextTabParentId = 0;
+nsDataHashtable<nsUint64HashKey, TabParent*> ContentParent::sNextTabParents;
 
 // This is true when subprocess launching is enabled.  This is the
 // case between StartUp() and ShutDown() or JoinAllSubprocesses().
 static bool sCanLaunchSubprocesses;
 
 // Set to true if the DISABLE_UNSAFE_CPOW_WARNINGS environment variable is
 // set.
 static bool sDisableUnsafeCPOWWarnings = false;
@@ -1147,27 +1149,33 @@ ContentParent::RecvFindPlugins(const uin
   *aRv = mozilla::plugins::FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
   return IPC_OK();
 }
 
 /*static*/ TabParent*
 ContentParent::CreateBrowser(const TabContext& aContext,
                              Element* aFrameElement,
                              ContentParent* aOpenerContentParent,
-                             TabParent* aSameTabGroupAs)
+                             TabParent* aSameTabGroupAs,
+                             uint64_t aNextTabParentId)
 {
   PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
 
   if (!sCanLaunchSubprocesses) {
     return nullptr;
   }
 
-  if (TabParent* parent = TabParent::GetNextTabParent()) {
-    parent->SetOwnerElement(aFrameElement);
-    return parent;
+  if (aNextTabParentId) {
+    if (TabParent* parent =
+          sNextTabParents.GetAndRemove(aNextTabParentId).valueOr(nullptr)) {
+      MOZ_ASSERT(!parent->GetOwnerElement(),
+                 "Shouldn't have an owner elemnt before");
+      parent->SetOwnerElement(aFrameElement);
+      return parent;
+    }
   }
 
   ProcessPriority initialPriority = GetInitialProcessPriority(aFrameElement);
   bool isInContentProcess = !XRE_IsParentProcess();
   TabId tabId;
 
   nsIDocShell* docShell = GetOpenerDocShellHelper(aFrameElement);
   TabId openerTabId;
@@ -4457,16 +4465,17 @@ ContentParent::CommonCreateWindow(PBrows
                                   const bool& aCalledFromJS,
                                   const bool& aPositionSpecified,
                                   const bool& aSizeSpecified,
                                   nsIURI* aURIToLoad,
                                   const nsCString& aFeatures,
                                   const nsCString& aBaseURI,
                                   const OriginAttributes& aOpenerOriginAttributes,
                                   const float& aFullZoom,
+                                  uint64_t aNextTabParentId,
                                   nsresult& aResult,
                                   nsCOMPtr<nsITabParent>& aNewTabParent,
                                   bool* aWindowIsNew)
 
 {
   // The content process should never be in charge of computing whether or
   // not a window should be private or remote - the parent will do that.
   const uint32_t badFlags = nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW |
@@ -4539,16 +4548,17 @@ ContentParent::CommonCreateWindow(PBrows
     nsCOMPtr<nsIOpenURIInFrameParams> params =
       new nsOpenURIInFrameParams(aOpenerOriginAttributes);
     params->SetReferrer(NS_ConvertUTF8toUTF16(aBaseURI));
     params->SetIsPrivate(isPrivate);
 
     nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner;
     aResult = browserDOMWin->OpenURIInFrame(aURIToLoad, params, openLocation,
                                             nsIBrowserDOMWindow::OPEN_NEW,
+                                            aNextTabParentId,
                                             getter_AddRefs(frameLoaderOwner));
     if (NS_SUCCEEDED(aResult) && frameLoaderOwner) {
       RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
       if (frameLoader) {
         frameLoader->GetTabParent(getter_AddRefs(aNewTabParent));
       }
     } else {
       *aWindowIsNew = false;
@@ -4560,16 +4570,17 @@ ContentParent::CommonCreateWindow(PBrows
   nsCOMPtr<nsPIWindowWatcher> pwwatch =
     do_GetService(NS_WINDOWWATCHER_CONTRACTID, &aResult);
   if (NS_WARN_IF(NS_FAILED(aResult))) {
     return IPC_OK();
   }
 
   aResult = pwwatch->OpenWindowWithTabParent(aSetOpener ? thisTabParent : nullptr,
                                              aFeatures, aCalledFromJS, aFullZoom,
+                                             aNextTabParentId,
                                              getter_AddRefs(aNewTabParent));
   if (NS_WARN_IF(NS_FAILED(aResult))) {
     return IPC_OK();
   }
 
   if (aURIToLoad) {
     nsCOMPtr<mozIDOMWindowProxy> openerWindow;
     if (aSetOpener && thisTabParent) {
@@ -4625,32 +4636,38 @@ ContentParent::RecvCreateWindow(PBrowser
       }
     }
   });
 
   // Content has requested that we open this new content window, so
   // we must have an opener.
   newTab->SetHasContentOpener(true);
 
-  TabParent::AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad);
+  TabParent::AutoUseNewTab aunt(newTab, aURLToLoad);
+  const uint64_t nextTabParentId = ++sNextTabParentId;
+  sNextTabParents.Put(nextTabParentId, newTab);
 
   nsCOMPtr<nsITabParent> newRemoteTab;
   mozilla::ipc::IPCResult ipcResult =
     CommonCreateWindow(aThisTab, /* aSetOpener = */ true, aChromeFlags,
                        aCalledFromJS, aPositionSpecified, aSizeSpecified,
                        nullptr, aFeatures, aBaseURI, aOpenerOriginAttributes,
-                       aFullZoom, *aResult, newRemoteTab, aWindowIsNew);
+                       aFullZoom, nextTabParentId, *aResult,
+                       newRemoteTab, aWindowIsNew);
   if (!ipcResult) {
     return ipcResult;
   }
 
   if (NS_WARN_IF(NS_FAILED(*aResult))) {
     return IPC_OK();
   }
 
+  if (sNextTabParents.GetAndRemove(nextTabParentId).valueOr(nullptr)) {
+    *aWindowIsNew = false;
+  }
   MOZ_ASSERT(TabParent::GetFrom(newRemoteTab) == newTab);
 
   newTab->SwapFrameScriptsFrom(*aFrameScripts);
 
   RenderFrameParent* rfp = static_cast<RenderFrameParent*>(aRenderFrame);
   if (!newTab->SetRenderFrame(rfp) ||
       !newTab->GetRenderFrameInfo(aTextureFactoryIdentifier, aLayersId)) {
     *aResult = NS_ERROR_FAILURE;
@@ -4676,17 +4693,18 @@ ContentParent::RecvCreateWindowInDiffere
   nsCOMPtr<nsITabParent> newRemoteTab;
   bool windowIsNew;
   nsCOMPtr<nsIURI> uriToLoad = DeserializeURI(aURIToLoad);
   nsresult rv;
   mozilla::ipc::IPCResult ipcResult =
     CommonCreateWindow(aThisTab, /* aSetOpener = */ false, aChromeFlags,
                        aCalledFromJS, aPositionSpecified, aSizeSpecified,
                        uriToLoad, aFeatures, aBaseURI, aOpenerOriginAttributes,
-                       aFullZoom, rv, newRemoteTab, &windowIsNew);
+                       aFullZoom, /* aNextTabParentId = */ 0, rv,
+                       newRemoteTab, &windowIsNew);
   if (!ipcResult) {
     return ipcResult;
   }
 
   if (NS_FAILED(rv)) {
     NS_WARNING("Call to CommonCreateWindow failed.");
   }
 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -184,17 +184,18 @@ public:
    * Get or create a content process for the given TabContext.  aFrameElement
    * should be the frame/iframe element with which this process will
    * associated.
    */
   static TabParent*
   CreateBrowser(const TabContext& aContext,
                 Element* aFrameElement,
                 ContentParent* aOpenerContentParent,
-                TabParent* aSameTabGroupAs);
+                TabParent* aSameTabGroupAs,
+                uint64_t aNextTabParentId);
 
   static void GetAll(nsTArray<ContentParent*>& aArray);
 
   static void GetAllEvenIfDead(nsTArray<ContentParent*>& aArray);
 
   const nsAString& GetRemoteType() const;
 
   enum CPIteratorPolicy {
@@ -714,16 +715,17 @@ private:
                      const bool& aCalledFromJS,
                      const bool& aPositionSpecified,
                      const bool& aSizeSpecified,
                      nsIURI* aURIToLoad,
                      const nsCString& aFeatures,
                      const nsCString& aBaseURI,
                      const OriginAttributes& aOpenerOriginAttributes,
                      const float& aFullZoom,
+                     uint64_t aNextTabParentId,
                      nsresult& aResult,
                      nsCOMPtr<nsITabParent>& aNewTabParent,
                      bool* aWindowIsNew);
 
   FORWARD_SHMEM_ALLOCATOR_TO(PContentParent)
 
   ContentParent(ContentParent* aOpener,
                 const nsAString& aRemoteType);
@@ -1269,16 +1271,19 @@ private:
   nsRefPtrHashtable<nsIDHashKey, GetFilesHelper> mGetFilesPendingRequests;
 
   nsTHashtable<nsCStringHashKey> mActivePermissionKeys;
 
   nsTArray<nsCString> mBlobURLs;
 #ifdef MOZ_CRASHREPORTER
   UniquePtr<mozilla::ipc::CrashReporterHost> mCrashReporter;
 #endif
+
+  static uint64_t sNextTabParentId;
+  static nsDataHashtable<nsUint64HashKey, TabParent*> sNextTabParents;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 class ParentIdleListener : public nsIObserver
 {
   friend class mozilla::dom::ContentParent;
--- a/dom/ipc/PProcessHangMonitor.ipdl
+++ b/dom/ipc/PProcessHangMonitor.ipdl
@@ -9,17 +9,16 @@ using base::ProcessId from "base/process
 using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
 
 namespace mozilla {
 
 struct SlowScriptData
 {
   TabId tabId;
   nsCString filename;
-  uint32_t lineno;
 };
 
 struct PluginHangData
 {
   uint32_t pluginId;
   ProcessId contentProcessId;
 };
 
--- a/dom/ipc/ProcessHangMonitor.cpp
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -80,21 +80,19 @@ class HangMonitorChild
  public:
   explicit HangMonitorChild(ProcessHangMonitor* aMonitor);
   ~HangMonitorChild() override;
 
   void Bind(Endpoint<PProcessHangMonitorChild>&& aEndpoint);
 
   typedef ProcessHangMonitor::SlowScriptAction SlowScriptAction;
   SlowScriptAction NotifySlowScript(nsITabChild* aTabChild,
-                                    const char* aFileName,
-                                    unsigned aLineNo);
+                                    const char* aFileName);
   void NotifySlowScriptAsync(TabId aTabId,
-                             const nsCString& aFileName,
-                             unsigned aLineNo);
+                             const nsCString& aFileName);
 
   bool IsDebuggerStartupComplete();
 
   void NotifyPluginHang(uint32_t aPluginId);
   void NotifyPluginHangAsync(uint32_t aPluginId);
 
   void ClearHang();
   void ClearHangAsync();
@@ -155,17 +153,16 @@ public:
 
   HangMonitoredProcess(HangMonitorParent* aActor,
                        ContentParent* aContentParent)
     : mActor(aActor), mContentParent(aContentParent) {}
 
   NS_IMETHOD GetHangType(uint32_t* aHangType) override;
   NS_IMETHOD GetScriptBrowser(nsIDOMElement** aBrowser) override;
   NS_IMETHOD GetScriptFileName(nsACString& aFileName) override;
-  NS_IMETHOD GetScriptLineNo(uint32_t* aLineNo) override;
 
   NS_IMETHOD GetPluginName(nsACString& aPluginName) override;
 
   NS_IMETHOD TerminateScript() override;
   NS_IMETHOD BeginStartingDebugger() override;
   NS_IMETHOD EndStartingDebugger() override;
   NS_IMETHOD TerminatePlugin() override;
   NS_IMETHOD UserCanceled() override;
@@ -453,28 +450,26 @@ HangMonitorChild::Bind(Endpoint<PProcess
   DebugOnly<bool> ok = aEndpoint.Bind(this);
   MOZ_ASSERT(ok);
 
   Unused << SendReady();
 }
 
 void
 HangMonitorChild::NotifySlowScriptAsync(TabId aTabId,
-                                        const nsCString& aFileName,
-                                        unsigned aLineNo)
+                                        const nsCString& aFileName)
 {
   if (mIPCOpen) {
-    Unused << SendHangEvidence(SlowScriptData(aTabId, aFileName, aLineNo));
+    Unused << SendHangEvidence(SlowScriptData(aTabId, aFileName));
   }
 }
 
 HangMonitorChild::SlowScriptAction
 HangMonitorChild::NotifySlowScript(nsITabChild* aTabChild,
-                                   const char* aFileName,
-                                   unsigned aLineNo)
+                                   const char* aFileName)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   mSentReport = true;
 
   {
     MonitorAutoLock lock(mMonitor);
 
@@ -492,19 +487,19 @@ HangMonitorChild::NotifySlowScript(nsITa
   TabId id;
   if (aTabChild) {
     RefPtr<TabChild> tabChild = static_cast<TabChild*>(aTabChild);
     id = tabChild->GetTabId();
   }
   nsAutoCString filename(aFileName);
 
   MonitorLoop()->PostTask(NewNonOwningRunnableMethod
-                          <TabId, nsCString, unsigned>(this,
-                                                       &HangMonitorChild::NotifySlowScriptAsync,
-                                                       id, filename, aLineNo));
+                          <TabId, nsCString>(this,
+                                             &HangMonitorChild::NotifySlowScriptAsync,
+                                             id, filename));
   return SlowScriptAction::Continue;
 }
 
 bool
 HangMonitorChild::IsDebuggerStartupComplete()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
@@ -958,28 +953,16 @@ HangMonitoredProcess::GetScriptFileName(
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   aFileName = mHangData.get_SlowScriptData().filename();
   return NS_OK;
 }
 
 NS_IMETHODIMP
-HangMonitoredProcess::GetScriptLineNo(uint32_t* aLineNo)
-{
-  MOZ_RELEASE_ASSERT(NS_IsMainThread());
-  if (mHangData.type() != HangData::TSlowScriptData) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  *aLineNo = mHangData.get_SlowScriptData().lineno();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 HangMonitoredProcess::GetPluginName(nsACString& aPluginName)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   if (mHangData.type() != HangData::TPluginHangData) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   uint32_t id = mHangData.get_PluginHangData().pluginId();
@@ -1177,21 +1160,20 @@ ProcessHangMonitor::Observe(nsISupports*
       obs->RemoveObserver(this, "xpcom-shutdown");
     }
   }
   return NS_OK;
 }
 
 ProcessHangMonitor::SlowScriptAction
 ProcessHangMonitor::NotifySlowScript(nsITabChild* aTabChild,
-                                     const char* aFileName,
-                                     unsigned aLineNo)
+                                     const char* aFileName)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
-  return HangMonitorChild::Get()->NotifySlowScript(aTabChild, aFileName, aLineNo);
+  return HangMonitorChild::Get()->NotifySlowScript(aTabChild, aFileName);
 }
 
 bool
 ProcessHangMonitor::IsDebuggerStartupComplete()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   return HangMonitorChild::Get()->IsDebuggerStartupComplete();
 }
--- a/dom/ipc/ProcessHangMonitor.h
+++ b/dom/ipc/ProcessHangMonitor.h
@@ -52,18 +52,17 @@ class ProcessHangMonitor final
   static void ClearForcePaint();
 
   enum SlowScriptAction {
     Continue,
     Terminate,
     StartDebugger
   };
   SlowScriptAction NotifySlowScript(nsITabChild* aTabChild,
-                                    const char* aFileName,
-                                    unsigned aLineNo);
+                                    const char* aFileName);
 
   void NotifyPluginHang(uint32_t aPluginId);
 
   bool IsDebuggerStartupComplete();
 
   void InitiateCPOWTimeout();
   bool ShouldTimeOutCPOWs();
 
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -560,26 +560,16 @@ TabParent::RecvEvent(const RemoteDOMEven
 
   event->SetOwner(target);
 
   bool dummy;
   target->DispatchEvent(event, &dummy);
   return IPC_OK();
 }
 
-TabParent* TabParent::sNextTabParent;
-
-/* static */ TabParent*
-TabParent::GetNextTabParent()
-{
-  TabParent* result = sNextTabParent;
-  sNextTabParent = nullptr;
-  return result;
-}
-
 bool
 TabParent::SendLoadRemoteScript(const nsString& aURL,
                                 const bool& aRunInGlobalScope)
 {
   if (mCreatingWindow) {
     mDelayedFrameScripts.AppendElement(FrameScriptInfo(aURL, aRunInGlobalScope));
     return true;
   }
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -548,18 +548,16 @@ public:
 
   virtual bool
   DeallocPPluginWidgetParent(PPluginWidgetParent* aActor) override;
 
   void SetInitedByParent() { mInitedByParent = true; }
 
   bool IsInitedByParent() const { return mInitedByParent; }
 
-  static TabParent* GetNextTabParent();
-
   bool SendLoadRemoteScript(const nsString& aURL,
                             const bool& aRunInGlobalScope);
 
   void LayerTreeUpdate(uint64_t aEpoch, bool aActive);
 
   virtual mozilla::ipc::IPCResult
   RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
                         const uint32_t& aAction,
@@ -693,23 +691,16 @@ private:
 
   // We keep a strong reference to the frameloader after we've sent the
   // Destroy message and before we've received __delete__. This allows us to
   // dispatch message manager messages during this time.
   RefPtr<nsFrameLoader> mFrameLoader;
 
   TabId mTabId;
 
-  // When loading a new tab or window via window.open, the child process sends
-  // a new PBrowser to use. We store that tab in sNextTabParent and then
-  // proceed through the browser's normal paths to create a new
-  // window/tab. When it comes time to create a new TabParent, we instead use
-  // sNextTabParent.
-  static TabParent* sNextTabParent;
-
   // When loading a new tab or window via window.open, the child is
   // responsible for loading the URL it wants into the new TabChild. When the
   // parent receives the CreateWindow message, though, it sends a LoadURL
   // message, usually for about:blank. It's important for the about:blank load
   // to get processed because the Firefox frontend expects every new window to
   // immediately start loading something (see bug 1123090). However, we want
   // the child to process the LoadURL message before it returns from
   // ProvideWindow so that the URL sent from the parent doesn't override the
@@ -773,41 +764,32 @@ private:
 
 public:
   static TabParent* GetTabParentFromLayersId(uint64_t aLayersId);
 };
 
 struct MOZ_STACK_CLASS TabParent::AutoUseNewTab final
 {
 public:
-  AutoUseNewTab(TabParent* aNewTab, bool* aWindowIsNew, nsCString* aURLToLoad)
-   : mNewTab(aNewTab), mWindowIsNew(aWindowIsNew), mURLToLoad(aURLToLoad)
+  AutoUseNewTab(TabParent* aNewTab, nsCString* aURLToLoad)
+   : mNewTab(aNewTab), mURLToLoad(aURLToLoad)
   {
-    MOZ_ASSERT(!TabParent::sNextTabParent);
     MOZ_ASSERT(!aNewTab->mCreatingWindow);
 
-    TabParent::sNextTabParent = aNewTab;
     aNewTab->mCreatingWindow = true;
     aNewTab->mDelayedURL.Truncate();
   }
 
   ~AutoUseNewTab()
   {
     mNewTab->mCreatingWindow = false;
     *mURLToLoad = mNewTab->mDelayedURL;
-
-    if (TabParent::sNextTabParent) {
-      MOZ_ASSERT(TabParent::sNextTabParent == mNewTab);
-      TabParent::sNextTabParent = nullptr;
-      *mWindowIsNew = false;
-    }
   }
 
 private:
   TabParent* mNewTab;
-  bool* mWindowIsNew;
   nsCString* mURLToLoad;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_tabs_TabParent_h
--- a/dom/ipc/nsIHangReport.idl
+++ b/dom/ipc/nsIHangReport.idl
@@ -27,17 +27,16 @@ interface nsIHangReport : nsISupports
   // The type of hang being reported: SLOW_SCRIPT or PLUGIN_HANG.
   readonly attribute unsigned long hangType;
 
   // For SLOW_SCRIPT reports, these fields contain information about the
   // slow script.
   // Only valid for SLOW_SCRIPT reports.
   readonly attribute nsIDOMElement scriptBrowser;
   readonly attribute ACString scriptFileName;
-  readonly attribute unsigned long scriptLineNo;
 
   // For PLUGIN_HANGs, this field contains information about the plugin.
   // Only valid for PLUGIN_HANG reports.
   readonly attribute ACString pluginName;
 
   // Called by front end code when user ignores or cancels
   // the notification.
   void userCanceled();
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -48,59 +48,66 @@ support-files =
 
 [test_AVC3_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_AudioChange_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_AutoRevocation.html]
 tags = firstpartyisolation
 [test_BufferedSeek.html]
+skip-if = android_version == '22' # bug 1329532 bug 1066090
 [test_BufferedSeek_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_BufferingWait.html]
 skip-if = toolkit == 'android' #timeout android bug 1199531
 [test_BufferingWait_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_ChangeWhileWaitingOnMissingData_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_DrainOnMissingData_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_DurationChange.html]
 [test_DurationUpdated.html]
 [test_DurationUpdated_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_EndedEvent.html]
+skip-if = android_version == '22' # bug 1358640
 [test_EndOfStream.html]
 [test_EndOfStream_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_Eviction_mp4.html]
 skip-if = (os == "win" && os_version == "5.1") || (android_version == '15') # Not supported on xp. Android(Bug 1358271)
 [test_FrameSelection.html]
+skip-if = android_version == '22' # bug 1341519
 [test_FrameSelection_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_HaveMetadataUnbufferedSeek.html]
+skip-if = android_version == '22' # bug 1342247
 [test_HaveMetadataUnbufferedSeek_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_LiveSeekable.html]
 [test_LoadedDataFired_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_LoadedMetadataFired.html]
 [test_LoadedMetadataFired_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_MediaSource.html]
+skip-if = android_version == '22' # bug 1341146
 [test_MediaSource_memory_reporting.html]
+skip-if = android_version == '22' # bug 1225758
 [test_MediaSource_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_MediaSource_flac_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android
 [test_MediaSource_disabled.html]
 [test_MultipleInitSegments.html]
 [test_MultipleInitSegments_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_OnEvents.html]
+skip-if = android_version == '22' # bug 1359010
 [test_PlayEvents.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_ResumeAfterClearing_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_SeekableAfterEndOfStream.html]
 [test_SeekableAfterEndOfStream_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_SeekableAfterEndOfStreamSplit.html]
@@ -121,26 +128,29 @@ skip-if = ((os == "win" && os_version ==
 [test_SeekToLastFrame_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_SeekTwice_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_Sequence_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_SetModeThrows.html]
 [test_SplitAppendDelay.html]
+skip-if = android_version == '22' # bug 1293896 bug 1342683
 [test_SplitAppendDelay_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_SplitAppend.html]
+skip-if = android_version == '22' # bug 1211999
 [test_SplitAppend_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_Threshold_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_TimestampOffset_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_TruncatedDuration.html]
+skip-if = android_version == '22' # bug 1359012
 [test_TruncatedDuration_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_WaitingOnMissingData.html]
 skip-if = (toolkit == 'android') #timeout android only bug 1101187
 [test_WaitingOnMissingData_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_WaitingOnMissingDataEnded_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
--- a/dom/media/test/crashtests/crashtests.list
+++ b/dom/media/test/crashtests/crashtests.list
@@ -6,17 +6,17 @@ load 468763-1.html
 load 474744-1.html
 HTTP load 481136-1.html # needs to be HTTP to recognize the ogg as an audio file?
 load 492286-1.xhtml
 load 493915-1.html
 load 495794-1.html
 load 576612-1.html
 load 752784-1.html
 load 789075-1.html
-HTTP load 795892-1.html
+skip-if(Android&&AndroidVersion=='22') HTTP load 795892-1.html # bug 1358718
 load 844563.html
 load 846612.html
 load 852838.html
 load 865537-1.html
 load 868504.html
 load 874869.html
 load 874915.html
 load 874934.html
--- a/gfx/ipc/GPUProcessHost.cpp
+++ b/gfx/ipc/GPUProcessHost.cpp
@@ -4,16 +4,17 @@
  * 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 "GPUProcessHost.h"
 #include "chrome/common/process_watcher.h"
 #include "gfxPrefs.h"
 #include "mozilla/gfx/Logging.h"
 #include "nsITimer.h"
+#include "mozilla/Preferences.h"
 
 namespace mozilla {
 namespace gfx {
 
 using namespace ipc;
 
 GPUProcessHost::GPUProcessHost(Listener* aListener)
  : GeckoChildProcessHost(GeckoProcessType_GPU),
@@ -33,16 +34,20 @@ GPUProcessHost::~GPUProcessHost()
 }
 
 bool
 GPUProcessHost::Launch()
 {
   MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched);
   MOZ_ASSERT(!mGPUChild);
 
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+  mSandboxLevel = Preferences::GetInt("security.sandbox.gpu.level");
+#endif
+
   mLaunchPhase = LaunchPhase::Waiting;
   mLaunchTime = TimeStamp::Now();
 
   if (!GeckoChildProcessHost::AsyncLaunch()) {
     mLaunchPhase = LaunchPhase::Complete;
     return false;
   }
   return true;
--- a/gfx/ipc/GPUProcessImpl.cpp
+++ b/gfx/ipc/GPUProcessImpl.cpp
@@ -2,16 +2,21 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=99: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "GPUProcessImpl.h"
 #include "mozilla/ipc/IOThreadChild.h"
 #include "nsXPCOM.h"
 
+#if defined(OS_WIN) && defined(MOZ_SANDBOX)
+#define TARGET_SANDBOX_EXPORTS
+#include "mozilla/sandboxTarget.h"
+#endif
+
 namespace mozilla {
 namespace gfx {
 
 using namespace ipc;
 
 GPUProcessImpl::GPUProcessImpl(ProcessId aParentPid)
  : ProcessChild(aParentPid)
 {
@@ -19,16 +24,20 @@ GPUProcessImpl::GPUProcessImpl(ProcessId
 
 GPUProcessImpl::~GPUProcessImpl()
 {
 }
 
 bool
 GPUProcessImpl::Init(int aArgc, char* aArgv[])
 {
+#if defined(MOZ_SANDBOX) && defined(OS_WIN)
+  mozilla::SandboxTarget::Instance()->StartSandbox();
+#endif
+
   return mGPU.Init(ParentPid(),
                    IOThreadChild::message_loop(),
                    IOThreadChild::channel());
 }
 
 void
 GPUProcessImpl::CleanUp()
 {
--- a/gfx/layers/apz/src/AndroidDynamicToolbarAnimator.cpp
+++ b/gfx/layers/apz/src/AndroidDynamicToolbarAnimator.cpp
@@ -282,17 +282,17 @@ AndroidDynamicToolbarAnimator::ToolbarAn
     break;
   }
 }
 
 bool
 AndroidDynamicToolbarAnimator::UpdateAnimation(const TimeStamp& aCurrentFrame)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
-  if (mToolbarState != eToolbarAnimating) {
+  if ((mToolbarState != eToolbarAnimating) || mCompositorShutdown) {
     return false;
   }
 
   bool continueAnimating = true;
 
   if (mCompositorAnimationStyle == eImmediate) {
     if (mCompositorAnimationDirection == MOVE_TOOLBAR_DOWN) {
       mCompositorToolbarHeight = mCompositorMaxToolbarHeight;
@@ -576,17 +576,20 @@ AndroidDynamicToolbarAnimator::HandleTou
   } else {
     ShowToolbarIfNotVisible(aCurrentToolbarState);
   }
 }
 
 void
 AndroidDynamicToolbarAnimator::PostMessage(int32_t aMessage) {
   RefPtr<UiCompositorControllerParent> uiController = UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayerTreeId);
-  MOZ_ASSERT(uiController);
+  if (!uiController) {
+    // Looks like IPC may be shutdown.
+    return;
+  }
   // ToolbarAnimatorMessageFromCompositor may be called from any thread.
   uiController->ToolbarAnimatorMessageFromCompositor(aMessage);
 }
 
 void
 AndroidDynamicToolbarAnimator::UpdateCompositorToolbarHeight(ScreenIntCoord aHeight)
 {
   if (!CompositorThreadHolder::IsInCompositorThread()) {
@@ -634,16 +637,19 @@ AndroidDynamicToolbarAnimator::UpdateCon
   mControllerCompositionHeight = aHeight;
 }
 
 // Ensures the margin for the fixed layers match the position of the toolbar
 void
 AndroidDynamicToolbarAnimator::UpdateFixedLayerMargins()
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  if (mCompositorShutdown) {
+    return;
+  }
   CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(mRootLayerTreeId);
   if (parent) {
     ScreenIntCoord surfaceHeight = parent->GetEGLSurfaceSize().height;
     if (surfaceHeight != mCompositorSurfaceHeight) {
       mCompositorSurfaceHeight = surfaceHeight;
       UpdateControllerSurfaceHeight(mCompositorSurfaceHeight);
     }
     AsyncCompositionManager* manager = parent->GetCompositionManager(nullptr);
@@ -763,16 +769,20 @@ AndroidDynamicToolbarAnimator::NotifyCon
 void
 AndroidDynamicToolbarAnimator::RequestComposite()
 {
   if (!CompositorThreadHolder::IsInCompositorThread()) {
     CompositorThreadHolder::Loop()->PostTask(NewRunnableMethod(this, &AndroidDynamicToolbarAnimator::RequestComposite));
     return;
   }
 
+  if (mCompositorShutdown) {
+    return;
+  }
+
   CompositorBridgeParent* parent = CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(mRootLayerTreeId);
   if (parent) {
     AsyncCompositionManager* manager = parent->GetCompositionManager(nullptr);
     if (manager) {
       manager->SetFixedLayerMarginsBottom(GetFixedLayerMarginsBottom());
       parent->Invalidate();
       parent->ScheduleComposition();
     }
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -1072,16 +1072,23 @@ GeckoChildProcessHost::PerformAsyncLaunc
         bool ok = mSandboxBroker.SetSecurityLevelForGMPlugin(level);
         if (!ok) {
           return false;
         }
         shouldSandboxCurrentProcess = true;
       }
       break;
     case GeckoProcessType_GPU:
+      if (mSandboxLevel > 0 && !PR_GetEnv("MOZ_DISABLE_GPU_SANDBOX")) {
+        // For now we treat every failure as fatal in SetSecurityLevelForGPUProcess
+        // and just crash there right away. Should this change in the future then we
+        // should also handle the error here.
+        mSandboxBroker.SetSecurityLevelForGPUProcess(mSandboxLevel);
+        shouldSandboxCurrentProcess = true;
+      }
       break;
     case GeckoProcessType_Default:
     default:
       MOZ_CRASH("Bad process type in GeckoChildProcessHost");
       break;
   };
 
   if (shouldSandboxCurrentProcess) {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/string-index.js
@@ -0,0 +1,86 @@
+function basic() {
+    var zero = "0";
+    var one  = "1";
+    var thousand = String(1000);
+    var max = String(0xffff);
+
+    assertEq(zero, "0");
+    assertEq(Number(zero), 0);
+    assertEq(String(Number(zero)), "0");
+
+    assertEq(one, "1");
+    assertEq(Number(one), 1);
+    assertEq(String(Number(one)), "1");
+
+    assertEq(thousand, "1000");
+    assertEq(Number(thousand), 1000);
+    assertEq(String(Number(thousand)), "1000");
+
+    assertEq(max, "65535");
+    assertEq(Number(max), 0xffff);
+    assertEq(String(Number(max)), "65535");
+}
+
+function index() {
+    var zero = "0";
+    var trippleZero = "000";
+
+    var seven = "7";
+    var doubleOhSeven = "007";
+
+    var object = {0: "a", "000": "b"};
+    var object2 = {7: "a", "007": "b"};
+
+    var array = ["a"];
+    array[trippleZero] = "b";
+    var array2 = [0, 1, 2, 3, 4, 5, 6, "a"];
+    array2[doubleOhSeven] = "b";
+
+    for (var i = 0; i < 30; i++) {
+        assertEq(object[zero], "a");
+        assertEq(object[0], "a");
+        assertEq(object[trippleZero], "b");
+
+        assertEq(object2[seven], "a");
+        assertEq(object2[7], "a");
+        assertEq(object2[doubleOhSeven], "b");
+
+        assertEq(array[zero], "a");
+        assertEq(array[0], "a");
+        assertEq(array[trippleZero], "b");
+
+        assertEq(array2[seven], "a");
+        assertEq(array2[7], "a");
+        assertEq(array2[doubleOhSeven], "b");
+    }
+}
+
+function forin() {
+    var array = [0, 1, 2, 3, 4, 5, 6];
+
+    var i = 0;
+    for (var name in array) {
+        assertEq(name, String(i));
+        assertEq(Number(name), i);
+
+        assertEq(array[name], i);
+        assertEq(array.hasOwnProperty(name), true);
+
+        i++;
+    }
+}
+
+function parse() {
+    var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1000, 0xffff];
+
+    for (var number of numbers) {
+        assertEq(parseInt(String(number)), number);
+        assertEq(parseInt(String(number), 10), number);
+        assertEq(parseInt(String(number), 0), number);
+    }
+}
+
+basic();
+index();
+forin();
+parse();
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -1517,30 +1517,39 @@ CacheIRCompiler::emitGuardAndGetIndexFro
 {
     Register str = allocator.useRegister(masm, reader.stringOperandId());
     Register output = allocator.defineRegister(masm, reader.int32OperandId());
 
     FailurePath* failure;
     if (!addFailurePath(&failure))
         return false;
 
-    LiveRegisterSet save(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs());
-    masm.PushRegsInMask(save);
+    Label vmCall, done;
+    masm.loadStringIndexValue(str, output, &vmCall);
+    masm.jump(&done);
+
+    {
+        masm.bind(&vmCall);
+        LiveRegisterSet save(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs());
+        masm.PushRegsInMask(save);
 
-    masm.setupUnalignedABICall(output);
-    masm.passABIArg(str);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, GetIndexFromString));
-    masm.mov(ReturnReg, output);
+        masm.setupUnalignedABICall(output);
+        masm.passABIArg(str);
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, GetIndexFromString));
+        masm.mov(ReturnReg, output);
 
-    LiveRegisterSet ignore;
-    ignore.add(output);
-    masm.PopRegsInMaskIgnore(save, ignore);
+        LiveRegisterSet ignore;
+        ignore.add(output);
+        masm.PopRegsInMaskIgnore(save, ignore);
 
-    // GetIndexFromString returns a negative value on failure.
-    masm.branchTest32(Assembler::Signed, output, output, failure->label());
+        // GetIndexFromString returns a negative value on failure.
+        masm.branchTest32(Assembler::Signed, output, output, failure->label());
+    }
+
+    masm.bind(&done);
     return true;
 }
 
 bool
 CacheIRCompiler::emitLoadProto()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     Register reg = allocator.defineRegister(masm, reader.objOperandId());
--- a/js/src/jit/InlinableNatives.h
+++ b/js/src/jit/InlinableNatives.h
@@ -153,16 +153,17 @@ struct JSJitInfo;
 
 namespace js {
 namespace jit {
 
 enum class InlinableNative : uint16_t {
 #define ADD_NATIVE(native) native,
     INLINABLE_NATIVE_LIST(ADD_NATIVE)
 #undef ADD_NATIVE
+    Limit
 };
 
 #define ADD_NATIVE(native) extern const JSJitInfo JitInfo_##native;
     INLINABLE_NATIVE_LIST(ADD_NATIVE)
 #undef ADD_NATIVE
 
 } // namespace jit
 } // namespace js
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -351,16 +351,18 @@ IonBuilder::inlineNativeCall(CallInfo& c
         return inlineObjectIsTypeDescr(callInfo);
       case InlinableNative::IntrinsicTypeDescrIsSimpleType:
         return inlineHasClass(callInfo,
                               &ScalarTypeDescr::class_, &ReferenceTypeDescr::class_);
       case InlinableNative::IntrinsicTypeDescrIsArrayType:
         return inlineHasClass(callInfo, &ArrayTypeDescr::class_);
       case InlinableNative::IntrinsicSetTypedObjectOffset:
         return inlineSetTypedObjectOffset(callInfo);
+      case InlinableNative::Limit:
+        break;
     }
 
     MOZ_CRASH("Shouldn't get here");
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineNativeGetter(CallInfo& callInfo, JSFunction* target)
 {
--- a/js/src/jit/MacroAssembler-inl.h
+++ b/js/src/jit/MacroAssembler-inl.h
@@ -22,16 +22,18 @@
 #elif defined(JS_CODEGEN_MIPS32)
 # include "jit/mips32/MacroAssembler-mips32-inl.h"
 #elif defined(JS_CODEGEN_MIPS64)
 # include "jit/mips64/MacroAssembler-mips64-inl.h"
 #elif !defined(JS_CODEGEN_NONE)
 # error "Unknown architecture!"
 #endif
 
+#include "wasm/WasmBuiltins.h"
+
 namespace js {
 namespace jit {
 
 //{{{ check_macroassembler_style
 // ===============================================================
 // Frame manipulation functions.
 
 uint32_t
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1418,16 +1418,30 @@ MacroAssembler::loadStringChar(Register 
     bind(&isLatin1);
     loadStringChars(output, output);
     load8ZeroExtend(BaseIndex(output, index, TimesOne), output);
 
     bind(&done);
 }
 
 void
+MacroAssembler::loadStringIndexValue(Register str, Register dest, Label* fail)
+{
+    MOZ_ASSERT(str != dest);
+
+    load32(Address(str, JSString::offsetOfFlags()), dest);
+
+    // Does not have a cached index value.
+    branchTest32(Assembler::Zero, dest, Imm32(JSString::INDEX_VALUE_BIT), fail);
+
+    // Extract the index.
+    rshift32(Imm32(JSString::INDEX_VALUE_SHIFT), dest);
+}
+
+void
 MacroAssembler::loadJSContext(Register dest)
 {
     CompileCompartment* compartment = GetJitContext()->compartment;
     if (compartment->zone()->isAtomsZone()) {
         // If we are in the atoms zone then we are generating a runtime wide
         // trampoline which can run in any zone. Load the context which is
         // currently running using cooperative scheduling in the runtime.
         // (This will need to be fixed when we have preemptive scheduling,
@@ -2755,26 +2769,32 @@ MacroAssembler::callWithABINoProfiler(vo
 }
 
 void
 MacroAssembler::callWithABI(wasm::BytecodeOffset callOffset, wasm::SymbolicAddress imm,
                             MoveOp::Type result)
 {
     MOZ_ASSERT(wasm::NeedsBuiltinThunk(imm));
 
+    // We clobber WasmTlsReg below in the loadWasmTlsRegFromFrame(), but Ion
+    // assumes it is non-volatile, so preserve it manually.
+    Push(WasmTlsReg);
+
     uint32_t stackAdjust;
     callWithABIPre(&stackAdjust, /* callFromWasm = */ true);
 
     // The TLS register is used in builtin thunks and must be set, by ABI:
     // reload it after passing arguments, which might have used it at spill
     // points when placing arguments.
     loadWasmTlsRegFromFrame();
 
     call(wasm::CallSiteDesc(callOffset.bytecodeOffset, wasm::CallSite::Symbolic), imm);
     callWithABIPost(stackAdjust, result, /* callFromWasm = */ true);
+
+    Pop(WasmTlsReg);
 }
 
 // ===============================================================
 // Exit frame footer.
 
 void
 MacroAssembler::linkExitFrame(Register cxreg)
 {
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1523,16 +1523,18 @@ class MacroAssembler : public MacroAssem
 
     void loadStringLength(Register str, Register dest) {
         load32(Address(str, JSString::offsetOfLength()), dest);
     }
 
     void loadStringChars(Register str, Register dest);
     void loadStringChar(Register str, Register index, Register output, Label* fail);
 
+    void loadStringIndexValue(Register str, Register dest, Label* fail);
+
     void loadJSContext(Register dest);
     void loadJitActivation(Register dest) {
         loadJSContext(dest);
         loadPtr(Address(dest, offsetof(JSContext, activation_)), dest);
     }
 
     void guardGroupHasUnanalyzedNewScript(Register group, Register scratch, Label* fail);
 
--- a/js/src/jit/arm64/SharedIC-arm64.cpp
+++ b/js/src/jit/arm64/SharedIC-arm64.cpp
@@ -8,16 +8,17 @@
 #include "jit/SharedICHelpers.h"
 
 #ifdef JS_SIMULATOR_ARM64
 #include "jit/arm64/Assembler-arm64.h"
 #include "jit/arm64/BaselineCompiler-arm64.h"
 #include "jit/arm64/vixl/Debugger-vixl.h"
 #endif
 
+#include "jit/arm64/MacroAssembler-arm64-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 namespace js {
 namespace jit {
 
 // ICBinaryArith_Int32
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -31,16 +31,18 @@
 #include "gc/AtomMarking-inl.h"
 #include "vm/String-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::ArrayEnd;
 using mozilla::ArrayLength;
+using mozilla::Maybe;
+using mozilla::Nothing;
 using mozilla::RangedPtr;
 
 const char*
 js::AtomToPrintableString(JSContext* cx, JSAtom* atom, JSAutoByteString* bytes)
 {
     JSString* str = QuoteString(cx, atom, 0);
     if (!str)
         return nullptr;
@@ -297,17 +299,18 @@ AtomIsPinnedInRuntime(JSRuntime* rt, JSA
 }
 
 #endif // DEBUG
 
 /* |tbchars| must not point into an inline or short string. */
 template <typename CharT>
 MOZ_ALWAYS_INLINE
 static JSAtom*
-AtomizeAndCopyChars(JSContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin)
+AtomizeAndCopyChars(JSContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin,
+                    const Maybe<uint32_t>& indexValue)
 {
     if (JSAtom* s = cx->staticStrings().lookup(tbchars, length))
         return s;
 
     AtomHasher::Lookup lookup(tbchars, length);
 
     // Try the per-Zone cache first. If we find the atom there we can avoid the
     // atoms lock, the markAtom call, and the multiple HashSet lookups below.
@@ -369,36 +372,41 @@ AtomizeAndCopyChars(JSContext* cx, const
             // please also fix or comment the similar case in Symbol::new_.
             ReportOutOfMemory(cx);
             return nullptr;
         }
 
         atom = flat->morphAtomizedStringIntoAtom(lookup.hash);
         MOZ_ASSERT(atom->hash() == lookup.hash);
 
+        if (indexValue)
+            atom->maybeInitializeIndex(*indexValue, true);
+
         // We have held the lock since looking up p, and the operations we've done
         // since then can't GC; therefore the atoms table has not been modified and
         // p is still valid.
         if (!atoms.add(p, AtomStateEntry(atom, bool(pin)))) {
             ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
             return nullptr;
         }
     }
 
     cx->atomMarking().inlinedMarkAtom(cx, atom);
     if (zonePtr)
         mozilla::Unused << zone->atomCache().add(*zonePtr, AtomStateEntry(atom, false));
     return atom;
 }
 
 template JSAtom*
-AtomizeAndCopyChars(JSContext* cx, const char16_t* tbchars, size_t length, PinningBehavior pin);
+AtomizeAndCopyChars(JSContext* cx, const char16_t* tbchars, size_t length, PinningBehavior pin,
+                    const Maybe<uint32_t>& indexValue);
 
 template JSAtom*
-AtomizeAndCopyChars(JSContext* cx, const Latin1Char* tbchars, size_t length, PinningBehavior pin);
+AtomizeAndCopyChars(JSContext* cx, const Latin1Char* tbchars, size_t length, PinningBehavior pin,
+                    const Maybe<uint32_t>& indexValue);
 
 JSAtom*
 js::AtomizeString(JSContext* cx, JSString* str,
                   js::PinningBehavior pin /* = js::DoNotPinAtom */)
 {
     if (str->isAtom()) {
         JSAtom& atom = str->asAtom();
         /* N.B. static atoms are effectively always interned. */
@@ -422,37 +430,42 @@ js::AtomizeString(JSContext* cx, JSStrin
         p->setPinned(bool(pin));
         return &atom;
     }
 
     JSLinearString* linear = str->ensureLinear(cx);
     if (!linear)
         return nullptr;
 
+    Maybe<uint32_t> indexValue;
+    if (str->hasIndexValue())
+        indexValue.emplace(str->getIndexValue());
+
     JS::AutoCheckCannotGC nogc;
     return linear->hasLatin1Chars()
-           ? AtomizeAndCopyChars(cx, linear->latin1Chars(nogc), linear->length(), pin)
-           : AtomizeAndCopyChars(cx, linear->twoByteChars(nogc), linear->length(), pin);
+           ? AtomizeAndCopyChars(cx, linear->latin1Chars(nogc), linear->length(), pin, indexValue)
+           : AtomizeAndCopyChars(cx, linear->twoByteChars(nogc), linear->length(), pin, indexValue);
 }
 
 JSAtom*
-js::Atomize(JSContext* cx, const char* bytes, size_t length, PinningBehavior pin)
+js::Atomize(JSContext* cx, const char* bytes, size_t length, PinningBehavior pin,
+            const Maybe<uint32_t>& indexValue)
 {
     CHECK_REQUEST(cx);
 
     const Latin1Char* chars = reinterpret_cast<const Latin1Char*>(bytes);
-    return AtomizeAndCopyChars(cx, chars, length, pin);
+    return AtomizeAndCopyChars(cx, chars, length, pin, indexValue);
 }
 
 template <typename CharT>
 JSAtom*
 js::AtomizeChars(JSContext* cx, const CharT* chars, size_t length, PinningBehavior pin)
 {
     CHECK_REQUEST(cx);
-    return AtomizeAndCopyChars(cx, chars, length, pin);
+    return AtomizeAndCopyChars(cx, chars, length, pin, Nothing());
 }
 
 template JSAtom*
 js::AtomizeChars(JSContext* cx, const Latin1Char* chars, size_t length, PinningBehavior pin);
 
 template JSAtom*
 js::AtomizeChars(JSContext* cx, const char16_t* chars, size_t length, PinningBehavior pin);
 
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.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 jsatom_h
 #define jsatom_h
 
 #include "mozilla/HashFunctions.h"
+#include "mozilla/Maybe.h"
 
 #include "jsalloc.h"
 
 #include "gc/Barrier.h"
 #include "gc/Marking.h"
 #include "gc/Rooting.h"
 #include "js/GCAPI.h"
 #include "js/GCHashTable.h"
@@ -174,17 +175,18 @@ TraceWellKnownSymbols(JSTracer* trc);
 enum PinningBehavior
 {
     DoNotPinAtom = false,
     PinAtom = true
 };
 
 extern JSAtom*
 Atomize(JSContext* cx, const char* bytes, size_t length,
-        js::PinningBehavior pin = js::DoNotPinAtom);
+        js::PinningBehavior pin = js::DoNotPinAtom,
+        const mozilla::Maybe<uint32_t>& indexValue = mozilla::Nothing());
 
 template <typename CharT>
 extern JSAtom*
 AtomizeChars(JSContext* cx, const CharT* chars, size_t length,
              js::PinningBehavior pin = js::DoNotPinAtom);
 
 extern JSAtom*
 AtomizeUTF8Chars(JSContext* cx, const char* utf8Chars, size_t utf8ByteLength);
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -204,36 +204,51 @@ ErrorObject::classes[JSEXN_ERROR_LIMIT] 
     IMPLEMENT_ERROR_CLASS(CompileError),
     IMPLEMENT_ERROR_CLASS(LinkError),
     IMPLEMENT_ERROR_CLASS(RuntimeError)
 };
 
 size_t
 ExtraMallocSize(JSErrorReport* report)
 {
-    if (report->linebuf())
-        return (report->linebufLength() + 1) * sizeof(char16_t);
+    if (report->linebuf()) {
+        /*
+         * Count with null terminator and alignment.
+         * See CopyExtraData for the details about alignment.
+         */
+        return (report->linebufLength() + 1) * sizeof(char16_t) + 1;
+    }
 
     return 0;
 }
 
 size_t
 ExtraMallocSize(JSErrorNotes::Note* note)
 {
     return 0;
 }
 
 bool
 CopyExtraData(JSContext* cx, uint8_t** cursor, JSErrorReport* copy, JSErrorReport* report)
 {
     if (report->linebuf()) {
+        /*
+         * Make sure cursor is properly aligned for char16_t for platforms
+         * which need it and it's at the end of the buffer on exit.
+         */
+        size_t alignment_backlog = 0;
+        if (size_t(*cursor) % 2)
+            (*cursor)++;
+        else
+            alignment_backlog = 1;
+
         size_t linebufSize = (report->linebufLength() + 1) * sizeof(char16_t);
         const char16_t* linebufCopy = (const char16_t*)(*cursor);
         js_memcpy(*cursor, report->linebuf(), linebufSize);
-        *cursor += linebufSize;
+        *cursor += linebufSize + alignment_backlog;
         copy->initBorrowedLinebuf(linebufCopy, report->linebufLength(), report->tokenOffset());
     }
 
     /* Copy non-pointer members. */
     copy->isMuted = report->isMuted;
     copy->exnType = report->exnType;
 
     /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -420,16 +420,24 @@ js::num_parseInt(JSContext* cx, unsigned
                 args.rval().setNumber(-floor(-d));
                 return true;
             }
             if (d == 0.0) {
                 args.rval().setInt32(0);
                 return true;
             }
         }
+
+        if (args[0].isString()) {
+            JSString* str = args[0].toString();
+            if (str->hasIndexValue()) {
+                args.rval().setNumber(str->getIndexValue());
+                return true;
+            }
+        }
     }
 
     /* Step 1. */
     RootedString inputString(cx, ToString<CanGC>(cx, args[0]));
     if (!inputString)
         return false;
     args[0].setString(inputString);
 
@@ -626,16 +634,18 @@ js::Int32ToString(JSContext* cx, int32_t
     Latin1Char buffer[JSFatInlineString::MAX_LENGTH_LATIN1 + 1];
     size_t length;
     Latin1Char* start = BackfillInt32InBuffer(si, buffer, ArrayLength(buffer), &length);
 
     mozilla::Range<const Latin1Char> chars(start, length);
     JSInlineString* str = NewInlineString<allowGC>(cx, chars);
     if (!str)
         return nullptr;
+    if (si >= 0)
+        str->maybeInitializeIndex(si);
 
     CacheNumber(cx, si, str);
     return str;
 }
 
 template JSFlatString*
 js::Int32ToString<CanGC>(JSContext* cx, int32_t si);
 
@@ -647,17 +657,21 @@ js::Int32ToAtom(JSContext* cx, int32_t s
 {
     if (JSFlatString* str = LookupInt32ToString(cx, si))
         return js::AtomizeString(cx, str);
 
     char buffer[JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1];
     size_t length;
     char* start = BackfillInt32InBuffer(si, buffer, JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1, &length);
 
-    JSAtom* atom = Atomize(cx, start, length);
+    Maybe<uint32_t> indexValue;
+    if (si >= 0)
+        indexValue.emplace(si);
+
+    JSAtom* atom = Atomize(cx, start, length, js::DoNotPinAtom, indexValue);
     if (!atom)
         return nullptr;
 
     CacheNumber(cx, si, atom);
     return atom;
 }
 
 /* Returns a non-nullptr pointer to inside cbuf.  */
@@ -1345,18 +1359,20 @@ NumberToStringWithBase(JSContext* cx, do
      * from the interpreter (which will report the error).
      */
     if (base < 2 || base > 36)
         return nullptr;
 
     JSCompartment* comp = cx->compartment();
 
     int32_t i;
+    bool isBase10Int = false;
     if (mozilla::NumberIsInt32(d, &i)) {
-        if (base == 10 && StaticStrings::hasInt(i))
+        isBase10Int = (base == 10);
+        if (isBase10Int && StaticStrings::hasInt(i))
             return cx->staticStrings().getInt(i);
         if (unsigned(i) < unsigned(base)) {
             if (i < 10)
                 return cx->staticStrings().getInt(i);
             char16_t c = 'a' + i - 10;
             MOZ_ASSERT(StaticStrings::hasUnit(c));
             return cx->staticStrings().getUnit(c);
         }
@@ -1378,16 +1394,21 @@ NumberToStringWithBase(JSContext* cx, do
         }
         MOZ_ASSERT_IF(base == 10,
                       !cbuf.dbuf && numStr >= cbuf.sbuf && numStr < cbuf.sbuf + cbuf.sbufSize);
         MOZ_ASSERT_IF(base != 10,
                       cbuf.dbuf && cbuf.dbuf == numStr);
     }
 
     JSFlatString* s = NewStringCopyZ<allowGC>(cx, numStr);
+    if (!s)
+        return nullptr;
+
+    if (isBase10Int && i >= 0)
+        s->maybeInitializeIndex(i);
 
     comp->dtoaCache.cache(base, d, s);
     return s;
 }
 
 template <AllowGC allowGC>
 JSString*
 js::NumberToString(JSContext* cx, double d)
@@ -1561,16 +1582,21 @@ CharsToNumber(JSContext* cx, const CharT
 bool
 js::StringToNumber(JSContext* cx, JSString* str, double* result)
 {
     AutoCheckCannotGC nogc;
     JSLinearString* linearStr = str->ensureLinear(cx);
     if (!linearStr)
         return false;
 
+    if (str->hasIndexValue()) {
+        *result = str->getIndexValue();
+        return true;
+    }
+
     return linearStr->hasLatin1Chars()
            ? CharsToNumber(cx, linearStr->latin1Chars(nogc), str->length(), result)
            : CharsToNumber(cx, linearStr->twoByteChars(nogc), str->length(), result);
 }
 
 JS_PUBLIC_API(bool)
 js::ToNumberSlow(JSContext* cx, HandleValue v_, double* out)
 {
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -9,16 +9,18 @@
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Range.h"
 
 #include "NamespaceImports.h"
 
 #include "js/Conversions.h"
 
+#include "vm/String.h"
+
 
 // This macro is should be `one' if current compiler supports builtin functions
 // like __builtin_sadd_overflow.
 #if __GNUC__ >= 5
     // GCC 5 and above supports these functions.
     #define BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(x) 1
 #else
     // For CLANG, we use its own function to check for this.
@@ -239,29 +241,37 @@ IsDefinitelyIndex(const Value& v, uint32
     }
 
     int32_t i;
     if (v.isDouble() && mozilla::NumberIsInt32(v.toDouble(), &i) && i >= 0) {
         *indexp = uint32_t(i);
         return true;
     }
 
+    if (v.isString() && v.toString()->hasIndexValue()) {
+        *indexp = v.toString()->getIndexValue();
+        return true;
+    }
+
     return false;
 }
 
 /* ES5 9.4 ToInteger. */
 static MOZ_MUST_USE inline bool
 ToInteger(JSContext* cx, HandleValue v, double* dp)
 {
     if (v.isInt32()) {
         *dp = v.toInt32();
         return true;
     }
     if (v.isDouble()) {
         *dp = v.toDouble();
+    } else if (v.isString() && v.toString()->hasIndexValue()) {
+        *dp = v.toString()->getIndexValue();
+        return true;
     } else {
         extern JS_PUBLIC_API(bool) ToNumberSlow(JSContext* cx, HandleValue v, double* dp);
         if (!ToNumberSlow(cx, v, dp))
             return false;
     }
     *dp = JS::ToInteger(*dp);
     return true;
 }
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -363,26 +363,26 @@ UNIFIED_SOURCES += [
     'vm/WeakMapPtr.cpp',
     'vm/Xdr.cpp',
     'wasm/AsmJS.cpp',
     'wasm/WasmBaselineCompile.cpp',
     'wasm/WasmBinaryIterator.cpp',
     'wasm/WasmBinaryToAST.cpp',
     'wasm/WasmBinaryToExperimentalText.cpp',
     'wasm/WasmBinaryToText.cpp',
+    'wasm/WasmBuiltins.cpp',
     'wasm/WasmCode.cpp',
     'wasm/WasmCompartment.cpp',
     'wasm/WasmCompile.cpp',
     'wasm/WasmFrameIterator.cpp',
     'wasm/WasmGenerator.cpp',
     'wasm/WasmInstance.cpp',
     'wasm/WasmIonCompile.cpp',
     'wasm/WasmJS.cpp',
     'wasm/WasmModule.cpp',
-    'wasm/WasmRuntime.cpp',
     'wasm/WasmSignalHandlers.cpp',
     'wasm/WasmStubs.cpp',
     'wasm/WasmTable.cpp',
     'wasm/WasmTextToBinary.cpp',
     'wasm/WasmTextUtils.cpp',
     'wasm/WasmTypes.cpp',
     'wasm/WasmValidate.cpp'
 ]
--- a/js/src/vm/ErrorReporting.cpp
+++ b/js/src/vm/ErrorReporting.cpp
@@ -9,16 +9,18 @@
 #include "mozilla/Move.h"
 
 #include <stdarg.h>
 
 #include "jscntxt.h"
 #include "jsexn.h"
 #include "jsfriendapi.h"
 
+#include "jscntxtinlines.h"
+
 using mozilla::Move;
 
 using JS::HandleObject;
 using JS::HandleValue;
 using JS::UniqueTwoByteChars;
 
 void
 js::CallWarningReporter(JSContext* cx, JSErrorReport* reportp)
--- a/js/src/vm/Initialization.cpp
+++ b/js/src/vm/Initialization.cpp
@@ -26,16 +26,17 @@
 #include "unicode/utypes.h"
 #endif // ENABLE_INTL_API
 #include "vm/DateTime.h"
 #include "vm/HelperThreads.h"
 #include "vm/Runtime.h"
 #include "vm/Time.h"
 #include "vm/TraceLogging.h"
 #include "vtune/VTuneWrapper.h"
+#include "wasm/WasmBuiltins.h"
 #include "wasm/WasmInstance.h"
 
 using JS::detail::InitState;
 using JS::detail::libraryInitState;
 using js::FutexThread;
 
 InitState JS::detail::libraryInitState;
 
@@ -187,18 +188,20 @@ JS_ShutDown(void)
     PRMJ_NowShutdown();
 
 #if EXPOSE_INTL_API
     u_cleanup();
 #endif // EXPOSE_INTL_API
 
     js::FinishDateTimeState();
 
-    if (!JSRuntime::hasLiveRuntimes())
+    if (!JSRuntime::hasLiveRuntimes()) {
+        js::wasm::ReleaseBuiltinThunks();
         js::jit::ReleaseProcessExecutableMemory();
+    }
 
     libraryInitState = InitState::ShutDown;
 }
 
 JS_PUBLIC_API(bool)
 JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn, JS_ICUReallocFn reallocFn, JS_ICUFreeFn freeFn)
 {
     MOZ_ASSERT(libraryInitState == InitState::Uninitialized,
--- a/js/src/vm/MutexIDs.h
+++ b/js/src/vm/MutexIDs.h
@@ -21,16 +21,18 @@
   _(RuntimeExclusiveAccess,      200) \
                                       \
   _(GlobalHelperThreadState,     300) \
                                       \
   _(ShellAsyncTasks,             350) \
                                       \
   _(GCLock,                      400) \
                                       \
+  _(WasmInitBuiltinThunks,       450) \
+                                      \
   _(SharedImmutableStringsCache, 500) \
   _(FutexThread,                 500) \
   _(PromiseTaskPtrVector,        500) \
   _(GeckoProfilerStrings,        500) \
   _(ProtectedRegionTree,         500) \
   _(WasmSigIdSet,                500) \
   _(ShellOffThreadState,         500) \
   _(SimulatorCacheLock,          500) \
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -262,33 +262,28 @@ JSRuntime::init(JSContext* cx, uint32_t 
         sharedImmutableStrings_ = js::SharedImmutableStringsCache::Create();
         if (!sharedImmutableStrings_)
             return false;
     }
 
     if (!caches().init())
         return false;
 
-    if (!wasm().init())
-        return false;
-
     return true;
 }
 
 void
 JSRuntime::destroyRuntime()
 {
     MOZ_ASSERT(!JS::CurrentThreadIsHeapBusy());
     MOZ_ASSERT(childRuntimeCount == 0);
     MOZ_ASSERT(initialized_);
 
     sharedIntlData.ref().destroyInstance();
 
-    wasm().destroy();
-
     if (gcInitialized) {
         /*
          * Finish any in-progress GCs first. This ensures the parseWaitingOnGC
          * list is empty in CancelOffThreadParses.
          */
         JSContext* cx = TlsContext.get();
         if (JS::IsIncrementalGCInProgress(cx))
             FinishGC(cx);
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -49,17 +49,16 @@
 #include "vm/DateTime.h"
 #include "vm/GeckoProfiler.h"
 #include "vm/MallocProvider.h"
 #include "vm/Scope.h"
 #include "vm/SharedImmutableStringsCache.h"
 #include "vm/Stack.h"
 #include "vm/Stopwatch.h"
 #include "vm/Symbol.h"
-#include "wasm/WasmRuntime.h"
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #endif
 
 namespace js {
 
@@ -531,23 +530,16 @@ struct JSRuntime : public js::MallocProv
 
   public:
     js::UnprotectedData<JS::BuildIdOp> buildIdOp;
 
     /* AsmJSCache callbacks are runtime-wide. */
     js::UnprotectedData<JS::AsmJSCacheOps> asmJSCacheOps;
 
   private:
-    // All runtime data needed for wasm and defined in wasm/WasmRuntime.h.
-    js::ActiveThreadData<js::wasm::Runtime> wasmRuntime_;
-
-  public:
-    js::wasm::Runtime& wasm() { return wasmRuntime_.ref(); }
-
-  private:
     js::UnprotectedData<const JSPrincipals*> trustedPrincipals_;
   public:
     void setTrustedPrincipals(const JSPrincipals* p) { trustedPrincipals_ = p; }
     const JSPrincipals* trustedPrincipals() const { return trustedPrincipals_; }
 
     js::ActiveThreadData<const JSWrapObjectCallbacks*> wrapObjectCallbacks;
     js::ActiveThreadData<js::PreserveWrapperCallback> preserveWrapperCallback;
 
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -203,16 +203,17 @@ JSString::dumpRepresentationHeader(FILE*
     // copy-and-paste into a debugger.
     fprintf(fp, "((%s*) %p) length: %" PRIuSIZE "  flags: 0x%x", subclass, this, length(), flags);
     if (flags & FLAT_BIT)               fputs(" FLAT", fp);
     if (flags & HAS_BASE_BIT)           fputs(" HAS_BASE", fp);
     if (flags & INLINE_CHARS_BIT)       fputs(" INLINE_CHARS", fp);
     if (flags & ATOM_BIT)               fputs(" ATOM", fp);
     if (isPermanentAtom())              fputs(" PERMANENT", fp);
     if (flags & LATIN1_CHARS_BIT)       fputs(" LATIN1", fp);
+    if (flags & INDEX_VALUE_BIT)        fprintf(fp, " INDEX_VALUE(%u)", getIndexValue());
     fputc('\n', fp);
 }
 
 void
 JSLinearString::dumpRepresentationChars(FILE* fp, int indent) const
 {
     if (hasLatin1Chars()) {
         fprintf(fp, "%*schars: ((Latin1Char*) %p) ", indent, "", rawLatin1Chars());
@@ -858,16 +859,19 @@ StaticStrings::init(JSContext* cx)
                                     Latin1Char('0' + (i % 10)),
                                     '\0' };
             JSFlatString* s = NewInlineString<NoGC>(cx, Latin1Range(buffer, 3));
             if (!s)
                 return false;
             HashNumber hash = mozilla::HashString(buffer, 3);
             intStaticTable[i] = s->morphAtomizedStringIntoPermanentAtom(hash);
         }
+
+        // Static string initialization can not race, so allow even without the lock.
+        intStaticTable[i]->maybeInitializeIndex(i, true);
     }
 
     return true;
 }
 
 void
 StaticStrings::trace(JSTracer* trc)
 {
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -90,16 +90,19 @@ static const size_t UINT32_CHAR_BUFFER_L
  *  - To avoid copying all strings created through the JSAPI, an "external"
  *    string (JSExternalString) can be created whose chars are managed by the
  *    JSAPI client.
  *
  *  - To avoid using two bytes per character for every string, string characters
  *    are stored as Latin1 instead of TwoByte if all characters are representable
  *    in Latin1.
  *
+ *  - To avoid slow conversions from strings to integer indexes, we cache 16 bit
+ *    unsigned indexes on strings representing such numbers.
+ *
  * Although all strings share the same basic memory layout, we can conceptually
  * arrange them into a hierarchy of operations/invariants and represent this
  * hierarchy in C++ with classes:
  *
  * C++ type                     operations+fields / invariants+properties
  * ==========================   =========================================
  * JSString (abstract)          get(Latin1|TwoByte)CharsZ, get(Latin1|TwoByte)Chars, length / -
  *  | \
@@ -241,16 +244,18 @@ class JSString : public js::gc::TenuredC
      *   Bit 3: IsAtom (Atom, PermanentAtom)
      *
      *  "HasBase" here refers to the two string types that have a 'base' field:
      *  JSDependentString and JSUndependedString.
      *  A JSUndependedString is a JSDependentString which has been 'fixed' (by ensureFixed)
      *  to be null-terminated.  In such cases, the string must keep marking its base since
      *  there may be any number of *other* JSDependentStrings transitively depending on it.
      *
+     * If the INDEX_VALUE_BIT is set the upper 16 bits of the flag word hold the integer
+     * index.
      */
 
     static const uint32_t FLAT_BIT               = JS_BIT(0);
     static const uint32_t HAS_BASE_BIT           = JS_BIT(1);
     static const uint32_t INLINE_CHARS_BIT       = JS_BIT(2);
     static const uint32_t ATOM_BIT               = JS_BIT(3);
 
     static const uint32_t ROPE_FLAGS             = 0;
@@ -265,16 +270,19 @@ class JSString : public js::gc::TenuredC
     /* Initial flags for thin inline and fat inline strings. */
     static const uint32_t INIT_THIN_INLINE_FLAGS = FLAT_BIT | INLINE_CHARS_BIT;
     static const uint32_t INIT_FAT_INLINE_FLAGS  = FLAT_BIT | FAT_INLINE_MASK;
 
     static const uint32_t TYPE_FLAGS_MASK        = JS_BIT(6) - 1;
 
     static const uint32_t LATIN1_CHARS_BIT       = JS_BIT(6);
 
+    static const uint32_t INDEX_VALUE_BIT        = JS_BIT(7);
+    static const uint32_t INDEX_VALUE_SHIFT      = 16;
+
     static const uint32_t MAX_LENGTH             = js::MaxStringLength;
 
     static const JS::Latin1Char MAX_LATIN1_CHAR = 0xff;
 
     /*
      * Helper function to validate that a string of a given length is
      * representable by a JSString. An allocation overflow is reported if false
      * is returned.
@@ -348,16 +356,26 @@ class JSString : public js::gc::TenuredC
     /* Strings have either Latin1 or TwoByte chars. */
     bool hasLatin1Chars() const {
         return d.u1.flags & LATIN1_CHARS_BIT;
     }
     bool hasTwoByteChars() const {
         return !(d.u1.flags & LATIN1_CHARS_BIT);
     }
 
+    /* Strings might contain cached indexes. */
+    bool hasIndexValue() const {
+        return d.u1.flags & INDEX_VALUE_BIT;
+    }
+    uint32_t getIndexValue() const {
+        MOZ_ASSERT(hasIndexValue());
+        MOZ_ASSERT(isFlat());
+        return d.u1.flags >> INDEX_VALUE_SHIFT;
+    }
+
     /* Fallible conversions to more-derived string types. */
 
     inline JSLinearString* ensureLinear(JSContext* cx);
     JSFlatString* ensureFlat(JSContext* cx);
 
     static bool ensureLinear(JSContext* cx, JSString* str) {
         return str->ensureLinear(cx) != nullptr;
     }
@@ -747,31 +765,57 @@ class JSFlatString : public JSLinearStri
     void init(const char16_t* chars, size_t length);
     void init(const JS::Latin1Char* chars, size_t length);
 
   public:
     template <js::AllowGC allowGC, typename CharT>
     static inline JSFlatString* new_(JSContext* cx,
                                      const CharT* chars, size_t length);
 
+    inline bool isIndexSlow(uint32_t* indexp) const {
+        MOZ_ASSERT(JSString::isFlat());
+        JS::AutoCheckCannotGC nogc;
+        if (hasLatin1Chars()) {
+            const JS::Latin1Char* s = latin1Chars(nogc);
+            return JS7_ISDEC(*s) && isIndexSlow(s, length(), indexp);
+        }
+        const char16_t* s = twoByteChars(nogc);
+        return JS7_ISDEC(*s) && isIndexSlow(s, length(), indexp);
+    }
+
     /*
      * Returns true if this string's characters store an unsigned 32-bit
      * integer value, initializing *indexp to that value if so.  (Thus if
      * calling isIndex returns true, js::IndexToString(cx, *indexp) will be a
      * string equal to this string.)
      */
     inline bool isIndex(uint32_t* indexp) const {
         MOZ_ASSERT(JSString::isFlat());
-        JS::AutoCheckCannotGC nogc;
-        if (hasLatin1Chars()) {
-            const JS::Latin1Char* s = latin1Chars(nogc);
-            return JS7_ISDEC(*s) && isIndexSlow(s, length(), indexp);
+
+        if (JSString::hasIndexValue()) {
+            *indexp = getIndexValue();
+            return true;
         }
-        const char16_t* s = twoByteChars(nogc);
-        return JS7_ISDEC(*s) && isIndexSlow(s, length(), indexp);
+
+        return isIndexSlow(indexp);
+    }
+
+    inline void maybeInitializeIndex(uint32_t index, bool allowAtom = false) {
+        MOZ_ASSERT(JSString::isFlat());
+        MOZ_ASSERT_IF(hasIndexValue(), getIndexValue() == index);
+        MOZ_ASSERT_IF(!allowAtom, !isAtom());
+
+        if (hasIndexValue() || index > UINT16_MAX)
+            return;
+
+        mozilla::DebugOnly<uint32_t> containedIndex;
+        MOZ_ASSERT(isIndexSlow(&containedIndex));
+        MOZ_ASSERT(index == containedIndex);
+
+        d.u1.flags |= (index << INDEX_VALUE_SHIFT) | INDEX_VALUE_BIT;
     }
 
     /*
      * Returns a property name represented by this string, or null on failure.
      * You must verify that this is not an index per isIndex before calling
      * this method.
      */
     inline js::PropertyName* toPropertyName(JSContext* cx);
rename from js/src/wasm/WasmRuntime.cpp
rename to js/src/wasm/WasmBuiltins.cpp
--- a/js/src/wasm/WasmRuntime.cpp
+++ b/js/src/wasm/WasmBuiltins.cpp
@@ -11,41 +11,48 @@
  *
  * 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 "wasm/WasmRuntime.h"
+#include "wasm/WasmBuiltins.h"
 
+#include "mozilla/Atomics.h"
 #include "mozilla/BinarySearch.h"
 
 #include "fdlibm.h"
-
 #include "jslibmath.h"
 
+#include "jit/InlinableNatives.h"
 #include "jit/MacroAssembler.h"
-
+#include "threading/Mutex.h"
 #include "wasm/WasmInstance.h"
 #include "wasm/WasmStubs.h"
 
 #include "vm/Debugger-inl.h"
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace jit;
 using namespace wasm;
 
+using mozilla::Atomic;
 using mozilla::BinarySearchIf;
+using mozilla::HashGeneric;
 using mozilla::IsNaN;
+using mozilla::MakeEnumeratedRange;
 
-static const unsigned BUILTIN_THUNK_LIFO_SIZE = 128;
-static const CodeKind BUILTIN_THUNK_CODEKIND = CodeKind::OTHER_CODE;
+static const unsigned BUILTIN_THUNK_LIFO_SIZE = 64 * 1024;
+
+// ============================================================================
+// WebAssembly builtin C++ functions called from wasm code to implement internal
+// wasm operations.
 
 #if defined(JS_CODEGEN_ARM)
 extern "C" {
 
 extern MOZ_EXPORT int64_t
 __aeabi_idivmod(int, int);
 
 extern MOZ_EXPORT int64_t
@@ -552,112 +559,22 @@ AddressOf(SymbolicAddress imm, ABIFuncti
         return FuncCast(Instance::currentMemory_i32, *abiType);
       case SymbolicAddress::Limit:
         break;
     }
 
     MOZ_CRASH("Bad SymbolicAddress");
 }
 
-static bool
-GenerateBuiltinThunk(JSContext* cx, void* func, ABIFunctionType abiType, ExitReason exitReason,
-                     UniqueBuiltinThunk* thunk)
-{
-    if (!cx->compartment()->ensureJitCompartmentExists(cx))
-        return false;
-
-    LifoAlloc lifo(BUILTIN_THUNK_LIFO_SIZE);
-    TempAllocator tempAlloc(&lifo);
-    MacroAssembler masm(MacroAssembler::WasmToken(), tempAlloc);
-
-    CallableOffsets offsets = GenerateBuiltinNativeExit(masm, abiType, exitReason, func);
-
-    masm.finish();
-    if (masm.oom())
-        return false;
-
-    // The executable allocator operates on pointer-aligned sizes.
-    uint32_t codeLength = AlignBytes(masm.bytesNeeded(), sizeof(void*));
-
-    ExecutablePool* pool = nullptr;
-    ExecutableAllocator& allocator = cx->runtime()->jitRuntime()->execAlloc();
-    uint8_t* codeBase = (uint8_t*) allocator.alloc(cx, codeLength, &pool, BUILTIN_THUNK_CODEKIND);
-    if (!codeBase)
-        return false;
-
-    {
-        AutoWritableJitCode awjc(cx->runtime(), codeBase, codeLength);
-        AutoFlushICache afc("GenerateBuiltinThunk");
-
-        masm.executableCopy(codeBase);
-        masm.processCodeLabels(codeBase);
-        memset(codeBase + masm.bytesNeeded(), 0, codeLength - masm.bytesNeeded());
-
-#ifdef DEBUG
-        if (!masm.oom()) {
-            MOZ_ASSERT(masm.callSites().empty());
-            MOZ_ASSERT(masm.callFarJumps().empty());
-            MOZ_ASSERT(masm.trapSites().empty());
-            MOZ_ASSERT(masm.trapFarJumps().empty());
-            MOZ_ASSERT(masm.extractMemoryAccesses().empty());
-            MOZ_ASSERT(!masm.numSymbolicAccesses());
-        }
-#endif
-    }
-
-    *thunk = js::MakeUnique<BuiltinThunk>(codeBase, codeLength, pool, offsets);
-    return !!*thunk;
-}
-
-struct BuiltinMatcher
-{
-    const uint8_t* address;
-    explicit BuiltinMatcher(const uint8_t* address) : address(address) {}
-    int operator()(const UniqueBuiltinThunk& thunk) const {
-        if (address < thunk->base)
-            return -1;
-        if (uintptr_t(address) >= uintptr_t(thunk->base) + thunk->size)
-            return 1;
-        return 0;
-    }
-};
-
 bool
-wasm::Runtime::getBuiltinThunk(JSContext* cx, void* funcPtr, ABIFunctionType abiType,
-                               ExitReason exitReason, void** thunkPtr)
-{
-    TypedFuncPtr lookup(funcPtr, abiType);
-    auto ptr = builtinThunkMap_.lookupForAdd(lookup);
-    if (ptr) {
-        *thunkPtr = ptr->value();
-        return true;
-    }
-
-    UniqueBuiltinThunk thunk;
-    if (!GenerateBuiltinThunk(cx, funcPtr, abiType, exitReason, &thunk))
-        return false;
-
-    // Maintain sorted order of thunk addresses.
-    size_t i;
-    size_t size = builtinThunkVector_.length();
-    if (BinarySearchIf(builtinThunkVector_, 0, size, BuiltinMatcher(thunk->base), &i))
-        MOZ_CRASH("clobbering memory");
-
-    *thunkPtr = thunk->base + thunk->codeRange.begin();
-
-    return builtinThunkVector_.insert(builtinThunkVector_.begin() + i, Move(thunk)) &&
-           builtinThunkMap_.add(ptr, lookup, *thunkPtr);
-}
-
-bool
-wasm::NeedsBuiltinThunk(SymbolicAddress func)
+wasm::NeedsBuiltinThunk(SymbolicAddress sym)
 {
     // Some functions don't want to a thunk, because they already have one or
     // they don't have frame info.
-    switch (func) {
+    switch (sym) {
       case SymbolicAddress::HandleExecutionInterrupt: // GenerateInterruptExit
       case SymbolicAddress::HandleDebugTrap:          // GenerateDebugTrapStub
       case SymbolicAddress::HandleThrow:              // GenerateThrowStub
       case SymbolicAddress::ReportTrap:               // GenerateTrapExit
       case SymbolicAddress::ReportOutOfBounds:        // GenerateOutOfBoundsExit
       case SymbolicAddress::ReportUnalignedAccess:    // GeneratesUnalignedExit
       case SymbolicAddress::CallImport_Void:          // GenerateImportInterpExit
       case SymbolicAddress::CallImport_I32:
@@ -712,28 +629,284 @@ wasm::NeedsBuiltinThunk(SymbolicAddress 
         return true;
       case SymbolicAddress::Limit:
         break;
     }
 
     MOZ_CRASH("unexpected symbolic address");
 }
 
-bool
-wasm::Runtime::getBuiltinThunk(JSContext* cx, SymbolicAddress func, void** thunkPtr)
-{
-    ABIFunctionType abiType;
-    void* funcPtr = AddressOf(func, &abiType);
+// ============================================================================
+// JS builtins that can be imported by wasm modules and called efficiently
+// through thunks. These thunks conform to the internal wasm ABI and thus can be
+// patched in for import calls. Calling a JS builtin through a thunk is much
+// faster than calling out through the generic import call trampoline which will
+// end up in the slowest C++ Instance::callImport path.
+//
+// Each JS builtin can have several overloads. These must all be enumerated in
+// PopulateTypedNatives() so they can be included in the process-wide thunk set.
 
-    if (!NeedsBuiltinThunk(func)) {
-        *thunkPtr = funcPtr;
-        return true;
+#define FOR_EACH_UNARY_NATIVE(_)   \
+    _(math_sin, MathSin)           \
+    _(math_tan, MathTan)           \
+    _(math_cos, MathCos)           \
+    _(math_exp, MathExp)           \
+    _(math_log, MathLog)           \
+    _(math_asin, MathASin)         \
+    _(math_atan, MathATan)         \
+    _(math_acos, MathACos)         \
+    _(math_log10, MathLog10)       \
+    _(math_log2, MathLog2)         \
+    _(math_log1p, MathLog1P)       \
+    _(math_expm1, MathExpM1)       \
+    _(math_sinh, MathSinH)         \
+    _(math_tanh, MathTanH)         \
+    _(math_cosh, MathCosH)         \
+    _(math_asinh, MathASinH)       \
+    _(math_atanh, MathATanH)       \
+    _(math_acosh, MathACosH)       \
+    _(math_sign, MathSign)         \
+    _(math_trunc, MathTrunc)       \
+    _(math_cbrt, MathCbrt)
+
+#define FOR_EACH_BINARY_NATIVE(_)  \
+    _(ecmaAtan2, MathATan2)        \
+    _(ecmaHypot, MathHypot)        \
+    _(ecmaPow, MathPow)            \
+
+#define DEFINE_UNARY_FLOAT_WRAPPER(func, _)        \
+    static float func##_uncached_f32(float x) {    \
+        return float(func##_uncached(double(x)));  \
+    }
+
+#define DEFINE_BINARY_FLOAT_WRAPPER(func, _)       \
+    static float func##_f32(float x, float y) {    \
+        return float(func(double(x), double(y)));  \
     }
 
-    return getBuiltinThunk(cx, funcPtr, abiType, ExitReason(func), thunkPtr);
+FOR_EACH_UNARY_NATIVE(DEFINE_UNARY_FLOAT_WRAPPER)
+FOR_EACH_BINARY_NATIVE(DEFINE_BINARY_FLOAT_WRAPPER)
+
+#undef DEFINE_UNARY_FLOAT_WRAPPER
+#undef DEFINE_BINARY_FLOAT_WRAPPER
+
+struct TypedNative
+{
+    InlinableNative native;
+    ABIFunctionType abiType;
+
+    TypedNative(InlinableNative native, ABIFunctionType abiType)
+      : native(native),
+        abiType(abiType)
+    {}
+
+    typedef TypedNative Lookup;
+    static HashNumber hash(const Lookup& l) {
+        return HashGeneric(uint32_t(l.native), uint32_t(l.abiType));
+    }
+    static bool match(const TypedNative& lhs, const Lookup& rhs) {
+        return lhs.native == rhs.native && lhs.abiType == rhs.abiType;
+    }
+};
+
+using TypedNativeToFuncPtrMap =
+    HashMap<TypedNative, void*, TypedNative, SystemAllocPolicy>;
+
+static bool
+PopulateTypedNatives(TypedNativeToFuncPtrMap* typedNatives)
+{
+    if (!typedNatives->init())
+        return false;
+
+#define ADD_OVERLOAD(funcName, native, abiType)                                           \
+    if (!typedNatives->putNew(TypedNative(InlinableNative::native, abiType),              \
+                              FuncCast(funcName, abiType)))                               \
+        return false;
+
+#define ADD_UNARY_OVERLOADS(funcName, native)                                             \
+    ADD_OVERLOAD(funcName##_uncached, native, Args_Double_Double)                         \
+    ADD_OVERLOAD(funcName##_uncached_f32, native, Args_Float32_Float32)
+
+#define ADD_BINARY_OVERLOADS(funcName, native)                                            \
+    ADD_OVERLOAD(funcName, native, Args_Double_DoubleDouble)                              \
+    ADD_OVERLOAD(funcName##_f32, native, Args_Float32_Float32Float32)
+
+    FOR_EACH_UNARY_NATIVE(ADD_UNARY_OVERLOADS)
+    FOR_EACH_BINARY_NATIVE(ADD_BINARY_OVERLOADS)
+
+#undef ADD_UNARY_OVERLOADS
+#undef ADD_BINARY_OVERLOADS
+
+    return true;
+}
+
+#undef FOR_EACH_UNARY_NATIVE
+#undef FOR_EACH_BINARY_NATIVE
+
+// ============================================================================
+// Process-wide builtin thunk set
+//
+// Thunks are inserted between wasm calls and the C++ callee and achieve two
+// things:
+//  - bridging the few differences between the internal wasm ABI and the external
+//    native ABI (viz. float returns on x86 and soft-fp ARM)
+//  - executing an exit prologue/epilogue which in turn allows any asynchronous
+//    interrupt to see the full stack up to the wasm operation that called out
+//
+// Thunks are created for two kinds of C++ callees, enumerated above:
+//  - SymbolicAddress: for statically compiled calls in the wasm module
+//  - Imported JS builtins: optimized calls to imports
+//
+// All thunks are created up front, lazily, when the first wasm module is
+// compiled in the process. Thunks are kept alive until the JS engine shuts down
+// in the process. No thunks are created at runtime after initialization. This
+// simple scheme allows several simplifications:
+//  - no reference counting to keep thunks alive
+//  - no problems toggling W^X permissions which, because of multiple executing
+//    threads, would require each thunk allocation to be on its own page
+// The cost for creating all thunks at once is relatively low since all thunks
+// fit within the smallest executable quanta (64k).
+
+using TypedNativeToCodeRangeMap =
+    HashMap<TypedNative, uint32_t, TypedNative, SystemAllocPolicy>;
+
+using SymbolicAddressToCodeRangeArray =
+    EnumeratedArray<SymbolicAddress, SymbolicAddress::Limit, uint32_t>;
+
+struct BuiltinThunks
+{
+    uint8_t* codeBase;
+    size_t codeSize;
+    CodeRangeVector codeRanges;
+    TypedNativeToCodeRangeMap typedNativeToCodeRange;
+    SymbolicAddressToCodeRangeArray symbolicAddressToCodeRange;
+
+    BuiltinThunks()
+      : codeBase(nullptr), codeSize(0)
+    {}
+
+    ~BuiltinThunks() {
+        if (codeBase)
+            DeallocateExecutableMemory(codeBase, codeSize);
+    }
+};
+
+Mutex initBuiltinThunks(mutexid::WasmInitBuiltinThunks);
+Atomic<const BuiltinThunks*> builtinThunks;
+
+bool
+wasm::EnsureBuiltinThunksInitialized()
+{
+    LockGuard<Mutex> guard(initBuiltinThunks);
+    if (builtinThunks)
+        return true;
+
+    auto thunks = MakeUnique<BuiltinThunks>();
+    if (!thunks)
+        return false;
+
+    LifoAlloc lifo(BUILTIN_THUNK_LIFO_SIZE);
+    TempAllocator tempAlloc(&lifo);
+    MacroAssembler masm(MacroAssembler::WasmToken(), tempAlloc);
+
+    for (auto sym : MakeEnumeratedRange(SymbolicAddress::Limit)) {
+        if (!NeedsBuiltinThunk(sym)) {
+            thunks->symbolicAddressToCodeRange[sym] = UINT32_MAX;
+            continue;
+        }
+
+        uint32_t codeRangeIndex = thunks->codeRanges.length();
+        thunks->symbolicAddressToCodeRange[sym] = codeRangeIndex;
+
+        ABIFunctionType abiType;
+        void* funcPtr = AddressOf(sym, &abiType);
+        ExitReason exitReason(sym);
+        CallableOffsets offset = GenerateBuiltinThunk(masm, abiType, exitReason, funcPtr);
+        if (masm.oom() || !thunks->codeRanges.emplaceBack(CodeRange::BuiltinThunk, offset))
+            return false;
+    }
+
+    TypedNativeToFuncPtrMap typedNatives;
+    if (!PopulateTypedNatives(&typedNatives))
+        return false;
+
+    if (!thunks->typedNativeToCodeRange.init())
+        return false;
+
+    for (TypedNativeToFuncPtrMap::Range r = typedNatives.all(); !r.empty(); r.popFront()) {
+        TypedNative typedNative = r.front().key();
+
+        uint32_t codeRangeIndex = thunks->codeRanges.length();
+        if (!thunks->typedNativeToCodeRange.putNew(typedNative, codeRangeIndex))
+            return false;
+
+        ABIFunctionType abiType = typedNative.abiType;
+        void* funcPtr = r.front().value();
+        ExitReason exitReason = ExitReason::Fixed::BuiltinNative;
+        CallableOffsets offset = GenerateBuiltinThunk(masm, abiType, exitReason, funcPtr);
+        if (masm.oom() || !thunks->codeRanges.emplaceBack(CodeRange::BuiltinThunk, offset))
+            return false;
+    }
+
+    masm.finish();
+    if (masm.oom())
+        return false;
+
+    size_t allocSize = AlignBytes(masm.bytesNeeded(), ExecutableCodePageSize);
+
+    thunks->codeSize = allocSize;
+    thunks->codeBase = (uint8_t*)AllocateExecutableMemory(allocSize, ProtectionSetting::Writable);
+    if (!thunks->codeBase)
+        return false;
+
+    masm.executableCopy(thunks->codeBase, /* flushICache = */ false);
+    memset(thunks->codeBase + masm.bytesNeeded(), 0, allocSize - masm.bytesNeeded());
+
+    masm.processCodeLabels(thunks->codeBase);
+#ifdef DEBUG
+    MOZ_ASSERT(masm.callSites().empty());
+    MOZ_ASSERT(masm.callFarJumps().empty());
+    MOZ_ASSERT(masm.trapSites().empty());
+    MOZ_ASSERT(masm.trapFarJumps().empty());
+    MOZ_ASSERT(masm.extractMemoryAccesses().empty());
+    MOZ_ASSERT(!masm.numSymbolicAccesses());
+#endif
+
+    ExecutableAllocator::cacheFlush(thunks->codeBase, thunks->codeSize);
+    if (!ExecutableAllocator::makeExecutable(thunks->codeBase, thunks->codeSize))
+        return false;
+
+    builtinThunks = thunks.release();
+    return true;
+}
+
+void
+wasm::ReleaseBuiltinThunks()
+{
+    if (builtinThunks) {
+        const BuiltinThunks* ptr = builtinThunks;
+        js_delete(const_cast<BuiltinThunks*>(ptr));
+        builtinThunks = nullptr;
+    }
+}
+
+void*
+wasm::SymbolicAddressTarget(SymbolicAddress sym)
+{
+    MOZ_ASSERT(builtinThunks);
+
+    ABIFunctionType abiType;
+    void* funcPtr = AddressOf(sym, &abiType);
+
+    if (!NeedsBuiltinThunk(sym))
+        return funcPtr;
+
+    const BuiltinThunks& thunks = *builtinThunks;
+    uint32_t codeRangeIndex = thunks.symbolicAddressToCodeRange[sym];
+    return thunks.codeBase + thunks.codeRanges[codeRangeIndex].begin();
 }
 
 static ABIFunctionType
 ToABIFunctionType(const Sig& sig)
 {
     const ValTypeVector& args = sig.args();
     ExprType ret = sig.ret();
 
@@ -750,41 +923,45 @@ ToABIFunctionType(const Sig& sig)
           case ValType::F64: abiType |= (ArgType_Double << (ArgType_Shift * (i + 1))); break;
           default:           MOZ_CRASH("unhandled arg type");
         }
     }
 
     return ABIFunctionType(abiType);
 }
 
-bool
-wasm::Runtime::getBuiltinThunk(JSContext* cx, void* funcPtr, const Sig& sig, void** thunkPtr)
+void*
+wasm::MaybeGetBuiltinThunk(HandleFunction f, const Sig& sig, JSContext* cx)
 {
+    MOZ_ASSERT(builtinThunks);
+
+    if (!f->isNative() || !f->jitInfo() || f->jitInfo()->type() != JSJitInfo::InlinableNative)
+        return nullptr;
+
+    InlinableNative native = f->jitInfo()->inlinableNative;
     ABIFunctionType abiType = ToABIFunctionType(sig);
-#ifdef JS_SIMULATOR
-    funcPtr = Simulator::RedirectNativeFunction(funcPtr, abiType);
-#endif
-    ExitReason nativeExitReason(ExitReason::Fixed::BuiltinNative);
-    return getBuiltinThunk(cx, funcPtr, abiType, nativeExitReason, thunkPtr);
+    TypedNative typedNative(native, abiType);
+
+    const BuiltinThunks& thunks = *builtinThunks;
+    auto p = thunks.typedNativeToCodeRange.readonlyThreadsafeLookup(typedNative);
+    if (!p)
+        return nullptr;
+
+    return thunks.codeBase + thunks.codeRanges[p->value()].begin();
 }
 
-BuiltinThunk*
-wasm::Runtime::lookupBuiltin(void* pc)
+bool
+wasm::LookupBuiltinThunk(void* pc, const CodeRange** codeRange, uint8_t** codeBase)
 {
-    size_t index;
-    size_t length = builtinThunkVector_.length();
-    if (!BinarySearchIf(builtinThunkVector_, 0, length, BuiltinMatcher((uint8_t*)pc), &index))
-        return nullptr;
-    return builtinThunkVector_[index].get();
-}
+    if (!builtinThunks)
+        return false;
 
-void
-wasm::Runtime::destroy()
-{
-    builtinThunkVector_.clear();
-    if (builtinThunkMap_.initialized())
-        builtinThunkMap_.clear();
+    const BuiltinThunks& thunks = *builtinThunks;
+    if (pc < thunks.codeBase || pc >= thunks.codeBase + thunks.codeSize)
+        return false;
+
+    *codeBase = thunks.codeBase;
+
+    CodeRange::OffsetInCode target((uint8_t*)pc - thunks.codeBase);
+    *codeRange = LookupInSorted(thunks.codeRanges, target);
+
+    return !!*codeRange;
 }
-
-BuiltinThunk::~BuiltinThunk()
-{
-    executablePool->release(size, BUILTIN_THUNK_CODEKIND);
-}
rename from js/src/wasm/WasmRuntime.h
rename to js/src/wasm/WasmBuiltins.h
--- a/js/src/wasm/WasmRuntime.h
+++ b/js/src/wasm/WasmBuiltins.h
@@ -11,96 +11,52 @@
  *
  * 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_runtime_h
-#define wasm_runtime_h
+#ifndef wasm_builtins_h
+#define wasm_builtins_h
 
-#include "NamespaceImports.h"
-
-#include "jit/IonTypes.h"
 #include "wasm/WasmTypes.h"
 
-using mozilla::HashGeneric;
-
 namespace js {
-
-namespace jit {
-    class ExecutablePool;
-}
-
 namespace wasm {
 
-class ExitReason;
+// A SymbolicAddress that NeedsBuiltinThunk() will call through a thunk to the
+// C++ function. This will be true for all normal calls from normal wasm
+// function code. Only calls to C++ from other exits/thunks do not need a thunk.
+
+bool
+NeedsBuiltinThunk(SymbolicAddress sym);
+
+// This function queries whether pc is in one of the process's builtin thunks
+// and, if so, returns the CodeRange and pointer to the code segment that the
+// CodeRange is relative to.
 
 bool
-NeedsBuiltinThunk(SymbolicAddress imm);
-
-struct TypedFuncPtr
-{
-    void* funcPtr;
-    jit::ABIFunctionType abiType;
-
-    TypedFuncPtr(void* funcPtr, jit::ABIFunctionType abiType)
-      : funcPtr(funcPtr),
-        abiType(abiType)
-    {}
+LookupBuiltinThunk(void* pc, const CodeRange** codeRange, uint8_t** codeBase);
 
-    typedef TypedFuncPtr Lookup;
-    static HashNumber hash(const Lookup& l) {
-        return HashGeneric(l.funcPtr, uint32_t(l.abiType));
-    }
-    static bool match(const TypedFuncPtr& lhs, const Lookup& rhs) {
-        return lhs.funcPtr == rhs.funcPtr && lhs.abiType == rhs.abiType;
-    }
-};
-
-typedef HashMap<TypedFuncPtr, void*, TypedFuncPtr, SystemAllocPolicy> BuiltinThunkMap;
-
-struct BuiltinThunk
-{
-    jit::ExecutablePool* executablePool;
-    CodeRange codeRange;
-    size_t size;
-    uint8_t* base;
+// EnsureBuiltinThunksInitialized() must be called, and must succeed, before
+// SymbolicAddressTarget() or MaybeGetBuiltinThunk(). This function creates all
+// thunks for the process. ReleaseBuiltinThunks() should be called before
+// ReleaseProcessExecutableMemory() so that the latter can assert that all
+// executable code has been released.
 
-    BuiltinThunk(uint8_t* base, size_t size, jit::ExecutablePool* executablePool,
-                 CallableOffsets offsets)
-      : executablePool(executablePool),
-        codeRange(CodeRange(CodeRange::BuiltinNativeExit, offsets)),
-        size(size),
-        base(base)
-    {}
-    ~BuiltinThunk();
-};
+bool
+EnsureBuiltinThunksInitialized();
 
-typedef UniquePtr<BuiltinThunk> UniqueBuiltinThunk;
-typedef Vector<UniqueBuiltinThunk, 4, SystemAllocPolicy> BuiltinThunkVector;
-
-// wasm::Runtime contains all the needed information for wasm that has the
-// lifetime of a JSRuntime.
+void*
+SymbolicAddressTarget(SymbolicAddress sym);
 
-class Runtime
-{
-    BuiltinThunkMap builtinThunkMap_;
-    BuiltinThunkVector builtinThunkVector_;
-
-    bool getBuiltinThunk(JSContext* cx, void* funcPtr, jit::ABIFunctionType type,
-                         ExitReason exitReason, void** thunkPtr);
+void*
+MaybeGetBuiltinThunk(HandleFunction f, const Sig& sig, JSContext* cx);
 
-  public:
-    bool init() { return builtinThunkMap_.init(); }
-    void destroy();
-
-    bool getBuiltinThunk(JSContext* cx, SymbolicAddress func, void** thunkPtr);
-    bool getBuiltinThunk(JSContext* cx, void* funcPtr, const Sig& sig, void** thunkPtr);
-    BuiltinThunk* lookupBuiltin(void* pc);
-};
+void
+ReleaseBuiltinThunks();
 
 } // namespace wasm
 } // namespace js
 
-#endif // wasm_runtime_h
+#endif // wasm_builtins_h
--- a/js/src/wasm/WasmCode.cpp
+++ b/js/src/wasm/WasmCode.cpp
@@ -90,36 +90,36 @@ AllocateCodeSegment(JSContext* cx, uint3
 
     cx->zone()->updateJitCodeMallocBytes(codeLength);
 
     wasmCodeAllocations++;
     return (uint8_t*)p;
 }
 
 static bool
-StaticallyLink(JSContext* cx, CodeSegment& cs, const LinkData& linkData)
+StaticallyLink(CodeSegment& cs, const LinkData& linkData)
 {
     for (LinkData::InternalLink link : linkData.internalLinks) {
         uint8_t* patchAt = cs.base() + link.patchAtOffset;
         void* target = cs.base() + link.targetOffset;
         if (link.isRawPointerPatch())
             *(void**)(patchAt) = target;
         else
             Assembler::PatchInstructionImmediate(patchAt, PatchedImmPtr(target));
     }
 
+    if (!EnsureBuiltinThunksInitialized())
+        return false;
+
     for (auto imm : MakeEnumeratedRange(SymbolicAddress::Limit)) {
         const Uint32Vector& offsets = linkData.symbolicLinks[imm];
         if (offsets.empty())
             continue;
 
-        void* target = nullptr;
-        if (!cx->runtime()->wasm().getBuiltinThunk(cx, imm, &target))
-            return false;
-
+        void* target = SymbolicAddressTarget(imm);
         for (uint32_t offset : offsets) {
             uint8_t* patchAt = cs.base() + offset;
             Assembler::PatchDataWithValueCheck(CodeLocationLabel(patchAt),
                                                PatchedImmPtr(target),
                                                PatchedImmPtr((void*)-1));
         }
     }
 
@@ -206,17 +206,17 @@ CodeSegment::create(JSContext* cx,
     cs->unalignedAccessCode_ = codeBase + linkData.unalignedAccessOffset;
 
     {
         JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread()));
         AutoFlushICache afc("CodeSegment::create");
         AutoFlushICache::setRange(uintptr_t(codeBase), cs->length());
 
         memcpy(codeBase, bytecode.begin(), bytecode.length());
-        if (!StaticallyLink(cx, *cs, linkData))
+        if (!StaticallyLink(*cs, linkData))
             return nullptr;
     }
 
     // Reprotect the whole region to avoid having separate RW and RX mappings.
     uint32_t size = JS_ROUNDUP(cs->length(), ExecutableCodePageSize);
     if (!ExecutableAllocator::makeExecutable(codeBase, size)) {
         ReportOutOfMemory(cx);
         return nullptr;
@@ -551,25 +551,18 @@ Code::lookupCallSite(void* returnAddress
         return nullptr;
 
     return &metadata_->callSites[match];
 }
 
 const CodeRange*
 Code::lookupRange(void* pc) const
 {
-    CodeRange::PC target((uint8_t*)pc - segment_->base());
-    size_t lowerBound = 0;
-    size_t upperBound = metadata_->codeRanges.length();
-
-    size_t match;
-    if (!BinarySearch(metadata_->codeRanges, lowerBound, upperBound, target, &match))
-        return nullptr;
-
-    return &metadata_->codeRanges[match];
+    CodeRange::OffsetInCode target((uint8_t*)pc - segment_->base());
+    return LookupInSorted(metadata_->codeRanges, target);
 }
 
 struct MemoryAccessOffset
 {
     const MemoryAccessVector& accesses;
     explicit MemoryAccessOffset(const MemoryAccessVector& accesses) : accesses(accesses) {}
     uintptr_t operator[](size_t index) const {
         return accesses[index].insnOffset();
--- a/js/src/wasm/WasmFrameIterator.cpp
+++ b/js/src/wasm/WasmFrameIterator.cpp
@@ -536,17 +536,17 @@ ProfilingFrameIterator::initFromExitFP()
       case CodeRange::Function:
         fp = CallerFPFromFP(fp);
         callerPC_ = ReturnAddressFromFP(fp);
         callerFP_ = CallerFPFromFP(fp);
         AssertMatchesCallSite(*activation_, callerPC_, callerFP_);
         break;
       case CodeRange::ImportJitExit:
       case CodeRange::ImportInterpExit:
-      case CodeRange::BuiltinNativeExit:
+      case CodeRange::BuiltinThunk:
       case CodeRange::TrapExit:
       case CodeRange::DebugTrap:
       case CodeRange::Inline:
       case CodeRange::Throw:
       case CodeRange::Interrupt:
       case CodeRange::FarJumpIsland:
         MOZ_CRASH("Unexpected CodeRange kind");
     }
@@ -572,48 +572,41 @@ ProfilingFrameIterator::ProfilingFrameIt
 {
     // In the case of ImportJitExit, the fp register may be temporarily
     // clobbered on return from Ion so always use activation.fp when it is set.
     if (activation.exitFP()) {
         initFromExitFP();
         return;
     }
 
-    code_ = activation_->compartment()->wasm.lookupCode(state.pc);
+    uint8_t* fp = (uint8_t*)state.fp;
+    uint8_t* pc = (uint8_t*)state.pc;
+    void** sp = (void**)state.sp;
 
-    const CodeRange* codeRange = nullptr;
-    uint8_t* codeBase = nullptr;
-    if (!code_) {
-        // Optimized builtin exits (see MaybeGetMatchingBuiltin in
-        // WasmInstance.cpp) are outside module's code.
-        AutoNoteSingleThreadedRegion anstr;
-        if (BuiltinThunk* thunk = activation_->cx()->runtime()->wasm().lookupBuiltin(state.pc)) {
-            codeRange = &thunk->codeRange;
-            codeBase = (uint8_t*) thunk->base;
-        } else {
-            // If pc isn't in any wasm code or builtin exit, we must be between
-            // pushing the WasmActivation and entering wasm code.
-            MOZ_ASSERT(done());
-            return;
-        }
-    } else {
-        codeRange = code_->lookupRange(state.pc);
+    // Get the CodeRange describing pc and the base address to which the
+    // CodeRange is relative. If the pc is not in a wasm module or a builtin
+    // thunk, then execution must be entering from or leaving to the C++ caller
+    // that pushed the WasmActivation.
+    const CodeRange* codeRange;
+    uint8_t* codeBase;
+    code_ = activation_->compartment()->wasm.lookupCode(pc);
+    if (code_) {
+        codeRange = code_->lookupRange(pc);
         codeBase = code_->segment().base();
+    } else if (!LookupBuiltinThunk(pc, &codeRange, &codeBase)) {
+        MOZ_ASSERT(done());
+        return;
     }
 
     // When the pc is inside the prologue/epilogue, the innermost call's Frame
     // is not complete and thus fp points to the second-to-innermost call's
     // Frame. Since fp can only tell you about its caller, naively unwinding
     // while pc is in the prologue/epilogue would skip the second-to-innermost
     // call. To avoid this problem, we use the static structure of the code in
     // the prologue and epilogue to do the Right Thing.
-    uint8_t* fp = (uint8_t*)state.fp;
-    uint8_t* pc = (uint8_t*)state.pc;
-    void** sp = (void**)state.sp;
-
     uint32_t offsetInCode = pc - codeBase;
     MOZ_ASSERT(offsetInCode >= codeRange->begin());
     MOZ_ASSERT(offsetInCode < codeRange->end());
 
     // Compute the offset of the pc from the (normal) entry of the code range.
     // The stack state of the pc for the entire table-entry is equivalent to
     // that of the first pc of the normal-entry. Thus, we can simplify the below
     // case analysis by redirecting all pc-in-table-entry cases to the
@@ -628,17 +621,17 @@ ProfilingFrameIterator::ProfilingFrameIt
         offsetFromEntry = offsetInCode - codeRange->begin();
     }
 
     switch (codeRange->kind()) {
       case CodeRange::Function:
       case CodeRange::FarJumpIsland:
       case CodeRange::ImportJitExit:
       case CodeRange::ImportInterpExit:
-      case CodeRange::BuiltinNativeExit:
+      case CodeRange::BuiltinThunk:
       case CodeRange::TrapExit:
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
         if (offsetFromEntry == BeforePushRetAddr || codeRange->isThunk()) {
             // The return address is still in lr and fp holds the caller's fp.
             callerPC_ = state.lr;
             callerFP_ = fp;
             AssertMatchesCallSite(*activation_, callerPC_, callerFP_);
         } else
@@ -738,17 +731,17 @@ ProfilingFrameIterator::operator++()
     switch (codeRange_->kind()) {
       case CodeRange::Entry:
         MOZ_ASSERT(callerFP_ == nullptr);
         callerPC_ = nullptr;
         break;
       case CodeRange::Function:
       case CodeRange::ImportJitExit:
       case CodeRange::ImportInterpExit:
-      case CodeRange::BuiltinNativeExit:
+      case CodeRange::BuiltinThunk:
       case CodeRange::TrapExit:
       case CodeRange::DebugTrap:
       case CodeRange::Inline:
       case CodeRange::FarJumpIsland:
         stackAddress_ = callerFP_;
         callerPC_ = ReturnAddressFromFP(callerFP_);
         AssertMatchesCallSite(*activation_, callerPC_, CallerFPFromFP(callerFP_));
         callerFP_ = CallerFPFromFP(callerFP_);
@@ -903,17 +896,17 @@ ProfilingFrameIterator::label() const
       case ExitReason::Fixed::DebugTrap:
         return debugTrapDescription;
     }
 
     switch (codeRange_->kind()) {
       case CodeRange::Function:          return code_->profilingLabel(codeRange_->funcIndex());
       case CodeRange::Entry:             return "entry trampoline (in wasm)";
       case CodeRange::ImportJitExit:     return importJitDescription;
-      case CodeRange::BuiltinNativeExit: return builtinNativeDescription;
+      case CodeRange::BuiltinThunk:      return builtinNativeDescription;
       case CodeRange::ImportInterpExit:  return importInterpDescription;
       case CodeRange::TrapExit:          return trapDescription;
       case CodeRange::DebugTrap:         return debugTrapDescription;
       case CodeRange::Inline:            return "inline stub (in wasm)";
       case CodeRange::FarJumpIsland:     return "interstitial (in wasm)";
       case CodeRange::Throw:             MOZ_FALLTHROUGH;
       case CodeRange::Interrupt:         MOZ_CRASH("does not have a frame");
     }
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -16,17 +16,17 @@
  * limitations under the License.
  */
 
 #include "wasm/WasmInstance.h"
 
 #include "jit/BaselineJIT.h"
 #include "jit/InlinableNatives.h"
 #include "jit/JitCommon.h"
-
+#include "wasm/WasmBuiltins.h"
 #include "wasm/WasmModule.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/ArrayBufferObject-inl.h"
 
 using namespace js;
 using namespace js::jit;
@@ -312,138 +312,16 @@ Instance::growMemory_i32(Instance* insta
 /* static */ uint32_t
 Instance::currentMemory_i32(Instance* instance)
 {
     uint32_t byteLength = instance->memoryLength();
     MOZ_ASSERT(byteLength % wasm::PageSize == 0);
     return byteLength / wasm::PageSize;
 }
 
-// asm.js has the ability to call directly into Math builtins, which wasm can't
-// do. Instead, wasm code generators have to pass the builtins as function
-// imports, resulting in slow import calls.
-//
-// However, we can optimize this by detecting that an import is just a JSNative
-// builtin and have wasm call straight to the builtin's C++ code, since the
-// ABIs perfectly match at the call site.
-//
-// Even though we could call into float32 variants of the math functions, we
-// do not do it, so as not to change the results.
-
-#define FOREACH_UNCACHED_MATH_BUILTIN(_) \
-    _(math_sin, MathSin)           \
-    _(math_tan, MathTan)           \
-    _(math_cos, MathCos)           \
-    _(math_exp, MathExp)           \
-    _(math_log, MathLog)           \
-    _(math_asin, MathASin)         \
-    _(math_atan, MathATan)         \
-    _(math_acos, MathACos)         \
-    _(math_log10, MathLog10)       \
-    _(math_log2, MathLog2)         \
-    _(math_log1p, MathLog1P)       \
-    _(math_expm1, MathExpM1)       \
-    _(math_sinh, MathSinH)         \
-    _(math_tanh, MathTanH)         \
-    _(math_cosh, MathCosH)         \
-    _(math_asinh, MathASinH)       \
-    _(math_atanh, MathATanH)       \
-    _(math_acosh, MathACosH)       \
-    _(math_sign, MathSign)         \
-    _(math_trunc, MathTrunc)       \
-    _(math_cbrt, MathCbrt)
-
-#define UNARY_FLOAT_WRAPPER(func)                 \
-    float func##_f32(float x) {                   \
-        return float(func(double(x)));            \
-    }
-
-#define BINARY_FLOAT_WRAPPER(func)                \
-    float func##_f32(float x, float y) {          \
-        return float(func(double(x), double(y))); \
-    }
-
-#define DEFINE_FLOAT_WRAPPER(name, _) UNARY_FLOAT_WRAPPER(name##_uncached)
-FOREACH_UNCACHED_MATH_BUILTIN(DEFINE_FLOAT_WRAPPER)
-
-BINARY_FLOAT_WRAPPER(ecmaAtan2)
-BINARY_FLOAT_WRAPPER(ecmaHypot)
-BINARY_FLOAT_WRAPPER(ecmaPow)
-
-#undef DEFINE_FLOAT_WRAPPER
-#undef BINARY_FLOAT_WRAPPER
-#undef UNARY_FLOAT_WRAPPER
-
-static void*
-IsMatchingBuiltin(HandleFunction f, const Sig& sig)
-{
-    if (!f->isNative() || !f->jitInfo() || f->jitInfo()->type() != JSJitInfo::InlinableNative)
-        return nullptr;
-
-    ExprType ret = sig.ret();
-    const ValTypeVector& args = sig.args();
-
-#define UNARY_BUILTIN(double_func, float_func)                 \
-        if (args.length() != 1)                                \
-            break;                                             \
-        if (args[0] == ValType::F64 && ret == ExprType::F64)   \
-            return JS_FUNC_TO_DATA_PTR(void*, double_func);    \
-        if (args[0] == ValType::F32 && ret == ExprType::F32)   \
-            return JS_FUNC_TO_DATA_PTR(void*, float_func);     \
-        break;
-
-#define BINARY_BUILTIN(double_func, float_func)                                         \
-        if (args.length() != 2)                                                         \
-            break;                                                                      \
-        if (args[0] == ValType::F64 && args[1] == ValType::F64 && ret == ExprType::F64) \
-            return JS_FUNC_TO_DATA_PTR(void*, double_func);                             \
-        if (args[0] == ValType::F32 && args[1] == ValType::F32 && ret == ExprType::F32) \
-            return JS_FUNC_TO_DATA_PTR(void*, float_func);                              \
-        break;
-
-    switch (f->jitInfo()->inlinableNative) {
-#define MAKE_CASE(funcName, inlinableNative)                        \
-      case InlinableNative::inlinableNative:                        \
-        UNARY_BUILTIN(funcName##_uncached, funcName##_uncached_f32)
-
-      FOREACH_UNCACHED_MATH_BUILTIN(MAKE_CASE)
-
-      case InlinableNative::MathATan2:
-        BINARY_BUILTIN(ecmaAtan2, ecmaAtan2_f32)
-      case InlinableNative::MathHypot:
-        BINARY_BUILTIN(ecmaHypot, ecmaHypot_f32)
-      case InlinableNative::MathPow:
-        BINARY_BUILTIN(ecmaPow, ecmaPow_f32)
-
-      default:
-        break;
-    }
-
-#undef MAKE_CASE
-#undef UNARY_BUILTIN
-#undef BINARY_BUILTIN
-#undef FOREACH_UNCACHED_MATH_BUILTIN
-
-    return nullptr;
-}
-
-static void*
-MaybeGetMatchingBuiltin(JSContext* cx, HandleFunction f, const Sig& sig)
-{
-    void* funcPtr = IsMatchingBuiltin(f, sig);
-    if (!funcPtr)
-        return nullptr;
-
-    void* thunkPtr = nullptr;
-    if (!cx->runtime()->wasm().getBuiltinThunk(cx, funcPtr, sig, &thunkPtr))
-        return nullptr;
-
-    return thunkPtr;
-}
-
 Instance::Instance(JSContext* cx,
                    Handle<WasmInstanceObject*> object,
                    UniqueCode code,
                    UniqueGlobalSegment globals,
                    HandleWasmMemoryObject memory,
                    SharedTableVector&& tables,
                    Handle<FunctionVector> funcImports,
                    const ValVector& globalImports)
@@ -474,17 +352,17 @@ Instance::Instance(JSContext* cx,
         if (!isAsmJS() && IsExportedWasmFunction(f)) {
             WasmInstanceObject* calleeInstanceObj = ExportedFunctionToInstanceObject(f);
             const CodeRange& codeRange = calleeInstanceObj->getExportedFunctionCodeRange(f);
             Instance& calleeInstance = calleeInstanceObj->instance();
             import.tls = calleeInstance.tlsData();
             import.code = calleeInstance.codeSegment().base() + codeRange.funcNormalEntry();
             import.baselineScript = nullptr;
             import.obj = calleeInstanceObj;
-        } else if (void* thunk = MaybeGetMatchingBuiltin(cx, f, fi.sig())) {
+        } else if (void* thunk = MaybeGetBuiltinThunk(f, fi.sig(), cx)) {
             import.tls = tlsData();
             import.code = thunk;
             import.baselineScript = nullptr;
             import.obj = f;
         } else {
             import.tls = tlsData();
             import.code = codeBase() + fi.interpExitCodeOffset();
             import.baselineScript = nullptr;
--- a/js/src/wasm/WasmSignalHandlers.cpp
+++ b/js/src/wasm/WasmSignalHandlers.cpp
@@ -20,16 +20,17 @@
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/ScopeExit.h"
 
 #include "jit/AtomicOperations.h"
 #include "jit/Disassembler.h"
 #include "vm/Runtime.h"
+#include "wasm/WasmBuiltins.h"
 #include "wasm/WasmInstance.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
 using JS::GenericNaN;
 using mozilla::DebugOnly;
@@ -1538,23 +1539,27 @@ js::InterruptRunningJitCode(JSContext* c
     // halts the thread and callers our JitInterruptHandler (which has already
     // been installed by EnsureSignalHandlersInstalled).
     pthread_t thread = (pthread_t)cx->threadNative();
     pthread_kill(thread, sInterruptSignal);
 #endif
 }
 
 MOZ_COLD bool
-js::wasm::IsPCInWasmCode(void *pc)
+js::wasm::IsPCInWasmCode(void* pc)
 {
     JSContext* cx = TlsContext.get();
     if (!cx)
         return false;
 
     MOZ_RELEASE_ASSERT(!cx->handlingSegFault);
 
     WasmActivation* activation = cx->wasmActivationStack();
     if (!activation)
         return false;
 
-    return !!activation->compartment()->wasm.lookupCode(pc) ||
-           !!activation->cx()->runtime()->wasm().lookupBuiltin(pc);
+    if (activation->compartment()->wasm.lookupCode(pc))
+        return true;
+
+    const CodeRange* codeRange;
+    uint8_t* codeBase;
+    return LookupBuiltinThunk(pc, &codeRange, &codeBase);
 }
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -894,18 +894,18 @@ struct ABIFunctionArgs
         uint32_t abi = uint32_t(abiType);
         while (i--)
             abi = abi >> ArgType_Shift;
         return ToMIRType(ABIArgType(abi));
     }
 };
 
 CallableOffsets
-wasm::GenerateBuiltinNativeExit(MacroAssembler& masm, ABIFunctionType abiType,
-                                ExitReason exitReason, void* func)
+wasm::GenerateBuiltinThunk(MacroAssembler& masm, ABIFunctionType abiType, ExitReason exitReason,
+                           void* funcPtr)
 {
     masm.setFramePushed(0);
 
     ABIFunctionArgs args(abiType);
     uint32_t framePushed = StackDecrementForCall(masm, ABIStackAlignment, args);
 
     CallableOffsets offsets;
     GenerateExitPrologue(masm, framePushed, exitReason, &offsets);
@@ -931,17 +931,17 @@ wasm::GenerateBuiltinNativeExit(MacroAss
         }
 
         Address src(masm.getStackPointer(), offsetToCallerStackArgs + i->offsetFromArgBase());
         Address dst(masm.getStackPointer(), i->offsetFromArgBase());
         StackCopy(masm, i.mirType(), scratch, src, dst);
     }
 
     AssertStackAlignment(masm, ABIStackAlignment);
-    masm.call(ImmPtr(func, ImmPtr::NoCheckToken()));
+    masm.call(ImmPtr(funcPtr, ImmPtr::NoCheckToken()));
 
 #if defined(JS_CODEGEN_X86)
     // x86 passes the return value on the x87 FP stack.
     Operand op(esp, 0);
     MIRType retType = ToMIRType(ABIArgType(abiType & ArgType_Mask));
     if (retType == MIRType::Float32) {
         masm.fstp32(op);
         masm.loadFloat32(op, ReturnFloat32Reg);
--- a/js/src/wasm/WasmStubs.h
+++ b/js/src/wasm/WasmStubs.h
@@ -44,18 +44,18 @@ GenerateImportFunction(jit::MacroAssembl
 extern CallableOffsets
 GenerateImportInterpExit(jit::MacroAssembler& masm, const FuncImport& fi, uint32_t funcImportIndex,
                          jit::Label* throwLabel);
 
 extern CallableOffsets
 GenerateImportJitExit(jit::MacroAssembler& masm, const FuncImport& fi, jit::Label* throwLabel);
 
 extern CallableOffsets
-GenerateBuiltinNativeExit(jit::MacroAssembler& masm, jit::ABIFunctionType abiType,
-                          ExitReason exitReason, void* func);
+GenerateBuiltinThunk(jit::MacroAssembler& masm, jit::ABIFunctionType abiType, ExitReason exitReason,
+                     void* func);
 
 extern CallableOffsets
 GenerateTrapExit(jit::MacroAssembler& masm, Trap trap, jit::Label* throwLabel);
 
 extern Offsets
 GenerateOutOfBoundsExit(jit::MacroAssembler& masm, jit::Label* throwLabel);
 
 extern Offsets
--- a/js/src/wasm/WasmTypes.cpp
+++ b/js/src/wasm/WasmTypes.cpp
@@ -685,18 +685,18 @@ CodeRange::CodeRange(Kind kind, Offsets 
       case FarJumpIsland:
       case Inline:
       case Throw:
       case Interrupt:
         break;
       case Function:
       case TrapExit:
       case ImportJitExit:
-      case BuiltinNativeExit:
       case ImportInterpExit:
+      case BuiltinThunk:
         MOZ_CRASH("should use more specific constructor");
     }
 #endif
 }
 
 CodeRange::CodeRange(Kind kind, CallableOffsets offsets)
   : begin_(offsets.begin),
     ret_(offsets.ret),
@@ -707,18 +707,18 @@ CodeRange::CodeRange(Kind kind, Callable
     kind_(kind)
 {
     MOZ_ASSERT(begin_ < ret_);
     MOZ_ASSERT(ret_ < end_);
 #ifdef DEBUG
     switch (kind_) {
       case TrapExit:
       case ImportJitExit:
-      case BuiltinNativeExit:
       case ImportInterpExit:
+      case BuiltinThunk:
         break;
       case Entry:
       case DebugTrap:
       case FarJumpIsland:
       case Inline:
       case Throw:
       case Interrupt:
       case Function:
@@ -735,8 +735,21 @@ CodeRange::CodeRange(uint32_t funcIndex,
     funcLineOrBytecode_(funcLineOrBytecode),
     funcBeginToNormalEntry_(offsets.normalEntry - begin_),
     kind_(Function)
 {
     MOZ_ASSERT(begin_ < ret_);
     MOZ_ASSERT(ret_ < end_);
     MOZ_ASSERT(offsets.normalEntry - begin_ <= UINT8_MAX);
 }
+
+const CodeRange*
+wasm::LookupInSorted(const CodeRangeVector& codeRanges, CodeRange::OffsetInCode target)
+{
+    size_t lowerBound = 0;
+    size_t upperBound = codeRanges.length();
+
+    size_t match;
+    if (!BinarySearch(codeRanges, lowerBound, upperBound, target, &match))
+        return nullptr;
+
+    return &codeRanges[match];
+}
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -826,17 +826,17 @@ struct FuncOffsets : CallableOffsets
 class CodeRange
 {
   public:
     enum Kind {
         Function,          // function definition
         Entry,             // calls into wasm from C++
         ImportJitExit,     // fast-path calling from wasm into JIT code
         ImportInterpExit,  // slow-path calling from wasm into C++ interp
-        BuiltinNativeExit, // fast-path calling from wasm into a C++ native
+        BuiltinThunk,      // fast-path calling from wasm into a C++ native
         TrapExit,          // calls C++ to report and jumps to throw stub
         DebugTrap,         // calls C++ to handle debug event
         FarJumpIsland,     // inserted to connect otherwise out-of-range insns
         Inline,            // stub that is jumped-to within prologue/epilogue
         Throw,             // special stack-unwinding stub
         Interrupt          // stub executes asynchronously to interrupt wasm
     };
 
@@ -870,17 +870,17 @@ class CodeRange
     Kind kind() const {
         return kind_;
     }
 
     bool isFunction() const {
         return kind() == Function;
     }
     bool isImportExit() const {
-        return kind() == ImportJitExit || kind() == ImportInterpExit || kind() == BuiltinNativeExit;
+        return kind() == ImportJitExit || kind() == ImportInterpExit || kind() == BuiltinThunk;
     }
     bool isTrapExit() const {
         return kind() == TrapExit;
     }
     bool isInline() const {
         return kind() == Inline;
     }
     bool isThunk() const {
@@ -912,32 +912,36 @@ class CodeRange
         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.
+    // A sorted array of CodeRanges can be looked up via BinarySearch and
+    // OffsetInCode.
 
-    struct PC {
+    struct OffsetInCode {
         size_t offset;
-        explicit PC(size_t offset) : offset(offset) {}
+        explicit OffsetInCode(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)
 
+extern const CodeRange*
+LookupInSorted(const CodeRangeVector& codeRanges, CodeRange::OffsetInCode target);
+
 // A wasm::Trap represents a wasm-defined trap that can occur during execution
 // which triggers a WebAssembly.RuntimeError. Generated code may jump to a Trap
 // symbolically, passing the bytecode offset to report as the trap offset. The
 // generated jump will be bound to a tiny stub which fills the offset and
 // then jumps to a per-Trap shared stub at the end of the module.
 
 enum class Trap
 {
--- a/layout/style/CSSStyleSheet.cpp
+++ b/layout/style/CSSStyleSheet.cpp
@@ -1026,17 +1026,17 @@ CSSStyleSheet::ReparseSheet(const nsAStr
     }
   }
 
   // nuke child sheets list and current namespace map
   for (StyleSheet* child = GetFirstChild(); child; ) {
     NS_ASSERTION(child->mParent == this, "Child sheet is not parented to this!");
     StyleSheet* next = child->mNext;
     child->mParent = nullptr;
-    child->mDocument = nullptr;
+    child->SetAssociatedDocument(nullptr, NotOwnedByDocument);
     child->mNext = nullptr;
     child = next;
   }
   SheetInfo().mFirstChild = nullptr;
   Inner()->mNameSpaceMap = nullptr;
 
   uint32_t lineNumber = 1;
   if (mOwningNode) {
--- a/layout/style/StyleSheet.cpp
+++ b/layout/style/StyleSheet.cpp
@@ -80,17 +80,22 @@ StyleSheet::UnlinkInner()
   // don't want to do any addrefing in the process, just to make sure
   // we don't confuse the cycle collector (though on the face of it,
   // addref/release pairs during unlink should probably be ok).
   RefPtr<StyleSheet> child;
   child.swap(SheetInfo().mFirstChild);
   while (child) {
     MOZ_ASSERT(child->mParent == this, "We have a unique inner!");
     child->mParent = nullptr;
-    child->mDocument = nullptr;
+    // We (and child) might still think we're owned by a document, because
+    // unlink order is non-deterministic, so the document's unlink, which would
+    // tell us it does't own us anymore, may not have happened yet.  But if
+    // we're being unlinked, clearly we're not owned by a document anymore
+    // conceptually!
+    child->SetAssociatedDocument(nullptr, NotOwnedByDocument);
 
     RefPtr<StyleSheet> next;
     // Null out child->mNext, but don't let it die yet
     next.swap(child->mNext);
     // Switch to looking at the old value of child->mNext next iteration
     child.swap(next);
     // "next" is now our previous value of child; it'll get released
     // as we loop around.
@@ -499,16 +504,19 @@ StyleSheet::UnparentChildren()
 {
   // XXXbz this is a little bogus; see the XXX comment where we
   // declare mFirstChild in StyleSheetInfo.
   for (StyleSheet* child = GetFirstChild();
        child;
        child = child->mNext) {
     if (child->mParent == this) {
       child->mParent = nullptr;
+      MOZ_ASSERT(child->mDocumentAssociationMode == NotOwnedByDocument,
+                 "How did we get to the destructor, exactly, if we're owned "
+                 "by a document?");
       child->mDocument = nullptr;
     }
   }
 }
 
 void
 StyleSheet::SubjectSubsumesInnerPrincipal(nsIPrincipal& aSubjectPrincipal,
                                           ErrorResult& aRv)
@@ -609,17 +617,17 @@ StyleSheet::AppendStyleSheet(StyleSheet*
   while (*tail) {
     tail = &(*tail)->mNext;
   }
   *tail = aSheet;
 
   // This is not reference counted. Our parent tells us when
   // it's going away.
   aSheet->mParent = this;
-  aSheet->mDocument = mDocument;
+  aSheet->SetAssociatedDocument(mDocument, mDocumentAssociationMode);
   DidDirty();
 }
 
 size_t
 StyleSheet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t n = 0;
   const StyleSheet* s = this;
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -3435,19 +3435,25 @@ nsBrowserAccess.prototype = {
     return browser;
   },
 
   openURI: function browser_openURI(aURI, aOpener, aWhere, aFlags) {
     let browser = this._getBrowser(aURI, aOpener, aWhere, aFlags);
     return browser ? browser.contentWindow : null;
   },
 
-  openURIInFrame: function browser_openURIInFrame(aURI, aParams, aWhere, aFlags) {
+  openURIInFrame: function browser_openURIInFrame(aURI, aParams, aWhere, aFlags,
+                                                  aNextTabParentId) {
+    // We currently ignore aNextTabParentId on mobile.  This needs to change
+    // when Fennec starts to support e10s.  Assertions will fire if this code
+    // isn't fixed by then.
     let browser = this._getBrowser(aURI, null, aWhere, aFlags);
-    return browser ? browser.QueryInterface(Ci.nsIFrameLoaderOwner) : null;
+    if (browser)
+      return browser.QueryInterface(Ci.nsIFrameLoaderOwner);
+    return null;
   },
 
   isTabContentWindow: function(aWindow) {
     return BrowserApp.getBrowserForWindow(aWindow) != null;
   },
 
   canClose() {
     return BrowserUtils.canCloseWindow(window);
--- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
+++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
@@ -287,16 +287,114 @@ SandboxBroker::SetSecurityLevelForConten
   result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
                             sandbox::TargetPolicy::HANDLES_DUP_ANY,
                             L"Semaphore");
   MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
                      "With these static arguments AddRule should never fail, what happened?");
 }
 #endif
 
+void
+SandboxBroker::SetSecurityLevelForGPUProcess(int32_t aSandboxLevel)
+{
+  MOZ_RELEASE_ASSERT(mPolicy, "mPolicy must be set before this call.");
+
+  sandbox::JobLevel jobLevel;
+  sandbox::TokenLevel accessTokenLevel;
+  sandbox::IntegrityLevel initialIntegrityLevel;
+  sandbox::IntegrityLevel delayedIntegrityLevel;
+
+  // The setting of these levels is pretty arbitrary, but they are a useful (if
+  // crude) tool while we are tightening the policy. Gaps are left to try and
+  // avoid changing their meaning.
+  MOZ_RELEASE_ASSERT(aSandboxLevel >= 1, "Should not be called with aSandboxLevel < 1");
+  if (aSandboxLevel >= 2) {
+    jobLevel = sandbox::JOB_NONE;
+    accessTokenLevel = sandbox::USER_LIMITED;
+    initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+    delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+  } else if (aSandboxLevel == 1) {
+    jobLevel = sandbox::JOB_NONE;
+    accessTokenLevel = sandbox::USER_NON_ADMIN;
+    initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+    delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+  }
+
+  sandbox::ResultCode result = mPolicy->SetJobLevel(jobLevel,
+                                                    0 /* ui_exceptions */);
+  MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+                     "Setting job level failed, have you set memory limit when jobLevel == JOB_NONE?");
+
+  // If the delayed access token is not restricted we don't want the initial one
+  // to be either, because it can interfere with running from a network drive.
+  sandbox::TokenLevel initialAccessTokenLevel =
+    (accessTokenLevel == sandbox::USER_UNPROTECTED ||
+     accessTokenLevel == sandbox::USER_NON_ADMIN)
+    ? sandbox::USER_UNPROTECTED : sandbox::USER_RESTRICTED_SAME_ACCESS;
+
+  result = mPolicy->SetTokenLevel(initialAccessTokenLevel, accessTokenLevel);
+  MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+                     "Lockdown level cannot be USER_UNPROTECTED or USER_LAST if initial level was USER_RESTRICTED_SAME_ACCESS");
+
+  result = mPolicy->SetIntegrityLevel(initialIntegrityLevel);
+  MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+                     "SetIntegrityLevel should never fail, what happened?");
+  result = mPolicy->SetDelayedIntegrityLevel(delayedIntegrityLevel);
+  MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+                     "SetDelayedIntegrityLevel should never fail, what happened?");
+
+  sandbox::MitigationFlags mitigations =
+    sandbox::MITIGATION_BOTTOM_UP_ASLR |
+    sandbox::MITIGATION_HEAP_TERMINATE |
+    sandbox::MITIGATION_SEHOP |
+    sandbox::MITIGATION_DEP_NO_ATL_THUNK |
+    sandbox::MITIGATION_DEP;
+
+  result = mPolicy->SetProcessMitigations(mitigations);
+  MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+                     "Invalid flags for SetProcessMitigations.");
+
+  mitigations =
+    sandbox::MITIGATION_STRICT_HANDLE_CHECKS |
+    sandbox::MITIGATION_DLL_SEARCH_ORDER;
+
+  result = mPolicy->SetDelayedProcessMitigations(mitigations);
+  MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+                     "Invalid flags for SetDelayedProcessMitigations.");
+
+  // Add the policy for the client side of a pipe. It is just a file
+  // in the \pipe\ namespace. We restrict it to pipes that start with
+  // "chrome." so the sandboxed process cannot connect to system services.
+  result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+                            sandbox::TargetPolicy::FILES_ALLOW_ANY,
+                            L"\\??\\pipe\\chrome.*");
+  MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+                     "With these static arguments AddRule should never fail, what happened?");
+
+  // Add the policy for the client side of the crash server pipe.
+  result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+                            sandbox::TargetPolicy::FILES_ALLOW_ANY,
+                            L"\\??\\pipe\\gecko-crash-server-pipe.*");
+  MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+                     "With these static arguments AddRule should never fail, what happened?");
+
+  // The process needs to be able to duplicate shared memory handles,
+  // which are Section handles, to the broker process and other child processes.
+  result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+                            sandbox::TargetPolicy::HANDLES_DUP_BROKER,
+                            L"Section");
+  MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+                     "With these static arguments AddRule should never fail, what happened?");
+  result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+                            sandbox::TargetPolicy::HANDLES_DUP_ANY,
+                            L"Section");
+  MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+                     "With these static arguments AddRule should never fail, what happened?");
+}
+
 #define SANDBOX_ENSURE_SUCCESS(result, message) \
   do { \
     MOZ_ASSERT(sandbox::SBOX_ALL_OK == result, message); \
     if (sandbox::SBOX_ALL_OK != result) \
       return false; \
   } while (0)
 
 bool
--- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.h
+++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.h
@@ -32,16 +32,19 @@ public:
                  void **aProcessHandle);
   virtual ~SandboxBroker();
 
   // Security levels for different types of processes
 #if defined(MOZ_CONTENT_SANDBOX)
   void SetSecurityLevelForContentProcess(int32_t aSandboxLevel,
                                          base::ChildPrivileges aPrivs);
 #endif
+
+  void SetSecurityLevelForGPUProcess(int32_t aSandboxLevel);
+
   bool SetSecurityLevelForPluginProcess(int32_t aSandboxLevel);
   enum SandboxLevel {
     LockDown,
     Restricted
   };
   bool SetSecurityLevelForGMPlugin(SandboxLevel aLevel);
 
   // File system permissions
--- a/toolkit/components/startup/nsAppStartup.cpp
+++ b/toolkit/components/startup/nsAppStartup.cpp
@@ -604,17 +604,17 @@ nsAppStartup::GetInterrupted(bool *aInte
 //
 
 NS_IMETHODIMP
 nsAppStartup::CreateChromeWindow(nsIWebBrowserChrome *aParent,
                                  uint32_t aChromeFlags,
                                  nsIWebBrowserChrome **_retval)
 {
   bool cancel;
-  return CreateChromeWindow2(aParent, aChromeFlags, nullptr, nullptr, &cancel, _retval);
+  return CreateChromeWindow2(aParent, aChromeFlags, nullptr, nullptr, 0, &cancel, _retval);
 }
 
 
 //
 // nsAppStartup->nsIWindowCreator2
 //
 
 NS_IMETHODIMP
@@ -628,16 +628,17 @@ nsAppStartup::SetScreenId(uint32_t aScre
   return appShell->SetScreenId(aScreenId);
 }
 
 NS_IMETHODIMP
 nsAppStartup::CreateChromeWindow2(nsIWebBrowserChrome *aParent,
                                   uint32_t aChromeFlags,
                                   nsITabParent *aOpeningTab,
                                   mozIDOMWindowProxy* aOpener,
+                                  uint64_t aNextTabParentId,
                                   bool *aCancel,
                                   nsIWebBrowserChrome **_retval)
 {
   NS_ENSURE_ARG_POINTER(aCancel);
   NS_ENSURE_ARG_POINTER(_retval);
   *aCancel = false;
   *_retval = 0;
 
@@ -647,20 +648,25 @@ nsAppStartup::CreateChromeWindow2(nsIWeb
 
   nsCOMPtr<nsIXULWindow> newWindow;
 
   if (aParent) {
     nsCOMPtr<nsIXULWindow> xulParent(do_GetInterface(aParent));
     NS_ASSERTION(xulParent, "window created using non-XUL parent. that's unexpected, but may work.");
 
     if (xulParent)
-      xulParent->CreateNewWindow(aChromeFlags, aOpeningTab, aOpener, getter_AddRefs(newWindow));
+      xulParent->CreateNewWindow(aChromeFlags, aOpeningTab, aOpener,
+                                 aNextTabParentId,
+                                 getter_AddRefs(newWindow));
     // And if it fails, don't try again without a parent. It could fail
     // intentionally (bug 115969).
   } else { // try using basic methods:
+    MOZ_RELEASE_ASSERT(aNextTabParentId == 0,
+                       "Unexpected aNextTabParentId, we shouldn't ever have a next actor ID without a parent");
+
     /* You really shouldn't be making dependent windows without a parent.
       But unparented modal (and therefore dependent) windows happen
       in our codebase, so we allow it after some bellyaching: */
     if (aChromeFlags & nsIWebBrowserChrome::CHROME_DEPENDENT)
       NS_WARNING("dependent window created without a parent");
 
     nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
     if (!appShell)
--- a/toolkit/components/windowcreator/nsIWindowCreator2.idl
+++ b/toolkit/components/windowcreator/nsIWindowCreator2.idl
@@ -32,26 +32,29 @@ interface nsIWindowCreator2 : nsIWindowC
                     window should be made a child/dependent window of
                     the parent, if any (and if the concept applies
                     to the underlying OS).
       @param chromeFlags Chrome features from nsIWebBrowserChrome
       @param aOpeningTab The TabParent that is trying to open this new chrome
                          window. Can be nullptr.
       @param aOpener The window which is trying to open this new chrome window.
                      Can be nullptr
+      @param aNextTabParentId The integer ID of the next tab parent actor to use.
+                              0 means there is no next tab parent ID to use.
       @param cancel Return |true| to reject window creation. If true the
                     implementation has determined the window should not
                     be created at all. The caller should not default
                     to any possible backup scheme for creating the window.
       @return the new window. Will be null if canceled or an error occurred.
   */
   nsIWebBrowserChrome createChromeWindow2(in nsIWebBrowserChrome parent,
                                           in uint32_t chromeFlags,
                                           in nsITabParent aOpeningTab,
                                           in mozIDOMWindowProxy aOpener,
+                                          in unsigned long long aNextTabParentId,
                                           out boolean cancel);
 
   /**
    * B2G multi-screen support. When open another top-level window on b2g,
    * a screen ID is needed for identifying which screen this window is
    * opened to.
    * @param aScreenId Differentiate screens of windows. It is platform-
    *                  specific due to the hardware limitation for now.
--- a/toolkit/components/windowwatcher/nsPIWindowWatcher.idl
+++ b/toolkit/components/windowwatcher/nsPIWindowWatcher.idl
@@ -97,24 +97,28 @@ interface nsPIWindowWatcher : nsISupport
    *        The nsITabParent that is requesting the new window be opened.
    * @param aFeatures
    *        Window features if called with window.open or similar.
    * @param aCalledFromJS
    *        True if called via window.open or similar.
    * @param aOpenerFullZoom
    *        The current zoom multiplier for the opener tab. This is then
    *        applied to the newly opened window.
+   * @param aNextTabParentId
+   *        The integer ID for the next tab parent actor.
+   *        0 means there is no next tab parent actor to use.
    *
    * @return the nsITabParent of the initial browser for the newly opened
    *         window.
    */
   nsITabParent openWindowWithTabParent(in nsITabParent aOpeningTab,
                                        in ACString aFeatures,
                                        in boolean aCalledFromJS,
-                                       in float aOpenerFullZoom);
+                                       in float aOpenerFullZoom,
+                                       in unsigned long long aNextTabParentId);
 
   /**
    * Find a named docshell tree item amongst all windows registered
    * with the window watcher.  This may be a subframe in some window,
    * for example.
    *
    * @param aName the name of the window.  Must not be null.
    * @param aRequestor the tree item immediately making the request.
--- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp
+++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp
@@ -477,16 +477,17 @@ CheckUserContextCompatibility(nsIDocShel
 }
 
 nsresult
 nsWindowWatcher::CreateChromeWindow(const nsACString& aFeatures,
                                     nsIWebBrowserChrome* aParentChrome,
                                     uint32_t aChromeFlags,
                                     nsITabParent* aOpeningTabParent,
                                     mozIDOMWindowProxy* aOpener,
+                                    uint64_t aNextTabParentId,
                                     nsIWebBrowserChrome** aResult)
 {
   nsCOMPtr<nsIWindowCreator2> windowCreator2(do_QueryInterface(mWindowCreator));
   if (NS_WARN_IF(!windowCreator2)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   // B2G multi-screen support. mozDisplayId is returned from the
@@ -495,17 +496,18 @@ nsWindowWatcher::CreateChromeWindow(cons
   int retval = WinHasOption(aFeatures, "mozDisplayId", 0, nullptr);
   windowCreator2->SetScreenId(retval);
 #endif
 
   bool cancel = false;
   nsCOMPtr<nsIWebBrowserChrome> newWindowChrome;
   nsresult rv =
     windowCreator2->CreateChromeWindow2(aParentChrome, aChromeFlags,
-                                        aOpeningTabParent, aOpener, &cancel,
+                                        aOpeningTabParent, aOpener,
+                                        aNextTabParentId, &cancel,
                                         getter_AddRefs(newWindowChrome));
 
   if (NS_SUCCEEDED(rv) && cancel) {
     newWindowChrome = nullptr;
     return NS_ERROR_ABORT;
   }
 
   newWindowChrome.forget(aResult);
@@ -542,16 +544,17 @@ nsWindowWatcher::MaybeDisablePersistence
   }
 }
 
 NS_IMETHODIMP
 nsWindowWatcher::OpenWindowWithTabParent(nsITabParent* aOpeningTabParent,
                                          const nsACString& aFeatures,
                                          bool aCalledFromJS,
                                          float aOpenerFullZoom,
+                                         uint64_t aNextTabParentId,
                                          nsITabParent** aResult)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(mWindowCreator);
 
   if (!nsContentUtils::IsSafeToRunScript()) {
     nsContentUtils::WarnScriptWasIgnored(nullptr);
     return NS_ERROR_FAILURE;
@@ -610,16 +613,17 @@ nsWindowWatcher::OpenWindowWithTabParent
   // that the new window will need to be remote.
   chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
 
   nsCOMPtr<nsIWebBrowserChrome> parentChrome(do_GetInterface(parentTreeOwner));
   nsCOMPtr<nsIWebBrowserChrome> newWindowChrome;
 
   CreateChromeWindow(aFeatures, parentChrome, chromeFlags,
                      aOpeningTabParent, nullptr,
+                     aNextTabParentId,
                      getter_AddRefs(newWindowChrome));
 
   if (NS_WARN_IF(!newWindowChrome)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsCOMPtr<nsIDocShellTreeItem> chromeTreeItem = do_GetInterface(newWindowChrome);
   if (NS_WARN_IF(!chromeTreeItem)) {
@@ -980,17 +984,18 @@ nsWindowWatcher::OpenWindowInternal(mozI
          we clear that indicator if the opener is chrome, so that the
          downstream consumer can treat the indicator to mean simply
          that the new window is subject to popup control. */
       nsCOMPtr<nsIWindowCreator2> windowCreator2(
         do_QueryInterface(mWindowCreator));
       if (windowCreator2) {
         mozIDOMWindowProxy* openerWindow = aForceNoOpener ? nullptr : aParent;
         rv = CreateChromeWindow(features, parentChrome, chromeFlags,
-                                nullptr, openerWindow, getter_AddRefs(newChrome));
+                                nullptr, openerWindow, 0,
+                                getter_AddRefs(newChrome));
 
       } else {
         rv = mWindowCreator->CreateChromeWindow(parentChrome, chromeFlags,
                                                 getter_AddRefs(newChrome));
       }
 
       if (parentTopInnerWindow) {
         parentTopInnerWindow->Resume();
--- a/toolkit/components/windowwatcher/nsWindowWatcher.h
+++ b/toolkit/components/windowwatcher/nsWindowWatcher.h
@@ -125,16 +125,17 @@ protected:
                                  nsIDocShellTreeOwner** aResult);
 
 private:
   nsresult CreateChromeWindow(const nsACString& aFeatures,
                               nsIWebBrowserChrome* aParentChrome,
                               uint32_t aChromeFlags,
                               nsITabParent* aOpeningTabParent,
                               mozIDOMWindowProxy* aOpener,
+                              uint64_t aNextTabParentId,
                               nsIWebBrowserChrome** aResult);
 
   void MaybeDisablePersistence(const nsACString& aFeatures,
                                nsIDocShellTreeOwner* aTreeOwner);
 
   static uint32_t CalculateChromeFlagsHelper(uint32_t aInitialFlags,
                                              const nsACString& aFeatures,
                                              bool &presenceFlag,
--- a/toolkit/content/xul.css
+++ b/toolkit/content/xul.css
@@ -18,16 +18,26 @@
  * THIS FILE IS LOCKED DOWN.  YOU ARE NOT ALLOWED TO MODIFY IT WITHOUT FIRST
  * HAVING YOUR CHANGES REVIEWED BY enndeakin@gmail.com
  */
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); /* set default namespace to XUL */
 @namespace html url("http://www.w3.org/1999/xhtml"); /* namespace for HTML elements */
 @namespace xbl url("http://www.mozilla.org/xbl"); /* namespace for XBL elements */
 
+/* All XUL pseudo elements needs to have appearance:auto by default, just in
+   case they have a -moz-appearance. */
+::-moz-tree-column, ::-moz-tree-row, ::-moz-tree-separator,
+::-moz-tree-cell, ::-moz-tree-indentation, ::-moz-tree-line,
+::-moz-tree-twisty, ::-moz-tree-image, ::-moz-tree-cell-text,
+::-moz-tree-checkbox, ::-moz-tree-progressmeter,
+::-moz-tree-drop-feedback {
+  appearance: auto;
+}
+
 /* ::::::::::
    :: Rules for 'hiding' portions of the chrome for special
    :: kinds of windows (not JUST browser windows) with toolbars
    ::::: */
 
 window[chromehidden~="menubar"] .chromeclass-menubar,
 window[chromehidden~="directories"] .chromeclass-directories,
 window[chromehidden~="status"] .chromeclass-status,
--- a/xpfe/appshell/nsIXULWindow.idl
+++ b/xpfe/appshell/nsIXULWindow.idl
@@ -111,21 +111,24 @@ interface nsIXULWindow : nsISupports
   void assumeChromeFlagsAreFrozen();
 
   /**
    * Create a new window.
    * @param aChromeFlags see nsIWebBrowserChrome
    * @param aOpeningTab the TabParent that requested this new window be opened.
    *                    Can be left null.
    * @param aOpener The window which is requesting that this new window be opened.
+   * @param aNextTabParentId The integer ID of the next tab parent actor to use.
+   *        0 means there is no next tab parent actor to use.
    * @return the newly minted window
    */
   nsIXULWindow createNewWindow(in int32_t aChromeFlags,
                                in nsITabParent aOpeningTab,
-                               in mozIDOMWindowProxy aOpener);
+                               in mozIDOMWindowProxy aOpener,
+                               in unsigned long long aNextTabParentId);
 
   attribute nsIXULBrowserWindow XULBrowserWindow;
 
   /**
    * Back-door method to force application of chrome flags at a particular
    * time.  Do NOT call this unless you know what you're doing!  In particular,
    * calling this when this XUL window doesn't yet have a document in its
    * docshell could cause problems.
@@ -154,9 +157,16 @@ interface nsIXULWindow : nsISupports
    *        The current width of the content area.
    * @param shellItemHeight
    *        The current height of the content area.
    */
   [noscript, notxpcom] void sizeShellToWithLimit(in int32_t aDesiredWidth,
                                                  in int32_t aDesiredHeight,
                                                  in int32_t shellItemWidth,
                                                  in int32_t shellItemHeight);
+
+  /**
+   * If the window was opened as a content window by script, this will return the
+   * integer ID of the next TabParent actor to use.
+   */
+  [noscript]
+  readonly attribute unsigned long long nextTabParentId;
 };
--- a/xpfe/appshell/nsXULWindow.cpp
+++ b/xpfe/appshell/nsXULWindow.cpp
@@ -102,17 +102,18 @@ nsXULWindow::nsXULWindow(uint32_t aChrom
     mIgnoreXULSize(false),
     mIgnoreXULPosition(false),
     mChromeFlagsFrozen(false),
     mIgnoreXULSizeMode(false),
     mDestroying(false),
     mRegistered(false),
     mPersistentAttributesDirty(0),
     mPersistentAttributesMask(0),
-    mChromeFlags(aChromeFlags)
+    mChromeFlags(aChromeFlags),
+    mNextTabParentId(0)
 {
 }
 
 nsXULWindow::~nsXULWindow()
 {
   Destroy();
 }
 
@@ -1896,23 +1897,27 @@ NS_IMETHODIMP nsXULWindow::ExitModalLoop
   mModalStatus = aStatus;
   return NS_OK;
 }
 
 // top-level function to create a new window
 NS_IMETHODIMP nsXULWindow::CreateNewWindow(int32_t aChromeFlags,
                                            nsITabParent *aOpeningTab,
                                            mozIDOMWindowProxy *aOpener,
+                                           uint64_t aNextTabParentId,
                                            nsIXULWindow **_retval)
 {
   NS_ENSURE_ARG_POINTER(_retval);
 
-  if (aChromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME)
+  if (aChromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME) {
+    MOZ_RELEASE_ASSERT(aNextTabParentId == 0,
+                       "Unexpected next tab parent ID, should never have a non-zero nextTabParentId when creating a new chrome window");
     return CreateNewChromeWindow(aChromeFlags, aOpeningTab, aOpener, _retval);
-  return CreateNewContentWindow(aChromeFlags, aOpeningTab, aOpener, _retval);
+  }
+  return CreateNewContentWindow(aChromeFlags, aOpeningTab, aOpener, aNextTabParentId, _retval);
 }
 
 NS_IMETHODIMP nsXULWindow::CreateNewChromeWindow(int32_t aChromeFlags,
                                                  nsITabParent *aOpeningTab,
                                                  mozIDOMWindowProxy *aOpener,
                                                  nsIXULWindow **_retval)
 {
   nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
@@ -1932,16 +1937,17 @@ NS_IMETHODIMP nsXULWindow::CreateNewChro
   NS_ADDREF(*_retval);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsXULWindow::CreateNewContentWindow(int32_t aChromeFlags,
                                                   nsITabParent *aOpeningTab,
                                                   mozIDOMWindowProxy *aOpener,
+                                                  uint64_t aNextTabParentId,
                                                   nsIXULWindow **_retval)
 {
   nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
   NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
 
   // We need to create a new top level window and then enter a nested
   // loop. Eventually the new window will be told that it has loaded,
   // at which time we know it is safe to spin out of the nested loop
@@ -1977,16 +1983,20 @@ NS_IMETHODIMP nsXULWindow::CreateNewCont
     NS_ENSURE_TRUE(newWindow, NS_ERROR_FAILURE);
   }
 
   // Specify that we want the window to remain locked until the chrome has loaded.
   nsXULWindow *xulWin = static_cast<nsXULWindow*>
                                    (static_cast<nsIXULWindow*>
                                                (newWindow));
 
+  if (aNextTabParentId) {
+    xulWin->mNextTabParentId = aNextTabParentId;
+  }
+
   if (aOpener) {
     nsCOMPtr<nsIDocShell> docShell;
     xulWin->GetDocShell(getter_AddRefs(docShell));
     MOZ_ASSERT(docShell);
     nsCOMPtr<nsIDOMChromeWindow> chromeWindow =
       do_QueryInterface(docShell->GetWindow());
     MOZ_ASSERT(chromeWindow);
 
@@ -2000,16 +2010,17 @@ NS_IMETHODIMP nsXULWindow::CreateNewCont
     nsIThread *thread = NS_GetCurrentThread();
     while (xulWin->IsLocked()) {
       if (!NS_ProcessNextEvent(thread))
         break;
     }
   }
 
   NS_ENSURE_STATE(xulWin->mPrimaryContentShell || xulWin->mPrimaryTabParent);
+  MOZ_ASSERT_IF(xulWin->mPrimaryContentShell, aNextTabParentId == 0);
 
   *_retval = newWindow;
   NS_ADDREF(*_retval);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsXULWindow::GetHasPrimaryContent(bool* aResult)
@@ -2299,8 +2310,16 @@ nsXULWindow::GetTabCount(uint32_t* aResu
 {
   if (mXULBrowserWindow) {
     return mXULBrowserWindow->GetTabCount(aResult);
   }
 
   *aResult = 0;
   return NS_OK;
 }
+
+nsresult
+nsXULWindow::GetNextTabParentId(uint64_t* aNextTabParentId)
+{
+  NS_ENSURE_ARG_POINTER(aNextTabParentId);
+  *aNextTabParentId = mNextTabParentId;
+  return NS_OK;
+}
--- a/xpfe/appshell/nsXULWindow.h
+++ b/xpfe/appshell/nsXULWindow.h
@@ -118,17 +118,21 @@ protected:
                              int32_t* aHeight);
    nsresult SetRootShellSize(int32_t aWidth,
                              int32_t aHeight);
 
    NS_IMETHOD SizeShellTo(nsIDocShellTreeItem* aShellItem, int32_t aCX, 
       int32_t aCY);
    NS_IMETHOD ExitModalLoop(nsresult aStatus);
    NS_IMETHOD CreateNewChromeWindow(int32_t aChromeFlags, nsITabParent* aOpeningTab, mozIDOMWindowProxy* aOpenerWindow, nsIXULWindow **_retval);
-   NS_IMETHOD CreateNewContentWindow(int32_t aChromeFlags, nsITabParent* aOpeningTab, mozIDOMWindowProxy* aOpenerWindow, nsIXULWindow **_retval);
+   NS_IMETHOD CreateNewContentWindow(int32_t aChromeFlags,
+                                     nsITabParent* aOpeningTab,
+                                     mozIDOMWindowProxy* aOpenerWindow,
+                                     uint64_t aNextTabParentId,
+                                     nsIXULWindow **_retval);
    NS_IMETHOD GetHasPrimaryContent(bool* aResult);
 
    void       EnableParent(bool aEnable);
    bool       ConstrainToZLevel(bool aImmediate, nsWindowZ *aPlacement,
                                 nsIWidget *aReqBelow, nsIWidget **aActualBelow);
    void       PlaceWindowLayersBehind(uint32_t aLowLevel, uint32_t aHighLevel,
                                       nsIXULWindow *aBehind);
    void       SetContentScrollbarVisibility(bool aVisible);
@@ -162,16 +166,17 @@ protected:
    bool                    mIgnoreXULSizeMode;
    // mDestroying is used to prevent reentry into into Destroy(), which can
    // otherwise happen due to script running as we tear down various things.
    bool                    mDestroying;
    bool                    mRegistered;
    uint32_t                mPersistentAttributesDirty; // persistentAttributes
    uint32_t                mPersistentAttributesMask;
    uint32_t                mChromeFlags;
+   uint64_t                mNextTabParentId;
    nsString                mTitle;
    nsIntRect               mOpenerScreenRect; // the screen rect of the opener
 
    nsCOMPtr<nsITabParent> mPrimaryTabParent;
 private:
    nsresult GetPrimaryTabParentSize(int32_t* aWidth, int32_t* aHeight);
    nsresult GetPrimaryContentShellSize(int32_t* aWidth, int32_t* aHeight);
    nsresult SetPrimaryTabParentSize(int32_t aWidth, int32_t aHeight);