bug 789919, (snow-white) make addref/release of CCable objects faster by removing indirect refcnt increase/decrease, r=mccr8, test changes r=ehsan
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Tue, 09 Jul 2013 13:30:58 -0400
changeset 151078 d42a2a82f3d248a677f5534c24fd7dc0dbc2930e
parent 151077 6bd848b916c8b466cff70aebad50345820ee91e2
child 151079 a65599fc1dd4f6e8938e83a48142f34203a5edb8
push id382
push userakeybl@mozilla.com
push dateMon, 21 Oct 2013 21:47:13 +0000
treeherdermozilla-release@5f1868ee45cb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8, test, ehsan
bugs789919
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 789919, (snow-white) make addref/release of CCable objects faster by removing indirect refcnt increase/decrease, r=mccr8, test changes r=ehsan
browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir_c.js
browser/components/privatebrowsing/test/browser/browser_privatebrowsing_popupblocker.js
browser/components/privatebrowsing/test/browser/browser_privatebrowsing_urlbarfocus.js
browser/components/privatebrowsing/test/browser/browser_privatebrowsing_windowtitle.js
browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoomrestore.js
browser/components/privatebrowsing/test/browser/head.js
browser/components/search/test/browser_private_search_perwindowpb.js
browser/components/search/test/head.js
browser/components/sessionstore/test/browser_248970_b_perwindowpb.js
browser/components/sessionstore/test/browser_394759_perwindowpb.js
browser/components/sessionstore/test/browser_819510_perwindowpb.js
browser/components/sessionstore/test/head.js
browser/devtools/debugger/test/browser_dbg_multiple-windows.js
caps/src/nsScriptSecurityManager.cpp
content/base/src/Attr.cpp
content/base/src/FragmentOrElement.cpp
content/base/src/nsContentIterator.cpp
content/base/src/nsDocument.cpp
content/base/src/nsGenericDOMDataNode.cpp
content/base/src/nsINode.cpp
content/base/src/nsNodeUtils.cpp
content/base/src/nsRange.cpp
content/events/src/nsDOMEventTargetHelper.cpp
content/events/src/nsDOMEventTargetHelper.h
dom/base/nsJSEnvironment.cpp
dom/file/ArchiveZipFile.cpp
dom/indexedDB/IDBDatabase.cpp
dom/indexedDB/IDBDatabase.h
dom/tests/browser/browser_geolocation_privatebrowsing_perwindowpb.js
editor/libeditor/base/DeleteRangeTxn.h
editor/libeditor/base/EditTxn.cpp
editor/libeditor/base/EditTxn.h
editor/txmgr/src/nsTransactionItem.cpp
editor/txmgr/src/nsTransactionItem.h
editor/txmgr/tests/TestTXMgr.cpp
js/xpconnect/src/xpcprivate.h
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
rdf/base/src/nsInMemoryDataSource.cpp
xpcom/base/CycleCollectedJSRuntime.cpp
xpcom/base/CycleCollectedJSRuntime.h
xpcom/base/nsAgg.h
xpcom/base/nsCycleCollector.cpp
xpcom/base/nsCycleCollector.h
xpcom/build/FrozenFunctions.cpp
xpcom/build/nsXPCOM.h
xpcom/build/nsXPCOMPrivate.h
xpcom/glue/nsCycleCollectionParticipant.h
xpcom/glue/nsISupportsImpl.h
xpcom/glue/standalone/nsXPCOMGlue.cpp
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir_c.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir_c.js
@@ -45,18 +45,16 @@ function test() {
   };
 
   prefs.setComplexValue("lastDir", Ci.nsIFile, tmpDir);
 
   function testOnWindow(aPrivate, aCallback) {
     whenNewWindowLoaded({private: aPrivate}, function(win) {
       let gDownloadLastDir = new DownloadLastDir(win);
       aCallback(win, gDownloadLastDir);
-      gDownloadLastDir.cleanupPrivateFile();
-      win.close();
     });
   }
 
   function testDownloadDir(aWin, gDownloadLastDir, aFile, aDisplayDir, aLastDir,
                            aGlobalLastDir, aCallback) {
     // Check lastDir preference.
     is(prefs.getComplexValue("lastDir", Ci.nsIFile).path, aDisplayDir.path,
        "LastDir should be the expected display dir");
@@ -72,16 +70,18 @@ function test() {
          "File picker should start with browser.download.lastDir");
       // browser.download.lastDir should be modified on not private windows
       is(prefs.getComplexValue("lastDir", Ci.nsIFile).path, aLastDir.path,
          "LastDir should be the expected last dir");
       // gDownloadLastDir should be usable outside of private windows
       is(gDownloadLastDir.file.path, aGlobalLastDir.path,
          "gDownloadLastDir should be the expected global last dir");
 
+      gDownloadLastDir.cleanupPrivateFile();
+      aWin.close();
       aCallback();
     });
   }
 
   testOnWindow(false, function(win, downloadDir) {
     testDownloadDir(win, downloadDir, file1, tmpDir, dir1, dir1, function() {
       testOnWindow(true, function(win, downloadDir) {
         testDownloadDir(win, downloadDir, file2, dir1, dir1, dir2, function() {
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_popupblocker.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_popupblocker.js
@@ -53,22 +53,18 @@ function test() {
 
   function finishTest() {
     // cleanup
     gPrefService.setBoolPref("dom.disable_open_during_load", oldPopupPolicy);
     finish();
   };
 
   function testOnWindow(options, callback) {
-    let win = OpenBrowserWindow(options);
-    win.addEventListener("load", function onLoad() {
-      win.removeEventListener("load", onLoad, false);
-      windowsToClose.push(win);
-      executeSoon(function() callback(win));
-    }, false);
+    let win = whenNewWindowLoaded(options, callback);
+    windowsToClose.push(win);
   };
 
   registerCleanupFunction(function() {
     windowsToClose.forEach(function(win) {
       win.close();
     });
   });
 
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_urlbarfocus.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_urlbarfocus.js
@@ -28,40 +28,43 @@ function test() {
   let windowsToClose = [];
   function testOnWindow(aPrivate, aCallback) {
     whenNewWindowLoaded({private: aPrivate}, function(win) {
       windowsToClose.push(win);
       executeSoon(function() aCallback(win));
     });
   }
 
-  registerCleanupFunction(function() {
+  function doneWithTests() {
     windowsToClose.forEach(function(win) {
       win.close();
     });
-  });
+    finish();
+  }
 
   function whenLoadTab(aPrivate, aCallback) {
     testOnWindow(aPrivate, function(win) {
-      let browser = win.gBrowser.selectedBrowser;
-      browser.addEventListener("load", function() {
-        browser.removeEventListener("load", arguments.callee, true);
-        aCallback(win);
-      }, true);
       if (!aPrivate) {
+        let browser = win.gBrowser.selectedBrowser;
+        browser.addEventListener("load", function() {
+          browser.removeEventListener("load", arguments.callee, true);
+          aCallback(win);
+        }, true);
         browser.focus();
         browser.loadURI(TEST_URL);
+      } else {
+        aCallback(win);
       }
     });
   }
 
   whenLoadTab(false, function(win) {
     checkUrlbarFocus(win, false, function() {
       whenLoadTab(true, function(win) {
         checkUrlbarFocus(win, true, function() {
           whenLoadTab(false, function(win) {
-            checkUrlbarFocus(win, false, finish);
+            checkUrlbarFocus(win, false, doneWithTests);
           });
         });
       });
     });
   });
 }
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_windowtitle.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_windowtitle.js
@@ -39,39 +39,46 @@ function test() {
   }
 
   function testTabTitle(aWindow, url, insidePB, expected_title, funcNext) {
     executeSoon(function () {
       let tab = aWindow.gBrowser.selectedTab = aWindow.gBrowser.addTab();
       let browser = aWindow.gBrowser.selectedBrowser;
       browser.stop();
       // ensure that the test is run after the titlebar has been updated
-      browser.addEventListener("pageshow", function () {
-        browser.removeEventListener("pageshow", arguments.callee, false);
+      browser.addEventListener("load", function () {
+        browser.removeEventListener("load", arguments.callee, true);
         executeSoon(function () {
+          if (aWindow.document.title != expected_title) {
+            executeSoon(arguments.callee);
+            return;
+          }
           is(aWindow.document.title, expected_title, "The window title for " + url +
              " is correct (" + (insidePB ? "inside" : "outside") +
              " private browsing mode)");
 
           let win = aWindow.gBrowser.replaceTabWithWindow(tab);
           win.addEventListener("load", function() {
             win.removeEventListener("load", arguments.callee, false);
-
             executeSoon(function() {
+              if (win.document.title != expected_title) {
+                executeSoon(arguments.callee);
+                return;
+              }
               is(win.document.title, expected_title, "The window title for " + url +
                  " detached tab is correct (" + (insidePB ? "inside" : "outside") +
                  " private browsing mode)");
               win.close();
               aWindow.close();
 
               setTimeout(funcNext, 0);
             });
           }, false);
         });
-      }, false);
+      }, true);
 
       browser.loadURI(url);
     });
   }
 
   whenNewWindowLoaded({private: false}, function(win) {
     testTabTitle(win, "about:blank", false, page_without_title, function() {
       whenNewWindowLoaded({private: false}, function(win) {
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoomrestore.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoomrestore.js
@@ -51,35 +51,31 @@ function test() {
     aCallback();
   }
 
   function finishTest() {
     // cleanup
     windowsToReset.forEach(function(win) {
       win.FullZoom.reset();
     });
+    windowsToClose.forEach(function(win) {
+      win.close();
+    });
     finish();
   }
 
   function testOnWindow(options, callback) {
-    let win = OpenBrowserWindow(options);
-    win.addEventListener("load", function onLoad() {
-      win.removeEventListener("load", onLoad, false);
-      windowsToClose.push(win);
-      windowsToReset.push(win);
-      executeSoon(function() callback(win));
-    }, false);
+    let win = whenNewWindowLoaded(options,
+      function(win) {
+        windowsToClose.push(win);
+        windowsToReset.push(win);
+        executeSoon(function() { callback(win); });
+      });
   };
 
-  registerCleanupFunction(function() {
-    windowsToClose.forEach(function(win) {
-      win.close();
-    });
-  });
-
   testOnWindow({}, function(win) {
     doTestWhenReady(true, win, function() {
       testOnWindow({private: true}, function(win) {
         doTestWhenReady(false, win, finishTest);
       });
     });
   });
 }
--- a/browser/components/privatebrowsing/test/browser/head.js
+++ b/browser/components/privatebrowsing/test/browser/head.js
@@ -1,14 +1,39 @@
 function whenNewWindowLoaded(aOptions, aCallback) {
   let win = OpenBrowserWindow(aOptions);
+  let gotLoad = false;
+  let gotActivate = (Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager).activeWindow == win);
+
+  function maybeRunCallback() {
+    if (gotLoad && gotActivate) {
+      win.BrowserChromeTest.runWhenReady(function() {
+        executeSoon(function() { aCallback(win); });
+      });
+    }
+  }
+
+  if (!gotActivate) {
+    win.addEventListener("activate", function onActivate() {
+      info("Got activate.");
+      win.removeEventListener("activate", onActivate, false);
+      gotActivate = true;
+      maybeRunCallback();
+    }, false);
+  } else {
+    info("Was activated.");
+  }
+
   win.addEventListener("load", function onLoad() {
+    info("Got load");
     win.removeEventListener("load", onLoad, false);
-    aCallback(win);
+    gotLoad = true;
+    maybeRunCallback();
   }, false);
+  return win;
 }
 
 function newDirectory() {
   let FileUtils =
     Cu.import("resource://gre/modules/FileUtils.jsm", {}).FileUtils;
   let tmpDir = FileUtils.getDir("TmpD", [], true);
   let dir = tmpDir.clone();
   dir.append("testdir");
--- a/browser/components/search/test/browser_private_search_perwindowpb.js
+++ b/browser/components/search/test/browser_private_search_perwindowpb.js
@@ -52,21 +52,18 @@ function test() {
       }
     };
     Services.search.addEngine(engineURL + "426329.xml",
                               Ci.nsISearchEngine.DATA_XML,
                               "data:image/x-icon,%00", false, installCallback);
   }
 
   function testOnWindow(aIsPrivate, aCallback) {
-    let win = OpenBrowserWindow({ private: aIsPrivate });
-    waitForFocus(function() {
-      windowsToClose.push(win);
-      executeSoon(function() aCallback(win));
-    }, win);
+    let win = whenNewWindowLoaded({ private: aIsPrivate }, aCallback);
+    windowsToClose.push(win);
   }
 
   addEngine(function() {
     testOnWindow(false, function(win) {
       performSearch(win, false, function() {
         testOnWindow(true, function(win) {
           performSearch(win, true, function() {
             testOnWindow(false, function(win) {
--- a/browser/components/search/test/head.js
+++ b/browser/components/search/test/head.js
@@ -1,11 +1,44 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+function whenNewWindowLoaded(aOptions, aCallback) {
+  let win = OpenBrowserWindow(aOptions);
+  let gotLoad = false;
+  let gotActivate = (Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager).activeWindow == win);
+
+  function maybeRunCallback() {
+    if (gotLoad && gotActivate) {
+      win.BrowserChromeTest.runWhenReady(function() {
+        executeSoon(function() { aCallback(win); });
+      });
+    }
+  }
+
+  if (!gotActivate) {
+    win.addEventListener("activate", function onActivate() {
+      info("Got activate.");
+      win.removeEventListener("activate", onActivate, false);
+      gotActivate = true;
+      maybeRunCallback();
+    }, false);
+  } else {
+    info("Was activated.");
+  }
+
+  win.addEventListener("load", function onLoad() {
+    info("Got load");
+    win.removeEventListener("load", onLoad, false);
+    gotLoad = true;
+    maybeRunCallback();
+  }, false);
+  return win;
+}
+
 /**
  * Recursively compare two objects and check that every property of expectedObj has the same value
  * on actualObj.
  */
 function isSubObjectOf(expectedObj, actualObj, name) {
   for (let prop in expectedObj) {
     if (typeof expectedObj[prop] == 'function')
       continue;
--- a/browser/components/sessionstore/test/browser_248970_b_perwindowpb.js
+++ b/browser/components/sessionstore/test/browser_248970_b_perwindowpb.js
@@ -77,17 +77,17 @@ function test() {
   // Test (B) : Session data restoration between windows          //
   //////////////////////////////////////////////////////////////////
 
   let rootDir = getRootDirectory(gTestPath);
   const testURL = rootDir + "browser_248970_b_sample.html";
   const testURL2 = "http://mochi.test:8888/browser/" +
     "browser/components/sessionstore/test/browser_248970_b_sample.html";
 
-  whenNewWindowLoaded(false, function(aWin) {
+  whenNewWindowLoaded({ private: false }, function(aWin) {
     windowsToClose.push(aWin);
 
     // get closed tab count
     let count = ss.getClosedTabCount(aWin);
     let max_tabs_undo =
       Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo");
     ok(0 <= count && count <= max_tabs_undo,
       "getClosedTabCount should return zero or at most max_tabs_undo");
@@ -119,17 +119,17 @@ function test() {
       // 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() {
         is(testURL, tab_A_restored.linkedBrowser.currentURI.spec,
            "it's the same tab that we expect");
         aWin.gBrowser.removeTab(tab_A_restored);
 
-        whenNewWindowLoaded(true, function(aWin) {
+        whenNewWindowLoaded({ private: true }, function(aWin) {
           windowsToClose.push(aWin);
 
           // setup a state for tab (B) so we can check that its duplicated
           // properly
           let key1 = "key1";
           let value1 = "Value " + Math.random();
           let state1 = {
             entries: [{ url: testURL2 }], extData: { key1: value1 }
--- a/browser/components/sessionstore/test/browser_394759_perwindowpb.js
+++ b/browser/components/sessionstore/test/browser_394759_perwindowpb.js
@@ -22,33 +22,33 @@ function test() {
   registerCleanupFunction(function() {
     Services.prefs.clearUserPref("browser.sessionstore.interval");
     windowsToClose.forEach(function(win) {
       win.close();
     });
   });
 
   function testOpenCloseWindow(aIsPrivate, aTest, aCallback) {
-    whenNewWindowLoaded(aIsPrivate, function(win) {
+    whenNewWindowLoaded({ private: aIsPrivate }, function(win) {
       win.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
         win.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
         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(aIsPrivate, function(win) {
+    whenNewWindowLoaded({ private: aIsPrivate }, function(win) {
       windowsToClose.push(win);
       executeSoon(function() checkClosedWindows(aIsPrivate, aValue, aCallback));
     });
   }
 
   function checkClosedWindows(aIsPrivate, aValue, aCallback) {
     let data = JSON.parse(ss.getClosedWindowData())[0];
     is(ss.getClosedWindowCount(), 1, "Check the closed window count");
@@ -105,15 +105,8 @@ function test() {
         testOnWindow(false, TESTS[0].value, function() {
           testOnWindow(true, TESTS[0].value, finish);
         });
       });
     });
   });
 }
 
-function whenNewWindowLoaded(aIsPrivate, aCallback) {
-  let win = OpenBrowserWindow({private: aIsPrivate});
-  win.addEventListener("load", function onLoad() {
-    win.removeEventListener("load", onLoad, false);
-    aCallback(win);
-  }, false);
-}
--- a/browser/components/sessionstore/test/browser_819510_perwindowpb.js
+++ b/browser/components/sessionstore/test/browser_819510_perwindowpb.js
@@ -1,21 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const originalState = ss.getBrowserState();
 
 /** Private Browsing Test for Bug 819510 **/
 function test() {
   waitForExplicitFinish();
-
-  registerCleanupFunction(function() {
-    ss.setBrowserState(originalState);
-  });
-
   runNextTest();
 }
 
 let tests = [test_1, test_2, test_3 ];
 
 const testState = {
   windows: [{
     tabs: [
@@ -28,17 +23,23 @@ function runNextTest() {
   // Set an empty state
   closeAllButPrimaryWindow();
 
   // Run the next test, or finish
   if (tests.length) {
     let currentTest = tests.shift();
     waitForBrowserState(testState, currentTest);
   } else {
-    finish();
+    Services.obs.addObserver(
+      function observe(aSubject, aTopic, aData) {
+        Services.obs.removeObserver(observe, aTopic);
+        finish();
+      },
+      "sessionstore-browser-state-restored", false);
+    ss.setBrowserState(originalState);
   }
 }
 
 // Test opening default mochitest-normal-private-normal-private windows
 // (saving the state with last window being private)
 function test_1() {
   testOnWindow(false, function(aWindow) {
     aWindow.gBrowser.addTab("http://www.example.com/1");
@@ -169,19 +170,31 @@ function forceWriteState(aCallback) {
       aCallback(JSON.parse(aSubject.data));
     }
   }, "sessionstore-state-write", false);
   Services.prefs.setIntPref("browser.sessionstore.interval", 0);
 }
 
 function testOnWindow(aIsPrivate, aCallback) {
   let win = OpenBrowserWindow({private: aIsPrivate});
+  let gotLoad = false;
+  let gotActivate = false;
+  win.addEventListener("activate", function onActivate() {
+    win.removeEventListener("activate", onActivate, false);
+    gotActivate = true;
+    if (gotLoad) {
+      executeSoon(function() { aCallback(win) });
+    }
+  }, false);
   win.addEventListener("load", function onLoad() {
     win.removeEventListener("load", onLoad, false);
-    executeSoon(function() { aCallback(win); });
+    gotLoad = true;
+    if (gotActivate) {
+      executeSoon(function() { aCallback(win) });
+    }
   }, false);
 }
 
 function waitForTabLoad(aWin, aURL, aCallback) {
   aWin.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
     aWin.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
     executeSoon(aCallback);
   }, true);
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -284,22 +284,47 @@ registerCleanupFunction(function () {
 function closeAllButPrimaryWindow() {
   for (let win in BrowserWindowIterator()) {
     if (win != window) {
       win.close();
     }
   }
 }
 
-function whenNewWindowLoaded(aIsPrivate, aCallback) {
-  let win = OpenBrowserWindow({private: aIsPrivate});
+function whenNewWindowLoaded(aOptions, aCallback) {
+  let win = OpenBrowserWindow(aOptions);
+  let gotLoad = false;
+  let gotActivate = (Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager).activeWindow == win);
+
+  function maybeRunCallback() {
+    if (gotLoad && gotActivate) {
+      win.BrowserChromeTest.runWhenReady(function() {
+        executeSoon(function() { aCallback(win); });
+      });
+    }
+  }
+
+  if (!gotActivate) {
+    win.addEventListener("activate", function onActivate() {
+      info("Got activate.");
+      win.removeEventListener("activate", onActivate, false);
+      gotActivate = true;
+      maybeRunCallback();
+    }, false);
+  } else {
+    info("Was activated.");
+  }
+
   win.addEventListener("load", function onLoad() {
+    info("Got load");
     win.removeEventListener("load", onLoad, false);
-    aCallback(win);
+    gotLoad = true;
+    maybeRunCallback();
   }, false);
+  return win;
 }
 
 /**
  * The test runner that controls the execution flow of our tests.
  */
 let TestRunner = {
   _iter: null,
 
--- a/browser/devtools/debugger/test/browser_dbg_multiple-windows.js
+++ b/browser/devtools/debugger/test/browser_dbg_multiple-windows.js
@@ -47,23 +47,50 @@ function test_open_window()
   let top = windowMediator.getMostRecentWindow("navigator:browser");
   var main2 = gSecondWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                    .getInterface(Components.interfaces.nsIWebNavigation)
                    .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
                    .rootTreeItem
                    .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                    .getInterface(Components.interfaces.nsIDOMWindow);
   is(top, main2, "The second window is on top.");
-  executeSoon(function() {
-    gClient.listTabs(function(aResponse) {
-      is(aResponse.selected, 2, "Tab2 is selected.");
+  let gotLoad = false;
+  let gotActivate = (Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager).activeWindow == main2);
+  function maybeWindowLoadedAndActive() {
+    if (gotLoad && gotActivate) {
+      top.BrowserChromeTest.runWhenReady(function() {
+        executeSoon(function() {
+          gClient.listTabs(function(aResponse) {
+            is(aResponse.selected, 2, "Tab2 is selected.");
+            test_focus_first();
+          });
+        });
+      })
+    }
+  }
 
-      test_focus_first();
-    });
-  });
+  if (!gotActivate) {
+    main2.addEventListener("activate", function() {
+        main2.removeEventListener("activate", arguments.callee, true);
+        gotActivate = true;
+        maybeWindowLoadedAndActive();
+      },
+      true
+    );
+  }
+  main2.document.addEventListener("load", function(e) {
+      if (e.target.documentURI != TAB2_URL) {
+        return;
+      }
+      main2.document.removeEventListener("load", arguments.callee, true);
+      gotLoad = true;
+      maybeWindowLoadedAndActive();
+    },
+    true
+  );
 }
 
 function test_focus_first()
 {
   window.content.addEventListener("focus", function onFocus() {
     window.content.removeEventListener("focus", onFocus, false);
     let top = windowMediator.getMostRecentWindow("navigator:browser");
     var main1 = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -487,17 +487,17 @@ JSBool
 nsScriptSecurityManager::CheckObjectAccess(JSContext *cx, JS::Handle<JSObject*> obj,
                                            JS::Handle<jsid> id, JSAccessMode mode,
                                            JS::MutableHandle<JS::Value> vp)
 {
     // Get the security manager
     nsScriptSecurityManager *ssm =
         nsScriptSecurityManager::GetScriptSecurityManager();
 
-    NS_ASSERTION(ssm, "Failed to get security manager service");
+    NS_WARN_IF_FALSE(ssm, "Failed to get security manager service");
     if (!ssm)
         return JS_FALSE;
 
     // Get the object being accessed.  We protect these cases:
     // 1. The Function.prototype.caller property's value, which might lead
     //    an attacker up a call-stack to a function or another object from
     //    a different trust domain.
     // 2. A user-defined getter or setter function accessible on another
@@ -2489,23 +2489,22 @@ nsScriptSecurityManager::Shutdown()
 
     NS_IF_RELEASE(sIOService);
     NS_IF_RELEASE(sStrBundle);
 }
 
 nsScriptSecurityManager *
 nsScriptSecurityManager::GetScriptSecurityManager()
 {
-    if (!gScriptSecMan)
+    if (!gScriptSecMan && nsXPConnect::XPConnect())
     {
         nsRefPtr<nsScriptSecurityManager> ssManager = new nsScriptSecurityManager();
 
         nsresult rv;
         rv = ssManager->Init();
-        NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to initialize nsScriptSecurityManager");
         if (NS_FAILED(rv)) {
             return nullptr;
         }
  
         rv = nsXPConnect::XPConnect()->
             SetDefaultSecurityManager(ssManager);
         if (NS_FAILED(rv)) {
             NS_WARNING("Failed to install xpconnect security manager!");
--- a/content/base/src/Attr.cpp
+++ b/content/base/src/Attr.cpp
@@ -105,18 +105,18 @@ NS_INTERFACE_TABLE_HEAD(Attr)
   NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(Attr)
   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference,
                                  new nsNodeSupportsWeakRefTearoff(this))
   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMXPathNSResolver,
                                  new nsNode3Tearoff(this))
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Attr)
-NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(Attr,
-                                              nsNodeUtils::LastRelease(this))
+NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(Attr,
+                                                   nsNodeUtils::LastRelease(this))
 
 void
 Attr::SetMap(nsDOMAttributeMap *aMap)
 {
   if (mAttrMap && !aMap && sInitialized) {
     // We're breaking a relationship with content and not getting a new one,
     // need to locally cache value. GetValue() does that.
     GetValue(mValue);
--- a/content/base/src/FragmentOrElement.cpp
+++ b/content/base/src/FragmentOrElement.cpp
@@ -1029,16 +1029,17 @@ public:
       PRTime start = PR_Now();
       for (uint32_t i = 0; i < len; ++i) {
         UnbindSubtree(mSubtreeRoots[i]);
       }
       mSubtreeRoots.Clear();
       Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_CONTENT_UNBIND,
                             uint32_t(PR_Now() - start) / PR_USEC_PER_MSEC);
     }
+    nsCycleCollector_dispatchDeferredDeletion();
     if (this == sContentUnbinder) {
       sContentUnbinder = nullptr;
       if (mNext) {
         nsRefPtr<ContentUnbinder> next;
         next.swap(mNext);
         sContentUnbinder = next;
         next->mLast = mLast;
         mLast = nullptr;
@@ -1687,18 +1688,18 @@ NS_INTERFACE_MAP_BEGIN(FragmentOrElement
   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIInlineEventHandlers,
                                  new nsInlineEventHandlersTearoff(this))
   // DOM bindings depend on the identity pointer being the
   // same as nsINode (which nsIContent inherits).
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(FragmentOrElement)
-NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(FragmentOrElement,
-                                              nsNodeUtils::LastRelease(this))
+NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(FragmentOrElement,
+                                                   nsNodeUtils::LastRelease(this))
 
 nsresult
 FragmentOrElement::PostQueryInterface(REFNSIID aIID, void** aInstancePtr)
 {
   return OwnerDoc()->BindingManager()->GetBindingImplementation(this, aIID,
                                                                 aInstancePtr);
 }
 
--- a/content/base/src/nsContentIterator.cpp
+++ b/content/base/src/nsContentIterator.cpp
@@ -129,16 +129,18 @@ protected:
   nsINode* NextNode(nsINode* aNode, nsTArray<int32_t>* aIndexes = nullptr);
   nsINode* PrevNode(nsINode* aNode, nsTArray<int32_t>* aIndexes = nullptr);
 
   // WARNING: This function is expensive
   nsresult RebuildIndexStack();
 
   void MakeEmpty();
 
+  virtual void LastRelease();
+
   nsCOMPtr<nsINode> mCurNode;
   nsCOMPtr<nsINode> mFirst;
   nsCOMPtr<nsINode> mLast;
   nsCOMPtr<nsINode> mCommonParent;
 
   // used by nsContentIterator to cache indices
   nsAutoTArray<int32_t, 8> mIndexes;
 
@@ -195,30 +197,40 @@ NS_NewPreContentIterator()
 }
 
 
 /******************************************************
  * XPCOM cruft
  ******************************************************/
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsContentIterator)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(nsContentIterator)
+NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsContentIterator,
+                                                   LastRelease())
 
 NS_INTERFACE_MAP_BEGIN(nsContentIterator)
   NS_INTERFACE_MAP_ENTRY(nsIContentIterator)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentIterator)
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsContentIterator)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_4(nsContentIterator,
                            mCurNode,
                            mFirst,
                            mLast,
                            mCommonParent)
 
+void
+nsContentIterator::LastRelease()
+{
+  mCurNode = nullptr;
+  mFirst = nullptr;
+  mLast = nullptr;
+  mCommonParent = nullptr;
+}
+
 /******************************************************
  * constructor/destructor
  ******************************************************/
 
 nsContentIterator::nsContentIterator(bool aPre) :
   // don't need to explicitly initialize |nsCOMPtr|s, they will automatically
   // be nullptr
   mCachedIndex(0), mIsDone(false), mPre(aPre)
@@ -1126,34 +1138,40 @@ protected:
   // the range endpoint, and (node, node.length) comes strictly before it, so
   // the range's start and end nodes will never be considered "in" it.
   nsIContent* GetTopAncestorInRange(nsINode* aNode);
 
   // no copy's or assigns  FIX ME
   nsContentSubtreeIterator(const nsContentSubtreeIterator&);
   nsContentSubtreeIterator& operator=(const nsContentSubtreeIterator&);
 
+  virtual void LastRelease() MOZ_OVERRIDE;
+
   nsRefPtr<nsRange> mRange;
 
   // these arrays all typically are used and have elements
   nsAutoTArray<nsIContent*, 8> mEndNodes;
   nsAutoTArray<int32_t, 8>     mEndOffsets;
 };
 
 NS_IMPL_ADDREF_INHERITED(nsContentSubtreeIterator, nsContentIterator)
 NS_IMPL_RELEASE_INHERITED(nsContentSubtreeIterator, nsContentIterator)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsContentSubtreeIterator)
 NS_INTERFACE_MAP_END_INHERITING(nsContentIterator)
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_1(nsContentSubtreeIterator, nsContentIterator,
                                      mRange)
 
-
-
+void
+nsContentSubtreeIterator::LastRelease()
+{
+  mRange = nullptr;
+  nsContentIterator::LastRelease();
+}
 
 /******************************************************
  * repository cruft
  ******************************************************/
 
 already_AddRefed<nsIContentIterator>
 NS_NewContentSubtreeIterator()
 {
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1611,23 +1611,27 @@ nsDocument::Release()
   NS_LOG_RELEASE(this, count, "nsDocument");
   if (count == 0) {
     if (mStackRefCnt && !mNeedsReleaseAfterStackRefCntRelease) {
       mNeedsReleaseAfterStackRefCntRelease = true;
       NS_ADDREF_THIS();
       return mRefCnt.get();
     }
     NS_ASSERT_OWNINGTHREAD(nsDocument);
-    mRefCnt.stabilizeForDeletion();
     nsNodeUtils::LastRelease(this);
-    return 0;
   }
   return count;
 }
 
+NS_IMETHODIMP_(void)
+nsDocument::DeleteCycleCollectable()
+{
+  delete this;
+}
+
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument)
   if (Element::CanSkip(tmp, aRemovingAllowed)) {
     nsEventListenerManager* elm = tmp->GetListenerManager(false);
     if (elm) {
       elm->MarkForCC();
     }
     return true;
   }
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -105,18 +105,18 @@ NS_INTERFACE_MAP_BEGIN(nsGenericDOMDataN
   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMXPathNSResolver,
                                  new nsNode3Tearoff(this))
   // DOM bindings depend on the identity pointer being the
   // same as nsINode (which nsIContent inherits).
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGenericDOMDataNode)
-NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(nsGenericDOMDataNode,
-                                              nsNodeUtils::LastRelease(this))
+NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsGenericDOMDataNode,
+                                                   nsNodeUtils::LastRelease(this))
 
 
 void
 nsGenericDOMDataNode::GetNodeValueInternal(nsAString& aNodeValue)
 {
   DebugOnly<nsresult> rv = GetData(aNodeValue);
   NS_ASSERTION(NS_SUCCEEDED(rv), "GetData() failed!");
 }
--- a/content/base/src/nsINode.cpp
+++ b/content/base/src/nsINode.cpp
@@ -136,18 +136,18 @@ nsINode::nsSlots::Unlink()
     NS_RELEASE(mChildNodes);
   }
 }
 
 //----------------------------------------------------------------------
 
 nsINode::~nsINode()
 {
-  NS_ASSERTION(!HasSlots(), "nsNodeUtils::LastRelease was not called?");
-  NS_ASSERTION(mSubtreeRoot == this, "Didn't restore state properly?");
+  MOZ_ASSERT(!HasSlots(), "nsNodeUtils::LastRelease was not called?");
+  MOZ_ASSERT(mSubtreeRoot == this, "Didn't restore state properly?");
 }
 
 void*
 nsINode::GetProperty(uint16_t aCategory, nsIAtom *aPropertyName,
                      nsresult *aStatus) const
 {
   return OwnerDoc()->PropertyTable(aCategory)->GetProperty(this, aPropertyName,
                                                            aStatus);
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -257,18 +257,16 @@ nsNodeUtils::LastRelease(nsINode* aNode)
     // attached
     if (aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) &&
         ownerDoc->BindingManager()) {
       ownerDoc->BindingManager()->RemovedFromDocument(elem, ownerDoc);
     }
   }
 
   nsContentUtils::ReleaseWrapper(aNode, aNode);
-
-  delete aNode;
 }
 
 struct MOZ_STACK_CLASS nsHandlerData
 {
   uint16_t mOperation;
   nsCOMPtr<nsIDOMNode> mSource;
   nsCOMPtr<nsIDOMNode> mDest;
   nsCxPusher mPusher;
--- a/content/base/src/nsRange.cpp
+++ b/content/base/src/nsRange.cpp
@@ -264,17 +264,18 @@ nsRange::CreateRange(nsIDOMNode* aStartP
   return rv;
 }
 
 /******************************************************
  * nsISupports
  ******************************************************/
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsRange)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(nsRange)
+NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsRange,
+                                                   DoSetRange(nullptr, 0, nullptr, 0, nullptr))
 
 // QueryInterface implementation for nsRange
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsRange)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsIDOMRange)
   NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMRange)
 NS_INTERFACE_MAP_END
--- a/content/events/src/nsDOMEventTargetHelper.cpp
+++ b/content/events/src/nsDOMEventTargetHelper.cpp
@@ -65,17 +65,18 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
   NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
   NS_INTERFACE_MAP_ENTRY(nsDOMEventTargetHelper)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMEventTargetHelper)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMEventTargetHelper)
+NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsDOMEventTargetHelper,
+                                                   LastRelease())
 
 NS_IMPL_DOMTARGET_DEFAULTS(nsDOMEventTargetHelper)
 
 nsDOMEventTargetHelper::~nsDOMEventTargetHelper()
 {
   if (nsPIDOMWindow* owner = GetOwner()) {
     static_cast<nsGlobalWindow*>(owner)->RemoveEventTargetObject(this);
   }
--- a/content/events/src/nsDOMEventTargetHelper.h
+++ b/content/events/src/nsDOMEventTargetHelper.h
@@ -121,16 +121,18 @@ public:
   nsIGlobalObject* GetParentObject() const { return mParentObject; }
   bool HasOrHasHadOwner() { return mHasOrHasHadOwnerWindow; }
 protected:
   nsRefPtr<nsEventListenerManager> mListenerManager;
   // Dispatch a trusted, non-cancellable and non-bubbling event to |this|.
   nsresult DispatchTrustedEvent(const nsAString& aEventName);
   // Make |event| trusted and dispatch |aEvent| to |this|.
   nsresult DispatchTrustedEvent(nsIDOMEvent* aEvent);
+
+  virtual void LastRelease() {}
 private:
   // Inner window or sandbox.
   nsIGlobalObject*           mParentObject;
   // mParentObject pre QI-ed and cached
   // (it is needed for off main thread access)
   nsPIDOMWindow*             mOwnerWindow;
   bool                       mHasOrHasHadOwnerWindow;
 };
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -2883,16 +2883,19 @@ nsJSContext::MaybePokeCC()
   }
 
   if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
     sCCTimerFireCount = 0;
     CallCreateInstance("@mozilla.org/timer;1", &sCCTimer);
     if (!sCCTimer) {
       return;
     }
+    // We can kill some objects before running forgetSkippable.
+    nsCycleCollector_dispatchDeferredDeletion();
+
     sCCTimer->InitWithFuncCallback(CCTimerFired, nullptr,
                                    NS_CC_SKIPPABLE_DELAY,
                                    nsITimer::TYPE_REPEATING_SLACK);
   }
 }
 
 //static
 void
--- a/dom/file/ArchiveZipFile.cpp
+++ b/dom/file/ArchiveZipFile.cpp
@@ -402,10 +402,10 @@ NS_IMPL_CYCLE_COLLECTION_1(ArchiveZipFil
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ArchiveZipFile)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile)
   NS_INTERFACE_MAP_ENTRY(nsIDOMBlob)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile)
   NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
   NS_INTERFACE_MAP_ENTRY(nsIMutable)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMFileCC)
 
-NS_IMPL_CYCLE_COLLECTING_ADDREF(ArchiveZipFile)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(ArchiveZipFile)
+NS_IMPL_ADDREF_INHERITED(ArchiveZipFile, nsDOMFileCC)
+NS_IMPL_RELEASE_INHERITED(ArchiveZipFile, nsDOMFileCC)
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -238,31 +238,38 @@ IDBDatabase::IDBDatabase()
   mRunningVersionChange(false)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 IDBDatabase::~IDBDatabase()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+}
+
+void
+IDBDatabase::LastRelease()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
   if (mActorChild) {
     NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
     mActorChild->Send__delete__(mActorChild);
     NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!");
   }
 
   if (mRegistered) {
     CloseInternal(true);
 
     QuotaManager* quotaManager = QuotaManager::Get();
     if (quotaManager) {
       quotaManager->UnregisterStorage(this);
     }
+    mRegistered = false;
   }
 }
 
 NS_IMETHODIMP_(void)
 IDBDatabase::Invalidate()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
--- a/dom/indexedDB/IDBDatabase.h
+++ b/dom/indexedDB/IDBDatabase.h
@@ -156,16 +156,17 @@ public:
     return mContentParent;
   }
 
   nsresult
   CreateObjectStoreInternal(IDBTransaction* aTransaction,
                             const ObjectStoreInfoGuts& aInfo,
                             IDBObjectStore** _retval);
 
+  virtual void LastRelease() MOZ_OVERRIDE;
 private:
   IDBDatabase();
   ~IDBDatabase();
 
   void OnUnlink();
 
   // The factory must be kept alive when IndexedDB is used in multiple
   // processes. If it dies then the entire actor tree will be destroyed with it
--- a/dom/tests/browser/browser_geolocation_privatebrowsing_perwindowpb.js
+++ b/dom/tests/browser/browser_geolocation_privatebrowsing_perwindowpb.js
@@ -1,36 +1,55 @@
 function test() {
   var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
   let baseProvider = "http://mochi.test:8888/browser/dom/tests/browser/network_geolocation.sjs";
   prefs.setCharPref("geo.wifi.uri", baseProvider + "?desired_access_token=fff");
 
   prefs.setBoolPref("geo.prompt.testing", true);
   prefs.setBoolPref("geo.prompt.testing.allow", true);
+  var origScanValue = true; // same default in NetworkGeolocationProvider.js.
+  try {
+    origScanValue = prefs.getBoolPref("geo.wifi.scan");
+  } catch(ex) {}
+  prefs.setBoolPref("geo.wifi.scan", false);
 
   const testPageURL = "http://mochi.test:8888/browser/" +
     "dom/tests/browser/browser_geolocation_privatebrowsing_page.html";
   waitForExplicitFinish();
 
   var windowsToClose = [];
   function testOnWindow(aIsPrivate, aCallback) {
-    var win = OpenBrowserWindow({private: aIsPrivate});
+    let win = OpenBrowserWindow({private: aIsPrivate});
+    let gotLoad = false;
+    let gotActivate = 
+      (Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager).activeWindow == win);
+    if (!gotActivate) {
+      win.addEventListener("activate", function onActivate() {
+        info("got activate");
+        win.removeEventListener("activate", onActivate, true);
+        gotActivate = true;
+        if (gotLoad) {
+          windowsToClose.push(win);
+          win.BrowserChromeTest.runWhenReady(function() { aCallback(win) });
+        }
+      }, true);
+    } else {
+      info("Was activated");
+    }
     win.addEventListener("load", function onLoad() {
-      win.removeEventListener("load", onLoad, false);
-      windowsToClose.push(win);
-      executeSoon(function() { aCallback(win); });
-    }, false);
+      info("Got load");
+      win.removeEventListener("load", onLoad, true);
+      gotLoad = true;
+      if (gotActivate) {
+        windowsToClose.push(win);
+        setTimeout(function() { aCallback(win) }, 1000);
+      }
+    }, true);
   }
 
-  registerCleanupFunction(function () {
-    windowsToClose.forEach(function(win) {
-      win.close();
-    });
-  });
-
   testOnWindow(false, function(aNormalWindow) {
     aNormalWindow.gBrowser.selectedBrowser.addEventListener("georesult", function load(ev) {
       aNormalWindow.gBrowser.selectedBrowser.removeEventListener("georesult", load, false);
       is(ev.detail, 200, "unexpected access token");
 
       prefs.setCharPref("geo.wifi.uri", baseProvider + "?desired_access_token=ggg");
 
       testOnWindow(true, function(aPrivateWindow) {
@@ -41,17 +60,20 @@ function test() {
           prefs.setCharPref("geo.wifi.uri", baseProvider + "?expected_access_token=fff");
 
           testOnWindow(false, function(aAnotherNormalWindow) {
             aAnotherNormalWindow.gBrowser.selectedBrowser.addEventListener("georesult", function load3(ev) {
               aAnotherNormalWindow.gBrowser.selectedBrowser.removeEventListener("georesult", load3, false);
               is(ev.detail, 200, "unexpected access token");
               prefs.setBoolPref("geo.prompt.testing", false);
               prefs.setBoolPref("geo.prompt.testing.allow", false);
-
+              prefs.setBoolPref("geo.wifi.scan", origScanValue);
+              windowsToClose.forEach(function(win) {
+                                       win.close();
+                                     });
               finish();
             }, false, true);
             aAnotherNormalWindow.content.location = testPageURL;
           });
         }, false, true);
         aPrivateWindow.content.location = testPageURL;
       });
     }, false, true);
--- a/editor/libeditor/base/DeleteRangeTxn.h
+++ b/editor/libeditor/base/DeleteRangeTxn.h
@@ -38,16 +38,21 @@ public:
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DeleteRangeTxn, EditAggregateTxn)
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
 
   NS_DECL_EDITTXN
 
   NS_IMETHOD RedoTransaction();
 
+  virtual void LastRelease()
+  {
+    mRange = nullptr;
+    EditAggregateTxn::LastRelease();
+  }
 protected:
 
   nsresult CreateTxnsToDeleteBetween(nsINode* aNode,
                                      int32_t aStartOffset,
                                      int32_t aEndOffset);
 
   nsresult CreateTxnsToDeleteNodesBetween();
 
--- a/editor/libeditor/base/EditTxn.cpp
+++ b/editor/libeditor/base/EditTxn.cpp
@@ -14,17 +14,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EditTxn)
   NS_INTERFACE_MAP_ENTRY(nsITransaction)
   NS_INTERFACE_MAP_ENTRY(nsPIEditorTransaction)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITransaction)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(EditTxn)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(EditTxn)
+NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(EditTxn, LastRelease())
 
 EditTxn::~EditTxn()
 {
 }
 
 NS_IMETHODIMP
 EditTxn::RedoTransaction(void)
 {
--- a/editor/libeditor/base/EditTxn.h
+++ b/editor/libeditor/base/EditTxn.h
@@ -19,16 +19,18 @@ class EditTxn : public nsITransaction,
                 public nsPIEditorTransaction
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(EditTxn, nsITransaction)
 
   virtual ~EditTxn();
 
+  virtual void LastRelease() {}
+
   NS_IMETHOD RedoTransaction(void);
   NS_IMETHOD GetIsTransient(bool *aIsTransient);
   NS_IMETHOD Merge(nsITransaction *aTransaction, bool *aDidMerge);
 };
 
 #define NS_DECL_EDITTXN \
   NS_IMETHOD DoTransaction(); \
   NS_IMETHOD UndoTransaction(); \
--- a/editor/txmgr/src/nsTransactionItem.cpp
+++ b/editor/txmgr/src/nsTransactionItem.cpp
@@ -21,28 +21,35 @@ nsTransactionItem::nsTransactionItem(nsI
 
 nsTransactionItem::~nsTransactionItem()
 {
   delete mRedoStack;
 
   delete mUndoStack;
 }
 
+void
+nsTransactionItem::CleanUp()
+{
+  mData.Clear();
+  mTransaction = nullptr;
+  if (mRedoStack) {
+    mRedoStack->DoUnlink();
+  }
+  if (mUndoStack) {
+    mUndoStack->DoUnlink();
+  }
+}
+
 NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(nsTransactionItem)
-NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(nsTransactionItem)
+NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE_WITH_LAST_RELEASE(nsTransactionItem,
+                                                          CleanUp())
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsTransactionItem)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mData)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransaction)
-  if (tmp->mRedoStack) {
-    tmp->mRedoStack->DoUnlink();
-  }
-  if (tmp->mUndoStack) {
-    tmp->mUndoStack->DoUnlink();
-  }
+  tmp->CleanUp();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTransactionItem)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
   if (tmp->mRedoStack) {
     tmp->mRedoStack->DoTraverse(cb);
   }
--- a/editor/txmgr/src/nsTransactionItem.h
+++ b/editor/txmgr/src/nsTransactionItem.h
@@ -53,14 +53,15 @@ private:
   virtual nsresult RedoChildren(nsTransactionManager *aTxMgr);
 
   virtual nsresult RecoverFromUndoError(nsTransactionManager *aTxMgr);
   virtual nsresult RecoverFromRedoError(nsTransactionManager *aTxMgr);
 
   virtual nsresult GetNumberOfUndoItems(int32_t *aNumItems);
   virtual nsresult GetNumberOfRedoItems(int32_t *aNumItems);
 
+  void CleanUp();
 protected:
   nsCycleCollectingAutoRefCnt mRefCnt;
   NS_DECL_OWNINGTHREAD
 };
 
 #endif // nsTransactionItem_h__
--- a/editor/txmgr/tests/TestTXMgr.cpp
+++ b/editor/txmgr/tests/TestTXMgr.cpp
@@ -440,21 +440,24 @@ public:
   virtual ~SimpleTransaction()
   {
     //
     // Make sure transactions are being destroyed in the order we expect!
     // Notice that we don't check to see if we go past the end of the array.
     // This is done on purpose since we want to crash if the order array is out
     // of date.
     //
+    /* Disabled because the current cycle collector doesn't delete
+       cycle collectable objects synchronously, nor doesn't guarantee any order.
     if (sDestructorOrderArr && mVal != sDestructorOrderArr[sDestructorCount]) {
       fail("~SimpleTransaction expected %d got %d.\n",
            mVal, sDestructorOrderArr[sDestructorCount]);
       exit(-1);
     }
+    */
 
     ++sDestructorCount;
 
 #ifdef ENABLE_DEBUG_PRINTFS
     printf("\n~SimpleTransaction: %d - 0x%.8x\n", mVal, (int32_t)this);
 #endif // ENABLE_DEBUG_PRINTFS
 
     mVal = -1;
@@ -2597,21 +2600,23 @@ quick_test(TestTransactionFactory *facto
 
   /*******************************************************************
    *
    * Make sure number of transactions created matches number of
    * transactions destroyed!
    *
    *******************************************************************/
 
+  /* Disabled because the current cycle collector doesn't delete
+     cycle collectable objects synchronously.
   if (sConstructorCount != sDestructorCount) {
     fail("Transaction constructor count (%d) != destructor count (%d).\n",
          sConstructorCount, sDestructorCount);
     return NS_ERROR_FAILURE;
-  }
+  }*/
 
   passed("Number of transactions created and destroyed match");
   passed("%d transactions processed during quick test", sConstructorCount);
 
   return NS_OK;
 }
 
 nsresult
@@ -4264,21 +4269,23 @@ quick_batch_test(TestTransactionFactory 
 
   /*******************************************************************
    *
    * Make sure number of transactions created matches number of
    * transactions destroyed!
    *
    *******************************************************************/
 
+  /* Disabled because the current cycle collector doesn't delete
+     cycle collectable objects synchronously.
   if (sConstructorCount != sDestructorCount) {
     fail("Transaction constructor count (%d) != destructor count (%d).\n",
          sConstructorCount, sDestructorCount);
     return NS_ERROR_FAILURE;
-  }
+  }*/
 
   passed("Number of transactions created and destroyed match");
   passed("%d transactions processed during quick batch test",
          sConstructorCount);
 
   return NS_OK;
 }
 
@@ -4454,21 +4461,23 @@ stress_test(TestTransactionFactory *fact
   printf("passed\n");
 
   result = mgr->Clear();
   if (NS_FAILED(result)) {
     fail("Clear() failed. (%d)\n", result);
     return result;
   }
 
+  /* Disabled because the current cycle collector doesn't delete
+     cycle collectable objects synchronously.
   if (sConstructorCount != sDestructorCount) {
     fail("Transaction constructor count (%d) != destructor count (%d).\n",
          sConstructorCount, sDestructorCount);
     return NS_ERROR_FAILURE;
-  }
+  }*/
 
   passed("%d transactions processed during stress test", sConstructorCount);
 
   return NS_OK;
 }
 
 nsresult
 simple_stress_test()
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -2250,17 +2250,17 @@ public:
       {
         static const CCParticipantVTable<NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)>
           ::Type participant =
           { NS_IMPL_CYCLE_COLLECTION_VTABLE(NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)) };
         return NS_PARTICIPANT_AS(nsXPCOMCycleCollectionParticipant,
                                  &participant);
       }
     };
-    NS_DECL_CYCLE_COLLECTION_UNMARK_PURPLE_STUB(XPCWrappedNative)
+    void DeleteCycleCollectable() {}
 
     nsIPrincipal* GetObjectPrincipal() const;
 
     JSBool
     IsValid() const {return nullptr != mFlatJSObject;}
 
 #define XPC_SCOPE_WORD(s)   (intptr_t(s))
 #define XPC_SCOPE_MASK      (intptr_t(0x3))
@@ -2718,17 +2718,17 @@ class nsXPCWrappedJS : protected nsAutoX
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
     NS_DECL_NSIXPCONNECTWRAPPEDJS
     NS_DECL_NSISUPPORTSWEAKREFERENCE
     NS_DECL_NSIPROPERTYBAG
 
     NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXPCWrappedJS, nsIXPConnectWrappedJS)
-    NS_DECL_CYCLE_COLLECTION_UNMARK_PURPLE_STUB(nsXPCWrappedJS)
+    void DeleteCycleCollectable() {}
 
     NS_IMETHOD CallMethod(uint16_t methodIndex,
                           const XPTMethodDescriptor *info,
                           nsXPTCMiniVariant* params);
 
     /*
     * This is rarely called directly. Instead one usually calls
     * XPCConvert::JSObject2NativeInterface which will handles cases where the
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -316,17 +316,25 @@ nsPresContext::~nsPresContext()
 }
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPresContext)
    NS_INTERFACE_MAP_ENTRY(nsISupports)
    NS_INTERFACE_MAP_ENTRY(nsIObserver)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPresContext)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(nsPresContext)
+NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsPresContext, LastRelease())
+
+void
+nsPresContext::LastRelease()
+{
+  if (IsRoot()) {
+    static_cast<nsRootPresContext*>(this)->CancelDidPaintTimer();
+  }
+}
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument);
   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDeviceContext); // not xpcom
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventManager);
   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mLanguage); // an atom
 
   // We own only the items in mDOMMediaQueryLists that have listeners;
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -1317,16 +1317,18 @@ protected:
     eDefaultFont_Monospace,
     eDefaultFont_Cursive,
     eDefaultFont_Fantasy,
     eDefaultFont_COUNT
   };
 
   nscolor MakeColorPref(const nsString& aColor);
 
+  void LastRelease();
+
 #ifdef DEBUG
 private:
   friend struct nsAutoLayoutPhase;
   uint32_t mLayoutPhaseCount[eLayoutPhase_COUNT];
 public:
   uint32_t LayoutPhaseCount(nsLayoutPhase aPhase) {
     return mLayoutPhaseCount[aPhase];
   }
--- a/rdf/base/src/nsInMemoryDataSource.cpp
+++ b/rdf/base/src/nsInMemoryDataSource.cpp
@@ -764,16 +764,17 @@ NS_NewRDFInMemoryDataSource(nsISupports*
 InMemoryDataSource::InMemoryDataSource(nsISupports* aOuter)
     : mNumObservers(0), mReadCount(0)
 {
     NS_INIT_AGGREGATED(aOuter);
 
     mForwardArcs.ops = nullptr;
     mReverseArcs.ops = nullptr;
     mPropagateChanges = true;
+    MOZ_COUNT_CTOR(InMemoryDataSource);
 }
 
 
 nsresult
 InMemoryDataSource::Init()
 {
     if (!PL_DHashTableInit(&mForwardArcs,
                            PL_DHashGetStubOps(),
@@ -817,16 +818,17 @@ InMemoryDataSource::~InMemoryDataSource(
         PL_DHashTableFinish(&mForwardArcs);
     }
     if (mReverseArcs.ops)
         PL_DHashTableFinish(&mReverseArcs);
 
     PR_LOG(gLog, PR_LOG_NOTICE,
            ("InMemoryDataSource(%p): destroyed.", this));
 
+    MOZ_COUNT_DTOR(InMemoryDataSource);
 }
 
 PLDHashOperator
 InMemoryDataSource::DeleteForwardArcsEntry(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
                                            uint32_t aNumber, void* aArg)
 {
     Entry* entry = reinterpret_cast<Entry*>(aHdr);
 
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -287,17 +287,17 @@ public:
   static NS_METHOD UnlinkImpl(void *n)
   {
     return NS_OK;
   }
   static NS_METHOD UnrootImpl(void *n)
   {
     return NS_OK;
   }
-  static NS_METHOD_(void) UnmarkIfPurpleImpl(void *n)
+  static NS_METHOD_(void) DeleteCycleCollectableImpl(void *n)
   {
   }
   static NS_METHOD TraverseImpl(JSContextParticipant *that, void *n,
                                 nsCycleCollectionTraversalCallback &cb)
   {
     JSContext *cx = static_cast<JSContext*>(n);
 
     // JSContexts do not have an internal refcount and always have a single
--- a/xpcom/base/CycleCollectedJSRuntime.h
+++ b/xpcom/base/CycleCollectedJSRuntime.h
@@ -35,17 +35,17 @@ public:
     return NS_OK;
   }
 
   static NS_METHOD UnrootImpl(void *n)
   {
     return NS_OK;
   }
 
-  static NS_METHOD_(void) UnmarkIfPurpleImpl(void *n)
+  static NS_METHOD_(void) DeleteCycleCollectableImpl(void *n)
   {
   }
 
   static NS_METHOD TraverseImpl(JSGCThingParticipant *that, void *n,
                                 nsCycleCollectionTraversalCallback &cb);
 };
 
 class JSZoneParticipant : public nsCycleCollectionParticipant
@@ -62,17 +62,17 @@ public:
     return NS_OK;
   }
 
   static NS_METHOD UnrootImpl(void *p)
   {
     return NS_OK;
   }
 
-  static NS_METHOD_(void) UnmarkIfPurpleImpl(void *n)
+  static NS_METHOD_(void) DeleteCycleCollectableImpl(void *n)
   {
   }
 
   static NS_METHOD TraverseImpl(JSZoneParticipant *that, void *p,
                                 nsCycleCollectionTraversalCallback &cb);
 };
 
 class IncrementalFinalizeRunnable;
--- a/xpcom/base/nsAgg.h
+++ b/xpcom/base/nsAgg.h
@@ -71,19 +71,20 @@ public:                                 
 class NS_CYCLE_COLLECTION_INNERCLASS                                        \
  : public nsXPCOMCycleCollectionParticipant                                 \
 {                                                                           \
 public:                                                                     \
   static NS_METHOD UnlinkImpl(void *p);                                     \
   static NS_METHOD TraverseImpl(NS_CYCLE_COLLECTION_INNERCLASS *that,       \
                                 void *p,                                    \
                                 nsCycleCollectionTraversalCallback &cb);    \
-  static NS_METHOD_(void) UnmarkIfPurpleImpl(void *p)                       \
+  static NS_METHOD_(void) DeleteCycleCollectableImpl(void* p)               \
   {                                                                         \
-    Downcast(static_cast<nsISupports *>(p))->UnmarkIfPurple();              \
+    NS_CYCLE_COLLECTION_CLASSNAME(_class)::                                 \
+      Downcast(static_cast<nsISupports*>(p))->DeleteCycleCollectable();     \
   }                                                                         \
   static _class* Downcast(nsISupports* s)                                   \
   {                                                                         \
     return (_class*)((char*)(s) - offsetof(_class, fAggregated));           \
   }                                                                         \
   static nsISupports* Upcast(_class *p)                                     \
   {                                                                         \
     return p->InnerObject();                                                \
@@ -144,36 +145,35 @@ NS_IMPL_AGGREGATED_HELPER(_class)       
                                                                             \
 NS_IMETHODIMP_(nsrefcnt)                                                    \
 _class::Internal::AddRef(void)                                              \
 {                                                                           \
     _class* agg = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Downcast(this);    \
     MOZ_ASSERT(int32_t(agg->mRefCnt) >= 0, "illegal refcnt");               \
     NS_CheckThreadSafe(agg->_mOwningThread.GetThread(),                     \
                        #_class " not thread-safe");                         \
-    nsrefcnt count = agg->mRefCnt.incr(this);                               \
+    nsrefcnt count = agg->mRefCnt.incr();                                   \
     NS_LOG_ADDREF(this, count, #_class, sizeof(*agg));                      \
     return count;                                                           \
 }                                                                           \
-                                                                            \
 NS_IMETHODIMP_(nsrefcnt)                                                    \
 _class::Internal::Release(void)                                             \
 {                                                                           \
     _class* agg = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Downcast(this);    \
     MOZ_ASSERT(int32_t(agg->mRefCnt) > 0, "dup release");                   \
     NS_CheckThreadSafe(agg->_mOwningThread.GetThread(),                     \
                        #_class " not thread-safe");                         \
     nsrefcnt count = agg->mRefCnt.decr(this);                               \
     NS_LOG_RELEASE(this, count, #_class);                                   \
-    if (count == 0) {                                                       \
-        agg->mRefCnt.stabilizeForDeletion();                                \
-        delete agg;                                                         \
-        return 0;                                                           \
-    }                                                                       \
     return count;                                                           \
+}                                                                           \
+NS_IMETHODIMP_(void)                                                        \
+_class::DeleteCycleCollectable(void)                                        \
+{                                                                           \
+  delete this;                                                              \
 }
 
 #define NS_IMPL_AGGREGATED_HELPER(_class)                                   \
 NS_IMETHODIMP                                                               \
 _class::QueryInterface(const nsIID& aIID, void** aInstancePtr)              \
 {                                                                           \
     return fOuter->QueryInterface(aIID, aInstancePtr);                      \
 }                                                                           \
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -18,16 +18,19 @@
 // Black nodes are definitely live. If we ever determine a node is
 // black, it's ok to forget about, drop from our records.
 //
 // White nodes are definitely garbage cycles. Once we finish with our
 // scanning, we unlink all the white nodes and expect that by
 // unlinking them they will self-destruct (since a garbage cycle is
 // only keeping itself alive with internal links, by definition).
 //
+// Snow-white is an addition to the original algorithm. Snow-white object
+// has reference count zero and is just waiting for deletion.
+//
 // Grey nodes are being scanned. Nodes that turn grey will turn
 // either black if we determine that they're live, or white if we
 // determine that they're a garbage cycle. After the main collection
 // algorithm there should be no grey nodes.
 //
 // Purple nodes are *candidates* for being scanned. They are nodes we
 // haven't begun scanning yet because they're not old enough, or we're
 // still partway through the algorithm.
@@ -54,19 +57,16 @@
 //    adjustment to occur (no AddRef / Release calls).
 //
 // A non-nsISupports ("native") object is scan-safe by explicitly
 // providing its nsCycleCollectionParticipant.
 //
 // An object is purple-safe if it satisfies the following properties:
 //
 //  - The object is scan-safe.
-//  - If the object calls |nsCycleCollector::suspect(this)|,
-//    it will null out the pointer from the purple buffer entry to
-//    the object before being destroyed.
 //
 // When we receive a pointer |ptr| via
 // |nsCycleCollector::suspect(ptr)|, we assume it is purple-safe. We
 // can check the scan-safety, but have no way to ensure the
 // purple-safety; objects must obey, or else the entire system falls
 // apart. Don't involve an object in this scheme if you can't
 // guarantee its purple-safety. The easiest way to ensure that an
 // object is purple-safe is to use nsCycleCollectingAutoRefCnt.
@@ -79,17 +79,17 @@
 // We do not |AddRef| or |Release| any objects during scanning. We
 // rely on the purple-safety of the roots that call |suspect| to
 // hold, such that we will clear the pointer from the purple buffer
 // entry to the object before it is destroyed. The pointers that are
 // merely scan-safe we hold only for the duration of scanning, and
 // there should be no objects released from the scan-safe set during
 // the scan.
 //
-// We *do* call |AddRef| and |Release| on every white object, on
+// We *do* call |Root| and |Unroot| on every white object, on
 // either side of the calls to |Unlink|. This keeps the set of white
 // objects alive during the unlinking.
 //
 
 #if !defined(__MINGW32__)
 #ifdef WIN32
 #include <crtdbg.h>
 #include <errno.h>
@@ -755,42 +755,42 @@ public:
         mFirstBlock.mNext = nullptr;
     }
 
     struct UnmarkRemainingPurpleVisitor
     {
         void
         Visit(nsPurpleBuffer &aBuffer, nsPurpleBufferEntry *aEntry)
         {
-            if (aEntry->mObject) {
-                void *obj = aEntry->mObject;
-                nsCycleCollectionParticipant *cp = aEntry->mParticipant;
-                CanonicalizeParticipant(&obj, &cp);
-                cp->UnmarkIfPurple(obj);
+            if (aEntry->mRefCnt) {
+                aEntry->mRefCnt->RemoveFromPurpleBuffer();
+                aEntry->mRefCnt = nullptr;
             }
+            aEntry->mObject = nullptr;
             --aBuffer.mCount;
         }
     };
 
     void UnmarkRemainingPurple(Block *b)
     {
         UnmarkRemainingPurpleVisitor visitor;
         b->VisitEntries(*this, visitor);
     }
 
     void SelectPointers(GCGraphBuilder &builder);
 
     // RemoveSkippable removes entries from the purple buffer if
-    // nsPurpleBufferEntry::mObject is null or if the object's
+    // nsPurpleBufferEntry::mRefCnt is 0 or if the object's
     // nsXPCOMCycleCollectionParticipant::CanSkip() returns true or
-    // if nsPurpleBufferEntry::mNotPurple is true.
+    // if nsPurpleBufferEntry::mRefCnt->IsPurple() is false.
     // If removeChildlessNodes is true, then any nodes in the purple buffer
     // that will have no children in the cycle collector graph will also be
     // removed. CanSkip() may be run on these children.
-    void RemoveSkippable(bool removeChildlessNodes);
+    void RemoveSkippable(bool removeChildlessNodes,
+                         CC_ForgetSkippableCallback aCb);
 
     nsPurpleBufferEntry* NewEntry()
     {
         if (!mFreeList) {
             Block *b = new Block;
             StartBlock(b);
 
             // Add the new block as the second block in the list.
@@ -799,34 +799,36 @@ public:
         }
 
         nsPurpleBufferEntry *e = mFreeList;
         mFreeList = (nsPurpleBufferEntry*)
             (uintptr_t(mFreeList->mNextInFreeList) & ~uintptr_t(1));
         return e;
     }
 
-    nsPurpleBufferEntry* Put(void *p, nsCycleCollectionParticipant *cp)
+    void Put(void *p, nsCycleCollectionParticipant *cp,
+             nsCycleCollectingAutoRefCnt *aRefCnt)
     {
         nsPurpleBufferEntry *e = NewEntry();
 
         ++mCount;
 
         e->mObject = p;
+        e->mRefCnt = aRefCnt;
         e->mParticipant = cp;
-        e->mNotPurple = false;
-
-        // Caller is responsible for filling in result's mRefCnt.
-        return e;
     }
 
     void Remove(nsPurpleBufferEntry *e)
     {
         MOZ_ASSERT(mCount != 0, "must have entries");
 
+        if (e->mRefCnt) {
+            e->mRefCnt->RemoveFromPurpleBuffer();
+            e->mRefCnt = nullptr;
+        }
         e->mNextInFreeList =
             (nsPurpleBufferEntry*)(uintptr_t(mFreeList) | uintptr_t(1));
         mFreeList = e;
 
         --mCount;
     }
 
     uint32_t Count() const
@@ -862,24 +864,21 @@ struct SelectPointersVisitor
 {
     SelectPointersVisitor(GCGraphBuilder &aBuilder)
         : mBuilder(aBuilder)
     {}
 
     void
     Visit(nsPurpleBuffer &aBuffer, nsPurpleBufferEntry *aEntry)
     {
-        if (aEntry->mObject && aEntry->mNotPurple) {
-            void* o = aEntry->mObject;
-            nsCycleCollectionParticipant* cp = aEntry->mParticipant;
-            CanonicalizeParticipant(&o, &cp);
-            cp->UnmarkIfPurple(o);
-            aBuffer.Remove(aEntry);
-        } else if (!aEntry->mObject ||
-                   AddPurpleRoot(mBuilder, aEntry->mObject, aEntry->mParticipant)) {
+        MOZ_ASSERT(!(aEntry->mObject && !aEntry->mRefCnt->get()),
+                   "SelectPointersVisitor: snow-white object in the purple buffer");
+        if (!aEntry->mObject ||
+            !aEntry->mRefCnt->IsPurple() ||
+            AddPurpleRoot(mBuilder, aEntry->mObject, aEntry->mParticipant)) {
             aBuffer.Remove(aEntry);
         }
     }
 
 private:
     GCGraphBuilder &mBuilder;
 };
 
@@ -1047,17 +1046,18 @@ public:
     bool CollectWhite(nsICycleCollectorListener *aListener);
 
     nsCycleCollector(CCThreadingModel aModel);
     ~nsCycleCollector();
 
     nsresult Init();
     void ShutdownThreads();
 
-    nsPurpleBufferEntry* Suspect(void *n, nsCycleCollectionParticipant *cp);
+    void Suspect(void *n, nsCycleCollectionParticipant *cp,
+                 nsCycleCollectingAutoRefCnt *aRefCnt);
 
     void CheckThreadSafety();
 
 private:
     void ShutdownCollect(nsICycleCollectorListener *aListener);
 
 public:
     void Collect(ccType aCCType,
@@ -1070,16 +1070,22 @@ public:
     void FixGrayBits(bool aForceGC);
     bool ShouldMergeZones(ccType aCCType);
     void CleanupAfterCollection();
 
     // Start and finish an individual collection.
     bool BeginCollection(ccType aCCType, nsICycleCollectorListener *aListener);
     bool FinishCollection(nsICycleCollectorListener *aListener);
 
+    void FreeSnowWhite(bool aUntilNoSWInPurpleBuffer);
+
+    // If there is a cycle collector available in the current thread,
+    // this calls FreeSnowWhite(false).
+    static void TryToFreeSnowWhite();
+
     uint32_t SuspectedCount();
     void Shutdown();
 
     void ClearGraph()
     {
         mGraph.mNodes.Clear();
         mGraph.mEdges.Clear();
         mGraph.mWeakMaps.Clear();
@@ -1157,16 +1163,18 @@ nsCycleCollectorRunner::Collect(ccType a
 
     if (mModel == CCWithTraverseThread && !mRunning)
         return;
 
     nsAutoTArray<PtrInfo*, 4000> whiteNodes;
     if (!mCollector->PrepareForCollection(aResults, &whiteNodes))
         return;
 
+    mCollector->FreeSnowWhite(true);
+
     MOZ_ASSERT(!mListener, "Should have cleared this already!");
     if (aListener && NS_FAILED(aListener->Begin()))
         aListener = nullptr;
     mListener = aListener;
     mCCType = aCCType;
 
     if (mModel == CCWithTraverseThread &&
         mCollector->JSRuntime()->NotifyLeaveMainThread()) {
@@ -2072,18 +2080,16 @@ AddPurpleRoot(GCGraphBuilder &builder, v
 
     if (builder.WantAllTraces() || !cp->CanSkipInCC(root)) {
         PtrInfo *pinfo = builder.AddNode(root, cp);
         if (!pinfo) {
             return false;
         }
     }
 
-    cp->UnmarkIfPurple(root);
-
     return true;
 }
 
 // MayHaveChild() will be false after a Traverse if the object does
 // not have any children the CC will visit.
 class ChildFinder : public nsCycleCollectionTraversalCallback
 {
 public:
@@ -2138,66 +2144,169 @@ ChildFinder::NoteJSChild(void *child)
 static bool
 MayHaveChild(void *o, nsCycleCollectionParticipant* cp)
 {
     ChildFinder cf;
     cp->Traverse(o, cf);
     return cf.MayHaveChild();
 }
 
-class RemoveSkippableVisitor
+struct SnowWhiteObject
+{
+  void* mPointer;
+  nsCycleCollectionParticipant* mParticipant;
+  nsCycleCollectingAutoRefCnt* mRefCnt;
+};
+
+class SnowWhiteKiller
 {
 public:
-    RemoveSkippableVisitor(bool aRemoveChildlessNodes)
-        : mRemoveChildlessNodes(aRemoveChildlessNodes)
+    SnowWhiteKiller(uint32_t aMaxCount)
+    {
+        mObjects.SetCapacity(aMaxCount);
+    }
+
+    ~SnowWhiteKiller()
+    {
+        for (uint32_t i = 0; i < mObjects.Length(); ++i) {
+            SnowWhiteObject& o = mObjects[i];
+            if (!o.mRefCnt->get() && !o.mRefCnt->IsInPurpleBuffer()) {
+                o.mRefCnt->stabilizeForDeletion();
+                o.mParticipant->DeleteCycleCollectable(o.mPointer);
+            }
+        }
+    }
+
+    void
+    Visit(nsPurpleBuffer& aBuffer, nsPurpleBufferEntry* aEntry)
+    {
+        if (aEntry->mObject && !aEntry->mRefCnt->get()) {
+            void *o = aEntry->mObject;
+            nsCycleCollectionParticipant *cp = aEntry->mParticipant;
+            CanonicalizeParticipant(&o, &cp);
+            SnowWhiteObject swo = { o, cp, aEntry->mRefCnt };
+            mObjects.AppendElement(swo);
+            aBuffer.Remove(aEntry);
+        }
+    }
+
+    bool HasSnowWhiteObjects()
+    {
+      return mObjects.Length() > 0;
+    }
+private:
+    nsTArray<SnowWhiteObject> mObjects;
+};
+
+class RemoveSkippableVisitor : public SnowWhiteKiller
+{
+public:
+    RemoveSkippableVisitor(uint32_t aMaxCount, bool aRemoveChildlessNodes,
+                           CC_ForgetSkippableCallback aCb)
+        : SnowWhiteKiller(aMaxCount),
+          mRemoveChildlessNodes(aRemoveChildlessNodes),
+          mCallback(aCb)
     {}
 
+    ~RemoveSkippableVisitor()
+    {
+        // Note, we must call the callback before SnowWhiteKiller calls
+        // DeleteCycleCollectable!
+        if (mCallback) {
+            mCallback();
+        }
+    }
+
     void
     Visit(nsPurpleBuffer &aBuffer, nsPurpleBufferEntry *aEntry)
     {
         if (aEntry->mObject) {
+            if (!aEntry->mRefCnt->get()) {
+              SnowWhiteKiller::Visit(aBuffer, aEntry);
+              return;
+            }
             void *o = aEntry->mObject;
             nsCycleCollectionParticipant *cp = aEntry->mParticipant;
             CanonicalizeParticipant(&o, &cp);
-            if (!aEntry->mNotPurple && !cp->CanSkip(o, false) &&
+            if (aEntry->mRefCnt->IsPurple() && !cp->CanSkip(o, false) &&
                 (!mRemoveChildlessNodes || MayHaveChild(o, cp))) {
                 return;
             }
-            cp->UnmarkIfPurple(o);
         }
         aBuffer.Remove(aEntry);
     }
 
 private:
     bool mRemoveChildlessNodes;
+    CC_ForgetSkippableCallback mCallback;
 };
 
 void
-nsPurpleBuffer::RemoveSkippable(bool removeChildlessNodes)
+nsPurpleBuffer::RemoveSkippable(bool removeChildlessNodes,
+                                CC_ForgetSkippableCallback aCb)
+{
+    RemoveSkippableVisitor visitor(Count(), removeChildlessNodes, aCb);
+    VisitEntries(visitor);
+    // If we're about to delete some objects when visitor goes out of scope,
+    // try to delete some more soon.
+    if (visitor.HasSnowWhiteObjects()) {
+        nsCycleCollector_dispatchDeferredDeletion();
+    }
+}
+
+class AsyncFreeSnowWhite : public nsRunnable
 {
-    RemoveSkippableVisitor visitor(removeChildlessNodes);
-    VisitEntries(visitor);
+public:
+  NS_IMETHOD Run()
+  {
+      nsCycleCollector::TryToFreeSnowWhite();
+      return NS_OK;
+  }
+
+  static void Dispatch()
+  {
+      nsRefPtr<AsyncFreeSnowWhite> ev = new AsyncFreeSnowWhite();
+      NS_DispatchToCurrentThread(ev);
+  }
+};
+
+void
+nsCycleCollector::FreeSnowWhite(bool aUntilNoSWInPurpleBuffer)
+{
+    do {
+        SnowWhiteKiller visitor(mPurpleBuf.Count());
+        mPurpleBuf.VisitEntries(visitor);
+        if (!visitor.HasSnowWhiteObjects()) {
+            break;
+        }
+    } while (aUntilNoSWInPurpleBuffer);
+}
+
+/* static */ void
+nsCycleCollector::TryToFreeSnowWhite()
+{
+  CollectorData* data = sCollectorData.get();
+  if (data->mCollector) {
+      data->mCollector->FreeSnowWhite(false);
+  }
 }
 
 void
 nsCycleCollector::SelectPurple(GCGraphBuilder &builder)
 {
     mPurpleBuf.SelectPointers(builder);
 }
 
 void
 nsCycleCollector::ForgetSkippable(bool removeChildlessNodes)
 {
     if (mJSRuntime) {
         mJSRuntime->PrepareForForgetSkippable();
     }
-    mPurpleBuf.RemoveSkippable(removeChildlessNodes);
-    if (mForgetSkippableCB) {
-        mForgetSkippableCB();
-    }
+    mPurpleBuf.RemoveSkippable(removeChildlessNodes, mForgetSkippableCB);
 }
 
 MOZ_NEVER_INLINE void
 nsCycleCollector::MarkRoots(GCGraphBuilder &builder)
 {
     mGraph.mRootCount = builder.Count();
 
     // read the PtrInfo out of the graph that we are building
@@ -2423,16 +2532,18 @@ nsCycleCollector::CollectWhite(nsICycleC
     for (uint32_t i = 0; i < count; ++i) {
         PtrInfo *pinfo = mWhiteNodes->ElementAt(i);
         rv = pinfo->mParticipant->Unroot(pinfo->mPointer);
         if (NS_FAILED(rv))
             Fault("Failed unroot call while unlinking", pinfo);
     }
     timeLog.Checkpoint("CollectWhite::Unroot");
 
+    nsCycleCollector_dispatchDeferredDeletion();
+
     return count > 0;
 }
 
 
 ////////////////////////
 // Memory reporter
 ////////////////////////
 
@@ -2599,36 +2710,36 @@ nsCycleCollector_isScanSafe(void *s, nsC
 
     nsXPCOMCycleCollectionParticipant *xcp;
     ToParticipant(static_cast<nsISupports*>(s), &xcp);
 
     return xcp != nullptr;
 }
 #endif
 
-nsPurpleBufferEntry*
-nsCycleCollector::Suspect(void *n, nsCycleCollectionParticipant *cp)
+void
+nsCycleCollector::Suspect(void *n, nsCycleCollectionParticipant *cp,
+                          nsCycleCollectingAutoRefCnt *aRefCnt)
 {
     CheckThreadSafety();
 
     // Re-entering ::Suspect during collection used to be a fault, but
     // we are canonicalizing nsISupports pointers using QI, so we will
     // see some spurious refcount traffic here.
 
     if (mScanInProgress)
-        return nullptr;
+        return;
 
     MOZ_ASSERT(nsCycleCollector_isScanSafe(n, cp),
                "suspected a non-scansafe pointer");
 
     if (mParams.mDoNothing)
-        return nullptr;
-
-    // Caller is responsible for filling in result's mRefCnt.
-    return mPurpleBuf.Put(n, cp);
+        return;
+
+    mPurpleBuf.Put(n, cp, aRefCnt);
 }
 
 void
 nsCycleCollector::CheckThreadSafety()
 {
 #ifdef DEBUG
     MOZ_ASSERT(mThread == PR_GetCurrentThread());
 #endif
@@ -2750,16 +2861,17 @@ nsCycleCollector::ShutdownCollect(nsICyc
 
     for (uint32_t i = 0; i < DEFAULT_SHUTDOWN_COLLECTIONS; ++i) {
         NS_ASSERTION(i < NORMAL_SHUTDOWN_COLLECTIONS, "Extra shutdown CC");
 
         // Synchronous cycle collection. Always force a JS GC beforehand.
         FixGrayBits(true);
         if (aListener && NS_FAILED(aListener->Begin()))
             aListener = nullptr;
+        FreeSnowWhite(true);
         if (!(BeginCollection(ShutdownCC, aListener) &&
               FinishCollection(aListener)))
             break;
     }
 
     CleanupAfterCollection();
 }
 
@@ -2893,16 +3005,19 @@ uint32_t
 nsCycleCollector::SuspectedCount()
 {
     return mPurpleBuf.Count();
 }
 
 void
 nsCycleCollector::Shutdown()
 {
+    // Always delete snow white objects.
+    FreeSnowWhite(true);
+
 #ifndef DEBUG
     if (PR_GetEnv("XPCOM_CC_RUN_DURING_SHUTDOWN"))
 #endif
     {
         nsCOMPtr<nsCycleCollectorLogger> listener;
         if (mParams.mLogAll || mParams.mLogShutdown) {
             listener = new nsCycleCollectorLogger();
             if (mParams.mAllTracesAtShutdown) {
@@ -3051,29 +3166,43 @@ cyclecollector::DeferredFinalize(Deferre
     // shut down.
     MOZ_ASSERT(data);
     // And we should have a runtime.
     MOZ_ASSERT(data->mRuntime);
 
     data->mRuntime->DeferredFinalize(aAppendFunc, aFunc, aThing);
 }
 
-nsPurpleBufferEntry*
-NS_CycleCollectorSuspect2(void *n, nsCycleCollectionParticipant *cp)
+void
+NS_CycleCollectorSuspect3(void *n, nsCycleCollectionParticipant *cp,
+                          nsCycleCollectingAutoRefCnt *aRefCnt,
+                          bool* aShouldDelete)
 {
     CollectorData *data = sCollectorData.get();
 
     // We should have started the cycle collector by now.
     MOZ_ASSERT(data);
 
     if (!data->mCollector) {
-        return nullptr;
+        if (aRefCnt->get() == 0) {
+            if (!aShouldDelete) {
+                CanonicalizeParticipant(&n, &cp);
+                aRefCnt->stabilizeForDeletion();
+                cp->DeleteCycleCollectable(n);
+            } else {
+                *aShouldDelete = true;
+            }
+        } else {
+          // Make sure we'll get called again.
+          aRefCnt->RemoveFromPurpleBuffer();
+        }
+        return;
     }
 
-    return data->mCollector->Suspect(n, cp);
+    return data->mCollector->Suspect(n, cp, aRefCnt);
 }
 
 uint32_t
 nsCycleCollector_suspectedCount()
 {
     CollectorData *data = sCollectorData.get();
 
     // We should have started the cycle collector by now.
@@ -3153,16 +3282,22 @@ nsCycleCollector_forgetSkippable(bool aR
 
     PROFILER_LABEL("CC", "nsCycleCollector_forgetSkippable");
     TimeLog timeLog;
     data->mCollector->ForgetSkippable(aRemoveChildlessNodes);
     timeLog.Checkpoint("ForgetSkippable()");
 }
 
 void
+nsCycleCollector_dispatchDeferredDeletion()
+{
+    AsyncFreeSnowWhite::Dispatch();
+}
+
+void
 nsCycleCollector_collect(bool aManuallyTriggered,
                          nsCycleCollectorResults *aResults,
                          nsICycleCollectorListener *aListener)
 {
     CollectorData *data = sCollectorData.get();
 
     // We should have started the cycle collector by now.
     MOZ_ASSERT(data);
--- a/xpcom/base/nsCycleCollector.h
+++ b/xpcom/base/nsCycleCollector.h
@@ -51,16 +51,18 @@ nsresult nsCycleCollector_startup(CCThre
 typedef void (*CC_BeforeUnlinkCallback)(void);
 void nsCycleCollector_setBeforeUnlinkCallback(CC_BeforeUnlinkCallback aCB);
 
 typedef void (*CC_ForgetSkippableCallback)(void);
 void nsCycleCollector_setForgetSkippableCallback(CC_ForgetSkippableCallback aCB);
 
 void nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes = false);
 
+void nsCycleCollector_dispatchDeferredDeletion();
+
 void nsCycleCollector_collect(bool aManuallyTriggered,
                               nsCycleCollectorResults *aResults,
                               nsICycleCollectorListener *aListener);
 uint32_t nsCycleCollector_suspectedCount();
 void nsCycleCollector_shutdownThreads();
 void nsCycleCollector_shutdown();
 
 // Helpers for interacting with JS
--- a/xpcom/build/FrozenFunctions.cpp
+++ b/xpcom/build/FrozenFunctions.cpp
@@ -84,19 +84,21 @@ static const XPCOMFunctions kFrozenFunct
     &NS_InvokeByIndex,
     nullptr,
     nullptr,
     &NS_StringSetIsVoid,
     &NS_StringGetIsVoid,
     &NS_CStringSetIsVoid,
     &NS_CStringGetIsVoid,
 
-    // these functions were added post 1.9
-    &NS_CycleCollectorSuspect2,
-    nullptr
+    // these functions were added post 1.9, but then made obsolete
+    nullptr,
+    nullptr,
+
+    &NS_CycleCollectorSuspect3,
 };
 
 EXPORT_XPCOM_API(nsresult)
 NS_GetFrozenFunctions(XPCOMFunctions *functions, const char* /* libraryPath */)
 {
     if (!functions)
         return NS_ERROR_OUT_OF_MEMORY;
 
--- a/xpcom/build/nsXPCOM.h
+++ b/xpcom/build/nsXPCOM.h
@@ -321,19 +321,22 @@ NS_LogCOMPtrRelease(void *aCOMPtr, nsISu
  * The XPCOM cycle collector analyzes and breaks reference cycles between
  * participating XPCOM objects. All objects in the cycle must implement
  * nsCycleCollectionParticipant to break cycles correctly.
  */
 
 #ifdef __cplusplus
 
 class nsCycleCollectionParticipant;
+class nsCycleCollectingAutoRefCnt;
 
-XPCOM_API(nsPurpleBufferEntry*)
-NS_CycleCollectorSuspect2(void *n, nsCycleCollectionParticipant *p);
+XPCOM_API(void)
+NS_CycleCollectorSuspect3(void *n, nsCycleCollectionParticipant *p,
+                          nsCycleCollectingAutoRefCnt *aRefCnt,
+                          bool* aShouldDelete);
 
 #endif
 
 /**
  * Categories (in the category manager service) used by XPCOM:
  */
 
 /**
--- a/xpcom/build/nsXPCOMPrivate.h
+++ b/xpcom/build/nsXPCOMPrivate.h
@@ -84,17 +84,17 @@ typedef void       (* LogCOMPtrFunc)(voi
 
 typedef nsresult   (* GetXPTCallStubFunc)(REFNSIID, nsIXPTCProxy*, nsISomeInterface**);
 typedef void       (* DestroyXPTCallStubFunc)(nsISomeInterface*);
 typedef nsresult   (* InvokeByIndexFunc)(nsISupports*, uint32_t, uint32_t, nsXPTCVariant*);
 typedef bool       (* CycleCollectorFunc)(nsISupports*);
 typedef nsPurpleBufferEntry*
                    (* CycleCollectorSuspect2Func)(void*, nsCycleCollectionParticipant*);
 typedef bool       (* CycleCollectorForget2Func)(nsPurpleBufferEntry*);
-
+typedef void       (* CycleCollectorSuspect3Func)(void*, nsCycleCollectionParticipant*,nsCycleCollectingAutoRefCnt*,bool*);
 // PRIVATE AND DEPRECATED
 typedef NS_CALLBACK(XPCOMExitRoutine)(void);
 
 typedef nsresult   (* RegisterXPCOMExitRoutineFunc)(XPCOMExitRoutine exitRoutine, uint32_t priority);
 typedef nsresult   (* UnregisterXPCOMExitRoutineFunc)(XPCOMExitRoutine exitRoutine);
 
 typedef struct XPCOMFunctions{
     uint32_t version;
@@ -152,27 +152,29 @@ typedef struct XPCOMFunctions{
     LogReleaseFunc logReleaseFunc;
     LogCtorFunc logCtorFunc;
     LogCtorFunc logDtorFunc;
     LogCOMPtrFunc logCOMPtrAddRefFunc;
     LogCOMPtrFunc logCOMPtrReleaseFunc;
     GetXPTCallStubFunc getXPTCallStubFunc;
     DestroyXPTCallStubFunc destroyXPTCallStubFunc;
     InvokeByIndexFunc invokeByIndexFunc;
-    CycleCollectorFunc cycleSuspectFunc; // obsolete: use cycleSuspect2Func
+    CycleCollectorFunc cycleSuspectFunc; // obsolete: use cycleSuspect3Func
     CycleCollectorFunc cycleForgetFunc; // obsolete
     StringSetIsVoidFunc stringSetIsVoid;
     StringGetIsVoidFunc stringGetIsVoid;
     CStringSetIsVoidFunc cstringSetIsVoid;
     CStringGetIsVoidFunc cstringGetIsVoid;
 
     // Added for Mozilla 1.9.1
-    CycleCollectorSuspect2Func cycleSuspect2Func;
+    CycleCollectorSuspect2Func cycleSuspect2Func; // obsolete: use cycleSuspect3Func
     CycleCollectorForget2Func cycleForget2Func; // obsolete
 
+    CycleCollectorSuspect3Func cycleSuspect3Func;
+
 } XPCOMFunctions;
 
 typedef nsresult (*GetFrozenFunctionsFunc)(XPCOMFunctions *entryPoints, const char* libraryPath);
 XPCOM_API(nsresult)
 NS_GetFrozenFunctions(XPCOMFunctions *entryPoints, const char* libraryPath);
 
 
 namespace mozilla {
--- a/xpcom/glue/nsCycleCollectionParticipant.h
+++ b/xpcom/glue/nsCycleCollectionParticipant.h
@@ -118,21 +118,21 @@ struct nsCycleCollectionParticipantVTabl
 {
     nsresult (NS_STDCALL *TraverseReal)
         (T *that, void *p, nsCycleCollectionTraversalCallback &cb);
 
     nsresult (NS_STDCALL *Root)(void *p);
     nsresult (NS_STDCALL *Unlink)(void *p);
     nsresult (NS_STDCALL *Unroot)(void *p);
 
-    void (NS_STDCALL *UnmarkIfPurple)(void *p);
-
     bool (NS_STDCALL *CanSkipReal)(void *p, bool aRemovingAllowed);
     bool (NS_STDCALL *CanSkipInCCReal)(void *p);
     bool (NS_STDCALL *CanSkipThisReal)(void *p);
+
+    void (NS_STDCALL *DeleteCycleCollectable)(void*);
 };
 
 typedef nsCycleCollectionParticipantVTableCommon<nsCycleCollectionParticipant>
     nsCycleCollectionParticipantVTable;
 
 /* Additional functions for nsScriptObjectTracer */
 struct nsScriptObjectTracerVTable
 {
@@ -542,19 +542,19 @@ T* DowncastCCParticipant(void *p)
 ///////////////////////////////////////////////////////////////////////////////
 // Helpers for implementing a concrete nsCycleCollectionParticipant
 ///////////////////////////////////////////////////////////////////////////////
 
 #define NS_DECL_CYCLE_COLLECTION_CLASS_BODY_NO_UNLINK(_class, _base)           \
 public:                                                                        \
   static NS_METHOD TraverseImpl(NS_CYCLE_COLLECTION_CLASSNAME(_class) *that,   \
                             void *p, nsCycleCollectionTraversalCallback &cb);  \
-  static NS_METHOD_(void) UnmarkIfPurpleImpl(void *s)                          \
+  static NS_METHOD_(void) DeleteCycleCollectableImpl(void* p)                  \
   {                                                                            \
-    Downcast(static_cast<nsISupports *>(s))->UnmarkIfPurple();                 \
+    DowncastCCParticipant<_class>(p)->DeleteCycleCollectable();                \
   }                                                                            \
   static _class* Downcast(nsISupports* s)                                      \
   {                                                                            \
     return static_cast<_class*>(static_cast<_base*>(s));                       \
   }                                                                            \
   static nsISupports* Upcast(_class *p)                                        \
   {                                                                            \
     return NS_ISUPPORTS_CAST(_base*, p);                                       \
@@ -705,27 +705,16 @@ class NS_CYCLE_COLLECTION_INNERCLASS    
  : public NS_CYCLE_COLLECTION_CLASSNAME(_base_class)                                   \
 {                                                                                      \
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY(_class, _base_class)                   \
   static NS_METHOD_(void) TraceImpl(void *p, const TraceCallbacks &cb, void *closure); \
   NS_IMPL_GET_XPCOM_CYCLE_COLLECTION_PARTICIPANT(_class)                               \
 };
 
 /**
- * This implements a stub UnmarkIfPurple function for classes that want to be
- * traversed but whose AddRef/Release functions don't add/remove them to/from
- * the purple buffer. If you're just using NS_DECL_CYCLE_COLLECTING_ISUPPORTS
- * then you don't need this.
- */
-#define NS_DECL_CYCLE_COLLECTION_UNMARK_PURPLE_STUB(_class)                    \
-  NS_IMETHODIMP_(void) UnmarkIfPurple()                                        \
-  {                                                                            \
-  }                                                                            \
-
-/**
  * Dummy class with a definition for CanSkip* function members, but no
  * implementation.
  * Implementation was added to please Win PGO. (See bug 765159)
  */
 struct SkippableDummy
 {
   static NS_METHOD_(bool) CanSkipImpl(void *p, bool aRemovingAllowed) { return false; }
   static NS_METHOD_(bool) CanSkipInCCImpl(void *p) { return false; }
@@ -747,20 +736,20 @@ struct Skippable
 // VTables for the cycle collector participant implementations.
 
 #define NS_IMPL_CYCLE_COLLECTION_NATIVE_VTABLE(_class)                         \
   {                                                                            \
     &_class::TraverseImpl,                                                     \
     &_class::RootImpl,                                                         \
     &_class::UnlinkImpl,                                                       \
     &_class::UnrootImpl,                                                       \
-    &_class::UnmarkIfPurpleImpl,                                               \
     _class::isSkippable ? &Skippable<_class>::CanSkipImpl : NULL,              \
     _class::isSkippable ? &Skippable<_class>::CanSkipInCCImpl : NULL,          \
-    _class::isSkippable ? &Skippable<_class>::CanSkipThisImpl : NULL           \
+    _class::isSkippable ? &Skippable<_class>::CanSkipThisImpl : NULL,          \
+    &_class::DeleteCycleCollectableImpl                                        \
   }
 
 #define NS_IMPL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_VTABLE(_class)           \
   NS_IMPL_CYCLE_COLLECTION_NATIVE_VTABLE(_class),                              \
   { &_class::TraceImpl }
 
 #define NS_IMPL_CYCLE_COLLECTION_VTABLE(_class)                                \
   NS_IMPL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_VTABLE(_class)
@@ -769,49 +758,51 @@ struct Skippable
 // Cycle collector participant declarations.
 
 #define NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY(_class)                     \
   public:                                                                      \
     static NS_METHOD RootImpl(void *n);                                        \
     static NS_METHOD UnlinkImpl(void *n);                                      \
     static NS_METHOD UnrootImpl(void *n);                                      \
     static NS_METHOD TraverseImpl(NS_CYCLE_COLLECTION_CLASSNAME(_class) *that, \
-                           void *n, nsCycleCollectionTraversalCallback &cb);
-
-#define NS_DECL_CYCLE_COLLECTION_NATIVE_UNMARK_IF_PURPLE(_class)               \
-    static NS_METHOD_(void) UnmarkIfPurpleImpl(void *p)                        \
+                           void *n, nsCycleCollectionTraversalCallback &cb);   \
+    static NS_METHOD_(void) DeleteCycleCollectableImpl(void* p)                \
     {                                                                          \
-        _class *tmp = static_cast<_class *>(p);                                \
-        if (MOZ_LIKELY(tmp->mRefCnt.HasPurpleBufferEntry()))                   \
-            tmp->mRefCnt.ReleasePurpleBufferEntry();                           \
+      DowncastCCParticipant<_class>(p)->DeleteCycleCollectable();              \
     }
 
 #define NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(_class)                          \
+  void DeleteCycleCollectable(void)                                            \
+  {                                                                            \
+    delete this;                                                               \
+  }                                                                            \
   class NS_CYCLE_COLLECTION_INNERCLASS                                         \
    : public nsCycleCollectionParticipant                                       \
   {                                                                            \
      NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY(_class)                        \
-     NS_DECL_CYCLE_COLLECTION_NATIVE_UNMARK_IF_PURPLE(_class)                  \
      static nsCycleCollectionParticipant* GetParticipant()                     \
      {                                                                         \
         static const CCParticipantVTable<NS_CYCLE_COLLECTION_CLASSNAME(_class)> \
         ::Type p = {                                                          \
            NS_IMPL_CYCLE_COLLECTION_NATIVE_VTABLE(                             \
               NS_CYCLE_COLLECTION_CLASSNAME(_class))                           \
         };                                                                     \
         return NS_PARTICIPANT_AS(nsCycleCollectionParticipant, &p);            \
     }                                                                          \
   };
 
 #define NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(_class)            \
+  void DeleteCycleCollectable(void)                                            \
+  {                                                                            \
+    delete this;                                                               \
+  }                                                                            \
   class NS_CYCLE_COLLECTION_INNERCLASS                                         \
    : public nsScriptObjectTracer                                               \
   {                                                                            \
     NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY(_class)                         \
-    NS_DECL_CYCLE_COLLECTION_NATIVE_UNMARK_IF_PURPLE(_class)                   \
     static NS_METHOD_(void) TraceImpl(void *p, const TraceCallbacks &cb,       \
                                       void *closure);                          \
     static nsScriptObjectTracer* GetParticipant()                              \
     {                                                                          \
       static const CCParticipantVTable<NS_CYCLE_COLLECTION_CLASSNAME(_class)>  \
         ::Type participant = {                                                 \
           NS_IMPL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_VTABLE(                \
             NS_CYCLE_COLLECTION_CLASSNAME(_class))                             \
--- a/xpcom/glue/nsISupportsImpl.h
+++ b/xpcom/glue/nsISupportsImpl.h
@@ -77,181 +77,118 @@ private:
 #else // !DEBUG
 
 #define NS_DECL_OWNINGTHREAD            /* nothing */
 #define NS_ASSERT_OWNINGTHREAD(_class)  ((void)0)
 #define NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(_class)  ((void)0)
 
 #endif // DEBUG
 
-#define NS_CCAR_REFCNT_BIT 1
-#define NS_CCAR_REFCNT_TO_TAGGED(rc_) \
-  NS_INT32_TO_PTR((rc_ << 1) | NS_CCAR_REFCNT_BIT)
-#define NS_CCAR_PURPLE_ENTRY_TO_TAGGED(pe_) \
-  static_cast<void*>(pe_)
-#define NS_CCAR_TAGGED_TO_REFCNT(tagged_) \
-  nsrefcnt(NS_PTR_TO_INT32(tagged_) >> 1)
-#define NS_CCAR_TAGGED_TO_PURPLE_ENTRY(tagged_) \
-  static_cast<nsPurpleBufferEntry*>(tagged_)
-#define NS_CCAR_TAGGED_STABILIZED_REFCNT NS_CCAR_PURPLE_ENTRY_TO_TAGGED(0)
-
 // Support for ISupports classes which interact with cycle collector.
 
 struct nsPurpleBufferEntry {
-  // mObject is set to null when nsCycleCollectingAutoRefCnt loses its
-  // reference to the PurpleBufferEntry so that the entry can be added to the
-  // free list. When mObject is null, mNotPurple has no meaning.
   union {
     void *mObject;                        // when low bit unset
     nsPurpleBufferEntry *mNextInFreeList; // when low bit set
   };
-  // When an object is in the purple buffer, it replaces its reference
-  // count with a (tagged) pointer to this entry, so we store the
-  // reference count for it.
-  nsrefcnt mRefCnt : 31;
-  // When this flag is true, the purple buffer entry is in
-  // a state where there's an object out there that holds onto it, but we aren't
-  // counting that object as purple. This is done to reduce the cost of removing
-  // objects from the purple buffer.
-  nsrefcnt mNotPurple : 1; // nsrefcnt to ensure right packing.
+
+  nsCycleCollectingAutoRefCnt *mRefCnt;
 
   nsCycleCollectionParticipant *mParticipant; // NULL for nsISupports
 };
 
+#define NS_NUMBER_OF_FLAGS_IN_REFCNT 2
+#define NS_IN_PURPLE_BUFFER (1 << 0)
+#define NS_IS_PURPLE (1 << 1)
+#define NS_REFCOUNT_CHANGE (1 << NS_NUMBER_OF_FLAGS_IN_REFCNT)
+#define NS_REFCOUNT_VALUE(_val) (_val >> NS_NUMBER_OF_FLAGS_IN_REFCNT)
+
 class nsCycleCollectingAutoRefCnt {
 
 public:
   nsCycleCollectingAutoRefCnt()
-    : mTagged(NS_CCAR_REFCNT_TO_TAGGED(0))
+    : mRefCntAndFlags(0)
   {}
 
-  nsCycleCollectingAutoRefCnt(nsrefcnt aValue)
-    : mTagged(NS_CCAR_REFCNT_TO_TAGGED(aValue))
+  nsCycleCollectingAutoRefCnt(uintptr_t aValue)
+    : mRefCntAndFlags(aValue << NS_NUMBER_OF_FLAGS_IN_REFCNT)
   {
   }
 
-  MOZ_ALWAYS_INLINE nsrefcnt incr(void *owner)
+  MOZ_ALWAYS_INLINE uintptr_t incr()
   {
-    if (MOZ_UNLIKELY(mTagged == NS_CCAR_TAGGED_STABILIZED_REFCNT)) {
-      // The sentinel value "purple bit alone, refcount 0" means
-      // that we're stabilized, during finalization. In this
-      // state we lie about our actual refcount if anyone asks
-      // and say it's 2, which is basically true: the caller who
-      // is incrementing has a reference, as does the decr() frame
-      // that stabilized-and-is-deleting us.
-      return 2;
-    }
-
-    nsrefcnt refcount;
-    if (HasPurpleBufferEntry()) {
-      nsPurpleBufferEntry *e = NS_CCAR_TAGGED_TO_PURPLE_ENTRY(mTagged);
-      MOZ_ASSERT(e->mObject == owner, "wrong entry");
-      MOZ_ASSERT(int32_t(e->mRefCnt) > 0, "purple ISupports with bad refcnt");
-      refcount = ++(e->mRefCnt);
-      e->mNotPurple = true;
-    } else {
-      refcount = NS_CCAR_TAGGED_TO_REFCNT(mTagged);
-      MOZ_ASSERT(int32_t(refcount) >= 0, "bad refcount");
-      ++refcount;
-      mTagged = NS_CCAR_REFCNT_TO_TAGGED(refcount);
-    }
-
-    return refcount;
+    mRefCntAndFlags += NS_REFCOUNT_CHANGE;
+    mRefCntAndFlags &= ~NS_IS_PURPLE;
+    return NS_REFCOUNT_VALUE(mRefCntAndFlags);
   }
 
   MOZ_ALWAYS_INLINE void stabilizeForDeletion()
   {
-    mTagged = NS_CCAR_TAGGED_STABILIZED_REFCNT;
+    // Set refcnt to 1 and mark us to be in the purple buffer.
+    // This way decr won't call suspect again.
+    mRefCntAndFlags = NS_REFCOUNT_CHANGE | NS_IN_PURPLE_BUFFER;
   }
 
-  MOZ_ALWAYS_INLINE nsrefcnt decr(nsISupports *owner)
+  MOZ_ALWAYS_INLINE uintptr_t decr(nsISupports *owner,
+                                   bool *shouldDelete = nullptr)
   {
-    return decr(owner, nullptr);
+    return decr(owner, nullptr, shouldDelete);
   }
 
-  MOZ_ALWAYS_INLINE nsrefcnt decr(void *owner, nsCycleCollectionParticipant *p)
+  MOZ_ALWAYS_INLINE uintptr_t decr(void *owner, nsCycleCollectionParticipant *p,
+                                   bool *shouldDelete = nullptr)
   {
-    if (MOZ_UNLIKELY(mTagged == NS_CCAR_TAGGED_STABILIZED_REFCNT))
-      return 1;
-
-    nsrefcnt refcount;
-    if (HasPurpleBufferEntry()) {
-      nsPurpleBufferEntry *e = NS_CCAR_TAGGED_TO_PURPLE_ENTRY(mTagged);
-      MOZ_ASSERT(e->mObject == owner, "wrong entry");
-      MOZ_ASSERT(int32_t(e->mRefCnt) > 0, "purple ISupports with bad refcnt");
-      refcount = --(e->mRefCnt);
-      if (MOZ_UNLIKELY(refcount == 0)) {
-        e->mObject = nullptr;
-        mTagged = NS_CCAR_REFCNT_TO_TAGGED(0);
-      } else {
-        e->mNotPurple = false;
-      }
-    } else {
-      refcount = NS_CCAR_TAGGED_TO_REFCNT(mTagged);
-      MOZ_ASSERT(int32_t(refcount) > 0, "bad refcount");
-      --refcount;
-
-      nsPurpleBufferEntry *e;
-      if (MOZ_LIKELY(refcount > 0) &&
-          ((e = NS_CycleCollectorSuspect2(owner, p)))) {
-        e->mRefCnt = refcount;
-        mTagged = NS_CCAR_PURPLE_ENTRY_TO_TAGGED(e);
-      } else {
-        mTagged = NS_CCAR_REFCNT_TO_TAGGED(refcount);
-      }
+    MOZ_ASSERT(get() > 0);
+    if (!IsInPurpleBuffer()) {
+      mRefCntAndFlags -= NS_REFCOUNT_CHANGE;
+      mRefCntAndFlags |= (NS_IN_PURPLE_BUFFER | NS_IS_PURPLE);
+      uintptr_t retval = NS_REFCOUNT_VALUE(mRefCntAndFlags);
+      // Suspect may delete 'owner' and 'this'!
+      NS_CycleCollectorSuspect3(owner, p, this, shouldDelete);
+      return retval;
     }
-
-    return refcount;
-  }
-
-  MOZ_ALWAYS_INLINE void ReleasePurpleBufferEntry()
-  {
-    MOZ_ASSERT(HasPurpleBufferEntry(), "must have purple buffer entry");
-    nsrefcnt refcount = NS_CCAR_TAGGED_TO_PURPLE_ENTRY(mTagged)->mRefCnt;
-    mTagged = NS_CCAR_REFCNT_TO_TAGGED(refcount);
+    mRefCntAndFlags -= NS_REFCOUNT_CHANGE;
+    mRefCntAndFlags |= (NS_IN_PURPLE_BUFFER | NS_IS_PURPLE);
+    return NS_REFCOUNT_VALUE(mRefCntAndFlags);
   }
 
   MOZ_ALWAYS_INLINE void RemovePurple()
   {
     MOZ_ASSERT(IsPurple(), "must be purple");
-    // The entry will be added to the free list later. 
-    NS_CCAR_TAGGED_TO_PURPLE_ENTRY(mTagged)->mObject = nullptr;
-    ReleasePurpleBufferEntry();
+    mRefCntAndFlags &= ~NS_IS_PURPLE;
   }
 
-  MOZ_ALWAYS_INLINE bool HasPurpleBufferEntry() const
+  MOZ_ALWAYS_INLINE void RemoveFromPurpleBuffer()
   {
-    MOZ_ASSERT(mTagged != NS_CCAR_TAGGED_STABILIZED_REFCNT,
-               "should have checked for stabilization first");
-    return !(NS_PTR_TO_INT32(mTagged) & NS_CCAR_REFCNT_BIT);
+    MOZ_ASSERT(IsInPurpleBuffer());
+    mRefCntAndFlags &= ~(NS_IS_PURPLE | NS_IN_PURPLE_BUFFER);
   }
 
   MOZ_ALWAYS_INLINE bool IsPurple() const
   {
-    return HasPurpleBufferEntry() &&
-      !(NS_CCAR_TAGGED_TO_PURPLE_ENTRY(mTagged)->mNotPurple);
+    return !!(mRefCntAndFlags & NS_IS_PURPLE);
+  }
+
+  MOZ_ALWAYS_INLINE bool IsInPurpleBuffer() const
+  {
+    return !!(mRefCntAndFlags & NS_IN_PURPLE_BUFFER);
   }
 
   MOZ_ALWAYS_INLINE nsrefcnt get() const
   {
-    if (MOZ_UNLIKELY(mTagged == NS_CCAR_TAGGED_STABILIZED_REFCNT))
-      return 1;
-
-    return MOZ_UNLIKELY(HasPurpleBufferEntry())
-             ? NS_CCAR_TAGGED_TO_PURPLE_ENTRY(mTagged)->mRefCnt
-             : NS_CCAR_TAGGED_TO_REFCNT(mTagged);
+    return NS_REFCOUNT_VALUE(mRefCntAndFlags);
   }
 
   MOZ_ALWAYS_INLINE operator nsrefcnt() const
   {
     return get();
   }
 
  private:
-  void *mTagged;
+  uintptr_t mRefCntAndFlags;
 };
 
 class nsAutoRefCnt {
 
  public:
     nsAutoRefCnt() : mValue(0) {}
     nsAutoRefCnt(nsrefcnt aValue) : mValue(aValue) {}
 
@@ -288,62 +225,76 @@ protected:                              
 public:
 
 #define NS_DECL_CYCLE_COLLECTING_ISUPPORTS                                    \
 public:                                                                       \
   NS_IMETHOD QueryInterface(REFNSIID aIID,                                    \
                             void** aInstancePtr);                             \
   NS_IMETHOD_(nsrefcnt) AddRef(void);                                         \
   NS_IMETHOD_(nsrefcnt) Release(void);                                        \
-  void UnmarkIfPurple()                                                       \
-  {                                                                           \
-    if (MOZ_LIKELY(mRefCnt.HasPurpleBufferEntry()))                           \
-      mRefCnt.ReleasePurpleBufferEntry();                                     \
-  }                                                                           \
+  NS_IMETHOD_(void) DeleteCycleCollectable(void);                             \
 protected:                                                                    \
   nsCycleCollectingAutoRefCnt mRefCnt;                                        \
   NS_DECL_OWNINGTHREAD                                                        \
 public:
 
 
 ///////////////////////////////////////////////////////////////////////////////
 
 /*
  * Implementation of AddRef and Release for non-nsISupports (ie "native")
  * cycle-collected classes that use the purple buffer to avoid leaks.
  */
 
 #define NS_IMPL_CC_NATIVE_ADDREF_BODY(_class)                                 \
     MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");                      \
     NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(_class);                          \
-    nsrefcnt count = mRefCnt.incr(this);                                      \
+    nsrefcnt count = mRefCnt.incr();                                          \
     NS_LOG_ADDREF(this, count, #_class, sizeof(*this));                       \
     return count;
 
 #define NS_IMPL_CC_NATIVE_RELEASE_BODY(_class)                                \
     MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");                          \
     NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(_class);                          \
     nsrefcnt count =                                                          \
       mRefCnt.decr(static_cast<void*>(this),                                  \
                    _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant()); \
     NS_LOG_RELEASE(this, count, #_class);                                     \
-    if (count == 0) {                                                         \
-      NS_ASSERT_OWNINGTHREAD(_class);                                         \
-      mRefCnt.stabilizeForDeletion();                                         \
-      delete this;                                                            \
-      return 0;                                                               \
-    }                                                                         \
     return count;
 
 #define NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(_class)                        \
 NS_METHOD_(nsrefcnt) _class::AddRef(void)                                     \
 {                                                                             \
   NS_IMPL_CC_NATIVE_ADDREF_BODY(_class)                                       \
 }
 
+#define NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE_WITH_LAST_RELEASE(_class, _last) \
+NS_METHOD_(nsrefcnt) _class::Release(void)                                       \
+{                                                                                \
+    MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");                             \
+    NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(_class);                             \
+    bool shouldDelete = false;                                                   \
+    nsrefcnt count =                                                             \
+      mRefCnt.decr(static_cast<void*>(this),                                     \
+                   _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant(),     \
+                   &shouldDelete);                                               \
+    NS_LOG_RELEASE(this, count, #_class);                                        \
+    if (count == 0) {                                                            \
+        mRefCnt.incr();                                                          \
+        _last;                                                                   \
+        mRefCnt.decr(static_cast<void*>(this),                                   \
+                     _class::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant());  \
+        if (shouldDelete) {                                                      \
+            mRefCnt.stabilizeForDeletion();                                      \
+            DeleteCycleCollectable();                                            \
+        }                                                                        \
+    }                                                                            \
+    return count;                                                                \
+}
+
 #define NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(_class)                       \
 NS_METHOD_(nsrefcnt) _class::Release(void)                                    \
 {                                                                             \
   NS_IMPL_CC_NATIVE_RELEASE_BODY(_class)                                      \
 }
 
 #define NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(_class)            \
 public:                                                                       \
@@ -525,42 +476,65 @@ NS_IMETHODIMP_(nsrefcnt) _class::Release
 }
 
 
 #define NS_IMPL_CYCLE_COLLECTING_ADDREF(_class)                               \
 NS_IMETHODIMP_(nsrefcnt) _class::AddRef(void)                                 \
 {                                                                             \
   MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");                        \
   NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(_class);                            \
-  nsrefcnt count =                                                            \
-    mRefCnt.incr(NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this));        \
+  nsrefcnt count = mRefCnt.incr();                                            \
   NS_LOG_ADDREF(this, count, #_class, sizeof(*this));                         \
   return count;                                                               \
 }
 
 #define NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(_class, _destroy)       \
 NS_IMETHODIMP_(nsrefcnt) _class::Release(void)                                \
 {                                                                             \
   MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");                            \
   NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(_class);                            \
   nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this);    \
   nsrefcnt count = mRefCnt.decr(base);                                        \
   NS_LOG_RELEASE(this, count, #_class);                                       \
-  if (count == 0) {                                                           \
-    NS_ASSERT_OWNINGTHREAD(_class);                                           \
-    mRefCnt.stabilizeForDeletion();                                           \
-    _destroy;                                                                 \
-    return 0;                                                                 \
-  }                                                                           \
   return count;                                                               \
+}                                                                             \
+NS_IMETHODIMP_(void) _class::DeleteCycleCollectable(void)                     \
+{                                                                             \
+  _destroy;                                                                   \
 }
 
 #define NS_IMPL_CYCLE_COLLECTING_RELEASE(_class)                              \
   NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(_class, delete (this))
 
+// _LAST_RELEASE can be useful when certain resources should be released
+// as soon as we know the object will be deleted.
+#define NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(_class, _last)     \
+NS_IMETHODIMP_(nsrefcnt) _class::Release(void)                                \
+{                                                                             \
+  MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");                            \
+  NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(_class);                            \
+  bool shouldDelete = false;                                                  \
+  nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this);    \
+  nsrefcnt count = mRefCnt.decr(base, &shouldDelete);                         \
+  NS_LOG_RELEASE(this, count, #_class);                                       \
+  if (count == 0) {                                                           \
+      mRefCnt.incr();                                                         \
+      _last;                                                                  \
+      mRefCnt.decr(base);                                                     \
+      if (shouldDelete) {                                                     \
+          mRefCnt.stabilizeForDeletion();                                     \
+          DeleteCycleCollectable();                                           \
+      }                                                                       \
+  }                                                                           \
+  return count;                                                               \
+}                                                                             \
+NS_IMETHODIMP_(void) _class::DeleteCycleCollectable(void)                     \
+{                                                                             \
+  delete this;                                                                \
+}
 
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
  * There are two ways of implementing QueryInterface, and we use both:
  *
  * Table-driven QueryInterface uses a static table of IID->offset mappings
  * and a shared helper function. Using it tends to reduce codesize and improve
--- a/xpcom/glue/standalone/nsXPCOMGlue.cpp
+++ b/xpcom/glue/standalone/nsXPCOMGlue.cpp
@@ -949,16 +949,25 @@ XPCOM_API(nsPurpleBufferEntry*)
 NS_CycleCollectorSuspect2(void* obj, nsCycleCollectionParticipant *p)
 {
     if (!xpcomFunctions.cycleSuspect2Func)
         return nullptr;
 
     return xpcomFunctions.cycleSuspect2Func(obj, p);
 }
 
+XPCOM_API(void)
+NS_CycleCollectorSuspect3(void* obj, nsCycleCollectionParticipant *p,
+                          nsCycleCollectingAutoRefCnt* aRefCnt,
+                          bool* aShouldDelete)
+{
+    if (xpcomFunctions.cycleSuspect3Func)
+        xpcomFunctions.cycleSuspect3Func(obj, p, aRefCnt, aShouldDelete);
+}
+
 XPCOM_API(bool)
 NS_CycleCollectorForget2(nsPurpleBufferEntry* e)
 {
     if (!xpcomFunctions.cycleForget2Func)
         return false;
 
     return xpcomFunctions.cycleForget2Func(e);
 }