summaryrefslogtreecommitdiffstats
path: root/dom/filesystem/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/filesystem/tests
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--dom/filesystem/tests/filesystem_commons.js180
-rw-r--r--dom/filesystem/tests/mochitest.ini12
-rw-r--r--dom/filesystem/tests/moz.build7
-rw-r--r--dom/filesystem/tests/script_fileList.js176
-rw-r--r--dom/filesystem/tests/script_promptHandler.js18
-rw-r--r--dom/filesystem/tests/test_basic.html118
-rw-r--r--dom/filesystem/tests/test_bug1319088.html65
-rw-r--r--dom/filesystem/tests/test_webkitdirectory.html309
-rw-r--r--dom/filesystem/tests/test_worker_basic.html73
-rw-r--r--dom/filesystem/tests/worker_basic.js50
10 files changed, 1008 insertions, 0 deletions
diff --git a/dom/filesystem/tests/filesystem_commons.js b/dom/filesystem/tests/filesystem_commons.js
new file mode 100644
index 0000000000..af0ce36339
--- /dev/null
+++ b/dom/filesystem/tests/filesystem_commons.js
@@ -0,0 +1,180 @@
+function createPath(parentDir, dirOrFile) {
+ return parentDir.path + (parentDir.path == "/" ? "" : "/") + dirOrFile.name;
+}
+
+function createRelativePath(parentDir, dirOrFile) {
+ let path = createPath(parentDir, dirOrFile);
+ is(path[0], "/", "The full path should start with '/'");
+ return path.substring(1);
+}
+
+function setup_tests(aNext) {
+ SimpleTest.requestLongerTimeout(2);
+ SpecialPowers.pushPrefEnv(
+ {
+ set: [
+ ["dom.filesystem.pathcheck.disabled", true],
+ ["dom.webkitBlink.dirPicker.enabled", true],
+ ],
+ },
+ aNext
+ );
+}
+
+function test_basic(aDirectory, aNext) {
+ ok(aDirectory, "Directory exists.");
+ ok(aDirectory instanceof Directory, "We have a directory.");
+ is(aDirectory.path, "/" + aDirectory.name, "directory.path must be '/'+name");
+ aNext();
+}
+
+function test_getFilesAndDirectories(aDirectory, aRecursive, aNext) {
+ function checkSubDir(dir) {
+ return dir.getFilesAndDirectories().then(function (data) {
+ for (var i = 0; i < data.length; ++i) {
+ ok(
+ data[i] instanceof File || data[i] instanceof Directory,
+ "Just Files or Directories"
+ );
+ if (data[i] instanceof Directory) {
+ isnot(
+ data[i].name,
+ "/",
+ "Subdirectory should be called with the leafname"
+ );
+ isnot(
+ data[i].path,
+ "/",
+ "Subdirectory path should be called with the leafname"
+ );
+ isnot(
+ data[i].path,
+ dir.path,
+ "Subdirectory path should contain the parent path."
+ );
+ is(
+ data[i].path,
+ createPath(dir, data[i]),
+ "Subdirectory path should be called parentdir.path + '/' + leafname: " +
+ data[i].path
+ );
+ }
+
+ if (data[i] instanceof File) {
+ is(
+ data[i].webkitRelativePath,
+ createRelativePath(dir, data[i]),
+ "File.webkitRelativePath should be called: parentdir.path + '/' + file.name: " +
+ data[i].webkitRelativePath
+ );
+ ok(
+ !data[i].webkitRelativePath.endsWith("symlink.txt"),
+ "We should never see a path ending with symlink.txt, our symlink sentinel."
+ );
+ }
+ }
+ });
+ }
+
+ aDirectory
+ .getFilesAndDirectories()
+ .then(
+ function (data) {
+ ok(data.length, "We should have some data.");
+ var promises = [];
+ for (var i = 0; i < data.length; ++i) {
+ ok(
+ data[i] instanceof File || data[i] instanceof Directory,
+ "Just Files or Directories: " + data[i].name
+ );
+ if (data[i] instanceof Directory) {
+ isnot(
+ data[i].name,
+ "/",
+ "Subdirectory should be called with the leafname"
+ );
+ is(
+ data[i].path,
+ createPath(aDirectory, data[i]),
+ "Subdirectory path should be called parentdir.path + '/' + leafname: " +
+ data[i].path
+ );
+ if (aRecursive) {
+ promises.push(checkSubDir(data[i]));
+ }
+ }
+
+ if (data[i] instanceof File) {
+ is(
+ data[i].webkitRelativePath,
+ createRelativePath(aDirectory, data[i]),
+ "File.webkitRelativePath should be called file.name: " +
+ data[i].webkitRelativePath
+ );
+ }
+ }
+
+ return Promise.all(promises);
+ },
+ function () {
+ ok(false, "Something when wrong");
+ }
+ )
+ .then(aNext);
+}
+
+function test_getFiles(aDirectory, aRecursive, aNext) {
+ aDirectory
+ .getFiles(aRecursive)
+ .then(
+ function (data) {
+ for (var i = 0; i < data.length; ++i) {
+ ok(data[i] instanceof File, "File: " + data[i].name);
+ is(aDirectory.path[0], "/", "Directory path must start with '/'");
+ ok(
+ data[i].webkitRelativePath.indexOf(aDirectory.path.substring(1)) ==
+ 0 &&
+ data[i].webkitRelativePath.indexOf("/" + data[i].name) +
+ ("/" + data[i].name).length ==
+ data[i].webkitRelativePath.length,
+ "File.webkitRelativePath should be called dir.path + '/' + file.name: " +
+ data[i].webkitRelativePath
+ );
+ }
+ },
+ function () {
+ ok(false, "Something when wrong");
+ }
+ )
+ .then(aNext);
+}
+
+function test_getFiles_recursiveComparison(aDirectory, aNext) {
+ aDirectory
+ .getFiles(true)
+ .then(function (data) {
+ is(data.length, 2, "Only 2 files for this test.");
+ ok(
+ data[0].name == "foo.txt" || data[0].name == "bar.txt",
+ "First filename matches"
+ );
+ ok(
+ data[1].name == "foo.txt" || data[1].name == "bar.txt",
+ "Second filename matches"
+ );
+ })
+ .then(function () {
+ return aDirectory.getFiles(false);
+ })
+ .then(function (data) {
+ is(data.length, 1, "Only 1 file for this test.");
+ ok(
+ data[0].name == "foo.txt" || data[0].name == "bar.txt",
+ "First filename matches"
+ );
+ })
+ .catch(function () {
+ ok(false, "Something when wrong");
+ })
+ .then(aNext);
+}
diff --git a/dom/filesystem/tests/mochitest.ini b/dom/filesystem/tests/mochitest.ini
new file mode 100644
index 0000000000..00326a6e0d
--- /dev/null
+++ b/dom/filesystem/tests/mochitest.ini
@@ -0,0 +1,12 @@
+[DEFAULT]
+support-files =
+ filesystem_commons.js
+ script_fileList.js
+ worker_basic.js
+
+[test_basic.html]
+[test_webkitdirectory.html]
+skip-if = os == "android" # Bug 1674428
+support-files = script_promptHandler.js
+[test_worker_basic.html]
+[test_bug1319088.html]
diff --git a/dom/filesystem/tests/moz.build b/dom/filesystem/tests/moz.build
new file mode 100644
index 0000000000..7c990fbc62
--- /dev/null
+++ b/dom/filesystem/tests/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+MOCHITEST_MANIFESTS += ["mochitest.ini"]
diff --git a/dom/filesystem/tests/script_fileList.js b/dom/filesystem/tests/script_fileList.js
new file mode 100644
index 0000000000..47438aa7b3
--- /dev/null
+++ b/dom/filesystem/tests/script_fileList.js
@@ -0,0 +1,176 @@
+/* eslint-env mozilla/chrome-script */
+// eslint-disable-next-line mozilla/reject-importGlobalProperties
+Cu.importGlobalProperties(["File"]);
+function createProfDFile() {
+ return Services.dirsvc
+ .QueryInterface(Ci.nsIProperties)
+ .get("ProfD", Ci.nsIFile);
+}
+
+const { AppConstants } = ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+);
+
+// Creates a parametric arity directory hierarchy as a function of depth.
+// Each directory contains one leaf file, and subdirectories of depth [1, depth).
+// e.g. for depth 3:
+//
+// subdir3
+// - file.txt
+// - subdir2
+// - file.txt
+// - subdir1
+// - file.txt
+// - subdir1
+// - file.txt
+//
+// Returns the parent directory of the subtree.
+function createTreeFile(depth, parent) {
+ if (!parent) {
+ parent = Services.dirsvc
+ .QueryInterface(Ci.nsIProperties)
+ .get("TmpD", Ci.nsIFile);
+ parent.append("dir-tree-test");
+ parent.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o700);
+ }
+
+ var nextFile = parent.clone();
+ if (depth == 0) {
+ nextFile.append("file.txt");
+ nextFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
+
+ // It's not possible to create symlinks on windows by default or on our
+ // Android platforms, so we can't create the symlink file there. Our
+ // callers that care are aware of this and also check AppConstants.
+ if (
+ AppConstants.platform !== "win" &&
+ AppConstants.platform !== "android"
+ ) {
+ var linkFile = parent.clone();
+ linkFile.append("symlink.txt");
+ createSymLink(nextFile.path, linkFile.path);
+ }
+ } else {
+ nextFile.append("subdir" + depth);
+ nextFile.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o700);
+ // Decrement the maximal depth by one for each level of nesting.
+ for (var i = 0; i < depth; i++) {
+ createTreeFile(i, nextFile);
+ }
+ }
+
+ return parent;
+}
+
+function createRootFile() {
+ var testFile = createProfDFile();
+
+ // Let's go back to the root of the FileSystem
+ while (true) {
+ var parent = testFile.parent;
+ if (!parent) {
+ break;
+ }
+
+ testFile = parent;
+ }
+
+ return testFile;
+}
+
+var process;
+function createSymLink(target, linkName) {
+ if (!process) {
+ var ln = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ ln.initWithPath("/bin/ln");
+
+ process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
+ process.init(ln);
+ }
+
+ const args = ["-s", target, linkName];
+ process.run(true, args, args.length);
+ Assert.equal(process.exitValue, 0);
+}
+
+function createTestFile() {
+ var tmpFile = Services.dirsvc
+ .QueryInterface(Ci.nsIProperties)
+ .get("TmpD", Ci.nsIFile);
+ tmpFile.append("dir-test");
+ tmpFile.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o700);
+
+ var file1 = tmpFile.clone();
+ file1.append("foo.txt");
+ file1.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
+
+ var dir = tmpFile.clone();
+ dir.append("subdir");
+ dir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o700);
+
+ var file2 = dir.clone();
+ file2.append("bar.txt");
+ file2.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
+
+ // It's not possible to create symlinks on windows by default or on our
+ // Android platforms, so we can't create the symlink file there. Our
+ // callers that care are aware of this and also check AppConstants.
+ if (AppConstants.platform !== "win" && AppConstants.platform !== "android") {
+ var linkFile = dir.clone();
+ linkFile.append("symlink.txt");
+ createSymLink(file1.path, linkFile.path);
+ }
+
+ return tmpFile;
+}
+
+addMessageListener("dir.open", function (e) {
+ var testFile;
+
+ switch (e.path) {
+ case "ProfD":
+ // Note that files in the profile directory are not guaranteed to persist-
+ // see bug 1284742.
+ testFile = createProfDFile();
+ break;
+
+ case "root":
+ testFile = createRootFile();
+ break;
+
+ case "test":
+ testFile = createTestFile();
+ break;
+
+ case "tree":
+ testFile = createTreeFile(3);
+ break;
+ }
+
+ sendAsyncMessage("dir.opened", {
+ dir: testFile.path,
+ name: testFile.leafName,
+ });
+});
+
+addMessageListener("file.open", function (e) {
+ var testFile = Services.dirsvc
+ .QueryInterface(Ci.nsIProperties)
+ .get("ProfD", Ci.nsIFile);
+ testFile.append("prefs.js");
+
+ File.createFromNsIFile(testFile).then(function (file) {
+ sendAsyncMessage("file.opened", { file });
+ });
+});
+
+addMessageListener("symlink.open", function (e) {
+ let testDir = createTestFile();
+ let testFile = testDir.clone();
+ testFile.append("subdir");
+ testFile.append("symlink.txt");
+
+ File.createFromNsIFile(testFile).then(function (file) {
+ sendAsyncMessage("symlink.opened", { dir: testDir.path, file });
+ });
+});
diff --git a/dom/filesystem/tests/script_promptHandler.js b/dom/filesystem/tests/script_promptHandler.js
new file mode 100644
index 0000000000..ed3bda7404
--- /dev/null
+++ b/dom/filesystem/tests/script_promptHandler.js
@@ -0,0 +1,18 @@
+/* eslint-env mozilla/chrome-script */
+
+let dialogObserverTopic = "common-dialog-loaded";
+
+function dialogObserver(subj, topic, data) {
+ subj.document.querySelector("dialog").acceptDialog();
+ sendAsyncMessage("promptAccepted");
+}
+
+addMessageListener("init", message => {
+ Services.obs.addObserver(dialogObserver, dialogObserverTopic);
+ sendAsyncMessage("initDone");
+});
+
+addMessageListener("cleanup", message => {
+ Services.obs.removeObserver(dialogObserver, dialogObserverTopic);
+ sendAsyncMessage("cleanupDone");
+});
diff --git a/dom/filesystem/tests/test_basic.html b/dom/filesystem/tests/test_basic.html
new file mode 100644
index 0000000000..b4daea79c9
--- /dev/null
+++ b/dom/filesystem/tests/test_basic.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Directory API</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="filesystem_commons.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<script type="application/javascript">
+
+var directory;
+var fileList;
+
+function create_fileList(aPath) {
+ fileList = document.createElement("input");
+ fileList.setAttribute("type", "file");
+ document.body.appendChild(fileList);
+
+ var url = SimpleTest.getTestFileURL("script_fileList.js");
+ var script = SpecialPowers.loadChromeScript(url);
+
+ function onOpened(message) {
+ SpecialPowers.wrap(fileList).mozSetDirectory(message.dir);
+ fileList.setAttribute("data-name", message.name);
+
+ SpecialPowers.wrap(fileList).getFilesAndDirectories().then(function(array) {
+ array = SpecialPowers.unwrap(array);
+ is(array.length, 1, "We want just 1 directory.");
+ ok(array[0] instanceof Directory, "We want just 1 directory.");
+
+ directory = array[0];
+ script.destroy();
+ next();
+ });
+ }
+
+ script.addMessageListener("dir.opened", onOpened);
+ script.sendAsyncMessage("dir.open", { path: aPath });
+}
+
+function test_simpleFilePicker(aPath) {
+ var url = SimpleTest.getTestFileURL("script_fileList.js");
+ var script = SpecialPowers.loadChromeScript(url);
+
+ function onOpened(message) {
+ SpecialPowers.wrap(fileList).mozSetFileArray([message.file]);
+
+ is(fileList.files.length, 1, "we want 1 element");
+ ok(fileList.files[0] instanceof File, "we want 1 file");
+ ok("webkitRelativePath" in fileList.files[0], "we have webkitRelativePath attribute");
+ is(fileList.files[0].webkitRelativePath, "", "No webkit relative path for normal filePicker");
+
+ script.destroy();
+ next();
+ }
+
+ script.addMessageListener("file.opened", onOpened);
+ script.sendAsyncMessage("file.open");
+}
+
+function test_duplicateGetFilesAndDirectories() {
+ var url = SimpleTest.getTestFileURL("script_fileList.js");
+ var script = SpecialPowers.loadChromeScript(url);
+
+ function onOpened(message) {
+ SpecialPowers.wrap(fileList).mozSetDirectory(message.dir);
+
+ var p1 = SpecialPowers.wrap(fileList).getFilesAndDirectories();
+ var p2 = SpecialPowers.wrap(fileList).getFilesAndDirectories();
+
+ isnot(p1, p2, "We create 2 different promises");
+
+ script.destroy();
+ next();
+ }
+
+ script.addMessageListener("dir.opened", onOpened);
+ script.sendAsyncMessage("dir.open", { path: "test" });
+}
+
+var tests = [
+ function() { setup_tests(next); },
+
+ function() { create_fileList("tree"); },
+ function() { test_basic(directory, next); },
+ function() { test_getFilesAndDirectories(directory, true, next); },
+ function() { test_getFiles(directory, false, next); },
+ function() { test_getFiles(directory, true, next); },
+
+ function() { create_fileList("test"); },
+ function() { test_getFiles_recursiveComparison(directory, next); },
+
+ function() { create_fileList("root"); },
+ function() { test_basic(directory, next); },
+ function() { test_getFilesAndDirectories(directory, false, next); },
+ function() { test_getFiles(directory, false, next); },
+
+ test_duplicateGetFilesAndDirectories,
+ test_simpleFilePicker,
+];
+
+function next() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+}
+
+SimpleTest.waitForExplicitFinish();
+next();
+</script>
+</body>
+</html>
diff --git a/dom/filesystem/tests/test_bug1319088.html b/dom/filesystem/tests/test_bug1319088.html
new file mode 100644
index 0000000000..98e0ad46f0
--- /dev/null
+++ b/dom/filesystem/tests/test_bug1319088.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for bug 1319088</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<input id="input" type="file"></input>
+
+<script type="application/javascript">
+
+function testSetup() {
+ SpecialPowers.pushPrefEnv({"set": [["dom.webkitBlink.dirPicker.enabled", true]]}, next);
+}
+
+function populateInputFile() {
+ var url = SimpleTest.getTestFileURL("script_fileList.js");
+ var script = SpecialPowers.loadChromeScript(url);
+
+ function onOpened(message) {
+ var input = document.getElementById("input");
+ SpecialPowers.wrap(input).mozSetFileArray([message.file]);
+
+ script.destroy();
+ next();
+ }
+
+ script.addMessageListener("file.opened", onOpened);
+ script.sendAsyncMessage("file.open");
+}
+
+function checkBug() {
+ var input = document.getElementById("input");
+ is(input.files[0].webkitRelativePath, "", "No relative path!");
+
+ let form = document.createElement("form");
+ form.appendChild(input);
+
+ is(input.files[0].webkitRelativePath, "", "No relative path!");
+ SimpleTest.finish();
+}
+
+var tests = [
+ testSetup,
+ populateInputFile,
+ checkBug,
+];
+
+function next() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+}
+
+SimpleTest.waitForExplicitFinish();
+next();
+</script>
+</body>
+</html>
diff --git a/dom/filesystem/tests/test_webkitdirectory.html b/dom/filesystem/tests/test_webkitdirectory.html
new file mode 100644
index 0000000000..50080ad7ba
--- /dev/null
+++ b/dom/filesystem/tests/test_webkitdirectory.html
@@ -0,0 +1,309 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for webkitdirectory and webkitRelativePath</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<input id="inputFileWebkitDirectory" type="file" webkitdirectory></input>
+<input id="inputFileWebkitFile" type="file"></input>
+<input id="inputFileDirectoryChange" type="file" webkitdirectory></input>
+
+<script type="application/javascript">
+
+const { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+);
+
+let promptHandler;
+
+function waitForEvent(element, eventName) {
+ return new Promise(function(resolve) {
+ element.addEventListener(eventName, e => resolve(e.detail), { once: true });
+ });
+}
+
+function waitForPromptHandled() {
+ return new Promise(resolve => promptHandler.addMessageListener("promptAccepted", resolve));
+}
+
+// Populate the given input type=file `aInputFile`'s `files` attribute by:
+// - loading `script_fileList.js` in the parent process
+// - telling it to generate the "test" template directory pattern which will
+// create "foo.txt", "subdir/bar.txt", and if symlinks are available on the
+// platform, "symlink.txt" which will be a symlink to "foo.txt". (Note that
+// we explicitly expect the symlink to be filtered out if generated, and
+// during the enhancement of the test we verified the file was created on
+// linux by running the test before fixing the GetFilesHelper logic to filter
+// the symlink out and verifying the subsequent `test_fileList` check failed.)
+// - Triggering the mock file picker with the base directory of the "test"
+// template directory.
+//
+// It's expected that `test_fileList` will be used after this step completes in
+// order to validate the results.
+function populateInputFile(aInputFile) {
+ var url = SimpleTest.getTestFileURL("script_fileList.js");
+ var script = SpecialPowers.loadChromeScript(url);
+
+ var MockFilePicker = SpecialPowers.MockFilePicker;
+ MockFilePicker.init(window, "A Mock File Picker", SpecialPowers.Ci.nsIFilePicker.modeGetFolder);
+
+ async function onOpened(message) {
+ MockFilePicker.useDirectory(message.dir);
+
+ let input = document.getElementById(aInputFile);
+ input.setAttribute("data-name", message.name);
+
+ let promptHandled = waitForPromptHandled();
+ let changeEvent = waitForEvent(input, "change");
+
+ input.click();
+
+ await promptHandled;
+ await changeEvent;
+
+ MockFilePicker.cleanup();
+ script.destroy();
+ next();
+ }
+
+ script.addMessageListener("dir.opened", onOpened);
+ script.sendAsyncMessage("dir.open", { path: "test" });
+}
+
+function checkFile(file, fileList, dirName) {
+ for (var i = 0; i < fileList.length; ++i) {
+ ok(fileList[i] instanceof File, "We want just files.");
+ if (fileList[i].name == file.name) {
+ is(fileList[i].webkitRelativePath, dirName + file.path, "Path matches");
+ return;
+ }
+ }
+
+ ok(false, "File not found.");
+}
+
+// Validate the contents of the given input type=file `aInputFile`'s' `files`
+// property against the expected list of files `aWhat`.
+function test_fileList(aInputFile, aWhat) {
+ var input = document.getElementById(aInputFile);
+ var fileList = input.files;
+
+ if (aWhat == null) {
+ is(fileList, null, "We want a null fileList for " + aInputFile);
+ next();
+ return;
+ }
+
+ is(fileList.length, aWhat.length, "We want just " + aWhat.length + " elements for " + aInputFile);
+ for (var i = 0; i < aWhat.length; ++i) {
+ checkFile(aWhat[i], fileList, input.dataset.name);
+ }
+
+ next();
+}
+
+// Verify that we can explicitly select a symlink and it will not be filtered
+// out. This is really a verification that GetFileHelper's file-handling logic
+// https://searchfox.org/mozilla-central/rev/065102493dfc49234120c37fc6a334a5b1d86d9e/dom/filesystem/GetFilesHelper.cpp#81-86
+// does not proactively take an action to filter out a selected symlink.
+//
+// This is a glass box test that is not entirely realistic for our actual system
+// file pickers but does reflect what will happen in the drag-and-drop case
+// for `HTMLInputElement::MozSetDndFilesAndDirectories` and this helps ensure
+// that future implementation changes will behave as expected. Specifically,
+// the presence of webkitdirectory will result in the file picker using
+// `modeGetFolder` which will only allow selection of a directory and forbid
+// file selection.
+//
+// This test explicitly does not validate HTMLInputElement's non-webkitdirectory
+// file selection mechanism because it does not involve GetFileHelper.
+async function test_individualSymlink(aInputFile) {
+ const input = document.getElementById(aInputFile);
+
+ // -- Create the symlink and get a `File` instance pointing at it.
+ const url = SimpleTest.getTestFileURL("script_fileList.js");
+ const script = SpecialPowers.loadChromeScript(url);
+
+ let opened = new Promise(resolve => script.addMessageListener("symlink.opened", resolve));
+ script.sendAsyncMessage("symlink.open", {});
+ let { dir, file: symlinkFile } = await opened;
+ info(`symlink.open provided dir: ${dir}`)
+
+ // -- Have the picker pick it
+ var MockFilePicker = SpecialPowers.MockFilePicker;
+ MockFilePicker.init(window, "A Mock File Picker", SpecialPowers.Ci.nsIFilePicker.modeOpen);
+
+ MockFilePicker.displayDirectory = dir;
+ let pickerShown = new Promise(resolve => {
+ MockFilePicker.showCallback = function() {
+ // This is where we are diverging from a realistic scenario in order to get
+ // the expected coverage.
+ MockFilePicker.setFiles([symlinkFile]);
+ resolve();
+ }
+ });
+ MockFilePicker.returnValue = MockFilePicker.returnOK;
+
+ let changeEvent = waitForEvent(input, "change");
+
+ input.click();
+
+ await pickerShown;
+ await changeEvent;
+
+ MockFilePicker.cleanup();
+ script.destroy();
+
+ // -- Verify that we see the symlink.
+ let fileList = input.files;
+ is(fileList.length, 1, "There should be 1 file.");
+ is(fileList[0].name, "symlink.txt", "The file should be the symlink.");
+ next();
+}
+
+function test_webkitdirectory_attribute() {
+ var a = document.createElement("input");
+ a.setAttribute("type", "file");
+
+ ok("webkitdirectory" in a, "HTMLInputElement.webkitdirectory exists");
+
+ ok(!a.hasAttribute("webkitdirectory"), "No webkitdirectory DOM attribute by default");
+ ok(!a.webkitdirectory, "No webkitdirectory attribute by default");
+
+ a.webkitdirectory = true;
+
+ ok(a.hasAttribute("webkitdirectory"), "Webkitdirectory DOM attribute is set");
+ ok(a.webkitdirectory, "Webkitdirectory attribute is set");
+
+ next();
+}
+
+function test_changeDataWhileWorking() {
+ var url = SimpleTest.getTestFileURL("script_fileList.js");
+ var script = SpecialPowers.loadChromeScript(url);
+
+ var MockFilePicker = SpecialPowers.MockFilePicker;
+ MockFilePicker.init(window, "A Mock File Picker", SpecialPowers.Ci.nsIFilePicker.modeGetFolder);
+ let promptHandled;
+
+ // Let's start retrieving the root nsIFile object
+ new Promise(function(resolve) {
+ function onOpened(message) {
+ script.removeMessageListener("dir.opened", onOpened);
+ resolve(message.dir);
+ }
+
+ script.addMessageListener("dir.opened", onOpened);
+ script.sendAsyncMessage("dir.open", { path: "root" });
+ })
+
+ // input.click() pointing to the root dir
+ .then(async function(aDir) {
+ MockFilePicker.cleanup();
+ MockFilePicker.init(window, "A Mock File Picker", SpecialPowers.Ci.nsIFilePicker.modeGetFolder);
+ MockFilePicker.useDirectory(aDir);
+ var input = document.getElementById("inputFileDirectoryChange");
+
+ promptHandled = waitForPromptHandled();
+ input.click();
+ })
+
+ // Before onchange, let's take the 'test' directory
+ .then(function() {
+ return new Promise(function(resolve) {
+ function onOpened(message) {
+ script.removeMessageListener("dir.opened", onOpened);
+ script.destroy();
+ resolve(message.dir);
+ }
+
+ script.addMessageListener("dir.opened", onOpened);
+ script.sendAsyncMessage("dir.open", { path: "test" });
+ });
+ })
+
+ // Now let's click again and wait for onchange.
+ .then(async function(aDir) {
+ MockFilePicker.cleanup();
+ MockFilePicker.init(window, "A Mock File Picker", SpecialPowers.Ci.nsIFilePicker.modeGetFolder);
+ MockFilePicker.useDirectory(aDir);
+
+ let input = document.getElementById("inputFileDirectoryChange");
+ let changeEvent = waitForEvent(input, "change");
+
+ input.click();
+
+ await promptHandled;
+ await changeEvent;
+
+ MockFilePicker.cleanup();
+ })
+ .then(function() {
+ test_fileList("inputFileWebkitDirectory", testDirData);
+ });
+}
+
+async function test_setup() {
+ let promptHandlerUrl = SimpleTest.getTestFileURL("script_promptHandler.js")
+ promptHandler = SpecialPowers.loadChromeScript(promptHandlerUrl);
+
+ let promptHandlerReady = new Promise(resolve => promptHandler.addMessageListener("initDone", resolve));
+ promptHandler.sendAsyncMessage("init");
+ await promptHandlerReady;
+
+ SpecialPowers.pushPrefEnv({"set": [["dom.filesystem.pathcheck.disabled", true],
+ ["dom.webkitBlink.dirPicker.enabled", true]]}, next);
+}
+
+async function test_cleanup() {
+ let promptHandlerDone = new Promise(resolve => promptHandler.addMessageListener("cleanupDone", resolve));
+ promptHandler.sendAsyncMessage("cleanup");
+ await promptHandlerDone;
+ promptHandler.destroy();
+}
+
+var testDirData = [ { name: "foo.txt", path: "/foo.txt" },
+ { name: "bar.txt", path: "/subdir/bar.txt" }];
+
+var tests = [
+ test_setup,
+
+ function() { populateInputFile("inputFileWebkitDirectory"); },
+
+ function() { test_fileList("inputFileWebkitDirectory", testDirData); },
+
+ function() {
+ // Symlinks are not available on Windows and so will not be created.
+ if (AppConstants.platform === "win" || AppConstants.platform === "android") {
+ info("Skipping individual symlink check on Windows and Android.");
+ next();
+ return;
+ }
+
+ test_individualSymlink("inputFileWebkitFile").catch(err => ok(false, `Problem in symlink case: ${err}`));
+ },
+
+ test_webkitdirectory_attribute,
+
+ test_changeDataWhileWorking,
+];
+
+async function next() {
+ if (!tests.length) {
+ await test_cleanup();
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ await test();
+}
+
+SimpleTest.waitForExplicitFinish();
+next();
+</script>
+</body>
+</html>
diff --git a/dom/filesystem/tests/test_worker_basic.html b/dom/filesystem/tests/test_worker_basic.html
new file mode 100644
index 0000000000..920f32719b
--- /dev/null
+++ b/dom/filesystem/tests/test_worker_basic.html
@@ -0,0 +1,73 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Directory API in workers</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="filesystem_commons.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<script type="application/javascript">
+
+var fileList;
+
+function create_fileList() {
+ fileList = document.createElement("input");
+ fileList.setAttribute("type", "file");
+ document.body.appendChild(fileList);
+
+ var url = SimpleTest.getTestFileURL("script_fileList.js");
+ var script = SpecialPowers.loadChromeScript(url);
+
+ function onOpened(message) {
+ SpecialPowers.wrap(fileList).mozSetDirectory(message.dir);
+ script.destroy();
+ next();
+ }
+
+ script.addMessageListener("dir.opened", onOpened);
+ script.sendAsyncMessage("dir.open", { path: "test" });
+}
+
+function test_worker() {
+ SpecialPowers.wrap(fileList).getFilesAndDirectories().then(function(array) {
+ array = SpecialPowers.unwrap(array);
+ var worker = new Worker("worker_basic.js");
+ worker.onmessage = function(e) {
+ if (e.data.type == "finish") {
+ next();
+ return;
+ }
+
+ if (e.data.type == "test") {
+ ok(e.data.test, e.data.message);
+ }
+ };
+
+ worker.postMessage(array[0]);
+ });
+}
+
+var tests = [
+ function() { setup_tests(next); },
+
+ create_fileList,
+ test_worker,
+];
+
+function next() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+}
+
+SimpleTest.waitForExplicitFinish();
+next();
+</script>
+</body>
+</html>
diff --git a/dom/filesystem/tests/worker_basic.js b/dom/filesystem/tests/worker_basic.js
new file mode 100644
index 0000000000..2771c778fd
--- /dev/null
+++ b/dom/filesystem/tests/worker_basic.js
@@ -0,0 +1,50 @@
+/* eslint-env worker */
+importScripts("filesystem_commons.js");
+
+function finish() {
+ postMessage({ type: "finish" });
+}
+
+function ok(a, msg) {
+ postMessage({ type: "test", test: !!a, message: msg });
+}
+
+function is(a, b, msg) {
+ ok(a === b, msg);
+}
+
+function isnot(a, b, msg) {
+ ok(a != b, msg);
+}
+
+var tests = [
+ function () {
+ test_basic(directory, next);
+ },
+ function () {
+ test_getFilesAndDirectories(directory, true, next);
+ },
+ function () {
+ test_getFiles(directory, false, next);
+ },
+ function () {
+ test_getFiles(directory, true, next);
+ },
+];
+
+function next() {
+ if (!tests.length) {
+ finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+}
+
+var directory;
+
+onmessage = function (e) {
+ directory = e.data;
+ next();
+};