Bug 807875 - Implement creationDate correctly for Mac OS X. r=froydnj
authorDavid Rajchenbach-Teller <dteller@mozilla.com>
Thu, 15 Nov 2012 11:22:27 -0800
changeset 113404 fb65f083690fee1208300bb18dd71271d91aa907
parent 113403 61232c5c748d06b3e905c31069a86ee873d27ebb
child 113405 c231b310485e2df40314842e2320618d7237298e
push id23870
push userryanvm@gmail.com
push dateFri, 16 Nov 2012 01:21:36 +0000
treeherdermozilla-central@58ebb638a7ea [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs807875
milestone19.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 807875 - Implement creationDate correctly for Mac OS X. r=froydnj
dom/system/OSFileConstants.cpp
toolkit/components/osfile/osfile_unix_back.jsm
toolkit/components/osfile/osfile_unix_front.jsm
toolkit/components/osfile/osfile_win_front.jsm
toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
--- a/dom/system/OSFileConstants.cpp
+++ b/dom/system/OSFileConstants.cpp
@@ -402,16 +402,21 @@ static dom::ConstantSpec gLibcProperties
   { "OSFILE_OFFSETOF_STAT_ST_MTIME", INT_TO_JSVAL(offsetof (struct stat, st_mtimespec)) },
   { "OSFILE_OFFSETOF_STAT_ST_CTIME", INT_TO_JSVAL(offsetof (struct stat, st_ctimespec)) },
 #else
   { "OSFILE_OFFSETOF_STAT_ST_ATIME", INT_TO_JSVAL(offsetof (struct stat, st_atime)) },
   { "OSFILE_OFFSETOF_STAT_ST_MTIME", INT_TO_JSVAL(offsetof (struct stat, st_mtime)) },
   { "OSFILE_OFFSETOF_STAT_ST_CTIME", INT_TO_JSVAL(offsetof (struct stat, st_ctime)) },
 #endif // defined(HAVE_ST_ATIME)
 
+  // Several OSes have a birthtime field. For the moment, supporting only Darwin.
+#if defined(_DARWIN_FEATURE_64_BIT_INODE)
+  { "OSFILE_OFFSETOF_STAT_ST_BIRTHTIME", INT_TO_JSVAL(offsetof (struct stat, st_birthtime)) },
+#endif // defined(_DARWIN_FEATURE_64_BIT_INODE)
+
 #endif // defined(XP_UNIX)
 
 
 
   // System configuration
 
   // Under MacOSX, to avoid using deprecated functions that do not
   // match the constants we define in this object (including
--- a/toolkit/components/osfile/osfile_unix_back.jsm
+++ b/toolkit/components/osfile/osfile_unix_back.jsm
@@ -150,16 +150,22 @@
          // that everybody has |time_t st_atime|, possibly followed by padding
          stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_ATIME,
                           "st_atime", Types.time_t.implementation);
          stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_MTIME,
                           "st_mtime", Types.time_t.implementation);
          stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_CTIME,
                           "st_ctime", Types.time_t.implementation);
 
+         // To complicate further, MacOS and some BSDs have a field |birthtime|
+         if ("OSFILE_OFFSETOF_STAT_ST_BIRTHTIME" in OS.Constants.libc) {
+           stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_BIRTHTIME,
+                             "st_birthtime", Types.time_t.implementation);
+         }
+
          stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_SIZE,
                         "st_size", Types.size_t.implementation);
          Types.stat = stat.getType();
        }
 
        // Structure |DIR|
        if ("OSFILE_SIZEOF_DIR" in OS.Constants.libc) {
          // On platforms for which we need to access the fields of DIR
--- a/toolkit/components/osfile/osfile_unix_front.jsm
+++ b/toolkit/components/osfile/osfile_unix_front.jsm
@@ -730,16 +730,20 @@
      let MODE_MASK = 4095 /*= 07777*/;
      File.Info = function Info(stat) {
        this._st_mode = stat.st_mode;
        this._st_uid = stat.st_uid;
        this._st_gid = stat.st_gid;
        this._st_atime = stat.st_atime;
        this._st_mtime = stat.st_mtime;
        this._st_ctime = stat.st_ctime;
+       // Some platforms (e.g. MacOS X, some BSDs) store a file creation date
+       if ("OSFILE_OFFSETOF_STAT_ST_BIRTHTIME" in OS.Constants.libc) {
+         this._st_birthtime = stat.st_birthtime;
+       }
        this._st_size = stat.st_size;
      };
      File.Info.prototype = {
        /**
         * |true| if this file is a directory, |false| otherwise
         */
        get isDir() {
          return (this._st_mode & OS.Constants.libc.S_IFMT) == OS.Constants.libc.S_IFDIR;
@@ -756,26 +760,22 @@
         * Note that the result may be |NaN| if the size of the file cannot be
         * represented in JavaScript.
         *
         * @type {number}
         */
        get size() {
          return exports.OS.Shared.Type.size_t.importFromC(this._st_size);
        },
-       /**
-        * The date of creation of this file
-        *
-        * @type {Date}
-        */
+       // Deprecated, use macBirthDate/winBirthDate instead
        get creationDate() {
-         delete this.creationDate;
-         let date = new Date(this._st_ctime * 1000);
-         Object.defineProperty(this, "creationDate", { value: date });
-         return date;
+         // On the Macintosh, returns the birth date if available.
+         // On other Unix, as the birth date is not available,
+         // returns the epoch.
+         return this.macBirthDate || new Date(0);
        },
        /**
         * The date of last access to this file.
         *
         * Note that the definition of last access may depend on the
         * underlying operating system and file system.
         *
         * @type {Date}
@@ -791,16 +791,27 @@
         */
        get lastModificationDate() {
          delete this.lastModificationDate;
          let date = new Date(this._st_mtime * 1000);
          Object.defineProperty(this, "lastModificationDate", {value: date});
          return date;
        },
        /**
+        * Return the date at which the status of this file was last modified
+        * (this is the date of the latest write/renaming/mode change/...
+        * of the file)
+        */
+       get unixLastStatusChangeDate() {
+         delete this.unixLastStatusChangeDate;
+         let date = new Date(this._st_ctime * 1000);
+         Object.defineProperty(this, "unixLastStatusChangeDate", {value: date});
+         return date;
+       },
+       /**
         * Return the Unix owner of this file.
         */
        get unixOwner() {
          return exports.OS.Shared.Type.uid_t.importFromC(this._st_uid);
        },
        /**
         * Return the Unix group of this file.
         */
@@ -810,16 +821,42 @@
        /**
         * Return the Unix mode of this file.
         */
        get unixMode() {
          return exports.OS.Shared.Type.mode_t.importFromC(this._st_mode & MODE_MASK);
        }
      };
 
+    /**
+     * The date of creation of this file.
+     *
+     * Note that the date returned by this method is not always
+     * reliable. Not all file systems are able to provide this
+     * information.
+     *
+     * @type {Date}
+     */
+     if ("OSFILE_OFFSETOF_STAT_ST_BIRTHTIME" in OS.Constants.libc) {
+       Object.defineProperty(
+         File.Info.prototype,
+         "macBirthDate",
+         {
+           get: function macBirthDate() {
+             delete this.macBirthDate;
+             let time;
+             time = this._st_birthtime;
+             let date = new Date(time * 1000);
+             Object.defineProperty(this, "macBirthDate", { value: date });
+             return date;
+           }
+         }
+       );
+     }
+
      /**
       * Return a version of an instance of File.Info that can be sent
       * from a worker thread to the main thread. Note that deserialization
       * is asymmetric and returns an object with a different implementation.
       */
      File.Info.toMsg = function toMsg(stat) {
        if (!stat instanceof File.Info) {
          throw new TypeError("parameter of File.Info.toMsg must be a File.Info");
--- a/toolkit/components/osfile/osfile_win_front.jsm
+++ b/toolkit/components/osfile/osfile_win_front.jsm
@@ -744,25 +744,29 @@
         * represented in JavaScript.
         *
         * @type {number}
         */
        get size() {
          let value = ctypes.UInt64.join(this._nFileSizeHigh, this._nFileSizeLow);
          return exports.OS.Shared.Type.uint64_t.importFromC(value);
        },
+       // Deprecated
+       get creationDate() {
+         return this.winBirthDate;
+       },
        /**
-        * The date of creation of this file
+        * The date of creation of this file.
         *
         * @type {Date}
         */
-       get creationDate() {
-         delete this.creationDate;
+       get winBirthDate() {
+         delete this.winBirthDate;
          let date = FILETIME_to_Date(this._ftCreationTime);
-         Object.defineProperty(this, "creationDate", { value: date });
+         Object.defineProperty(this, "winBirthDate", { value: date });
          return date;
        },
        /**
         * The date of last access to this file.
         *
         * Note that the definition of last access may depend on the
         * underlying operating system and file system.
         *
--- a/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
+++ b/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
@@ -648,26 +648,36 @@ function test_info() {
   is(stat.size.toString(), size, "test_info: correct size");
 
   let stop = new Date();
 
   // We round down/up by 1s as file system precision is lower than Date precision
   let startMs = start.getTime() - 1000;
   let stopMs  = stop.getTime() + 1000;
 
-  let birth = stat.creationDate;
-  ok(birth.getTime() <= stopMs,
-     "test_info: file was created before now - " + stop + ", " + birth);
-  // Note: Previous versions of this test checked whether the file has
-  // been created after the start of the test. Unfortunately, this sometimes
-  // failed under Windows, in specific circumstances: if the file has been
-  // removed at the start of the test and recreated immediately, the Windows
-  // file system detects this and decides that the file was actually truncated
-  // rather than recreated, hence that it should keep its previous creation date.
-  // Debugging hilarity ensues.
+  (function() {
+    let birth;
+    if ("winBirthDate" in info) {
+      birth = info.winBirthDate;
+    } else if ("macBirthDate" in info) {
+      birth = info.macBirthDate;
+    } else {
+      ok(true, "Skipping birthdate test");
+      return;
+    }
+    ok(birth.getTime() <= stopMs,
+    "test_info: file was created before now - " + stop + ", " + birth);
+    // Note: Previous versions of this test checked whether the file has
+    // been created after the start of the test. Unfortunately, this sometimes
+    // failed under Windows, in specific circumstances: if the file has been
+    // removed at the start of the test and recreated immediately, the Windows
+    // file system detects this and decides that the file was actually truncated
+    // rather than recreated, hence that it should keep its previous creation date.
+    // Debugging hilarity ensues.
+  });
 
   let change = stat.lastModificationDate;
   ok(change.getTime() >= startMs
      && change.getTime() <= stopMs,
      "test_info: file has changed between the start of the test and now - " + start + ", " + stop + ", " + change);
 
   // Test OS.File.prototype.stat on new file
   file = OS.File.open(filename);