<!DOCTYPE html>
<title>Test storage partitioning in fenced frames</title>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/utils.js"></script>
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="resources/utils.js"></script>
<body>
<script>
// `getter(key)` : reads the value of `key`, null if not set
// `setter(key, value)`: sets `key` to `value`
async function runTest(getter, setter) {
const key = "key";
const outer_value = "outer";
const inner_value = "inner";
// Set the value in the top-level frame, and check that it worked.
await setter(key, outer_value);
assert_equals(await getter(key), outer_value,
"Stored the value in the top-level frame.");
// Attach a fenced frame.
const frame = attachFencedFrameContext();
// Check that the outer value isn't visible.
const inner_before_set = await frame.execute(getter, [key]);
assert_equals(inner_before_set, null,
"The outer value isn't visible inside the fenced frame.");
// Set the value inside the fenced frame, and check that it worked.
await frame.execute(setter, [key, inner_value]);
const inner_after_set = await frame.execute(getter, [key]);
assert_equals(inner_after_set, inner_value,
"Stored the value in the fenced frame.");
// Check that the inner value isn't visible in the top-level frame.
assert_equals(await getter(key), outer_value,
"The inner value isn't visible outside the fenced frame.");
// Perform an embedder-initiated navigation that will fail.
const original_config = frame.config;
frame.config = new FencedFrameConfig("resources/response-204.py");
await step_timeout(() => {}, 1000);
// Check that the failed navigation didn't change the storage partition.
// (The partition nonce should be reinitialized on navigation commit.)
const inner_after_failure = await frame.execute(getter, [key]);
assert_equals(inner_after_failure, inner_value,
"The inner value is still present after the failed navigation.");
// Refresh the fenced frame from within.
await frame.execute(() => {
window.executor.suspend(() => { location.href = location.href; });
});
// Check that the storage partition is the same.
const inner_after_inner_refresh = await frame.execute(getter, [key]);
assert_equals(inner_after_inner_refresh, inner_value,
"The inner value is the same after a fencedframe-initiated refresh.");
// Refresh the fenced frame from the embedder.
await frame.execute(() => window.executor.suspend(() => {}));
frame.element.config = original_config;
// Check that there is a blank slate.
const inner_after_embedder_refresh = await frame.execute(getter, [key]);
assert_equals(inner_after_embedder_refresh, null,
"The inner value is gone after an embedder-initiated refresh.");
}
promise_test(async () => {
return runTest(
(_) => { return document.cookie || null; },
(_, value) => { document.cookie = value;}
);
}, 'document.cookie');
promise_test(async () => {
return runTest(
(key) => { return localStorage.getItem(key); },
(key, value) => { return localStorage.setItem(key, value); }
);
}, 'localStorage');
promise_test(async () => {
return runTest(
(key) => { return sessionStorage.getItem(key); },
(key, value) => { return sessionStorage.setItem(key, value); }
);
}, 'sessionStorage');
promise_test(async () => {
return runTest(
async (key) => {
const newCache = await caches.open('test-cache');
const response = await newCache.match(key);
if (!response) {
return null;
}
return response.text();
},
async (key, value) => {
const newCache = await caches.open('test-cache');
return newCache.put(key, new Response(value));
}
);
}, 'Cache API');
promise_test(async () => {
return runTest(
async (key) => {
const root = await navigator.storage.getDirectory();
const draftHandle = await root.getFileHandle(key, { create: true });
const file = await draftHandle.getFile();
const text = await file.text();
return text || null;
},
async (key, value) => {
const root = await navigator.storage.getDirectory();
const draftHandle = await root.getFileHandle(key, { create: true });
const writable = await draftHandle.createWritable()
await writable.truncate(0);
await writable.write(value);
await writable.close();
}
);
}, 'File System Access API');
promise_test(async () => {
return runTest(
async (key) => {
const openRequest = indexedDB.open('test-db', 2);
const db = await new Promise((resolve) => {
openRequest.onsuccess = (event) => {
resolve(event.target.result);
};
openRequest.onupgradeneeded = (event) => {
const db = event.target.result;
const objStore = db.createObjectStore('test-tbl', {keyPath: 'key'});
objStore.transaction.oncomplete = (event) => {
resolve(db);
};
};
});
const readRequest = db.transaction(['test-tbl'])
.objectStore('test-tbl')
.get(key);
return new Promise((resolve) => {
readRequest.onsuccess = (event) => {
if (!event.target.result) {
resolve(null);
} else {
resolve(event.target.result['value']);
}
};
});
},
async (key, value) => {
const openRequest = indexedDB.open('test-db', 2);
const db = await new Promise((resolve) => {
openRequest.onsuccess = (event) => {
resolve(event.target.result);
};
openRequest.onupgradeneeded = (event) => {
const db = event.target.result;
const objStore = db.createObjectStore('test-tbl', {keyPath: 'key'});
objStore.transaction.oncomplete = (event) => {
resolve(db);
};
};
});
const writeRequest = db.transaction(['test-tbl'], 'readwrite')
.objectStore('test-tbl')
.add({'key': key, 'value': value});
return new Promise((resolve) => {
writeRequest.onsuccess = (event) => {
resolve(event.target.result);
};
});
}
);
}, 'IndexedDB');
</script>
</body>