merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 11 Feb 2015 14:58:16 +0100
changeset 228606 38058cb42a0ee28016d2cc619568b45249202799
parent 228504 117e52087be31a7cba51699f9b8e73b9b9e0cf25 (current diff)
parent 228605 0ebc9ae84e41d2eb6d21f0670a433605ac017dce (diff)
child 228617 2411b6e3a97d5723a62118451e7b5a166c28a7e6
child 228634 db5d46d32a331545a5abfc9f9018037dca3cad59
child 228673 037072f411fe811230f90b3904bb0b456abd4aed
push id28264
push usercbook@mozilla.com
push dateWed, 11 Feb 2015 13:58:35 +0000
treeherdermozilla-central@38058cb42a0e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone38.0a1
first release with
nightly linux32
38058cb42a0e / 38.0a1 / 20150211143627 / files
nightly linux64
38058cb42a0e / 38.0a1 / 20150211143627 / files
nightly win64
38058cb42a0e / 38.0a1 / 20150211143627 / files
nightly mac
nightly win32
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly win64
merge mozilla-inbound to mozilla-central a=merge
js/src/jsproxy.h
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -314,17 +314,17 @@ pref("media.cache_readahead_limit", 30);
 // Enable/Disable Gonk Decoder Module
 pref("media.fragmented-mp4.gonk.enabled", true);
 #endif
 // The default number of decoded video frames that are enqueued in
 // MediaDecoderReader's mVideoQueue.
 pref("media.video-queue.default-size", 3);
 
 // optimize images' memory usage
-pref("image.mem.decodeondraw", false);
+pref("image.mem.decodeondraw", true);
 pref("image.mem.allow_locking_in_content_processes", false); /* don't allow image locking */
 // Limit the surface cache to 1/8 of main memory or 128MB, whichever is smaller.
 // Almost everything that was factored into 'max_decoded_image_kb' is now stored
 // in the surface cache.  1/8 of main memory is 32MB on a 256MB device, which is
 // about the same as the old 'max_decoded_image_kb'.
 pref("image.mem.surfacecache.max_size_kb", 131072);  // 128MB
 pref("image.mem.surfacecache.size_factor", 8);  // 1/8 of main memory
 pref("image.mem.surfacecache.discard_factor", 2);  // Discard 1/2 of the surface cache at a time.
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1646,19 +1646,25 @@ pref("pdfjs.disabled", false);
 // Used by pdf.js to know the first time firefox is run with it installed so it
 // can become the default pdf viewer.
 pref("pdfjs.firstRun", true);
 // The values of preferredAction and alwaysAskBeforeHandling before pdf.js
 // became the default.
 pref("pdfjs.previousHandler.preferredAction", 0);
 pref("pdfjs.previousHandler.alwaysAskBeforeHandling", false);
 
+// Shumway is only bundled in Nightly.
 #ifdef NIGHTLY_BUILD
-// Shumway component (SWF player) is disabled by default. Also see bug 904346.
+// By default, Shumway (SWF player) is only enabled for whitelisted SWFs on Windows + OS X.
+#ifdef UNIX_BUT_NOT_MAC
 pref("shumway.disabled", true);
+#else
+pref("shumway.disabled", false);
+pref("shumway.swf.whitelist", "http://g-ecx.images-amazon.com/*/AiryBasicRenderer*.swf");
+#endif
 #endif
 
 // The maximum amount of decoded image data we'll willingly keep around (we
 // might keep around more than this, but we'll try to get down to this value).
 // (This is intentionally on the high side; see bug 746055.)
 pref("image.mem.max_decoded_image_kb", 256000);
 
 pref("loop.enabled", true);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -872,17 +872,32 @@ function LoadInOtherProcess(browser, loa
     // history into it.
     SessionStore.setTabState(tab, JSON.stringify(tabState));
   }
 }
 
 // Called when a docshell has attempted to load a page in an incorrect process.
 // This function is responsible for loading the page in the correct process.
 function RedirectLoad({ target: browser, data }) {
-  LoadInOtherProcess(browser, data.loadOptions, data.historyIndex);
+  // We should only start the redirection if the browser window has finished
+  // starting up. Otherwise, we should wait until the startup is done.
+  if (gBrowserInit.delayedStartupFinished) {
+    LoadInOtherProcess(browser, data.loadOptions, data.historyIndex);
+  } else {
+    let delayedStartupFinished = (subject, topic) => {
+      if (topic == "browser-delayed-startup-finished" &&
+          subject == window) {
+        Services.obs.removeObserver(delayedStartupFinished, topic);
+        LoadInOtherProcess(browser, data.loadOptions, data.historyIndex);
+      }
+    };
+    Services.obs.addObserver(delayedStartupFinished,
+                             "browser-delayed-startup-finished",
+                             false);
+  }
 }
 
 var gBrowserInit = {
   delayedStartupFinished: false,
 
   onLoad: function() {
     var mustLoadSidebar = false;
 
@@ -901,16 +916,18 @@ var gBrowserInit = {
     LanguageDetectionListener.init();
     BrowserOnClick.init();
     DevEdition.init();
 
     let mm = window.getGroupMessageManager("browsers");
     mm.loadFrameScript("chrome://browser/content/content.js", true);
     mm.loadFrameScript("chrome://browser/content/content-UITour.js", true);
 
+    window.messageManager.addMessageListener("Browser:LoadURI", RedirectLoad);
+
     // initialize observers and listeners
     // and give C++ access to gBrowser
     XULBrowserWindow.init();
     window.QueryInterface(Ci.nsIInterfaceRequestor)
           .getInterface(nsIWebNavigation)
           .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
           .QueryInterface(Ci.nsIInterfaceRequestor)
           .getInterface(Ci.nsIXULWindow)
@@ -918,18 +935,16 @@ var gBrowserInit = {
     window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow =
       new nsBrowserAccess();
 
     if (!gMultiProcessBrowser) {
       // There is a Content:Click message manually sent from content.
       Cc["@mozilla.org/eventlistenerservice;1"]
         .getService(Ci.nsIEventListenerService)
         .addSystemEventListener(gBrowser, "click", contentAreaClick, true);
-    } else {
-      gBrowser.updateBrowserRemoteness(gBrowser.selectedBrowser, true);
     }
 
     // hook up UI through progress listener
     gBrowser.addProgressListener(window.XULBrowserWindow);
     gBrowser.addTabsProgressListener(window.TabsProgressListener);
 
     // setup simple gestures support
     gGestureSupport.init(true);
@@ -1176,23 +1191,39 @@ var gBrowserInit = {
         // so that we don't disrupt startup
         try {
           gBrowser.loadTabs(specs, false, true);
         } catch (e) {}
       }
       else if (uriToLoad instanceof XULElement) {
         // swap the given tab with the default about:blank tab and then close
         // the original tab in the other window.
+        let tabToOpen = uriToLoad;
 
         // Stop the about:blank load
         gBrowser.stop();
         // make sure it has a docshell
         gBrowser.docShell;
 
-        gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, uriToLoad);
+        // If the browser that we're swapping in was remote, then we'd better
+        // be able to support remote browsers, and then make our selectedTab
+        // remote.
+        try {
+          if (tabToOpen.linkedBrowser.isRemoteBrowser) {
+            if (!gMultiProcessBrowser) {
+              throw new Error("Cannot drag a remote browser into a window " +
+                              "without the remote tabs load context.");
+            }
+
+            gBrowser.updateBrowserRemoteness(gBrowser.selectedBrowser, true);
+          }
+          gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToOpen);
+        } catch(e) {
+          Cu.reportError(e);
+        }
       }
       // window.arguments[2]: referrer (nsIURI)
       //                 [3]: postData (nsIInputStream)
       //                 [4]: allowThirdPartyFixup (bool)
       else if (window.arguments.length >= 3) {
         loadURI(uriToLoad, window.arguments[2], window.arguments[3] || null,
                 window.arguments[4] || false);
         window.focus();
@@ -1211,17 +1242,16 @@ var gBrowserInit = {
 
     Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
     window.messageManager.addMessageListener("Browser:URIFixup", gKeywordURIFixup);
-    window.messageManager.addMessageListener("Browser:LoadURI", RedirectLoad);
 
     BrowserOffline.init();
     OfflineApps.init();
     IndexedDBPromptHelper.init();
 #ifdef E10S_TESTING_ONLY
     gRemoteTabsUI.init();
 #endif
 
@@ -4077,16 +4107,23 @@ var XULBrowserWindow = {
     var securityUI = gBrowser.securityUI;
     this.onSecurityChange(null, null, securityUI.state);
   },
 
   setJSStatus: function () {
     // unsupported
   },
 
+  forceInitialBrowserRemote: function() {
+    let initBrowser =
+      document.getAnonymousElementByAttribute(gBrowser, "anonid", "initialBrowser");
+    gBrowser.updateBrowserRemoteness(initBrowser, true);
+    return initBrowser.frameLoader.tabParent;
+  },
+
   setDefaultStatus: function (status) {
     this.defaultStatus = status;
     this.updateStatusField();
   },
 
   setOverLink: function (url, anchorElt) {
     // Encode bidirectional formatting characters.
     // (RFC 3987 sections 3.2 and 4.1 paragraph 6)
@@ -4732,17 +4769,17 @@ var TabsProgressListener = {
   }
 }
 
 function nsBrowserAccess() { }
 
 nsBrowserAccess.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow, Ci.nsISupports]),
 
-  _openURIInNewTab: function(aURI, aReferrer, aIsPrivate, aIsExternal) {
+  _openURIInNewTab: function(aURI, aReferrer, aIsPrivate, aIsExternal, aForceNotRemote=false) {
     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;
@@ -4759,29 +4796,44 @@ nsBrowserAccess.prototype = {
       return win.gBrowser.selectedBrowser;
     }
 
     let loadInBackground = gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground");
 
     let tab = win.gBrowser.loadOneTab(aURI ? aURI.spec : "about:blank", {
                                       referrerURI: aReferrer,
                                       fromExternal: aIsExternal,
-                                      inBackground: loadInBackground});
+                                      inBackground: loadInBackground,
+                                      forceNotRemote: aForceNotRemote});
     let browser = win.gBrowser.getBrowserForTab(tab);
 
     if (needToFocusWin || (!loadInBackground && aIsExternal))
       win.focus();
 
     return browser;
   },
 
   openURI: function (aURI, aOpener, aWhere, aContext) {
+    // This function should only ever be called if we're opening a URI
+    // from a non-remote browser window (via nsContentTreeOwner).
+    if (aOpener && Cu.isCrossProcessWrapper(aOpener)) {
+      Cu.reportError("nsBrowserAccess.openURI was passed a CPOW for aOpener. " +
+                     "openURI should only ever be called from non-remote browsers.");
+      throw Cr.NS_ERROR_FAILURE;
+    }
+
     var newWindow = null;
     var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
 
+    if (aOpener && isExternal) {
+      Cu.reportError("nsBrowserAccess.openURI did not expect an opener to be " +
+                     "passed if the context is OPEN_EXTERNAL.");
+      throw Cr.NS_ERROR_FAILURE;
+    }
+
     if (isExternal && aURI && aURI.schemeIs("chrome")) {
       dump("use --chrome command-line option to load external chrome urls\n");
       return null;
     }
 
     if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW) {
       if (isExternal &&
           gPrefService.prefHasUserValue("browser.link.open_newwindow.override.external"))
@@ -4800,17 +4852,24 @@ nsBrowserAccess.prototype = {
           features += ",private";
         }
         // Pass all params to openDialog to ensure that "url" isn't passed through
         // loadOneOrMoreURIs, which splits based on "|"
         newWindow = openDialog(getBrowserURL(), "_blank", features, url, null, null, null);
         break;
       case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB :
         let referrer = aOpener ? makeURI(aOpener.location.href) : null;
-        let browser = this._openURIInNewTab(aURI, referrer, isPrivate, isExternal);
+        // If we have an opener, that means that the caller is expecting access
+        // to the nsIDOMWindow of the opened tab right away. For e10s windows,
+        // this means forcing the newly opened browser to be non-remote so that
+        // we can hand back the nsIDOMWindow. The XULBrowserWindow.shouldLoadURI
+        // will do the job of shuttling off the newly opened browser to run in
+        // the right process once it starts loading a URI.
+        let forceNotRemote = !!aOpener;
+        let browser = this._openURIInNewTab(aURI, referrer, isPrivate, isExternal, forceNotRemote);
         if (browser)
           newWindow = browser.contentWindow;
         break;
       default : // OPEN_CURRENTWINDOW or an illegal value
         newWindow = content;
         if (aURI) {
           let referrer = aOpener ? makeURI(aOpener.location.href) : null;
           let loadflags = isExternal ?
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -327,19 +327,19 @@ nsContextMenu.prototype = {
     if (this.inFrame) {
       if (mimeTypeIsTextBased(this.target.ownerDocument.contentType))
         this.isFrameImage.removeAttribute('hidden');
       else
         this.isFrameImage.setAttribute('hidden', 'true');
     }
 
     // BiDi UI
-    this.showItem("context-sep-bidi", top.gBidiUI);
+    this.showItem("context-sep-bidi", !this.onNumeric && top.gBidiUI);
     this.showItem("context-bidi-text-direction-toggle",
-                  this.onTextInput && top.gBidiUI);
+                  this.onTextInput && !this.onNumeric && top.gBidiUI);
     this.showItem("context-bidi-page-direction-toggle",
                   !this.onTextInput && top.gBidiUI);
 
     // SocialMarks. Marks does not work with text selections, only links. If
     // there is more than MENU_LIMIT providers, we show a submenu for them,
     // otherwise we have a menuitem per provider (added in SocialMarks class).
     let markProviders = SocialMarks.getProviders();
     let enablePageMarks = markProviders.length > 0 && !(this.onLink || this.onImage
@@ -558,16 +558,17 @@ nsContextMenu.prototype = {
     this.onImage           = false;
     this.onLoadedImage     = false;
     this.onCompletedImage  = false;
     this.imageDescURL      = "";
     this.onCanvas          = false;
     this.onVideo           = false;
     this.onAudio           = false;
     this.onTextInput       = false;
+    this.onNumeric         = false;
     this.onKeywordField    = false;
     this.mediaURL          = "";
     this.onLink            = false;
     this.onMailtoLink      = false;
     this.onSaveableLink    = false;
     this.link              = null;
     this.linkURL           = "";
     this.linkURI           = null;
@@ -653,16 +654,17 @@ nsContextMenu.prototype = {
         this.onAudio = true;
         let mediaURL = this.target.currentSrc || this.target.src;
         if (this.isMediaURLReusable(mediaURL)) {
           this.mediaURL = mediaURL;
         }
       }
       else if (editFlags & (SpellCheckHelper.INPUT | SpellCheckHelper.TEXTAREA)) {
         this.onTextInput = (editFlags & SpellCheckHelper.TEXTINPUT) !== 0;
+        this.onNumeric = (editFlags & SpellCheckHelper.NUMERIC) !== 0;
         this.onEditableArea = (editFlags & SpellCheckHelper.EDITABLE) !== 0;
         if (this.onEditableArea) {
           if (this.isRemote) {
             InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo);
           }
           else {
             InlineSpellCheckerUI.init(this.target.QueryInterface(Ci.nsIDOMNSEditableElement).editor);
             InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1361,44 +1361,47 @@
         <parameter name="aLoadInBackground"/>
         <parameter name="aAllowThirdPartyFixup"/>
         <body>
           <![CDATA[
             var aFromExternal;
             var aRelatedToCurrent;
             var aAllowMixedContent;
             var aSkipAnimation;
+            var aForceNotRemote;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aReferrerURI          = params.referrerURI;
               aCharset              = params.charset;
               aPostData             = params.postData;
               aLoadInBackground     = params.inBackground;
               aAllowThirdPartyFixup = params.allowThirdPartyFixup;
               aFromExternal         = params.fromExternal;
               aRelatedToCurrent     = params.relatedToCurrent;
               aAllowMixedContent    = params.allowMixedContent;
               aSkipAnimation        = params.skipAnimation;
+              aForceNotRemote       = params.forceNotRemote;
             }
 
             var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
                          Services.prefs.getBoolPref("browser.tabs.loadInBackground");
             var owner = bgLoad ? null : this.selectedTab;
             var tab = this.addTab(aURI, {
                                   referrerURI: aReferrerURI,
                                   charset: aCharset,
                                   postData: aPostData,
                                   ownerTab: owner,
                                   allowThirdPartyFixup: aAllowThirdPartyFixup,
                                   fromExternal: aFromExternal,
                                   relatedToCurrent: aRelatedToCurrent,
                                   skipAnimation: aSkipAnimation,
-                                  allowMixedContent: aAllowMixedContent });
+                                  allowMixedContent: aAllowMixedContent,
+                                  forceNotRemote: aForceNotRemote });
             if (!bgLoad)
               this.selectedTab = tab;
 
             return tab;
          ]]>
         </body>
       </method>
 
@@ -1667,29 +1670,31 @@
         <parameter name="aAllowThirdPartyFixup"/>
         <body>
           <![CDATA[
             const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
             var aFromExternal;
             var aRelatedToCurrent;
             var aSkipAnimation;
             var aAllowMixedContent;
+            var aForceNotRemote;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aReferrerURI          = params.referrerURI;
               aCharset              = params.charset;
               aPostData             = params.postData;
               aOwner                = params.ownerTab;
               aAllowThirdPartyFixup = params.allowThirdPartyFixup;
               aFromExternal         = params.fromExternal;
               aRelatedToCurrent     = params.relatedToCurrent;
               aSkipAnimation        = params.skipAnimation;
               aAllowMixedContent    = params.allowMixedContent;
+              aForceNotRemote       = params.forceNotRemote;
             }
 
             // 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");
 
@@ -1697,16 +1702,17 @@
 
             t.setAttribute("crop", "end");
             t.setAttribute("onerror", "this.removeAttribute('image');");
             t.className = "tabbrowser-tab";
 
             // The new browser should be remote if this is an e10s window and
             // the uri to load can be loaded remotely.
             let remote = gMultiProcessBrowser &&
+                         !aForceNotRemote &&
                          E10SUtils.canLoadURIInProcess(aURI, Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT);
             if (remote)
               t.setAttribute("remote", "true");
 
             this.tabContainer._unlockTabSizing();
 
             // When overflowing, new tabs are scrolled into view smoothly, which
             // doesn't go well together with the width transition. So we skip the
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -208,17 +208,16 @@ skip-if = e10s # Bug 1056146 - zoom test
 skip-if = e10s # Bug 1093373 - relies on browser.sessionHistory
 [browser_bug556061.js]
 [browser_bug559991.js]
 [browser_bug561623.js]
 skip-if = e10s
 [browser_bug561636.js]
 skip-if = e10s # Bug 1093677 - automated form submission from the test doesn't seem to quite work yet
 [browser_bug562649.js]
-skip-if = e10s # Bug 940195 - XULBrowserWindow.isBusy is false as a remote tab starts loading
 [browser_bug563588.js]
 [browser_bug565575.js]
 skip-if = e10s
 [browser_bug565667.js]
 skip-if = toolkit != "cocoa"
 [browser_bug567306.js]
 skip-if = e10s # Bug XXX - Needs some massaging to run in e10s
 [browser_bug575561.js]
@@ -255,35 +254,34 @@ skip-if = buildapp == 'mulet' || e10s # 
 [browser_bug655584.js]
 skip-if = e10s
 [browser_bug664672.js]
 [browser_bug676619.js]
 skip-if = buildapp == 'mulet' || os == "mac" # mac: Intermittent failures, bug 925225
 [browser_bug678392.js]
 skip-if = e10s # bug 1102331 - does focus things on the content window which break in e10s mode
 [browser_bug710878.js]
-skip-if = e10s # Bug 1100653 - test uses waitForFocus on content
 [browser_bug719271.js]
 skip-if = e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
 [browser_bug724239.js]
 [browser_bug734076.js]
 skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and gets in a mess when in e10s mode
 [browser_bug735471.js]
 [browser_bug749738.js]
-skip-if = e10s # Bug 921935 - focusmanager issues with e10s
 [browser_bug763468_perwindowpb.js]
 skip-if = e10s
 [browser_bug767836_perwindowpb.js]
 [browser_bug771331.js]
 [browser_bug783614.js]
 [browser_bug817947.js]
 [browser_bug822367.js]
 [browser_bug832435.js]
 [browser_bug839103.js]
 [browser_bug880101.js]
+skip-if = e10s # Bug 1126316 - New e10s windows erroneously fire initial about:blank location through nsIWebProgressListener
 [browser_bug882977.js]
 [browser_bug902156.js]
 [browser_bug906190.js]
 skip-if = buildapp == "mulet" || e10s # Bug 1093642 - test manipulates content and relies on content focus
 [browser_bug970746.js]
 skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and gets in a mess when in e10s mode
 [browser_bug1015721.js]
 skip-if = os == 'win' || e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
@@ -422,16 +420,18 @@ skip-if = os == "linux" || os == "mac" #
 [browser_tabfocus.js]
 skip-if = e10s # Bug 921935 - focusmanager issues with e10s (test calls getFocusedElementForWindow with a content window)
 [browser_tabkeynavigation.js]
 skip-if = e10s
 [browser_tabopen_reflows.js]
 [browser_tabs_isActive.js]
 skip-if = e10s # Bug 1100664 - test relies on linkedBrowser.docShell
 [browser_tabs_owner.js]
+[browser_testOpenNewRemoteTabsFromNonRemoteBrowsers.js]
+skip-if = !e10s && os == "linux" # Bug 994541 - Need OMTC enabled by default on Linux, or else we get blocked by an alert dialog when opening e10s window.
 [browser_trackingUI.js]
 support-files =
   trackingPage.html
   benignPage.html
 [browser_typeAheadFind.js]
 skip-if = buildapp == 'mulet' || e10s # Bug 921935 - focusmanager issues with e10s (test calls waitForFocus)
 [browser_unloaddialogs.js]
 skip-if = e10s # Bug 1100700 - test relies on unload event firing on closed tabs, which it doesn't
--- a/browser/base/content/test/general/browser_bug562649.js
+++ b/browser/base/content/test/general/browser_bug562649.js
@@ -1,16 +1,15 @@
 function test() {
   const URI = "data:text/plain,bug562649";
   browserDOMWindow.openURI(makeURI(URI),
                            null,
                            Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
                            Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
 
-  ok(XULBrowserWindow.isBusy, "window is busy loading a page");
   is(gBrowser.userTypedValue, URI, "userTypedValue matches test URI");
   is(gURLBar.value, URI, "location bar value matches test URI");
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.removeCurrentTab();
   is(gBrowser.userTypedValue, URI, "userTypedValue matches test URI after switching tabs");
   is(gURLBar.value, URI, "location bar value matches test URI after switching tabs");
 
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_testOpenNewRemoteTabsFromNonRemoteBrowsers.js
@@ -0,0 +1,124 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const OPEN_LOCATION_PREF = "browser.link.open_newwindow";
+const NON_REMOTE_PAGE = "about:welcomeback";
+
+Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+function frame_script() {
+  content.document.body.innerHTML = `
+    <a href="about:home" target="_blank" id="testAnchor">Open a window</a>
+  `;
+
+  let element = content.document.getElementById("testAnchor");
+  element.click();
+}
+
+/**
+ * Takes some browser in some window, and forces that browser
+ * to become non-remote, and then navigates it to a page that
+ * we're not supposed to be displaying remotely. Returns a
+ * Promise that resolves when the browser is no longer remote.
+ */
+function prepareNonRemoteBrowser(aWindow, browser) {
+  browser.loadURI(NON_REMOTE_PAGE);
+  return waitForDocLoadComplete(browser);
+}
+
+registerCleanupFunction(() => {
+  Services.prefs.clearUserPref(OPEN_LOCATION_PREF);
+});
+
+/**
+ * Test that if we open a new tab from a link in a non-remote
+ * browser in an e10s window, that the new tab will load properly.
+ */
+add_task(function* test_new_tab() {
+  let normalWindow = yield promiseOpenAndLoadWindow({
+    remote: true
+  }, true);
+  let privateWindow = yield promiseOpenAndLoadWindow({
+    remote: true,
+    private: true,
+  }, true);
+
+  for (let testWindow of [normalWindow, privateWindow]) {
+    yield promiseWaitForFocus(testWindow);
+    let testBrowser = testWindow.gBrowser.selectedBrowser;
+    info("Preparing non-remote browser");
+    yield prepareNonRemoteBrowser(testWindow, testBrowser);
+    info("Non-remote browser prepared - sending frame script");
+
+    // Get our framescript ready
+    let mm = testBrowser.messageManager;
+    mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", true);
+
+    let tabOpenEvent = yield waitForNewTabEvent(testWindow.gBrowser);
+    let newTab = tabOpenEvent.target;
+
+    yield promiseTabLoadEvent(newTab);
+
+    // Our framescript opens to about:home which means that the
+    // tab should eventually become remote.
+    ok(newTab.linkedBrowser.isRemoteBrowser,
+       "The opened browser never became remote.");
+
+    testWindow.gBrowser.removeTab(newTab);
+  }
+
+  normalWindow.close();
+  privateWindow.close();
+});
+
+/**
+ * Test that if we open a new window from a link in a non-remote
+ * browser in an e10s window, that the new window is not an e10s
+ * window. Also tests with a private browsing window.
+ */
+add_task(function* test_new_window() {
+  let normalWindow = yield promiseOpenAndLoadWindow({
+    remote: true
+  }, true);
+  let privateWindow = yield promiseOpenAndLoadWindow({
+    remote: true,
+    private: true,
+  }, true);
+
+  // Fiddle with the prefs so that we open target="_blank" links
+  // in new windows instead of new tabs.
+  Services.prefs.setIntPref(OPEN_LOCATION_PREF,
+                            Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW);
+
+  for (let testWindow of [normalWindow, privateWindow]) {
+    yield promiseWaitForFocus(testWindow);
+    let testBrowser = testWindow.gBrowser.selectedBrowser;
+    yield prepareNonRemoteBrowser(testWindow, testBrowser);
+
+    // Get our framescript ready
+    let mm = testBrowser.messageManager;
+    mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", true);
+
+    // Click on the link in the browser, and wait for the new window.
+    let {subject: newWindow} =
+      yield promiseTopicObserved("browser-delayed-startup-finished");
+
+    is(PrivateBrowsingUtils.isWindowPrivate(testWindow),
+       PrivateBrowsingUtils.isWindowPrivate(newWindow),
+       "Private browsing state of new window does not match the original!");
+
+    let newTab = newWindow.gBrowser.selectedTab;
+
+    yield promiseTabLoadEvent(newTab);
+
+    // Our framescript opens to about:home which means that the
+    // tab should eventually become remote.
+    ok(newTab.linkedBrowser.isRemoteBrowser,
+       "The opened browser never became remote.");
+    newWindow.close();
+  }
+
+  normalWindow.close();
+  privateWindow.close();
+});
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -118,16 +118,33 @@ function promiseWaitForEvent(object, eve
       resolve(event);
     }
 
     info("Waiting for " + eventName);
     object.addEventListener(eventName, listener, capturing, chrome);
   });
 }
 
+/**
+ * Allows setting focus on a window, and waiting for that window to achieve
+ * focus.
+ *
+ * @param aWindow
+ *        The window to focus and wait for.
+ *
+ * @return {Promise}
+ * @resolves When the window is focused.
+ * @rejects Never.
+ */
+function promiseWaitForFocus(aWindow) {
+  return new Promise((resolve) => {
+    waitForFocus(resolve, aWindow);
+  });
+}
+
 function getTestPlugin(aName) {
   var pluginName = aName || "Test Plug-in";
   var ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
   var tags = ph.getPluginTags();
 
   // Find the test plugin
   for (var i = 0; i < tags.length; i++) {
     if (tags[i].name == pluginName)
@@ -567,16 +584,31 @@ function promiseTabLoadEvent(tab, url, e
   }, 30000);
 
   tab.linkedBrowser.addEventListener(eventType, handle, true, true);
   if (url)
     tab.linkedBrowser.loadURI(url);
   return deferred.promise;
 }
 
+/**
+ * Returns a Promise that resolves once a new tab has been opened in
+ * a xul:tabbrowser.
+ *
+ * @param aTabBrowser
+ *        The xul:tabbrowser to monitor for a new tab.
+ * @return {Promise}
+ *        Resolved when the new tab has been opened.
+ * @resolves to the TabOpen event that was fired.
+ * @rejects Never.
+ */
+function waitForNewTabEvent(aTabBrowser) {
+  return promiseWaitForEvent(aTabBrowser.tabContainer, "TabOpen");
+}
+
 function assertWebRTCIndicatorStatus(expected) {
   let ui = Cu.import("resource:///modules/webrtcUI.jsm", {}).webrtcUI;
   let expectedState = expected ? "visible" : "hidden";
   let msg = "WebRTC indicator " + expectedState;
   is(ui.showGlobalIndicator, !!expected, msg);
 
   let expectVideo = false, expectAudio = false, expectScreen = false;
   if (expected) {
@@ -740,8 +772,31 @@ function promiseAutocompleteResultPopup(
   waitForFocus(() => {
     win.gURLBar.focus();
     win.gURLBar.value = inputText;
     win.gURLBar.controller.startSearch(inputText);
   }, win);
 
   return promiseSearchComplete(win);
 }
+
+/**
+ * Allows waiting for an observer notification once.
+ *
+ * @param aTopic
+ *        Notification topic to observe.
+ *
+ * @return {Promise}
+ * @resolves An object with subject and data properties from the observed
+ *           notification.
+ * @rejects Never.
+ */
+function promiseTopicObserved(aTopic)
+{
+  return new Promise((resolve) => {
+    Services.obs.addObserver(
+      function PTO_observe(aSubject, aTopic, aData) {
+        Services.obs.removeObserver(PTO_observe, aTopic);
+        resolve({subject: aSubject, data: aData});
+      }, aTopic, false);
+  });
+}
+
--- a/browser/base/content/test/general/test_contextmenu_input.html
+++ b/browser/base/content/test/general/test_contextmenu_input.html
@@ -155,17 +155,18 @@ function runTest(testNum) {
         input.type = 'password';
         openContextMenuFor(input); // Invoke context menu for next test.
         break;
 
     case 7: // password
     case 8: // email
     case 9: // url
     case 10: // tel
-        // Context menu for tel, password, email and url input fields.
+    case 11: // type='number'
+        // Context menu for tel, password, email, url and number input fields.
         checkContextMenu(["context-undo",        false,
                           "---",                 null,
                           "context-cut",         false,
                           "context-copy",        false,
                           "context-paste",       null, // ignore clipboard state
                           "context-delete",      false,
                           "---",                 null,
                           "context-selectall",   false,
@@ -176,25 +177,26 @@ function runTest(testNum) {
 
         if (testNum == 7) {
           input.type = 'email';
         } else if (testNum == 8) {
           input.type = 'url';
         } else if (testNum == 9) {
           input.type = 'tel';
         } else if (testNum == 10) {
+          input.type = 'number';
+        } else if (testNum == 11) {
           input.type = 'date';
         }
 
         openContextMenuFor(input); // Invoke context menu for next test.
         break;
 
-    case 11: // type='date'
-    case 12: // type='time'
-    case 13: // type='number'
+    case 12: // type='date'
+    case 13: // type='time'
     case 14: // type='color'
     case 15: // type='range'
         checkContextMenu(["context-navigation", null,
                               ["context-back",         false,
                                "context-forward",      false,
                                "context-reload",       true,
                                "context-bookmarkpage", true], null,
                           "---",                  null,
@@ -204,20 +206,18 @@ function runTest(testNum) {
                           "context-selectall",    null,
                           "---",                  null,
                           "context-viewsource",   true,
                           "context-viewinfo",     true,
                           "---",                 null,
                           "context-inspect",     true]);
         closeContextMenu();
 
-        if (testNum == 11) {
+        if (testNum == 12) {
           input.type = 'time';
-        } else if (testNum == 12) {
-          input.type = 'number';
         } else if (testNum == 13) {
           input.type = 'color';
         } else if (testNum == 14) {
           input.type = 'range';
         } else {
           input.type = 'search';
         }
 
--- a/browser/components/sessionstore/test/browser_394759_behavior.js
+++ b/browser/components/sessionstore/test/browser_394759_behavior.js
@@ -34,21 +34,33 @@ function test() {
 
     // hack to force window to be considered a popup (toolbar=no didn't work)
     let winData = windowsToOpen.shift();
     let settings = "chrome,dialog=no," +
                    (winData.isPopup ? "all=no" : "all");
     let url = "http://example.com/?window=" + windowsToOpen.length;
 
     provideWindow(function onTestURLLoaded(win) {
-      win.close();
-      // Give it time to close
-      executeSoon(function() {
-        openWindowRec(windowsToOpen, expectedResults, recCallback);
-      });
+      let tabReady = () => {
+        win.close();
+        // Give it time to close
+        executeSoon(function() {
+          openWindowRec(windowsToOpen, expectedResults, recCallback);
+        });
+      };
+
+      if (win.gMultiProcessBrowser) {
+        let tab = win.gBrowser.selectedTab;
+        tab.addEventListener("SSTabRestored", function onTabRestored() {
+          tab.removeEventListener("SSTabRestored", onTabRestored);
+          tabReady();
+        });
+      } else {
+        tabReady();
+      }
     }, url, settings);
   }
 
   let windowsToOpen = [{isPopup: false},
                        {isPopup: false},
                        {isPopup: true},
                        {isPopup: true},
                        {isPopup: true}];
--- a/browser/components/sessionstore/test/browser_423132.js
+++ b/browser/components/sessionstore/test/browser_423132.js
@@ -25,51 +25,60 @@ function test() {
   newWin.addEventListener("load", function (aEvent) {
     newWin.removeEventListener("load", arguments.callee, false);
 
     // Wait for sessionstore to be ready to restore this window
     executeSoon(function() {
       newWin.gBrowser.loadURI(testURL, null, null);
 
       promiseBrowserLoaded(newWin.gBrowser.selectedBrowser).then(() => {
-        // get the sessionstore state for the window
-        TabState.flush(newWin.gBrowser.selectedBrowser);
-        let state = ss.getWindowState(newWin);
+        let ready = () => {
+          // get the sessionstore state for the window
+          TabState.flush(newWin.gBrowser.selectedBrowser);
+          let state = ss.getWindowState(newWin);
 
-        // verify our cookie got set during pageload
-        let e = cs.enumerator;
-        let cookie;
-        let i = 0;
-        while (e.hasMoreElements()) {
-          cookie = e.getNext().QueryInterface(Ci.nsICookie);
-          i++;
-        }
-        is(i, 1, "expected one cookie");
+          // verify our cookie got set during pageload
+          let e = cs.enumerator;
+          let cookie;
+          let i = 0;
+          while (e.hasMoreElements()) {
+            cookie = e.getNext().QueryInterface(Ci.nsICookie);
+            i++;
+          }
+          is(i, 1, "expected one cookie");
 
-        // remove the cookie
-        cs.removeAll();
+          // remove the cookie
+          cs.removeAll();
+
+          // restore the window state
+          ss.setWindowState(newWin, state, true);
 
-        // restore the window state
-        ss.setWindowState(newWin, state, true);
+          // at this point, the cookie should be restored...
+          e = cs.enumerator;
+          let cookie2;
+          while (e.hasMoreElements()) {
+            cookie2 = e.getNext().QueryInterface(Ci.nsICookie);
+            if (cookie.name == cookie2.name)
+              break;
+          }
+          is(cookie.name, cookie2.name, "cookie name successfully restored");
+          is(cookie.value, cookie2.value, "cookie value successfully restored");
+          is(cookie.path, cookie2.path, "cookie path successfully restored");
 
-        // at this point, the cookie should be restored...
-        e = cs.enumerator;
-        let cookie2;
-        while (e.hasMoreElements()) {
-          cookie2 = e.getNext().QueryInterface(Ci.nsICookie);
-          if (cookie.name == cookie2.name)
-            break;
+          // clean up
+          if (gPrefService.prefHasUserValue("browser.sessionstore.interval"))
+            gPrefService.clearUserPref("browser.sessionstore.interval");
+          cs.removeAll();
+          newWin.close();
+          finish();
+        };
+
+        if (newWin.gMultiProcessBrowser) {
+          let tab = newWin.gBrowser.selectedTab;
+          promiseTabRestored(tab).then(ready);
+        } else {
+          ready();
         }
-        is(cookie.name, cookie2.name, "cookie name successfully restored");
-        is(cookie.value, cookie2.value, "cookie value successfully restored");
-        is(cookie.path, cookie2.path, "cookie path successfully restored");
-
-        // clean up
-        if (gPrefService.prefHasUserValue("browser.sessionstore.interval"))
-          gPrefService.clearUserPref("browser.sessionstore.interval");
-        cs.removeAll();
-        newWin.close();
-        finish();
       }, true, testURL);
     });
   }, false);
 }
 
--- a/browser/devtools/canvasdebugger/canvasdebugger.js
+++ b/browser/devtools/canvasdebugger/canvasdebugger.js
@@ -411,20 +411,27 @@ let SnapshotsListView = Heritage.extend(
     fp.init(window, L10N.getStr("snapshotsList.saveDialogTitle"), Ci.nsIFilePicker.modeOpen);
     fp.appendFilter(L10N.getStr("snapshotsList.saveDialogJSONFilter"), "*.json");
     fp.appendFilter(L10N.getStr("snapshotsList.saveDialogAllFilter"), "*.*");
 
     if (fp.show() != Ci.nsIFilePicker.returnOK) {
       return;
     }
 
-    let channel = NetUtil.newChannel(fp.file);
+    let channel = NetUtil.newChannel2(fp.file,
+                                      null,
+                                      null,
+                                      window.document,
+                                      null, // aLoadingPrincipal
+                                      null, // aTriggeringPrincipal
+                                      Ci.nsILoadInfo.SEC_NORMAL,
+                                      Ci.nsIContentPolicy.TYPE_OTHER);
     channel.contentType = "text/plain";
 
-    NetUtil.asyncFetch(channel, (inputStream, status) => {
+    NetUtil.asyncFetch2(channel, (inputStream, status) => {
       if (!Components.isSuccessCode(status)) {
         console.error("Could not import recorded animation frame snapshot file.");
         return;
       }
       try {
         let string = NetUtil.readInputStreamToString(inputStream, inputStream.available());
         var data = JSON.parse(string);
       } catch (e) {
--- a/browser/devtools/profiler/ui-recordings.js
+++ b/browser/devtools/profiler/ui-recordings.js
@@ -321,20 +321,27 @@ function saveRecordingToFile(recordingIt
  *        The file to import the data from.
  * @return object
  *         A promise that is resolved once importing finishes, or rejected
  *         if there was an error.
  */
 function loadRecordingFromFile(file) {
   let deferred = promise.defer();
 
-  let channel = NetUtil.newChannel(file);
+  let channel = NetUtil.newChannel2(file,
+                                    null,
+                                    null,
+                                    window.document,
+                                    null,  // aLoadingPrincipal
+                                    null,  // aTriggeringPrincipal
+                                    Ci.nsILoadInfo.SEC_NORMAL,
+                                    Ci.nsIContentPolicy.TYPE_OTHER);
   channel.contentType = "text/plain";
 
-  NetUtil.asyncFetch(channel, (inputStream, status) => {
+  NetUtil.asyncFetch2(channel, (inputStream, status) => {
     if (!Components.isSuccessCode(status)) {
       deferred.reject(new Error("Could not import recording data file."));
       return;
     }
     try {
       let string = NetUtil.readInputStreamToString(inputStream, inputStream.available());
       var recordingData = JSON.parse(string);
     } catch (e) {
--- a/browser/devtools/projecteditor/test/head.js
+++ b/browser/devtools/projecteditor/test/head.js
@@ -255,32 +255,37 @@ function getTempFile(path) {
 
 // https://developer.mozilla.org/en-US/Add-ons/Code_snippets/File_I_O#Writing_to_a_file
 function* getFileData(file) {
   if (typeof file === "string") {
     file = new FileUtils.File(file);
   }
   let def = promise.defer();
 
-  NetUtil.asyncFetch(file, function(inputStream, status) {
+  NetUtil.asyncFetch2(file, function(inputStream, status) {
     if (!Components.isSuccessCode(status)) {
       info("ERROR READING TEMP FILE", status);
     }
 
     // Detect if an empty file is loaded
     try {
       inputStream.available();
     } catch(e) {
       def.resolve("");
       return;
     }
 
     var data = NetUtil.readInputStreamToString(inputStream, inputStream.available());
     def.resolve(data);
-  });
+  },
+  null,      // aLoadingNode
+  Services.scriptSecurityManager.getSystemPrincipal(),
+  null,      // aTriggeringPrincipal
+  Ci.nsILoadInfo.SEC_NORMAL,
+  Ci.nsIContentPolicy.TYPE_OTHER);
 
   return def.promise;
 }
 
 function onceEditorCreated(projecteditor) {
   let def = promise.defer();
   projecteditor.once("onEditorCreated", (editor) => {
     def.resolve(editor);
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -1128,22 +1128,29 @@ var Scratchpad = {
    *        Optional function you want to call when file load completes. It will
    *        get the following arguments:
    *        1) the nsresult status code for the import operation.
    *        2) the data that was read from the file, if any.
    */
   importFromFile: function SP_importFromFile(aFile, aSilentError, aCallback)
   {
     // Prevent file type detection.
-    let channel = NetUtil.newChannel(aFile);
+    let channel = NetUtil.newChannel2(aFile,
+                                      null,
+                                      null,
+                                      window.document,
+                                      null, // aLoadingPrincipal
+                                      null, // aTriggeringPrincipal
+                                      Ci.nsILoadInfo.SEC_NORMAL,
+                                      Ci.nsIContentPolicy.TYPE_OTHER);
     channel.contentType = "application/javascript";
 
     this.notificationBox.removeAllNotifications(false);
 
-    NetUtil.asyncFetch(channel, (aInputStream, aStatus) => {
+    NetUtil.asyncFetch2(channel, (aInputStream, aStatus) => {
       let content = null;
 
       if (Components.isSuccessCode(aStatus)) {
         let charsets = this._getApplicableCharsets();
         content = NetUtil.readInputStreamToString(aInputStream,
                                                   aInputStream.available());
         content = this._getUnicodeContent(content, charsets);
         if (!content) {
--- a/browser/devtools/scratchpad/test/browser_scratchpad_confirm_close.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_confirm_close.js
@@ -209,20 +209,27 @@ function writeFile(file, content, callba
   converter.charset = "UTF-8";
   let fileContentStream = converter.convertToInputStream(content);
 
   NetUtil.asyncCopy(fileContentStream, fout, callback);
 }
 
 function readFile(file, callback)
 {
-  let channel = NetUtil.newChannel(file);
+  let channel = NetUtil.newChannel2(file,
+                                    null,
+                                    null,
+                                    null,      // aLoadingNode
+                                    Services.scriptSecurityManager.getSystemPrincipal(),
+                                    null,      // aTriggeringPrincipal
+                                    Ci.nsILoadInfo.SEC_NORMAL,
+                                    Ci.nsIContentPolicy.TYPE_OTHER);
   channel.contentType = "application/javascript";
 
-  NetUtil.asyncFetch(channel, function(inputStream, status) {
+  NetUtil.asyncFetch2(channel, function(inputStream, status) {
     ok(Components.isSuccessCode(status),
        "file was read successfully");
 
     let content = NetUtil.readInputStreamToString(inputStream,
                                                   inputStream.available());
     callback(content);
   });
 }
--- a/browser/devtools/scratchpad/test/browser_scratchpad_files.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_files.js
@@ -86,21 +86,28 @@ function fileExported(aStatus)
                           fileExported2);
 
   gScratchpadWindow.confirm = oldConfirm;
 
   ok(askedConfirmation, "exportToFile() asked for overwrite confirmation");
 
   gFileContent = oldContent;
 
-  let channel = NetUtil.newChannel(gFile);
+  let channel = NetUtil.newChannel2(gFile,
+                                    null,
+                                    null,
+                                    null,      // aLoadingNode
+                                    Services.scriptSecurityManager.getSystemPrincipal(),
+                                    null,      // aTriggeringPrincipal
+                                    Ci.nsILoadInfo.SEC_NORMAL,
+                                    Ci.nsIContentPolicy.TYPE_OTHER);
   channel.contentType = "application/javascript";
 
   // Read back the temporary file.
-  NetUtil.asyncFetch(channel, fileRead);
+  NetUtil.asyncFetch2(channel, fileRead);
 }
 
 function fileExported2()
 {
   ok(false, "exportToFile() did not cancel file overwrite");
 }
 
 function fileRead(aInputStream, aStatus)
--- a/browser/devtools/shared/AppCacheUtils.jsm
+++ b/browser/devtools/shared/AppCacheUtils.jsm
@@ -180,17 +180,24 @@ AppCacheUtils.prototype = {
     return deferred.promise;
   },
 
   _getURIInfo: function ACU__getURIInfo(uri) {
     let inputStream = Cc["@mozilla.org/scriptableinputstream;1"]
                         .createInstance(Ci.nsIScriptableInputStream);
     let deferred = promise.defer();
     let buffer = "";
-    let channel = Services.io.newChannel(uri, null, null);
+    let channel = Services.io.newChannel2(uri,
+                                          null,
+                                          null,
+                                          null,      // aLoadingNode
+                                          Services.scriptSecurityManager.getSystemPrincipal(),
+                                          null,      // aTriggeringPrincipal
+                                          Ci.nsILoadInfo.SEC_NORMAL,
+                                          Ci.nsIContentPolicy.TYPE_OTHER);
 
     // Avoid the cache:
     channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
     channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
 
     channel.asyncOpen({
       onStartRequest: function (request, context) {
         // This empty method is needed in order for onDataAvailable to be
--- a/browser/devtools/sourceeditor/test/head.js
+++ b/browser/devtools/sourceeditor/test/head.js
@@ -82,17 +82,24 @@ function limit(source, [line, ch]) {
     return list[0].slice(0, ch);
   return [...list.slice(0, line - 1), list[line - 1].slice(0, ch)].join("\n");
 }
 
 function read(url) {
   let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
     .getService(Ci.nsIScriptableInputStream);
 
-  let channel = Services.io.newChannel(url, null, null);
+  let channel = Services.io.newChannel2(url,
+                                        null,
+                                        null,
+                                        null,      // aLoadingNode
+                                        Services.scriptSecurityManager.getSystemPrincipal(),
+                                        null,      // aTriggeringPrincipal
+                                        Ci.nsILoadInfo.SEC_NORMAL,
+                                        Ci.nsIContentPolicy.TYPE_OTHER);
   let input = channel.open();
   scriptableStream.init(input);
 
   let data = "";
   while (input.available()) {
     data = data.concat(scriptableStream.read(input.available()));
   }
   scriptableStream.close();
--- a/browser/devtools/styleeditor/StyleEditorUI.jsm
+++ b/browser/devtools/styleeditor/StyleEditorUI.jsm
@@ -334,29 +334,33 @@ StyleEditorUI.prototype = {
    *        Optional parent window for the file picker.
    */
   _importFromFile: function(file, parentWindow) {
     let onFileSelected = (file) => {
       if (!file) {
         // nothing selected
         return;
       }
-      NetUtil.asyncFetch(file, (stream, status) => {
+      NetUtil.asyncFetch2(file, (stream, status) => {
         if (!Components.isSuccessCode(status)) {
           this.emit("error", { key: LOAD_ERROR });
           return;
         }
         let source = NetUtil.readInputStreamToString(stream, stream.available());
         stream.close();
 
         this._debuggee.addStyleSheet(source).then((styleSheet) => {
           this._onStyleSheetCreated(styleSheet, file);
         });
-      });
-
+      },
+      this._window.document,
+      null,  // aLoadingPrincipal
+      null,  // aTriggeringPrincipal
+      Ci.nsILoadInfo.SEC_NORMAL,
+      Ci.nsIContentPolicy.TYPE_OTHER);
     };
 
     showFilePicker(file, false, parentWindow, onFileSelected);
   },
 
 
   /**
    * When a new or imported stylesheet has been added to the document.
--- a/browser/devtools/styleeditor/test/browser_styleeditor_filesave.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_filesave.js
@@ -60,17 +60,24 @@ function copy(aSrcChromeURL, aDestFileNa
   write(read(aSrcChromeURL), destFile, aCallback);
 }
 
 function read(aSrcChromeURL)
 {
   let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
     .getService(Ci.nsIScriptableInputStream);
 
-  let channel = Services.io.newChannel(aSrcChromeURL, null, null);
+  let channel = Services.io.newChannel2(aSrcChromeURL,
+                                        null,
+                                        null,
+                                        null,      // aLoadingNode
+                                        Services.scriptSecurityManager.getSystemPrincipal(),
+                                        null,      // aTriggeringPrincipal
+                                        Ci.nsILoadInfo.SEC_NORMAL,
+                                        Ci.nsIContentPolicy.TYPE_OTHER);
   let input = channel.open();
   scriptableStream.init(input);
 
   let data = "";
   while (input.available()) {
     data = data.concat(scriptableStream.read(input.available()));
   }
   scriptableStream.close();
--- a/browser/devtools/styleeditor/test/browser_styleeditor_sourcemap_watching.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_sourcemap_watching.js
@@ -145,17 +145,24 @@ function copy(aSrcChromeURL, aDestFilePa
   return write(read(aSrcChromeURL), destFile);
 }
 
 function read(aSrcChromeURL)
 {
   let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
     .getService(Ci.nsIScriptableInputStream);
 
-  let channel = Services.io.newChannel(aSrcChromeURL, null, null);
+  let channel = Services.io.newChannel2(aSrcChromeURL,
+                                        null,
+                                        null,
+                                        null,      // aLoadingNode
+                                        Services.scriptSecurityManager.getSystemPrincipal(),
+                                        null,      // aTriggeringPrincipal
+                                        Ci.nsILoadInfo.SEC_NORMAL,
+                                        Ci.nsIContentPolicy.TYPE_OTHER);
   let input = channel.open();
   scriptableStream.init(input);
 
   let data = "";
   while (input.available()) {
     data = data.concat(scriptableStream.read(input.available()));
   }
   scriptableStream.close();
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -43,16 +43,17 @@ SEARCH_PATHS = [
     'layout/tools/reftest',
     'other-licenses/ply',
     'xpcom/idl-parser',
     'testing',
     'testing/taskcluster',
     'testing/xpcshell',
     'testing/web-platform',
     'testing/web-platform/harness',
+    'testing/marionette/client',
     'testing/marionette/client/marionette',
     'testing/marionette/transport',
     'testing/mozbase/mozcrash',
     'testing/mozbase/mozdebug',
     'testing/mozbase/mozdevice',
     'testing/mozbase/mozfile',
     'testing/mozbase/mozhttpd',
     'testing/mozbase/mozlog',
--- a/build/moz-automation.mk
+++ b/build/moz-automation.mk
@@ -76,16 +76,20 @@ automation/upload: automation/package-te
 automation/upload: automation/buildsymbols
 automation/upload: automation/update-packaging
 
 # automation/{pretty-}package should depend on build (which is implicit due to
 # the way client.mk invokes automation/build), but buildsymbols changes the
 # binaries/libs, and that's what we package/test.
 automation/pretty-package: automation/buildsymbols
 
+# The installer and packager both run stage-package, and may conflict
+# with each other.
+automation/installer: automation/package
+
 # The 'pretty' versions of targets run before the regular ones to avoid
 # conflicts in writing to the same files.
 automation/installer: automation/pretty-installer
 automation/package: automation/pretty-package
 automation/package-tests: automation/pretty-package-tests
 automation/l10n-check: automation/pretty-l10n-check
 automation/update-packaging: automation/pretty-update-packaging
 
--- a/config/external/nss/nss.def
+++ b/config/external/nss/nss.def
@@ -672,12 +672,12 @@ SSL_VersionRangeSet
 SSL_VersionRangeSetDefault
 UTIL_SetForkState
 VFY_Begin
 VFY_CreateContext
 VFY_DestroyContext
 VFY_End
 VFY_Update
 VFY_VerifyData
-VFY_VerifyDataDirect
 VFY_VerifyDataWithAlgorithmID
+VFY_VerifyDigestDirect
 _SGN_VerifyPKCS1DigestInfo
 PK11_PQG_ParamGenV2
--- a/config/gcc-stl-wrapper.template.h
+++ b/config/gcc-stl-wrapper.template.h
@@ -5,17 +5,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/. */
 
 #ifndef mozilla_${HEADER}_h
 #define mozilla_${HEADER}_h
 
 // For some reason, Apple's GCC refuses to honor -fno-exceptions when
 // compiling ObjC.
-#if __EXCEPTIONS && !(__OBJC__ && __GNUC__ && XP_IOS)
+#if defined(__EXCEPTIONS) && __EXCEPTIONS && !(__OBJC__ && __GNUC__ && XP_IOS)
 #  error "STL code can only be used with -fno-exceptions"
 #endif
 
 // Silence "warning: #include_next is a GCC extension"
 #pragma GCC system_header
 
 #ifdef _WIN32
 // Suppress windef.h min and max macros - they make std::min/max not compile.
--- a/configure.in
+++ b/configure.in
@@ -617,18 +617,18 @@ See https://developer.mozilla.org/en/Win
                                 [std::vector<int> v; return v.at(1);],
                                 ac_cv_have_dllimport_exception_bug="no",
                                 ac_cv_have_dllimport_exception_bug="yes")
                     CXXFLAGS="$_SAVE_CXXFLAGS"
                     AC_LANG_RESTORE
                 ])
             if test "$ac_cv_have_dllimport_exception_bug" = "no"; then
                 WRAP_STL_INCLUDES=1
-                MOZ_MSVC_STL_WRAP__Throw=1
-                AC_DEFINE(MOZ_MSVC_STL_WRAP__Throw)
+                MOZ_MSVC_STL_WRAP_Throw=1
+                AC_DEFINE(MOZ_MSVC_STL_WRAP_Throw)
             fi
         else
             AC_CACHE_CHECK(for overridable _RAISE,
                            ac_cv_have__RAISE,
                 [
                     AC_LANG_SAVE
                     AC_LANG_CPLUSPLUS
                     _SAVE_CXXFLAGS="$CXXFLAGS"
@@ -641,18 +641,18 @@ See https://developer.mozilla.org/en/Win
                                    [std::vector<int> v; return v.at(1);],
                                    ac_cv_have__RAISE="no",
                                    ac_cv_have__RAISE="yes")
                     CXXFLAGS="$_SAVE_CXXFLAGS"
                     AC_LANG_RESTORE
                 ])
             if test "$ac_cv_have__RAISE" = "yes"; then
                 WRAP_STL_INCLUDES=1
-                MOZ_MSVC_STL_WRAP__RAISE=1
-                AC_DEFINE(MOZ_MSVC_STL_WRAP__RAISE)
+                MOZ_MSVC_STL_WRAP_RAISE=1
+                AC_DEFINE(MOZ_MSVC_STL_WRAP_RAISE)
             else
                 AC_MSG_ERROR([Gecko exception wrapping doesn't understand your your MSVC/SDK.  Please file a bug describing this error and your build configuration.])
             fi
         fi
 
         if test "$WRAP_STL_INCLUDES" = "1"; then
             STL_FLAGS='-I$(DIST)/stl_wrappers'
         fi
@@ -762,18 +762,18 @@ AC_SUBST(GNU_AS)
 AC_SUBST(GNU_LD)
 AC_SUBST(GNU_CC)
 AC_SUBST(GNU_CXX)
 AC_SUBST(INTEL_CC)
 AC_SUBST(INTEL_CXX)
 
 AC_SUBST(STL_FLAGS)
 AC_SUBST(WRAP_STL_INCLUDES)
-AC_SUBST(MOZ_MSVC_STL_WRAP__Throw)
-AC_SUBST(MOZ_MSVC_STL_WRAP__RAISE)
+AC_SUBST(MOZ_MSVC_STL_WRAP_Throw)
+AC_SUBST(MOZ_MSVC_STL_WRAP_RAISE)
 
 dnl ========================================================
 dnl Checks for programs.
 dnl ========================================================
 AC_PROG_INSTALL
 AC_PROG_LN_S
 
 AC_MSG_CHECKING([for minimum required perl version >= $PERL_VERSION])
@@ -7278,17 +7278,17 @@ if test -f "${srcdir}/${MOZ_BUILD_APP}/c
   rm -f $tmpscript
 fi
 
 dnl We need to wrap dlopen and related functions on Android because we use
 dnl our own linker.
 if test "$OS_TARGET" = Android; then
     MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=PR_GetEnv,--wrap=PR_SetEnv"
     if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_NUWA_PROCESS"; then
-        MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=pthread_create,--wrap=epoll_wait,--wrap=poll,--wrap=pthread_cond_timedwait,--wrap=pthread_cond_wait,--wrap=epoll_create,--wrap=epoll_ctl,--wrap=close,--wrap=pthread_key_create,--wrap=pthread_key_delete,--wrap=socketpair,--wrap=pthread_self,--wrap=pthread_mutex_lock,--wrap=pthread_join,--wrap=pipe,--wrap=pipe2"
+        MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=pthread_create,--wrap=epoll_wait,--wrap=poll,--wrap=pthread_cond_timedwait,--wrap=pthread_cond_wait,--wrap=epoll_create,--wrap=epoll_ctl,--wrap=close,--wrap=pthread_key_create,--wrap=pthread_key_delete,--wrap=socketpair,--wrap=pthread_self,--wrap=pthread_mutex_lock,--wrap=pthread_mutex_trylock,--wrap=pthread_join,--wrap=pipe,--wrap=pipe2"
     fi
     if test "$MOZ_WIDGET_TOOLKIT" = android; then
         MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=getaddrinfo,--wrap=freeaddrinfo,--wrap=gai_strerror"
     fi
 fi
 
 AC_SUBST_LIST(MOZ_GLUE_WRAP_LDFLAGS)
 export MOZ_GLUE_WRAP_LDFLAGS
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -13749,29 +13749,16 @@ nsDocShell::SetOpener(nsITabParent* aOpe
 
 nsITabParent*
 nsDocShell::GetOpener()
 {
   nsCOMPtr<nsITabParent> opener(do_QueryReferent(mOpener));
   return opener;
 }
 
-void
-nsDocShell::SetOpenedRemote(nsITabParent* aOpenedRemote)
-{
-  mOpenedRemote = do_GetWeakReference(aOpenedRemote);
-}
-
-nsITabParent*
-nsDocShell::GetOpenedRemote()
-{
-  nsCOMPtr<nsITabParent> openedRemote(do_QueryReferent(mOpenedRemote));
-  return openedRemote;
-}
-
 URLSearchParams*
 nsDocShell::GetURLSearchParams()
 {
   return mURLSearchParams;
 }
 
 void
 nsDocShell::NotifyJSRunToCompletionStart()
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -958,17 +958,16 @@ private:
     nsCString         mParentCharset;
     int32_t           mParentCharsetSource;
     nsCOMPtr<nsIPrincipal> mParentCharsetPrincipal;
     nsTObserverArray<nsWeakPtr> mPrivacyObservers;
     nsTObserverArray<nsWeakPtr> mReflowObservers;
     nsTObserverArray<nsWeakPtr> mScrollObservers;
     nsCString         mOriginalUriString;
     nsWeakPtr mOpener;
-    nsWeakPtr mOpenedRemote;
 
     // A depth count of how many times NotifyRunToCompletionStart
     // has been called without a matching NotifyRunToCompletionStop.
     uint32_t          mJSRunToCompletionDepth;
 
     // True if recording profiles.
     bool mProfileTimelineRecording;
 
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -49,17 +49,17 @@ interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 interface nsIScrollObserver;
 interface nsITabParent;
  
 typedef unsigned long nsLoadFlags;
 
-[scriptable, builtinclass, uuid(888fcf04-a69b-11e4-8d33-6fbb72d2eb03)]
+[scriptable, builtinclass, uuid(f84b1ae4-2f78-4bad-b36a-6a8516ee6e40)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -1020,23 +1020,16 @@ interface nsIDocShell : nsIDocShellTreeI
    * Regarding setOpener / getOpener - We can't use XPIDL's "attribute"
    * for notxpcom, so we're relegated to using explicit gets / sets. This
    * should be fine, considering that these methods should only ever be
    * called from native code.
    */
   [noscript,notxpcom,nostdcall] void setOpener(in nsITabParent aOpener);
   [noscript,notxpcom,nostdcall] nsITabParent getOpener();
 
-  /**
-   * See the documentation for setOpener and getOpener about why we
-   * don't use attribute here instead.
-   */
-  [noscript,notxpcom,nostdcall] void setOpenedRemote(in nsITabParent aOpenedRemote);
-  [noscript,notxpcom,nostdcall] nsITabParent getOpenedRemote();
-
   // URLSearchParams for the window.location is owned by the docShell.
   [noscript,notxpcom] URLSearchParams getURLSearchParams();
 
   /**
    * Notify DocShell when the browser is about to start executing JS, and after
    * that execution has stopped.  This only occurs when the Timeline devtool
    * is collecting information.
    */
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -987,31 +987,35 @@ Element::CreateShadowRoot(ErrorResult& a
   protoBinding->SetInheritsStyle(false);
 
   // Calling SetPrototypeBinding takes ownership of protoBinding.
   docInfo->SetPrototypeBinding(NS_LITERAL_CSTRING("shadowroot"), protoBinding);
 
   nsRefPtr<ShadowRoot> shadowRoot = new ShadowRoot(this, nodeInfo.forget(),
                                                    protoBinding);
 
+  shadowRoot->SetIsComposedDocParticipant(IsInComposedDoc());
+
   // Replace the old ShadowRoot with the new one and let the old
   // ShadowRoot know about the younger ShadowRoot because the old
   // ShadowRoot is projected into the younger ShadowRoot's shadow
   // insertion point (if it exists).
   ShadowRoot* olderShadow = GetShadowRoot();
   SetShadowRoot(shadowRoot);
   if (olderShadow) {
     olderShadow->SetYoungerShadow(shadowRoot);
 
     // Unbind children of older shadow root because they
     // are no longer in the composed tree.
     for (nsIContent* child = olderShadow->GetFirstChild(); child;
          child = child->GetNextSibling()) {
       child->UnbindFromTree(true, false);
     }
+
+    olderShadow->SetIsComposedDocParticipant(false);
   }
 
   // xblBinding takes ownership of docInfo.
   nsRefPtr<nsXBLBinding> xblBinding = new nsXBLBinding(shadowRoot, protoBinding);
   shadowRoot->SetAssociatedBinding(xblBinding);
   xblBinding->SetBoundElement(this);
 
   SetXBLBinding(xblBinding);
@@ -1464,23 +1468,16 @@ Element::BindToTree(nsIDocument* aDocume
 
     // We no longer need to track the subtree pointer (and in fact we'll assert
     // if we do this any later).
     ClearSubtreeRootPointer();
 
     // Being added to a document.
     SetInDocument();
 
-    // Attached callback must be enqueued whenever custom element is inserted into a
-    // document and this document has a browsing context.
-    if (GetCustomElementData() && aDocument->GetDocShell()) {
-      // Enqueue an attached callback for the custom element.
-      aDocument->EnqueueLifecycleCallback(nsIDocument::eAttached, this);
-    }
-
     // Unset this flag since we now really are in a document.
     UnsetFlags(NODE_FORCE_XBL_BINDINGS |
                // And clear the lazy frame construction bits.
                NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES |
                // And the restyle bits
                ELEMENT_ALL_RESTYLE_FLAGS);
   } else if (IsInShadowTree()) {
     // We're not in a document, but we did get inserted into a shadow tree.
@@ -1493,16 +1490,26 @@ Element::BindToTree(nsIDocument* aDocume
                NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES |
                ELEMENT_ALL_RESTYLE_FLAGS);
   } else {
     // If we're not in the doc and not in a shadow tree,
     // update our subtree pointer.
     SetSubtreeRootPointer(aParent->SubtreeRoot());
   }
 
+  nsIDocument* composedDoc = GetComposedDoc();
+  if (composedDoc) {
+    // Attached callback must be enqueued whenever custom element is inserted into a
+    // document and this document has a browsing context.
+    if (GetCustomElementData() && composedDoc->GetDocShell()) {
+      // Enqueue an attached callback for the custom element.
+      composedDoc->EnqueueLifecycleCallback(nsIDocument::eAttached, this);
+    }
+  }
+
   // Propagate scoped style sheet tracking bit.
   if (mParent->IsContent()) {
     nsIContent* parent;
     ShadowRoot* shadowRootParent = ShadowRoot::FromNode(mParent);
     if (shadowRootParent) {
       parent = shadowRootParent->GetHost();
     } else {
       parent = mParent->AsContent();
@@ -1573,16 +1580,17 @@ Element::BindToTree(nsIDocument* aDocume
     if (sheet) {
       mAttrsAndChildren.SetMappedAttrStyleSheet(sheet);
     }
   }
 
   // Call BindToTree on shadow root children.
   ShadowRoot* shadowRoot = GetShadowRoot();
   if (shadowRoot) {
+    shadowRoot->SetIsComposedDocParticipant(IsInComposedDoc());
     for (nsIContent* child = shadowRoot->GetFirstChild(); child;
          child = child->GetNextSibling()) {
       rv = child->BindToTree(nullptr, shadowRoot,
                              shadowRoot->GetBindingParent(),
                              aCompileEventHandlers);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
@@ -1630,18 +1638,17 @@ Element::UnbindFromTree(bool aDeep, bool
   NS_PRECONDITION(aDeep || (!GetUncomposedDoc() && !GetBindingParent()),
                   "Shallow unbind won't clear document and binding parent on "
                   "kids!");
 
   RemoveFromIdTable();
 
   // Make sure to unbind this node before doing the kids
   nsIDocument* document =
-    HasFlag(NODE_FORCE_XBL_BINDINGS) || IsInShadowTree() ?
-      OwnerDoc() : GetUncomposedDoc();
+    HasFlag(NODE_FORCE_XBL_BINDINGS) ? OwnerDoc() : GetComposedDoc();
 
   if (aNullParent) {
     if (IsFullScreenAncestor()) {
       // The element being removed is an ancestor of the full-screen element,
       // exit full-screen state.
       nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                       NS_LITERAL_CSTRING("DOM"), OwnerDoc(),
                                       nsContentUtils::eDOM_PROPERTIES,
@@ -1751,16 +1758,18 @@ Element::UnbindFromTree(bool aDeep, bool
 
   // Unbind children of shadow root.
   ShadowRoot* shadowRoot = GetShadowRoot();
   if (shadowRoot) {
     for (nsIContent* child = shadowRoot->GetFirstChild(); child;
          child = child->GetNextSibling()) {
       child->UnbindFromTree(true, false);
     }
+
+    shadowRoot->SetIsComposedDocParticipant(false);
   }
 }
 
 nsICSSDeclaration*
 Element::GetSMILOverrideStyle()
 {
   Element::nsDOMSlots *slots = DOMSlots();
 
--- a/dom/base/File.cpp
+++ b/dom/base/File.cpp
@@ -637,16 +637,20 @@ File::Constructor(const GlobalObject& aG
 
   nsRefPtr<MultipartFileImpl> impl = new MultipartFileImpl(EmptyString());
   impl->InitializeChromeFile(aData, aBag, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   MOZ_ASSERT(impl->IsFile());
 
+  if (aBag.mLastModified.WasPassed()) {
+    impl->SetLastModified(aBag.mLastModified.Value());
+  }
+
   nsRefPtr<File> domFile = new File(aGlobal.GetAsSupports(), impl);
   return domFile.forget();
 }
 
 /* static */ already_AddRefed<File>
 File::Constructor(const GlobalObject& aGlobal,
                   nsIFile* aData,
                   const ChromeFilePropertyBag& aBag,
@@ -662,16 +666,20 @@ File::Constructor(const GlobalObject& aG
 
   nsRefPtr<MultipartFileImpl> impl = new MultipartFileImpl(EmptyString());
   impl->InitializeChromeFile(window, aData, aBag, true, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   MOZ_ASSERT(impl->IsFile());
 
+  if (aBag.mLastModified.WasPassed()) {
+    impl->SetLastModified(aBag.mLastModified.Value());
+  }
+
   nsRefPtr<File> domFile = new File(aGlobal.GetAsSupports(), impl);
   return domFile.forget();
 }
 
 /* static */ already_AddRefed<File>
 File::Constructor(const GlobalObject& aGlobal,
                   const nsAString& aData,
                   const ChromeFilePropertyBag& aBag,
@@ -686,16 +694,20 @@ File::Constructor(const GlobalObject& aG
 
   nsRefPtr<MultipartFileImpl> impl = new MultipartFileImpl(EmptyString());
   impl->InitializeChromeFile(window, aData, aBag, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   MOZ_ASSERT(impl->IsFile());
 
+  if (aBag.mLastModified.WasPassed()) {
+    impl->SetLastModified(aBag.mLastModified.Value());
+  }
+
   nsRefPtr<File> domFile = new File(aGlobal.GetAsSupports(), impl);
   return domFile.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////
 // mozilla::dom::FileImpl implementation
 
 already_AddRefed<FileImpl>
--- a/dom/base/MultipartFileImpl.cpp
+++ b/dom/base/MultipartFileImpl.cpp
@@ -184,41 +184,54 @@ MultipartFileImpl::InitializeBlob(
 
 void
 MultipartFileImpl::SetLengthAndModifiedDate()
 {
   MOZ_ASSERT(mLength == UINT64_MAX);
   MOZ_ASSERT(mLastModificationDate == UINT64_MAX);
 
   uint64_t totalLength = 0;
+  uint64_t lastModified = 0;
+  bool lastModifiedSet = false;
 
   for (uint32_t index = 0, count = mBlobImpls.Length(); index < count; index++) {
     nsRefPtr<FileImpl>& blob = mBlobImpls[index];
 
 #ifdef DEBUG
     MOZ_ASSERT(!blob->IsSizeUnknown());
     MOZ_ASSERT(!blob->IsDateUnknown());
 #endif
 
     ErrorResult error;
     uint64_t subBlobLength = blob->GetSize(error);
     MOZ_ALWAYS_TRUE(!error.Failed());
 
     MOZ_ASSERT(UINT64_MAX - subBlobLength >= totalLength);
     totalLength += subBlobLength;
+
+    if (blob->IsFile()) {
+      uint64_t partLastModified = blob->GetLastModified(error);
+      MOZ_ALWAYS_TRUE(!error.Failed());
+
+      if (lastModified < partLastModified) {
+        lastModified = partLastModified;
+        lastModifiedSet = true;
+      }
+    }
   }
 
   mLength = totalLength;
 
   if (mIsFile) {
     // We cannot use PR_Now() because bug 493756 and, for this reason:
     //   var x = new Date(); var f = new File(...);
     //   x.getTime() < f.dateModified.getTime()
     // could fail.
-    mLastModificationDate = JS_Now();
+    mLastModificationDate =
+      lastModifiedSet ? lastModified * PR_USEC_PER_MSEC : JS_Now();
   }
 }
 
 void
 MultipartFileImpl::GetMozFullPathInternal(nsAString& aFilename,
                                           ErrorResult& aRv)
 {
   if (!mIsFromNsIFile || mBlobImpls.Length() == 0) {
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -62,17 +62,17 @@ NS_INTERFACE_MAP_END_INHERITING(Document
 NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment)
 NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment)
 
 ShadowRoot::ShadowRoot(nsIContent* aContent,
                        already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                        nsXBLPrototypeBinding* aProtoBinding)
   : DocumentFragment(aNodeInfo), mPoolHost(aContent),
     mProtoBinding(aProtoBinding), mShadowElement(nullptr),
-    mInsertionPointChanged(false)
+    mInsertionPointChanged(false), mIsComposedDocParticipant(false)
 {
   SetHost(aContent);
 
   // Nodes in a shadow tree should never store a value
   // in the subtree root pointer, nodes in the shadow tree
   // track the subtree root using GetContainingShadow().
   ClearSubtreeRootPointer();
 
--- a/dom/base/ShadowRoot.h
+++ b/dom/base/ShadowRoot.h
@@ -122,16 +122,22 @@ public:
                            const nsAString& aLocalName);
   already_AddRefed<nsContentList>
     GetElementsByClassName(const nsAString& aClasses);
   void GetInnerHTML(nsAString& aInnerHTML);
   void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError);
   Element* Host();
   ShadowRoot* GetOlderShadowRoot() { return mOlderShadow; }
   void StyleSheetChanged();
+
+  bool IsComposedDocParticipant() { return mIsComposedDocParticipant; }
+  void SetIsComposedDocParticipant(bool aIsComposedDocParticipant)
+  {
+    mIsComposedDocParticipant = aIsComposedDocParticipant;
+  }
 protected:
   virtual ~ShadowRoot();
 
   // The pool host is the parent of the nodes that will be distributed
   // into the insertion points in this ShadowRoot. See |ChangeShadowRoot|.
   nsCOMPtr<nsIContent> mPoolHost;
 
   // An array of content insertion points that are a descendant of the ShadowRoot
@@ -166,16 +172,22 @@ protected:
   // this ShadowRoot was created.
   nsRefPtr<ShadowRoot> mYoungerShadow;
 
   // A boolean that indicates that an insertion point was added or removed
   // from this ShadowRoot and that the nodes need to be redistributed into
   // the insertion points. After this flag is set, nodes will be distributed
   // on the next mutation event.
   bool mInsertionPointChanged;
+
+  // Flag to indicate whether the descendants of this shadow root are part of the
+  // composed document. Ideally, we would use a node flag on nodes to
+  // mark whether it is in the composed document, but we have run out of flags
+  // so instead we track it here.
+  bool mIsComposedDocParticipant;
 };
 
 class ShadowRootStyleSheetList : public StyleSheetList
 {
 public:
   explicit ShadowRootStyleSheetList(ShadowRoot* aShadowRoot);
 
   NS_DECL_ISUPPORTS_INHERITED
--- a/dom/base/nsContentList.cpp
+++ b/dom/base/nsContentList.cpp
@@ -219,17 +219,17 @@ NS_GetContentList(nsINode* aRootNode,
     PL_DHashTableInit(&gContentListHashTable, &hash_table_ops,
                       sizeof(ContentListHashEntry));
   }
 
   ContentListHashEntry *entry = nullptr;
   // First we look in our hashtable.  Then we create a content list if needed
   if (gContentListHashTable.IsInitialized()) {
     entry = static_cast<ContentListHashEntry *>
-      (PL_DHashTableAdd(&gContentListHashTable, &hashKey, fallible));
+                       (PL_DHashTableAdd(&gContentListHashTable, &hashKey));
     if (entry)
       list = entry->mContentList;
   }
 
   if (!list) {
     // We need to create a ContentList and add it to our new entry, if
     // we have an entry
     nsCOMPtr<nsIAtom> xmlAtom = do_GetAtom(aTagname);
@@ -327,17 +327,18 @@ GetFuncStringContentList(nsINode* aRootN
   }
 
   FuncStringContentListHashEntry *entry = nullptr;
   // First we look in our hashtable.  Then we create a content list if needed
   if (gFuncStringContentListHashTable.IsInitialized()) {
     nsFuncStringCacheKey hashKey(aRootNode, aFunc, aString);
 
     entry = static_cast<FuncStringContentListHashEntry *>
-      (PL_DHashTableAdd(&gFuncStringContentListHashTable, &hashKey, fallible));
+                       (PL_DHashTableAdd(&gFuncStringContentListHashTable,
+                                         &hashKey));
     if (entry) {
       list = entry->mContentList;
 #ifdef DEBUG
       MOZ_ASSERT_IF(list, list->mType == ListType::sType);
 #endif
     }
   }
 
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -3959,17 +3959,17 @@ nsContentUtils::GetListenerManagerForNod
     // We're already shut down, don't bother creating an event listener
     // manager.
 
     return nullptr;
   }
 
   EventListenerManagerMapEntry *entry =
     static_cast<EventListenerManagerMapEntry *>
-      (PL_DHashTableAdd(&sEventListenerManagersHash, aNode, fallible));
+               (PL_DHashTableAdd(&sEventListenerManagersHash, aNode));
 
   if (!entry) {
     return nullptr;
   }
 
   if (!entry->mListenerManager) {
     entry->mListenerManager = new EventListenerManager(aNode);
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -453,17 +453,17 @@ CustomElementCallback::Call()
 
       // The callback hasn't actually been invoked yet, but we need to flip
       // this now in order to enqueue the attached callback. This is a spec
       // bug (w3c bug 27437).
       mOwnerData->mCreatedCallbackInvoked = true;
 
       // If ELEMENT is in a document and this document has a browsing context,
       // enqueue attached callback for ELEMENT.
-      nsIDocument* document = mThisObject->GetUncomposedDoc();
+      nsIDocument* document = mThisObject->GetComposedDoc();
       if (document && document->GetDocShell()) {
         document->EnqueueLifecycleCallback(nsIDocument::eAttached, mThisObject);
       }
 
       static_cast<LifecycleCreatedCallback *>(mCallback.get())->Call(mThisObject, rv);
       mOwnerData->mElementIsBeingCreated = false;
       break;
     }
@@ -1710,18 +1710,18 @@ nsDocument::~nsDocument()
     mAnimationController->Disconnect();
   }
 
   mParentDocument = nullptr;
 
   // Kill the subdocument map, doing this will release its strong
   // references, if any.
   if (mSubDocuments) {
-    PL_DHashTableFinish(mSubDocuments);
-    delete mSubDocuments;
+    PL_DHashTableDestroy(mSubDocuments);
+
     mSubDocuments = nullptr;
   }
 
   // Destroy link map now so we don't waste time removing
   // links one by one
   DestroyElementMaps();
 
   nsAutoScriptBlocker scriptBlocker;
@@ -2121,18 +2121,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets)
 
   if (tmp->mStyleSheetSetList) {
     tmp->mStyleSheetSetList->Disconnect();
     tmp->mStyleSheetSetList = nullptr;
   }
 
   if (tmp->mSubDocuments) {
-    PL_DHashTableFinish(tmp->mSubDocuments);
-    delete tmp->mSubDocuments;
+    PL_DHashTableDestroy(tmp->mSubDocuments);
     tmp->mSubDocuments = nullptr;
   }
 
   tmp->mFrameRequestCallbacks.Clear();
 
   tmp->mRadioGroups.Clear();
 
   // nsDocument has a pretty complex destructor, so we're going to
@@ -2327,18 +2326,18 @@ nsDocument::ResetToURI(nsIURI *aURI, nsI
 
   mSecurityInfo = nullptr;
 
   mDocumentLoadGroup = nullptr;
 
   // Delete references to sub-documents and kill the subdocument map,
   // if any. It holds strong references
   if (mSubDocuments) {
-    PL_DHashTableFinish(mSubDocuments);
-    delete mSubDocuments;
+    PL_DHashTableDestroy(mSubDocuments);
+
     mSubDocuments = nullptr;
   }
 
   // Destroy link map now so we don't waste time removing
   // links one by one
   DestroyElementMaps();
 
   bool oldVal = mInUnlinkOrDeletion;
@@ -3997,23 +3996,26 @@ nsDocument::SetSubDocumentFor(Element* a
       {
         PL_DHashVoidPtrKeyStub,
         PL_DHashMatchEntryStub,
         PL_DHashMoveEntryStub,
         SubDocClearEntry,
         SubDocInitEntry
       };
 
-      mSubDocuments = new PLDHashTable();
-      PL_DHashTableInit(mSubDocuments, &hash_table_ops, sizeof(SubDocMapEntry));
+      mSubDocuments = PL_NewDHashTable(&hash_table_ops, sizeof(SubDocMapEntry));
+      if (!mSubDocuments) {
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
     }
 
     // Add a mapping to the hash table
-    SubDocMapEntry *entry = static_cast<SubDocMapEntry*>
-      (PL_DHashTableAdd(mSubDocuments, aElement, fallible));
+    SubDocMapEntry *entry =
+      static_cast<SubDocMapEntry*>
+                 (PL_DHashTableAdd(mSubDocuments, aElement));
 
     if (!entry) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     if (entry->mSubDocument) {
       entry->mSubDocument->SetParentDocument(nullptr);
 
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -164,24 +164,22 @@ nsFrameLoader::nsFrameLoader(Element* aO
   , mInSwap(false)
   , mInShow(false)
   , mHideCalled(false)
   , mNetworkCreated(aNetworkCreated)
   , mRemoteBrowserShown(false)
   , mRemoteFrame(false)
   , mClipSubdocument(true)
   , mClampScrollPosition(true)
-  , mRemoteBrowserInitialized(false)
   , mObservingOwnerContent(false)
   , mVisible(true)
   , mCurrentRemoteFrame(nullptr)
   , mRemoteBrowser(nullptr)
   , mChildID(0)
   , mEventMode(EVENT_MODE_NORMAL_DISPATCH)
-  , mPendingFrameSent(false)
 {
   ResetPermissionManagerStatus();
 }
 
 nsFrameLoader::~nsFrameLoader()
 {
   mNeedsAsyncDestroy = true;
   if (mMessageManager) {
@@ -358,25 +356,16 @@ nsFrameLoader::ReallyStartLoadingInterna
 
   nsresult rv = MaybeCreateDocShell();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (mRemoteFrame) {
     if (!mRemoteBrowser) {
-      if (!mPendingFrameSent) {
-        nsCOMPtr<nsIObserverService> os = services::GetObserverService();
-        if (os && !mRemoteBrowserInitialized) {
-          os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
-                              "remote-browser-pending", nullptr);
-          mPendingFrameSent = true;
-        }
-      }
-
       TryRemoteBrowser();
 
       if (!mRemoteBrowser) {
         NS_WARNING("Couldn't create child process for iframe.");
         return NS_ERROR_FAILURE;
       }
     }
 
@@ -908,26 +897,21 @@ nsFrameLoader::ShowRemoteFrame(const nsI
         parentIsActive = topWin && topWin->IsActive();
       }
     }
     mRemoteBrowser->Show(size, parentIsActive);
     mRemoteBrowserShown = true;
 
     EnsureMessageManager();
 
+    InitializeBrowserAPI();
     nsCOMPtr<nsIObserverService> os = services::GetObserverService();
-    if (os && !mRemoteBrowserInitialized) {
-      if (!mPendingFrameSent) {
-        os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
-                            "remote-browser-pending", nullptr);
-        mPendingFrameSent = true;
-      }
+    if (os) {
       os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
                           "remote-browser-shown", nullptr);
-      mRemoteBrowserInitialized = true;
     }
   } else {
     nsIntRect dimensions;
     NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), false);
 
     // Don't show remote iframe if we are waiting for the completion of reflow.
     if (!aFrame || !(aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
       nsIntPoint chromeDisp = aFrame->GetChromeDisplacement();
@@ -1780,16 +1764,17 @@ nsFrameLoader::MaybeCreateDocShell()
     uint32_t containingAppId = nsIScriptSecurityManager::NO_APP_ID;
     if (containingApp) {
       NS_ENSURE_SUCCESS(containingApp->GetLocalId(&containingAppId),
                         NS_ERROR_FAILURE);
     }
     mDocShell->SetIsBrowserInsideApp(containingAppId);
   }
 
+  InitializeBrowserAPI();
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
     os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
                         "inprocess-browser-shown", nullptr);
   }
 
   if (OwnerIsBrowserOrAppFrame() && mMessageManager) {
     mMessageManager->LoadFrameScript(
@@ -2200,17 +2185,16 @@ nsFrameLoader::TryRemoteBrowser()
     mContentParent = mRemoteBrowser->Manager();
 
     if (mOwnerContent->AttrValueIs(kNameSpaceID_None,
                                    nsGkAtoms::mozpasspointerevents,
                                    nsGkAtoms::_true,
                                    eCaseMatters)) {
       unused << mRemoteBrowser->SendSetUpdateHitRegion(true);
     }
-    parentDocShell->SetOpenedRemote(mRemoteBrowser);
   }
   return true;
 }
 
 mozilla::dom::PBrowserParent*
 nsFrameLoader::GetRemoteBrowser()
 {
   return mRemoteBrowser;
@@ -2744,8 +2728,17 @@ nsFrameLoader::GetLoadContext(nsILoadCon
   } else {
     nsCOMPtr<nsIDocShell> docShell;
     GetDocShell(getter_AddRefs(docShell));
     loadContext = do_GetInterface(docShell);
   }
   loadContext.forget(aLoadContext);
   return NS_OK;
 }
+
+void
+nsFrameLoader::InitializeBrowserAPI()
+{
+  nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
+  if (browserFrame) {
+    browserFrame->InitializeBrowserAPI();
+  }
+}
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -307,16 +307,18 @@ private:
   nsIAtom* TypeAttrName() const {
     return mOwnerContent->IsXUL() ? nsGkAtoms::type : nsGkAtoms::mozframetype;
   }
 
   // Update the permission manager's app-id refcount based on mOwnerContent's
   // own-or-containing-app.
   void ResetPermissionManagerStatus();
 
+  void InitializeBrowserAPI();
+
   nsCOMPtr<nsIDocShell> mDocShell;
   nsCOMPtr<nsIURI> mURIToLoad;
   mozilla::dom::Element* mOwnerContent; // WEAK
 
   // Note: this variable must be modified only by ResetPermissionManagerStatus()
   uint32_t mAppIdSentToPermissionManager;
 
 public:
@@ -346,17 +348,16 @@ private:
   // created using NS_FROM_PARSER_NETWORK flag. If the element is modified,
   // it may lose the flag.
   bool mNetworkCreated : 1;
 
   bool mRemoteBrowserShown : 1;
   bool mRemoteFrame : 1;
   bool mClipSubdocument : 1;
   bool mClampScrollPosition : 1;
-  bool mRemoteBrowserInitialized : 1;
   bool mObservingOwnerContent : 1;
 
   // Backs nsIFrameLoader::{Get,Set}Visible.  Visibility state here relates to
   // whether this frameloader's <iframe mozbrowser> is setVisible(true)'ed, and
   // doesn't necessarily correlate with docshell/document visibility.
   bool mVisible : 1;
 
   // The ContentParent associated with mRemoteBrowser.  This was added as a
@@ -364,14 +365,11 @@ private:
   nsRefPtr<mozilla::dom::nsIContentParent> mContentParent;
   RenderFrameParent* mCurrentRemoteFrame;
   TabParent* mRemoteBrowser;
   uint64_t mChildID;
 
   // See nsIFrameLoader.idl. EVENT_MODE_NORMAL_DISPATCH automatically
   // forwards some input events to out-of-process content.
   uint32_t mEventMode;
-
-  // Indicate if we have sent 'remote-browser-pending'.
-  bool mPendingFrameSent;
 };
 
 #endif
--- a/dom/base/nsGenericDOMDataNode.cpp
+++ b/dom/base/nsGenericDOMDataNode.cpp
@@ -108,16 +108,20 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   if (slots) {
     slots->Traverse(cb);
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericDOMDataNode)
   nsINode::Unlink(tmp);
 
+  // Clear flag here because unlinking slots will clear the
+  // containing shadow root pointer.
+  tmp->UnsetFlags(NODE_IS_IN_SHADOW_TREE);
+
   nsDataSlots *slots = tmp->GetExistingDataSlots();
   if (slots) {
     slots->Unlink();
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN(nsGenericDOMDataNode)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
@@ -557,18 +561,17 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
 void
 nsGenericDOMDataNode::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   // Unset frame flags; if we need them again later, they'll get set again.
   UnsetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
              NS_REFRAME_IF_WHITESPACE);
 
   nsIDocument* document =
-    HasFlag(NODE_FORCE_XBL_BINDINGS) || IsInShadowTree() ?
-      OwnerDoc() : GetUncomposedDoc();
+    HasFlag(NODE_FORCE_XBL_BINDINGS) ? OwnerDoc() : GetComposedDoc();
 
   if (aNullParent) {
     if (GetParent()) {
       NS_RELEASE(mParent);
     } else {
       mParent = nullptr;
     }
     SetParentIsContent(false);
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -385,27 +385,18 @@ nsINode::GetTextContentInternal(nsAStrin
 }
 
 nsIDocument*
 nsINode::GetComposedDocInternal() const
 {
   MOZ_ASSERT(HasFlag(NODE_IS_IN_SHADOW_TREE) && IsContent(),
              "Should only be caled on nodes in the shadow tree.");
 
-  // Cross ShadowRoot boundary.
   ShadowRoot* containingShadow = AsContent()->GetContainingShadow();
-
-  nsIContent* poolHost = containingShadow->GetPoolHost();
-  if (!poolHost) {
-    // This node is in an older shadow root that does not get projected into
-    // an insertion point, thus this node can not be in the composed document.
-    return nullptr;
-  }
-
-  return poolHost->GetComposedDoc();
+  return containingShadow->IsComposedDocParticipant() ?  OwnerDoc() : nullptr;
 }
 
 #ifdef DEBUG
 void
 nsINode::CheckNotNativeAnonymous() const
 {
   if (!IsNodeOfType(eCONTENT))
     return;
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -515,21 +515,19 @@ public:
    * @deprecated
    */
   nsIDocument *GetCurrentDoc() const
   {
     return GetUncomposedDoc();
   }
 
   /**
-   * This method gets the current doc of the node hosting this content
-   * or the current doc of this content if it is not being hosted. This
-   * method walks through ShadowRoot boundaries until it reach the host
-   * that is located in the root of the "tree of trees" (see Shadow DOM
-   * spec) and returns the current doc for that host.
+   * This method returns the owner doc if the node is in the
+   * composed document (as defined in the Shadow DOM spec), otherwise
+   * it returns null.
    */
   nsIDocument* GetComposedDoc() const
   {
     return IsInShadowTree() ?
       GetComposedDocInternal() : GetUncomposedDoc();
   }
 
   /**
--- a/dom/base/nsISlowScriptDebug.idl
+++ b/dom/base/nsISlowScriptDebug.idl
@@ -9,26 +9,26 @@ interface nsIDOMEventTarget;
 
 [scriptable, function, uuid(f7dbb80c-5d1e-4fd9-b55c-a9ffda4a75b1)]
 interface nsISlowScriptDebugCallback : nsISupports
 {
   void handleSlowScriptDebug(in nsIDOMWindow aWindow);
 };
 
 [scriptable, function, uuid(b1c6ecd0-8fa4-11e4-b4a9-0800200c9a66)]
-interface nsISlowScriptDebugerStartupCallback : nsISupports
+interface nsISlowScriptDebuggerStartupCallback : nsISupports
 {
   void finishDebuggerStartup();
 };
 
 [scriptable, function, uuid(dbee14b0-8fa0-11e4-b4a9-0800200c9a66)]
 interface nsISlowScriptDebugRemoteCallback : nsISupports
 {
   void handleSlowScriptDebug(in nsIDOMEventTarget aBrowser,
-      	                     in nsISlowScriptDebugerStartupCallback aCallback);
+                             in nsISlowScriptDebuggerStartupCallback aCallback);
 };
 
 [scriptable, uuid(f75d4164-3aa7-4395-ba44-a5f95b2e8427)]
 interface nsISlowScriptDebug : nsISupports
 {
   attribute nsISlowScriptDebugCallback activationHandler;
   attribute nsISlowScriptDebugRemoteCallback remoteActivationHandler;
 };
--- a/dom/base/nsPropertyTable.cpp
+++ b/dom/base/nsPropertyTable.cpp
@@ -225,17 +225,17 @@ nsPropertyTable::SetPropertyInternal(nsP
     propertyList->mNext = mPropertyList;
     mPropertyList = propertyList;
   }
 
   // The current property value (if there is one) is replaced and the current
   // value is destroyed
   nsresult result = NS_OK;
   PropertyListMapEntry *entry = static_cast<PropertyListMapEntry*>
-    (PL_DHashTableAdd(&propertyList->mObjectValueMap, aObject, fallible));
+                                           (PL_DHashTableAdd(&propertyList->mObjectValueMap, aObject));
   if (!entry)
     return NS_ERROR_OUT_OF_MEMORY;
   // A nullptr entry->key is the sign that the entry has just been allocated
   // for us.  If it's non-nullptr then we have an existing entry.
   if (entry->key) {
     if (aOldValue)
       *aOldValue = entry->value;
     else if (propertyList->mDtorFunc)
--- a/dom/base/nsScriptNameSpaceManager.cpp
+++ b/dom/base/nsScriptNameSpaceManager.cpp
@@ -117,35 +117,39 @@ GlobalNameHashInitEntry(PLDHashTable *ta
 
 NS_IMPL_ISUPPORTS(
   nsScriptNameSpaceManager,
   nsIObserver,
   nsISupportsWeakReference,
   nsIMemoryReporter)
 
 nsScriptNameSpaceManager::nsScriptNameSpaceManager()
+  : mIsInitialized(false)
 {
   MOZ_COUNT_CTOR(nsScriptNameSpaceManager);
 }
 
 nsScriptNameSpaceManager::~nsScriptNameSpaceManager()
 {
-  UnregisterWeakMemoryReporter(this);
-  // Destroy the hash
-  PL_DHashTableFinish(&mGlobalNames);
-  PL_DHashTableFinish(&mNavigatorNames);
+  if (mIsInitialized) {
+    UnregisterWeakMemoryReporter(this);
+    // Destroy the hash
+    PL_DHashTableFinish(&mGlobalNames);
+    PL_DHashTableFinish(&mNavigatorNames);
+  }
   MOZ_COUNT_DTOR(nsScriptNameSpaceManager);
 }
 
 nsGlobalNameStruct *
 nsScriptNameSpaceManager::AddToHash(PLDHashTable *aTable, const nsAString *aKey,
                                     const char16_t **aClassName)
 {
-  GlobalNameMapEntry *entry = static_cast<GlobalNameMapEntry *>
-    (PL_DHashTableAdd(aTable, aKey, fallible));
+  GlobalNameMapEntry *entry =
+    static_cast<GlobalNameMapEntry *>
+               (PL_DHashTableAdd(aTable, aKey));
 
   if (!entry) {
     return nullptr;
   }
 
   if (aClassName) {
     *aClassName = entry->mKey.get();
   }
@@ -317,23 +321,31 @@ nsScriptNameSpaceManager::Init()
   {
     GlobalNameHashHashKey,
     GlobalNameHashMatchEntry,
     PL_DHashMoveEntryStub,
     GlobalNameHashClearEntry,
     GlobalNameHashInitEntry
   };
 
-  PL_DHashTableInit(&mGlobalNames, &hash_table_ops,
-                    sizeof(GlobalNameMapEntry),
-                    GLOBALNAME_HASHTABLE_INITIAL_LENGTH);
+  mIsInitialized = PL_DHashTableInit(&mGlobalNames, &hash_table_ops,
+                                     sizeof(GlobalNameMapEntry),
+                                     fallible,
+                                     GLOBALNAME_HASHTABLE_INITIAL_LENGTH);
+  NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_OUT_OF_MEMORY);
 
-  PL_DHashTableInit(&mNavigatorNames, &hash_table_ops,
-                    sizeof(GlobalNameMapEntry),
-                    GLOBALNAME_HASHTABLE_INITIAL_LENGTH);
+  mIsInitialized = PL_DHashTableInit(&mNavigatorNames, &hash_table_ops,
+                                     sizeof(GlobalNameMapEntry),
+                                     fallible,
+                                     GLOBALNAME_HASHTABLE_INITIAL_LENGTH);
+  if (!mIsInitialized) {
+    PL_DHashTableFinish(&mGlobalNames);
+
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
 
   RegisterWeakMemoryReporter(this);
 
   nsresult rv = NS_OK;
 
   rv = RegisterExternalInterfaces(false);
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/dom/base/nsScriptNameSpaceManager.h
+++ b/dom/base/nsScriptNameSpaceManager.h
@@ -235,11 +235,13 @@ private:
                                     nsISupports* aEntry,
                                     bool aRemove);
 
   nsGlobalNameStruct* LookupNameInternal(const nsAString& aName,
                                          const char16_t **aClassName = nullptr);
 
   PLDHashTable mGlobalNames;
   PLDHashTable mNavigatorNames;
+
+  bool mIsInitialized;
 };
 
 #endif /* nsScriptNameSpaceManager_h__ */
--- a/dom/base/nsWrapperCache.cpp
+++ b/dom/base/nsWrapperCache.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsWrapperCacheInlines.h"
 
-#include "jsproxy.h"
+#include "js/Proxy.h"
 #include "mozilla/dom/DOMJSProxyHandler.h"
 #include "mozilla/HoldDropJSObjects.h"
 #include "nsCycleCollectionTraversalCallback.h"
 #include "nsCycleCollector.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
--- a/dom/base/test/bug403852_fileOpener.js
+++ b/dom/base/test/bug403852_fileOpener.js
@@ -6,10 +6,12 @@ let testFile = Cc["@mozilla.org/file/dir
                  .QueryInterface(Ci.nsIProperties)
                  .get("ProfD", Ci.nsIFile);
 testFile.append("prefs.js");
 
 addMessageListener("file.open", function () {
   sendAsyncMessage("file.opened", {
     file: new File(testFile),
     mtime: testFile.lastModifiedTime,
+    fileWithDate: new File(testFile, { lastModified: 123 }),
+    fileDate: 123,
   });
 });
--- a/dom/base/test/test_bug403852.html
+++ b/dom/base/test/test_bug403852.html
@@ -33,28 +33,33 @@ function onOpened(message) {
   // Make sure the file is accessible with indexed notation
   var domFile = fileList.files[0];
 
   is(domFile.name, "prefs.js", "fileName should be prefs.js");
 
   ok("lastModifiedDate" in domFile, "lastModifiedDate must be present");
 
   var d = new Date(message.mtime);
-  todo(d.getTime() == domFile.lastModifiedDate.getTime(), "lastModifiedDate should be the same (FIXME: bug 1121722)");
+  is(d.getTime(), domFile.lastModifiedDate.getTime(), "lastModifiedDate should be the same");
 
   var x = new Date();
 
   // In our implementation of File object, lastModifiedDate is unknown only for new objects.
   // Using canvas or input[type=file] elements, we 'often' have a valid lastModifiedDate values.
   // For canvas we use memory files and the lastModifiedDate is now().
   var f = new File([new Blob(['test'], {type: 'text/plain'})], "test-name");
 
   var y = f.lastModifiedDate;
   var z = new Date();
 
   ok((x.getTime() <= y.getTime()) && (y.getTime() <= z.getTime()), "lastModifiedDate of file which does not have last modified date should be current time");
+
+
+  var d = new Date(message.fileDate);
+  is(d.getTime(), message.fileWithDate.lastModifiedDate.getTime(), "lastModifiedDate should be the same when lastModified is set: " + message.fileWithDate.lastModified);
+
   script.destroy();
   SimpleTest.finish();
 }
 
 </script>
 </pre>
 </body> </html>
--- a/dom/bindings/DOMJSProxyHandler.h
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -5,17 +5,17 @@
 
 #ifndef mozilla_dom_DOMJSProxyHandler_h
 #define mozilla_dom_DOMJSProxyHandler_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Likely.h"
 
 #include "jsapi.h"
-#include "jsproxy.h"
+#include "js/Proxy.h"
 #include "nsString.h"
 
 #define DOM_PROXY_OBJECT_SLOT js::PROXY_PRIVATE_SLOT
 
 namespace mozilla {
 namespace dom {
 
 enum {
--- a/dom/events/TextComposition.cpp
+++ b/dom/events/TextComposition.cpp
@@ -219,16 +219,27 @@ TextComposition::DispatchCompositionEven
   // composition string empty or didn't have clause information), we don't
   // need to dispatch redundant DOM text event.
   if (dispatchDOMTextEvent &&
       aCompositionEvent->message != NS_COMPOSITION_CHANGE &&
       !mIsComposing && mLastData == aCompositionEvent->mData) {
     dispatchEvent = dispatchDOMTextEvent = false;
   }
 
+  // widget may dispatch redundant NS_COMPOSITION_CHANGE event
+  // which modifies neither composition string, clauses nor caret
+  // position.  In such case, we shouldn't dispatch DOM events.
+  if (dispatchDOMTextEvent &&
+      aCompositionEvent->message == NS_COMPOSITION_CHANGE &&
+      mLastData == aCompositionEvent->mData &&
+      mRanges && aCompositionEvent->mRanges &&
+      mRanges->Equals(*aCompositionEvent->mRanges)) {
+    dispatchEvent = dispatchDOMTextEvent = false;
+  }
+
   if (dispatchDOMTextEvent) {
     if (!MaybeDispatchCompositionUpdate(aCompositionEvent)) {
       return;
     }
   }
 
   if (dispatchEvent) {
     // If the composition event should cause a DOM text event, we should
--- a/dom/geolocation/nsGeolocationSettings.cpp
+++ b/dom/geolocation/nsGeolocationSettings.cpp
@@ -219,28 +219,36 @@ nsGeolocationSettings::HandleGeolocation
   }
 
   mAlaEnabled = aVal.toBoolean();
 }
 
 void
 nsGeolocationSettings::HandleGeolocationPerOriginSettingsChange(const JS::Value& aVal)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   if (!aVal.isObject()) {
     return;
   }
 
   // clear the hash table
   mPerOriginSettings.Clear();
 
-  // enumerate the array
-  AutoJSAPI jsapi;
-  jsapi.Init();
-  JSContext* cx = jsapi.cx();
-  JS::Rooted<JSObject*> obj(cx, &aVal.toObject());
+  // root the object and get the global
+  JS::Rooted<JSObject*> obj(nsContentUtils::RootingCx(), &aVal.toObject());
+  MOZ_ASSERT(obj);
+  nsIGlobalObject* global = xpc::NativeGlobal(obj);
+  NS_ENSURE_TRUE_VOID(global && global->GetGlobalJSObject());
+
+  // because the spec requires calling getters when enumerating the key of a
+  // dictionary
+  AutoEntryScript aes(global);
+  aes.TakeOwnershipOfErrorReporting();
+  JSContext *cx = aes.cx();
   JS::AutoIdArray ids(cx, JS_Enumerate(cx, obj));
 
   // if we get no ids then the exception list is empty and we can return here.
   if (!ids)
       return;
 
   // go through all of the objects in the exceptions dictionary
   for (size_t i = 0; i < ids.length(); i++) {
--- a/dom/html/HTMLShadowElement.cpp
+++ b/dom/html/HTMLShadowElement.cpp
@@ -141,16 +141,18 @@ HTMLShadowElement::BindToTree(nsIDocumen
 
     containingShadow->SetInsertionPointChanged();
   }
 
   if (mIsInsertionPoint && containingShadow) {
     // Propagate BindToTree calls to projected shadow root children.
     ShadowRoot* projectedShadow = containingShadow->GetOlderShadowRoot();
     if (projectedShadow) {
+      projectedShadow->SetIsComposedDocParticipant(IsInComposedDoc());
+
       for (nsIContent* child = projectedShadow->GetFirstChild(); child;
            child = child->GetNextSibling()) {
         rv = child->BindToTree(nullptr, projectedShadow,
                                projectedShadow->GetBindingParent(),
                                aCompileEventHandlers);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
@@ -168,16 +170,18 @@ HTMLShadowElement::UnbindFromTree(bool a
     // Propagate UnbindFromTree call to previous projected shadow
     // root children.
     ShadowRoot* projectedShadow = oldContainingShadow->GetOlderShadowRoot();
     if (projectedShadow) {
       for (nsIContent* child = projectedShadow->GetFirstChild(); child;
            child = child->GetNextSibling()) {
         child->UnbindFromTree(true, false);
       }
+
+      projectedShadow->SetIsComposedDocParticipant(false);
     }
   }
 
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 
   if (oldContainingShadow && !GetContainingShadow() && mIsInsertionPoint) {
     nsTArray<HTMLShadowElement*>& shadowDescendants =
       oldContainingShadow->ShadowDescendants();
--- a/dom/html/HTMLStyleElement.cpp
+++ b/dom/html/HTMLStyleElement.cpp
@@ -152,25 +152,24 @@ HTMLStyleElement::BindToTree(nsIDocument
 
   return rv;  
 }
 
 void
 HTMLStyleElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   nsCOMPtr<nsIDocument> oldDoc = GetUncomposedDoc();
-  nsCOMPtr<nsIDocument> oldComposedDoc = GetComposedDoc();
   ShadowRoot* oldShadow = GetContainingShadow();
 
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 
-  if (GetContainingShadow() && !oldComposedDoc) {
-    // The style is in a shadow tree and was already not
-    // in the composed document. Thus the sheet does not
-    // need to be updated.
+  if (oldShadow && GetContainingShadow()) {
+    // The style is in a shadow tree and is still in the
+    // shadow tree. Thus the sheets in the shadow DOM
+    // do not need to be updated.
     return;
   }
 
   UpdateStyleSheetInternal(oldDoc, oldShadow);
 }
 
 nsresult
 HTMLStyleElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
--- a/dom/html/nsBrowserElement.cpp
+++ b/dom/html/nsBrowserElement.cpp
@@ -14,101 +14,22 @@
 #include "mozilla/dom/ToJSValue.h"
 
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsFrameLoader.h"
 #include "nsIDOMDOMRequest.h"
 #include "nsIDOMElement.h"
 #include "nsINode.h"
-#include "nsIObserver.h"
-#include "nsIObserverService.h"
 #include "nsIPrincipal.h"
-#include "nsWeakReference.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
-static const char kRemoteBrowserPending[] = "remote-browser-pending";
-static const char kInprocessBrowserShown[] = "inprocess-browser-shown";
-
-class nsBrowserElement::BrowserShownObserver : public nsIObserver
-                                             , public nsSupportsWeakReference
-{
-public:
-  explicit BrowserShownObserver(nsBrowserElement* aBrowserElement);
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIOBSERVER
-  void AddObserver();
-  void RemoveObserver();
-private:
-  virtual ~BrowserShownObserver();
-
-  // Weak reference to the browser element. nsBrowserElement has a
-  // reference to us. nsBrowserElement's destructor is responsible to
-  // null out this weak reference via RemoveObserver()
-  nsBrowserElement* mBrowserElement;
-};
-
-NS_IMPL_ISUPPORTS(nsBrowserElement::BrowserShownObserver, nsIObserver, nsISupportsWeakReference)
-
-nsBrowserElement::BrowserShownObserver::BrowserShownObserver(nsBrowserElement* aBrowserElement)
-  : mBrowserElement(aBrowserElement)
-{
-}
-
-nsBrowserElement::BrowserShownObserver::~BrowserShownObserver()
-{
-  RemoveObserver();
-}
-
-NS_IMETHODIMP
-nsBrowserElement::BrowserShownObserver::Observe(nsISupports* aSubject,
-                                                const char* aTopic,
-                                                const char16_t* aData)
-{
-  NS_ENSURE_TRUE(mBrowserElement, NS_OK);
-
-  if (!strcmp(aTopic, kRemoteBrowserPending) ||
-      !strcmp(aTopic, kInprocessBrowserShown)) {
-    nsCOMPtr<nsIFrameLoader> frameLoader = do_QueryInterface(aSubject);
-    nsCOMPtr<nsIFrameLoader> myFrameLoader = mBrowserElement->GetFrameLoader();
-    // The browser element API needs the frameloader to
-    // initialize. We still use the observer to get notified when the
-    // frameloader is created. So we check if the frameloader created
-    // is ours, then initialize the browser element API.
-    if (frameLoader && frameLoader == myFrameLoader) {
-      mBrowserElement->InitBrowserElementAPI();
-    }
-  }
-  return NS_OK;
-}
-
-void
-nsBrowserElement::BrowserShownObserver::AddObserver()
-{
-  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-  if (obs) {
-    obs->AddObserver(this, kRemoteBrowserPending, true);
-    obs->AddObserver(this, kInprocessBrowserShown, true);
-  }
-}
-
-void
-nsBrowserElement::BrowserShownObserver::RemoveObserver()
-{
-  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-  if (obs) {
-    obs->RemoveObserver(this, kRemoteBrowserPending);
-    obs->RemoveObserver(this, kInprocessBrowserShown);
-  }
-  mBrowserElement = nullptr;
-}
-
 bool
 nsBrowserElement::IsBrowserElementOrThrow(ErrorResult& aRv)
 {
   if (mBrowserElementAPI) {
     return true;
   }
   aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
   return false;
@@ -140,28 +61,16 @@ nsBrowserElement::InitBrowserElementAPI(
   }
 
   mBrowserElementAPI = do_CreateInstance("@mozilla.org/dom/browser-element-api;1");
   if (mBrowserElementAPI) {
     mBrowserElementAPI->SetFrameLoader(frameLoader);
   }
 }
 
-nsBrowserElement::nsBrowserElement()
-  : mOwnerIsWidget(false)
-{
-  mObserver = new BrowserShownObserver(this);
-  mObserver->AddObserver();
-}
-
-nsBrowserElement::~nsBrowserElement()
-{
-  mObserver->RemoveObserver();
-}
-
 void
 nsBrowserElement::SetVisible(bool aVisible, ErrorResult& aRv)
 {
   NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv));
 
   nsresult rv = mBrowserElementAPI->SetVisible(aVisible);
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/dom/html/nsBrowserElement.h
+++ b/dom/html/nsBrowserElement.h
@@ -8,17 +8,16 @@
 #define nsBrowserElement_h
 
 #include "mozilla/dom/BindingDeclarations.h"
 
 #include "nsCOMPtr.h"
 #include "nsIBrowserElementAPI.h"
 
 class nsFrameLoader;
-class nsIObserver;
 
 namespace mozilla {
 
 namespace dom {
 struct BrowserElementDownloadOptions;
 class BrowserElementNextPaintEventCallback;
 class DOMRequest;
 } // namespace dom
@@ -26,18 +25,18 @@ class DOMRequest;
 class ErrorResult;
 
 /**
  * A helper class for browser-element frames
  */
 class nsBrowserElement
 {
 public:
-  nsBrowserElement();
-  virtual ~nsBrowserElement();
+  nsBrowserElement() : mOwnerIsWidget(false) {}
+  virtual ~nsBrowserElement() {}
 
   void SetVisible(bool aVisible, ErrorResult& aRv);
   already_AddRefed<dom::DOMRequest> GetVisible(ErrorResult& aRv);
   void SetActive(bool aActive, ErrorResult& aRv);
   bool GetActive(ErrorResult& aRv);
 
   void SendMouseEvent(const nsAString& aType,
                       uint32_t aX,
@@ -89,24 +88,20 @@ public:
   already_AddRefed<dom::DOMRequest> SetInputMethodActive(bool isActive,
                                                          ErrorResult& aRv);
 
   void SetNFCFocus(bool isFocus,
                    ErrorResult& aRv);
 
 protected:
   NS_IMETHOD_(already_AddRefed<nsFrameLoader>) GetFrameLoader() = 0;
+  void InitBrowserElementAPI();
   nsCOMPtr<nsIBrowserElementAPI> mBrowserElementAPI;
 
 private:
-  void InitBrowserElementAPI();
   bool IsBrowserElementOrThrow(ErrorResult& aRv);
   bool IsNotWidgetOrThrow(ErrorResult& aRv);
   bool mOwnerIsWidget;
-
-  class BrowserShownObserver;
-  friend class BrowserShownObserver;
-  nsRefPtr<BrowserShownObserver> mObserver;
 };
 
 } // namespace mozilla
 
 #endif // nsBrowserElement_h
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -494,8 +494,16 @@ nsGenericHTMLFrameElement::DisallowCreat
 NS_IMETHODIMP
 nsGenericHTMLFrameElement::AllowCreateFrameLoader()
 {
   MOZ_ASSERT(!mFrameLoader);
   MOZ_ASSERT(mFrameLoaderCreationDisallowed);
   mFrameLoaderCreationDisallowed = false;
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsGenericHTMLFrameElement::InitializeBrowserAPI()
+{
+  MOZ_ASSERT(mFrameLoader);
+  InitBrowserElementAPI();
+  return NS_OK;
+}
--- a/dom/interfaces/html/nsIMozBrowserFrame.idl
+++ b/dom/interfaces/html/nsIMozBrowserFrame.idl
@@ -80,9 +80,15 @@ interface nsIMozBrowserFrame : nsIDOMMoz
 
   /**
    * Create a remote (i.e., out-of-process) frame loader attached to the given
    * tab parent.
    *
    * It is an error to call this method if we already have a frame loader.
    */
   void createRemoteFrameLoader(in nsITabParent aTabParent);
+
+  /**
+   * Initialize the API, and add frame message listener to listen to API
+   * invocations.
+   */
+  [noscript] void initializeBrowserAPI();
 };
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -466,16 +466,23 @@ ProcessPriorityManagerImpl::Observe(
 
   return NS_OK;
 }
 
 already_AddRefed<ParticularProcessPriorityManager>
 ProcessPriorityManagerImpl::GetParticularProcessPriorityManager(
   ContentParent* aContentParent)
 {
+#ifdef MOZ_NUWA_PROCESS
+  // Do not attempt to change the priority of the Nuwa process
+  if (aContentParent->IsNuwaProcess()) {
+    return nullptr;
+  }
+#endif
+
   nsRefPtr<ParticularProcessPriorityManager> pppm;
   uint64_t cpId = aContentParent->ChildID();
   mParticularManagers.Get(cpId, &pppm);
   if (!pppm) {
     pppm = new ParticularProcessPriorityManager(aContentParent);
     pppm->Init();
     mParticularManagers.Put(cpId, pppm);
 
@@ -489,17 +496,19 @@ ProcessPriorityManagerImpl::GetParticula
 void
 ProcessPriorityManagerImpl::SetProcessPriority(ContentParent* aContentParent,
                                                ProcessPriority aPriority,
                                                uint32_t aBackgroundLRU)
 {
   MOZ_ASSERT(aContentParent);
   nsRefPtr<ParticularProcessPriorityManager> pppm =
     GetParticularProcessPriorityManager(aContentParent);
-  pppm->SetPriorityNow(aPriority, aBackgroundLRU);
+  if (pppm) {
+    pppm->SetPriorityNow(aPriority, aBackgroundLRU);
+  }
 }
 
 void
 ProcessPriorityManagerImpl::ObserveContentParentCreated(
   nsISupports* aContentParent)
 {
   // Do nothing; it's sufficient to get the PPPM.  But assign to nsRefPtr so we
   // don't leak the already_AddRefed object.
@@ -527,28 +536,27 @@ ProcessPriorityManagerImpl::ObserveConte
   NS_ENSURE_TRUE_VOID(props);
 
   uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
   props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
   NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN);
 
   nsRefPtr<ParticularProcessPriorityManager> pppm;
   mParticularManagers.Get(childID, &pppm);
-  MOZ_ASSERT(pppm);
   if (pppm) {
     pppm->ShutDown();
-  }
 
-  mParticularManagers.Remove(childID);
+    mParticularManagers.Remove(childID);
 
-  if (mHighPriorityChildIDs.Contains(childID)) {
-    mHighPriorityChildIDs.RemoveEntry(childID);
+    if (mHighPriorityChildIDs.Contains(childID)) {
+      mHighPriorityChildIDs.RemoveEntry(childID);
 
-    // We just lost a high-priority process; reset everyone's CPU priorities.
-    ResetAllCPUPriorities();
+      // We just lost a high-priority process; reset everyone's CPU priorities.
+      ResetAllCPUPriorities();
+    }
   }
 }
 
 void
 ProcessPriorityManagerImpl::ResetAllCPUPriorities( void )
 {
   nsTArray<nsRefPtr<ParticularProcessPriorityManager> > pppms;
   mParticularManagers.EnumerateRead(
@@ -801,32 +809,35 @@ ParticularProcessPriorityManager::OnAudi
 }
 
 void
 ParticularProcessPriorityManager::OnRemoteBrowserFrameShown(nsISupports* aSubject)
 {
   nsCOMPtr<nsIFrameLoader> fl = do_QueryInterface(aSubject);
   NS_ENSURE_TRUE_VOID(fl);
 
-  // Ignore notifications that aren't from a BrowserOrApp
-  bool isBrowserOrApp;
-  fl->GetOwnerIsBrowserOrAppFrame(&isBrowserOrApp);
-  if (!isBrowserOrApp) {
-    return;
-  }
-
   TabParent* tp = TabParent::GetFrom(fl);
   NS_ENSURE_TRUE_VOID(tp);
 
   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   if (tp->Manager() != mContentParent) {
     return;
   }
 
-  ResetPriority();
+  // Ignore notifications that aren't from a BrowserOrApp
+  bool isBrowserOrApp;
+  fl->GetOwnerIsBrowserOrAppFrame(&isBrowserOrApp);
+  if (isBrowserOrApp) {
+    ResetPriority();
+  }
+
+  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+  if (os) {
+    os->RemoveObserver(this, "remote-browser-shown");
+  }
 }
 
 void
 ParticularProcessPriorityManager::OnTabParentDestroyed(nsISupports* aSubject)
 {
   nsCOMPtr<nsITabParent> tp = do_QueryInterface(aSubject);
   NS_ENSURE_TRUE_VOID(tp);
 
@@ -1034,23 +1045,16 @@ ParticularProcessPriorityManager::SetPri
   SetPriorityNow(aPriority, ComputeCPUPriority(aPriority), aBackgroundLRU);
 }
 
 void
 ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority,
                                                  ProcessCPUPriority aCPUPriority,
                                                  uint32_t aBackgroundLRU)
 {
-#ifdef MOZ_NUWA_PROCESS
-  // Do not attempt to change the priority of the Nuwa process
-  if (mContentParent->IsNuwaProcess()) {
-    return;
-  }
-#endif
-
   if (aPriority == PROCESS_PRIORITY_UNKNOWN) {
     MOZ_ASSERT(false);
     return;
   }
 
   if (aBackgroundLRU > 0 &&
       aPriority == PROCESS_PRIORITY_BACKGROUND &&
       mPriority == PROCESS_PRIORITY_BACKGROUND) {
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -642,21 +642,32 @@ TabParent::RecvCreateWindow(PBrowserPare
                             NS_ConvertUTF16toUTF8(aName).get(),
                             NS_ConvertUTF16toUTF8(aFeatures).get(), aCalledFromJS,
                             false, false, this, nullptr, getter_AddRefs(window));
   NS_ENSURE_SUCCESS(rv, false);
 
   nsCOMPtr<nsPIDOMWindow> pwindow = do_QueryInterface(window);
   NS_ENSURE_TRUE(pwindow, false);
 
-  nsRefPtr<nsIDocShell> newDocShell = pwindow->GetDocShell();
-  NS_ENSURE_TRUE(newDocShell, false);
-
-  nsCOMPtr<nsITabParent> newRemoteTab = newDocShell->GetOpenedRemote();
-  NS_ENSURE_TRUE(newRemoteTab, false);
+  nsCOMPtr<nsIDocShell> windowDocShell = pwindow->GetDocShell();
+  NS_ENSURE_TRUE(windowDocShell, false);
+
+  nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
+  windowDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
+
+  nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(treeOwner);
+  NS_ENSURE_TRUE(xulWin, false);
+
+  nsCOMPtr<nsIXULBrowserWindow> xulBrowserWin;
+  xulWin->GetXULBrowserWindow(getter_AddRefs(xulBrowserWin));
+  NS_ENSURE_TRUE(xulBrowserWin, false);
+
+  nsCOMPtr<nsITabParent> newRemoteTab;
+  rv = xulBrowserWin->ForceInitialBrowserRemote(getter_AddRefs(newRemoteTab));
+  NS_ENSURE_SUCCESS(rv, false);
 
   MOZ_ASSERT(TabParent::GetFrom(newRemoteTab) == newTab);
 
   aFrameScripts->SwapElements(newTab->mDelayedFrameScripts);
   return true;
 }
 
 TabParent* TabParent::sNextTabParent;
--- a/dom/media/MediaDecoderReader.cpp
+++ b/dom/media/MediaDecoderReader.cpp
@@ -309,16 +309,15 @@ MediaDecoderReader::Shutdown()
   // out mTaskQueue, since otherwise any remaining tasks could crash when they
   // invoke GetTaskQueue()->IsCurrentThreadIn().
   if (mTaskQueue && !mTaskQueueIsBorrowed) {
     // If we own our task queue, shutdown ends when the task queue is done.
     p = mTaskQueue->BeginShutdown();
   } else {
     // If we don't own our task queue, we resolve immediately (though
     // asynchronously).
-    p = new ShutdownPromise(__func__);
-    p->Resolve(true, __func__);
+    p = ShutdownPromise::CreateAndResolve(true, __func__);
   }
 
   return p;
 }
 
 } // namespace mozilla
--- a/dom/media/MediaPromise.h
+++ b/dom/media/MediaPromise.h
@@ -57,38 +57,52 @@ template<typename T> class MediaPromiseH
 template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
 class MediaPromise
 {
 public:
   typedef ResolveValueT ResolveValueType;
   typedef RejectValueT RejectValueType;
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaPromise)
+
+protected:
+  // MediaPromise is the public type, and never constructed directly. Construct
+  // a MediaPromise::Private, defined below.
   explicit MediaPromise(const char* aCreationSite)
     : mCreationSite(aCreationSite)
     , mMutex("MediaPromise Mutex")
     , mHaveConsumer(false)
   {
     PROMISE_LOG("%s creating MediaPromise (%p)", mCreationSite, this);
   }
 
+public:
+  // MediaPromise::Private allows us to separate the public interface (upon which
+  // consumers of the promise may invoke methods like Then()) from the private
+  // interface (upon which the creator of the promise may invoke Resolve() or
+  // Reject()). APIs should create and store a MediaPromise::Private (usually
+  // via a MediaPromiseHolder), and return a MediaPromise to consumers.
+  //
+  // NB: We can include the definition of this class inline once B2G ICS is gone.
+  class Private;
+
   static nsRefPtr<MediaPromise>
   CreateAndResolve(ResolveValueType aResolveValue, const char* aResolveSite)
   {
-    nsRefPtr<MediaPromise> p = new MediaPromise(aResolveSite);
+    nsRefPtr<typename MediaPromise::Private> p = new MediaPromise::Private(aResolveSite);
     p->Resolve(aResolveValue, aResolveSite);
-    return p;
+    return Move(p);
   }
 
   static nsRefPtr<MediaPromise>
   CreateAndReject(RejectValueType aRejectValue, const char* aRejectSite)
   {
-    nsRefPtr<MediaPromise> p = new MediaPromise(aRejectSite);
+    nsRefPtr<typename MediaPromise::Private> p = new MediaPromise::Private(aRejectSite);
     p->Reject(aRejectValue, aRejectSite);
-    return p;
+    return Move(p);
   }
 
   class Consumer
   {
   public:
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Consumer)
 
     void Disconnect()
@@ -323,66 +337,48 @@ public:
   void Then(TargetType* aResponseTarget, const char* aCallSite, ThisType* aThisVal,
             ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
   {
     nsRefPtr<Consumer> c =
       RefableThen(aResponseTarget, aCallSite, aThisVal, aResolveMethod, aRejectMethod);
     return;
   }
 
-  void ChainTo(already_AddRefed<MediaPromise> aChainedPromise, const char* aCallSite)
+  void ChainTo(already_AddRefed<Private> aChainedPromise, const char* aCallSite)
   {
     MutexAutoLock lock(mMutex);
     MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveConsumer);
     mHaveConsumer = true;
-    nsRefPtr<MediaPromise> chainedPromise = aChainedPromise;
+    nsRefPtr<Private> chainedPromise = aChainedPromise;
     PROMISE_LOG("%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
                 aCallSite, this, chainedPromise.get(), (int) IsPending());
     if (!IsPending()) {
       ForwardTo(chainedPromise);
     } else {
       mChainedPromises.AppendElement(chainedPromise);
     }
   }
 
-  void Resolve(ResolveValueType aResolveValue, const char* aResolveSite)
-  {
-    MutexAutoLock lock(mMutex);
-    MOZ_ASSERT(IsPending());
-    PROMISE_LOG("%s resolving MediaPromise (%p created at %s)", aResolveSite, this, mCreationSite);
-    mResolveValue.emplace(aResolveValue);
-    DispatchAll();
-  }
-
-  void Reject(RejectValueType aRejectValue, const char* aRejectSite)
-  {
-    MutexAutoLock lock(mMutex);
-    MOZ_ASSERT(IsPending());
-    PROMISE_LOG("%s rejecting MediaPromise (%p created at %s)", aRejectSite, this, mCreationSite);
-    mRejectValue.emplace(aRejectValue);
-    DispatchAll();
-  }
-
 protected:
   bool IsPending() { return mResolveValue.isNothing() && mRejectValue.isNothing(); }
   void DispatchAll()
   {
     mMutex.AssertCurrentThreadOwns();
     for (size_t i = 0; i < mThenValues.Length(); ++i) {
       mThenValues[i]->Dispatch(this);
     }
     mThenValues.Clear();
 
     for (size_t i = 0; i < mChainedPromises.Length(); ++i) {
       ForwardTo(mChainedPromises[i]);
     }
     mChainedPromises.Clear();
   }
 
-  void ForwardTo(MediaPromise* aOther)
+  void ForwardTo(Private* aOther)
   {
     MOZ_ASSERT(!IsPending());
     if (mResolveValue.isSome()) {
       aOther->Resolve(mResolveValue.ref(), "<chained promise>");
     } else {
       aOther->Reject(mRejectValue.ref(), "<chained promise>");
     }
   }
@@ -395,20 +391,46 @@ protected:
     MOZ_ASSERT(mChainedPromises.IsEmpty());
   };
 
   const char* mCreationSite; // For logging
   Mutex mMutex;
   Maybe<ResolveValueType> mResolveValue;
   Maybe<RejectValueType> mRejectValue;
   nsTArray<nsRefPtr<ThenValueBase>> mThenValues;
-  nsTArray<nsRefPtr<MediaPromise>> mChainedPromises;
+  nsTArray<nsRefPtr<Private>> mChainedPromises;
   bool mHaveConsumer;
 };
 
+template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
+class MediaPromise<ResolveValueT, RejectValueT, IsExclusive>::Private
+  : public MediaPromise<ResolveValueT, RejectValueT, IsExclusive>
+{
+public:
+  explicit Private(const char* aCreationSite) : MediaPromise(aCreationSite) {}
+
+  void Resolve(ResolveValueT aResolveValue, const char* aResolveSite)
+  {
+    MutexAutoLock lock(mMutex);
+    MOZ_ASSERT(IsPending());
+    PROMISE_LOG("%s resolving MediaPromise (%p created at %s)", aResolveSite, this, mCreationSite);
+    mResolveValue.emplace(aResolveValue);
+    DispatchAll();
+  }
+
+  void Reject(RejectValueT aRejectValue, const char* aRejectSite)
+  {
+    MutexAutoLock lock(mMutex);
+    MOZ_ASSERT(IsPending());
+    PROMISE_LOG("%s rejecting MediaPromise (%p created at %s)", aRejectSite, this, mCreationSite);
+    mRejectValue.emplace(aRejectValue);
+    DispatchAll();
+  }
+};
+
 /*
  * Class to encapsulate a promise for a particular role. Use this as the member
  * variable for a class whose method returns a promise.
  */
 template<typename PromiseType>
 class MediaPromiseHolder
 {
 public:
@@ -417,40 +439,40 @@ public:
 
   ~MediaPromiseHolder() { MOZ_ASSERT(!mPromise); }
 
   already_AddRefed<PromiseType> Ensure(const char* aMethodName) {
     if (mMonitor) {
       mMonitor->AssertCurrentThreadOwns();
     }
     if (!mPromise) {
-      mPromise = new PromiseType(aMethodName);
+      mPromise = new (typename PromiseType::Private)(aMethodName);
     }
-    nsRefPtr<PromiseType> p = mPromise;
+    nsRefPtr<PromiseType> p = mPromise.get();
     return p.forget();
   }
 
   // Provide a Monitor that should always be held when accessing this instance.
   void SetMonitor(Monitor* aMonitor) { mMonitor = aMonitor; }
 
   bool IsEmpty()
   {
     if (mMonitor) {
       mMonitor->AssertCurrentThreadOwns();
     }
     return !mPromise;
   }
 
-  already_AddRefed<PromiseType> Steal()
+  already_AddRefed<typename PromiseType::Private> Steal()
   {
     if (mMonitor) {
       mMonitor->AssertCurrentThreadOwns();
     }
 
-    nsRefPtr<PromiseType> p = mPromise;
+    nsRefPtr<typename PromiseType::Private> p = mPromise;
     mPromise = nullptr;
     return p.forget();
   }
 
   void Resolve(typename PromiseType::ResolveValueType aResolveValue,
                const char* aMethodName)
   {
     if (mMonitor) {
@@ -485,17 +507,17 @@ public:
   {
     if (!IsEmpty()) {
       Reject(aRejectValue, aMethodName);
     }
   }
 
 private:
   Monitor* mMonitor;
-  nsRefPtr<PromiseType> mPromise;
+  nsRefPtr<typename PromiseType::Private> mPromise;
 };
 
 /*
  * Class to encapsulate a MediaPromise::Consumer reference. Use this as the member
  * variable for a class waiting on a media promise.
  */
 template<typename PromiseType>
 class MediaPromiseConsumerHolder
@@ -585,41 +607,41 @@ protected:
   Arg1Type mArg1;
   Arg2Type mArg2;
 };
 
 template<typename PromiseType>
 class ProxyRunnable : public nsRunnable
 {
 public:
-  ProxyRunnable(PromiseType* aProxyPromise, MethodCallBase<PromiseType>* aMethodCall)
+  ProxyRunnable(typename PromiseType::Private* aProxyPromise, MethodCallBase<PromiseType>* aMethodCall)
     : mProxyPromise(aProxyPromise), mMethodCall(aMethodCall) {}
 
   NS_IMETHODIMP Run()
   {
     nsRefPtr<PromiseType> p = mMethodCall->Invoke();
     mMethodCall = nullptr;
     p->ChainTo(mProxyPromise.forget(), "<Proxy Promise>");
     return NS_OK;
   }
 
 private:
-  nsRefPtr<PromiseType> mProxyPromise;
+  nsRefPtr<typename PromiseType::Private> mProxyPromise;
   nsAutoPtr<MethodCallBase<PromiseType>> mMethodCall;
 };
 
 template<typename PromiseType, typename TargetType>
 static nsRefPtr<PromiseType>
 ProxyInternal(TargetType* aTarget, MethodCallBase<PromiseType>* aMethodCall, const char* aCallerName)
 {
-  nsRefPtr<PromiseType> p = new PromiseType(aCallerName);
+  nsRefPtr<typename PromiseType::Private> p = new (typename PromiseType::Private)(aCallerName);
   nsRefPtr<ProxyRunnable<PromiseType>> r = new ProxyRunnable<PromiseType>(p, aMethodCall);
   nsresult rv = detail::DispatchMediaPromiseRunnable(aTarget, r);
   MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
-  return p;
+  return Move(p);
 }
 
 } // namespace detail
 
 template<typename PromiseType, typename TargetType, typename ThisType>
 static nsRefPtr<PromiseType>
 ProxyMediaCall(TargetType* aTarget, ThisType* aThisVal, const char* aCallerName,
                nsRefPtr<PromiseType>(ThisType::*aMethod)())
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -77,16 +77,17 @@ CopySessions(const nsAString& aKey,
 }
 
 static PLDHashOperator
 CloseSessions(const nsAString& aKey,
               nsRefPtr<MediaKeySession>& aSession,
               void* aClosure)
 {
   aSession->OnClosed();
+  ((MediaKeys*)aClosure)->Release();
   return PL_DHASH_NEXT;
 }
 
 void
 MediaKeys::Terminated()
 {
   KeySessionHashMap keySessions;
   // Remove entries during iteration will screw it. Make a copy first.
@@ -106,17 +107,19 @@ MediaKeys::Terminated()
 void
 MediaKeys::Shutdown()
 {
   if (mProxy) {
     mProxy->Shutdown();
     mProxy = nullptr;
   }
 
-  mPromises.Enumerate(&RejectPromises, nullptr);
+  nsRefPtr<MediaKeys> kungFuDeathGrip = this;
+
+  mPromises.Enumerate(&RejectPromises, this);
   mPromises.Clear();
 }
 
 nsPIDOMWindow*
 MediaKeys::GetParentObject() const
 {
   return mParent;
 }
@@ -164,26 +167,32 @@ MediaKeys::MakePromise(ErrorResult& aRv)
 }
 
 PromiseId
 MediaKeys::StorePromise(Promise* aPromise)
 {
   static uint32_t sEMEPromiseCount = 1;
   MOZ_ASSERT(aPromise);
   uint32_t id = sEMEPromiseCount++;
+
+  // Keep MediaKeys alive for the lifetime of its promises. Any still-pending
+  // promises are rejected in Shutdown().
+  AddRef();
+
   mPromises.Put(id, aPromise);
   return id;
 }
 
 already_AddRefed<Promise>
 MediaKeys::RetrievePromise(PromiseId aId)
 {
   MOZ_ASSERT(mPromises.Contains(aId));
   nsRefPtr<Promise> promise;
   mPromises.Remove(aId, getter_AddRefs(promise));
+  Release();
   return promise.forget();
 }
 
 void
 MediaKeys::RejectPromise(PromiseId aId, nsresult aExceptionCode)
 {
   nsRefPtr<Promise> promise(RetrievePromise(aId));
   if (!promise) {
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -551,20 +551,17 @@ nsRefPtr<MediaDecoderReader::VideoDataPr
 MP4Reader::RequestVideoData(bool aSkipToNextKeyframe,
                             int64_t aTimeThreshold)
 {
   MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
   VLOG("RequestVideoData skip=%d time=%lld", aSkipToNextKeyframe, aTimeThreshold);
 
   if (mShutdown) {
     NS_WARNING("RequestVideoData on shutdown MP4Reader!");
-    MonitorAutoLock lock(mVideo.mMonitor);
-    nsRefPtr<VideoDataPromise> p = mVideo.mPromise.Ensure(__func__);
-    p->Reject(CANCELED, __func__);
-    return p;
+    return VideoDataPromise::CreateAndReject(CANCELED, __func__);
   }
 
   MOZ_ASSERT(HasVideo() && mPlatform && mVideo.mDecoder);
 
   bool eos = false;
   if (ShouldSkip(aSkipToNextKeyframe, aTimeThreshold)) {
     uint32_t parsed = 0;
     eos = !SkipVideoDemuxToNextKeyFrame(aTimeThreshold, parsed);
@@ -585,24 +582,24 @@ MP4Reader::RequestVideoData(bool aSkipTo
   return p;
 }
 
 nsRefPtr<MediaDecoderReader::AudioDataPromise>
 MP4Reader::RequestAudioData()
 {
   MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
   VLOG("RequestAudioData");
+  if (mShutdown) {
+    NS_WARNING("RequestAudioData on shutdown MP4Reader!");
+    return AudioDataPromise::CreateAndReject(CANCELED, __func__);
+  }
+
   MonitorAutoLock lock(mAudio.mMonitor);
   nsRefPtr<AudioDataPromise> p = mAudio.mPromise.Ensure(__func__);
-  if (!mShutdown) {
-    ScheduleUpdate(kAudio);
-  } else {
-    NS_WARNING("RequestAudioData on shutdown MP4Reader!");
-    p->Reject(CANCELED, __func__);
-  }
+  ScheduleUpdate(kAudio);
   return p;
 }
 
 void
 MP4Reader::ScheduleUpdate(TrackType aTrack)
 {
   auto& decoder = GetDecoderData(aTrack);
   decoder.mMonitor.AssertCurrentThreadOwns();
--- a/dom/media/gmp-plugin/gmp-test-decryptor.cpp
+++ b/dom/media/gmp-plugin/gmp-test-decryptor.cpp
@@ -266,27 +266,30 @@ private:
 
 static const string OpenAgainRecordId = "open-again-record-id";
 
 class OpenedSecondTimeContinuation : public OpenContinuation {
 public:
   explicit OpenedSecondTimeContinuation(GMPRecord* aRecord,
                                         TestManager* aTestManager,
                                         const string& aTestID)
-    : mRecord(aRecord), mTestmanager(aTestManager), mTestID(aTestID) {}
+    : mRecord(aRecord), mTestmanager(aTestManager), mTestID(aTestID) {
+    MOZ_ASSERT(aRecord);
+  }
 
   virtual void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) MOZ_OVERRIDE {
     if (GMP_SUCCEEDED(aStatus)) {
       FakeDecryptor::Message("FAIL OpenSecondTimeContinuation should not be able to re-open record.");
     }
-
+    if (aRecord) {
+      aRecord->Close();
+    }
     // Succeeded, open should have failed.
     mTestmanager->EndTest(mTestID);
     mRecord->Close();
-    delete this;
   }
 
 private:
   GMPRecord* mRecord;
   TestManager* const mTestmanager;
   const string mTestID;
 };
 
@@ -296,22 +299,24 @@ public:
                               TestManager* aTestManager,
                               const string& aTestID)
     : mID(aID), mTestmanager(aTestManager), mTestID(aTestID) {}
 
   virtual void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) MOZ_OVERRIDE {
     if (GMP_FAILED(aStatus)) {
       FakeDecryptor::Message("FAIL OpenAgainContinuation to open record initially.");
       mTestmanager->EndTest(mTestID);
+      if (aRecord) {
+        aRecord->Close();
+      }
       return;
     }
 
     auto cont = new OpenedSecondTimeContinuation(aRecord, mTestmanager, mTestID);
     GMPOpenRecord(mID, cont);
-    delete this;
   }
 
 private:
   const string mID;
   TestManager* const mTestmanager;
   const string mTestID;
 };
 
--- a/dom/media/gmp-plugin/gmp-test-storage.cpp
+++ b/dom/media/gmp-plugin/gmp-test-storage.cpp
@@ -166,54 +166,68 @@ GMPErr
 GMPRunOnMainThread(GMPTask* aTask)
 {
   MOZ_ASSERT(g_platform_api);
   return g_platform_api->runonmainthread(aTask);
 }
 
 class OpenRecordClient : public GMPRecordClient {
 public:
-  GMPErr Init(GMPRecord* aRecord,
-              OpenContinuation* aContinuation) {
-    mRecord = aRecord;
-    mContinuation = aContinuation;
-    return mRecord->Open();
+  /*
+   * This function will take the memory ownership of the parameters and
+   * delete them when done.
+   */
+  static void Open(const std::string& aRecordName,
+            OpenContinuation* aContinuation) {
+    MOZ_ASSERT(aContinuation);
+    (new OpenRecordClient(aContinuation))->Do(aRecordName);
   }
 
   virtual void OpenComplete(GMPErr aStatus) MOZ_OVERRIDE {
-    mContinuation->OpenComplete(aStatus, mRecord);
-    delete this;
+    Done(aStatus);
   }
 
   virtual void ReadComplete(GMPErr aStatus,
                             const uint8_t* aData,
-                            uint32_t aDataSize) MOZ_OVERRIDE { }
+                            uint32_t aDataSize) MOZ_OVERRIDE {
+    MOZ_CRASH("Should not reach here.");
+  }
 
-  virtual void WriteComplete(GMPErr aStatus) MOZ_OVERRIDE { }
+  virtual void WriteComplete(GMPErr aStatus) MOZ_OVERRIDE {
+    MOZ_CRASH("Should not reach here.");
+  }
 
 private:
+  explicit OpenRecordClient(OpenContinuation* aContinuation)
+    : mRecord(nullptr), mContinuation(aContinuation) {}
+
+  void Do(const std::string& aName) {
+    auto err = GMPOpenRecord(aName.c_str(), aName.size(), &mRecord, this);
+    if (GMP_FAILED(err) ||
+        GMP_FAILED(err = mRecord->Open())) {
+      Done(err);
+    }
+  }
+
+  void Done(GMPErr err) {
+    // mContinuation is responsible for closing mRecord.
+    mContinuation->OpenComplete(err, mRecord);
+    delete mContinuation;
+    delete this;
+  }
+
   GMPRecord* mRecord;
   OpenContinuation* mContinuation;
 };
 
-GMPErr
+void
 GMPOpenRecord(const std::string& aRecordName,
               OpenContinuation* aContinuation)
 {
-  MOZ_ASSERT(aContinuation);
-  GMPRecord* record;
-  OpenRecordClient* client = new OpenRecordClient();
-  auto err = GMPOpenRecord(aRecordName.c_str(),
-                           aRecordName.size(),
-                           &record,
-                           client);
-  if (GMP_FAILED(err)) {
-    return err;
-  }
-  return client->Init(record, aContinuation);
+  OpenRecordClient::Open(aRecordName, aContinuation);
 }
 
 GMPErr
 GMPEnumRecordNames(RecvGMPRecordIteratorPtr aRecvIteratorFunc,
                    void* aUserArg)
 {
   return g_platform_api->getrecordenumerator(aRecvIteratorFunc, aUserArg);
 }
--- a/dom/media/gmp-plugin/gmp-test-storage.h
+++ b/dom/media/gmp-plugin/gmp-test-storage.h
@@ -47,17 +47,17 @@ GMPErr
 GMPRunOnMainThread(GMPTask* aTask);
 
 class OpenContinuation {
 public:
   virtual ~OpenContinuation() {}
   virtual void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) = 0;
 };
 
-GMPErr
+void
 GMPOpenRecord(const std::string& aRecordName,
               OpenContinuation* aContinuation);
 
 GMPErr
 GMPEnumRecordNames(RecvGMPRecordIteratorPtr aRecvIteratorFunc,
                    void* aUserArg);
 
 #endif // TEST_GMP_STORAGE_H__
--- a/dom/media/gmp/GMPDecryptorParent.cpp
+++ b/dom/media/gmp/GMPDecryptorParent.cpp
@@ -264,21 +264,19 @@ GMPDecryptorParent::RecvSessionError(con
   return true;
 }
 
 bool
 GMPDecryptorParent::RecvKeyStatusChanged(const nsCString& aSessionId,
                                          InfallibleTArray<uint8_t>&& aKeyId,
                                          const GMPMediaKeyStatus& aStatus)
 {
-  if (!mIsOpen) {
-    NS_WARNING("Trying to use a dead GMP decrypter!");
-    return false;
+  if (mIsOpen) {
+    mCallback->KeyStatusChanged(aSessionId, aKeyId, aStatus);
   }
-  mCallback->KeyStatusChanged(aSessionId, aKeyId, aStatus);
   return true;
 }
 
 bool
 GMPDecryptorParent::RecvSetCaps(const uint64_t& aCaps)
 {
   if (!mIsOpen) {
     NS_WARNING("Trying to use a dead GMP decrypter!");
--- a/dom/media/gmp/GMPStorageChild.cpp
+++ b/dom/media/gmp/GMPStorageChild.cpp
@@ -104,16 +104,21 @@ GMPStorageChild::CreateRecord(const nsCS
   MonitorAutoLock lock(mMonitor);
 
   if (mShutdown) {
     NS_WARNING("GMPStorage used after it's been shutdown!");
     return GMPClosedErr;
   }
 
   MOZ_ASSERT(aRecordName.Length() && aOutRecord);
+
+  if (HasRecord(aRecordName)) {
+    return GMPRecordInUse;
+  }
+
   nsRefPtr<GMPRecordImpl> record(new GMPRecordImpl(this, aRecordName, aClient));
   mRecords.Put(aRecordName, record); // Addrefs
 
   // The GMPRecord holds a self reference until the GMP calls Close() on
   // it. This means the object is always valid (even if neutered) while
   // the GMP expects it to be.
   record.forget(aOutRecord);
 
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -185,86 +185,88 @@ TrackBuffer::AppendData(LargeDataBuffer*
   int64_t start = 0, end = 0;
   bool gotMedia = mParser->ParseStartAndEndTimestamps(aData, start, end);
   bool gotInit = mParser->HasCompleteInitData();
 
   if (newInitData) {
     if (!gotInit) {
       // We need a new decoder, but we can't initialize it yet.
       nsRefPtr<SourceBufferDecoder> decoder =
-        NewDecoder(aTimestampOffset - mAdjustedTimestamp);
+        NewDecoder(aTimestampOffset);
       // The new decoder is stored in mDecoders/mCurrentDecoder, so we
       // don't need to do anything with 'decoder'. It's only a placeholder.
       if (!decoder) {
         mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
         return p;
       }
     } else {
-      if (!decoders.NewDecoder(aTimestampOffset - mAdjustedTimestamp)) {
+      if (!decoders.NewDecoder(aTimestampOffset)) {
         mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
         return p;
       }
     }
   } else if (!hadCompleteInitData && gotInit) {
     MOZ_ASSERT(mCurrentDecoder);
     // Queue pending decoder for initialization now that we have a full
     // init segment.
     decoders.AppendElement(mCurrentDecoder);
   }
 
   if (gotMedia) {
-    if (mLastEndTimestamp &&
+    if (mParser->IsMediaSegmentPresent(aData) && mLastEndTimestamp &&
         (!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp.value()) ||
          mLastTimestampOffset != aTimestampOffset ||
          mDecoderPerSegment ||
          (mCurrentDecoder && mCurrentDecoder->WasTrimmed()))) {
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
                 this, mLastStartTimestamp, mLastEndTimestamp.value(), start, end);
 
       if (!newInitData) {
         // This data is earlier in the timeline than data we have already
         // processed or not continuous, so we must create a new decoder
         // to handle the decoding.
         if (!hadCompleteInitData ||
-            !decoders.NewDecoder(aTimestampOffset - mAdjustedTimestamp)) {
+            !decoders.NewDecoder(aTimestampOffset)) {
           mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
           return p;
         }
         MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this);
         AppendDataToCurrentResource(oldInit, 0);
       }
       mLastStartTimestamp = start;
     } else {
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
                 this, mLastStartTimestamp, mLastEndTimestamp ? mLastEndTimestamp.value() : 0, start, end);
     }
     mLastEndTimestamp.reset();
     mLastEndTimestamp.emplace(end);
   }
 
-  if (gotMedia && start > 0 &&
-      (start < FUZZ_TIMESTAMP_OFFSET || start < mAdjustedTimestamp)) {
+  if (gotMedia && start != mAdjustedTimestamp &&
+      ((start < 0 && -start < FUZZ_TIMESTAMP_OFFSET && start < mAdjustedTimestamp) ||
+       (start > 0 && (start < FUZZ_TIMESTAMP_OFFSET || start < mAdjustedTimestamp)))) {
     AdjustDecodersTimestampOffset(mAdjustedTimestamp - start);
     mAdjustedTimestamp = start;
   }
 
   if (!AppendDataToCurrentResource(aData, end - start)) {
     mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
     return p;
   }
 
   if (decoders.Length()) {
     // We're going to have to wait for the decoder to initialize, the promise
     // will be resolved once initialization completes.
     return p;
-  } else if (gotMedia) {
-    // Tell our reader that we have more data to ensure that playback starts if
-    // required when data is appended.
-    mParentDecoder->GetReader()->MaybeNotifyHaveData();
   }
+
+  // Tell our reader that we have more data to ensure that playback starts if
+  // required when data is appended.
+  mParentDecoder->GetReader()->MaybeNotifyHaveData();
+
   mInitializationPromise.Resolve(gotMedia, __func__);
   return p;
 }
 
 bool
 TrackBuffer::AppendDataToCurrentResource(LargeDataBuffer* aData, uint32_t aDuration)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -450,17 +452,18 @@ TrackBuffer::Buffered(dom::TimeRanges* a
 already_AddRefed<SourceBufferDecoder>
 TrackBuffer::NewDecoder(int64_t aTimestampOffset)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mParentDecoder);
 
   DiscardCurrentDecoder();
 
-  nsRefPtr<SourceBufferDecoder> decoder = mParentDecoder->CreateSubDecoder(mType, aTimestampOffset);
+  nsRefPtr<SourceBufferDecoder> decoder =
+    mParentDecoder->CreateSubDecoder(mType, aTimestampOffset - mAdjustedTimestamp);
   if (!decoder) {
     return nullptr;
   }
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
   mCurrentDecoder = decoder;
   mDecoders.AppendElement(decoder);
 
   mLastStartTimestamp = 0;
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -1867,17 +1867,17 @@ nsNPObjWrapper::GetNewOrUsed(NPP npp, JS
   if (!sNPObjWrappers.IsInitialized()) {
     // No hash yet (or any more), initialize it.
     if (!CreateNPObjWrapperTable()) {
       return nullptr;
     }
   }
 
   NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
-    (PL_DHashTableAdd(&sNPObjWrappers, npobj, fallible));
+    (PL_DHashTableAdd(&sNPObjWrappers, npobj));
 
   if (!entry) {
     // Out of memory
     JS_ReportOutOfMemory(cx);
 
     return nullptr;
   }
 
@@ -2030,17 +2030,17 @@ static NPP
 LookupNPP(NPObject *npobj)
 {
   if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
     nsJSObjWrapper* o = static_cast<nsJSObjWrapper*>(npobj);
     return o->mNpp;
   }
 
   NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
-    (PL_DHashTableAdd(&sNPObjWrappers, npobj, fallible));
+    (PL_DHashTableAdd(&sNPObjWrappers, npobj));
 
   if (!entry) {
     return nullptr;
   }
 
   NS_ASSERTION(entry->mNpp, "Live NPObject entry w/o an NPP!");
 
   return entry->mNpp;
--- a/dom/tests/mochitest/webcomponents/mochitest.ini
+++ b/dom/tests/mochitest/webcomponents/mochitest.ini
@@ -5,16 +5,17 @@ support-files =
 [test_bug900724.html]
 [test_bug1017896.html]
 [test_content_element.html]
 [test_custom_element_adopt_callbacks.html]
 [test_custom_element_callback_innerhtml.html]
 [test_custom_element_clone_callbacks.html]
 [test_custom_element_clone_callbacks_extended.html]
 [test_custom_element_import_node_created_callback.html]
+[test_custom_element_in_shadow.html]
 [test_nested_content_element.html]
 [test_dest_insertion_points.html]
 [test_dest_insertion_points_shadow.html]
 [test_fallback_dest_insertion_points.html]
 [test_detached_style.html]
 [test_dynamic_content_element_matching.html]
 [test_document_register.html]
 [test_document_register_base_queue.html]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_in_shadow.html
@@ -0,0 +1,132 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1087460
+-->
+<head>
+  <title>Test for custom element callbacks in shadow DOM.</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1087460">Bug 1087460</a>
+<div id="container"></div>
+
+<script>
+
+// Test callback for custom element when used after registration.
+
+var createdCallbackCount = 0;
+var attachedCallbackCount = 0;
+var detachedCallbackCount = 0;
+var attributeChangedCallbackCount = 0;
+
+var p1 = Object.create(HTMLElement.prototype);
+
+p1.createdCallback = function() {
+  createdCallbackCount++;
+};
+
+p1.attachedCallback = function() {
+  attachedCallbackCount++;
+};
+
+p1.detachedCallback = function() {
+  detachedCallbackCount++;
+};
+
+p1.attributeChangedCallback = function(name, oldValue, newValue) {
+  attributeChangedCallbackCount++;
+};
+
+document.registerElement("x-foo", { prototype: p1 });
+
+var container = document.getElementById("container");
+var shadow = container.createShadowRoot();
+
+is(createdCallbackCount, 0, "createdCallback should not be called more than once in this test.");
+var customElem = document.createElement("x-foo");
+is(createdCallbackCount, 1, "createdCallback should be called after creating custom element.");
+
+is(attributeChangedCallbackCount, 0, "attributeChangedCallback should not be called after just creating an element.");
+customElem.setAttribute("data-foo", "bar");
+is(attributeChangedCallbackCount, 1, "attributeChangedCallback should be called after setting an attribute.");
+
+is(attachedCallbackCount, 0, "attachedCallback should not be called on an element that is not in a document/composed document.");
+shadow.appendChild(customElem);
+is(attachedCallbackCount, 1, "attachedCallback should be called after attaching custom element to the composed document.");
+
+is(detachedCallbackCount, 0, "detachedCallback should not be called without detaching custom element.");
+shadow.removeChild(customElem);
+is(detachedCallbackCount, 1, "detachedCallback should be called after detaching custom element from the composed document.");
+
+// Test callback for custom element already in the composed doc when created.
+
+createdCallbackCount = 0;
+attachedCallbackCount = 0;
+detachedCallbackCount = 0;
+attributeChangedCallbackCount = 0;
+
+shadow.innerHTML = "<x-foo></x-foo>";
+is(createdCallbackCount, 1, "createdCallback should be called after creating a custom element.");
+is(attachedCallbackCount, 1, "attachedCallback should be called after creating an element in the composed document.");
+
+shadow.innerHTML = "";
+is(detachedCallbackCount, 1, "detachedCallback should be called after detaching custom element from the composed document.");
+
+// Test callback for custom element in shadow DOM when host attached/detached to/from document.
+
+createdCallbackCount = 0;
+attachedCallbackCount = 0;
+detachedCallbackCount = 0;
+attributeChangedCallbackCount = 0;
+
+var host = document.createElement("div");
+shadow = host.createShadowRoot();
+customElem = document.createElement("x-foo");
+
+is(attachedCallbackCount, 0, "attachedCallback should not be called on newly created element.");
+shadow.appendChild(customElem);
+is(attachedCallbackCount, 0, "attachedCallback should not be called on attaching to a tree that is not in the composed document.");
+
+is(attachedCallbackCount, 0, "detachedCallback should not be called.");
+shadow.removeChild(customElem);
+is(detachedCallbackCount, 0, "detachedCallback should not be called when detaching from a tree that is not in the composed document.");
+
+shadow.appendChild(customElem);
+is(attachedCallbackCount, 0, "attachedCallback should still not be called after reattaching to a shadow tree that is not in the composed document.");
+
+container.appendChild(host);
+is(attachedCallbackCount, 1, "attachedCallback should be called after host is inserted into document.");
+
+container.removeChild(host);
+is(detachedCallbackCount, 1, "detachedCallback should be called after host is removed from document.");
+
+// Test callback for custom element for upgraded element.
+
+createdCallbackCount = 0;
+attachedCallbackCount = 0;
+detachedCallbackCount = 0;
+attributeChangedCallbackCount = 0;
+
+shadow = container.shadowRoot;
+shadow.innerHTML = "<x-bar></x-bar>";
+
+var p2 = Object.create(HTMLElement.prototype);
+
+p2.createdCallback = function() {
+  createdCallbackCount++;
+};
+
+p2.attachedCallback = function() {
+  attachedCallbackCount++;
+};
+
+document.registerElement("x-bar", { prototype: p2 });
+is(createdCallbackCount, 1, "createdCallback should be called after registering element.");
+is(attachedCallbackCount, 1, "attachedCallback should be called after upgrading element in composed document.");
+
+</script>
+
+</body>
+</html>
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -1991,17 +1991,17 @@ ServiceWorkerManager::CreateServiceWorke
   // Alternatively we could persist the original load group values and use
   // them here.
   rv = NS_NewLoadGroup(getter_AddRefs(info.mLoadGroup), info.mPrincipal);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsRefPtr<ServiceWorker> serviceWorker;
-  RuntimeService* rs = RuntimeService::GetService();
+  RuntimeService* rs = RuntimeService::GetOrCreateService();
   if (!rs) {
     return NS_ERROR_FAILURE;
   }
 
   AutoJSAPI jsapi;
   jsapi.Init();
   rv = rs->CreateServiceWorkerFromLoadInfo(jsapi.cx(), &info,
                                            NS_ConvertUTF8toUTF16(aScriptSpec),
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -30,17 +30,17 @@ var ecmaGlobals =
     "Float32Array",
     "Float64Array",
     "Function",
     "Infinity",
     "Int16Array",
     "Int32Array",
     "Int8Array",
     "InternalError",
-    {name: "Intl", desktop: true},
+    {name: "Intl", b2g: false, android: false},
     "Iterator",
     "JSON",
     "Map",
     "Math",
     "NaN",
     "Number",
     "Object",
     "Proxy",
@@ -162,31 +162,32 @@ var interfaceNamesInGlobalScope =
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "WorkerLocation",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "WorkerNavigator",
 // IMPORTANT: Do not change this list without review from a DOM peer!
   ];
 // IMPORTANT: Do not change the list above without review from a DOM peer!
 
-function createInterfaceMap(prefMap, permissionMap, version, userAgent) {
+function createInterfaceMap(prefMap, permissionMap, version, userAgent, isB2G) {
   var isNightly = version.endsWith("a1");
   var isRelease = !version.contains("a");
   var isDesktop = !/Mobile|Tablet/.test(userAgent);
-  var isB2G = !isDesktop && !userAgent.contains("Android");
+  var isAndroid = !!navigator.userAgent.contains("Android");
 
   var interfaceMap = {};
 
   function addInterfaces(interfaces)
   {
     for (var entry of interfaces) {
       if (typeof(entry) === "string") {
         interfaceMap[entry] = true;
       } else if ((entry.nightly === !isNightly) ||
                  (entry.desktop === !isDesktop) ||
+                 (entry.android === !isAndroid) ||
                  (entry.b2g === !isB2G) ||
                  (entry.release === !isRelease) ||
                  (entry.pref && !prefMap[entry.pref])  ||
                  (entry.permission && !permissionMap[entry.permission])) {
         interfaceMap[entry.name] = false;
       } else {
         interfaceMap[entry.name] = true;
       }
@@ -194,18 +195,18 @@ function createInterfaceMap(prefMap, per
   }
 
   addInterfaces(ecmaGlobals);
   addInterfaces(interfaceNamesInGlobalScope);
 
   return interfaceMap;
 }
 
-function runTest(prefMap, permissionMap, version, userAgent) {
-  var interfaceMap = createInterfaceMap(prefMap, permissionMap, version, userAgent);
+function runTest(prefMap, permissionMap, version, userAgent, isB2G) {
+  var interfaceMap = createInterfaceMap(prefMap, permissionMap, version, userAgent, isB2G);
   for (var name of Object.getOwnPropertyNames(self)) {
     // An interface name should start with an upper case character.
     if (!/^[A-Z]/.test(name)) {
       continue;
     }
     ok(interfaceMap[name],
        "If this is failing: DANGER, are you sure you want to expose the new interface " + name +
        " to all webpages as a property on the worker? Do not make a change to this file without a " +
@@ -247,14 +248,16 @@ function appendPermissions(permissions, 
 var permissions = [];
 appendPermissions(permissions, ecmaGlobals);
 appendPermissions(permissions, interfaceNamesInGlobalScope);
 
 workerTestGetPrefs(prefs, function(prefMap) {
   workerTestGetPermissions(permissions, function(permissionMap) {
     workerTestGetVersion(function(version) {
       workerTestGetUserAgent(function(userAgent) {
-        runTest(prefMap, permissionMap, version, userAgent);
-        workerTestDone();
+        workerTestGetIsB2G(function(isB2G) {
+          runTest(prefMap, permissionMap, version, userAgent, isB2G);
+          workerTestDone();
+	});
       });
     });
   });
 });
--- a/dom/workers/test/worker_driver.js
+++ b/dom/workers/test/worker_driver.js
@@ -71,16 +71,21 @@ function workerTestExec(script) {
         type: 'returnUserAgent',
         result: navigator.userAgent
       });
     } else if (event.data.type == 'getOSCPU') {
       worker.postMessage({
         type: 'returnOSCPU',
         result: navigator.oscpu
       });
+    } else if (event.data.type == 'getIsB2G') {
+      worker.postMessage({
+        type: 'returnIsB2G',
+        result: SpecialPowers.isB2G
+      });
     }
   }
 
   worker.onerror = function(event) {
     ok(false, 'Worker had an error: ' + event.data);
     SimpleTest.finish();
   };
 
--- a/dom/workers/test/worker_wrapper.js
+++ b/dom/workers/test/worker_wrapper.js
@@ -94,16 +94,29 @@ function workerTestGetOSCPU(cb) {
     removeEventListener('message', workerTestGetOSCPUCB);
     cb(e.data.result);
   });
   postMessage({
     type: 'getOSCPU'
   });
 }
 
+function workerTestGetIsB2G(cb) {
+  addEventListener('message', function workerTestGetIsB2GCB(e) {
+    if (e.data.type !== 'returnIsB2G') {
+      return;
+    }
+    removeEventListener('message', workerTestGetIsB2GCB);
+    cb(e.data.result);
+  });
+  postMessage({
+    type: 'getIsB2G'
+  });
+}
+
 addEventListener('message', function workerWrapperOnMessage(e) {
   removeEventListener('message', workerWrapperOnMessage);
   var data = e.data;
   try {
     importScripts(data.script);
   } catch(e) {
     postMessage({
       type: 'status',
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -211,18 +211,17 @@ XULDocument::~XULDocument()
     // decls never got resolved.
     mForwardReferences.Clear();
     // Likewise for any references we have to IDs where we might
     // look for persisted data:
     mPersistenceIds.Clear();
 
     // Destroy our broadcaster map.
     if (mBroadcasterMap) {
-        PL_DHashTableFinish(mBroadcasterMap);
-        delete mBroadcasterMap;
+        PL_DHashTableDestroy(mBroadcasterMap);
     }
 
     delete mTemplateBuilderTable;
 
     Preferences::UnregisterCallback(XULDocument::DirectionChanged,
                                     "intl.uidirection.", this);
 
     if (mOffThreadCompileStringBuf) {
@@ -764,27 +763,32 @@ XULDocument::AddBroadcastListenerFor(Ele
         PL_DHashVoidPtrKeyStub,
         PL_DHashMatchEntryStub,
         PL_DHashMoveEntryStub,
         ClearBroadcasterMapEntry,
         nullptr
     };
 
     if (! mBroadcasterMap) {
-        mBroadcasterMap = new PLDHashTable();
-        PL_DHashTableInit(mBroadcasterMap, &gOps, sizeof(BroadcasterMapEntry));
+        mBroadcasterMap = PL_NewDHashTable(&gOps, sizeof(BroadcasterMapEntry));
+
+        if (! mBroadcasterMap) {
+            aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+            return;
+        }
     }
 
     BroadcasterMapEntry* entry =
         static_cast<BroadcasterMapEntry*>
                    (PL_DHashTableSearch(mBroadcasterMap, &aBroadcaster));
 
     if (!entry) {
-        entry = static_cast<BroadcasterMapEntry*>
-            (PL_DHashTableAdd(mBroadcasterMap, &aBroadcaster, fallible));
+        entry =
+            static_cast<BroadcasterMapEntry*>
+                       (PL_DHashTableAdd(mBroadcasterMap, &aBroadcaster));
 
         if (! entry) {
             aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
             return;
         }
 
         entry->mBroadcaster = &aBroadcaster;
 
--- a/dom/xul/templates/nsContentSupportMap.h
+++ b/dom/xul/templates/nsContentSupportMap.h
@@ -23,18 +23,17 @@ class nsContentSupportMap {
 public:
     nsContentSupportMap() { Init(); }
     ~nsContentSupportMap() { Finish(); }
 
     nsresult Put(nsIContent* aElement, nsTemplateMatch* aMatch) {
         if (!mMap.IsInitialized())
             return NS_ERROR_NOT_INITIALIZED;
 
-        PLDHashEntryHdr* hdr =
-            PL_DHashTableAdd(&mMap, aElement, mozilla::fallible);
+        PLDHashEntryHdr* hdr = PL_DHashTableAdd(&mMap, aElement);
         if (!hdr)
             return NS_ERROR_OUT_OF_MEMORY;
 
         Entry* entry = static_cast<Entry*>(hdr);
         NS_ASSERTION(entry->mMatch == nullptr, "over-writing entry");
         entry->mContent = aElement;
         entry->mMatch   = aMatch;
         return NS_OK;
--- a/dom/xul/templates/nsTemplateMap.h
+++ b/dom/xul/templates/nsTemplateMap.h
@@ -32,18 +32,17 @@ public:
 
     ~nsTemplateMap() { Finish(); }
 
     void
     Put(nsIContent* aContent, nsIContent* aTemplate) {
         NS_ASSERTION(!PL_DHashTableSearch(&mTable, aContent),
                      "aContent already in map");
 
-        Entry* entry = static_cast<Entry*>
-            (PL_DHashTableAdd(&mTable, aContent, fallible));
+        Entry* entry = static_cast<Entry*>(PL_DHashTableAdd(&mTable, aContent));
 
         if (entry) {
             entry->mContent = aContent;
             entry->mTemplate = aTemplate;
         }
     }
 
     void
--- a/embedding/components/commandhandler/nsCommandParams.cpp
+++ b/embedding/components/commandhandler/nsCommandParams.cpp
@@ -225,18 +225,17 @@ nsCommandParams::GetOrMakeEntry(const ch
 {
   HashEntry *foundEntry =
     (HashEntry *)PL_DHashTableSearch(&mValuesHash, (void *)aName);
   if (foundEntry) { // reuse existing entry
     foundEntry->Reset(entryType);
     return foundEntry;
   }
 
-  foundEntry = static_cast<HashEntry*>
-    (PL_DHashTableAdd(&mValuesHash, (void *)aName, fallible));
+  foundEntry = (HashEntry *)PL_DHashTableAdd(&mValuesHash, (void *)aName);
   if (!foundEntry) {
     return nullptr;
   }
 
   // Use placement new. Our ctor does not clobber keyHash, which is important.
   new (foundEntry) HashEntry(entryType, aName);
   return foundEntry;
 }
--- a/gfx/layers/TiledLayerBuffer.h
+++ b/gfx/layers/TiledLayerBuffer.h
@@ -7,16 +7,17 @@
 
 // Debug defines
 //#define GFX_TILEDLAYER_DEBUG_OVERLAY
 //#define GFX_TILEDLAYER_PREF_WARNINGS
 
 #include <stdint.h>                     // for uint16_t, uint32_t
 #include <sys/types.h>                  // for int32_t
 #include "gfxPlatform.h"                // for GetTileWidth/GetTileHeight
+#include "mozilla/gfx/Logging.h"        // for gfxCriticalError
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsRect.h"                     // for nsIntRect
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nsTArray.h"                   // for nsTArray
 
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
 #include <ui/Fence.h>
@@ -335,31 +336,33 @@ TiledLayerBuffer<Derived, Tile>::Dump(st
       }
       y += h;
     }
     x += w;
   }
 }
 
 template<typename Derived, typename Tile> void
-TiledLayerBuffer<Derived, Tile>::Update(const nsIntRegion& aNewValidRegion,
+TiledLayerBuffer<Derived, Tile>::Update(const nsIntRegion& newValidRegion,
                                         const nsIntRegion& aPaintRegion)
 {
   gfx::IntSize scaledTileSize = GetScaledTileSize();
 
   nsTArray<Tile>  newRetainedTiles;
   nsTArray<Tile>& oldRetainedTiles = mRetainedTiles;
   const nsIntRect oldBound = mValidRegion.GetBounds();
-  const nsIntRect newBound = aNewValidRegion.GetBounds();
+  const nsIntRect newBound = newValidRegion.GetBounds();
   const nsIntPoint oldBufferOrigin(RoundDownToTileEdge(oldBound.x, scaledTileSize.width),
                                    RoundDownToTileEdge(oldBound.y, scaledTileSize.height));
   const nsIntPoint newBufferOrigin(RoundDownToTileEdge(newBound.x, scaledTileSize.width),
                                    RoundDownToTileEdge(newBound.y, scaledTileSize.height));
+
+  // This is the reason we break the style guide with newValidRegion instead
+  // of aNewValidRegion - so that the names match better and code easier to read
   const nsIntRegion& oldValidRegion = mValidRegion;
-  const nsIntRegion& newValidRegion = aNewValidRegion;
   const int oldRetainedHeight = mRetainedHeight;
 
   // Pass 1: Recycle valid content from the old buffer
   // Recycle tiles from the old buffer that contain valid regions.
   // Insert placeholders tiles if we have no valid area for that tile
   // which we will allocate in pass 2.
   // TODO: Add a tile pool to reduce new allocation
   int tileX = 0;
@@ -441,22 +444,33 @@ TiledLayerBuffer<Derived, Tile>::Update(
     if (oldTileCount >= tilesMissing) {
       oldRetainedTiles[i] = AsDerived().GetPlaceholderTile();
       AsDerived().ReleaseTile(oldTile);
     } else {
       oldTileCount ++;
     }
   }
 
-  MOZ_ASSERT(aNewValidRegion.Contains(aPaintRegion), "Painting a region outside the visible region");
+  if (!newValidRegion.Contains(aPaintRegion)) {
+    gfxCriticalError() << "Painting outside visible:"
+		       << " paint " << aPaintRegion.ToString().get()
+                       << " old valid " << oldValidRegion.ToString().get()
+                       << " new valid " << newValidRegion.ToString().get();
+  }
 #ifdef DEBUG
   nsIntRegion oldAndPainted(oldValidRegion);
   oldAndPainted.Or(oldAndPainted, aPaintRegion);
+  if (!oldAndPainted.Contains(newValidRegion)) {
+    gfxCriticalError() << "Not fully painted:"
+		       << " paint " << aPaintRegion.ToString().get()
+                       << " old valid " << oldValidRegion.ToString().get()
+                       << " old painted " << oldAndPainted.ToString().get()
+                       << " new valid " << newValidRegion.ToString().get();
+  }
 #endif
-  MOZ_ASSERT(oldAndPainted.Contains(newValidRegion), "newValidRegion has not been fully painted");
 
   nsIntRegion regionToPaint(aPaintRegion);
 
   // Pass 2: Validate
   // We know at this point that any tile in the new buffer that had valid content
   // from the previous buffer is placed correctly in the new buffer.
   // We know that any tile in the old buffer that isn't a place holder is
   // of no use and can be recycled.
@@ -548,16 +562,16 @@ TiledLayerBuffer<Derived, Tile>::Update(
   AsDerived().PostValidate(aPaintRegion);
   for (unsigned int i = 0; i < mRetainedTiles.Length(); ++i) {
     AsDerived().UnlockTile(mRetainedTiles[i]);
   }
 
   // At this point, oldTileCount should be zero
   MOZ_ASSERT(oldTileCount == 0, "Failed to release old tiles");
 
-  mValidRegion = aNewValidRegion;
+  mValidRegion = newValidRegion;
   mPaintedRegion.Or(mPaintedRegion, aPaintRegion);
 }
 
 } // layers
 } // mozilla
 
 #endif // GFX_TILEDLAYERBUFFER_H
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -683,18 +683,19 @@ public:
             }
             uint32_t timestamp = strtoul(beginning, nullptr, 10);
             beginning = end + 1;
             if (!(end = strchr(beginning, ';'))) {
                 break;
             }
             uint32_t filesize = strtoul(beginning, nullptr, 10);
 
-            FNCMapEntry* mapEntry = static_cast<FNCMapEntry*>
-                (PL_DHashTableAdd(&mMap, filename.get(), fallible));
+            FNCMapEntry* mapEntry =
+                static_cast<FNCMapEntry*>
+                (PL_DHashTableAdd(&mMap, filename.get()));
             if (mapEntry) {
                 mapEntry->mFilename.Assign(filename);
                 mapEntry->mTimestamp = timestamp;
                 mapEntry->mFilesize = filesize;
                 mapEntry->mFaces.Assign(faceList);
                 // entries from the startupcache are marked "non-existing"
                 // until we have confirmed that the file still exists
                 mapEntry->mFileExists = false;
@@ -731,18 +732,19 @@ public:
 
     virtual void
     CacheFileInfo(const nsCString& aFileName, const nsCString& aFaceList,
                   uint32_t aTimestamp, uint32_t aFilesize)
     {
         if (!mMap.IsInitialized()) {
             return;
         }
-        FNCMapEntry* entry = static_cast<FNCMapEntry*>
-            (PL_DHashTableAdd(&mMap, aFileName.get(), fallible));
+        FNCMapEntry* entry =
+            static_cast<FNCMapEntry*>
+            (PL_DHashTableAdd(&mMap, aFileName.get()));
         if (entry) {
             entry->mFilename.Assign(aFileName);
             entry->mTimestamp = aTimestamp;
             entry->mFilesize = aFilesize;
             entry->mFaces.Assign(aFaceList);
             entry->mFileExists = true;
         }
         mWriteNeeded = true;
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -1179,16 +1179,17 @@ RasterImage::OnImageDataComplete(nsIRequ
 
 void
 RasterImage::NotifyForDecodeOnDrawOnly()
 {
   if (!NS_IsMainThread()) {
     nsCOMPtr<nsIRunnable> runnable =
       NS_NewRunnableMethod(this, &RasterImage::NotifyForDecodeOnDrawOnly);
     NS_DispatchToMainThread(runnable);
+    return;
   }
 
   NotifyProgress(FLAG_DECODE_STARTED | FLAG_ONLOAD_BLOCKED);
 }
 
 nsresult
 RasterImage::OnImageDataAvailable(nsIRequest*,
                                   nsISupports*,
--- a/image/src/SurfaceCache.cpp
+++ b/image/src/SurfaceCache.cpp
@@ -6,17 +6,17 @@
 /**
  * SurfaceCache is a service for caching temporary surfaces in imagelib.
  */
 
 #include "SurfaceCache.h"
 
 #include <algorithm>
 #include "mozilla/Assertions.h"
-#include "mozilla/Attributes.h"  // for MOZ_THIS_IN_INITIALIZER_LIST
+#include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Move.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/StaticPtr.h"
 #include "nsIMemoryReporter.h"
 #include "gfx2DGlue.h"
--- a/js/ipc/JavaScriptParent.cpp
+++ b/js/ipc/JavaScriptParent.cpp
@@ -4,18 +4,18 @@
  * 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 "JavaScriptParent.h"
 #include "mozilla/dom/ContentParent.h"
 #include "nsJSUtils.h"
 #include "jsfriendapi.h"
-#include "jsproxy.h"
 #include "jswrapper.h"
+#include "js/Proxy.h"
 #include "HeapAPI.h"
 #include "xpcprivate.h"
 #include "mozilla/Casting.h"
 
 using namespace js;
 using namespace JS;
 using namespace mozilla;
 using namespace mozilla::jsipc;
--- a/js/ipc/WrapperOwner.h
+++ b/js/ipc/WrapperOwner.h
@@ -7,33 +7,33 @@
 
 #ifndef mozilla_jsipc_WrapperOwner_h__
 #define mozilla_jsipc_WrapperOwner_h__
 
 #include "JavaScriptShared.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "js/Class.h"
-#include "jsproxy.h"
+#include "js/Proxy.h"
 
 namespace mozilla {
 namespace jsipc {
 
 class WrapperOwner : public virtual JavaScriptShared
 {
   public:
     typedef mozilla::ipc::IProtocolManager<
                        mozilla::ipc::IProtocol>::ActorDestroyReason
            ActorDestroyReason;
 
     explicit WrapperOwner(JSRuntime *rt);
     bool init();
 
     // Standard internal methods.
-    // (The traps should be in the same order like js/src/jsproxy.h)
+    // (The traps should be in the same order like js/Proxy.h)
     bool getOwnPropertyDescriptor(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
                                   JS::MutableHandle<JSPropertyDescriptor> desc);
     bool defineProperty(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
                         JS::MutableHandle<JSPropertyDescriptor> desc);
     bool ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props);
     bool delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp);
     bool preventExtensions(JSContext *cx, JS::HandleObject proxy, bool *succeeded);
     bool isExtensible(JSContext *cx, JS::HandleObject proxy, bool *extensible);
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -54,16 +54,17 @@ namespace JS {
     D(DEBUG_GC)                                 \
     D(COMPARTMENT_REVIVED)                      \
     D(RESET)                                    \
     D(OUT_OF_NURSERY)                           \
     D(EVICT_NURSERY)                            \
     D(FULL_STORE_BUFFER)                        \
     D(SHARED_MEMORY_LIMIT)                      \
     D(PERIODIC_FULL_GC)                         \
+    D(INCREMENTAL_TOO_SLOW)                     \
                                                 \
     /* These are reserved for future use. */    \
     D(RESERVED0)                                \
     D(RESERVED1)                                \
     D(RESERVED2)                                \
     D(RESERVED3)                                \
     D(RESERVED4)                                \
     D(RESERVED5)                                \
@@ -73,17 +74,16 @@ namespace JS {
     D(RESERVED9)                                \
     D(RESERVED10)                               \
     D(RESERVED11)                               \
     D(RESERVED12)                               \
     D(RESERVED13)                               \
     D(RESERVED14)                               \
     D(RESERVED15)                               \
     D(RESERVED16)                               \
-    D(RESERVED17)                               \
                                                 \
     /* Reasons from Firefox */                  \
     D(DOM_WINDOW_UTILS)                         \
     D(COMPONENT_UTILS)                          \
     D(MEM_PRESSURE)                             \
     D(CC_WAITING)                               \
     D(CC_FORCED)                                \
     D(LOAD_END)                                 \
rename from js/src/jsproxy.h
rename to js/public/Proxy.h
--- a/js/src/jsproxy.h
+++ b/js/public/Proxy.h
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef jsproxy_h
-#define jsproxy_h
+#ifndef js_Proxy_h
+#define js_Proxy_h
 
 #include "mozilla/Maybe.h"
 
 #include "jsfriendapi.h"
 
 #include "js/CallNonGenericMethod.h"
 #include "js/Class.h"
 
@@ -674,9 +674,9 @@ inline void assertEnteredPolicy(JSContex
 {}
 #endif
 
 } /* namespace js */
 
 extern JS_FRIEND_API(JSObject *)
 js_InitProxyClass(JSContext *cx, JS::HandleObject obj);
 
-#endif /* jsproxy_h */
+#endif /* js_Proxy_h */
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -175,356 +175,50 @@ static inline char* js_strdup(const char
  *   operations on the FreeOp provided to the finalizer:
  *
  *     FreeOp::{free_,delete_}
  *
  *   The advantage of these operations is that the memory is batched and freed
  *   on another thread.
  */
 
-#define JS_NEW_BODY(allocator, t, parms)                                       \
-    void *memory = allocator(sizeof(t));                                       \
-    return memory ? new(memory) t parms : nullptr;
-#define JS_MAKE_BODY(newname, T, parms)                                        \
-    T *ptr = newname<T> parms;                                                 \
-    return mozilla::UniquePtr<T, JS::DeletePolicy<T>>(ptr);
-
 /*
- * Given a class which should provide 'new' methods, add
- * JS_DECLARE_NEW_METHODS (see JSContext for a usage example). This
- * adds news with up to 12 parameters. Add more versions of new below if
- * you need more than 12 parameters.
+ * Given a class which should provide a 'new' method, add
+ * JS_DECLARE_NEW_METHODS (see js::MallocProvider for an example).
  *
  * Note: Do not add a ; at the end of a use of JS_DECLARE_NEW_METHODS,
  * or the build will break.
  */
 #define JS_DECLARE_NEW_METHODS(NEWNAME, ALLOCATOR, QUALIFIERS)\
-    template <class T>\
-    QUALIFIERS T *NEWNAME() MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T, ())\
-    }\
-\
-    template <class T, class P1>\
-    QUALIFIERS T *NEWNAME(P1 &&p1) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1)))\
-    }\
-\
-    template <class T, class P1, class P2>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8),\
-                     mozilla::Forward<P9>(p9)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8),\
-                     mozilla::Forward<P9>(p9),\
-                     mozilla::Forward<P10>(p10)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10, class P11>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10, P11 &&p11) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8),\
-                     mozilla::Forward<P9>(p9),\
-                     mozilla::Forward<P10>(p10),\
-                     mozilla::Forward<P11>(p11)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10, class P11, class P12>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10, P11 &&p11, P12 &&p12) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8),\
-                     mozilla::Forward<P9>(p9),\
-                     mozilla::Forward<P10>(p10),\
-                     mozilla::Forward<P11>(p11),\
-                     mozilla::Forward<P12>(p12)))\
-    }\
+    template <class T, typename... Args> \
+    QUALIFIERS T * \
+    NEWNAME(Args&&... args) MOZ_HEAP_ALLOCATOR { \
+        void *memory = ALLOCATOR(sizeof(T)); \
+        return memory \
+               ? new(memory) T(mozilla::Forward<Args>(args)...) \
+               : nullptr; \
+    }
 
 /*
  * Given a class which should provide 'make' methods, add
- * JS_DECLARE_MAKE_METHODS (see JSContext for a usage example).  This method
- * is functionally the same as JS_DECLARE_NEW_METHODS: it just declares methods
- * that return mozilla::UniquePtr instances that will singly-manage ownership
- * of the created object.  This adds makes with up to 12 parameters.  Add more
- * versions below if you need more than 12 parameters.
+ * JS_DECLARE_MAKE_METHODS (see js::MallocProvider for an example).  This
+ * method is functionally the same as JS_DECLARE_NEW_METHODS: it just declares
+ * methods that return mozilla::UniquePtr instances that will singly-manage
+ * ownership of the created object.
  *
  * Note: Do not add a ; at the end of a use of JS_DECLARE_MAKE_METHODS,
  * or the build will break.
  */
 #define JS_DECLARE_MAKE_METHODS(MAKENAME, NEWNAME, QUALIFIERS)\
-    template <class T> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME() MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T, ())\
-    }\
-\
-    template <class T, class P1> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1)))\
-    }\
-\
-    template <class T, class P1, class P2> \
-    QUALIFIERS \
-mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8),\
-                     mozilla::Forward<P9>(p9)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8),\
-                     mozilla::Forward<P9>(p9),\
-                     mozilla::Forward<P10>(p10)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10, class P11> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10, P11 &&p11) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8),\
-                     mozilla::Forward<P9>(p9),\
-                     mozilla::Forward<P10>(p10),\
-                     mozilla::Forward<P11>(p11)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10, class P11, class P12> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10, P11 &&p11, P12 &&p12) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8),\
-                     mozilla::Forward<P9>(p9),\
-                     mozilla::Forward<P10>(p10),\
-                     mozilla::Forward<P11>(p11),\
-                     mozilla::Forward<P12>(p12)))\
-    }\
+    template <class T, typename... Args> \
+    QUALIFIERS mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
+    MAKENAME(Args&&... args) MOZ_HEAP_ALLOCATOR { \
+        T *ptr = NEWNAME<T>(mozilla::Forward<Args>(args)...); \
+        return mozilla::UniquePtr<T, JS::DeletePolicy<T>>(ptr); \
+    }
 
 JS_DECLARE_NEW_METHODS(js_new, js_malloc, static MOZ_ALWAYS_INLINE)
 
 template <class T>
 static MOZ_ALWAYS_INLINE void
 js_delete(T *p)
 {
     if (p) {
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -178,16 +178,124 @@ CaseExpr(ParseNode *pn)
 
 static inline ParseNode *
 CaseBody(ParseNode *pn)
 {
     MOZ_ASSERT(pn->isKind(PNK_CASE) || pn->isKind(PNK_DEFAULT));
     return BinaryRight(pn);
 }
 
+static inline ParseNode *
+BinaryOpLeft(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isBinaryOperation());
+    MOZ_ASSERT(pn->isArity(PN_LIST));
+    MOZ_ASSERT(pn->pn_count == 2);
+    return ListHead(pn);
+}
+
+static inline ParseNode *
+BinaryOpRight(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isBinaryOperation());
+    MOZ_ASSERT(pn->isArity(PN_LIST));
+    MOZ_ASSERT(pn->pn_count == 2);
+    return NextNode(ListHead(pn));
+}
+
+static inline ParseNode *
+BitwiseLeft(ParseNode *pn)
+{
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+BitwiseRight(ParseNode *pn)
+{
+    return BinaryOpRight(pn);
+}
+
+static inline ParseNode *
+MultiplyLeft(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isKind(PNK_STAR));
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+MultiplyRight(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isKind(PNK_STAR));
+    return BinaryOpRight(pn);
+}
+
+static inline ParseNode *
+AddSubLeft(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isKind(PNK_ADD) || pn->isKind(PNK_SUB));
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+AddSubRight(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isKind(PNK_ADD) || pn->isKind(PNK_SUB));
+    return BinaryOpRight(pn);
+}
+
+static inline ParseNode *
+DivOrModLeft(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isKind(PNK_DIV) || pn->isKind(PNK_MOD));
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+DivOrModRight(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isKind(PNK_DIV) || pn->isKind(PNK_MOD));
+    return BinaryOpRight(pn);
+}
+
+static inline ParseNode *
+ComparisonLeft(ParseNode *pn)
+{
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+ComparisonRight(ParseNode *pn)
+{
+    return BinaryOpRight(pn);
+}
+
+static inline ParseNode *
+AndOrLeft(ParseNode *pn)
+{
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+AndOrRight(ParseNode *pn)
+{
+    return BinaryOpRight(pn);
+}
+
+static inline ParseNode *
+RelationalLeft(ParseNode *pn)
+{
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+RelationalRight(ParseNode *pn)
+{
+    return BinaryOpRight(pn);
+}
+
 static inline bool
 IsExpressionStatement(ParseNode *pn)
 {
     return pn->isKind(PNK_SEMI);
 }
 
 static inline ParseNode *
 ExpressionStatementExpr(ParseNode *pn)
@@ -3707,23 +3815,23 @@ CheckGlobalVariableInitConstant(ModuleCo
 }
 
 static bool
 CheckTypeAnnotation(ModuleCompiler &m, ParseNode *coercionNode, AsmJSCoercion *coercion,
                     ParseNode **coercedExpr = nullptr)
 {
     switch (coercionNode->getKind()) {
       case PNK_BITOR: {
-        ParseNode *rhs = BinaryRight(coercionNode);
+        ParseNode *rhs = BitwiseRight(coercionNode);
         uint32_t i;
         if (!IsLiteralInt(m, rhs, &i) || i != 0)
             return m.fail(rhs, "must use |0 for argument/return coercion");
         *coercion = AsmJS_ToInt32;
         if (coercedExpr)
-            *coercedExpr = BinaryLeft(coercionNode);
+            *coercedExpr = BitwiseLeft(coercionNode);
         return true;
       }
       case PNK_POS: {
         *coercion = AsmJS_ToNumber;
         if (coercedExpr)
             *coercedExpr = UnaryKid(coercionNode);
         return true;
       }
@@ -4324,18 +4432,20 @@ IsLiteralOrConstInt(FunctionCompiler &f,
 
     return IsLiteralInt(lit, u32);
 }
 
 static bool
 FoldMaskedArrayIndex(FunctionCompiler &f, ParseNode **indexExpr, int32_t *mask,
                      NeedsBoundsCheck *needsBoundsCheck)
 {
-    ParseNode *indexNode = BinaryLeft(*indexExpr);
-    ParseNode *maskNode = BinaryRight(*indexExpr);
+    MOZ_ASSERT((*indexExpr)->isKind(PNK_BITAND));
+
+    ParseNode *indexNode = BitwiseLeft(*indexExpr);
+    ParseNode *maskNode = BitwiseRight(*indexExpr);
 
     uint32_t mask2;
     if (IsLiteralOrConstInt(f, maskNode, &mask2)) {
         // Flag the access to skip the bounds check if the mask ensures that an 'out of
         // bounds' access can not occur based on the current heap length constraint.
         if (mask2 == 0 ||
             CountLeadingZeroes32(f.m().minHeapLength() - 1) <= CountLeadingZeroes32(mask2)) {
             *needsBoundsCheck = NO_BOUNDS_CHECK;
@@ -4383,26 +4493,27 @@ CheckArrayAccess(FunctionCompiler &f, Pa
 
     // Mask off the low bits to account for the clearing effect of a right shift
     // followed by the left shift implicit in the array access. E.g., H32[i>>2]
     // loses the low two bits.
     int32_t mask = ~(TypedArrayElemSize(*viewType) - 1);
 
     MDefinition *pointerDef;
     if (indexExpr->isKind(PNK_RSH)) {
-        ParseNode *shiftNode = BinaryRight(indexExpr);
-        ParseNode *pointerNode = BinaryLeft(indexExpr);
+        ParseNode *shiftAmountNode = BitwiseRight(indexExpr);
 
         uint32_t shift;
-        if (!IsLiteralInt(f.m(), shiftNode, &shift))
-            return f.failf(shiftNode, "shift amount must be constant");
+        if (!IsLiteralInt(f.m(), shiftAmountNode, &shift))
+            return f.failf(shiftAmountNode, "shift amount must be constant");
 
         unsigned requiredShift = TypedArrayShift(*viewType);
         if (shift != requiredShift)
-            return f.failf(shiftNode, "shift amount must be %u", requiredShift);
+            return f.failf(shiftAmountNode, "shift amount must be %u", requiredShift);
+
+        ParseNode *pointerNode = BitwiseLeft(indexExpr);
 
         if (pointerNode->isKind(PNK_BITAND))
             FoldMaskedArrayIndex(f, &pointerNode, &mask, needsBoundsCheck);
 
         f.enterHeapExpression();
 
         Type pointerType;
         if (!CheckExpr(f, pointerNode, &pointerDef, &pointerType))
@@ -4591,16 +4702,17 @@ CheckAssignName(FunctionCompiler &f, Par
     *type = rhsType;
     return true;
 }
 
 static bool
 CheckAssign(FunctionCompiler &f, ParseNode *assign, MDefinition **def, Type *type)
 {
     MOZ_ASSERT(assign->isKind(PNK_ASSIGN));
+
     ParseNode *lhs = BinaryLeft(assign);
     ParseNode *rhs = BinaryRight(assign);
 
     if (lhs->getKind() == PNK_ELEM)
         return CheckStoreArray(f, lhs, rhs, def, type);
 
     if (lhs->getKind() == PNK_NAME)
         return CheckAssignName(f, lhs, rhs, def, type);
@@ -5089,18 +5201,18 @@ CheckFuncPtrCall(FunctionCompiler &f, Pa
     if (const ModuleCompiler::Global *existing = f.lookupGlobal(name)) {
         if (existing->which() != ModuleCompiler::Global::FuncPtrTable)
             return f.failName(tableNode, "'%s' is not the name of a function-pointer array", name);
     }
 
     if (!indexExpr->isKind(PNK_BITAND))
         return f.fail(indexExpr, "function-pointer table index expression needs & mask");
 
-    ParseNode *indexNode = BinaryLeft(indexExpr);
-    ParseNode *maskNode = BinaryRight(indexExpr);
+    ParseNode *indexNode = BitwiseLeft(indexExpr);
+    ParseNode *maskNode = BitwiseRight(indexExpr);
 
     uint32_t mask;
     if (!IsLiteralInt(f.m(), maskNode, &mask) || mask == UINT32_MAX || !IsPowerOfTwo(mask + 1))
         return f.fail(maskNode, "function-pointer table index mask value must be a power of two minus 1");
 
     MDefinition *indexDef;
     Type indexType;
     if (!CheckExpr(f, indexNode, &indexDef, &indexType))
@@ -5735,32 +5847,22 @@ static bool
 CheckSimdOperationCall(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Global *global,
                        MDefinition **def, Type *type)
 {
     MOZ_ASSERT(global->isSimdOperation());
 
     AsmJSSimdType opType = global->simdOperationType();
 
     switch (global->simdOperation()) {
-      case AsmJSSimdOperation_add:
-        return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Add, def, type);
-      case AsmJSSimdOperation_sub:
-        return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Sub, def, type);
-      case AsmJSSimdOperation_mul:
-        return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Mul, def, type);
-      case AsmJSSimdOperation_div:
-        return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Div, def, type);
-      case AsmJSSimdOperation_max:
-        return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Max, def, type);
-      case AsmJSSimdOperation_min:
-        return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Min, def, type);
-      case AsmJSSimdOperation_maxNum:
-        return CheckSimdBinary(f, call, opType, MSimdBinaryArith::MaxNum, def, type);
-      case AsmJSSimdOperation_minNum:
-        return CheckSimdBinary(f, call, opType, MSimdBinaryArith::MinNum, def, type);
+#define OP_CHECK_CASE_LIST_(OP)                                                         \
+      case AsmJSSimdOperation_##OP:                                                     \
+        return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Op_##OP, def, type);
+      ARITH_COMMONX4_SIMD_OP(OP_CHECK_CASE_LIST_)
+      ARITH_FLOAT32X4_SIMD_OP(OP_CHECK_CASE_LIST_)
+#undef OP_CHECK_CASE_LIST_
 
       case AsmJSSimdOperation_lessThan:
         return CheckSimdBinary(f, call, opType, MSimdBinaryComp::lessThan, def, type);
       case AsmJSSimdOperation_lessThanOrEqual:
         return CheckSimdBinary(f, call, opType, MSimdBinaryComp::lessThanOrEqual, def, type);
       case AsmJSSimdOperation_equal:
         return CheckSimdBinary(f, call, opType, MSimdBinaryComp::equal, def, type);
       case AsmJSSimdOperation_notEqual:
@@ -6282,18 +6384,18 @@ IsValidIntMultiplyConstant(ModuleCompile
 
     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad literal");
 }
 
 static bool
 CheckMultiply(FunctionCompiler &f, ParseNode *star, MDefinition **def, Type *type)
 {
     MOZ_ASSERT(star->isKind(PNK_STAR));
-    ParseNode *lhs = BinaryLeft(star);
-    ParseNode *rhs = BinaryRight(star);
+    ParseNode *lhs = MultiplyLeft(star);
+    ParseNode *rhs = MultiplyRight(star);
 
     MDefinition *lhsDef;
     Type lhsType;
     if (!CheckExpr(f, lhs, &lhsDef, &lhsType))
         return false;
 
     MDefinition *rhsDef;
     Type rhsType;
@@ -6325,18 +6427,18 @@ CheckMultiply(FunctionCompiler &f, Parse
 
 static bool
 CheckAddOrSub(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type,
               unsigned *numAddOrSubOut = nullptr)
 {
     JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
 
     MOZ_ASSERT(expr->isKind(PNK_ADD) || expr->isKind(PNK_SUB));
-    ParseNode *lhs = BinaryLeft(expr);
-    ParseNode *rhs = BinaryRight(expr);
+    ParseNode *lhs = AddSubLeft(expr);
+    ParseNode *rhs = AddSubRight(expr);
 
     MDefinition *lhsDef, *rhsDef;
     Type lhsType, rhsType;
     unsigned lhsNumAddOrSub, rhsNumAddOrSub;
 
     if (lhs->isKind(PNK_ADD) || lhs->isKind(PNK_SUB)) {
         if (!CheckAddOrSub(f, lhs, &lhsDef, &lhsType, &lhsNumAddOrSub))
             return false;
@@ -6387,18 +6489,19 @@ CheckAddOrSub(FunctionCompiler &f, Parse
         *numAddOrSubOut = numAddOrSub;
     return true;
 }
 
 static bool
 CheckDivOrMod(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
 {
     MOZ_ASSERT(expr->isKind(PNK_DIV) || expr->isKind(PNK_MOD));
-    ParseNode *lhs = BinaryLeft(expr);
-    ParseNode *rhs = BinaryRight(expr);
+
+    ParseNode *lhs = DivOrModLeft(expr);
+    ParseNode *rhs = DivOrModRight(expr);
 
     MDefinition *lhsDef, *rhsDef;
     Type lhsType, rhsType;
     if (!CheckExpr(f, lhs, &lhsDef, &lhsType))
         return false;
     if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
         return false;
 
@@ -6441,18 +6544,19 @@ CheckDivOrMod(FunctionCompiler &f, Parse
                    "%s and %s are given", lhsType.toChars(), rhsType.toChars());
 }
 
 static bool
 CheckComparison(FunctionCompiler &f, ParseNode *comp, MDefinition **def, Type *type)
 {
     MOZ_ASSERT(comp->isKind(PNK_LT) || comp->isKind(PNK_LE) || comp->isKind(PNK_GT) ||
                comp->isKind(PNK_GE) || comp->isKind(PNK_EQ) || comp->isKind(PNK_NE));
-    ParseNode *lhs = BinaryLeft(comp);
-    ParseNode *rhs = BinaryRight(comp);
+
+    ParseNode *lhs = ComparisonLeft(comp);
+    ParseNode *rhs = ComparisonRight(comp);
 
     MDefinition *lhsDef, *rhsDef;
     Type lhsType, rhsType;
     if (!CheckExpr(f, lhs, &lhsDef, &lhsType))
         return false;
     if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
         return false;
 
@@ -6479,18 +6583,18 @@ CheckComparison(FunctionCompiler &f, Par
 
     return f.failf(comp, "arguments to a comparison must both be signed, unsigned, floats or doubles; "
                    "%s and %s are given", lhsType.toChars(), rhsType.toChars());
 }
 
 static bool
 CheckBitwise(FunctionCompiler &f, ParseNode *bitwise, MDefinition **def, Type *type)
 {
-    ParseNode *lhs = BinaryLeft(bitwise);
-    ParseNode *rhs = BinaryRight(bitwise);
+    ParseNode *lhs = BitwiseLeft(bitwise);
+    ParseNode *rhs = BitwiseRight(bitwise);
 
     int32_t identityElement;
     bool onlyOnRight;
     switch (bitwise->getKind()) {
       case PNK_BITOR:  identityElement = 0;  onlyOnRight = false; *type = Type::Signed;   break;
       case PNK_BITAND: identityElement = -1; onlyOnRight = false; *type = Type::Signed;   break;
       case PNK_BITXOR: identityElement = 0;  onlyOnRight = false; *type = Type::Signed;   break;
       case PNK_LSH:    identityElement = 0;  onlyOnRight = true;  *type = Type::Signed;   break;
@@ -7203,58 +7307,58 @@ CheckByteLengthCall(ModuleCompiler &m, P
 
     return true;
 }
 
 static bool
 CheckHeapLengthCondition(ModuleCompiler &m, ParseNode *cond, PropertyName *newBufferName,
                          uint32_t *mask, uint32_t *minLength, uint32_t *maxLength)
 {
-    if (!cond->isKind(PNK_OR) || !BinaryLeft(cond)->isKind(PNK_OR))
+    if (!cond->isKind(PNK_OR) || !AndOrLeft(cond)->isKind(PNK_OR))
         return m.fail(cond, "expecting byteLength & K || byteLength <= L || byteLength > M");
 
-    ParseNode *cond1 = BinaryLeft(BinaryLeft(cond));
-    ParseNode *cond2 = BinaryRight(BinaryLeft(cond));
-    ParseNode *cond3 = BinaryRight(cond);
+    ParseNode *cond1 = AndOrLeft(AndOrLeft(cond));
+    ParseNode *cond2 = AndOrRight(AndOrLeft(cond));
+    ParseNode *cond3 = AndOrRight(cond);
 
     if (!cond1->isKind(PNK_BITAND))
         return m.fail(cond1, "expecting byteLength & K");
 
-    if (!CheckByteLengthCall(m, BinaryLeft(cond1), newBufferName))
-        return false;
-
-    ParseNode *maskNode = BinaryRight(cond1);
+    if (!CheckByteLengthCall(m, BitwiseLeft(cond1), newBufferName))
+        return false;
+
+    ParseNode *maskNode = BitwiseRight(cond1);
     if (!IsLiteralInt(m, maskNode, mask))
         return m.fail(maskNode, "expecting integer literal mask");
     if ((*mask & 0xffffff) != 0xffffff)
         return m.fail(maskNode, "mask value must have the bits 0xffffff set");
 
     if (!cond2->isKind(PNK_LE))
         return m.fail(cond2, "expecting byteLength <= L");
 
-    if (!CheckByteLengthCall(m, BinaryLeft(cond2), newBufferName))
-        return false;
-
-    ParseNode *minLengthNode = BinaryRight(cond2);
+    if (!CheckByteLengthCall(m, RelationalLeft(cond2), newBufferName))
+        return false;
+
+    ParseNode *minLengthNode = RelationalRight(cond2);
     uint32_t minLengthExclusive;
     if (!IsLiteralInt(m, minLengthNode, &minLengthExclusive))
         return m.fail(minLengthNode, "expecting integer literal");
     if (minLengthExclusive < 0xffffff)
         return m.fail(minLengthNode, "literal must be >= 0xffffff");
 
     // Add one to convert from exclusive (the branch rejects if ==) to inclusive.
     *minLength = minLengthExclusive + 1;
 
     if (!cond3->isKind(PNK_GT))
         return m.fail(cond3, "expecting byteLength > M");
 
-    if (!CheckByteLengthCall(m, BinaryLeft(cond3), newBufferName))
-        return false;
-
-    ParseNode *maxLengthNode = BinaryRight(cond3);
+    if (!CheckByteLengthCall(m, RelationalLeft(cond3), newBufferName))
+        return false;
+
+    ParseNode *maxLengthNode = RelationalRight(cond3);
     if (!IsLiteralInt(m, maxLengthNode, maxLength))
         return m.fail(maxLengthNode, "expecting integer literal");
     if (*maxLength > 0x80000000)
         return m.fail(maxLengthNode, "literal must be <= 0x80000000");
 
     if (*maxLength < *minLength)
         return m.fail(maxLengthNode, "maximum length must be greater or equal to minimum length");
 
--- a/js/src/builtin/SIMD.h
+++ b/js/src/builtin/SIMD.h
@@ -185,41 +185,47 @@
   INT32X4_SHUFFLE_FUNCTION_LIST(V)
 
 #define FOREACH_INT32X4_SIMD_OP(_)   \
     _(fromFloat32x4)                 \
     _(fromFloat32x4Bits)             \
     _(shiftLeftByScalar)             \
     _(shiftRightArithmeticByScalar)  \
     _(shiftRightLogicalByScalar)
+#define ARITH_FLOAT32X4_SIMD_OP(_)   \
+    _(div)                           \
+    _(max)                           \
+    _(min)                           \
+    _(maxNum)                        \
+    _(minNum)
 #define FOREACH_FLOAT32X4_SIMD_OP(_) \
+    ARITH_FLOAT32X4_SIMD_OP(_)       \
     _(abs)                           \
     _(sqrt)                          \
     _(reciprocal)                    \
     _(reciprocalSqrt)                \
     _(fromInt32x4)                   \
-    _(fromInt32x4Bits)               \
-    _(div)                           \
-    _(max)                           \
-    _(min)                           \
-    _(maxNum)                        \
-    _(minNum)
-#define FOREACH_COMMONX4_SIMD_OP(_)  \
+    _(fromInt32x4Bits)
+#define ARITH_COMMONX4_SIMD_OP(_)    \
     _(add)                           \
     _(sub)                           \
-    _(mul)                           \
+    _(mul)
+#define BITWISE_COMMONX4_SIMD_OP(_)  \
+    _(and)                           \
+    _(or)                            \
+    _(xor)
+#define FOREACH_COMMONX4_SIMD_OP(_)  \
+    ARITH_COMMONX4_SIMD_OP(_)        \
+    BITWISE_COMMONX4_SIMD_OP(_)      \
     _(lessThan)                      \
     _(lessThanOrEqual)               \
     _(equal)                         \
     _(notEqual)                      \
     _(greaterThan)                   \
     _(greaterThanOrEqual)            \
-    _(and)                           \
-    _(or)                            \
-    _(xor)                           \
     _(bitselect)                     \
     _(select)                        \
     _(swizzle)                       \
     _(shuffle)                       \
     _(splat)                         \
     _(withX)                         \
     _(withY)                         \
     _(withZ)                         \
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2034,25 +2034,20 @@ CheckSideEffects(ExclusiveContext *cx, B
                 if (!CheckSideEffects(cx, bce, pn->pn_right, answer))
                     return false;
                 if (!*answer && (!pn->isOp(JSOP_NOP) || !pn2->isConst()))
                     *answer = true;
             }
             return true;
         }
 
-        if (pn->isOp(JSOP_OR) || pn->isOp(JSOP_AND) || pn->isOp(JSOP_STRICTEQ) ||
-            pn->isOp(JSOP_STRICTNE)) {
-            /*
-             * ||, &&, ===, and !== do not convert their operands via
-             * toString or valueOf method calls.
-             */
-            return CheckSideEffects(cx, bce, pn->pn_left, answer) &&
-                   CheckSideEffects(cx, bce, pn->pn_right, answer);
-        }
+        MOZ_ASSERT(!pn->isOp(JSOP_OR), "|| produces a list now");
+        MOZ_ASSERT(!pn->isOp(JSOP_AND), "&& produces a list now");
+        MOZ_ASSERT(!pn->isOp(JSOP_STRICTEQ), "=== produces a list now");
+        MOZ_ASSERT(!pn->isOp(JSOP_STRICTNE), "!== produces a list now");
 
         /*
          * We can't easily prove that neither operand ever denotes an
          * object with a toString or valueOf method.
          */
         *answer = true;
         return true;
 
@@ -4570,17 +4565,20 @@ EmitTry(ExclusiveContext *cx, BytecodeEm
     // Emit jump over catch and/or finally.
     ptrdiff_t catchJump = -1;
     if (EmitBackPatchOp(cx, bce, &catchJump) < 0)
         return false;
 
     ptrdiff_t tryEnd = bce->offset();
 
     // If this try has a catch block, emit it.
-    if (ParseNode *pn2 = pn->pn_kid2) {
+    ParseNode *catchList = pn->pn_kid2;
+    if (catchList) {
+        MOZ_ASSERT(catchList->isKind(PNK_CATCHLIST));
+
         // The emitted code for a catch block looks like:
         //
         // [pushblockscope]             only if any local aliased
         // exception
         // if there is a catchguard:
         //   dup
         // setlocal 0; pop              assign or possibly destructure exception
         // if there is a catchguard:
@@ -4596,17 +4594,17 @@ EmitTry(ExclusiveContext *cx, BytecodeEm
         // [popblockscope]              only if any local aliased
         // goto <end of catch blocks>   non-local; finally applies
         //
         // If there's no catch block without a catchguard, the last <next catch
         // block> points to rethrow code.  This code will [gosub] to the finally
         // code if appropriate, and is also used for the catch-all trynote for
         // capturing exceptions thrown from catch{} blocks.
         //
-        for (ParseNode *pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
+        for (ParseNode *pn3 = catchList->pn_head; pn3; pn3 = pn3->pn_next) {
             MOZ_ASSERT(bce->stackDepth == depth);
 
             // Emit the lexical scope and catch body.
             MOZ_ASSERT(pn3->isKind(PNK_LEXICALSCOPE));
             if (!EmitTree(cx, bce, pn3))
                 return false;
 
             // gosub <finally>, if required.
@@ -4671,17 +4669,17 @@ EmitTry(ExclusiveContext *cx, BytecodeEm
         return false;
 
     // Fix up the end-of-try/catch jumps to come here.
     if (!BackPatch(cx, bce, catchJump, bce->code().end(), JSOP_GOTO))
         return false;
 
     // Add the try note last, to let post-order give us the right ordering
     // (first to last for a given nesting level, inner to outer by level).
-    if (pn->pn_kid2 && !bce->tryNoteList.append(JSTRY_CATCH, depth, tryStart, tryEnd))
+    if (catchList && !bce->tryNoteList.append(JSTRY_CATCH, depth, tryStart, tryEnd))
         return false;
 
     // If we've got a finally, mark try+catch region with additional
     // trynote to catch exceptions (re)thrown from a catch block or
     // for the try{}finally{} case.
     if (pn->pn_kid3 && !bce->tryNoteList.append(JSTRY_FINALLY, depth, tryStart, finallyStart))
         return false;
 
@@ -6323,46 +6321,28 @@ EmitCallOrNew(ExclusiveContext *cx, Byte
             return false;
     }
     return true;
 }
 
 static bool
 EmitLogical(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
+    MOZ_ASSERT(pn->isArity(PN_LIST));
+
     /*
      * JSOP_OR converts the operand on the stack to boolean, leaves the original
      * value on the stack and jumps if true; otherwise it falls into the next
      * bytecode, which pops the left operand and then evaluates the right operand.
      * The jump goes around the right operand evaluation.
      *
      * JSOP_AND converts the operand on the stack to boolean and jumps if false;
      * otherwise it falls into the right operand's bytecode.
      */
 
-    if (pn->isArity(PN_BINARY)) {
-        if (!EmitTree(cx, bce, pn->pn_left))
-            return false;
-        ptrdiff_t top = EmitJump(cx, bce, JSOP_BACKPATCH, 0);
-        if (top < 0)
-            return false;
-        if (Emit1(cx, bce, JSOP_POP) < 0)
-            return false;
-        if (!EmitTree(cx, bce, pn->pn_right))
-            return false;
-        ptrdiff_t off = bce->offset();
-        jsbytecode *pc = bce->code(top);
-        SET_JUMP_OFFSET(pc, off - top);
-        *pc = pn->getOp();
-        return true;
-    }
-
-    MOZ_ASSERT(pn->isArity(PN_LIST));
-    MOZ_ASSERT(pn->pn_head->pn_next->pn_next);
-
     /* Left-associative operator chain: avoid too much recursion. */
     ParseNode *pn2 = pn->pn_head;
     if (!EmitTree(cx, bce, pn2))
         return false;
     ptrdiff_t top = EmitJump(cx, bce, JSOP_BACKPATCH, 0);
     if (top < 0)
         return false;
     if (Emit1(cx, bce, JSOP_POP) < 0)
@@ -7130,39 +7110,31 @@ frontend::EmitTree(ExclusiveContext *cx,
       case PNK_GE:
       case PNK_IN:
       case PNK_INSTANCEOF:
       case PNK_LSH:
       case PNK_RSH:
       case PNK_URSH:
       case PNK_STAR:
       case PNK_DIV:
-      case PNK_MOD:
-        if (pn->isArity(PN_LIST)) {
-            /* Left-associative operator chain: avoid too much recursion. */
-            ParseNode *pn2 = pn->pn_head;
-            if (!EmitTree(cx, bce, pn2))
-                return false;
-            JSOp op = pn->getOp();
-            while ((pn2 = pn2->pn_next) != nullptr) {
-                if (!EmitTree(cx, bce, pn2))
-                    return false;
-                if (Emit1(cx, bce, op) < 0)
-                    return false;
-            }
-        } else {
-            /* Binary operators that evaluate both operands unconditionally. */
-            if (!EmitTree(cx, bce, pn->pn_left))
-                return false;
-            if (!EmitTree(cx, bce, pn->pn_right))
-                return false;
-            if (Emit1(cx, bce, pn->getOp()) < 0)
+      case PNK_MOD: {
+        MOZ_ASSERT(pn->isArity(PN_LIST));
+        /* Left-associative operator chain: avoid too much recursion. */
+        ParseNode *subexpr = pn->pn_head;
+        if (!EmitTree(cx, bce, subexpr))
+            return false;
+        JSOp op = pn->getOp();
+        while ((subexpr = subexpr->pn_next) != nullptr) {
+            if (!EmitTree(cx, bce, subexpr))
+                return false;
+            if (Emit1(cx, bce, op) < 0)
                 return false;
         }
         break;
+      }
 
       case PNK_THROW:
       case PNK_TYPEOF:
       case PNK_VOID:
       case PNK_NOT:
       case PNK_BITNOT:
       case PNK_POS:
       case PNK_NEG:
@@ -7194,22 +7166,24 @@ frontend::EmitTree(ExclusiveContext *cx,
       case PNK_GENEXP:
         ok = EmitCallOrNew(cx, bce, pn);
         break;
 
       case PNK_LEXICALSCOPE:
         ok = EmitLexicalScope(cx, bce, pn);
         break;
 
-      case PNK_LET:
+      case PNK_LETBLOCK:
+      case PNK_LETEXPR:
+        ok = EmitLet(cx, bce, pn);
+        break;
+
       case PNK_CONST:
-        MOZ_ASSERT_IF(pn->isKind(PNK_CONST), !pn->isArity(PN_BINARY));
-        ok = pn->isArity(PN_BINARY)
-             ? EmitLet(cx, bce, pn)
-             : EmitVariables(cx, bce, pn, InitializeVars);
+      case PNK_LET:
+        ok = EmitVariables(cx, bce, pn, InitializeVars);
         break;
 
       case PNK_IMPORT:
       case PNK_EXPORT:
       case PNK_EXPORT_FROM:
        // TODO: Implement emitter support for modules
        bce->reportError(nullptr, JSMSG_MODULES_NOT_IMPLEMENTED);
        return false;
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -24,77 +24,403 @@ using mozilla::IsNaN;
 using mozilla::IsNegative;
 using mozilla::NegativeInfinity;
 using mozilla::PositiveInfinity;
 using JS::GenericNaN;
 using JS::ToInt32;
 using JS::ToUint32;
 
 static bool
-ContainsVarOrConst(ExclusiveContext *cx, ParseNode *pn, ParseNode **resultp)
+ContainsHoistedDeclaration(ExclusiveContext *cx, ParseNode *node, bool *result);
+
+static bool
+ListContainsHoistedDeclaration(ExclusiveContext *cx, ListNode *list, bool *result)
+{
+    for (ParseNode *node = list->pn_head; node; node = node->pn_next) {
+        if (!ContainsHoistedDeclaration(cx, node, result))
+            return false;
+        if (*result)
+            return true;
+    }
+
+    *result = false;
+    return true;
+}
+
+// Determines whether the given ParseNode contains any declarations whose
+// visibility will extend outside the node itself -- that is, whether the
+// ParseNode contains any var statements.
+//
+// THIS IS NOT A GENERAL-PURPOSE FUNCTION.  It is only written to work in the
+// specific context of deciding that |node|, as one arm of a PNK_IF controlled
+// by a constant condition, contains a declaration that forbids |node| being
+// completely eliminated as dead.
+static bool
+ContainsHoistedDeclaration(ExclusiveContext *cx, ParseNode *node, bool *result)
 {
     JS_CHECK_RECURSION(cx, return false);
 
-    if (!pn) {
-        *resultp = nullptr;
+    // With a better-typed AST, we would have distinct parse node classes for
+    // expressions and for statements and would characterize expressions with
+    // ExpressionKind and statements with StatementKind.  Perhaps someday.  In
+    // the meantime we must characterize every ParseNodeKind, even the
+    // expression/sub-expression ones that, if we handle all statement kinds
+    // correctly, we'll never see.
+    switch (node->getKind()) {
+      // Base case.
+      case PNK_VAR:
+        *result = true;
+        return true;
+
+      // Non-global lexical declarations are block-scoped (ergo not hoistable).
+      // (Global lexical declarations, in addition to being irrelevant here as
+      // ContainsHoistedDeclaration is only used on the arms of an |if|
+      // statement, are handled by PNK_GLOBALCONST and PNK_VAR.)
+      case PNK_LET:
+      case PNK_CONST:
+        MOZ_ASSERT(node->isArity(PN_LIST));
+        *result = false;
+        return true;
+
+      // ContainsHoistedDeclaration is only called on nested nodes, so any
+      // instance of this can't be function statements at body level.  In
+      // SpiderMonkey, a binding induced by a function statement is added when
+      // the function statement is evaluated.  Thus any declaration introduced
+      // by a function statement, as observed by this function, isn't a hoisted
+      // declaration.
+      case PNK_FUNCTION:
+        MOZ_ASSERT(node->isArity(PN_CODE));
+        *result = false;
         return true;
-    }
-    if (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST)) {
-        *resultp = pn;
+
+      // Statements with no sub-components at all.
+      case PNK_NOP: // induced by function f() {} function f() {}
+      case PNK_DEBUGGER:
+        MOZ_ASSERT(node->isArity(PN_NULLARY));
+        *result = false;
+        return true;
+
+      // Statements containing only an expression have no declarations.
+      case PNK_SEMI:
+      case PNK_THROW:
+        MOZ_ASSERT(node->isArity(PN_UNARY));
+        *result = false;
+        return true;
+
+      case PNK_RETURN:
+      // These two aren't statements in the spec, but we sometimes insert them
+      // in statement lists anyway.
+      case PNK_YIELD_STAR:
+      case PNK_YIELD:
+        MOZ_ASSERT(node->isArity(PN_BINARY));
+        *result = false;
+        return true;
+
+      // Other statements with no sub-statement components.
+      case PNK_BREAK:
+      case PNK_CONTINUE:
+      case PNK_IMPORT:
+      case PNK_IMPORT_SPEC_LIST:
+      case PNK_IMPORT_SPEC:
+      case PNK_EXPORT_FROM:
+      case PNK_EXPORT_SPEC_LIST:
+      case PNK_EXPORT_SPEC:
+      case PNK_EXPORT:
+      case PNK_EXPORT_BATCH_SPEC:
+        *result = false;
         return true;
-    }
-    switch (pn->getArity()) {
-      case PN_LIST:
-        for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
-            if (!ContainsVarOrConst(cx, pn2, resultp))
-                return false;
-            if (*resultp)
-                return true;
-        }
-        break;
+
+      // Statements possibly containing hoistable declarations only in the left
+      // half, in ParseNode terms -- the loop body in AST terms.
+      case PNK_DOWHILE:
+        return ContainsHoistedDeclaration(cx, node->pn_left, result);
+
+      // Statements possibly containing hoistable declarations only in the
+      // right half, in ParseNode terms -- the loop body or nested statement
+      // (usually a block statement), in AST terms.
+      case PNK_WHILE:
+      case PNK_WITH:
+        return ContainsHoistedDeclaration(cx, node->pn_right, result);
+
+      case PNK_LABEL:
+        return ContainsHoistedDeclaration(cx, node->pn_expr, result);
+
+      // Statements with more complicated structures.
+
+      // if-statement nodes may have hoisted declarations in their consequent
+      // and alternative components.
+      case PNK_IF: {
+        MOZ_ASSERT(node->isArity(PN_TERNARY));
+
+        ParseNode *consequent = node->pn_kid2;
+        if (!ContainsHoistedDeclaration(cx, consequent, result))
+            return false;
+        if (*result)
+            return true;
+
+        if (ParseNode *alternative = node->pn_kid3)
+            return ContainsHoistedDeclaration(cx, alternative, result);
+
+        *result = false;
+        return true;
+      }
+
+      // Legacy array and generator comprehensions use PNK_IF to represent
+      // conditions specified in the comprehension tail: for example,
+      // [x for (x in obj) if (x)].  The consequent of such PNK_IF nodes is
+      // either PNK_YIELD in a PNK_SEMI statement (generator comprehensions) or
+      // PNK_ARRAYPUSH (array comprehensions) .  The first case is consistent
+      // with normal if-statement structure with consequent/alternative as
+      // statements.  The second case is abnormal and requires that we not
+      // banish PNK_ARRAYPUSH to the unreachable list, handling it explicitly.
+      //
+      // We could require that this one weird PNK_ARRAYPUSH case be packaged in
+      // a PNK_SEMI, for consistency.  That requires careful bytecode emitter
+      // adjustment that seems unwarranted for a deprecated feature.
+      case PNK_ARRAYPUSH:
+        *result = false;
+        return true;
 
-      case PN_TERNARY:
-        if (!ContainsVarOrConst(cx, pn->pn_kid1, resultp))
+      // try-statements have statements to execute, and one or both of a
+      // catch-list and a finally-block.
+      case PNK_TRY: {
+        MOZ_ASSERT(node->isArity(PN_TERNARY));
+        MOZ_ASSERT(node->pn_kid2 || node->pn_kid3,
+                   "must have either catch(es) or finally");
+
+        ParseNode *tryBlock = node->pn_kid1;
+        if (!ContainsHoistedDeclaration(cx, tryBlock, result))
             return false;
-        if (*resultp)
+        if (*result)
             return true;
-        if (!ContainsVarOrConst(cx, pn->pn_kid2, resultp))
-            return false;
-        if (*resultp)
-            return true;
-        return ContainsVarOrConst(cx, pn->pn_kid3, resultp);
+
+        if (ParseNode *catchList = node->pn_kid2) {
+            for (ParseNode *lexicalScope = catchList->pn_head;
+                 lexicalScope;
+                 lexicalScope = lexicalScope->pn_next)
+            {
+                MOZ_ASSERT(lexicalScope->isKind(PNK_LEXICALSCOPE));
+
+                ParseNode *catchNode = lexicalScope->pn_expr;
+                MOZ_ASSERT(catchNode->isKind(PNK_CATCH));
+
+                ParseNode *catchStatements = catchNode->pn_kid3;
+                if (!ContainsHoistedDeclaration(cx, catchStatements, result))
+                    return false;
+                if (*result)
+                    return true;
+            }
+        }
+
+        if (ParseNode *finallyBlock = node->pn_kid3)
+            return ContainsHoistedDeclaration(cx, finallyBlock, result);
+
+        *result = false;
+        return true;
+      }
+
+      // A switch node's left half is an expression; only its right half (a
+      // list of cases/defaults, or a block node) could contain hoisted
+      // declarations.
+      case PNK_SWITCH:
+        MOZ_ASSERT(node->isArity(PN_BINARY));
+        return ContainsHoistedDeclaration(cx, node->pn_right, result);
+
+      // A case/default node's right half is its statements.  A default node's
+      // left half is null; a case node's left half is its expression.
+      case PNK_DEFAULT:
+        MOZ_ASSERT(!node->pn_left);
+      case PNK_CASE:
+        MOZ_ASSERT(node->isArity(PN_BINARY));
+        return ContainsHoistedDeclaration(cx, node->pn_right, result);
 
-      case PN_BINARY:
-      case PN_BINARY_OBJ:
-        // Limit recursion if pn is a binary expression, which can't contain a
-        // var statement.
-        if (!pn->isOp(JSOP_NOP)) {
-            *resultp = nullptr;
-            return true;
+      // PNK_SEQ has two purposes.
+      //
+      // The first is to prepend destructuring operations to the body of a
+      // deprecated function expression closure: irrelevant here, as this
+      // function doesn't recur into PNK_FUNCTION, and this method's sole
+      // caller acts upon statements nested in if-statements not found in
+      // destructuring operations.
+      //
+      // The second is to provide a place for a hoisted declaration to go, in
+      // the bizarre for-in/of loops that have as target a declaration with an
+      // assignment, e.g. |for (var i = 0 in expr)|.  This case is sadly still
+      // relevant, so we can't banish this ParseNodeKind to the unreachable
+      // list and must check every list member.
+      case PNK_SEQ:
+        return ListContainsHoistedDeclaration(cx, &node->as<ListNode>(), result);
+
+      case PNK_FOR: {
+        MOZ_ASSERT(node->isArity(PN_BINARY));
+
+        ParseNode *loopHead = node->pn_left;
+        MOZ_ASSERT(loopHead->isKind(PNK_FORHEAD) ||
+                   loopHead->isKind(PNK_FORIN) ||
+                   loopHead->isKind(PNK_FOROF));
+
+        if (loopHead->isKind(PNK_FORHEAD)) {
+            // for (init?; cond?; update?), with only init possibly containing
+            // a hoisted declaration.  (Note: a lexical-declaration |init| is
+            // (at present) hoisted in SpiderMonkey parlance -- but such
+            // hoisting doesn't extend outside of this statement, so it is not
+            // hoisting in the sense meant by ContainsHoistedDeclaration.)
+            MOZ_ASSERT(loopHead->isArity(PN_TERNARY));
+
+            ParseNode *init = loopHead->pn_kid1;
+            if (init && init->isKind(PNK_VAR)) {
+                *result = true;
+                return true;
+            }
+        } else {
+            MOZ_ASSERT(loopHead->isKind(PNK_FORIN) || loopHead->isKind(PNK_FOROF));
+
+            // for each? (target in ...), where only target may introduce
+            // hoisted declarations.
+            //
+            //   -- or --
+            //
+            // for (target of ...), where only target may introduce hoisted
+            // declarations.
+            //
+            // Either way, if |target| contains a declaration, it's either
+            // |loopHead|'s first kid, *or* that declaration was hoisted to
+            // become a child of an ancestral PNK_SEQ node.  The former case we
+            // detect here.  The latter case is handled by this method
+            // recurring on PNK_SEQ, above.
+            MOZ_ASSERT(loopHead->isArity(PN_TERNARY));
+
+            ParseNode *decl = loopHead->pn_kid1;
+            if (decl && decl->isKind(PNK_VAR)) {
+                *result = true;
+                return true;
+            }
         }
-        if (!ContainsVarOrConst(cx, pn->pn_left, resultp))
-            return false;
-        if (*resultp)
-            return true;
-        return ContainsVarOrConst(cx, pn->pn_right, resultp);
+
+        ParseNode *loopBody = node->pn_right;
+        return ContainsHoistedDeclaration(cx, loopBody, result);
+      }
+
+      case PNK_LETBLOCK: {
+        MOZ_ASSERT(node->isArity(PN_BINARY));
+        MOZ_ASSERT(node->pn_left->isKind(PNK_LET));
+        MOZ_ASSERT(node->pn_right->isKind(PNK_LEXICALSCOPE));
+        return ContainsHoistedDeclaration(cx, node->pn_right, result);
+      }
+
+      case PNK_LEXICALSCOPE: {
+        MOZ_ASSERT(node->isArity(PN_NAME));
+        ParseNode *expr = node->pn_expr;
+
+        if (expr->isKind(PNK_FOR))
+            return ContainsHoistedDeclaration(cx, expr, result);
+
+        MOZ_ASSERT(expr->isKind(PNK_STATEMENTLIST));
+        return ListContainsHoistedDeclaration(cx, &node->pn_expr->as<ListNode>(), result);
+      }
+
+      // List nodes with all non-null children.
+      case PNK_STATEMENTLIST:
+        return ListContainsHoistedDeclaration(cx, &node->as<ListNode>(), result);
 
-      case PN_UNARY:
-        if (!pn->isOp(JSOP_NOP)) {
-            *resultp = nullptr;
-            return true;
-        }
-        return ContainsVarOrConst(cx, pn->pn_kid, resultp);
+      // Grammar sub-components that should never be reached directly by this
+      // method, because some parent component should have asserted itself.
+      case PNK_COMPUTED_NAME:
+      case PNK_SPREAD:
+      case PNK_MUTATEPROTO:
+      case PNK_COLON:
+      case PNK_SHORTHAND:
+      case PNK_CONDITIONAL:
+      case PNK_TYPEOF:
+      case PNK_VOID:
+      case PNK_NOT:
+      case PNK_BITNOT:
+      case PNK_DELETE:
+      case PNK_POS:
+      case PNK_NEG:
+      case PNK_PREINCREMENT:
+      case PNK_POSTINCREMENT:
+      case PNK_PREDECREMENT:
+      case PNK_POSTDECREMENT:
+      case PNK_OR:
+      case PNK_AND:
+      case PNK_BITOR:
+      case PNK_BITXOR:
+      case PNK_BITAND:
+      case PNK_STRICTEQ:
+      case PNK_EQ:
+      case PNK_STRICTNE:
+      case PNK_NE:
+      case PNK_LT:
+      case PNK_LE:
+      case PNK_GT:
+      case PNK_GE:
+      case PNK_INSTANCEOF:
+      case PNK_IN:
+      case PNK_LSH:
+      case PNK_RSH:
+      case PNK_URSH:
+      case PNK_ADD:
+      case PNK_SUB:
+      case PNK_STAR:
+      case PNK_DIV:
+      case PNK_MOD:
+      case PNK_ASSIGN:
+      case PNK_ADDASSIGN:
+      case PNK_SUBASSIGN:
+      case PNK_BITORASSIGN:
+      case PNK_BITXORASSIGN:
+      case PNK_BITANDASSIGN:
+      case PNK_LSHASSIGN:
+      case PNK_RSHASSIGN:
+      case PNK_URSHASSIGN:
+      case PNK_MULASSIGN:
+      case PNK_DIVASSIGN:
+      case PNK_MODASSIGN:
+      case PNK_COMMA:
+      case PNK_ARRAY:
+      case PNK_OBJECT:
+      case PNK_DOT:
+      case PNK_ELEM:
+      case PNK_CALL:
+      case PNK_NAME:
+      case PNK_TEMPLATE_STRING:
+      case PNK_TEMPLATE_STRING_LIST:
+      case PNK_TAGGED_TEMPLATE:
+      case PNK_CALLSITEOBJ:
+      case PNK_STRING:
+      case PNK_REGEXP:
+      case PNK_TRUE:
+      case PNK_FALSE:
+      case PNK_NULL:
+      case PNK_THIS:
+      case PNK_LETEXPR:
+      case PNK_ELISION:
+      case PNK_NUMBER:
+      case PNK_NEW:
+      case PNK_GENERATOR:
+      case PNK_GENEXP:
+      case PNK_ARRAYCOMP:
+      case PNK_ARGSBODY:
+      case PNK_CATCHLIST:
+      case PNK_CATCH:
+      case PNK_FORIN:
+      case PNK_FOROF:
+      case PNK_FORHEAD:
+        MOZ_CRASH("ContainsHoistedDeclaration should have indicated false on "
+                  "some parent node without recurring to test this node");
 
-      case PN_NAME:
-        return ContainsVarOrConst(cx, pn->maybeExpr(), resultp);
+      case PNK_GLOBALCONST:
+        MOZ_CRASH("ContainsHoistedDeclaration is only called on nested nodes where "
+                  "globalconst nodes should never have been generated");
 
-      default:;
+      case PNK_LIMIT: // invalid sentinel value
+        MOZ_CRASH("unexpected PNK_LIMIT in node");
     }
-    *resultp = nullptr;
-    return true;
+
+    MOZ_CRASH("invalid node kind");
 }
 
 /*
  * Fold from one constant type to another.
  * XXX handles only strings and numbers for now
  */
 static bool
 FoldType(ExclusiveContext *cx, ParseNode *pn, ParseNodeKind kind)
@@ -419,33 +745,37 @@ Fold(ExclusiveContext *cx, ParseNode **p
       case PN_NULLARY:
         break;
     }
 
     // The immediate child of a PNK_DELETE node should not be replaced
     // with node indicating a different syntactic form; |delete x| is not
     // the same as |delete (true && x)|. See bug 888002.
     //
-    // pn is the immediate child in question. Its descendents were already
+    // pn is the immediate child in question. Its descendants were already
     // constant-folded above, so we're done.
     if (sc == SyntacticContext::Delete)
         return true;
 
     switch (pn->getKind()) {
       case PNK_IF:
         {
-            ParseNode *decl;
-            if (!ContainsVarOrConst(cx, pn2, &decl))
-                return false;
-            if (decl)
-                break;
-            if (!ContainsVarOrConst(cx, pn3, &decl))
-                return false;
-            if (decl)
-                break;
+            bool result;
+            if (ParseNode *consequent = pn2) {
+                if (!ContainsHoistedDeclaration(cx, consequent, &result))
+                    return false;
+                if (result)
+                    break;
+            }
+            if (ParseNode *alternative = pn3) {
+                if (!ContainsHoistedDeclaration(cx, alternative, &result))
+                    return false;
+                if (result)
+                    break;
+            }
         }
         /* FALL THROUGH */
 
       case PNK_CONDITIONAL:
         /* Reduce 'if (C) T; else E' into T for true C, E for false. */
         switch (pn1->getKind()) {
           case PNK_NUMBER:
             if (pn1->pn_dval == 0 || IsNaN(pn1->pn_dval))
@@ -490,102 +820,57 @@ Fold(ExclusiveContext *cx, ParseNode **p
         }
         if (pn3 && pn3 != pn2)
             handler.freeTree(pn3);
         break;
 
       case PNK_OR:
       case PNK_AND:
         if (sc == SyntacticContext::Condition) {
-            if (pn->isArity(PN_LIST)) {
-                ParseNode **listp = &pn->pn_head;
-                MOZ_ASSERT(*listp == pn1);
-                uint32_t orig = pn->pn_count;
-                do {
-                    Truthiness t = Boolish(pn1);
-                    if (t == Unknown) {
-                        listp = &pn1->pn_next;
-                        continue;
+            ParseNode **listp = &pn->pn_head;
+            MOZ_ASSERT(*listp == pn1);
+            uint32_t orig = pn->pn_count;
+            do {
+                Truthiness t = Boolish(pn1);
+                if (t == Unknown) {
+                    listp = &pn1->pn_next;
+                    continue;
+                }
+                if ((t == Truthy) == pn->isKind(PNK_OR)) {
+                    for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
+                        pn3 = pn2->pn_next;
+                        handler.freeTree(pn2);
+                        --pn->pn_count;
                     }
-                    if ((t == Truthy) == pn->isKind(PNK_OR)) {
-                        for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
-                            pn3 = pn2->pn_next;
-                            handler.freeTree(pn2);
-                            --pn->pn_count;
-                        }
-                        pn1->pn_next = nullptr;
-                        break;
-                    }
-                    MOZ_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
-                    if (pn->pn_count == 1)
-                        break;
-                    *listp = pn1->pn_next;
-                    handler.freeTree(pn1);
-                    --pn->pn_count;
-                } while ((pn1 = *listp) != nullptr);
+                    pn1->pn_next = nullptr;
+                    break;
+                }
+                MOZ_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
+                if (pn->pn_count == 1)
+                    break;
+                *listp = pn1->pn_next;
+                handler.freeTree(pn1);
+                --pn->pn_count;
+            } while ((pn1 = *listp) != nullptr);
 
-                // We may have to change arity from LIST to BINARY.
-                pn1 = pn->pn_head;
-                if (pn->pn_count == 2) {
-                    pn2 = pn1->pn_next;
-                    pn1->pn_next = nullptr;
-                    MOZ_ASSERT(!pn2->pn_next);
-                    pn->setArity(PN_BINARY);
-                    pn->pn_left = pn1;
-                    pn->pn_right = pn2;
-                } else if (pn->pn_count == 1) {
-                    ReplaceNode(pnp, pn1);
-                    pn = pn1;
-                } else if (orig != pn->pn_count) {
-                    // Adjust list tail.
-                    pn2 = pn1->pn_next;
-                    for (; pn1; pn2 = pn1, pn1 = pn1->pn_next)
-                        ;
-                    pn->pn_tail = &pn2->pn_next;
-                }
-            } else {
-                Truthiness t = Boolish(pn1);
-                if (t != Unknown) {
-                    if ((t == Truthy) == pn->isKind(PNK_OR)) {
-                        handler.freeTree(pn2);
-                        ReplaceNode(pnp, pn1);
-                        pn = pn1;
-                    } else {
-                        MOZ_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
-                        handler.freeTree(pn1);
-                        ReplaceNode(pnp, pn2);
-                        pn = pn2;
-                    }
-                }
+            // We may have to replace a one-element list with its element.
+            pn1 = pn->pn_head;
+            if (pn->pn_count == 1) {
+                ReplaceNode(pnp, pn1);
+                pn = pn1;
+            } else if (orig != pn->pn_count) {
+                // Adjust list tail.
+                pn2 = pn1->pn_next;
+                for (; pn1; pn2 = pn1, pn1 = pn1->pn_next)
+                    continue;
+                pn->pn_tail = &pn2->pn_next;
             }
         }
         break;
 
-      case PNK_SUBASSIGN:
-      case PNK_BITORASSIGN:
-      case PNK_BITXORASSIGN:
-      case PNK_BITANDASSIGN:
-      case PNK_LSHASSIGN:
-      case PNK_RSHASSIGN:
-      case PNK_URSHASSIGN:
-      case PNK_MULASSIGN:
-      case PNK_DIVASSIGN:
-      case PNK_MODASSIGN:
-        /*
-         * Compound operators such as *= should be subject to folding, in case
-         * the left-hand side is constant, and so that the decompiler produces
-         * the same string that you get from decompiling a script or function
-         * compiled from that same string.  += is special and so must be
-         * handled below.
-         */
-        goto do_binary_op;
-
-      case PNK_ADDASSIGN:
-        MOZ_ASSERT(pn->isOp(JSOP_ADD));
-        /* FALL THROUGH */
       case PNK_ADD:
         if (pn->isArity(PN_LIST)) {
             bool folded = false;
 
             pn2 = pn1->pn_next;
             if (pn1->isKind(PNK_NUMBER)) {
                 // Fold addition of numeric literals: (1 + 2 + x === 3 + x).
                 // Note that we can only do this the front of the list:
@@ -694,58 +979,52 @@ Fold(ExclusiveContext *cx, ParseNode **p
             pn->setOp(JSOP_STRING);
             pn->setArity(PN_NULLARY);
             handler.freeTree(pn1);
             handler.freeTree(pn2);
             break;
         }
 
         /* Can't concatenate string literals, let's try numbers. */
-        goto do_binary_op;
+        if (!FoldType(cx, pn1, PNK_NUMBER) || !FoldType(cx, pn2, PNK_NUMBER))
+            return false;
+        if (pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER)) {
+            if (!FoldBinaryNumeric(cx, pn->getOp(), pn1, pn2, pn))
+                return false;
+        }
+        break;
 
       case PNK_SUB:
       case PNK_STAR:
       case PNK_LSH:
       case PNK_RSH:
       case PNK_URSH:
       case PNK_DIV:
       case PNK_MOD:
-      do_binary_op:
-        if (pn->isArity(PN_LIST)) {
-            MOZ_ASSERT(pn->pn_count > 2);
-            for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
-                if (!FoldType(cx, pn2, PNK_NUMBER))
-                    return false;
-            }
-            for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
-                /* XXX fold only if all operands convert to number */
-                if (!pn2->isKind(PNK_NUMBER))
-                    break;
-            }
-            if (!pn2) {
-                JSOp op = pn->getOp();
+        MOZ_ASSERT(pn->getArity() == PN_LIST);
+        MOZ_ASSERT(pn->pn_count >= 2);
+        for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
+            if (!FoldType(cx, pn2, PNK_NUMBER))
+                return false;
+        }
+        for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
+            /* XXX fold only if all operands convert to number */
+            if (!pn2->isKind(PNK_NUMBER))
+                break;
+        }
+        if (!pn2) {
+            JSOp op = pn->getOp();
 
-                pn2 = pn1->pn_next;
+            pn2 = pn1->pn_next;
+            pn3 = pn2->pn_next;
+            if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn))
+                return false;
+            while ((pn2 = pn3) != nullptr) {
                 pn3 = pn2->pn_next;
-                if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn))
-                    return false;
-                while ((pn2 = pn3) != nullptr) {
-                    pn3 = pn2->pn_next;
-                    if (!FoldBinaryNumeric(cx, op, pn, pn2, pn))
-                        return false;
-                }
-            }
-        } else {
-            MOZ_ASSERT(pn->isArity(PN_BINARY));
-            if (!FoldType(cx, pn1, PNK_NUMBER) ||
-                !FoldType(cx, pn2, PNK_NUMBER)) {
-                return false;
-            }
-            if (pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER)) {
-                if (!FoldBinaryNumeric(cx, pn->getOp(), pn1, pn2, pn))
+                if (!FoldBinaryNumeric(cx, op, pn, pn2, pn))
                     return false;
             }
         }
         break;
 
       case PNK_TYPEOF:
       case PNK_VOID:
       case PNK_NOT:
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -22,17 +22,16 @@ class Parser;
 class SyntaxParseHandler;
 
 // Parse handler used when generating a full parse tree for all code which the
 // parser encounters.
 class FullParseHandler
 {
     ParseNodeAllocator allocator;
     TokenStream &tokenStream;
-    bool foldConstants;
 
     ParseNode *allocParseNode(size_t size) {
         MOZ_ASSERT(size == sizeof(ParseNode));
         return static_cast<ParseNode *>(allocator.allocNode());
     }
 
     ParseNode *cloneNode(const ParseNode &other) {
         ParseNode *node = allocParseNode(sizeof(ParseNode));
@@ -66,21 +65,20 @@ class FullParseHandler
 
     /* new_ methods for creating parse nodes. These report OOM on context. */
     JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
 
     typedef ParseNode *Node;
     typedef Definition *DefinitionNode;
 
     FullParseHandler(ExclusiveContext *cx, LifoAlloc &alloc,
-                     TokenStream &tokenStream, bool foldConstants,
-                     Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction)
+                     TokenStream &tokenStream, Parser<SyntaxParseHandler> *syntaxParser,
+                     LazyScript *lazyOuterFunction)
       : allocator(cx, alloc),
         tokenStream(tokenStream),
-        foldConstants(foldConstants),
         lazyOuterFunction_(lazyOuterFunction),
         lazyInnerFunctionIndex(0),
         syntaxParser(syntaxParser)
     {}
 
     static ParseNode *null() { return nullptr; }
 
     ParseNode *freeTree(ParseNode *pn) { return allocator.freeTree(pn); }
@@ -212,20 +210,20 @@ class FullParseHandler
                          JSOp op = JSOP_NOP) {
         return new_<BinaryNode>(kind, op, left->pn_pos, left, (ParseNode *) nullptr);
     }
     ParseNode *newBinary(ParseNodeKind kind, ParseNode *left, ParseNode *right,
                          JSOp op = JSOP_NOP) {
         TokenPos pos(left->pn_pos.begin, right->pn_pos.end);
         return new_<BinaryNode>(kind, op, pos, left, right);
     }
-    ParseNode *newBinaryOrAppend(ParseNodeKind kind, ParseNode *left, ParseNode *right,
-                                 ParseContext<FullParseHandler> *pc, JSOp op = JSOP_NOP)
+    ParseNode *appendOrCreateList(ParseNodeKind kind, ParseNode *left, ParseNode *right,
+                                  ParseContext<FullParseHandler> *pc, JSOp op = JSOP_NOP)
     {
-        return ParseNode::newBinaryOrAppend(kind, op, left, right, this, pc, foldConstants);
+        return ParseNode::appendOrCreateList(kind, op, left, right, this, pc);
     }
 
     ParseNode *newTernary(ParseNodeKind kind,
                           ParseNode *first, ParseNode *second, ParseNode *third,
                           JSOp op = JSOP_NOP)
     {
         return new_<TernaryNode>(kind, op, first, second, third);
     }
@@ -535,20 +533,36 @@ class FullParseHandler
 
     ParseNode *newLexicalScope(ObjectBox *blockBox) {
         return new_<LexicalScopeNode>(blockBox, pos());
     }
     void setLexicalScopeBody(ParseNode *block, ParseNode *body) {
         block->pn_expr = body;
     }
 
+    ParseNode *newLetExpression(ParseNode *vars, ParseNode *block, const TokenPos &pos) {
+        ParseNode *letExpr = newBinary(PNK_LETEXPR, vars, block);
+        if (!letExpr)
+            return nullptr;
+        letExpr->pn_pos = pos;
+        return letExpr;
+    }
+
+    ParseNode *newLetBlock(ParseNode *vars, ParseNode *block, const TokenPos &pos) {
+        ParseNode *letBlock = newBinary(PNK_LETBLOCK, vars, block);
+        if (!letBlock)
+            return nullptr;
+        letBlock->pn_pos = pos;
+        return letBlock;
+    }
+
     ParseNode *newAssignment(ParseNodeKind kind, ParseNode *lhs, ParseNode *rhs,
                              ParseContext<FullParseHandler> *pc, JSOp op)
     {
-        return newBinaryOrAppend(kind, lhs, rhs, pc, op);
+        return newBinary(kind, lhs, rhs, op);
     }
 
     bool isUnparenthesizedYieldExpression(ParseNode *node) {
         return node->isKind(PNK_YIELD) && !node->isInParens();
     }
 
     bool isUnparenthesizedCommaExpression(ParseNode *node) {
         return node->isKind(PNK_COMMA) && !node->isInParens();
@@ -595,16 +609,19 @@ class FullParseHandler
         return new_<ListNode>(kind, op, pos());
     }
 
     /* New list with one initial child node. kid must be non-null. */
     ParseNode *newList(ParseNodeKind kind, ParseNode *kid, JSOp op = JSOP_NOP) {
         return new_<ListNode>(kind, op, kid);
     }
 
+    ParseNode *newCatchList() {
+        return new_<ListNode>(PNK_CATCHLIST, JSOP_NOP, pos());
+    }
 
     ParseNode *newCommaExpressionList(ParseNode *kid) {
         return newList(PNK_COMMA, kid, JSOP_NOP);
     }
 
     void addList(ParseNode *list, ParseNode *kid) {
         list->append(kid);
     }
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -240,66 +240,48 @@ ParseNodeAllocator::allocNode()
 
     void *p = alloc.alloc(sizeof (ParseNode));
     if (!p)
         js_ReportOutOfMemory(cx);
     return p;
 }
 
 ParseNode *
-ParseNode::append(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
-                  FullParseHandler *handler)
+ParseNode::appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
+                              FullParseHandler *handler, ParseContext<FullParseHandler> *pc)
 {
-    if (!left || !right)
-        return nullptr;
-
-    MOZ_ASSERT(left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC));
+    // The asm.js specification is written in ECMAScript grammar terms that
+    // specify *only* a binary tree.  It's a royal pain to implement the asm.js
+    // spec to act upon n-ary lists as created below.  So for asm.js, form a
+    // binary tree of lists exactly as ECMAScript would by skipping the
+    // following optimization.
+    if (!pc->useAsmOrInsideUseAsm()) {
+        // Left-associative trees of a given operator (e.g. |a + b + c|) are
+        // binary trees in the spec: (+ (+ a b) c) in Lisp terms.  Recursively
+        // processing such a tree, exactly implemented that way, would blow the
+        // the stack.  We use a list node that uses O(1) stack to represent
+        // such operations: (+ a b c).
+        if (left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
+            ListNode *list = &left->as<ListNode>();
 
-    ListNode *list;
-    if (left->pn_arity == PN_LIST) {
-        list = &left->as<ListNode>();
-    } else {
-        ParseNode *pn1 = left->pn_left, *pn2 = left->pn_right;
-        list = handler->new_<ListNode>(kind, op, pn1);
-        if (!list)
-            return nullptr;
-        list->append(pn2);
+            list->append(right);
+            list->pn_pos.end = right->pn_pos.end;
+
+            return list;
+        }
     }
 
+    ParseNode *list = handler->new_<ListNode>(kind, op, left);
+    if (!list)
+        return nullptr;
+
     list->append(right);
-    list->pn_pos.end = right->pn_pos.end;
-
     return list;
 }
 
-ParseNode *
-ParseNode::newBinaryOrAppend(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
-                             FullParseHandler *handler, ParseContext<FullParseHandler> *pc,
-                             bool foldConstants)
-{
-    if (!left || !right)
-        return nullptr;
-
-    /*
-     * Ensure that the parse tree is faithful to the source when "use asm" (for
-     * the purpose of type checking).
-     */
-    if (pc->useAsmOrInsideUseAsm())
-        return handler->new_<BinaryNode>(kind, op, left, right);
-
-    /*
-     * Flatten a left-associative (left-heavy) tree of a given operator into
-     * a list to reduce js::FoldConstants and js::frontend::EmitTree recursion.
-     */
-    if (left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC))
-        return append(kind, op, left, right, handler);
-
-    return handler->new_<BinaryNode>(kind, op, left, right);
-}
-
 const char *
 Definition::kindString(Kind kind)
 {
     static const char * const table[] = {
         "", js_var_str, js_const_str, js_const_str, js_let_str, "argument", js_function_str, "unknown"
     };
 
     MOZ_ASSERT(unsigned(kind) <= unsigned(ARG));
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -98,17 +98,16 @@ class UpvarCookie
     F(CALLSITEOBJ) \
     F(REGEXP) \
     F(TRUE) \
     F(FALSE) \
     F(NULL) \
     F(THIS) \
     F(FUNCTION) \
     F(IF) \
-    F(ELSE) \
     F(SWITCH) \
     F(CASE) \
     F(DEFAULT) \
     F(WHILE) \
     F(DOWHILE) \
     F(FOR) \
     F(BREAK) \
     F(CONTINUE) \
@@ -117,27 +116,28 @@ class UpvarCookie
     F(GLOBALCONST) \
     F(WITH) \
     F(RETURN) \
     F(NEW) \
     F(DELETE) \
     F(TRY) \
     F(CATCH) \
     F(CATCHLIST) \
-    F(FINALLY) \
     F(THROW) \
     F(DEBUGGER) \
     F(GENERATOR) \
     F(YIELD) \
     F(YIELD_STAR) \
     F(GENEXP) \
     F(ARRAYCOMP) \
     F(ARRAYPUSH) \
     F(LEXICALSCOPE) \
     F(LET) \
+    F(LETBLOCK) \
+    F(LETEXPR) \
     F(IMPORT) \
     F(IMPORT_SPEC_LIST) \
     F(IMPORT_SPEC) \
     F(EXPORT) \
     F(EXPORT_FROM) \
     F(EXPORT_SPEC_LIST) \
     F(EXPORT_SPEC) \
     F(EXPORT_BATCH_SPEC) \
@@ -282,20 +282,21 @@ enum ParseNodeKind
  *                            to left of 'of'; if pn_kid1, then this
  *                            is a clone of pn_kid1->pn_head
  *                          pn_kid3: expr to right of 'of'
  * PNK_FORHEAD  ternary     pn_kid1:  init expr before first ';' or nullptr
  *                          pn_kid2:  cond expr before second ';' or nullptr
  *                          pn_kid3:  update expr after second ';' or nullptr
  * PNK_THROW    unary       pn_op: JSOP_THROW, pn_kid: exception
  * PNK_TRY      ternary     pn_kid1: try block
- *                          pn_kid2: null or PNK_CATCHLIST list of
- *                          PNK_LEXICALSCOPE nodes, each with pn_expr pointing
- *                          to a PNK_CATCH node
+ *                          pn_kid2: null or PNK_CATCHLIST list
  *                          pn_kid3: null or finally block
+ * PNK_CATCHLIST list       pn_head: list of PNK_LEXICALSCOPE nodes, one per
+ *                                   catch-block, each with pn_expr pointing
+ *                                   to a PNK_CATCH node
  * PNK_CATCH    ternary     pn_kid1: PNK_NAME, PNK_ARRAY, or PNK_OBJECT catch var node
  *                                   (PNK_ARRAY or PNK_OBJECT if destructuring)
  *                          pn_kid2: null or the catch guard expression
  *                          pn_kid3: catch block statements
  * PNK_BREAK    name        pn_atom: label or null
  * PNK_CONTINUE name        pn_atom: label or null
  * PNK_WITH     binary-obj  pn_left: head expr; pn_right: body; pn_binary_obj: StaticWithObject
  * PNK_VAR,     list        pn_head: list of PNK_NAME or PNK_ASSIGN nodes
@@ -504,16 +505,21 @@ class ParseNode
     bool isArity(ParseNodeArity a) const   { return getArity() == a; }
     void setArity(ParseNodeArity a)        { pn_arity = a; }
 
     bool isAssignment() const {
         ParseNodeKind kind = getKind();
         return PNK_ASSIGNMENT_START <= kind && kind <= PNK_ASSIGNMENT_LAST;
     }
 
+    bool isBinaryOperation() const {
+        ParseNodeKind kind = getKind();
+        return PNK_BINOP_FIRST <= kind && kind <= PNK_BINOP_LAST;
+    }
+
     /* Boolean attributes. */
     bool isInParens() const                { return pn_parens; }
     bool isLikelyIIFE() const              { return isInParens(); }
     void setInParens(bool enabled)         { pn_parens = enabled; }
     bool isUsed() const                    { return pn_used; }
     void setUsed(bool enabled)             { pn_used = enabled; }
     bool isDefn() const                    { return pn_defn; }
     void setDefn(bool enabled)             { pn_defn = enabled; }
@@ -635,31 +641,22 @@ class ParseNode
         pn_parens = false;
         MOZ_ASSERT(!pn_used);
         MOZ_ASSERT(!pn_defn);
         pn_next = pn_link = nullptr;
     }
 
   public:
     /*
-     * Append right to left, forming a list node.  |left| must have the given
-     * kind and op, and op must be left-associative.
+     * If |left| is a list of the given kind/left-associative op, append
+     * |right| to it and return |left|.  Otherwise return a [left, right] list.
      */
     static ParseNode *
-    append(ParseNodeKind tt, JSOp op, ParseNode *left, ParseNode *right, FullParseHandler *handler);
-
-    /*
-     * Either append right to left, if left meets the conditions necessary to
-     * append (see append), or form a binary node whose children are right and
-     * left.
-     */
-    static ParseNode *
-    newBinaryOrAppend(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
-                      FullParseHandler *handler, ParseContext<FullParseHandler> *pc,
-                      bool foldConstants);
+    appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
+                       FullParseHandler *handler, ParseContext<FullParseHandler> *pc);
 
     inline PropertyName *name() const;
     inline JSAtom *atom() const;
 
     /*
      * The pn_expr and lexdef members are arms of an unsafe union. Unless you
      * know exactly what you're doing, use only the following methods to access
      * them. For less overhead and assertions for protection, use pn->expr()
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -511,17 +511,17 @@ Parser<ParseHandler>::Parser(ExclusiveCo
     abortedSyntaxParse(false),
     isUnexpectedEOF_(false),
     sawDeprecatedForEach(false),
     sawDeprecatedDestructuringForIn(false),
     sawDeprecatedLegacyGenerator(false),
     sawDeprecatedExpressionClosure(false),
     sawDeprecatedLetBlock(false),
     sawDeprecatedLetExpression(false),
-    handler(cx, *alloc, tokenStream, foldConstants, syntaxParser, lazyOuterFunction)
+    handler(cx, *alloc, tokenStream, syntaxParser, lazyOuterFunction)
 {
     {
         AutoLockForExclusiveAccess lock(cx);
         cx->perThreadData->addActiveCompilation();
     }
 
     // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
     // which are not generated if functions are parsed lazily. Note that the
@@ -1225,30 +1225,20 @@ struct BindData
         this->op = op;
         this->isConst = op == JSOP_DEFCONST;
         this->binder = Parser<ParseHandler>::bindVarOrGlobalConst;
     }
 };
 
 template <typename ParseHandler>
 JSFunction *
-Parser<ParseHandler>::newFunction(GenericParseContext *pc, HandleAtom atom,
-                                  FunctionSyntaxKind kind, JSObject *proto)
+Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind, JSObject *proto)
 {
     MOZ_ASSERT_IF(kind == Statement, atom != nullptr);
 
-    /*
-     * Find the global compilation context in order to pre-set the newborn
-     * function's parent slot to pc->sc->as<GlobalObject>()->scopeChain. If the
-     * global context is a compile-and-go one, we leave the pre-set parent
-     * intact; otherwise we clear parent and proto.
-     */
-    while (pc->parent)
-        pc = pc->parent;
-
     RootedFunction fun(context);
     JSFunction::Flags flags = (kind == Expression)
                               ? JSFunction::INTERPRETED_LAMBDA
                               : (kind == Arrow)
                                 ? JSFunction::INTERPRETED_LAMBDA_ARROW
                                 : JSFunction::INTERPRETED;
     gc::AllocKind allocKind = JSFunction::FinalizeKind;
     if (kind == Arrow)
@@ -2184,17 +2174,17 @@ Parser<ParseHandler>::functionDef(Handle
         // If we are off the main thread, the generator meta-objects have
         // already been created by js::StartOffThreadParseScript, so cx will not
         // be necessary.
         JSContext *cx = context->maybeJSContext();
         proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
         if (!proto)
             return null();
     }
-    RootedFunction fun(context, newFunction(pc, funName, kind, proto));
+    RootedFunction fun(context, newFunction(funName, kind, proto));
     if (!fun)
         return null();
 
     // Speculatively parse using the directives of the parent parsing context.
     // If a directive is encountered (e.g., "use strict") that changes how the
     // function should have been parsed, we backup and reparse with the new set
     // of directives.
     Directives directives(pc);
@@ -3627,17 +3617,17 @@ Parser<SyntaxParseHandler>::pushLetScope
 
 /*
  * Parse a let block statement or let expression (determined by 'letContext').
  * In both cases, bindings are not hoisted to the top of the enclosing block
  * and thus must be carefully injected between variables() and the let body.
  */
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::letBlock(LetContext letContext)
+Parser<ParseHandler>::deprecatedLetBlockOrExpression(LetContext letContext)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LET));
 
     RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
     if (!blockObj)
         return null();
 
     uint32_t begin = pos().begin;
@@ -3650,21 +3640,16 @@ Parser<ParseHandler>::letBlock(LetContex
 
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
 
     StmtInfoPC stmtInfo(context);
     Node block = pushLetScope(blockObj, &stmtInfo);
     if (!block)
         return null();
 
-    Node pnlet = handler.newBinary(PNK_LET, vars, block);
-    if (!pnlet)
-        return null();
-    handler.setBeginPosition(pnlet, begin);
-
     bool needExprStmt = false;
     if (letContext == LetStatement) {
         bool matched;
         if (!tokenStream.matchToken(&matched, TOK_LC, TokenStream::Operand))
             return null();
         if (!matched) {
             /*
              * Strict mode eliminates a grammar ambiguity with unparenthesized
@@ -3677,26 +3662,26 @@ Parser<ParseHandler>::letBlock(LetContex
              *   // Does this parse as
              *   //   (let (loc = "inner") id)(loc) // "outer"
              *   // or as
              *   //   let (loc = "inner") (id(loc)) // "inner"
              *   let (loc = "inner") id(loc);
              *
              * See bug 569464.
              */
-            if (!report(ParseStrictError, pc->sc->strict, pnlet,
-                        JSMSG_STRICT_CODE_LET_EXPR_STMT))
+            if (!reportWithOffset(ParseStrictError, pc->sc->strict, begin,
+                                  JSMSG_STRICT_CODE_LET_EXPR_STMT))
             {
                 return null();
             }
 
             /*
              * If this is really an expression in let statement guise, then we
-             * need to wrap the PNK_LET node in a PNK_SEMI node so that we pop
-             * the return value of the expression.
+             * need to wrap the PNK_LETEXPR node in a PNK_SEMI node so that we
+             * pop the return value of the expression.
              */
             needExprStmt = true;
             letContext = LetExpression;
         }
     }
 
     Node expr;
     if (letContext == LetStatement) {
@@ -3716,24 +3701,32 @@ Parser<ParseHandler>::letBlock(LetContex
 
         sawDeprecatedLetExpression = true;
         if (!report(ParseWarning, pc->sc->strict, expr, JSMSG_DEPRECATED_LET_EXPRESSION))
             return null();
     }
     handler.setLexicalScopeBody(block, expr);
     PopStatementPC(tokenStream, pc);
 
-    handler.setEndPosition(pnlet, pos().end);
-
-    if (needExprStmt) {
-        if (!MatchOrInsertSemicolon(tokenStream))
-            return null();
-        return handler.newExprStatement(pnlet, pos().end);
-    }
-    return pnlet;
+    TokenPos letPos(begin, pos().end);
+
+    if (letContext == LetExpression) {
+        if (needExprStmt) {
+            if (!MatchOrInsertSemicolon(tokenStream))
+                return null();
+        }
+
+        Node letExpr = handler.newLetExpression(vars, block, letPos);
+        if (!letExpr)
+            return null();
+
+        return needExprStmt ? handler.newExprStatement(letExpr, pos().end) : letExpr;
+    }
+
+    return handler.newLetBlock(vars, block, letPos);
 }
 
 template <typename ParseHandler>
 static bool
 PushBlocklikeStatement(TokenStream &ts, StmtInfoPC *stmt, StmtType type,
                        ParseContext<ParseHandler> *pc)
 {
     PushStatementPC(pc, stmt, type);
@@ -3883,17 +3876,17 @@ Parser<ParseHandler>::variables(ParseNod
 
                 Node init = assignExpr();
                 if (!init)
                     return null();
 
                 if (!bindBeforeInitializer && !checkDestructuring(&data, pn2))
                     return null();
 
-                pn2 = handler.newBinaryOrAppend(PNK_ASSIGN, pn2, init, pc);
+                pn2 = handler.newBinary(PNK_ASSIGN, pn2, init);
                 if (!pn2)
                     return null();
                 handler.addList(pn, pn2);
                 break;
             }
 
             if (tt != TOK_NAME) {
                 if (tt == TOK_YIELD) {
@@ -4102,37 +4095,54 @@ SyntaxParseHandler::Node
 Parser<SyntaxParseHandler>::lexicalDeclaration(bool)
 {
     JS_ALWAYS_FALSE(abortIfSyntaxParser());
     return SyntaxParseHandler::NodeFailure;
 }
 
 template <>
 ParseNode *
-Parser<FullParseHandler>::letStatement()
+Parser<FullParseHandler>::letDeclarationOrBlock()
 {
     handler.disableSyntaxParser();
 
     /* Check for a let statement or let expression. */
-    ParseNode *pn;
     TokenKind tt;
     if (!tokenStream.peekToken(&tt))
         return null();
     if (tt == TOK_LP) {
-        pn = letBlock(LetStatement);
-        MOZ_ASSERT_IF(pn, pn->isKind(PNK_LET) || pn->isKind(PNK_SEMI));
-    } else {
-        pn = lexicalDeclaration(/* isConst = */ false);
-    }
-    return pn;
+        ParseNode *node = deprecatedLetBlockOrExpression(LetStatement);
+        if (!node)
+            return nullptr;
+
+        if (node->isKind(PNK_LETBLOCK)) {
+            MOZ_ASSERT(node->isArity(PN_BINARY));
+        } else {
+            MOZ_ASSERT(node->isKind(PNK_SEMI));
+            MOZ_ASSERT(node->pn_kid->isKind(PNK_LETEXPR));
+            MOZ_ASSERT(node->pn_kid->isArity(PN_BINARY));
+        }
+
+        return node;
+    }
+
+    ParseNode *decl = lexicalDeclaration(/* isConst = */ false);
+    if (!decl)
+        return nullptr;
+
+    // let-declarations at global scope are currently treated as plain old var.
+    // See bug 589199.
+    MOZ_ASSERT(decl->isKind(PNK_LET) || decl->isKind(PNK_VAR));
+    MOZ_ASSERT(decl->isArity(PN_LIST));
+    return decl;
 }
 
 template <>
 SyntaxParseHandler::Node
-Parser<SyntaxParseHandler>::letStatement()
+Parser<SyntaxParseHandler>::letDeclarationOrBlock()
 {
     JS_ALWAYS_FALSE(abortIfSyntaxParser());
     return SyntaxParseHandler::NodeFailure;
 }
 
 template<typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::importDeclaration()
@@ -4660,17 +4670,17 @@ Parser<FullParseHandler>::forStatement()
                 pn1 = variables(PNK_VAR);
             } else if (tt == TOK_LET || tt == TOK_CONST) {
                 handler.disableSyntaxParser();
                 bool constDecl = tt == TOK_CONST;
                 tokenStream.consumeKnownToken(tt);
                 if (!tokenStream.peekToken(&tt))
                     return null();
                 if (tt == TOK_LP) {
-                    pn1 = letBlock(LetExpression);
+                    pn1 = deprecatedLetBlockOrExpression(LetExpression);
                 } else {
                     isForDecl = true;
                     blockObj = StaticBlockObject::create(context);
                     if (!blockObj)
                         return null();
                     pn1 = variables(constDecl ? PNK_CONST : PNK_LET, nullptr, blockObj,
                                     DontHoistVars);
                 }
@@ -4933,21 +4943,17 @@ Parser<FullParseHandler>::forStatement()
             return null();
         pnseq->pn_pos = forLoop->pn_pos;
         pnseq->append(forLoop);
         return pnseq;
     }
     if (forLetImpliedBlock) {
         forLetImpliedBlock->pn_expr = forLoop;
         forLetImpliedBlock->pn_pos = forLoop->pn_pos;
-        ParseNode *let = handler.newBinary(PNK_LET, forLetDecl, forLetImpliedBlock);
-        if (!let)
-            return null();
-        let->pn_pos = forLoop->pn_pos;
-        return let;
+        return handler.newLetBlock(forLetDecl, forLetImpliedBlock, forLoop->pn_pos);
     }
     return forLoop;
 }
 
 template <>
 SyntaxParseHandler::Node
 Parser<SyntaxParseHandler>::forStatement()
 {
@@ -5634,17 +5640,17 @@ Parser<ParseHandler>::tryStatement()
     PopStatementPC(tokenStream, pc);
 
     bool hasUnconditionalCatch = false;
     Node catchList = null();
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
     if (tt == TOK_CATCH) {
-        catchList = handler.newList(PNK_CATCH);
+        catchList = handler.newCatchList();
         if (!catchList)
             return null();
 
         do {
             Node pnblock;
             BindData<ParseHandler> data(context);
 
             /* Check for another catch after unconditional catch. */
@@ -5817,17 +5823,17 @@ Parser<ParseHandler>::statement(bool can
         handler.setListFlag(pn, PNX_POPVAR);
 
         if (!MatchOrInsertSemicolon(tokenStream))
             return null();
         return pn;
       }
 
       case TOK_LET:
-        return letStatement();
+        return letDeclarationOrBlock();
       case TOK_IMPORT:
         return importDeclaration();
       case TOK_EXPORT:
         return exportDeclaration();
       case TOK_SEMI:
         return handler.newEmptyStatement(pos());
       case TOK_IF:
         return ifStatement();
@@ -6072,17 +6078,17 @@ Parser<ParseHandler>::orExpr1(InvokedPre
         // The >= in this condition works because all the operators in question
         // are left-associative; if any were not, the case where two operators
         // have equal precedence would need to be handled specially, and the
         // stack would need to be a Vector.
         while (depth > 0 && Precedence(kindStack[depth - 1]) >= Precedence(pnk)) {
             depth--;
             ParseNodeKind combiningPnk = kindStack[depth];
             JSOp combiningOp = BinaryOpParseNodeKindToJSOp(combiningPnk);
-            pn = handler.newBinaryOrAppend(combiningPnk, nodeStack[depth], pn, pc, combiningOp);
+            pn = handler.appendOrCreateList(combiningPnk, nodeStack[depth], pn, pc, combiningOp);
             if (!pn)
                 return pn;
         }
 
         if (pnk == PNK_LIMIT)
             break;
 
         nodeStack[depth] = pn;
@@ -7057,17 +7063,17 @@ Parser<ParseHandler>::generatorComprehen
     RootedObject proto(context);
     if (comprehensionKind == StarGenerator) {
         JSContext *cx = context->maybeJSContext();
         proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
         if (!proto)
             return null();
     }
 
-    RootedFunction fun(context, newFunction(outerpc, /* atom = */ NullPtr(), Expression, proto));
+    RootedFunction fun(context, newFunction(/* atom = */ NullPtr(), Expression, proto));
     if (!fun)
         return null();
 
     // Create box for fun->object early to root it.
     Directives directives(/* strict = */ outerpc->sc->strict);
     FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives, comprehensionKind);
     if (!genFunbox)
         return null();
@@ -7906,16 +7912,18 @@ Parser<ParseHandler>::objectLiteral()
         JSOp op = JSOP_INITPROP;
         Node propname;
         switch (ltok) {
           case TOK_NUMBER:
             atom = DoubleToAtom(context, tokenStream.currentToken().number());
             if (!atom)
                 return null();
             propname = newNumber(tokenStream.currentToken());
+            if (!propname)
+                return null();
             break;
 
           case TOK_LB: {
               propname = computedPropertyName(literal);
               if (!propname)
                   return null();
               break;
           }
@@ -8061,16 +8069,18 @@ Parser<ParseHandler>::objectLiteral()
                 if (!tokenStream.checkForKeyword(atom, nullptr))
                     return null();
                 PropertyName *name = handler.isName(propname);
                 MOZ_ASSERT(atom);
                 propname = newName(name);
                 if (!propname)
                     return null();
                 Node ident = identifierName();
+                if (!ident)
+                    return null();
                 if (!handler.addPropertyDefinition(literal, propname, ident, true))
                     return null();
             } else if (tt == TOK_LP) {
                 tokenStream.ungetToken();
                 if (!methodDefinition(literal, propname, Normal, Method,
                                       isGenerator ? StarGenerator : NotGenerator, op)) {
                     return null();
                 }
@@ -8134,17 +8144,17 @@ Parser<ParseHandler>::primaryExpr(TokenK
 
       case TOK_LB:
         return arrayInitializer();
 
       case TOK_LC:
         return objectLiteral();
 
       case TOK_LET:
-        return letBlock(LetExpression);
+        return deprecatedLetBlockOrExpression(LetExpression);
 
       case TOK_LP: {
         TokenKind next;
         if (!tokenStream.peekToken(&next, TokenStream::Operand))
             return null();
         if (next != TOK_RP)
             return parenExprOrGeneratorComprehension();
 
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -438,21 +438,20 @@ class Parser : private JS::AutoGCRooter,
      * Allocate a new parsed object or function container from
      * cx->tempLifoAlloc.
      */
     ObjectBox *newObjectBox(NativeObject *obj);
     FunctionBox *newFunctionBox(Node fn, JSFunction *fun, ParseContext<ParseHandler> *pc,
                                 Directives directives, GeneratorKind generatorKind);
 
     /*
-     * Create a new function object given parse context (pc) and a name (which
-     * is optional if this is a function expression).
+     * Create a new function object given a name (which is optional if this is
+     * a function expression).
      */
-    JSFunction *newFunction(GenericParseContext *pc, HandleAtom atom, FunctionSyntaxKind kind,
-                            JSObject *proto = nullptr);
+    JSFunction *newFunction(HandleAtom atom, FunctionSyntaxKind kind, JSObject *proto = nullptr);
 
     void trace(JSTracer *trc);
 
     bool hadAbortedSyntaxParse() {
         return abortedSyntaxParse;
     }
     void clearAbortedSyntaxParse() {
         abortedSyntaxParse = false;
@@ -553,17 +552,17 @@ class Parser : private JS::AutoGCRooter,
     Node returnStatement();
     Node withStatement();
     Node labeledStatement();
     Node throwStatement();
     Node tryStatement();
     Node debuggerStatement();
 
     Node lexicalDeclaration(bool isConst);
-    Node letStatement();
+    Node letDeclarationOrBlock();
     Node importDeclaration();
     Node exportDeclaration();
     Node expressionStatement(InvokedPrediction invoked = PredictUninvoked);
     Node variables(ParseNodeKind kind, bool *psimple = nullptr,
                    StaticBlockObject *blockObj = nullptr,
                    VarContext varContext = HoistVars);
     Node expr(InvokedPrediction invoked = PredictUninvoked);
     Node assignExpr(InvokedPrediction invoked = PredictUninvoked);
@@ -610,17 +609,17 @@ class Parser : private JS::AutoGCRooter,
     Node comprehensionTail(GeneratorKind comprehensionKind);
     Node comprehensionIf(GeneratorKind comprehensionKind);
     Node comprehensionFor(GeneratorKind comprehensionKind);
     Node comprehension(GeneratorKind comprehensionKind);
     Node arrayComprehension(uint32_t begin);
     Node generatorComprehension(uint32_t begin);
 
     bool argumentList(Node listNode, bool *isSpread);
-    Node letBlock(LetContext letContext);
+    Node deprecatedLetBlockOrExpression(LetContext letContext);
     Node destructuringExpr(BindData<ParseHandler> *data, TokenKind tt);
     Node destructuringExprWithoutYield(BindData<ParseHandler> *data, TokenKind tt, unsigned msg);
 
     Node identifierName();
 
     bool matchLabel(MutableHandle<PropertyName*> label);
 
     bool allowsForEachIn() {
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -92,18 +92,18 @@ class SyntaxParseHandler
                node == NodeUnparenthesizedCommaExpr ||
                node == NodeUnparenthesizedYieldExpr ||
                node == NodeUnparenthesizedAssignment;
     }
 
 
   public:
     SyntaxParseHandler(ExclusiveContext *cx, LifoAlloc &alloc,
-                       TokenStream &tokenStream, bool foldConstants,
-                       Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction)
+                       TokenStream &tokenStream, Parser<SyntaxParseHandler> *syntaxParser,
+                       LazyScript *lazyOuterFunction)
       : lastAtom(nullptr),
         tokenStream(tokenStream)
     {}
 
     static Node null() { return NodeFailure; }
 
     void trace(JSTracer *trc) {}
 
@@ -158,18 +158,18 @@ class SyntaxParseHandler
         return NodeGeneric;
     }
 
     Node newBinary(ParseNodeKind kind, JSOp op = JSOP_NOP) { return NodeGeneric; }
     Node newBinary(ParseNodeKind kind, Node left, JSOp op = JSOP_NOP) { return NodeGeneric; }
     Node newBinary(ParseNodeKind kind, Node left, Node right, JSOp op = JSOP_NOP) {
         return NodeGeneric;
     }
-    Node newBinaryOrAppend(ParseNodeKind kind, Node left, Node right,
-                           ParseContext<SyntaxParseHandler> *pc, JSOp op = JSOP_NOP) {
+    Node appendOrCreateList(ParseNodeKind kind, Node left, Node right,
+                            ParseContext<SyntaxParseHandler> *pc, JSOp op = JSOP_NOP) {
         return NodeGeneric;
     }
 
     Node newTernary(ParseNodeKind kind, Node first, Node second, Node third, JSOp op = JSOP_NOP) {
         return NodeGeneric;
     }
 
     // Expressions
@@ -241,16 +241,24 @@ class SyntaxParseHandler
 
     Node newForHead(ParseNodeKind kind, Node decls, Node lhs, Node rhs, const TokenPos &pos) {
         return NodeGeneric;
     }
 
     Node newLexicalScope(ObjectBox *blockbox) { return NodeGeneric; }
     void setLexicalScopeBody(Node block, Node body) {}
 
+    Node newLetExpression(Node vars, Node block, const TokenPos &pos) {
+        return NodeGeneric;
+    }
+
+    Node newLetBlock(Node vars, Node block, const TokenPos &pos) {
+        return NodeGeneric;
+    }
+
     bool finishInitializerAssignment(Node pn, Node init, JSOp op) { return true; }
 
     void setBeginPosition(Node pn, Node oth) {}
     void setBeginPosition(Node pn, uint32_t begin) {}
 
     void setEndPosition(Node pn, Node oth) {}
     void setEndPosition(Node pn, uint32_t end) {}
 
@@ -262,30 +270,34 @@ class SyntaxParseHandler
 
     Node newList(ParseNodeKind kind, JSOp op = JSOP_NOP) {
         return NodeGeneric;
     }
     Node newList(ParseNodeKind kind, Node kid, JSOp op = JSOP_NOP) {
         return NodeGeneric;
     }
 
+    Node newCatchList() {
+        return newList(PNK_CATCHLIST, JSOP_NOP);
+    }
+
     Node newCommaExpressionList(Node kid) {
         return NodeUnparenthesizedCommaExpr;
     }
 
     void addList(Node list, Node kid) {
         MOZ_ASSERT(list == NodeGeneric || list == NodeUnparenthesizedCommaExpr);
     }
 
     Node newAssignment(ParseNodeKind kind, Node lhs, Node rhs,
                        ParseContext<SyntaxParseHandler> *pc, JSOp op)
     {
         if (kind == PNK_ASSIGN)
             return NodeUnparenthesizedAssignment;
-        return newBinaryOrAppend(kind, lhs, rhs, pc, op);
+        return newBinary(kind, lhs, rhs, op);
     }
 
     bool isUnparenthesizedYieldExpression(Node node) {
         return node == NodeUnparenthesizedYieldExpr;
     }
 
     bool isUnparenthesizedCommaExpression(Node node) {
         return node == NodeUnparenthesizedCommaExpr;
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -463,16 +463,23 @@ class GCSchedulingTunables
  *      cusp, sometimes it will trigger a non-incremental GC moments before
  *      returning to the event loop, where it could have done an incremental
  *      GC. Thus, we recently added an incremental version of the above with a
  *      substantially lower threshold, so that we have a soft limit here. If
  *      IGC can collect faster than the allocator generates garbage, even if
  *      the allocator does not return to the event loop frequently, we should
  *      not have to fall back to a non-incremental GC.
  *
+ *      INCREMENTAL_TOO_SLOW
+ *      --------------------
+ *      Do a full, non-incremental GC if we overflow ALLOC_TRIGGER during an
+ *      incremental GC. When in the middle of an incremental GC, we suppress
+ *      our other triggers, so we need a way to backstop the IGC if the
+ *      mutator allocates faster than the IGC can clean things up.
+ *
  *      TOO_MUCH_MALLOC
  *      ---------------
  *      Performs a GC before size[allocated] - size[retained] gets too large
  *      for non-incremental sweeping to be fast in the case that we have
  *      significantly more malloc allocation than GC allocation. This is meant
  *      to complement MAYBEGC triggers. We track this by counting malloced
  *      bytes; the counter gets reset at every GC since we do not always have a
  *      size at the time we call free. Because of this, the malloc heuristic
@@ -840,16 +847,17 @@ class GCRuntime
     friend class ArenaLists;
     Chunk *pickChunk(const AutoLockGC &lock,
                      AutoMaybeStartBackgroundAllocation &maybeStartBGAlloc);
     ArenaHeader *allocateArena(Chunk *chunk, Zone *zone, AllocKind kind, const AutoLockGC &lock);
     inline void arenaAllocatedDuringGC(JS::Zone *zone, ArenaHeader *arena);
 
     template <AllowGC allowGC>
     static void *refillFreeListFromMainThread(JSContext *cx, AllocKind thingKind);
+    static void *tryRefillFreeListFromMainThread(JSContext *cx, AllocKind thingKind);
     static void *refillFreeListOffMainThread(ExclusiveContext *cx, AllocKind thingKind);
 
     /*
      * Return the list of chunks that can be released outside the GC lock.
      * Must be called either during the GC or with the GC lock taken.
      */
     ChunkPool expireEmptyChunkPool(bool shrinkBuffers, const AutoLockGC &lock);
     void freeEmptyChunks(JSRuntime *rt, const AutoLockGC &lock);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1127303.js
@@ -0,0 +1,9 @@
+
+function test1() {}
+function test() { test1.call(this); }
+var length = 30 * 1024 - 1;
+var obj = new test();
+for(var i = 0 ; i < length ; i++) {
+  obj.next = new (function  (   )  {  }  )  ();
+  obj = obj.next;
+}
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -9162,23 +9162,28 @@ GetTemplateObjectForNative(JSContext *cx
         RootedObject proto(cx, args[0].toObjectOrNull());
         res.set(ObjectCreateImpl(cx, proto, TenuredObject));
         if (!res)
             return false;
         return true;
     }
 
     if (JitSupportsSimd()) {
-        if (native == js::simd_int32x4_add || native == js::simd_int32x4_and) {
+#define ADD_INT32X4_SIMD_OP_NAME_(OP) || native == js::simd_int32x4_##OP
+       if (false
+           ARITH_COMMONX4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_)
+           BITWISE_COMMONX4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_))
+       {
             Rooted<TypeDescr *> descr(cx, &Int32x4::GetTypeDescr(*cx->global()));
             res.set(TypedObject::createZeroed(cx, descr, 0, gc::TenuredHeap));
             if (!res)
                 return false;
             return true;
-        }
+       }
+#undef ADD_INT32X4_SIMD_OP_NAME_
     }
 
     return true;
 }
 
 static bool
 GetTemplateObjectForClassHook(JSContext *cx, JSNative hook, CallArgs &args,
                               MutableHandleObject templateObject)
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -3,30 +3,30 @@
  * 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 "jit/IonCaches.h"
 
 #include "mozilla/TemplateLib.h"
 
-#include "jsproxy.h"
 #include "jstypes.h"
 
 #include "builtin/TypedObject.h"
 #include "jit/BaselineIC.h"
 #include "jit/Ion.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitSpewer.h"
 #include "jit/Linker.h"
 #include "jit/Lowering.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "jit/VMFunctions.h"
+#include "js/Proxy.h"
 #include "vm/Shape.h"
 
 #include "jit/JitFrames-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
 using namespace js::jit;
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -252,20 +252,27 @@ IonBuilder::inlineNativeCall(CallInfo &c
     if (native == testingFunc_assertFloat32)
         return inlineAssertFloat32(callInfo);
 
     // Bound function
     if (native == js::CallOrConstructBoundFunction)
         return inlineBoundFunction(callInfo, target);
 
     // Simd functions
-    if (native == js::simd_int32x4_add)
-        return inlineSimdInt32x4BinaryArith(callInfo, native, MSimdBinaryArith::Add);
-    if (native == js::simd_int32x4_and)
-        return inlineSimdInt32x4BinaryBitwise(callInfo, native, MSimdBinaryBitwise::and_);
+#define INLINE_INT32X4_SIMD_ARITH_(OP)                                                      \
+    if (native == js::simd_int32x4_##OP)                                                    \
+        return inlineSimdInt32x4BinaryArith(callInfo, native, MSimdBinaryArith::Op_##OP);
+    ARITH_COMMONX4_SIMD_OP(INLINE_INT32X4_SIMD_ARITH_)
+#undef INLINE_INT32X4_SIMD_ARITH_
+
+#define INLINE_INT32X4_SIMD_BITWISE_(OP)                                                    \
+    if (native == js::simd_int32x4_##OP)                                                    \
+        return inlineSimdInt32x4BinaryBitwise(callInfo, native, MSimdBinaryBitwise::OP##_);
+    BITWISE_COMMONX4_SIMD_OP(INLINE_INT32X4_SIMD_BITWISE_)
+#undef INLINE_INT32X4_SIMD_BITWISE_
 
     return InliningStatus_NotInlined;
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineNativeGetter(CallInfo &callInfo, JSFunction *target)
 {
     MOZ_ASSERT(target->isNative());
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -10,16 +10,17 @@
  */
 
 #ifndef jit_MIR_h
 #define jit_MIR_h
 
 #include "mozilla/Array.h"
 #include "mozilla/DebugOnly.h"
 
+#include "builtin/SIMD.h"
 #include "jit/AtomicOp.h"
 #include "jit/FixedList.h"
 #include "jit/InlineList.h"
 #include "jit/JitAllocPolicy.h"
 #include "jit/MacroAssembler.h"
 #include "jit/MOpcodes.h"
 #include "jit/TypedObjectPrediction.h"
 #include "jit/TypePolicy.h"
@@ -1953,51 +1954,43 @@ class MSimdBinaryComp
 };
 
 class MSimdBinaryArith
   : public MBinaryInstruction,
     public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdSameAsReturnedTypePolicy<1> >::Data
 {
   public:
     enum Operation {
-        Add,
-        Sub,
-        Mul,
-        Div,
-        Min,
-        Max,
-        MinNum,
-        MaxNum
+#define OP_LIST_(OP) Op_##OP,
+        ARITH_COMMONX4_SIMD_OP(OP_LIST_)
+        ARITH_FLOAT32X4_SIMD_OP(OP_LIST_)
+#undef OP_LIST_
     };
 
     static const char* OperationName(Operation op) {
         switch (op) {
-          case Add:    return "Add";
-          case Sub:    return "Sub";
-          case Mul:    return "Mul";
-          case Div:    return "Div";
-          case Min:    return "Min";
-          case Max:    return "Max";
-          case MinNum: return "MinNum";
-          case MaxNum: return "MaxNum";
+#define OP_CASE_LIST_(OP) case Op_##OP: return #OP;
+          ARITH_COMMONX4_SIMD_OP(OP_CASE_LIST_)
+          ARITH_FLOAT32X4_SIMD_OP(OP_CASE_LIST_)
+#undef OP_CASE_LIST_
         }
         MOZ_CRASH("unexpected operation");
     }
 
   private:
     Operation operation_;
 
     MSimdBinaryArith(MDefinition *left, MDefinition *right, Operation op, MIRType type)
       : MBinaryInstruction(left, right), operation_(op)
     {
-        MOZ_ASSERT_IF(type == MIRType_Int32x4, op == Add || op == Sub || op == Mul);
+        MOZ_ASSERT_IF(type == MIRType_Int32x4, op == Op_add || op == Op_sub || op == Op_mul);
         MOZ_ASSERT(IsSimdType(type));
         setResultType(type);
         setMovable();
-        if (op == Add || op == Mul || op == Min || op == Max)
+        if (op == Op_add || op == Op_mul || op == Op_min || op == Op_max)
             setCommutative();
     }
 
   public:
     INSTRUCTION_HEADER(SimdBinaryArith)
     static MSimdBinaryArith *New(TempAllocator &alloc, MDefinition *left, MDefinition *right,
                                  Operation op, MIRType t)
     {
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -2652,23 +2652,23 @@ void
 CodeGeneratorX86Shared::visitSimdBinaryArithIx4(LSimdBinaryArithIx4 *ins)
 {
     FloatRegister lhs = ToFloatRegister(ins->lhs());
     Operand rhs = ToOperand(ins->rhs());
     FloatRegister output = ToFloatRegister(ins->output());
 
     MSimdBinaryArith::Operation op = ins->operation();
     switch (op) {
-      case MSimdBinaryArith::Add:
+      case MSimdBinaryArith::Op_add:
         masm.vpaddd(rhs, lhs, output);
         return;
-      case MSimdBinaryArith::Sub:
+      case MSimdBinaryArith::Op_sub:
         masm.vpsubd(rhs, lhs, output);
         return;
-      case MSimdBinaryArith::Mul: {
+      case MSimdBinaryArith::Op_mul: {
         if (AssemblerX86Shared::HasSSE41()) {
             masm.vpmulld(rhs, lhs, output);
             return;
         }
 
         masm.loadAlignedInt32x4(rhs, ScratchSimdReg);
         masm.vpmuludq(lhs, ScratchSimdReg, ScratchSimdReg);
         // ScratchSimdReg contains (Rx, _, Rz, _) where R is the resulting vector.
@@ -2679,76 +2679,76 @@ CodeGeneratorX86Shared::visitSimdBinaryA
         masm.vpmuludq(temp, lhs, lhs);
         // lhs contains (Ry, _, Rw, _) where R is the resulting vector.
 
         masm.vshufps(MacroAssembler::ComputeShuffleMask(LaneX, LaneZ, LaneX, LaneZ), ScratchSimdReg, lhs, lhs);
         // lhs contains (Ry, Rw, Rx, Rz)
         masm.vshufps(MacroAssembler::ComputeShuffleMask(LaneZ, LaneX, LaneW, LaneY), lhs, lhs, lhs);
         return;
       }
-      case MSimdBinaryArith::Div:
+      case MSimdBinaryArith::Op_div:
         // x86 doesn't have SIMD i32 div.
         break;
-      case MSimdBinaryArith::Max:
+      case MSimdBinaryArith::Op_max:
         // we can do max with a single instruction only if we have SSE4.1
         // using the PMAXSD instruction.
         break;
-      case MSimdBinaryArith::Min:
+      case MSimdBinaryArith::Op_min:
         // we can do max with a single instruction only if we have SSE4.1
         // using the PMINSD instruction.
         break;
-      case MSimdBinaryArith::MinNum:
-      case MSimdBinaryArith::MaxNum:
+      case MSimdBinaryArith::Op_minNum:
+      case MSimdBinaryArith::Op_maxNum:
         break;
     }
     MOZ_CRASH("unexpected SIMD op");
 }
 
 void
 CodeGeneratorX86Shared::visitSimdBinaryArithFx4(LSimdBinaryArithFx4 *ins)
 {
     FloatRegister lhs = ToFloatRegister(ins->lhs());
     Operand rhs = ToOperand(ins->rhs());
     FloatRegister output = ToFloatRegister(ins->output());
 
     MSimdBinaryArith::Operation op = ins->operation();
     switch (op) {
-      case MSimdBinaryArith::Add:
+      case MSimdBinaryArith::Op_add:
         masm.vaddps(rhs, lhs, output);
         return;
-      case MSimdBinaryArith::Sub:
+      case MSimdBinaryArith::Op_sub:
         masm.vsubps(rhs, lhs, output);
         return;
-      case MSimdBinaryArith::Mul:
+      case MSimdBinaryArith::Op_mul:
         masm.vmulps(rhs, lhs, output);
         return;
-      case MSimdBinaryArith::Div:
+      case MSimdBinaryArith::Op_div:
         masm.vdivps(rhs, lhs, output);
         return;
-      case MSimdBinaryArith::Max: {
+      case MSimdBinaryArith::Op_max: {
         FloatRegister lhsCopy = masm.reusedInputFloat32x4(lhs, ScratchSimdReg);
         masm.vcmpunordps(rhs, lhsCopy, ScratchSimdReg);
 
         FloatRegister tmp = ToFloatRegister(ins->temp());
         FloatRegister rhsCopy = masm.reusedInputAlignedFloat32x4(rhs, tmp);
         masm.vmaxps(Operand(lhs), rhsCopy, tmp);
         masm.vmaxps(rhs, lhs, output);
 
         masm.vandps(tmp, output, output);
         masm.vorps(ScratchSimdReg, output, output); // or in the all-ones NaNs
         return;
       }
-      case MSimdBinaryArith::Min: {
+      case MSimdBinaryArith::Op_min: {
         FloatRegister rhsCopy = masm.reusedInputAlignedFloat32x4(rhs, ScratchSimdReg);
         masm.vminps(Operand(lhs), rhsCopy, ScratchSimdReg);
         masm.vminps(rhs, lhs, output);
         masm.vorps(ScratchSimdReg, output, output); // NaN or'd with arbitrary bits is NaN
         return;
       }
-      case MSimdBinaryArith::MinNum: {
+      case MSimdBinaryArith::Op_minNum: {
         FloatRegister tmp = ToFloatRegister(ins->temp());
         masm.loadConstantInt32x4(SimdConstant::SplatX4(int32_t(0x80000000)), tmp);
 
         FloatRegister mask = ScratchSimdReg;
         FloatRegister tmpCopy = masm.reusedInputFloat32x4(tmp, ScratchSimdReg);
         masm.vpcmpeqd(Operand(lhs), tmpCopy, mask);
         masm.vandps(tmp, mask, mask);
 
@@ -2768,17 +2768,17 @@ CodeGeneratorX86Shared::visitSimdBinaryA
             if (lhs != output)
                 masm.moveFloat32x4(lhs, output);
             masm.vandps(Operand(mask), output, output);
             masm.vandnps(Operand(tmp), mask, mask);
             masm.vorps(Operand(mask), output, output);
         }
         return;
       }
-      case MSimdBinaryArith::MaxNum: {
+      case MSimdBinaryArith::Op_maxNum: {
         FloatRegister mask = ScratchSimdReg;
         masm.loadConstantInt32x4(SimdConstant::SplatX4(0), mask);
         masm.vpcmpeqd(Operand(lhs), mask, mask);
 
         FloatRegister tmp = ToFloatRegister(ins->temp());
         masm.loadConstantInt32x4(SimdConstant::SplatX4(int32_t(0x80000000)), tmp);
         masm.vandps(tmp, mask, mask);
 
--- a/js/src/jit/shared/Lowering-x86-shared.cpp
+++ b/js/src/jit/shared/Lowering-x86-shared.cpp
@@ -634,29 +634,29 @@ LIRGeneratorX86Shared::visitSimdBinaryAr
     MDefinition *lhs = ins->lhs();
     MDefinition *rhs = ins->rhs();
 
     if (ins->isCommutative())
         ReorderCommutative(&lhs, &rhs, ins);
 
     if (ins->type() == MIRType_Int32x4) {
         LSimdBinaryArithIx4 *lir = new(alloc()) LSimdBinaryArithIx4();
-        bool needsTemp = ins->operation() == MSimdBinaryArith::Mul && !MacroAssembler::HasSSE41();
+        bool needsTemp = ins->operation() == MSimdBinaryArith::Op_mul && !MacroAssembler::HasSSE41();
         lir->setTemp(0, needsTemp ? temp(LDefinition::INT32X4) : LDefinition::BogusTemp());
         lowerForFPU(lir, ins, lhs, rhs);
         return;
     }
 
     MOZ_ASSERT(ins->type() == MIRType_Float32x4, "unknown simd type on binary arith operation");
 
     LSimdBinaryArithFx4 *lir = new(alloc()) LSimdBinaryArithFx4();
 
-    bool needsTemp = ins->operation() == MSimdBinaryArith::Max ||
-                     ins->operation() == MSimdBinaryArith::MinNum ||
-                     ins->operation() == MSimdBinaryArith::MaxNum;
+    bool needsTemp = ins->operation() == MSimdBinaryArith::Op_max ||
+                     ins->operation() == MSimdBinaryArith::Op_minNum ||
+                     ins->operation() == MSimdBinaryArith::Op_maxNum;
     lir->setTemp(0, needsTemp ? temp(LDefinition::FLOAT32X4) : LDefinition::BogusTemp());
 
     lowerForFPU(lir, ins, lhs, rhs);
 }
 
 void
 LIRGeneratorX86Shared::visitSimdSelect(MSimdSelect *ins)
 {
--- a/js/src/jsapi-tests/testSetPropertyIgnoringNamedGetter.cpp
+++ b/js/src/jsapi-tests/testSetPropertyIgnoringNamedGetter.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  */
 
 #include "jsfriendapi.h"
-#include "jsproxy.h"
+
+#include "js/Proxy.h"
 
 #include "jsapi-tests/tests.h"
 
 using namespace js;
 using namespace JS;
 
 class CustomProxyHandler : public DirectProxyHandler {
   public:
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -30,17 +30,16 @@
 #include "jsgc.h"
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsmath.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "json.h"
 #include "jsprf.h"
-#include "jsproxy.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #include "jswatchpoint.h"
 #include "jsweakmap.h"
 #include "jswrapper.h"
 
@@ -57,16 +56,17 @@
 #endif
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/FullParseHandler.h"  // for JS_BufferIsCompileableUnit
 #include "frontend/Parser.h" // for JS_BufferIsCompileableUnit
 #include "gc/Marking.h"
 #include "jit/JitCommon.h"
 #include "js/CharacterEncoding.h"
 #include "js/Conversions.h"
+#include "js/Proxy.h"
 #include "js/SliceBudget.h"
 #include "js/StructuredClone.h"
 #if ENABLE_INTL_API
 #include "unicode/uclean.h"
 #include "unicode/utypes.h"
 #endif // ENABLE_INTL_API
 #include "vm/DateObject.h"
 #include "vm/Debugger.h"
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -8,22 +8,22 @@
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "jscntxt.h"
 #include "jsfriendapi.h"
 #include "jsgc.h"
 #include "jsiter.h"
-#include "jsproxy.h"
 #include "jswatchpoint.h"
 #include "jswrapper.h"
 
 #include "gc/Marking.h"
 #include "jit/JitCompartment.h"
+#include "js/Proxy.h"
 #include "js/RootingAPI.h"
 #include "proxy/DeadObjectProxy.h"
 #include "vm/Debugger.h"
 #include "vm/StopIterationObject.h"
 #include "vm/WrapperObject.h"
 
 #include "jsatominlines.h"
 #include "jsfuninlines.h"
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -10,23 +10,23 @@
 
 #include <stdint.h>
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsgc.h"
 #include "jsobj.h"
 #include "jsprf.h"
-#include "jsproxy.h"
 #include "jswatchpoint.h"
 #include "jsweakmap.h"
 #include "jswrapper.h"
 #include "prmjtime.h"
 
 #include "builtin/TestingFunctions.h"
+#include "js/Proxy.h"
 #include "proxy/DeadObjectProxy.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/WrapperObject.h"
 
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/NativeObject-inl.h"
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -16,30 +16,30 @@
 
 #include <string.h>
 
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsobj.h"
-#include "jsproxy.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstypes.h"
 #include "jswrapper.h"
 
 #include "builtin/Eval.h"
 #include "builtin/Object.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/TokenStream.h"
 #include "gc/Marking.h"
 #include "jit/Ion.h"
 #include "jit/JitFrameIterator.h"
 #include "js/CallNonGenericMethod.h"
+#include "js/Proxy.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Shape.h"
 #include "vm/StringBuffer.h"
 #include "vm/WrapperObject.h"
 #include "vm/Xdr.h"
 
 #include "jsscriptinlines.h"
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2917,89 +2917,73 @@ void
 ArenaLists::queueForegroundThingsForSweep(FreeOp *fop)
 {
     gcShapeArenasToUpdate = arenaListsToSweep[FINALIZE_SHAPE];
     gcAccessorShapeArenasToUpdate = arenaListsToSweep[FINALIZE_ACCESSOR_SHAPE];
     gcObjectGroupArenasToUpdate = arenaListsToSweep[FINALIZE_OBJECT_GROUP];
     gcScriptArenasToUpdate = arenaListsToSweep[FINALIZE_SCRIPT];
 }
 
-static void *
-RunLastDitchGC(JSContext *cx, JS::Zone *zone, AllocKind thingKind)
-{
-    PrepareZoneForGC(zone);
-
-    JSRuntime *rt = cx->runtime();
-
-    /* The last ditch GC preserves all atoms. */
-    AutoKeepAtoms keepAtoms(cx->perThreadData);
-    rt->gc.gc(GC_NORMAL, JS::gcreason::LAST_DITCH);
-
-    /*
-     * The JSGC_END callback can legitimately allocate new GC
-     * things and populate the free list. If that happens, just
-     * return that list head.
-     */
-    size_t thingSize = Arena::thingSize(thingKind);
-    return zone->arenas.allocateFromFreeList(thingKind, thingSize);
+/* static */ void *
+GCRuntime::tryRefillFreeListFromMainThread(JSContext *cx, AllocKind thingKind)
+{
+    ArenaLists *arenas = cx->arenas();
+    Zone *zone = cx->zone();
+
+    AutoMaybeStartBackgroundAllocation maybeStartBGAlloc;
+
+    void *thing = arenas->allocateFromArena(zone, thingKind, maybeStartBGAlloc);
+    if (MOZ_LIKELY(thing))
+        return thing;
+
+    // Even if allocateFromArena failed due to OOM, a background
+    // finalization or allocation task may be running freeing more memory
+    // or adding more available memory to our free pool; wait for them to
+    // finish, then try to allocate again in case they made more memory
+    // available.
+    cx->runtime()->gc.waitBackgroundSweepOrAllocEnd();
+
+    thing = arenas->allocateFromArena(zone, thingKind, maybeStartBGAlloc);
+    if (thing)
+        return thing;
+
+    return nullptr;
 }
 
 template <AllowGC allowGC>
 /* static */ void *
 GCRuntime::refillFreeListFromMainThread(JSContext *cx, AllocKind thingKind)
 {
     JSRuntime *rt = cx->runtime();
     MOZ_ASSERT(!rt->isHeapBusy(), "allocating while under GC");
     MOZ_ASSERT_IF(allowGC, !rt->currentThreadHasExclusiveAccess());
 
-    ArenaLists *arenas = cx->arenas();
-    Zone *zone = cx->zone();
-
-    // If we have grown past our GC heap threshold while in the middle of an
-    // incremental GC, we're growing faster than we're GCing, so stop the world
-    // and do a full, non-incremental GC right now, if possible.
-    const bool mustCollectNow = allowGC && rt->gc.isIncrementalGCInProgress() &&
-                                zone->usage.gcBytes() > zone->threshold.gcTriggerBytes();
-
-    bool outOfMemory = false;  // Set true if we fail to allocate.
-    bool ranGC = false;  // Once we've GC'd and still cannot allocate, report.
-    do {
-        if (MOZ_UNLIKELY(mustCollectNow || outOfMemory)) {
-            // If we are doing a fallible allocation, percolate up the OOM
-            // instead of reporting it.
-            if (!allowGC) {
-                MOZ_ASSERT(!mustCollectNow);
-                return nullptr;
-            }
-
-            if (void *thing = RunLastDitchGC(cx, zone, thingKind))
-                return thing;
-            ranGC = true;
-        }
-
-        AutoMaybeStartBackgroundAllocation maybeStartBGAlloc;
-        void *thing = arenas->allocateFromArena(zone, thingKind, maybeStartBGAlloc);
-        if (MOZ_LIKELY(thing))
-            return thing;
-
-        // Even if allocateFromArena failed due to OOM, a background
-        // finalization or allocation task may be running freeing more memory
-        // or adding more available memory to our free pool; wait for them to
-        // finish, then try to allocate again in case they made more memory
-        // available.
-        rt->gc.waitBackgroundSweepOrAllocEnd();
-
-        thing = arenas->allocateFromArena(zone, thingKind, maybeStartBGAlloc);
-        if (MOZ_LIKELY(thing))
-            return thing;
-
-        // Retry after a last-ditch GC, unless we've already tried that.
-        outOfMemory = true;
-    } while (!ranGC);
-
+    // Try to allocate; synchronize with background GC threads if necessary.
+    void *thing = tryRefillFreeListFromMainThread(cx, thingKind);
+    if (MOZ_LIKELY(thing))
+        return thing;
+
+    // Perform a last-ditch GC to hopefully free up some memory.
+    {
+        // If we are doing a fallible allocation, percolate up the OOM
+        // instead of reporting it.
+        if (!allowGC)
+            return nullptr;
+
+        JS::PrepareZoneForGC(cx->zone());
+        AutoKeepAtoms keepAtoms(cx->perThreadData);
+        rt->gc.gc(GC_NORMAL, JS::gcreason::LAST_DITCH);
+    }
+
+    // Retry the allocation after the last-ditch GC.
+    thing = tryRefillFreeListFromMainThread(cx, thingKind);
+    if (thing)
+        return thing;
+
+    // We are really just totally out of memory.
     MOZ_ASSERT(allowGC, "A fallible allocation must not report OOM on failure.");
     js_ReportOutOfMemory(cx);
     return nullptr;
 }
 
 /* static */ void *
 GCRuntime::refillFreeListOffMainThread(ExclusiveContext *cx, AllocKind thingKind)
 {
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -432,30 +432,40 @@ CheckAllocatorState(ExclusiveContext *cx
 #endif
 
     // Crash if we perform a GC action when it is not safe.
     if (allowGC && !rt->mainThread.suppressGC)
         JS::AutoAssertOnGC::VerifyIsSafeToGC(rt);
 
     // For testing out of memory conditions
     if (!PossiblyFail()) {
-        js_ReportOutOfMemory(cx->asJSContext());
+        js_ReportOutOfMemory(ncx);
         return false;
     }
 
     if (allowGC) {
 #ifdef JS_GC_ZEAL
         if (rt->gc.needZealousGC())
             rt->gc.runDebugGC();
 #endif
-
         if (rt->hasPendingInterrupt()) {
             // Invoking the interrupt callback can fail and we can't usefully
             // handle that here. Just check in case we need to collect instead.
-            rt->gc.gcIfRequested();
+            rt->gc.gcIfRequested(ncx);
+        }
+
+        // If we have grown past our GC heap threshold while in the middle of
+        // an incremental GC, we're growing faster than we're GCing, so stop
+        // the world and do a full, non-incremental GC right now, if possible.
+        if (rt->gc.isIncrementalGCInProgress() &&
+            ncx->zone()->usage.gcBytes() > ncx->zone()->threshold.gcTriggerBytes())
+        {
+            PrepareZoneForGC(ncx->zone());
+            AutoKeepAtoms keepAtoms(cx->perThreadData);
+            rt->gc.gc(GC_NORMAL, JS::gcreason::INCREMENTAL_TOO_SLOW);
         }
     }
 
     return true;
 }
 
 template <typename T>
 static inline void
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -14,23 +14,23 @@
 #include "mozilla/PodOperations.h"
 
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsgc.h"
 #include "jsobj.h"
 #include "jsopcode.h"
-#include "jsproxy.h"
 #include "jsscript.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "ds/Sort.h"
 #include "gc/Marking.h"
+#include "js/Proxy.h"
 #include "vm/GeneratorObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Shape.h"
 #include "vm/StopIterationObject.h"
 #include "vm/TypedArrayCommon.h"
 
 #include "jsscriptinlines.h"
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -23,32 +23,32 @@
 #include "jscntxt.h"
 #include "jsfriendapi.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsiter.h"
 #include "jsnum.h"
 #include "jsopcode.h"
 #include "jsprf.h"
-#include "jsproxy.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #include "jswatchpoint.h"
 #include "jswrapper.h"
 
 #include "asmjs/AsmJSModule.h"
 #include "builtin/Eval.h"
 #include "builtin/Object.h"
 #include "builtin/SymbolObject.h"
 #include "frontend/BytecodeCompiler.h"
 #include "gc/Marking.h"
 #include "jit/BaselineJIT.h"
 #include "js/MemoryMetrics.h"
+#include "js/Proxy.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Interpreter.h"
 #include "vm/ProxyObject.h"
 #include "vm/RegExpStaticsObject.h"
 #include "vm/Shape.h"
 #include "vm/TypedArrayCommon.h"
 
 #include "jsatominlines.h"
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -725,17 +725,17 @@ namespace js {
  * is actually quite good on this topic, though you may have to read it a few
  * times. See ES6 draft rev 29 (6 Dec 2014) 6.1.7.2 and 6.1.7.3.
  *
  * When 'obj' is an ordinary object, these functions have boring standard
  * behavior as specified by ES6 draft rev 29 section 9.1; see the section about
  * internal methods in vm/NativeObject.h.
  *
  * Proxies override the behavior of internal methods. So when 'obj' is a proxy,
- * any one of the functions below could do just about anything. See jsproxy.h.
+ * any one of the functions below could do just about anything. See js/Proxy.h.
  */
 
 /*
  * ES6 [[GetPrototypeOf]]. Get obj's prototype, storing it in protop.
  *
  * If obj is definitely not a proxy, the infallible obj->getProto() can be used
  * instead. See the comment on JSObject::getTaggedProto().
  */
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -2297,21 +2297,21 @@ ASTSerializer::tryStatement(ParseNode *p
 
     RootedValue body(cx);
     if (!statement(pn->pn_kid1, &body))
         return false;
 
     NodeVector guarded(cx);
     RootedValue unguarded(cx, NullValue());
 
-    if (pn->pn_kid2) {
-        if (!guarded.reserve(pn->pn_kid2->pn_count))
+    if (ParseNode *catchList = pn->pn_kid2) {
+        if (!guarded.reserve(catchList->pn_count))
             return false;
 
-        for (ParseNode *next = pn->pn_kid2->pn_head; next; next = next->pn_next) {
+        for (ParseNode *next = catchList->pn_head; next; next = next->pn_next) {
             RootedValue clause(cx);
             bool isGuarded;
             if (!catchClause(next->pn_expr, &isGuarded, &clause))
                 return false;
             if (isGuarded)
                 guarded.infallibleAppend(clause);
             else
                 unguarded = clause;
@@ -2362,21 +2362,22 @@ ASTSerializer::statement(ParseNode *pn, 
 {
     JS_CHECK_RECURSION(cx, return false);
     switch (pn->getKind()) {
       case PNK_FUNCTION:
       case PNK_VAR:
       case PNK_GLOBALCONST:
         return declaration(pn, dst);
 
+      case PNK_LETBLOCK:
+        return let(pn, false, dst);
+
       case PNK_LET:
       case PNK_CONST:
-        return pn->isArity(PN_BINARY)
-               ? let(pn, false, dst)
-               : declaration(pn, dst);
+        return declaration(pn, dst);
 
       case PNK_IMPORT:
         return importDeclaration(pn, dst);
 
       case PNK_EXPORT:
       case PNK_EXPORT_FROM:
         return exportDeclaration(pn, dst);
 
@@ -2750,28 +2751,17 @@ ASTSerializer::expression(ParseNode *pn,
         return expression(pn->pn_kid1, &test) &&
                expression(pn->pn_kid2, &cons) &&
                expression(pn->pn_kid3, &alt) &&
                builder.conditionalExpression(test, cons, alt, &pn->pn_pos, dst);
       }
 
       case PNK_OR:
       case PNK_AND:
-      {
-        if (pn->isArity(PN_BINARY)) {
-            MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
-            MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
-
-            RootedValue left(cx), right(cx);
-            return expression(pn->pn_left, &left) &&
-                   expression(pn->pn_right, &right) &&
-                   builder.logicalExpression(pn->isKind(PNK_OR), left, right, &pn->pn_pos, dst);
-        }
         return leftAssociate(pn, dst);
-      }
 
       case PNK_PREINCREMENT:
       case PNK_PREDECREMENT:
       {
         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
 
         bool inc = pn->isKind(PNK_PREINCREMENT);
         RootedValue expr(cx);
@@ -2831,28 +2821,16 @@ ASTSerializer::expression(ParseNode *pn,
       case PNK_STAR:
       case PNK_DIV:
       case PNK_MOD:
       case PNK_BITOR:
       case PNK_BITXOR:
       case PNK_BITAND:
       case PNK_IN:
       case PNK_INSTANCEOF:
-        if (pn->isArity(PN_BINARY)) {
-            MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
-            MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
-
-            BinaryOperator op = binop(pn->getKind(), pn->getOp());
-            LOCAL_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT);
-
-            RootedValue left(cx), right(cx);
-            return expression(pn->pn_left, &left) &&
-                   expression(pn->pn_right, &right) &&
-                   builder.binaryExpression(op, left, right, &pn->pn_pos, dst);
-        }
         return leftAssociate(pn, dst);
 
       case PNK_DELETE:
       case PNK_TYPEOF:
       case PNK_VOID:
       case PNK_NOT:
       case PNK_BITNOT:
       case PNK_POS:
@@ -3062,17 +3040,17 @@ ASTSerializer::expression(ParseNode *pn,
 
       case PNK_ARRAYCOMP:
         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_head->pn_pos));
 
         /* NB: it's no longer the case that pn_count could be 2. */
         LOCAL_ASSERT(pn->pn_count == 1);
         return comprehension(pn->pn_head, dst);
 
-      case PNK_LET:
+      case PNK_LETEXPR:
         return let(pn, true, dst);
 
       default:
         LOCAL_NOT_REACHED("unexpected expression type");
     }
 }
 
 bool
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -4,17 +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/. */
 
 #ifndef jswrapper_h
 #define jswrapper_h
 
 #include "mozilla/Attributes.h"
 
-#include "jsproxy.h"
+#include "js/Proxy.h"
 
 namespace js {
 
 class DummyFrameGuard;
 
 /*
  * Helper for Wrapper::New default options.
  *
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -46,17 +46,16 @@ EXPORTS += [
     'jsalloc.h',
     'jsapi.h',
     'jsbytecode.h',
     'jsclist.h',
     'jscpucfg.h',
     'jsfriendapi.h',
     'jsprf.h',
     'jsprototypes.h',
-    'jsproxy.h',
     'jspubtd.h',
     'jstypes.h',
     'jsversion.h',
     'jswrapper.h',
     'perf/jsperf.h',
 ]
 
 # If you add a header here, add it to js/src/jsapi-tests/testIntTypesABI.cpp so
@@ -74,16 +73,17 @@ EXPORTS.js += [
     '../public/HashTable.h',
     '../public/HeapAPI.h',
     '../public/Id.h',
     '../public/LegacyIntTypes.h',
     '../public/MemoryMetrics.h',
     '../public/Principals.h',
     '../public/ProfilingFrameIterator.h',
     '../public/ProfilingStack.h',
+    '../public/Proxy.h',
     '../public/RequiredDefines.h',
     '../public/RootingAPI.h',
     '../public/SliceBudget.h',
     '../public/StructuredClone.h',
     '../public/TracingAPI.h',
     '../public/TrackedOptimizationInfo.h',
     '../public/TypeDecls.h',
     '../public/UbiNode.h',
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "jsproxy.h"
-
+#include "js/Proxy.h"
 #include "vm/ProxyObject.h"
 
 #include "jscntxtinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 
 bool
--- a/js/src/proxy/DeadObjectProxy.h
+++ b/js/src/proxy/DeadObjectProxy.h
@@ -2,17 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef proxy_DeadObjectProxy_h
 #define proxy_DeadObjectProxy_h
 
-#include "jsproxy.h"
+#include "js/Proxy.h"
 
 namespace js {
 
 class DeadObjectProxy : public BaseProxyHandler
 {
   public:
     explicit MOZ_CONSTEXPR DeadObjectProxy()
       : BaseProxyHandler(&family)
--- a/js/src/proxy/DirectProxyHandler.cpp
+++ b/js/src/proxy/DirectProxyHandler.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "jsproxy.h"
 #include "jswrapper.h" // UncheckedUnwrap
 
+#include "js/Proxy.h"
 #include "vm/ProxyObject.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 
 bool
 DirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -1,21 +1,22 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "js/Proxy.h"
+
 #include <string.h>
 
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsfun.h"
 #include "jsgc.h"
-#include "jsproxy.h"
 #include "jswrapper.h"
 
 #include "gc/Marking.h"
 #include "proxy/DeadObjectProxy.h"
 #include "proxy/ScriptedDirectProxyHandler.h"
 #include "proxy/ScriptedIndirectProxyHandler.h"
 #include "vm/WrapperObject.h"
 
@@ -109,53 +110,30 @@ Proxy::getPropertyDescriptor(JSContext *
     if (!handler->getOwnPropertyDescriptor(cx, proxy, id, desc))
         return false;
     if (desc.object())
         return true;
     INVOKE_ON_PROTOTYPE(cx, handler, proxy, GetPropertyDescriptor(cx, proto, id, desc));
 }
 
 bool
-Proxy::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandleValue vp)
-{
-    JS_CHECK_RECURSION(cx, return false);
-
-    Rooted<PropertyDescriptor> desc(cx);
-    if (!Proxy::getPropertyDescriptor(cx, proxy, id, &desc))
-        return false;
-    return NewPropertyDescriptorObject(cx, desc, vp);
-}
-
-bool
 Proxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                 MutableHandle<PropertyDescriptor> desc)
 {
     JS_CHECK_RECURSION(cx, return false);
 
     const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
     desc.object().set(nullptr); // default result if we refuse to perform this action
     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET_PROPERTY_DESCRIPTOR, true);
     if (!policy.allowed())
         return policy.returnValue();
     return handler->getOwnPropertyDescriptor(cx, proxy, id, desc);
 }
 
 bool
-Proxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
-                                MutableHandleValue vp)
-{
-    JS_CHECK_RECURSION(cx, return false);
-
-    Rooted<PropertyDescriptor> desc(cx);
-    if (!Proxy::getOwnPropertyDescriptor(cx, proxy, id, &desc))
-        return false;
-    return NewPropertyDescriptorObject(cx, desc, vp);
-}
-
-bool
 Proxy::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                       MutableHandle<PropertyDescriptor> desc)
 {
     JS_CHECK_RECURSION(cx, return false);
     const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
     if (!policy.allowed())
         return policy.returnValue();
--- a/js/src/proxy/Proxy.h
+++ b/js/src/proxy/Proxy.h
@@ -23,18 +23,16 @@ class RegExpGuard;
  * 945826 comment 0.
  */
 class Proxy
 {
   public:
     /* Standard internal methods. */
     static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                          MutableHandle<JSPropertyDescriptor> desc);
-    static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
-                                         MutableHandleValue vp);
     static bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                MutableHandle<JSPropertyDescriptor> desc);
     static bool ownPropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props);
     static bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
     static bool enumerate(JSContext *cx, HandleObject proxy, MutableHandleObject objp);
     static bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible);
     static bool preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded);
     static bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop);
@@ -46,18 +44,16 @@ class Proxy
     static bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
                     bool strict, MutableHandleValue vp);
     static bool call(JSContext *cx, HandleObject proxy, const CallArgs &args);
     static bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args);
 
     /* SpiderMonkey extensions. */
     static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                       MutableHandle<JSPropertyDescriptor> desc);
-    static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
-                                      MutableHandleValue vp);
     static bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
     static bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
                                              AutoIdVector &props);
     static bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
     static bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp);
     static bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx);
     static const char *className(JSContext *cx, HandleObject proxy);
     static JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent);
--- a/js/src/proxy/ScriptedDirectProxyHandler.h
+++ b/js/src/proxy/ScriptedDirectProxyHandler.h
@@ -2,17 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef proxy_ScriptedDirectProxyHandler_h
 #define proxy_ScriptedDirectProxyHandler_h
 
-#include "jsproxy.h"
+#include "js/Proxy.h"
 
 namespace js {
 
 /* Derived class for all scripted direct proxy handlers. */
 class ScriptedDirectProxyHandler : public DirectProxyHandler {
   public:
     MOZ_CONSTEXPR ScriptedDirectProxyHandler()
       : DirectProxyHandler(&family)
--- a/js/src/proxy/ScriptedIndirectProxyHandler.h
+++ b/js/src/proxy/ScriptedIndirectProxyHandler.h
@@ -2,17 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef proxy_ScriptedIndirectProxyHandler_h
 #define proxy_ScriptedIndirectProxyHandler_h
 
-#include "jsproxy.h"
+#include "js/Proxy.h"
 
 namespace js {
 
 /* Derived class for all scripted indirect proxy handlers. */
 class ScriptedIndirectProxyHandler : public BaseProxyHandler
 {
   public:
     MOZ_CONSTEXPR ScriptedIndirectProxyHandler()
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -231,21 +231,25 @@ class ObjectGroup : public gc::TenuredCe
         if (addendumKind() == Addendum_UnboxedLayout)
             return reinterpret_cast<UnboxedLayout *>(addendum_);
         return nullptr;
     }
 
     TypeNewScript *anyNewScript();
     void detachNewScript(bool writeBarrier);
 
+    ObjectGroupFlags flagsDontCheckGeneration() {
+        return flags_;
+    }
+
   public:
 
     ObjectGroupFlags flags() {
         maybeSweep(nullptr);
-        return flags_;
+        return flagsDontCheckGeneration();
     }
 
     void addFlags(ObjectGroupFlags flags) {
         maybeSweep(nullptr);
         flags_ |= flags;
     }
 
     void clearFlags(ObjectGroupFlags flags) {
@@ -373,27 +377,39 @@ class ObjectGroup : public gc::TenuredCe
   public:
 
     inline ObjectGroup(const Class *clasp, TaggedProto proto, ObjectGroupFlags initialFlags);
 
     inline bool hasAnyFlags(ObjectGroupFlags flags) {
         MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags);
         return !!(this->flags() & flags);
     }
+
     bool hasAllFlags(ObjectGroupFlags flags) {
         MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags);
         return (this->flags() & flags) == flags;
     }
 
+    bool hasAllFlagsDontCheckGeneration(ObjectGroupFlags flags) {
+        MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags);
+        return (this->flagsDontCheckGeneration() & flags) == flags;
+    }
+
     bool unknownProperties() {
         MOZ_ASSERT_IF(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES,
                       hasAllFlags(OBJECT_FLAG_DYNAMIC_MASK));
         return !!(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES);
     }
 
+    bool unknownPropertiesDontCheckGeneration() {
+        MOZ_ASSERT_IF(flagsDontCheckGeneration() & OBJECT_FLAG_UNKNOWN_PROPERTIES,
+                      hasAllFlagsDontCheckGeneration(OBJECT_FLAG_DYNAMIC_MASK));
+        return !!(flagsDontCheckGeneration() & OBJECT_FLAG_UNKNOWN_PROPERTIES);
+    }
+
     bool shouldPreTenure() {
         return hasAnyFlags(OBJECT_FLAG_PRE_TENURE) && !unknownProperties();
     }
 
     gc::InitialHeap initialHeap(CompilerConstraintList *constraints);
 
     bool canPreTenure() {
         return !unknownProperties();
--- a/js/src/vm/ProxyObject.h
+++ b/js/src/vm/ProxyObject.h
@@ -2,18 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_ProxyObject_h
 #define vm_ProxyObject_h
 
-#include "jsproxy.h"
-
+#include "js/Proxy.h"
 #include "vm/NativeObject.h"
 
 namespace js {
 
 // This is the base class for the various kinds of proxy objects.  It's never
 // instantiated.
 class ProxyObject : public JSObject
 {
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -15,30 +15,30 @@
 #include "mozilla/Scoped.h"
 #include "mozilla/ThreadLocal.h"
 #include "mozilla/UniquePtr.h"
 
 #include <setjmp.h>
 
 #include "jsatom.h"
 #include "jsclist.h"
-#ifdef DEBUG
-# include "jsproxy.h"
-#endif
 #include "jsscript.h"
 
 #ifdef XP_MACOSX
 # include "asmjs/AsmJSSignalHandlers.h"
 #endif
 #include "ds/FixedSizeHash.h"
 #include "frontend/ParseMaps.h"
 #include "gc/GCRuntime.h"
 #include "gc/Tracer.h"
 #include "irregexp/RegExpStack.h"
 #include "js/HashTable.h"
+#ifdef DEBUG
+# include "js/Proxy.h" // For AutoEnterPolicy
+#endif
 #include "js/Vector.h"
 #include "vm/CommonPropertyNames.h"
 #include "vm/DateTime.h"
 #include "vm/MallocProvider.h"
 #include "vm/SPSProfiler.h"
 #include "vm/Stack.h"
 #include "vm/Symbol.h"
 
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -3704,17 +3704,17 @@ ConstraintTypeSet::sweep(Zone *zone, Aut
                     *pentry = key;
                 } else {
                     oom.setOOM();
                     flags |= TYPE_FLAG_ANYOBJECT;
                     clearObjects();
                     objectCount = 0;
                     break;
                 }
-            } else if (key->isGroup() && key->group()->unknownProperties()) {
+            } else if (key->isGroup() && key->group()->unknownPropertiesDontCheckGeneration()) {
                 // Object sets containing objects with unknown properties might
                 // not be complete. Mark the type set as unknown, which it will
                 // be treated as during Ion compilation.
                 flags |= TYPE_FLAG_ANYOBJECT;
                 clearObjects();
                 objectCount = 0;
                 break;
             }
@@ -3722,17 +3722,17 @@ ConstraintTypeSet::sweep(Zone *zone, Aut
         setBaseObjectCount(objectCount);
     } else if (objectCount == 1) {
         ObjectKey *key = (ObjectKey *) objectSet;
         if (!IsAboutToBeFinalized(&key)) {
             objectSet = reinterpret_cast<ObjectKey **>(key);
         } else {
             // As above, mark type sets containing objects with unknown
             // properties as unknown.
-            if (key->isGroup() && key->group()->unknownProperties())
+            if (key->isGroup() && key->group()->unknownPropertiesDontCheckGeneration())
                 flags |= TYPE_FLAG_ANYOBJECT;
             objectSet = nullptr;
             setBaseObjectCount(0);
         }
     }
 
     /*
      * Type constraints only hold weak references. Copy constraints referring
--- a/js/xpconnect/src/ExportHelpers.cpp
+++ b/js/xpconnect/src/ExportHelpers.cpp
@@ -3,19 +3,19 @@
 /* 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 "xpcprivate.h"
 #include "WrapperFactory.h"
 #include "AccessCheck.h"
 #include "jsfriendapi.h"
-#include "jsproxy.h"
 #include "jswrapper.h"
 #include "js/StructuredClone.h"
+#include "js/Proxy.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/File.h"
 #include "nsGlobalWindow.h"
 #include "nsJSUtils.h"
 #include "nsIDOMFileList.h"
 
 using namespace mozilla;
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * The Components.Sandbox object.
  */
 
 #include "AccessCheck.h"
 #include "jsfriendapi.h"
-#include "jsproxy.h"
+#include "js/Proxy.h"
 #include "js/StructuredClone.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIURI.h"
 #include "nsJSUtils.h"
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1531,18 +1531,19 @@ DetachedWrappedNativeProtoShutdownMarker
 void XPCJSRuntime::DestroyJSContextStack()
 {
     delete mJSContextStack;
     mJSContextStack = nullptr;
 }
 
 void XPCJSRuntime::SystemIsBeingShutDown()
 {
-    mDetachedWrappedNativeProtoMap->
-        Enumerate(DetachedWrappedNativeProtoShutdownMarker, nullptr);
+    if (mDetachedWrappedNativeProtoMap)
+        mDetachedWrappedNativeProtoMap->
+            Enumerate(DetachedWrappedNativeProtoShutdownMarker, nullptr);
 }
 
 #define JS_OPTIONS_DOT_STR "javascript.options."
 
 static void
 ReloadPrefsCallback(const char *pref, void *data)
 {
     XPCJSRuntime *runtime = reinterpret_cast<XPCJSRuntime *>(data);
@@ -1614,43 +1615,61 @@ XPCJSRuntime::~XPCJSRuntime()
     if (mCallContext)
         mCallContext->SystemIsBeingShutDown();
 
     auto rtPrivate = static_cast<PerThreadAtomCache*>(JS_GetRuntimePrivate(Runtime()));
     delete rtPrivate;
     JS_SetRuntimePrivate(Runtime(), nullptr);
 
     // clean up and destroy maps...
-    mWrappedJSMap->ShutdownMarker();
-    delete mWrappedJSMap;
-    mWrappedJSMap = nullptr;
-
-    delete mWrappedJSClassMap;
-    mWrappedJSClassMap = nullptr;
-
-    delete mIID2NativeInterfaceMap;
-    mIID2NativeInterfaceMap = nullptr;
-
-    delete mClassInfo2NativeSetMap;
-    mClassInfo2NativeSetMap = nullptr;
-
-    delete mNativeSetMap;
-    mNativeSetMap = nullptr;
-
-    delete mThisTranslatorMap;
-    mThisTranslatorMap = nullptr;
-
-    delete mNativeScriptableSharedMap;
-    mNativeScriptableSharedMap = nullptr;
-
-    delete mDyingWrappedNativeProtoMap;
-    mDyingWrappedNativeProtoMap = nullptr;
-
-    delete mDetachedWrappedNativeProtoMap;
-    mDetachedWrappedNativeProtoMap = nullptr;
+    if (mWrappedJSMap) {
+        mWrappedJSMap->ShutdownMarker();
+        delete mWrappedJSMap;
+        mWrappedJSMap = nullptr;
+    }
+
+    if (mWrappedJSClassMap) {
+        delete mWrappedJSClassMap;
+        mWrappedJSClassMap = nullptr;
+    }
+
+    if (mIID2NativeInterfaceMap) {
+        delete mIID2NativeInterfaceMap;
+        mIID2NativeInterfaceMap = nullptr;
+    }
+
+    if (mClassInfo2NativeSetMap) {
+        delete mClassInfo2NativeSetMap;
+        mClassInfo2NativeSetMap = nullptr;
+    }
+
+    if (mNativeSetMap) {
+        delete mNativeSetMap;
+        mNativeSetMap = nullptr;
+    }
+
+    if (mThisTranslatorMap) {
+        delete mThisTranslatorMap;
+        mThisTranslatorMap = nullptr;
+    }
+
+    if (mNativeScriptableSharedMap) {
+        delete mNativeScriptableSharedMap;
+        mNativeScriptableSharedMap = nullptr;
+    }
+
+    if (mDyingWrappedNativeProtoMap) {
+        delete mDyingWrappedNativeProtoMap;
+        mDyingWrappedNativeProtoMap = nullptr;
+    }
+
+    if (mDetachedWrappedNativeProtoMap) {
+        delete mDetachedWrappedNativeProtoMap;
+        mDetachedWrappedNativeProtoMap = nullptr;
+    }
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
     // Tell the profiler that the runtime is gone
     if (PseudoStack *stack = mozilla_get_pseudo_stack())
         stack->sampleRuntime(nullptr);
 #endif
 
     Preferences::UnregisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this);
@@ -3490,48 +3509,52 @@ XPCJSRuntime::DebugDump(int16_t depth)
         while (JS_ContextIterator(Runtime(), &iter)) {
             XPCContext *xpc = XPCContext::GetXPCContext(iter);
             XPC_LOG_INDENT();
             xpc->DebugDump(depth);
             XPC_LOG_OUTDENT();
         }
 
         XPC_LOG_ALWAYS(("mWrappedJSClassMap @ %x with %d wrapperclasses(s)",  \
-                        mWrappedJSClassMap, mWrappedJSClassMap->Count()));
+                        mWrappedJSClassMap, mWrappedJSClassMap ?              \
+                        mWrappedJSClassMap->Count() : 0));
         // iterate wrappersclasses...
-        if (depth && mWrappedJSClassMap->Count()) {
+        if (depth && mWrappedJSClassMap && mWrappedJSClassMap->Count()) {
             XPC_LOG_INDENT();
             mWrappedJSClassMap->Enumerate(WrappedJSClassMapDumpEnumerator, &depth);
             XPC_LOG_OUTDENT();
         }
         XPC_LOG_ALWAYS(("mWrappedJSMap @ %x with %d wrappers(s)",             \
-                        mWrappedJSMap, mWrappedJSMap->Count()));
+                        mWrappedJSMap, mWrappedJSMap ?                        \
+                        mWrappedJSMap->Count() : 0));
         // iterate wrappers...
-        if (depth && mWrappedJSMap->Count()) {
+        if (depth && mWrappedJSMap && mWrappedJSMap->Count()) {
             XPC_LOG_INDENT();
             mWrappedJSMap->Dump(depth);
             XPC_LOG_OUTDENT();
         }
 
         XPC_LOG_ALWAYS(("mIID2NativeInterfaceMap @ %x with %d interface(s)",  \
-                        mIID2NativeInterfaceMap,
-                        mIID2NativeInterfaceMap->Count()));
+                        mIID2NativeInterfaceMap, mIID2NativeInterfaceMap ?    \
+                        mIID2NativeInterfaceMap->Count() : 0));
 
         XPC_LOG_ALWAYS(("mClassInfo2NativeSetMap @ %x with %d sets(s)",       \
-                        mClassInfo2NativeSetMap,                              \
-                        mClassInfo2NativeSetMap->Count()));
+                        mClassInfo2NativeSetMap, mClassInfo2NativeSetMap ?    \
+                        mClassInfo2NativeSetMap->Count() : 0));
 
         XPC_LOG_ALWAYS(("mThisTranslatorMap @ %x with %d translator(s)",      \
-                        mThisTranslatorMap, mThisTranslatorMap->Count()));
+                        mThisTranslatorMap, mThisTranslatorMap ?              \
+                        mThisTranslatorMap->Count() : 0));
 
         XPC_LOG_ALWAYS(("mNativeSetMap @ %x with %d sets(s)",                 \
-                        mNativeSetMap, mNativeSetMap->Count()));
+                        mNativeSetMap, mNativeSetMap ?                        \
+                        mNativeSetMap->Count() : 0));
 
         // iterate sets...
-        if (depth && mNativeSetMap->Count()) {
+        if (depth && mNativeSetMap && mNativeSetMap->Count()) {
             XPC_LOG_INDENT();
             mNativeSetMap->Enumerate(NativeSetDumpEnumerator, &depth);
             XPC_LOG_OUTDENT();
         }
 
         XPC_LOG_OUTDENT();
 #endif
 }
--- a/js/xpconnect/src/XPCMaps.cpp
+++ b/js/xpconnect/src/XPCMaps.cpp
@@ -164,37 +164,44 @@ JSObject2WrappedJSMap::SizeOfWrappedJS(m
 
 /***************************************************************************/
 // implement Native2WrappedNativeMap...
 
 // static
 Native2WrappedNativeMap*
 Native2WrappedNativeMap::newMap(int length)
 {
-    return new Native2WrappedNativeMap(length);
+    Native2WrappedNativeMap* map = new Native2WrappedNativeMap(length);
+    if (map && map->mTable)
+        return map;
+    // Allocation of the map or the creation of its hash table has
+    // failed. This will cause a nullptr deref later when we attempt
+    // to use the map, so we abort immediately to provide a more
+    // useful crash stack.
+    NS_RUNTIMEABORT("Ran out of memory.");
+    return nullptr;
 }
 
 Native2WrappedNativeMap::Native2WrappedNativeMap(int length)
 {
-    mTable = new PLDHashTable();
-    PL_DHashTableInit(mTable, PL_DHashGetStubOps(), sizeof(Entry), length);
+    mTable = PL_NewDHashTable(PL_DHashGetStubOps(), sizeof(Entry), length);
 }
 
 Native2WrappedNativeMap::~Native2WrappedNativeMap()
 {
-    PL_DHashTableFinish(mTable);
-    delete mTable;
+    if (mTable)
+        PL_DHashTableDestroy(mTable);
 }
 
 size_t
 Native2WrappedNativeMap::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
 {
     size_t n = 0;
     n += mallocSizeOf(this);
-    n += PL_DHashTableSizeOfIncludingThis(mTable, SizeOfEntryExcludingThis, mallocSizeOf);
+    n += mTable ? PL_DHashTableSizeOfIncludingThis(mTable, SizeOfEntryExcludingThis, mallocSizeOf) : 0;
     return n;
 }
 
 /* static */ size_t
 Native2WrappedNativeMap::SizeOfEntryExcludingThis(PLDHashEntryHdr *hdr,
                                                   mozilla::MallocSizeOf mallocSizeOf, void *)
 {
     return mallocSizeOf(((Native2WrappedNativeMap::Entry*)hdr)->value);
@@ -210,29 +217,32 @@ const struct PLDHashTableOps IID2Wrapped
     PL_DHashMoveEntryStub,
     PL_DHashClearEntryStub
 };
 
 // static
 IID2WrappedJSClassMap*
 IID2WrappedJSClassMap::newMap(int length)
 {
-    return new IID2WrappedJSClassMap(length);
+    IID2WrappedJSClassMap* map = new IID2WrappedJSClassMap(length);
+    if (map && map->mTable)
+        return map;
+    delete map;
+    return nullptr;
 }
 
 IID2WrappedJSClassMap::IID2WrappedJSClassMap(int length)
 {
-    mTable = new PLDHashTable();
-    PL_DHashTableInit(mTable, &Entry::sOps, sizeof(Entry), length);
+    mTable = PL_NewDHashTable(&Entry::sOps, sizeof(Entry), length);
 }
 
 IID2WrappedJSClassMap::~IID2WrappedJSClassMap()
 {
-    PL_DHashTableFinish(mTable);
-    delete mTable;
+    if (mTable)
+        PL_DHashTableDestroy(mTable);
 }
 
 
 /***************************************************************************/
 // implement IID2NativeInterfaceMap...
 
 const struct PLDHashTableOps IID2NativeInterfaceMap::Entry::sOps =
 {
@@ -241,37 +251,40 @@ const struct PLDHashTableOps IID2NativeI
     PL_DHashMoveEntryStub,
     PL_DHashClearEntryStub
 };
 
 // static
 IID2NativeInterfaceMap*
 IID2NativeInterfaceMap::newMap(int length)
 {
-    return new IID2NativeInterfaceMap(length);
+    IID2NativeInterfaceMap* map = new IID2NativeInterfaceMap(length);
+    if (map && map->mTable)
+        return map;
+    delete map;
+    return nullptr;
 }
 
 IID2NativeInterfaceMap::IID2NativeInterfaceMap(int length)
 {
-    mTable = new PLDHashTable();
-    PL_DHashTableInit(mTable, &Entry::sOps, sizeof(Entry), length);
+    mTable = PL_NewDHashTable(&Entry::sOps, sizeof(Entry), length);
 }
 
 IID2NativeInterfaceMap::~IID2NativeInterfaceMap()
 {
-    PL_DHashTableFinish(mTable);
-    delete mTable;
+    if (mTable)
+        PL_DHashTableDestroy(mTable);
 }
 
 size_t
 IID2NativeInterfaceMap::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
 {
     size_t n = 0;
     n += mallocSizeOf(this);
-    n += PL_DHashTableSizeOfIncludingThis(mTable, SizeOfEntryExcludingThis, mallocSizeOf);
+    n += mTable ? PL_DHashTableSizeOfIncludingThis(mTable, SizeOfEntryExcludingThis, mallocSizeOf) : 0;
     return n;
 }
 
 /* static */ size_t
 IID2NativeInterfaceMap::SizeOfEntryExcludingThis(PLDHashEntryHdr *hdr,
                                                  mozilla::MallocSizeOf mallocSizeOf, void *)
 {
     XPCNativeInterface *iface = ((IID2NativeInterfaceMap::Entry*)hdr)->value;
@@ -280,69 +293,79 @@ IID2NativeInterfaceMap::SizeOfEntryExclu
 
 /***************************************************************************/
 // implement ClassInfo2NativeSetMap...
 
 // static
 ClassInfo2NativeSetMap*
 ClassInfo2NativeSetMap::newMap(int length)
 {
-    return new ClassInfo2NativeSetMap(length);
+    ClassInfo2NativeSetMap* map = new ClassInfo2NativeSetMap(length);
+    if (map && map->mTable)
+        return map;
+    delete map;
+    return nullptr;
 }
 
 ClassInfo2NativeSetMap::ClassInfo2NativeSetMap(int length)
 {
-    mTable = new PLDHashTable();
-    PL_DHashTableInit(mTable, PL_DHashGetStubOps(), sizeof(Entry), length);
+    mTable = PL_NewDHashTable(PL_DHashGetStubOps(), sizeof(Entry), length);
 }
 
 ClassInfo2NativeSetMap::~ClassInfo2NativeSetMap()
 {
-    PL_DHashTableFinish(mTable);
-    delete mTable;
+    if (mTable)
+        PL_DHashTableDestroy(mTable);
 }
 
 size_t
 ClassInfo2NativeSetMap::ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
 {
     size_t n = 0;
     n += mallocSizeOf(this);
     // The second arg is nullptr because this is a "shallow" measurement of the map.
-    n += PL_DHashTableSizeOfIncludingThis(mTable, nullptr, mallocSizeOf);
+    n += mTable ? PL_DHashTableSizeOfIncludingThis(mTable, nullptr, mallocSizeOf) : 0;
     return n;
 }
 
 /***************************************************************************/
 // implement ClassInfo2WrappedNativeProtoMap...
 
 // static
 ClassInfo2WrappedNativeProtoMap*
 ClassInfo2WrappedNativeProtoMap::newMap(int length)
 {
-    return new ClassInfo2WrappedNativeProtoMap(length);
+    ClassInfo2WrappedNativeProtoMap* map = new ClassInfo2WrappedNativeProtoMap(length);
+    if (map && map->mTable)
+        return map;
+    // Allocation of the map or the creation of its hash table has
+    // failed. This will cause a nullptr deref later when we attempt
+    // to use the map, so we abort immediately to provide a more
+    // useful crash stack.
+    NS_RUNTIMEABORT("Ran out of memory.");
+    return nullptr;
 }
 
 ClassInfo2WrappedNativeProtoMap::ClassInfo2WrappedNativeProtoMap(int length)
 {
-    mTable = new PLDHashTable();
-    PL_DHashTableInit(mTable, PL_DHashGetStubOps(), sizeof(Entry), length);
+    mTable = PL_NewDHashTable(PL_DHashGetStubOps(), sizeof(Entry), length);
 }
 
 ClassInfo2WrappedNativeProtoMap::~ClassInfo2WrappedNativeProtoMap()
 {
-    PL_DHashTableFinish(mTable);
-    delete mTable;
+    if (mTable)
+        PL_DHashTableDestroy(mTable);
 }
 
 size_t
 ClassInfo2WrappedNativeProtoMap::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
 {
     size_t n = 0;
     n += mallocSizeOf(this);
-    n += PL_DHashTableSizeOfIncludingThis(mTable, SizeOfEntryExcludingThis, mallocSizeOf);
+    n += mTable ? PL_DHashTableSizeOfIncludingThis(mTable, SizeOfEntryExcludingThis, mallocSizeOf) : 0;
     return n;
 }
 
 /* static */ size_t
 ClassInfo2WrappedNativeProtoMap::SizeOfEntryExcludingThis(PLDHashEntryHdr *hdr,
                                                           mozilla::MallocSizeOf mallocSizeOf, void *)
 {
     return mallocSizeOf(((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value);
@@ -430,37 +453,40 @@ const struct PLDHashTableOps NativeSetMa
     PL_DHashMoveEntryStub,
     PL_DHashClearEntryStub
 };
 
 // static
 NativeSetMap*
 NativeSetMap::newMap(int length)
 {
-    return new NativeSetMap(length);
+    NativeSetMap* map = new NativeSetMap(length);
+    if (map && map->mTable)
+        return map;
+    delete map;
+    return nullptr;
 }
 
 NativeSetMap::NativeSetMap(int length)
 {
-    mTable = new PLDHashTable();
-    PL_DHashTableInit(mTable, &Entry::sOps, sizeof(Entry), length);
+    mTable = PL_NewDHashTable(&Entry::sOps, sizeof(Entry), length);
 }
 
 NativeSetMap::~NativeSetMap()
 {
-    PL_DHashTableFinish(mTable);
-    delete mTable;
+    if (mTable)
+        PL_DHashTableDestroy(mTable);
 }
 
 size_t
 NativeSetMap::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
 {
     size_t n = 0;
     n += mallocSizeOf(this);
-    n += PL_DHashTableSizeOfIncludingThis(mTable, SizeOfEntryExcludingThis, mallocSizeOf);
+    n += mTable ? PL_DHashTableSizeOfIncludingThis(mTable, SizeOfEntryExcludingThis, mallocSizeOf) : 0;
     return n;
 }
 
 /* static */ size_t
 NativeSetMap::SizeOfEntryExcludingThis(PLDHashEntryHdr *hdr, mozilla::MallocSizeOf mallocSizeOf, void *)
 {
     XPCNativeSet *set = ((NativeSetMap::Entry*)hdr)->key_value;
     return set->SizeOfIncludingThis(mallocSizeOf);
@@ -491,29 +517,32 @@ const struct PLDHashTableOps IID2ThisTra
     PL_DHashMoveEntryStub,
     Clear
 };
 
 // static
 IID2ThisTranslatorMap*
 IID2ThisTranslatorMap::newMap(int length)
 {
-    return new IID2ThisTranslatorMap(length);
+    IID2ThisTranslatorMap* map = new IID2ThisTranslatorMap(length);
+    if (map && map->mTable)
+        return map;
+    delete map;
+    return nullptr;
 }
 
 IID2ThisTranslatorMap::IID2ThisTranslatorMap(int length)
 {
-    mTable = new PLDHashTable();
-    PL_DHashTableInit(mTable, &Entry::sOps, sizeof(Entry), length);
+    mTable = PL_NewDHashTable(&Entry::sOps, sizeof(Entry), length);
 }
 
 IID2ThisTranslatorMap::~IID2ThisTranslatorMap()
 {
-    PL_DHashTableFinish(mTable);
-    delete mTable;
+    if (mTable)
+        PL_DHashTableDestroy(mTable);
 }
 
 /***************************************************************************/
 
 PLDHashNumber
 XPCNativeScriptableSharedMap::Entry::Hash(PLDHashTable *table, const void *key)
 {
     PLDHashNumber h;
@@ -564,42 +593,45 @@ const struct PLDHashTableOps XPCNativeSc
     PL_DHashMoveEntryStub,
     PL_DHashClearEntryStub
 };
 
 // static
 XPCNativeScriptableSharedMap*
 XPCNativeScriptableSharedMap::newMap(int length)
 {
-    return new XPCNativeScriptableSharedMap(length);
+    XPCNativeScriptableSharedMap* map =
+        new XPCNativeScriptableSharedMap(length);
+    if (map && map->mTable)
+        return map;
+    delete map;
+    return nullptr;
 }
 
 XPCNativeScriptableSharedMap::XPCNativeScriptableSharedMap(int length)
 {
-    mTable = new PLDHashTable();
-    PL_DHashTableInit(mTable, &Entry::sOps, sizeof(Entry), length);
+    mTable = PL_NewDHashTable(&Entry::sOps, sizeof(Entry), length);
 }
 
 XPCNativeScriptableSharedMap::~XPCNativeScriptableSharedMap()
 {
-    PL_DHashTableFinish(mTable);
-    delete mTable;
+    if (mTable)
+        PL_DHashTableDestroy(mTable);
 }
 
 bool
 XPCNativeScriptableSharedMap::GetNewOrUsed(uint32_t flags,
                                            char* name,
                                            XPCNativeScriptableInfo* si)
 {
     NS_PRECONDITION(name,"bad param");
     NS_PRECONDITION(si,"bad param");
 
     XPCNativeScriptableShared key(flags, name);
-    Entry* entry = static_cast<Entry*>
-        (PL_DHashTableAdd(mTable, &key, fallible));
+    Entry* entry = (Entry*) PL_DHashTableAdd(mTable, &key);
     if (!entry)
         return false;
 
     XPCNativeScriptableShared* shared = entry->key;
 
     if (!shared) {
         entry->key = shared =
             new XPCNativeScriptableShared(flags, key.TransferNameOwnership());
@@ -613,25 +645,28 @@ XPCNativeScriptableSharedMap::GetNewOrUs
 
 /***************************************************************************/
 // implement XPCWrappedNativeProtoMap...
 
 // static
 XPCWrappedNativeProtoMap*
 XPCWrappedNativeProtoMap::newMap(int length)
 {
-    return new XPCWrappedNativeProtoMap(length);
+    XPCWrappedNativeProtoMap* map = new XPCWrappedNativeProtoMap(length);
+    if (map && map->mTable)
+        return map;
+    delete map;
+    return nullptr;
 }
 
 XPCWrappedNativeProtoMap::XPCWrappedNativeProtoMap(int length)
 {
-    mTable = new PLDHashTable();
-    PL_DHashTableInit(mTable, PL_DHashGetStubOps(), sizeof(PLDHashEntryStub),
-                      length);
+    mTable = PL_NewDHashTable(PL_DHashGetStubOps(),
+                              sizeof(PLDHashEntryStub), length);
 }
 
 XPCWrappedNativeProtoMap::~XPCWrappedNativeProtoMap()
 {
-    PL_DHashTableFinish(mTable);
-    delete mTable;
+    if (mTable)
+        PL_DHashTableDestroy(mTable);
 }
 
 /***************************************************************************/
--- a/js/xpconnect/src/XPCMaps.h
+++ b/js/xpconnect/src/XPCMaps.h
@@ -27,17 +27,17 @@
 class JSObject2WrappedJSMap
 {
     typedef js::HashMap<JSObject*, nsXPCWrappedJS*, js::PointerHasher<JSObject*, 3>,
                         js::SystemAllocPolicy> Map;
 
 public:
     static JSObject2WrappedJSMap* newMap(int length) {
         JSObject2WrappedJSMap* map = new JSObject2WrappedJSMap();
-        if (map->mTable.init(length))
+        if (map && map->mTable.init(length))
             return map;
         delete map;
         return nullptr;
     }
 
     inline nsXPCWrappedJS* Find(JSObject* Obj) {
         NS_PRECONDITION(Obj,"bad param");
         Map::Ptr p = mTable.lookup(Obj);
@@ -115,18 +115,17 @@ public:
         return entry ? entry->value : nullptr;
     }
 
     inline XPCWrappedNative* Add(XPCWrappedNative* wrapper)
     {
         NS_PRECONDITION(wrapper,"bad param");
         nsISupports* obj = wrapper->GetIdentityObject();
         MOZ_ASSERT(!Find(obj), "wrapper already in new scope!");
-        Entry* entry = static_cast<Entry*>
-            (PL_DHashTableAdd(mTable, obj, mozilla::fallible));
+        Entry* entry = (Entry*) PL_DHashTableAdd(mTable, obj);
         if (!entry)
             return nullptr;
         if (entry->key)
             return entry->value;
         entry->key = obj;
         entry->value = wrapper;
         return wrapper;
     }
@@ -181,18 +180,17 @@ public:
         Entry* entry = (Entry*) PL_DHashTableSearch(mTable, &iid);
         return entry ? entry->value : nullptr;
     }
 
     inline nsXPCWrappedJSClass* Add(nsXPCWrappedJSClass* clazz)
     {
         NS_PRECONDITION(clazz,"bad param");
         const nsIID* iid = &clazz->GetIID();
-        Entry* entry = static_cast<Entry*>
-            (PL_DHashTableAdd(mTable, iid, mozilla::fallible));
+        Entry* entry = (Entry*) PL_DHashTableAdd(mTable, iid);
         if (!entry)
             return nullptr;
         if (entry->key)
             return entry->value;
         entry->key = iid;
         entry->value = clazz;
         return clazz;
     }
@@ -235,18 +233,17 @@ public:
         Entry* entry = (Entry*) PL_DHashTableSearch(mTable, &iid);
         return entry ? entry->value : nullptr;
     }
 
     inline XPCNativeInterface* Add(XPCNativeInterface* iface)
     {
         NS_PRECONDITION(iface,"bad param");
         const nsIID* iid = iface->GetIID();
-        Entry* entry = static_cast<Entry*>
-            (PL_DHashTableAdd(mTable, iid, mozilla::fallible));
+        Entry* entry = (Entry*) PL_DHashTableAdd(mTable, iid);
         if (!entry)
             return nullptr;
         if (entry->key)
             return entry->value;
         entry->key = iid;
         entry->value = iface;
         return iface;
     }
@@ -291,18 +288,17 @@ public:
     {
         Entry* entry = (Entry*) PL_DHashTableSearch(mTable, info);
         return entry ? entry->value : nullptr;
     }
 
     inline XPCNativeSet* Add(nsIClassInfo* info, XPCNativeSet* set)
     {
         NS_PRECONDITION(info,"bad param");
-        Entry* entry = static_cast<Entry*>
-            (PL_DHashTableAdd(mTable, info, mozilla::fallible));
+        Entry* entry = (Entry*) PL_DHashTableAdd(mTable, info);
         if (!entry)
             return nullptr;
         if (entry->key)
             return entry->value;
         entry->key = info;
         entry->value = set;
         return set;
     }
@@ -348,18 +344,17 @@ public:
     {
         Entry* entry = (Entry*) PL_DHashTableSearch(mTable, info);
         return entry ? entry->value : nullptr;
     }
 
     inline XPCWrappedNativeProto* Add(nsIClassInfo* info, XPCWrappedNativeProto* proto)
     {
         NS_PRECONDITION(info,"bad param");
-        Entry* entry = static_cast<Entry*>
-            (PL_DHashTableAdd(mTable, info, mozilla::fallible));
+        Entry* entry = (Entry*) PL_DHashTableAdd(mTable, info);
         if (!entry)
             return nullptr;
         if (entry->key)
             return entry->value;
         entry->key = info;
         entry->value = proto;
         return proto;
     }
@@ -411,18 +406,17 @@ public:
         Entry* entry = (Entry*) PL_DHashTableSearch(mTable, key);
         return entry ? entry->key_value : nullptr;
     }
 
     inline XPCNativeSet* Add(const XPCNativeSetKey* key, XPCNativeSet* set)
     {
         NS_PRECONDITION(key,"bad param");
         NS_PRECONDITION(set,"bad param");
-        Entry* entry = static_cast<Entry*>
-            (PL_DHashTableAdd(mTable, key, mozilla::fallible));
+        Entry* entry = (Entry*) PL_DHashTableAdd(mTable, key);
         if (!entry)
             return nullptr;
         if (entry->key_value)
             return entry->key_value;
         entry->key_value = set;
         return set;
     }
 
@@ -484,18 +478,18 @@ public:
     {
         Entry* entry = (Entry*) PL_DHashTableSearch(mTable, &iid);
         return entry ? entry->value : nullptr;
     }
 
     inline nsIXPCFunctionThisTranslator* Add(REFNSIID iid,
                                              nsIXPCFunctionThisTranslator* obj)
     {
-        Entry* entry = static_cast<Entry*>
-            (PL_DHashTableAdd(mTable, &iid, mozilla::fallible));
+
+        Entry* entry = (Entry*) PL_DHashTableAdd(mTable, &iid);
         if (!entry)
             return nullptr;
         entry->value = obj;
         entry->key = iid;
         return obj;
     }
 
     inline void Remove(REFNSIID iid)
@@ -556,18 +550,18 @@ private:
 class XPCWrappedNativeProtoMap
 {
 public:
     static XPCWrappedNativeProtoMap* newMap(int length);
 
     inline XPCWrappedNativeProto* Add(XPCWrappedNativeProto* proto)
     {
         NS_PRECONDITION(proto,"bad param");
-        PLDHashEntryStub* entry = static_cast<PLDHashEntryStub*>
-            (PL_DHashTableAdd(mTable, proto, mozilla::fallible));
+        PLDHashEntryStub* entry = (PLDHashEntryStub*)
+            PL_DHashTableAdd(mTable, proto);
         if (!entry)
             return nullptr;
         if (entry->key)
             return (XPCWrappedNativeProto*) entry->key;
         entry->key = proto;
         return proto;
     }
 
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -3,19 +3,19 @@
 /* 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 xpcpublic_h
 #define xpcpublic_h
 
 #include "jsapi.h"
-#include "jsproxy.h"
 #include "js/HeapAPI.h"
 #include "js/GCAPI.h"
+#include "js/Proxy.h"
 
 #include "nsISupports.h"
 #include "nsIURI.h"
 #include "nsIPrincipal.h"
 #include "nsIGlobalObject.h"
 #include "nsPIDOMWindow.h"
 #include "nsWrapperCache.h"
 #include "nsStringGlue.h"
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -4110,17 +4110,20 @@ FrameLayerBuilder::BuildContainerLayerFo
 
   LayerManagerData* data = static_cast<LayerManagerData*>
     (aManager->GetUserData(&gLayerManagerUserData));
 
   nsIntRect pixBounds;
   nscoord appUnitsPerDevPixel;
   bool flattenToSingleLayer = false;
   if ((aContainerFrame->GetStateBits() & NS_FRAME_NO_COMPONENT_ALPHA) &&
-      mRetainingManager && mRetainingManager->ShouldAvoidComponentAlphaLayers()) {
+      mRetainingManager &&
+      mRetainingManager->ShouldAvoidComponentAlphaLayers() &&
+      !gfxPrefs::AsyncPanZoomEnabled())
+  {
     flattenToSingleLayer = true;
   }
   uint32_t flags;
   while (true) {
     ContainerState state(aBuilder, aManager, aManager->GetLayerBuilder(),
                          aContainerFrame, aContainerItem, bounds,
                          containerLayer, scaleParameters, flattenToSingleLayer);
 
@@ -4133,17 +4136,19 @@ FrameLayerBuilder::BuildContainerLayerFo
     pixBounds = state.ScaleToOutsidePixels(bounds, false);
     appUnitsPerDevPixel = state.GetAppUnitsPerDevPixel();
     state.Finish(&flags, data, pixBounds, aChildren, hasComponentAlphaChildren);
 
     if (hasComponentAlphaChildren &&
         mRetainingManager &&
         mRetainingManager->ShouldAvoidComponentAlphaLayers() &&
         containerLayer->HasMultipleChildren() &&
-        !flattenToSingleLayer) {
+        !flattenToSingleLayer &&
+        !gfxPrefs::AsyncPanZoomEnabled())
+    {
       // Since we don't want any component alpha layers on BasicLayers, we repeat
       // the layer building process with this explicitely forced off.
       // We restore the previous FrameLayerBuilder state since the first set
       // of layer building will have changed it.
       flattenToSingleLayer = true;
       data->mDisplayItems.EnumerateEntries(RestoreDisplayItemData,
                                            &mContainerLayerGeneration);
       mPaintedLayerItems.EnumerateEntries(RestorePaintedLayerItemEntries,
--- a/layout/base/SelectionCarets.cpp
+++ b/layout/base/SelectionCarets.cpp
@@ -1121,16 +1121,18 @@ SelectionCarets::DispatchSelectionStateC
 void
 SelectionCarets::NotifyBlur(bool aIsLeavingDocument)
 {
   SELECTIONCARETS_LOG("Send out the blur event");
   SetVisibility(false);
   if (aIsLeavingDocument) {
     CancelLongTapDetector();
   }
+  CancelScrollEndDetector();
+  mInAsyncPanZoomGesture = false;
   DispatchSelectionStateChangedEvent(nullptr, SelectionState::Blur);
 }
 
 nsresult
 SelectionCarets::NotifySelectionChanged(nsIDOMDocument* aDoc,
                                         nsISelection* aSel,
                                         int16_t aReason)
 {
@@ -1173,39 +1175,49 @@ DispatchScrollViewChangeEvent(nsIPresShe
     event->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
     doc->DispatchEvent(event, &ret);
   }
 }
 
 void
 SelectionCarets::AsyncPanZoomStarted(const mozilla::CSSIntPoint aScrollPos)
 {
-  mInAsyncPanZoomGesture = true;
-  SetVisibility(false);
+  if (mVisible) {
+    mInAsyncPanZoomGesture = true;
+    SetVisibility(false);
 
-  SELECTIONCARETS_LOG("Dispatch scroll started with position x=%d, y=%d",
-                      aScrollPos.x, aScrollPos.y);
-  DispatchScrollViewChangeEvent(mPresShell, dom::ScrollState::Started, aScrollPos);
+    SELECTIONCARETS_LOG("Dispatch scroll started with position x=%d, y=%d",
+                        aScrollPos.x, aScrollPos.y);
+    DispatchScrollViewChangeEvent(mPresShell, dom::ScrollState::Started, aScrollPos);
+  } else {
+    nsRefPtr<dom::Selection> selection = GetSelection();
+    if (selection && selection->RangeCount() && selection->IsCollapsed()) {
+      mInAsyncPanZoomGesture = true;
+      DispatchScrollViewChangeEvent(mPresShell, dom::ScrollState::Started, aScrollPos);
+    }
+  }
 }
 
 void
 SelectionCarets::AsyncPanZoomStopped(const mozilla::CSSIntPoint aScrollPos)
 {
-  mInAsyncPanZoomGesture = false;
-  SELECTIONCARETS_LOG("Update selection carets after APZ is stopped!");
-  UpdateSelectionCarets();
+  if (mInAsyncPanZoomGesture) {
+    mInAsyncPanZoomGesture = false;
+    SELECTIONCARETS_LOG("Update selection carets after APZ is stopped!");
+    UpdateSelectionCarets();
 
-  // SelectionStateChangedEvent should be dispatched before ScrollViewChangeEvent.
-  DispatchSelectionStateChangedEvent(GetSelection(),
-                                     SelectionState::Updateposition);
+    // SelectionStateChangedEvent should be dispatched before ScrollViewChangeEvent.
+    DispatchSelectionStateChangedEvent(GetSelection(),
+                                       SelectionState::Updateposition);
 
-  SELECTIONCARETS_LOG("Dispatch scroll stopped with position x=%d, y=%d",
-                      aScrollPos.x, aScrollPos.y);
+    SELECTIONCARETS_LOG("Dispatch scroll stopped with position x=%d, y=%d",
+                        aScrollPos.x, aScrollPos.y);
 
-  DispatchScrollViewChangeEvent(mPresShell, dom::ScrollState::Stopped, aScrollPos);
+    DispatchScrollViewChangeEvent(mPresShell, dom::ScrollState::Stopped, aScrollPos);
+  }
 }
 
 void
 SelectionCarets::ScrollPositionChanged()
 {
   if (mVisible) {
     if (!mAsyncPanZoomEnabled) {
       SetVisibility(false);
@@ -1292,16 +1304,28 @@ SelectionCarets::LaunchScrollEndDetector
 
   SELECTIONCARETS_LOG("Will fire scroll end after %d ms", kScrollEndTimerDelay);
   mScrollEndDetectorTimer->InitWithFuncCallback(FireScrollEnd,
                                                 this,
                                                 kScrollEndTimerDelay,
                                                 nsITimer::TYPE_ONE_SHOT);
 }
 
+void
+SelectionCarets::CancelScrollEndDetector()
+{
+  if (!mScrollEndDetectorTimer) {
+    return;
+  }
+
+  SELECTIONCARETS_LOG("Cancel scroll end detector!");
+  mScrollEndDetectorTimer->Cancel();
+}
+
+
 /* static */void
 SelectionCarets::FireScrollEnd(nsITimer* aTimer, void* aSelectionCarets)
 {
   nsRefPtr<SelectionCarets> self = static_cast<SelectionCarets*>(aSelectionCarets);
   NS_PRECONDITION(aTimer == self->mScrollEndDetectorTimer,
                   "Unexpected timer");
 
   SELECTIONCARETS_LOG_STATIC("Update selection carets!");
--- a/layout/base/SelectionCarets.h
+++ b/layout/base/SelectionCarets.h
@@ -213,16 +213,17 @@ private:
   /**
    * Detecting long tap using timer
    */
   void LaunchLongTapDetector();
   void CancelLongTapDetector();
   static void FireLongTap(nsITimer* aTimer, void* aSelectionCarets);
 
   void LaunchScrollEndDetector();
+  void CancelScrollEndDetector();
   static void FireScrollEnd(nsITimer* aTimer, void* aSelectionCarets);
 
   nsIPresShell* mPresShell;
   WeakPtr<nsDocShell> mDocShell;
 
   // This timer is used for detecting long tap fire. If content process
   // has APZC, we'll use APZC for long tap detecting. Otherwise, we use this
   // timer to detect long tap.
--- a/layout/base/nsFrameManager.cpp
+++ b/layout/base/nsFrameManager.cpp
@@ -170,19 +170,18 @@ nsFrameManager::RegisterPlaceholderFrame
 {
   NS_PRECONDITION(aPlaceholderFrame, "null param unexpected");
   NS_PRECONDITION(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(),
                   "unexpected frame type");
   if (!mPlaceholderMap.IsInitialized()) {
     PL_DHashTableInit(&mPlaceholderMap, &PlaceholderMapOps,
                       sizeof(PlaceholderMapEntry));
   }
-  PlaceholderMapEntry *entry = static_cast<PlaceholderMapEntry*>
-    (PL_DHashTableAdd(&mPlaceholderMap,
-                      aPlaceholderFrame->GetOutOfFlowFrame(), fallible));
+  PlaceholderMapEntry *entry = static_cast<PlaceholderMapEntry*>(PL_DHashTableAdd(&mPlaceholderMap,
+                              aPlaceholderFrame->GetOutOfFlowFrame()));
   if (!entry)
     return NS_ERROR_OUT_OF_MEMORY;
 
   NS_ASSERTION(!entry->placeholderFrame, "Registering a placeholder for a frame that already has a placeholder!");
   entry->placeholderFrame = aPlaceholderFrame;
 
   return NS_OK;
 }
--- a/layout/generic/nsLineLayout.h
+++ b/layout/generic/nsLineLayout.h
@@ -302,16 +302,21 @@ public:
   // Retrieve last set optional break position. When this returns null, no
   // optional break has been recorded (which means that the line can't break yet).
   nsIFrame* GetLastOptionalBreakPosition(int32_t* aOffset,
                                          gfxBreakPriority* aPriority) {
     *aOffset = mLastOptionalBreakFrameOffset;
     *aPriority = mLastOptionalBreakPriority;
     return mLastOptionalBreakFrame;
   }
+  // Whether any optional break position has been recorded.
+  bool HasOptionalBreakPosition() const
+  {
+    return mLastOptionalBreakFrame != nullptr;
+  }
   
   /**
    * Check whether frames overflowed the available width and CanPlaceFrame
    * requested backing up to a saved break position.
    */  
   bool NeedsBackup() { return mNeedBackup; }
   
   // Line layout may place too much content on a line, overflowing its available
--- a/layout/generic/nsRubyBaseContainerFrame.cpp
+++ b/layout/generic/nsRubyBaseContainerFrame.cpp
@@ -9,16 +9,17 @@
 #include "nsRubyBaseContainerFrame.h"
 #include "nsContentUtils.h"
 #include "nsLineLayout.h"
 #include "nsPresContext.h"
 #include "nsStyleContext.h"
 #include "nsStyleStructInlines.h"
 #include "WritingModes.h"
 #include "RubyUtils.h"
+#include "nsTextFrame.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/DebugOnly.h"
 
 using namespace mozilla;
 
 //----------------------------------------------------------------------
 
 // Frame class boilerplate
@@ -305,34 +306,23 @@ nsRubyBaseContainerFrame::ComputeSize(ns
 /* virtual */ nscoord
 nsRubyBaseContainerFrame::GetLogicalBaseline(WritingMode aWritingMode) const
 {
   return mBaseline;
 }
 
 struct nsRubyBaseContainerFrame::ReflowState
 {
+  bool mAllowInitialLineBreak;
   bool mAllowLineBreak;
   const TextContainerArray& mTextContainers;
   const nsHTMLReflowState& mBaseReflowState;
   const nsTArray<UniquePtr<nsHTMLReflowState>>& mTextReflowStates;
 };
 
-// Check whether the given extra isize can fit in the line in base level.
-static bool
-ShouldBreakBefore(const nsHTMLReflowState& aReflowState, nscoord aExtraISize)
-{
-  nsLineLayout* lineLayout = aReflowState.mLineLayout;
-  int32_t offset;
-  gfxBreakPriority priority;
-  nscoord icoord = lineLayout->GetCurrentICoord();
-  return icoord + aExtraISize > aReflowState.AvailableISize() &&
-         lineLayout->GetLastOptionalBreakPosition(&offset, &priority);
-}
-
 /* virtual */ void
 nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext,
                                  nsHTMLReflowMetrics& aDesiredSize,
                                  const nsHTMLReflowState& aReflowState,
                                  nsReflowStatus& aStatus)
 {
   DO_GLOBAL_REFLOW_COUNT("nsRubyBaseContainerFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
@@ -413,66 +403,50 @@ nsRubyBaseContainerFrame::Reflow(nsPresC
   // Allow line break between ruby bases when white-space allows,
   // we are not inside a nested ruby, and there is no span.
   bool allowLineBreak = !inNestedRuby && StyleText()->WhiteSpaceCanWrap(this);
   bool allowInitialLineBreak = allowLineBreak;
   if (!GetPrevInFlow()) {
     allowInitialLineBreak = !inNestedRuby &&
       parent->StyleText()->WhiteSpaceCanWrap(parent);
   }
-  if (allowInitialLineBreak && aReflowState.mLineLayout->LineIsBreakable() &&
-      aReflowState.mLineLayout->NotifyOptionalBreakPosition(
-        this, 0, 0 <= aReflowState.AvailableISize(),
-        gfxBreakPriority::eNormalBreak)) {
-    aStatus = NS_INLINE_LINE_BREAK_BEFORE();
+  if (!aReflowState.mLineLayout->LineIsBreakable()) {
+    allowInitialLineBreak = false;
   }
 
   nscoord isize = 0;
-  if (aStatus == NS_FRAME_COMPLETE) {
-    // Reflow columns excluding any span
-    ReflowState reflowState = {
-      allowLineBreak && !hasSpan, textContainers, aReflowState, reflowStates
-    };
-    isize = ReflowColumns(reflowState, aStatus);
-  }
+  // Reflow columns excluding any span
+  ReflowState reflowState = {
+    allowInitialLineBreak, allowLineBreak && !hasSpan,
+    textContainers, aReflowState, reflowStates
+  };
+  isize = ReflowColumns(reflowState, aStatus);
   DebugOnly<nscoord> lineSpanSize = aReflowState.mLineLayout->EndSpan(this);
   aDesiredSize.ISize(lineWM) = isize;
   // When there are no frames inside the ruby base container, EndSpan
   // will return 0. However, in this case, the actual width of the
   // container could be non-zero because of non-empty ruby annotations.
-  MOZ_ASSERT(NS_INLINE_IS_BREAK_BEFORE(aStatus) ||
+  MOZ_ASSERT(NS_INLINE_IS_BREAK(aStatus) ||
              isize == lineSpanSize || mFrames.IsEmpty());
 
   // If there exists any span, the columns must either be completely
   // reflowed, or be not reflowed at all.
   MOZ_ASSERT(NS_INLINE_IS_BREAK_BEFORE(aStatus) ||
              NS_FRAME_IS_COMPLETE(aStatus) || !hasSpan);
   if (!NS_INLINE_IS_BREAK_BEFORE(aStatus) &&
       NS_FRAME_IS_COMPLETE(aStatus) && hasSpan) {
     // Reflow spans
     ReflowState reflowState = {
-      false, textContainers, aReflowState, reflowStates
+      false, false, textContainers, aReflowState, reflowStates
     };
     nscoord spanISize = ReflowSpans(reflowState);
-    nscoord deltaISize = spanISize - isize;
-    if (deltaISize > 0) {
-      if (allowLineBreak && ShouldBreakBefore(aReflowState, deltaISize)) {
-        aStatus = NS_INLINE_LINE_BREAK_BEFORE();
-      } else {
-        isize = spanISize;
-      }
-    }
-    // When there are spans, ReflowColumns and ReflowOneColumn won't
-    // record any optional break position. We have to record one
-    // at the end of this segment.
-    if (!NS_INLINE_IS_BREAK(aStatus) && allowLineBreak &&
-        aReflowState.mLineLayout->NotifyOptionalBreakPosition(
-          this, INT32_MAX, isize <= aReflowState.AvailableISize(),
-          gfxBreakPriority::eNormalBreak)) {
-      aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus);
+    isize = std::max(isize, spanISize);
+    if (isize > aReflowState.AvailableISize() &&
+        aReflowState.mLineLayout->HasOptionalBreakPosition()) {
+      aStatus = NS_INLINE_LINE_BREAK_BEFORE();
     }
   }
 
   for (uint32_t i = 0; i < rtcCount; i++) {
     // It happens before the ruby text container is reflowed, and that
     // when it is reflowed, it will just use this size.
     nsRubyTextContainerFrame* textContainer = textContainers[i];
     nsLineLayout* lineLayout = lineLayouts[i].get();
@@ -622,25 +596,75 @@ nsRubyBaseContainerFrame::ReflowColumns(
     // not need to push anything.
     MOZ_ASSERT(e.AtEnd());
     aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus);
   }
 
   return icoord;
 }
 
+static gfxBreakPriority
+LineBreakBefore(const nsHTMLReflowState& aReflowState, nsRubyBaseFrame* aFrame)
+{
+  for (nsIFrame* child = aFrame; child;
+       child = child->GetFirstPrincipalChild()) {
+    if (!child->CanContinueTextRun()) {
+      // It is not an inline element. We can break before it.
+      return gfxBreakPriority::eNormalBreak;
+    }
+    if (child->GetType() != nsGkAtoms::textFrame) {
+      continue;
+    }
+
+    auto textFrame = static_cast<nsTextFrame*>(child);
+    gfxSkipCharsIterator iter =
+      textFrame->EnsureTextRun(nsTextFrame::eInflated,
+                               aReflowState.rendContext->ThebesContext(),
+                               aReflowState.mLineLayout->LineContainerFrame(),
+                               aReflowState.mLineLayout->GetLine());
+    iter.SetOriginalOffset(textFrame->GetContentOffset());
+    uint32_t pos = iter.GetSkippedOffset();
+    gfxTextRun* textRun = textFrame->GetTextRun(nsTextFrame::eInflated);
+    // Return whether we can break before the first character.
+    if (textRun->CanBreakLineBefore(pos)) {
+      return gfxBreakPriority::eNormalBreak;
+    }
+    // Check whether we can wrap word here.
+    const nsStyleText* textStyle = textFrame->StyleText();
+    if (textStyle->WordCanWrap(textFrame) && textRun->IsClusterStart(pos)) {
+      return gfxBreakPriority::eWordWrapBreak;
+    }
+    // We cannot break before.
+    return gfxBreakPriority::eNoBreak;
+  }
+  // Neither block, nor text frame is found as a leaf. We won't break
+  // before this base frame. It is the behavior of empty spans.
+  return gfxBreakPriority::eNoBreak;
+}
+
 nscoord
 nsRubyBaseContainerFrame::ReflowOneColumn(const ReflowState& aReflowState,
                                           uint32_t aColumnIndex,
                                           const RubyColumn& aColumn,
                                           nsReflowStatus& aStatus)
 {
   const nsHTMLReflowState& baseReflowState = aReflowState.mBaseReflowState;
   const auto& textReflowStates = aReflowState.mTextReflowStates;
 
+  if (aColumn.mBaseFrame) {
+    int32_t pos = baseReflowState.mLineLayout->
+      GetForcedBreakPosition(aColumn.mBaseFrame);
+    MOZ_ASSERT(pos == -1 || pos == 0,
+               "It should either break before, or not break at all.");
+    if (pos >= 0) {
+      aStatus = NS_INLINE_LINE_BREAK_BEFORE();
+      return 0;
+    }
+  }
+
   const uint32_t rtcCount = aReflowState.mTextContainers.Length();
   MOZ_ASSERT(aColumn.mTextFrames.Length() == rtcCount);
   MOZ_ASSERT(textReflowStates.Length() == rtcCount);
   nscoord istart = baseReflowState.mLineLayout->GetCurrentICoord();
   nscoord columnISize = 0;
 
   nsAutoString baseText;
   if (aColumn.mBaseFrame) {
@@ -677,43 +701,65 @@ nsRubyBaseContainerFrame::ReflowOneColum
       nscoord textIStart = lineLayout->GetCurrentICoord();
       lineLayout->ReflowFrame(textFrame, reflowStatus, nullptr, pushedFrame);
       MOZ_ASSERT(!NS_INLINE_IS_BREAK(reflowStatus) && !pushedFrame,
                  "Any line break inside ruby box should has been suppressed");
       nscoord textISize = lineLayout->GetCurrentICoord() - textIStart;
       columnISize = std::max(columnISize, textISize);
     }
   }
-  if (aReflowState.mAllowLineBreak &&
-      ShouldBreakBefore(baseReflowState, columnISize)) {
-    // Since ruby text container uses an independent line layout, it
-    // may successfully place a frame because the line is empty while
-    // the line of base container is not.
-    aStatus = NS_INLINE_LINE_BREAK_BEFORE();
-    return 0;
-  }
 
   // Reflow the base frame
   if (aColumn.mBaseFrame) {
     RubyUtils::ClearReservedISize(aColumn.mBaseFrame);
 
     bool pushedFrame;
     nsReflowStatus reflowStatus;
     nsLineLayout* lineLayout = baseReflowState.mLineLayout;
     nscoord baseIStart = lineLayout->GetCurrentICoord();
     lineLayout->ReflowFrame(aColumn.mBaseFrame, reflowStatus,
                             nullptr, pushedFrame);
     MOZ_ASSERT(!NS_INLINE_IS_BREAK(reflowStatus) && !pushedFrame,
                "Any line break inside ruby box should has been suppressed");
     nscoord baseISize = lineLayout->GetCurrentICoord() - baseIStart;
     columnISize = std::max(columnISize, baseISize);
+
+    bool allowBreakBefore = aColumnIndex ?
+      aReflowState.mAllowLineBreak : aReflowState.mAllowInitialLineBreak;
+    if (allowBreakBefore) {
+      bool shouldBreakBefore = false;
+      gfxBreakPriority breakPriority =
+        LineBreakBefore(baseReflowState, aColumn.mBaseFrame);
+      if (breakPriority != gfxBreakPriority::eNoBreak) {
+        int32_t offset;
+        gfxBreakPriority lastBreakPriority;
+        baseReflowState.mLineLayout->
+          GetLastOptionalBreakPosition(&offset, &lastBreakPriority);
+        shouldBreakBefore = breakPriority >= lastBreakPriority;
+      }
+      if (shouldBreakBefore) {
+        bool fits = istart <= baseReflowState.AvailableISize();
+        DebugOnly<bool> breakBefore =
+          baseReflowState.mLineLayout->NotifyOptionalBreakPosition(
+            aColumn.mBaseFrame, 0, fits, breakPriority);
+        MOZ_ASSERT(!breakBefore, "The break notified here should have "
+                   "triggered at the start of this method.");
+      }
+    }
+  }
+
+  nscoord icoord = istart + columnISize;
+  // If we can break here, do it now.
+  if (icoord > baseReflowState.AvailableISize() &&
+      baseReflowState.mLineLayout->HasOptionalBreakPosition()) {
+    aStatus = NS_INLINE_LINE_BREAK_BEFORE();
+    return 0;
   }
 
   // Align all the line layout to the new coordinate.
-  nscoord icoord = istart + columnISize;
   nscoord deltaISize = icoord - baseReflowState.mLineLayout->GetCurrentICoord();
   if (deltaISize > 0) {
     baseReflowState.mLineLayout->AdvanceICoord(deltaISize);
     if (aColumn.mBaseFrame) {
       RubyUtils::SetReservedISize(aColumn.mBaseFrame, deltaISize);
     }
   }
   for (uint32_t i = 0; i < rtcCount; i++) {
@@ -729,23 +775,16 @@ nsRubyBaseContainerFrame::ReflowOneColum
         RubyUtils::SetReservedISize(textFrame, deltaISize);
       }
     }
     if (aColumn.mBaseFrame && textFrame) {
       lineLayout->AttachLastFrameToBaseLineLayout();
     }
   }
 
-  if (aReflowState.mAllowLineBreak &&
-      baseReflowState.mLineLayout->NotifyOptionalBreakPosition(
-        this, aColumnIndex + 1, icoord <= baseReflowState.AvailableISize(),
-        gfxBreakPriority::eNormalBreak)) {
-    aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus);
-  }
-
   return columnISize;
 }
 
 nsRubyBaseContainerFrame::PullFrameState::PullFrameState(
     nsRubyBaseContainerFrame* aBaseContainer,
     const TextContainerArray& aTextContainers)
   : mBase(aBaseContainer)
   , mTextContainers(aTextContainers)
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -1144,40 +1144,38 @@ CanTextCrossFrameBoundary(nsIFrame* aFra
       result.mFrameToScan = aFrame->GetFirstPrincipalChild();
       result.mOverflowFrameToScan =
         aFrame->GetFirstChild(nsIFrame::kOverflowList);
       NS_WARN_IF_FALSE(!result.mOverflowFrameToScan,
                        "Scanning overflow inline frames is something we should avoid");
       result.mScanSiblings = true;
       result.mTextRunCanCrossFrameBoundary = true;
       result.mLineBreakerCanCrossFrameBoundary = true;
-    } else if (aFrame->GetType() == nsGkAtoms::rubyTextFrame ||
-               aFrame->GetType() == nsGkAtoms::rubyTextContainerFrame) {
-      result.mFrameToScan = aFrame->GetFirstPrincipalChild();
-      result.mOverflowFrameToScan =
-        aFrame->GetFirstChild(nsIFrame::kOverflowList);
-      NS_WARN_IF_FALSE(!result.mOverflowFrameToScan,
-                       "Scanning overflow inline frames is something we should avoid");
-      result.mScanSiblings = true;
-      result.mTextRunCanCrossFrameBoundary = false;
-      result.mLineBreakerCanCrossFrameBoundary = false;
     } else {
+      MOZ_ASSERT(aType != nsGkAtoms::rubyTextContainerFrame,
+                 "Shouldn't call this method for ruby text container");
       result.mFrameToScan = nullptr;
       result.mOverflowFrameToScan = nullptr;
       result.mTextRunCanCrossFrameBoundary = false;
       result.mLineBreakerCanCrossFrameBoundary = false;
     }
   }    
   return result;
 }
 
 BuildTextRunsScanner::FindBoundaryResult
 BuildTextRunsScanner::FindBoundaries(nsIFrame* aFrame, FindBoundaryState* aState)
 {
   nsIAtom* frameType = aFrame->GetType();
+  if (frameType == nsGkAtoms::rubyTextContainerFrame) {
+    // Don't stop a text run for ruby text container. We want ruby text
+    // containers to be skipped, but continue the text run across them.
+    return FB_CONTINUE;
+  }
+
   nsTextFrame* textFrame = frameType == nsGkAtoms::textFrame
     ? static_cast<nsTextFrame*>(aFrame) : nullptr;
   if (textFrame) {
     if (aState->mLastTextFrame &&
         textFrame != aState->mLastTextFrame->GetNextInFlow() &&
         !ContinueTextRunAcrossFrames(aState->mLastTextFrame, textFrame)) {
       aState->mSeenTextRunBoundaryOnThisLine = true;
       if (aState->mSeenSpaceForLineBreakingOnThisLine)
@@ -1658,36 +1656,41 @@ BuildTextRunsScanner::ContinueTextRunAcr
     sc1->StyleFont()->mLanguage == sc2->StyleFont()->mLanguage &&
     textStyle1->mTextTransform == textStyle2->mTextTransform &&
     nsLayoutUtils::GetTextRunFlagsForStyle(sc1, fontStyle1, textStyle1, letterSpacing1) ==
       nsLayoutUtils::GetTextRunFlagsForStyle(sc2, fontStyle2, textStyle2, letterSpacing2);
 }
 
 void BuildTextRunsScanner::ScanFrame(nsIFrame* aFrame)
 {
+  nsIAtom* frameType = aFrame->GetType();
+  if (frameType == nsGkAtoms::rubyTextContainerFrame) {
+    // Don't include any ruby text container into the text run.
+    return;
+  }
+
   // First check if we can extend the current mapped frame block. This is common.
   if (mMappedFlows.Length() > 0) {
     MappedFlow* mappedFlow = &mMappedFlows[mMappedFlows.Length() - 1];
     if (mappedFlow->mEndFrame == aFrame &&
         (aFrame->GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION)) {
-      NS_ASSERTION(aFrame->GetType() == nsGkAtoms::textFrame,
+      NS_ASSERTION(frameType == nsGkAtoms::textFrame,
                    "Flow-sibling of a text frame is not a text frame?");
 
       // Don't do this optimization if mLastFrame has a terminal newline...
       // it's quite likely preformatted and we might want to end the textrun here.
       // This is almost always true:
       if (mLastFrame->StyleContext() == aFrame->StyleContext() &&
           !HasTerminalNewline(mLastFrame)) {
         AccumulateRunInfo(static_cast<nsTextFrame*>(aFrame));
         return;
       }
     }
   }
 
-  nsIAtom* frameType = aFrame->GetType();
   // Now see if we can add a new set of frames to the current textrun
   if (frameType == nsGkAtoms::textFrame) {
     nsTextFrame* frame = static_cast<nsTextFrame*>(aFrame);
 
     if (mLastFrame) {
       if (!ContinueTextRunAcrossFrames(mLastFrame, frame)) {
         FlushFrames(false, false);
       } else {
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ruby/line-breaking-1-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="ja">
+<head>
+  <meta charset="UTF-8">
+  <title>Bug 1089431 - Meet the specification for line breaking between ruby bases</title>
+  <link rel="stylesheet" href="common.css">
+  <style>
+    body { line-height: 3; }
+    span { white-space: nowrap; }
+  </style>
+</head>
+<body>
+  <div style="width: .5em; border: 1px solid silver;">
+    <span>「</span><span>な</span><span>に</span><span>、</span><span>誰</span><span>?</span><span>」</span><span>「</span><span>私</span><span>です</span><span>」</span>
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-ruby/line-breaking-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="ja">
+<head>
+  <meta charset="UTF-8">
+  <title>Bug 1089431 - Meet the specification for line breaking between ruby bases</title>
+  <link rel="stylesheet" href="common.css">
+  <style>
+    body { line-height: 3; }
+  </style>
+</head>
+<body>
+  <div style="width: .5em; border: 1px solid silver;">
+    <ruby>
+      <rb>「<rb>な<rb>に<rb>、<rb>誰<rb>?<rb>」<rb>「<rb>私<rb>です<rb>」</rb>
+      <!-- Check if ruby text containers are skipped from the text run -->
+      <rtc><rt>
+    </ruby>
+  </div>
+</body>
+</html>
--- a/layout/reftests/css-ruby/reftest.list
+++ b/layout/reftests/css-ruby/reftest.list
@@ -24,16 +24,17 @@ fails == autohiding-3.html autohiding-3-
 == inlinize-blocks-3.html inlinize-blocks-3-ref.html
 == inlinize-blocks-4.html inlinize-blocks-4-ref.html
 == inlinize-blocks-5.html inlinize-blocks-5-ref.html
 == intra-level-whitespace-1.html intra-level-whitespace-1-ref.html
 == intra-level-whitespace-2.html intra-level-whitespace-2-ref.html
 == intra-level-whitespace-3.html intra-level-whitespace-3-ref.html
 == justification-1.html justification-1-ref.html
 == justification-2.html justification-2-ref.html
+== line-breaking-1.html line-breaking-1-ref.html
 == line-height-1.html line-height-1-ref.html
 == line-height-2.html line-height-2-ref.html
 == line-height-3.html line-height-3-ref.html
 load nested-ruby-1.html
 == no-transform.html no-transform-ref.html
 fails-if(cocoaWidget) == relative-positioning-1.html relative-positioning-1-ref.html # bug 1120280
 == relative-positioning-2.html relative-positioning-2-ref.html
 == ruby-position-horizontal.html ruby-position-horizontal-ref.html
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -568,29 +568,29 @@ RuleHash::~RuleHash()
   }
 }
 
 void RuleHash::AppendRuleToTable(PLDHashTable* aTable, const void* aKey,
                                  const RuleSelectorPair& aRuleInfo)
 {
   // Get a new or existing entry.
   RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
-    (PL_DHashTableAdd(aTable, aKey, fallible));
+                                         (PL_DHashTableAdd(aTable, aKey));
   if (!entry)
     return;
   entry->mRules.AppendElement(RuleValue(aRuleInfo, mRuleCount++, mQuirksMode));
 }
 
 static void
 AppendRuleToTagTable(PLDHashTable* aTable, nsIAtom* aKey,
                      const RuleValue& aRuleInfo)
 {
   // Get a new or exisiting entry
   RuleHashTagTableEntry *entry = static_cast<RuleHashTagTableEntry*>
-    (PL_DHashTableAdd(aTable, aKey, fallible));
+    (PL_DHashTableAdd(aTable, aKey));
   if (!entry)
     return;
 
   entry->mRules.AppendElement(aRuleInfo);
 }
 
 void RuleHash::AppendUniversalRule(const RuleSelectorPair& aRuleInfo)
 {
@@ -1036,17 +1036,17 @@ RuleCascadeData::SizeOfIncludingThis(Mal
   return n;
 }
 
 nsTArray<nsCSSSelector*>*
 RuleCascadeData::AttributeListFor(nsIAtom* aAttribute)
 {
   AtomSelectorEntry *entry =
     static_cast<AtomSelectorEntry*>
-               (PL_DHashTableAdd(&mAttributeSelectors, aAttribute, fallible));
+               (PL_DHashTableAdd(&mAttributeSelectors, aAttribute));
   if (!entry)
     return nullptr;
   return &entry->mSelectors;
 }
 
 // -------------------------------
 // CSS Style rule processor implementation
 //
@@ -3129,33 +3129,34 @@ AddSelector(RuleCascadeData* aCascade,
         nsCSSRuleProcessor::StateSelector(dependentStates,
                                           aSelectorInTopLevel));
     }
 
     // Build mIDSelectors
     if (negation == aSelectorInTopLevel) {
       for (nsAtomList* curID = negation->mIDList; curID;
            curID = curID->mNext) {
-        AtomSelectorEntry *entry = static_cast<AtomSelectorEntry*>
-          (PL_DHashTableAdd(&aCascade->mIdSelectors, curID->mAtom, fallible));
+        AtomSelectorEntry *entry =
+          static_cast<AtomSelectorEntry*>(PL_DHashTableAdd(&aCascade->mIdSelectors,
+                                                           curID->mAtom));
         if (entry) {
           entry->mSelectors.AppendElement(aSelectorInTopLevel);
         }
       }
     } else if (negation->mIDList) {
       aCascade->mPossiblyNegatedIDSelectors.AppendElement(aSelectorInTopLevel);
     }
 
     // Build mClassSelectors
     if (negation == aSelectorInTopLevel) {
       for (nsAtomList* curClass = negation->mClassList; curClass;
            curClass = curClass->mNext) {
-        AtomSelectorEntry *entry = static_cast<AtomSelectorEntry*>
-          (PL_DHashTableAdd(&aCascade->mClassSelectors, curClass->mAtom,
-                            fallible));
+        AtomSelectorEntry *entry =
+          static_cast<AtomSelectorEntry*>(PL_DHashTableAdd(&aCascade->mClassSelectors,
+                                                           curClass->mAtom));
         if (entry) {
           entry->mSelectors.AppendElement(aSelectorInTopLevel);
         }
       }
     } else if (negation->mClassList) {
       aCascade->mPossiblyNegatedClassSelectors.AppendElement(aSelectorInTopLevel);
     }
 
@@ -3404,18 +3405,17 @@ CascadeRuleEnumFunc(css::Rule* aRule, vo
 
   if (css::Rule::STYLE_RULE == type) {
     css::StyleRule* styleRule = static_cast<css::StyleRule*>(aRule);
 
     for (nsCSSSelectorList *sel = styleRule->Selector();
          sel; sel = sel->mNext) {
       int32_t weight = sel->mWeight;
       RuleByWeightEntry *entry = static_cast<RuleByWeightEntry*>(
-        PL_DHashTableAdd(&data->mRulesByWeight, NS_INT32_TO_PTR(weight),
-                         fallible));
+        PL_DHashTableAdd(&data->mRulesByWeight, NS_INT32_TO_PTR(weight)));
       if (!entry)
         return false;
       entry->data.mWeight = weight;
       // entry->data.mRuleSelectorPairs should be linked in forward order;
       // entry->data.mTail is the slot to write to.
       PerWeightDataListItem *newItem =
         new (data->mArena) PerWeightDataListItem(styleRule, sel->mSelectors);
       if (newItem) {
--- a/layout/style/nsHTMLStyleSheet.cpp
+++ b/layout/style/nsHTMLStyleSheet.cpp
@@ -478,19 +478,18 @@ nsHTMLStyleSheet::SetVisitedLinkColor(ns
 
 already_AddRefed<nsMappedAttributes>
 nsHTMLStyleSheet::UniqueMappedAttributes(nsMappedAttributes* aMapped)
 {
   if (!mMappedAttrTable.IsInitialized()) {
     PL_DHashTableInit(&mMappedAttrTable, &MappedAttrTable_Ops,
                       sizeof(MappedAttrTableEntry));
   }
-  MappedAttrTableEntry *entry =
-    static_cast<MappedAttrTableEntry*>
-               (PL_DHashTableAdd(&mMappedAttrTable, aMapped, fallible));
+  MappedAttrTableEntry *entry = static_cast<MappedAttrTableEntry*>
+                                           (PL_DHashTableAdd(&mMappedAttrTable, aMapped));
   if (!entry)
     return nullptr;
   if (!entry->mAttributes) {
     // We added a new entry to the hashtable, so we have a new unique set.
     entry->mAttributes = aMapped;
   }
   nsRefPtr<nsMappedAttributes> ret = entry->mAttributes;
   return ret.forget();
@@ -514,17 +513,17 @@ nsHTMLStyleSheet::DropMappedAttributes(n
 nsIStyleRule*
 nsHTMLStyleSheet::LangRuleFor(const nsString& aLanguage)
 {
   if (!mLangRuleTable.IsInitialized()) {
     PL_DHashTableInit(&mLangRuleTable, &LangRuleTable_Ops,
                       sizeof(LangRuleTableEntry));
   }
   LangRuleTableEntry *entry = static_cast<LangRuleTableEntry*>
-    (PL_DHashTableAdd(&mLangRuleTable, &aLanguage, fallible));
+    (PL_DHashTableAdd(&mLangRuleTable, &aLanguage));
   if (!entry) {
     NS_ASSERTION(false, "out of memory");
     return nullptr;
   }
   return entry->mRule;
 }
 
 static size_t
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -1415,18 +1415,17 @@ nsRuleNode::DestroyInternal(nsRuleNode *
     destroyQueueTail = &destroyQueue;
   }
 
   if (ChildrenAreHashed()) {
     PLDHashTable *children = ChildrenHash();
     PL_DHashTableEnumerate(children, EnqueueRuleNodeChildren,
                            &destroyQueueTail);
     *destroyQueueTail = nullptr; // ensure null-termination
-    PL_DHashTableFinish(children);
-    delete children;
+    PL_DHashTableDestroy(children);
   } else if (HaveChildren()) {
     *destroyQueueTail = ChildrenList();
     do {
       destroyQueueTail = &(*destroyQueueTail)->mNextSibling;
     } while (*destroyQueueTail);
   }
   mChildren.asVoid = nullptr;
 
@@ -1529,17 +1528,17 @@ nsRuleNode::Transition(nsIStyleRule* aRu
     if (curr)
       next = curr;
     else if (numKids >= kMaxChildrenInList)
       ConvertChildrenToHash(numKids);
   }
 
   if (ChildrenAreHashed()) {
     ChildrenHashEntry *entry = static_cast<ChildrenHashEntry*>
-      (PL_DHashTableAdd(ChildrenHash(), &key, fallible));
+                                          (PL_DHashTableAdd(ChildrenHash(), &key));
     if (!entry) {
       NS_WARNING("out of memory");
       return this;
     }
     if (entry->mRuleNode)
       next = entry->mRuleNode;
     else {
       next = entry->mRuleNode = new (mPresContext)
@@ -1599,23 +1598,25 @@ void nsRuleNode::SetUsedDirectly()
   }
 }
 
 void
 nsRuleNode::ConvertChildrenToHash(int32_t aNumKids)
 {
   NS_ASSERTION(!ChildrenAreHashed() && HaveChildren(),
                "must have a non-empty list of children");
-  PLDHashTable *hash = new PLDHashTa