summaryrefslogtreecommitdiffstats
path: root/dom/filesystem/tests/test_webkitdirectory.html
diff options
context:
space:
mode:
Diffstat (limited to 'dom/filesystem/tests/test_webkitdirectory.html')
-rw-r--r--dom/filesystem/tests/test_webkitdirectory.html309
1 files changed, 309 insertions, 0 deletions
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>