Bug 923540 - Add a function to recursively remove directories. r=Yoric
authorMarco Castelluccio <mar.castelluccio@studenti.unina.it>
Thu, 10 Oct 2013 10:59:49 -0400
changeset 164141 dc3d80fd48b0377df01c86ca0bb1bcc67720d020
parent 164140 e18f53aabaac6b4f995b901f33655461251408a1
child 164142 a222b928494de41b3c3d115865fb55045d1fedfc
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersYoric
bugs923540
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 923540 - Add a function to recursively remove directories. r=Yoric
toolkit/components/osfile/modules/osfile_async_front.jsm
toolkit/components/osfile/modules/osfile_async_worker.js
toolkit/components/osfile/modules/osfile_shared_front.jsm
toolkit/components/osfile/modules/osfile_unix_front.jsm
toolkit/components/osfile/modules/osfile_win_front.jsm
toolkit/components/osfile/tests/xpcshell/test_removeDir.js
toolkit/components/osfile/tests/xpcshell/xpcshell.ini
--- a/toolkit/components/osfile/modules/osfile_async_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_async_front.jsm
@@ -766,16 +766,21 @@ File.writeAtomic = function writeAtomic(
   //   threads;
   // - we take care of any |byteOffset|.
   return Scheduler.post("writeAtomic",
     [Type.path.toMsg(path),
      Type.void_t.in_ptr.toMsg(buffer),
      options], [options, buffer]);
 };
 
+File.removeDir = function(path, options = {}) {
+  return Scheduler.post("removeDir",
+    [Type.path.toMsg(path), options], path);
+};
+
 /**
  * Information on a file, as returned by OS.File.stat or
  * OS.File.prototype.stat
  *
  * @constructor
  */
 File.Info = function Info(value) {
   // Note that we can't just do this[k] = value[k] because our
--- a/toolkit/components/osfile/modules/osfile_async_worker.js
+++ b/toolkit/components/osfile/modules/osfile_async_worker.js
@@ -281,16 +281,19 @@ if (this.Components) {
      if (options.tmpPath) {
        options.tmpPath = Type.path.fromMsg(options.tmpPath);
      }
      return File.writeAtomic(Type.path.fromMsg(path),
                              Type.voidptr_t.fromMsg(buffer),
                              options
                             );
    },
+   removeDir: function(path, options) {
+     return File.removeDir(Type.path.fromMsg(path), options);
+   },
    new_DirectoryIterator: function new_DirectoryIterator(path, options) {
      let directoryPath = Type.path.fromMsg(path);
      let iterator = new File.DirectoryIterator(directoryPath, options);
      return OpenedDirectoryIterators.add(iterator, {
        // Adding path information to keep track of opened directory
        // iterators to report leaks when debugging.
        path: directoryPath
      });
--- a/toolkit/components/osfile/modules/osfile_shared_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_shared_front.jsm
@@ -389,10 +389,44 @@ AbstractFile.writeAtomic =
   } finally {
     tmpFile.close();
   }
 
   OS.File.move(options.tmpPath, path, {noCopy: true});
   return bytesWritten;
 };
 
+/**
+  * Remove an existing directory and its contents.
+  *
+  * @param {string} path The name of the directory.
+  * @param {*=} options Additional options.
+  *   - {bool} ignoreAbsent If |false|, throw an error if the directory doesn't
+  *     exist. |true| by default.
+  *   - {boolean} ignorePermissions If |true|, remove the file even when lacking write
+  *     permission.
+  *
+  * @throws {OS.File.Error} In case of I/O error, in particular if |path| is
+            not a directory.
+  */
+AbstractFile.removeDir = function(path, options = {}) {
+  let iterator = new OS.File.DirectoryIterator(path);
+  if (!iterator.exists() && options.ignoreAbsent) {
+    return;
+  }
+
+  try {
+    for (let entry in iterator) {
+      if (entry.isDir) {
+        OS.File.removeDir(entry.path, options);
+      } else {
+        OS.File.remove(entry.path, options);
+      }
+    }
+  } finally {
+    iterator.close();
+  }
+
+  OS.File.removeEmptyDir(path);
+};
+
    exports.OS.Shared.AbstractFile = AbstractFile;
 })(this);
--- a/toolkit/components/osfile/modules/osfile_unix_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_unix_front.jsm
@@ -812,16 +812,17 @@
        } else {
          throw_on_negative("stat", UnixFile.stat(path, gStatDataPtr));
        }
        return new File.Info(gStatData);
      };
 
      File.read = exports.OS.Shared.AbstractFile.read;
      File.writeAtomic = exports.OS.Shared.AbstractFile.writeAtomic;
+     File.removeDir = exports.OS.Shared.AbstractFile.removeDir;
 
      /**
       * Get the current directory by getCurrentDirectory.
       */
      File.getCurrentDirectory = function getCurrentDirectory() {
        let path = UnixFile.get_current_dir_name?UnixFile.get_current_dir_name():
          UnixFile.getwd_auto(null);
        throw_on_null("getCurrentDirectory",path);
--- a/toolkit/components/osfile/modules/osfile_win_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_win_front.jsm
@@ -751,16 +751,17 @@
        winAccess: 0,
        // Directories can only be opened with backup semantics(!)
        winFlags: OS.Constants.Win.FILE_FLAG_BACKUP_SEMANTICS,
        winDisposition: OS.Constants.Win.OPEN_EXISTING
      };
 
      File.read = exports.OS.Shared.AbstractFile.read;
      File.writeAtomic = exports.OS.Shared.AbstractFile.writeAtomic;
+     File.removeDir = exports.OS.Shared.AbstractFile.removeDir;
 
      /**
       * Get the current directory by getCurrentDirectory.
       */
      File.getCurrentDirectory = function getCurrentDirectory() {
            // This function is more complicated than one could hope.
            //
            // This is due to two facts:
new file mode 100644
--- /dev/null
+++ b/toolkit/components/osfile/tests/xpcshell/test_removeDir.js
@@ -0,0 +1,88 @@
+/* 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/. */
+
+"use strict";
+
+Components.utils.import("resource://gre/modules/osfile.jsm");
+Components.utils.import("resource://gre/modules/Task.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+do_register_cleanup(function() {
+  Services.prefs.setBoolPref("toolkit.osfile.log", false);
+});
+
+function run_test() {
+  Services.prefs.setBoolPref("toolkit.osfile.log", true);
+
+  run_next_test();
+}
+
+add_task(function() {
+  // Set up profile. We create the directory in the profile, because the profile
+  // is removed after every test run.
+  do_get_profile();
+
+  let file = OS.Path.join(OS.Constants.Path.profileDir, "file");
+  let dir = OS.Path.join(OS.Constants.Path.profileDir, "directory");
+  let file1 = OS.Path.join(dir, "file1");
+  let file2 = OS.Path.join(dir, "file2");
+  let subDir = OS.Path.join(dir, "subdir");
+  let fileInSubDir = OS.Path.join(subDir, "file");
+
+  // Sanity checking for the test
+  do_check_false((yield OS.File.exists(dir)));
+
+  // Remove non-existent directory
+  let exception = null;
+  try {
+    yield OS.File.removeDir(dir);
+  } catch (ex) {
+    exception = ex;
+  }
+
+  do_check_true(!!exception);
+  do_check_true(exception instanceof OS.File.Error);
+
+  // Remove non-existent directory with ignoreAbsent
+  yield OS.File.removeDir(dir, {ignoreAbsent: true});
+
+  // Remove file
+  yield OS.File.writeAtomic(file, "content", { tmpPath: file + ".tmp" });
+  exception = null;
+  try {
+    yield OS.File.removeDir(file);
+  } catch (ex) {
+    exception = ex;
+  }
+
+  do_check_true(!!exception);
+  do_check_true(exception instanceof OS.File.Error);
+
+  // Remove empty directory
+  yield OS.File.makeDir(dir);
+  yield OS.File.removeDir(dir);
+  do_check_false((yield OS.File.exists(dir)));
+
+  // Remove directory that contains one file
+  yield OS.File.makeDir(dir);
+  yield OS.File.writeAtomic(file1, "content", { tmpPath: file1 + ".tmp" });
+  //yield OS.File.open(file1, {create:true});
+  yield OS.File.removeDir(dir)
+  do_check_false((yield OS.File.exists(dir)));
+
+  // Remove directory that contains multiple files
+  yield OS.File.makeDir(dir);
+  yield OS.File.writeAtomic(file1, "content", { tmpPath: file1 + ".tmp" });
+  yield OS.File.writeAtomic(file2, "content", { tmpPath: file2 + ".tmp" });
+  yield OS.File.removeDir(dir)
+  do_check_false((yield OS.File.exists(dir)));
+
+  // Remove directory that contains a file and a directory
+  yield OS.File.makeDir(dir);
+  yield OS.File.writeAtomic(file1, "content", { tmpPath: file1 + ".tmp" });
+  yield OS.File.makeDir(subDir);
+  yield OS.File.writeAtomic(fileInSubDir, "content", { tmpPath: fileInSubDir + ".tmp" });
+  yield OS.File.removeDir(dir);
+  do_check_false((yield OS.File.exists(dir)));
+});
--- a/toolkit/components/osfile/tests/xpcshell/xpcshell.ini
+++ b/toolkit/components/osfile/tests/xpcshell/xpcshell.ini
@@ -4,8 +4,9 @@ tail =
 
 [test_osfile_closed.js]
 [test_path.js]
 [test_osfile_async.js]
 [test_profiledir.js]
 [test_logging.js]
 [test_creationDate.js]
 [test_path_constants.js]
+[test_removeDir.js]