Bug 1223372 - Handle ToWindowProxyIfWindow returning a dead wrapper in PrepareForWrapping. r=bholley
authorJan de Mooij <jdemooij@mozilla.com>
Tue, 17 Nov 2015 19:24:28 +0100
changeset 272981 29001cebc62c4366d36ef36ab79c75c3800a7984
parent 272980 2a05b69065e655f8123fd7b1412c066fffaef6de
child 272982 bb2ece1c131b9faf7d560ac10c4d8daa6f6a09b0
push id68121
push userjandemooij@gmail.com
push dateTue, 17 Nov 2015 18:24:37 +0000
treeherdermozilla-inbound@29001cebc62c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs1223372
milestone45.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 1223372 - Handle ToWindowProxyIfWindow returning a dead wrapper in PrepareForWrapping. r=bholley
js/src/jsfriendapi.h
js/xpconnect/tests/chrome/chrome.ini
js/xpconnect/tests/chrome/test_windowProxyDeadWrapper.html
js/xpconnect/wrappers/WrapperFactory.cpp
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -2910,19 +2910,19 @@ IsWindow(JSObject* obj)
 
 /**
  * Returns true iff `obj` has the WindowProxy Class (see SetWindowProxyClass).
  */
 JS_FRIEND_API(bool)
 IsWindowProxy(JSObject* obj);
 
 /**
- * If `obj` is a Window, get its associated WindowProxy (or a CCW if the
- * page was navigated away from), else return `obj`. This function is
- * infallible and never returns nullptr.
+ * If `obj` is a Window, get its associated WindowProxy (or a CCW or dead
+ * wrapper if the page was navigated away from), else return `obj`. This
+ * function is infallible and never returns nullptr.
  */
 extern JS_FRIEND_API(JSObject*)
 ToWindowProxyIfWindow(JSObject* obj);
 
 /**
  * If `obj` is a WindowProxy, get its associated Window (the compartment's
  * global), else return `obj`. This function is infallible and never returns
  * nullptr.
--- a/js/xpconnect/tests/chrome/chrome.ini
+++ b/js/xpconnect/tests/chrome/chrome.ini
@@ -104,8 +104,9 @@ skip-if = buildapp == 'mulet'
 [test_weakref.xul]
 [test_wrappers.xul]
 [test_weakmaps.xul]
 [test_wrappers-2.xul]
 # Disabled until this test gets updated to test the new proxy based wrappers.
 skip-if = true
 [test_watchpoints.xul]
 [test_nsScriptErrorWithStack.html]
+[test_windowProxyDeadWrapper.html]
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_windowProxyDeadWrapper.html
@@ -0,0 +1,72 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1223372
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1223372</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+/** Test for Bug 1223372 **/
+
+function go() {
+    SimpleTest.waitForExplicitFinish();
+
+    var frame = $('subframe');
+    frame.onload = null;
+
+    var w = frame.contentWindow;
+
+    w.eval("checkDead = function() { return Components.utils.isDeadWrapper(this); };");
+    var checkDead = w.checkDead;
+
+    w.eval("getObject = function() { return {}; }");
+    var getObject = w.getObject;
+
+    ok(!checkDead(), "WindowProxy shouldn't be dead yet");
+
+    // Load a content page in the chrome frame.
+    w.location = "http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html";
+    tryWindow();
+
+    function tryWindow() {
+        if (w.document.title != 'empty test page') {
+            info("Document not loaded yet - retrying");
+            SimpleTest.executeSoon(tryWindow);
+            return;
+        }
+
+        // Remove the frame. This will nuke the WindowProxy wrapper from our chrome
+        // document's global, so evaluating 'this' in it will return a dead wrapper.
+        frame.remove();
+
+        SimpleTest.executeSoon(function() {
+            ok(checkDead(), "Expected a dead object wrapper");
+
+            // Wrapping the Window should throw now.
+            var exc;
+            try {
+                Components.utils.getGlobalForObject(getObject());
+            } catch(e) {
+                exc = e;
+            }
+            is(exc.message, "Can't wrap dead object");
+
+            SimpleTest.finish();
+        });
+    }
+}
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1223372">Mozilla Bug 1223372</a>
+
+<iframe id="subframe" src="data:text/html," onload="go()"></iframe>
+
+</body>
+</html>
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -157,16 +157,20 @@ WrapperFactory::PrepareForWrapping(JSCon
     // have to worry about them for the rest of the wrapping code.
     if (js::IsWindow(obj)) {
         JSAutoCompartment ac(cx, obj);
         obj = js::ToWindowProxyIfWindow(obj);
         MOZ_ASSERT(obj);
         // ToWindowProxyIfWindow can return a CCW if |obj| was a
         // navigated-away-from Window. Strip any CCWs.
         obj = js::UncheckedUnwrap(obj);
+        if (JS_IsDeadWrapper(obj)) {
+            JS_ReportError(cx, "Can't wrap dead object");
+            return nullptr;
+        }
         MOZ_ASSERT(js::IsWindowProxy(obj));
     }
 
     // If we've got a WindowProxy, there's nothing special that needs to be
     // done here, and we can move on to the next phase of wrapping. We handle
     // this case first to allow us to assert against wrappers below.
     if (js::IsWindowProxy(obj))
         return waive ? WaiveXray(cx, obj) : obj;