tor-browser

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

commit 590a4914146708a1e32e4ff6543e5fb6ba859f17
parent 205afdc4b93a1a27f9df971b97651fa0918b1348
Author: Lingqi Chi <lingqi@chromium.org>
Date:   Fri, 31 Oct 2025 08:54:33 +0000

Bug 1996771 [wpt PR 55700] - [pus] add first wpt_test and test utility, a=testonly

Automatic update from web-platform-tests
[pus] add first wpt_test and test utility

Since prerender-until-script pages cannot execute scripts, the existing
test utilities cannot help test this case. More specifically,
prerender-until-script pages cannot tell the initiator pages that they
are ready for activation.

This CL introduces a server-side handler (pus-handler.py) to coordinate
the test.

Workflows:
1. The initiator page (pus-initiator-page-template.html)
triggers the prerender and simultaneously fetches a signal_url. This
fetch is designed to hang on the server.

2. The prerendered page (pus-page-template.html) is allowed to fetch an
async script (which also hits the signal_url, but with
isprerendering=True).

3. When the server receives the request from the prerendered page, it
puts a token in its stash, which in turn unblocks the initiator's
hanging fetch.

4. The .then() block of the initiator's fetch then runs, navigating to
the prerendered page and causing activation.

5. The (previously blocked) inline script on the prerendered page then
executes, verifying that document.prerendering is false and sending the
result back to the main test page.

Bug: 428500219
Change-Id: I5e1098a6adbf7abfd02f692eb32e93c9093fd501
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7082540
Reviewed-by: Hiroki Nakagawa <nhiroki@chromium.org>
Commit-Queue: Lingqi Chi <lingqi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1536464}

--

wpt-commits: 9061388bb460b15a46cdca3030eef6d301151fe3
wpt-pr: 55700

Diffstat:
Atesting/web-platform/tests/speculation-rules/prerender-until-script/prerender-until-script.https.html | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atesting/web-platform/tests/speculation-rules/prerender-until-script/resources/pus-handler.py | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atesting/web-platform/tests/speculation-rules/prerender-until-script/resources/pus-initiator-page-template.html | 42++++++++++++++++++++++++++++++++++++++++++
Atesting/web-platform/tests/speculation-rules/prerender-until-script/resources/pus-page-template.html | 20++++++++++++++++++++
4 files changed, 196 insertions(+), 0 deletions(-)

diff --git a/testing/web-platform/tests/speculation-rules/prerender-until-script/prerender-until-script.https.html b/testing/web-platform/tests/speculation-rules/prerender-until-script/prerender-until-script.https.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<title>Prerender and activate prerender-until-script action page correctly</title> +<meta name="timeout" content="long"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/utils.js"></script> +<script src="/speculation-rules/prerender/resources/utils.js"></script> +<script src="../resources/utils.js"></script> +<body> +<script> +setup(() => assertSpeculationRulesIsSupported()); + +promise_test(async t => { + /* + Workflows: + 1. The initiator page (pus-initiator-page-template.html) + triggers the prerender and simultaneously fetches a signal_url. This + fetch is designed to hang on the server. + + 2. The prerendered page (pus-page-template.html) is allowed to fetch an + async script (which also hits the signal_url, but with + isprerendering=True). + + 3. When the server receives the request from the prerendered page, it + puts a token in its stash, which in turn unblocks the initiator's + hanging fetch. + + 4. The .then() block of the initiator's fetch then runs, navigating to + the prerendered page and causing activation. + + 5. The (previously blocked) inline script on the prerendered page then + executes, verifying that document.prerendering is false and sending the + result back to the main test page. + */ + + const uid = token(); + const bc = new PrerenderChannel('test-channel', uid); + + const gotMessage = new Promise(resolve => { + bc.addEventListener('message', e => { + resolve(e.data); + }, { + once: true + }); + }); + const url = `resources/pus-handler.py?type=main&uid=${uid}`; + window.open(url, '_blank', 'noopener'); + + const result = await gotMessage; + assert_equals(result, 'prerendering state when executing JS: false'); + + bc.close(); +}, `when prerender-until-script action prerenders a page, the script on that page should always be executed after activation.`); +</script> diff --git a/testing/web-platform/tests/speculation-rules/prerender-until-script/resources/pus-handler.py b/testing/web-platform/tests/speculation-rules/prerender-until-script/resources/pus-handler.py @@ -0,0 +1,80 @@ +""" +This script handles requests for the "main" and "signal" resources. +The "main" resource serves an HTML page that either initiates or acts as the +target for a prerendering speculation rule. The "signal" resource is used to +coordinate between the initiator and the prerendered page, ensuring the +prerendering is complete before proceeding. It uses query parameters to +determine its behavior and coordinate the process. +""" + +import os +import copy +import time +from urllib.parse import parse_qs + +def encode_query(query_dict): + encoded_pairs = [f"{key}={value[0]}" if value[0] !="" else key for key, value in query_dict.items()] + return '&'.join(encoded_pairs) + +def calculate_signal_path(url_parts): + url_parts = copy.deepcopy(url_parts) + query_dict = parse_qs(url_parts.query, keep_blank_values=True) + query_dict['type'] = ['signal'] + return url_parts._replace(query=encode_query(query_dict)).geturl() + +def calculate_prerendering_path(url_parts): + query_dict = parse_qs(url_parts.query) + query_dict['isprerendering'] = [True] + return url_parts._replace(query=encode_query(query_dict)).geturl() + +def main_resource_handler(request, response): + is_prerendering = b"isprerendering" in request.GET + uid = request.GET.get(b"uid") + signal_path = calculate_signal_path(request.url_parts) + content = '' + if not is_prerendering: + # First request, which is the initiator + template_path = os.path.join(os.path.dirname(__file__), "pus-initiator-page-template.html") + prerendering_path = calculate_prerendering_path(request.url_parts) + with open(template_path, "r") as f: + content = f.read().replace("{{signal_url}}", signal_path).replace( + "{{prerendering_url}}", prerendering_path) + else: + template_path = os.path.join(os.path.dirname( + __file__), "pus-page-template.html") + with open(template_path, "r") as f: + content = f.read().replace("{{signal_path}}", signal_path) + response.headers.set(b"Content-Type", b"text/html") + response.status = 200 + response.content = content.encode('utf-8') + +def signal_handler(request, response): + is_prerendering = b"isprerendering" in request.GET + uid = request.GET.get(b"uid") + if is_prerendering: + with request.server.stash.lock: + request.server.stash.put(uid, "ok") + else: + # This will hang until the gate is released + while True: + with request.server.stash.lock: + if request.server.stash.take(uid) is None: + time.sleep(0.1) + else: + break + response.headers.set(b"Content-Type", b"text/javascript") + response.status = 200 + response.content = "console.error('orz')".encode('utf-8') + +def main(request, response): + resource_router = { + b"main":main_resource_handler, + b"signal": signal_handler + } + resource_type = request.GET.get(b"type") + resource_handler = resource_router.get(resource_type) + if resource_handler is not None: + return resource_handler(request, response) + response.status = 400 # Bad Request + response.content = b"Invalid resource type" + return diff --git a/testing/web-platform/tests/speculation-rules/prerender-until-script/resources/pus-initiator-page-template.html b/testing/web-platform/tests/speculation-rules/prerender-until-script/resources/pus-initiator-page-template.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<html> +<head> +<title>Initiator page</title> +</head> +<body> +<script src="/speculation-rules/prerender/resources/utils.js"></script> +<script type="speculationrules"> +{ + "prerender_until_script": [ + { + "urls": ["{{prerendering_url}}"] + } + ] +} +</script> +<script> + const params = new URLSearchParams(location.search); + const uid = params.get('uid'); + + const signal_url = `{{signal_url}}`; + const prerendering_url = `{{prerendering_url}}`; + + fetch(signal_url).then(response => { + // The fetch promise will be resolved after the prerendering page is ready + // for activation. The server will hold the response to signal_url until it + // receives a signal from the prerendering page to confirm it is ready for + // activation. + if (response.status == 200){ + window.location = prerendering_url.toString(); + } else { + const testChannel = new PrerenderChannel('test-channel'); + testChannel.postMessage( + `Failed to navigate the prerendered page: ${e.toString()}`); + testChannel.close(); + window.close(); + } + }); +</script> +42 +</body> +</html> diff --git a/testing/web-platform/tests/speculation-rules/prerender-until-script/resources/pus-page-template.html b/testing/web-platform/tests/speculation-rules/prerender-until-script/resources/pus-page-template.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html> +<head> +<title>prerender-until-script</title> +</head> +<body> +<h1>This is the prerendering request.</h1> +<div> +<script async src="{{signal_path}}"></script> +</div> +<script src="/speculation-rules/prerender/resources/utils.js"></script> +<script> + const testChannel = new PrerenderChannel('test-channel'); + testChannel.postMessage( + `prerendering state when executing JS: ${document.prerendering}`); + testChannel.close(); + window.close(); +</script> +</body> +</html>