// 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. 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.
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; })(); function directory_test(func, description) { promise_test(async t => { const directory = await directory_promise; // To be resilient against tests not cleaning up properly, cleanup before // every test. for await (let entry of directory.values()) { await directory.removeEntry( entry.name, {recursive: entry.kind === 'directory'}); } 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 newAnonymousIframe(same_site_origin), // anonymousFrameSameSite null, // anonymousFrameSameSite_AncestorBit newAnonymousIframe(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(newAnonymousIframe(cross_site_origin), child_frame_js(same_site_origin, "newAnonymousIframe", 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); }