document-rules.https.html (12079B)
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>