DocumentRules: Add WPTs
Bug: 1371522
Change-Id: Iffbef05ee295b73b7ab6eff1078684df64dba2e7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4048640
Reviewed-by: Jeremy Roman <jbroman@chromium.org>
Reviewed-by: Stephen Chenney <schenney@chromium.org>
Commit-Queue: Adithya Srinivasan <adithyas@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1076377}
diff --git a/speculation-rules/prefetch/document-rules.https.html b/speculation-rules/prefetch/document-rules.https.html
new file mode 100644
index 0000000..a5030f6
--- /dev/null
+++ b/speculation-rules/prefetch/document-rules.https.html
@@ -0,0 +1,136 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="resources/utils.sub.js"></script>
+<script src="/common/subset-tests-by-key.js"></script>
+
+<meta name="variant" content="?include=defaultPredicate">
+<meta name="variant" content="?include=hrefMatches">
+<meta name="variant" content="?include=and">
+<meta name="variant" content="?include=or">
+<meta name="variant" content="?include=not">
+<meta name="variant" content="?include=invalidPredicate">
+<meta name="variant" content="?include=linkInShadowTree">
+
+<body>
+<script>
+ subsetTestByKey('defaultPredicate', promise_test, async t => {
+ assert_implements(HTMLScriptElement.supports('speculationrules'),
+ 'Speculation Rules not supported');
+
+ const url = getPrefetchUrl();
+ addLink(url);
+ insertDocumentRule();
+ await new Promise(resolve => t.step_timeout(resolve, 2000));
+
+ assert_equals(await isUrlPrefetched(url), 1);
+ }, 'test document rule with no predicate');
+
+ subsetTestByKey('hrefMatches', promise_test, async t => {
+ assert_implements(HTMLScriptElement.supports('speculationrules'),
+ 'Speculation Rules not supported');
+
+ insertDocumentRule({ href_matches: '*\\?uuid=*&foo=bar' });
+
+ const url_1 = getPrefetchUrl({foo: 'bar'});
+ addLink(url_1);
+ const url_2 = getPrefetchUrl({foo: 'buzz'});
+ addLink(url_2)
+ await new Promise(resolve => t.step_timeout(resolve, 2000));
+
+ assert_equals(await isUrlPrefetched(url_1), 1);
+ assert_equals(await isUrlPrefetched(url_2), 0);
+ }, 'test href_matches document rule');
+
+ subsetTestByKey('and', promise_test, async t => {
+ assert_implements(HTMLScriptElement.supports('speculationrules'),
+ 'Speculation Rules not supported');
+
+ insertDocumentRule({
+ 'and': [
+ { href_matches: '*\\?*foo=bar*' },
+ { href_matches: '*\\?*fizz=buzz*' }]
+ });
+
+ const url_1 = getPrefetchUrl({foo: 'bar'});
+ const url_2 = getPrefetchUrl({fizz: 'buzz'});
+ const url_3 = getPrefetchUrl({foo: 'bar', fizz: 'buzz'});
+ [url_1, url_2, url_3].forEach(url => addLink(url));
+ await new Promise(resolve => t.step_timeout(resolve, 2000));
+
+ assert_equals(await isUrlPrefetched(url_1), 0);
+ assert_equals(await isUrlPrefetched(url_2), 0);
+ assert_equals(await isUrlPrefetched(url_3), 1);
+ }, 'test document rule with conjunction predicate');
+
+ subsetTestByKey('or', promise_test, async t => {
+ assert_implements(HTMLScriptElement.supports('speculationrules'),
+ 'Speculation Rules not supported');
+
+ insertDocumentRule({
+ 'or': [
+ { href_matches: '*\\?*foo=bar*' },
+ { href_matches: '*\\?*fizz=buzz*' }]
+ });
+
+ const url_1 = getPrefetchUrl({ foo: 'buzz' });
+ const url_2 = getPrefetchUrl({ fizz: 'buzz' });
+ const url_3 = getPrefetchUrl({ foo: 'bar'});
+ [url_1, url_2, url_3].forEach(url => addLink(url));
+ await new Promise(resolve => t.step_timeout(resolve, 2000));
+
+ assert_equals(await isUrlPrefetched(url_1), 0);
+ assert_equals(await isUrlPrefetched(url_2), 1);
+ assert_equals(await isUrlPrefetched(url_3), 1);
+ }, 'test document rule with disjunction predicate');
+
+ subsetTestByKey('not', promise_test, async t => {
+ assert_implements(HTMLScriptElement.supports('speculationrules'),
+ "Speculation Rules not supported");
+
+ insertDocumentRule({ not: { href_matches: '*\\?uuid=*&foo=bar' } });
+
+ const url_1 = getPrefetchUrl({foo: 'bar'});
+ addLink(url_1);
+ const url_2 = getPrefetchUrl({foo: 'buzz'});
+ addLink(url_2)
+ await new Promise(resolve => t.step_timeout(resolve, 2000));
+
+ assert_equals(await isUrlPrefetched(url_1), 0);
+ assert_equals(await isUrlPrefetched(url_2), 1);
+ }, 'test document rule with negation predicate');
+
+ subsetTestByKey('invalidPredicate', promise_test, async t => {
+ assert_implements(HTMLScriptElement.supports('speculationrules'),
+ 'Speculation Rules not supported');
+
+ const url = getPrefetchUrl();
+ addLink(url);
+ insertDocumentRule({invalid: 'predicate'});
+ await new Promise(resolve => t.step_timeout(resolve, 2000));
+
+ assert_equals(await isUrlPrefetched(url), 0);
+ }, 'invalid predicate should not throw error or start prefetch');
+
+ subsetTestByKey('linkInShadowTree', promise_test, async t => {
+ assert_implements(HTMLScriptElement.supports('speculationrules'),
+ 'Speculation Rules not supported');
+
+ insertDocumentRule();
+
+ // Create shadow root.
+ const shadowHost = document.createElement('div');
+ document.body.appendChild(shadowHost);
+ const shadowRoot = shadowHost.attachShadow({mode: 'open'});
+
+ const url = getPrefetchUrl();
+ addLink(url, shadowRoot);
+ await new Promise(resolve => t.step_timeout(resolve, 2000));
+
+ assert_equals(await isUrlPrefetched(url), 1);
+ }, 'test that matching link in a shadow tree is prefetched');
+
+</script>
+</body>
diff --git a/speculation-rules/prefetch/referrer-policy-from-rules.https.html b/speculation-rules/prefetch/referrer-policy-from-rules.https.html
index 01e32ba..ce3db0f 100644
--- a/speculation-rules/prefetch/referrer-policy-from-rules.https.html
+++ b/speculation-rules/prefetch/referrer-policy-from-rules.https.html
@@ -6,7 +6,8 @@
<meta name="variant" content="?2-2">
<meta name="variant" content="?3-3">
<meta name="variant" content="?4-4">
-<meta name="variant" content="?5-last">
+<meta name="variant" content="?5-5">
+<meta name="variant" content="?6-last">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
@@ -38,6 +39,25 @@
assert_implements(HTMLScriptElement.supports('speculationrules'), "Speculation Rules not supported");
const agent = await spawnWindow(t);
+ const next_url = agent.getExecutorURL({ page: 2 });
+ await agent.execute_script((url) => {
+ const a = addLink(url);
+ a.referrerPolicy = 'no-referrer';
+ insertDocumentRule(undefined, { referrer_policy: 'strict-origin' });
+ }, [next_url]);
+ await new Promise(resolve => t.step_timeout(resolve, 2000));
+ await agent.navigate(next_url);
+
+ const headers = await agent.getRequestHeaders();
+ assert_prefetched(headers, 'must be prefetched');
+ const expected_referrer = next_url.origin + '/';
+ assert_equals(headers.referer, expected_referrer, 'must send the origin as the referrer');
+}, 'with "strict-origin" referrer policy in rule set override "no-referrer" of link');
+
+subsetTest(promise_test, async t => {
+ assert_implements(HTMLScriptElement.supports('speculationrules'), "Speculation Rules not supported");
+
+ const agent = await spawnWindow(t);
await agent.setReferrerPolicy("unsafe-url");
const nextURL = agent.getExecutorURL({ page: 2 });
diff --git a/speculation-rules/prefetch/referrer-policy.https.html b/speculation-rules/prefetch/referrer-policy.https.html
index aaeae16..1987d2e 100644
--- a/speculation-rules/prefetch/referrer-policy.https.html
+++ b/speculation-rules/prefetch/referrer-policy.https.html
@@ -4,7 +4,8 @@
<!--Split test cases due to the use of timeouts in speculation rules test utilities.-->
<meta name="variant" content="?1-1">
<meta name="variant" content="?2-2">
-<meta name="variant" content="?3-last">
+<meta name="variant" content="?3-3">
+<meta name="variant" content="?4-last">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
@@ -63,4 +64,25 @@
assert_equals(headers.referer, '', "must send no referrer");
}, 'with "no-referrer" referrer policy');
+subsetTest(promise_test, async t => {
+ assert_implements(HTMLScriptElement.supports('speculationrules'), "Speculation Rules not supported");
+
+ const agent = await spawnWindow(t);
+ await agent.setReferrerPolicy("no-referrer");
+
+ const next_url = agent.getExecutorURL({ page: 2 });
+ await agent.execute_script((url) => {
+ const a = addLink(url);
+ a.referrerPolicy = 'strict-origin';
+ insertDocumentRule();
+ }, [next_url]);
+ await new Promise(resolve => t.step_timeout(resolve, 2000));
+ await agent.navigate(next_url);
+
+ const headers = await agent.getRequestHeaders();
+ const expected_referrer = next_url.origin + '/';
+ assert_prefetched(headers, 'must be prefetched');
+ assert_equals(headers.referer, expected_referrer);
+}, 'with "strict-origin" link referrer policy overriding "no-referrer" of referring page');
+
</script>
diff --git a/speculation-rules/prefetch/resources/utils.sub.js b/speculation-rules/prefetch/resources/utils.sub.js
index f532f35..b29dd6b 100644
--- a/speculation-rules/prefetch/resources/utils.sub.js
+++ b/speculation-rules/prefetch/resources/utils.sub.js
@@ -96,14 +96,17 @@
}
}
+// Produces a URL with a UUID which will record when it's prefetched.
+// |extra_params| can be specified to add extra search params to the generated
+// URL.
+function getPrefetchUrl(extra_params={}) {
+ let params = new URLSearchParams({ uuid: token(), ...extra_params });
+ return new URL(`prefetch.py?${params}`, SR_PREFETCH_UTILS_URL);
+}
+
// Produces n URLs with unique UUIDs which will record when they are prefetched.
function getPrefetchUrlList(n) {
- let urls = [];
- for (let i=0; i<n; i++) {
- let params = new URLSearchParams({uuid: token()});
- urls.push(new URL(`prefetch.py?${params}`, SR_PREFETCH_UTILS_URL));
- }
- return urls;
+ return Array.from({ length: n }, () => getPrefetchUrl());
}
function getRedirectUrl() {
@@ -132,6 +135,28 @@
document.head.appendChild(script);
}
+// Creates and appends <a href=|href|> to |insertion point|. If
+// |insertion_point| is not specified, document.body is used.
+function addLink(href, insertion_point=document.body) {
+ const a = document.createElement('a');
+ a.href = href;
+ insertion_point.appendChild(a);
+ return a;
+}
+
+// Inserts a prefetch document rule with |predicate|. |predicate| can be
+// undefined, in which case the default predicate will be used (i.e. all links
+// in document will match).
+function insertDocumentRule(predicate, extra_options={}) {
+ insertSpeculationRules({
+ prefetch: [{
+ source: 'document',
+ where: predicate,
+ ...extra_options
+ }]
+ });
+}
+
function assert_prefetched (requestHeaders, description) {
assert_in_array(requestHeaders.purpose, ["", "prefetch"], "The vendor-specific header Purpose, if present, must be 'prefetch'.");
assert_equals(requestHeaders.sec_purpose, "prefetch", description);