| function loadSharedStorageImage(data) { |
| let {key, value, hasSharedStorageWritableAttribute, isSameOrigin} = data; |
| const encodedKey = encodeURIComponent(key); |
| const encodedValue = encodeURIComponent(value); |
| const sameOriginSrc = `/shared-storage/resources/` + |
| `shared-storage-writable-pixel.png?key=${encodedKey}&value=${encodedValue}`; |
| const crossOriginSrc = |
| 'https://{{domains[www]}}:{{ports[https][0]}}' + sameOriginSrc; |
| |
| let image = document.createElement('img'); |
| image.src = isSameOrigin ? sameOriginSrc : crossOriginSrc; |
| if (hasSharedStorageWritableAttribute) { |
| image.sharedStorageWritable = true; |
| } |
| |
| const promise = new Promise((resolve, reject) => { |
| image.addEventListener('load', () => { |
| resolve(image); |
| }); |
| image.addEventListener('error', () => { |
| reject(new Error('Image load failed')); |
| }); |
| }); |
| |
| document.body.appendChild(image); |
| return promise; |
| } |
| |
| function navigateSharedStorageIframe(data) { |
| let { |
| hasSharedStorageWritableAttribute, |
| rawWriteHeader, |
| isSameOrigin, |
| expectSharedStorageWritableHeader |
| } = data; |
| const writeHeader = encodeURIComponent(rawWriteHeader); |
| const sameOriginSrc = |
| `/shared-storage/resources/shared-storage-write-notify-parent.py` + |
| `?write=${writeHeader}`; |
| const crossOriginSrc = |
| 'https://{{domains[www]}}:{{ports[https][0]}}' + sameOriginSrc; |
| |
| let frame = document.createElement('iframe'); |
| frame.src = isSameOrigin ? sameOriginSrc : crossOriginSrc; |
| if (hasSharedStorageWritableAttribute) { |
| frame.sharedStorageWritable = true; |
| } |
| |
| const expectedResult = expectSharedStorageWritableHeader ? |
| '?1' : |
| 'NO_SHARED_STORAGE_WRITABLE_HEADER'; |
| |
| function checkExpectedResult(data) { |
| assert_equals(data.sharedStorageWritableHeader, expectedResult); |
| } |
| |
| const promise = new Promise((resolve, reject) => { |
| window.addEventListener('message', async function handler(evt) { |
| if (evt.source === frame.contentWindow) { |
| checkExpectedResult(evt.data); |
| document.body.removeChild(frame); |
| window.removeEventListener('message', handler); |
| resolve(); |
| } |
| }); |
| window.addEventListener('error', () => { |
| reject(new Error('Navigation error')); |
| }); |
| }); |
| |
| document.body.appendChild(frame); |
| return promise; |
| } |
| |
| async function loadNestedSharedStorageFrameInNewFrame(data) { |
| const SCOPE = '/shared-storage/resources/shared-storage-writ'; |
| const INTERMEDIATE_FRAME_SUFFIX = |
| 'able-fetch-request-fallback-to-network-iframe.https.html' |
| const CROSS_ORIGIN = 'https://{{domains[www]}}:{{ports[https][0]}}'; |
| |
| let {key, value, hasSharedStorageWritableAttribute, isSameOrigin} = data; |
| |
| const windowPromise = new Promise((resolve, reject) => { |
| window.addEventListener('message', async function handler(evt) { |
| if (evt.data.msg && evt.data.msg === 'iframe loaded') { |
| window.removeEventListener('message', handler); |
| resolve(); |
| } |
| }); |
| window.addEventListener('error', () => { |
| reject(new Error('Navigation error')); |
| }); |
| }); |
| |
| const framePromise = new Promise((resolve, reject) => { |
| let frame = document.createElement('iframe'); |
| frame.src = SCOPE + INTERMEDIATE_FRAME_SUFFIX; |
| frame.onload = function() { |
| resolve(frame); |
| }; |
| frame.onerror = function() { |
| reject(new Error('Iframe load failed')); |
| }; |
| document.body.appendChild(frame); |
| }); |
| let frame = await framePromise; |
| |
| let rawWriteHeader = `set;key=${key};value=${value}`; |
| let writeHeader = encodeURIComponent(rawWriteHeader); |
| const sameOriginNestedSrc = `/shared-storage/resources/` + |
| `shared-storage-write.py?write=${writeHeader}`; |
| const nestedSrc = |
| isSameOrigin ? sameOriginNestedSrc : CROSS_ORIGIN + sameOriginNestedSrc; |
| |
| let nestedFrame = frame.contentWindow.loadFrame( |
| nestedSrc, hasSharedStorageWritableAttribute); |
| await windowPromise; |
| return {frame: frame, nestedFrame: nestedFrame, nestedFrameUrl: nestedSrc}; |
| } |
| |
| async function createWorkletAndVerifyDataOrigin( |
| t, data_origin, script_origin, expect_success, error_type) { |
| if (error_type) { |
| assert_false(expect_success); |
| } |
| const key = 'key0'; |
| const value = 'value0'; |
| const sameOrigin = location.origin; |
| const sameOriginScriptUrl = `/shared-storage/resources/simple-module.js`; |
| const scriptUrl = (script_origin === location.origin) ? |
| sameOriginScriptUrl : |
| script_origin + sameOriginScriptUrl; |
| let dataOrigin = sameOrigin; |
| if (data_origin === 'script-origin') { |
| dataOrigin = script_origin; |
| } else if (data_origin !== '' && data_origin !== 'context-origin') { |
| try { |
| dataOrigin = new URL(data_origin).origin; |
| } catch (e) { |
| if (e.message !== 'Failed to construct \'URL\': Invalid URL') { |
| throw e; |
| } |
| assert_false(expect_success); |
| } |
| } |
| const options = (data_origin === '') ? |
| {credentials: 'omit'} : |
| {credentials: 'omit', dataOrigin: data_origin}; |
| let success = false; |
| let error = null; |
| |
| try { |
| const worklet = await sharedStorage.createWorklet(scriptUrl, options); |
| |
| const ancestor_key = token(); |
| let url0 = |
| generateURL('/shared-storage/resources/frame0.html', [ancestor_key]); |
| |
| let select_url_result = |
| await worklet.selectURL('test-url-selection-operation', [{url: url0}], { |
| data: {'mockResult': 0, 'setKey': key, 'setValue': value}, |
| resolveToConfig: true, |
| keepAlive: true |
| }); |
| |
| assert_true(validateSelectURLResult(select_url_result, true)); |
| attachFencedFrame(select_url_result, 'opaque-ads'); |
| const result0 = await nextValueFromServer(ancestor_key); |
| assert_equals(result0, 'frame0_loaded'); |
| |
| await verifyKeyValueForOrigin(key, value, dataOrigin); |
| await deleteKeyForOrigin(key, dataOrigin); |
| success = true; |
| } catch (e) { |
| error = e; |
| assert_equals(e.name, error_type, e.message); |
| } finally { |
| assert_equals( |
| expect_success, success, |
| error ? 'expected success but error thrown: ' + error.toString() : |
| 'no error caught even though one was expected'); |
| t.done(); |
| } |
| } |