tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit f37b7bd1f23ffcf5a50bb18efb771d64d9df679d
parent 9212b47900ea7a1a701a832ece3e7ca0ce88da05
Author: Dominic Farolino <dom@chromium.org>
Date:   Mon,  8 Dec 2025 12:29:24 +0000

Bug 2004566 [wpt PR 56537] - Blink: Add lifecycle regression test for about:srcdoc iframes, a=testonly

Automatic update from web-platform-tests
Blink: Add lifecycle regression test for about:srcdoc iframes

See https://crbug.com/40937729 for the exploration that led to this
regression test.

Specifically see this comment thread [1] for discussion about the fact
that Mojo callbacks are not supposed to be able to run on HeapMojoRemote
after the associated Document/execution context becomes inactive. This
bug is caused by the fact that we reuse the execution context/
LocalDOMWindow of the initial empty document for subsequent navigations.
This allows for IPCs that target the document to be scheduled, by
virtue of its now-reused Window still being alive. This causes mojo
callbacks to run on the detached document, which have the reasonable
expectation that the document is attached. See [2] for a full diagnosis
and write-up.

[1]:
https://chromium-review.googlesource.com/c/chromium/src/+/4908152/comment/f5e0fbe8_2b1d3aaf/
[2]:
https://docs.google.com/document/u/1/d/1wmvqbNZGtp1QluxW1sntZd_W1o1XsHlQd-ywNqwyu-Y/edit?tab=t.0

R=dcheng

Bug: 40937729
Change-Id: I2cf4766892b7f6ea2550ecd7dac8ab5548158cb6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6148512
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Commit-Queue: Dominic Farolino <dom@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1554835}

--

wpt-commits: 25c0ff0e51a009a17a8b8ab4bffb322fdd6f8ba3
wpt-pr: 56537

Diffstat:
Atesting/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc-lifecycle-crash-crbug-1472607.https.html | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+), 0 deletions(-)

diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc-lifecycle-crash-crbug-1472607.https.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc-lifecycle-crash-crbug-1472607.https.html @@ -0,0 +1,60 @@ +<!doctype html> +<meta charset=utf-8> +<title>Chromium regression test for a Promise-related lifecycle crash in about:srcdoc iframes</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=/resources/testdriver.js></script> +<script src=/resources/testdriver-vendor.js></script> + +<body> +<script> +promise_test(async t => { + await test_driver.set_permission({ name: 'storage-access' }, 'granted'); +}, "common setup"); + +// In Chromium, this test would previously crash the renderer process. See +// documentation below. +promise_test(async t => { + const new_iframe = document.createElement('iframe'); + // Having this come before `append()` keeps the iframe on the "initial empty + // Document" (see [1]) while the about:srcdoc navigation loads asynchronously. + // + // [1]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/dom/document.h;l=2574;drc=69640c83afac0bde8247ceac5fe259f13a5265a7. + new_iframe.srcdoc = 'stuff'; + document.body.append(new_iframe); + + // It is not clear why this is needed, but it appears to reliably slow the + // *next*/last `requestStorageAccess()` call down such that the IPC coming + // from the browser to resolve its promise comes after the about:srcdoc + // navigation commits (and after the initial empty document is detached). + // + // If for some reason the about:srcdoc navigation commits after this first + // call to `requestStorageAccess()` resolves, then the next assert will fail. + await new_iframe.contentDocument.requestStorageAccess(); + + assert_equals(new_iframe.contentDocument.URL, "about:blank"); + // Now we're set up to trigger the Chromium bug that this test is exercising. + // The series of events that we're relying on, and trying to trigger, is: + // 1. Call `requestStorageAccess()` from the initial empty about:blank + // Document, before it gets replaced by a same-origin navigation (to + // about:srcdoc in this case). + // 2. While waiting for the Promise to resolve, the Document gets replaced by + // the navigation. This new Document shares a Window with the old + // now-detached initial one (per spec) since it is the first same-origin + // navigation after the initial empty Document, and those "special" in + // that they are done with replacement, and share the same Window. + // 3. After the navigation commits and the initial empty about:blank Document + // is replaced, the `requestStorageAccess()` Promise finally resolves. The + // callback runs in the renderer, since the lifetime of the callback is + // tied to the Window. + // + // In Chromium, this previously put us in a weird state where the callback ran + // against a live Window, but was associated with a detached Document, which + // bucked some assumptions and crashed the renderer; see + // https://crbug.com/40937729. The root cause of this is discussed here: + // https://docs.google.com/document/d/1wmvqbNZGtp1QluxW1sntZd_W1o1XsHlQd-ywNqwyu-Y/edit. + return new_iframe.contentDocument.requestStorageAccess(); +}, "requestStorageAccess() from about:blank Document before it gets " + + "replaced with a srcdoc resource does not crash the host process"); +</script> +</body>