Merge m-i to m-c
authorPhil Ringnalda <philringnalda@gmail.com>
Sun, 24 Nov 2013 17:45:53 -0800
changeset 157303 250bb14d76d4268424869451d8554fa0226ea9ef
parent 157302 641d5e86ed847bf4c3c2ff65468a79846d0f3dad (current diff)
parent 157279 67cea1cdd62620ab0861396dbd51e07763c21858 (diff)
child 157311 0e32ded5807226cf6211648a5ccf315e7ef594f2
child 157348 4862074511a3ac1ac436f722991ad2b284cbdd9e
push id36690
push usercbook@mozilla.com
push dateMon, 25 Nov 2013 14:10:32 +0000
treeherdermozilla-inbound@0dbba019d66b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone28.0a1
first release with
nightly linux32
250bb14d76d4 / 28.0a1 / 20131125030201 / files
nightly linux64
250bb14d76d4 / 28.0a1 / 20131125030201 / files
nightly mac
250bb14d76d4 / 28.0a1 / 20131125030201 / files
nightly win32
250bb14d76d4 / 28.0a1 / 20131125030201 / files
nightly win64
250bb14d76d4 / 28.0a1 / 20131125030201 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-i to m-c
js/ductwork/debugger/Makefile.in
toolkit/components/reflect/Makefile.in
--- a/browser/base/content/test/general/browser_bug623893.js
+++ b/browser/base/content/test/general/browser_bug623893.js
@@ -23,19 +23,20 @@ function runTests() {
     });
   });
 }
 
 function duplicate(delta, msg, cb) {
   var start = gBrowser.sessionHistory.index;
 
   duplicateTabIn(gBrowser.selectedTab, "tab", delta);
+  let tab = gBrowser.selectedTab;
 
-  gBrowser.selectedBrowser.addEventListener("pageshow", function () {
-    gBrowser.selectedBrowser.removeEventListener("pageshow", arguments.callee, false);
+  tab.addEventListener("SSTabRestored", function tabRestoredListener() {
+    tab.removeEventListener("SSTabRestored", tabRestoredListener, false);
     is(gBrowser.sessionHistory.index, start + delta, msg);
     executeSoon(cb);
   }, false);
 }
 
 function loadAndWait(url, cb) {
   gBrowser.selectedBrowser.addEventListener("load", function () {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
--- a/browser/components/build/Makefile.in
+++ b/browser/components/build/Makefile.in
@@ -17,20 +17,16 @@ ifeq ($(OS_ARCH),WINNT)
 OS_LIBS += $(call EXPAND_LIBNAME,version)
 endif
 
 EXTRA_DSO_LDOPTS += \
 	$(XPCOM_GLUE_LDOPTS) \
 	$(MOZ_COMPONENT_LIBS) \
 	$(NULL)
 
-ifdef JS_SHARED_LIBRARY
-EXTRA_DSO_LDOPTS += $(MOZ_JS_LIBS)
-endif
-
 LOCAL_INCLUDES += -I$(srcdir)/../migration/src
 
 # Mac: Need to link with CoreFoundation for Mac Migrators (PList reading code)
 # GTK2: Need to link with glib for GNOME shell service
 ifneq (,$(filter cocoa gtk2 gtk3,$(MOZ_WIDGET_TOOLKIT)))
 EXTRA_DSO_LDOPTS += \
   $(TK_LIBS) \
   $(NULL)
--- a/browser/components/dirprovider/Makefile.in
+++ b/browser/components/dirprovider/Makefile.in
@@ -1,10 +1,5 @@
 # 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/.
 
 LOCAL_INCLUDES = -I$(srcdir)/../build
-
-EXTRA_DSO_LDOPTS = \
-	$(XPCOM_GLUE_LDOPTS) \
-	$(NSPR_LIBS) \
-	$(NULL)
--- a/browser/components/sessionstore/src/SessionStore.jsm
+++ b/browser/components/sessionstore/src/SessionStore.jsm
@@ -2619,44 +2619,42 @@ let SessionStoreInternal = {
         delete tab.__SS_extdata;
       }
 
       // Flush all data from the content script synchronously. This is done so
       // that all async messages that are still on their way to chrome will
       // be ignored and don't override any tab data set by restoreHistory().
       TabState.flush(tab.linkedBrowser);
 
+      // Ensure the index is in bounds.
+      let activeIndex = (tabData.index || tabData.entries.length) - 1;
+      activeIndex = Math.min(activeIndex, tabData.entries.length - 1);
+      activeIndex = Math.max(activeIndex, 0);
+
+      // Save the index in case we updated it above.
+      tabData.index = activeIndex + 1;
+
       // keep the data around to prevent dataloss in case
       // a tab gets closed before it's been properly restored
       browser.__SS_data = tabData;
       browser.__SS_restoreState = TAB_STATE_NEEDS_RESTORE;
       browser.setAttribute("pending", "true");
       tab.setAttribute("pending", "true");
 
       // Update the persistent tab state cache with |tabData| information.
       TabStateCache.updatePersistent(browser, {
         storage: tabData.storage || null,
         disallow: tabData.disallow || null,
         pageStyle: tabData.pageStyle || null
       });
 
-      if (tabData.entries.length == 0) {
-        // make sure to blank out this tab's content
-        // (just purging the tab's history won't be enough)
-        browser.loadURIWithFlags("about:blank",
-                                 Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY,
-                                 null, null, null);
-        continue;
-      }
-
       browser.stop(); // in case about:blank isn't done yet
 
       // wall-paper fix for bug 439675: make sure that the URL to be loaded
       // is always visible in the address bar
-      let activeIndex = (tabData.index || tabData.entries.length) - 1;
       let activePageData = tabData.entries[activeIndex] || null;
       let uri = activePageData ? activePageData.url || null : null;
       browser.userTypedValue = uri;
 
       // Also make sure currentURI is set so that switch-to-tab works before
       // the tab is restored. We'll reset this to about:blank when we try to
       // restore the tab to ensure that docshell doeesn't get confused.
       if (uri) {
@@ -2795,25 +2793,23 @@ let SessionStoreInternal = {
     // Set this tab's state to restoring
     browser.__SS_restoreState = TAB_STATE_RESTORING;
     browser.removeAttribute("pending");
     aTab.removeAttribute("pending");
 
     // Remove the history listener, since we no longer need it once we start restoring
     this._removeSHistoryListener(aTab);
 
-    let activeIndex = (tabData.index || tabData.entries.length) - 1;
-    if (activeIndex >= tabData.entries.length)
-      activeIndex = tabData.entries.length - 1;
+    let activeIndex = tabData.index - 1;
     // Reset currentURI.  This creates a new session history entry with a new
     // doc identifier, so we need to explicitly save and restore the old doc
     // identifier (corresponding to the SHEntry at activeIndex) below.
     browser.webNavigation.setCurrentURI(Utils.makeURI("about:blank"));
     // Attach data that will be restored on "load" event, after tab is restored.
-    if (activeIndex > -1) {
+    if (tabData.entries.length) {
       // restore those aspects of the currently active documents which are not
       // preserved in the plain history entries (mainly scroll state and text data)
       browser.__SS_restore_data = tabData.entries[activeIndex] || {};
       browser.__SS_restore_pageStyle = tabData.pageStyle || "";
       browser.__SS_restore_tab = aTab;
       didStartLoad = true;
       try {
         // In order to work around certain issues in session history, we need to
@@ -2821,16 +2817,24 @@ let SessionStoreInternal = {
         // instead of gotoIndex. See bug 597315.
         browser.webNavigation.sessionHistory.getEntryAtIndex(activeIndex, true);
         browser.webNavigation.sessionHistory.reloadCurrentEntry();
       }
       catch (ex) {
         // ignore page load errors
         didStartLoad = false;
       }
+    } else {
+      browser.__SS_restore_data = {};
+      browser.__SS_restore_pageStyle = "";
+      browser.__SS_restore_tab = aTab;
+      browser.loadURIWithFlags("about:blank",
+                               Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY,
+                               null, null, null);
+      didStartLoad = true;
     }
 
     // Handle userTypedValue. Setting userTypedValue seems to update gURLbar
     // as needed. Calling loadURI will cancel form filling in restoreDocument
     if (tabData.userTypedValue) {
       browser.userTypedValue = tabData.userTypedValue;
       if (tabData.userTypedClear) {
         // Make it so that we'll enter restoreDocument on page load. We will
@@ -2845,18 +2849,18 @@ let SessionStoreInternal = {
                                  Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP);
       }
     }
 
     // If we didn't start a load, then we won't reset this tab through the usual
     // channel (via the progress listener), so reset the tab ourselves. We will
     // also send SSTabRestored since this tab has technically been restored.
     if (!didStartLoad) {
+      this._resetTabRestoringState(aTab);
       this._sendTabRestoredNotification(aTab);
-      this._resetTabRestoringState(aTab);
     }
 
     return didStartLoad;
   },
 
   /**
    * This _attempts_ to restore the next available tab. If the restore fails,
    * then we will attempt the next one.
@@ -2930,22 +2934,29 @@ let SessionStoreInternal = {
         aEvent.originalTarget.defaultView != aEvent.originalTarget.defaultView.top) {
       return;
     }
 
     let frameList = this.getFramesToRestore(aBrowser);
     PageStyle.restore(aBrowser.docShell, frameList, aBrowser.__SS_restore_pageStyle);
     TextAndScrollData.restore(frameList);
 
-    // notify the tabbrowser that this document has been completely restored
-    this._sendTabRestoredNotification(aBrowser.__SS_restore_tab);
-
+    let tab = aBrowser.__SS_restore_tab;
+
+    // Drop all the state associated with restoring the tab. We're
+    // done with that now.
+    delete aBrowser.__SS_data;
     delete aBrowser.__SS_restore_data;
     delete aBrowser.__SS_restore_pageStyle;
     delete aBrowser.__SS_restore_tab;
+
+    // Notify the tabbrowser that this document has been completely
+    // restored. Do so after restoration is completely finished and
+    // all state for it has been dropped.
+    this._sendTabRestoredNotification(tab);
   },
 
   /**
    * Restore visibility and dimension features to a window
    * @param aWindow
    *        Window reference
    * @param aWinData
    *        Object containing session data for the window
--- a/browser/components/sessionstore/test/browser_248970_b_perwindowpb.js
+++ b/browser/components/sessionstore/test/browser_248970_b_perwindowpb.js
@@ -114,17 +114,17 @@ function test() {
 
       // verify that closedTabCount increased
       ok(ss.getClosedTabCount(aWin) > count,
          "getClosedTabCount has increased after closing a tab");
 
       // verify tab: (A), in undo list
       let tab_A_restored = test(function() ss.undoCloseTab(aWin, 0));
       ok(tab_A_restored, "a tab is in undo list");
-      whenBrowserLoaded(tab_A_restored.linkedBrowser, function() {
+      whenTabRestored(tab_A_restored, function() {
         is(testURL, tab_A_restored.linkedBrowser.currentURI.spec,
            "it's the same tab that we expect");
         aWin.gBrowser.removeTab(tab_A_restored);
 
         whenNewWindowLoaded({ private: true }, function(aWin) {
           windowsToClose.push(aWin);
 
           // setup a state for tab (B) so we can check that its duplicated
@@ -132,24 +132,24 @@ function test() {
           let key1 = "key1";
           let value1 = "Value " + Math.random();
           let state1 = {
             entries: [{ url: testURL2 }], extData: { key1: value1 }
           };
 
           let tab_B = aWin.gBrowser.addTab(testURL2);
           ss.setTabState(tab_B, JSON.stringify(state1));
-          whenBrowserLoaded(tab_B.linkedBrowser, function() {
+          whenTabRestored(tab_B, function() {
             // populate tab: (B) with different form data
             for (let item in fieldList)
               setFormValue(tab_B, item, fieldList[item]);
 
             // duplicate tab: (B)
             let tab_C = aWin.gBrowser.duplicateTab(tab_B);
-            whenBrowserLoaded(tab_C.linkedBrowser, function() {
+            whenTabRestored(tab_C, function() {
               // verify the correctness of the duplicated tab
               is(ss.getTabValue(tab_C, key1), value1,
                 "tab successfully duplicated - correct state");
 
               for (let item in fieldList)
                 ok(compareFormValue(tab_C, item, fieldList[item]),
                   "The value for \"" + item + "\" was correctly duplicated");
 
--- a/browser/components/sessionstore/test/browser_339445.js
+++ b/browser/components/sessionstore/test/browser_339445.js
@@ -6,29 +6,27 @@ function test() {
   /** Test for Bug 339445 **/
 
   waitForExplicitFinish();
 
   let testURL = "http://mochi.test:8888/browser/" +
     "browser/components/sessionstore/test/browser_339445_sample.html";
 
   let tab = gBrowser.addTab(testURL);
-  tab.linkedBrowser.addEventListener("load", function(aEvent) {
-    this.removeEventListener("load", arguments.callee, true);
+  whenBrowserLoaded(tab.linkedBrowser, function() {
     let doc = tab.linkedBrowser.contentDocument;
     is(doc.getElementById("storageTestItem").textContent, "PENDING",
        "sessionStorage value has been set");
 
     let tab2 = gBrowser.duplicateTab(tab);
-    tab2.linkedBrowser.addEventListener("load", function(aEvent) {
-      this.removeEventListener("load", arguments.callee, true);
+    whenTabRestored(tab2, function() {
       let doc2 = tab2.linkedBrowser.contentDocument;
       is(doc2.getElementById("storageTestItem").textContent, "SUCCESS",
          "sessionStorage value has been duplicated");
 
       // clean up
       gBrowser.removeTab(tab2);
       gBrowser.removeTab(tab);
 
       finish();
-    }, true);
-  }, true);
+    });
+  });
 }
--- a/browser/components/sessionstore/test/browser_346337.js
+++ b/browser/components/sessionstore/test/browser_346337.js
@@ -83,44 +83,41 @@ function test() {
   waitForExplicitFinish();
 
   // make sure we don't save form data at all (except for tab duplication)
   gPrefService.setIntPref("browser.sessionstore.privacy_level", 2);
 
   let rootDir = getRootDirectory(gTestPath);
   let testURL = rootDir + "browser_346337_sample.html";
   let tab = tabbrowser.addTab(testURL);
-  tab.linkedBrowser.addEventListener("load", function(aEvent) {
-    this.removeEventListener("load", arguments.callee, true);
+  whenBrowserLoaded(tab.linkedBrowser, function() {
     for (let xpath in fieldList)
       setFormValue(tab, xpath, fieldList[xpath]);
 
     let tab2 = tabbrowser.duplicateTab(tab);
-    tab2.linkedBrowser.addEventListener("load", function(aEvent) {
-      this.removeEventListener("load", arguments.callee, true);
+    whenTabRestored(tab2, function() {
       for (let xpath in fieldList)
         ok(compareFormValue(tab2, xpath, fieldList[xpath]),
            "The value for \"" + xpath + "\" was correctly restored");
 
       // clean up
       tabbrowser.removeTab(tab2);
       tabbrowser.removeTab(tab);
 
       tab = undoCloseTab();
-      tab.linkedBrowser.addEventListener("load", function(aEvent) {
-        this.removeEventListener("load", arguments.callee, true);
+      whenTabRestored(tab, function() {
         for (let xpath in fieldList)
           if (fieldList[xpath])
             ok(!compareFormValue(tab, xpath, fieldList[xpath]),
                "The value for \"" + xpath + "\" was correctly discarded");
 
         if (gPrefService.prefHasUserValue("browser.sessionstore.privacy_level"))
           gPrefService.clearUserPref("browser.sessionstore.privacy_level");
         // undoCloseTab can reuse a single blank tab, so we have to
         // make sure not to close the window when closing our last tab
         if (tabbrowser.tabs.length == 1)
           tabbrowser.addTab();
         tabbrowser.removeTab(tab);
         finish();
-      }, true);
-    }, true);
-  }, true);
+      });
+    });
+  });
 }
--- a/browser/components/sessionstore/test/browser_350525.js
+++ b/browser/components/sessionstore/test/browser_350525.js
@@ -66,36 +66,34 @@ function test() {
   let count = ss.getClosedTabCount(window);
   let max_tabs_undo = gPrefService.getIntPref("browser.sessionstore.max_tabs_undo");
   ok(0 <= count && count <= max_tabs_undo,
      "getClosedTabCount returns zero or at most max_tabs_undo");
 
   // create a new tab
   let testURL = "about:";
   tab = gBrowser.addTab(testURL);
-  tab.linkedBrowser.addEventListener("load", function(aEvent) {
-    this.removeEventListener("load", arguments.callee, true);
+  whenBrowserLoaded(tab.linkedBrowser, function() {
     // make sure that the next closed tab will increase getClosedTabCount
     gPrefService.setIntPref("browser.sessionstore.max_tabs_undo", max_tabs_undo + 1);
 
     // remove tab
     gBrowser.removeTab(tab);
 
     // getClosedTabCount
     var newcount = ss.getClosedTabCount(window);
     ok(newcount > count, "after closing a tab, getClosedTabCount has been incremented");
 
     // undoCloseTab
     tab = test(function() ss.undoCloseTab(window, 0));
     ok(tab, "undoCloseTab doesn't throw")
 
-    tab.linkedBrowser.addEventListener("load", function(aEvent) {
-      this.removeEventListener("load", arguments.callee, true);
-      is(this.currentURI.spec, testURL, "correct tab was reopened");
+    whenTabRestored(tab, function() {
+      is(tab.linkedBrowser.currentURI.spec, testURL, "correct tab was reopened");
 
       // clean up
       if (gPrefService.prefHasUserValue("browser.sessionstore.max_tabs_undo"))
         gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
       gBrowser.removeTab(tab);
       finish();
-    }, true);
-  }, true);
+    });
+  });
 }
--- a/browser/components/sessionstore/test/browser_367052.js
+++ b/browser/components/sessionstore/test/browser_367052.js
@@ -9,30 +9,27 @@ function test() {
 
   // make sure that the next closed tab will increase getClosedTabCount
   let max_tabs_undo = gPrefService.getIntPref("browser.sessionstore.max_tabs_undo");
   gPrefService.setIntPref("browser.sessionstore.max_tabs_undo", max_tabs_undo + 1);
   let closedTabCount = ss.getClosedTabCount(window);
 
   // restore a blank tab
   let tab = gBrowser.addTab("about:");
-  tab.linkedBrowser.addEventListener("load", function(aEvent) {
-    this.removeEventListener("load", arguments.callee, true);
-
+  whenBrowserLoaded(tab.linkedBrowser, function() {
     let history = tab.linkedBrowser.webNavigation.sessionHistory;
     ok(history.count >= 1, "the new tab does have at least one history entry");
 
     ss.setTabState(tab, JSON.stringify({ entries: [] }));
-    tab.linkedBrowser.addEventListener("load", function(aEvent) {
-      this.removeEventListener("load", arguments.callee, true);
+    whenTabRestored(tab, function() {
       ok(history.count == 0, "the tab was restored without any history whatsoever");
 
       gBrowser.removeTab(tab);
       ok(ss.getClosedTabCount(window) == closedTabCount,
          "The closed blank tab wasn't added to Recently Closed Tabs");
 
       // clean up
       if (gPrefService.prefHasUserValue("browser.sessionstore.max_tabs_undo"))
         gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
       finish();
-    }, true);
-  }, true);
+    });
+  });
 }
--- a/browser/components/sessionstore/test/browser_393716.js
+++ b/browser/components/sessionstore/test/browser_393716.js
@@ -8,67 +8,65 @@ function test() {
   /////////////////
   let key = "Unique key: " + Date.now();
   let value = "Unique value: " + Math.random();
   let testURL = "about:config";
 
   // create a new tab
   let tab = gBrowser.addTab(testURL);
   ss.setTabValue(tab, key, value);
-  tab.linkedBrowser.addEventListener("load", function(aEvent) {
-    this.removeEventListener("load", arguments.callee, true);
+  whenBrowserLoaded(tab.linkedBrowser, function() {
     // get the tab's state
     let state = ss.getTabState(tab);
     ok(state, "get the tab's state");
 
     // verify the tab state's integrity
     state = JSON.parse(state);
     ok(state instanceof Object && state.entries instanceof Array && state.entries.length > 0,
        "state object seems valid");
     ok(state.entries.length == 1 && state.entries[0].url == testURL,
        "Got the expected state object (test URL)");
     ok(state.extData && state.extData[key] == value,
        "Got the expected state object (test manually set tab value)");
 
     // clean up
     gBrowser.removeTab(tab);
-  }, true);
+  });
 
   //////////////////////////////////
   // setTabState and duplicateTab //
   //////////////////////////////////
   let key2 = "key2";
   let value2 = "Value " + Math.random();
   let value3 = "Another value: " + Date.now();
   let state = { entries: [{ url: testURL }], extData: { key2: value2 } };
 
   // create a new tab
   let tab2 = gBrowser.addTab();
   // set the tab's state
   ss.setTabState(tab2, JSON.stringify(state));
-  tab2.linkedBrowser.addEventListener("load", function(aEvent) {
-    this.removeEventListener("load", arguments.callee, true);
+  whenTabRestored(tab2, function() {
     // verify the correctness of the restored tab
-    ok(ss.getTabValue(tab2, key2) == value2 && this.currentURI.spec == testURL,
+    ok(ss.getTabValue(tab2, key2) == value2 && tab2.linkedBrowser.currentURI.spec == testURL,
        "the tab's state was correctly restored");
 
     // add text data
-    let textbox = this.contentDocument.getElementById("textbox");
+    let textbox = tab2.linkedBrowser.contentDocument.getElementById("textbox");
     textbox.value = value3;
 
     // duplicate the tab
     let duplicateTab = ss.duplicateTab(window, tab2);
     gBrowser.removeTab(tab2);
 
-    duplicateTab.linkedBrowser.addEventListener("load", function(aEvent) {
-      this.removeEventListener("load", arguments.callee, true);
+    whenTabRestored(duplicateTab, function() {
       // verify the correctness of the duplicated tab
-      ok(ss.getTabValue(duplicateTab, key2) == value2 && this.currentURI.spec == testURL,
+      ok(ss.getTabValue(duplicateTab, key2) == value2 &&
+         duplicateTab.linkedBrowser.currentURI.spec == testURL,
          "correctly duplicated the tab's state");
-      let textbox = this.contentDocument.getElementById("textbox");
+      let textbox = duplicateTab.linkedBrowser.contentDocument.getElementById("textbox");
       is(textbox.value, value3, "also duplicated text data");
 
       // clean up
       gBrowser.removeTab(duplicateTab);
       finish();
-    }, true);
-  }, true);
+    });
+  });
 }
--- a/browser/components/sessionstore/test/browser_394759_perwindowpb.js
+++ b/browser/components/sessionstore/test/browser_394759_perwindowpb.js
@@ -23,26 +23,25 @@ function test() {
     Services.prefs.clearUserPref("browser.sessionstore.interval");
     windowsToClose.forEach(function(win) {
       win.close();
     });
   });
 
   function testOpenCloseWindow(aIsPrivate, aTest, aCallback) {
     whenNewWindowLoaded({ private: aIsPrivate }, function(win) {
-      win.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
-        win.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+      whenBrowserLoaded(win.gBrowser.selectedBrowser, function() {
         executeSoon(function() {
           // Mark the window with some unique data to be restored later on.
           ss.setWindowValue(win, aTest.key, aTest.value);
           // Close.
           win.close();
           aCallback();
         });
-      }, true);
+      });
       win.gBrowser.selectedBrowser.loadURI(aTest.url);
     });
   }
 
   function testOnWindow(aIsPrivate, aValue, aCallback) {
     whenNewWindowLoaded({ private: aIsPrivate }, function(win) {
       windowsToClose.push(win);
       executeSoon(function() checkClosedWindows(aIsPrivate, aValue, aCallback));
--- a/browser/components/sessionstore/test/browser_423132.js
+++ b/browser/components/sessionstore/test/browser_423132.js
@@ -22,19 +22,17 @@ function test() {
   let newWin = openDialog(location, "_blank", "chrome,all,dialog=no", "about:blank");
 
   // make sure sessionstore saves the cookie data, then close the window
   newWin.addEventListener("load", function (aEvent) {
     newWin.removeEventListener("load", arguments.callee, false);
 
     newWin.gBrowser.loadURI(testURL, null, null);
 
-    newWin.gBrowser.addEventListener("load", function (aEvent) {
-      newWin.gBrowser.removeEventListener("load", arguments.callee, true);
-
+    whenBrowserLoaded(newWin.gBrowser.selectedBrowser, function() {
       // get the sessionstore state for the window
       let state = ss.getWindowState(newWin);
 
       // verify our cookie got set during pageload
       let e = cs.enumerator;
       let cookie;
       let i = 0;
       while (e.hasMoreElements()) {
@@ -62,12 +60,12 @@ function test() {
       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);
+    });
   }, false);
 }
 
--- a/browser/components/sessionstore/test/browser_447951.js
+++ b/browser/components/sessionstore/test/browser_447951.js
@@ -5,46 +5,52 @@
 function test() {
   /** Test for Bug 447951 **/
 
   waitForExplicitFinish();
   const baseURL = "http://mochi.test:8888/browser/" +
     "browser/components/sessionstore/test/browser_447951_sample.html#";
 
   let tab = gBrowser.addTab();
-  tab.linkedBrowser.addEventListener("load", function(aEvent) {
-    tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
-
+  whenBrowserLoaded(tab.linkedBrowser, function() {
     let tabState = { entries: [] };
     let max_entries = gPrefService.getIntPref("browser.sessionhistory.max_entries");
     for (let i = 0; i < max_entries; i++)
       tabState.entries.push({ url: baseURL + i });
 
     ss.setTabState(tab, JSON.stringify(tabState));
-    tab.addEventListener("SSTabRestored", function(aEvent) {
-      tab.removeEventListener("SSTabRestored", arguments.callee, false);
+    whenTabRestored(tab, function() {
       tabState = JSON.parse(ss.getTabState(tab));
       is(tabState.entries.length, max_entries, "session history filled to the limit");
       is(tabState.entries[0].url, baseURL + 0, "... but not more");
 
       // visit yet another anchor (appending it to session history)
       let doc = tab.linkedBrowser.contentDocument;
       let event = doc.createEvent("MouseEvents");
       event.initMouseEvent("click", true, true, doc.defaultView, 1,
                            0, 0, 0, 0, false, false, false, false, 0, null);
       doc.querySelector("a").dispatchEvent(event);
 
-      executeSoon(function() {
+      function check() {
         tabState = JSON.parse(ss.getTabState(tab));
+        if (tabState.entries[tabState.entries.length - 1].url != baseURL + "end") {
+          // It may take a few passes through the event loop before we
+          // get the right URL.
+          executeSoon(check);
+          return;
+        }
+
         is(tab.linkedBrowser.currentURI.spec, baseURL + "end",
            "the new anchor was loaded");
         is(tabState.entries[tabState.entries.length - 1].url, baseURL + "end",
            "... and ignored");
         is(tabState.entries[0].url, baseURL + 1,
            "... and the first item was removed");
 
         // clean up
         gBrowser.removeTab(tab);
         finish();
-      });
-    }, false);
-  }, true);
+      }
+
+      check();
+    });
+  });
 }
--- a/browser/components/sessionstore/test/browser_454908.js
+++ b/browser/components/sessionstore/test/browser_454908.js
@@ -13,27 +13,25 @@ function test() {
   };
 
   // make sure we do save form data
   gPrefService.setIntPref("browser.sessionstore.privacy_level", 0);
 
   let rootDir = getRootDirectory(gTestPath);
   let testURL = rootDir + "browser_454908_sample.html";
   let tab = gBrowser.addTab(testURL);
-  tab.linkedBrowser.addEventListener("load", function(aEvent) {
-    tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+  whenBrowserLoaded(tab.linkedBrowser, function() {
     let doc = tab.linkedBrowser.contentDocument;
     for (let id in fieldValues)
       doc.getElementById(id).value = fieldValues[id];
 
     gBrowser.removeTab(tab);
 
     tab = undoCloseTab();
-    tab.linkedBrowser.addEventListener("load", function(aEvent) {
-      tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+    whenTabRestored(tab, function() {
       let doc = tab.linkedBrowser.contentDocument;
       for (let id in fieldValues) {
         let node = doc.getElementById(id);
         if (node.type == "password")
           is(node.value, "", "password wasn't saved/restored");
         else
           is(node.value, fieldValues[id], "username was saved/restored");
       }
@@ -42,11 +40,11 @@ function test() {
       if (gPrefService.prefHasUserValue("browser.sessionstore.privacy_level"))
         gPrefService.clearUserPref("browser.sessionstore.privacy_level");
       // undoCloseTab can reuse a single blank tab, so we have to
       // make sure not to close the window when closing our last tab
       if (gBrowser.tabs.length == 1)
         gBrowser.addTab();
       gBrowser.removeTab(tab);
       finish();
-    }, true);
-  }, true);
+    });
+  });
 }
--- a/browser/components/sessionstore/test/browser_456342.js
+++ b/browser/components/sessionstore/test/browser_456342.js
@@ -8,23 +8,21 @@ function test() {
   waitForExplicitFinish();
 
   // make sure we do save form data
   gPrefService.setIntPref("browser.sessionstore.privacy_level", 0);
 
   let rootDir = getRootDirectory(gTestPath);
   let testURL = rootDir + "browser_456342_sample.xhtml";
   let tab = gBrowser.addTab(testURL);
-  tab.linkedBrowser.addEventListener("load", function(aEvent) {
-    this.removeEventListener("load", arguments.callee, true);
-
+  whenBrowserLoaded(tab.linkedBrowser, function() {
     let expectedValue = "try to save me";
     // Since bug 537289 we only save non-default values, so we need to set each
     // form field's value after load.
-    let formEls = aEvent.originalTarget.forms[0].elements;
+    let formEls = tab.linkedBrowser.contentDocument.forms[0].elements;
     for (let i = 0; i < formEls.length; i++)
       formEls[i].value = expectedValue;
 
     gBrowser.removeTab(tab);
 
     let undoItems = JSON.parse(ss.getClosedTabData(window));
     let savedFormData = undoItems[0].state.entries[0].formdata;
 
@@ -44,10 +42,10 @@ function test() {
 
     is(countGood, 4, "Saved text for non-standard input fields");
     is(countBad,  0, "Didn't save text for ignored field types");
 
     // clean up
     if (gPrefService.prefHasUserValue("browser.sessionstore.privacy_level"))
       gPrefService.clearUserPref("browser.sessionstore.privacy_level");
     finish();
-  }, true);
+  });
 }
--- a/browser/components/sessionstore/test/browser_463206.js
+++ b/browser/components/sessionstore/test/browser_463206.js
@@ -26,24 +26,18 @@ function test() {
       aTextField.dispatchEvent(event);
     }
 
     let doc = tab.linkedBrowser.contentDocument;
     typeText(doc.getElementById("out1"), Date.now());
     typeText(doc.getElementsByName("1|#out2")[0], Math.random());
     typeText(doc.defaultView.frames[0].frames[1].document.getElementById("in1"), new Date());
 
-    frameCount = 0;
     let tab2 = gBrowser.duplicateTab(tab);
-    tab2.linkedBrowser.addEventListener("load", function(aEvent) {
-      // wait for all frames to load completely
-      if (frameCount++ < 5)
-        return;
-      tab2.linkedBrowser.removeEventListener("load", arguments.callee, true);
-
+    whenTabRestored(tab2, function() {
       let doc = tab2.linkedBrowser.contentDocument;
       let win = tab2.linkedBrowser.contentWindow;
       isnot(doc.getElementById("out1").value,
             win.frames[1].document.getElementById("out1").value,
             "text isn't reused for frames");
       isnot(doc.getElementsByName("1|#out2")[0].value, "",
             "text containing | and # is correctly restored");
       is(win.frames[1].document.getElementById("out2").value, "",
@@ -54,11 +48,11 @@ function test() {
       is(win.frames[1].frames[0].document.getElementById("in1").value, "",
             "id prefixes aren't mixed up");
 
       // clean up
       gBrowser.removeTab(tab2);
       gBrowser.removeTab(tab);
 
       finish();
-    }, true);
+    });
   }, true);
 }
--- a/browser/components/sessionstore/test/browser_465215.js
+++ b/browser/components/sessionstore/test/browser_465215.js
@@ -8,32 +8,30 @@ function test() {
   waitForExplicitFinish();
 
   let uniqueName = "bug 465215";
   let uniqueValue1 = "as good as unique: " + Date.now();
   let uniqueValue2 = "as good as unique: " + Math.random();
 
   // set a unique value on a new, blank tab
   let tab1 = gBrowser.addTab();
-  tab1.linkedBrowser.addEventListener("load", function() {
-    tab1.linkedBrowser.removeEventListener("load", arguments.callee, true);
+  whenBrowserLoaded(tab1.linkedBrowser, function() {
     ss.setTabValue(tab1, uniqueName, uniqueValue1);
 
     // duplicate the tab with that value
     let tab2 = ss.duplicateTab(window, tab1);
     is(ss.getTabValue(tab2, uniqueName), uniqueValue1, "tab value was duplicated");
 
     ss.setTabValue(tab2, uniqueName, uniqueValue2);
     isnot(ss.getTabValue(tab1, uniqueName), uniqueValue2, "tab values aren't sync'd");
 
     // overwrite the tab with the value which should remove it
     ss.setTabState(tab1, JSON.stringify({ entries: [] }));
-    tab1.linkedBrowser.addEventListener("load", function() {
-      tab1.linkedBrowser.removeEventListener("load", arguments.callee, true);
+    whenTabRestored(tab1, function() {
       is(ss.getTabValue(tab1, uniqueName), "", "tab value was cleared");
 
       // clean up
       gBrowser.removeTab(tab2);
       gBrowser.removeTab(tab1);
       finish();
-    }, true);
-  }, true);
+    });
+  });
 }
--- a/browser/components/sessionstore/test/browser_466937.js
+++ b/browser/components/sessionstore/test/browser_466937.js
@@ -13,33 +13,31 @@ function test() {
   file.append("466937_test.file");
   file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666);
   let testPath = file.path;
 
   let testURL = "http://mochi.test:8888/browser/" +
     "browser/components/sessionstore/test/browser_466937_sample.html";
 
   let tab = gBrowser.addTab(testURL);
-  tab.linkedBrowser.addEventListener("load", function(aEvent) {
-    tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+  whenBrowserLoaded(tab.linkedBrowser, function() {
     let doc = tab.linkedBrowser.contentDocument;
     doc.getElementById("reverse_thief").value = "/home/user/secret2";
     doc.getElementById("bystander").value = testPath;
 
     let tab2 = gBrowser.duplicateTab(tab);
-    tab2.linkedBrowser.addEventListener("load", function(aEvent) {
-      tab2.linkedBrowser.removeEventListener("load", arguments.callee, true);
+    whenTabRestored(tab2, function() {
       doc = tab2.linkedBrowser.contentDocument;
       is(doc.getElementById("thief").value, "",
          "file path wasn't set to text field value");
       is(doc.getElementById("reverse_thief").value, "",
          "text field value wasn't set to full file path");
       is(doc.getElementById("bystander").value, testPath,
          "normal case: file path was correctly preserved");
 
       // clean up
       gBrowser.removeTab(tab2);
       gBrowser.removeTab(tab);
 
       finish();
-    }, true);
-  }, true);
+    });
+  });
 }
--- a/browser/components/sessionstore/test/browser_480893.js
+++ b/browser/components/sessionstore/test/browser_480893.js
@@ -8,48 +8,44 @@ function test() {
   waitForExplicitFinish();
 
   // Test that starting a new session loads a blank page if Firefox is
   // configured to display a blank page at startup (browser.startup.page = 0)
   gPrefService.setIntPref("browser.startup.page", 0);
   let tab = gBrowser.addTab("about:sessionrestore");
   gBrowser.selectedTab = tab;
   let browser = tab.linkedBrowser;
-  browser.addEventListener("load", function(aEvent) {
-    browser.removeEventListener("load", arguments.callee, true);
+  whenBrowserLoaded(browser, function() {
     let doc = browser.contentDocument;
 
     // click on the "Start New Session" button after about:sessionrestore is loaded
     doc.getElementById("errorCancel").click();
-    browser.addEventListener("load", function(aEvent) {
-      browser.removeEventListener("load", arguments.callee, true);
+    whenBrowserLoaded(browser, function() {
       let doc = browser.contentDocument;
 
       is(doc.URL, "about:blank", "loaded page is about:blank");
 
       // Test that starting a new session loads the homepage (set to http://mochi.test:8888)
       // if Firefox is configured to display a homepage at startup (browser.startup.page = 1)
       let homepage = "http://mochi.test:8888/";
       gPrefService.setCharPref("browser.startup.homepage", homepage);
       gPrefService.setIntPref("browser.startup.page", 1);
       gBrowser.loadURI("about:sessionrestore");
-      browser.addEventListener("load", function(aEvent) {
-        browser.removeEventListener("load", arguments.callee, true);
+      whenBrowserLoaded(browser, function() {
         let doc = browser.contentDocument;
 
         // click on the "Start New Session" button after about:sessionrestore is loaded
         doc.getElementById("errorCancel").click();
-        browser.addEventListener("load", function(aEvent) {
-          browser.removeEventListener("load", arguments.callee, true);
+        whenBrowserLoaded(browser, function() {
           let doc = browser.contentDocument;
 
           is(doc.URL, homepage, "loaded page is the homepage");
 
           // close tab, restore default values and finish the test
           gBrowser.removeTab(tab);
           gPrefService.clearUserPref("browser.startup.page");
           gPrefService.clearUserPref("browser.startup.homepage");
           finish();
-        }, true);
-      }, true);
-    }, true);
-  }, true);
+        });
+      });
+    });
+  });
 }
--- a/browser/components/sessionstore/test/browser_485482.js
+++ b/browser/components/sessionstore/test/browser_485482.js
@@ -7,30 +7,28 @@ function test() {
 
   waitForExplicitFinish();
 
   let uniqueValue = Math.random();
 
   let rootDir = getRootDirectory(gTestPath);
   let testURL = rootDir + "browser_485482_sample.html";
   let tab = gBrowser.addTab(testURL);
-  tab.linkedBrowser.addEventListener("load", function(aEvent) {
-    tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+  whenBrowserLoaded(tab.linkedBrowser, function() {
     let doc = tab.linkedBrowser.contentDocument;
     doc.querySelector("input[type=text]").value = uniqueValue;
     doc.querySelector("input[type=checkbox]").checked = true;
 
     let tab2 = gBrowser.duplicateTab(tab);
-    tab2.linkedBrowser.addEventListener("load", function(aEvent) {
-      tab2.linkedBrowser.removeEventListener("load", arguments.callee, true);
+    whenTabRestored(tab2, function() {
       doc = tab2.linkedBrowser.contentDocument;
       is(doc.querySelector("input[type=text]").value, uniqueValue,
          "generated XPath expression was valid");
       ok(doc.querySelector("input[type=checkbox]").checked,
          "generated XPath expression was valid");
 
       // clean up
       gBrowser.removeTab(tab2);
       gBrowser.removeTab(tab);
       finish();
-    }, true);
-  }, true);
+    });
+  });
 }
--- a/browser/components/sessionstore/test/browser_485563.js
+++ b/browser/components/sessionstore/test/browser_485563.js
@@ -5,23 +5,22 @@
 function test() {
   /** Test for Bug 485563 **/
 
   waitForExplicitFinish();
 
   let uniqueValue = Math.random() + "\u2028Second line\u2029Second paragraph\u2027";
 
   let tab = gBrowser.addTab();
-  tab.linkedBrowser.addEventListener("load", function(aEvent) {
-    tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+  whenBrowserLoaded(tab.linkedBrowser, function() {
     ss.setTabValue(tab, "bug485563", uniqueValue);
     let tabState = JSON.parse(ss.getTabState(tab));
     is(tabState.extData["bug485563"], uniqueValue,
        "unicode line separator wasn't over-encoded");
     ss.deleteTabValue(tab, "bug485563");
     ss.setTabState(tab, JSON.stringify(tabState));
     is(ss.getTabValue(tab, "bug485563"), uniqueValue,
        "unicode line separator was correctly preserved");
 
     gBrowser.removeTab(tab);
     finish();
-  }, true);
+  });
 }
--- a/browser/components/sessionstore/test/browser_491168.js
+++ b/browser/components/sessionstore/test/browser_491168.js
@@ -9,45 +9,34 @@ function test() {
 
   const REFERRER1 = "http://example.org/?" + Date.now();
   const REFERRER2 = "http://example.org/?" + Math.random();
 
   let tab = gBrowser.addTab();
   gBrowser.selectedTab = tab;
 
   let browser = tab.linkedBrowser;
-  browser.addEventListener("load", function() {
-    browser.removeEventListener("load", arguments.callee, true);
-
+  whenBrowserLoaded(browser, function() {
     let tabState = JSON.parse(ss.getTabState(tab));
     is(tabState.entries[0].referrer,  REFERRER1,
        "Referrer retrieved via getTabState matches referrer set via loadURI.");
 
     tabState.entries[0].referrer = REFERRER2;
     ss.setTabState(tab, JSON.stringify(tabState));
 
-    tab.addEventListener("SSTabRestored", function(e) {
-      tab.removeEventListener("SSTabRestored", arguments.callee, true);
+    whenTabRestored(tab, function(e) {
       is(window.content.document.referrer, REFERRER2, "document.referrer matches referrer set via setTabState.");
 
       gBrowser.removeTab(tab);
-      // Call stopPropagation on the event so we won't fire the
-      // tabbrowser's SSTabRestored listeners.
-      e.stopPropagation();
 
       let newTab = ss.undoCloseTab(window, 0);
-      newTab.addEventListener("SSTabRestored", function(e) {
-        newTab.removeEventListener("SSTabRestored", arguments.callee, true);
-
+      whenTabRestored(newTab, function() {
         is(window.content.document.referrer, REFERRER2, "document.referrer is still correct after closing and reopening the tab.");
         gBrowser.removeTab(newTab);
-        // Call stopPropagation on the event so we won't fire the
-        // tabbrowser's SSTabRestored listeners.
-        e.stopPropagation();
 
         finish();
-      }, true);
-    }, true);
-  },true);
+      });
+    });
+  });
 
   let referrerURI = Services.io.newURI(REFERRER1, null, null);
   browser.loadURI("http://example.org", referrerURI, null);
 }
--- a/browser/components/sessionstore/test/browser_500328.js
+++ b/browser/components/sessionstore/test/browser_500328.js
@@ -67,26 +67,22 @@ function test() {
   // history.replaceState().  (Bug 500328)
 
   waitForExplicitFinish();
 
   // We open a new blank window, let it load, and then load in
   // http://example.com.  We need to load the blank window first, otherwise the
   // docshell gets confused and doesn't have a current history entry.
   let tab = gBrowser.addTab("about:blank");
-  let tabBrowser = tab.linkedBrowser;
-
-  tabBrowser.addEventListener("load", function(aEvent) {
-    tabBrowser.removeEventListener("load", arguments.callee, true);
+  let browser = tab.linkedBrowser;
 
-    tabBrowser.loadURI("http://example.com", null, null);
+  whenBrowserLoaded(browser, function() {
+    browser.loadURI("http://example.com", null, null);
 
-    tabBrowser.addEventListener("load", function(aEvent) {
-      tabBrowser.removeEventListener("load", arguments.callee, true);
-
+    whenBrowserLoaded(browser, function() {
       // After these push/replaceState calls, the window should have three
       // history entries:
       //   testURL        (state object: null)          <-- oldest
       //   testURL        (state object: {obj1:1})
       //   testURL?page2  (state object: {obj3:/^a$/})  <-- newest
       let contentWindow = tab.linkedBrowser.contentWindow;
       let history = contentWindow.history;
       history.pushState({obj1:1}, "title-obj1");
@@ -97,18 +93,17 @@ function test() {
       gBrowser.removeTab(tab);
 
       // Restore the state into a new tab.  Things don't work well when we
       // restore into the old tab, but that's not a real use case anyway.
       let tab2 = gBrowser.addTab("about:blank");
       ss.setTabState(tab2, state, true);
 
       // Run checkState() once the tab finishes loading its restored state.
-      tab2.linkedBrowser.addEventListener("load", function() {
-        tab2.linkedBrowser.removeEventListener("load", arguments.callee, true);
+      whenTabRestored(tab2, function() {
         SimpleTest.executeSoon(function() {
           checkState(tab2);
         });
-      }, true);
+      });
 
-    }, true);
-  }, true);
+    });
+  });
 }
--- a/browser/components/sessionstore/test/browser_506482.js
+++ b/browser/components/sessionstore/test/browser_506482.js
@@ -42,19 +42,17 @@ function test() {
   // make sure sessionstore.js is saved ASAP on all events
   gPrefService.setIntPref(PREF_INTERVAL, 0);
 
   // get the initial sessionstore.js mtime (-1 if it doesn't exist yet)
   let mtime0 = getSessionstorejsModificationTime();
 
   // create and select a first tab
   let tab = gBrowser.addTab(TEST_URL);
-  tab.linkedBrowser.addEventListener("load", function loadListener(e) {
-    tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
-
+  whenBrowserLoaded(tab.linkedBrowser, function() {
     // step1: the above has triggered some saveStateDelayed(), sleep until
     // it's done, and get the initial sessionstore.js mtime
     setTimeout(function step1(e) {
       let mtime1 = getSessionstorejsModificationTime();
       isnot(mtime1, mtime0, "initial sessionstore.js update");
 
       // step2: test sessionstore.js is not updated on tab selection
       // or content scrolling
@@ -67,10 +65,10 @@ function test() {
 
         // ok, done, cleanup and finish
         if (gPrefService.prefHasUserValue(PREF_INTERVAL))
           gPrefService.clearUserPref(PREF_INTERVAL);
         gBrowser.removeTab(tab);
         finish();
       }, 3500); // end of sleep after tab selection and scrolling
     }, 3500); // end of sleep after initial saveStateDelayed()
-  }, true);
+  });
 }
--- a/browser/components/sessionstore/test/browser_522545.js
+++ b/browser/components/sessionstore/test/browser_522545.js
@@ -193,34 +193,30 @@ function test() {
       is(browser.userTypedValue, null, "userTypedValue is empty to start");
       is(browser.userTypedClear, 0, "userTypedClear is 0 to start");
 
       gURLBar.value = "example.org";
       let event = document.createEvent("Events");
       event.initEvent("input", true, false);
       gURLBar.dispatchEvent(event);
 
-      browser.addEventListener("load", function onLoad() {
-        browser.removeEventListener("load", onLoad, true);
-
-        executeSoon(function () {
-          is(browser.userTypedValue, "example.org",
-             "userTypedValue was set when changing gURLBar.value");
-          is(browser.userTypedClear, 0,
-             "userTypedClear was not changed when changing gURLBar.value");
+      executeSoon(function () {
+        is(browser.userTypedValue, "example.org",
+           "userTypedValue was set when changing gURLBar.value");
+        is(browser.userTypedClear, 0,
+           "userTypedClear was not changed when changing gURLBar.value");
 
-          // Now make sure ss gets these values too
-          let newState = JSON.parse(ss.getBrowserState());
-          is(newState.windows[0].tabs[0].userTypedValue, "example.org",
-             "sessionstore got correct userTypedValue");
-          is(newState.windows[0].tabs[0].userTypedClear, 0,
-             "sessionstore got correct userTypedClear");
-          runNextTest();
-        });
-      }, true);
+        // Now make sure ss gets these values too
+        let newState = JSON.parse(ss.getBrowserState());
+        is(newState.windows[0].tabs[0].userTypedValue, "example.org",
+           "sessionstore got correct userTypedValue");
+        is(newState.windows[0].tabs[0].userTypedClear, 0,
+           "sessionstore got correct userTypedClear");
+        runNextTest();
+      });
     });
   }
 
   // test_getBrowserState_lotsOfTabsOpening tested userTypedClear in a few cases,
   // but not necessarily any that had legitimate URIs in the state of loading
   // (eg, "http://example.com"), so this test will cover that case.
   function test_userTypedClearLoadURI() {
     let state = {
--- a/browser/components/sessionstore/test/browser_579868.js
+++ b/browser/components/sessionstore/test/browser_579868.js
@@ -1,21 +1,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/. */
 
 function test() {
   let tab1 = gBrowser.addTab("about:rights");
   let tab2 = gBrowser.addTab("about:mozilla");
-  tab1.linkedBrowser.addEventListener("load", mainPart, true);
+  whenBrowserLoaded(tab1.linkedBrowser, mainPart);
   waitForExplicitFinish();
 
   function mainPart() {
-    tab1.linkedBrowser.removeEventListener("load", mainPart, true);
-
     // Tell the session storer that the tab is pinned
     let newTabState = '{"entries":[{"url":"about:rights"}],"pinned":true,"userTypedValue":"Hello World!"}';
     ss.setTabState(tab1, newTabState);
 
     // Undo pinning
     gBrowser.unpinTab(tab1);
 
     // Close and restore tab
--- a/browser/components/sessionstore/test/browser_579879.js
+++ b/browser/components/sessionstore/test/browser_579879.js
@@ -1,23 +1,21 @@
 function test() {
   waitForExplicitFinish();
 
   var tab1 = gBrowser.addTab("data:text/plain;charset=utf-8,foo");
   gBrowser.pinTab(tab1);
 
-  tab1.linkedBrowser.addEventListener("load", function () {
-    tab1.linkedBrowser.removeEventListener("load", arguments.callee, true);
-
+  whenBrowserLoaded(tab1.linkedBrowser, function() {
     var tab2 = gBrowser.addTab();
     gBrowser.pinTab(tab2);
 
     is(Array.indexOf(gBrowser.tabs, tab1), 0, "pinned tab 1 is at the first position");
     gBrowser.removeTab(tab1);
     tab1 = undoCloseTab();
     ok(tab1.pinned, "pinned tab 1 has been restored as a pinned tab");
     is(Array.indexOf(gBrowser.tabs, tab1), 0, "pinned tab 1 has been restored to the first position");
 
     gBrowser.removeTab(tab1);
     gBrowser.removeTab(tab2);
     finish();
-  }, true);
+  });
 }
--- a/browser/components/sessionstore/test/browser_589246.js
+++ b/browser/components/sessionstore/test/browser_589246.js
@@ -158,19 +158,17 @@ function onStateRestored(aSubject, aTopi
   // change this window's windowtype so that closing a new window will trigger
   // browser-lastwindow-close-granted.
   document.documentElement.setAttribute("windowtype", "navigator:testrunner");
 
   let newWin = openDialog(location, "_blank", "chrome,all,dialog=no", "http://example.com");
   newWin.addEventListener("load", function(aEvent) {
     newWin.removeEventListener("load", arguments.callee, false);
 
-    newWin.gBrowser.selectedBrowser.addEventListener("load", function() {
-      newWin.gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
-
+    whenBrowserLoaded(newWin.gBrowser.selectedBrowser, function() {
       // pin this tab
       if (shouldPinTab)
         newWin.gBrowser.pinTab(newWin.gBrowser.selectedTab);
 
       newWin.addEventListener("unload", function () {
         newWin.removeEventListener("unload", arguments.callee, false);
         onWindowUnloaded();
       }, false);
@@ -192,17 +190,17 @@ function onStateRestored(aSubject, aTopi
             newWin.gBrowser.removeTab(newTab2);
           }
           newWin.BrowserTryToCloseWindow();
         }, true);
       }
       else {
         newWin.BrowserTryToCloseWindow();
       }
-    }, true);
+    });
   }, false);
 }
 
 // This will be called before the window is actually closed
 function onLastWindowClosed(aSubject, aTopic, aData) {
   info("test #" + testNum + ": onLastWindowClosed");
   Services.obs.removeObserver(onLastWindowClosed, "browser-lastwindow-close-granted");
   gotLastWindowClosedTopic = true;
--- a/browser/components/sessionstore/test/browser_662743.js
+++ b/browser/components/sessionstore/test/browser_662743.js
@@ -77,35 +77,31 @@ function test() {
 }
 
 function testTabRestoreData(aFormData, aExpectedValues, aCallback) {
   let testURL =
     getRootDirectory(gTestPath) + "browser_662743_sample.html";
   let tab = gBrowser.addTab(testURL);
   let tabState = { entries: [{ url: testURL, formdata: aFormData}] };
 
-  tab.linkedBrowser.addEventListener("load", function(aEvent) {
-    tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+  whenBrowserLoaded(tab.linkedBrowser, function() {
     ss.setTabState(tab, JSON.stringify(tabState));
 
-    tab.addEventListener("SSTabRestored", function(aEvent) {
-      tab.removeEventListener("SSTabRestored", arguments.callee);
+    whenTabRestored(tab, function() {
       let doc = tab.linkedBrowser.contentDocument;
       let select = doc.getElementById("select_id");
       let value = select.options[select.selectedIndex].value;
 
       // test select options values
       is(value, aExpectedValues[0],
         "Select Option by selectedIndex &/or value has been restored correctly");
 
       // clean up
       gBrowser.removeTab(tab);
-      // Call stopPropagation on the event so we won't fire the
-      // tabbrowser's SSTabRestored listeners.
-      aEvent.stopPropagation();
+
       aCallback();
     });
 
     tab.addEventListener("TabClose", function(aEvent) {
       tab.removeEventListener("TabClose", arguments.callee);
       let restoredTabState = JSON.parse(ss.getTabState(tab));
       let restoredFormData = restoredTabState.entries[0].formdata;
       let selectIdFormData = restoredFormData.id.select_id;
@@ -122,10 +118,10 @@ function testTabRestoreData(aFormData, a
         "select format is valid");
       // validate that there are no old keys
       is(Object.keys(selectIdFormData).length, 2,
         "select_id length is valid");
        // test set collection values
       is(value, aExpectedValues[0],
         "Collection has been saved correctly");
     });
-  }, true);
+  });
 }
--- a/browser/components/sessionstore/test/browser_819510_perwindowpb.js
+++ b/browser/components/sessionstore/test/browser_819510_perwindowpb.js
@@ -173,14 +173,11 @@ function forceWriteState(aCallback) {
   Services.prefs.setIntPref("browser.sessionstore.interval", 0);
 }
 
 function testOnWindow(aIsPrivate, aCallback) {
   whenNewWindowLoaded({private: aIsPrivate}, aCallback);
 }
 
 function waitForTabLoad(aWin, aURL, aCallback) {
-  aWin.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
-    aWin.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
-    executeSoon(aCallback);
-  }, true);
+  whenBrowserLoaded(aWin.gBrowser.selectedBrowser, aCallback);
   aWin.gBrowser.selectedBrowser.loadURI(aURL);
 }
--- a/browser/components/sessionstore/test/browser_attributes.js
+++ b/browser/components/sessionstore/test/browser_attributes.js
@@ -47,17 +47,17 @@ function runTests() {
   whenTabRestoring(tab);
   yield ss.setTabState(tab, JSON.stringify(state));
 
   ok(tab.hasAttribute("pending"), "tab is pending");
   is(gBrowser.getIcon(tab), state.attributes.image, "tab has correct icon");
 
   // Let the pending tab load.
   gBrowser.selectedTab = tab;
-  yield whenBrowserLoaded(tab.linkedBrowser);
+  yield whenTabRestored(tab);
 
   // Ensure no 'image' or 'pending' attributes are stored.
   let {attributes} = JSON.parse(ss.getTabState(tab));
   ok(!("image" in attributes), "'image' attribute not saved");
   ok(!("pending" in attributes), "'pending' attribute not saved");
   is(attributes.custom, "foobaz", "'custom' attribute is correct");
 
   // Clean up.
--- a/browser/components/sessionstore/test/browser_form_restore_events.js
+++ b/browser/components/sessionstore/test/browser_form_restore_events.js
@@ -9,18 +9,17 @@ function test() {
 
   let file = Components.classes["@mozilla.org/file/directory_service;1"]
              .getService(Components.interfaces.nsIProperties)
              .get("TmpD", Components.interfaces.nsIFile);
 
   let testURL = "http://mochi.test:8888/browser/" +
     "browser/components/sessionstore/test/browser_form_restore_events_sample.html";
   let tab = gBrowser.addTab(testURL);
-  tab.linkedBrowser.addEventListener("load", function(aEvent) {
-    tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+  whenBrowserLoaded(tab.linkedBrowser, function() {
     let doc = tab.linkedBrowser.contentDocument;
 
     // text fields
     doc.getElementById("modify01").value += Math.random();
     doc.getElementById("modify02").value += " " + Date.now();
 
     // textareas
     doc.getElementById("modify03").value += Math.random();
@@ -40,28 +39,27 @@ function test() {
 
     // radio
     // select one then another in the same group - only last one should get event on restore
     doc.getElementById("modify10").checked = true;
     doc.getElementById("modify11").checked = true;
 
 
     let tab2 = gBrowser.duplicateTab(tab);
-    tab2.linkedBrowser.addEventListener("load", function(aEvent) {
-      tab2.linkedBrowser.removeEventListener("load", arguments.callee, true);
+    whenTabRestored(tab2, function() {
       let doc = tab2.linkedBrowser.contentDocument;
       let inputFired = doc.getElementById("inputFired").textContent.trim().split();
       let changeFired = doc.getElementById("changeFired").textContent.trim().split();
 
       is(inputFired.sort().join(" "), "modify01 modify02 modify03 modify04 modify05",
          "input events were only dispatched for modified input, textarea fields");
 
       is(changeFired.sort().join(" "), "modify06 modify07 modify08 modify09 modify11",
          "change events were only dispatched for modified select, checkbox, radio fields");
 
       // clean up
       gBrowser.removeTab(tab2);
       gBrowser.removeTab(tab);
 
       finish();
-    }, true);
-  }, true);
+    });
+  });
 }
--- a/browser/components/sessionstore/test/browser_tabStateCache.js
+++ b/browser/components/sessionstore/test/browser_tabStateCache.js
@@ -132,10 +132,8 @@ add_task(function browsing() {
     });
     is(delta.hits, numberOfUntrackedTabs + 2, PREFIX + " has all hits, thanks to prefetching");
     is(delta.misses, 0, PREFIX + " has no miss, thanks to prefetching");
     is(delta.clears, 0, PREFIX + " has no clear");
   }
   gBrowser.removeTab(tab2);
   gBrowser.removeTab(tab1);
 });
-
-
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -306,16 +306,25 @@ function whenWindowLoaded(aWindow, aCall
   aWindow.addEventListener("load", function windowLoadListener() {
     aWindow.removeEventListener("load", windowLoadListener, false);
     executeSoon(function executeWhenWindowLoaded() {
       aCallback(aWindow);
     });
   }, false);
 }
 
+function whenTabRestored(aTab, aCallback = next) {
+  aTab.addEventListener("SSTabRestored", function onRestored(aEvent) {
+    aTab.removeEventListener("SSTabRestored", onRestored, true);
+    executeSoon(function executeWhenTabRestored() {
+      aCallback();
+    });
+  }, true);
+}
+
 var gUniqueCounter = 0;
 function r() {
   return Date.now() + "-" + (++gUniqueCounter);
 }
 
 function BrowserWindowIterator() {
   let windowsEnum = Services.wm.getEnumerator("navigator:browser");
   while (windowsEnum.hasMoreElements()) {
@@ -344,37 +353,37 @@ let gProgressListener = {
       window.gBrowser.removeTabsProgressListener(this);
     }
   },
 
   onStateChange:
   function gProgressListener_onStateChange(aBrowser, aWebProgress, aRequest,
                                            aStateFlags, aStatus) {
     if ((!this._checkRestoreState ||
-         aBrowser.__SS_restoreState == TAB_STATE_RESTORING) &&
+         (aBrowser.__SS_restoreState && aBrowser.__SS_restoreState == TAB_STATE_RESTORING)) &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
       let args = [aBrowser].concat(this._countTabs());
       this._callback.apply(this, args);
     }
   },
 
   _countTabs: function gProgressListener_countTabs() {
     let needsRestore = 0, isRestoring = 0, wasRestored = 0;
 
     for (let win in BrowserWindowIterator()) {
       for (let i = 0; i < win.gBrowser.tabs.length; i++) {
         let browser = win.gBrowser.tabs[i].linkedBrowser;
-        if (browser.__SS_restoreState == TAB_STATE_RESTORING)
+        if (!browser.__SS_restoreState)
+          wasRestored++;
+        else if (browser.__SS_restoreState == TAB_STATE_RESTORING)
           isRestoring++;
         else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
           needsRestore++;
-        else
-          wasRestored++;
       }
     }
     return [needsRestore, isRestoring, wasRestored];
   }
 };
 
 registerCleanupFunction(function () {
   gProgressListener.unsetCallback();
--- a/caps/include/nsNullPrincipal.h
+++ b/caps/include/nsNullPrincipal.h
@@ -11,16 +11,17 @@
 
 #ifndef nsNullPrincipal_h__
 #define nsNullPrincipal_h__
 
 #include "nsIPrincipal.h"
 #include "nsJSPrincipals.h"
 #include "nsCOMPtr.h"
 #include "nsPrincipal.h"
+#include "nsIContentSecurityPolicy.h"
 
 class nsIURI;
 
 #define NS_NULLPRINCIPAL_CID \
 { 0xdd156d62, 0xd26f, 0x4441, \
  { 0x9c, 0xdb, 0xe8, 0xf0, 0x91, 0x07, 0xc2, 0x73 } }
 #define NS_NULLPRINCIPAL_CONTRACTID "@mozilla.org/nullprincipal;1"
 
@@ -48,11 +49,12 @@ public:
 #ifdef DEBUG
   virtual void dumpImpl() MOZ_OVERRIDE;
 #endif 
 
  protected:
   virtual ~nsNullPrincipal();
 
   nsCOMPtr<nsIURI> mURI;
+  nsCOMPtr<nsIContentSecurityPolicy> mCSP;
 };
 
 #endif // nsNullPrincipal_h__
--- a/caps/src/nsNullPrincipal.cpp
+++ b/caps/src/nsNullPrincipal.cpp
@@ -144,49 +144,59 @@ nsNullPrincipal::GetHashValue(uint32_t *
 {
   *aResult = (NS_PTR_TO_INT32(this) >> 2);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNullPrincipal::GetSecurityPolicy(void** aSecurityPolicy)
 {
-  // We don't actually do security policy caching.  And it's not like anyone
-  // can set a security policy for us anyway.
+  // Leftover from old security model, a "security policy" is a set of
+  // rules for property access that can override the SOP. Policies are
+  // associated with origins and since nsNullPinricipals never get the
+  // same origin twice, it's not possible to specify a "security
+  // policy" for it.  Hence, we do not cache the security policy.
   *aSecurityPolicy = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNullPrincipal::SetSecurityPolicy(void* aSecurityPolicy)
 {
-  // We don't actually do security policy caching.  And it's not like anyone
-  // can set a security policy for us anyway.
+  // Leftover from old security model, a "security policy" is a set of
+  // rules for property access that can override the SOP. Policies are
+  // associated with origins and since nsNullPinricipals never get the
+  // same origin twice, it's not possible to specify a "security
+  // policy" for it.  Hence, we do not cache the security policy.
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 nsNullPrincipal::GetURI(nsIURI** aURI)
 {
   return NS_EnsureSafeToReturn(mURI, aURI);
 }
 
 NS_IMETHODIMP
 nsNullPrincipal::GetCsp(nsIContentSecurityPolicy** aCsp)
 {
-  // CSP on a null principal makes no sense
-  *aCsp = nullptr;
+  NS_IF_ADDREF(*aCsp = mCSP);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNullPrincipal::SetCsp(nsIContentSecurityPolicy* aCsp)
 {
-  // CSP on a null principal makes no sense
-  return NS_ERROR_NOT_AVAILABLE;
+  // If CSP was already set, it should not be destroyed!  Instead, it should
+  // get set anew when a new principal is created.
+  if (mCSP)
+    return NS_ERROR_ALREADY_INITIALIZED;
+
+  mCSP = aCsp;
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNullPrincipal::GetDomain(nsIURI** aDomain)
 {
   return NS_EnsureSafeToReturn(mURI, aDomain);
 }
 
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -126,17 +126,17 @@ ifdef COMPILE_ENVIRONMENT
 
 # Compile the tests to $(DIST)/bin.  Make lots of niceties available by default
 # through TestHarness.h, by modifying the list of includes and the libs against
 # which stuff links.
 CPPSRCS += $(CPP_UNIT_TESTS)
 CPP_UNIT_TEST_BINS := $(CPP_UNIT_TESTS:.cpp=$(BIN_SUFFIX))
 SIMPLE_PROGRAMS += $(CPP_UNIT_TEST_BINS)
 INCLUDES += -I$(DIST)/include/testing
-LIBS += $(XPCOM_GLUE_LDOPTS) $(NSPR_LIBS) $(MOZ_JS_LIBS) $(if $(JS_SHARED_LIBRARY),,$(MOZ_ZLIB_LIBS))
+LIBS += $(XPCOM_GLUE_LDOPTS) $(NSPR_LIBS) $(if $(JS_SHARED_LIBRARY),,$(MOZ_ZLIB_LIBS))
 
 ifndef MOZ_PROFILE_GENERATE
 libs:: $(CPP_UNIT_TEST_BINS) $(call mkdir_deps,$(DIST)/cppunittests)
 	$(NSINSTALL) $(CPP_UNIT_TEST_BINS) $(DIST)/cppunittests
 endif
 
 check::
 	@$(PYTHON) $(topsrcdir)/testing/runcppunittests.py --xre-path=$(DIST)/bin --symbols-path=$(DIST)/crashreporter-symbols $(subst .cpp,$(BIN_SUFFIX),$(CPP_UNIT_TESTS))
@@ -1704,8 +1704,12 @@ default all:: $(PURGECACHES_FILES)
 
 #############################################################################
 # Derived targets and dependencies
 
 include $(topsrcdir)/config/makefiles/autotargets.mk
 ifneq ($(NULL),$(AUTO_DEPS))
   default all libs tools export:: $(AUTO_DEPS)
 endif
+
+export:: $(GENERATED_FILES)
+
+GARBAGE += $(GENERATED_FILES)
--- a/content/base/src/contentSecurityPolicy.js
+++ b/content/base/src/contentSecurityPolicy.js
@@ -46,19 +46,19 @@ Cu.import("resource://gre/modules/CSPUti
 function ContentSecurityPolicy() {
   CSPdebug("CSP CREATED");
   this._isInitialized = false;
 
   this._policies = [];
 
   this._request = "";
   this._requestOrigin = "";
-  this._requestPrincipal = "";
+  this._weakRequestPrincipal =  { get : function() { return null; } };
   this._referrer = "";
-  this._docRequest = null;
+  this._weakDocRequest = { get : function() { return null; } };
   CSPdebug("CSP object initialized, no policies to enforce yet");
 
   this._cache = { };
 }
 
 /*
  * Set up mappings from nsIContentPolicy content types to CSP directives.
  */
@@ -291,29 +291,30 @@ ContentSecurityPolicy.prototype = {
    * Given an nsIHttpChannel, fill out the appropriate data.
    */
   scanRequestData:
   function(aChannel) {
     if (!aChannel)
       return;
 
     // Save the docRequest for fetching a policy-uri
-    this._docRequest = aChannel;
+    this._weakDocRequest = Cu.getWeakReference(aChannel);
 
     // save the document URI (minus <fragment>) and referrer for reporting
     let uri = aChannel.URI.cloneIgnoringRef();
     try { // GetUserPass throws for some protocols without userPass
       uri.userPass = '';
     } catch (ex) {}
     this._request = uri.asciiSpec;
     this._requestOrigin = uri;
 
     //store a reference to the principal, that can later be used in shouldLoad
-    this._requestPrincipal = Components.classes["@mozilla.org/scriptsecuritymanager;1"].
-    getService(Components.interfaces.nsIScriptSecurityManager).getChannelPrincipal(aChannel);
+    this._weakRequestPrincipal = Cu.getWeakReference(Cc["@mozilla.org/scriptsecuritymanager;1"]
+                                                       .getService(Ci.nsIScriptSecurityManager)
+                                                       .getChannelPrincipal(aChannel));
 
     if (aChannel.referrer) {
       let referrer = aChannel.referrer.cloneIgnoringRef();
       try { // GetUserPass throws for some protocols without userPass
         referrer.userPass = '';
       } catch (ex) {}
       this._referrer = referrer.asciiSpec;
     }
@@ -353,23 +354,23 @@ ContentSecurityPolicy.prototype = {
 
     // If we want to be CSP 1.0 spec compliant, use the new parser.
     // The old one will be deprecated in the future and will be
     // removed at that time.
     if (aSpecCompliant) {
       newpolicy = CSPRep.fromStringSpecCompliant(aPolicy,
                                                  selfURI,
                                                  aReportOnly,
-                                                 this._docRequest,
+                                                 this._weakDocRequest.get(),
                                                  this);
     } else {
       newpolicy = CSPRep.fromString(aPolicy,
                                     selfURI,
                                     aReportOnly,
-                                    this._docRequest,
+                                    this._weakDocRequest.get(),
                                     this);
     }
 
     newpolicy._specCompliant = !!aSpecCompliant;
     newpolicy._isInitialized = true;
     this._policies.push(newpolicy);
     this._cache = {}; // reset cache since effective policy changes
   },
@@ -476,18 +477,18 @@ ContentSecurityPolicy.prototype = {
 
           // make sure this is an anonymous request (no cookies) so in case the
           // policy URI is injected, it can't be abused for CSRF.
           chan.loadFlags |= Ci.nsIChannel.LOAD_ANONYMOUS;
 
           // we need to set an nsIChannelEventSink on the channel object
           // so we can tell it to not follow redirects when posting the reports
           chan.notificationCallbacks = new CSPReportRedirectSink(policy);
-          if (this._docRequest) {
-            chan.loadGroup = this._docRequest.loadGroup;
+          if (this._weakDocRequest.get()) {
+            chan.loadGroup = this._weakDocRequest.get().loadGroup;
           }
 
           chan.QueryInterface(Ci.nsIUploadChannel)
               .setUploadStream(content, "application/json", content.available());
 
           try {
             // if this is an HTTP channel, set the request method to post
             chan.QueryInterface(Ci.nsIHttpChannel);
@@ -496,17 +497,17 @@ ContentSecurityPolicy.prototype = {
 
           // check with the content policy service to see if we're allowed to
           // send this request.
           try {
             var contentPolicy = Cc["@mozilla.org/layout/content-policy;1"]
                                   .getService(Ci.nsIContentPolicy);
             if (contentPolicy.shouldLoad(Ci.nsIContentPolicy.TYPE_CSP_REPORT,
                                          chan.URI, this._requestOrigin,
-                                         null, null, null, this._requestPrincipal)
+                                         null, null, null, this._weakRequestPrincipal.get())
                 != Ci.nsIContentPolicy.ACCEPT) {
               continue; // skip unauthorized URIs
             }
           } catch(e) {
             continue; // refuse to load if we can't do a security check.
           }
 
           //send data (and set up error notifications)
--- a/content/base/src/nsCCUncollectableMarker.cpp
+++ b/content/base/src/nsCCUncollectableMarker.cpp
@@ -25,16 +25,17 @@
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsJSEnvironment.h"
 #include "nsInProcessTabChildGlobal.h"
 #include "nsFrameLoader.h"
 #include "mozilla/dom/Element.h"
 #include "xpcpublic.h"
 #include "nsObserverService.h"
+#include "nsFocusManager.h"
 
 using namespace mozilla::dom;
 
 static bool sInited = 0;
 uint32_t nsCCUncollectableMarker::sGeneration = 0;
 #ifdef MOZ_XUL
 #include "nsXULPrototypeCache.h"
 #endif
@@ -205,16 +206,26 @@ MarkContentViewer(nsIContentViewer* aVie
         EnumerateAll(MarkUserDataHandler, &nsCCUncollectableMarker::sGeneration);
     } else if (aPrepareForCC) {
       // Unfortunately we need to still mark user data just before running CC so
       // that it has the right generation. 
       doc->PropertyTable(DOM_USER_DATA)->
         EnumerateAll(MarkUserData, &nsCCUncollectableMarker::sGeneration);
     }
   }
+  if (doc) {
+    nsPIDOMWindow* inner = doc->GetInnerWindow();
+    if (inner) {
+      inner->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
+    }
+    nsPIDOMWindow* outer = doc->GetWindow();
+    if (outer) {
+      outer->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
+    }
+  }
 }
 
 void MarkDocShell(nsIDocShellTreeNode* aNode, bool aCleanupJS,
                   bool aPrepareForCC);
 
 void
 MarkSHEntry(nsISHEntry* aSHEntry, bool aCleanupJS, bool aPrepareForCC)
 {
@@ -331,16 +342,18 @@ nsCCUncollectableMarker::Observe(nsISupp
     Element::ClearContentUnbinder();
   }
 
   // Increase generation to effectively unmark all current objects
   if (!++sGeneration) {
     ++sGeneration;
   }
 
+  nsFocusManager::MarkUncollectableForCCGeneration(sGeneration);
+
   nsresult rv;
 
   // Iterate all toplevel windows
   nsCOMPtr<nsISimpleEnumerator> windowList;
   nsCOMPtr<nsIWindowMediator> med =
     do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
   if (med) {
     rv = med->GetEnumerator(nullptr, getter_AddRefs(windowList));
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -2458,18 +2458,21 @@ nsDocument::StartDocumentLoad(const char
   // to the document. These are immutable after being set here.
   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
 
   if (docShell) {
     nsresult rv = docShell->GetSandboxFlags(&mSandboxFlags);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  nsresult rv = InitCSP(aChannel);
-  NS_ENSURE_SUCCESS(rv, rv);
+  // If this is not a data document, set CSP.
+  if (!mLoadedAsData) {
+    nsresult rv = InitCSP(aChannel);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
 
   return NS_OK;
 }
 
 void
 CSPErrorQueue::Add(const char* aMessageName)
 {
   mErrors.AppendElement(aMessageName);
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_CSP_bug941404.html
@@ -0,0 +1,27 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+  <body>
+
+    <!-- this should be allowed (no CSP)-->
+    <img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img_good&type=img/png"> </img>
+
+
+    <script type="text/javascript">
+      var req = new XMLHttpRequest();
+      req.onload = function() {
+        //this should be allowed (no CSP)
+        try {
+        var img = document.createElement("img");
+        img.src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img2_good&type=img/png";
+        document.body.appendChild(img);
+        } catch(e) {
+          console.log("yo: "+e);
+        }
+      };
+      req.open("get", "file_CSP_bug941404_xhr.html", true);
+      req.responseType = "document";
+      req.send();
+    </script>
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_CSP_bug941404_xhr.html
@@ -0,0 +1,5 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+  <body>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_CSP_bug941404_xhr.html^headers^
@@ -0,0 +1,1 @@
+Content-Security-Policy: default-src 'none' 'unsafe-inline' 'unsafe-eval'
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_bug886164.html
@@ -0,0 +1,15 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+  <body>
+    <!-- sandbox="allow-same-origin" -->
+    <!-- Content-Security-Policy: default-src 'self' -->
+
+    <!-- these should be stopped by CSP -->
+    <img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img_bad&type=img/png"> </img>
+
+    <!-- these should load ok -->
+    <img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img_good&type=img/png" />
+    <script src='/tests/content/base/test/csp/file_CSP.sjs?testid=scripta_bad&type=text/javascript'></script>
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_bug886164.html^headers^
@@ -0,0 +1,1 @@
+Content-Security-Policy: default-src 'self'
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_bug886164_2.html
@@ -0,0 +1,14 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+  <body>
+    <!-- sandbox -->
+    <!-- Content-Security-Policy: default-src 'self' -->
+
+    <!-- these should be stopped by CSP -->
+    <img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img2_bad&type=img/png"> </img>
+
+    <!-- these should load ok -->
+    <img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img2a_good&type=img/png" />
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_bug886164_2.html^headers^
@@ -0,0 +1,1 @@
+Content-Security-Policy: default-src 'self'
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_bug886164_3.html
@@ -0,0 +1,12 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+  <body>
+    <!-- sandbox -->
+    <!-- Content-Security-Policy: default-src 'none' -->
+
+    <!-- these should be stopped by CSP -->
+    <img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img3_bad&type=img/png"> </img>
+    <img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img3a_bad&type=img/png" />
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_bug886164_3.html^headers^
@@ -0,0 +1,1 @@
+Content-Security-Policy: default-src 'none'
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_bug886164_4.html
@@ -0,0 +1,12 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+  <body>
+    <!-- sandbox -->
+    <!-- Content-Security-Policy: default-src 'none' -->
+
+    <!-- these should be stopped by CSP -->
+    <img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img4_bad&type=img/png"> </img>
+    <img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img4a_bad&type=img/png" />
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_bug886164_4.html^headers^
@@ -0,0 +1,1 @@
+Content-Security-Policy: default-src 'none'
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_bug886164_5.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head> <meta charset="utf-8"> </head>
+<script type="text/javascript">
+  function ok(result, desc) {
+    window.parent.postMessage({ok: result, desc: desc}, "*");
+  }
+
+  function doStuff() {
+    ok(true, "documents sandboxed with allow-scripts should be able to run inline scripts");
+  }
+</script>
+<script src='file_iframe_sandbox_pass.js'></script>
+<body onLoad='ok(true, "documents sandboxed with allow-scripts should be able to run script from event listeners");doStuff();'>
+  I am sandboxed but with only inline "allow-scripts"
+
+ <!-- sandbox="allow-scripts" -->
+ <!-- Content-Security-Policy: default-src 'none' 'unsafe-inline'-->
+
+ <!-- these should be stopped by CSP -->
+ <img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img5_bad&type=img/png" />
+ <img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img5a_bad&type=img/png"> </img>
+ <script src='/tests/content/base/test/csp/file_CSP.sjs?testid=script5_bad&type=text/javascript'></script>
+ <script src='http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=script5a_bad&type=text/javascript'></script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_bug886164_5.html^headers^
@@ -0,0 +1,1 @@
+Content-Security-Policy: default-src 'none' 'unsafe-inline';
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_bug886164_6.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<script type="text/javascript">
+  function ok(result, desc) {
+    window.parent.postMessage({ok: result, desc: desc}, "*");
+  }
+
+  function doStuff() {
+    ok(true, "documents sandboxed with allow-scripts should be able to run inline scripts");
+
+    document.getElementById('a_form').submit();
+
+    // trigger the javascript: url test
+    sendMouseEvent({type:'click'}, 'a_link');
+  }
+</script>
+<script src='file_iframe_sandbox_pass.js'></script>
+<body onLoad='ok(true, "documents sandboxed with allow-scripts should be able to run script from event listeners");doStuff();'>
+  I am sandboxed but with "allow-scripts"
+  <img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img6_bad&type=img/png"> </img>
+  <script src='http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=script6_bad&type=text/javascript'></script>
+
+  <form method="get" action="file_iframe_sandbox_form_fail.html" id="a_form">
+    First name: <input type="text" name="firstname">
+    Last name: <input type="text" name="lastname">
+    <input type="submit" onclick="doSubmit()" id="a_button">
+  </form>
+
+  <a href = 'javascript:ok(true, "documents sandboxed with allow-scripts should be able to run script from javascript: URLs");' id='a_link'>click me</a>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/file_bug886164_6.html^headers^
@@ -0,0 +1,1 @@
+Content-Security-Policy: default-src 'self' 'unsafe-inline';
--- a/content/base/test/csp/mochitest.ini
+++ b/content/base/test/csp/mochitest.ini
@@ -63,16 +63,28 @@ support-files =
   file_CSP_main_spec_compliant.html^headers^
   file_CSP_main_spec_compliant.js
   file_bothCSPheaders.html
   file_bothCSPheaders.html^headers^
   file_bug836922_npolicies.html
   file_bug836922_npolicies.html^headers^
   file_bug836922_npolicies_ro_violation.sjs
   file_bug836922_npolicies_violation.sjs
+  file_bug886164.html
+  file_bug886164.html^headers^
+  file_bug886164_2.html
+  file_bug886164_2.html^headers^
+  file_bug886164_3.html
+  file_bug886164_3.html^headers^
+  file_bug886164_4.html
+  file_bug886164_4.html^headers^
+  file_bug886164_5.html
+  file_bug886164_5.html^headers^
+  file_bug886164_6.html
+  file_bug886164_6.html^headers^
   file_csp_bug768029.html
   file_csp_bug768029.sjs
   file_csp_bug773891.html
   file_csp_bug773891.sjs
   file_csp_redirects_main.html
   file_csp_redirects_page.sjs
   file_csp_redirects_resource.sjs
   file_CSP_bug910139.sjs
@@ -82,27 +94,32 @@ support-files =
   file_CSP_bug909029_star.html^headers^
   file_CSP_bug909029_none.html
   file_CSP_bug909029_none.html^headers^
   file_policyuri_regression_from_multipolicy.html
   file_policyuri_regression_from_multipolicy.html^headers^
   file_policyuri_regression_from_multipolicy_policy
   file_nonce_source.html
   file_nonce_source.html^headers^
+  file_CSP_bug941404.html
+  file_CSP_bug941404_xhr.html
+  file_CSP_bug941404_xhr.html^headers^
 
 [test_CSP.html]
 [test_CSP_bug663567.html]
 [test_CSP_bug802872.html]
 [test_CSP_bug885433.html]
 [test_CSP_bug888172.html]
 [test_CSP_bug916446.html]
 [test_CSP_evalscript.html]
 [test_CSP_evalscript_getCRMFRequest.html]
 [test_CSP_frameancestors.html]
 [test_CSP_inlinescript.html]
 [test_CSP_inlinestyle.html]
 [test_bothCSPheaders.html]
 [test_bug836922_npolicies.html]
+[test_bug886164.html]
 [test_csp_redirects.html]
 [test_CSP_bug910139.html]
 [test_CSP_bug909029.html]
 [test_policyuri_regression_from_multipolicy.html]
 [test_nonce_source.html]
+[test_CSP_bug941404.html]
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/test_CSP_bug941404.html
@@ -0,0 +1,115 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Bug 941404 - Data documents should not set CSP</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+
+</div>
+
+<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
+<script class="testbody" type="text/javascript">
+
+
+var path = "/tests/content/base/test/csp/";
+
+// These are test results: -1 means it hasn't run,
+// true/false is the pass/fail result.
+window.tests = {
+  img_good: -1,
+  img2_good: -1,
+};
+
+
+//csp related
+
+// This is used to watch the blocked data bounce off CSP and allowed data
+// get sent out to the wire.
+function examiner() {
+  SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+  SpecialPowers.addObserver(this, "http-on-modify-request", false);
+}
+
+examiner.prototype  = {
+  observe: function(subject, topic, data) {
+    // subject should be an nsURI, and should be either allowed or blocked.
+    if (!SpecialPowers.can_QI(subject))
+      return;
+
+    var testpat = new RegExp("testid=([a-z0-9_]+)");
+
+    //_good things better be allowed!
+    //_bad things better be stopped!
+
+    if (topic === "http-on-modify-request") {
+      //these things were allowed by CSP
+      var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"), "URI.asciiSpec");
+      if (!testpat.test(asciiSpec)) return;
+      var testid = testpat.exec(asciiSpec)[1];
+
+      window.testResult(testid,
+                        /_good/.test(testid),
+                        asciiSpec + " allowed by csp");
+    }
+
+    if(topic === "csp-on-violate-policy") {
+      //these were blocked... record that they were blocked
+      var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
+      if (!testpat.test(asciiSpec)) return;
+      var testid = testpat.exec(asciiSpec)[1];
+      window.testResult(testid,
+                        /_bad/.test(testid),
+                        asciiSpec + " blocked by \"" + data + "\"");
+    }
+  },
+
+  // must eventually call this to remove the listener,
+  // or mochitests might get borked.
+  remove: function() {
+    SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+    SpecialPowers.removeObserver(this, "http-on-modify-request");
+  }
+}
+
+window.examiner = new examiner();
+
+window.testResult = function(testname, result, msg) {
+  //test already complete.... forget it... remember the first result.
+  if (window.tests[testname] != -1)
+    return;
+
+  window.tests[testname] = result;
+  is(result, true, testname + ' test: ' + msg);
+
+  // if any test is incomplete, keep waiting
+  for (var v in window.tests)
+    if(tests[v] == -1) {
+      console.log(v + " is not complete");
+      return;
+    }
+
+  // ... otherwise, finish
+  window.examiner.remove();
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv(
+  {'set':[["security.csp.speccompliant", true]]},
+  function() {
+    // save this for last so that our listeners are registered.
+    // ... this loads the testbed of good and bad requests.
+    document.getElementById('cspframe').src = 'file_CSP_bug941404.html';
+  });
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/csp/test_bug886164.html
@@ -0,0 +1,183 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Bug 886164 - Enforce CSP in sandboxed iframe</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+
+</div>
+
+<iframe style="width:200px;height:200px;" id='cspframe'  sandbox="allow-same-origin"></iframe>
+<iframe style="width:200px;height:200px;" id='cspframe2' sandbox></iframe>
+<iframe style="width:200px;height:200px;" id='cspframe3' sandbox="allow-same-origin"></iframe>
+<iframe style="width:200px;height:200px;" id='cspframe4' sandbox></iframe>
+<iframe style="width:200px;height:200px;" id='cspframe5' sandbox="allow-scripts"></iframe>
+<iframe style="width:200px;height:200px;" id='cspframe6' sandbox="allow-same-origin allow-scripts"></iframe>
+<script class="testbody" type="text/javascript">
+
+
+var path = "/tests/content/base/test/csp/";
+
+// These are test results: -1 means it hasn't run,
+// true/false is the pass/fail result.
+window.tests = {
+  // sandbox allow-same-origin; 'self'
+  img_good: -1, // same origin
+  img_bad: -1, //example.com
+
+  // sandbox; 'self'
+  img2_bad: -1, //example.com
+  img2a_good: -1, // same origin & is image
+
+  // sandbox allow-same-origin; 'none'
+  img3_bad: -1,
+  img3a_bad: -1,
+
+  // sandbox; 'none'
+  img4_bad: -1,
+  img4a_bad: -1,
+
+  // sandbox allow-scripts; 'none' 'unsafe-inline'
+  img5_bad: -1,
+  img5a_bad: -1,
+  script5_bad: -1,
+  script5a_bad: -1,
+
+  // sandbox allow-same-origin allow-scripts; 'self' 'unsafe-inline'
+  img6_bad: -1,
+  script6_bad: -1,
+};
+
+// a postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin' to communicate pass/fail back to this main page.
+// it expects to be called with an object like {ok: true/false, desc:
+// <description of the test> which it then forwards to ok()
+window.addEventListener("message", receiveMessage, false);
+
+function receiveMessage(event)
+{
+  ok_wrapper(event.data.ok, event.data.desc);
+}
+
+var cspTestsDone = false;
+var iframeSandboxTestsDone = false;
+
+// iframe related
+var completedTests = 0;
+var passedTests = 0;
+
+function ok_wrapper(result, desc) {
+  ok(result, desc);
+
+  completedTests++;
+
+  if (result) {
+    passedTests++;
+  }
+
+  if (completedTests === 5) {
+    iframeSandboxTestsDone = true;
+    if (cspTestsDone) {
+      SimpleTest.finish();
+    }
+  }
+}
+
+
+//csp related
+
+// This is used to watch the blocked data bounce off CSP and allowed data
+// get sent out to the wire.
+function examiner() {
+  SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+  SpecialPowers.addObserver(this, "http-on-modify-request", false);
+}
+examiner.prototype  = {
+  observe: function(subject, topic, data) {
+    // subject should be an nsURI, and should be either allowed or blocked.
+    if (!SpecialPowers.can_QI(subject))
+      return;
+
+    var testpat = new RegExp("testid=([a-z0-9_]+)");
+
+    //_good things better be allowed!
+    //_bad things better be stopped!
+
+    if (topic === "http-on-modify-request") {
+      //these things were allowed by CSP
+      var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"), "URI.asciiSpec");
+      if (!testpat.test(asciiSpec)) return;
+      var testid = testpat.exec(asciiSpec)[1];
+
+      window.testResult(testid,
+                        /_good/.test(testid),
+                        asciiSpec + " allowed by csp");
+    }
+
+    if(topic === "csp-on-violate-policy") {
+      //these were blocked... record that they were blocked
+      var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
+      if (!testpat.test(asciiSpec)) return;
+      var testid = testpat.exec(asciiSpec)[1];
+      window.testResult(testid,
+                        /_bad/.test(testid),
+                        asciiSpec + " blocked by \"" + data + "\"");
+    }
+  },
+
+  // must eventually call this to remove the listener,
+  // or mochitests might get borked.
+  remove: function() {
+    SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+    SpecialPowers.removeObserver(this, "http-on-modify-request");
+  }
+}
+
+window.examiner = new examiner();
+
+window.testResult = function(testname, result, msg) {
+  //test already complete.... forget it... remember the first result.
+  if (window.tests[testname] != -1)
+    return;
+
+  window.tests[testname] = result;
+  ok(result, testname + ' test: ' + msg);
+
+  // if any test is incomplete, keep waiting
+  for (var v in window.tests)
+    if(tests[v] == -1)
+      return;
+
+  // ... otherwise, finish
+  window.examiner.remove();
+  cspTestsDone = true;
+  if (iframeSandboxTestsDone) {
+    SimpleTest.finish();
+  }
+}
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv(
+  {'set':[["security.csp.speccompliant", true]]},
+  function() {
+    // save this for last so that our listeners are registered.
+    // ... this loads the testbed of good and bad requests.
+    document.getElementById('cspframe').src = 'file_bug886164.html';
+    document.getElementById('cspframe2').src = 'file_bug886164_2.html';
+    document.getElementById('cspframe3').src = 'file_bug886164_3.html';
+    document.getElementById('cspframe4').src = 'file_bug886164_4.html';
+    document.getElementById('cspframe5').src = 'file_bug886164_5.html';
+    document.getElementById('cspframe6').src = 'file_bug886164_6.html';
+  });
+
+</script>
+</pre>
+</body>
+</html>
--- a/content/base/test/csp/test_policyuri_regression_from_multipolicy.html
+++ b/content/base/test/csp/test_policyuri_regression_from_multipolicy.html
@@ -7,20 +7,21 @@
    enforced due to regressions introduced by Bug 836922.
    -->
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <iframe style="width:200px;height:200px;" id='testframe'></iframe>
 <script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+
 SpecialPowers.pushPrefEnv(
   {'set':[["security.csp.speccompliant", true]]},
-  function() {
-    SimpleTest.waitForExplicitFinish();
+  function () {
     var testframe = document.getElementById('testframe');
     testframe.src = 'file_policyuri_regression_from_multipolicy.html';
     testframe.addEventListener('load', function checkInlineScriptExecuted () {
       is(this.contentDocument.getElementById('testdiv').innerHTML,
          'Inline Script Executed',
          'Inline script should execute (it would be blocked by the policy, but the policy is report-only)');
       SimpleTest.finish();
     });
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -1168,16 +1168,20 @@ WebGLContext::ForceClearFramebufferWithD
     }
 }
 
 // For an overview of how WebGL compositing works, see:
 // https://wiki.mozilla.org/Platform/GFX/WebGL/Compositing
 bool
 WebGLContext::PresentScreenBuffer()
 {
+    if (IsContextLost()) {
+        return false;
+    }
+
     if (!mShouldPresent) {
         return false;
     }
 
     gl->MakeCurrent();
     if (!gl->PublishFrame()) {
         this->ForceLoseContext();
         return false;
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -366,17 +366,17 @@ public:
     void GetShaderInfoLog(WebGLShader *shader, nsAString& retval);
     void GetShaderSource(WebGLShader *shader, nsAString& retval);
     JS::Value GetTexParameter(GLenum target, GLenum pname);
     JS::Value GetTexParameter(JSContext * /* unused */, GLenum target,
                               GLenum pname) {
         return GetTexParameter(target, pname);
     }
     JS::Value GetUniform(JSContext* cx, WebGLProgram *prog,
-                         WebGLUniformLocation *location, ErrorResult& rv);
+                         WebGLUniformLocation *location);
     already_AddRefed<WebGLUniformLocation>
       GetUniformLocation(WebGLProgram *prog, const nsAString& name);
     void Hint(GLenum target, GLenum mode);
     bool IsFramebuffer(WebGLFramebuffer *fb);
     bool IsProgram(WebGLProgram *prog);
     bool IsRenderbuffer(WebGLRenderbuffer *rb);
     bool IsShader(WebGLShader *shader);
     bool IsTexture(WebGLTexture *tex);
@@ -420,17 +420,17 @@ public:
     {
         if (IsContextLost())
             return;
         nsRefPtr<gfxImageSurface> isurf;
         WebGLTexelFormat srcFormat;
         nsLayoutUtils::SurfaceFromElementResult res = SurfaceFromElement(elt);
         rv = SurfaceFromElementResultToImageSurface(res, getter_AddRefs(isurf),
                                                     &srcFormat);
-        if (rv.Failed())
+        if (rv.Failed() || !isurf)
             return;
 
         uint32_t byteLength = isurf->Stride() * isurf->Height();
         return TexImage2D_base(target, level, internalformat,
                                isurf->Width(), isurf->Height(), isurf->Stride(),
                                0, format, type, isurf->Data(), byteLength,
                                -1, srcFormat, mPixelStorePremultiplyAlpha);
     }
@@ -459,17 +459,17 @@ public:
     {
         if (IsContextLost())
             return;
         nsRefPtr<gfxImageSurface> isurf;
         WebGLTexelFormat srcFormat;
         nsLayoutUtils::SurfaceFromElementResult res = SurfaceFromElement(elt);
         rv = SurfaceFromElementResultToImageSurface(res, getter_AddRefs(isurf),
                                                     &srcFormat);
-        if (rv.Failed())
+        if (rv.Failed() || !isurf)
             return;
 
         uint32_t byteLength = isurf->Stride() * isurf->Height();
         return TexSubImage2D_base(target, level, xoffset, yoffset,
                                   isurf->Width(), isurf->Height(),
                                   isurf->Stride(), format, type,
                                   isurf->Data(), byteLength,
                                   -1, srcFormat, mPixelStorePremultiplyAlpha);
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -1802,17 +1802,17 @@ WebGLContext::GetTexParameter(GLenum tar
             ErrorInvalidEnumInfo("getTexParameter: parameter", pname);
     }
 
     return JS::NullValue();
 }
 
 JS::Value
 WebGLContext::GetUniform(JSContext* cx, WebGLProgram *prog,
-                         WebGLUniformLocation *location, ErrorResult& rv)
+                         WebGLUniformLocation *location)
 {
     if (IsContextLost())
         return JS::NullValue();
 
     if (!ValidateObject("getUniform: program", prog))
         return JS::NullValue();
 
     if (!ValidateObject("getUniform: location", location))
@@ -1871,69 +1871,69 @@ WebGLContext::GetUniform(JSContext* cx, 
                     break;
                 }
             }
             if (found_it) break;
         }
     }
 
     if (index == uniforms) {
-        rv.Throw(NS_ERROR_FAILURE); // XXX GL error? shouldn't happen.
+        GenerateWarning("getUniform: internal error: hit an OpenGL driver bug");
         return JS::NullValue();
     }
 
     GLenum baseType;
     GLint unitSize;
     if (!BaseTypeAndSizeFromUniformType(uniformType, &baseType, &unitSize)) {
-        rv.Throw(NS_ERROR_FAILURE);
+        GenerateWarning("getUniform: internal error: unknown uniform type 0x%x", uniformType);
         return JS::NullValue();
     }
 
     // this should never happen
     if (unitSize > 16) {
-        rv.Throw(NS_ERROR_FAILURE);
+        GenerateWarning("getUniform: internal error: unexpected uniform unit size %d", unitSize);
         return JS::NullValue();
     }
 
     if (baseType == LOCAL_GL_FLOAT) {
         GLfloat fv[16] = { GLfloat(0) };
         gl->fGetUniformfv(progname, location->Location(), fv);
         if (unitSize == 1) {
             return JS::DoubleValue(fv[0]);
         } else {
             JSObject* obj = Float32Array::Create(cx, this, unitSize, fv);
             if (!obj) {
-                rv.Throw(NS_ERROR_OUT_OF_MEMORY);
+                ErrorOutOfMemory("getUniform: out of memory");
             }
             return JS::ObjectOrNullValue(obj);
         }
     } else if (baseType == LOCAL_GL_INT) {
         GLint iv[16] = { 0 };
         gl->fGetUniformiv(progname, location->Location(), iv);
         if (unitSize == 1) {
             return JS::Int32Value(iv[0]);
         } else {
             JSObject* obj = Int32Array::Create(cx, this, unitSize, iv);
             if (!obj) {
-                rv.Throw(NS_ERROR_OUT_OF_MEMORY);
+                ErrorOutOfMemory("getUniform: out of memory");
             }
             return JS::ObjectOrNullValue(obj);
         }
     } else if (baseType == LOCAL_GL_BOOL) {
         GLint iv[16] = { 0 };
         gl->fGetUniformiv(progname, location->Location(), iv);
         if (unitSize == 1) {
             return JS::BooleanValue(iv[0] ? true : false);
         } else {
             JS::Value uv[16];
             for (int k = 0; k < unitSize; k++)
                 uv[k] = JS::BooleanValue(iv[k] ? true : false);
             JSObject* obj = JS_NewArrayObject(cx, unitSize, uv);
             if (!obj) {
-                rv.Throw(NS_ERROR_OUT_OF_MEMORY);
+                ErrorOutOfMemory("getUniform: out of memory");
             }
             return JS::ObjectOrNullValue(obj);
         }
     }
 
     // Else preserving behavior, but I'm not sure this is correct per spec
     return JS::UndefinedValue();
 }
@@ -2634,21 +2634,24 @@ WebGLContext::StencilOpSeparate(GLenum f
     MakeContextCurrent();
     gl->fStencilOpSeparate(face, sfail, dpfail, dppass);
 }
 
 nsresult
 WebGLContext::SurfaceFromElementResultToImageSurface(nsLayoutUtils::SurfaceFromElementResult& res,
                                                      gfxImageSurface **imageOut, WebGLTexelFormat *format)
 {
+   *imageOut = nullptr;
+   *format = WebGLTexelFormat::None;
+
     if (!res.mSurface)
-        return NS_ERROR_FAILURE;
+        return NS_OK;
     if (res.mSurface->GetType() != gfxSurfaceTypeImage) {
         // SurfaceFromElement lied!
-        return NS_ERROR_FAILURE;
+        return NS_OK;
     }
 
     // We disallow loading cross-domain images and videos that have not been validated
     // with CORS as WebGL textures. The reason for doing that is that timing
     // attacks on WebGL shaders are able to retrieve approximations of the
     // pixel values in WebGL textures; see bug 655987.
     //
     // To prevent a loophole where a Canvas2D would be used as a proxy to load
--- a/content/canvas/src/WebGLTexelConversions.cpp
+++ b/content/canvas/src/WebGLTexelConversions.cpp
@@ -32,17 +32,17 @@ class WebGLImageConverter
     bool mAlreadyRun;
     bool mSuccess;
 
     /*
      * Returns sizeof(texel)/sizeof(type). The point is that we will iterate over
      * texels with typed pointers and this value will tell us by how much we need
      * to increment these pointers to advance to the next texel.
      */
-    template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Format>
+    template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
     static size_t NumElementsPerTexelForFormat() {
         switch (Format) {
             case WebGLTexelFormat::R8:
             case WebGLTexelFormat::A8:
             case WebGLTexelFormat::R32F:
             case WebGLTexelFormat::A32F:
             case WebGLTexelFormat::RGBA5551:
             case WebGLTexelFormat::RGBA4444:
@@ -68,19 +68,19 @@ class WebGLImageConverter
     /*
      * This is the completely format-specific templatized conversion function,
      * that will be instantiated hundreds of times for all different combinations.
      * It is important to avoid generating useless code here. In particular, many
      * instantiations of this function template will never be called, so we try
      * to return immediately in these cases to allow the compiler to avoid generating
      * useless code.
      */
-    template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) SrcFormat,
-             MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) DstFormat,
-             MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelPremultiplicationOp) PremultiplicationOp>
+    template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) SrcFormat,
+             MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) DstFormat,
+             MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelPremultiplicationOp) PremultiplicationOp>
     void run()
     {
         // check for never-called cases. We early-return to allow the compiler
         // to avoid generating this code. It would be tempting to abort() instead,
         // as returning early does leave the destination surface with uninitialized
         // data, but that would not allow the compiler to avoid generating this code.
         // So instead, we return early, so Success() will return false, and the caller
         // must check that and abort in that case. See WebGLContext::ConvertImage.
@@ -141,19 +141,19 @@ class WebGLImageConverter
 
         typedef
             typename DataTypeForFormat<SrcFormat>::Type
             SrcType;
         typedef
             typename DataTypeForFormat<DstFormat>::Type
             DstType;
 
-        const MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) IntermediateSrcFormat
+        const MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) IntermediateSrcFormat
             = IntermediateFormat<SrcFormat>::Value;
-        const MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) IntermediateDstFormat
+        const MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) IntermediateDstFormat
             = IntermediateFormat<DstFormat>::Value;
         typedef
             typename DataTypeForFormat<IntermediateSrcFormat>::Type
             IntermediateSrcType;
         typedef
             typename DataTypeForFormat<IntermediateDstFormat>::Type
             IntermediateDstType;
 
@@ -208,18 +208,18 @@ class WebGLImageConverter
             srcRowStart += srcStrideInElements;
             dstRowStart += dstStrideInElements;
         }
 
         mSuccess = true;
         return;
     }
 
-    template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) SrcFormat,
-             MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) DstFormat>
+    template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) SrcFormat,
+             MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) DstFormat>
     void run(WebGLTexelPremultiplicationOp premultiplicationOp)
     {
         #define WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(PremultiplicationOp) \
             case PremultiplicationOp: \
                 return run<SrcFormat, DstFormat, PremultiplicationOp>();
 
         switch (premultiplicationOp) {
             WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(WebGLTexelPremultiplicationOp::None)
@@ -227,17 +227,17 @@ class WebGLImageConverter
             WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP(WebGLTexelPremultiplicationOp::Unpremultiply)
             default:
                 MOZ_ASSERT(false, "unhandled case. Coding mistake?");
         }
 
         #undef WEBGLIMAGECONVERTER_CASE_PREMULTIPLICATIONOP
     }
 
-    template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) SrcFormat>
+    template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) SrcFormat>
     void run(WebGLTexelFormat dstFormat,
              WebGLTexelPremultiplicationOp premultiplicationOp)
     {
         #define WEBGLIMAGECONVERTER_CASE_DSTFORMAT(DstFormat) \
             case DstFormat: \
                 return run<SrcFormat, DstFormat>(premultiplicationOp);
 
         switch (dstFormat) {
--- a/content/canvas/src/WebGLTexelConversions.h
+++ b/content/canvas/src/WebGLTexelConversions.h
@@ -41,67 +41,60 @@ namespace mozilla {
 MOZ_BEGIN_ENUM_CLASS(WebGLTexelPremultiplicationOp, int)
     None,
     Premultiply,
     Unpremultiply
 MOZ_END_ENUM_CLASS(WebGLTexelPremultiplicationOp)
 
 namespace WebGLTexelConversions {
 
-// remove this as soon as B2G and Windows use newer compilers
-#ifdef MOZ_HAVE_CXX11_STRONG_ENUMS
-#define MOZ_ENUM_CLASS_INTEGER_TYPE(X) X
-#else
-#define MOZ_ENUM_CLASS_INTEGER_TYPE(X) X::Enum
-#endif
-
-template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Format>
+template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
 struct IsFloatFormat
 {
     static const bool Value =
         Format == WebGLTexelFormat::RGBA32F ||
         Format == WebGLTexelFormat::RGB32F ||
         Format == WebGLTexelFormat::RA32F ||
         Format == WebGLTexelFormat::R32F ||
         Format == WebGLTexelFormat::A32F;
 };
 
-template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Format>
+template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
 struct Is16bppFormat
 {
     static const bool Value =
         Format == WebGLTexelFormat::RGBA4444 ||
         Format == WebGLTexelFormat::RGBA5551 ||
         Format == WebGLTexelFormat::RGB565;
 };
 
-template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Format,
+template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format,
          bool IsFloat = IsFloatFormat<Format>::Value,
          bool Is16bpp = Is16bppFormat<Format>::Value>
 struct DataTypeForFormat
 {
     typedef uint8_t Type;
 };
 
-template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Format>
+template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
 struct DataTypeForFormat<Format, true, false>
 {
     typedef float Type;
 };
 
-template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Format>
+template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
 struct DataTypeForFormat<Format, false, true>
 {
     typedef uint16_t Type;
 };
 
-template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Format>
+template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format>
 struct IntermediateFormat
 {
-    static const MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Value
+    static const MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Value
         = IsFloatFormat<Format>::Value
           ? WebGLTexelFormat::RGBA32F
           : WebGLTexelFormat::RGBA8;
 };
 
 inline size_t TexelBytesForFormat(WebGLTexelFormat format) {
     switch (format) {
         case WebGLTexelFormat::R8:
@@ -167,17 +160,17 @@ MOZ_ALWAYS_INLINE bool HasColor(WebGLTex
 /****** BEGIN CODE SHARED WITH WEBKIT ******/
 
 // the pack/unpack functions here are originally from this file:
 //   http://trac.webkit.org/browser/trunk/WebCore/platform/graphics/GraphicsContext3D.cpp
 
 //----------------------------------------------------------------------
 // Pixel unpacking routines.
 
-template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Format, typename SrcType, typename DstType>
+template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format, typename SrcType, typename DstType>
 MOZ_ALWAYS_INLINE void
 unpack(const SrcType* __restrict src,
        DstType* __restrict dst)
 {
     MOZ_ASSERT(false, "Unimplemented texture format conversion");
 }
 
 template<> MOZ_ALWAYS_INLINE void
@@ -327,18 +320,18 @@ unpack<WebGLTexelFormat::A32F, float, fl
     dst[2] = 0;
     dst[3] = src[0];
 }
 
 //----------------------------------------------------------------------
 // Pixel packing routines.
 //
 
-template<MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelFormat) Format,
-         MOZ_ENUM_CLASS_INTEGER_TYPE(WebGLTexelPremultiplicationOp) PremultiplicationOp,
+template<MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelFormat) Format,
+         MOZ_ENUM_CLASS_ENUM_TYPE(WebGLTexelPremultiplicationOp) PremultiplicationOp,
          typename SrcType,
          typename DstType>
 MOZ_ALWAYS_INLINE void
 pack(const SrcType* __restrict src,
      DstType* __restrict dst)
 {
     MOZ_ASSERT(false, "Unimplemented texture format conversion");
 }
--- a/content/canvas/src/WebGLTypes.h
+++ b/content/canvas/src/WebGLTypes.h
@@ -91,16 +91,18 @@ MOZ_END_ENUM_CLASS(WebGLImageDataStatus)
  * The formats that may participate, either as source or destination formats,
  * in WebGL texture conversions. This includes:
  *  - all the formats accepted by WebGL.texImage2D, e.g. RGBA4444
  *  - additional formats provided by extensions, e.g. RGB32F
  *  - additional source formats, depending on browser details, used when uploading
  *    textures from DOM elements. See gfxImageSurface::Format().
  */
 MOZ_BEGIN_ENUM_CLASS(WebGLTexelFormat, int)
+    // returned by SurfaceFromElementResultToImageSurface to indicate absence of image data
+    None,
     // dummy error code returned by GetWebGLTexelFormat in error cases,
     // after assertion failure (so this never happens in debug builds)
     BadFormat,
     // dummy pseudo-format meaning "use the other format".
     // For example, if SrcFormat=Auto and DstFormat=RGB8, then the source
     // is implicitly treated as being RGB8 itself.
     Auto,
     // 1-channel formats
--- a/content/media/moz.build
+++ b/content/media/moz.build
@@ -112,17 +112,16 @@ UNIFIED_SOURCES += [
     'AudioNodeStream.cpp',
     'AudioSegment.cpp',
     'AudioStream.cpp',
     'AudioStreamTrack.cpp',
     'BufferDecoder.cpp',
     'DOMMediaStream.cpp',
     'EncodedBufferCache.cpp',
     'FileBlockCache.cpp',
-    'Latency.cpp',
     'MediaCache.cpp',
     'MediaDecoder.cpp',
     'MediaDecoderReader.cpp',
     'MediaDecoderStateMachine.cpp',
     'MediaRecorder.cpp',
     'MediaResource.cpp',
     'MediaStreamGraph.cpp',
     'MediaStreamTrack.cpp',
@@ -139,18 +138,20 @@ UNIFIED_SOURCES += [
     'VideoPlaybackQuality.cpp',
     'VideoSegment.cpp',
     'VideoStreamTrack.cpp',
     'VideoUtils.cpp',
     'WebVTTListener.cpp',
 ]
 
 # DecoderTraits.cpp needs to be built separately because of Mac OS X headers.
+# Latency.cpp needs to be built separately because it forces NSPR logging.
 SOURCES += [
     'DecoderTraits.cpp',
+    'Latency.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
 if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['BUILD_ARM_NEON']:
     SOURCES += [
         'AudioNodeEngineNEON.cpp',
     ]
--- a/docshell/base/moz.build
+++ b/docshell/base/moz.build
@@ -46,27 +46,31 @@ EXPORTS.mozilla += [
     'IHistory.h',
     'LoadContext.h',
 ]
 
 UNIFIED_SOURCES += [
     'LoadContext.cpp',
     'nsAboutRedirector.cpp',
     'nsDefaultURIFixup.cpp',
-    'nsDocShell.cpp',
     'nsDocShellEditorData.cpp',
     'nsDocShellEnumerator.cpp',
     'nsDocShellLoadInfo.cpp',
     'nsDocShellTransferableHooks.cpp',
     'nsDownloadHistory.cpp',
     'nsDSURIContentListener.cpp',
     'nsWebNavigationInfo.cpp',
     'SerializedLoadContext.cpp',
 ]
 
+# nsDocShell.cpp cannot be built in unified mode because it forces NSPR logging.
+SOURCES += [
+    'nsDocShell.cpp',
+]
+
 FAIL_ON_WARNINGS = True
 
 MSVC_ENABLE_PGO = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -77,17 +77,16 @@ UNIFIED_SOURCES += [
     'nsContentPermissionHelper.cpp',
     'nsDOMClassInfo.cpp',
     'nsDOMNavigationTiming.cpp',
     'nsDOMScriptObjectFactory.cpp',
     'nsDOMWindowList.cpp',
     'nsFocusManager.cpp',
     'nsGlobalWindowCommands.cpp',
     'nsHistory.cpp',
-    'nsJSEnvironment.cpp',
     'nsJSTimeoutHandler.cpp',
     'nsJSUtils.cpp',
     'nsLocation.cpp',
     'nsMimeTypeArray.cpp',
     'nsPerformance.cpp',
     'nsQueryContentEventResult.cpp',
     'nsScreen.cpp',
     'nsScriptNameSpaceManager.cpp',
@@ -102,16 +101,18 @@ UNIFIED_SOURCES += [
 # these files couldn't be in UNIFIED_SOURCES for now for reasons given below:
 SOURCES += [
     # this file doesn't like windows.h
     'MessagePort.cpp',
     # this file doesn't like windows.h
     'nsDOMWindowUtils.cpp',
     # This file has a #error "Never include windows.h in this file!"
     'nsGlobalWindow.cpp',
+    # This file forces NSPR logging.
+    'nsJSEnvironment.cpp',
     # nsPluginArray.cpp includes npapi.h indirectly, and that includes a lot of system headers
     'nsPluginArray.cpp',
 ]
 
 EXTRA_COMPONENTS += [
     'ConsoleAPI.js',
     'ConsoleAPI.manifest',
     'SiteSpecificUserAgent.js',
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -3431,14 +3431,51 @@ nsFocusManager::SetFocusedWindowInternal
       nsContentUtils::IsInPointerLockContext(mFocusedWindow) &&
       !nsContentUtils::IsInPointerLockContext(aWindow)) {
     nsCOMPtr<nsIRunnable> runnable = new PointerUnlocker();
     NS_DispatchToCurrentThread(runnable);
   }
   mFocusedWindow = aWindow;
 }
 
+void
+nsFocusManager::MarkUncollectableForCCGeneration(uint32_t aGeneration)
+{
+  if (!sInstance) {
+    return;
+  }
+
+  if (sInstance->mActiveWindow) {
+    sInstance->mActiveWindow->
+      MarkUncollectableForCCGeneration(aGeneration);
+  }
+  if (sInstance->mFocusedWindow) {
+    sInstance->mFocusedWindow->
+      MarkUncollectableForCCGeneration(aGeneration);
+  }
+  if (sInstance->mWindowBeingLowered) {
+    sInstance->mWindowBeingLowered->
+      MarkUncollectableForCCGeneration(aGeneration);
+  }
+  if (sInstance->mFocusedContent) {
+    sInstance->mFocusedContent->OwnerDoc()->
+      MarkUncollectableForCCGeneration(aGeneration);
+  }
+  if (sInstance->mFirstBlurEvent) {
+    sInstance->mFirstBlurEvent->OwnerDoc()->
+      MarkUncollectableForCCGeneration(aGeneration);
+  }
+  if (sInstance->mFirstFocusEvent) {
+    sInstance->mFirstFocusEvent->OwnerDoc()->
+      MarkUncollectableForCCGeneration(aGeneration);
+  }
+  if (sInstance->mMouseDownEventHandlingDocument) {
+    sInstance->mMouseDownEventHandlingDocument->
+      MarkUncollectableForCCGeneration(aGeneration);
+  }
+}
+
 nsresult
 NS_NewFocusManager(nsIFocusManager** aResult)
 {
   NS_IF_ADDREF(*aResult = nsFocusManager::GetFocusManager());
   return NS_OK;
 }
--- a/dom/base/nsFocusManager.h
+++ b/dom/base/nsFocusManager.h
@@ -116,16 +116,17 @@ public:
 
   /**
    * Returns an InputContextAction cause for aFlags.
    */
   static InputContextAction::Cause GetFocusMoveActionCause(uint32_t aFlags);
 
   static bool sMouseFocusesFormControl;
 
+  static void MarkUncollectableForCCGeneration(uint32_t aGeneration);
 protected:
 
   nsFocusManager();
   ~nsFocusManager();
 
   /**
    * Ensure that the widget associated with the currently focused window is
    * focused at the widget level.
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -545,17 +545,18 @@ nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWind
   mRunningTimeout(nullptr), mMutationBits(0), mIsDocumentLoaded(false),
   mIsHandlingResizeEvent(false), mIsInnerWindow(aOuterWindow != nullptr),
   mMayHavePaintEventListener(false), mMayHaveTouchEventListener(false),
   mMayHaveMouseEnterLeaveEventListener(false),
   mIsModalContentWindow(false),
   mIsActive(false), mIsBackground(false),
   mInnerWindow(nullptr), mOuterWindow(aOuterWindow),
   // Make sure no actual window ends up with mWindowID == 0
-  mWindowID(++gNextWindowID), mHasNotifiedGlobalCreated(false)
+  mWindowID(++gNextWindowID), mHasNotifiedGlobalCreated(false),
+  mMarkedCCGeneration(0)
  {}
 
 nsPIDOMWindow::~nsPIDOMWindow() {}
 
 // DialogValueHolder CC goop.
 NS_IMPL_CYCLE_COLLECTION_1(DialogValueHolder, mValue)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DialogValueHolder)
@@ -1614,34 +1615,34 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGloba
 static PLDHashOperator
 MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure)
 {
   JS::ExposeObjectToActiveJS(aData);
   return PL_DHASH_NEXT;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindow)
-  if (tmp->IsBlackForCC()) {
+  if (tmp->IsBlackForCC(false)) {
     if (tmp->mCachedXBLPrototypeHandlers) {
       tmp->mCachedXBLPrototypeHandlers->Enumerate(MarkXBLHandlers, nullptr);
     }
     if (nsEventListenerManager* elm = tmp->GetExistingListenerManager()) {
       elm->MarkForCC();
     }
     tmp->UnmarkGrayTimers();
     return true;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindow)
-  return tmp->IsBlackForCC();
+  return tmp->IsBlackForCC(true);
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindow)
-  return tmp->IsBlackForCC();
+  return tmp->IsBlackForCC(false);
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
 
 inline void
 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
                             IdleObserverHolder& aField,
                             const char* aName,
                             unsigned aFlags)
 {
@@ -1654,20 +1655,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
     char name[512];
     PR_snprintf(name, sizeof(name), "nsGlobalWindow #%ld", tmp->mWindowID);
     cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
   } else {
     NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindow, tmp->mRefCnt.get())
   }
 
-  if (!cb.WantAllTraces() && tmp->IsBlackForCC()) {
-    return NS_SUCCESS_INTERRUPTED_TRAVERSE;
-  }
-
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDialogArguments)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
@@ -1786,22 +1783,26 @@ TraceXBLHandlers(nsXBLPrototypeHandler* 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindow)
   if (tmp->mCachedXBLPrototypeHandlers) {
     TraceData data = { aCallbacks, aClosure };
     tmp->mCachedXBLPrototypeHandlers->Enumerate(TraceXBLHandlers, &data);
   }
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 bool
-nsGlobalWindow::IsBlackForCC()
-{
-  return
-    (mDoc &&
-     nsCCUncollectableMarker::InGeneration(mDoc->GetMarkedCCGeneration())) ||
-    (nsCCUncollectableMarker::sGeneration && IsBlack());
+nsGlobalWindow::IsBlackForCC(bool aTracingNeeded)
+{
+  if (!nsCCUncollectableMarker::sGeneration) {
+    return false;
+  }
+
+  return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) ||
+          IsBlack()) &&
+         (!aTracingNeeded ||
+          HasNothingToTrace(static_cast<nsIDOMEventTarget*>(this)));
 }
 
 void
 nsGlobalWindow::UnmarkGrayTimers()
 {
   for (nsTimeout* timeout = mTimeouts.getFirst();
        timeout;
        timeout = timeout->getNext()) {
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -350,17 +350,17 @@ public:
 
   virtual nsresult EnsureScriptEnvironment();
 
   virtual nsIScriptContext *GetScriptContext();
 
   void PoisonOuterWindowProxy(JSObject *aObject);
   virtual void OnFinalize(JSObject* aObject);
 
-  virtual bool IsBlackForCC();
+  virtual bool IsBlackForCC(bool aTracingNeeded = true);
 
   // nsIScriptObjectPrincipal
   virtual nsIPrincipal* GetPrincipal();
 
   // nsIDOMWindow
   NS_DECL_NSIDOMWINDOW
 
 #ifdef MOZ_B2G
--- a/dom/base/nsIScriptGlobalObject.h
+++ b/dom/base/nsIScriptGlobalObject.h
@@ -22,18 +22,18 @@ class nsIScriptGlobalObject;
 // aStatus will be filled in with the status.
 bool
 NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal,
                      mozilla::InternalScriptErrorEvent *aErrorEvent,
                      nsEventStatus *aStatus);
 
 
 #define NS_ISCRIPTGLOBALOBJECT_IID \
-{ 0xa6c0bfae, 0x8be4, 0x4747, \
-  { 0xaf, 0x1a, 0xe3, 0xf0, 0x3f, 0xb6, 0x0e, 0xb8 } }
+{ 0x30c64680, 0x909a, 0x4435, \
+  { 0x90, 0x3b, 0x29, 0x3e, 0xb5, 0x5d, 0xc7, 0xa0 } }
 
 /**
  * The global object which keeps a script context for each supported script
  * language. This often used to store per-window global state.
  * This is a heavyweight interface implemented only by DOM globals, and
  * it might go away some time in the future.
  */
 
@@ -75,15 +75,15 @@ public:
    */
   virtual nsresult HandleScriptError(
                      mozilla::InternalScriptErrorEvent *aErrorEvent,
                      nsEventStatus *aEventStatus) {
     NS_ENSURE_STATE(NS_HandleScriptError(this, aErrorEvent, aEventStatus));
     return NS_OK;
   }
 
-  virtual bool IsBlackForCC() { return false; }
+  virtual bool IsBlackForCC(bool aTracingNeeded = true) { return false; }
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIScriptGlobalObject,
                               NS_ISCRIPTGLOBALOBJECT_IID)
 
 #endif
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -55,18 +55,18 @@ enum PopupControlState {
 enum UIStateChangeType
 {
   UIStateChangeType_NoChange,
   UIStateChangeType_Set,
   UIStateChangeType_Clear
 };
 
 #define NS_PIDOMWINDOW_IID \
-{ 0x4f4eadf9, 0xe795, 0x48e5, \
-  { 0x89, 0x4b, 0x04, 0x40, 0xb2, 0x5d, 0xa6, 0xfa } }
+{ 0xf26953de, 0xa799, 0x4a92, \
+  { 0x87, 0x49, 0x7c, 0x37, 0xe5, 0x90, 0x3f, 0x37 } }
 
 class nsPIDOMWindow : public nsIDOMWindowInternal
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMWINDOW_IID)
 
   virtual nsPIDOMWindow* GetPrivateRoot() = 0;
 
@@ -648,16 +648,25 @@ public:
     }
 
     return outer;
   }
 
   // WebIDL-ish APIs
   nsPerformance* GetPerformance();
 
+  void MarkUncollectableForCCGeneration(uint32_t aGeneration)
+  {
+    mMarkedCCGeneration = aGeneration;
+  }
+
+  uint32_t GetMarkedCCGeneration()
+  {
+    return mMarkedCCGeneration;
+  }
 protected:
   // The nsPIDOMWindow constructor. The aOuterWindow argument should
   // be null if and only if the created window itself is an outer
   // window. In all other cases aOuterWindow should be the outer
   // window for the inner window that is being created.
   nsPIDOMWindow(nsPIDOMWindow *aOuterWindow);
 
   ~nsPIDOMWindow();
@@ -731,16 +740,18 @@ protected:
 
   // A unique (as long as our 64-bit counter doesn't roll over) id for
   // this window.
   uint64_t mWindowID;
 
   // This is only used by the inner window. Set to true once we've sent
   // the (chrome|content)-document-global-created notification.
   bool mHasNotifiedGlobalCreated;
+
+  uint32_t mMarkedCCGeneration;
 };
 
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindow, NS_PIDOMWINDOW_IID)
 
 #ifdef MOZILLA_INTERNAL_API
 PopupControlState
 PushPopupControlState(PopupControlState aState, bool aForce);
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -151,16 +151,18 @@ public:
   bool IsBlack();
 
   /**
    * Returns true if the object has a black wrapper,
    * and all the GC things it is keeping alive are black too.
    */
   bool IsBlackAndDoesNotNeedTracing(nsISupports* aThis);
 
+  bool HasNothingToTrace(nsISupports* aThis);
+
   // Only meant to be called by code that preserves a wrapper.
   void SetPreservingWrapper(bool aPreserve)
   {
     if(aPreserve) {
       SetWrapperFlags(WRAPPER_BIT_PRESERVED);
     }
     else {
       UnsetWrapperFlags(WRAPPER_BIT_PRESERVED);
--- a/dom/base/nsWrapperCacheInlines.h
+++ b/dom/base/nsWrapperCacheInlines.h
@@ -32,26 +32,29 @@ SearchGray(void* aGCThing, const char* a
 {
   bool* hasGrayObjects = static_cast<bool*>(aClosure);
   if (!*hasGrayObjects && aGCThing && JS::GCThingIsMarkedGray(aGCThing)) {
     *hasGrayObjects = true;
   }
 }
 
 inline bool
+nsWrapperCache::HasNothingToTrace(nsISupports* aThis)
+{
+  nsXPCOMCycleCollectionParticipant* participant = nullptr;
+  CallQueryInterface(aThis, &participant);
+  bool hasGrayObjects = false;
+  participant->Trace(aThis, TraceCallbackFunc(SearchGray), &hasGrayObjects);
+  return !hasGrayObjects;
+}
+
+inline bool
 nsWrapperCache::IsBlackAndDoesNotNeedTracing(nsISupports* aThis)
 {
-  if (IsBlack()) {
-    nsXPCOMCycleCollectionParticipant* participant = nullptr;
-    CallQueryInterface(aThis, &participant);
-    bool hasGrayObjects = false;
-    participant->Trace(aThis, TraceCallbackFunc(SearchGray), &hasGrayObjects);
-    return !hasGrayObjects;
-  }
-  return false;
+  return IsBlack() && HasNothingToTrace(aThis);
 }
 
 inline void
 nsWrapperCache::TraceWrapperJSObject(JSTracer* aTrc, const char* aName)
 {
   JS_CallHeapObjectTracer(aTrc, &mWrapper, aName);
 }
 
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2142,17 +2142,17 @@ ConvertJSValueToByteString(JSContext* cx
   return true;
 }
 
 bool
 ThreadsafeCheckIsChrome(JSContext* aCx, JSObject* aObj)
 {
   using mozilla::dom::workers::GetWorkerPrivateFromContext;
   return NS_IsMainThread() ? xpc::AccessCheck::isChrome(aObj):
-                             GetWorkerPrivateFromContext(aCx)->IsChromeWorker();
+                             GetWorkerPrivateFromContext(aCx)->UsesSystemPrincipal();
 }
 
 void
 TraceGlobal(JSTracer* aTrc, JSObject* aObj)
 {
   MOZ_ASSERT(js::GetObjectClass(aObj)->flags & JSCLASS_DOM_GLOBAL);
   mozilla::dom::TraceProtoAndIfaceCache(aTrc, aObj);
 }
--- a/dom/encoding/Makefile.in
+++ b/dom/encoding/Makefile.in
@@ -1,19 +1,11 @@
 # 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 $(topsrcdir)/config/rules.mk
 
-EncodingUtils.$(OBJ_SUFFIX): labelsencodings.properties.h
-FallbackEncoding.$(OBJ_SUFFIX): localesfallbacks.properties.h
-
 PROPS2ARRAYS = $(topsrcdir)/intl/locale/src/props2arrays.py
 labelsencodings.properties.h: $(PROPS2ARRAYS) labelsencodings.properties
 	$(PYTHON) $^ $@
 localesfallbacks.properties.h: $(PROPS2ARRAYS) localesfallbacks.properties
 	$(PYTHON) $^ $@
-
-GARBAGE += \
-	labelsencodings.properties.h \
-	localesfallbacks.properties.h \
-	$(NULL)
--- a/dom/encoding/moz.build
+++ b/dom/encoding/moz.build
@@ -25,8 +25,12 @@ UNIFIED_SOURCES += [
 
 FAIL_ON_WARNINGS = True
 
 FINAL_LIBRARY = 'gklayout'
 LOCAL_INCLUDES += [
     '/intl/locale/src',
 ]
 
+GENERATED_FILES += [
+    'labelsencodings.properties.h',
+    'localesfallbacks.properties.h',
+]
--- a/dom/file/moz.build
+++ b/dom/file/moz.build
@@ -28,17 +28,17 @@ EXPORTS.mozilla.dom.file += [
     'File.h',
     'FileCommon.h',
     'FileHandle.h',
     'FileHelper.h',
     'FileService.h',
     'LockedFile.h',
 ]
 
-SOURCES += [
+UNIFIED_SOURCES += [
     'ArchiveEvent.cpp',
     'ArchiveReader.cpp',
     'ArchiveRequest.cpp',
     'ArchiveZipEvent.cpp',
     'ArchiveZipFile.cpp',
     'AsyncHelper.cpp',
     'DOMFileHandle.cpp',
     'DOMFileRequest.cpp',
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -273,17 +273,17 @@ IDBRequest::GetJSContext()
 void
 IDBRequest::CaptureCaller()
 {
   AutoJSContext cx;
 
   const char* filename = nullptr;
   uint32_t lineNo = 0;
   if (!nsJSUtils::GetCallingLocation(cx, &filename, &lineNo)) {
-    MOZ_CRASH("Failed to get caller.");
+    NS_WARNING("Failed to get caller.");
     return;
   }
 
   mFilename.Assign(NS_ConvertUTF8toUTF16(filename));
   mLineNo = lineNo;
 }
 
 void
--- a/dom/indexedDB/test/mochitest.ini
+++ b/dom/indexedDB/test/mochitest.ini
@@ -107,8 +107,9 @@ support-files =
 [test_transaction_abort_hang.html]
 [test_transaction_lifetimes.html]
 [test_transaction_lifetimes_nested.html]
 [test_transaction_ordering.html]
 [test_unique_index_update.html]
 [test_webapp_clearBrowserData_inproc_inproc.html]
 [test_webapp_clearBrowserData_inproc_oop.html]
 [test_webapp_clearBrowserData_oop_inproc.html]
+[test_bug937006.html]
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_bug937006.html
@@ -0,0 +1,37 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Bug 937006 - "Hit MOZ_CRASH(Failed to get caller.)" using setTimeout on IndexedDB call</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+</head>
+<body onload="runTest();">
+  <script type="text/javascript;version=1.7">
+
+  function runTest() {
+    if (!SpecialPowers.isMainProcess()) {
+      window.runTest = function() {
+        todo(false, "Figure out this test for child processes!");
+        SimpleTest.finish();
+        return;
+      }
+    }
+
+    // doing this IDBRequest should not be able to retrieve the filename and
+    // line number.
+    //setTimeout(indexedDB.deleteDatabase.bind(indexedDB), 0, 'x');
+    setTimeout(function() {
+      ok(true, "Still alive");
+      SimpleTest.finish();
+    }, 10);
+  }
+
+  </script>
+</body>
+</html>
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -18,16 +18,18 @@
 #include "nsJSPrincipals.h"
 #include "nsJSUtils.h"
 #include "nsPIDOMWindow.h"
 #include "nsJSEnvironment.h"
 
 namespace mozilla {
 namespace dom {
 
+using namespace workers;
+
 NS_IMPL_ISUPPORTS0(PromiseNativeHandler)
 
 // PromiseTask
 
 // This class processes the promise's callbacks with promise's result.
 class PromiseTask MOZ_FINAL : public nsRunnable
 {
 public:
@@ -49,66 +51,143 @@ public:
     mPromise->RunTask();
     return NS_OK;
   }
 
 private:
   nsRefPtr<Promise> mPromise;
 };
 
-// This class processes the promise's callbacks with promise's result.
-class PromiseResolverTask MOZ_FINAL : public nsRunnable
+class WorkerPromiseTask MOZ_FINAL : public WorkerRunnable
 {
 public:
-  PromiseResolverTask(Promise* aPromise,
-                      JS::Handle<JS::Value> aValue,
-                      Promise::PromiseState aState)
+  WorkerPromiseTask(WorkerPrivate* aWorkerPrivate, Promise* aPromise)
+    : WorkerRunnable(aWorkerPrivate, WorkerThread,
+                     UnchangedBusyCount, SkipWhenClearing)
+    , mPromise(aPromise)
+  {
+    MOZ_ASSERT(aPromise);
+    MOZ_COUNT_CTOR(WorkerPromiseTask);
+  }
+
+  ~WorkerPromiseTask()
+  {
+    MOZ_COUNT_DTOR(WorkerPromiseTask);
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+  {
+    mPromise->mTaskPending = false;
+    mPromise->RunTask();
+    return true;
+  }
+
+private:
+  nsRefPtr<Promise> mPromise;
+};
+
+class PromiseResolverMixin
+{
+public:
+  PromiseResolverMixin(Promise* aPromise,
+                       JS::Handle<JS::Value> aValue,
+                       Promise::PromiseState aState)
     : mPromise(aPromise)
     , mValue(aValue)
     , mState(aState)
   {
     MOZ_ASSERT(aPromise);
     MOZ_ASSERT(mState != Promise::Pending);
-    MOZ_COUNT_CTOR(PromiseResolverTask);
+    MOZ_COUNT_CTOR(PromiseResolverMixin);
 
-    JSContext* cx = nsContentUtils::GetSafeJSContext();
+    JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
 
     /* It's safe to use unsafeGet() here: the unsafeness comes from the
      * possibility of updating the value of mJSObject without triggering the
      * barriers.  However if the value will always be marked, post barriers
      * unnecessary. */
     JS_AddNamedValueRootRT(JS_GetRuntime(cx), mValue.unsafeGet(),
-                           "PromiseResolverTask.mValue");
+                           "PromiseResolverMixin.mValue");
   }
 
-  ~PromiseResolverTask()
+  virtual ~PromiseResolverMixin()
   {
-    MOZ_COUNT_DTOR(PromiseResolverTask);
+    NS_ASSERT_OWNINGTHREAD(PromiseResolverMixin);
+    MOZ_COUNT_DTOR(PromiseResolverMixin);
 
-    JSContext* cx = nsContentUtils::GetSafeJSContext();
+    JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
 
     /* It's safe to use unsafeGet() here: the unsafeness comes from the
      * possibility of updating the value of mJSObject without triggering the
      * barriers.  However if the value will always be marked, post barriers
      * unnecessary. */
     JS_RemoveValueRootRT(JS_GetRuntime(cx), mValue.unsafeGet());
   }
 
-  NS_IMETHOD Run()
+protected:
+  void
+  RunInternal()
   {
+    NS_ASSERT_OWNINGTHREAD(PromiseResolverMixin);
     mPromise->RunResolveTask(
       JS::Handle<JS::Value>::fromMarkedLocation(mValue.address()),
       mState, Promise::SyncTask);
-    return NS_OK;
   }
 
 private:
   nsRefPtr<Promise> mPromise;
   JS::Heap<JS::Value> mValue;
   Promise::PromiseState mState;
+  NS_DECL_OWNINGTHREAD;
+};
+
+// This class processes the promise's callbacks with promise's result.
+class PromiseResolverTask MOZ_FINAL : public nsRunnable,
+                                      public PromiseResolverMixin
+{
+public:
+  PromiseResolverTask(Promise* aPromise,
+                      JS::Handle<JS::Value> aValue,
+                      Promise::PromiseState aState)
+    : PromiseResolverMixin(aPromise, aValue, aState)
+  {}
+
+  ~PromiseResolverTask()
+  {}
+
+  NS_IMETHOD Run()
+  {
+    RunInternal();
+    return NS_OK;
+  }
+};
+
+class WorkerPromiseResolverTask MOZ_FINAL : public WorkerRunnable,
+                                            public PromiseResolverMixin
+{
+public:
+  WorkerPromiseResolverTask(WorkerPrivate* aWorkerPrivate,
+                            Promise* aPromise,
+                            JS::Handle<JS::Value> aValue,
+                            Promise::PromiseState aState)
+    : WorkerRunnable(aWorkerPrivate, WorkerThread,
+                     UnchangedBusyCount, SkipWhenClearing),
+      PromiseResolverMixin(aPromise, aValue, aState)
+  {}
+
+  ~WorkerPromiseResolverTask()
+  {}
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+  {
+    RunInternal();
+    return true;
+  }
 };
 
 // Promise
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(Promise)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Promise)
   tmp->MaybeReportRejected();
@@ -162,36 +241,32 @@ Promise::~Promise()
 
 JSObject*
 Promise::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return PromiseBinding::Wrap(aCx, aScope, this);
 }
 
 /* static */ bool
-Promise::PrefEnabled()
-{
-  return Preferences::GetBool("dom.promise.enabled", false);
-}
-
-/* static */ bool
 Promise::EnabledForScope(JSContext* aCx, JSObject* /* unused */)
 {
+  if (NS_IsMainThread()) {
+    // No direct return so the chrome/certified app checks happen below.
+    if (Preferences::GetBool("dom.promise.enabled", false)) {
+      return true;
+    }
+  } else {
+    WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
+    return workerPrivate->PromiseEnabled() || workerPrivate->UsesSystemPrincipal();
+  }
   // Enable if the pref is enabled or if we're chrome or if we're a
   // certified app.
-  if (PrefEnabled()) {
-    return true;
-  }
-
   // Note that we have no concept of a certified app in workers.
   // XXXbz well, why not?
-  if (!NS_IsMainThread()) {
-    return workers::GetWorkerPrivateFromContext(aCx)->IsChromeWorker();
-  }
-
+  // FIXME(nsm): Remove these checks once promises are enabled by default.
   nsIPrincipal* prin = nsContentUtils::GetSubjectPrincipal();
   return nsContentUtils::IsSystemPrincipal(prin) ||
     prin->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED;
 }
 
 void
 Promise::MaybeResolve(JSContext* aCx,
                       JS::Handle<JS::Value> aValue)
@@ -273,20 +348,25 @@ Promise::CreateFunction(JSContext* aCx, 
   return obj;
 }
 
 /* static */ already_AddRefed<Promise>
 Promise::Constructor(const GlobalObject& aGlobal,
                      PromiseInit& aInit, ErrorResult& aRv)
 {
   JSContext* cx = aGlobal.GetContext();
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
-  if (!window) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
-    return nullptr;
+  nsCOMPtr<nsPIDOMWindow> window;
+
+  // On workers, let the window be null.
+  if (MOZ_LIKELY(NS_IsMainThread())) {
+    window = do_QueryInterface(aGlobal.GetAsSupports());
+    if (!window) {
+      aRv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
   }
 
   nsRefPtr<Promise> promise = new Promise(window);
 
   JS::Rooted<JSObject*> resolveFunc(cx,
                                     CreateFunction(cx, aGlobal.Get(), promise,
                                                    PromiseCallback::Resolve));
   if (!resolveFunc) {
@@ -317,37 +397,43 @@ Promise::Constructor(const GlobalObject&
 
   return promise.forget();
 }
 
 /* static */ already_AddRefed<Promise>
 Promise::Resolve(const GlobalObject& aGlobal, JSContext* aCx,
                  const Optional<JS::Handle<JS::Value>>& aValue, ErrorResult& aRv)
 {
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
-  if (!window) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
-    return nullptr;
+  nsCOMPtr<nsPIDOMWindow> window;
+  if (MOZ_LIKELY(NS_IsMainThread())) {
+    window = do_QueryInterface(aGlobal.GetAsSupports());
+    if (!window) {
+      aRv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
   }
 
   nsRefPtr<Promise> promise = new Promise(window);
 
   promise->MaybeResolveInternal(aCx,
     aValue.WasPassed() ? aValue.Value() : JS::UndefinedHandleValue);
   return promise.forget();
 }
 
 /* static */ already_AddRefed<Promise>
 Promise::Reject(const GlobalObject& aGlobal, JSContext* aCx,
                 const Optional<JS::Handle<JS::Value>>& aValue, ErrorResult& aRv)
 {
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
-  if (!window) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
-    return nullptr;
+  nsCOMPtr<nsPIDOMWindow> window;
+  if (MOZ_LIKELY(NS_IsMainThread())) {
+    window = do_QueryInterface(aGlobal.GetAsSupports());
+    if (!window) {
+      aRv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
   }
 
   nsRefPtr<Promise> promise = new Promise(window);
 
   promise->MaybeRejectInternal(aCx,
     aValue.WasPassed() ? aValue.Value() : JS::UndefinedHandleValue);
   return promise.forget();
 }
@@ -408,34 +494,41 @@ Promise::AppendCallbacks(PromiseCallback
     mHadRejectCallback = true;
     mRejectCallbacks.AppendElement(aRejectCallback);
   }
 
   // If promise's state is resolved, queue a task to process our resolve
   // callbacks with promise's result. If promise's state is rejected, queue a
   // task to process our reject callbacks with promise's result.
   if (mState != Pending && !mTaskPending) {
-    nsRefPtr<PromiseTask> task = new PromiseTask(this);
-    NS_DispatchToCurrentThread(task);
+    if (MOZ_LIKELY(NS_IsMainThread())) {
+      nsRefPtr<PromiseTask> task = new PromiseTask(this);
+      NS_DispatchToCurrentThread(task);
+    } else {
+      WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+      MOZ_ASSERT(worker);
+      nsRefPtr<WorkerPromiseTask> task = new WorkerPromiseTask(worker, this);
+      worker->Dispatch(task);
+    }
     mTaskPending = true;
   }
 }
 
 void
 Promise::RunTask()
 {
   MOZ_ASSERT(mState != Pending);
 
   nsTArray<nsRefPtr<PromiseCallback> > callbacks;
   callbacks.SwapElements(mState == Resolved ? mResolveCallbacks
                                             : mRejectCallbacks);
   mResolveCallbacks.Clear();
   mRejectCallbacks.Clear();
 
-  JSContext* cx = nsContentUtils::GetSafeJSContext();
+  JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
   JSAutoRequest ar(cx);
   JS::Rooted<JS::Value> value(cx, mResult);
 
   for (uint32_t i = 0; i < callbacks.Length(); ++i) {
     callbacks[i]->Call(value);
   }
 }
 
@@ -448,26 +541,37 @@ Promise::MaybeReportRejected()
 
   JSErrorReport* report = js::ErrorFromException(mResult);
   if (!report) {
     return;
   }
 
   MOZ_ASSERT(mResult.isObject(), "How did we get a JSErrorReport?");
 
-  nsCOMPtr<nsPIDOMWindow> win =
-    do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(&mResult.toObject()));
+  // Remains null in case of worker.
+  nsCOMPtr<nsPIDOMWindow> win;
+  bool isChromeError = false;
 
-  nsIPrincipal* principal = nsContentUtils::GetObjectPrincipal(&mResult.toObject());
+  if (MOZ_LIKELY(NS_IsMainThread())) {
+    win =
+      do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(&mResult.toObject()));
+    nsIPrincipal* principal = nsContentUtils::GetObjectPrincipal(&mResult.toObject());
+    isChromeError = nsContentUtils::IsSystemPrincipal(principal);
+  } else {
+    WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(worker);
+    isChromeError = worker->IsChromeWorker();
+  }
+
   // Now post an event to do the real reporting async
-  NS_DispatchToCurrentThread(
+  NS_DispatchToMainThread(
     new AsyncErrorReporter(JS_GetObjectRuntime(&mResult.toObject()),
                            report,
                            nullptr,
-                           nsContentUtils::IsSystemPrincipal(principal),
+                           isChromeError,
                            win));
 }
 
 void
 Promise::MaybeResolveInternal(JSContext* aCx,
                               JS::Handle<JS::Value> aValue,
                               PromiseTaskSync aAsynchronous)
 {
@@ -534,19 +638,27 @@ Promise::RejectInternal(JSContext* aCx,
 void
 Promise::RunResolveTask(JS::Handle<JS::Value> aValue,
                         PromiseState aState,
                         PromiseTaskSync aAsynchronous)
 {
   // If the synchronous flag is unset, queue a task to process our
   // accept callbacks with value.
   if (aAsynchronous == AsyncTask) {
-    nsRefPtr<PromiseResolverTask> task =
-      new PromiseResolverTask(this, aValue, aState);
-    NS_DispatchToCurrentThread(task);
+    if (MOZ_LIKELY(NS_IsMainThread())) {
+      nsRefPtr<PromiseResolverTask> task =
+        new PromiseResolverTask(this, aValue, aState);
+      NS_DispatchToCurrentThread(task);
+    } else {
+      WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+      MOZ_ASSERT(worker);
+      nsRefPtr<WorkerPromiseResolverTask> task =
+        new WorkerPromiseResolverTask(worker, this, aValue, aState);
+      worker->Dispatch(task);
+    }
     return;
   }
 
   SetResult(aValue);
   SetState(aState);
   RunTask();
 }
 
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -24,30 +24,32 @@ class AnyCallback;
 class PromiseCallback;
 class PromiseInit;
 class PromiseNativeHandler;
 
 class Promise MOZ_FINAL : public nsISupports,
                           public nsWrapperCache
 {
   friend class NativePromiseCallback;
-  friend class PromiseTask;
+  friend class PromiseResolverMixin;
   friend class PromiseResolverTask;
+  friend class PromiseTask;
+  friend class RejectPromiseCallback;
   friend class ResolvePromiseCallback;
-  friend class RejectPromiseCallback;
+  friend class WorkerPromiseResolverTask;
+  friend class WorkerPromiseTask;
   friend class WrapperPromiseCallback;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Promise)
 
   Promise(nsPIDOMWindow* aWindow);
   ~Promise();
 
-  static bool PrefEnabled();
   static bool EnabledForScope(JSContext* aCx, JSObject* /* unused */);
 
   void MaybeResolve(JSContext* aCx,
                     JS::Handle<JS::Value> aValue);
   void MaybeReject(JSContext* aCx,
                    JS::Handle<JS::Value> aValue);
 
   // WebIDL
--- a/dom/promise/PromiseCallback.cpp
+++ b/dom/promise/PromiseCallback.cpp
@@ -70,17 +70,23 @@ ResolvePromiseCallback::~ResolvePromiseC
 {
   MOZ_COUNT_DTOR(ResolvePromiseCallback);
 }
 
 void
 ResolvePromiseCallback::Call(JS::Handle<JS::Value> aValue)
 {
   // Run resolver's algorithm with value and the synchronous flag set.
-  AutoJSContext cx;
+  JSContext *cx = nsContentUtils::GetDefaultJSContextForThread();
+
+  Maybe<AutoCxPusher> pusher;
+  if (NS_IsMainThread()) {
+    pusher.construct(cx);
+  }
+
   Maybe<JSAutoCompartment> ac;
   EnterCompartment(ac, cx, aValue);
 
   mPromise->ResolveInternal(cx, aValue, Promise::SyncTask);
 }
 
 // RejectPromiseCallback
 
@@ -105,17 +111,23 @@ RejectPromiseCallback::~RejectPromiseCal
 {
   MOZ_COUNT_DTOR(RejectPromiseCallback);
 }
 
 void
 RejectPromiseCallback::Call(JS::Handle<JS::Value> aValue)
 {
   // Run resolver's algorithm with value and the synchronous flag set.
-  AutoJSContext cx;
+  JSContext *cx = nsContentUtils::GetDefaultJSContextForThread();
+
+  Maybe<AutoCxPusher> pusher;
+  if (NS_IsMainThread()) {
+    pusher.construct(cx);
+  }
+
   Maybe<JSAutoCompartment> ac;
   EnterCompartment(ac, cx, aValue);
 
   mPromise->RejectInternal(cx, aValue, Promise::SyncTask);
 }
 
 // WrapperPromiseCallback
 
@@ -141,17 +153,27 @@ WrapperPromiseCallback::WrapperPromiseCa
 WrapperPromiseCallback::~WrapperPromiseCallback()
 {
   MOZ_COUNT_DTOR(WrapperPromiseCallback);
 }
 
 void
 WrapperPromiseCallback::Call(JS::Handle<JS::Value> aValue)
 {
-  AutoJSContext cx;
+  // AutoCxPusher and co. interact with xpconnect, which crashes on
+  // workers. On workers we'll get the right context from
+  // GetDefaultJSContextForThread(), and since there is only one context, we
+  // don't need to push or pop it from the stack.
+  JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
+
+  Maybe<AutoCxPusher> pusher;
+  if (NS_IsMainThread()) {
+    pusher.construct(cx);
+  }
+
   Maybe<JSAutoCompartment> ac;
   EnterCompartment(ac, cx, aValue);
 
   ErrorResult rv;
 
   // If invoking callback threw an exception, run resolver's reject with the
   // thrown exception as argument and the synchronous flag set.
   JS::Rooted<JS::Value> value(cx,
--- a/dom/promise/PromiseCallback.h
+++ b/dom/promise/PromiseCallback.h
@@ -32,17 +32,17 @@ public:
   };
 
   // This factory returns a PromiseCallback object with refcount of 0.
   static PromiseCallback*
   Factory(Promise* aNextPromise, AnyCallback* aCallback, Task aTask);
 };
 
 // WrapperPromiseCallback execs a JS Callback with a value, and then the return
-// value is sent to the aNextPromise->resolveFunction() or to
+// value is sent to the aNextPromise->ResolveFunction() or to
 // aNextPromise->RejectFunction() if the JS Callback throws.
 class WrapperPromiseCallback MOZ_FINAL : public PromiseCallback
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(WrapperPromiseCallback,
                                            PromiseCallback)
 
--- a/dom/promise/tests/test_promise.html
+++ b/dom/promise/tests/test_promise.html
@@ -58,16 +58,25 @@ function promiseReject() {
     runTest();
   }, function(what) {
     ok(true, "Then - rejectCb has been called");
     is(what, 42, "RejectCb received 42");
     runTest();
   });
 }
 
+function promiseRejectNoHandler() {
+  // This test only checks that the code that reports unhandled errors in the
+  // Promises implementation does not crash or leak.
+  var promise = new Promise(function(res, rej) {
+    noSuchMethod();
+  });
+  runTest();
+}
+
 function promiseRejectNoArg() {
   var promise = new Promise(function(resolve, reject) {
     reject();
   }).then(function(what) {
     ok(false, "Then - resolveCb has been called");
     runTest();
   }, function(what) {
     ok(true, "Then - rejectCb has been called");
@@ -469,17 +478,18 @@ var tests = [ promiseResolve, promiseRej
               promiseWrongNestedPromise, promiseLoop,
               promiseStaticReject, promiseStaticResolve,
               promiseResolveNestedPromise,
               promiseResolveNoArg,
               promiseRejectNoArg,
               promiseThenNoArg,
               promiseThenUndefinedResolveFunction,
               promiseThenNullResolveFunction,
-              promiseCatchNoArg
+              promiseCatchNoArg,
+              promiseRejectNoHandler,
             ];
 
 function runTest() {
   if (!tests.length) {
     SimpleTest.finish();
     return;
   }
 
--- a/dom/webidl/WebGLRenderingContext.webidl
+++ b/dom/webidl/WebGLRenderingContext.webidl
@@ -616,17 +616,16 @@ interface WebGLRenderingContext {
     WebGLShaderPrecisionFormat? getShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype);
 
     DOMString? getShaderInfoLog(WebGLShader? shader);
 
     DOMString? getShaderSource(WebGLShader? shader);
 
     any getTexParameter(GLenum target, GLenum pname);
 
-    [Throws]
     any getUniform(WebGLProgram? program, WebGLUniformLocation? location);
 
     [NewObject]
     WebGLUniformLocation? getUniformLocation(WebGLProgram? program, DOMString name);
 
     [Throws]
     any getVertexAttrib(GLuint index, GLenum pname);
 
--- a/dom/workers/RegisterBindings.cpp
+++ b/dom/workers/RegisterBindings.cpp
@@ -1,29 +1,31 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* 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 "WorkerPrivate.h"
 #include "ChromeWorkerScope.h"
 #include "File.h"
+#include "RuntimeService.h"
 
 #include "jsapi.h"
 #include "js/OldDebugAPI.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMExceptionBinding.h"
 #include "mozilla/dom/EventBinding.h"
 #include "mozilla/dom/EventHandlerBinding.h"
 #include "mozilla/dom/EventTargetBinding.h"
 #include "mozilla/dom/FileReaderSyncBinding.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/ImageDataBinding.h"
 #include "mozilla/dom/MessageEventBinding.h"
 #include "mozilla/dom/MessagePortBinding.h"
+#include "mozilla/dom/PromiseBinding.h"
 #include "mozilla/dom/TextDecoderBinding.h"
 #include "mozilla/dom/TextEncoderBinding.h"
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 #include "mozilla/dom/XMLHttpRequestUploadBinding.h"
 #include "mozilla/dom/URLBinding.h"
 #include "mozilla/dom/WorkerBinding.h"
 #include "mozilla/dom/WorkerLocationBinding.h"
 #include "mozilla/dom/WorkerNavigatorBinding.h"
@@ -56,25 +58,27 @@ WorkerPrivate::RegisterBindings(JSContex
 
   // Init other paris-bindings.
   if (!DOMExceptionBinding::GetConstructorObject(aCx, aGlobal) ||
       !EventBinding::GetConstructorObject(aCx, aGlobal) ||
       !FileReaderSyncBinding_workers::GetConstructorObject(aCx, aGlobal) ||
       !ImageDataBinding::GetConstructorObject(aCx, aGlobal) ||
       !MessageEventBinding::GetConstructorObject(aCx, aGlobal) ||
       !MessagePortBinding::GetConstructorObject(aCx, aGlobal) ||
+      (PromiseEnabled() &&
+        !PromiseBinding::GetConstructorObject(aCx, aGlobal)) ||
       !TextDecoderBinding::GetConstructorObject(aCx, aGlobal) ||
       !TextEncoderBinding::GetConstructorObject(aCx, aGlobal) ||
       !XMLHttpRequestBinding_workers::GetConstructorObject(aCx, aGlobal) ||
       !XMLHttpRequestUploadBinding_workers::GetConstructorObject(aCx, aGlobal) ||
       !URLBinding_workers::GetConstructorObject(aCx, aGlobal) ||
       !WorkerBinding::GetConstructorObject(aCx, aGlobal) ||
       !WorkerLocationBinding_workers::GetConstructorObject(aCx, aGlobal) ||
       !WorkerNavigatorBinding_workers::GetConstructorObject(aCx, aGlobal)) {
     return false;
   }
 
   if (!JS_DefineProfilingFunctions(aCx, aGlobal)) {
     return false;
   }
 
   return true;
-}
\ No newline at end of file
+}
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -171,34 +171,19 @@ const char* gStringChars[] = {
 };
 
 static_assert(NS_ARRAY_LENGTH(gStringChars) == ID_COUNT,
               "gStringChars should have the right length.");
 
 #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
 #define DUMP_CONTROLLED_BY_PREF 1
 #define PREF_DOM_WINDOW_DUMP_ENABLED "browser.dom.window.dump.enabled"
-
-// Protected by RuntimeService::mMutex.
-// Initialized by DumpPrefChanged via RuntimeService::Init().
-bool gWorkersDumpEnabled;
+#endif
 
-static int
-DumpPrefChanged(const char* aPrefName, void* aClosure)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  bool enabled = Preferences::GetBool(PREF_DOM_WINDOW_DUMP_ENABLED, false);
-
-  Mutex* mutex = static_cast<Mutex*>(aClosure);
-  MutexAutoLock lock(*mutex);
-  gWorkersDumpEnabled = enabled;
-  return 0;
-}
-#endif
+#define PREF_PROMISE_ENABLED "dom.promise.enabled"
 
 class LiteralRebindingCString : public nsDependentCString
 {
 public:
   template<int N>
   void RebindLiteral(const char (&aStr)[N])
   {
     Rebind(aStr, N-1);
@@ -1154,16 +1139,17 @@ GetCurrentThreadJSContext()
 {
   return GetCurrentThreadWorkerPrivate()->GetJSContext();
 }
 
 END_WORKERS_NAMESPACE
 
 // This is only touched on the main thread. Initialized in Init() below.
 JSSettings RuntimeService::sDefaultJSSettings;
+bool RuntimeService::sDefaultPreferences[WORKERPREF_COUNT] = { false };
 
 RuntimeService::RuntimeService()
 : mMutex("RuntimeService::mMutex"), mObserved(false),
   mShuttingDown(false), mNavigatorStringsLoaded(false)
 {
   AssertIsOnMainThread();
   NS_ASSERTION(!gRuntimeService, "More than one service!");
 }
@@ -1564,16 +1550,21 @@ RuntimeService::Init()
     sDefaultJSSettings.gcZealFrequency = JS_DEFAULT_ZEAL_FREQ;
     sDefaultJSSettings.gcZeal = 0;
 #endif
     SetDefaultJSGCSettings(JSGC_MAX_BYTES, WORKER_DEFAULT_RUNTIME_HEAPSIZE);
     SetDefaultJSGCSettings(JSGC_ALLOCATION_THRESHOLD,
                            WORKER_DEFAULT_ALLOCATION_THRESHOLD);
   }
 
+// If dump is not controlled by pref, it's set to true.
+#ifndef DUMP_CONTROLLED_BY_PREF
+  sDefaultPreferences[WORKERPREF_DUMP] = true;
+#endif
+
   mIdleThreadTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
   NS_ENSURE_STATE(mIdleThreadTimer);
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
 
   nsresult rv =
     obs->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false);
@@ -1619,20 +1610,24 @@ RuntimeService::Init()
                                              nullptr)) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                         LoadGCZealOptions,
                                         PREF_WORKERS_OPTIONS_PREFIX PREF_GCZEAL,
                                         nullptr)) ||
 #endif
 #if DUMP_CONTROLLED_BY_PREF
       NS_FAILED(Preferences::RegisterCallbackAndCall(
-                                              DumpPrefChanged,
+                                              WorkerPrefChanged,
                                               PREF_DOM_WINDOW_DUMP_ENABLED,
-                                              &mMutex)) ||
+                                              reinterpret_cast<void *>(WORKERPREF_DUMP))) ||
 #endif
+      NS_FAILED(Preferences::RegisterCallbackAndCall(
+                                              WorkerPrefChanged,
+                                              PREF_PROMISE_ENABLED,
+                                              reinterpret_cast<void *>(WORKERPREF_PROMISE))) ||
       NS_FAILED(Preferences::RegisterCallback(LoadJSContextOptions,
                                               PREF_JS_OPTIONS_PREFIX,
                                               nullptr)) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                                     LoadJSContextOptions,
                                                     PREF_WORKERS_OPTIONS_PREFIX,
                                                     nullptr))) {
     NS_WARNING("Failed to register pref callbacks!");
@@ -1785,20 +1780,23 @@ RuntimeService::Cleanup()
 
   if (mObserved) {
     if (NS_FAILED(Preferences::UnregisterCallback(LoadJSContextOptions,
                                                   PREF_JS_OPTIONS_PREFIX,
                                                   nullptr)) ||
         NS_FAILED(Preferences::UnregisterCallback(LoadJSContextOptions,
                                                   PREF_WORKERS_OPTIONS_PREFIX,
                                                   nullptr)) ||
+        NS_FAILED(Preferences::UnregisterCallback(WorkerPrefChanged,
+                                                  PREF_PROMISE_ENABLED,
+                                                  reinterpret_cast<void *>(WORKERPREF_PROMISE))) ||
 #if DUMP_CONTROLLED_BY_PREF
-        NS_FAILED(Preferences::UnregisterCallback(DumpPrefChanged,
+        NS_FAILED(Preferences::UnregisterCallback(WorkerPrefChanged,
                                                   PREF_DOM_WINDOW_DUMP_ENABLED,
-                                                  &mMutex)) ||
+                                                  reinterpret_cast<void *>(WORKERPREF_DUMP))) ||
 #endif
 #ifdef JS_GC_ZEAL
         NS_FAILED(Preferences::UnregisterCallback(
                                              LoadGCZealOptions,
                                              PREF_JS_OPTIONS_PREFIX PREF_GCZEAL,
                                              nullptr)) ||
         NS_FAILED(Preferences::UnregisterCallback(
                                         LoadGCZealOptions,
@@ -2167,16 +2165,22 @@ void
 RuntimeService::UpdateAllWorkerJSContextOptions()
 {
   BROADCAST_ALL_WORKERS(UpdateJSContextOptions,
                         sDefaultJSSettings.content.options,
                         sDefaultJSSettings.chrome.options);
 }
 
 void
+RuntimeService::UpdateAllWorkerPreference(WorkerPreference aPref, bool aValue)
+{
+  BROADCAST_ALL_WORKERS(UpdatePreference, aPref, aValue);
+}
+
+void
 RuntimeService::UpdateAllWorkerMemoryParameter(JSGCParamKey aKey,
                                                uint32_t aValue)
 {
   BROADCAST_ALL_WORKERS(UpdateJSWorkerMemoryParameter, aKey, aValue);
 }
 
 #ifdef JS_GC_ZEAL
 void
@@ -2225,21 +2229,38 @@ RuntimeService::Observe(nsISupports* aSu
     GarbageCollectAllWorkers(true);
     return NS_OK;
   }
 
   NS_NOTREACHED("Unknown observer topic!");
   return NS_OK;
 }
 
-bool
-RuntimeService::WorkersDumpEnabled()
+/* static */ int
+RuntimeService::WorkerPrefChanged(const char* aPrefName, void* aClosure)
 {
-#if DUMP_CONTROLLED_BY_PREF
-  MutexAutoLock lock(mMutex);
-  // In optimized builds we check a pref that controls if we should
-  // enable output from dump() or not, in debug builds it's always
-  // enabled.
-  return gWorkersDumpEnabled;
-#else
-  return true;
+  AssertIsOnMainThread();
+
+  uintptr_t tmp = reinterpret_cast<uintptr_t>(aClosure);
+  MOZ_ASSERT(tmp < WORKERPREF_COUNT);
+  WorkerPreference key = static_cast<WorkerPreference>(tmp);
+
+  if (key == WORKERPREF_PROMISE) {
+    sDefaultPreferences[WORKERPREF_PROMISE] =
+      Preferences::GetBool(PREF_PROMISE_ENABLED, false);
+#ifdef DUMP_CONTROLLED_BY_PREF
+  } else if (key == WORKERPREF_DUMP) {
+    key = WORKERPREF_DUMP;
+    sDefaultPreferences[WORKERPREF_DUMP] =
+      Preferences::GetBool(PREF_DOM_WINDOW_DUMP_ENABLED, false);
 #endif
+  }
+
+  // This function should never be registered as a callback for a preference it
+  // does not handle.
+  MOZ_ASSERT(key != WORKERPREF_COUNT);
+
+  RuntimeService* rts = RuntimeService::GetService();
+  if (rts) {
+    rts->UpdateAllWorkerPreference(key, sDefaultPreferences[key]);
+  }
+  return 0;
 }
--- a/dom/workers/RuntimeService.h
+++ b/dom/workers/RuntimeService.h
@@ -96,16 +96,17 @@ class RuntimeService MOZ_FINAL : public 
 
   // Only used on the main thread.
   nsCOMPtr<nsITimer> mIdleThreadTimer;
 
   nsCString mDetectorName;
   nsCString mSystemCharset;
 
   static JSSettings sDefaultJSSettings;
+  static bool sDefaultPreferences[WORKERPREF_COUNT];
 
 public:
   struct NavigatorStrings
   {
     nsString mAppName;
     nsString mAppVersion;
     nsString mPlatform;
     nsString mUserAgent;
@@ -177,27 +178,37 @@ public:
   static void
   GetDefaultJSSettings(JSSettings& aSettings)
   {
     AssertIsOnMainThread();
     aSettings = sDefaultJSSettings;
   }
 
   static void
+  GetDefaultPreferences(bool aPreferences[WORKERPREF_COUNT])
+  {
+    AssertIsOnMainThread();
+    memcpy(aPreferences, sDefaultPreferences, WORKERPREF_COUNT * sizeof(bool));
+  }
+
+  static void
   SetDefaultJSContextOptions(const JS::ContextOptions& aContentOptions,
                              const JS::ContextOptions& aChromeOptions)
   {
     AssertIsOnMainThread();
     sDefaultJSSettings.content.options = aContentOptions;
     sDefaultJSSettings.chrome.options = aChromeOptions;
   }
 
   void
   UpdateAllWorkerJSContextOptions();
 
+  void
+  UpdateAllWorkerPreference(WorkerPreference aPref, bool aValue);
+
   static void
   SetDefaultJSGCSettings(JSGCParamKey aKey, uint32_t aValue)
   {
     AssertIsOnMainThread();
     sDefaultJSSettings.ApplyGCSetting(aKey, aValue);
   }
 
   void
@@ -236,19 +247,16 @@ public:
   }
 
   void
   UpdateAllWorkerJITHardening(bool aJITHardening);
 
   void
   GarbageCollectAllWorkers(bool aShrinking);
 
-  bool
-  WorkersDumpEnabled();
-
 private:
   RuntimeService();
   ~RuntimeService();
 
   nsresult
   Init();
 
   void
@@ -276,13 +284,16 @@ private:
   GetWorkersForWindow(nsPIDOMWindow* aWindow,
                       nsTArray<WorkerPrivate*>& aWorkers);
 
   bool
   ScheduleWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
 
   static void
   ShutdownIdleThreads(nsITimer* aTimer, void* aClosure);
+
+  static int
+  WorkerPrefChanged(const char* aPrefName, void* aClosure);
 };
 
 END_WORKERS_NAMESPACE
 
 #endif /* mozilla_dom_workers_runtimeservice_h__ */
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1460,16 +1460,39 @@ public:
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   {
     aWorkerPrivate->UpdateJSContextOptionsInternal(aCx, mContentOptions,
                                                    mChromeOptions);
     return true;
   }
 };
 
+class UpdatePreferenceRunnable : public WorkerControlRunnable
+{
+  WorkerPreference mPref;
+  bool mValue;
+
+public:
+  UpdatePreferenceRunnable(WorkerPrivate* aWorkerPrivate,
+                           WorkerPreference aPref,
+                           bool aValue)
+    : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount),
+      mPref(aPref),
+      mValue(aValue)
+  {
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+  {
+    aWorkerPrivate->UpdatePreferenceInternal(aCx, mPref, mValue);
+    return true;
+  }
+};
+
 class UpdateJSWorkerMemoryParameterRunnable : public WorkerControlRunnable
 {
   uint32_t mValue;
   JSGCParamKey mKey;
 
 public:
   UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate* aWorkerPrivate,
                                         JSGCParamKey aKey,
@@ -2775,16 +2798,31 @@ WorkerPrivateParent<Derived>::UpdateJSCo
   if (!runnable->Dispatch(aCx)) {
     NS_WARNING("Failed to update worker context options!");
     JS_ClearPendingException(aCx);
   }
 }
 
 template <class Derived>
 void
+WorkerPrivateParent<Derived>::UpdatePreference(JSContext* aCx, WorkerPreference aPref, bool aValue)
+{
+  AssertIsOnParentThread();
+  MOZ_ASSERT(aPref >= 0 && aPref < WORKERPREF_COUNT);
+
+  nsRefPtr<UpdatePreferenceRunnable> runnable =
+    new UpdatePreferenceRunnable(ParentAsWorkerPrivate(), aPref, aValue);
+  if (!runnable->Dispatch(aCx)) {
+    NS_WARNING("Failed to update worker preferences!");
+    JS_ClearPendingException(aCx);
+  }
+}
+
+template <class Derived>
+void
 WorkerPrivateParent<Derived>::UpdateJSWorkerMemoryParameter(JSContext* aCx,
                                                             JSGCParamKey aKey,
                                                             uint32_t aValue)
 {
   AssertIsOnParentThread();
 
   bool found = false;
 
@@ -3279,16 +3317,25 @@ WorkerPrivate::WorkerPrivate(JSContext* 
   mJSContext(nullptr), mErrorHandlerRecursionCount(0), mNextTimeoutId(1),
   mStatus(Pending), mSuspended(false), mTimerRunning(false),
   mRunningExpiredTimeouts(false), mCloseHandlerStarted(false),
   mCloseHandlerFinished(false), mMemoryReporterRunning(false),
   mBlockedForMemoryReporter(false)
 {
   MOZ_ASSERT_IF(IsSharedWorker(), !aSharedWorkerName.IsVoid());
   MOZ_ASSERT_IF(!IsSharedWorker(), aSharedWorkerName.IsEmpty());
+
+  if (aParent) {
+    aParent->AssertIsOnWorkerThread();
+    aParent->GetAllPreferences(mPreferences);
+  }
+  else {
+    AssertIsOnMainThread();
+    RuntimeService::GetDefaultPreferences(mPreferences);
+  }
 }
 
 WorkerPrivate::~WorkerPrivate()
 {
 }
 
 // static
 already_AddRefed<WorkerPrivate>
@@ -5079,16 +5126,29 @@ WorkerPrivate::UpdateJSContextOptionsInt
 
   for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
     mChildWorkers[index]->UpdateJSContextOptions(aCx, aContentOptions,
                                                  aChromeOptions);
   }
 }
 
 void
+WorkerPrivate::UpdatePreferenceInternal(JSContext* aCx, WorkerPreference aPref, bool aValue)
+{
+  AssertIsOnWorkerThread();
+  MOZ_ASSERT(aPref >= 0 && aPref < WORKERPREF_COUNT);
+
+  mPreferences[aPref] = aValue;
+
+  for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
+    mChildWorkers[index]->UpdatePreference(aCx, aPref, aValue);
+  }
+}
+
+void
 WorkerPrivate::UpdateJSWorkerMemoryParameterInternal(JSContext* aCx,
                                                      JSGCParamKey aKey,
                                                      uint32_t aValue)
 {
   AssertIsOnWorkerThread();
 
   // XXX aValue might be 0 here (telling us to unset a previous value for child
   // workers). Calling JS_SetGCParameter with a value of 0 isn't actually
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -472,16 +472,19 @@ public:
   uint64_t
   GetInnerWindowId();
 
   void
   UpdateJSContextOptions(JSContext* aCx, const JS::ContextOptions& aChromeOptions,
                          const JS::ContextOptions& aContentOptions);
 
   void
+  UpdatePreference(JSContext* aCx, WorkerPreference aPref, bool aValue);
+
+  void
   UpdateJSWorkerMemoryParameter(JSContext* aCx, JSGCParamKey key,
                                 uint32_t value);
 
 #ifdef JS_GC_ZEAL
   void
   UpdateGCZeal(JSContext* aCx, uint8_t aGCZeal, uint32_t aFrequency);
 #endif
 
@@ -810,16 +813,18 @@ class WorkerPrivate : public WorkerPriva
   bool mCloseHandlerFinished;
   bool mMemoryReporterRunning;
   bool mBlockedForMemoryReporter;
 
 #ifdef DEBUG
   nsCOMPtr<nsIThread> mThread;
 #endif
 
+  bool mPreferences[WORKERPREF_COUNT];
+
 protected:
   ~WorkerPrivate();
 
 public:
   static already_AddRefed<WorkerPrivate>
   Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL,
               ErrorResult& aRv);
 
@@ -973,16 +978,19 @@ public:
     mCloseHandlerFinished = true;
   }
 
   void
   UpdateJSContextOptionsInternal(JSContext* aCx, const JS::ContextOptions& aContentOptions,
                                  const JS::ContextOptions& aChromeOptions);
 
   void
+  UpdatePreferenceInternal(JSContext* aCx, WorkerPreference aPref, bool aValue);
+
+  void
   UpdateJSWorkerMemoryParameterInternal(JSContext* aCx, JSGCParamKey key, uint32_t aValue);
 
   void
   ScheduleDeletion(bool aWasPending);
 
   bool
   BlockAndCollectRuntimeStats(JS::RuntimeStats* aRtStats);
 
@@ -1064,16 +1072,30 @@ public:
   GetMessagePort(uint64_t aMessagePortSerial);
 
   JSObject*
   CreateGlobalScope(JSContext* aCx);
 
   bool
   RegisterBindings(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
 
+  bool
+  DumpEnabled() const
+  {
+    AssertIsOnWorkerThread();
+    return mPreferences[WORKERPREF_DUMP];
+  }
+
+  bool
+  PromiseEnabled() const
+  {
+    AssertIsOnWorkerThread();
+    return mPreferences[WORKERPREF_PROMISE];
+  }
+
 private:
   WorkerPrivate(JSContext* aCx, WorkerPrivate* aParent,
                 const nsAString& aScriptURL, bool aIsChromeWorker,
                 WorkerType aWorkerType, const nsAString& aSharedWorkerName,
                 LoadInfo& aLoadInfo);
 
   bool
   Dispatch(WorkerRunnable* aEvent, EventQueue* aQueue);
@@ -1141,16 +1163,23 @@ private:
 
   void
   PostMessageToParentInternal(JSContext* aCx,
                               JS::Handle<JS::Value> aMessage,
                               const Optional<Sequence<JS::Value>>& aTransferable,
                               bool aToMessagePort,
                               uint64_t aMessagePortSerial,
                               ErrorResult& aRv);
+
+  void
+  GetAllPreferences(bool aPreferences[WORKERPREF_COUNT]) const
+  {
+    AssertIsOnWorkerThread();
+    memcpy(aPreferences, mPreferences, WORKERPREF_COUNT * sizeof(bool));
+  }
 };
 
 // This class is only used to trick the DOM bindings.  We never create
 // instances of it, and static_casting to it is fine since it doesn't add
 // anything to WorkerPrivate.
 class ChromeWorkerPrivate : public WorkerPrivate
 {
 public:
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -229,20 +229,17 @@ void
 WorkerGlobalScope::Dump(const Optional<nsAString>& aString) const
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (!aString.WasPassed()) {
     return;
   }
 
-  RuntimeService* runtimeService = RuntimeService::GetService();
-  MOZ_ASSERT(runtimeService);
-
-  if (!runtimeService->WorkersDumpEnabled()) {
+  if (!mWorkerPrivate->DumpEnabled()) {
     return;
   }
 
   NS_ConvertUTF16toUTF8 str(aString.Value());
 
 #ifdef ANDROID
   __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", str.get());
 #endif
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -157,16 +157,23 @@ struct JSSettings
       foundSetting->Unset();
       return true;
     }
 
     return false;
   }
 };
 
+enum WorkerPreference
+{
+  WORKERPREF_DUMP = 0, // browser.dom.window.dump.enabled
+  WORKERPREF_PROMISE,  // dom.promise.enabled
+  WORKERPREF_COUNT
+};
+
 // All of these are implemented in RuntimeService.cpp
 bool
 ResolveWorkerClasses(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId,
                      unsigned aFlags, JS::MutableHandle<JSObject*> aObjp);
 
 void
 CancelWorkersForWindow(nsPIDOMWindow* aWindow);
 
--- a/dom/workers/test/mochitest.ini
+++ b/dom/workers/test/mochitest.ini
@@ -22,16 +22,17 @@ support-files =
   json_worker.js
   loadEncoding_worker.js
   location_worker.js
   longThread_worker.js
   multi_sharedWorker_frame.html
   multi_sharedWorker_sharedWorker.js
   navigator_worker.js
   newError_worker.js
+  promise_worker.js
   recursion_worker.js
   recursiveOnerror_worker.js
   relativeLoad_import.js
   relativeLoad_worker.js
   relativeLoad_worker2.js
   rvals_worker.js
   sharedWorker_sharedWorker.js
   simpleThread_worker.js
@@ -79,16 +80,17 @@ support-files =
 [test_loadEncoding.html]
 [test_loadError.html]
 [test_location.html]
 [test_longThread.html]
 [test_multi_sharedWorker.html]
 [test_multi_sharedWorker_lifetimes.html]
 [test_navigator.html]
 [test_newError.html]
+[test_promise.html]
 [test_recursion.html]
 [test_recursiveOnerror.html]
 [test_relativeLoad.html]
 [test_resolveWorker-assignment.html]
 [test_resolveWorker.html]
 [test_rvals.html]
 [test_sharedWorker.html]
 [test_simpleThread.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/promise_worker.js
@@ -0,0 +1,395 @@
+function ok(a, msg) {
+  dump("OK: " + !!a + "  =>  " + a + " " + msg + "\n");
+  postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
+}
+
+function is(a, b, msg) {
+  dump("IS: " + (a===b) + "  =>  " + a + " | " + b + " " + msg + "\n");
+  postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
+}
+
+function isnot(a, b, msg) {
+  dump("ISNOT: " + (a!==b) + "  =>  " + a + " | " + b + " " + msg + "\n");
+  postMessage({type: 'status', status: a !== b, msg: a + " !== " + b + ": " + msg });
+}
+
+function promiseResolve() {
+  ok(Promise, "Promise object should exist");
+
+  var promise = new Promise(function(resolve, reject) {
+    ok(resolve, "Promise.resolve exists");
+    ok(reject, "Promise.reject exists");
+
+    resolve(42);
+  }).then(function(what) {
+    ok(true, "Then - resolveCb has been called");
+    is(what, 42, "ResolveCb received 42");
+    runTest();
+  }, function() {
+    ok(false, "Then - rejectCb has been called");
+    runTest();
+  });
+}
+
+
+function promiseReject() {
+  var promise = new Promise(function(resolve, reject) {
+    reject(42);
+  }).then(function(what) {
+    ok(false, "Then - resolveCb has been called");
+    runTest();
+  }, function(what) {
+    ok(true, "Then - rejectCb has been called");
+    is(what, 42, "RejectCb received 42");
+    runTest();
+  });
+}
+
+function promiseException() {
+  var promise = new Promise(function(resolve, reject) {
+    throw 42;
+  }).then(function(what) {
+    ok(false, "Then - resolveCb has been called");
+    runTest();
+  }, function(what) {
+    ok(true, "Then - rejectCb has been called");
+    is(what, 42, "RejectCb received 42");
+    runTest();
+  });
+}
+
+function promiseAsync() {
+  var global = "foo";
+  var f = new Promise(function(r1, r2) {
+    is(global, "foo", "Global should be foo");
+    r1(42);
+    is(global, "foo", "Global should still be foo");
+    setTimeout(function() {
+      is(global, "bar", "Global should still be bar!");
+      runTest();
+    }, 0);
+  }).then(function() {
+    global = "bar";
+  });
+  is(global, "foo", "Global should still be foo (2)");
+}
+
+function promiseDoubleThen() {
+  var steps = 0;
+  var promise = new Promise(function(r1, r2) {
+    r1(42);
+  });
+
+  promise.then(function(what) {
+    ok(true, "Then.resolve has been called");
+    is(what, 42, "Value == 42");
+    steps++;
+  }, function(what) {
+    ok(false, "Then.reject has been called");
+  });
+
+  promise.then(function(what) {
+    ok(true, "Then.resolve has been called");
+    is(steps, 1, "Then.resolve - step == 1");
+    is(what, 42, "Value == 42");
+    runTest();
+  }, function(what) {
+    ok(false, "Then.reject has been called");
+  });
+}
+
+function promiseThenException() {
+  var promise = new Promise(function(resolve, reject) {
+    resolve(42);
+  });
+
+  promise.then(function(what) {
+    ok(true, "Then.resolve has been called");
+    throw "booh";
+  }).catch(function(e) {
+    ok(true, "Catch has been called!");
+    runTest();
+  });
+}
+
+function promiseThenCatchThen() {
+  var promise = new Promise(function(resolve, reject) {
+    resolve(42);
+  });
+
+  var promise2 = promise.then(function(what) {
+    ok(true, "Then.resolve has been called");
+    is(what, 42, "Value == 42");
+    return what + 1;
+  }, function(what) {
+    ok(false, "Then.reject has been called");
+  });
+
+  isnot(promise, promise2, "These 2 promise objs are different");
+
+  promise2.then(function(what) {
+    ok(true, "Then.resolve has been called");
+    is(what, 43, "Value == 43");
+    return what + 1;
+  }, function(what) {
+    ok(false, "Then.reject has been called");
+  }).catch(function() {
+    ok(false, "Catch has been called");
+  }).then(function(what) {
+    ok(true, "Then.resolve has been called");
+    is(what, 44, "Value == 44");
+    runTest();
+  }, function(what) {
+    ok(false, "Then.reject has been called");
+  });
+}
+
+function promiseRejectThenCatchThen() {
+  var promise = new Promise(function(resolve, reject) {
+    reject(42);
+  });
+
+  var promise2 = promise.then(function(what) {
+    ok(false, "Then.resolve has been called");
+  }, function(what) {
+    ok(true, "Then.reject has been called");
+    is(what, 42, "Value == 42");
+    return what + 1;
+  });
+
+  isnot(promise, promise2, "These 2 promise objs are different");
+
+  promise2.then(function(what) {
+    ok(true, "Then.resolve has been called");
+    is(what, 43, "Value == 43");
+    return what+1;
+  }).catch(function(what) {
+    ok(false, "Catch has been called");
+  }).then(function(what) {
+    ok(true, "Then.resolve has been called");
+    is(what, 44, "Value == 44");
+    runTest();
+  });
+}
+
+function promiseRejectThenCatchThen2() {
+  var promise = new Promise(function(resolve, reject) {
+    reject(42);
+  });
+
+  promise.then(function(what) {
+    ok(true, "Then.resolve has been called");
+    is(what, 42, "Value == 42");
+    return what+1;
+  }).catch(function(what) {
+    is(what, 42, "Value == 42");
+    ok(true, "Catch has been called");
+    return what+1;
+  }).then(function(what) {
+    ok(true, "Then.resolve has been called");
+    is(what, 43, "Value == 43");
+    runTest();
+  });
+}
+
+function promiseRejectThenCatchExceptionThen() {
+  var promise = new Promise(function(resolve, reject) {
+    reject(42);
+  });
+
+  promise.then(function(what) {
+    ok(false, "Then.resolve has been called");
+  }, function(what) {
+    ok(true, "Then.reject has been called");
+    is(what, 42, "Value == 42");
+    throw(what + 1);
+  }).catch(function(what) {
+    ok(true, "Catch has been called");
+    is(what, 43, "Value == 43");
+    return what + 1;
+  }).then(function(what) {
+    ok(true, "Then.resolve has been called");
+    is(what, 44, "Value == 44");
+    runTest();
+  });
+}
+
+function promiseThenCatchOrderingResolve() {
+  var global = 0;
+  var f = new Promise(function(r1, r2) {
+    r1(42);
+  });
+
+  f.then(function() {
+    f.then(function() {
+      global++;
+    });
+    f.catch(function() {
+      global++;
+    });
+    f.then(function() {
+      global++;
+    });
+    setTimeout(function() {
+      is(global, 2, "Many steps... should return 2");
+      runTest();
+    }, 0);
+  });
+}
+
+function promiseThenCatchOrderingReject() {
+  var global = 0;
+  var f = new Promise(function(r1, r2) {
+    r2(42);
+  })
+
+  f.then(function() {}, function() {
+    f.then(function() {
+      global++;
+    });
+    f.catch(function() {
+      global++;
+    });
+    f.then(function() {}, function() {
+      global++;
+    });
+    setTimeout(function() {
+      is(global, 2, "Many steps... should return 2");
+      runTest();
+    }, 0);
+  });
+}
+
+function promiseNestedPromise() {
+  new Promise(function(resolve, reject) {
+    resolve(new Promise(function(resolve, reject) {
+      ok(true, "Nested promise is executed");
+      resolve(42);
+    }));
+  }).then(function(value) {
+    is(value, 42, "Nested promise is executed and then == 42");
+    runTest();
+  });
+}
+
+function promiseNestedNestedPromise() {
+  new Promise(function(resolve, reject) {
+    resolve(new Promise(function(resolve, reject) {
+      ok(true, "Nested promise is executed");
+      resolve(42);
+    }).then(function(what) { return what+1; }));
+  }).then(function(value) {
+    is(value, 43, "Nested promise is executed and then == 43");
+    runTest();
+  });
+}
+
+function promiseWrongNestedPromise() {
+  new Promise(function(resolve, reject) {
+    resolve(new Promise(function(r, r2) {
+      ok(true, "Nested promise is executed");
+      r(42);
+    }));
+    reject(42);
+  }).then(function(value) {
+    is(value, 42, "Nested promise is executed and then == 42");
+    runTest();
+  }, function(value) {
+     ok(false, "This is wrong");
+  });
+}
+
+function promiseLoop() {
+  new Promise(function(resolve, reject) {
+    resolve(new Promise(function(r1, r2) {
+      ok(true, "Nested promise is executed");
+      r1(new Promise(function(r1, r2) {
+        ok(true, "Nested nested promise is executed");
+        r1(42);
+      }));
+    }));
+  }).then(function(value) {
+    is(value, 42, "Nested nested promise is executed and then == 42");
+    runTest();
+  }, function(value) {
+     ok(false, "This is wrong");
+  });
+}
+
+function promiseStaticReject() {
+  var promise = Promise.reject(42).then(function(what) {
+    ok(false, "This should not be called");
+  }, function(what) {
+    is(what, 42, "Value == 42");
+    runTest();
+  });
+}
+
+function promiseStaticResolve() {
+  var promise = Promise.resolve(42).then(function(what) {
+    is(what, 42, "Value == 42");
+    runTest();
+  }, function() {
+    ok(false, "This should not be called");
+  });
+}
+
+function promiseResolveNestedPromise() {
+  var promise = Promise.resolve(new Promise(function(r, r2) {
+    ok(true, "Nested promise is executed");
+    r(42);
+  }, function() {
+    ok(false, "This should not be called");
+  })).then(function(what) {
+    is(what, 42, "Value == 42");
+    runTest();
+  }, function() {
+    ok(false, "This should not be called");
+  });
+}
+
+function promiseRejectNoHandler() {
+  // This test only checks that the code that reports unhandled errors in the
+  // Promises implementation does not crash or leak.
+  var promise = new Promise(function(res, rej) {
+    noSuchMethod();
+  });
+  runTest();
+}
+
+var tests = [
+    promiseResolve,
+    promiseReject,
+    promiseException,
+    promiseAsync,
+    promiseDoubleThen,
+    promiseThenException,
+    promiseThenCatchThen,
+    promiseRejectThenCatchThen,
+    promiseRejectThenCatchThen2,
+    promiseRejectThenCatchExceptionThen,
+    promiseThenCatchOrderingResolve,
+    promiseThenCatchOrderingReject,
+    promiseNestedPromise,
+    promiseNestedNestedPromise,
+    promiseWrongNestedPromise,
+    promiseLoop,
+    promiseStaticReject,
+    promiseStaticResolve,
+    promiseResolveNestedPromise,
+    promiseRejectNoHandler,
+];
+
+function runTest() {
+  if (!tests.length) {
+    postMessage({ type: 'finish' });
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
+
+onmessage = function() {
+  runTest();
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_promise.html
@@ -0,0 +1,44 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Promise object in workers</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+  function runTest() {
+    var worker = new Worker("promise_worker.js");
+
+    worker.onmessage = function(event) {
+
+      if (event.data.type == 'finish') {
+        SimpleTest.finish();
+      } else if (event.data.type == 'status') {
+        ok(event.data.status, event.data.msg);
+      }
+    }
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    worker.postMessage(true);
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({"set": [["dom.promise.enabled", true]]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
--- a/extensions/auth/moz.build
+++ b/extensions/auth/moz.build
@@ -1,28 +1,24 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-UNIFIED_SOURCES += [
-    'nsAuthFactory.cpp',
-    'nsAuthGSSAPI.cpp',
+SOURCES += [
+    'nsAuthFactory.cpp',       # forces NSPR logging
+    'nsAuthGSSAPI.cpp',        # forces NSPR logging
     'nsAuthSASL.cpp',
-]
-
-# contains constants whose names conflict with constants in other files
-SOURCES += [
-    'nsHttpNegotiateAuth.cpp',
+    'nsHttpNegotiateAuth.cpp', # contains constants whose names conflict with constants in other files
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     SOURCES += [
         'nsAuthSSPI.cpp',
     ]
     DEFINES['USE_SSPI'] = True
 else:
-    UNIFIED_SOURCES += [
-        'nsAuthSambaNTLM.cpp',
+    SOURCES += [
+        'nsAuthSambaNTLM.cpp', # forces NSPR logging
     ]
 
 FINAL_LIBRARY = 'xul'
--- a/extensions/pref/autoconfig/src/moz.build
+++ b/extensions/pref/autoconfig/src/moz.build
@@ -1,14 +1,15 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-UNIFIED_SOURCES += [
+# These files cannot be built in unified mode because they force NSPR logging.
+SOURCES += [
     'nsAutoConfig.cpp',
     'nsConfigFactory.cpp',
     'nsJSConfigTriggers.cpp',
     'nsReadConfig.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
--- a/gfx/thebes/Makefile.in
+++ b/gfx/thebes/Makefile.in
@@ -74,10 +74,8 @@ endif
 
 ifdef SOLARIS_SUNPRO_CXX
 gfxAlphaRecoverySSE2.$(OBJ_SUFFIX): OS_CXXFLAGS += -xarch=sse2 -xO4
 endif
 endif
 
 PremultiplyTables.h: $(srcdir)/genTables.py
 	$(PYTHON) $(srcdir)/genTables.py
-
-gfxUtils.$(OBJ_SUFFIX): PremultiplyTables.h
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -3306,16 +3306,27 @@ gfxFont::ShapeFragmentWithoutWordCache(g
         aText += fragLen;
         aOffset += fragLen;
         aLength -= fragLen;
     }
 
     return ok;
 }
 
+// Check if aCh is an unhandled control character that should be displayed
+// as a hexbox rather than rendered by some random font on the system.
+// We exclude \r as stray &#13;s are rather common (bug 941940).
+// Note that \n and \t don't come through here, as they have specific
+// meanings that have already been handled.
+static bool
+IsInvalidControlChar(uint32_t aCh)
+{
+    return aCh != '\r' && ((aCh & 0x7f) < 0x20 || aCh == 0x7f);
+}
+
 template<typename T>
 bool
 gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext,
                                    const T    *aText,
                                    uint32_t    aOffset,
                                    uint32_t    aLength,
                                    int32_t     aScript,
                                    gfxTextRun *aTextRun)
@@ -3345,17 +3356,17 @@ gfxFont::ShapeTextWithoutWordCache(gfxCo
 
         // fragment was terminated by an invalid char: skip it,
         // unless it's a control char that we want to show as a hexbox,
         // but record where TAB or NEWLINE occur
         if (ch == '\t') {
             aTextRun->SetIsTab(aOffset + i);
         } else if (ch == '\n') {
             aTextRun->SetIsNewline(aOffset + i);
-        } else if ((ch & 0x7f) < 0x20 || ch == 0x7f) {
+        } else if (IsInvalidControlChar(ch)) {
             aTextRun->SetMissingGlyph(aOffset + i, ch, this);
         }
         fragStart = i + 1;
     }
 
     NS_WARN_IF_FALSE(ok, "failed to shape text - expect garbled text");
     return ok;
 }
@@ -3489,17 +3500,17 @@ gfxFont::SplitAndInitTextRun(gfxContext 
 
         // word was terminated by an invalid char: skip it,
         // unless it's a control char that we want to show as a hexbox,
         // but record where TAB or NEWLINE occur
         if (ch == '\t') {
             aTextRun->SetIsTab(aRunStart + i);
         } else if (ch == '\n') {
             aTextRun->SetIsNewline(aRunStart + i);
-        } else if ((ch & 0x7f) < 0x20 || ch == 0x7f) {
+        } else if (IsInvalidControlChar(ch)) {
             aTextRun->SetMissingGlyph(aRunStart + i, ch, this);
         }
 
         hash = 0;
         wordStart = i + 1;
         wordIs8Bit = true;
     }
 
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -274,8 +274,12 @@ FAIL_ON_WARNINGS = not CONFIG['_MSC_VER'
 
 MSVC_ENABLE_PGO = True
 
 LIBRARY_NAME = 'thebes'
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
+
+GENERATED_FILES = [
+    'PremultiplyTables.h',
+]
--- a/intl/locale/src/Makefile.in
+++ b/intl/locale/src/Makefile.in
@@ -14,19 +14,13 @@ EXPORT_RESOURCE = \
 
 
 LOCAL_INCLUDES = \
 	-I$(topsrcdir)/intl/uconv/src \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
-nsCharsetAlias.$(OBJ_SUFFIX): charsetalias.properties.h
-
 charsetalias.properties.h: props2arrays.py charsetalias.properties
 	$(PYTHON) $^ $@
 
-GARBAGE += \
-	charsetalias.properties.h \
-	$(NULL)
-
 libs::
 	$(INSTALL) $(EXPORT_RESOURCE) $(DIST)/bin/res
--- a/intl/locale/src/moz.build
+++ b/intl/locale/src/moz.build
@@ -29,8 +29,12 @@ SOURCES += [
 
 EXTRA_JS_MODULES += [
     'PluralForm.jsm',
 ]
 
 MSVC_ENABLE_PGO = True
 
 FINAL_LIBRARY = 'i18n'
+
+GENERATED_FILES = [
+    'charsetalias.properties.h',
+]
--- a/intl/locale/src/os2/Makefile.in
+++ b/intl/locale/src/os2/Makefile.in
@@ -2,16 +2,10 @@
 # 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 $(topsrcdir)/config/rules.mk
 
 INCLUDES	+= -I$(srcdir)/..
 
-nsOS2Charset.$(OBJ_SUFFIX): os2charset.properties.h
-
 os2charset.properties.h: $(srcdir)/../props2arrays.py os2charset.properties
 	$(PYTHON) $^ $@
-
-GARBAGE += \
-	os2charset.properties.h \
-	$(NULL)
--- a/intl/locale/src/os2/moz.build
+++ b/intl/locale/src/os2/moz.build
@@ -7,8 +7,12 @@
 SOURCES += [
     'nsCollationOS2.cpp',
     'nsDateTimeFormatOS2.cpp',
     'nsOS2Charset.cpp',
     'nsOS2Locale.cpp',
 ]
 
 FINAL_LIBRARY = 'i18n'
+
+GENERATED_FILES = [
+    'os2charset.properties.h',
+]
--- a/intl/locale/src/unix/Makefile.in
+++ b/intl/locale/src/unix/Makefile.in
@@ -11,16 +11,10 @@ INCLUDES	+= -I$(srcdir)/..
 # Define _XOPEN_SOURCE so CODESET will get defined and thus allow
 # nl_langinfo(CODESET) to compile on these systems.
 ifeq ($(OS_ARCH), Linux)
 DEFINES		+= -D_XOPEN_SOURCE=500
 endif
 
 DEFINES += -DOSTYPE=\"$(OS_CONFIG)\"
 
-nsUNIXCharset.$(OBJ_SUFFIX): unixcharset.properties.h
-
 unixcharset.properties.h: $(srcdir)/../props2arrays.py unixcharset.properties
 	$(PYTHON) $^ $@
-
-GARBAGE += \
-	unixcharset.properties.h \
-	$(NULL)
--- a/intl/locale/src/unix/moz.build
+++ b/intl/locale/src/unix/moz.build
@@ -15,8 +15,12 @@ if CONFIG['OS_TARGET'] == 'Android':
         'nsAndroidCharset.cpp',
     ]
 else:
     SOURCES += [
         'nsUNIXCharset.cpp',
     ]
 
 FINAL_LIBRARY = 'i18n'
+
+GENERATED_FILES = [
+    'unixcharset.properties.h',
+]
--- a/intl/locale/src/windows/Makefile.in
+++ b/intl/locale/src/windows/Makefile.in
@@ -2,16 +2,10 @@
 # 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 $(topsrcdir)/config/rules.mk
 
 INCLUDES	+= -I$(srcdir)/..
 
-nsWinCharset.$(OBJ_SUFFIX): wincharset.properties.h
-
 wincharset.properties.h: $(srcdir)/../props2arrays.py wincharset.properties
 	$(PYTHON) $^ $@
-
-GARBAGE += \
-	wincharset.properties.h \
-	$(NULL)
--- a/intl/locale/src/windows/moz.build
+++ b/intl/locale/src/windows/moz.build
@@ -7,8 +7,12 @@
 SOURCES += [
     'nsCollationWin.cpp',
     'nsDateTimeFormatWin.cpp',
     'nsWin32Locale.cpp',
     'nsWinCharset.cpp',
 ]
 
 FINAL_LIBRARY = 'i18n'
+
+GENERATED_FILES = [
+    'wincharset.properties.h',
+]
--- a/ipc/ipdl/test/cxx/app/Makefile.in
+++ b/ipc/ipdl/test/cxx/app/Makefile.in
@@ -5,17 +5,16 @@
 NSDISTMODE = copy
 
 LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
 LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/base
 
 LIBS = \
   $(DIST)/lib/$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \
   $(LIBXUL_LIBS) \
-  $(MOZ_JS_LIBS) \
   $(NSPR_LIBS) \
   $(NULL)
 
 include $(topsrcdir)/config/config.mk
 
 ifdef _MSC_VER
 WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
 endif
deleted file mode 100644
--- a/js/ductwork/debugger/Makefile.in
+++ /dev/null
@@ -1,8 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-EXTRA_DSO_LDOPTS += \
-  $(MOZ_COMPONENT_LIBS) \
-  $(MOZ_JS_LIBS) \
-  $(NULL)
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -601,18 +601,16 @@ endif
 
 # Force auto-header generation before compiling any source that may use them
 $(OBJS): $(CURDIR)/jsautokw.h $(CURDIR)/jsautooplen.h
 
 ifdef MOZ_ETW
 ETWProvider.h ETWProvider.rc ETWProvider.mof: ETWProvider.man
 	$(MC) -um -mof $^
 
-Probes.$(OBJ_SUFFIX): ETWProvider.h
-
 ETWProvider.res: ETWProvider.rc
 	$(RC) -r -i "$(SDKDIR)Include" $^
 
 export:: ETWProvider.res
 
 install:: ETWProvider.mof ETWProvider.man
 	$(SYSINSTALL) $^ $(DESTDIR)$(bindir)
 
--- a/js/src/assembler/assembler/AssemblerBuffer.h
+++ b/js/src/assembler/assembler/AssemblerBuffer.h
@@ -52,39 +52,39 @@
     do {                                                    \
         spew("FIXME insn printing %s:%d",                   \
              __FILE__, __LINE__);                           \
     } while (0)
 
 namespace JSC {
 
     class AssemblerBuffer {
-        static const int inlineCapacity = 256;
+        static const size_t inlineCapacity = 256;
     public:
         AssemblerBuffer()
             : m_buffer(m_inlineBuffer)
             , m_capacity(inlineCapacity)
             , m_size(0)
             , m_oom(false)
         {
         }
 
         ~AssemblerBuffer()
         {
             if (m_buffer != m_inlineBuffer)
                 js_free(m_buffer);
         }
 
-        void ensureSpace(int space)
+        void ensureSpace(size_t space)
         {
             if (m_size > m_capacity - space)
                 grow();
         }
 
-        bool isAligned(int alignment) const
+        bool isAligned(size_t alignment) const
         {
             return !(m_size & (alignment - 1));
         }
 
         void putByteUnchecked(int value)
         {
             ASSERT(!(m_size > m_capacity - 4));
             m_buffer[m_size] = char(value);
@@ -133,17 +133,17 @@ namespace JSC {
             putIntUnchecked(value);
         }
 
         void* data() const
         {
             return m_buffer;
         }
 
-        int size() const
+        size_t size() const
         {
             return m_size;
         }
 
         bool oom() const
         {
             return m_oom;
         }
@@ -172,17 +172,17 @@ namespace JSC {
         }
 
         unsigned char *buffer() const {
             ASSERT(!m_oom);
             return reinterpret_cast<unsigned char *>(m_buffer);
         }
 
     protected:
-        void append(const char* data, int size)
+        void append(const char* data, size_t size)
         {
             if (m_size > m_capacity - size)
                 grow(size);
 
             // If we OOM and size > inlineCapacity, this would crash.
             if (m_oom)
                 return;
             memcpy(m_buffer + m_size, data, size);
@@ -199,28 +199,37 @@ namespace JSC {
          * Our strategy for handling an OOM is to set m_oom, and then set
          * m_size to 0, preserving the current buffer. This way, the user
          * can continue assembling into the buffer, deferring OOM checking
          * until the user wants to read code out of the buffer.
          *
          * See also the |executableAllocAndCopy| and |buffer| methods.
          */
 
-        void grow(int extraCapacity = 0)
+        void grow(size_t extraCapacity = 0)
         {
+            char* newBuffer;
+
             /*
              * If |extraCapacity| is zero (as it almost always is) this is an
              * allocator-friendly doubling growth strategy.
              */
-            int newCapacity = m_capacity + m_capacity + extraCapacity;
-            char* newBuffer;
+            size_t doubleCapacity = m_capacity + m_capacity;
 
-            // Do not allow offsets to grow beyond INT_MAX / 2. This mirrors
-            // Assembler-shared.h.
-            if (newCapacity >= INT_MAX / 2) {
+            // Check for overflow.
+            if (doubleCapacity < m_capacity) {
+                m_size = 0;
+                m_oom = true;
+                return;
+            }
+
+            size_t newCapacity = doubleCapacity + extraCapacity;
+
+            // Check for overflow.
+            if (newCapacity < doubleCapacity) {
                 m_size = 0;
                 m_oom = true;
                 return;
             }
 
             if (m_buffer == m_inlineBuffer) {
                 newBuffer = static_cast<char*>(js_malloc(newCapacity));
                 if (!newBuffer) {
@@ -239,18 +248,18 @@ namespace JSC {
             }
 
             m_buffer = newBuffer;
             m_capacity = newCapacity;
         }
 
         char m_inlineBuffer[inlineCapacity];
         char* m_buffer;
-        int m_capacity;
-        int m_size;
+        size_t m_capacity;
+        size_t m_size;
         bool m_oom;
     };
 
     class GenericAssembler
     {
         js::Sprinter *printer;
 
       public:
--- a/js/src/config/rules.mk
+++ b/js/src/config/rules.mk
@@ -126,17 +126,17 @@ ifdef COMPILE_ENVIRONMENT
 
 # Compile the tests to $(DIST)/bin.  Make lots of niceties available by default
 # through TestHarness.h, by modifying the list of includes and the libs against
 # which stuff links.
 CPPSRCS += $(CPP_UNIT_TESTS)
 CPP_UNIT_TEST_BINS := $(CPP_UNIT_TESTS:.cpp=$(BIN_SUFFIX))
 SIMPLE_PROGRAMS += $(CPP_UNIT_TEST_BINS)
 INCLUDES += -I$(DIST)/include/testing
-LIBS += $(XPCOM_GLUE_LDOPTS) $(NSPR_LIBS) $(MOZ_JS_LIBS) $(if $(JS_SHARED_LIBRARY),,$(MOZ_ZLIB_LIBS))
+LIBS += $(XPCOM_GLUE_LDOPTS) $(NSPR_LIBS) $(if $(JS_SHARED_LIBRARY),,$(MOZ_ZLIB_LIBS))
 
 ifndef MOZ_PROFILE_GENERATE
 libs:: $(CPP_UNIT_TEST_BINS) $(call mkdir_deps,$(DIST)/cppunittests)
 	$(NSINSTALL) $(CPP_UNIT_TEST_BINS) $(DIST)/cppunittests
 endif
 
 check::
 	@$(PYTHON) $(topsrcdir)/testing/runcppunittests.py --xre-path=$(DIST)/bin --symbols-path=$(DIST)/crashreporter-symbols $(subst .cpp,$(BIN_SUFFIX),$(CPP_UNIT_TESTS))
@@ -1704,8 +1704,12 @@ default all:: $(PURGECACHES_FILES)
 
 #############################################################################
 # Derived targets and dependencies
 
 include $(topsrcdir)/config/makefiles/autotargets.mk
 ifneq ($(NULL),$(AUTO_DEPS))
   default all libs tools export:: $(AUTO_DEPS)
 endif
+
+export:: $(GENERATED_FILES)
+
+GARBAGE += $(GENERATED_FILES)
--- a/js/src/jit-test/tests/ion/bug938431.js
+++ b/js/src/jit-test/tests/ion/bug938431.js
@@ -1,6 +1,9 @@
+if (typeof ParallelArray === "undefined")
+  quit();
+
 function x() {}
 ParallelArray(3385, function(y) {
     Object.defineProperty([], 8, {
         e: (y ? x : Math.fround(1))
     })
 })
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -779,17 +779,17 @@ BacktrackingAllocator::distributeUses(Li
         LiveInterval *addInterval = nullptr;
         for (size_t i = 0; i < newIntervals.length(); i++) {
             LiveInterval *newInterval = newIntervals[i];
             if (newInterval->covers(pos)) {
                 if (!addInterval || newInterval->start() < addInterval->start())
                     addInterval = newInterval;
             }
         }
-        addInterval->addUse(new(alloc()) UsePosition(iter->use, iter->pos));
+        addInterval->addUseAtEnd(new(alloc()) UsePosition(iter->use, iter->pos));
     }
 }
 
 bool
 BacktrackingAllocator::split(LiveInterval *interval,
                              const LiveIntervalVector &newIntervals)
 {
     if (IonSpewEnabled(IonSpew_RegAlloc)) {
@@ -1118,34 +1118,34 @@ BacktrackingAllocator::populateSafepoint
         for (size_t j = 1; j < reg->numIntervals(); j++)
             end = Max(end, reg->getInterval(j)->end());
 
         for (size_t j = firstSafepoint; j < graph.numSafepoints(); j++) {
             LInstruction *ins = graph.getSafepoint(j);
 
             // Stop processing safepoints if we know we're out of this virtual
             // register's range.
-            if (end < inputOf(ins))
+            if (end < outputOf(ins))
                 break;
 
             // Include temps but not instruction outputs. Also make sure MUST_REUSE_INPUT
             // is not used with gcthings or nunboxes, or we would have to add the input reg
             // to this safepoint.
             if (ins == reg->ins() && !reg->isTemp()) {
                 DebugOnly<LDefinition*> def = reg->def();
                 JS_ASSERT_IF(def->policy() == LDefinition::MUST_REUSE_INPUT,
                              def->type() == LDefinition::GENERAL || def->type() == LDefinition::DOUBLE);
                 continue;
             }
 
             LSafepoint *safepoint = ins->safepoint();
 
             for (size_t k = 0; k < reg->numIntervals(); k++) {
                 LiveInterval *interval = reg->getInterval(k);
-                if (!interval->covers(outputOf(ins)))
+                if (!interval->covers(inputOf(ins)))
                     continue;
 
                 LAllocation *a = interval->getAllocation();
                 if (a->isGeneralReg() && ins->isCall())
                     continue;
 
                 switch (reg->type()) {
                   case LDefinition::OBJECT:
@@ -1506,60 +1506,21 @@ BacktrackingAllocator::trySplitAcrossHot
         if (!hotRange->contains(interval->getRange(i))) {
             coldCode = true;
             break;
         }
     }
     if (!coldCode)
         return true;
 
-    LiveInterval *hotInterval = LiveInterval::New(alloc(), interval->vreg(), 0);
-    LiveInterval *preInterval = nullptr, *postInterval = nullptr;
-
-    // Accumulate the ranges of hot and cold code in the interval. Note that
-    // we are only comparing with the single hot range found, so the cold code
-    // may contain separate hot ranges.
-    Vector<LiveInterval::Range, 1, SystemAllocPolicy> hotList, coldList;
-    for (size_t i = 0; i < interval->numRanges(); i++) {
-        LiveInterval::Range hot, coldPre, coldPost;
-        interval->getRange(i)->intersect(hotRange, &coldPre, &hot, &coldPost);
-
-        if (!hot.empty() && !hotInterval->addRange(hot.from, hot.to))
-            return false;
-
-        if (!coldPre.empty()) {
-            if (!preInterval)
-                preInterval = LiveInterval::New(alloc(), interval->vreg(), 0);
-            if (!preInterval->addRange(coldPre.from, coldPre.to))
-                return false;
-        }
-
-        if (!coldPost.empty()) {
-            if (!postInterval)
-                postInterval = LiveInterval::New(alloc(), interval->vreg(), 0);
-            if (!postInterval->addRange(coldPost.from, coldPost.to))
-                return false;
-        }
-    }
-
-    JS_ASSERT(preInterval || postInterval);
-    JS_ASSERT(hotInterval->numRanges());
-
-    LiveIntervalVector newIntervals;
-    if (!newIntervals.append(hotInterval))
+    SplitPositionVector splitPositions;
+    if (!splitPositions.append(hotRange->from) || !splitPositions.append(hotRange->to))
         return false;
-    if (preInterval && !newIntervals.append(preInterval))
-        return false;
-    if (postInterval && !newIntervals.append(postInterval))
-        return false;
-
-    distributeUses(interval, newIntervals);
-
     *success = true;
-    return split(interval, newIntervals) && requeueIntervals(newIntervals);
+    return splitAt(interval, splitPositions);
 }
 
 bool
 BacktrackingAllocator::trySplitAfterLastRegisterUse(LiveInterval *interval, bool *success)
 {
     // If this interval's later uses do not require it to be in a register,
     // split it after the last use which does require a register.
 
@@ -1656,70 +1617,84 @@ BacktrackingAllocator::splitAtAllRegiste
     }
 
     for (UsePositionIterator iter(interval->usesBegin());
          iter != interval->usesEnd();
          iter++)
     {
         LInstruction *ins = insData[iter->pos].ins();
         if (iter->pos < spillStart) {
-            newIntervals.back()->addUse(new(alloc()) UsePosition(iter->use, iter->pos));
+            newIntervals.back()->addUseAtEnd(new(alloc()) UsePosition(iter->use, iter->pos));
         } else if (isRegisterUse(iter->use, ins)) {
             // For register uses which are not useRegisterAtStart, pick an
             // interval that covers both the instruction's input and output, so
             // that the register is not reused for an output.
             CodePosition from = inputOf(ins);
             CodePosition to = iter->pos.next();
 
             // Use the same interval for duplicate use positions, except when
             // the uses are fixed (they may require incompatible registers).
             if (newIntervals.empty() || newIntervals.back()->end() != to || iter->use->policy() == LUse::FIXED) {
                 if (!addLiveInterval(newIntervals, vreg, spillInterval, from, to))
                     return false;
             }
 
-            newIntervals.back()->addUse(new(alloc()) UsePosition(iter->use, iter->pos));
+            newIntervals.back()->addUseAtEnd(new(alloc()) UsePosition(iter->use, iter->pos));
         } else {
             JS_ASSERT(spillIntervalIsNew);
-            spillInterval->addUse(new(alloc()) UsePosition(iter->use, iter->pos));
+            spillInterval->addUseAtEnd(new(alloc()) UsePosition(iter->use, iter->pos));
         }
     }
 
     if (spillIntervalIsNew && !newIntervals.append(spillInterval))
         return false;
 
     return split(interval, newIntervals) && requeueIntervals(newIntervals);
 }
 
-bool
-BacktrackingAllocator::splitAcrossCalls(LiveInterval *interval)
+// Find the next split position after the current position.
+static size_t NextSplitPosition(size_t activeSplitPosition,
+                                const SplitPositionVector &splitPositions,
+                                CodePosition currentPos)
 {
-    // Split the interval to separate register uses and non-register uses and
-    // allow the vreg to be spilled across its range. Unlike splitAtAllRegisterUses,
-    // consolidate any register uses which have no intervening calls into the
+    while (activeSplitPosition < splitPositions.length() &&
+           splitPositions[activeSplitPosition] <= currentPos)
+    {
+        ++activeSplitPosition;
+    }
+    return activeSplitPosition;
+}
+
+// Test whether the current position has just crossed a split point.
+static bool SplitHere(size_t activeSplitPosition,
+                      const SplitPositionVector &splitPositions,
+                      CodePosition currentPos)
+{
+    return activeSplitPosition < splitPositions.length() &&
+           currentPos >= splitPositions[activeSplitPosition];
+}
+
+bool
+BacktrackingAllocator::splitAt(LiveInterval *interval,
+                               const SplitPositionVector &splitPositions)
+{
+    // Split the interval at the given split points. Unlike splitAtAllRegisterUses,
+    // consolidate any register uses which have no intervening split points into the
     // same resulting interval.
 
+    // splitPositions should be non-empty and sorted.
+    JS_ASSERT(!splitPositions.empty());
+    for (size_t i = 1; i < splitPositions.length(); ++i)
+        JS_ASSERT(splitPositions[i-1] < splitPositions[i]);
+
     // Don't spill the interval until after the end of its definition.
     CodePosition spillStart = interval->start();
     if (isRegisterDefinition(interval))
         spillStart = minimalDefEnd(insData[interval->start()].ins()).next();
 
-    // Find the locations of all calls in the interval's range. Fixed intervals
-    // are introduced by buildLivenessInfo only for calls when allocating for
-    // the backtracking allocator.
-    Vector<CodePosition, 4, SystemAllocPolicy> callPositions;
-    for (size_t i = 0; i < fixedIntervalsUnion->numRanges(); i++) {
-        const LiveInterval::Range *range = fixedIntervalsUnion->getRange(i);
-        if (interval->covers(range->from) && spillStart < range->from) {
-            if (!callPositions.append(range->from))
-                return false;
-        }
-    }
-    JS_ASSERT(callPositions.length());
-
     uint32_t vreg = interval->vreg();
 
     // If this LiveInterval is the result of an earlier split which created a
     // spill interval, that spill interval covers the whole range, so we don't
     // need to create a new one.
     bool spillIntervalIsNew = false;
     LiveInterval *spillInterval = interval->spillInterval();
     if (!spillInterval) {
@@ -1740,86 +1715,103 @@ BacktrackingAllocator::splitAcrossCalls(
     if (spillStart != interval->start()) {
         LiveInterval *newInterval = LiveInterval::New(alloc(), vreg, 0);
         newInterval->setSpillInterval(spillInterval);
         if (!newIntervals.append(newInterval))
             return false;
         lastRegisterUse = interval->start();
     }
 
-    int activeCallPosition = callPositions.length() - 1;
+    size_t activeSplitPosition = NextSplitPosition(0, splitPositions, interval->start());
     for (UsePositionIterator iter(interval->usesBegin()); iter != interval->usesEnd(); iter++) {
         LInstruction *ins = insData[iter->pos].ins();
         if (iter->pos < spillStart) {
-            newIntervals.back()->addUse(new(alloc()) UsePosition(iter->use, iter->pos));
+            newIntervals.back()->addUseAtEnd(new(alloc()) UsePosition(iter->use, iter->pos));
+            activeSplitPosition = NextSplitPosition(activeSplitPosition, splitPositions, iter->pos);
         } else if (isRegisterUse(iter->use, ins)) {
-            bool useNewInterval = false;
-            if (lastRegisterUse.pos() == 0) {
-                useNewInterval = true;
-            } else {
+            if (lastRegisterUse.pos() == 0 ||
+                SplitHere(activeSplitPosition, splitPositions, iter->pos))
+            {
                 // Place this register use into a different interval from the
-                // last one if there are any calls between the two uses or if
-                // the register uses are in different subranges of the original
-                // interval.
-                for (; activeCallPosition >= 0; activeCallPosition--) {
-                    CodePosition pos = callPositions[activeCallPosition];
-                    if (iter->pos < pos)
-                        break;
-                    if (lastRegisterUse < pos) {
-                        useNewInterval = true;
-                        break;
-                    }
-                }
-                if (!useNewInterval) {
-                    for (size_t i = 0; i < interval->numRanges(); i++) {
-                        const LiveInterval::Range *range = interval->getRange(i);
-                        if (range->from <= lastRegisterUse && range->to <= iter->pos) {
-                            useNewInterval = true;
-                            break;
-                        }
-                    }
-                }
-            }
-            if (useNewInterval) {
+                // last one if there are any split points between the two uses.
                 LiveInterval *newInterval = LiveInterval::New(alloc(), vreg, 0);
                 newInterval->setSpillInterval(spillInterval);
                 if (!newIntervals.append(newInterval))
                     return false;
+                activeSplitPosition = NextSplitPosition(activeSplitPosition,
+                                                        splitPositions,
+                                                        iter->pos);
             }
-            newIntervals.back()->addUse(new(alloc()) UsePosition(iter->use, iter->pos));
+            newIntervals.back()->addUseAtEnd(new(alloc()) UsePosition(iter->use, iter->pos));
             lastRegisterUse = iter->pos;
         } else {
             JS_ASSERT(spillIntervalIsNew);
-            spillInterval->addUse(new(alloc()) UsePosition(iter->use, iter->pos));
+            spillInterval->addUseAtEnd(new(alloc()) UsePosition(iter->use, iter->pos));
         }
     }
 
     // Compute ranges for each new interval that cover all its uses.
+    size_t activeRange = interval->numRanges();
     for (size_t i = 0; i < newIntervals.length(); i++) {
         LiveInterval *newInterval = newIntervals[i];
         CodePosition start, end;
         if (i == 0 && spillStart != interval->start()) {
             start = interval->start();
-            end = spillStart;
+            if (newInterval->usesEmpty())
+                end = spillStart;
+            else
+                end = newInterval->usesBack()->pos.next();
         } else {
             start = inputOf(insData[newInterval->usesBegin()->pos].ins());
+            end = newInterval->usesBack()->pos.next();
         }
-        if (newInterval->usesBegin() != newInterval->usesEnd())
-            end = newInterval->usesBack()->pos.next();
-        if (!newInterval->addRange(start, end))
-            return false;
+        for (; activeRange > 0; --activeRange) {
+            const LiveInterval::Range *range = interval->getRange(activeRange - 1);
+            if (range->to <= start)
+                continue;
+            if (range->from >= end)
+                break;
+            if (!newInterval->addRange(Max(range->from, start),
+                                       Min(range->to, end)))
+                return false;
+            if (range->to >= end)
+                break;
+        }
     }
 
     if (spillIntervalIsNew && !newIntervals.append(spillInterval))
         return false;
 
     return split(interval, newIntervals) && requeueIntervals(newIntervals);
 }
 
 bool
+BacktrackingAllocator::splitAcrossCalls(LiveInterval *interval)
+{
+    // Split the interval to separate register uses and non-register uses and
+    // allow the vreg to be spilled across its range.
+
+    // Find the locations of all calls in the interval's range. Fixed intervals
+    // are introduced by buildLivenessInfo only for calls when allocating for
+    // the backtracking allocator. fixedIntervalsUnion is sorted backwards, so
+    // iterate through it backwards.
+    SplitPositionVector callPositions;
+    for (size_t i = fixedIntervalsUnion->numRanges(); i > 0; i--) {
+        const LiveInterval::Range *range = fixedIntervalsUnion->getRange(i - 1);
+        if (interval->covers(range->from) && interval->covers(range->from.previous())) {
+            if (!callPositions.append(range->from))
+                return false;
+        }
+    }
+    JS_ASSERT(callPositions.length());
+
+    return splitAt(interval, callPositions);
+}
+
+bool
 BacktrackingAllocator::chooseIntervalSplit(LiveInterval *interval)
 {
     bool success = false;
 
     if (!trySplitAcrossHotcode(interval, &success))
         return false;
     if (success)
         return true;
--- a/js/src/jit/BacktrackingAllocator.h
+++ b/js/src/jit/BacktrackingAllocator.h
@@ -99,16 +99,20 @@ class BacktrackingVirtualRegister : publ
     void setGroup(VirtualRegisterGroup *group) {
         group_ = group;
     }
     VirtualRegisterGroup *group() {
         return group_;
     }
 };
 
+// A sequence of code positions, for tellings BacktrackingAllocator::splitAt
+// where to split.
+typedef js::Vector<CodePosition, 4, SystemAllocPolicy> SplitPositionVector;
+
 class BacktrackingAllocator : public LiveRangeAllocator<BacktrackingVirtualRegister>
 {
     // Priority queue element: either an interval or group of intervals and the
     // associated priority.
     struct QueueItem
     {
         LiveInterval *interval;
         VirtualRegisterGroup *group;
@@ -225,16 +229,19 @@ class BacktrackingAllocator : public Liv
 
     size_t computePriority(const LiveInterval *interval);
     size_t computeSpillWeight(const LiveInterval *interval);
 
     size_t computePriority(const VirtualRegisterGroup *group);
     size_t computeSpillWeight(const VirtualRegisterGroup *group);
 
     bool chooseIntervalSplit(LiveInterval *interval);
+
+    bool splitAt(LiveInterval *interval,
+                 const SplitPositionVector &splitPositions);
     bool trySplitAcrossHotcode(LiveInterval *interval, bool *success);
     bool trySplitAfterLastRegisterUse(LiveInterval *interval, bool *success);
     bool splitAtAllRegisterUses(LiveInterval *interval);
     bool splitAcrossCalls(LiveInterval *interval);
 };
 
 } // namespace jit
 } // namespace js
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -848,17 +848,16 @@ CodeGenerator::visitLambdaPar(LLambdaPar
     emitAllocateGCThingPar(lir, resultReg, sliceReg, tempReg1, tempReg2, info.fun);
     emitLambdaInit(resultReg, scopeChainReg, info);
     return true;
 }
 
 bool
 CodeGenerator::visitLabel(LLabel *lir)
 {
-    masm.bind(lir->label());
     return true;
 }
 
 bool
 CodeGenerator::visitNop(LNop *lir)
 {
     return true;
 }
@@ -2838,37 +2837,30 @@ CodeGenerator::generateBody()
 #if defined(JS_ION_PERF)
     PerfSpewer *perfSpewer = &perfSpewer_;
     if (gen->compilingAsmJS())
         perfSpewer = &gen->perfSpewer();
 #endif
 
     for (size_t i = 0; i < graph.numBlocks(); i++) {
         current = graph.getBlock(i);
-
-        LInstructionIterator iter = current->begin();
-
-        // Separately visit the label at the start of every block, so that
-        // count instrumentation is inserted after the block label is bound.
-        if (!iter->accept(this))
-            return false;
-        iter++;
+        masm.bind(current->label());
 
         mozilla::Maybe<ScriptCountBlockState> blockCounts;
         if (counts) {
             blockCounts.construct(&counts->block(i), &masm);
             if (!blockCounts.ref().init())
                 return false;
         }
 
 #if defined(JS_ION_PERF)
         perfSpewer->startBasicBlock(current->mir(), masm);
 #endif
 
-        for (; iter != current->end(); iter++) {
+        for (LInstructionIterator iter = current->begin(); iter != current->end(); iter++) {
             IonSpewStart(IonSpew_Codegen, "instruction %s", iter->opName());
 #ifdef DEBUG
             if (const char *extra = iter->extraName())
                 IonSpewCont(IonSpew_Codegen, ":%s", extra);
 #endif
             IonSpewFin(IonSpew_Codegen);
 
             if (counts)
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -23,28 +23,22 @@ class LBinaryMath : public LInstructionH
     const LAllocation *lhs() {
         return this->getOperand(0);
     }
     const LAllocation *rhs() {
         return this->getOperand(1);
     }
 };
 
-// Used for jumps from other blocks. Also simplifies register allocation since
-// the first instruction of a block is guaranteed to have no uses.
+// Simplifies register allocation since the first instruction of a block is
+// guaranteed to have no uses.
 class LLabel : public LInstructionHelper<0, 0, 0>
 {
-    Label label_;
-
   public:
     LIR_HEADER(Label)
-
-    Label *label() {
-        return &label_;
-    }
 };
 
 class LNop : public LInstructionHelper<0, 0, 0>
 {
   public:
     LIR_HEADER(Nop)
 };
 
--- a/js/src/jit/LIR.cpp
+++ b/js/src/jit/LIR.cpp
@@ -50,22 +50,16 @@ LIRGraph::noteNeedsSafepoint(LInstructio
 }
 
 void
 LIRGraph::removeBlock(size_t i)
 {
     blocks_.erase(blocks_.begin() + i);
 }
 
-Label *
-LBlock::label()
-{
-    return begin()->toLabel()->label();
-}
-
 uint32_t
 LBlock::firstId()
 {
     if (phis_.length()) {
         return phis_[0]->id();
     } else {
         for (LInstructionIterator i(instructions_.begin()); i != instructions_.end(); i++) {
             if (i->id())
@@ -85,18 +79,20 @@ LBlock::lastId()
 }
 
 LMoveGroup *
 LBlock::getEntryMoveGroup(TempAllocator &alloc)
 {
     if (entryMoveGroup_)
         return entryMoveGroup_;
     entryMoveGroup_ = new LMoveGroup(alloc);
-    JS_ASSERT(begin()->isLabel());
-    insertAfter(*begin(), entryMoveGroup_);
+    if (begin()->isLabel())
+        insertAfter(*begin(), entryMoveGroup_);
+    else
+        insertBefore(*begin(), entryMoveGroup_);
     return entryMoveGroup_;
 }
 
 LMoveGroup *
 LBlock::getExitMoveGroup(TempAllocator &alloc)
 {
     if (exitMoveGroup_)
         return exitMoveGroup_;
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -730,16 +730,17 @@ class LPhi;
 class LMoveGroup;
 class LBlock : public TempObject
 {
     MBasicBlock *block_;
     Vector<LPhi *, 4, IonAllocPolicy> phis_;
     InlineList<LInstruction> instructions_;
     LMoveGroup *entryMoveGroup_;
     LMoveGroup *exitMoveGroup_;
+    Label label_;
 
     LBlock(TempAllocator &alloc, MBasicBlock *block)
       : block_(block),
         phis_(alloc),
         entryMoveGroup_(nullptr),
         exitMoveGroup_(nullptr)
     { }
 
@@ -793,17 +794,19 @@ class LBlock : public TempObject
         instructions_.insertAfter(at, ins);
     }
     void insertBefore(LInstruction *at, LInstruction *ins) {
         JS_ASSERT(!at->isLabel());
         instructions_.insertBefore(at, ins);
     }
     uint32_t firstId();
     uint32_t lastId();
-    Label *label();
+    Label *label() {
+        return &label_;
+    }
     LMoveGroup *getEntryMoveGroup(TempAllocator &alloc);
     LMoveGroup *getExitMoveGroup(TempAllocator &alloc);
 };
 
 template <size_t Defs, size_t Operands, size_t Temps>
 class LInstructionHelper : public LInstruction
 {
     mozilla::Array<LDefinition, Defs> defs_;
--- a/js/src/jit/LiveRangeAllocator.cpp
+++ b/js/src/jit/LiveRangeAllocator.cpp
@@ -280,16 +280,23 @@ LiveInterval::addUse(UsePosition *use)
     }
 
     if (prev)
         uses_.insertAfter(prev, use);
     else
         uses_.pushFront(use);
 }
 
+void
+LiveInterval::addUseAtEnd(UsePosition *use)
+{
+    JS_ASSERT(uses_.empty() || use->pos >= uses_.back()->pos);
+    uses_.pushBack(use);
+}
+
 UsePosition *
 LiveInterval::nextUseAfter(CodePosition after)
 {
     for (UsePositionIterator usePos(usesBegin()); usePos != usesEnd(); usePos++) {
         if (usePos->pos >= after) {
             LUse::Policy policy = usePos->use->policy();
             JS_ASSERT(policy != LUse::RECOVERED_INPUT);
             if (policy != LUse::KEEPALIVE)
@@ -659,17 +666,17 @@ LiveRangeAllocator<VREG>::buildLivenessI
             DebugOnly<bool> hasUseRegister = false;
             DebugOnly<bool> hasUseRegisterAtStart = false;
 
             for (LInstruction::InputIterator inputAlloc(**ins); inputAlloc.more(); inputAlloc.next()) {
                 if (inputAlloc->isUse()) {
                     LUse *use = inputAlloc->toUse();
 
                     // The first instruction, LLabel, has no uses.
-                    JS_ASSERT(inputOf(*ins) > outputOf(block->firstId()));
+                    JS_ASSERT_IF(forLSRA, inputOf(*ins) > outputOf(block->firstId()));
 
                     // Call uses should always be at-start or fixed, since the fixed intervals
                     // use all registers.
                     JS_ASSERT_IF(ins->isCall() && !inputAlloc.isSnapshotInput(),
                                  use->isFixedRegister() || use->usedAtStart());
 
 #ifdef DEBUG
                     // Don't allow at-start call uses if there are temps of the same kind,
--- a/js/src/jit/LiveRangeAllocator.h
+++ b/js/src/jit/LiveRangeAllocator.h
@@ -351,28 +351,33 @@ class LiveInterval
         hint_ = hint;
     }
     bool isSpill() const {
         return alloc_.isStackSlot();
     }
     bool splitFrom(CodePosition pos, LiveInterval *after);
 
     void addUse(UsePosition *use);
+    void addUseAtEnd(UsePosition *use);
     UsePosition *nextUseAfter(CodePosition pos);
     CodePosition nextUsePosAfter(CodePosition pos);
     CodePosition firstIncompatibleUse(LAllocation alloc);
 
     UsePositionIterator usesBegin() const {
         return uses_.begin();
     }
 
     UsePositionIterator usesEnd() const {
         return uses_.end();
     }
 
+    bool usesEmpty() const {
+        return uses_.empty();
+    }
+
     UsePosition *usesBack() {
         return uses_.back();
     }
 
 #ifdef DEBUG
     void validateRanges();
 #endif
 };
@@ -641,17 +646,17 @@ class LiveRangeAllocator : public Regist
         return addMove(moves, from, to);
     }
 
     size_t findFirstNonCallSafepoint(CodePosition from) const
     {
         size_t i = 0;
         for (; i < graph.numNonCallSafepoints(); i++) {
             const LInstruction *ins = graph.getNonCallSafepoint(i);
-            if (from <= (forLSRA ? inputOf(ins) : outputOf(ins)))
+            if (from <= inputOf(ins))
                 break;
         }
         return i;
     }
 
     void addLiveRegistersForInterval(VirtualRegister *reg, LiveInterval *interval)
     {
         // Fill in the live register sets for all non-call safepoints.
@@ -662,21 +667,21 @@ class LiveRangeAllocator : public Regist
         // Don't add output registers to the safepoint.
         CodePosition start = interval->start();
         if (interval->index() == 0 && !reg->isTemp())
             start = start.next();
 
         size_t i = findFirstNonCallSafepoint(start);
         for (; i < graph.numNonCallSafepoints(); i++) {
             LInstruction *ins = graph.getNonCallSafepoint(i);
-            CodePosition pos = forLSRA ? inputOf(ins) : outputOf(ins);
+            CodePosition pos = inputOf(ins);
 
             // Safepoints are sorted, so we can shortcut out of this loop
             // if we go out of range.
-            if (interval->end() < pos)
+            if (interval->end() <= pos)
                 break;
 
             if (!interval->covers(pos))
                 continue;
 
             LSafepoint *safepoint = ins->safepoint();
             safepoint->addLiveRegister(a->toRegister());
 
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -392,17 +392,21 @@ LIRGenerator::visitReturnFromCtor(MRetur
 
 bool
 LIRGenerator::visitComputeThis(MComputeThis *ins)
 {
     JS_ASSERT(ins->type() == MIRType_Object);
     JS_ASSERT(ins->input()->type() == MIRType_Value);
 
     LComputeThis *lir = new LComputeThis();
-    if (!useBoxAtStart(lir, LComputeThis::ValueIndex, ins->input()))
+
+    // Don't use useBoxAtStart because ComputeThis has a safepoint and needs to
+    // have its inputs in different registers than its return value so that
+    // they aren't clobbered.
+    if (!useBox(lir, LComputeThis::ValueIndex, ins->input()))
         return false;
 
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 
 bool
 LIRGenerator::visitCall(MCall *call)
@@ -3483,18 +3487,20 @@ bool
 LIRGenerator::visitBlock(MBasicBlock *block)
 {
     current = block->lir();
     updateResumeState(block);
 
     if (!definePhis())
         return false;
 
-    if (!add(new LLabel()))
-        return false;
+    if (js_IonOptions.registerAllocator == RegisterAllocator_LSRA) {
+        if (!add(new LLabel()))
+            return false;
+    }
 
     for (MInstructionIterator iter = block->begin(); *iter != block->lastIns(); iter++) {
         if (!visitInstruction(*iter))
             return false;
     }
 
     if (block->successorWithPhis()) {
         // If we have a successor with phis, lower the phi input now that we
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -68,17 +68,25 @@ LIRGeneratorShared::defineFixed(LInstruc
 {
     LDefinition::Type type = LDefinition::TypeFrom(mir->type());
 
     LDefinition def(type, LDefinition::PRESET);
     def.setOutput(output);
 
     // Add an LNop to avoid regalloc problems if the next op uses this value
     // with a fixed or at-start policy.
-    return define(lir, mir, def) && add(new LNop);
+    if (!define(lir, mir, def))
+        return false;
+
+    if (js_IonOptions.registerAllocator == RegisterAllocator_LSRA) {
+        if (!add(new LNop))
+            return false;
+    }
+
+    return true;
 }
 
 template <size_t Ops, size_t Temps> bool
 LIRGeneratorShared::defineReuseInput(LInstructionHelper<1, Ops, Temps> *lir, MDefinition *mir, uint32_t operand)
 {
     // The input should be used at the start of the instruction, to avoid moves.
     JS_ASSERT(lir->getOperand(operand)->toUse()->usedAtStart());
 
@@ -147,17 +155,25 @@ LIRGeneratorShared::defineReturn(LInstru
       default:
         LDefinition::Type type = LDefinition::TypeFrom(mir->type());
         JS_ASSERT(type != LDefinition::DOUBLE);
         lir->setDef(0, LDefinition(vreg, type, LGeneralReg(ReturnReg)));
         break;
     }
 
     mir->setVirtualRegister(vreg);
-    return add(lir) && add(new LNop);
+    if (!add(lir))
+        return false;
+
+    if (js_IonOptions.registerAllocator == RegisterAllocator_LSRA) {
+        if (!add(new LNop))
+            return false;
+    }
+
+    return true;
 }
 
 // In LIR, we treat booleans and integers as the same low-level type (INTEGER).
 // When snapshotting, we recover the actual JS type from MIR. This function
 // checks that when making redefinitions, we don't accidentally coerce two
 // incompatible types.
 static inline bool
 IsCompatibleLIRCoercion(MIRType to, MIRType from)
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -386,8 +386,13 @@ HOST_SIMPLE_PROGRAMS += [
 #
 # In fact, we now build both a static and a shared library, as the
 # JS shell would like to link to the static library.
 
 if CONFIG['JS_SHARED_LIBRARY']:
     FORCE_SHARED_LIB = True
 
 FORCE_STATIC_LIB = True
+
+if CONFIG['MOZ_ETW']:
+    GENERATED_FILES = [
+        'ETWProvider.h',
+    ]
--- a/js/xpconnect/loader/moz.build
+++ b/js/xpconnect/loader/moz.build
@@ -1,14 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+# These files cannot be built in unified mode because they rely on plarena.h
+# and they want to force NSPR logging.
 SOURCES += [
     'mozJSComponentLoader.cpp',
     'mozJSLoaderUtils.cpp',
     'mozJSSubScriptLoader.cpp',
 ]
 
 EXTRA_JS_MODULES += [
     'ISO8601DateUtils.jsm',
--- a/js/xpconnect/shell/Makefile.in
+++ b/js/xpconnect/shell/Makefile.in
@@ -10,20 +10,16 @@ LOCAL_INCLUDES += \
   $(NULL)
 
 LIBS		= \
 		$(DIST)/lib/$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \
 		$(LIBXUL_LIBS) \
 		$(XPCOM_LIBS) \
 		$(NULL)
 
-ifdef JS_SHARED_LIBRARY
-LIBS +=	$(MOZ_JS_LIBS)
-endif
-
 LIBS +=	$(NSPR_LIBS)
 
 NSDISTMODE = copy
 
 ifeq ($(OS_TEST),ia64)
 LIBS += $(JEMALLOC_LIBS)
 endif
 
--- a/js/xpconnect/src/Makefile.in
+++ b/js/xpconnect/src/Makefile.in
@@ -58,20 +58,16 @@ include $(topsrcdir)/config/rules.mk
 DEFINES += \
   -DJS_THREADSAFE \
   $(NULL)
 
 ifdef MOZ_JSDEBUGGER
 DEFINES += -DMOZ_JSDEBUGGER
 endif
 
-nsXPConnect.$(OBJ_SUFFIX): dom_quickstubs.h
-
-XPCJSRuntime.$(OBJ_SUFFIX): dom_quickstubs.h
-
 dom_quickstubs.h: dom_quickstubs.cpp
 
 dom_quickstubs.cpp: $(srcdir)/dom_quickstubs.qsconf \
                     $(srcdir)/qsgen.py \
                     $(LIBXUL_DIST)/sdk/bin/header.py \
                     $(LIBXUL_DIST)/sdk/bin/xpidl.py
 	$(PYTHON) $(topsrcdir)/config/pythonpath.py \
 	  $(PLY_INCLUDE) \
@@ -79,18 +75,16 @@ dom_quickstubs.cpp: $(srcdir)/dom_quicks
 	  $(srcdir)/qsgen.py \
 	  --idlpath=$(DEPTH)/dist/idl \
 	  --header-output dom_quickstubs.h \
 	  --stub-output dom_quickstubs.cpp \
 	  --makedepend-output $(MDDEPDIR)/dom_qsgen.pp \
 	  $(ENABLE_TRACEABLE_FLAGS) \
 	  $(srcdir)/dom_quickstubs.qsconf
 
-DictionaryHelpers.$(OBJ_SUFFIX): DictionaryHelpers.cpp
-
 DictionaryHelpers.h: DictionaryHelpers.cpp
 
 DictionaryHelpers.cpp: $(srcdir)/dictionary_helper_gen.conf \
                        event_impl_gen.conf \
                        $(srcdir)/dictionary_helper_gen.py \
                        $(LIBXUL_DIST)/sdk/bin/header.py \
                        $(LIBXUL_DIST)/sdk/bin/xpidl.py
 	$(PYTHON) $(topsrcdir)/config/pythonpath.py \
@@ -99,19 +93,16 @@ DictionaryHelpers.cpp: $(srcdir)/diction
 	  $(srcdir)/dictionary_helper_gen.py \
 	  -I $(DEPTH)/dist/idl \
 	  --header-output DictionaryHelpers.h \
 	  --stub-output DictionaryHelpers.cpp \
 	  --makedepend-output $(MDDEPDIR)/dictionary_helper_gen.pp \
 	  $(srcdir)/dictionary_helper_gen.conf \
 	  event_impl_gen.conf
 
-GeneratedEvents.$(OBJ_SUFFIX): GeneratedEvents.h \
-                               GeneratedEvents.cpp
-
 event_impl_gen.conf : $(srcdir)/event_impl_gen.conf.in
 	$(call py_action,preprocessor,$(DEFINES) $(ACDEFINES) $^ -o event_impl_gen.conf)
 
 GeneratedEvents.h: $(srcdir)/dictionary_helper_gen.conf \
                    event_impl_gen.conf \
                    $(srcdir)/dictionary_helper_gen.py \
                    $(srcdir)/event_impl_gen.py \
                    $(LIBXUL_DIST)/sdk/bin/header.py \
@@ -149,23 +140,16 @@ GeneratedEvents-webidl: event_impl_gen.c
 	  $(PLY_INCLUDE) \
 	  -I$(LIBXUL_DIST)/sdk/bin \
 	  $(srcdir)/event_impl_gen.py \
 	  -I $(DEPTH)/dist/idl \
 	  --webidltarget=$(top_srcdir)/dom/webidl \
 	  event_impl_gen.conf
 
 GARBAGE += \
-		dom_quickstubs.h \
-		dom_quickstubs.cpp \
-		DictionaryHelpers.h \
-		DictionaryHelpers.cpp \
-		GeneratedEvents.h \
-		GeneratedEvents.cpp \
-		GeneratedEventClasses.h \
 		event_impl_gen.conf \
 		xpidl_debug \
 		$(MDDEPDIR)/dom_qsgen.pp \
 		$(MDDEPDIR)/dombindingsgen.pp \
 		$(MDDEPDIR)/dictionary_helper_gen.pp \
 		$(MDDEPDIR)/event_impl_gen.pp \
 		$(wildcard $(topsrcdir)/other-licenses/ply/ply/*.pyc) \
 		$(NULL)
--- a/js/xpconnect/src/moz.build
+++ b/js/xpconnect/src/moz.build
@@ -8,23 +8,22 @@ EXPORTS += [
     'BackstagePass.h',
     'nsCxPusher.h',
     'qsObjectHelper.h',
     'XPCJSMemoryReporter.h',
     'xpcObjectHelper.h',
     'xpcpublic.h',
 ]
 
-SOURCES += [
+UNIFIED_SOURCES += [
     'nsCxPusher.cpp',
     'nsScriptError.cpp',
     'nsXPConnect.cpp',
     'Sandbox.cpp',
     'XPCCallContext.cpp',
-    'XPCComponents.cpp',
     'XPCContext.cpp',
     'XPCConvert.cpp',
     'XPCDebug.cpp',
     'XPCException.cpp',
     'XPCJSContextStack.cpp',
     'XPCJSID.cpp',
     'XPCJSRuntime.cpp',
     'XPCJSWeakReference.cpp',
@@ -43,23 +42,35 @@ SOURCES += [
     'XPCWrappedNative.cpp',
     'XPCWrappedNativeInfo.cpp',
     'XPCWrappedNativeJSOps.cpp',
     'XPCWrappedNativeProto.cpp',
     'XPCWrappedNativeScope.cpp',
     'XPCWrapper.cpp',
 ]
 
+# XPCComponents.cpp cannot be built in unified mode because it uses plarena.h.
+SOURCES += [
+    'XPCComponents.cpp',
+]
+
 GENERATED_SOURCES += [
     'DictionaryHelpers.cpp',
     'dom_quickstubs.cpp',
     'GeneratedEvents.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
 MSVC_ENABLE_PGO = True
 
 LIBRARY_NAME = 'xpconnect_s'
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'gklayout'
+
+GENERATED_FILES = [
+    'DictionaryHelpers.h',
+    'dom_quickstubs.h',
+    'GeneratedEventClasses.h',
+    'GeneratedEvents.h',
+]
--- a/js/xpconnect/tests/Makefile.in
+++ b/js/xpconnect/tests/Makefile.in
@@ -1,11 +1,10 @@
 #
 # 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/.
 
 LIBS = \
   $(DIST)/lib/$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \
   $(LIBS_DIR) \
-  $(MOZ_JS_LIBS) \
   $(MOZ_COMPONENT_LIBS) \
   $(NULL)
--- a/js/xpconnect/tests/components/native/Makefile.in
+++ b/js/xpconnect/tests/components/native/Makefile.in
@@ -7,16 +7,15 @@ componentdir = js/xpconnect/tests/compon
 
 MANIFEST = xpctest.manifest
 MANIFEST_PATH = $(testxpcobjdir)/$(componentdir)
 PP_TARGETS += MANIFEST
 
 EXTRA_DSO_LDOPTS += \
   $(XPCOM_GLUE_LDOPTS) \
   $(MOZ_COMPONENT_LIBS) \
-  $(MOZ_JS_LIBS) \
   $(NULL)
 
 DEFINES += -DLIBRARY_FILENAME="$(SHARED_LIBRARY)"
 
 LIB_FILES = $(SHARED_LIBRARY)
 LIB_DEST = $(testxpcobjdir)/$(componentdir)
 INSTALL_TARGETS += LIB
--- a/js/xpconnect/tests/components/native/moz.build
+++ b/js/xpconnect/tests/components/native/moz.build
@@ -1,17 +1,17 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 NO_DIST_INSTALL = True
 
-SOURCES += [
+UNIFIED_SOURCES += [
     'xpctest_attributes.cpp',
     'xpctest_module.cpp',
     'xpctest_params.cpp',
 ]
 
 LIBRARY_NAME = 'xpctest'
 
 IS_COMPONENT = True
--- a/js/xpconnect/wrappers/moz.build
+++ b/js/xpconnect/wrappers/moz.build
@@ -3,22 +3,26 @@
 # 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/.
 
 EXPORTS += [
     'WrapperFactory.h',
 ]
 
-SOURCES += [
+UNIFIED_SOURCES += [
     'AccessCheck.cpp',
     'ChromeObjectWrapper.cpp',
     'FilteringWrapper.cpp',
     'WaiveXrayWrapper.cpp',
     'WrapperFactory.cpp',
+]
+
+# XrayWrapper needs to be built separately becaue of template instantiations.
+SOURCES += [
     'XrayWrapper.cpp',
 ]
 
 # warning C4661 for FilteringWrapper
 FAIL_ON_WARNINGS = not CONFIG['_MSC_VER']
 
 MSVC_ENABLE_PGO = True
 
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -74,39 +74,41 @@ UNIFIED_SOURCES += [
     'nsCaret.cpp',
     'nsCounterManager.cpp',
     'nsCSSColorUtils.cpp',
     'nsCSSFrameConstructor.cpp',
     'nsCSSRendering.cpp',
     'nsCSSRenderingBorders.cpp',
     'nsDisplayList.cpp',
     'nsDisplayListInvalidation.cpp',
-    'nsDocumentViewer.cpp',
     'nsFrameManager.cpp',
     'nsFrameTraversal.cpp',
     'nsGenConList.cpp',
     'nsLayoutDebugger.cpp',
     'nsLayoutHistoryState.cpp',
     'nsLayoutUtils.cpp',
     'nsPresContext.cpp',
-    'nsPresShell.cpp',
     'nsQuoteList.cpp',
     'nsStyleChangeList.cpp',
     'nsStyleSheetService.cpp',
     'PaintTracker.cpp',
     'PositionedEventTargeting.cpp',
     'RestyleManager.cpp',
     'RestyleTracker.cpp',
     'StackArena.cpp',
 ]
 
+# nsDocumentViewer.cpp and nsPresShell.cpp need to be built separately
+# because they force NSPR logging.
 # nsPresArena.cpp needs to be built separately because it uses plarena.h.
 # nsRefreshDriver.cpp needs to be built separately because of name clashes in the OS X headers
 SOURCES += [
+    'nsDocumentViewer.cpp',
     'nsPresArena.cpp',
+    'nsPresShell.cpp',
     'nsRefreshDriver.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
 MSVC_ENABLE_PGO = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
--- a/layout/base/nsStyleSheetService.cpp
+++ b/layout/base/nsStyleSheetService.cpp
@@ -175,17 +175,17 @@ nsStyleSheetService::LoadAndRegisterShee
     if (XRE_GetProcessType() == GeckoProcessType_Default) {
       nsTArray<dom::ContentParent*> children;
       dom::ContentParent::GetAll(children);
 
       if (children.IsEmpty()) {
         return rv;
       }
 
-      ipc::URIParams uri;
+      mozilla::ipc::URIParams uri;
       SerializeURI(aSheetURI, uri);
 
       for (uint32_t i = 0; i < children.Length(); i++) {
         unused << children[i]->SendLoadAndRegisterSheet(uri, aSheetType);
       }
     }
   }
   return rv;
@@ -263,17 +263,17 @@ nsStyleSheetService::UnregisterSheet(nsI
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     nsTArray<dom::ContentParent*> children;
     dom::ContentParent::GetAll(children);
 
     if (children.IsEmpty()) {
       return NS_OK;
     }
 
-    ipc::URIParams uri;
+    mozilla::ipc::URIParams uri;
     SerializeURI(aSheetURI, uri);
 
     for (uint32_t i = 0; i < children.Length(); i++) {
       unused << children[i]->SendUnregisterSheet(uri, aSheetType);
     }
   }
 
   return NS_OK;
--- a/layout/build/Makefile.in
+++ b/layout/build/Makefile.in
@@ -34,33 +34,22 @@ INCLUDES	+= \
 		-I$(ANDROID_SOURCE)/dalvik/libnativehelper/include/nativehelper \
 		-I$(ANDROID_SOURCE)/frameworks/base/include/ \
 		-I$(ANDROID_SOURCE)/frameworks/base/include/binder/ \
 		-I$(ANDROID_SOURCE)/frameworks/base/include/utils/ \
 		-I$(ANDROID_SOURCE)/frameworks/base/include/media/ \
 		-I$(ANDROID_SOURCE)/frameworks/base/include/media/stagefright/openmax \
 		-I$(ANDROID_SOURCE)/frameworks/base/media/libstagefright/include \
 		$(NULL)
-
-EXTRA_DSO_LDOPTS += \
-		-lutils -lstagefright -lmedia -lstagefright_omx -lbinder -lui \
-		-lhardware -lcutils \
-		$(NULL)
 endif
 
 LOCAL_INCLUDES  += \
       -I$(srcdir)/../inspector/src \
       $(NULL)
 
-ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
-ifdef MOZ_GSTREAMER
-EXTRA_DSO_LDOPTS += $(GSTREAMER_LIBS)
-endif
-endif
-
 include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES	+= -I$(srcdir)/../base \
 		   -I$(srcdir)/../generic \
 		   -I$(srcdir)/../forms \
 		   -I$(srcdir)/../tables \
 		   -I$(srcdir)/../style \
 		   -I$(srcdir)/../xul/base/src \
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -1089,18 +1089,17 @@ public:
   // For now, we just have Reflow directly call
   // SingleLineCrossAxisPositionTracker::SetLineCrossSize().
 };
 
 // Utility class for managing our position along the cross axis, *within* a
 // single flex line.
 class MOZ_STACK_CLASS SingleLineCrossAxisPositionTracker : public PositionTracker {
 public:
-  SingleLineCrossAxisPositionTracker(nsFlexContainerFrame* aFlexContainerFrame,
-                                     const FlexboxAxisTracker& aAxisTracker);
+  SingleLineCrossAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker);
 
   void ComputeLineCrossSize(const nsTArray<FlexItem>& aItems);
   inline nscoord GetLineCrossSize() const { return mLineCrossSize; }
 
   // Used to override the flex line's size, for cases when the flex container is
   // single-line and has a fixed size, and also in cases where
   // "align-self: stretch" triggers some space-distribution between lines
   // (when we support that property).
@@ -1389,18 +1388,18 @@ nsFlexContainerFrame::ResolveFlexibleLen
         item.SetMainSize(item.GetFlexBaseSize());
       }
       availableFreeSpace -= item.GetMainSize();
     }
 
     PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,
            (" available free space = %d\n", availableFreeSpace));
 
-    // If sign of free space matches flexType, give each flexible
-    // item a portion of availableFreeSpace.
+    // If sign of free space matches the type of flexing that we're doing, give
+    // each flexible item a portion of availableFreeSpace.
     if ((availableFreeSpace > 0 && havePositiveFreeSpace) ||
         (availableFreeSpace < 0 && !havePositiveFreeSpace)) {
 
       // STRATEGY: On each item, we compute & store its "share" of the total
       // flex weight that we've seen so far:
       //   curFlexWeight / runningFlexWeightSum
       //
       // Then, when we go to actually distribute the space (in the next loop),
@@ -1675,18 +1674,17 @@ MainAxisPositionTracker::TraversePacking
 
     mPosition += curPackingSpace;
     mNumPackingSpacesRemaining--;
     mPackingSpaceRemaining -= curPackingSpace;
   }
 }
 
 SingleLineCrossAxisPositionTracker::
-  SingleLineCrossAxisPositionTracker(nsFlexContainerFrame* aFlexContainerFrame,
-                                     const FlexboxAxisTracker& aAxisTracker)
+  SingleLineCrossAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker)
   : PositionTracker(aAxisTracker.GetCrossAxis()),
     mLineCrossSize(0),
     mCrossStartToFurthestBaseline(nscoord_MIN) // Starts at -infinity, and then
                                                // we progressively increase it.
 {
 }
 
 void
@@ -2364,18 +2362,17 @@ nsFlexContainerFrame::Reflow(nsPresConte
   // Calculate the cross size of our (single) flex line:
   // Set up state for cross-axis alignment, at a high level (outside the
   // scope of a particular flex line)
   CrossAxisPositionTracker
     crossAxisPosnTracker(this, axisTracker, aReflowState);
 
   // Set up state for cross-axis-positioning of children _within_ a single
   // flex line.
-  SingleLineCrossAxisPositionTracker
-    lineCrossAxisPosnTracker(this, axisTracker);
+  SingleLineCrossAxisPositionTracker lineCrossAxisPosnTracker(axisTracker);
 
   lineCrossAxisPosnTracker.ComputeLineCrossSize(items);
   // XXXdholbert Once we've got multi-line flexbox support: here, after we've
   // computed the cross size of all lines, we need to check if if
   // 'align-content' is 'stretch' -- if it is, we need to give each line an
   // additional share of our flex container's desired cross-size. (if it's
   // not NS_AUTOHEIGHT and there's any cross-size left over to distribute)
   bool isCrossSizeDefinite;
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1807,42 +1807,42 @@ nsIFrame::BuildDisplayListForStackingCon
   bool isTransformed = IsTransformed();
   // reset blend mode so we can keep track if this stacking context needs have
   // a nsDisplayBlendContainer. Set the blend mode back when the routine exits
   // so we keep track if the parent stacking context needs a container too.
   AutoSaveRestoreBlendMode autoRestoreBlendMode(*aBuilder);
   aBuilder->SetContainsBlendMode(false);
  
   if (isTransformed) {
+    const nsRect overflow = GetVisualOverflowRectRelativeToSelf();
     if (aBuilder->IsForPainting() &&
         nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder, this)) {
-      dirtyRect = GetVisualOverflowRectRelativeToSelf();
+      dirtyRect = overflow;
     } else {
-      // Trying to  back-transform arbitrary rects gives us really weird results. I believe 
-      // this is from points that lie beyond the vanishing point. As a workaround we transform t
-      // he overflow rect into screen space and compare in that coordinate system.
-
-      // Transform the overflow rect into screen space
-      nsRect overflow = GetVisualOverflowRectRelativeToSelf();
+      if (overflow.IsEmpty() && !Preserves3DChildren()) {
+        return;
+      }
+      // Trying to back-transform arbitrary rects gives us really weird results. I believe 
+      // this is from points that lie beyond the vanishing point. As a workaround we transform
+      // the overflow rect into screen space and compare in that coordinate system.
+
+      // Transform the overflow rect into screen space.
       nsPoint offset = aBuilder->ToReferenceFrame(this);
-      overflow += offset;
-      overflow = nsDisplayTransform::TransformRect(overflow, this, offset);
-
+      nsRect trans = nsDisplayTransform::TransformRect(overflow + offset, this, offset);
       dirtyRect += offset;
-
-      if (dirtyRect.Intersects(overflow)) {
+      if (dirtyRect.Intersects(trans)) {
         // If they intersect, we take our whole overflow rect. We could instead take the intersection
         // and then reverse transform it but I doubt this extra work is worthwhile.
-        dirtyRect = GetVisualOverflowRectRelativeToSelf();
+        dirtyRect = overflow;
       } else {
+        if (!Preserves3DChildren()) {
+          return;
+        }
         dirtyRect.SetEmpty();
       }
-      if (!Preserves3DChildren() && !dirtyRect.Intersects(GetVisualOverflowRectRelativeToSelf())) {
-        return;
-      }
     }
     inTransform = true;
   }
 
   bool useOpacity = HasOpacity() && !nsSVGUtils::CanOptimizeOpacity(this);
   bool useBlendMode = disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL;
   bool usingSVGEffects = nsSVGIntegrationUtils::UsingEffectsForFrame(this);
   bool useStickyPosition = disp->mPosition == NS_STYLE_POSITION_STICKY &&
--- a/layout/printing/moz.build
+++ b/layout/printing/moz.build
@@ -9,17 +9,21 @@ XPIDL_SOURCES += [
     'nsIPrintProgressParams.idl',
     'nsIPrintStatusFeedback.idl',
 ]
 
 XPIDL_MODULE = 'layout_printing'
 
 UNIFIED_SOURCES += [
     'nsPagePrintTimer.cpp',
+    'nsPrintObject.cpp',
+    'nsPrintPreviewListener.cpp',
+]
+
+# These files cannot be built in unified mode because they force NSPR logging.
+SOURCES += [
     'nsPrintData.cpp',
     'nsPrintEngine.cpp',
-    'nsPrintObject.cpp',
-    'nsPrintPreviewListener.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
 FINAL_LIBRARY = 'gklayout'
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/941940-1-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<p><tt>foobar</tt>
+<p><tt>foo bar</tt>
+<pre>
+foo
+bar
+</pre>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/941940-1.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<p><tt>foo&#13;bar</tt>
+<p><tt>foo&#13;&#10;bar</tt>
+<pre>
+foo&#13;
+bar&#13;
+</pre>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1775,12 +1775,13 @@ random-if(B2G) == 849407-1.html 849407-1
 == 890495-1.html 890495-1-ref.html
 == 894931-1.html 894931-1-ref.html
 == 897491-1.html 897491-1-ref.html
 == 897491-2.html 897491-2-ref.html
 fuzzy(1,10000) fuzzy-if(Android&&AndroidVersion>=15,5,10000) == 902330-1.html 902330-1-ref.html
 fuzzy-if(Android,8,400) == 906199-1.html 906199-1-ref.html
 == 921716-1.html 921716-1-ref.html
 fuzzy-if(cocoaWidget,1,40) == 928607-1.html 928607-1-ref.html
-== 936670-1.svg 936670-1-ref.svg
+== 931464-1.html 931464-1-ref.html
 == 931853.html 931853-ref.html
 == 931853-quirks.html 931853-quirks-ref.html
-== 931464-1.html 931464-1-ref.html
+== 936670-1.svg 936670-1-ref.svg
+== 941940-1.html 941940-1-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-stroke-01.svg
@@ -0,0 +1,21 @@
+<!--
+    Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait">
+
+  <title>Test for changing stroke from none to a solid color value</title>
+
+  <!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=939942 -->
+
+  <rect width="100%" height="100%" fill="red"/>
+
+  <rect id="r" width="100%" height="50%" y="25%" stroke="none" stroke-width="75%" fill="lime"/>
+
+  <script>
+    window.addEventListener("MozReftestInvalidate", function() {
+      document.getElementById("r").style.stroke = "lime";
+      document.documentElement.removeAttribute("class");
+    }, false);
+  </script>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-stroke-opacity-01.svg
@@ -0,0 +1,21 @@
+<!--
+    Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait">
+
+  <title>Test for changing stroke-opacity from 0 to non-zero</title>
+
+  <!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=939942 -->
+
+  <rect width="100%" height="100%" fill="red"/>
+
+  <rect id="r" width="100%" height="50%" y="25%" stroke-opacity="0" stroke="lime" stroke-width="75%" fill="lime"/>
+
+  <script>
+    window.addEventListener("MozReftestInvalidate", function() {
+      document.getElementById("r").style.strokeOpacity = "1";
+      document.documentElement.removeAttribute("class");
+    }, false);
+  </script>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-stroke-width-01.svg
@@ -0,0 +1,21 @@
+<!--
+    Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait">
+
+  <title>Test for changing stroke-width from zero to non-zero</title>
+
+  <!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=939942 -->
+
+  <rect width="100%" height="100%" fill="red"/>
+
+  <rect id="r" width="100%" height="50%" y="25%" stroke="lime" stroke-width="0" fill="lime"/>
+
+  <script>
+    window.addEventListener("MozReftestInvalidate", function() {
+      document.getElementById("r").style.strokeWidth = "75%";
+      document.documentElement.removeAttribute("class");
+    }, false);
+  </script>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/image/image-preserveAspectRatio-05.svg
@@ -0,0 +1,41 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="reftest-wait">
+
+  <title>Testing that dynamic changes to preserveAspectRatio cause a reflow</title>
+  <script>
+<![CDATA[
+
+function doTest() {
+  var i1 = document.getElementById("i1");
+  i1.preserveAspectRatio.baseVal.align =
+    SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMID;
+  var i2 = document.getElementById("i2");
+  i2.preserveAspectRatio.baseVal.align =
+    SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_NONE;
+  document.documentElement.removeAttribute("class");
+}
+
+document.addEventListener("MozReftestInvalidate", doTest, false);
+setTimeout(doTest, 4000); // fallback for running outside reftest
+
+]]>
+  </script>
+
+   <rect width="100%" height="100%" fill="lime"/>
+
+   <!-- this image is a 1x1 red gif -->
+   <image id="i1" preserveAspectRatio="none" x="50" y="50" width="200" height="300"
+          style="image-rendering: optimizeSpeed;"
+          xlink:href="data:image/gif,GIF87a%01%00%01%00%80%01%00%FF%00%00%FF%FF%FF%2C%00%00%00%00%01%00%01%00%00%02%02D%01%00%3B"/>
+  <!-- exactly cover the image if it had preserveAspectRatio="xMidYMid" -->
+  <rect x="50" y="100" width="200" height="200" fill="lime"/>
+
+  <rect x="300" y="100" width="200" height="300" fill="red"/>
+  <!-- and this one is a 1x1 lime PNG -->
+  <!-- which would cover the red rect if it had preserveAspectRatio="none" -->
+  <image id="i2" x="300" y="100" width="200" height="300" preserveAspectRatio="xMidYMid" xlink:href="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%01%00%00%00%01%08%02%00%00%00%90wS%DE%00%00%00%01sRGB%00%AE%CE%1C%E9%00%00%00%0CIDAT%08%D7c%60%F8%CF%00%00%02%02%01%00%AA*%9E'%00%00%00%00IEND%AEB%60%82" />
+
+</svg>
--- a/layout/reftests/svg/image/reftest.list
+++ b/layout/reftests/svg/image/reftest.list
@@ -33,8 +33,9 @@ fuzzy-if(Android,4,34) == image-opacity-
 
 # Tests for <image> with preserveAspectRatio
 skip-if(B2G) == image-preserveAspectRatio-01-raster.svg image-preserveAspectRatio-01-ref.svg # bug 773482
 skip-if(B2G) == image-preserveAspectRatio-01-svg.svg    image-preserveAspectRatio-01-ref.svg # bug 773482
 skip-if(B2G) == image-preserveAspectRatio-02-raster.svg image-preserveAspectRatio-02-ref.svg # bug 773482
 skip-if(B2G) == image-preserveAspectRatio-02-svg.svg    image-preserveAspectRatio-02-ref.svg # bug 773482
 skip-if(B2G) == image-preserveAspectRatio-03.svg        image-preserveAspectRatio-03-ref.svg # bug 773482
 skip-if(B2G) == image-preserveAspectRatio-04.svg        image-preserveAspectRatio-04-ref.svg # bug 773482
+== image-preserveAspectRatio-05.svg ../pass.svg
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -97,16 +97,19 @@ skip-if(B2G) == dynamic-pattern-contents
 == dynamic-rect-01.svg dynamic-rect-01-ref.svg
 == dynamic-rect-02.svg dynamic-rect-02-ref.svg
 == dynamic-rect-03.svg dynamic-rect-03-ref.svg
 == dynamic-rect-04.xhtml pass.svg
 == dynamic-rect-05.svg pass.svg
 == dynamic-reflow-01.svg dynamic-reflow-01-ref.svg
 == dynamic-small-object-scaled-up-01.svg pass.svg
 == dynamic-small-object-scaled-up-02.svg pass.svg
+== dynamic-stroke-01.svg pass.svg
+== dynamic-stroke-opacity-01.svg pass.svg
+== dynamic-stroke-width-01.svg pass.svg
 == dynamic-switch-01.svg pass.svg
 == dynamic-text-01.svg dynamic-text-01-ref.svg
 fuzzy-if(d2d&&layersGPUAccelerated,2,12739) == dynamic-text-02.svg dynamic-text-02-ref.svg # bug 776038 for Win7
 fuzzy-if(d2d&&layersGPUAccelerated,2,10539) == dynamic-text-03.svg dynamic-text-03-ref.svg # bug 776038 for Win7
 random-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu),47,89) == dynamic-text-04.svg dynamic-text-04-ref.svg # bug 421587 for WinXP, bug 776038 for Win7
 skip-if(B2G) == dynamic-text-05.svg pass.svg
 skip-if(B2G) == dynamic-text-06.svg pass.svg
 == dynamic-text-07.svg dynamic-text-07-ref.svg
--- a/layout/style/moz.build
+++ b/layout/style/moz.build
@@ -95,17 +95,16 @@ UNIFIED_SOURCES += [
     'nsCSSStyleSheet.cpp',
     'nsCSSValue.cpp',
     'nsDOMCSSAttrDeclaration.cpp',
     'nsDOMCSSDeclaration.cpp',
     'nsDOMCSSRect.cpp',
     'nsDOMCSSRGBColor.cpp',
     'nsDOMCSSValueList.cpp',
     'nsDOMMediaQueryList.cpp',
-    'nsFontFaceLoader.cpp',
     'nsHTMLCSSStyleSheet.cpp',
     'nsHTMLStyleSheet.cpp',
     'nsLayoutStylesheetCache.cpp',
     'nsMediaFeatures.cpp',
     'nsNthIndexCache.cpp',
     'nsROCSSPrimitiveValue.cpp',
     'nsRuleData.cpp',
     'nsRuleNode.cpp',
@@ -116,18 +115,20 @@ UNIFIED_SOURCES += [
     'nsStyleStruct.cpp',
     'nsStyleTransformMatrix.cpp',
     'nsStyleUtil.cpp',
     'nsTransitionManager.cpp',
     'StyleRule.cpp',
 ]
 
 # nsCSSRuleProcessor.cpp needs to be built separately because it uses plarena.h.
+# nsFontFaceLoader.cpp needs to be built separately because it forces NSPR logging.
 SOURCES += [
     'nsCSSRuleProcessor.cpp',
+    'nsFontFaceLoader.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
 MSVC_ENABLE_PGO = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -939,18 +939,31 @@ nsChangeHint nsStyleSVG::CalcDifference(
     NS_UpdateHint(hint, nsChangeHint_UpdateEffects);
     NS_UpdateHint(hint, nsChangeHint_NeedReflow);
     NS_UpdateHint(hint, nsChangeHint_NeedDirtyReflow); // XXX remove me: bug 876085
     NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
     return hint;
   }
 
   if (mFill != aOther.mFill ||
-      mStroke != aOther.mStroke) {
+      mStroke != aOther.mStroke ||
+      mFillOpacity != aOther.mFillOpacity ||
+      mStrokeOpacity != aOther.mStrokeOpacity) {
     NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
+    if (HasStroke() != aOther.HasStroke() ||
+        (!HasStroke() && HasFill() != aOther.HasFill())) {
+      // Frame bounds and overflow rects depend on whether we "have" fill or
+      // stroke. Whether we have stroke or not just changed, or else we have no
+      // stroke (in which case whether we have fill or not is significant to frame
+      // bounds) and whether we have fill or not just changed. In either case we
+      // need to reflow so the frame rect is updated.
+      // XXXperf this is a waste on non nsSVGPathGeometryFrames.
+      NS_UpdateHint(hint, nsChangeHint_NeedReflow);
+      NS_UpdateHint(hint, nsChangeHint_NeedDirtyReflow); // XXX remove me: bug 876085
+    }
     if (PaintURIChanged(mFill, aOther.mFill) ||
         PaintURIChanged(mStroke, aOther.mStroke)) {
       NS_UpdateHint(hint, nsChangeHint_UpdateEffects);
     }
   }
 
   // Stroke currently contributes to nsSVGPathGeometryFrame::mRect, so
   // we need a reflow here. No intrinsic sizes need to change, so
@@ -970,18 +983,16 @@ nsChangeHint nsStyleSVG::CalcDifference(
     return hint;
   }
 
   if (hint & nsChangeHint_RepaintFrame) {
     return hint; // we don't add anything else below
   }
 
   if ( mStrokeDashoffset      != aOther.mStrokeDashoffset      ||
-       mFillOpacity           != aOther.mFillOpacity           ||
-       mStrokeOpacity         != aOther.mStrokeOpacity         ||
        mClipRule              != aOther.mClipRule              ||
        mColorInterpolation    != aOther.mColorInterpolation    ||
        mColorInterpolationFilters != aOther.mColorInterpolationFilters ||
        mFillRule              != aOther.mFillRule              ||
        mImageRendering        != aOther.mImageRendering        ||
        mPaintOrder            != aOther.mPaintOrder            ||
        mShapeRendering        != aOther.mShapeRendering        ||
        mStrokeDasharrayLength != aOther.mStrokeDasharrayLength ||
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2382,16 +2382,32 @@ struct nsStyleSVG {
   // SVG glyph outer object inheritance for other properties
   bool mStrokeDasharrayFromObject   : 1;
   bool mStrokeDashoffsetFromObject  : 1;
   bool mStrokeWidthFromObject       : 1;
 
   bool HasMarker() const {
     return mMarkerStart || mMarkerMid || mMarkerEnd;
   }
+
+  /**
+   * Returns true if the stroke is not "none" and the stroke-opacity is greater
+   * than zero. This ignores stroke-widths as that depends on the context.
+   */
+  bool HasStroke() const {
+    return mStroke.mType != eStyleSVGPaintType_None && mStrokeOpacity > 0;
+  }
+
+  /**
+   * Returns true if the fill is not "none" and the fill-opacity is greater
+   * than zero.
+   */
+  bool HasFill() const {
+    return mFill.mType != eStyleSVGPaintType_None && mFillOpacity > 0;
+  }
 };
 
 struct nsStyleFilter {
   nsStyleFilter();
   nsStyleFilter(const nsStyleFilter& aSource);
   ~nsStyleFilter();
 
   nsStyleFilter& operator=(const nsStyleFilter& aOther);
--- a/layout/style/nsStyleUtil.h
+++ b/layout/style/nsStyleUtil.h
@@ -2,17 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef nsStyleUtil_h___
 #define nsStyleUtil_h___
 
 #include "nsCoord.h"
 #include "nsCSSProperty.h"
-#include "nsStringFwd.h"
+#include "nsString.h"
 
 class nsCSSValue;
 class nsStringComparator;
 class nsStyleCoord;
 class nsIContent;
 class nsIPrincipal;
 class nsIURI;
 struct gfxFontFeature;
--- a/layout/svg/nsSVGImageFrame.cpp
+++ b/layout/svg/nsSVGImageFrame.cpp
@@ -190,18 +190,21 @@ nsSVGImageFrame::AttributeChanged(int32_
         aAttribute == nsGkAtoms::y ||
         aAttribute == nsGkAtoms::width ||
         aAttribute == nsGkAtoms::height) {
       nsSVGEffects::InvalidateRenderingObservers(this);
       nsSVGUtils::ScheduleReflowSVG(this);
       return NS_OK;
     }
     else if (aAttribute == nsGkAtoms::preserveAspectRatio) {
-      // Don't invalidate (the layers code does that).
-      SchedulePaint();
+      // We don't paint the content of the image using display lists, therefore
+      // we have to invalidate for this children-only transform changes since
+      // there is no layer tree to notice that the transform changed and
+      // recomposite.
+      InvalidateFrame();
       return NS_OK;
     }
   }
   if (aNameSpaceID == kNameSpaceID_XLink &&
       aAttribute == nsGkAtoms::href) {
 
     // Prevent setting image.src by exiting early
     if (nsContentUtils::IsImageSrcSetDisabled()) {
--- a/layout/svg/nsSVGTextFrame2.cpp
+++ b/layout/svg/nsSVGTextFrame2.cpp
@@ -5047,21 +5047,20 @@ nsSVGTextFrame2::ShouldRenderAsPath(nsRe
   // non-1 opacity.
   if (!(style->mFill.mType == eStyleSVGPaintType_None ||
         (style->mFill.mType == eStyleSVGPaintType_Color &&
          style->mFillOpacity == 1))) {
     return true;
   }
 
   // Text has a stroke.
-  if (!(style->mStroke.mType == eStyleSVGPaintType_None ||
-        style->mStrokeOpacity == 0 ||
-        SVGContentUtils::CoordToFloat(PresContext(),
-                                      static_cast<nsSVGElement*>(mContent),
-                                      style->mStrokeWidth) == 0)) {
+  if (style->HasStroke() &&
+      SVGContentUtils::CoordToFloat(PresContext(),
+                                    static_cast<nsSVGElement*>(mContent),
+                                    style->mStrokeWidth) > 0) {
     return true;
   }
 
   return false;
 }
 
 void
 nsSVGTextFrame2::ScheduleReflowSVG()
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -1240,19 +1240,17 @@ nsSVGUtils::CanOptimizeOpacity(nsIFrame 
   // XXX The SVG WG is intending to allow fill, stroke and markers on <image>
   if (type == nsGkAtoms::svgImageFrame) {
     return true;
   }
   const nsStyleSVG *style = aFrame->StyleSVG();
   if (style->HasMarker()) {
     return false;
   }
-  if (style->mFill.mType == eStyleSVGPaintType_None ||
-      style->mFillOpacity <= 0 ||
-      !HasStroke(aFrame)) {
+  if (!style->HasFill() || !HasStroke(aFrame)) {
     return true;
   }
   return false;
 }
 
 gfxMatrix
 nsSVGUtils::AdjustMatrixForUnits(const gfxMatrix &aMatrix,
                                  nsSVGEnum *aUnits,
@@ -1554,19 +1552,17 @@ nsSVGUtils::GetOpacity(nsStyleSVGOpacity
   }
   return opacity;
 }
 
 bool
 nsSVGUtils::HasStroke(nsIFrame* aFrame, gfxTextContextPaint *aContextPaint)
 {
   const nsStyleSVG *style = aFrame->StyleSVG();
-  return style->mStroke.mType != eStyleSVGPaintType_None &&
-         style->mStrokeOpacity > 0 &&
-         GetStrokeWidth(aFrame, aContextPaint) > 0;
+  return style->HasStroke() && GetStrokeWidth(aFrame, aContextPaint) > 0;
 }
 
 float
 nsSVGUtils::GetStrokeWidth(nsIFrame* aFrame, gfxTextContextPaint *aContextPaint)
 {
   const nsStyleSVG *style = aFrame->StyleSVG();
   if (aContextPaint && style->mStrokeWidthFromObject) {
     return aContextPaint->GetStrokeWidth();
--- a/media/libjpeg/Makefile.in
+++ b/media/libjpeg/Makefile.in
@@ -11,10 +11,8 @@ ifeq ($(AS),yasm)
   # yasm doesn't like -c
   AS_DASH_C_FLAG=
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 jpeg_nbits_table.h: $(srcdir)/genTables.py
 	$(PYTHON) $(srcdir)/genTables.py
-
-jchuff.$(OBJ_SUFFIX): jpeg_nbits_table.h
--- a/media/libjpeg/moz.build
+++ b/media/libjpeg/moz.build
@@ -145,8 +145,12 @@ else: # No SIMD support?
     ]
 
 MSVC_ENABLE_PGO = True
 
 if CONFIG['OS_TARGET'] == 'WINNT':
     NO_VISIBILITY_FLAGS = True
 
 FINAL_LIBRARY = 'gkmedias'
+
+GENERATED_FILES = [
+    'jpeg_nbits_table.h',
+]
--- a/media/libsoundtouch/src/Makefile.in
+++ b/media/libsoundtouch/src/Makefile.in
@@ -1,14 +1,12 @@
 # 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/.
 
-EXTRA_DSO_LDOPTS += $(MOZALLOC_LIB)
-
 # Use abort() instead of exception in SoundTouch.
 DEFINES += -DST_NO_EXCEPTION_HANDLING=1
 
 include $(topsrcdir)/config/rules.mk
 
 ifneq (,$(INTEL_ARCHITECTURE))
 ifdef GNU_CC
 mmx_optimized.$(OBJ_SUFFIX): CXXFLAGS+=-msse2
--- a/mfbt/TypedEnum.h
+++ b/mfbt/TypedEnum.h
@@ -118,16 +118,24 @@
 #  define MOZ_BEGIN_NESTED_ENUM_CLASS_HELPER1(Name) \
      enum class Name {
    /* Two-argument form. */
 #  define MOZ_BEGIN_NESTED_ENUM_CLASS_HELPER2(Name, type) \
      enum class Name : type {
 #  define MOZ_END_NESTED_ENUM_CLASS(Name) \
      };
 #  define MOZ_FINISH_NESTED_ENUM_CLASS(Name) /* nothing */
+
+  /*
+   * MOZ_ENUM_CLASS_ENUM_TYPE allows using enum classes
+   * as template parameter types. For that, we need integer types.
+   * In the present case where the compiler supports strong enums,
+   * these are already integer types so there is nothing more to do.
+   */
+#  define MOZ_ENUM_CLASS_ENUM_TYPE(Name) Name
 #else
    /**
     * We need Name to both name a type, and scope the provided enumerator
     * names.  Namespaces and classes both provide scoping, but namespaces
     * aren't types, so we need to use a class that wraps the enum values.  We
     * have an implicit conversion from the inner enum type to the class, so
     * statements like
     *
@@ -233,16 +241,23 @@
      inline int& operator*=(int&, const Name::Enum&) MOZ_DELETE; \
      inline int& operator/=(int&, const Name::Enum&) MOZ_DELETE; \
      inline int& operator%=(int&, const Name::Enum&) MOZ_DELETE; \
      inline int& operator&=(int&, const Name::Enum&) MOZ_DELETE; \
      inline int& operator|=(int&, const Name::Enum&) MOZ_DELETE; \
      inline int& operator^=(int&, const Name::Enum&) MOZ_DELETE; \
      inline int& operator<<=(int&, const Name::Enum&) MOZ_DELETE; \
      inline int& operator>>=(int&, const Name::Enum&) MOZ_DELETE;
+
+  /*
+   * MOZ_ENUM_CLASS_ENUM_TYPE allows using enum classes
+   * as template parameter types. For that, we need integer types.
+   * In the present case, the integer type is the Enum nested type.
+   */
+#  define MOZ_ENUM_CLASS_ENUM_TYPE(Name) Name::Enum
 #endif
 
    /*
     * Count the number of arguments passed to MOZ_COUNT_BEGIN_ENUM_CLASS_ARGS,
     * very carefully tiptoeing around an MSVC bug where it improperly expands
     * __VA_ARGS__ as a single token in argument lists. See these URLs for
     * details:
     *
--- a/netwerk/base/src/moz.build
+++ b/netwerk/base/src/moz.build
@@ -18,17 +18,16 @@ EXPORTS.mozilla.net += [
 
 UNIFIED_SOURCES += [
     'ArrayBufferInputStream.cpp',
     'BackgroundFileSaver.cpp',
     'Dashboard.cpp',
     'EventTokenBucket.cpp',
     'LoadContextInfo.cpp',
     'NetworkActivityMonitor.cpp',
-    'nsAsyncRedirectVerifyHelper.cpp',
     'nsAsyncStreamCopier.cpp',
     'nsAuthInformationHolder.cpp',
     'nsBase64Encoder.cpp',
     'nsBaseChannel.cpp',
     'nsBaseContentStream.cpp',
     'nsBufferedStreams.cpp',
     'nsChannelClassifier.cpp',
     'nsDirectoryIndexStream.cpp',
@@ -49,18 +48,16 @@ UNIFIED_SOURCES += [
     'nsProtocolProxyService.cpp',
     'nsProxyInfo.cpp',
     'nsRequestObserverProxy.cpp',
     'nsSerializationHelper.cpp',
     'nsServerSocket.cpp',
     'nsSimpleNestedURI.cpp',
     'nsSimpleStreamListener.cpp',
     'nsSimpleURI.cpp',
-    'nsSocketTransport2.cpp',
-    'nsSocketTransportService2.cpp',
     'nsStandardURL.cpp',
     'nsStreamListenerTee.cpp',
     'nsStreamListenerWrapper.cpp',
     'nsStreamLoader.cpp',
     'nsStreamTransportService.cpp',
     'nsSyncStreamListener.cpp',
     'nsTemporaryFileInputStream.cpp',
     'nsTransportUtils.cpp',
@@ -71,16 +68,23 @@ UNIFIED_SOURCES += [
     'nsURLParsers.cpp',
     'ProxyAutoConfig.cpp',
     'RedirectChannelRegistrar.cpp',
     'Seer.cpp',
     'StreamingProtocolService.cpp',
     'Tickler.cpp',
 ]
 
+# These files cannot be built in unified mode because they force NSPR logging.
+SOURCES += [
+    'nsAsyncRedirectVerifyHelper.cpp',
+    'nsSocketTransport2.cpp',
+    'nsSocketTransportService2.cpp',
+]
+
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'os2':
     SOURCES += [
         'nsURLHelperOS2.cpp',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     SOURCES += [
         'nsAutodialWin.cpp',
         'nsNativeConnectionHelper.cpp',
--- a/netwerk/dns/Makefile.in
+++ b/netwerk/dns/Makefile.in
@@ -8,15 +8,10 @@ LOCAL_INCLUDES = \
   -I$(srcdir)/../base/src \
   -I. \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 # Generate the include file containing compact, static definitions
 # for effective TLD data.
-nsEffectiveTLDService.$(OBJ_SUFFIX): etld_data.inc
-nsEffectiveTLDService.h: etld_data.inc
-
 etld_data.inc: $(srcdir)/prepare_tlds.py $(srcdir)/effective_tld_names.dat
 	$(PYTHON) $(srcdir)/prepare_tlds.py $(srcdir)/effective_tld_names.dat > etld_data.inc
-
-GARBAGE += etld_data.inc
--- a/netwerk/dns/moz.build
+++ b/netwerk/dns/moz.build
@@ -16,25 +16,29 @@ XPIDL_SOURCES += [
 XPIDL_MODULE = 'necko_dns'
 
 EXPORTS.mozilla.net += [
     'DNS.h',
 ]
 
 SOURCES += [
     'nsEffectiveTLDService.cpp', # Excluded from UNIFIED_SOURCES due to special build flags.
+    'nsHostResolver.cpp',        # Excluded from UNIFIED_SOURCES due to NSPR forced logging.
 ]
 
 UNIFIED_SOURCES += [
     'DNS.cpp',
     'nameprep.c',
     'nsDNSService2.cpp',
-    'nsHostResolver.cpp',
     'nsIDNService.cpp',
     'punycode.c',
     'race.c',
 ]
 
 FAIL_ON_WARNINGS = True
 
 MSVC_ENABLE_PGO = True
 
 FINAL_LIBRARY = 'necko'
+
+GENERATED_FILES = [
+    'etld_data.inc',
+]
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -26,17 +26,16 @@
 #include "prerror.h"
 #include "prtime.h"
 #include "prlog.h"
 #include "pldhash.h"
 #include "plstr.h"
 #include "nsURLHelper.h"
 #include "nsThreadUtils.h"
 
-#include "mozilla/DebugOnly.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/VisualEventTracer.h"
 
 using namespace mozilla;
 using namespace mozilla::net;
 
@@ -304,17 +303,17 @@ HostDB_MoveEntry(PLDHashTable *table,
 
 static void
 HostDB_ClearEntry(PLDHashTable *table,
                   PLDHashEntryHdr *entry)
 {
     nsHostDBEnt *he = static_cast<nsHostDBEnt*>(entry);
     MOZ_ASSERT(he, "nsHostDBEnt is null!");
 
-    DebugOnly<nsHostRecord*> hr = he->rec;
+    nsHostRecord *hr = he->rec;
     MOZ_ASSERT(hr, "nsHostDBEnt has null host record!");
 
     LOG(("Clearing cache db entry for host [%s].\n", hr->host));
 #if defined(DEBUG) && defined(PR_LOGGING)
     {
         MutexAutoLock lock(hr->addr_info_lock);
         if (!hr->addr_info) {
             LOG(("No address info for host [%s].\n", hr->host));
--- a/parser/expat/lib/Makefile.in
+++ b/parser/expat/lib/Makefile.in
@@ -1,9 +1,7 @@
 # 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 $(topsrcdir)/config/rules.mk
 
-xmltok.$(OBJ_SUFFIX): moz_extensions.c
-
 DEFINES += -DHAVE_EXPAT_CONFIG_H
--- a/parser/expat/lib/moz.build
+++ b/parser/expat/lib/moz.build
@@ -13,8 +13,12 @@ SOURCES += [
     'xmlparse.c',
     'xmlrole.c',
     'xmltok.c',
 ]
 
 MSVC_ENABLE_PGO = True
 
 FINAL_LIBRARY = 'gkmedias'
+
+GENERATED_FILES = [
+    'moz_extensions.c',
+]
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -197,16 +197,17 @@ class TreeMetadataEmitter(LoggingMixin):
             EXTRA_COMPONENTS='EXTRA_COMPONENTS',
             EXTRA_JS_MODULES='EXTRA_JS_MODULES',
             EXTRA_PP_COMPONENTS='EXTRA_PP_COMPONENTS',
             EXTRA_PP_JS_MODULES='EXTRA_PP_JS_MODULES',
             FAIL_ON_WARNINGS='FAIL_ON_WARNINGS',
             FILES_PER_UNIFIED_FILE='FILES_PER_UNIFIED_FILE',
             FORCE_SHARED_LIB='FORCE_SHARED_LIB',
             FORCE_STATIC_LIB='FORCE_STATIC_LIB',
+            GENERATED_FILES='GENERATED_FILES',
             HOST_LIBRARY_NAME='HOST_LIBRARY_NAME',
             IS_COMPONENT='IS_COMPONENT',
             JS_MODULES_PATH='JS_MODULES_PATH',
             LIBS='LIBS',
             LIBXUL_LIBRARY='LIBXUL_LIBRARY',
             MSVC_ENABLE_PGO='MSVC_ENABLE_PGO',
             NO_DIST_INSTALL='NO_DIST_INSTALL',
             OS_LIBS='OS_LIBS',
--- a/python/mozbuild/mozbuild/frontend/sandbox_symbols.py
+++ b/python/mozbuild/mozbuild/frontend/sandbox_symbols.py
@@ -103,16 +103,24 @@ VARIABLES = {
         """Generated source code files that can be compiled together.
 
         This variable contains a list of generated source code files to
         compile, that can be concatenated all together, with UNIFIED_SOURCES,
         and built as a single source file. This can help make the build faster
         and reduce the debug info size.
         """, 'compile'),
 
+    'GENERATED_FILES': (StrictOrderingOnAppendList, list, [],
+        """Generic generated files.
+
+        This variable contains a list of generate files for the build system
+        to generate at export time. The rules for those files still live in
+        Makefile.in.
+        """, 'export'),
+
     'DEFINES': (OrderedDict, dict, OrderedDict(),
         """Dictionary of compiler defines to declare.
 
         These are passed in to the compiler as ``-Dkey='value'`` for string
         values, ``-Dkey=value`` for numeric values, or ``-Dkey`` if the
         value is True. Note that for string values, the outer-level of
         single-quotes will be consumed by the shell. If you want to have
         a string-literal in the program, the value needs to have
--- a/testing/mochitest/b2g.json
+++ b/testing/mochitest/b2g.json
@@ -204,20 +204,22 @@
     "content/base/test/test_object.html":"needs plugin support",
     "content/base/test/test_bug827160.html": "needs plugin support",
 
     "content/base/test/csp/test_CSP_evalscript.html":"observer not working",
     "content/base/test/csp/test_CSP_evalscript_getCRMFRequest.html":"observer not working",
     "content/base/test/csp/test_CSP_frameancestors.html":"observer not working",
     "content/base/test/csp/test_CSP.html":"observer not working",
     "content/base/test/csp/test_bug836922_npolicies.html":"observer not working",
+    "content/base/test/csp/test_bug886164.html":"observer not working",
     "content/base/test/csp/test_CSP_bug916446.html":"observer not working",
     "content/base/test/csp/test_CSP_bug909029.html":"observer not working",
     "content/base/test/csp/test_policyuri_regression_from_multipolicy.html":"observer not working",
     "content/base/test/csp/test_nonce_source.html":"observer not working",
+    "content/base/test/csp/test_CSP_bug941404.html":"observer not working",
 
     "content/base/test/test_CrossSiteXHR_origin.html":"https not working, bug 907770",
     "content/base/test/test_plugin_freezing.html":"",
     "content/base/test/test_bug466409.html":"",
     "content/base/test/test_bug482935.html":"",
     "content/base/test/test_bug498433.html":"",
     "content/base/test/test_bug650386_redirect_301.html":"",
     "content/base/test/test_bug650386_redirect_302.html":"",
--- a/testing/mochitest/mach_commands.py
+++ b/testing/mochitest/mach_commands.py
@@ -309,17 +309,25 @@ class MochitestRunner(MozbuildObject):
         if test_path:
             test_root = runner.getTestRoot(options)
             test_root_file = mozpack.path.join(self.mochitest_dir, test_root, test_path)
             if not os.path.exists(test_root_file):
                 print('Specified test path does not exist: %s' % test_root_file)
                 print('You may need to run |mach build| to build the test files.')
                 return 1
 
-            options.testPath = test_path
+            # Handle test_path pointing at a manifest file so conditions in
+            # the manifest are processed.  This is a temporary solution
+            # pending bug 938019.
+            # The manifest basename is the same as |suite|, except for plain
+            manifest_base = 'mochitest' if suite == 'plain' else suite
+            if os.path.basename(test_root_file) == manifest_base + '.ini':
+                options.manifestFile = test_root_file
+            else:
+                options.testPath = test_path
 
         if rerun_failures:
             options.testManifest = failure_file_path
 
         if debugger:
             options.debugger = debugger
 
         if debugger_args:
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -355,19 +355,22 @@ class MochitestUtilsMixin(object):
     """ Build the url path to the specific test harness and test file or directory
         Build a manifest of tests to run and write out a json file for the harness to read
     """
     if options.manifestFile and os.path.isfile(options.manifestFile):
       manifest = TestManifest(strict=False)
       manifest.read(options.manifestFile)
       # Bug 883858 - return all tests including disabled tests
       tests = manifest.active_tests(disabled=True, **mozinfo.info)
+      # We need to ensure we match on a complete directory name matching the
+      # test root, and not a substring somewhere else in the path.
+      test_root = os.path.sep + self.getTestRoot(options) + os.path.sep
       paths = []
       for test in tests:
-        tp = test['path'].split(self.getTestRoot(options), 1)[1].strip('/')
+        tp = test['path'].split(test_root, 1)[1].replace('\\', '/').strip('/')
 
         # Filter out tests if we are using --test-path
         if options.testPath and not tp.startswith(options.testPath):
           continue
 
         testob = {'path': tp}
         if test.has_key('disabled'):
           testob['disabled'] = test['disabled']
deleted file mode 100644
--- a/toolkit/components/reflect/Makefile.in
+++ /dev/null
@@ -1,8 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-EXTRA_DSO_LDOPTS += \
-    $(MOZ_COMPONENT_LIBS) \
-    $(MOZ_JS_LIBS) \
-    $(NULL)
--- a/toolkit/components/telemetry/Makefile.in
+++ b/toolkit/components/telemetry/Makefile.in
@@ -4,21 +4,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 USE_RCS_MK := 1
 include $(topsrcdir)/config/makefiles/rcs.mk
 
 LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/build
 LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/threads
 
-EXTRA_DSO_LDOPTS += \
-  $(MOZ_COMPONENT_LIBS) \
-  $(MOZ_JS_LIBS) \
-  $(NULL)
-
 ifdef MOZILLA_OFFICIAL
 DEFINES += -DMOZILLA_OFFICIAL
 endif
 
 MOZ_HISTOGRAMS_VERSION ?= $(call getSourceRepo)/rev/$(firstword $(shell hg -R $(topsrcdir) parent --template="{node|short}\n" 2>/dev/null))
 ifdef MOZ_HISTOGRAMS_VERSION
 DEFINES += -DHISTOGRAMS_FILE_VERSION="$(MOZ_HISTOGRAMS_VERSION)"
 endif
@@ -44,11 +39,9 @@ data_python_deps := \
   $(srcdir)/histogram_tools.py \
   $(NULL)
 
 $(histogram_enum_file): $(histograms_file) $(enum_python_deps)
 	$(PYTHON) $(srcdir)/gen-histogram-enum.py $< > $@
 $(histogram_data_file): $(histograms_file) $(data_python_deps)
 	$(PYTHON) $(srcdir)/gen-histogram-data.py $< > $@
 
-Telemetry.$(OBJ_SUFFIX): $(histogram_data_file)
-
-GARBAGE += $(histogram_data_file) $(histogram_enum_file)
+GARBAGE += $(histogram_enum_file)
--- a/toolkit/components/telemetry/moz.build
+++ b/toolkit/components/telemetry/moz.build
@@ -39,8 +39,12 @@ EXTRA_JS_MODULES += [
 
 FAIL_ON_WARNINGS = True
 
 MSVC_ENABLE_PGO = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
+
+GENERATED_FILES = [
+    'TelemetryHistogramData.inc',
+]
--- a/toolkit/devtools/server/Makefile.in
+++ b/toolkit/devtools/server/Makefile.in
@@ -1,15 +1,10 @@
 # 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/.
 
-EXTRA_DSO_LDOPTS += \
-    $(MOZ_COMPONENT_LIBS) \
-    $(MOZ_JS_LIBS) \
-    $(NULL)
-
 include $(topsrcdir)/config/rules.mk
 
 libs::
 	$(INSTALL) $(IFLAGS1) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools
 	$(INSTALL) $(IFLAGS1) $(srcdir)/*.js $(FINAL_TARGET)/modules/devtools/server
 	$(INSTALL) $(IFLAGS1) $(srcdir)/actors/*.js $(FINAL_TARGET)/modules/devtools/server/actors
--- a/widget/cocoa/moz.build
+++ b/widget/cocoa/moz.build
@@ -19,25 +19,22 @@ EXPORTS += [
 
 LIBRARY_NAME = 'widget_mac'
 
 UNIFIED_SOURCES += [
     'GfxInfo.mm',
     'NativeKeyBindings.mm',
     'nsAppShell.mm',
     'nsBidiKeyboard.mm',
-    'nsChildView.mm',
-    'nsClipboard.mm',
     'nsCocoaFeatures.mm',
     'nsCocoaUtils.mm',
     'nsCocoaWindow.mm',
     'nsColorPicker.mm',
     'nsCursorManager.mm',
     'nsDeviceContextSpecX.mm',
-    'nsDragService.mm',
     'nsFilePicker.mm',
     'nsIdleServiceX.mm',
     'nsLookAndFeel.mm',
     'nsMacCursor.mm',
     'nsMacDockSupport.mm',
     'nsMacWebAppUtils.mm',
     'nsMenuBarX.mm',
     'nsMenuGroupOwnerX.mm',
@@ -51,18 +48,25 @@ UNIFIED_SOURCES += [
     'nsPrintSettingsX.mm',
     'nsScreenCocoa.mm',
     'nsScreenManagerCocoa.mm',
     'nsSound.mm',
     'nsStandaloneNativeMenu.mm',
     'nsToolkit.mm',
     'nsWidgetFactory.mm',
     'nsWindowMap.mm',
+    'WidgetTraceEvent.mm',
+]
+
+# These files cannot be built in unified mode because they force NSPR logging.
+SOURCES += [
+    'nsChildView.mm',
+    'nsClipboard.mm',
+    'nsDragService.mm',
     'TextInputHandler.mm',
-    'WidgetTraceEvent.mm',
 ]
 
 if CONFIG['TARGET_CPU'] == 'x86_64':
     UNIFIED_SOURCES += [
         'ComplexTextInputPanel.mm',
     ]
 
 FINAL_LIBRARY = 'xul'
--- a/widget/qt/faststartupqt/Makefile.in
+++ b/widget/qt/faststartupqt/Makefile.in
@@ -1,21 +1,15 @@
 # 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/.
 
 DIST_INSTALL = 1
 STL_FLAGS=
 
-EXTRA_DSO_LDOPTS = \
-	$(XPCOM_GLUE_LDOPTS) \
-	$(XPCOM_FROZEN_LDOPTS) \
-	$(MOZ_QT_LIBS) \
-	$(NULL)
-
 LOCAL_INCLUDES += \
 	-I$(topsrcdir)/xpcom/build \
 	-I$(topsrcdir)/widget/qt \
 	$(NULL)
 
 EXPORT_SOURCES = \
 	$(topsrcdir)/widget/qt/moziqwidget.h \
 	$(topsrcdir)/toolkit/xre/nsQAppInstance.h \
--- a/xpcom/components/moz.build
+++ b/xpcom/components/moz.build
@@ -21,23 +21,23 @@ EXPORTS += [
 ]
 
 EXPORTS.mozilla += [
     'Module.h',
     'ModuleLoader.h',
     'ModuleUtils.h',
 ]
 
-# These two files cannot be built in unified mode because they use the
-# PL_ARENA_CONST_ALIGN_MASK macro with plarena.h.
+# nsCategoryManager.cpp and nsComponentManager.cpp cannot be built in
+# unified mode because they use thea PL_ARENA_CONST_ALIGN_MASK macro
+# with plarena.h.
+# nsNativeComponentLoader.cpp cannot be built in unified mode because it
+# forces NSPR logging.
 SOURCES += [
+    'ManifestParser.cpp',
     'nsCategoryManager.cpp',
     'nsComponentManager.cpp',
-]
-
-UNIFIED_SOURCES += [
-    'ManifestParser.cpp',
     'nsNativeComponentLoader.cpp',
 ]
 
 MSVC_ENABLE_PGO = True
 
 FINAL_LIBRARY = 'xpcom_core'
--- a/xpcom/reflect/xptcall/src/md/unix/Makefile.in
+++ b/xpcom/reflect/xptcall/src/md/unix/Makefile.in
@@ -159,17 +159,13 @@ ifndef GNU_CC
 xptcstubsdef_asm.solx86: $(DIST)/include/xptcstubsdef.inc
 	sed \
 	  -e 's/^\(STUB_ENTRY\)(\([0-9]\))/\11\(\2\)/' \
 	  -e 's/^\(STUB_ENTRY\)(\([0-9][0-9]\))/\12\(\2\)/' \
 	  -e 's/^\(STUB_ENTRY\)(\([0-9][0-9][0-9]\))/\13\(\2\)/' \
 	$(DIST)/include/xptcstubsdef.inc > $@
 ifeq (x86_64,$(OS_TEST))
 ASFLAGS += -xarch=amd64
-
-xptcstubs_asm_x86_64_solaris_SUNW.$(OBJ_SUFFIX): xptcstubsdef_asm.solx86
-else
-xptcstubs_asm_x86_solaris_SUNW.$(OBJ_SUFFIX): xptcstubsdef_asm.solx86
 endif
 
 endif
 endif
 endif
--- a/xpcom/reflect/xptcall/src/md/unix/moz.build
+++ b/xpcom/reflect/xptcall/src/md/unix/moz.build
@@ -276,16 +276,19 @@ if CONFIG['OS_ARCH'] == 'OpenBSD' and CO
     SOURCES += [
         'xptcinvoke_asm_sparc64_openbsd.s',
         'xptcinvoke_sparc64_openbsd.cpp',
         'xptcstubs_asm_sparc64_openbsd.s',
         'xptcstubs_sparc64_openbsd.cpp',
     ]
 
 if CONFIG['OS_ARCH'] == 'SunOS' and CONFIG['OS_TEST'].find('86') == -1:
+    GENERATED_FILES = [
+        'xptcstubsdef_asm.solx86',
+    ]
     if CONFIG['HAVE_64BIT_OS']:
         SOURCES += [
             'xptcinvoke_sparcv9_solaris.cpp',
             'xptcstubs_sparcv9_solaris.cpp',
         ]
     else:
         SOURCES += [
             'xptcinvoke_sparc_solaris.cpp',