| <!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.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"> | |
| <meta name="variant" content="?include=linkHrefChanged"> | |
| <meta name="variant" content="?include=newRuleSetAdded"> | |
| <meta name="variant" content="?include=selectorMatches"> | |
| <meta name="variant" content="?include=selectorMatchesScopingRoot"> | |
| <meta name="variant" content="?include=selectorMatchesDisplayNone"> | |
| <meta name="variant" content="?include=selectorMatchesDisplayLocked"> | |
| <meta name="variant" content="?include=unslottedLink"> | |
| <meta name="variant" content="?include=immediateMutation"> | |
| <meta name="variant" content="?include=baseURLChangedBySameDocumentNavigation"> | |
| <meta name="variant" content="?include=baseURLChangedByBaseElement"> | |
| <meta name="variant" content="?include=linkToSelfFragment"> | |
| <body> | |
| <script> | |
| setup(() => assertSpeculationRulesIsSupported()); | |
| subsetTestByKey('defaultPredicate', promise_test, async t => { | |
| 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 => { | |
| 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 => { | |
| 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 => { | |
| 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 => { | |
| 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 => { | |
| 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 => { | |
| 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'); | |
| subsetTestByKey('linkHrefChanged', promise_test, async t => { | |
| insertDocumentRule({href_matches: "*\\?*foo=bar*"}); | |
| const url = getPrefetchUrl(); | |
| const link = addLink(url); | |
| await new Promise(resolve => t.step_timeout(resolve, 2000)); | |
| assert_equals(await isUrlPrefetched(url), 0); | |
| const matching_url = getPrefetchUrl({foo: 'bar'}); | |
| link.href = matching_url; | |
| await new Promise(resolve => t.step_timeout(resolve, 2000)); | |
| assert_equals(await isUrlPrefetched(matching_url), 1); | |
| }, 'test that changing the href of an invalid link to a matching value triggers a prefetch'); | |
| subsetTestByKey('newRuleSetAdded', promise_test, async t => { | |
| insertDocumentRule({href_matches: "*\\?*foo=bar*"}); | |
| const url = getPrefetchUrl({fizz: "buzz"}); | |
| addLink(url); | |
| await new Promise(resolve => t.step_timeout(resolve, 2000)); | |
| assert_equals(await isUrlPrefetched(url), 0); | |
| insertDocumentRule({href_matches: "*\\?*fizz=buzz*"}); | |
| await new Promise(resolve => t.step_timeout(resolve, 2000)); | |
| assert_equals(await isUrlPrefetched(url), 1); | |
| }, 'test that adding a second rule set triggers prefetch'); | |
| subsetTestByKey('selectorMatches', promise_test, async t => { | |
| insertDocumentRule({ selector_matches: 'a.important-link' }); | |
| const url_1 = getPrefetchUrl({foo: 'bar'}); | |
| const importantLink = addLink(url_1); | |
| importantLink.className = 'important-link'; | |
| 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 selector_matches document rule'); | |
| subsetTestByKey('selectorMatchesScopingRoot', promise_test, async t => { | |
| insertDocumentRule({ selector_matches: ':root > body > a' }); | |
| const url_1 = getPrefetchUrl({ foo: 'bar' }); | |
| addLink(url_1); | |
| const url_2 = getPrefetchUrl({ foo: 'buzz' }); | |
| const extraContainer = document.createElement('div'); | |
| document.body.appendChild(extraContainer); | |
| addLink(url_2, extraContainer); | |
| await new Promise(resolve => t.step_timeout(resolve, 2000)); | |
| assert_equals(await isUrlPrefetched(url_1), 1); | |
| assert_equals(await isUrlPrefetched(url_2), 0); | |
| }, 'test selector_matches with :root'); | |
| subsetTestByKey('selectorMatchesDisplayNone', promise_test, async t => { | |
| const style = document.createElement('style'); | |
| style.innerText = ".important-section { display: none; }"; | |
| document.head.appendChild(style); | |
| insertDocumentRule(); | |
| const importantSection = document.createElement('div'); | |
| importantSection.className = 'important-section'; | |
| document.body.appendChild(importantSection); | |
| const url = getPrefetchUrl(); | |
| addLink(url, importantSection); | |
| await new Promise(resolve => t.step_timeout(resolve, 2000)); | |
| assert_equals(await isUrlPrefetched(url), 0); | |
| style.remove(); | |
| await new Promise(resolve => t.step_timeout(resolve, 2000)); | |
| assert_equals(await isUrlPrefetched(url), 1); | |
| }, 'test selector_matches with link inside display:none container'); | |
| subsetTestByKey('selectorMatchesDisplayLocked', promise_test, async t => { | |
| const style = document.createElement('style'); | |
| style.innerText = ".important-section { content-visibility: hidden; }"; | |
| document.head.appendChild(style); | |
| insertDocumentRule({ selector_matches: '.important-section a' }); | |
| const importantSection = document.createElement('div'); | |
| importantSection.className = 'important-section'; | |
| document.body.appendChild(importantSection); | |
| const url = getPrefetchUrl(); | |
| addLink(url, importantSection); | |
| await new Promise(resolve => t.step_timeout(resolve, 2000)); | |
| assert_equals(await isUrlPrefetched(url), 0); | |
| style.remove(); | |
| await new Promise(resolve => t.step_timeout(resolve, 2000)); | |
| assert_equals(await isUrlPrefetched(url), 1); | |
| }, 'test selector_matches with link inside display locked container'); | |
| subsetTestByKey('unslottedLink', promise_test, async t => { | |
| insertDocumentRule(); | |
| // Create shadow root. | |
| const shadowHost = document.createElement('div'); | |
| document.body.appendChild(shadowHost); | |
| const shadowRoot = shadowHost.attachShadow({ mode: 'open' }); | |
| // Add unslotted link. | |
| const url = getPrefetchUrl(); | |
| addLink(url, shadowHost); | |
| await new Promise(resolve => t.step_timeout(resolve, 2000)); | |
| assert_equals(await isUrlPrefetched(url), 0); | |
| }, 'test that unslotted link never matches document rule'); | |
| subsetTestByKey('immediateMutation', promise_test, async t => { | |
| // Add a link and allow it to get its style computed. | |
| // (Double RAF lets this happen normally.) | |
| const url = getPrefetchUrl(); | |
| const link = addLink(url, document.body); | |
| await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(() => resolve()))); | |
| // Add a document rule and then immediately change the DOM to make it match. | |
| insertDocumentRule({ selector_matches: '.late-class *' }); | |
| document.body.className = 'late-class'; | |
| await new Promise(resolve => t.step_timeout(resolve, 2000)); | |
| assert_equals(await isUrlPrefetched(url), 1); | |
| }, 'test that selector_matches predicates respect changes immediately'); | |
| const baseURLChangedTestFixture = (testName, modifyBaseURLFunc) => { | |
| return subsetTestByKey(testName, promise_test, async t => { | |
| const url = getPrefetchUrl(); | |
| const link = addLink(url); | |
| const url_pattern_string = `prefetch.py${url.search}`; | |
| // Insert a document rule with a url pattern predicate that uses a | |
| // relative URL which will not match with |url|, due to |document.baseURI| | |
| // being different from |url|'s path. | |
| assert_false((new URLPattern(url_pattern_string, document.baseURI)).test(url)); | |
| insertDocumentRule({ href_matches: url_pattern_string }); | |
| await new Promise(resolve => t.step_timeout(resolve, 2000)); | |
| assert_equals(await isUrlPrefetched(url), 0); | |
| // Change the baseURL of the document to |url|. |url| should now be | |
| // prefetched. | |
| modifyBaseURLFunc(url); | |
| assert_true((new URLPattern(url_pattern_string, document.baseURI)).test(url)); | |
| await new Promise(resolve => t.step_timeout(resolve, 2000)); | |
| assert_equals(await isUrlPrefetched(url), 1); | |
| }); | |
| } | |
| baseURLChangedTestFixture('baseURLChangedBySameDocumentNavigation', url => { | |
| history.pushState({}, "", url); | |
| }); | |
| baseURLChangedTestFixture('baseURLChangedByBaseElement', url => { | |
| const base = document.createElement('base'); | |
| base.href = url; | |
| document.head.appendChild(base); | |
| }); | |
| subsetTestByKey('linkToSelfFragment', promise_test, async t => { | |
| const url = getPrefetchUrl(); | |
| history.pushState({}, "", url); | |
| addLink(new URL('#fragment', url)); | |
| insertDocumentRule(); | |
| await new Promise(resolve => t.step_timeout(resolve, 2000)); | |
| assert_equals(await isUrlPrefetched(url), 0); | |
| }, 'test that a fragment link to the current document does not prefetch'); | |
| </script> | |
| </body> |