diff options
Diffstat (limited to 'dom/filesystem/tests/test_webkitdirectory.html')
-rw-r--r-- | dom/filesystem/tests/test_webkitdirectory.html | 309 |
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> |