<!doctype html>
<meta charset="utf8">
<meta name="timeout" content="long">
<title>IndexedDB: origins have isolated namespaces</title>
<link rel="author" href="[email protected]" 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(
dbName, sameOriginDbNames,
`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>