| <!doctype html> |
| <meta charset="utf8"> |
| <meta name="timeout" content="long"> |
| <title>IndexedDB: origins have isolated namespaces</title> |
| <link rel="author" href="pwnall@chromium.org" title="Victor Costan"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="../common/get-host-info.sub.js"></script> |
| <script src="resources/support-promises.js"></script> |
| |
| <body> |
| <script> |
| 'use strict'; |
| |
| // Returns a Promise that resolves with the helper's response. |
| function waitForCrossOriginHelperResponse(origin, request) { |
| return new Promise((resolve, reject) => { |
| self.addEventListener('message', event => { |
| if (event.origin !== origin) { |
| reject(new Error(`Unexpected message from ${event.origin}`)); |
| return; |
| } |
| |
| if (event.data.action === request.action) { |
| resolve(event.data.response); |
| } else { |
| reject(new Error(`Unexpected message ${JSON.stringify(event.data)}`)); |
| } |
| }, { once: true }); |
| }); |
| } |
| |
| // Returns a Promise that resolves with the helper's response. |
| async function crossOriginIframeHelper(testCase, origin, request) { |
| const iframe = document.createElement('iframe'); |
| iframe.src = origin + '/IndexedDB/resources/cross-origin-helper-frame.html'; |
| document.body.appendChild(iframe); |
| testCase.add_cleanup(() => { |
| try { |
| document.body.removeChild(iframe); |
| } catch (e) { |
| // removeChild() throws if the iframe was already removed, which happens |
| // if this method runs to completion. |
| } |
| }); |
| |
| await new Promise((resolve, reject) => { |
| iframe.onload = resolve; |
| iframe.onerror = reject; |
| }); |
| |
| iframe.contentWindow.postMessage(request, origin); |
| const response = await waitForCrossOriginHelperResponse(origin, request); |
| document.body.removeChild(iframe); |
| return response; |
| }; |
| |
| // Returns a Promise that resolves with the helper's response. |
| async function crossOriginWindowHelper(testCase, origin, request) { |
| const helperWindow = window.open( |
| origin + '/IndexedDB/resources/cross-origin-helper-frame.html', |
| '_blank'); |
| testCase.add_cleanup(() => { helperWindow.close(); }); |
| |
| await new Promise((resolve, reject) => { |
| self.addEventListener('message', event => { |
| if (event.origin !== origin) { |
| reject(new Error(`Unexpected message from ${event.origin}`)); |
| return; |
| } |
| |
| if (event.data.action === null && event.data.response === 'ready') { |
| resolve(event.data.response); |
| } else { |
| reject(new Error(`Unexpected message ${JSON.stringify(event.data)}`)); |
| } |
| }, { once: true }); |
| }); |
| |
| helperWindow.postMessage(request, origin); |
| const response = await waitForCrossOriginHelperResponse(origin, request); |
| helperWindow.close(); |
| return response; |
| }; |
| |
| // Returns a Promise that resolves with the helper's response. |
| function crossOriginHelper(testCase, mode, origin, request) { |
| switch (mode) { |
| case 'iframe': |
| return crossOriginIframeHelper(testCase, origin, request); |
| case 'window': |
| return crossOriginWindowHelper(testCase, origin, request); |
| default: |
| throw new Error(`Unsupported cross-origin helper mode ${mode}`); |
| } |
| } |
| |
| const sameOrigin = get_host_info().ORIGIN; |
| const otherOrigin = get_host_info().REMOTE_ORIGIN; |
| |
| // The disclosure that inspired this test demonstrated leaked open database |
| // connections across windows. |
| for (const databaseKind of ['open', 'closed']) { |
| for (const mode of ['iframe', 'window']) { |
| promise_test(async testCase => { |
| const dbName = databaseName(testCase); |
| |
| assert_true( |
| await crossOriginHelper( |
| testCase, mode, sameOrigin, |
| {action: 'delete-database', name: dbName}), |
| 'Same-origin setup error'); |
| assert_true( |
| await crossOriginHelper( |
| testCase, mode, otherOrigin, |
| { action: 'delete-database', name: dbName }), |
| 'Cross-origin setup error'); |
| |
| const db = await createNamedDatabase(testCase, dbName, database => { |
| database.createObjectStore('store'); |
| }); |
| |
| if (databaseKind === 'closed') |
| await db.close(); |
| |
| const sameOriginDbNames = await crossOriginHelper( |
| testCase, mode, sameOrigin, { action: 'get-database-names' }); |
| assert_in_array( |
| sameOriginDbNames, dbName, |
| `Database creation should reflect in same-origin ${mode}`); |
| |
| const otherOriginDbNames = await crossOriginHelper( |
| testCase, mode, otherOrigin, { action: 'get-database-names' }); |
| assert_true( |
| otherOriginDbNames.indexOf(dbName) === -1, |
| `Database creation should not impact cross-origin ${mode} list`); |
| |
| if (databaseKind !== 'closed') |
| await db.close(); |
| }, `${databaseKind} database names don't leak to cross-origin ${mode}`); |
| } |
| } |
| </script> |
| </body> |