merge m-c to fx-team
authorTim Taubert <ttaubert@mozilla.com>
Sun, 22 Jul 2012 10:38:00 +0200
changeset 100050 462106f027af8eeadfa78e357f77465df6813ef3
parent 100040 defbe00ca0916c2f2488c641190c547c84603d72 (current diff)
parent 100049 b258d6ce2e47882f008079aeb817c9be30308077 (diff)
child 100067 19fe7ecd0af8e6319db784e01410fb0a716365ae
child 100070 2a8871238241a99975b3acce32dca5dcb77daaa7
push id23163
push userttaubert@mozilla.com
push dateSun, 22 Jul 2012 08:38:10 +0000
treeherdermozilla-central@462106f027af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone17.0a1
first release with
nightly linux32
462106f027af / 17.0a1 / 20120722030555 / files
nightly linux64
462106f027af / 17.0a1 / 20120722030555 / files
nightly mac
462106f027af / 17.0a1 / 20120722030555 / files
nightly win32
462106f027af / 17.0a1 / 20120722030555 / files
nightly win64
462106f027af / 17.0a1 / 20120722030555 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge m-c to fx-team
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4857,20 +4857,27 @@ function toggleSidebar(commandID, forceO
 
   var sidebarBroadcaster = document.getElementById(commandID);
   var sidebar = document.getElementById("sidebar"); // xul:browser
   var sidebarTitle = document.getElementById("sidebar-title");
   var sidebarSplitter = document.getElementById("sidebar-splitter");
 
   if (sidebarBroadcaster.getAttribute("checked") == "true") {
     if (!forceOpen) {
+      // Replace the document currently displayed in the sidebar with about:blank
+      // so that we can free memory by unloading the page. We need to explicitly
+      // create a new content viewer because the old one doesn't get destroyed
+      // until about:blank has loaded (which does not happen as long as the
+      // element is hidden).
+      sidebar.setAttribute("src", "about:blank");
+      sidebar.docShell.createAboutBlankContentViewer(null);
+
       sidebarBroadcaster.removeAttribute("checked");
       sidebarBox.setAttribute("sidebarcommand", "");
       sidebarTitle.value = "";
-      sidebar.setAttribute("src", "about:blank");
       sidebarBox.hidden = true;
       sidebarSplitter.hidden = true;
       content.focus();
     } else {
       fireSidebarFocusedEvent();
     }
     return;
   }
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -421,17 +421,17 @@ def wrapCommand(cmd):
   return cmd
 
 class ShutdownLeakLogger(object):
   """
   Parses the mochitest run log when running a debug build, assigns all leaked
   DOM windows (that are still around after test suite shutdown, despite running
   the GC) to the tests that created them and prints leak statistics.
   """
-  MAX_LEAK_COUNT = 5
+  MAX_LEAK_COUNT = 3
 
   def __init__(self, logger):
     self.logger = logger
     self.tests = []
     self.leakedWindows = {}
     self.leakedDocShells = set()
     self.currentTest = None
     self.seenShutdown = False
--- a/dom/system/OSFileConstants.cpp
+++ b/dom/system/OSFileConstants.cpp
@@ -5,16 +5,17 @@
 #include "fcntl.h"
 #include "errno.h"
 
 #include "prsystem.h"
 
 #if defined(XP_UNIX)
 #include "unistd.h"
 #include "dirent.h"
+#include "sys/stat.h"
 #endif // defined(XP_UNIX)
 
 #if defined(XP_MACOSX)
 #include "copyfile.h"
 #endif // defined(XP_MACOSX)
 
 #if defined(XP_WIN)
 #include <windows.h>
@@ -272,28 +273,49 @@ static dom::ConstantSpec gLibcProperties
   INT_CONSTANT(DT_CHR),
   INT_CONSTANT(DT_DIR),
   INT_CONSTANT(DT_BLK),
   INT_CONSTANT(DT_REG),
   INT_CONSTANT(DT_LNK),
   INT_CONSTANT(DT_SOCK),
 #endif // defined(DT_UNKNOWN)
 
+#if defined(S_IFIFO)
+  // Constants for |stat|
+  INT_CONSTANT(S_IFMT),
+  INT_CONSTANT(S_IFIFO),
+  INT_CONSTANT(S_IFCHR),
+  INT_CONSTANT(S_IFDIR),
+  INT_CONSTANT(S_IFBLK),
+  INT_CONSTANT(S_IFREG),
+  INT_CONSTANT(S_IFLNK),
+  INT_CONSTANT(S_IFSOCK),
+#endif // defined(S_IFIFO)
+
   // Constants used to define data structures
   //
   // Many data structures have different fields/sizes/etc. on
   // various OSes / versions of the same OS / platforms. For these
   // data structures, we need to compute and export from C the size
   // and, if necessary, the offset of fields, so as to be able to
   // define the structure in JS.
 
 #if defined(XP_UNIX)
   // The size of |mode_t|.
   { "OSFILE_SIZEOF_MODE_T", INT_TO_JSVAL(sizeof (mode_t)) },
 
+  // The size of |gid_t|.
+  { "OSFILE_SIZEOF_GID_T", INT_TO_JSVAL(sizeof (gid_t)) },
+
+  // The size of |uid_t|.
+  { "OSFILE_SIZEOF_UID_T", INT_TO_JSVAL(sizeof (uid_t)) },
+
+  // The size of |time_t|.
+  { "OSFILE_SIZEOF_TIME_T", INT_TO_JSVAL(sizeof (time_t)) },
+
   // Defining |dirent|.
   // Size
   { "OSFILE_SIZEOF_DIRENT", INT_TO_JSVAL(sizeof (dirent)) },
 
   // Offset of field |d_name|.
   { "OSFILE_OFFSETOF_DIRENT_D_NAME", INT_TO_JSVAL(offsetof (struct dirent, d_name)) },
   // An upper bound to the length of field |d_name| of struct |dirent|.
   // (may not be exact, depending on padding).
@@ -301,30 +323,56 @@ static dom::ConstantSpec gLibcProperties
 
 #if defined(DT_UNKNOWN)
   // Position of field |d_type| in |dirent|
   // Not strictly posix, but seems defined on all platforms
   // except mingw32.
   { "OSFILE_OFFSETOF_DIRENT_D_TYPE", INT_TO_JSVAL(offsetof (struct dirent, d_type)) },
 #endif // defined(DT_UNKNOWN)
 
+
+  // Defining |stat|
+
+  { "OSFILE_SIZEOF_STAT", INT_TO_JSVAL(sizeof (struct stat)) },
+
+  { "OSFILE_OFFSETOF_STAT_ST_MODE", INT_TO_JSVAL(offsetof (struct stat, st_mode)) },
+  { "OSFILE_OFFSETOF_STAT_ST_UID", INT_TO_JSVAL(offsetof (struct stat, st_uid)) },
+  { "OSFILE_OFFSETOF_STAT_ST_GID", INT_TO_JSVAL(offsetof (struct stat, st_gid)) },
+  { "OSFILE_OFFSETOF_STAT_ST_SIZE", INT_TO_JSVAL(offsetof (struct stat, st_size)) },
+
+#if defined(HAVE_ST_ATIMESPEC)
+  { "OSFILE_OFFSETOF_STAT_ST_ATIME", INT_TO_JSVAL(offsetof (struct stat, st_atimespec)) },
+  { "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)
+
 #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
   // |sizeof|/|offsetof| stuff, but not only), for a number of
   // functions, we need to adapt the name of the symbols we are using,
   // whenever macro _DARWIN_FEATURE_64_BIT_INODE is set. We export
   // this value to be able to do so from JavaScript.
 #if defined(_DARWIN_FEATURE_64_BIT_INODE)
-  { "_DARWIN_FEATURE_64_BIT_INODE", INT_TO_JSVAL(1) },
-#endif // defind(_DARWIN_FEATURE_64_BIT_INODE)
+   { "_DARWIN_FEATURE_64_BIT_INODE", INT_TO_JSVAL(1) },
+#endif // defined(_DARWIN_FEATURE_64_BIT_INODE)
+
+  // Similar feature for Linux
+#if defined(_STAT_VER)
+  INT_CONSTANT(_STAT_VER),
+#endif // defined(_STAT_VER)
 
   PROP_END
 };
 
 
 #if defined(XP_WIN)
 /**
  * The properties defined in windows.h.
@@ -363,16 +411,17 @@ static dom::ConstantSpec gWinProperties[
 
   // CreateFile attributes
   INT_CONSTANT(FILE_ATTRIBUTE_ARCHIVE),
   INT_CONSTANT(FILE_ATTRIBUTE_DIRECTORY),
   INT_CONSTANT(FILE_ATTRIBUTE_NORMAL),
   INT_CONSTANT(FILE_ATTRIBUTE_READONLY),
   INT_CONSTANT(FILE_ATTRIBUTE_REPARSE_POINT),
   INT_CONSTANT(FILE_ATTRIBUTE_TEMPORARY),
+  INT_CONSTANT(FILE_FLAG_BACKUP_SEMANTICS),
 
   // CreateFile error constant
   { "INVALID_HANDLE_VALUE", INT_TO_JSVAL(INT_PTR(INVALID_HANDLE_VALUE)) },
 
 
   // CreateFile flags
   INT_CONSTANT(FILE_FLAG_DELETE_ON_CLOSE),
 
--- a/toolkit/components/osfile/osfile_shared.jsm
+++ b/toolkit/components/osfile/osfile_shared.jsm
@@ -146,48 +146,50 @@
              }
            });
          return ptr_t;
        },
 
        /**
         * Attach a finalizer to a type.
         */
-       releaseWith: function(finalizer) {
+       releaseWith: function releaseWith(finalizer) {
          let parent = this;
          let type = new Type("[auto " + finalizer +"] " + this.name,
            this.implementation,
-           function (value, operation) {
+           function release(value, operation) {
              return ctypes.CDataFinalizer(
                parent.convert_from_c(value, operation),
                finalizer);
            });
          return type;
+       },
+
+       /**
+        * Return an alias to a type with a different name.
+        */
+       withName: function withName(name) {
+         return Object.create(this, {name: {value: name}});
        }
      };
 
      exports.OS.Shared.Type = Type;
      let Types = Type;
 
      /*
       * Some values are large integers on 64 bit platforms. Unfortunately,
       * in practice, 64 bit integers cannot be manipulated in JS. We
       * therefore project them to regular numbers whenever possible.
       */
 
      let projectLargeInt = function projectLargeInt(x) {
        return parseInt(x.toString(), 10);
      };
      let projectLargeUInt = function projectLargeUInt(x) {
-       if (ctypes.UInt64.hi(x)) {
-         throw new Error("Number too large " + x +
-             "(unsigned, hi: " + ctypes.UInt64.hi(x) +
-                 ", lo:" + ctypes.UInt64.lo(x) + ")");
-       }
-       return ctypes.UInt64.lo(x);
+       return parseInt(x.toString(), 10);
      };
      let projectValue = function projectValue(x) {
        if (!(x instanceof ctypes.CData)) {
          return x;
        }
        if (!("value" in x)) { // Sanity check
          throw new TypeError("Number " + x.toSource() + " has no field |value|");
        }
@@ -474,17 +476,17 @@
        /**
         * Add a field at a given offset.
         *
         * @param {number} offset The offset at which to insert the field.
         * @param {string} name The name of the field.
         * @param {CType|Type} type The type of the field.
         */
        add_field_at: function add_field_at(offset, name, type) {
-         if (offset === null) {
+         if (offset == null) {
            throw new TypeError("add_field_at requires a non-null offset");
          }
          if (!name) {
            throw new TypeError("add_field_at requires a non-null name");
          }
          if (!type) {
            throw new TypeError("add_field_at requires a non-null type");
          }
--- a/toolkit/components/osfile/osfile_unix_back.jsm
+++ b/toolkit/components/osfile/osfile_unix_back.jsm
@@ -141,20 +141,24 @@
 
        // Note: support for strings in js-ctypes is very limited.
        // Once bug 552551 has progressed, we should extend this
        // type using ctypes.readString/ctypes.writeString
 
        /**
         * Type |mode_t|
         */
-       Types.mode_t = Object.create(
-         Types.intn_t(OS.Constants.libc.OSFILE_SIZEOF_MODE_T),
-         {name: {value: "mode_t"}});
+       Types.mode_t =
+         Types.intn_t(OS.Constants.libc.OSFILE_SIZEOF_MODE_T).withName("mode_t");
 
+       /**
+        * Type |time_t|
+        */
+       Types.time_t =
+         Types.intn_t(OS.Constants.libc.OSFILE_SIZEOF_TIME_T).withName("time_t");
 
        Types.DIR =
          new Type("DIR",
                   ctypes.StructType("DIR"));
 
        Types.null_or_DIR_ptr =
          new Type("null_or_DIR*",
                   Types.DIR.out_ptr.implementation,
@@ -186,16 +190,46 @@
          // We now have built |dirent|.
          Types.dirent = dirent.getType();
          LOG("dirent is: " + Types.dirent.implementation.toSource());
        }
        Types.null_or_dirent_ptr =
          new Type("null_of_dirent",
                   Types.dirent.out_ptr.implementation);
 
+       // Structure |stat|
+       // Same technique
+       {
+         let stat = new OS.Shared.HollowStructure("stat",
+           OS.Constants.libc.OSFILE_SIZEOF_STAT);
+         stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_MODE,
+                        "st_mode", Types.mode_t.implementation);
+         stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_UID,
+                          "st_uid", ctypes.int);
+         stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_GID,
+                          "st_gid", ctypes.int);
+
+         // Here, things get complicated with different data structures.
+         // Some platforms have |time_t st_atime| and some platforms have
+         // |timespec st_atimespec|. However, since |timespec| starts with
+         // a |time_t|, followed by nanoseconds, we just cheat and pretend
+         // 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);
+
+         stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_SIZE,
+                        "st_size", Types.size_t.implementation);
+         Types.stat = stat.getType();
+       }
+
+
        // Declare libc functions as functions of |OS.Unix.File|
 
        // Finalizer-related functions
        let _close =
          libc.declare("close", ctypes.default_abi,
                         /*return */ctypes.int,
                         /*fd*/     ctypes.int);
 
@@ -310,16 +344,32 @@
                     /*fd*/     Types.fd); // Note: MacOS/BSD-specific
 
        UnixFile.ftruncate =
          declareFFI("ftruncate", ctypes.default_abi,
                     /*return*/ Types.negativeone_or_nothing,
                     /*fd*/     Types.fd,
                     /*length*/ Types.off_t);
 
+       if (OS.Constants.libc._DARWIN_FEATURE_64_BIT_INODE) {
+         UnixFile.fstat =
+           declareFFI("fstat$INODE64", ctypes.default_abi,
+                      /*return*/ Types.negativeone_or_nothing,
+                      /*path*/   Types.fd,
+                      /*buf*/    Types.stat.out_ptr
+                     );
+       } else {
+         UnixFile.fstat =
+           declareFFI("fstat", ctypes.default_abi,
+                      /*return*/ Types.negativeone_or_nothing,
+                      /*path*/   Types.fd,
+                      /*buf*/    Types.stat.out_ptr
+                     );
+       }
+
        UnixFile.lchown =
          declareFFI("lchown", ctypes.default_abi,
                     /*return*/ Types.negativeone_or_nothing,
                     /*path*/   Types.string,
                     /*uid_t*/  Types.uid_t,
                     /*gid_t*/  Types.gid_t);
 
        UnixFile.link =
@@ -444,23 +494,98 @@
          declareFFI("write", ctypes.default_abi,
                     /*return*/ Types.negativeone_or_ssize_t,
                     /*fd*/     Types.fd,
                     /*buf*/    Types.char.in_ptr,
                     /*nbytes*/ Types.size_t);
 
        // Weird cases that require special treatment
 
+       // OSes use a variety of hacks to differentiate between
+       // 32-bits and 64-bits versions of |stat|, |lstat|, |fstat|.
+       if (OS.Constants.libc._DARWIN_FEATURE_64_BIT_INODE) {
+         // MacOS X 64-bits
+         UnixFile.stat =
+           declareFFI("stat$INODE64", ctypes.default_abi,
+                      /*return*/ Types.negativeone_or_nothing,
+                      /*path*/   Types.string,
+                      /*buf*/    Types.stat.out_ptr
+                     );
+         UnixFile.lstat =
+           declareFFI("lstat$INODE64", ctypes.default_abi,
+                      /*return*/ Types.negativeone_or_nothing,
+                      /*path*/   Types.string,
+                      /*buf*/    Types.stat.out_ptr
+                     );
+         UnixFile.fstat =
+           declareFFI("fstat$INODE64", ctypes.default_abi,
+                      /*return*/ Types.negativeone_or_nothing,
+                      /*path*/   Types.fd,
+                      /*buf*/    Types.stat.out_ptr
+                     );
+       } else if (OS.Constants.libc._STAT_VER != undefined) {
+         const ver = OS.Constants.libc._STAT_VER;
+         // Linux, all widths
+         let xstat =
+           declareFFI("__xstat", ctypes.default_abi,
+                      /*return*/    Types.negativeone_or_nothing,
+                      /*_stat_ver*/ Types.int,
+                      /*path*/      Types.string,
+                      /*buf*/       Types.stat.out_ptr);
+         let lxstat =
+           declareFFI("__lxstat", ctypes.default_abi,
+                      /*return*/    Types.negativeone_or_nothing,
+                      /*_stat_ver*/ Types.int,
+                      /*path*/      Types.string,
+                      /*buf*/       Types.stat.out_ptr);
+         let fxstat =
+           declareFFI("__fxstat", ctypes.default_abi,
+                      /*return*/    Types.negativeone_or_nothing,
+                      /*_stat_ver*/ Types.int,
+                      /*fd*/        Types.fd,
+                      /*buf*/       Types.stat.out_ptr);
+
+         UnixFile.stat = function stat(path, buf) {
+           return xstat(ver, path, buf);
+         };
+         UnixFile.lstat = function stat(path, buf) {
+           return lxstat(ver, path, buf);
+         };
+         UnixFile.fstat = function stat(fd, buf) {
+           return fxstat(ver, fd, buf);
+         };
+       } else {
+         // Mac OS X 32-bits, other Unix
+         UnixFile.stat =
+           declareFFI("stat", ctypes.default_abi,
+                      /*return*/ Types.negativeone_or_nothing,
+                      /*path*/   Types.string,
+                      /*buf*/    Types.stat.out_ptr
+                     );
+         UnixFile.lstat =
+           declareFFI("lstat", ctypes.default_abi,
+                      /*return*/ Types.negativeone_or_nothing,
+                      /*path*/   Types.string,
+                      /*buf*/    Types.stat.out_ptr
+                     );
+         UnixFile.fstat =
+           declareFFI("fstat", ctypes.default_abi,
+                      /*return*/ Types.negativeone_or_nothing,
+                      /*fd*/     Types.fd,
+                      /*buf*/    Types.stat.out_ptr
+                     );
+       }
+
        // We cannot make a C array of CDataFinalizer, so
        // pipe cannot be directly defined as a C function.
 
        let _pipe =
-         declareFFI("pipe", ctypes.default_abi,
-                    /*return*/ Types.negativeone_or_nothing,
-                    /*fds*/    Types.int.out_ptr);
+         libc.declare("pipe", ctypes.default_abi,
+                    /*return*/ ctypes.int,
+                    /*fds*/    ctypes.ArrayType(ctypes.int, 2));
 
        // A shared per-thread buffer used to communicate with |pipe|
        let _pipebuf = new (ctypes.ArrayType(ctypes.int, 2))();
 
        UnixFile.pipe = function pipe(array) {
          let result = _pipe(_pipebuf);
          if (result == -1) {
            return result;
--- a/toolkit/components/osfile/osfile_unix_front.jsm
+++ b/toolkit/components/osfile/osfile_unix_front.jsm
@@ -155,16 +155,26 @@
          // In this implementation,
          // OS.File.POS_START == OS.Constants.libc.SEEK_SET
          // OS.File.POS_CURRENT == OS.Constants.libc.SEEK_CUR
          // OS.File.POS_END == OS.Constants.libc.SEEK_END
          whence = (whence == undefined)?OS.Constants.libc.SEEK_SET:whence;
          return throw_on_negative("setPosition",
            UnixFile.lseek(this.fd, pos, whence)
          );
+       },
+
+       /**
+        * Fetch the information on the file.
+        *
+        * @return File.Info The information on |this| file.
+        */
+       stat: function stat() {
+         throw_on_negative("stat", UnixFile.fstat(this.fd, gStatDataPtr));
+         return new File.Info(gStatData);
        }
      };
 
      /**
       * A File-related error.
       *
       * To obtain a human-readable error message, use method |toString|.
       * To determine the cause of the error, use the various |becauseX|
@@ -669,17 +679,17 @@
         */
        get isDir() {
          return this._d_type == OS.Constants.libc.DT_DIR;
        },
 
        /**
         * |true| if the entry is a symbolic link, |false| otherwise
         */
-       get isLink() {
+       get isSymLink() {
          return this._d_type == OS.Constants.libc.DT_LNK;
        },
 
        /**
         * The name of the entry.
         * @type {string}
         */
        get name() {
@@ -692,16 +702,136 @@
        get path() {
          delete this.path;
          let path = OS.Unix.Path.join(this._parent, this.name);
          Object.defineProperty(this, "path", {value: path});
          return path;
        }
      };
 
+     let gStatData = new OS.Shared.Type.stat.implementation();
+     let gStatDataPtr = gStatData.address();
+     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;
+       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;
+       },
+       /**
+        * |true| if this file is a symbolink link, |false| otherwise
+        */
+       get isSymLink() {
+         return (this._st_mode & OS.Constants.libc.S_IFMT) == OS.Constants.libc.S_IFLNK;
+       },
+       /**
+        * The size of the file, in bytes.
+        *
+        * Note that the result may be |NaN| if the size of the file cannot be
+        * represented in JavaScript.
+        *
+        * @type {number}
+        */
+       get size() {
+         delete this.size;
+         let size;
+         try {
+           size = OS.Shared.projectValue(this._st_size);
+         } catch(x) {
+           LOG("get size error", x);
+           size = NaN;
+         }
+         Object.defineProperty(this, "size", { value: size });
+         return size;
+       },
+       /**
+        * The date of creation of this file
+        *
+        * @type {Date}
+        */
+       get creationDate() {
+         delete this.creationDate;
+         let date = new Date(this._st_ctime * 1000);
+         Object.defineProperty(this, "creationDate", { 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.
+        *
+        * @type {Date}
+        */
+       get lastAccessDate() {
+         delete this.lastAccessDate;
+         let date = new Date(this._st_atime * 1000);
+         Object.defineProperty(this, "lastAccessDate", {value: date});
+         return date;
+       },
+       /**
+        * Return the date of last modification of this file.
+        */
+       get lastModificationDate() {
+         delete this.lastModificationDate;
+         let date = new Date(this._st_mtime * 1000);
+         Object.defineProperty(this, "lastModificationDate", {value: date});
+         return date;
+       },
+       /**
+        * Return the Unix owner of this file.
+        */
+       get unixOwner() {
+         return this._st_uid;
+       },
+       /**
+        * Return the Unix group of this file.
+        */
+       get unixGroup() {
+         return this._st_gid;
+       },
+       /**
+        * Return the Unix mode of this file.
+        */
+       get unixMode() {
+         return this._st_mode & MODE_MASK;
+       }
+     };
+
+     /**
+      * Fetch the information on a file.
+      *
+      * @param {string} path The full name of the file to open.
+      * @param {*=} options Additional options. In this implementation:
+      *
+      * - {bool} unixNoFollowingLinks If set and |true|, if |path|
+      * represents a symbolic link, the call will return the information
+      * of the link itself, rather than that of the target file.
+      *
+      * @return {File.Information}
+      */
+     File.stat = function stat(path, options) {
+       options = options || noOptions;
+       if (options.unixNoFollowingLinks) {
+         throw_on_negative("stat", UnixFile.lstat(path, gStatDataPtr));
+       } else {
+         throw_on_negative("stat", UnixFile.stat(path, gStatDataPtr));
+       }
+       return new File.Info(gStatData);
+     };
 
      /**
       * Get/set the current directory.
       */
      Object.defineProperty(File, "curDir", {
          set: function(path) {
            throw_on_negative("curDir",
              UnixFile.chdir(path)
--- a/toolkit/components/osfile/osfile_win_back.jsm
+++ b/toolkit/components/osfile/osfile_win_back.jsm
@@ -140,32 +140,46 @@
          new Type("zero_or_nothing",
                   Types.bool.implementation);
 
        Types.FILETIME =
          new Type("FILETIME",
                   ctypes.StructType("FILETIME", [
                   { lo: Types.DWORD.implementation },
                   { hi: Types.DWORD.implementation }]));
-              
+
        Types.FindData =
          new Type("FIND_DATA",
                   ctypes.StructType("FIND_DATA", [
                     { dwFileAttributes: ctypes.uint32_t },
                     { ftCreationTime:   Types.FILETIME.implementation },
                     { ftLastAccessTime: Types.FILETIME.implementation },
                     { ftLastWriteTime:  Types.FILETIME.implementation },
                     { nFileSizeHigh:    Types.DWORD.implementation },
                     { nFileSizeLow:     Types.DWORD.implementation },
                     { dwReserved0:      Types.DWORD.implementation },
                     { dwReserved1:      Types.DWORD.implementation },
                     { cFileName:        ctypes.ArrayType(ctypes.jschar, exports.OS.Constants.Win.MAX_PATH) },
                     { cAlternateFileName: ctypes.ArrayType(ctypes.jschar, 14) }
                       ]));
-                  
+
+       Types.FILE_INFORMATION =
+         new Type("FILE_INFORMATION",
+                  ctypes.StructType("FILE_INFORMATION", [
+                    { dwFileAttributes: ctypes.uint32_t },
+                    { ftCreationTime:   Types.FILETIME.implementation },
+                    { ftLastAccessTime: Types.FILETIME.implementation },
+                    { ftLastWriteTime:  Types.FILETIME.implementation },
+                    { dwVolumeSerialNumber: ctypes.uint32_t },
+                    { nFileSizeHigh:    Types.DWORD.implementation },
+                    { nFileSizeLow:     Types.DWORD.implementation },
+                    { nNumberOfLinks:   ctypes.uint32_t },
+                    { nFileIndex: ctypes.uint64_t }
+                   ]));
+
        Types.SystemTime =
          new Type("SystemTime",
                   ctypes.StructType("SystemTime", [
                   { wYear:      ctypes.int16_t },
                   { wMonth:     ctypes.int16_t },
                   { wDayOfWeek: ctypes.int16_t },
                   { wDay:       ctypes.int16_t },
                   { wHour:      ctypes.int16_t },
@@ -251,16 +265,22 @@
 
        WinFile.GetCurrentDirectory =
          declareFFI("GetCurrentDirectoryW", ctypes.winapi_abi,
                     /*return*/ Types.zero_or_DWORD,
                     /*length*/ Types.DWORD,
                     /*buf*/    Types.jschar.out_ptr
                    );
 
+       WinFile.GetFileInformationByHandle =
+         declareFFI("GetFileInformationByHandle", ctypes.winapi_abi,
+                    /*return*/ Types.zero_or_nothing,
+                    /*handle*/ Types.HANDLE,
+                    /*info*/   Types.FILE_INFORMATION.out_ptr);
+
        WinFile.MoveFileEx =
          declareFFI("MoveFileExW", ctypes.winapi_abi,
                     /*return*/   Types.zero_or_nothing,
                     /*sourcePath*/ Types.jschar.in_ptr,
                     /*destPath*/ Types.jschar.in_ptr,
                     /*flags*/    Types.DWORD
                    );
 
--- a/toolkit/components/osfile/osfile_win_front.jsm
+++ b/toolkit/components/osfile/osfile_win_front.jsm
@@ -41,16 +41,20 @@
      // of the fact that the state is thread-private -- hence that two
      // |read|/|write| operations cannot take place at the same time --
      // and we use the following global mutable values:
      let gBytesRead = new ctypes.int32_t(-1);
      let gBytesReadPtr = gBytesRead.address();
      let gBytesWritten = new ctypes.int32_t(-1);
      let gBytesWrittenPtr = gBytesWritten.address();
 
+     // Same story for GetFileInformationByHandle
+     let gFileInfo = new OS.Shared.Type.FILE_INFORMATION.implementation();
+     let gFileInfoPtr = gFileInfo.address();
+
      /**
       * Representation of a file.
       *
       * You generally do not need to call this constructor yourself. Rather,
       * to open a file, use function |OS.File.open|.
       *
       * @param fd A OS-specific file descriptor.
       * @constructor
@@ -174,17 +178,28 @@
        setPosition: function setPosition(pos, whence) {
          // We are cheating to avoid one unnecessary conversion:
          // In this implementation,
          // OS.File.POS_START == OS.Constants.Win.FILE_BEGIN
          // OS.File.POS_CURRENT == OS.Constants.Win.FILE_CURRENT
          // OS.File.POS_END == OS.Constants.Win.FILE_END
          whence = (whence == undefined)?Const.FILE_BEGIN:whence;
          return throw_on_negative("setPosition",
-	   WinFile.SetFilePointer(this.fd, pos, null, whence));
+           WinFile.SetFilePointer(this.fd, pos, null, whence));
+       },
+
+       /**
+        * Fetch the information on the file.
+        *
+        * @return File.Info The information on |this| file.
+        */
+       stat: function stat() {
+         throw_on_zero("stat",
+           WinFile.GetFileInformationByHandle(this.fd, gFileInfoPtr));
+         return new File.Info(gFileInfo);
        }
      };
 
      /**
       * A File-related error.
       *
       * To obtain a human-readable error message, use method |toString|.
       * To determine the cause of the error, use the various |becauseX|
@@ -316,17 +331,17 @@
       * |winAccess| and this replaces argument |mode|. If unspecified,
       * uses the string |mode|.
       *
       * @return {File} A file object.
       * @throws {OS.File.Error} If the file could not be opened.
       */
      File.open = function Win_open(path, mode, options) {
        options = options || noOptions;
-
+       mode = mode || noOptions;
        let share = options.winShare || DEFAULT_SHARE;
        let security = options.winSecurity || null;
        let flags = options.winFlags || DEFAULT_FLAGS;
        let template = options.winTemplate?options.winTemplate._fd:null;
        let access;
        let disposition;
        if ("winAccess" in options && "winDisposition" in options) {
          access = options.winAccess;
@@ -466,27 +481,31 @@
       */
      let gSystemTime = new OS.Shared.Type.SystemTime.implementation();
      let gSystemTimePtr = gSystemTime.address();
 
      /**
       * Utility function: convert a FILETIME to a JavaScript Date.
       */
      let FILETIME_to_Date = function FILETIME_to_Date(fileTime) {
-       LOG("fileTimeToDate:", fileTime);
        if (fileTime == null) {
          throw new TypeError("Expecting a non-null filetime");
        }
-       LOG("fileTimeToDate normalized:", fileTime);
-       throw_on_zero("FILETIME_to_Date", WinFile.FileTimeToSystemTime(fileTime.address(),
+       throw_on_zero("FILETIME_to_Date",
+                     WinFile.FileTimeToSystemTime(fileTime.address(),
                                                   gSystemTimePtr));
-       return new Date(gSystemTime.wYear, gSystemTime.wMonth,
-                       gSystemTime.wDay, gSystemTime.wHour,
-                       gSystemTime.wMinute, gSystemTime.wSecond,
-                       gSystemTime.wMilliSeconds);
+       // Windows counts hours, minutes, seconds from UTC,
+       // JS counts from local time, so we need to go through UTC.
+       let utc = Date.UTC(gSystemTime.wYear,
+                          gSystemTime.wMonth - 1
+                          /*Windows counts months from 1, JS from 0*/,
+                          gSystemTime.wDay, gSystemTime.wHour,
+                          gSystemTime.wMinute, gSystemTime.wSecond,
+                          gSystemTime.wMilliSeconds);
+       return new Date(utc);
      };
 
      /**
       * Iterate on one directory.
       *
       * This iterator will not enter subdirectories.
       *
       * @param {string} path The directory upon which to iterate.
@@ -610,23 +629,23 @@
        }
        this._parent = parent;
      };
      File.DirectoryIterator.Entry.prototype = {
        /**
         * |true| if the entry is a directory, |false| otherwise
         */
        get isDir() {
-         return this._dwFileAttributes & Const.FILE_ATTRIBUTE_DIRECTORY;
+         return !!(this._dwFileAttributes & Const.FILE_ATTRIBUTE_DIRECTORY);
        },
        /**
         * |true| if the entry is a symbolic link, |false| otherwise
         */
-       get isLink() {
-         return this._dwFileAttributes & Const.FILE_ATTRIBUTE_REPARSE_POINT;
+       get isSymLink() {
+         return !!(this._dwFileAttributes & Const.FILE_ATTRIBUTE_REPARSE_POINT);
        },
        /**
         * The name of the entry.
         * @type {string}
         */
        get name() {
          return this._name;
        },
@@ -668,16 +687,139 @@
          delete this.path;
          let path = OS.Win.Path.join(this._parent, this.name);
          Object.defineProperty(this, "path", {value: path});
          return path;
        }
      };
 
      /**
+      * Information on a file.
+      *
+      * To obtain the latest information on a file, use |File.stat|
+      * (for an unopened file) or |File.prototype.stat| (for an
+      * already opened file).
+      *
+      * @constructor
+      */
+     File.Info = function Info(stat) {
+       this._dwFileAttributes = stat.dwFileAttributes;
+       this._ftCreationTime = stat.ftCreationTime;
+       this._ftLastAccessTime = stat.ftLastAccessTime;
+       this._ftLastWriteTime = stat.ftLastAccessTime;
+       this._nFileSizeHigh = stat.nFileSizeHigh;
+       this._nFileSizeLow = stat.nFileSizeLow;
+     };
+     File.Info.prototype = {
+       /**
+        * |true| if this file is a directory, |false| otherwise
+        */
+       get isDir() {
+         return !!(this._dwFileAttributes & Const.FILE_ATTRIBUTE_DIRECTORY);
+       },
+       /**
+        * |true| if this file is a symbolink link, |false| otherwise
+        */
+       get isSymLink() {
+         return !!(this._dwFileAttributes & Const.FILE_ATTRIBUTE_REPARSE_POINT);
+       },
+       /**
+        * The size of the file, in bytes.
+        *
+        * Note that the result may be |NaN| if the size of the file cannot be
+        * represented in JavaScript.
+        *
+        * @type {number}
+        */
+       get size() {
+         try {
+           return OS.Shared.projectValue(
+             ctypes.uint64_t("" +
+             this._nFileSizeHigh +
+             this._nFileSizeLow));
+         } catch (x) {
+           return NaN;
+         }
+       },
+       /**
+        * The date of creation of this file
+        *
+        * @type {Date}
+        */
+       get creationDate() {
+         delete this.creationDate;
+         let date = FILETIME_to_Date(this._ftCreationTime);
+         Object.defineProperty(this, "creationDate", { 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.
+        *
+        * @type {Date}
+        */
+       get lastAccessDate() {
+         delete this.lastAccess;
+         let date = FILETIME_to_Date(this._ftLastAccessTime);
+         Object.defineProperty(this, "lastAccessDate", { value: date });
+         return date;
+       },
+       /**
+        * Return the date of last modification of this file.
+        *
+        * Note that the definition of last access may depend on the
+        * underlying operating system and file system.
+        *
+        * @type {Date}
+        */
+       get lastModificationDate() {
+         delete this.lastModification;
+         let date = FILETIME_to_Date(this._ftLastWriteTime);
+         Object.defineProperty(this, "lastModificationDate", { value: date });
+         return date;
+       }
+     };
+
+     /**
+      * Fetch the information on a file.
+      *
+      * Performance note: if you have opened the file already,
+      * method |File.prototype.stat| is generally much faster
+      * than method |File.stat|.
+      *
+      * Platform-specific note: under Windows, if the file is
+      * already opened without sharing of the read capability,
+      * this function will fail.
+      *
+      * @return {File.Information}
+      */
+     File.stat = function stat(path) {
+       let file = File.open(path, FILE_STAT_MODE, FILE_STAT_OPTIONS);
+       try {
+         return file.stat();
+       } finally {
+         file.close();
+       }
+     };
+     // All of the following is required to ensure that File.stat
+     // also works on directories.
+     const FILE_STAT_MODE = {
+       read:true
+     };
+     const FILE_STAT_OPTIONS = {
+       // Directories can be opened neither for reading(!) nor for writing
+       winAccess: 0,
+       // Directories can only be opened with backup semantics(!)
+       winFlags: OS.Constants.Win.FILE_FLAG_BACKUP_SEMANTICS,
+       winDisposition: OS.Constants.Win.OPEN_EXISTING
+     };
+
+     /**
       * Get/set the current directory.
       */
      Object.defineProperty(File, "curDir", {
          set: function(path) {
            throw_on_zero("set curDir",
              WinFile.SetCurrentDirectory(path));
          },
          get: function() {
--- a/toolkit/components/osfile/tests/mochi/test_osfile_front.xul
+++ b/toolkit/components/osfile/tests/mochi/test_osfile_front.xul
@@ -22,25 +22,25 @@ function test() {
   SimpleTest.waitForExplicitFinish();
   ok(true, "test_osfile_front.xul: Chrome worker created");
   dump("MAIN: go\n");
   worker.onerror = function(error) {
     error.preventDefault();
     ok(false, "error "+error);
   }
   worker.onmessage = function(msg) {
-    ok(true, "MAIN: onmessage "+JSON.stringify(msg));
     switch (msg.data.kind) {
     case "is":
       return SimpleTest.is(msg.data.a, msg.data.b, msg.data.description);
     case "isnot":
       return SimpleTest.isnot(msg.data.a, msg.data.b, msg.data.description);
     case "ok":
       return SimpleTest.ok(msg.data.condition, msg.data.description);
     case "finish":
+      worker.terminate();
       SimpleTest.finish();
       return;
     default:
       SimpleTest.ok(false, "test_osfile_front.xul: wrong message "+JSON.stringify(msg.data));
       return;
     }
   };
   worker.postMessage(0);
--- a/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
+++ b/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
@@ -4,28 +4,29 @@
 function log(text) {
   dump("WORKER "+text+"\n");
 }
 
 function send(message) {
   self.postMessage(message);
 }
 
-self.onmessage = function(msg) {
-  self.onmessage = function(msg) {
-    log("ignored message "+JSON.stringify(msg.data));
+self.onmessage = function onmessage_start(msg) {
+  self.onmessage = function onmessage_ignored(msg) {
+    log("ignored message " + JSON.stringify(msg.data));
   };
   try {
     test_init();
     test_open_existing_file();
     test_open_non_existing_file();
     test_copy_existing_file();
     test_read_write_file();
     test_move_file();
     test_iter_dir();
+    test_info();
   } catch (x) {
     log("Catching error: " + x);
     log("Stack: " + x.stack);
     log("Source: " + x.toSource());
     ok(false, x.toString() + "\n" + x.stack);
   }
   finish();
 };
@@ -138,17 +139,17 @@ function test_read_write_file()
   let dest = OS.File.open(tmp_file_name, {write: true, trunc:true});
 
   let buf = new ArrayBuffer(4096);
   for (let bytesAvailable = source.read(buf, 4096);
          bytesAvailable != 0;
          bytesAvailable = source.read(buf, 4096)) {
     let bytesWritten = dest.write(buf, bytesAvailable);
     if (bytesWritten != bytesAvailable) {
-      eq(bytesWritten, bytesAvailable, "test_read_write_file: writing all bytes");
+      is(bytesWritten, bytesAvailable, "test_read_write_file: writing all bytes");
     }
   }
 
   ok(true, "test_read_write_file: copy complete");
   source.close();
   dest.close();
 
   compare_files("test_read_write_file", src_file_name, tmp_file_name);
@@ -215,17 +216,17 @@ function test_iter_dir()
   let encountered_tmp_file = false;
   for (let entry in iterator) {
     // Checking that |name| can be decoded properly
     ok(true, "test_iter_dir: encountering entry " + entry.name);
 
     if (entry.name == tmp_file_name) {
       encountered_tmp_file = true;
       isnot(entry.isDir, "test_iter_dir: The temporary file is not a directory");
-      isnot(entry.isLink, "test_iter_dir: The temporary file is not a link");
+      isnot(entry.isSymLink, "test_iter_dir: The temporary file is not a link");
     }
 
     let file;
     let success = true;
     try {
       file = OS.File.open(entry.path);
     } catch (x) {
       if (x.becauseNoSuchFile) {
@@ -254,8 +255,101 @@ function test_iter_dir()
 
   }
   ok(encountered_tmp_file, "test_iter_dir: We have found the temporary file");
 
   ok(true, "test_iter_dir: Cleaning up");
   iterator.close();
   ok(true, "test_iter_dir: Complete");
 }
+
+function test_info() {
+  ok(true, "test_info: Starting");
+
+  let filename = "test_info.tmp";
+  let size = 261;// An arbitrary file length
+  let start = new Date();
+
+ // Cleanup any leftover from previous tests
+  try {
+    OS.File.remove(filename);
+    ok(true, "test_info: Cleaned up previous garbage");
+  } catch (x) {
+    if (!x.becauseNoSuchFile) {
+      throw x;
+    }
+    ok(true, "test_info: No previous garbage");
+  }
+
+  let file = OS.File.open(filename, {trunc: true});
+  let buf = new ArrayBuffer(size);
+  file.write(buf, size);
+  file.close();
+
+  // Test OS.File.stat on new file
+  let info = OS.File.stat(filename);
+  ok(!!info, "test_info: info acquired");
+  ok(!info.isDir, "test_info: file is not a directory");
+  is(info.isSymLink, false, "test_info: file is not a link");
+  is(info.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 = info.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.
+
+  let change = info.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);
+  try {
+    info = file.stat();
+  } finally {
+    file.close();
+  }
+
+  ok(!!info, "test_info: info acquired 2");
+  ok(!info.isDir, "test_info: file is not a directory 2");
+  ok(!info.isSymLink, "test_info: file is not a link 2");
+  is(info.size.toString(), size, "test_info: correct size 2");
+
+  stop = new Date();
+
+  // We round down/up by 1s as file system precision is lower than Date precision
+  startMs = start.getTime() - 1000;
+  stopMs  = stop.getTime() + 1000;
+
+  birth = info.creationDate;
+  ok(birth.getTime() <= stopMs,
+      "test_info: file 2 was created between the start of the test and now - " + start +  ", " + stop + ", " + birth);
+
+  let access = info.lastModificationDate;
+  ok(access.getTime() >= startMs
+     && access.getTime() <= stopMs,
+     "test_info: file 2 was accessed between the start of the test and now - " + start + ", " + stop + ", " + access);
+
+  change = info.lastModificationDate;
+  ok(change.getTime() >= startMs
+     && change.getTime() <= stopMs,
+     "test_info: file 2 has changed between the start of the test and now - " + start + ", " + stop + ", " + change);
+
+  // Test OS.File.stat on directory
+  info = OS.File.stat(OS.File.curDir);
+  ok(!!info, "test_info: info on directory acquired");
+  ok(info.isDir, "test_info: directory is a directory");
+
+  ok(true, "test_info: Complete");
+}