tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_webkitdirectory.html (11073B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <head>
      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" />
      7 </head>
      8 
      9 <body>
     10 <input id="inputFileWebkitDirectory" type="file" webkitdirectory></input>
     11 <input id="inputFileWebkitFile" type="file"></input>
     12 <input id="inputFileDirectoryChange" type="file" webkitdirectory></input>
     13 
     14 <script type="application/javascript">
     15 
     16 const { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
     17  "resource://gre/modules/AppConstants.sys.mjs"
     18 );
     19 
     20 let promptHandler;
     21 
     22 function waitForEvent(element, eventName) {
     23  return new Promise(function(resolve) {
     24    element.addEventListener(eventName, e => resolve(e.detail), { once: true });
     25  });
     26 }
     27 
     28 function waitForPromptHandled() {
     29  if (AppConstants.platform === "android") {
     30    return new Promise(resolve => {
     31      let MockPromptCollection = SpecialPowers.MockPromptCollection;
     32      MockPromptCollection.showConfirmFolderUploadCallback = function() {
     33        resolve();
     34        return true;
     35      };
     36    });
     37  }
     38  return new Promise(resolve => promptHandler.addMessageListener("promptAccepted", resolve));
     39 }
     40 
     41 // Populate the given input type=file `aInputFile`'s `files` attribute by:
     42 // - loading `script_fileList.js` in the parent process
     43 // - telling it to generate the "test" template directory pattern which will
     44 //   create "foo.txt", "subdir/bar.txt", and if symlinks are available on the
     45 //   platform, "symlink.txt" which will be a symlink to "foo.txt".  (Note that
     46 //   we explicitly expect the symlink to be filtered out if generated, and
     47 //   during the enhancement of the test we verified the file was created on
     48 //   linux by running the test before fixing the GetFilesHelper logic to filter
     49 //   the symlink out and verifying the subsequent `test_fileList` check failed.)
     50 // - Triggering the mock file picker with the base directory of the "test"
     51 //   template directory.
     52 //
     53 // It's expected that `test_fileList` will be used after this step completes in
     54 // order to validate the results.
     55 function populateInputFile(aInputFile) {
     56  var url = SimpleTest.getTestFileURL("script_fileList.js");
     57  var script = SpecialPowers.loadChromeScript(url);
     58 
     59  var MockFilePicker = SpecialPowers.MockFilePicker;
     60  MockFilePicker.init(SpecialPowers.wrap(window).browsingContext);
     61 
     62  async function onOpened(message) {
     63    MockFilePicker.useDirectory(message.dir);
     64 
     65    let input = document.getElementById(aInputFile);
     66    input.setAttribute("data-name", message.name);
     67 
     68    let promptHandled = waitForPromptHandled();
     69    let changeEvent = waitForEvent(input, "change");
     70 
     71    SpecialPowers.wrap(document).notifyUserGestureActivation();
     72    input.click();
     73 
     74    await promptHandled;
     75    await changeEvent;
     76 
     77    MockFilePicker.cleanup();
     78    script.destroy();
     79    next();
     80  }
     81 
     82  script.addMessageListener("dir.opened", onOpened);
     83  script.sendAsyncMessage("dir.open", { path: "test" });
     84 }
     85 
     86 function checkFile(file, fileList, dirName) {
     87  for (var i = 0; i < fileList.length; ++i) {
     88    ok(fileList[i] instanceof File, "We want just files.");
     89    if (fileList[i].name == file.name) {
     90      is(fileList[i].webkitRelativePath, dirName + file.path, "Path matches");
     91      return;
     92    }
     93  }
     94 
     95  ok(false, "File not found.");
     96 }
     97 
     98 // Validate the contents of the given input type=file `aInputFile`'s' `files`
     99 // property against the expected list of files `aWhat`.
    100 function test_fileList(aInputFile, aWhat) {
    101  var input = document.getElementById(aInputFile);
    102  var fileList = input.files;
    103 
    104  if (aWhat == null) {
    105    is(fileList, null, "We want a null fileList for " + aInputFile);
    106    next();
    107    return;
    108  }
    109 
    110  is(fileList.length, aWhat.length, "We want just " + aWhat.length + " elements for " + aInputFile);
    111  for (var i = 0; i < aWhat.length; ++i) {
    112    checkFile(aWhat[i], fileList, input.dataset.name);
    113  }
    114 
    115  next();
    116 }
    117 
    118 // Verify that we can explicitly select a symlink and it will not be filtered
    119 // out.  This is really a verification that GetFileHelper's file-handling logic
    120 // https://searchfox.org/mozilla-central/rev/065102493dfc49234120c37fc6a334a5b1d86d9e/dom/filesystem/GetFilesHelper.cpp#81-86
    121 // does not proactively take an action to filter out a selected symlink.
    122 //
    123 // This is a glass box test that is not entirely realistic for our actual system
    124 // file pickers but does reflect what will happen in the drag-and-drop case
    125 // for `HTMLInputElement::MozSetDndFilesAndDirectories` and this helps ensure
    126 // that future implementation changes will behave as expected.  Specifically,
    127 // the presence of webkitdirectory will result in the file picker using
    128 // `modeGetFolder` which will only allow selection of a directory and forbid
    129 // file selection.
    130 //
    131 // This test explicitly does not validate HTMLInputElement's non-webkitdirectory
    132 // file selection mechanism because it does not involve GetFileHelper.
    133 async function test_individualSymlink(aInputFile) {
    134  const input = document.getElementById(aInputFile);
    135 
    136  // -- Create the symlink and get a `File` instance pointing at it.
    137  const url = SimpleTest.getTestFileURL("script_fileList.js");
    138  const script = SpecialPowers.loadChromeScript(url);
    139 
    140  let opened = new Promise(resolve => script.addMessageListener("symlink.opened", resolve));
    141  script.sendAsyncMessage("symlink.open", {});
    142  let { dir, file: symlinkFile } = await opened;
    143  info(`symlink.open provided dir: ${dir}`)
    144 
    145  // -- Have the picker pick it
    146  var MockFilePicker = SpecialPowers.MockFilePicker;
    147  MockFilePicker.init(SpecialPowers.wrap(window).browsingContext);
    148 
    149  MockFilePicker.displayDirectory = dir;
    150  let pickerShown = new Promise(resolve => {
    151    MockFilePicker.showCallback = function() {
    152      // This is where we are diverging from a realistic scenario in order to get
    153      // the expected coverage.
    154      MockFilePicker.setFiles([symlinkFile]);
    155      resolve();
    156    }
    157  });
    158  MockFilePicker.returnValue = MockFilePicker.returnOK;
    159 
    160  let changeEvent = waitForEvent(input, "change");
    161 
    162  SpecialPowers.wrap(document).notifyUserGestureActivation();
    163  input.click();
    164 
    165  await pickerShown;
    166  await changeEvent;
    167 
    168  MockFilePicker.cleanup();
    169  script.destroy();
    170 
    171  // -- Verify that we see the symlink.
    172  let fileList = input.files;
    173  is(fileList.length, 1, "There should be 1 file.");
    174  is(fileList[0].name, "symlink.txt", "The file should be the symlink.");
    175  next();
    176 }
    177 
    178 function test_webkitdirectory_attribute() {
    179  var a = document.createElement("input");
    180  a.setAttribute("type", "file");
    181 
    182  ok("webkitdirectory" in a, "HTMLInputElement.webkitdirectory exists");
    183 
    184  ok(!a.hasAttribute("webkitdirectory"), "No webkitdirectory DOM attribute by default");
    185  ok(!a.webkitdirectory, "No webkitdirectory attribute by default");
    186 
    187  a.webkitdirectory = true;
    188 
    189  ok(a.hasAttribute("webkitdirectory"), "Webkitdirectory DOM attribute is set");
    190  ok(a.webkitdirectory, "Webkitdirectory attribute is set");
    191 
    192  next();
    193 }
    194 
    195 function test_changeDataWhileWorking() {
    196  var url = SimpleTest.getTestFileURL("script_fileList.js");
    197  var script = SpecialPowers.loadChromeScript(url);
    198 
    199  var MockFilePicker = SpecialPowers.MockFilePicker;
    200  MockFilePicker.init(SpecialPowers.wrap(window).browsingContext);
    201  let promptHandled;
    202 
    203  // Let's start retrieving the root nsIFile object
    204  new Promise(function(resolve) {
    205    function onOpened(message) {
    206      script.removeMessageListener("dir.opened", onOpened);
    207      resolve(message.dir);
    208    }
    209 
    210    script.addMessageListener("dir.opened", onOpened);
    211    script.sendAsyncMessage("dir.open", { path: "root" });
    212  })
    213 
    214  // input.click() pointing to the root dir
    215  .then(async function(aDir) {
    216    MockFilePicker.cleanup();
    217    MockFilePicker.init(SpecialPowers.wrap(window).browsingContext);
    218    MockFilePicker.useDirectory(aDir);
    219    var input = document.getElementById("inputFileDirectoryChange");
    220 
    221    promptHandled = waitForPromptHandled();
    222 
    223    SpecialPowers.wrap(document).notifyUserGestureActivation();
    224    input.click();
    225  })
    226 
    227  // Before onchange, let's take the 'test' directory
    228  .then(function() {
    229    return new Promise(function(resolve) {
    230      function onOpened(message) {
    231        script.removeMessageListener("dir.opened", onOpened);
    232        script.destroy();
    233        resolve(message.dir);
    234      }
    235 
    236      script.addMessageListener("dir.opened", onOpened);
    237      script.sendAsyncMessage("dir.open", { path: "test" });
    238    });
    239  })
    240 
    241  // Now let's click again and wait for onchange.
    242  .then(async function(aDir) {
    243      MockFilePicker.cleanup();
    244      MockFilePicker.init(SpecialPowers.wrap(window).browsingContext);
    245      MockFilePicker.useDirectory(aDir);
    246 
    247      let input = document.getElementById("inputFileDirectoryChange");
    248      let changeEvent = waitForEvent(input, "change");
    249 
    250      SpecialPowers.wrap(document).notifyUserGestureActivation();
    251      input.click();
    252 
    253      await promptHandled;
    254      await changeEvent;
    255 
    256      MockFilePicker.cleanup();
    257  })
    258  .then(function() {
    259    test_fileList("inputFileWebkitDirectory", testDirData);
    260  });
    261 }
    262 
    263 async function test_setup() {
    264  if (AppConstants.platform === "android") {
    265    let MockPromptCollection = SpecialPowers.MockPromptCollection;
    266    MockPromptCollection.init(SpecialPowers.wrap(window).browsingContext);
    267  } else {
    268    let promptHandlerUrl = SimpleTest.getTestFileURL("script_promptHandler.js")
    269    promptHandler = SpecialPowers.loadChromeScript(promptHandlerUrl);
    270 
    271    let promptHandlerReady = new Promise(resolve => promptHandler.addMessageListener("initDone", resolve));
    272    promptHandler.sendAsyncMessage("init");
    273    await promptHandlerReady;
    274  }
    275 
    276  SpecialPowers.pushPrefEnv({"set": [["dom.filesystem.pathcheck.disabled", true],
    277                                     ["dom.webkitBlink.dirPicker.enabled", true]]}, next);
    278 }
    279 
    280 async function test_cleanup() {
    281  if (AppConstants.platform === "android") {
    282    return;
    283  }
    284  let promptHandlerDone = new Promise(resolve => promptHandler.addMessageListener("cleanupDone", resolve));
    285  promptHandler.sendAsyncMessage("cleanup");
    286  await promptHandlerDone;
    287  promptHandler.destroy();
    288 }
    289 
    290 var testDirData = [ { name: "foo.txt", path: "/foo.txt" },
    291                    { name: "bar.txt", path: "/subdir/bar.txt" }];
    292 
    293 var tests = [
    294  test_setup,
    295 
    296  function() { populateInputFile("inputFileWebkitDirectory"); },
    297 
    298  function() { test_fileList("inputFileWebkitDirectory", testDirData); },
    299 
    300  function() {
    301    // Symlinks are not available on Windows and so will not be created.
    302    if (AppConstants.platform === "win" || AppConstants.platform === "android") {
    303      info("Skipping individual symlink check on Windows and Android.");
    304      next();
    305      return;
    306    }
    307 
    308    test_individualSymlink("inputFileWebkitFile").catch(err => ok(false, `Problem in symlink case: ${err}`));
    309  },
    310 
    311  test_webkitdirectory_attribute,
    312 
    313  test_changeDataWhileWorking,
    314 ];
    315 
    316 async function next() {
    317  if (!tests.length) {
    318    await test_cleanup();
    319    SimpleTest.finish();
    320    return;
    321  }
    322 
    323  var test = tests.shift();
    324  await test();
    325 }
    326 
    327 SimpleTest.waitForExplicitFinish();
    328 next();
    329 </script>
    330 </body>
    331 </html>