4 <title>Test for webkitdirectory and webkitRelativePath
</title>
5 <script src=
"/tests/SimpleTest/SimpleTest.js"></script>
6 <link rel=
"stylesheet" type=
"text/css" href=
"/tests/SimpleTest/test.css" />
10 <input id=
"inputFileWebkitDirectory" type=
"file" webkitdirectory
></input>
11 <input id=
"inputFileWebkitFile" type=
"file"></input>
12 <input id=
"inputFileDirectoryChange" type=
"file" webkitdirectory
></input>
14 <script type=
"application/javascript">
16 const { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
17 "resource://gre/modules/AppConstants.sys.mjs"
22 function waitForEvent(element, eventName) {
23 return new Promise(function(resolve) {
24 element.addEventListener(eventName, e =
> resolve(e.detail), { once: true });
28 function waitForPromptHandled() {
29 return new Promise(resolve =
> promptHandler.addMessageListener(
"promptAccepted", resolve));
32 // Populate the given input type=file `aInputFile`'s `files` attribute by:
33 // - loading `script_fileList.js` in the parent process
34 // - telling it to generate the
"test" template directory pattern which will
35 // create
"foo.txt",
"subdir/bar.txt", and if symlinks are available on the
36 // platform,
"symlink.txt" which will be a symlink to
"foo.txt". (Note that
37 // we explicitly expect the symlink to be filtered out if generated, and
38 // during the enhancement of the test we verified the file was created on
39 // linux by running the test before fixing the GetFilesHelper logic to filter
40 // the symlink out and verifying the subsequent `test_fileList` check failed.)
41 // - Triggering the mock file picker with the base directory of the
"test"
42 // template directory.
44 // It's expected that `test_fileList` will be used after this step completes in
45 // order to validate the results.
46 function populateInputFile(aInputFile) {
47 var url = SimpleTest.getTestFileURL(
"script_fileList.js");
48 var script = SpecialPowers.loadChromeScript(url);
50 var MockFilePicker = SpecialPowers.MockFilePicker;
51 MockFilePicker.init(SpecialPowers.wrap(window).browsingContext,
"A Mock File Picker", SpecialPowers.Ci.nsIFilePicker.modeGetFolder);
53 async function onOpened(message) {
54 MockFilePicker.useDirectory(message.dir);
56 let input = document.getElementById(aInputFile);
57 input.setAttribute(
"data-name", message.name);
59 let promptHandled = waitForPromptHandled();
60 let changeEvent = waitForEvent(input,
"change");
67 MockFilePicker.cleanup();
72 script.addMessageListener(
"dir.opened", onOpened);
73 script.sendAsyncMessage(
"dir.open", { path:
"test" });
76 function checkFile(file, fileList, dirName) {
77 for (var i =
0; i < fileList.length; ++i) {
78 ok(fileList[i] instanceof File,
"We want just files.");
79 if (fileList[i].name == file.name) {
80 is(fileList[i].webkitRelativePath, dirName + file.path,
"Path matches");
85 ok(false,
"File not found.");
88 // Validate the contents of the given input type=file `aInputFile`'s' `files`
89 // property against the expected list of files `aWhat`.
90 function test_fileList(aInputFile, aWhat) {
91 var input = document.getElementById(aInputFile);
92 var fileList = input.files;
95 is(fileList, null,
"We want a null fileList for " + aInputFile);
100 is(fileList.length, aWhat.length,
"We want just " + aWhat.length +
" elements for " + aInputFile);
101 for (var i =
0; i < aWhat.length; ++i) {
102 checkFile(aWhat[i], fileList, input.dataset.name);
108 // Verify that we can explicitly select a symlink and it will not be filtered
109 // out. This is really a verification that GetFileHelper's file-handling logic
110 // https://searchfox.org/mozilla-central/rev/
065102493dfc49234120c37fc6a334a5b1d86d9e/dom/filesystem/GetFilesHelper.cpp#
81-
86
111 // does not proactively take an action to filter out a selected symlink.
113 // This is a glass box test that is not entirely realistic for our actual system
114 // file pickers but does reflect what will happen in the drag-and-drop case
115 // for `HTMLInputElement::MozSetDndFilesAndDirectories` and this helps ensure
116 // that future implementation changes will behave as expected. Specifically,
117 // the presence of webkitdirectory will result in the file picker using
118 // `modeGetFolder` which will only allow selection of a directory and forbid
121 // This test explicitly does not validate HTMLInputElement's non-webkitdirectory
122 // file selection mechanism because it does not involve GetFileHelper.
123 async function test_individualSymlink(aInputFile) {
124 const input = document.getElementById(aInputFile);
126 // -- Create the symlink and get a `File` instance pointing at it.
127 const url = SimpleTest.getTestFileURL(
"script_fileList.js");
128 const script = SpecialPowers.loadChromeScript(url);
130 let opened = new Promise(resolve =
> script.addMessageListener(
"symlink.opened", resolve));
131 script.sendAsyncMessage(
"symlink.open", {});
132 let { dir, file: symlinkFile } = await opened;
133 info(`symlink.open provided dir: ${dir}`)
135 // -- Have the picker pick it
136 var MockFilePicker = SpecialPowers.MockFilePicker;
137 MockFilePicker.init(SpecialPowers.wrap(window).browsingContext,
"A Mock File Picker", SpecialPowers.Ci.nsIFilePicker.modeOpen);
139 MockFilePicker.displayDirectory = dir;
140 let pickerShown = new Promise(resolve =
> {
141 MockFilePicker.showCallback = function() {
142 // This is where we are diverging from a realistic scenario in order to get
143 // the expected coverage.
144 MockFilePicker.setFiles([symlinkFile]);
148 MockFilePicker.returnValue = MockFilePicker.returnOK;
150 let changeEvent = waitForEvent(input,
"change");
157 MockFilePicker.cleanup();
160 // -- Verify that we see the symlink.
161 let fileList = input.files;
162 is(fileList.length,
1,
"There should be 1 file.");
163 is(fileList[
0].name,
"symlink.txt",
"The file should be the symlink.");
167 function test_webkitdirectory_attribute() {
168 var a = document.createElement(
"input");
169 a.setAttribute(
"type",
"file");
171 ok(
"webkitdirectory" in a,
"HTMLInputElement.webkitdirectory exists");
173 ok(!a.hasAttribute(
"webkitdirectory"),
"No webkitdirectory DOM attribute by default");
174 ok(!a.webkitdirectory,
"No webkitdirectory attribute by default");
176 a.webkitdirectory = true;
178 ok(a.hasAttribute(
"webkitdirectory"),
"Webkitdirectory DOM attribute is set");
179 ok(a.webkitdirectory,
"Webkitdirectory attribute is set");
184 function test_changeDataWhileWorking() {
185 var url = SimpleTest.getTestFileURL(
"script_fileList.js");
186 var script = SpecialPowers.loadChromeScript(url);
188 var MockFilePicker = SpecialPowers.MockFilePicker;
189 MockFilePicker.init(SpecialPowers.wrap(window).browsingContext,
"A Mock File Picker", SpecialPowers.Ci.nsIFilePicker.modeGetFolder);
192 // Let's start retrieving the root nsIFile object
193 new Promise(function(resolve) {
194 function onOpened(message) {
195 script.removeMessageListener(
"dir.opened", onOpened);
196 resolve(message.dir);
199 script.addMessageListener(
"dir.opened", onOpened);
200 script.sendAsyncMessage(
"dir.open", { path:
"root" });
203 // input.click() pointing to the root dir
204 .then(async function(aDir) {
205 MockFilePicker.cleanup();
206 MockFilePicker.init(SpecialPowers.wrap(window).browsingContext,
"A Mock File Picker", SpecialPowers.Ci.nsIFilePicker.modeGetFolder);
207 MockFilePicker.useDirectory(aDir);
208 var input = document.getElementById(
"inputFileDirectoryChange");
210 promptHandled = waitForPromptHandled();
214 // Before onchange, let's take the 'test' directory
216 return new Promise(function(resolve) {
217 function onOpened(message) {
218 script.removeMessageListener(
"dir.opened", onOpened);
220 resolve(message.dir);
223 script.addMessageListener(
"dir.opened", onOpened);
224 script.sendAsyncMessage(
"dir.open", { path:
"test" });
228 // Now let's click again and wait for onchange.
229 .then(async function(aDir) {
230 MockFilePicker.cleanup();
231 MockFilePicker.init(SpecialPowers.wrap(window).browsingContext,
"A Mock File Picker", SpecialPowers.Ci.nsIFilePicker.modeGetFolder);
232 MockFilePicker.useDirectory(aDir);
234 let input = document.getElementById(
"inputFileDirectoryChange");
235 let changeEvent = waitForEvent(input,
"change");
242 MockFilePicker.cleanup();
245 test_fileList(
"inputFileWebkitDirectory", testDirData);
249 async function test_setup() {
250 let promptHandlerUrl = SimpleTest.getTestFileURL(
"script_promptHandler.js")
251 promptHandler = SpecialPowers.loadChromeScript(promptHandlerUrl);
253 let promptHandlerReady = new Promise(resolve =
> promptHandler.addMessageListener(
"initDone", resolve));
254 promptHandler.sendAsyncMessage(
"init");
255 await promptHandlerReady;
257 SpecialPowers.pushPrefEnv({
"set": [[
"dom.filesystem.pathcheck.disabled", true],
258 [
"dom.webkitBlink.dirPicker.enabled", true]]}, next);
261 async function test_cleanup() {
262 let promptHandlerDone = new Promise(resolve =
> promptHandler.addMessageListener(
"cleanupDone", resolve));
263 promptHandler.sendAsyncMessage(
"cleanup");
264 await promptHandlerDone;
265 promptHandler.destroy();
268 var testDirData = [ { name:
"foo.txt", path:
"/foo.txt" },
269 { name:
"bar.txt", path:
"/subdir/bar.txt" }];
274 function() { populateInputFile(
"inputFileWebkitDirectory"); },
276 function() { test_fileList(
"inputFileWebkitDirectory", testDirData); },
279 // Symlinks are not available on Windows and so will not be created.
280 if (AppConstants.platform ===
"win" || AppConstants.platform ===
"android") {
281 info(
"Skipping individual symlink check on Windows and Android.");
286 test_individualSymlink(
"inputFileWebkitFile").catch(err =
> ok(false, `Problem in symlink case: ${err}`));
289 test_webkitdirectory_attribute,
291 test_changeDataWhileWorking,
294 async function next() {
296 await test_cleanup();
301 var test = tests.shift();
305 SimpleTest.waitForExplicitFinish();