Bug 840887 - Fix behavior of DirectoryIterator when the directory doesn't exist - Windows version. r=froydnj
authorDavid Rajchenbach-Teller <dteller@mozilla.com>
Tue, 26 Feb 2013 12:08:07 -0500
changeset 123037 692f9ca217450328cb8d31c96385a3b4f22a6ec0
parent 123036 bd4206b887ffaba3b779218f29da7c48a0037f28
child 123038 65108c196870bc6a9c7fc787559367caeabc0d2f
push id24372
push useremorley@mozilla.com
push dateWed, 27 Feb 2013 13:22:59 +0000
treeherdermozilla-central@0a91da5f5eab [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs840887
milestone22.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 840887 - Fix behavior of DirectoryIterator when the directory doesn't exist - Windows version. r=froydnj
toolkit/components/osfile/osfile_win_allthreads.jsm
toolkit/components/osfile/osfile_win_front.jsm
--- a/toolkit/components/osfile/osfile_win_allthreads.jsm
+++ b/toolkit/components/osfile/osfile_win_allthreads.jsm
@@ -333,9 +333,13 @@ if (typeof Components != "undefined") {
   // Special constructors that need to be defined on all threads
   OSError.closed = function closed(operation) {
     return new OSError(operation, exports.OS.Constants.Win.INVALID_HANDLE_VALUE);
   };
 
   OSError.exists = function exists(operation) {
     return new OSError(operation, exports.OS.Constants.Win.ERROR_FILE_EXISTS);
   };
+
+  OSError.noSuchFile = function noSuchFile(operation) {
+    return new OSError(operation, exports.OS.Constants.Win.ERROR_FILE_NOT_FOUND);
+  };
 })(this);
--- a/toolkit/components/osfile/osfile_win_front.jsm
+++ b/toolkit/components/osfile/osfile_win_front.jsm
@@ -453,23 +453,16 @@
          flags = flags | Const.MOVEFILE_REPLACE_EXISTING;
        }
        throw_on_zero("move",
          WinFile.MoveFileEx(sourcePath, destPath, flags)
        );
      };
 
      /**
-      * A global value used to receive data during a
-      * |FindFirstFile|/|FindNextFile|.
-      */
-     let gFindData = new OS.Shared.Type.FindData.implementation();
-     let gFindDataPtr = gFindData.address();
-
-     /**
       * A global value used to receive data during time conversions.
       */
      let gSystemTime = new OS.Shared.Type.SystemTime.implementation();
      let gSystemTimePtr = gSystemTime.address();
 
      /**
       * Utility function: convert a FILETIME to a JavaScript Date.
       */
@@ -505,74 +498,93 @@
       */
      File.DirectoryIterator = function DirectoryIterator(path, options) {
        exports.OS.Shared.AbstractFile.AbstractIterator.call(this);
        if (options && options.winPattern) {
          this._pattern = path + "\\" + options.winPattern;
        } else {
          this._pattern = path + "\\*";
        }
-       this._handle = null;
        this._path = path;
-       this._started = false;
-       this._closed = false;
+
+       // Pre-open the first item.
+       this._first = true;
+       this._findData = new OS.Shared.Type.FindData.implementation();
+       this._findDataPtr = this._findData.address();
+       this._handle = WinFile.FindFirstFile(this._pattern, this._findDataPtr);
+       if (this._handle == Const.INVALID_HANDLE_VALUE) {
+         let error = ctypes.winLastError;
+         this._findData = null;
+         this._findDataPtr = null;
+         if (error == Const.ERROR_FILE_NOT_FOUND) {
+           // Directory is empty, let's behave as if it were closed
+           LOG("Directory is empty");
+           this._closed = true;
+           this._exists = true;
+         } else if (error == Const.ERROR_PATH_NOT_FOUND) {
+           // Directory does not exist, let's throw if we attempt to walk it
+           LOG("Directory does not exist");
+           this._closed = true;
+           this._exists = false;
+         } else {
+           throw new File.Error("DirectoryIterator", error);
+         }
+       } else {
+         this._closed = false;
+         this._exists = true;
+       }
      };
+
      File.DirectoryIterator.prototype = Object.create(exports.OS.Shared.AbstractFile.AbstractIterator.prototype);
 
-       /**
-        * Fetch the next entry in the directory.
-        *
-        * @return null If we have reached the end of the directory.
-        */
+
+     /**
+      * Fetch the next entry in the directory.
+      *
+      * @return null If we have reached the end of the directory.
+      */
      File.DirectoryIterator.prototype._next = function _next() {
-        // Bailout if the iterator is closed. Note that this may
-        // happen even before it is fully initialized.
-        if (this._closed) {
-          return null;
-        }
+       // Bailout if the directory does not exist
+       if (!this._exists) {
+         throw File.Error.noSuchFile("DirectoryIterator.prototype.next");
+       }
+       // Bailout if the iterator is closed.
+       if (this._closed) {
+         return null;
+       }
+       // If this is the first entry, we have obtained it already
+       // during construction.
+       if (this._first) {
+         this._first = false;
+         return this._findData;
+       }
 
-         // Iterator is not fully initialized yet. Finish
-         // initialization.
-         if (!this._started) {
-            this._started = true;
-            this._handle = WinFile.FindFirstFile(this._pattern, gFindDataPtr);
-            if (this._handle == null) {
-              let error = ctypes.winLastError;
-              if (error == Const.ERROR_FILE_NOT_FOUND) {
-                this.close();
-                return null;
-              } else {
-                throw new File.Error("iter (FindFirstFile)", error);
-              }
-            }
-            return gFindData;
-         }
-
-         if (WinFile.FindNextFile(this._handle, gFindDataPtr)) {
-           return gFindData;
+       if (WinFile.FindNextFile(this._handle, this._findDataPtr)) {
+         return this._findData;
+       } else {
+         let error = ctypes.winLastError;
+         this.close();
+         if (error == Const.ERROR_NO_MORE_FILES) {
+            return null;
          } else {
-           let error = ctypes.winLastError;
-           this.close();
-           if (error == Const.ERROR_NO_MORE_FILES) {
-              return null;
-           } else {
-              throw new File.Error("iter (FindNextFile)", error);
-           }
+            throw new File.Error("iter (FindNextFile)", error);
          }
-       },
-       /**
-        * Return the next entry in the directory, if any such entry is
-        * available.
-        *
-        * Skip special directories "." and "..".
-        *
-        * @return {File.Entry} The next entry in the directory.
-        * @throws {StopIteration} Once all files in the directory have been
-        * encountered.
-        */
+       }
+     },
+
+     /**
+      * Return the next entry in the directory, if any such entry is
+      * available.
+      *
+      * Skip special directories "." and "..".
+      *
+      * @return {File.Entry} The next entry in the directory.
+      * @throws {StopIteration} Once all files in the directory have been
+      * encountered.
+      */
      File.DirectoryIterator.prototype.next = function next() {
          // FIXME: If we start supporting "\\?\"-prefixed paths, do not forget
          // that "." and ".." are absolutely normal file names if _path starts
          // with such prefix
          for (let entry = this._next(); entry != null; entry = this._next()) {
            let name = entry.cFileName.readString();
            if (name == "." || name == "..") {
              continue;
@@ -591,16 +603,25 @@
          // We might not have a handle if the iterator is closed
          // before being used.
          throw_on_zero("FindClose",
            WinFile.FindClose(this._handle));
          this._handle = null;
        }
      };
 
+    /**
+     * Determine whether the directory exists.
+     *
+     * @return {boolean}
+     */
+     File.DirectoryIterator.prototype.exists = function exists() {
+       return this._exists;
+     };
+
      File.DirectoryIterator.Entry = function Entry(win_entry, parent) {
        if (!win_entry.dwFileAttributes || !win_entry.ftCreationTime ||
            !win_entry.ftLastAccessTime || !win_entry.ftLastWriteTime)
         throw new TypeError();
 
        // Copy the relevant part of |win_entry| to ensure that
        // our data is not overwritten prematurely.
        let isDir = !!(win_entry.dwFileAttributes & Const.FILE_ATTRIBUTE_DIRECTORY);