Detach frame from document when entering page cache
authordbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 3 May 2017 17:52:30 +0000 (17:52 +0000)
committerdbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 3 May 2017 17:52:30 +0000 (17:52 +0000)
https://bugs.webkit.org/show_bug.cgi?id=166774
<rdar://problem/29904368>

Reviewed by Chris Dumez.

* TestExpectations: Unskip tests.
* fast/history/page-cache-after-window-open-expected.txt: Update expected result.
* fast/history/page-cache-after-window-open.html: Ditto.
* fast/history/page-cache-with-opener-expected.txt: Ditto.
* fast/history/page-cache-with-opener.html: Update test given its new expected behavior.
* fast/history/resources/page-cache-window-with-opener.html: Ditto.
* http/tests/security/xss-DENIED-click-and-form-submission-from-inactive-domwindow-expected.txt: Added.
* http/tests/security/xss-DENIED-click-and-form-submission-from-inactive-domwindow.html: Added.
* http/tests/security/xss-DENIED-script-inject-into-inactive-window-expected.txt: Added.
* http/tests/security/xss-DENIED-script-inject-into-inactive-window.html: Added.
* http/tests/security/xss-DENIED-script-inject-into-inactive-window2-expected.txt: Added.
* http/tests/security/xss-DENIED-script-inject-into-inactive-window2.html: Added.
* http/tests/security/xss-DENIED-script-inject-into-inactive-window3-expected.txt: Added.
* http/tests/security/xss-DENIED-script-inject-into-inactive-window3.html: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@216126 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/fast/history/page-cache-after-window-open-expected.txt
LayoutTests/fast/history/page-cache-after-window-open.html
LayoutTests/fast/history/page-cache-with-opener-expected.txt
LayoutTests/fast/history/page-cache-with-opener.html
LayoutTests/fast/history/resources/page-cache-window-with-opener.html
LayoutTests/http/tests/security/xss-DENIED-click-and-form-submission-from-inactive-domwindow-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/xss-DENIED-click-and-form-submission-from-inactive-domwindow.html [new file with mode: 0644]
LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window.html [new file with mode: 0644]
LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window2-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window2.html [new file with mode: 0644]
LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window3-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window3.html [new file with mode: 0644]

index ffbba17..f5077de 100644 (file)
@@ -1,3 +1,26 @@
+2017-05-03  Daniel Bates  <dabates@apple.com>
+
+        Detach frame from document when entering page cache
+        https://bugs.webkit.org/show_bug.cgi?id=166774
+        <rdar://problem/29904368>
+
+        Reviewed by Chris Dumez.
+
+        * TestExpectations: Unskip tests.
+        * fast/history/page-cache-after-window-open-expected.txt: Update expected result.
+        * fast/history/page-cache-after-window-open.html: Ditto.
+        * fast/history/page-cache-with-opener-expected.txt: Ditto.
+        * fast/history/page-cache-with-opener.html: Update test given its new expected behavior.
+        * fast/history/resources/page-cache-window-with-opener.html: Ditto.
+        * http/tests/security/xss-DENIED-click-and-form-submission-from-inactive-domwindow-expected.txt: Added.
+        * http/tests/security/xss-DENIED-click-and-form-submission-from-inactive-domwindow.html: Added.
+        * http/tests/security/xss-DENIED-script-inject-into-inactive-window-expected.txt: Added.
+        * http/tests/security/xss-DENIED-script-inject-into-inactive-window.html: Added.
+        * http/tests/security/xss-DENIED-script-inject-into-inactive-window2-expected.txt: Added.
+        * http/tests/security/xss-DENIED-script-inject-into-inactive-window2.html: Added.
+        * http/tests/security/xss-DENIED-script-inject-into-inactive-window3-expected.txt: Added.
+        * http/tests/security/xss-DENIED-script-inject-into-inactive-window3.html: Added.
+
 2017-05-03  Matt Lewis  <jlewis3@apple.com>
 
         Marking three imported/w3c/web-platform-tests/webrtc test as flaky failures.
index 88c7f24..3a09f3a 100644 (file)
@@ -1120,10 +1120,6 @@ webkit.org/b/169264 imported/w3c/web-platform-tests/html/browsers/browsing-the-w
 webkit.org/b/168066 performance-api/performance-now-api.html [ Pass Failure ]
 webkit.org/b/168005 performance-api/performance-now-time-origin-in-worker.html [ Pass Failure ]
 
-fast/history/page-cache-after-window-open.html [ Skip ]
-fast/history/page-cache-back-navigation-crash.html [ Skip ]
-fast/history/page-cache-with-opener.html [ Skip ]
-
 webkit.org/b/168238 imported/w3c/web-platform-tests/dom/events/EventListener-invoke-legacy.html [ Pass Failure ]
 
 # Ignore MIME type refused console messages when comparing nosniff tests results as their ordering is non-deterministic.
index bd596fa..0fba44b 100644 (file)
@@ -1,10 +1,11 @@
-Tests that a page that has called window.open() does not go into the page cache.
+Tests that a page that has called window.open() goes into the page cache.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-pageshow - not from cache
-PASS Page was not restored from page cache
+PASS page was not restored from the page cache.
+PASS page was saved to the page cache.
+PASS page was restored from the page cache.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index dc10019..595ea41 100644 (file)
@@ -3,7 +3,7 @@
 <body>
 <script src="../../resources/js-test-pre.js"></script>
 <script>
-description('Tests that a page that has called window.open() does not go into the page cache.');
+description("Tests that a page that has called window.open() goes into the page cache.");
 window.jsTestIsAsync = true;
 
 if (window.testRunner) {
@@ -12,36 +12,38 @@ if (window.testRunner) {
 }
 
 window.addEventListener("pageshow", function(event) {
-    debug("pageshow - " + (event.persisted ? "" : "not ") + "from cache");
-
-    if (!window.sessionStorage.page_cache_after_window_open_test_started)
+    if (!window.sessionStorage.didStartPageCacheAfterWindowOpenTest) {
+        if (event.persisted)
+            testFailed("page was restored from the page cache.");
+        else
+            testPassed("page was not restored from the page cache.");
         return;
+    }
 
-    delete window.sessionStorage.page_cache_after_window_open_test_started;
+    delete window.sessionStorage.didStartPageCacheAfterWindowOpenTest;
 
     if (event.persisted)
-        testFailed("Page did enter and was restored from the page cache");
+        testPassed("page was restored from the page cache.");
     else
-        testPassed("Page was not restored from page cache");
+        testFailed("page was not restored from the page cache.");
 
     finishJSTest();
 }, false);
 
 window.addEventListener("pagehide", function(event) {
-    debug("pagehide - " + (event.persisted ? "" : "not ") + "entering cache");
-    if (event.persisted) {
-        testFailed("Page entered the page cache.");
-        finishJSTest();
-    }
+    if (event.persisted)
+        testPassed("page was saved to the page cache.");
+    else
+        testFailed("page was not saved to the page cache.");
 }, false);
 
-window.addEventListener('load', function() {
-    newWindow = open("about:blank", "one");
+window.addEventListener("load", function() {
+    newWindow = open("about:blank");
     otherWindowDocument = newWindow.document;
 
     setTimeout(function() {
         // Force a back navigation back to this page.
-        window.sessionStorage.page_cache_after_window_open_test_started = true;
+        window.sessionStorage.didStartPageCacheAfterWindowOpenTest = true;
         window.location.href = "resources/page-cache-helper.html";
     }, 0);
 }, false);
index 379b189..fd1284b 100644 (file)
@@ -1,9 +1,11 @@
-Tests that a page that has a window opener does not go into the page cache.
+Tests that a page that has a window opener goes into the page cache.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS Page was not restored from page cache
+PASS page was not restored from the page cache.
+PASS page was saved to the page cache.
+PASS page was restored from the page cache.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index dbb3af1..2098581 100644 (file)
@@ -3,7 +3,7 @@
 <body>
 <script src="../../resources/js-test-pre.js"></script>
 <script>
-description('Tests that a page that has a window opener does not go into the page cache.');
+description("Tests that a page that has a window opener goes into the page cache.");
 window.jsTestIsAsync = true;
 
 if (window.testRunner) {
@@ -11,7 +11,7 @@ if (window.testRunner) {
     testRunner.setCanOpenWindows();
 }
 
-window.addEventListener('load', function() {
+window.addEventListener("load", function() {
     newWindow = open("resources/page-cache-window-with-opener.html", "one");
     otherWindowDocument = newWindow.document;
 }, false);
index 652779a..4b5dc71 100644 (file)
@@ -7,33 +7,35 @@ if (window.testRunner)
     testRunner.overridePreference("WebKitUsesPageCachePreferenceKey", 1);
 
 window.addEventListener("pageshow", function(event) {
-    debug("pageshow - " + (event.persisted ? "" : "not ") + "from cache");
-
-    if (!window.sessionStorage.page_cache_window_opener_test_started)
+    if (!window.sessionStorage.didStartPageCacheWindowWithOpenerTest) {
+        if (event.persisted)
+            window.opener.testFailed("page was restored from the page cache.");
+        else
+            window.opener.testPassed("page was not restored from the page cache.");
         return;
+    }
 
-    delete window.sessionStorage.page_cache_window_opener_test_started;
+    delete window.sessionStorage.didStartPageCacheWindowWithOpenerTest;
 
     if (event.persisted)
-        window.opener.testFailed("Page did enter and was restored from the page cache");
+        window.opener.testPassed("page was restored from the page cache.");
     else
-        window.opener.testPassed("Page was not restored from page cache");
+        window.opener.testFailed("page was not restored from the page cache.");
 
     window.opener.finishJSTest();
 }, false);
 
 window.addEventListener("pagehide", function(event) {
-    debug("pagehide - " + (event.persisted ? "" : "not ") + "entering cache");
-    if (event.persisted) {
-        window.opener.testFailed("Page entered the page cache.");
-        window.opener.finishJSTest();
-    }
+    if (event.persisted)
+        window.opener.testPassed("page was saved to the page cache.");
+    else
+        window.opener.testFailed("page was not saved to the page cache.");
 }, false);
 
 window.addEventListener('load', function() {
     setTimeout(function() {
         // Force a back navigation back to this page.
-        window.sessionStorage.page_cache_window_opener_test_started = true;
+        window.sessionStorage.didStartPageCacheWindowWithOpenerTest = true;
         window.location.href = "page-cache-helper.html";
     }, 0);
 }, false);
diff --git a/LayoutTests/http/tests/security/xss-DENIED-click-and-form-submission-from-inactive-domwindow-expected.txt b/LayoutTests/http/tests/security/xss-DENIED-click-and-form-submission-from-inactive-domwindow-expected.txt
new file mode 100644 (file)
index 0000000..84f976e
--- /dev/null
@@ -0,0 +1 @@
+This page opens an about:blank window and navigates it to a victim page such that the about:blank page is put into the page cache. Once the victim page is loaded it tries to navigate to a javascript URL. Ensure that popup-blocking is disabled when running this test by hand. This test PASSED if no JavaScript alert dialogs are shown. Otherwise, it FAILED.
diff --git a/LayoutTests/http/tests/security/xss-DENIED-click-and-form-submission-from-inactive-domwindow.html b/LayoutTests/http/tests/security/xss-DENIED-click-and-form-submission-from-inactive-domwindow.html
new file mode 100644 (file)
index 0000000..037430a
--- /dev/null
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.setCanOpenWindows();
+    testRunner.setPopupBlockingEnabled(false);
+    testRunner.setCloseRemainingWindowsWhenComplete(true);
+    testRunner.overridePreference("WebKitUsesPageCachePreferenceKey", 1);
+    testRunner.waitUntilDone();
+}
+</script>
+</head>
+<body>
+<p>This page opens an about:blank window and navigates it to a victim page such that the about:blank page
+is put into the page cache. Once the victim page is loaded it tries to navigate to a <code>javascript</code>
+URL. Ensure that popup-blocking is disabled when running this test by hand. This test PASSED if no JavaScript
+alert dialogs are shown. Otherwise, it FAILED.</p>
+<script>
+function testClickAnchorElement(aDocument)
+{
+    var link = aDocument.createElement("a");
+    link.href = "javascript:alert('FAIL clicked HTML anchor element.')";
+    link.click();
+}
+
+function testClickLinkElement(aDocument)
+{
+    var link = aDocument.createElement("link");
+    link.href = "javascript:alert('FAIL clicked HTML link element.')";
+    link.click();
+}
+
+function testSubmitForm(aDocument)
+{
+    var form = aDocument.createElement("form");
+    form.action = "javascript:alert('FAIL submitted form.')";
+    form.submit();
+}
+
+function createClickEvent(aDocument)
+{
+    var mouseEventInitDict = {
+        bubbles: true,
+        cancelable: false,
+        view: aDocument.defaultView,
+        detail: 1,
+        screenX: 10,
+        screenY: 10,
+        clientX: 10,
+        clientY: 10,
+        ctrlKey: false,
+        shiftKey: false,
+        altKey: false,
+        metaKey: false,
+        button: 1,
+        relatedTarget: null
+    };
+    return new MouseEvent("click", mouseEventInitDict)
+}
+
+function testClickSVGAnchorElement(aDocument)
+{
+    var link = aDocument.createElementNS("http://www.w3.org/2000/svg", "svg:a");
+    link.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", "javascript:alert('FAIL clicked SVG anchor element.')");
+    link.dispatchEvent(createClickEvent(aDocument));
+}
+
+function testClickMathElement(aDocument)
+{
+    var link = aDocument.createElementNS("http://www.w3.org/1998/Math/MathML", "math");
+    link.setAttribute("href", "javascript:alert('FAIL clicked Math element.')");
+    link.dispatchEvent(createClickEvent(aDocument));
+}
+
+function runTests()
+{
+    var childWindow = window.open("about:blank", "one");
+    var childDocument = childWindow.document;
+
+    var link = childDocument.createElement("a");
+    link.href = "http://localhost:8000/security/resources/innocent-victim.html";
+    link.click(); // Navigate window; moves about:blank into the page cache.
+
+    function checkDidNavigate()
+    {
+        try {
+            childWindow.location.href.toString;
+        } catch (e) {
+            // Victim page did load.
+            clearInterval(intervalID);
+
+            testClickAnchorElement(childDocument);
+            testClickLinkElement(childDocument);
+            testClickSVGAnchorElement(childDocument);
+            testClickMathElement(childDocument);
+            testSubmitForm(childDocument);
+
+            if (window.testRunner)
+                testRunner.notifyDone();
+        }
+    }
+    var intervalID = window.setInterval(checkDidNavigate, 0);
+}
+runTests();
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window-expected.txt b/LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window-expected.txt
new file mode 100644 (file)
index 0000000..41af320
--- /dev/null
@@ -0,0 +1,3 @@
+Check the child window. The test FAILED if you see a JavaScript alert() dialog. Otherwise, it PASSED.
+
+
diff --git a/LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window.html b/LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window.html
new file mode 100644 (file)
index 0000000..8627b65
--- /dev/null
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+<body>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.setCanOpenWindows();
+    testRunner.setPopupBlockingEnabled(false);
+    testRunner.setCloseRemainingWindowsWhenComplete(true);
+    testRunner.overridePreference("WebKitUsesPageCachePreferenceKey", 1);
+    testRunner.waitUntilDone();
+}
+
+var intervalId;
+
+function checkDidLoadVictim()
+{
+    try {
+        _openedWindowDocument.location.href
+    } catch (e) {
+        // Victim loaded; |_openedWindowDocument| is an inactive document.
+        window.clearInterval(intervalId);
+
+        // Run code in victim.
+        var scriptToRunInVictim = _openedWindowDocument.createElement("script");
+        scriptToRunInVictim.textContent = "alert('XSS')";
+        _openedWindowDocument.body.appendChild(scriptToRunInVictim);
+        if (window.testRunner)
+            window.setTimeout(function () { window.testRunner.notifyDone(); }, 500);
+    }
+}
+
+function attack()
+{
+    var iframe = document.querySelector("iframe");
+    iframe.contentWindow.openVictimInNewWindow();
+
+    document.body.removeChild(iframe); // Clear _openedWindow.opener.
+    document.getElementById("description").textContent = 'Check the child window. The test FAILED if you see a JavaScript alert() dialog. Otherwise, it PASSED.';
+
+    intervalId = window.setInterval(checkDidLoadVictim, 100);
+}
+</script>
+</head>
+<p id="description">This tests that a &lt;script&gt; added to an inactive document window does not execute. The test FAILED if you see JavaScript alert() dialog. Otherwise, it PASSED. Popup blocking must be disabled to run this test by hand.</p>
+<iframe onload="attack()" srcdoc='
+<body>
+<script>
+function openVictimInNewWindow()
+{
+    var childWindow = window.open("about:blank");
+    var childWindowDocument = childWindow.document;
+    window.parent._openedWindow = childWindow;
+    window.parent._openedWindowDocument = childWindowDocument;
+
+    var link = childWindowDocument.createElement("a");
+    link.href = "http://localhost:8000/security/resources/innocent-victim.html";
+    link.click(); // Navigate to victim
+}
+</script>
+</body>
+'></iframe>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window2-expected.txt b/LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window2-expected.txt
new file mode 100644 (file)
index 0000000..4cc180b
--- /dev/null
@@ -0,0 +1 @@
+This page doesn't do anything special.
diff --git a/LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window2.html b/LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window2.html
new file mode 100644 (file)
index 0000000..2be35a0
--- /dev/null
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.setCanOpenWindows();
+    testRunner.setPopupBlockingEnabled(false);
+    testRunner.setCloseRemainingWindowsWhenComplete(true);
+    testRunner.overridePreference("WebKitUsesPageCachePreferenceKey", 1);
+    testRunner.waitUntilDone();
+}
+</script>
+</head>
+<body>
+<p id="description">This tests that a &lt;script&gt; added to an inactive document window does not execute. The test FAILED if you see &quot;XSS&quot; on this page. Popup blocking must be disabled to run this test by hand.</p>
+<script>
+// Idea: Open a new window and have it navigate its opener to the victim site while holding a reference to the opener document.
+var openerDocument;
+var intervalId;
+if (document.location.search.indexOf("?actually-attack") !== -1) {
+    document.getElementById("description").textContent = 'Check the original window. The test FAILED if you see "XSS" on the page. Otherwise, it PASSED.';
+
+    // Case: New window
+    openerDocument = window.opener.document;
+
+    // Navigate same frame to victim.
+    var link = openerDocument.createElement("a");
+    link.target = "_self";
+    link.href = "http://localhost:8000/security/resources/innocent-victim.html";
+    link.click();
+
+    intervalId = window.setInterval(checkDidLoadVictim, 100);
+} else {
+    // Case: Initial load
+    var link = document.createElement("a");
+    link.target = "_blank";
+    link.href = "?actually-attack";
+    link.click(); // Open a new window.
+}
+
+function checkDidLoadVictim()
+{
+    try {
+        openerDocument.location.href
+    } catch (e) {
+        // Victim loaded.
+        window.clearInterval(intervalId);
+
+        // Run code in victim.
+        var scriptToRunInVictim = openerDocument.createElement("script");
+        scriptToRunInVictim.textContent = "document.writeln('XSS')";
+        openerDocument.body.appendChild(scriptToRunInVictim);
+        if (window.testRunner)
+            window.setTimeout(function () { window.testRunner.notifyDone(); }, 500);
+    }
+}
+</script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window3-expected.txt b/LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window3-expected.txt
new file mode 100644 (file)
index 0000000..4cc180b
--- /dev/null
@@ -0,0 +1 @@
+This page doesn't do anything special.
diff --git a/LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window3.html b/LayoutTests/http/tests/security/xss-DENIED-script-inject-into-inactive-window3.html
new file mode 100644 (file)
index 0000000..39a8271
--- /dev/null
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.setCanOpenWindows();
+    testRunner.setPopupBlockingEnabled(false);
+    testRunner.setCloseRemainingWindowsWhenComplete(true);
+    testRunner.overridePreference("WebKitUsesPageCachePreferenceKey", 1);
+    testRunner.waitUntilDone();
+}
+</script>
+</head>
+<body>
+<p id="description">This tests that a &lt;script&gt; added to an inactive document window does not execute. The test FAILED if you see &quot;XSS&quot; on this page. Popup blocking must be disabled to run this test by hand.</p>
+<script>
+var firstOpenerWindow;
+var firstOpenerDocument;
+var secondOpenerDocument;
+var intervalId;
+
+var ACTUALLY_ACTUALLY_ATTACK_URL = "?actually-actually-attack";
+
+if (!window.location.search) {
+    // Initial load
+    window.open('?actually-attack');
+} else if (window.location.search.indexOf("?actually-attack") !== -1) {
+    // New window
+    firstOpenerWindow = window.opener;
+    firstOpenerDocument = firstOpenerWindow.document;
+
+    // Reload ourself
+    firstOpenerWindow.location.href = ACTUALLY_ACTUALLY_ATTACK_URL;
+
+    function checkDidLoad()
+    {
+        if (window.opener.location.search.indexOf(ACTUALLY_ACTUALLY_ATTACK_URL) == -1)
+            return;
+
+        // Loaded frame.
+        window.clearInterval(intervalId);
+
+        // Update descriptive text
+        document.getElementById("description").textContent = 'Check the original window. The test FAILED if you see "XSS" on the page. Otherwise, it PASSED.';
+
+        // window.opener.document is in the page cache, has no opener, and has never called
+        // window.open().
+        secondOpenerDocument = window.opener.document;
+
+        // Navigate same frame to victim.
+        var link = secondOpenerDocument.createElement("a");
+        link.href = "http://localhost:8000/security/resources/innocent-victim.html";
+        link.click();
+
+        intervalId = window.setInterval(checkDidLoadVictim, 100);
+    }
+    intervalId = window.setInterval(checkDidLoad, 100);
+}
+
+function checkDidLoadVictim()
+{
+    try {
+        secondOpenerDocument.location.href
+    } catch (e) {
+        // Victim loaded.
+        window.clearInterval(intervalId);
+
+        // Run code in victim.
+        var scriptToRunInVictim = secondOpenerDocument.createElement("script");
+        scriptToRunInVictim.textContent = "document.writeln('XSS')";
+        secondOpenerDocument.body.appendChild(scriptToRunInVictim);
+        if (window.testRunner)
+            window.setTimeout(function () { window.testRunner.notifyDone(); }, 500);
+    }
+}
+</script>
+</body>
+</html>