Skip to content

Commit aa36ac4

Browse files
committed
Implement speculation rules - same origin conservative prefetch
https://bugs.webkit.org/show_bug.cgi?id=295193 Reviewed by NOBODY (OOPS!). This implements an initial version of same-origin conservative prefetch speculation rules. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/document-rules.https.html: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/document-rules.https_include=and-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/document-rules.https_include=baseURLChangedByBaseElement-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/document-rules.https_include=baseURLChangedBySameDocumentNavigation-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/document-rules.https_include=defaultPredicate-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/document-rules.https_include=hrefMatches-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/document-rules.https_include=immediateMutation-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/document-rules.https_include=invalidPredicate-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/document-rules.https_include=linkHrefChanged-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/document-rules.https_include=linkInShadowTree-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/document-rules.https_include=linkToSelfFragment-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/document-rules.https_include=newRuleSetAdded-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/document-rules.https_include=not-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/document-rules.https_include=or-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/document-rules.https_include=selectorMatches-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/document-rules.https_include=selectorMatchesDisplayLocked-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/document-rules.https_include=selectorMatchesDisplayNone-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/document-rules.https_include=selectorMatchesScopingRoot-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/document-rules.https_include=unslottedLink-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/prefetch-single.https.html: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/prefetch-single.https_from_protocol=http&to_protocol=http-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/prefetch-single.https_from_protocol=http&to_protocol=https-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/prefetch-single.https_from_protocol=https&to_protocol=http-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/prefetch-single.https_from_protocol=https&to_protocol=https-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/resources/executor.sub.html: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/resources/executor.sub.html.headers: Added. * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/resources/prefetch.py: Added. (main): * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/prefetch/resources/utils.sub.js: Added. (PrefetchAgent): (PrefetchAgent.prototype.getExecutorURL): (PrefetchAgent.prototype.async forceSinglePrefetch): (PrefetchAgent.prototype.async forceSpeculationRules): (PrefetchAgent.prototype.async navigate): (PrefetchAgent.prototype.async getRequestHeaders): (PrefetchAgent.prototype.async getResponseCookies): (PrefetchAgent.prototype.async getRequestCookies): (PrefetchAgent.prototype.async getRequestCredentials): (PrefetchAgent.prototype.async setReferrerPolicy): (PrefetchAgent.prototype.async getDeliveryType): (getPrefetchUrl): * LayoutTests/imported/w3c/web-platform-tests/speculation-rules/resources/utils.js: Added. (globalThis.assertSpeculationRulesIsSupported): (globalThis.RemoteContextHelper.PreloadingRemoteContextWrapper.prototype.addPreload): (globalThis.RemoteContextHelper.PreloadingRemoteContextWrapper): * Source/JavaScriptCore/CMakeLists.txt: * Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj: * Source/JavaScriptCore/Sources.txt: * Source/JavaScriptCore/runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::JSGlobalObject): * Source/JavaScriptCore/runtime/JSGlobalObject.h: (JSC::JSGlobalObject::speculationRules const): (JSC::JSGlobalObject::speculationRules): * Source/JavaScriptCore/runtime/SpeculationRules.cpp: Added. (JSC::parseStringOrStringList): (JSC::parseDocumentPredicate): (JSC::parseSingleRule): (JSC::parseRules): (JSC::SpeculationRules::parseSpeculationRules): * Source/JavaScriptCore/runtime/SpeculationRules.h: Added. (JSC::SpeculationRules::DocumentPredicate::DocumentPredicate): (JSC::SpeculationRules::DocumentPredicate::value const): (JSC::SpeculationRules::create): (JSC::SpeculationRules::prefetchRules const): * Source/WebCore/Headers.cmake: * Source/WebCore/Sources.txt: * Source/WebCore/WebCore.xcodeproj/project.pbxproj: * Source/WebCore/bindings/js/ScriptController.cpp: (WebCore::ScriptController::registerSpeculationRules): * Source/WebCore/bindings/js/ScriptController.h: * Source/WebCore/dom/Document.cpp: (WebCore::Document::resolveStyle): (WebCore::Document::updateBaseURL): (WebCore::Document::considerSpeculationRules): (WebCore::Document::prefetch): * Source/WebCore/dom/Document.h: * Source/WebCore/dom/ScriptElement.cpp: (WebCore::ScriptElement::determineScriptType): (WebCore::ScriptElement::prepareScript): (WebCore::ScriptElement::registerSpeculationRules): * Source/WebCore/dom/ScriptElement.h: * Source/WebCore/dom/ScriptElementCachedScriptFetcher.h: (WebCore::ScriptElementCachedScriptFetcher::isSpeculationRules const): * Source/WebCore/dom/ScriptType.h: * Source/WebCore/dom/SpeculationRulesMatcher.cpp: Copied from Source/WebCore/dom/ScriptElementCachedScriptFetcher.h. (WebCore::matches): (WebCore::SpeculationRulesMatcher::hasMatchingRule): * Source/WebCore/dom/SpeculationRulesMatcher.h: Copied from Source/WebCore/dom/ScriptElementCachedScriptFetcher.h. * Source/WebCore/dom/TextDecoderStreamDecoder.cpp: * Source/WebCore/dom/TrustedTypePolicy.cpp: * Source/WebCore/html/HTMLAnchorElement.cpp: (WebCore::HTMLAnchorElement::defaultEventHandler): (WebCore::HTMLAnchorElement::attributeChanged): (WebCore::HTMLAnchorElement::insertedIntoAncestor): (WebCore::HTMLAnchorElement::setFullURL): (WebCore::HTMLAnchorElement::setShouldBePrefetched): (WebCore::HTMLAnchorElement::checkForSpeculationRules): * Source/WebCore/html/HTMLAnchorElement.h: * Source/WebCore/html/HTMLScriptElement.h: * Source/WebCore/loader/DocumentLoader.cpp: (WebCore::DocumentLoader::becomeMainResourceClient): (WebCore::DocumentLoader::setPrefetchedMainResource): * Source/WebCore/loader/DocumentLoader.h: * Source/WebCore/loader/DocumentPrefetcher.cpp: Copied from Source/WebCore/dom/ScriptType.h. (WebCore::DocumentPrefetcher::DocumentPrefetcher): (WebCore::DocumentPrefetcher::~DocumentPrefetcher): (WebCore::DocumentPrefetcher::prefetch): (WebCore::DocumentPrefetcher::matchPrefetchedDocument): (WebCore::DocumentPrefetcher::clear): * Source/WebCore/loader/DocumentPrefetcher.h: Copied from Source/WebCore/dom/ScriptType.h. (WebCore::DocumentPrefetcher::create): * Source/WebCore/loader/FrameLoader.cpp: (WebCore::FrameLoader::FrameLoader): (WebCore::FrameLoader::commitProvisionalLoad): (WebCore::FrameLoader::continueLoadAfterNavigationPolicy): (WebCore::FrameLoader::dispatchDidCommitLoad): (WebCore::FrameLoader::prefetch): * Source/WebCore/loader/FrameLoader.h: * Source/WebCore/loader/LinkLoader.cpp: * Source/WebCore/loader/cache/CachedResourceLoader.cpp: (WebCore::CachedResourceLoader::allowedByContentSecurityPolicy const): * Source/WebCore/platform/ReferrerPolicy.cpp: (WebCore::parseReferrerPolicy): * Source/WebCore/platform/ReferrerPolicy.h: * Source/WebCore/platform/network/HTTPHeaderNames.in: * Tools/Scripts/webkitpy/style/checkers/cpp.py: (_check_parameter_name_against_text):
1 parent 4eb0d4f commit aa36ac4

66 files changed

Lines changed: 1909 additions & 13 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
<!DOCTYPE html>
2+
<script src="/resources/testharness.js"></script>
3+
<script src="/resources/testharnessreport.js"></script>
4+
<script src="/common/utils.js"></script>
5+
<script src="/common/dispatcher/dispatcher.js"></script>
6+
<script src="../resources/utils.js"></script>
7+
<script src="resources/utils.sub.js"></script>
8+
<script src="/common/subset-tests-by-key.js"></script>
9+
10+
<meta name="variant" content="?include=defaultPredicate">
11+
<meta name="variant" content="?include=hrefMatches">
12+
<meta name="variant" content="?include=and">
13+
<meta name="variant" content="?include=or">
14+
<meta name="variant" content="?include=not">
15+
<meta name="variant" content="?include=invalidPredicate">
16+
<meta name="variant" content="?include=linkInShadowTree">
17+
<meta name="variant" content="?include=linkHrefChanged">
18+
<meta name="variant" content="?include=newRuleSetAdded">
19+
<meta name="variant" content="?include=selectorMatches">
20+
<meta name="variant" content="?include=selectorMatchesScopingRoot">
21+
<meta name="variant" content="?include=selectorMatchesDisplayNone">
22+
<meta name="variant" content="?include=selectorMatchesDisplayLocked">
23+
<meta name="variant" content="?include=unslottedLink">
24+
<meta name="variant" content="?include=immediateMutation">
25+
<meta name="variant" content="?include=baseURLChangedBySameDocumentNavigation">
26+
<meta name="variant" content="?include=baseURLChangedByBaseElement">
27+
<meta name="variant" content="?include=linkToSelfFragment">
28+
29+
<body>
30+
<script>
31+
setup(() => assertSpeculationRulesIsSupported());
32+
33+
subsetTestByKey('defaultPredicate', promise_test, async t => {
34+
const url = getPrefetchUrl();
35+
addLink(url);
36+
insertDocumentRule();
37+
await new Promise(resolve => t.step_timeout(resolve, 2000));
38+
39+
assert_equals(await isUrlPrefetched(url), 1);
40+
}, 'test document rule with no predicate');
41+
42+
subsetTestByKey('hrefMatches', promise_test, async t => {
43+
insertDocumentRule({ href_matches: '*\\?uuid=*&foo=bar' });
44+
45+
const url_1 = getPrefetchUrl({foo: 'bar'});
46+
addLink(url_1);
47+
const url_2 = getPrefetchUrl({foo: 'buzz'});
48+
addLink(url_2)
49+
await new Promise(resolve => t.step_timeout(resolve, 2000));
50+
51+
assert_equals(await isUrlPrefetched(url_1), 1);
52+
assert_equals(await isUrlPrefetched(url_2), 0);
53+
}, 'test href_matches document rule');
54+
55+
subsetTestByKey('and', promise_test, async t => {
56+
insertDocumentRule({
57+
'and': [
58+
{ href_matches: '*\\?*foo=bar*' },
59+
{ href_matches: '*\\?*fizz=buzz*' }]
60+
});
61+
62+
const url_1 = getPrefetchUrl({foo: 'bar'});
63+
const url_2 = getPrefetchUrl({fizz: 'buzz'});
64+
const url_3 = getPrefetchUrl({foo: 'bar', fizz: 'buzz'});
65+
[url_1, url_2, url_3].forEach(url => addLink(url));
66+
await new Promise(resolve => t.step_timeout(resolve, 2000));
67+
68+
assert_equals(await isUrlPrefetched(url_1), 0);
69+
assert_equals(await isUrlPrefetched(url_2), 0);
70+
assert_equals(await isUrlPrefetched(url_3), 1);
71+
}, 'test document rule with conjunction predicate');
72+
73+
subsetTestByKey('or', promise_test, async t => {
74+
insertDocumentRule({
75+
'or': [
76+
{ href_matches: '*\\?*foo=bar*' },
77+
{ href_matches: '*\\?*fizz=buzz*' }]
78+
});
79+
80+
const url_1 = getPrefetchUrl({ foo: 'buzz' });
81+
const url_2 = getPrefetchUrl({ fizz: 'buzz' });
82+
const url_3 = getPrefetchUrl({ foo: 'bar'});
83+
[url_1, url_2, url_3].forEach(url => addLink(url));
84+
await new Promise(resolve => t.step_timeout(resolve, 2000));
85+
86+
assert_equals(await isUrlPrefetched(url_1), 0);
87+
assert_equals(await isUrlPrefetched(url_2), 1);
88+
assert_equals(await isUrlPrefetched(url_3), 1);
89+
}, 'test document rule with disjunction predicate');
90+
91+
subsetTestByKey('not', promise_test, async t => {
92+
insertDocumentRule({ not: { href_matches: '*\\?uuid=*&foo=bar' } });
93+
94+
const url_1 = getPrefetchUrl({foo: 'bar'});
95+
addLink(url_1);
96+
const url_2 = getPrefetchUrl({foo: 'buzz'});
97+
addLink(url_2)
98+
await new Promise(resolve => t.step_timeout(resolve, 2000));
99+
100+
assert_equals(await isUrlPrefetched(url_1), 0);
101+
assert_equals(await isUrlPrefetched(url_2), 1);
102+
}, 'test document rule with negation predicate');
103+
104+
subsetTestByKey('invalidPredicate', promise_test, async t => {
105+
const url = getPrefetchUrl();
106+
addLink(url);
107+
insertDocumentRule({invalid: 'predicate'});
108+
await new Promise(resolve => t.step_timeout(resolve, 2000));
109+
110+
assert_equals(await isUrlPrefetched(url), 0);
111+
}, 'invalid predicate should not throw error or start prefetch');
112+
113+
subsetTestByKey('linkInShadowTree', promise_test, async t => {
114+
insertDocumentRule();
115+
116+
// Create shadow root.
117+
const shadowHost = document.createElement('div');
118+
document.body.appendChild(shadowHost);
119+
const shadowRoot = shadowHost.attachShadow({mode: 'open'});
120+
121+
const url = getPrefetchUrl();
122+
addLink(url, shadowRoot);
123+
await new Promise(resolve => t.step_timeout(resolve, 2000));
124+
125+
assert_equals(await isUrlPrefetched(url), 1);
126+
}, 'test that matching link in a shadow tree is prefetched');
127+
128+
subsetTestByKey('linkHrefChanged', promise_test, async t => {
129+
insertDocumentRule({href_matches: "*\\?*foo=bar*"});
130+
131+
const url = getPrefetchUrl();
132+
const link = addLink(url);
133+
await new Promise(resolve => t.step_timeout(resolve, 2000));
134+
assert_equals(await isUrlPrefetched(url), 0);
135+
136+
const matching_url = getPrefetchUrl({foo: 'bar'});
137+
link.href = matching_url;
138+
await new Promise(resolve => t.step_timeout(resolve, 2000));
139+
assert_equals(await isUrlPrefetched(matching_url), 1);
140+
}, 'test that changing the href of an invalid link to a matching value triggers a prefetch');
141+
142+
subsetTestByKey('newRuleSetAdded', promise_test, async t => {
143+
insertDocumentRule({href_matches: "*\\?*foo=bar*"});
144+
const url = getPrefetchUrl({fizz: "buzz"});
145+
addLink(url);
146+
await new Promise(resolve => t.step_timeout(resolve, 2000));
147+
assert_equals(await isUrlPrefetched(url), 0);
148+
149+
insertDocumentRule({href_matches: "*\\?*fizz=buzz*"});
150+
await new Promise(resolve => t.step_timeout(resolve, 2000));
151+
assert_equals(await isUrlPrefetched(url), 1);
152+
}, 'test that adding a second rule set triggers prefetch');
153+
154+
subsetTestByKey('selectorMatches', promise_test, async t => {
155+
insertDocumentRule({ selector_matches: 'a.important-link' });
156+
157+
const url_1 = getPrefetchUrl({foo: 'bar'});
158+
const importantLink = addLink(url_1);
159+
importantLink.className = 'important-link';
160+
const url_2 = getPrefetchUrl({foo: 'buzz'});
161+
addLink(url_2)
162+
await new Promise(resolve => t.step_timeout(resolve, 2000));
163+
164+
assert_equals(await isUrlPrefetched(url_1), 1);
165+
assert_equals(await isUrlPrefetched(url_2), 0);
166+
}, 'test selector_matches document rule');
167+
168+
subsetTestByKey('selectorMatchesScopingRoot', promise_test, async t => {
169+
insertDocumentRule({ selector_matches: ':root > body > a' });
170+
171+
const url_1 = getPrefetchUrl({ foo: 'bar' });
172+
addLink(url_1);
173+
174+
const url_2 = getPrefetchUrl({ foo: 'buzz' });
175+
const extraContainer = document.createElement('div');
176+
document.body.appendChild(extraContainer);
177+
addLink(url_2, extraContainer);
178+
179+
await new Promise(resolve => t.step_timeout(resolve, 2000));
180+
181+
assert_equals(await isUrlPrefetched(url_1), 1);
182+
assert_equals(await isUrlPrefetched(url_2), 0);
183+
}, 'test selector_matches with :root');
184+
185+
subsetTestByKey('selectorMatchesDisplayNone', promise_test, async t => {
186+
const style = document.createElement('style');
187+
style.innerText = ".important-section { display: none; }";
188+
document.head.appendChild(style);
189+
insertDocumentRule();
190+
191+
const importantSection = document.createElement('div');
192+
importantSection.className = 'important-section';
193+
document.body.appendChild(importantSection);
194+
const url = getPrefetchUrl();
195+
addLink(url, importantSection);
196+
197+
await new Promise(resolve => t.step_timeout(resolve, 2000));
198+
assert_equals(await isUrlPrefetched(url), 0);
199+
200+
style.remove();
201+
await new Promise(resolve => t.step_timeout(resolve, 2000));
202+
assert_equals(await isUrlPrefetched(url), 1);
203+
}, 'test selector_matches with link inside display:none container');
204+
205+
subsetTestByKey('selectorMatchesDisplayLocked', promise_test, async t => {
206+
const style = document.createElement('style');
207+
style.innerText = ".important-section { content-visibility: hidden; }";
208+
document.head.appendChild(style);
209+
insertDocumentRule({ selector_matches: '.important-section a' });
210+
211+
const importantSection = document.createElement('div');
212+
importantSection.className = 'important-section';
213+
document.body.appendChild(importantSection);
214+
const url = getPrefetchUrl();
215+
addLink(url, importantSection);
216+
217+
await new Promise(resolve => t.step_timeout(resolve, 2000));
218+
assert_equals(await isUrlPrefetched(url), 0);
219+
220+
style.remove();
221+
await new Promise(resolve => t.step_timeout(resolve, 2000));
222+
assert_equals(await isUrlPrefetched(url), 1);
223+
}, 'test selector_matches with link inside display locked container');
224+
225+
subsetTestByKey('unslottedLink', promise_test, async t => {
226+
insertDocumentRule();
227+
228+
// Create shadow root.
229+
const shadowHost = document.createElement('div');
230+
document.body.appendChild(shadowHost);
231+
const shadowRoot = shadowHost.attachShadow({ mode: 'open' });
232+
233+
// Add unslotted link.
234+
const url = getPrefetchUrl();
235+
addLink(url, shadowHost);
236+
237+
await new Promise(resolve => t.step_timeout(resolve, 2000));
238+
assert_equals(await isUrlPrefetched(url), 0);
239+
}, 'test that unslotted link never matches document rule');
240+
241+
subsetTestByKey('immediateMutation', promise_test, async t => {
242+
// Add a link and allow it to get its style computed.
243+
// (Double RAF lets this happen normally.)
244+
const url = getPrefetchUrl();
245+
const link = addLink(url, document.body);
246+
await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(() => resolve())));
247+
248+
// Add a document rule and then immediately change the DOM to make it match.
249+
insertDocumentRule({ selector_matches: '.late-class *' });
250+
document.body.className = 'late-class';
251+
252+
await new Promise(resolve => t.step_timeout(resolve, 2000));
253+
assert_equals(await isUrlPrefetched(url), 1);
254+
}, 'test that selector_matches predicates respect changes immediately');
255+
256+
const baseURLChangedTestFixture = (testName, modifyBaseURLFunc) => {
257+
return subsetTestByKey(testName, promise_test, async t => {
258+
const url = getPrefetchUrl();
259+
const link = addLink(url);
260+
const url_pattern_string = `prefetch.py${url.search}`;
261+
262+
// Insert a document rule with a url pattern predicate that uses a
263+
// relative URL which will not match with |url|, due to |document.baseURI|
264+
// being different from |url|'s path.
265+
assert_false((new URLPattern(url_pattern_string, document.baseURI)).test(url));
266+
insertDocumentRule({ href_matches: url_pattern_string });
267+
await new Promise(resolve => t.step_timeout(resolve, 2000));
268+
assert_equals(await isUrlPrefetched(url), 0);
269+
270+
// Change the baseURL of the document to |url|. |url| should now be
271+
// prefetched.
272+
modifyBaseURLFunc(url);
273+
assert_true((new URLPattern(url_pattern_string, document.baseURI)).test(url));
274+
await new Promise(resolve => t.step_timeout(resolve, 2000));
275+
assert_equals(await isUrlPrefetched(url), 1);
276+
});
277+
}
278+
279+
baseURLChangedTestFixture('baseURLChangedBySameDocumentNavigation', url => {
280+
history.pushState({}, "", url);
281+
});
282+
283+
baseURLChangedTestFixture('baseURLChangedByBaseElement', url => {
284+
const base = document.createElement('base');
285+
base.href = url;
286+
document.head.appendChild(base);
287+
});
288+
289+
subsetTestByKey('linkToSelfFragment', promise_test, async t => {
290+
const url = getPrefetchUrl();
291+
history.pushState({}, "", url);
292+
addLink(new URL('#fragment', url));
293+
insertDocumentRule();
294+
await new Promise(resolve => t.step_timeout(resolve, 2000));
295+
assert_equals(await isUrlPrefetched(url), 0);
296+
}, 'test that a fragment link to the current document does not prefetch');
297+
298+
</script>
299+
</body>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
PASS test document rule with conjunction predicate
3+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
PASS document-rules
3+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
PASS document-rules
3+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
PASS test document rule with no predicate
3+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
PASS test href_matches document rule
3+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
PASS test that selector_matches predicates respect changes immediately
3+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
PASS invalid predicate should not throw error or start prefetch
3+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
PASS test that changing the href of an invalid link to a matching value triggers a prefetch
3+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
PASS test that matching link in a shadow tree is prefetched
3+

0 commit comments

Comments
 (0)