1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
|
// This file defines a directory_test() function that can be used to define
// tests that require a FileSystemDirectoryHandle. The implementation of that
// function in this file will ask the user to select an empty directory and uses
// that directory.
//
// Another implementation of this function exists in
// fs/resources/sandboxed-fs-test-helpers.js, where that version uses the
// sandboxed file system instead.
function getFileSystemType() {
return 'local';
}
const directory_promise = (async () => {
await new Promise(resolve => {
window.addEventListener('DOMContentLoaded', resolve);
});
// Small delay to give chrome's test automation a chance to actually install
// itself.
await new Promise(resolve => step_timeout(resolve, 100));
await window.test_driver.bless(
'show a file picker.<br />Please select an empty directory');
const entries = await self.showDirectoryPicker();
assert_true(entries instanceof FileSystemHandle);
assert_true(entries instanceof FileSystemDirectoryHandle);
for await (const entry of entries) {
assert_unreached('Selected directory is not empty');
}
return entries;
})();
async function cleanupDirectory(dir, ignoreRejections) {
// Get a snapshot of the entries.
const entries = await Array.fromAsync(dir.values());
// Call removeEntry on all of them.
const remove_entry_promises = entries.map(
entry =>
dir.removeEntry(entry.name, {recursive: entry.kind === 'directory'}));
// Wait for them all to resolve or reject.
if (ignoreRejections) {
await Promise.allSettled(remove_entry_promises);
} else {
await Promise.all(remove_entry_promises);
}
}
function directory_test(func, description) {
promise_test(async t => {
const directory = await directory_promise;
// To be extra resilient against bad tests, cleanup before every test.
await cleanupDirectory(directory, /*ignoreRejections=*/ false);
// Cleanup after every test.
t.add_cleanup(async () => {
// Ignore any rejections since other cleanup code may have deleted them
// before we could.
await cleanupDirectory(directory, /*ignoreRejections=*/ true);
});
await func(t, directory);
}, description);
}
directory_test(async (t, dir) => {
assert_equals(await dir.queryPermission({mode: 'read'}), 'granted');
}, 'User succesfully selected an empty directory.');
directory_test(async (t, dir) => {
const status = await dir.queryPermission({mode: 'readwrite'});
if (status == 'granted')
return;
await window.test_driver.bless('ask for write permission');
assert_equals(await dir.requestPermission({mode: 'readwrite'}), 'granted');
}, 'User granted write access.');
const child_frame_js = (origin, frameFn, done) => `
const importScript = ${importScript};
await importScript("/html/cross-origin-embedder-policy/credentialless" +
"/resources/common.js");
await importScript("/html/anonymous-iframe/resources/common.js");
await importScript("/common/utils.js");
await send("${done}", ${frameFn}("${origin}"));
`;
/**
* Context identifiers for executor subframes of framed tests. Individual
* contexts (or convenience context lists below) can be used to send JavaScript
* for evaluation in each frame (see framed_test below).
*
* Note that within framed tests:
* - firstParty represents the top-level document.
* - thirdParty represents an embedded context (iframe).
* - ancestorBit contexts include a cross-site ancestor iframe.
* - anonymousFrame contexts are third-party anonymous iframe contexts.
*/
const FRAME_CONTEXT = {
firstParty: 0,
thirdPartySameSite: 1,
thirdPartySameSite_AncestorBit: 2,
thirdPartyCrossSite: 3,
anonymousFrameSameSite: 4,
anonymousFrameSameSite_AncestorBit: 5,
anonymousFrameCrossSite: 6,
};
// TODO(crbug.com/1322897): Add AncestorBit contexts.
const sameSiteContexts = [
FRAME_CONTEXT.firstParty,
FRAME_CONTEXT.thirdPartySameSite,
FRAME_CONTEXT.anonymousFrameSameSite,
];
// TODO(crbug.com/1322897): Add AncestorBit contexts.
const crossSiteContexts = [
FRAME_CONTEXT.thirdPartyCrossSite,
FRAME_CONTEXT.anonymousFrameCrossSite,
];
// TODO(crbug.com/1322897): Add AncestorBit contexts.
const childContexts = [
FRAME_CONTEXT.thirdPartySameSite,
FRAME_CONTEXT.thirdPartyCrossSite,
FRAME_CONTEXT.anonymousFrameSameSite,
FRAME_CONTEXT.anonymousFrameCrossSite,
];
/**
* Creates a promise test with same- & cross-site executor subframes.
*
* In addition to the standard testing object, the provided func will be called
* with a sendTo function. sendTo expects:
* - contexts: an Iterable of FRAME_CONTEXT constants representing the
* frame(s) in which the provided script will be concurrently run.
* - js_gen: a function which should generate a script string when called
* with a string token. sendTo will wait until a "done" message
* is sent to this queue.
*/
function framed_test(func, description) {
const same_site_origin = get_host_info().HTTPS_ORIGIN;
const cross_site_origin = get_host_info().HTTPS_NOTSAMESITE_ORIGIN;
const frames = Object.values(FRAME_CONTEXT);
promise_test(async (t) => {
return new Promise(async (resolve, reject) => {
try {
// Set up handles to all third party frames.
const handles = [
null, // firstParty
newIframe(same_site_origin), // thirdPartySameSite
null, // thirdPartySameSite_AncestorBit
newIframe(cross_site_origin), // thirdPartyCrossSite
newIframeCredentialless(same_site_origin), // anonymousFrameSameSite
null, // anonymousFrameSameSite_AncestorBit
newIframeCredentialless(
cross_site_origin), // anonymousFrameCrossSite
];
// Set up nested SameSite frames for ancestor bit contexts.
const setUpQueue = token();
send(newIframe(cross_site_origin),
child_frame_js(same_site_origin, "newIframe", setUpQueue));
handles[FRAME_CONTEXT.thirdPartySameSite_AncestorBit] =
await receive(setUpQueue);
send(
newIframeCredentialless(cross_site_origin),
child_frame_js(
same_site_origin, 'newIframeCredentialless', setUpQueue));
handles[FRAME_CONTEXT.anonymousFrameSameSite_AncestorBit] =
await receive(setUpQueue);
const sendTo = (contexts, js_generator) => {
// Send to all contexts in parallel to minimize timeout concerns.
return Promise.all(contexts.map(async (context) => {
const queue = token();
const js_string = js_generator(queue, context);
switch (context) {
case FRAME_CONTEXT.firstParty:
// Code is executed directly in this frame via eval() rather
// than in a new context to avoid differences in API access.
eval(`(async () => {${js_string}})()`);
break;
case FRAME_CONTEXT.thirdPartySameSite:
case FRAME_CONTEXT.thirdPartyCrossSite:
case FRAME_CONTEXT.anonymousFrameSameSite:
case FRAME_CONTEXT.anonymousFrameCrossSite:
case FRAME_CONTEXT.thirdPartySameSite_AncestorBit:
case FRAME_CONTEXT.anonymousFrameSameSite_AncestorBit:
send(handles[context], js_string);
break;
default:
reject(`Cannot execute in context: ${context}`);
}
if (await receive(queue) != "done") {
reject(`Script failed in frame ${context}: ${js_string}`);
}
}));
};
await func(t, sendTo);
} catch (e) {
reject(e);
}
resolve();
});
}, description);
}
|