diff options
Diffstat (limited to 'testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js')
-rw-r--r-- | testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js new file mode 100644 index 0000000000..a1d18d108e --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js @@ -0,0 +1,229 @@ +// Helpers called on the main test HTMLs. +// Functions in `RemoteContext.execute_script()`'s 1st argument are evaluated +// on the executors (`executor.html`), and helpers available on the executors +// are defined in `executor.html`. + +const originSameOrigin = + location.protocol === 'http:' ? + 'http://{{host}}:{{ports[http][0]}}' : + 'https://{{host}}:{{ports[https][0]}}'; +const originSameSite = + location.protocol === 'http:' ? + 'http://{{host}}:{{ports[http][1]}}' : + 'https://{{host}}:{{ports[https][1]}}'; +const originCrossSite = + location.protocol === 'http:' ? + 'http://{{hosts[alt][www]}}:{{ports[http][0]}}' : + 'https://{{hosts[alt][www]}}:{{ports[https][0]}}'; + +const executorPath = + '/html/browsers/browsing-the-web/back-forward-cache/resources/executor.html?uuid='; + +// Asserts that the executor `target` is (or isn't, respectively) +// restored from BFCache. These should be used in the following fashion: +// 1. Call prepareNavigation() on the executor `target`. +// 2. Navigate the executor to another page. +// 3. Navigate back to the executor `target`. +// 4. Call assert_bfcached() or assert_not_bfcached() on the main test HTML. +async function assert_bfcached(target) { + const status = await getBFCachedStatus(target); + assert_implements_optional(status === 'BFCached', + "Could have been BFCached but actually wasn't"); +} + +async function assert_not_bfcached(target) { + const status = await getBFCachedStatus(target); + assert_implements(status !== 'BFCached', + 'Should not be BFCached but actually was'); +} + +async function getBFCachedStatus(target) { + const [loadCount, isPageshowFired, isPageshowPersisted] = + await target.execute_script(() => [ + window.loadCount, window.isPageshowFired, window.isPageshowPersisted]); + + if (loadCount === 1 && isPageshowFired === true && + isPageshowPersisted === true) { + return 'BFCached'; + } else if (loadCount === 2 && isPageshowFired === false) { + return 'Not BFCached'; + } else { + // This can occur for example when this is called before first navigating + // away (loadCount = 1, isPageshowFired = false), e.g. when + // 1. sending a script for navigation and then + // 2. calling getBFCachedStatus() without waiting for the completion of + // the script on the `target` page. + assert_unreached( + `Got unexpected BFCache status: loadCount = ${loadCount}, ` + + `isPageshowFired = ${isPageshowFired}, ` + + `isPageshowPersisted = ${isPageshowPersisted}`); + } +} + +// Always call `await remoteContext.execute_script(waitForPageShow);` after +// triggering to navigation to the page, to wait for pageshow event on the +// remote context. +const waitForPageShow = () => window.pageShowPromise; + +// Run a test that navigates A->B->A: +// 1. Page A is opened by `params.openFunc(url)`. +// 2. `params.funcBeforeNavigation(params.argsBeforeNavigation)` is executed +// on page A. +// 3. The window is navigated to page B on `params.targetOrigin`. +// 4. The window is back navigated to page A (expecting BFCached). +// +// Events `params.events` (an array of strings) are observed on page A and +// `params.expectedEvents` (an array of strings) is expected to be recorded. +// See `event-recorder.js` for event recording. +// +// Parameters can be omitted. See `defaultParams` below for default. +function runEventTest(params, description) { + const defaultParams = { + openFunc(url) { + window.open( + `${url}&events=${this.events.join(',')}`, + '_blank', + 'noopener' + ) + }, + events: ['pagehide', 'pageshow', 'load'], + expectedEvents: [ + 'window.load', + 'window.pageshow', + 'window.pagehide.persisted', + 'window.pageshow.persisted' + ], + async funcAfterAssertion(pageA) { + assert_array_equals( + await pageA.execute_script(() => getRecordedEvents()), + this.expectedEvents); + } + } + // Apply defaults. + params = { ...defaultParams, ...params }; + + runBfcacheTest(params, description); +} + +async function navigateAndThenBack(pageA, pageB, urlB, + funcBeforeBackNavigation, + argsBeforeBackNavigation) { + await pageA.execute_script( + (url) => { + prepareNavigation(() => { + location.href = url; + }); + }, + [urlB] + ); + + await pageB.execute_script(waitForPageShow); + if (funcBeforeBackNavigation) { + await pageB.execute_script(funcBeforeBackNavigation, + argsBeforeBackNavigation); + } + await pageB.execute_script( + () => { + prepareNavigation(() => { history.back(); }); + } + ); + + await pageA.execute_script(waitForPageShow); +} + +function runBfcacheTest(params, description) { + const defaultParams = { + openFunc: url => window.open(url, '_blank', 'noopener'), + scripts: [], + funcBeforeNavigation: () => {}, + argsBeforeNavigation: [], + targetOrigin: originCrossSite, + funcBeforeBackNavigation: () => {}, + argsBeforeBackNavigation: [], + shouldBeCached: true, + funcAfterAssertion: () => {}, + } + // Apply defaults. + params = {...defaultParams, ...params }; + + promise_test(async t => { + const pageA = new RemoteContext(token()); + const pageB = new RemoteContext(token()); + + const urlA = executorPath + pageA.context_id; + const urlB = params.targetOrigin + executorPath + pageB.context_id; + + // So that tests can refer to these URLs for assertions if necessary. + pageA.url = originSameOrigin + urlA; + pageB.url = urlB; + + params.openFunc(urlA); + + await pageA.execute_script(waitForPageShow); + + for (const src of params.scripts) { + await pageA.execute_script((src) => { + const script = document.createElement("script"); + script.src = src; + document.head.append(script); + return new Promise(resolve => script.onload = resolve); + }, [src]); + } + + await pageA.execute_script(params.funcBeforeNavigation, + params.argsBeforeNavigation); + await navigateAndThenBack(pageA, pageB, urlB, + params.funcBeforeBackNavigation, + params.argsBeforeBackNavigation); + + if (params.shouldBeCached) { + await assert_bfcached(pageA); + } else { + await assert_not_bfcached(pageA); + } + + if (params.funcAfterAssertion) { + await params.funcAfterAssertion(pageA, pageB, t); + } + }, description); +} + +// Call clients.claim() on the service worker +async function claim(t, worker) { + const channel = new MessageChannel(); + const saw_message = new Promise(function(resolve) { + channel.port1.onmessage = t.step_func(function(e) { + assert_equals(e.data, 'PASS', 'Worker call to claim() should fulfill.'); + resolve(); + }); + }); + worker.postMessage({type: "claim", port: channel.port2}, [channel.port2]); + await saw_message; +} + +// Assigns the current client to a local variable on the service worker. +async function storeClients(t, worker) { + const channel = new MessageChannel(); + const saw_message = new Promise(function(resolve) { + channel.port1.onmessage = t.step_func(function(e) { + assert_equals(e.data, 'PASS', 'storeClients'); + resolve(); + }); + }); + worker.postMessage({type: "storeClients", port: channel.port2}, [channel.port2]); + await saw_message; +} + +// Call storedClients.postMessage("") on the service worker +async function postMessageToStoredClients(t, worker) { + const channel = new MessageChannel(); + const saw_message = new Promise(function(resolve) { + channel.port1.onmessage = t.step_func(function(e) { + assert_equals(e.data, 'PASS', 'postMessageToStoredClients'); + resolve(); + }); + }); + worker.postMessage({type: "postMessageToStoredClients", + port: channel.port2}, [channel.port2]); + await saw_message; +} |