diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/file-system-access | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/file-system-access')
48 files changed, 2127 insertions, 0 deletions
diff --git a/testing/web-platform/tests/file-system-access/META.yml b/testing/web-platform/tests/file-system-access/META.yml new file mode 100644 index 0000000000..b4eaf1a49c --- /dev/null +++ b/testing/web-platform/tests/file-system-access/META.yml @@ -0,0 +1 @@ +spec: https://wicg.github.io/file-system-access/ diff --git a/testing/web-platform/tests/file-system-access/README.md b/testing/web-platform/tests/file-system-access/README.md new file mode 100644 index 0000000000..b007264cea --- /dev/null +++ b/testing/web-platform/tests/file-system-access/README.md @@ -0,0 +1,2 @@ +This directory contains tests for the +[File System Access](https://wicg.github.io/file-system-access/) specification. diff --git a/testing/web-platform/tests/file-system-access/idlharness.https.any.js b/testing/web-platform/tests/file-system-access/idlharness.https.any.js new file mode 100644 index 0000000000..ca3e92061a --- /dev/null +++ b/testing/web-platform/tests/file-system-access/idlharness.https.any.js @@ -0,0 +1,18 @@ +// META: script=/resources/WebIDLParser.js +// META: script=/resources/idlharness.js +// META: timeout=long + +'use strict'; + +idl_test( + ['file-system-access'], + ['fs', 'permissions', 'html', 'dom'], + idl_array => { + if (self.GLOBAL.isWindow()) { + idl_array.add_objects({ + Window: ['window'], + // TODO: DataTransferItem + }); + } + } +); diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-IndexedDB-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-IndexedDB-manual.https.html new file mode 100644 index 0000000000..f9a58d457f --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-IndexedDB-manual.https.html @@ -0,0 +1,13 @@ +<!doctype html> +<meta charset=utf-8> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="resources/messaging-helpers.js"></script> +<script src="resources/messaging-serialize-helpers.js"></script> +<script src="/IndexedDB/resources/support-promises.js"></script> +<script src="../fs/script-tests/FileSystemBaseHandle-IndexedDB.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-getUniqueId-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-getUniqueId-manual.https.html new file mode 100644 index 0000000000..32fc5ea103 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-getUniqueId-manual.https.html @@ -0,0 +1,10 @@ +<!doctype html> +<meta charset=utf-8> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="../fs/script-tests/FileSystemBaseHandle-getUniqueId.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-isSameEntry-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-isSameEntry-manual.https.html new file mode 100644 index 0000000000..6587bdf506 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-isSameEntry-manual.https.html @@ -0,0 +1,10 @@ +<!doctype html> +<meta charset=utf-8> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="../fs/script-tests/FileSystemBaseHandle-isSameEntry.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-BroadcastChannel-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-BroadcastChannel-manual.https.html new file mode 100644 index 0000000000..3981372df0 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-BroadcastChannel-manual.https.html @@ -0,0 +1,14 @@ +<!doctype html> +<meta charset=utf-8> +<meta name=timeout content=long> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="resources/messaging-helpers.js"></script> +<script src="resources/messaging-serialize-helpers.js"></script> +<script src="../fs/script-tests/FileSystemBaseHandle-postMessage-BroadcastChannel.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-Error-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-Error-manual.https.html new file mode 100644 index 0000000000..b872839f2a --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-Error-manual.https.html @@ -0,0 +1,16 @@ +<!doctype html> +<meta charset=utf-8> +<meta name=timeout content=long> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="resources/messaging-helpers.js"></script> +<script src="resources/messaging-blob-helpers.js"></script> +<script src="resources/messaging-serialize-helpers.js"></script> +<script src="../fs/script-tests/FileSystemBaseHandle-postMessage-Error.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-MessagePort-frames-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-MessagePort-frames-manual.https.html new file mode 100644 index 0000000000..3ec98c86c0 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-MessagePort-frames-manual.https.html @@ -0,0 +1,14 @@ +<!doctype html> +<meta charset=utf-8> +<meta name=timeout content=long> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="resources/messaging-helpers.js"></script> +<script src="resources/messaging-blob-helpers.js"></script> +<script src="resources/messaging-serialize-helpers.js"></script> +<script src="../fs/script-tests/FileSystemBaseHandle-postMessage-MessagePort-frames.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-MessagePort-windows-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-MessagePort-windows-manual.https.html new file mode 100644 index 0000000000..359f11cd17 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-MessagePort-windows-manual.https.html @@ -0,0 +1,14 @@ +<!doctype html> +<meta charset=utf-8> +<meta name=timeout content=long> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="resources/messaging-helpers.js"></script> +<script src="resources/messaging-blob-helpers.js"></script> +<script src="resources/messaging-serialize-helpers.js"></script> +<script src="../fs/script-tests/FileSystemBaseHandle-postMessage-MessagePort-windows.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-MessagePort-workers-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-MessagePort-workers-manual.https.html new file mode 100644 index 0000000000..af0d616909 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-MessagePort-workers-manual.https.html @@ -0,0 +1,15 @@ +<!doctype html> +<meta charset=utf-8> +<meta name=timeout content=long> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="resources/messaging-helpers.js"></script> +<script src="resources/messaging-blob-helpers.js"></script> +<script src="resources/messaging-serialize-helpers.js"></script> +<script src="../fs/script-tests/FileSystemBaseHandle-postMessage-MessagePort-workers.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-frames-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-frames-manual.https.html new file mode 100644 index 0000000000..1581a645a5 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-frames-manual.https.html @@ -0,0 +1,14 @@ +<!doctype html> +<meta charset=utf-8> +<meta name=timeout content=long> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="resources/messaging-helpers.js"></script> +<script src="resources/messaging-blob-helpers.js"></script> +<script src="resources/messaging-serialize-helpers.js"></script> +<script src="../fs/script-tests/FileSystemBaseHandle-postMessage-frames.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-windows-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-windows-manual.https.html new file mode 100644 index 0000000000..21fd7cb274 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-windows-manual.https.html @@ -0,0 +1,14 @@ +<!doctype html> +<meta charset=utf-8> +<meta name=timeout content=long> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="resources/messaging-helpers.js"></script> +<script src="resources/messaging-blob-helpers.js"></script> +<script src="resources/messaging-serialize-helpers.js"></script> +<script src="../fs/script-tests/FileSystemBaseHandle-postMessage-windows.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-workers-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-workers-manual.https.html new file mode 100644 index 0000000000..f30ecddd9a --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-postMessage-workers-manual.https.html @@ -0,0 +1,15 @@ +<!doctype html> +<meta charset=utf-8> +<meta name=timeout content=long> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="resources/messaging-helpers.js"></script> +<script src="resources/messaging-blob-helpers.js"></script> +<script src="resources/messaging-serialize-helpers.js"></script> +<script src="../fs/script-tests/FileSystemBaseHandle-postMessage-workers.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-remove-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-remove-manual.https.html new file mode 100644 index 0000000000..ef7edd3aa8 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemBaseHandle-remove-manual.https.html @@ -0,0 +1,10 @@ +<!doctype html> +<meta charset=utf-8> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="../fs/script-tests/FileSystemBaseHandle-remove.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-getDirectoryHandle-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-getDirectoryHandle-manual.https.html new file mode 100644 index 0000000000..e403d225ea --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-getDirectoryHandle-manual.https.html @@ -0,0 +1,10 @@ +<!doctype html> +<meta charset=utf-8> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="../fs/script-tests/FileSystemDirectoryHandle-getDirectoryHandle.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-getFileHandle-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-getFileHandle-manual.https.html new file mode 100644 index 0000000000..1fa4e11699 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-getFileHandle-manual.https.html @@ -0,0 +1,10 @@ +<!doctype html> +<meta charset=utf-8> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="../fs/script-tests/FileSystemDirectoryHandle-getFileHandle.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-iteration-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-iteration-manual.https.html new file mode 100644 index 0000000000..e8c293266c --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-iteration-manual.https.html @@ -0,0 +1,11 @@ +<!doctype html> +<meta charset=utf-8> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/common/gc.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="../fs/script-tests/FileSystemDirectoryHandle-iteration.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-move-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-move-manual.https.html new file mode 100644 index 0000000000..20c6ccfdeb --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-move-manual.https.html @@ -0,0 +1,10 @@ +<!doctype html> +<meta charset=utf-8> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="script-tests/FileSystemDirectoryHandle-move.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-partitioned-manual.https.tentative.html b/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-partitioned-manual.https.tentative.html new file mode 100644 index 0000000000..3422914e23 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-partitioned-manual.https.tentative.html @@ -0,0 +1,116 @@ +<!doctype html> +<head> + <meta charset=utf-8> + <script src="/common/dispatcher/dispatcher.js"></script> + <script src="/common/get-host-info.sub.js"></script> + <script src="/common/utils.js"></script> + <script src="/html/cross-origin-embedder-policy/credentialless/resources/common.js"></script> + <script src="/html/anonymous-iframe/resources/common.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src="resources/test-helpers.js"></script> + <script src="resources/local-fs-test-helpers.js"></script> +</head> +<body> +<script> + +const setUpChildFrame = (done) => ` + const importScript = ${importScript}; + await importScript("/resources/testharness.js"); + await importScript("/resources/testdriver.js"); + await importScript("/resources/testdriver-vendor.js"); + await importScript("/file-system-access/resources/local-fs-test-helpers.js"); + await importScript("/file-system-access/resources/test-helpers.js"); + await window.test_driver.bless( + 'show a file picker.<br />Please select an empty directory'); + await send("${done}", "done"); +`; + +const createTestDir = (name) => (done) => ` + self.showDirectoryPicker().then(async (dir) => { + await dir.getDirectoryHandle("${name}", {create: true}); + return send("${done}", "done"); + }); +`; + +const removeTestDir = (name) => (done) => ` + self.showDirectoryPicker().then(async (dir) => { + await dir.removeEntry("${name}", {recursive: true}); + return send("${done}", "done"); + }); +`; + +const assertNumEntries = (numFiles) => (done) => ` + self.showDirectoryPicker().then(async (dir) => { + assert_equals(${numFiles}, await getDirectoryEntryCount(dir)); + return send("${done}", "done"); + }); +`; + +const assertNoAccess = (done) => ` + try { + await self.showDirectoryPicker(); + await send("${done}", "unexpected"); + } catch (e) { + await send("${done}", "done"); + } +`; + +// To be resilient against tests not cleaning up properly, cleanup before +// every test. +function clearDirectories() { + const directory = await directory_promise; + for await (let entry of directory.values()) { + await directory.removeEntry( + entry.name, {recursive: entry.kind === 'directory'}); + } +} + +// The following tests make use of helper framed_test to define promise tests +// that send assertion scripts to multiple executor subframes. + +framed_test(async (t, sendTo) => { + clearDirectories(); + // Ensure we have directory picker access in all child contexts. + await sendTo(childContexts, setUpChildFrame); + await sendTo(sameSiteContexts, assertNumEntries(0)); + + // Create directory in anonymous same-site context. + await sendTo([FRAME_CONTEXT.anonymousFrameSameSite], createTestDir("test")); + + // Assert we can see the directory from all same-site contexts. + await sendTo(sameSiteContexts, assertNumEntries(1)); +}, 'getDirectoryHandle can access handles across same-site contexts.'); + +framed_test(async (t, sendTo) => { + clearDirectories(); + // Ensure we have directory picker access in all child contexts. + await sendTo(childContexts, setUpChildFrame); + await sendTo(sameSiteContexts, assertNumEntries(0)); + + // Create a test directory in a third-party same-site context. + await sendTo([FRAME_CONTEXT.thirdPartySameSite], createTestDir("file")); + await sendTo([FRAME_CONTEXT.thirdPartySameSite], assertNumEntries(1)); + + // Remove directory from an anonymous same-site context. + await sendTo([FRAME_CONTEXT.anonymousFrameSameSite], removeTestDir("file")); + // Assert third-party same-site context can no longer access directory. + await sendTo([FRAME_CONTEXT.thirdPartySameSite], assertNumEntries(0)); +}, 'Directory handles can be removed from other same-site contexts.'); + +framed_test(async (t, sendTo) => { + clearDirectories(); + // Ensure we have directory picker access in all child contexts. + await sendTo(childContexts, setUpChildFrame); + // Assert that an error is raised when attempting to access + // getDirectoryHandle. + await sendTo(crossSiteContexts, assertNoAccess); +}, 'Cross-site sub-frames can not access getDirectoryHandle.'); + +// TODO(crbug.com/1322897): Add tests for ancestor bit frames. +// TODO(crbug.com/1099413): Add tests for non-default buckets. + +</script> +</body> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-removeEntry-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-removeEntry-manual.https.html new file mode 100644 index 0000000000..cf97b9d1bb --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-removeEntry-manual.https.html @@ -0,0 +1,10 @@ +<!doctype html> +<meta charset=utf-8> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="../fs/script-tests/FileSystemDirectoryHandle-removeEntry.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-resolve-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-resolve-manual.https.html new file mode 100644 index 0000000000..25be22afaf --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemDirectoryHandle-resolve-manual.https.html @@ -0,0 +1,10 @@ +<!doctype html> +<meta charset=utf-8> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="../fs/script-tests/FileSystemDirectoryHandle-resolve.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemFileHandle-create-sync-access-handle-manual.https.tentative.html b/testing/web-platform/tests/file-system-access/local_FileSystemFileHandle-create-sync-access-handle-manual.https.tentative.html new file mode 100644 index 0000000000..4641b13a45 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemFileHandle-create-sync-access-handle-manual.https.tentative.html @@ -0,0 +1,17 @@ +<!doctype html> +<meta charset=utf-8> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="resources/messaging-helpers.js"></script> +<script src="../fs/script-tests/FileSystemFileHandle-create-sync-access-handle.js"></script> + +<script> + //This variable allows the test to differentiate between local and sandboxed + //file systems, since createSyncAccessHandle() behavior is different each one. + const file_system_type = "local"; +</script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemFileHandle-getFile-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemFileHandle-getFile-manual.https.html new file mode 100644 index 0000000000..9583032b59 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemFileHandle-getFile-manual.https.html @@ -0,0 +1,10 @@ +<!doctype html> +<meta charset=utf-8> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="../fs/script-tests/FileSystemFileHandle-getFile.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemFileHandle-move-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemFileHandle-move-manual.https.html new file mode 100644 index 0000000000..360b09c3b7 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemFileHandle-move-manual.https.html @@ -0,0 +1,10 @@ +<!doctype html> +<meta charset=utf-8> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="../fs/script-tests/FileSystemFileHandle-move.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemFileHandle-partitioned-manual.https.tentative.html b/testing/web-platform/tests/file-system-access/local_FileSystemFileHandle-partitioned-manual.https.tentative.html new file mode 100644 index 0000000000..0aefd9eca3 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemFileHandle-partitioned-manual.https.tentative.html @@ -0,0 +1,125 @@ +<!doctype html> +<meta charset=utf-8> +<meta name="timeout" content="long"> +<script src="/common/dispatcher/dispatcher.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="/common/utils.js"></script> +<script src="/html/cross-origin-embedder-policy/credentialless/resources/common.js"></script> +<script src="/html/anonymous-iframe/resources/common.js"></script> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<body> +<script> + +const setUpChildFrame = (done) => ` + const importScript = ${importScript}; + await importScript("/resources/testharness.js"); + await importScript("/resources/testdriver.js"); + await importScript("/resources/testdriver-vendor.js"); + await importScript("/file-system-access/resources/local-fs-test-helpers.js"); + await importScript("/file-system-access/resources/test-helpers.js"); + await window.test_driver.bless( + 'show a file picker.<br />Please select an empty directory'); + await send("${done}", "done"); +`; + +const createTestFile = (name, contents) => (done) => ` + self.showDirectoryPicker().then(async (dir) => { + const handle = await dir.getFileHandle("${name}", {create: true}); + const writer = await handle.createWritable(); + await writer.write(new Blob(["${contents}"])); + await writer.close(); + return send("${done}", "done"); + }); +`; + +const removeTestFile = (name) => (done) => ` + self.showDirectoryPicker().then(async (dir) => { + await dir.removeEntry("${name}"); + return send("${done}", "done"); + }); +`; + +const assertNumEntries = (numFiles) => (done) => ` + self.showDirectoryPicker().then(async (dir) => { + assert_equals(${numFiles}, await getDirectoryEntryCount(dir)); + return send("${done}", "done"); + }); +`; + +const assertFileContents = (file, contents) => (done) => ` + self.showDirectoryPicker().then(async (dir) => { + const handle = await dir.getFileHandle("${file}"); + assert_equals("${contents}", await getFileContents(handle)); + return send("${done}", "done"); + }); +`; + +const assertNoAccess = (done) => ` + try { + await self.showDirectoryPicker(); + await send("${done}", "unexpected"); + } catch (e) { + await send("${done}", "done"); + } +`; + +// To be resilient against tests not cleaning up properly, cleanup before +// every test. +function clearDirectories() { + const directory = await directory_promise; + for await (let entry of directory.values()) { + await directory.removeEntry( + entry.name, {recursive: entry.kind === 'directory'}); + } +} + +// The following tests make use of helper framed_test to define promise tests +// that send assertion scripts to multiple executor subframes. + +framed_test(async (t, sendTo) => { + clearDirectories(); + // Ensure we have directory picker access in all child contexts. + await sendTo(childContexts, setUpChildFrame); + await sendTo(sameSiteContexts, assertNumEntries(0)); + + // Create file in first-party context. + await sendTo([FRAME_CONTEXT.firstParty], createTestFile("test.txt", "abc")); + // Assert file contents from all same-site contexts. + await sendTo(sameSiteContexts, assertNumEntries(1)); + await sendTo(sameSiteContexts, assertFileContents("test.txt", "abc")); +}, 'getFileHandle can access handles across same-site contexts.'); + +framed_test(async (t, sendTo) => { + clearDirectories(); + // Ensure we have directory picker access in all child frames. + await sendTo(childContexts, setUpChildFrame); + await sendTo(sameSiteContexts, assertNumEntries(0)); + + // Create file in third-party same-site context. + await sendTo([FRAME_CONTEXT.thirdPartySameSite], createTestFile("file", "b")); + await sendTo([FRAME_CONTEXT.thirdPartySameSite], assertNumEntries(1)); + + // Remove file from an anonymous same-site context. + await sendTo([FRAME_CONTEXT.anonymousFrameSameSite], removeTestFile("file")); + // Assert third-party same-site context can no longer access file. + await sendTo([FRAME_CONTEXT.thirdPartySameSite], assertNumEntries(0)); +}, 'File handles can be removed from other same-site contexts.'); + +framed_test(async (t, sendTo) => { + clearDirectories(); + // Ensure we have directory picker access in all child contexts. + await sendTo(childContexts, setUpChildFrame); + // Assert that an error is raised when attempting to access getFileHandle. + await sendTo(crossSiteContexts, assertNoAccess); +}, 'Cross-site sub-frames cannot access getFileHandle.'); + +// TODO(crbug.com/1322897): Add tests for ancestor bit frames. +// TODO(crbug.com/1099413): Add tests for non-default buckets. + +</script> +</body> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemWritableFileStream-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemWritableFileStream-manual.https.html new file mode 100644 index 0000000000..b9f28c5b08 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemWritableFileStream-manual.https.html @@ -0,0 +1,10 @@ +<!doctype html> +<meta charset=utf-8> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="../fs/script-tests/FileSystemWritableFileStream.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemWritableFileStream-piped-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemWritableFileStream-piped-manual.https.html new file mode 100644 index 0000000000..645eddbb50 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemWritableFileStream-piped-manual.https.html @@ -0,0 +1,11 @@ +<!doctype html> +<meta charset=utf-8> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="../streams/resources/recording-streams.js"></script> +<script src="../fs/script-tests/FileSystemWritableFileStream-piped.js"></script> diff --git a/testing/web-platform/tests/file-system-access/local_FileSystemWritableFileStream-write-manual.https.html b/testing/web-platform/tests/file-system-access/local_FileSystemWritableFileStream-write-manual.https.html new file mode 100644 index 0000000000..f1c4960ad3 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/local_FileSystemWritableFileStream-write-manual.https.html @@ -0,0 +1,10 @@ +<!doctype html> +<meta charset=utf-8> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> +<script src="resources/local-fs-test-helpers.js"></script> +<script src="../fs/script-tests/FileSystemWritableFileStream-write.js"></script> diff --git a/testing/web-platform/tests/file-system-access/opaque-origin.https.window.js b/testing/web-platform/tests/file-system-access/opaque-origin.https.window.js new file mode 100644 index 0000000000..8f431a2406 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/opaque-origin.https.window.js @@ -0,0 +1,86 @@ +'use strict'; + +const kSandboxWindowUrl = 'resources/opaque-origin-sandbox.html'; + +function add_iframe(test, src, sandbox) { + const iframe = document.createElement('iframe'); + iframe.src = src; + if (sandbox !== undefined) { + iframe.sandbox = sandbox; + } + document.body.appendChild(iframe); + test.add_cleanup(() => { + iframe.remove(); + }); +} + +// Creates a data URI iframe that uses postMessage() to provide its parent +// with the test result. The iframe checks for the existence of +// |property_name| on the window. +async function verify_does_not_exist_in_data_uri_iframe( + test, property_name) { + const iframe_content = + '<script>' + + ' const is_property_name_defined = ' + + ` (self.${property_name} !== undefined);` + + ' parent.postMessage({is_property_name_defined}, "*")' + + '</script>'; + + const data_uri = `data:text/html,${encodeURIComponent(iframe_content)}`; + add_iframe(test, data_uri); + + const event_watcher = new EventWatcher(test, self, 'message'); + const message_event = await event_watcher.wait_for('message') + + assert_false(message_event.data.is_property_name_defined, + `Data URI iframes must not define '${property_name}'.`); +} + +// |kSandboxWindowUrl| sends two messages to this window. The first is the +// result of showDirectoryPicker(). The second is the result of +// navigator.storage.getDirectory(). For windows using sandbox='allow-scripts', +// both results must produce rejected promises. +async function verify_results_from_sandboxed_child_window(test) { + const event_watcher = new EventWatcher(test, self, 'message'); + + const first_message_event = await event_watcher.wait_for('message'); + assert_equals( + first_message_event.data, + 'showDirectoryPicker(): REJECTED: SecurityError'); + + const second_message_event = await event_watcher.wait_for('message'); + assert_equals(second_message_event.data, + 'navigator.storage.getDirectory(): REJECTED: SecurityError'); +} + +promise_test(async test => { + await verify_does_not_exist_in_data_uri_iframe(test, 'showDirectoryPicker'); +}, 'showDirectoryPicker() must be undefined for data URI iframes.'); + +promise_test(async test => { + await verify_does_not_exist_in_data_uri_iframe( + test, 'FileSystemDirectoryHandle'); +}, 'FileSystemDirectoryHandle must be undefined for data URI iframes.'); + +promise_test( + async test => { + add_iframe(test, kSandboxWindowUrl, /*sandbox=*/ 'allow-scripts'); + await verify_results_from_sandboxed_child_window(test); + }, + 'navigator.storage.getDirectory() and ' + + 'showDirectoryPicker() must reject in a sandboxed iframe.'); + +promise_test( + async test => { + const child_window_url = kSandboxWindowUrl + + '?pipe=header(Content-Security-Policy, sandbox allow-scripts)'; + + const child_window = window.open(child_window_url); + test.add_cleanup(() => { + child_window.close(); + }); + + await verify_results_from_sandboxed_child_window(test); + }, + 'navigator.storage.getDirectory() and ' + + 'showDirectoryPicker() must reject in a sandboxed opened window.'); diff --git a/testing/web-platform/tests/file-system-access/resources/data/testfile.txt b/testing/web-platform/tests/file-system-access/resources/data/testfile.txt new file mode 100644 index 0000000000..980a0d5f19 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/resources/data/testfile.txt @@ -0,0 +1 @@ +Hello World! diff --git a/testing/web-platform/tests/file-system-access/resources/local-fs-test-helpers.js b/testing/web-platform/tests/file-system-access/resources/local-fs-test-helpers.js new file mode 100644 index 0000000000..54961ae54b --- /dev/null +++ b/testing/web-platform/tests/file-system-access/resources/local-fs-test-helpers.js @@ -0,0 +1,182 @@ +// 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.<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; +})(); + +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); +} diff --git a/testing/web-platform/tests/file-system-access/resources/message-target-dedicated-worker.js b/testing/web-platform/tests/file-system-access/resources/message-target-dedicated-worker.js new file mode 100644 index 0000000000..26ff23ef8a --- /dev/null +++ b/testing/web-platform/tests/file-system-access/resources/message-target-dedicated-worker.js @@ -0,0 +1,9 @@ +'use strict'; + +importScripts( + 'test-helpers.js', + 'messaging-serialize-helpers.js', + 'message-target.js' +); + +add_message_event_handlers(/*receiver=*/self, /*target=*/self); diff --git a/testing/web-platform/tests/file-system-access/resources/message-target-service-worker.js b/testing/web-platform/tests/file-system-access/resources/message-target-service-worker.js new file mode 100644 index 0000000000..4a6174ae3b --- /dev/null +++ b/testing/web-platform/tests/file-system-access/resources/message-target-service-worker.js @@ -0,0 +1,9 @@ +'use strict'; + +importScripts( + 'test-helpers.js', + 'messaging-serialize-helpers.js', + 'message-target.js' +); + +add_message_event_handlers(/*receiver=*/self);
\ No newline at end of file diff --git a/testing/web-platform/tests/file-system-access/resources/message-target-shared-worker.js b/testing/web-platform/tests/file-system-access/resources/message-target-shared-worker.js new file mode 100644 index 0000000000..6829c61d4c --- /dev/null +++ b/testing/web-platform/tests/file-system-access/resources/message-target-shared-worker.js @@ -0,0 +1,14 @@ +'use strict'; + +importScripts( + 'test-helpers.js', + 'messaging-serialize-helpers.js', + 'message-target.js' +); + +self.addEventListener('connect', connect_event => { + const message_port = connect_event.ports[0]; + add_message_event_handlers( + /*receiver=*/message_port, /*target=*/message_port); + message_port.start(); +});
\ No newline at end of file diff --git a/testing/web-platform/tests/file-system-access/resources/message-target.html b/testing/web-platform/tests/file-system-access/resources/message-target.html new file mode 100644 index 0000000000..32c7f0c56c --- /dev/null +++ b/testing/web-platform/tests/file-system-access/resources/message-target.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<script src='test-helpers.js'></script> +<script src='messaging-serialize-helpers.js'></script> +<script src='message-target.js'></script> +<script id="inline_script"> + 'use strict' + + if (window.parent !== null) { + window.parent.postMessage('LOADED', { targetOrigin: '*' }); + } + + if (window.opener !== null) { + window.opener.postMessage('LOADED', { targetOrigin: '*' }); + } + + // Use an undefined message target to send responses to + // MessageEvent::source instead. + const target = undefined; + + add_message_event_handlers( + /*receiver=*/self, target, /*target_origin=*/'*'); +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/file-system-access/resources/message-target.js b/testing/web-platform/tests/file-system-access/resources/message-target.js new file mode 100644 index 0000000000..191b4748ab --- /dev/null +++ b/testing/web-platform/tests/file-system-access/resources/message-target.js @@ -0,0 +1,157 @@ +'use strict'; + +// This script depends on the following scripts: +// /file-system-access/resources/messaging-helpers.js +// /file-system-access/resources/test-helpers.js + +// add_message_event_handlers() is the helper function used to setup all +// message targets, including iframes and workers. +// +// Adds a message event handler and a message error handler to |receiver|. +// The 'data' property from received MessageEvents must include a 'type' +// property. The 'type' selects the test logic to run. Most message type +// handlers use postMessage() to respond to the sender with test results. +// The sender then validates the test results after receiving the response. +// +// Both |target| and |target_origin| are optional. |target| is used +// to send message responses back to the sender. When omitted, the +// 'source' from received MessageEvents is used instead. +// +// For window messaging, |target_origin| specifies the origin to receive +// responses. Most window tests use '*' for the |target_origin|. Worker +// and message port tests must use undefined for |target_origin| to avoid +// exceptions. +function add_message_event_handlers(receiver, target, target_origin) { + receiver.addEventListener('message', async function (message_event) { + const message_data = message_event.data; + + // Reply to the sender using the 'source' from the received MessageEvent. + let message_source = message_event.source; + if (message_source === null) { + // However, some message senders, like DedicatedWorkers, don't include + // a source. Fallback to the target when the source is null. + message_source = target; + } + + try { + switch (message_data.type) { + case 'receive-message-port': + // Receive a MessagePort to use as a message target for testing. + add_message_event_handlers( + /*receiver=*/message_data.message_port, + /*target=*/message_data.message_port); + message_data.message_port.start(); + break; + + case 'create-broadcast-channel': + // Create a BroadcastChannel to use as a message target for testing. + const broadcast_channel = + new BroadcastChannel(message_data.broadcast_channel_name); + add_message_event_handlers( + /*receiver=*/broadcast_channel, + /*target=*/broadcast_channel); + message_source.postMessage( + { type: 'broadcast-channel-created' }, + { targetOrigin: target_origin }); + break; + + case 'receive-file-system-handles': + // Receive a list of cloned FileSystemFileHandles. Access the + // properties of each FileSystemFileHandle by serializing the + // handle to a JavaScript object. Then respond with the serialized + // results, enabling the sender to verify that the cloned handle + // produced the expected property values from this execution context. + const serialized_handles = []; + const cloned_handles = message_data.cloned_handles; + for (let i = 0; i < cloned_handles.length; ++i) { + const serialized = await serialize_handle(cloned_handles[i]); + serialized_handles.push(serialized); + } + message_source.postMessage({ + type: 'receive-serialized-file-system-handles', + serialized_handles, + // Respond with the cloned handles to create new clones for + // the sender to verify. + cloned_handles, + }, { targetOrigin: target_origin }); + break; + + case 'receive-serialized-file-system-handles': + // Do nothing. This message is meant for test runner validation. + // Other message targets may receive this message while testing + // broadcast channels. + break; + + case 'create-file': + // Create a new file and then respond to the sender with it. + const directory = await navigator.storage.getDirectory(); + const file_handle = + await directory.getFileHandle('temp-file', { create: true }); + message_source.postMessage( + { type: 'receive-file', file_handle }, + { targetOrigin: target_origin }); + break; + + case 'create-directory': + // Create a new directory and then respond to the sender with it. + const parent_directory = await navigator.storage.getDirectory(); + const directory_handle = + await parent_directory.getDirectoryHandle('temp-directory', + { create: true }); + message_source.postMessage( + { type: 'receive-directory', directory_handle }, + { targetOrigin: target_origin }); + break; + + case 'create-sync-access-handle': + // Receive a file and create a sync access handle out of it. Report + // success to the sender. + let success = true; + try { + const access_handle = await message_data.file_handle.createSyncAccessHandle(); + access_handle.close(); + } catch (error) { + success = false; + } + + message_source.postMessage( + { type: 'receive-sync-access-handle-result', success }, + { targetOrigin: target_origin }); + break; + + default: + throw `Unknown message type: '${message_data.type}'`; + } + } catch (error) { + // Respond with an error to trigger a failure in the sender's + // test runner. + message_source.postMessage(`ERROR: ${error}`, + { targetOrigin: target_origin }); + } + }); + + receiver.addEventListener('messageerror', async function (message_event) { + // Select the target for message responses (see comment in 'message' event + // listener above). + let message_source = message_event.source; + if (message_source === null) { + message_source = target; + } + + try { + // Respond with the MessageEvent's property values, enabling the sender + // to verify results. + const serialized_message_error_event = + serialize_message_error_event(message_event); + message_source.postMessage({ + type: 'serialized-message-error', + serialized_message_error_event + }, { targetOrigin: target_origin }); + } catch (error) { + // Respond with an error to trigger a failure in the sender's + // test runner. + message_source.postMessage(`ERROR: ${error}`, + { targetOrigin: target_origin }); + } + }); +} diff --git a/testing/web-platform/tests/file-system-access/resources/messaging-blob-helpers.js b/testing/web-platform/tests/file-system-access/resources/messaging-blob-helpers.js new file mode 100644 index 0000000000..852f2e2d32 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/resources/messaging-blob-helpers.js @@ -0,0 +1,51 @@ +'use strict'; + +// Creates a blob URL with the contents of 'message-target.html'. Use the +// blob as an iframe src or a window.open() URL, which creates a same origin +// message target. +async function create_message_target_blob_url(test) { + const html = await create_message_target_html_without_subresources(test); + const blob = new Blob([html], { type: 'text/html' }); + return URL.createObjectURL(blob); +} + +// Creates a data URI with the contents of 'message-target.html'. Use the +// data URI as an iframe src, which creates a cross origin message target. +async function create_message_target_data_uri(test) { + const iframe_html = + await create_message_target_html_without_subresources(test); + return `data:text/html,${encodeURIComponent(iframe_html)}`; +} + +// Constructs a version of 'message-target.html' without any subresources. +// Enables the creation of blob URLs, data URIs and iframe srcdocs re-using +// the contents of 'message-target.html'. +async function create_message_target_html_without_subresources(test) { + const test_helpers_script = await fetch_text('resources/test-helpers.js'); + + const messaging_helpers_script = + await fetch_text('resources/messaging-helpers.js'); + + const messaging_serialize_helpers_script = + await fetch_text('resources/messaging-serialize-helpers.js'); + + const message_target_script = + await fetch_text('resources/message-target.js'); + + // Get the inline script code from 'message-target.html'. + const iframe = await add_iframe(test, { src: 'resources/message-target.html' }); + const iframe_script = + iframe.contentWindow.document.getElementById('inline_script').outerHTML; + iframe.remove(); + + return '<!DOCTYPE html>' + + `<script>${test_helpers_script}</script>` + + `<script>${messaging_serialize_helpers_script}</script>` + + `<script>${message_target_script}</script>` + + `${iframe_script}`; +} + +async function fetch_text(url) { + const response = await fetch(url); + return await response.text(); +} diff --git a/testing/web-platform/tests/file-system-access/resources/messaging-helpers.js b/testing/web-platform/tests/file-system-access/resources/messaging-helpers.js new file mode 100644 index 0000000000..55fc04ab81 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/resources/messaging-helpers.js @@ -0,0 +1,187 @@ +'use strict'; + +// This script depends on the following script: +// /file-system-access/resources/test-helpers.js +// /service-workers/service-worker/resources/test-helpers.sub.js + +// Define the URL constants used for each type of message target, including +// iframes and workers. +const kDocumentMessageTarget = 'resources/message-target.html'; +const kSharedWorkerMessageTarget = 'resources/message-target-shared-worker.js'; +const kServiceWorkerMessageTarget = + 'resources/message-target-service-worker.js'; +const kDedicatedWorkerMessageTarget = + 'resources/message-target-dedicated-worker.js'; + +function create_dedicated_worker(test, url) { + const dedicated_worker = new Worker(url); + test.add_cleanup(() => { + dedicated_worker.terminate(); + }); + return dedicated_worker; +} + +async function create_service_worker(test, script_url, scope) { + const registration = await service_worker_unregister_and_register( + test, script_url, scope); + test.add_cleanup(() => { + return registration.unregister(); + }); + return registration; +} + +// Creates an iframe and waits to receive a message from the iframe. +// Valid |options| include src, srcdoc and sandbox, which mirror the +// corresponding iframe element properties. +async function add_iframe(test, options) { + const iframe = document.createElement('iframe'); + + if (options.sandbox !== undefined) { + iframe.sandbox = options.sandbox; + } + + if (options.src !== undefined) { + iframe.src = options.src; + } + + if (options.srcdoc !== undefined) { + iframe.srcdoc = options.srcdoc; + } + + document.body.appendChild(iframe); + test.add_cleanup(() => { + iframe.remove(); + }); + + await wait_for_loaded_message(self); + return iframe; +} + +// Creates a child window using window.open() and waits to receive a message +// from the child window. +async function open_window(test, url) { + const child_window = window.open(url); + test.add_cleanup(() => { + child_window.close(); + }); + await wait_for_loaded_message(self); + return child_window; +} + +// Wait until |receiver| gets a message event with the data set to 'LOADED'. +// The postMessage() tests use messaging instead of the loaded event because +// cross-origin child windows from window.open() do not dispatch the loaded +// event to the parent window. +async function wait_for_loaded_message(receiver) { + const message_promise = new Promise((resolve, reject) => { + receiver.addEventListener('message', message_event => { + if (message_event.data === 'LOADED') { + resolve(); + } else { + reject('The message target must receive a "LOADED" message response.'); + } + }); + }); + await message_promise; +} + +// Sets up a new message channel. Sends one port to |target| and then returns +// the other port. +function create_message_channel(target, target_origin) { + const message_channel = new MessageChannel(); + + const message_data = + { type: 'receive-message-port', message_port: message_channel.port2 }; + target.postMessage( + message_data, + { + transfer: [message_channel.port2], + targetOrigin: target_origin + }); + message_channel.port1.start(); + return message_channel.port1; +} + +// Creates a variety of different FileSystemFileHandles for testing. +async function create_file_system_handles(test, root) { + // Create some files to use with postMessage(). + const empty_file = await createEmptyFile(test, 'empty-file', root); + const first_file = await createFileWithContents( + test, 'first-file-with-contents', 'first-text-content', root); + const second_file = await createFileWithContents( + test, 'second-file-with-contents', 'second-text-content', root); + + // Create an empty directory to use with postMessage(). + const empty_directory = await createDirectory(test, 'empty-directory', root); + + // Create a directory containing both files and subdirectories to use + // with postMessage(). + const directory_with_files = + await createDirectory(test, 'directory-with-files', root); + await createFileWithContents(test, 'first-file-in-directory', + 'first-directory-text-content', directory_with_files); + await createFileWithContents(test, 'second-file-in-directory', + 'second-directory-text-content', directory_with_files); + const subdirectory = + await createDirectory(test, 'subdirectory', directory_with_files); + await createFileWithContents(test, 'first-file-in-subdirectory', + 'first-subdirectory-text-content', subdirectory); + + return [ + empty_file, + first_file, + second_file, + // Include the same FileSystemFileHandle twice. + second_file, + empty_directory, + // Include the Same FileSystemDirectoryHandle object twice. + empty_directory, + directory_with_files + ]; +} + +// Tests sending an array of FileSystemHandles to |target| with postMessage(). +// The array includes both FileSystemFileHandles and FileSystemDirectoryHandles. +// After receiving the message, |target| accesses all cloned handles by +// serializing the properties of each handle to a JavaScript object. +// +// |target| then responds with the resulting array of serialized handles. The +// response also includes the array of cloned handles, which creates more +// clones. After receiving the response, this test runner verifies that both +// the serialized handles and the cloned handles contain the expected properties. +async function do_post_message_test( + test, root_dir, receiver, target, target_origin) { + // Create and send the handles to |target|. + const handles = + await create_file_system_handles(test, root_dir, target, target_origin); + target.postMessage( + { type: 'receive-file-system-handles', cloned_handles: handles }, + { targetOrigin: target_origin }); + + // Wait for |target| to respond with results. + const event_watcher = new EventWatcher(test, receiver, 'message'); + const message_event = await event_watcher.wait_for('message'); + const response = message_event.data; + + assert_equals(response.type, 'receive-serialized-file-system-handles', + 'The test runner must receive a "serialized-file-system-handles" ' + + `message response. Actual response: ${response}`); + + // Verify the results. + const expected_serialized_handles = await serialize_handles(handles); + + assert_equals_serialized_handles( + response.serialized_handles, expected_serialized_handles); + + await assert_equals_cloned_handles(response.cloned_handles, handles); +} + +// Runs the same test as do_post_message_test(), but uses a MessagePort. +// This test starts by establishing a message channel between the test runner +// and |target|. Afterwards, the test sends FileSystemHandles through the +// message port channel. +async function do_message_port_test(test, root_dir, target, target_origin) { + const message_port = create_message_channel(target, target_origin); + await do_post_message_test( + test, root_dir, /*receiver=*/ message_port, /*target=*/ message_port); +} diff --git a/testing/web-platform/tests/file-system-access/resources/messaging-serialize-helpers.js b/testing/web-platform/tests/file-system-access/resources/messaging-serialize-helpers.js new file mode 100644 index 0000000000..ada68f43db --- /dev/null +++ b/testing/web-platform/tests/file-system-access/resources/messaging-serialize-helpers.js @@ -0,0 +1,230 @@ +'use strict'; + +// This script depends on the following script: +// /file-system-access/resources/test-helpers.js + +// Serializes an array of FileSystemHandles where each element can be either a +// FileSystemFileHandle or FileSystemDirectoryHandle. +async function serialize_handles(handle_array) { + const serialized_handle_array = []; + for (let i = 0; i < handle_array.length; ++i) { + serialized_handle_array.push(await serialize_handle(handle_array[i])); + } + return serialized_handle_array; +} + +// Serializes either a FileSystemFileHandle or FileSystemDirectoryHandle. +async function serialize_handle(handle) { + switch (handle.kind) { + case 'directory': + return await serialize_file_system_directory_handle(handle); + case 'file': + return await serialize_file_system_file_handle(handle); + default: + throw 'Object is not a FileSystemFileHandle or ' + + `FileSystemDirectoryHandle ${handle}`; + } +} + +// Creates a dictionary for a FileSystemHandle base, which contains +// serialized properties shared by both FileSystemFileHandle and +// FileSystemDirectoryHandle. +async function serialize_file_system_handle(handle) { + const read_permission = + await handle.queryPermission({ mode: 'read' }); + + const write_permission = + await handle.queryPermission({ mode: 'readwrite' }) + + return { + kind: handle.kind, + name: handle.name, + read_permission, + write_permission + }; +} + +// Create a dictionary with each property value in FileSystemFileHandle. +// Also, reads the contents of the file to include with the returned +// dictionary. Example output: +// { +// kind: "file", +// name: "example-file-name" +// read_permission: "granted", +// write_permission: "granted", +// contents: "example-file-contents" +// } +async function serialize_file_system_file_handle(file_handle) { + const contents = await getFileContents(file_handle); + + const serialized_file_system_handle = + await serialize_file_system_handle(file_handle); + + return Object.assign(serialized_file_system_handle, { contents }); +} + +// Create a dictionary with each property value in FileSystemDirectoryHandle. +// Example output: +// { +// kind: "directory", +// name: "example-directory-name" +// read_permission: "granted", +// write_permission: "granted", +// files: [<first serialized file>, ...] +// directories: [<first serialized subdirectory>, ...] +// } +async function serialize_file_system_directory_handle(directory_handle) { + // Serialize the contents of the directory. + const serialized_files = []; + const serialized_directories = []; + for await (const child_handle of directory_handle.values()) { + const serialized_child_handle = await serialize_handle(child_handle); + if (child_handle.kind === "directory") { + serialized_directories.push(serialized_child_handle); + } else { + serialized_files.push(serialized_child_handle); + } + } + + // Order the serialized contents of the directory by name. + serialized_files.sort((left, right) => { + return left.name.localeCompare(right.name); + }); + serialized_directories.sort((left, right) => { + return left.name.localeCompare(right.name); + }); + + // Serialize the directory's common properties shared by all + // FileSystemHandles. + const serialized_file_system_handle = + await serialize_file_system_handle(directory_handle); + + return Object.assign( + serialized_file_system_handle, + { files: serialized_files, directories: serialized_directories }); +} + +// Verifies |left_array| is a clone of |right_array| where each element +// is a cloned FileSystemHandle with the same properties and contents. +async function assert_equals_cloned_handles(left_array, right_array) { + assert_equals(left_array.length, right_array.length, + 'Each array of FileSystemHandles must have the same length'); + + for (let i = 0; i < left_array.length; ++i) { + assert_not_equals(left_array[i], right_array[i], + 'Clones must create new FileSystemHandle instances.'); + + const left_serialized = await serialize_handle(left_array[i]); + const right_serialized = await serialize_handle(right_array[i]); + assert_equals_serialized_handle(left_serialized, right_serialized); + } +} + +// Verifies |left_array| is the same as |right_array| where each element +// is a serialized FileSystemHandle with the same properties. +function assert_equals_serialized_handles(left_array, right_array) { + assert_equals(left_array.length, right_array.length, + 'Each array of serialized handles must have the same length'); + + for (let i = 0; i < left_array.length; ++i) { + assert_equals_serialized_handle(left_array[i], right_array[i]); + } +} + +// Verifies each property of a serialized FileSystemFileHandle or +// FileSystemDirectoryHandle. +function assert_equals_serialized_handle(left, right) { + switch (left.kind) { + case 'directory': + assert_equals_serialized_file_system_directory_handle(left, right); + break; + case 'file': + assert_equals_serialized_file_system_file_handle(left, right); + break; + default: + throw 'Object is not a FileSystemFileHandle or ' + + `FileSystemDirectoryHandle ${left}`; + } +} + +// Compares the output of serialize_file_system_handle() for +// two FileSystemHandles. +function assert_equals_serialized_file_system_handle(left, right) { + assert_equals(left.kind, right.kind, + 'Each FileSystemHandle instance must use the expected "kind".'); + + assert_equals(left.name, right.name, + 'Each FileSystemHandle instance must use the expected "name" ' + + ' property.'); + + assert_equals(left.read_permission, right.read_permission, + 'Each FileSystemHandle instance must have the expected read ' + + ' permission.'); + + assert_equals(left.write_permission, right.write_permission, + 'Each FileSystemHandle instance must have the expected write ' + + ' permission.'); +} + +// Compares the output of serialize_file_system_file_handle() +// for two FileSystemFileHandle. +function assert_equals_serialized_file_system_file_handle(left, right) { + assert_equals_serialized_file_system_handle(left, right); + assert_equals(left.contents, right.contents, + 'Each FileSystemFileHandle instance must have the same contents.'); +} + +// Compares the output of serialize_file_system_directory_handle() +// for two FileSystemDirectoryHandles. +function assert_equals_serialized_file_system_directory_handle(left, right) { + assert_equals_serialized_file_system_handle(left, right); + + assert_equals(left.files.length, right.files.length, + 'Each FileSystemDirectoryHandle must contain the same number of ' + + 'file children'); + + for (let i = 0; i < left.files.length; ++i) { + assert_equals_serialized_file_system_file_handle( + left.files[i], right.files[i]); + } + + assert_equals(left.directories.length, right.directories.length, + 'Each FileSystemDirectoryHandle must contain the same number of ' + + 'directory children'); + + for (let i = 0; i < left.directories.length; ++i) { + assert_equals_serialized_file_system_directory_handle( + left.directories[i], right.directories[i]); + } +} + +// Creates a dictionary with interesting property values from MessageEvent. +function serialize_message_error_event(message_error_event) { + return { + data: message_error_event.data, + origin: message_error_event.origin, + last_event_id: message_error_event.lastEventId, + has_source: (message_error_event.source !== null), + ports_length: message_error_event.ports.length + }; +} + +// Compares the output of serialize_message_error_event() with an +// expected result. +function assert_equals_serialized_message_error_event( + serialized_event, expected_origin, expected_has_source) { + assert_equals(serialized_event.data, null, + 'The message error event must set the "data" property to null.'); + + assert_equals(serialized_event.origin, expected_origin, + 'The message error event must have the expected "origin" property.'); + + assert_equals(serialized_event.last_event_id, "", + 'The message error event must set the "lastEventId" property to the empty string.'); + + assert_equals(serialized_event.has_source, expected_has_source, + 'The message error event must have the expected "source" property.'); + + assert_equals(serialized_event.ports_length, 0, + 'The message error event must not contain any message ports.'); +} diff --git a/testing/web-platform/tests/file-system-access/resources/opaque-origin-sandbox.html b/testing/web-platform/tests/file-system-access/resources/opaque-origin-sandbox.html new file mode 100644 index 0000000000..f489f889b3 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/resources/opaque-origin-sandbox.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<script> + 'use strict' + + // Sends two messages to its creator: + // (1) The result of showDirectoryPicker(). + // (2) The result of navigator.storage.getDirectory(). + + function post_message(data) { + if (window.parent !== null) { + window.parent.postMessage(data, { targetOrigin: '*' }); + } + if (window.opener !== null) { + window.opener.postMessage(data, { targetOrigin: '*' }); + } + } + + try { + window.showDirectoryPicker() + .then(() => { + post_message('showDirectoryPicker(): FULFILLED'); + }).catch(error => { + post_message(`showDirectoryPicker(): REJECTED: ${error.name}`); + }); + } catch (error) { + post_message(`showDirectoryPicker(): EXCEPTION: ${error.name}`); + } + + try { + navigator.storage.getDirectory() + .then(() => { + post_message('navigator.storage.getDirectory(): FULFILLED'); + }).catch(error => { + post_message(`navigator.storage.getDirectory(): REJECTED: ${error.name}`); + }); + } catch (error) { + post_message(`navigator.storage.getDirectory(): EXCEPTION: ${error.name}`); + } +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/file-system-access/resources/test-helpers.js b/testing/web-platform/tests/file-system-access/resources/test-helpers.js new file mode 100644 index 0000000000..893cd19848 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/resources/test-helpers.js @@ -0,0 +1,80 @@ +// A special path component meaning "this directory." +const kCurrentDirectory = '.'; + +// A special path component meaning "the parent directory." +const kParentDirectory = '..'; + +// Array of separators used to separate components in hierarchical paths. +let kPathSeparators; +if (navigator.userAgent.includes('Windows NT')) { + // Windows uses both '/' and '\' as path separators. + kPathSeparators = ['/', '\\']; +} else { + kPathSeparators = ['/']; +} + +async function getFileSize(handle) { + const file = await handle.getFile(); + return file.size; +} + +async function getFileContents(handle) { + const file = await handle.getFile(); + return new Response(file).text(); +} + +async function getDirectoryEntryCount(handle) { + let result = 0; + for await (let entry of handle) { + result++; + } + return result; +} + +async function getSortedDirectoryEntries(handle) { + let result = []; + for await (let entry of handle.values()) { + if (entry.kind === 'directory') + result.push(entry.name + '/'); + else + result.push(entry.name); + } + result.sort(); + return result; +} + +async function createDirectory(test, name, parent) { + const new_dir_handle = await parent.getDirectoryHandle(name, {create: true}); + test.add_cleanup(async () => { + try { + await parent.removeEntry(name, {recursive: true}); + } catch (e) { + // Ignore any errors when removing directories, as tests might + // have already removed the directory. + } + }); + return new_dir_handle; +} + +async function createEmptyFile(test, name, parent) { + const handle = await parent.getFileHandle(name, {create: true}); + test.add_cleanup(async () => { + try { + await parent.removeEntry(name); + } catch (e) { + // Ignore any errors when removing files, as tests might already remove + // the file. + } + }); + // Make sure the file is empty. + assert_equals(await getFileSize(handle), 0); + return handle; +} + +async function createFileWithContents(test, name, contents, parent) { + const handle = await createEmptyFile(test, name, parent); + const writer = await handle.createWritable(); + await writer.write(new Blob([contents])); + await writer.close(); + return handle; +} diff --git a/testing/web-platform/tests/file-system-access/sandboxed_FileSystemDirectoryHandle-move.https.any.js b/testing/web-platform/tests/file-system-access/sandboxed_FileSystemDirectoryHandle-move.https.any.js new file mode 100644 index 0000000000..00578f1c8b --- /dev/null +++ b/testing/web-platform/tests/file-system-access/sandboxed_FileSystemDirectoryHandle-move.https.any.js @@ -0,0 +1,3 @@ +// META: script=resources/test-helpers.js +// META: script=../fs/resources/sandboxed-fs-test-helpers.js +// META: script=script-tests/FileSystemDirectoryHandle-move.js diff --git a/testing/web-platform/tests/file-system-access/script-tests/FileSystemDirectoryHandle-move.js b/testing/web-platform/tests/file-system-access/script-tests/FileSystemDirectoryHandle-move.js new file mode 100644 index 0000000000..abdbc9ef2b --- /dev/null +++ b/testing/web-platform/tests/file-system-access/script-tests/FileSystemDirectoryHandle-move.js @@ -0,0 +1,256 @@ +// META: script=resources/test-helpers.js + +'use strict'; + +directory_test(async (t, root) => { + const dir = await root.getDirectoryHandle('dir-before', {create: true}); + await dir.move('dir-after'); + + assert_array_equals(await getSortedDirectoryEntries(root), ['dir-after/']); + assert_array_equals(await getSortedDirectoryEntries(dir), []); +}, 'move(name) to rename an empty directory'); + +directory_test(async (t, root) => { + const dir = await root.getDirectoryHandle('dir-before', {create: true}); + await promise_rejects_js(t, TypeError, dir.move('')); + + assert_array_equals(await getSortedDirectoryEntries(root), ['dir-before/']); + assert_array_equals(await getSortedDirectoryEntries(dir), []); +}, 'move("") to rename an empty directory fails'); + +directory_test(async (t, root) => { + const dir = await root.getDirectoryHandle('dir-before', {create: true}); + await createFileWithContents(t, 'file-in-dir', 'abc', dir); + await dir.move('dir-after'); + + assert_array_equals(await getSortedDirectoryEntries(root), ['dir-after/']); + assert_array_equals(await getSortedDirectoryEntries(dir), ['file-in-dir']); +}, 'move(name) to rename a non-empty directory'); + +directory_test(async (t, root) => { + const dir = await root.getDirectoryHandle('dir-before', {create: true}); + await dir.move(root, 'dir-after'); + + assert_array_equals(await getSortedDirectoryEntries(root), ['dir-after/']); + assert_array_equals(await getSortedDirectoryEntries(dir), []); +}, 'move(dir, name) to rename an empty directory'); + +directory_test(async (t, root) => { + const dir = await root.getDirectoryHandle('dir-before', {create: true}); + await createFileWithContents(t, 'file-in-dir', 'abc', dir); + await dir.move(root, 'dir-after'); + + assert_array_equals(await getSortedDirectoryEntries(root), ['dir-after/']); + assert_array_equals(await getSortedDirectoryEntries(dir), ['file-in-dir']); +}, 'move(dir, name) to rename a non-empty directory'); + +directory_test(async (t, root) => { + const dir_src = await root.getDirectoryHandle('dir-src', {create: true}); + const dir_dest = await root.getDirectoryHandle('dir-dest', {create: true}); + const dir_in_dir = + await dir_src.getDirectoryHandle('dir-in-dir', {create: true}); + await dir_in_dir.move(dir_dest); + + assert_array_equals( + await getSortedDirectoryEntries(root), ['dir-dest/', 'dir-src/']); + assert_array_equals(await getSortedDirectoryEntries(dir_src), []); + assert_array_equals( + await getSortedDirectoryEntries(dir_dest), ['dir-in-dir/']); + assert_array_equals(await getSortedDirectoryEntries(dir_in_dir), []); +}, 'move(dir) to move an empty directory to a new directory'); + +directory_test(async (t, root) => { + const dir_src = await root.getDirectoryHandle('dir-src', {create: true}); + const dir_dest = await root.getDirectoryHandle('dir-dest', {create: true}); + const dir_in_dir = + await dir_src.getDirectoryHandle('dir-in-dir', {create: true}); + await dir_in_dir.move(dir_dest, 'dir-in-dir'); + + assert_array_equals( + await getSortedDirectoryEntries(root), ['dir-dest/', 'dir-src/']); + assert_array_equals(await getSortedDirectoryEntries(dir_src), []); + assert_array_equals( + await getSortedDirectoryEntries(dir_dest), ['dir-in-dir/']); + assert_array_equals(await getSortedDirectoryEntries(dir_in_dir), []); +}, 'move(dir, name) to move an empty directory to a new directory'); + +directory_test(async (t, root) => { + const dir_src = await root.getDirectoryHandle('dir-src', {create: true}); + const dir_dest = await root.getDirectoryHandle('dir-dest', {create: true}); + const dir_in_dir = + await dir_src.getDirectoryHandle('dir-in-dir', {create: true}); + const file = + await createFileWithContents(t, 'file-in-dir', 'abc', dir_in_dir); + await dir_in_dir.move(dir_dest); + + assert_array_equals( + await getSortedDirectoryEntries(root), ['dir-dest/', 'dir-src/']); + assert_array_equals(await getSortedDirectoryEntries(dir_src), []); + assert_array_equals( + await getSortedDirectoryEntries(dir_dest), ['dir-in-dir/']); + assert_array_equals( + await getSortedDirectoryEntries(dir_in_dir), ['file-in-dir']); + // `file` should be invalidated after moving directories. + await promise_rejects_dom(t, 'NotFoundError', getFileContents(file)); +}, 'move(dir) to move a non-empty directory to a new directory'); + +directory_test(async (t, root) => { + const dir_src = await root.getDirectoryHandle('dir-src', {create: true}); + const dir_dest = await root.getDirectoryHandle('dir-dest', {create: true}); + const dir_in_dir = + await dir_src.getDirectoryHandle('dir-in-dir', {create: true}); + const file = + await createFileWithContents(t, 'file-in-dir', 'abc', dir_in_dir); + await dir_in_dir.move(dir_dest, 'dir-in-dir'); + + assert_array_equals( + await getSortedDirectoryEntries(root), ['dir-dest/', 'dir-src/']); + assert_array_equals(await getSortedDirectoryEntries(dir_src), []); + assert_array_equals( + await getSortedDirectoryEntries(dir_dest), ['dir-in-dir/']); + assert_array_equals( + await getSortedDirectoryEntries(dir_in_dir), ['file-in-dir']); + // `file` should be invalidated after moving directories. + await promise_rejects_dom(t, 'NotFoundError', getFileContents(file)); +}, 'move(dir, name) to move a non-empty directory to a new directory'); + +directory_test(async (t, root) => { + const dir1 = await root.getDirectoryHandle('dir1', {create: true}); + const dir2 = await root.getDirectoryHandle('dir2', {create: true}); + const handle = await createFileWithContents(t, 'file', 'foo', root); + + await handle.move(dir1); + assert_array_equals( + await getSortedDirectoryEntries(root), ['dir1/', 'dir2/']); + assert_array_equals(await getSortedDirectoryEntries(dir1), ['file']); + assert_array_equals(await getSortedDirectoryEntries(dir2), []); + assert_equals(await getFileContents(handle), 'foo'); + + await handle.move(dir2); + assert_array_equals( + await getSortedDirectoryEntries(root), ['dir1/', 'dir2/']); + assert_array_equals(await getSortedDirectoryEntries(dir1), []); + assert_array_equals(await getSortedDirectoryEntries(dir2), ['file']); + assert_equals(await getFileContents(handle), 'foo'); + + await handle.move(root); + assert_array_equals( + await getSortedDirectoryEntries(root), ['dir1/', 'dir2/', 'file']); + assert_array_equals(await getSortedDirectoryEntries(dir1), []); + assert_array_equals(await getSortedDirectoryEntries(dir2), []); + assert_equals(await getFileContents(handle), 'foo'); +}, 'move(dir) can be called multiple times'); + +directory_test(async (t, root) => { + const dir1 = await root.getDirectoryHandle('dir1', {create: true}); + const dir2 = await root.getDirectoryHandle('dir2', {create: true}); + const handle = await createFileWithContents(t, 'file', 'foo', root); + + await handle.move(dir1, 'file-1'); + assert_array_equals( + await getSortedDirectoryEntries(root), ['dir1/', 'dir2/']); + assert_array_equals(await getSortedDirectoryEntries(dir1), ['file-1']); + assert_array_equals(await getSortedDirectoryEntries(dir2), []); + assert_equals(await getFileContents(handle), 'foo'); + + await handle.move(dir2, 'file-2'); + assert_array_equals( + await getSortedDirectoryEntries(root), ['dir1/', 'dir2/']); + assert_array_equals(await getSortedDirectoryEntries(dir1), []); + assert_array_equals(await getSortedDirectoryEntries(dir2), ['file-2']); + assert_equals(await getFileContents(handle), 'foo'); + + await handle.move(root, 'file-3'); + assert_array_equals( + await getSortedDirectoryEntries(root), ['dir1/', 'dir2/', 'file-3']); + assert_array_equals(await getSortedDirectoryEntries(dir1), []); + assert_array_equals(await getSortedDirectoryEntries(dir2), []); + assert_equals(await getFileContents(handle), 'foo'); +}, 'move(dir, name) can be called multiple times'); + +directory_test(async (t, root) => { + const handle = await createFileWithContents(t, 'file-before', 'foo', root); + const valid_name = '#$23423@352^*3243'; + await handle.move(root, valid_name); + + assert_array_equals(await getSortedDirectoryEntries(root), [valid_name]); + assert_equals(await getFileContents(handle), 'foo'); + assert_equals(await getFileSize(handle), 3); + + const contains_invalid_characters = '/file\\'; + await promise_rejects_js( + t, TypeError, handle.move(root, contains_invalid_characters)); + + assert_array_equals(await getSortedDirectoryEntries(root), [valid_name]); + assert_equals(await getFileContents(handle), 'foo'); + assert_equals(await getFileSize(handle), 3); + + const empty_name = ''; + await promise_rejects_js(t, TypeError, handle.move(root, empty_name)); + + assert_array_equals(await getSortedDirectoryEntries(root), [valid_name]); + assert_equals(await getFileContents(handle), 'foo'); + assert_equals(await getFileSize(handle), 3); + + const banned_name = '..'; + await promise_rejects_js(t, TypeError, handle.move(root, banned_name)); + + assert_array_equals(await getSortedDirectoryEntries(root), [valid_name]); + assert_equals(await getFileContents(handle), 'foo'); + assert_equals(await getFileSize(handle), 3); +}, 'move(dir, name) with a name with invalid characters should fail'); + +directory_test(async (t, root) => { + const handle = await createFileWithContents(t, 'file-before', 'foo', root); + await promise_rejects_js(t, TypeError, handle.move(root, '')); + + assert_array_equals(await getSortedDirectoryEntries(root), ['file-before']); + assert_equals(await getFileContents(handle), 'foo'); + assert_equals(await getFileSize(handle), 3); +}, 'move(dir, "") should fail'); + +directory_test(async (t, root) => { + const dir = await root.getDirectoryHandle('dir', {create: true}); + await promise_rejects_dom( + t, 'InvalidModificationError', dir.move(dir)); + + assert_array_equals(await getSortedDirectoryEntries(root), ['dir/']); + assert_array_equals(await getSortedDirectoryEntries(dir), []); +}, 'move(dir, name) to move a directory within itself fails'); + +directory_test(async (t, root) => { + const dir = await root.getDirectoryHandle('dir', {create: true}); + await promise_rejects_dom( + t, 'InvalidModificationError', dir.move(dir, 'dir-fail')); + + assert_array_equals(await getSortedDirectoryEntries(root), ['dir/']); + assert_array_equals(await getSortedDirectoryEntries(dir), []); +}, 'move(dir, name) to move a directory within itself and rename fails'); + +directory_test(async (t, root) => { + const parent_dir = + await root.getDirectoryHandle('parent-dir', {create: true}); + const child_dir = + await parent_dir.getDirectoryHandle('child-dir', {create: true}); + await promise_rejects_dom( + t, 'InvalidModificationError', parent_dir.move(child_dir)); + + assert_array_equals(await getSortedDirectoryEntries(root), ['parent-dir/']); + assert_array_equals( + await getSortedDirectoryEntries(parent_dir), ['child-dir/']); + assert_array_equals(await getSortedDirectoryEntries(child_dir), []); +}, 'move(dir) to move a directory within a descendent fails'); + +directory_test(async (t, root) => { + const parent_dir = + await root.getDirectoryHandle('parent-dir', {create: true}); + const child_dir = + await parent_dir.getDirectoryHandle('child-dir', {create: true}); + await promise_rejects_dom( + t, 'InvalidModificationError', parent_dir.move(child_dir, 'dir')); + + assert_array_equals(await getSortedDirectoryEntries(root), ['parent-dir/']); + assert_array_equals( + await getSortedDirectoryEntries(parent_dir), ['child-dir/']); + assert_array_equals(await getSortedDirectoryEntries(child_dir), []); +}, 'move(dir, name) to move a directory within a descendent fails'); diff --git a/testing/web-platform/tests/file-system-access/showDirectoryPicker-manual.https.html b/testing/web-platform/tests/file-system-access/showDirectoryPicker-manual.https.html new file mode 100644 index 0000000000..d1abf93eaf --- /dev/null +++ b/testing/web-platform/tests/file-system-access/showDirectoryPicker-manual.https.html @@ -0,0 +1,34 @@ +<!doctype html> +<meta charset=utf-8> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> + +<script> + + promise_test(async t => { + 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 directory picker.<br />Please select file-system-access/resources/data/'); + const dir = await self.showDirectoryPicker(); + assert_true(dir instanceof FileSystemHandle); + assert_true(dir instanceof FileSystemDirectoryHandle); + assert_equals(dir.kind, "directory"); + assert_equals(dir.name, 'data'); + assert_array_equals(await getSortedDirectoryEntries(dir), ['testfile.txt']); + + promise_test(async t => { + assert_equals(await dir.queryPermission(), 'granted'); + }, 'showDirectoryPicker returns correct permissions'); + }, 'showDirectoryPicker works'); + +</script> diff --git a/testing/web-platform/tests/file-system-access/showOpenFilePicker-manual.https.html b/testing/web-platform/tests/file-system-access/showOpenFilePicker-manual.https.html new file mode 100644 index 0000000000..2c8f29d7b6 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/showOpenFilePicker-manual.https.html @@ -0,0 +1,42 @@ +<!doctype html> +<meta charset=utf-8> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> + +<script> + + promise_test(async t => { + 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 file-system-access/resources/data/testfile.txt'); + const files = await self.showOpenFilePicker({ + multiple: false, types: [ + { description: 'Text files', accept: { ' text/plain ': ['.txt'] } }, + { description: 'Images', accept: { ' image/* ': ['.jpg', '.jpeg', '.png'] } }, + ], + }); + assert_true(Array.isArray(files)); + assert_equals(files.length, 1); + assert_true(files[0] instanceof FileSystemHandle); + assert_true(files[0] instanceof FileSystemFileHandle); + assert_equals(files[0].kind, "file"); + assert_equals(files[0].name, 'testfile.txt'); + assert_equals(await (await files[0].getFile()).text(), 'Hello World!\n'); + + promise_test(async t => { + assert_equals(await files[0].queryPermission(), 'granted'); + }, 'showOpenFilePicker returns correct permissions'); + }, 'showOpenFilePicker works'); + +</script> + diff --git a/testing/web-platform/tests/file-system-access/showPicker-errors.https.window.js b/testing/web-platform/tests/file-system-access/showPicker-errors.https.window.js new file mode 100644 index 0000000000..ed66e1093b --- /dev/null +++ b/testing/web-platform/tests/file-system-access/showPicker-errors.https.window.js @@ -0,0 +1,142 @@ +// META: script=resources/test-helpers.js + +promise_test(async t => { + await promise_rejects_dom(t, 'SecurityError', self.showOpenFilePicker()); +}, 'showOpenFilePicker: Showing a picker requires user activation.'); + +promise_test(async t => { + await promise_rejects_dom(t, 'SecurityError', self.showSaveFilePicker()); +}, 'showSaveFilePicker: Showing a picker requires user activation.'); + +promise_test(async t => { + await promise_rejects_dom(t, 'SecurityError', self.showDirectoryPicker()); +}, 'showDirectoryPicker: Showing a picker requires user activation.'); + +// TODO(mek): Add tests for cross-origin iframes, opaque origins, etc. + +define_file_picker_error_tests('showOpenFilePicker'); +define_file_picker_error_tests('showSaveFilePicker'); + +function define_file_picker_error_tests(showPickerMethod) { + promise_test(async t => { + await promise_rejects_js( + t, TypeError, + self[showPickerMethod]({excludeAcceptAllOption: true, types: []})); + }, showPickerMethod + ': File picker requires at least one accepted type.'); + + promise_test(async t => { + await promise_rejects_js( + t, TypeError, + self[showPickerMethod]({types: [{accept: {'': ['.foo']}}]})); + await promise_rejects_js( + t, TypeError, + self[showPickerMethod]({types: [{accept: {' ': ['.foo']}}]})); + }, showPickerMethod + ': MIME type can\'t be an empty string.'); + + promise_test(async t => { + await promise_rejects_js( + t, TypeError, + self[showPickerMethod]({types: [{accept: {'image': ['.foo']}}]})); + }, showPickerMethod + ': MIME type must have subtype.'); + + promise_test(async t => { + await promise_rejects_js( + t, TypeError, + self[showPickerMethod]({types: [{accept: {' /plain': ['.foo']}}]})); + }, showPickerMethod + ': MIME type can\'t have empty type.'); + + promise_test(async t => { + await promise_rejects_js( + t, TypeError, + self[showPickerMethod]({types: [{accept: {'image/ ': ['.foo']}}]})); + }, showPickerMethod + ': MIME type can\'t have empty subtype.'); + + promise_test(async t => { + await promise_rejects_js( + t, TypeError, + self[showPickerMethod]( + {types: [{accept: {'text/plain;charset=utf8': ['.txt']}}]})); + }, showPickerMethod + ': MIME type can\'t have parameters.'); + + promise_test(async t => { + await promise_rejects_js(t, TypeError, self[showPickerMethod]({ + types: [{accept: {'text>foo/plain': ['.txt']}}] + })); + }, showPickerMethod + ': MIME type can\'t have invalid characters in type.'); + + promise_test(async t => { + await promise_rejects_js( + t, TypeError, + self[showPickerMethod]({types: [{accept: {'text / plain': ['.txt']}}]})); + }, showPickerMethod + ': MIME type can\'t have whitespace in the middle.'); + + promise_test( + async t => { + await promise_rejects_js( + t, TypeError, + self[showPickerMethod]( + {types: [{accept: {'text/plain>foo': ['.txt']}}]})); + }, + showPickerMethod + + ': MIME type can\'t have invalid characters in subtype.'); + + promise_test(async t => { + await promise_rejects_js(t, TypeError, self[showPickerMethod]({ + startIn: 'secrets', + })); + }, showPickerMethod + ': unknown well-known starting directory.'); + + promise_test(async t => { + await promise_rejects_js(t, TypeError, self[showPickerMethod]({ + startIn: null, + })); + }, showPickerMethod + ': starting directory can\t be null.'); + + promise_test(async t => { + await promise_rejects_js(t, TypeError, self[showPickerMethod]({ + id: "inv*l:d\\ chara<ters", + })); + }, showPickerMethod + ': starting directory ID contains invalid characters.'); + + promise_test(async t => { + await promise_rejects_js(t, TypeError, self[showPickerMethod]({ + id: "id-length-cannot-exceed-32-characters", + })); + }, showPickerMethod + ': starting directory ID cannot exceed 32 characters.'); + + const invalid_extensions = { + '.extensiontoolong': 'extension length more than 16.', + '.txt.': 'extenstion ends with "."', + 'txt': 'extenstion does not start with "."', + '.$txt' : 'illegal character "$"', + '.t<xt': 'illegal character "<"', + '.t/xt': 'illegal character "\"', + '.\txt': 'illegal character "/"', + '.txt\\': 'illegal characters "\\"', + '.txt?': 'illegal character "?"', + '.txt*': 'illegal character "*"', + '.{txt': 'illegal character "{"', + '.}txt': 'illegal character "}"', + ' .txt': 'illegal whitespace at front of extension', + '. txt': 'illegal whitespace in extension', + '.txt ': 'illegal whitespace at end of extension', + '.\u202etxt\u202e' : 'illegal RTL character', + '.t\u00E6xt': 'non-ASCII character "æ"', + '.קום': 'non-ASCII character "קום"', + '.txt🙂': 'non-ASCII character "🙂"', + '.{txt}': 'illegal characters "{" and "}"', + } + + for (const [extension, description] of Object.entries(invalid_extensions)) { + define_file_picker_extension_error_test(showPickerMethod, extension, description) + } +} + +function define_file_picker_extension_error_test(showPickerMethod, extension, description) { + promise_test(async t => { + await promise_rejects_js( + t, TypeError, + self[showPickerMethod]( + { types: [{ accept: { 'text/plain': ['.txt', extension] } }] })); + }, showPickerMethod + ': invalid extension "' + extension + '". ' + description + "."); +}
\ No newline at end of file diff --git a/testing/web-platform/tests/file-system-access/showSaveFilePicker-manual.https.html b/testing/web-platform/tests/file-system-access/showSaveFilePicker-manual.https.html new file mode 100644 index 0000000000..332a1a4273 --- /dev/null +++ b/testing/web-platform/tests/file-system-access/showSaveFilePicker-manual.https.html @@ -0,0 +1,33 @@ +<!doctype html> +<meta charset=utf-8> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/test-helpers.js"></script> + +<script> + + promise_test(async t => { + await window.test_driver.bless( + 'show a file picker.<br />Please make a copy of file-system-access/resources/data/testfile.txt in a writable directory and pick that file'); + const file = await self.showSaveFilePicker({ + multiple: false, types: [ + { description: 'Text files', accept: { 'text/plain': ['.txt'] } }, + ], + }); + assert_true(file instanceof FileSystemHandle); + assert_true(file instanceof FileSystemFileHandle); + assert_equals(file.kind, "file"); + assert_equals(file.name, 'testfile.txt'); + assert_equals(await (await file.getFile()).text(), '', + 'showSaveFilePicker should clear contents of file'); + + promise_test(async t => { + assert_equals(await file.queryPermission(), 'granted'); + assert_equals(await file.queryPermission({ mode: 'readwrite' }), 'granted'); + }, 'showSaveFilePicker returns correct permissions'); + }, 'showSaveFilePicker works'); + +</script> |