diff --git a/resources/testdriver.js b/resources/testdriver.js index 5b390dedeb72bb..7d303d2ab1f8ee 100644 --- a/resources/testdriver.js +++ b/resources/testdriver.js @@ -81,6 +81,74 @@ /** * `bluetooth `_ module. */ + /*# RFC N: Add test-only APIs to improve the speculation rules prefetch tests + (*Note: N should be replaced by the PR number*) + + ## Summary + + Add testdriver.js support for the [Prefetch Status Updated command]. + This will allow tests to subscribe to the `speculation.prefetch_status_updated` + event and verify that the prefetch status is updated correctly. + [Prefetch Status Updated command]: *https://w3c.github.io/webdriver-bidi/#command-speculation-prefetch-status-updated* (not real yet) + */ + speculation: { + prefetch_status_updated: { + /** + * @typedef {object} PrefetchStatusUpdated + * `speculation.PrefetchStatusUpdateParameters + */ + + /** + * Subscribes to the event. Events will be emitted only if + * there is a subscription for the event. This method does + * not add actual listeners. To listen to the event, use the + * `on` or `once` methods. The buffered events will be + * emitted before the command promise is resolved. + * + * @param {object} [params] Parameters for the subscription. + * @param {null|Array.<(Context)>} [params.contexts] The + * optional contexts parameter specifies which browsing + * contexts to subscribe to the event on. It should be + * either an array of Context objects, or null. If null, the + * event will be subscribed to globally. If omitted, the + * event will be subscribed to on the current browsing + * context. + * @returns {Promise} Resolves when the subscription + * is successfully done. + */ + subscribe: async function(params = {}) { + assertBidiIsEnabled(); + return window.test_driver_internal.bidi.speculation + .prefetch_status_updated.subscribe(params); + }, + /** + * Adds an event listener for the event. + * + * @param {function(PrefetchStatusUpdate): void} callback The + * callback to be called when the event is emitted. The + * callback is called with the event object as a parameter. + * @returns {function(): void} A function that removes the + * added event listener when called. + */ + on: function(callback) { + assertBidiIsEnabled(); + return window.test_driver_internal.bidi.speculation + .prefetch_status_updated.on(callback); + }, + + once: function() { + assertBidiIsEnabled(); + return new Promise(resolve => { + const remove_handler = + window.test_driver_internal.bidi.speculation + .prefetch_status_updated.on(event => { + resolve(event); + remove_handler(); + }); + }); + }, + } + }, bluetooth: { /** * Handle a bluetooth device prompt with the given params. Matches the @@ -665,7 +733,7 @@ */ subscribe: async function(params = {}) { assertBidiIsEnabled(); - return window.test_driver_internal.bidi.bluetooth + return window.test_driver.bidi.bluetooth .characteristic_event_generated.subscribe(params); }, /** @@ -679,7 +747,7 @@ */ on: function(callback) { assertBidiIsEnabled(); - return window.test_driver_internal.bidi.bluetooth + return window.test_driver.bidi.bluetooth .characteristic_event_generated.on(callback); }, /** @@ -2182,6 +2250,18 @@ in_automation: false, bidi: { + speculation: { + prefetch_status_updated: { + async subscribe() { + throw new Error( + 'bidi.speculation.prefetch_status_updated.subscribe is not implemented by testdriver-vendor.js'); + }, + on() { + throw new Error( + 'bidi.speculation.prefetch_status_updated.on is not implemented by testdriver-vendor.js'); + } + }, + }, bluetooth: { handle_request_device_prompt: function() { throw new Error( diff --git a/speculation-rules/prefetch/anonymous-client.https.html b/speculation-rules/prefetch/anonymous-client.https.html index a371255eede730..fd0ca3f2fc7c23 100644 --- a/speculation-rules/prefetch/anonymous-client.https.html +++ b/speculation-rules/prefetch/anonymous-client.https.html @@ -5,12 +5,22 @@ + + + + - + + @@ -25,7 +26,16 @@ let agent = await spawnWindow(t); let nextUrl = agent.getExecutorURL({ executor: "cookies.py", cookieindices: "1" }); + + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); await agent.forceSinglePrefetch(nextUrl); + await statusPromise; // Wait for prefetch status update + await agent.navigate(nextUrl); assert_prefetched(await agent.getRequestHeaders()); diff --git a/speculation-rules/prefetch/cross-origin-cookies-anonymous-client-ip-duplicate.https.html b/speculation-rules/prefetch/cross-origin-cookies-anonymous-client-ip-duplicate.https.html index 4e793c9f04aaf5..d583e9ea08d78b 100644 --- a/speculation-rules/prefetch/cross-origin-cookies-anonymous-client-ip-duplicate.https.html +++ b/speculation-rules/prefetch/cross-origin-cookies-anonymous-client-ip-duplicate.https.html @@ -2,7 +2,8 @@ - + + @@ -24,8 +25,17 @@ assert_equals(response_cookies["type"], "navigate"); let nextUrl = agent.getExecutorURL({ executor, hostname: CROSS_ORIGIN_HOST_THAT_WORKS_WITH_ACIWCO, page: 2 }); + + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + await agent.forceSinglePrefetch(nextUrl, { requires: ["anonymous-client-ip-when-cross-origin"] }); await agent.forceSinglePrefetch(nextUrl); + await statusPromise; // Wait for prefetch status update await agent.navigate(nextUrl); response_cookies = await agent.getResponseCookies(); diff --git a/speculation-rules/prefetch/cross-origin-cookies.https.html b/speculation-rules/prefetch/cross-origin-cookies.https.html index e6785d83536a46..29a383c6978430 100644 --- a/speculation-rules/prefetch/cross-origin-cookies.https.html +++ b/speculation-rules/prefetch/cross-origin-cookies.https.html @@ -2,7 +2,8 @@ - + + @@ -25,7 +26,16 @@ assert_equals(response_cookies["type"], "navigate"); let nextUrl = agent.getExecutorURL({ executor, hostname: get_host_info().NOTSAMESITE_HOST, page: 2 }); + + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + await agent.forceSinglePrefetch(nextUrl); + await statusPromise; // Wait for prefetch status update await agent.navigate(nextUrl); response_cookies = await agent.getResponseCookies(); diff --git a/speculation-rules/prefetch/different-initiators-2.https.html b/speculation-rules/prefetch/different-initiators-2.https.html index dc314d22c54f52..dd596119321b6e 100644 --- a/speculation-rules/prefetch/different-initiators-2.https.html +++ b/speculation-rules/prefetch/different-initiators-2.https.html @@ -2,6 +2,9 @@ + + + @@ -22,8 +25,14 @@ // The Documents #2 and #4 are different, but the same RenderFrameHost is // used before https://crbug.com/936696 is done. + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); await win.forceSinglePrefetch(nextUrl); - + await statusPromise; // Wait for prefetch status update // Register a SW for `nextUrl` -- this is a trick to make the prefetched // result to put in `PrefetchService::prefetches_ready_to_serve_` in // Chromium implementation but actually not used by this navigation. diff --git a/speculation-rules/prefetch/different-initiators.sub.https.html b/speculation-rules/prefetch/different-initiators.sub.https.html index 691dfd855ead5b..ca01f03358ed8d 100644 --- a/speculation-rules/prefetch/different-initiators.sub.https.html +++ b/speculation-rules/prefetch/different-initiators.sub.https.html @@ -7,6 +7,8 @@ + + @@ -29,7 +31,16 @@ // Start speculation rules prefetch from `initiator1`. const nextUrl = initiator1.getExecutorURL({ protocol: 'https', page: 2 }); + + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + await initiator1.forceSinglePrefetch(nextUrl); + await statusPromise; // Wait for prefetch status update // Register a SW for `nextUrl` -- this is a trick to make the prefetched // result to put in `PrefetchService::prefetches_ready_to_serve_` in diff --git a/speculation-rules/prefetch/document-rules.https.html b/speculation-rules/prefetch/document-rules.https.html index ae75ec94036f45..7506fb468d1f47 100644 --- a/speculation-rules/prefetch/document-rules.https.html +++ b/speculation-rules/prefetch/document-rules.https.html @@ -1,6 +1,8 @@ + + @@ -32,20 +34,36 @@ subsetTestByKey('defaultPredicate', promise_test, async t => { const url = getPrefetchUrl(); + + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + addLink(url); insertDocumentRule(); + await statusPromise; 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 => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); insertDocumentRule({ href_matches: '*\\?uuid=*&foo=bar' }); const url_1 = getPrefetchUrl({foo: 'bar'}); addLink(url_1); const url_2 = getPrefetchUrl({foo: 'buzz'}); - addLink(url_2) + addLink(url_2); + await statusPromise; await new Promise(resolve => t.step_timeout(resolve, 2000)); assert_equals(await isUrlPrefetched(url_1), 1); @@ -53,6 +71,12 @@ }, 'test href_matches document rule'); subsetTestByKey('and', promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); insertDocumentRule({ 'and': [ { href_matches: '*\\?*foo=bar*' }, @@ -63,6 +87,7 @@ 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 statusPromise; await new Promise(resolve => t.step_timeout(resolve, 2000)); assert_equals(await isUrlPrefetched(url_1), 0); @@ -71,6 +96,12 @@ }, 'test document rule with conjunction predicate'); subsetTestByKey('or', promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); insertDocumentRule({ 'or': [ { href_matches: '*\\?*foo=bar*' }, @@ -81,6 +112,7 @@ const url_2 = getPrefetchUrl({ fizz: 'buzz' }); const url_3 = getPrefetchUrl({ foo: 'bar'}); [url_1, url_2, url_3].forEach(url => addLink(url)); + await statusPromise; await new Promise(resolve => t.step_timeout(resolve, 2000)); assert_equals(await isUrlPrefetched(url_1), 0); @@ -89,12 +121,19 @@ }, 'test document rule with disjunction predicate'); subsetTestByKey('not', promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); 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) + addLink(url_2); + await statusPromise; await new Promise(resolve => t.step_timeout(resolve, 2000)); assert_equals(await isUrlPrefetched(url_1), 0); @@ -111,6 +150,12 @@ }, 'invalid predicate should not throw error or start prefetch'); subsetTestByKey('linkInShadowTree', promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); insertDocumentRule(); // Create shadow root. @@ -120,6 +165,7 @@ const url = getPrefetchUrl(); addLink(url, shadowRoot); + await statusPromise; await new Promise(resolve => t.step_timeout(resolve, 2000)); assert_equals(await isUrlPrefetched(url), 1); @@ -152,13 +198,20 @@ }, 'test that adding a second rule set triggers prefetch'); subsetTestByKey('selectorMatches', promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); 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) + addLink(url_2); + await statusPromise; await new Promise(resolve => t.step_timeout(resolve, 2000)); assert_equals(await isUrlPrefetched(url_1), 1); @@ -166,6 +219,12 @@ }, 'test selector_matches document rule'); subsetTestByKey('selectorMatchesScopingRoot', promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); insertDocumentRule({ selector_matches: ':root > body > a' }); const url_1 = getPrefetchUrl({ foo: 'bar' }); @@ -176,6 +235,7 @@ document.body.appendChild(extraContainer); addLink(url_2, extraContainer); + await statusPromise; await new Promise(resolve => t.step_timeout(resolve, 2000)); assert_equals(await isUrlPrefetched(url_1), 1); @@ -183,6 +243,12 @@ }, 'test selector_matches with :root'); subsetTestByKey('selectorMatchesDisplayNone', promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); const style = document.createElement('style'); style.innerText = ".important-section { display: none; }"; document.head.appendChild(style); @@ -194,6 +260,7 @@ const url = getPrefetchUrl(); addLink(url, importantSection); + await statusPromise; await new Promise(resolve => t.step_timeout(resolve, 2000)); assert_equals(await isUrlPrefetched(url), 0); @@ -203,6 +270,12 @@ }, 'test selector_matches with link inside display:none container'); subsetTestByKey('selectorMatchesDisplayLocked', promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); const style = document.createElement('style'); style.innerText = ".important-section { content-visibility: hidden; }"; document.head.appendChild(style); @@ -214,6 +287,7 @@ const url = getPrefetchUrl(); addLink(url, importantSection); + await statusPromise; await new Promise(resolve => t.step_timeout(resolve, 2000)); assert_equals(await isUrlPrefetched(url), 0); @@ -223,6 +297,12 @@ }, 'test selector_matches with link inside display locked container'); subsetTestByKey('unslottedLink', promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); insertDocumentRule(); // Create shadow root. @@ -235,10 +315,17 @@ addLink(url, shadowHost); await new Promise(resolve => t.step_timeout(resolve, 2000)); + await statusPromise; assert_equals(await isUrlPrefetched(url), 0); }, 'test that unslotted link never matches document rule'); subsetTestByKey('immediateMutation', promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); // Add a link and allow it to get its style computed. // (Double RAF lets this happen normally.) const url = getPrefetchUrl(); @@ -250,11 +337,18 @@ document.body.className = 'late-class'; await new Promise(resolve => t.step_timeout(resolve, 2000)); + await statusPromise; 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 => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); const url = getPrefetchUrl(); const link = addLink(url); const url_pattern_string = `prefetch.py${url.search}`; @@ -272,6 +366,7 @@ modifyBaseURLFunc(url); assert_true((new URLPattern(url_pattern_string, document.baseURI)).test(url)); await new Promise(resolve => t.step_timeout(resolve, 2000)); + await statusPromise; assert_equals(await isUrlPrefetched(url), 1); }); } @@ -287,11 +382,18 @@ }); subsetTestByKey('linkToSelfFragment', promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); const url = getPrefetchUrl(); history.pushState({}, "", url); addLink(new URL('#fragment', url)); insertDocumentRule(); await new Promise(resolve => t.step_timeout(resolve, 2000)); + await statusPromise; assert_equals(await isUrlPrefetched(url), 0); }, 'test that a fragment link to the current document does not prefetch'); diff --git a/speculation-rules/prefetch/duplicate-urls.https.html b/speculation-rules/prefetch/duplicate-urls.https.html index f9e46a6a384a5d..e1d6930b9f680d 100644 --- a/speculation-rules/prefetch/duplicate-urls.https.html +++ b/speculation-rules/prefetch/duplicate-urls.https.html @@ -1,6 +1,8 @@ + + @@ -10,8 +12,15 @@ setup(() => assertSpeculationRulesIsSupported()); promise_test(async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); let urls = Array(5).fill(getPrefetchUrlList(1)[0]); insertSpeculationRules({ prefetch: [{ source: 'list', urls: urls }] }); + await statusPromise; await new Promise(resolve => t.step_timeout(resolve, 2000)); let prefetched_count = await isUrlPrefetched(urls[0]); diff --git a/speculation-rules/prefetch/fragment.https.html b/speculation-rules/prefetch/fragment.https.html index 7b35500303e16b..ac691dda31285c 100644 --- a/speculation-rules/prefetch/fragment.https.html +++ b/speculation-rules/prefetch/fragment.https.html @@ -1,6 +1,8 @@ + + @@ -9,6 +11,12 @@ setup(() => assertSpeculationRulesIsSupported()); promise_test(async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); const testUrl = document.URL; const [prefetchUrl, anotherPrefetchUrl] = getPrefetchUrlList(2); try { @@ -18,6 +26,7 @@ new URL('#fragment', anotherPrefetchUrl), ]; insertSpeculationRules({prefetch: [{source: 'list', urls}]}); + await statusPromise; await new Promise(resolve => t.step_timeout(resolve, 2000)); assert_equals(await isUrlPrefetched(prefetchUrl), 0); assert_equals(await isUrlPrefetched(anotherPrefetchUrl), 1); diff --git a/speculation-rules/prefetch/implicit-source.https.html b/speculation-rules/prefetch/implicit-source.https.html index b0b166798466f2..243f3ea9a882e2 100644 --- a/speculation-rules/prefetch/implicit-source.https.html +++ b/speculation-rules/prefetch/implicit-source.https.html @@ -1,6 +1,8 @@ + + @@ -10,6 +12,12 @@ setup(() => assertSpeculationRulesIsSupported()); promise_test(async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); let urls = getPrefetchUrlList(2); let a = document.createElement('a'); @@ -24,6 +32,7 @@ {where: {selector_matches: '.prefetch-me'}, eagerness: 'immediate'}, ]}); + await statusPromise; await new Promise(resolve => t.step_timeout(resolve, 2000)); let wasPrefetched = urls.map(isUrlPrefetched); diff --git a/speculation-rules/prefetch/initiators-a-element.sub.https.html b/speculation-rules/prefetch/initiators-a-element.sub.https.html index 9cfedb20efa5eb..6a26db2fd1fe0e 100644 --- a/speculation-rules/prefetch/initiators-a-element.sub.https.html +++ b/speculation-rules/prefetch/initiators-a-element.sub.https.html @@ -6,6 +6,8 @@ + + @@ -17,6 +19,12 @@ // `sourceDocument` (instead of `navigable`'s active document) should be // used as the referring document for prefetch. promise_test(async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); const win = await spawnWindow(t, { protocol: 'https' }); const hostname = @@ -24,6 +32,7 @@ const nextUrl = win.getExecutorURL({ protocol: 'https', hostname, page: 2 }); await win.forceSinglePrefetch(nextUrl); + await statusPromise; // sourceDocument == `win`'s Document == active document of window being // navigated. @@ -45,6 +54,12 @@ }, ``); promise_test(async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); const win = await spawnWindow(t, { protocol: 'https' }); const hostname = @@ -52,6 +67,7 @@ const nextUrl = win.getExecutorURL({ protocol: 'https', hostname, page: 2 }); await win.forceSinglePrefetch(nextUrl); + await statusPromise; // sourceDocument == `win`'s Document != active document of window being // navigated, since the window being navigated is a new window. diff --git a/speculation-rules/prefetch/initiators-iframe-location-href.sub.https.html b/speculation-rules/prefetch/initiators-iframe-location-href.sub.https.html index 0c195ee454f03b..11b2cd01743f70 100644 --- a/speculation-rules/prefetch/initiators-iframe-location-href.sub.https.html +++ b/speculation-rules/prefetch/initiators-iframe-location-href.sub.https.html @@ -6,6 +6,8 @@ + + @@ -22,6 +24,12 @@ // If a browser does start allowing these in narrower cases where the partition // would nonetheless be the same, this test might need tweaking. promise_test(async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); const win = await spawnWindow(t, { protocol: 'https' }); const hostname = @@ -29,6 +37,7 @@ const nextUrl = win.getExecutorURL({ protocol: 'https', hostname, page: 2 }); await win.forceSinglePrefetch(nextUrl); + await statusPromise; // In https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigate, // `sourceDocument` is the incumbent Document and thus `win`'s Document. diff --git a/speculation-rules/prefetch/initiators-window-open.sub.https.html b/speculation-rules/prefetch/initiators-window-open.sub.https.html index 6923d44dfba5fd..5f9c55aef5f35d 100644 --- a/speculation-rules/prefetch/initiators-window-open.sub.https.html +++ b/speculation-rules/prefetch/initiators-window-open.sub.https.html @@ -6,6 +6,8 @@ + + @@ -17,6 +19,12 @@ // `sourceDocument` (instead of `navigable`'s active document) should be // used as the referring document for prefetch. promise_test(async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); const win = await spawnWindow(t, { protocol: 'https' }); const hostname = @@ -24,6 +32,7 @@ const nextUrl = win.getExecutorURL({ protocol: 'https', hostname, page: 2 }); await win.forceSinglePrefetch(nextUrl); + await statusPromise; await win.execute_script((url) => { window.executor.suspend(() => { @@ -45,6 +54,12 @@ }, `window.open()`); promise_test(async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); const win = await spawnWindow(t, { protocol: 'https' }); const hostname = @@ -52,6 +67,7 @@ const nextUrl = win.getExecutorURL({ protocol: 'https', hostname, page: 2 }); await win.forceSinglePrefetch(nextUrl); + await statusPromise; await win.execute_script((url) => { window.executor.suspend(() => { diff --git a/speculation-rules/prefetch/multiple-url.https.html b/speculation-rules/prefetch/multiple-url.https.html index 34a8817a98213d..5eff809de345e6 100644 --- a/speculation-rules/prefetch/multiple-url.https.html +++ b/speculation-rules/prefetch/multiple-url.https.html @@ -5,13 +5,22 @@ + + + + @@ -25,8 +27,17 @@ const prefetchUrl = bypassCache ? agent.getExecutorURL({ a: "b" }) : agent.getExecutorURL(); - if (prefetchEnabled) + let statusPromise; + if (prefetchEnabled) { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); await agent.forceSinglePrefetch(prefetchUrl); + await statusPromise; + } await agent.navigate(prefetchUrl); diff --git a/speculation-rules/prefetch/navigation-timing-requestStart-responseStart.https.html b/speculation-rules/prefetch/navigation-timing-requestStart-responseStart.https.html index edcd83e699764e..3aeed254e67905 100644 --- a/speculation-rules/prefetch/navigation-timing-requestStart-responseStart.https.html +++ b/speculation-rules/prefetch/navigation-timing-requestStart-responseStart.https.html @@ -6,6 +6,8 @@ + + @@ -40,7 +42,14 @@ subsetTestByKey('afterResponse', promise_test, async t => { const agent = await spawnWindow(t); const landingUrl = agent.getExecutorURL({page: 2}); + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); await agent.forceSinglePrefetch(landingUrl); + await statusPromise; await agent.navigate(landingUrl); assert_prefetched(await agent.getRequestHeaders(), `${landingUrl} should have been prefetched.`); @@ -56,6 +65,12 @@ subsetTestByKey('waitingForResponse', promise_test, async t => { const agent = await spawnWindow(t); const landingUrl = agent.getExecutorURL({executor: 'slow-executor.py', delay: '3', page: 2}); + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); await agent.forceSinglePrefetch(landingUrl, {}, /*wait_for_completion=*/false); // Chromium, at least, will give up the prefetch if the response head doesn't @@ -63,6 +78,7 @@ // 3 seconds to respond, we wait 2.5 seconds before navigating, to ensure the // response comes back within about 0.5 seconds. await new Promise(resolve => t.step_timeout(resolve, 2_500)); + await statusPromise; await agent.navigate(landingUrl); assert_prefetched(await agent.getRequestHeaders(), `${landingUrl} should have been prefetched.`); @@ -81,10 +97,17 @@ const agent = await spawnWindow(t); const landingUrl = agent.getExecutorURL({page: 2}); const slowRedirectUrl = new URL(`/common/slow-redirect.py?delay=3&location=${encodeURIComponent(landingUrl)}`, document.baseURI); + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); await agent.forceSinglePrefetch(slowRedirectUrl, {}, /*wait_for_completion=*/false); // Considerations here are the same as for `waitingForResponse`. await new Promise(resolve => t.step_timeout(resolve, 2_500)); + await statusPromise; await agent.navigate(slowRedirectUrl, {expectedDestinationUrl: landingUrl}); assert_prefetched(await agent.getRequestHeaders(), `${landingUrl} should have been prefetched.`); diff --git a/speculation-rules/prefetch/navigation-timing-sizes.https.html b/speculation-rules/prefetch/navigation-timing-sizes.https.html index b3cca1c22dadf4..cbf186b362a819 100644 --- a/speculation-rules/prefetch/navigation-timing-sizes.https.html +++ b/speculation-rules/prefetch/navigation-timing-sizes.https.html @@ -5,6 +5,8 @@ + + @@ -27,8 +29,17 @@ const prefetchUrl = bypassCache ? agent.getExecutorURL({ a: "b" }) : agent.getExecutorURL(); - if (prefetchEnabled) + let statusPromise; + if (prefetchEnabled) { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + statusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); await agent.forceSinglePrefetch(prefetchUrl); + await statusPromise; + } await agent.navigate(prefetchUrl); diff --git a/speculation-rules/prefetch/no-http-cache-interference.https.html b/speculation-rules/prefetch/no-http-cache-interference.https.html index e390edca7851fc..17e9e7f3ec8796 100644 --- a/speculation-rules/prefetch/no-http-cache-interference.https.html +++ b/speculation-rules/prefetch/no-http-cache-interference.https.html @@ -5,15 +5,24 @@ + + + + + + @@ -28,6 +30,14 @@ setup(() => assertSpeculationRulesIsSupported()); async function runSpeculationRulesFetchTest(t, options) { + // Subscribe to the BiDi prefetch_status_updated event and set up a promise to await it. + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + options = { // Whether a prefetch is expected to succeed. shouldPrefetch: true, @@ -95,6 +105,9 @@ await new Promise(resolve => t.step_timeout(resolve, 2000)); + // Wait for the BiDi prefetch_status_updated event before assertions + await prefetchStatusPromise; + let test_case_desc = JSON.stringify(options); if (options.shouldPrefetch) assert_prefetched(await agent.getRequestHeaders(), `Prefetch should work for request ${test_case_desc}.`); diff --git a/speculation-rules/prefetch/prefetch-single.https.html b/speculation-rules/prefetch/prefetch-single.https.html index 15a4466880611d..f6da8a0e90d54c 100644 --- a/speculation-rules/prefetch/prefetch-single.https.html +++ b/speculation-rules/prefetch/prefetch-single.https.html @@ -1,6 +1,9 @@ + + + @@ -17,15 +20,26 @@ // This is split across four test variants due to the test timeouts. let { from_protocol, to_protocol } = Object.fromEntries(new URLSearchParams(location.search)); promise_test(async t => { + // Subscribe to the BiDi prefetch_status_updated event and set up a promise to await it. + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + let agent = await spawnWindow(t, { protocol: from_protocol }); let nextUrl = agent.getExecutorURL({ protocol: to_protocol, page: 2 }); await agent.forceSinglePrefetch(nextUrl); await agent.navigate(nextUrl); + // Wait for the BiDi prefetch_status_updated event before assertions + await prefetchStatusPromise; + if (to_protocol == "https") { assert_prefetched(await agent.getRequestHeaders(), "Prefetch should work for HTTPS urls."); } else { assert_not_prefetched(await agent.getRequestHeaders(), "Prefetch should not work for HTTP urls."); } }, `test single ${to_protocol} url prefetch from a ${from_protocol} url`); - + \ No newline at end of file diff --git a/speculation-rules/prefetch/prefetch-status.https.html b/speculation-rules/prefetch/prefetch-status.https.html index 94d6a4020e14cf..ae5967c178903f 100644 --- a/speculation-rules/prefetch/prefetch-status.https.html +++ b/speculation-rules/prefetch/prefetch-status.https.html @@ -5,27 +5,43 @@ + + - - - - - + \ No newline at end of file diff --git a/speculation-rules/prefetch/prefetch-traverse-reload.sub.html b/speculation-rules/prefetch/prefetch-traverse-reload.sub.html index 2b66db2f8aec5e..147bd9155184b4 100644 --- a/speculation-rules/prefetch/prefetch-traverse-reload.sub.html +++ b/speculation-rules/prefetch/prefetch-traverse-reload.sub.html @@ -2,17 +2,30 @@ + + + + + + + + @@ -14,6 +18,14 @@ setup(() => assertSpeculationRulesIsSupported()); promise_test(async t => { + // Subscribe to the BiDi prefetch_status_updated event and set up a promise to await it. + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + const is_same_site = location.search === '?same-site'; const initiator = await spawnWindow(t); const url1 = initiator.getExecutorURL({ @@ -47,6 +59,8 @@ 'Navigation response should not have been from the HTTP Cache.'); } + // Wait for the BiDi prefetch_status_updated event before the next assertions + await prefetchStatusPromise; await initiator.forceSinglePrefetch(url2); initiator.navigate(url2); diff --git a/speculation-rules/prefetch/redirect-middle-of-prefetch.https.html b/speculation-rules/prefetch/redirect-middle-of-prefetch.https.html index 2afa5c82a76a10..6beea27e130125 100644 --- a/speculation-rules/prefetch/redirect-middle-of-prefetch.https.html +++ b/speculation-rules/prefetch/redirect-middle-of-prefetch.https.html @@ -10,6 +10,8 @@ + + + + + + - + + @@ -25,6 +26,14 @@ setup(() => assertSpeculationRulesIsSupported()); subsetTest(promise_test, async t => { + // Subscribe to the BiDi prefetch_status_updated event and set up a promise to await it. + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + const agent = await spawnWindow(t); await agent.setReferrerPolicy("strict-origin-when-cross-origin"); const expectedReferrer = agent.getExecutorURL().origin + "/"; @@ -33,12 +42,22 @@ await agent.forceSinglePrefetch(nextURL, { referrer_policy: "strict-origin" }); await agent.navigate(nextURL); + // Wait for the BiDi prefetch_status_updated event before assertions + await prefetchStatusPromise; + const headers = await agent.getRequestHeaders(); assert_prefetched(headers, "must be prefetched"); assert_equals(headers.referer, expectedReferrer, "must send the origin as the referrer"); }, 'with "strict-origin" referrer policy in rule set overriding "strict-origin-when-cross-origin" of referring page'); subsetTest(promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + const agent = await spawnWindow(t); const next_url = agent.getExecutorURL({ page: 2 }); await agent.execute_script((url) => { @@ -49,6 +68,8 @@ await new Promise(resolve => t.step_timeout(resolve, 2000)); await agent.navigate(next_url); + await prefetchStatusPromise; + const headers = await agent.getRequestHeaders(); assert_prefetched(headers, 'must be prefetched'); const expected_referrer = next_url.origin + '/'; @@ -56,6 +77,13 @@ }, 'with "strict-origin" referrer policy in rule set override "no-referrer" of link'); subsetTest(promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + const agent = await spawnWindow(t); await agent.setReferrerPolicy("unsafe-url"); @@ -63,6 +91,8 @@ await agent.forceSinglePrefetch(nextURL, { referrer_policy: "no-referrer" }); await agent.navigate(nextURL); + await prefetchStatusPromise; + // This referring page's referrer policy would not be eligible for // cross-site prefetching, but setting a sufficiently strict policy in the // rule allows for prefetching. @@ -72,6 +102,13 @@ }, 'with "no-referrer" referrer policy in rule set overriding "unsafe-url" of cross-site referring page'); subsetTest(promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + const agent = await spawnWindow(t); await agent.setReferrerPolicy("strict-origin-when-cross-origin"); @@ -79,11 +116,20 @@ await agent.forceSinglePrefetch(nextURL, { referrer_policy: "no-referrrrrrrer" }); await agent.navigate(nextURL); + await prefetchStatusPromise; + const headers = await agent.getRequestHeaders(); assert_not_prefetched(headers, "must not be prefetched"); }, 'unrecognized policies invalidate the rule'); subsetTest(promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + const agent = await spawnWindow(t); await agent.setReferrerPolicy("strict-origin"); const expectedReferrer = agent.getExecutorURL().origin + "/"; @@ -97,12 +143,21 @@ await new Promise(resolve => t.step_timeout(resolve, 2000)); await agent.navigate(nextURL); + await prefetchStatusPromise; + const headers = await agent.getRequestHeaders(); assert_prefetched(headers, "must be prefetched"); assert_equals(headers.referer, expectedReferrer, "must send the origin as the referrer"); }, 'unrecognized policies in link referrerpolicy attribute are ignored'); subsetTest(promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + const agent = await spawnWindow(t); await agent.setReferrerPolicy("strict-origin-when-cross-origin"); @@ -110,11 +165,20 @@ await agent.forceSinglePrefetch(nextURL, { referrer_policy: "never" }); await agent.navigate(nextURL); + await prefetchStatusPromise; + const headers = await agent.getRequestHeaders(); assert_not_prefetched(headers, "must not be prefetched"); }, 'treat legacy referrer policy values as invalid'); subsetTest(promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + const agent = await spawnWindow(t); await agent.setReferrerPolicy("strict-origin"); const expectedReferrer = agent.getExecutorURL().origin + "/"; @@ -123,6 +187,8 @@ await agent.forceSinglePrefetch(nextURL, { referrer_policy: "unsafe-url" }); await agent.navigate(nextURL); + await prefetchStatusPromise; + // This referring page's referrer policy would normally make it eligible for // cross-site prefetching, but setting an unacceptable policy in the rule // makes it ineligible. @@ -132,6 +198,13 @@ }, 'with "unsafe-url" referrer policy in rule set overriding "strict-origin" of cross-site referring page'); subsetTest(promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + const agent = await spawnWindow(t); await agent.setReferrerPolicy("strict-origin"); const expectedReferrer = agent.getExecutorURL().origin + "/"; @@ -142,6 +215,8 @@ await agent.forceSinglePrefetch(nextURL, { referrer_policy: "" }); await agent.navigate(nextURL); + await prefetchStatusPromise; + const headers = await agent.getRequestHeaders(); assert_prefetched(headers, "must be prefetched"); assert_equals(headers.referer, expectedReferrer, "must send the origin as the referrer"); diff --git a/speculation-rules/prefetch/referrer-policy-not-accepted.https.html b/speculation-rules/prefetch/referrer-policy-not-accepted.https.html index 24f75bb9c3c254..b62ae8d61b89ac 100644 --- a/speculation-rules/prefetch/referrer-policy-not-accepted.https.html +++ b/speculation-rules/prefetch/referrer-policy-not-accepted.https.html @@ -8,6 +8,8 @@ + + @@ -20,6 +22,14 @@ setup(() => assertSpeculationRulesIsSupported()); subsetTest(promise_test, async t => { + // Subscribe to the BiDi prefetch_status_updated event and set up a promise to await it. + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + const agent = await spawnWindow(t); await agent.setReferrerPolicy("unsafe-url"); const expectedReferrer = agent.getExecutorURL().href; @@ -28,6 +38,9 @@ await agent.forceSinglePrefetch(nextURL); await agent.navigate(nextURL); + // Wait for the BiDi prefetch_status_updated event before assertions + await prefetchStatusPromise; + const headers = await agent.getRequestHeaders(); // The referrer policy restriction does not apply to same-site prefetch. assert_prefetched(headers, "must be prefetched"); @@ -35,6 +48,13 @@ }, 'with "unsafe-url" referrer policy on same-site referring page'); subsetTest(promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + const agent = await spawnWindow(t); await agent.setReferrerPolicy("unsafe-url"); const expectedReferrer = agent.getExecutorURL().href; @@ -44,12 +64,21 @@ await agent.forceSinglePrefetch(nextURL); await agent.navigate(nextURL); + await prefetchStatusPromise; + const headers = await agent.getRequestHeaders(); assert_not_prefetched(headers, "must not be prefetched"); assert_equals(headers.referer, expectedReferrer, "must send the full URL as the referrer"); }, 'with "unsafe-url" referrer policy on cross-site referring page'); subsetTest(promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + const agent = await spawnWindow(t); await agent.setReferrerPolicy("unsafe-url"); const expectedReferrer = agent.getExecutorURL().href; @@ -63,6 +92,8 @@ await new Promise(resolve => t.step_timeout(resolve, 2000)); await agent.navigate(nextURL); + await prefetchStatusPromise; + const headers = await agent.getRequestHeaders(); assert_not_prefetched(headers, "must not be prefetched"); assert_equals(headers.referer, expectedReferrer, "must send the full URL as the referrer"); diff --git a/speculation-rules/prefetch/referrer-policy.https.html b/speculation-rules/prefetch/referrer-policy.https.html index a1d2aa4b188de8..875a5f757a22d7 100644 --- a/speculation-rules/prefetch/referrer-policy.https.html +++ b/speculation-rules/prefetch/referrer-policy.https.html @@ -7,6 +7,8 @@ + + @@ -20,6 +22,14 @@ setup(() => assertSpeculationRulesIsSupported()); subsetTest(promise_test, async t => { + // Subscribe to the BiDi prefetch_status_updated event and set up a promise to await it. + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + const agent = await spawnWindow(t); await agent.setReferrerPolicy("strict-origin-when-cross-origin"); const expectedReferrer = agent.getExecutorURL().href; @@ -28,12 +38,22 @@ await agent.forceSinglePrefetch(nextURL); await agent.navigate(nextURL); + // Wait for the BiDi prefetch_status_updated event before assertions + await prefetchStatusPromise; + const headers = await agent.getRequestHeaders(); assert_prefetched(headers, "must be prefetched"); assert_equals(headers.referer, expectedReferrer, "must send the full URL as the referrer"); }, 'with "strict-origin-when-cross-origin" referrer policy'); subsetTest(promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + const agent = await spawnWindow(t); await agent.setReferrerPolicy("strict-origin"); const expectedReferrer = agent.getExecutorURL().origin + "/"; @@ -42,12 +62,21 @@ await agent.forceSinglePrefetch(nextURL); await agent.navigate(nextURL); + await prefetchStatusPromise; + const headers = await agent.getRequestHeaders(); assert_prefetched(headers, "must be prefetched"); assert_equals(headers.referer, expectedReferrer, "must send the origin as the referrer"); }, 'with "strict-origin" referrer policy'); subsetTest(promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + const agent = await spawnWindow(t); await agent.setReferrerPolicy("no-referrer"); @@ -55,12 +84,21 @@ await agent.forceSinglePrefetch(nextURL); await agent.navigate(nextURL); + await prefetchStatusPromise; + const headers = await agent.getRequestHeaders(); assert_prefetched(headers, "must be prefetched"); assert_equals(headers.referer, undefined, "must send no referrer"); }, 'with "no-referrer" referrer policy'); subsetTest(promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + const agent = await spawnWindow(t); await agent.setReferrerPolicy("no-referrer"); @@ -73,6 +111,8 @@ await new Promise(resolve => t.step_timeout(resolve, 2000)); await agent.navigate(next_url); + await prefetchStatusPromise; + const headers = await agent.getRequestHeaders(); const expected_referrer = next_url.origin + '/'; assert_prefetched(headers, 'must be prefetched'); diff --git a/speculation-rules/prefetch/resources/utils.sub.js b/speculation-rules/prefetch/resources/utils.sub.js index ceb5e3d06ffc0c..f6cf18d29b2983 100644 --- a/speculation-rules/prefetch/resources/utils.sub.js +++ b/speculation-rules/prefetch/resources/utils.sub.js @@ -49,6 +49,15 @@ class PrefetchAgent extends RemoteContext { // occur despite heuristic matching, etc., and await the completion of the // prefetch. async forceSinglePrefetch(url, extra = {}, wait_for_completion = true) { + // const subscribe = await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + // await(subscribe, "Prefetch status update subscription should be successful."); + + // const result = await test_driver.bidi.speculation.prefetch_status_updated.on(); + // if (result.status === 'success') { + // // Prefetch succeeded, proceed as needed + // } else if (result.status === 'failure') { + // throw new Error(`Prefetch failed: ${result.error}`); + // } return this.forceSpeculationRules( { prefetch: [{source: 'list', urls: [url], ...extra}] @@ -62,8 +71,10 @@ class PrefetchAgent extends RemoteContext { if (!wait_for_completion) { return Promise.resolve(); } - return new Promise(resolve => this.t.step_timeout(resolve, 2000)); - } + // return test_driver.bidi.speculation.prefetch_status_updated.on(); + // return test_driver.bidi.speculation.prefetch_status_updated.on(); + await new Promise(resolve => setTimeout(resolve, 1000)); +} // `url` is the URL to navigate. // diff --git a/speculation-rules/prefetch/same-origin-cookies.https.html b/speculation-rules/prefetch/same-origin-cookies.https.html index 2f93c1ebb4f5ce..ae70cec5c47927 100644 --- a/speculation-rules/prefetch/same-origin-cookies.https.html +++ b/speculation-rules/prefetch/same-origin-cookies.https.html @@ -2,7 +2,8 @@ - + + @@ -18,6 +19,14 @@ setup(() => assertSpeculationRulesIsSupported()); subsetTest(promise_test, async t => { + // Subscribe to the BiDi prefetch_status_updated event and set up a promise to await it. + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + await test_driver.delete_all_cookies(); let executor = 'cookies.py'; @@ -33,6 +42,9 @@ await agent.forceSinglePrefetch(nextUrl); await agent.navigate(nextUrl); + // Wait for the BiDi prefetch_status_updated event before assertions + await prefetchStatusPromise; + response_cookies = await agent.getResponseCookies(); request_cookies = await agent.getRequestCookies(); assert_equals(request_cookies["count"], "1"); @@ -45,6 +57,13 @@ // Regression test for https://crbug.com/1524338 subsetTest(promise_test, async t => { + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + await test_driver.delete_all_cookies(); let executor = 'cookies.py'; @@ -62,6 +81,8 @@ await agent.forceSinglePrefetch(nextUrl); await agent.navigate(nextUrl); + await prefetchStatusPromise; + response_cookies = await agent.getResponseCookies(); request_cookies = await agent.getRequestCookies(); assert_equals(request_cookies["count"], "1"); diff --git a/speculation-rules/prefetch/sec-fetch-headers.https.html b/speculation-rules/prefetch/sec-fetch-headers.https.html index 3d1d1770d789e6..e9b16d4ea69c07 100644 --- a/speculation-rules/prefetch/sec-fetch-headers.https.html +++ b/speculation-rules/prefetch/sec-fetch-headers.https.html @@ -7,6 +7,8 @@ + + + + @@ -16,6 +18,14 @@ let cross_origin = Object.fromEntries(new URLSearchParams(location.search))["cross-origin"] === "true"; promise_test(async t => { + // Subscribe to the BiDi prefetch_status_updated event and set up a promise to await it. + await test_driver.bidi.speculation.prefetch_status_updated.subscribe(); + let prefetchStatusPromise = new Promise(resolve => { + test_driver.bidi.speculation.prefetch_status_updated.on(event => { + if (event && event.status !== undefined) resolve(event.status); + }); + }); + let executor = "authenticate.py"; let credentials = { username: "user", password: "pass" }; let agent = await spawnWindow(t, { executor, ...credentials }); @@ -28,6 +38,9 @@ await agent.forceSinglePrefetch(nextUrl); await agent.navigate(nextUrl); + // Wait for the BiDi prefetch_status_updated event before assertions + await prefetchStatusPromise; + let requestHeaders = await agent.getRequestHeaders(); request_credentials = await agent.getRequestCredentials(); if (cross_origin) { diff --git a/tools/wpt/run.py b/tools/wpt/run.py index 3eee16da2d6b8e..753e7853721b23 100644 --- a/tools/wpt/run.py +++ b/tools/wpt/run.py @@ -485,6 +485,7 @@ def setup_kwargs(self, kwargs): webdriver_binary = None if webdriver_binary is None: + # logger.info("Downloading chromedriver") install = self.prompt_install("chromedriver") if install: diff --git a/tools/wptrunner/wptrunner/testdriver-extra.js b/tools/wptrunner/wptrunner/testdriver-extra.js index 4d26a14097f530..4a9ff576024790 100644 --- a/tools/wptrunner/wptrunner/testdriver-extra.js +++ b/tools/wptrunner/wptrunner/testdriver-extra.js @@ -227,9 +227,31 @@ }); } }; - + const subscribe_global = async function (params) { + return create_action("bidi.session.subscribe", { + ...params + }); + }; window.test_driver_internal.in_automation = true; + window.test_driver_internal.bidi.speculation.prefetch_status_updated.subscribe = function(params) { + return subscribe_global({ + ...params, events: ['speculation.prefetchStatusUpdated'] + }); + }; + console.log("Assigned prefetch_status_updated.subscribe in testdriver-extra.js"); + + window.test_driver_internal.bidi.speculation.prefetch_status_updated.on = function(callback) { + const on_event = (event) => { + // console.log("Testdriver extra: prefetch_status_updated event received"); + callback(event.payload); + }; + event_target.addEventListener( + 'speculation.prefetchStatusUpdated', on_event); + return () => event_target.removeEventListener( + 'speculation.prefetchStatusUpdated', on_event); + }; + window.test_driver_internal.bidi.bluetooth.handle_request_device_prompt = function(params) { return create_action('bidi.bluetooth.handle_request_device_prompt', { @@ -657,4 +679,4 @@ window.test_driver_internal.clear_display_features = function(context=null) { return create_context_action("clear_display_features", context, {}); } -})(); +})(); \ No newline at end of file