Bug 1352827: Handle potential null reference during cleanup by CC. r=hsivonen
authorKris Maglione <maglione.k@gmail.com>
Sun, 02 Apr 2017 15:05:51 -0700
changeset 352883 dd2c353c32bce83784c93f7511795290c621da75
parent 352882 b59c6604cafa5ba52a6c741b2e8424cad9398c13
child 352884 31efcf6136159b6d670c37916f9bc6b447c8be7f
push id40736
push usermaglione.k@gmail.com
push dateThu, 13 Apr 2017 20:23:04 +0000
treeherderautoland@dd2c353c32bc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershsivonen
bugs1352827
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1352827: Handle potential null reference during cleanup by CC. r=hsivonen MozReview-Commit-ID: G7BM3nuEKY1
dom/base/nsDocument.cpp
dom/base/test/test_blockParsing.html
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -10638,24 +10638,28 @@ public:
     MaybeUnblockParser();
 
     mPromise->MaybeReject(aCx, aValue);
   }
 
 protected:
   virtual ~UnblockParsingPromiseHandler()
   {
-    MaybeUnblockParser();
+    // If we're being cleaned up by the cycle collector, our mDocument reference
+    // may have been unlinked while our mParser weak reference is still alive.
+    if (mDocument) {
+      MaybeUnblockParser();
+    }
   }
 
 private:
   void MaybeUnblockParser() {
     nsCOMPtr<nsIParser> parser = do_QueryReferent(mParser);
     if (parser) {
-      MOZ_ASSERT(mDocument);
+      MOZ_DIAGNOSTIC_ASSERT(mDocument);
       nsCOMPtr<nsIParser> docParser = mDocument->CreatorParserOrNull();
       if (parser == docParser) {
         parser->UnblockParser();
         parser->ContinueInterruptedParsingAsync();
       }
     }
     mParser = nullptr;
     mDocument = nullptr;
--- a/dom/base/test/test_blockParsing.html
+++ b/dom/base/test/test_blockParsing.html
@@ -76,13 +76,56 @@ add_task(function* () {
   yield runTest("http://mochi.test:8888/chrome/dom/base/test/file_external_script.html",
                 '<html lang="en"></html>',
                 '<html lang="en"><head>\n  <script src="file_script.js"><\/script>\n  <meta charset="utf-8">\n  <title></title>\n</head>\n<body>\n  <p>Hello Mochitest</p>\n\n\n</body></html>');
 
   yield runTest("http://mochi.test:8888/chrome/dom/base/test/file_external_script.xhtml",
                 '<html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"></html>',
                 '<html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">\n<head>\n  <script src="file_script.js"><\/script>\n  <title></title>\n</head>\n<body>\n  <p>Hello Mochitest</p>\n</body>\n</html>');
 });
+
+add_task(function* test_cleanup() {
+  const TOPIC = "blocking-promise-destroyed";
+
+  const finalizationWitness = Components.classes["@mozilla.org/toolkit/finalizationwitness;1"]
+      .getService(Components.interfaces.nsIFinalizationWitnessService);
+
+  for (let url of ["http://mochi.test:8888/chrome/dom/base/test/file_inline_script.html",
+                   "http://mochi.test:8888/chrome/dom/base/test/file_inline_script.xhtml"]) {
+    let iframe = document.createElement("iframe");
+    iframe.src = url;
+
+    // Create a promise that never resolves.
+    let blockerPromise = new Promise(() => {});
+
+    // Create a finalization witness so we can be sure that the promises
+    // have been collected before the end of the test.
+    let destroyedPromise = TestUtils.topicObserved(TOPIC);
+    let witness = finalizationWitness.make(TOPIC, url);
+    blockerPromise.witness = witness;
+
+    let insertedPromise = TestUtils.topicObserved("document-element-inserted", document => {
+      document.blockParsing(blockerPromise).witness = witness;
+
+      return true;
+    });
+
+    document.body.appendChild(iframe);
+    yield insertedPromise;
+
+    // Clear the promise reference, destroy the document, and force GC/CC. This should
+    // trigger any potential leaks or cleanup issues.
+    blockerPromise = null;
+    witness = null;
+    iframe.remove();
+
+    Components.utils.forceGC();
+    Components.utils.forceCC();
+    Components.utils.forceGC();
+
+    // Make sure the blocker promise has been collected.
+    let [, data] = yield destroyedPromise;
+    is(data, url, "Should have correct finalizer URL");
+  }
+});
 </script>
 </body>
 </html>
-
-