Bug 726593 - Implement FileHandle. r=bent
authorJan Varga <jan.varga@gmail.com>
Sun, 03 Jun 2012 18:33:52 +0200
changeset 95643 48918f0df283d2732d80e57ccfce57a2a17fdcc1
parent 95642 e7ca047b71b26c8da62a063bb0b814f88c66a936
child 95678 07d362aa2c1b4cb14beb0d1fd2739bddbfc3881a
push idunknown
push userunknown
push dateunknown
reviewersbent
bugs726593
milestone15.0a1
Bug 726593 - Implement FileHandle. r=bent
b2g/installer/package-manifest.in
browser/installer/package-manifest.in
content/base/public/nsContentUtils.h
content/base/public/nsDOMFile.h
content/base/src/nsContentUtils.cpp
content/base/src/nsDOMBlobBuilder.cpp
content/base/src/nsDOMBlobBuilder.h
content/base/src/nsDOMFile.cpp
content/base/src/nsDOMFileReader.cpp
content/base/src/nsDOMFileReader.h
content/base/src/nsXMLHttpRequest.h
content/events/src/nsDOMEventTargetHelper.h
db/sqlite3/src/test_quota.c
db/sqlite3/src/test_quota.h
dom/Makefile.in
dom/base/DOMRequest.cpp
dom/base/DOMRequest.h
dom/base/StructuredCloneTags.h
dom/base/domerr.msg
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/base/nsDOMError.h
dom/base/nsDOMException.cpp
dom/base/nsDOMScriptObjectFactory.cpp
dom/base/nsDOMWindowUtils.cpp
dom/base/nsIDOMDOMRequest.idl
dom/dom-config.mk
dom/file/AsyncHelper.cpp
dom/file/AsyncHelper.h
dom/file/DOMFileHandle.cpp
dom/file/DOMFileHandle.h
dom/file/File.cpp
dom/file/File.h
dom/file/FileCommon.h
dom/file/FileHandle.cpp
dom/file/FileHandle.h
dom/file/FileHelper.cpp
dom/file/FileHelper.h
dom/file/FileRequest.cpp
dom/file/FileRequest.h
dom/file/FileService.cpp
dom/file/FileService.h
dom/file/FileStreamWrappers.cpp
dom/file/FileStreamWrappers.h
dom/file/LockedFile.cpp
dom/file/LockedFile.h
dom/file/Makefile.in
dom/file/MemoryStreams.cpp
dom/file/MemoryStreams.h
dom/file/MetadataHelper.cpp
dom/file/MetadataHelper.h
dom/file/nsIDOMFileHandle.idl
dom/file/nsIDOMFileRequest.idl
dom/file/nsIDOMLockedFile.idl
dom/file/nsIFileStorage.h
dom/file/test/Makefile.in
dom/file/test/helpers.js
dom/file/test/test_append_read_data.html
dom/file/test/test_getFileId.html
dom/file/test/test_lockedfile_lifetimes.html
dom/file/test/test_lockedfile_lifetimes_nested.html
dom/file/test/test_lockedfile_ordering.html
dom/file/test/test_overlapping_lockedfiles.html
dom/file/test/test_progress_events.html
dom/file/test/test_readonly_lockedfiles.html
dom/file/test/test_request_readyState.html
dom/file/test/test_stream_tracking.html
dom/file/test/test_success_events_after_abort.html
dom/file/test/test_truncate.html
dom/file/test/test_write_read_data.html
dom/indexedDB/FileStream.cpp
dom/indexedDB/FileStream.h
dom/indexedDB/IDBCursor.cpp
dom/indexedDB/IDBDatabase.cpp
dom/indexedDB/IDBDatabase.h
dom/indexedDB/IDBFileHandle.cpp
dom/indexedDB/IDBFileHandle.h
dom/indexedDB/IDBIndex.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/indexedDB/IDBObjectStore.h
dom/indexedDB/IDBTransaction.cpp
dom/indexedDB/IDBTransaction.h
dom/indexedDB/IndexedDatabase.h
dom/indexedDB/IndexedDatabaseInlines.h
dom/indexedDB/IndexedDatabaseManager.cpp
dom/indexedDB/IndexedDatabaseManager.h
dom/indexedDB/Makefile.in
dom/indexedDB/TransactionThreadPool.cpp
dom/indexedDB/TransactionThreadPool.h
dom/indexedDB/nsIIDBDatabase.idl
dom/indexedDB/nsIIDBFileHandle.idl
dom/indexedDB/nsIStandardFileStream.idl
dom/indexedDB/test/Makefile.in
dom/indexedDB/test/file.js
dom/indexedDB/test/helpers.js
dom/indexedDB/test/test_file_cross_database_copying.html
dom/indexedDB/test/test_file_quota.html
dom/indexedDB/test/test_filehandle_quota.html
dom/indexedDB/test/test_filehandle_serialization.html
dom/indexedDB/test/test_filehandle_store_snapshot.html
dom/interfaces/base/nsIDOMWindowUtils.idl
dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
js/xpconnect/src/dictionary_helper_gen.conf
js/xpconnect/src/dom_quickstubs.qsconf
layout/build/Makefile.in
mobile/android/installer/package-manifest.in
mobile/xul/installer/package-manifest.in
testing/mochitest/android.json
toolkit/toolkit-makefiles.sh
xpcom/base/nsError.h
xpcom/io/nsStreamUtils.cpp
xpcom/io/nsStreamUtils.h
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -161,16 +161,17 @@
 @BINPATH@/components/dom_bluetooth.xpt
 #endif
 @BINPATH@/components/dom_canvas.xpt
 @BINPATH@/components/dom_contacts.xpt
 @BINPATH@/components/dom_core.xpt
 @BINPATH@/components/dom_css.xpt
 @BINPATH@/components/dom_devicestorage.xpt
 @BINPATH@/components/dom_events.xpt
+@BINPATH@/components/dom_file.xpt
 @BINPATH@/components/dom_geolocation.xpt
 @BINPATH@/components/dom_network.xpt
 @BINPATH@/components/dom_notification.xpt
 @BINPATH@/components/dom_html.xpt
 @BINPATH@/components/dom_indexeddb.xpt
 @BINPATH@/components/dom_offline.xpt
 @BINPATH@/components/dom_json.xpt
 @BINPATH@/components/dom_power.xpt
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -166,16 +166,17 @@
 @BINPATH@/components/dom_bluetooth.xpt
 #endif
 @BINPATH@/components/dom_canvas.xpt
 @BINPATH@/components/dom_contacts.xpt
 @BINPATH@/components/dom_core.xpt
 @BINPATH@/components/dom_css.xpt
 @BINPATH@/components/dom_devicestorage.xpt
 @BINPATH@/components/dom_events.xpt
+@BINPATH@/components/dom_file.xpt
 @BINPATH@/components/dom_geolocation.xpt
 @BINPATH@/components/dom_network.xpt
 @BINPATH@/components/dom_notification.xpt
 @BINPATH@/components/dom_html.xpt
 @BINPATH@/components/dom_indexeddb.xpt
 @BINPATH@/components/dom_offline.xpt
 @BINPATH@/components/dom_json.xpt
 @BINPATH@/components/dom_power.xpt
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -545,18 +545,20 @@ public:
    * UTF-16BE
    *
    * @param aBuffer the buffer to check
    * @param aLength the length of the buffer
    * @param aCharset empty if not found
    * @return boolean indicating whether a BOM was detected.
    */
   static bool CheckForBOM(const unsigned char* aBuffer, PRUint32 aLength,
-                            nsACString& aCharset, bool *bigEndian = nsnull);
+                          nsACString& aCharset, bool *bigEndian = nsnull);
 
+  static nsresult GuessCharset(const char *aData, PRUint32 aDataLen,
+                               nsACString &aCharset);
 
   /**
    * Determine whether aContent is in some way associated with aForm.  If the
    * form is a container the only elements that are considered to be associated
    * with a form are the elements that are contained within the form. If the
    * form is a leaf element then all elements will be accepted into this list,
    * since this can happen due to content fixup when a form spans table rows or
    * table cells.
--- a/content/base/public/nsDOMFile.h
+++ b/content/base/public/nsDOMFile.h
@@ -38,16 +38,29 @@ nsresult NS_NewBlobBuilder(nsISupports* 
 
 class nsDOMFileBase : public nsIDOMFile,
                       public nsIXHRSendable,
                       public nsIMutable
 {
 public:
   typedef mozilla::dom::indexedDB::FileInfo FileInfo;
 
+  virtual already_AddRefed<nsIDOMBlob>
+  CreateSlice(PRUint64 aStart, PRUint64 aLength,
+              const nsAString& aContentType) = 0;
+
+  virtual const nsTArray<nsCOMPtr<nsIDOMBlob> >*
+  GetSubBlobs() const { return nsnull; }
+
+  NS_DECL_NSIDOMBLOB
+  NS_DECL_NSIDOMFILE
+  NS_DECL_NSIXHRSENDABLE
+  NS_DECL_NSIMUTABLE
+
+protected:
   nsDOMFileBase(const nsAString& aName, const nsAString& aContentType,
                 PRUint64 aLength)
     : mIsFile(true), mImmutable(false), mContentType(aContentType),
       mName(aName), mStart(0), mLength(aLength)
   {
     // Ensure non-null mContentType by default
     mContentType.SetIsVoid(false);
   }
@@ -55,131 +68,178 @@ public:
   nsDOMFileBase(const nsAString& aContentType, PRUint64 aLength)
     : mIsFile(false), mImmutable(false), mContentType(aContentType),
       mStart(0), mLength(aLength)
   {
     // Ensure non-null mContentType by default
     mContentType.SetIsVoid(false);
   }
 
-  nsDOMFileBase(const nsAString& aContentType,
-                PRUint64 aStart, PRUint64 aLength)
+  nsDOMFileBase(const nsAString& aContentType, PRUint64 aStart,
+                PRUint64 aLength)
     : mIsFile(false), mImmutable(false), mContentType(aContentType),
       mStart(aStart), mLength(aLength)
   {
     NS_ASSERTION(aLength != UINT64_MAX,
                  "Must know length when creating slice");
     // Ensure non-null mContentType by default
     mContentType.SetIsVoid(false);
   }
 
   virtual ~nsDOMFileBase() {}
 
-  virtual already_AddRefed<nsIDOMBlob>
-  CreateSlice(PRUint64 aStart, PRUint64 aLength,
-              const nsAString& aContentType) = 0;
-
-  virtual const nsTArray<nsCOMPtr<nsIDOMBlob> >*
-  GetSubBlobs() const { return nsnull; }
-
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIDOMBLOB
-  NS_DECL_NSIDOMFILE
-  NS_DECL_NSIXHRSENDABLE
-  NS_DECL_NSIMUTABLE
-
-protected:
-  bool IsSizeUnknown()
+  bool IsSizeUnknown() const
   {
     return mLength == UINT64_MAX;
   }
 
-  virtual bool IsStoredFile()
+  virtual bool IsStoredFile() const
   {
     return false;
   }
 
-  virtual bool IsWholeFile()
+  virtual bool IsWholeFile() const
   {
     NS_NOTREACHED("Should only be called on dom blobs backed by files!");
     return false;
   }
 
+  virtual bool IsSnapshot() const
+  {
+    return false;
+  }
+
+  FileInfo* GetFileInfo() const
+  {
+    NS_ASSERTION(IsStoredFile(), "Should only be called on stored files!");
+    NS_ASSERTION(!mFileInfos.IsEmpty(), "Must have at least one file info!");
+
+    return mFileInfos.ElementAt(0);
+  }
+
   bool mIsFile;
   bool mImmutable;
   nsString mContentType;
   nsString mName;
 
   PRUint64 mStart;
   PRUint64 mLength;
 
   // Protected by IndexedDatabaseManager::FileMutex()
   nsTArray<nsRefPtr<FileInfo> > mFileInfos;
 };
 
-class nsDOMFileFile : public nsDOMFileBase,
+class nsDOMFile : public nsDOMFileBase
+{
+public:
+  nsDOMFile(const nsAString& aName, const nsAString& aContentType,
+            PRUint64 aLength)
+  : nsDOMFileBase(aName, aContentType, aLength)
+  { }
+
+  nsDOMFile(const nsAString& aContentType, PRUint64 aLength)
+  : nsDOMFileBase(aContentType, aLength)
+  { }
+
+  nsDOMFile(const nsAString& aContentType, PRUint64 aStart, PRUint64 aLength)
+  : nsDOMFileBase(aContentType, aStart, aLength)
+  { }
+
+  NS_DECL_ISUPPORTS
+};
+
+class nsDOMFileCC : public nsDOMFileBase
+{
+public:
+  nsDOMFileCC(const nsAString& aName, const nsAString& aContentType,
+              PRUint64 aLength)
+  : nsDOMFileBase(aName, aContentType, aLength)
+  { }
+
+  nsDOMFileCC(const nsAString& aContentType, PRUint64 aLength)
+  : nsDOMFileBase(aContentType, aLength)
+  { }
+
+  nsDOMFileCC(const nsAString& aContentType, PRUint64 aStart, PRUint64 aLength)
+  : nsDOMFileBase(aContentType, aStart, aLength)
+  { }
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMFileCC, nsIDOMFile)
+};
+
+class nsDOMFileFile : public nsDOMFile,
                       public nsIJSNativeInitializer
 {
 public:
   // Create as a file
   nsDOMFileFile(nsIFile *aFile)
-    : nsDOMFileBase(EmptyString(), EmptyString(), UINT64_MAX),
+    : nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX),
       mFile(aFile), mWholeFile(true), mStoredFile(false)
   {
     NS_ASSERTION(mFile, "must have file");
     // Lazily get the content type and size
     mContentType.SetIsVoid(true);
     mFile->GetLeafName(mName);
   }
 
+  // Create as a file
+  nsDOMFileFile(const nsAString& aName, const nsAString& aContentType,
+                PRUint64 aLength, nsIFile *aFile)
+    : nsDOMFile(aName, aContentType, aLength),
+      mFile(aFile), mWholeFile(true), mStoredFile(false)
+  {
+    NS_ASSERTION(mFile, "must have file");
+  }
+
   // Create as a blob
   nsDOMFileFile(nsIFile *aFile, const nsAString& aContentType,
                 nsISupports *aCacheToken)
-    : nsDOMFileBase(aContentType, UINT64_MAX),
+    : nsDOMFile(aContentType, UINT64_MAX),
       mFile(aFile), mWholeFile(true), mStoredFile(false),
       mCacheToken(aCacheToken)
   {
     NS_ASSERTION(mFile, "must have file");
   }
 
   // Create as a file with custom name
   nsDOMFileFile(nsIFile *aFile, const nsAString& aName)
-    : nsDOMFileBase(EmptyString(), EmptyString(), UINT64_MAX),
+    : nsDOMFile(aName, EmptyString(), UINT64_MAX),
       mFile(aFile), mWholeFile(true), mStoredFile(false)
   {
     NS_ASSERTION(mFile, "must have file");
     // Lazily get the content type and size
     mContentType.SetIsVoid(true);
-    mName.Assign(aName);
   }
 
   // Create as a stored file
   nsDOMFileFile(const nsAString& aName, const nsAString& aContentType,
                 PRUint64 aLength, nsIFile* aFile,
                 FileInfo* aFileInfo)
-    : nsDOMFileBase(aName, aContentType, aLength),
+    : nsDOMFile(aName, aContentType, aLength),
       mFile(aFile), mWholeFile(true), mStoredFile(true)
   {
     NS_ASSERTION(mFile, "must have file");
     mFileInfos.AppendElement(aFileInfo);
   }
 
   // Create as a stored blob
   nsDOMFileFile(const nsAString& aContentType, PRUint64 aLength,
                 nsIFile* aFile, FileInfo* aFileInfo)
-    : nsDOMFileBase(aContentType, aLength),
+    : nsDOMFile(aContentType, aLength),
       mFile(aFile), mWholeFile(true), mStoredFile(true)
   {
     NS_ASSERTION(mFile, "must have file");
     mFileInfos.AppendElement(aFileInfo);
   }
 
   // Create as a file to be later initialized
   nsDOMFileFile()
-    : nsDOMFileBase(EmptyString(), EmptyString(), UINT64_MAX),
+    : nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX),
       mWholeFile(true), mStoredFile(false)
   {
     // Lazily get the content type and size
     mContentType.SetIsVoid(true);
     mName.SetIsVoid(true);
   }
 
   NS_DECL_ISUPPORTS_INHERITED
@@ -201,93 +261,91 @@ public:
   // DOMClassInfo constructor (for File("foo"))
   static nsresult
   NewFile(nsISupports* *aNewObject);
 
 protected:
   // Create slice
   nsDOMFileFile(const nsDOMFileFile* aOther, PRUint64 aStart, PRUint64 aLength,
                 const nsAString& aContentType)
-    : nsDOMFileBase(aContentType, aOther->mStart + aStart, aLength),
+    : nsDOMFile(aContentType, aOther->mStart + aStart, aLength),
       mFile(aOther->mFile), mWholeFile(false),
       mStoredFile(aOther->mStoredFile), mCacheToken(aOther->mCacheToken)
   {
     NS_ASSERTION(mFile, "must have file");
     mImmutable = aOther->mImmutable;
 
     if (mStoredFile) {
       FileInfo* fileInfo;
 
-      if (!mozilla::dom::indexedDB::IndexedDatabaseManager::IsClosed()) {
-        mozilla::dom::indexedDB::IndexedDatabaseManager::FileMutex().Lock();
-      }
+      using mozilla::dom::indexedDB::IndexedDatabaseManager;
 
-      NS_ASSERTION(!aOther->mFileInfos.IsEmpty(),
-                   "A stored file must have at least one file info!");
-
-      fileInfo = aOther->mFileInfos.ElementAt(0);
-
-      if (!mozilla::dom::indexedDB::IndexedDatabaseManager::IsClosed()) {
-        mozilla::dom::indexedDB::IndexedDatabaseManager::FileMutex().Unlock();
+      if (IndexedDatabaseManager::IsClosed()) {
+        fileInfo = aOther->GetFileInfo();
+      }
+      else {
+        mozilla::MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
+        fileInfo = aOther->GetFileInfo();
       }
 
       mFileInfos.AppendElement(fileInfo);
     }
   }
+
   virtual already_AddRefed<nsIDOMBlob>
   CreateSlice(PRUint64 aStart, PRUint64 aLength,
               const nsAString& aContentType);
 
-  virtual bool IsStoredFile()
+  virtual bool IsStoredFile() const
   {
     return mStoredFile;
   }
 
-  virtual bool IsWholeFile()
+  virtual bool IsWholeFile() const
   {
     return mWholeFile;
   }
 
   nsCOMPtr<nsIFile> mFile;
   bool mWholeFile;
   bool mStoredFile;
   nsCOMPtr<nsISupports> mCacheToken;
 };
 
-class nsDOMMemoryFile : public nsDOMFileBase
+class nsDOMMemoryFile : public nsDOMFile
 {
 public:
   // Create as file
   nsDOMMemoryFile(void *aMemoryBuffer,
                   PRUint64 aLength,
                   const nsAString& aName,
                   const nsAString& aContentType)
-    : nsDOMFileBase(aName, aContentType, aLength),
+    : nsDOMFile(aName, aContentType, aLength),
       mDataOwner(new DataOwner(aMemoryBuffer))
   {
     NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");
   }
 
   // Create as blob
   nsDOMMemoryFile(void *aMemoryBuffer,
                   PRUint64 aLength,
                   const nsAString& aContentType)
-    : nsDOMFileBase(aContentType, aLength),
+    : nsDOMFile(aContentType, aLength),
       mDataOwner(new DataOwner(aMemoryBuffer))
   {
     NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");
   }
 
   NS_IMETHOD GetInternalStream(nsIInputStream**);
 
 protected:
   // Create slice
   nsDOMMemoryFile(const nsDOMMemoryFile* aOther, PRUint64 aStart,
                   PRUint64 aLength, const nsAString& aContentType)
-    : nsDOMFileBase(aContentType, aOther->mStart + aStart, aLength),
+    : nsDOMFile(aContentType, aOther->mStart + aStart, aLength),
       mDataOwner(aOther->mDataOwner)
   {
     NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");
     mImmutable = aOther->mImmutable;
   }
   virtual already_AddRefed<nsIDOMBlob>
   CreateSlice(PRUint64 aStart, PRUint64 aLength,
               const nsAString& aContentType);
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -173,43 +173,32 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_
 #include "mozilla/Base64.h"
 #include "mozilla/Preferences.h"
 #include "nsDOMMutationObserver.h"
 
 #include "nsWrapperCacheInlines.h"
 #include "nsIDOMDocumentType.h"
 #include "nsCharSeparatedTokenizer.h"
 
+#include "nsICharsetDetector.h"
+#include "nsICharsetDetectionObserver.h"
+#include "nsIPlatformCharset.h"
+
 extern "C" int MOZ_XMLTranslateEntity(const char* ptr, const char* end,
                                       const char** next, PRUnichar* result);
 extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end,
                                  int ns_aware, const char** colon);
 
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace mozilla::widget;
 using namespace mozilla;
 
 const char kLoadAsData[] = "loadAsData";
 
-/**
- * Default values for the ViewportInfo structure.
- */
-static const float    kViewportMinScale = 0.0;
-static const float    kViewportMaxScale = 10.0;
-static const PRUint32 kViewportMinWidth = 200;
-static const PRUint32 kViewportMaxWidth = 10000;
-static const PRUint32 kViewportMinHeight = 223;
-static const PRUint32 kViewportMaxHeight = 10000;
-static const PRInt32  kViewportDefaultScreenWidth = 980;
-
-static const char kJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
-static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
-static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
-
 nsIDOMScriptObjectFactory *nsContentUtils::sDOMScriptObjectFactory = nsnull;
 nsIXPConnect *nsContentUtils::sXPConnect;
 nsIScriptSecurityManager *nsContentUtils::sSecurityManager;
 nsIThreadJSContextStack *nsContentUtils::sThreadJSContextStack;
 nsIParserService *nsContentUtils::sParserService = nsnull;
 nsINameSpaceManager *nsContentUtils::sNameSpaceManager;
 nsIIOService *nsContentUtils::sIOService;
 #ifdef MOZ_XTF
@@ -255,16 +244,33 @@ bool nsContentUtils::sTrustedFullScreenO
 
 PRUint32 nsContentUtils::sHandlingInputTimeout = 1000;
 
 nsHtml5StringParser* nsContentUtils::sHTMLFragmentParser = nsnull;
 nsIParser* nsContentUtils::sXMLFragmentParser = nsnull;
 nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nsnull;
 bool nsContentUtils::sFragmentParsingActive = false;
 
+namespace {
+
+/**
+ * Default values for the ViewportInfo structure.
+ */
+static const float    kViewportMinScale = 0.0;
+static const float    kViewportMaxScale = 10.0;
+static const PRUint32 kViewportMinWidth = 200;
+static const PRUint32 kViewportMaxWidth = 10000;
+static const PRUint32 kViewportMinHeight = 223;
+static const PRUint32 kViewportMaxHeight = 10000;
+static const PRInt32  kViewportDefaultScreenWidth = 980;
+
+static const char kJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
+static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
+static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
+
 static PLDHashTable sEventListenerManagersHash;
 
 class EventListenerManagerMapEntry : public PLDHashEntryHdr
 {
 public:
   EventListenerManagerMapEntry(const void *aKey)
     : mKey(aKey)
   {
@@ -296,24 +302,46 @@ EventListenerManagerHashClearEntry(PLDHa
 {
   EventListenerManagerMapEntry *lm =
     static_cast<EventListenerManagerMapEntry *>(entry);
 
   // Let the EventListenerManagerMapEntry clean itself up...
   lm->~EventListenerManagerMapEntry();
 }
 
-class nsSameOriginChecker : public nsIChannelEventSink,
-                            public nsIInterfaceRequestor
+class SameOriginChecker : public nsIChannelEventSink,
+                          public nsIInterfaceRequestor
 {
   NS_DECL_ISUPPORTS
   NS_DECL_NSICHANNELEVENTSINK
   NS_DECL_NSIINTERFACEREQUESTOR
 };
 
+class CharsetDetectionObserver : public nsICharsetDetectionObserver
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  NS_IMETHOD Notify(const char *aCharset, nsDetectionConfident aConf)
+  {
+    mCharset = aCharset;
+    return NS_OK;
+  }
+
+  const nsACString& GetResult() const
+  {
+    return mCharset;
+  }
+
+private:
+  nsCString mCharset;
+};
+
+} // anonymous namespace
+
 /* static */
 TimeDuration
 nsContentUtils::HandlingUserInputTimeout()
 {
   return TimeDuration::FromMilliseconds(sHandlingInputTimeout);
 }
 
 // static
@@ -3531,16 +3559,99 @@ nsContentUtils::CheckForBOM(const unsign
       *bigEndian = false;
   } else {
     found = false;
   }
 
   return found;
 }
 
+NS_IMPL_ISUPPORTS1(CharsetDetectionObserver,
+                   nsICharsetDetectionObserver)
+
+/* static */
+nsresult
+nsContentUtils::GuessCharset(const char *aData, PRUint32 aDataLen,
+                             nsACString &aCharset)
+{
+  // First try the universal charset detector
+  nsCOMPtr<nsICharsetDetector> detector =
+    do_CreateInstance(NS_CHARSET_DETECTOR_CONTRACTID_BASE
+                      "universal_charset_detector");
+  if (!detector) {
+    // No universal charset detector, try the default charset detector
+    const nsAdoptingCString& detectorName =
+      Preferences::GetLocalizedCString("intl.charset.detector");
+    if (!detectorName.IsEmpty()) {
+      nsCAutoString detectorContractID;
+      detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE);
+      detectorContractID += detectorName;
+      detector = do_CreateInstance(detectorContractID.get());
+    }
+  }
+
+  nsresult rv;
+
+  // The charset detector doesn't work for empty (null) aData. Testing
+  // aDataLen instead of aData so that we catch potential errors.
+  if (detector && aDataLen) {
+    nsRefPtr<CharsetDetectionObserver> observer =
+      new CharsetDetectionObserver();
+
+    rv = detector->Init(observer);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    bool dummy;
+    rv = detector->DoIt(aData, aDataLen, &dummy);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = detector->Done();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    aCharset = observer->GetResult();
+  } else {
+    // no charset detector available, check the BOM
+    unsigned char sniffBuf[3];
+    PRUint32 numRead =
+      (aDataLen >= sizeof(sniffBuf) ? sizeof(sniffBuf) : aDataLen);
+    memcpy(sniffBuf, aData, numRead);
+
+    bool bigEndian;
+    if (CheckForBOM(sniffBuf, numRead, aCharset, &bigEndian) &&
+        aCharset.EqualsLiteral("UTF-16")) {
+      if (bigEndian) {
+        aCharset.AppendLiteral("BE");
+      }
+      else {
+        aCharset.AppendLiteral("LE");
+      }
+    }
+  }
+
+  if (aCharset.IsEmpty()) {
+    // no charset detected, default to the system charset
+    nsCOMPtr<nsIPlatformCharset> platformCharset =
+      do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
+    if (NS_SUCCEEDED(rv)) {
+      rv = platformCharset->GetCharset(kPlatformCharsetSel_PlainTextInFile,
+                                       aCharset);
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Failed to get the system charset!");
+      }
+    }
+  }
+
+  if (aCharset.IsEmpty()) {
+    // no sniffed or default charset, assume UTF-8
+    aCharset.AssignLiteral("UTF-8");
+  }
+
+  return NS_OK;
+}
+
 /* static */
 void
 nsContentUtils::RegisterShutdownObserver(nsIObserver* aObserver)
 {
   nsCOMPtr<nsIObserverService> observerService =
     mozilla::services::GetObserverService();
   if (observerService) {
     observerService->AddObserver(aObserver, 
@@ -5512,17 +5623,17 @@ nsContentUtils::EqualsLiteralIgnoreASCII
   return true;
 }
 
 /* static */
 nsIInterfaceRequestor*
 nsContentUtils::GetSameOriginChecker()
 {
   if (!sSameOriginChecker) {
-    sSameOriginChecker = new nsSameOriginChecker();
+    sSameOriginChecker = new SameOriginChecker();
     NS_IF_ADDREF(sSameOriginChecker);
   }
   return sSameOriginChecker;
 }
 
 /* static */
 nsresult
 nsContentUtils::CheckSameOrigin(nsIChannel *aOldChannel, nsIChannel *aNewChannel)
@@ -5544,38 +5655,38 @@ nsContentUtils::CheckSameOrigin(nsIChann
   nsresult rv = oldPrincipal->CheckMayLoad(newURI, false);
   if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
     rv = oldPrincipal->CheckMayLoad(newOriginalURI, false);
   }
 
   return rv;
 }
 
-NS_IMPL_ISUPPORTS2(nsSameOriginChecker,
+NS_IMPL_ISUPPORTS2(SameOriginChecker,
                    nsIChannelEventSink,
                    nsIInterfaceRequestor)
 
 NS_IMETHODIMP
-nsSameOriginChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
-                                            nsIChannel *aNewChannel,
-                                            PRUint32 aFlags,
-                                            nsIAsyncVerifyRedirectCallback *cb)
+SameOriginChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
+                                          nsIChannel *aNewChannel,
+                                          PRUint32 aFlags,
+                                          nsIAsyncVerifyRedirectCallback *cb)
 {
   NS_PRECONDITION(aNewChannel, "Redirecting to null channel?");
 
   nsresult rv = nsContentUtils::CheckSameOrigin(aOldChannel, aNewChannel);
   if (NS_SUCCEEDED(rv)) {
     cb->OnRedirectVerifyCallback(NS_OK);
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
-nsSameOriginChecker::GetInterface(const nsIID & aIID, void **aResult)
+SameOriginChecker::GetInterface(const nsIID & aIID, void **aResult)
 {
   return QueryInterface(aIID, aResult);
 }
 
 /* static */
 nsresult
 nsContentUtils::GetASCIIOrigin(nsIPrincipal* aPrincipal, nsCString& aOrigin)
 {
--- a/content/base/src/nsDOMBlobBuilder.cpp
+++ b/content/base/src/nsDOMBlobBuilder.cpp
@@ -12,17 +12,17 @@
 #include "nsTArray.h"
 #include "nsJSUtils.h"
 #include "nsContentUtils.h"
 #include "DictionaryHelpers.h"
 #include "nsIScriptError.h"
 
 using namespace mozilla;
 
-NS_IMPL_ISUPPORTS_INHERITED1(nsDOMMultipartFile, nsDOMFileBase,
+NS_IMPL_ISUPPORTS_INHERITED1(nsDOMMultipartFile, nsDOMFile,
                              nsIJSNativeInitializer)
 
 NS_IMETHODIMP
 nsDOMMultipartFile::GetSize(PRUint64* aLength)
 {
   if (mLength == UINT64_MAX) {
     CheckedUint64 length = 0;
   
@@ -135,16 +135,25 @@ nsDOMMultipartFile::CreateSlice(PRUint64
   }
 
   // we can create our blob now
   nsCOMPtr<nsIDOMBlob> blob = new nsDOMMultipartFile(blobs, aContentType);
   return blob.forget();
 }
 
 /* static */ nsresult
+nsDOMMultipartFile::NewFile(const nsAString& aName, nsISupports* *aNewObject)
+{
+  nsCOMPtr<nsISupports> file =
+    do_QueryObject(new nsDOMMultipartFile(aName));
+  file.forget(aNewObject);
+  return NS_OK;
+}
+
+/* static */ nsresult
 nsDOMMultipartFile::NewBlob(nsISupports* *aNewObject)
 {
   nsCOMPtr<nsISupports> file = do_QueryObject(new nsDOMMultipartFile());
   file.forget(aNewObject);
   return NS_OK;
 }
 
 static nsIDOMBlob*
--- a/content/base/src/nsDOMBlobBuilder.h
+++ b/content/base/src/nsDOMBlobBuilder.h
@@ -7,40 +7,46 @@
 #define nsDOMBlobBuilder_h
 
 #include "nsDOMFile.h"
 
 #include "mozilla/CheckedInt.h"
 
 using namespace mozilla;
 
-class nsDOMMultipartFile : public nsDOMFileBase,
+class nsDOMMultipartFile : public nsDOMFile,
                            public nsIJSNativeInitializer
 {
 public:
   // Create as a file
   nsDOMMultipartFile(nsTArray<nsCOMPtr<nsIDOMBlob> > aBlobs,
                      const nsAString& aName,
                      const nsAString& aContentType)
-    : nsDOMFileBase(aName, aContentType, UINT64_MAX),
+    : nsDOMFile(aName, aContentType, UINT64_MAX),
       mBlobs(aBlobs)
   {
   }
 
   // Create as a blob
   nsDOMMultipartFile(nsTArray<nsCOMPtr<nsIDOMBlob> > aBlobs,
                      const nsAString& aContentType)
-    : nsDOMFileBase(aContentType, UINT64_MAX),
+    : nsDOMFile(aContentType, UINT64_MAX),
       mBlobs(aBlobs)
   {
   }
 
+  // Create as a file to be later initialized
+  nsDOMMultipartFile(const nsAString& aName)
+    : nsDOMFile(aName, EmptyString(), UINT64_MAX)
+  {
+  }
+
   // Create as a blob to be later initialized
   nsDOMMultipartFile()
-    : nsDOMFileBase(EmptyString(), UINT64_MAX)
+    : nsDOMFile(EmptyString(), UINT64_MAX)
   {
   }
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIJSNativeInitializer
   NS_IMETHOD Initialize(nsISupports* aOwner,
                         JSContext* aCx,
@@ -55,16 +61,19 @@ public:
                         UnwrapFuncPtr aUnwrapFunc);
 
   already_AddRefed<nsIDOMBlob>
   CreateSlice(PRUint64 aStart, PRUint64 aLength, const nsAString& aContentType);
 
   NS_IMETHOD GetSize(PRUint64*);
   NS_IMETHOD GetInternalStream(nsIInputStream**);
 
+  static nsresult
+  NewFile(const nsAString& aName, nsISupports* *aNewObject);
+
   // DOMClassInfo constructor (for Blob([b1, "foo"], { type: "image/png" }))
   static nsresult
   NewBlob(nsISupports* *aNewObject);
 
   virtual const nsTArray<nsCOMPtr<nsIDOMBlob> >*
   GetSubBlobs() const { return &mBlobs; }
 
 protected:
--- a/content/base/src/nsDOMFile.cpp
+++ b/content/base/src/nsDOMFile.cpp
@@ -98,33 +98,16 @@ nsresult DataOwnerAdapter::Create(DataOw
   NS_ADDREF(*_retval = new DataOwnerAdapter(aDataOwner, stream));
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////
 // nsDOMFileBase implementation
 
-DOMCI_DATA(File, nsDOMFileBase)
-DOMCI_DATA(Blob, nsDOMFileBase)
-
-NS_INTERFACE_MAP_BEGIN(nsDOMFileBase)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMBlob)
-  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile)
-  NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
-  NS_INTERFACE_MAP_ENTRY(nsIMutable)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, mIsFile)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !mIsFile)
-NS_INTERFACE_MAP_END
-
-// Threadsafe when GetMutable() == false
-NS_IMPL_THREADSAFE_ADDREF(nsDOMFileBase)
-NS_IMPL_THREADSAFE_RELEASE(nsDOMFileBase)
-
 NS_IMETHODIMP
 nsDOMFileBase::GetName(nsAString &aFileName)
 {
   NS_ASSERTION(mIsFile, "Should only be called on files");
   aFileName = mName;
   return NS_OK;
 }
 
@@ -297,17 +280,17 @@ nsDOMFileBase::GetInternalUrl(nsIPrincip
   return NS_OK;
 }
 
 NS_IMETHODIMP_(PRInt64)
 nsDOMFileBase::GetFileId()
 {
   PRInt64 id = -1;
 
-  if (IsStoredFile() && IsWholeFile()) {
+  if (IsStoredFile() && IsWholeFile() && !IsSnapshot()) {
     if (!indexedDB::IndexedDatabaseManager::IsClosed()) {
       indexedDB::IndexedDatabaseManager::FileMutex().Lock();
     }
 
     NS_ASSERTION(!mFileInfos.IsEmpty(),
                  "A stored file must have at least one file info!");
 
     nsRefPtr<indexedDB::FileInfo>& fileInfo = mFileInfos.ElementAt(0);
@@ -348,17 +331,24 @@ nsDOMFileBase::GetFileInfo(indexedDB::Fi
   if (indexedDB::IndexedDatabaseManager::IsClosed()) {
     NS_ERROR("Shouldn't be called after shutdown!");
     return nsnull;
   }
 
   // A slice created from a stored file must keep the file info alive.
   // However, we don't support sharing of slices yet, so the slice must be
   // copied again. That's why we have to ignore the first file info.
-  PRUint32 startIndex = IsStoredFile() && !IsWholeFile() ? 1 : 0;
+  // Snapshots are handled in a similar way (they have to be copied).
+  PRUint32 startIndex;
+  if (IsStoredFile() && (!IsWholeFile() || IsSnapshot())) {
+    startIndex = 1;
+  }
+  else {
+    startIndex = 0;
+  }
 
   MutexAutoLock lock(indexedDB::IndexedDatabaseManager::FileMutex());
 
   for (PRUint32 i = startIndex; i < mFileInfos.Length(); i++) {
     nsRefPtr<indexedDB::FileInfo>& fileInfo = mFileInfos.ElementAt(i);
     if (fileInfo->Manager() == aFileManager) {
       return fileInfo;
     }
@@ -415,19 +405,57 @@ nsDOMFileBase::SetMutable(bool aMutable)
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   mImmutable = !aMutable;
   return rv;
 }
 
 ////////////////////////////////////////////////////////////////////////////
+// nsDOMFile implementation
+
+DOMCI_DATA(File, nsDOMFile)
+DOMCI_DATA(Blob, nsDOMFile)
+
+NS_INTERFACE_MAP_BEGIN(nsDOMFile)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMBlob)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile)
+  NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
+  NS_INTERFACE_MAP_ENTRY(nsIMutable)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, mIsFile)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !mIsFile)
+NS_INTERFACE_MAP_END
+
+// Threadsafe when GetMutable() == false
+NS_IMPL_THREADSAFE_ADDREF(nsDOMFile)
+NS_IMPL_THREADSAFE_RELEASE(nsDOMFile)
+
+////////////////////////////////////////////////////////////////////////////
+// nsDOMFileCC implementation
+
+NS_IMPL_CYCLE_COLLECTION_0(nsDOMFileCC)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMFileCC)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMBlob)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile)
+  NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
+  NS_INTERFACE_MAP_ENTRY(nsIMutable)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, mIsFile)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !mIsFile)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMFileCC)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMFileCC)
+
+////////////////////////////////////////////////////////////////////////////
 // nsDOMFileFile implementation
 
-NS_IMPL_ISUPPORTS_INHERITED1(nsDOMFileFile, nsDOMFileBase,
+NS_IMPL_ISUPPORTS_INHERITED1(nsDOMFileFile, nsDOMFile,
                              nsIJSNativeInitializer)
 
 already_AddRefed<nsIDOMBlob>
 nsDOMFileFile::CreateSlice(PRUint64 aStart, PRUint64 aLength,
                            const nsAString& aContentType)
 {
   nsCOMPtr<nsIDOMBlob> t = new nsDOMFileFile(this, aStart, aLength, aContentType);
   return t.forget();
--- a/content/base/src/nsDOMFileReader.cpp
+++ b/content/base/src/nsDOMFileReader.cpp
@@ -6,24 +6,22 @@
 #include "nsDOMFileReader.h"
 
 #include "nsContentCID.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
 #include "nsDOMFile.h"
 #include "nsDOMError.h"
 #include "nsCharsetAlias.h"
-#include "nsICharsetDetector.h"
 #include "nsICharsetConverterManager.h"
 #include "nsIConverterInputStream.h"
 #include "nsIFile.h"
 #include "nsIFileStreams.h"
 #include "nsIInputStream.h"
 #include "nsIMIMEService.h"
-#include "nsIPlatformCharset.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 
 #include "plbase64.h"
 #include "prmem.h"
 
 #include "nsLayoutCID.h"
@@ -92,40 +90,30 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 DOMCI_DATA(FileReader, nsDOMFileReader)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMFileReader)
   NS_INTERFACE_MAP_ENTRY(nsIDOMFileReader)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
-  NS_INTERFACE_MAP_ENTRY(nsICharsetDetectionObserver)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(FileReader)
 NS_INTERFACE_MAP_END_INHERITING(FileIOObject)
 
 NS_IMPL_ADDREF_INHERITED(nsDOMFileReader, FileIOObject)
 NS_IMPL_RELEASE_INHERITED(nsDOMFileReader, FileIOObject)
 
 void
 nsDOMFileReader::RootResultArrayBuffer()
 {
   nsContentUtils::PreserveWrapper(
     static_cast<nsIDOMEventTarget*>(
       static_cast<nsDOMEventTargetHelper*>(this)), this);
 }
 
-//nsICharsetDetectionObserver
-
-NS_IMETHODIMP
-nsDOMFileReader::Notify(const char *aCharset, nsDetectionConfident aConf)
-{
-  mCharset = aCharset;
-  return NS_OK;
-}
-
 //nsDOMFileReader constructors/initializers
 
 nsDOMFileReader::nsDOMFileReader()
   : mFileData(nsnull),
     mDataLen(0), mDataFormat(FILE_AS_BINARY),
     mResultArrayBuffer(nsnull)     
 {
   nsLayoutStatics::AddRef();
@@ -452,17 +440,17 @@ nsDOMFileReader::GetAsText(const nsACStr
                            PRUint32 aDataLen,
                            nsAString& aResult)
 {
   nsresult rv;
   nsCAutoString charsetGuess;
   if (!aCharset.IsEmpty()) {
     charsetGuess = aCharset;
   } else {
-    rv = GuessCharset(aFileData, aDataLen, charsetGuess);
+    rv = nsContentUtils::GuessCharset(aFileData, aDataLen, charsetGuess);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsCAutoString charset;
   rv = nsCharsetAlias::GetPreferred(charsetGuess, charset);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = ConvertStream(aFileData, aDataLen, charset.get(), aResult);
@@ -535,85 +523,8 @@ nsDOMFileReader::ConvertStream(const cha
     return NS_ERROR_OUT_OF_MEMORY;
 
   PRInt32 srcLength = aDataLen;
   rv = unicodeDecoder->Convert(aFileData, &srcLength, aResult.BeginWriting(), &destLength);
   aResult.SetLength(destLength); //Trim down to the correct size
 
   return rv;
 }
-
-nsresult
-nsDOMFileReader::GuessCharset(const char *aFileData,
-                              PRUint32 aDataLen,
-                              nsACString &aCharset)
-{
-  // First try the universal charset detector
-  nsCOMPtr<nsICharsetDetector> detector
-    = do_CreateInstance(NS_CHARSET_DETECTOR_CONTRACTID_BASE
-                        "universal_charset_detector");
-  if (!detector) {
-    // No universal charset detector, try the default charset detector
-    const nsAdoptingCString& detectorName =
-      Preferences::GetLocalizedCString("intl.charset.detector");
-    if (!detectorName.IsEmpty()) {
-      nsCAutoString detectorContractID;
-      detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE);
-      detectorContractID += detectorName;
-      detector = do_CreateInstance(detectorContractID.get());
-    }
-  }
-
-  nsresult rv;
-  // The charset detector doesn't work for empty (null) aFileData. Testing
-  // aDataLen instead of aFileData so that we catch potential errors.
-  if (detector && aDataLen != 0) {
-    mCharset.Truncate();
-    detector->Init(this);
-
-    bool done;
-
-    rv = detector->DoIt(aFileData, aDataLen, &done);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = detector->Done();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    aCharset = mCharset;
-  } else {
-    // no charset detector available, check the BOM
-    unsigned char sniffBuf[3];
-    PRUint32 numRead = (aDataLen >= sizeof(sniffBuf) ? sizeof(sniffBuf) : aDataLen);
-    memcpy(sniffBuf, aFileData, numRead);
-
-    if (numRead >= 2 &&
-               sniffBuf[0] == 0xfe &&
-               sniffBuf[1] == 0xff) {
-      aCharset = "UTF-16BE";
-    } else if (numRead >= 2 &&
-               sniffBuf[0] == 0xff &&
-               sniffBuf[1] == 0xfe) {
-      aCharset = "UTF-16LE";
-    } else if (numRead >= 3 &&
-               sniffBuf[0] == 0xef &&
-               sniffBuf[1] == 0xbb &&
-               sniffBuf[2] == 0xbf) {
-      aCharset = "UTF-8";
-    }
-  }
-
-  if (aCharset.IsEmpty()) {
-    // no charset detected, default to the system charset
-    nsCOMPtr<nsIPlatformCharset> platformCharset =
-      do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
-    if (NS_SUCCEEDED(rv)) {
-      rv = platformCharset->GetCharset(kPlatformCharsetSel_PlainTextInFile,
-                                       aCharset);
-    }
-  }
-
-  if (aCharset.IsEmpty()) {
-    // no sniffed or default charset, try UTF-8
-    aCharset.AssignLiteral("UTF-8");
-  }
-
-  return NS_OK;
-}
--- a/content/base/src/nsDOMFileReader.h
+++ b/content/base/src/nsDOMFileReader.h
@@ -11,36 +11,33 @@
 #include "nsWeakReference.h"
 #include "nsIStreamListener.h"     
 #include "nsIInterfaceRequestor.h" 
 #include "nsJSUtils.h"             
 #include "nsTArray.h"              
 #include "nsIJSNativeInitializer.h"
 #include "prtime.h"                
 #include "nsITimer.h"              
-#include "nsICharsetDetector.h"
-#include "nsICharsetDetectionObserver.h"
 
 #include "nsIDOMFile.h"
 #include "nsIDOMFileReader.h"
 #include "nsIDOMFileList.h"
 #include "nsIInputStream.h"
 #include "nsCOMPtr.h"
 #include "nsIStreamLoader.h"
 #include "nsIChannel.h"
 #include "prmem.h"
 
 #include "FileIOObject.h"
 
 class nsDOMFileReader : public mozilla::dom::FileIOObject,
                         public nsIDOMFileReader,
                         public nsIInterfaceRequestor,
                         public nsSupportsWeakReference,
-                        public nsIJSNativeInitializer,
-                        public nsICharsetDetectionObserver
+                        public nsIJSNativeInitializer
 {
 public:
   nsDOMFileReader();
   virtual ~nsDOMFileReader();
 
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_DECL_NSIDOMFILEREADER
@@ -49,23 +46,20 @@ public:
 
   // nsIInterfaceRequestor 
   NS_DECL_NSIINTERFACEREQUESTOR
 
   NS_DECL_EVENT_HANDLER(load)
   NS_DECL_EVENT_HANDLER(loadend)
   NS_DECL_EVENT_HANDLER(loadstart)
 
-  // nsIJSNativeInitializer                                                
-  NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj, 
+  // nsIJSNativeInitializer
+  NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj,
                         PRUint32 argc, jsval* argv);
 
-  // nsICharsetDetectionObserver
-  NS_IMETHOD Notify(const char *aCharset, nsDetectionConfident aConf);
-
   // FileIOObject overrides
   NS_IMETHOD DoAbort(nsAString& aEvent);
   NS_IMETHOD DoOnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
                              nsresult aStatus, nsAString& aSuccessEvent,
                              nsAString& aTerminationEvent);
   NS_IMETHOD DoOnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
                                nsIInputStream* aInputStream, PRUint32 aOffset,
                                PRUint32 aCount);
@@ -83,17 +77,16 @@ protected:
     FILE_AS_TEXT,
     FILE_AS_DATAURL
   };
 
   nsresult ReadFileContent(JSContext* aCx, nsIDOMBlob *aFile, const nsAString &aCharset, eDataFormat aDataFormat); 
   nsresult GetAsText(const nsACString &aCharset,
                      const char *aFileData, PRUint32 aDataLen, nsAString &aResult);
   nsresult GetAsDataURL(nsIDOMBlob *aFile, const char *aFileData, PRUint32 aDataLen, nsAString &aResult); 
-  nsresult GuessCharset(const char *aFileData, PRUint32 aDataLen, nsACString &aCharset); 
   nsresult ConvertStream(const char *aFileData, PRUint32 aDataLen, const char *aCharset, nsAString &aResult); 
 
   void FreeFileData() {
     PR_Free(mFileData);
     mFileData = nsnull;
     mDataLen = 0;
   }
 
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -611,17 +611,17 @@ protected:
   ResponseType mResponseType;
 
   // It is either a cached blob-response from the last call to GetResponse,
   // but is also explicitly set in OnStopRequest.
   nsCOMPtr<nsIDOMBlob> mResponseBlob;
   // Non-null only when we are able to get a os-file representation of the
   // response, i.e. when loading from a file, or when the http-stream
   // caches into a file or is reading from a cached file.
-  nsRefPtr<nsDOMFileBase> mDOMFile;
+  nsRefPtr<nsDOMFile> mDOMFile;
   // We stream data to mBuilder when response type is "blob" or "moz-blob"
   // and mDOMFile is null.
   nsRefPtr<nsDOMBlobBuilder> mBuilder;
 
   nsString mOverrideMimeType;
 
   /**
    * The notification callbacks the channel had when Send() was
--- a/content/events/src/nsDOMEventTargetHelper.h
+++ b/content/events/src/nsDOMEventTargetHelper.h
@@ -188,9 +188,55 @@ private:
 #define NS_DISCONNECT_EVENT_HANDLER(_event)                                   \
   if (mOn##_event##Listener) { mOn##_event##Listener->Disconnect(); }
 
 #define NS_UNMARK_LISTENER_WRAPPER(_event)                                    \
   if (tmp->mOn##_event##Listener) {                                           \
     xpc_TryUnmarkWrappedGrayObject(tmp->mOn##_event##Listener->GetInner());   \
   }
 
+/* Use this macro to declare functions that forward the behavior of this
+ * interface to another object.
+ * This macro doesn't forward PreHandleEvent because sometimes subclasses
+ * want to override it.
+ */
+#define NS_FORWARD_NSIDOMEVENTTARGET_NOPREHANDLEEVENT(_to) \
+  NS_SCRIPTABLE NS_IMETHOD AddEventListener(const nsAString & type, nsIDOMEventListener *listener, bool useCapture, bool wantsUntrusted, PRUint8 _argc) { \
+    return _to AddEventListener(type, listener, useCapture, wantsUntrusted, _argc); \
+  } \
+  NS_IMETHOD AddSystemEventListener(const nsAString & type, nsIDOMEventListener *listener, bool aUseCapture, bool aWantsUntrusted, PRUint8 _argc) { \
+    return _to AddSystemEventListener(type, listener, aUseCapture, aWantsUntrusted, _argc); \
+  } \
+  NS_SCRIPTABLE NS_IMETHOD RemoveEventListener(const nsAString & type, nsIDOMEventListener *listener, bool useCapture) { \
+    return _to RemoveEventListener(type, listener, useCapture); \
+  } \
+  NS_IMETHOD RemoveSystemEventListener(const nsAString & type, nsIDOMEventListener *listener, bool aUseCapture) { \
+    return _to RemoveSystemEventListener(type, listener, aUseCapture); \
+  } \
+  NS_SCRIPTABLE NS_IMETHOD DispatchEvent(nsIDOMEvent *evt, bool *_retval NS_OUTPARAM) { \
+    return _to DispatchEvent(evt, _retval); \
+  } \
+  virtual nsIDOMEventTarget * GetTargetForDOMEvent(void) { \
+    return _to GetTargetForDOMEvent(); \
+  } \
+  virtual nsIDOMEventTarget * GetTargetForEventTargetChain(void) { \
+    return _to GetTargetForEventTargetChain(); \
+  } \
+  virtual nsresult WillHandleEvent(nsEventChainPostVisitor & aVisitor) { \
+    return _to WillHandleEvent(aVisitor); \
+  } \
+  virtual nsresult PostHandleEvent(nsEventChainPostVisitor & aVisitor) { \
+    return _to PostHandleEvent(aVisitor); \
+  } \
+  virtual nsresult DispatchDOMEvent(nsEvent *aEvent, nsIDOMEvent *aDOMEvent, nsPresContext *aPresContext, nsEventStatus *aEventStatus) { \
+    return _to DispatchDOMEvent(aEvent, aDOMEvent, aPresContext, aEventStatus); \
+  } \
+  virtual nsEventListenerManager * GetListenerManager(bool aMayCreate) { \
+    return _to GetListenerManager(aMayCreate); \
+  } \
+  virtual nsIScriptContext * GetContextForEventHandlers(nsresult *aRv NS_OUTPARAM) { \
+    return _to GetContextForEventHandlers(aRv); \
+  } \
+  virtual JSContext * GetJSContextForEventHandlers(void) { \
+    return _to GetJSContextForEventHandlers(); \
+  } 
+
 #endif // nsDOMEventTargetHelper_h_
--- a/db/sqlite3/src/test_quota.c
+++ b/db/sqlite3/src/test_quota.c
@@ -1037,27 +1037,27 @@ size_t sqlite3_quota_fread(
   return fread(pBuf, size, nmemb, p->f);
 }
 
 /*
 ** Write content into a quota_FILE.  Invoke the quota callback and block
 ** the write if we exceed quota.
 */
 size_t sqlite3_quota_fwrite(
-  void *pBuf,            /* Take content to write from here */
+  const void *pBuf,      /* Take content to write from here */
   size_t size,           /* Size of each element */
   size_t nmemb,          /* Number of elements */
   quota_FILE *p          /* Write to this quota_FILE objecct */
 ){
   sqlite3_int64 iOfst;
   sqlite3_int64 iEnd;
   sqlite3_int64 szNew;
   quotaFile *pFile;
   size_t rc;
-  
+
   iOfst = ftell(p->f);
   iEnd = iOfst + size*nmemb;
   pFile = p->pFile;
   if( pFile && pFile->iSize<iEnd ){
     quotaGroup *pGroup = pFile->pGroup;
     quotaEnter();
     szNew = pGroup->iSize - pFile->iSize + iEnd;
     if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
@@ -1086,17 +1086,17 @@ size_t sqlite3_quota_fwrite(
     size_t nWritten = rc>=0 ? rc : 0;
     sqlite3_int64 iNewEnd = iOfst + size*nWritten;
     if( iNewEnd<iEnd ) iNewEnd = iEnd;
     quotaEnter();
     pFile->pGroup->iSize += iNewEnd - pFile->iSize;
     pFile->iSize = iNewEnd;
     quotaLeave();
   }
-  return rc;    
+  return rc;
 }
 
 /*
 ** Close an open quota_FILE stream.
 */
 int sqlite3_quota_fclose(quota_FILE *p){
   int rc;
   quotaFile *pFile;
@@ -1156,16 +1156,23 @@ void sqlite3_quota_rewind(quota_FILE *p)
 /*
 ** Tell the current location of a quota_FILE stream.
 */
 long sqlite3_quota_ftell(quota_FILE *p){
   return ftell(p->f);
 }
 
 /*
+** Test the error indicator for the given file.
+*/
+int sqlite3_quota_ferror(quota_FILE *p){
+  return ferror(p->f);
+}
+
+/*
 ** Truncate a file to szNew bytes.
 */
 int sqlite3_quota_ftruncate(quota_FILE *p, sqlite3_int64 szNew){
   quotaFile *pFile = p->pFile;
   int rc;
   if( (pFile = p->pFile)!=0 && pFile->iSize<szNew ){
     quotaGroup *pGroup;
     if( pFile->iSize<szNew ){
@@ -1233,16 +1240,35 @@ sqlite3_int64 sqlite3_quota_file_truesiz
 /*
 ** Return the size of the file, as it is known to the quota subsystem.
 */
 sqlite3_int64 sqlite3_quota_file_size(quota_FILE *p){
   return p->pFile ? p->pFile->iSize : -1;
 }
 
 /*
+** Determine the amount of data in bytes available for reading
+** in the given file.
+*/
+long sqlite3_quota_file_available(quota_FILE *p){
+  FILE* f = p->f;
+  long pos1, pos2;
+  int rc;
+  pos1 = ftell(f);
+  if ( pos1 < 0 ) return -1;
+  rc = fseek(f, 0, SEEK_END);
+  if ( rc != 0 ) return -1;
+  pos2 = ftell(f);
+  if ( pos2 < 0 ) return -1;
+  rc = fseek(f, pos1, SEEK_SET);
+  if ( rc != 0 ) return -1;
+  return pos2 - pos1;
+}
+
+/*
 ** Remove a managed file.  Update quotas accordingly.
 */
 int sqlite3_quota_remove(const char *zFilename){
   char *zFull;            /* Full pathname for zFilename */
   size_t nFull;           /* Number of bytes in zFilename */
   int rc;                 /* Result code */
   quotaGroup *pGroup;     /* Group containing zFilename */
   quotaFile *pFile;       /* A file in the group */
--- a/db/sqlite3/src/test_quota.h
+++ b/db/sqlite3/src/test_quota.h
@@ -157,17 +157,17 @@ typedef struct quota_FILE quota_FILE;
 quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode);
 
 /*
 ** Perform I/O against a quota_FILE object.  When doing writes, the
 ** quota mechanism may result in a short write, in order to prevent
 ** the sum of sizes of all files from going over quota.
 */
 size_t sqlite3_quota_fread(void*, size_t, size_t, quota_FILE*);
-size_t sqlite3_quota_fwrite(void*, size_t, size_t, quota_FILE*);
+size_t sqlite3_quota_fwrite(const void*, size_t, size_t, quota_FILE*);
 
 /*
 ** Flush all written content held in memory buffers out to disk.
 ** This is the equivalent of fflush() in the standard library.
 **
 ** If the hardSync parameter is true (non-zero) then this routine
 ** also forces OS buffers to disk - the equivalent of fsync().
 **
@@ -186,24 +186,31 @@ int sqlite3_quota_fclose(quota_FILE*);
 ** Move the read/write pointer for a quota_FILE object.  Or tell the
 ** current location of the read/write pointer.
 */
 int sqlite3_quota_fseek(quota_FILE*, long, int);
 void sqlite3_quota_rewind(quota_FILE*);
 long sqlite3_quota_ftell(quota_FILE*);
 
 /*
+** Test the error indicator for the given file.
+**
+** Return non-zero if the error indicator is set.
+*/
+int sqlite3_quota_ferror(quota_FILE*);
+
+/*
 ** Truncate a file previously opened by sqlite3_quota_fopen().  Return
 ** zero on success and non-zero on any kind of failure.
 **
 ** The newSize argument must be less than or equal to the current file size.
 ** Any attempt to "truncate" a file to a larger size results in 
 ** undefined behavior.
 */
-int sqlite3_quota_ftrunate(quota_FILE*, sqlite3_int64 newSize);
+int sqlite3_quota_ftruncate(quota_FILE*, sqlite3_int64 newSize);
 
 /*
 ** Return the last modification time of the opened file, in seconds
 ** since 1970.
 */
 int sqlite3_quota_file_mtime(quota_FILE*, time_t *pTime);
 
 /*
@@ -228,16 +235,24 @@ sqlite3_int64 sqlite3_quota_file_size(qu
 ** pending writes have not yet been flushed to disk.
 **
 ** Return -1 if the file does not exist or if the size of the file
 ** cannot be determined for some reason.
 */
 sqlite3_int64 sqlite3_quota_file_truesize(quota_FILE*);
 
 /*
+** Determine the amount of data in bytes available for reading
+** in the given file.
+**
+** Return -1 if the amount cannot be determined for some reason.
+*/
+long sqlite3_quota_file_available(quota_FILE*);
+
+/*
 ** Delete a file from the disk, if that file is under quota management.
 ** Adjust quotas accordingly.
 **
 ** If zFilename is the name of a directory that matches one of the
 ** quota glob patterns, then all files under quota management that
 ** are contained within that directory are deleted.
 **
 ** A standard SQLite result code is returned (SQLITE_OK, SQLITE_NOMEM, etc.)
--- a/dom/Makefile.in
+++ b/dom/Makefile.in
@@ -45,16 +45,17 @@ DIRS += \
 
 DIRS += \
   apps \
   base \
   bindings \
   battery \
   contacts \
   devicestorage \
+  file \
   power \
   settings \
   sms \
   src \
   locales \
   network \
   plugins/base \
   plugins/ipc \
--- a/dom/base/DOMRequest.cpp
+++ b/dom/base/DOMRequest.cpp
@@ -12,41 +12,45 @@
 #include "nsEventDispatcher.h"
 #include "nsIPrivateDOMEvent.h"
 #include "nsDOMEvent.h"
 
 using mozilla::dom::DOMRequest;
 using mozilla::dom::DOMRequestService;
 
 DOMRequest::DOMRequest(nsIDOMWindow* aWindow)
-  : mDone(false)
-  , mResult(JSVAL_VOID)
+  : mResult(JSVAL_VOID)
+  , mDone(false)
   , mRooted(false)
 {
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
   BindToOwner(window->IsInnerWindow() ? window.get() :
                                         window->GetCurrentInnerWindow());
 }
 
 DOMCI_DATA(DOMRequest, DOMRequest)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMRequest)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMRequest,
                                                   nsDOMEventTargetHelper)
   NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(success)
   NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(error)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mError)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMRequest,
                                                 nsDOMEventTargetHelper)
-  tmp->mResult = JSVAL_VOID;
-  tmp->UnrootResultVal();
+  if (tmp->mRooted) {
+    tmp->mResult = JSVAL_VOID;
+    tmp->UnrootResultVal();
+  }
   NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(success)
   NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(error)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mError)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DOMRequest,
                                                nsDOMEventTargetHelper)
   // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
   // nsDOMEventTargetHelper does it for us.
   if (JSVAL_IS_GCTHING(tmp->mResult)) {
     void *gcThing = JSVAL_TO_GCTHING(tmp->mResult);
@@ -93,58 +97,93 @@ DOMRequest::GetError(nsIDOMDOMError** aE
   NS_IF_ADDREF(*aError = mError);
 
   return NS_OK;
 }
 
 void
 DOMRequest::FireSuccess(jsval aResult)
 {
-  NS_ABORT_IF_FALSE(!mDone, "Already fired success/error");
+  NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
+  NS_ASSERTION(!mError, "mError shouldn't have been set!");
+  NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!");
 
   mDone = true;
-  RootResultVal();
+  if (JSVAL_IS_GCTHING(aResult)) {
+    RootResultVal();
+  }
   mResult = aResult;
 
-  FireEvent(NS_LITERAL_STRING("success"));
+  FireEvent(NS_LITERAL_STRING("success"), false, false);
 }
 
 void
 DOMRequest::FireError(const nsAString& aError)
 {
-  NS_ABORT_IF_FALSE(!mDone, "Already fired success/error");
+  NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
+  NS_ASSERTION(!mError, "mError shouldn't have been set!");
+  NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!");
 
   mDone = true;
   mError = DOMError::CreateWithName(aError);
 
-  FireEvent(NS_LITERAL_STRING("error"));
+  FireEvent(NS_LITERAL_STRING("error"), true, true);
 }
 
 void
-DOMRequest::FireEvent(const nsAString& aType)
+DOMRequest::FireError(nsresult aError)
+{
+  NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
+  NS_ASSERTION(!mError, "mError shouldn't have been set!");
+  NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!");
+
+  mDone = true;
+  mError = DOMError::CreateForNSResult(aError);
+
+  FireEvent(NS_LITERAL_STRING("error"), true, true);
+}
+
+void
+DOMRequest::FireEvent(const nsAString& aType, bool aBubble, bool aCancelable)
 {
   if (NS_FAILED(CheckInnerWindowCorrectness())) {
     return;
   }
 
   nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nsnull, nsnull);
-  nsresult rv = event->InitEvent(aType, false, false);
+  nsresult rv = event->InitEvent(aType, aBubble, aCancelable);
   if (NS_FAILED(rv)) {
     return;
   }
 
   rv = event->SetTrusted(true);
   if (NS_FAILED(rv)) {
     return;
   }
 
   bool dummy;
   DispatchEvent(event, &dummy);
 }
 
+void
+DOMRequest::RootResultVal()
+{
+  NS_ASSERTION(!mRooted, "Don't call me if already rooted!");
+  NS_HOLD_JS_OBJECTS(this, DOMRequest);
+  mRooted = true;
+}
+
+void
+DOMRequest::UnrootResultVal()
+{
+  NS_ASSERTION(mRooted, "Don't call me if not rooted!");
+  NS_DROP_JS_OBJECTS(this, DOMRequest);
+  mRooted = false;
+}
+
 NS_IMPL_ISUPPORTS1(DOMRequestService, nsIDOMRequestService)
 
 NS_IMETHODIMP
 DOMRequestService::CreateRequest(nsIDOMWindow* aWindow,
                                  nsIDOMDOMRequest** aRequest)
 {
   NS_ADDREF(*aRequest = new DOMRequest(aWindow));
   
--- a/dom/base/DOMRequest.h
+++ b/dom/base/DOMRequest.h
@@ -15,60 +15,51 @@
 #include "nsCOMPtr.h"
 
 namespace mozilla {
 namespace dom {
 
 class DOMRequest : public nsDOMEventTargetHelper,
                    public nsIDOMDOMRequest
 {
+protected:
+  jsval mResult;
+  nsCOMPtr<nsIDOMDOMError> mError;
+  bool mDone;
+  bool mRooted;
+
   NS_DECL_EVENT_HANDLER(success)
   NS_DECL_EVENT_HANDLER(error)
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMDOMREQUEST
   NS_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper::)
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(DOMRequest,
                                                          nsDOMEventTargetHelper)
 
   void FireSuccess(jsval aResult);
   void FireError(const nsAString& aError);
+  void FireError(nsresult aError);
 
   DOMRequest(nsIDOMWindow* aWindow);
 
   virtual ~DOMRequest()
   {
-    UnrootResultVal();
-  }
-
-  bool mDone;
-  jsval mResult;
-  nsCOMPtr<nsIDOMDOMError> mError;
-  bool mRooted;
-
-private:
-  void FireEvent(const nsAString& aType);
-
-  void RootResultVal()
-  {
-    if (!mRooted) {
-      NS_HOLD_JS_OBJECTS(this, DOMRequest);
-      mRooted = true;
+    if (mRooted) {
+      UnrootResultVal();
     }
   }
 
-  void UnrootResultVal()
-  {
-    if (mRooted) {
-      NS_DROP_JS_OBJECTS(this, DOMRequest);
-      mRooted = false;
-    }
-  }
+protected:
+  void FireEvent(const nsAString& aType, bool aBubble, bool aCancelable);
+
+  virtual void RootResultVal();
+  virtual void UnrootResultVal();
 };
 
 class DOMRequestService : public nsIDOMRequestService
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMREQUESTSERVICE
 
--- a/dom/base/StructuredCloneTags.h
+++ b/dom/base/StructuredCloneTags.h
@@ -12,16 +12,17 @@ namespace dom {
 
 enum StructuredCloneTags {
   SCTAG_BASE = JS_SCTAG_USER_MIN,
 
   // These tags are used only for main thread structured clone.
   SCTAG_DOM_BLOB,
   SCTAG_DOM_FILE,
   SCTAG_DOM_FILELIST,
+  SCTAG_DOM_FILEHANDLE,
 
   // These tags are used for both main thread and workers.
   SCTAG_DOM_IMAGEDATA,
 
   SCTAG_DOM_MAX
 };
 
 } // namespace dom
--- a/dom/base/domerr.msg
+++ b/dom/base/domerr.msg
@@ -102,8 +102,15 @@ DOM_MSG_DEF(NS_ERROR_OUT_OF_MEMORY      
 DOM_MSG_DEF(NS_ERROR_ILLEGAL_VALUE                 , "Illegal value")
 DOM_MSG_DEF(NS_ERROR_INVALID_ARG                   , "Invalid argument")
 DOM_MSG_DEF(NS_ERROR_NO_AGGREGATION                , "Component does not support aggregation")
 DOM_MSG_DEF(NS_ERROR_NOT_AVAILABLE                 , "Component is not available")
 DOM_MSG_DEF(NS_ERROR_FACTORY_NOT_REGISTERED        , "Factory not registered")
 DOM_MSG_DEF(NS_ERROR_FACTORY_NOT_LOADED            , "Factory not loaded")
 DOM_MSG_DEF(NS_ERROR_FACTORY_NO_SIGNATURE_SUPPORT  , "Factory does not support signatures")
 DOM_MSG_DEF(NS_ERROR_FACTORY_EXISTS                , "Factory already exists")
+
+DOM4_MSG_DEF(UnknownError, "The operation failed for reasons unrelated to the file storage itself and not covered by any other error code.", NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR)
+DOM4_MSG_DEF(LockedFileInactiveError, "A request was placed against a locked file which is currently not active, or which is finished.", NS_ERROR_DOM_FILEHANDLE_LOCKEDFILE_INACTIVE_ERR)
+DOM4_MSG_DEF(ReadOnlyError, "A mutation operation was attempted in a READ_ONLY locked file.", NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR)
+
+DOM4_MSG_DEF(InvalidStateError, "A mutation operation was attempted on a file storage that did not allow mutations.", NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR)
+DOM4_MSG_DEF(AbortError, "A request was aborted, for example through a call to LockedFile.abort.", NS_ERROR_DOM_FILEHANDLE_ABORT_ERR)
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -459,16 +459,17 @@
 #include "DOMSVGNumberList.h"
 #include "DOMSVGPathSegList.h"
 #include "DOMSVGPointList.h"
 #include "DOMSVGStringList.h"
 #include "DOMSVGTransformList.h"
 
 #include "mozilla/dom/indexedDB/IDBWrapperCache.h"
 #include "mozilla/dom/indexedDB/IDBFactory.h"
+#include "mozilla/dom/indexedDB/IDBFileHandle.h"
 #include "mozilla/dom/indexedDB/IDBRequest.h"
 #include "mozilla/dom/indexedDB/IDBDatabase.h"
 #include "mozilla/dom/indexedDB/IDBEvents.h"
 #include "mozilla/dom/indexedDB/IDBObjectStore.h"
 #include "mozilla/dom/indexedDB/IDBTransaction.h"
 #include "mozilla/dom/indexedDB/IDBCursor.h"
 #include "mozilla/dom/indexedDB/IDBKeyRange.h"
 #include "mozilla/dom/indexedDB/IDBIndex.h"
@@ -508,16 +509,20 @@ using mozilla::dom::indexedDB::IDBWrappe
 #ifdef MOZ_B2G_BT
 #include "BluetoothManager.h"
 #include "BluetoothAdapter.h"
 #endif
 
 #include "DOMError.h"
 #include "DOMRequest.h"
 
+#include "DOMFileHandle.h"
+#include "FileRequest.h"
+#include "LockedFile.h"
+
 #include "mozilla/Likely.h"
 
 #undef None // something included above defines this preprocessor symbol, maybe Xlib headers
 #include "WebGLContext.h"
 
 #include "nsIDOMGlobalObjectConstructor.h"
 
 using namespace mozilla;
@@ -1568,16 +1573,18 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(WebSocket, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(CloseEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(IDBFactory, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
+  NS_DEFINE_CLASSINFO_DATA_WITH_NAME(IDBFileHandle, FileHandle, nsEventTargetSH,
+                           EVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(IDBRequest, IDBEventTargetSH,
                            IDBEVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(IDBDatabase, IDBEventTargetSH,
                            IDBEVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(IDBObjectStore, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(IDBTransaction, IDBEventTargetSH,
                            IDBEVENTTARGET_SCRIPTABLE_FLAGS)
@@ -1633,16 +1640,23 @@ static nsDOMClassInfoData sClassInfoData
                            EVENTTARGET_SCRIPTABLE_FLAGS)
 #endif
 
   NS_DEFINE_CLASSINFO_DATA(DOMError, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(DOMRequest, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
+
+  NS_DEFINE_CLASSINFO_DATA_WITH_NAME(DOMFileHandle, FileHandle, nsEventTargetSH,
+                           EVENTTARGET_SCRIPTABLE_FLAGS)
+  NS_DEFINE_CLASSINFO_DATA(FileRequest, nsEventTargetSH,
+                           EVENTTARGET_SCRIPTABLE_FLAGS)
+  NS_DEFINE_CLASSINFO_DATA(LockedFile, nsEventTargetSH,
+                           EVENTTARGET_SCRIPTABLE_FLAGS)
 };
 
 // Objects that should be constructable through |new Name();|
 struct nsContractIDMapData
 {
   PRInt32 mDOMClassInfoID;
   const char *mContractID;
 };
@@ -4325,16 +4339,21 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCloseEvent)
     DOM_CLASSINFO_EVENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(IDBFactory, nsIIDBFactory)
     DOM_CLASSINFO_MAP_ENTRY(nsIIDBFactory)
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(IDBFileHandle, nsIDOMFileHandle)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMFileHandle)
+    DOM_CLASSINFO_MAP_ENTRY(nsIIDBFileHandle)
+  DOM_CLASSINFO_MAP_END
+
   DOM_CLASSINFO_MAP_BEGIN(IDBRequest, nsIIDBRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIIDBRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(IDBDatabase, nsIIDBDatabase)
     DOM_CLASSINFO_MAP_ENTRY(nsIIDBDatabase)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
@@ -4454,16 +4473,30 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMError)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(DOMRequest, nsIDOMDOMRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(DOMFileHandle, nsIDOMFileHandle)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMFileHandle)
+  DOM_CLASSINFO_MAP_END
+
+  DOM_CLASSINFO_MAP_BEGIN(FileRequest, nsIDOMFileRequest)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMFileRequest)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMRequest)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
+  DOM_CLASSINFO_MAP_END
+
+  DOM_CLASSINFO_MAP_BEGIN(LockedFile, nsIDOMLockedFile)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMLockedFile)
+  DOM_CLASSINFO_MAP_END
+
 #ifdef NS_DEBUG
   {
     PRUint32 i = ArrayLength(sClassInfoData);
 
     if (i != eDOMClassInfoIDCount) {
       NS_ERROR("The number of items in sClassInfoData doesn't match the "
                "number of nsIDOMClassInfo ID's, this is bad! Fix it!");
 
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -482,16 +482,17 @@ DOMCI_CLASS(FormData)
 DOMCI_CLASS(DesktopNotification)
 DOMCI_CLASS(DesktopNotificationCenter)
 
 // WebSocket
 DOMCI_CLASS(WebSocket)
 DOMCI_CLASS(CloseEvent)
 
 DOMCI_CLASS(IDBFactory)
+DOMCI_CLASS(IDBFileHandle)
 DOMCI_CLASS(IDBRequest)
 DOMCI_CLASS(IDBDatabase)
 DOMCI_CLASS(IDBObjectStore)
 DOMCI_CLASS(IDBTransaction)
 DOMCI_CLASS(IDBCursor)
 DOMCI_CLASS(IDBCursorWithValue)
 DOMCI_CLASS(IDBKeyRange)
 DOMCI_CLASS(IDBIndex)
@@ -521,8 +522,12 @@ DOMCI_CLASS(CallEvent)
 
 #ifdef MOZ_B2G_BT
 DOMCI_CLASS(BluetoothManager)
 DOMCI_CLASS(BluetoothAdapter)
 #endif
 
 DOMCI_CLASS(DOMError)
 DOMCI_CLASS(DOMRequest)
+
+DOMCI_CLASS(DOMFileHandle)
+DOMCI_CLASS(FileRequest)
+DOMCI_CLASS(LockedFile)
--- a/dom/base/nsDOMError.h
+++ b/dom/base/nsDOMError.h
@@ -84,9 +84,16 @@
 #define NS_ERROR_DOM_BAD_URI                     NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,1012)
 #define NS_ERROR_DOM_RETVAL_UNDEFINED            NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,1013)
 #define NS_ERROR_DOM_QUOTA_REACHED               NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,1014)
 
 #define NS_ERROR_DOM_FILE_NOT_FOUND_ERR          NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILE, 0)
 #define NS_ERROR_DOM_FILE_NOT_READABLE_ERR       NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILE, 1)
 #define NS_ERROR_DOM_FILE_ABORT_ERR              NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILE, 2)
 
+#define NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR      NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILEHANDLE,1)
+#define NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR  NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILEHANDLE,2)
+#define NS_ERROR_DOM_FILEHANDLE_LOCKEDFILE_INACTIVE_ERR \
+                                                 NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILEHANDLE,3)
+#define NS_ERROR_DOM_FILEHANDLE_ABORT_ERR        NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILEHANDLE,4)
+#define NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR    NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILEHANDLE,5)
+
 #endif // nsDOMError_h__
--- a/dom/base/nsDOMException.cpp
+++ b/dom/base/nsDOMException.cpp
@@ -91,16 +91,19 @@ enum DOM4ErrorTypeCodeMap {
   ConstraintError          = 0,
   DataError                = 0,
   TransactionInactiveError = 0,
   ReadOnlyError            = 0,
   VersionError             = 0,
 
   /* File API errors http://dev.w3.org/2006/webapi/FileAPI/#ErrorAndException */
   NotReadableError         = 0,
+
+  /* FileHandle API errors */
+  LockedFileInactiveError = 0,
 };
 
 #define DOM4_MSG_DEF(name, message, nsresult) {(nsresult), name, #name, message},
 #define DOM_MSG_DEF(val, message) {(val), NS_ERROR_GET_CODE(val), #val, message},
 
 static struct ResultStruct
 {
   nsresult mNSResult;
--- a/dom/base/nsDOMScriptObjectFactory.cpp
+++ b/dom/base/nsDOMScriptObjectFactory.cpp
@@ -50,18 +50,19 @@ nsDOMScriptObjectFactory::nsDOMScriptObj
   nsCOMPtr<nsIExceptionProvider> provider = new nsDOMExceptionProvider();
   nsCOMPtr<nsIExceptionService> xs =
     do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID);
 
   if (xs) {
     xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM);
     xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_SVG);
     xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM_XPATH);
+    xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_XPCONNECT);
     xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM_INDEXEDDB);
-    xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_XPCONNECT);
+    xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM_FILEHANDLE);
   }
 
   NS_ASSERTION(!gExceptionProvider, "Registered twice?!");
   provider.swap(gExceptionProvider);
 
   // And pre-create the javascript language.
   NS_CreateJSRuntime(getter_AddRefs(mJSRuntime));
 }
@@ -137,16 +138,20 @@ nsDOMScriptObjectFactory::Observe(nsISup
         xs->UnregisterExceptionProvider(gExceptionProvider,
                                         NS_ERROR_MODULE_DOM);
         xs->UnregisterExceptionProvider(gExceptionProvider,
                                         NS_ERROR_MODULE_SVG);
         xs->UnregisterExceptionProvider(gExceptionProvider,
                                         NS_ERROR_MODULE_DOM_XPATH);
         xs->UnregisterExceptionProvider(gExceptionProvider,
                                         NS_ERROR_MODULE_XPCONNECT);
+        xs->UnregisterExceptionProvider(gExceptionProvider,
+                                        NS_ERROR_MODULE_DOM_INDEXEDDB);
+        xs->UnregisterExceptionProvider(gExceptionProvider,
+                                        NS_ERROR_MODULE_DOM_FILEHANDLE);
       }
 
       NS_RELEASE(gExceptionProvider);
     }
   }
 
   return NS_OK;
 }
@@ -246,15 +251,16 @@ nsDOMExceptionProvider::GetException(nsr
       return NS_NewSVGException(result, aDefaultException, _retval);
     case NS_ERROR_MODULE_DOM_XPATH:
       return NS_NewXPathException(result, aDefaultException, _retval);
     case NS_ERROR_MODULE_XPCONNECT:
       return CreateXPConnectException(result, aDefaultException, _retval);
     default:
       MOZ_ASSERT(NS_ERROR_GET_MODULE(result) == NS_ERROR_MODULE_DOM ||
           NS_ERROR_GET_MODULE(result) == NS_ERROR_MODULE_DOM_FILE ||
-          NS_ERROR_GET_MODULE(result) == NS_ERROR_MODULE_DOM_INDEXEDDB,
+          NS_ERROR_GET_MODULE(result) == NS_ERROR_MODULE_DOM_INDEXEDDB ||
+          NS_ERROR_GET_MODULE(result) == NS_ERROR_MODULE_DOM_FILEHANDLE,
           "Trying to create an exception for the wrong error module.");
       return NS_NewDOMException(result, aDefaultException, _retval);
   }
   NS_NOTREACHED("Not reached");
   return NS_ERROR_UNEXPECTED;
 }
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -58,16 +58,18 @@
 
 #include "Layers.h"
 #include "nsIIOService.h"
 
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/indexedDB/FileInfo.h"
 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
 #include "sampler.h"
+#include "nsDOMBlobBuilder.h"
+#include "nsIDOMFileHandle.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace mozilla::widget;
 
 static bool IsUniversalXPConnectCapable()
 {
@@ -2265,24 +2267,109 @@ nsDOMWindowUtils::CheckAndClearPaintedSt
     *aResult = false;
     return NS_OK;
   }
 
   *aResult = frame->CheckAndClearPaintedState();
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsDOMWindowUtils::GetFileId(nsIDOMBlob* aBlob, PRInt64* aResult)
+static nsresult
+GetFileOrBlob(const nsAString& aName, const jsval& aBlobParts,
+              const jsval& aParameters, JSContext* aCx,
+              PRUint8 aOptionalArgCount, nsISupports** aResult)
 {
   if (!IsUniversalXPConnectCapable()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  *aResult = aBlob->GetFileId();
+  nsresult rv;
+
+  nsCOMPtr<nsISupports> file;
+
+  if (aName.IsVoid()) {
+    rv = nsDOMMultipartFile::NewBlob(getter_AddRefs(file));
+  }
+  else {
+    rv = nsDOMMultipartFile::NewFile(aName, getter_AddRefs(file));
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIJSNativeInitializer> initializer = do_QueryInterface(file);
+  NS_ASSERTION(initializer, "what?");
+
+  jsval args[2] = { aBlobParts, aParameters };
+
+  rv = initializer->Initialize(nsnull, aCx, nsnull, aOptionalArgCount, args);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  file.forget(aResult);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWindowUtils::GetFile(const nsAString& aName, const jsval& aBlobParts,
+                          const jsval& aParameters, JSContext* aCx,
+                          PRUint8 aOptionalArgCount, nsIDOMFile** aResult)
+{
+  nsCOMPtr<nsISupports> file;
+  nsresult rv = GetFileOrBlob(aName, aBlobParts, aParameters, aCx,
+                              aOptionalArgCount, getter_AddRefs(file));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIDOMFile> result = do_QueryInterface(file);
+  result.forget(aResult);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWindowUtils::GetBlob(const jsval& aBlobParts, const jsval& aParameters,
+                          JSContext* aCx, PRUint8 aOptionalArgCount,
+                          nsIDOMBlob** aResult)
+{
+  nsAutoString name;
+  name.SetIsVoid(true);
+
+  nsCOMPtr<nsISupports> blob;
+  nsresult rv = GetFileOrBlob(name, aBlobParts, aParameters, aCx,
+                              aOptionalArgCount, getter_AddRefs(blob));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIDOMBlob> result = do_QueryInterface(blob);
+  result.forget(aResult);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWindowUtils::GetFileId(const jsval& aFile, JSContext* aCx,
+                            PRInt64* aResult)
+{
+
+  if (!JSVAL_IS_PRIMITIVE(aFile)) {
+    JSObject* obj = JSVAL_TO_OBJECT(aFile);
+
+    nsISupports* nativeObj =
+      nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj);
+
+    nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(nativeObj);
+    if (blob) {
+      *aResult = blob->GetFileId();
+      return NS_OK;
+    }
+
+    nsCOMPtr<nsIDOMFileHandle> fileHandle = do_QueryInterface(nativeObj);
+    if (fileHandle) {
+      *aResult = fileHandle->GetFileId();
+      return NS_OK;
+    }
+  }
+
+  *aResult = -1;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::GetFileReferences(const nsAString& aDatabaseName,
                                     PRInt64 aId, PRInt32* aRefCnt,
                                     PRInt32* aDBRefCnt, PRInt32* aSliceRefCnt,
                                     bool* aResult)
--- a/dom/base/nsIDOMDOMRequest.idl
+++ b/dom/base/nsIDOMDOMRequest.idl
@@ -12,18 +12,18 @@ interface nsIDOMWindow;
 [scriptable, builtinclass, uuid(a3ad2846-ffb2-48d7-a786-2254cb82560d)]
 interface nsIDOMDOMRequest : nsIDOMEventTarget
 {
   readonly attribute DOMString readyState; // "pending" or "done"
 
   readonly attribute jsval result;
   readonly attribute nsIDOMDOMError error;
 
-           attribute nsIDOMEventListener onsuccess;
-           attribute nsIDOMEventListener onerror;
+  attribute nsIDOMEventListener onsuccess;
+  attribute nsIDOMEventListener onerror;
 };
 
 [scriptable, builtinclass, uuid(eebcdf29-f8fa-4c36-bbc7-2146b1cbaf7b)]
 interface nsIDOMRequestService : nsISupports
 {
   nsIDOMDOMRequest createRequest(in nsIDOMWindow window);
 
   void fireSuccess(in nsIDOMDOMRequest request, in jsval result);
--- a/dom/dom-config.mk
+++ b/dom/dom-config.mk
@@ -1,15 +1,16 @@
 # 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/.
 
 DOM_SRCDIRS = \
   dom/base \
   dom/battery \
+  dom/file \
   dom/power \
   dom/network/src \
   dom/settings \
   dom/sms/src \
   dom/contacts \
   dom/src/events \
   dom/src/storage \
   dom/src/offline \
new file mode 100644
--- /dev/null
+++ b/dom/file/AsyncHelper.cpp
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "AsyncHelper.h"
+
+#include "nsIRequestObserver.h"
+
+#include "nsNetUtil.h"
+
+#include "FileService.h"
+
+USING_FILE_NAMESPACE
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(AsyncHelper, nsIRunnable, nsIRequest)
+
+nsresult
+AsyncHelper::AsyncWork(nsIRequestObserver* aObserver, nsISupports* aCtxt)
+{
+  nsresult rv;
+
+  if (aObserver) {
+    // build proxy for observer events
+    rv = NS_NewRequestObserverProxy(getter_AddRefs(mObserver), aObserver);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  mCtxt = aCtxt;
+
+  FileService* service = FileService::GetOrCreate();
+  NS_ENSURE_TRUE(service, NS_ERROR_FAILURE);
+
+  nsIEventTarget* target = service->StreamTransportTarget();
+
+  rv = target->Dispatch(this, NS_DISPATCH_NORMAL);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AsyncHelper::Run()
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mObserver) {
+    mObserver->OnStartRequest(this, mCtxt);
+  }
+
+  mStatus = DoStreamWork(mStream);
+
+  if (mObserver) {
+    mObserver->OnStopRequest(this, mCtxt, mStatus);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AsyncHelper::GetName(nsACString& aName)
+{
+  NS_WARNING("Shouldn't be called!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+AsyncHelper::IsPending(bool* _retval)
+{
+  NS_WARNING("Shouldn't be called!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+AsyncHelper::GetStatus(nsresult* aStatus)
+{
+  *aStatus = mStatus;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AsyncHelper::Cancel(nsresult aStatus)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AsyncHelper::Suspend()
+{
+  NS_WARNING("Shouldn't be called!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+AsyncHelper::Resume()
+{
+  NS_WARNING("Shouldn't be called!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+AsyncHelper::GetLoadGroup(nsILoadGroup** aLoadGroup)
+{
+  NS_WARNING("Shouldn't be called!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+AsyncHelper::SetLoadGroup(nsILoadGroup* aLoadGroup)
+{
+  NS_WARNING("Shouldn't be called!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+AsyncHelper::GetLoadFlags(nsLoadFlags* aLoadFlags)
+{
+  NS_WARNING("Shouldn't be called!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+AsyncHelper::SetLoadFlags(nsLoadFlags aLoadFlags)
+{
+  NS_WARNING("Shouldn't be called!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
new file mode 100644
--- /dev/null
+++ b/dom/file/AsyncHelper.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_file_asynchelper_h__
+#define mozilla_dom_file_asynchelper_h__
+
+#include "FileCommon.h"
+
+#include "nsIRequest.h"
+#include "nsIRunnable.h"
+
+class nsIRequestObserver;
+
+BEGIN_FILE_NAMESPACE
+
+/**
+ * Must be subclassed. The subclass must implement DoStreamWork.
+ * Async operations that are not supported in necko (truncate, flush, etc.)
+ * should use this helper. Call AsyncWork to invoke the operation.
+ */
+class AsyncHelper : public nsIRunnable,
+                    public nsIRequest
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIRUNNABLE
+  NS_DECL_NSIREQUEST
+
+  nsresult
+  AsyncWork(nsIRequestObserver* aObserver, nsISupports* aCtxt);
+
+protected:
+  AsyncHelper(nsISupports* aStream)
+  : mStream(aStream),
+    mStatus(NS_OK)
+  { }
+
+  virtual ~AsyncHelper()
+  { }
+
+  virtual nsresult
+  DoStreamWork(nsISupports* aStream) = 0;
+
+private:
+  nsCOMPtr<nsISupports> mStream;
+  nsCOMPtr<nsIRequestObserver> mObserver;
+  nsCOMPtr<nsISupports> mCtxt;
+
+  nsresult mStatus;
+};
+
+END_FILE_NAMESPACE
+
+#endif // mozilla_dom_file_asynchelper_h__
new file mode 100644
--- /dev/null
+++ b/dom/file/DOMFileHandle.cpp
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "DOMFileHandle.h"
+
+#include "nsIFileStreams.h"
+
+#include "nsDOMClassInfoID.h"
+#include "nsNetUtil.h"
+
+#include "File.h"
+#include "LockedFile.h"
+
+USING_FILE_NAMESPACE
+
+// static
+already_AddRefed<DOMFileHandle>
+DOMFileHandle::Create(nsPIDOMWindow* aWindow,
+                      nsIFileStorage* aFileStorage,
+                      nsIFile* aFile)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsRefPtr<DOMFileHandle> newFile(new DOMFileHandle);
+
+  newFile->BindToOwner(aWindow);
+
+  newFile->mFileStorage = aFileStorage;
+  nsresult rv = aFile->GetLeafName(newFile->mName);
+  NS_ENSURE_SUCCESS(rv, nsnull);
+
+  newFile->mFile = aFile;
+  newFile->mFileName = newFile->mName;
+
+  return newFile.forget();
+}
+
+already_AddRefed<nsISupports>
+DOMFileHandle::CreateStream(nsIFile* aFile, bool aReadOnly)
+{
+  nsresult rv;
+
+  if (aReadOnly) {
+    nsCOMPtr<nsIInputStream> stream;
+    rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), aFile, -1, -1,
+                                    nsIFileInputStream::DEFER_OPEN);
+    NS_ENSURE_SUCCESS(rv, nsnull);
+    return stream.forget();
+  }
+
+  nsCOMPtr<nsIFileStream> stream;
+  rv = NS_NewLocalFileStream(getter_AddRefs(stream), aFile, -1, -1,
+                             nsIFileStream::DEFER_OPEN);
+  NS_ENSURE_SUCCESS(rv, nsnull);
+  return stream.forget();
+}
+
+already_AddRefed<nsIDOMFile>
+DOMFileHandle::CreateFileObject(LockedFile* aLockedFile, PRUint32 aFileSize)
+{
+  nsCOMPtr<nsIDOMFile> file = 
+    new File(mName, mType, aFileSize, mFile, aLockedFile);
+
+  return file.forget();
+}
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMFileHandle)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DOMFileHandle)
+NS_INTERFACE_MAP_END_INHERITING(FileHandle)
+
+NS_IMPL_ADDREF_INHERITED(DOMFileHandle, FileHandle)
+NS_IMPL_RELEASE_INHERITED(DOMFileHandle, FileHandle)
+
+DOMCI_DATA(DOMFileHandle, DOMFileHandle)
new file mode 100644
--- /dev/null
+++ b/dom/file/DOMFileHandle.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_file_domfilehandle_h__
+#define mozilla_dom_file_domfilehandle_h__
+
+#include "FileCommon.h"
+
+#include "FileHandle.h"
+
+BEGIN_FILE_NAMESPACE
+
+class DOMFileHandle : public FileHandle
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  static already_AddRefed<DOMFileHandle>
+  Create(nsPIDOMWindow* aWindow,
+         nsIFileStorage* aFileStorage,
+         nsIFile* aFile);
+
+  virtual already_AddRefed<nsISupports>
+  CreateStream(nsIFile* aFile, bool aReadOnly);
+
+  virtual already_AddRefed<nsIDOMFile>
+  CreateFileObject(LockedFile* aLockedFile, PRUint32 aFileSize);
+
+protected:
+  DOMFileHandle()
+  { }
+
+  ~DOMFileHandle()
+  { }
+};
+
+END_FILE_NAMESPACE
+
+#endif // mozilla_dom_file_domfilehandle_h__
new file mode 100644
--- /dev/null
+++ b/dom/file/File.cpp
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "File.h"
+
+#include "LockedFile.h"
+
+USING_FILE_NAMESPACE
+using mozilla::dom::indexedDB::IndexedDatabaseManager;
+
+// Create slice
+File::File(const File* aOther, PRUint64 aStart, PRUint64 aLength,
+           const nsAString& aContentType)
+: nsDOMFileCC(aContentType, aOther->mStart + aStart, aLength),
+  mFile(aOther->mFile), mLockedFile(aOther->mLockedFile),
+  mWholeFile(false), mStoredFile(aOther->mStoredFile)
+{
+  NS_ASSERTION(mFile, "Null file!");
+  NS_ASSERTION(mLockedFile, "Null locked file!");
+
+  if (mStoredFile) {
+    FileInfo* fileInfo;
+
+    if (IndexedDatabaseManager::IsClosed()) {
+      fileInfo = aOther->GetFileInfo();
+    }
+    else {
+      MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
+      fileInfo = aOther->GetFileInfo();
+    }
+
+    mFileInfos.AppendElement(fileInfo);
+  }
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(File)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(File, nsDOMFileCC)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mLockedFile,
+                                                       nsIDOMLockedFile)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(File, nsDOMFileCC)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mLockedFile)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(File)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMFileCC)
+
+NS_IMPL_ADDREF_INHERITED(File, nsDOMFileCC)
+NS_IMPL_RELEASE_INHERITED(File, nsDOMFileCC)
+
+NS_IMETHODIMP
+File::GetInternalStream(nsIInputStream **aStream)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsresult rv = mLockedFile->OpenInputStream(mWholeFile, mStart, mLength,
+                                             aStream);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+already_AddRefed<nsIDOMBlob>
+File::CreateSlice(PRUint64 aStart, PRUint64 aLength,
+                  const nsAString& aContentType)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsCOMPtr<nsIDOMBlob> t =
+    new File(this, aStart, aLength, aContentType);
+  return t.forget();
+}
+
+NS_IMETHODIMP
+File::GetMozFullPathInternal(nsAString &aFilename)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(mIsFile, "Should only be called on files");
+
+  return mFile->GetPath(aFilename);
+}
new file mode 100644
--- /dev/null
+++ b/dom/file/File.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_file_file_h__
+#define mozilla_dom_file_file_h__
+
+#include "FileCommon.h"
+
+#include "nsDOMFile.h"
+
+#include "LockedFile.h"
+
+BEGIN_FILE_NAMESPACE
+
+class File : public nsDOMFileCC
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(File, nsDOMFileCC)
+
+  // Create as a file
+  File(const nsAString& aName, const nsAString& aContentType,
+       PRUint64 aLength, nsIFile* aFile, LockedFile* aLockedFile)
+  : nsDOMFileCC(aName, aContentType, aLength),
+    mFile(aFile), mLockedFile(aLockedFile),
+    mWholeFile(true), mStoredFile(false)
+  {
+    NS_ASSERTION(mFile, "Null file!");
+    NS_ASSERTION(mLockedFile, "Null locked file!");
+  }
+
+  // Create as a stored file
+  File(const nsAString& aName, const nsAString& aContentType,
+       PRUint64 aLength, nsIFile* aFile, LockedFile* aLockedFile,
+       FileInfo* aFileInfo)
+  : nsDOMFileCC(aName, aContentType, aLength),
+    mFile(aFile), mLockedFile(aLockedFile),
+    mWholeFile(true), mStoredFile(true)
+  {
+    NS_ASSERTION(mFile, "Null file!");
+    NS_ASSERTION(mLockedFile, "Null locked file!");
+    mFileInfos.AppendElement(aFileInfo);
+  }
+
+  // Overrides
+  NS_IMETHOD
+  GetMozFullPathInternal(nsAString& aFullPath);
+
+  NS_IMETHOD
+  GetInternalStream(nsIInputStream** aStream);
+
+protected:
+  // Create slice
+  File(const File* aOther, PRUint64 aStart, PRUint64 aLength,
+       const nsAString& aContentType);
+
+  virtual ~File()
+  { }
+
+  virtual already_AddRefed<nsIDOMBlob>
+  CreateSlice(PRUint64 aStart, PRUint64 aLength,
+              const nsAString& aContentType);
+
+  virtual bool
+  IsStoredFile() const
+  {
+    return mStoredFile;
+  }
+
+  virtual bool
+  IsWholeFile() const
+  {
+    return mWholeFile;
+  }
+
+  virtual bool
+  IsSnapshot() const
+  {
+    return true;
+  }
+
+private:
+  nsCOMPtr<nsIFile> mFile;
+  nsRefPtr<LockedFile> mLockedFile;
+
+  bool mWholeFile;
+  bool mStoredFile;
+};
+
+END_FILE_NAMESPACE
+
+#endif // mozilla_dom_file_file_h__
new file mode 100644
--- /dev/null
+++ b/dom/file/FileCommon.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_file_filecommon_h__
+#define mozilla_dom_file_filecommon_h__
+
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsDOMEventTargetHelper.h"
+#include "nsDebug.h"
+#include "nsStringGlue.h"
+#include "nsTArray.h"
+
+#define BEGIN_FILE_NAMESPACE \
+  namespace mozilla { namespace dom { namespace file {
+#define END_FILE_NAMESPACE \
+  } /* namespace file */ } /* namespace dom */ } /* namespace mozilla */
+#define USING_FILE_NAMESPACE \
+  using namespace mozilla::dom::file;
+
+#endif // mozilla_dom_file_filecommon_h__
new file mode 100644
--- /dev/null
+++ b/dom/file/FileHandle.cpp
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "FileHandle.h"
+
+#include "nsContentUtils.h"
+#include "nsDOMClassInfoID.h"
+
+#include "nsIDOMFile.h"
+#include "nsIFileStorage.h"
+
+#include "FileRequest.h"
+#include "FileService.h"
+#include "LockedFile.h"
+#include "MetadataHelper.h"
+
+USING_FILE_NAMESPACE
+
+namespace {
+
+class GetFileHelper : public MetadataHelper
+{
+public:
+  GetFileHelper(LockedFile* aLockedFile,
+                FileRequest* aFileRequest,
+                MetadataParameters* aParams,
+                FileHandle* aFileHandle)
+  : MetadataHelper(aLockedFile, aFileRequest, aParams),
+    mFileHandle(aFileHandle)
+  { }
+
+  nsresult
+  GetSuccessResult(JSContext* aCx, jsval* aVal);
+
+  void
+  ReleaseObjects()
+  {
+    mFileHandle = nsnull;
+
+    MetadataHelper::ReleaseObjects();
+  }
+
+private:
+  nsRefPtr<FileHandle> mFileHandle;
+};
+
+} // anonymous namespace
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(FileHandle)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FileHandle,
+                                                  nsDOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFileStorage)
+  NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(abort)
+  NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(error)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileHandle,
+                                                nsDOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFileStorage)
+  NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(abort)
+  NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(error)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileHandle)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMFileHandle)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
+
+NS_IMPL_ADDREF_INHERITED(FileHandle, nsDOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(FileHandle, nsDOMEventTargetHelper)
+
+NS_IMPL_EVENT_HANDLER(FileHandle, abort);
+NS_IMPL_EVENT_HANDLER(FileHandle, error);
+
+NS_IMETHODIMP
+FileHandle::GetName(nsAString& aName)
+{
+  aName = mName;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileHandle::GetType(nsAString& aType)
+{
+  aType = mType;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileHandle::Open(const nsAString& aMode,
+                 PRUint8 aOptionalArgCount,
+                 nsIDOMLockedFile** _retval)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (FileService::IsShuttingDown() || mFileStorage->IsStorageShuttingDown()) {
+    return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
+  }
+
+  LockedFile::Mode mode;
+  if (aOptionalArgCount) {
+    if (aMode.EqualsLiteral("readwrite")) {
+      mode = LockedFile::READ_WRITE;
+    }
+    else if (aMode.EqualsLiteral("readonly")) {
+      mode = LockedFile::READ_ONLY;
+    }
+    else {
+      return NS_ERROR_TYPE_ERR;
+    }
+  }
+  else {
+    mode = LockedFile::READ_ONLY;
+  }
+
+  nsRefPtr<LockedFile> lockedFile = LockedFile::Create(this, mode);
+  NS_ENSURE_TRUE(lockedFile, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  lockedFile.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileHandle::GetFile(nsIDOMFileRequest** _retval)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // Do nothing if the window is closed
+  if (!GetOwner()) {
+    return NS_OK;
+  }
+
+  nsRefPtr<LockedFile> lockedFile =
+    LockedFile::Create(this, LockedFile::READ_ONLY, LockedFile::PARALLEL);
+  NS_ENSURE_TRUE(lockedFile, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  nsRefPtr<FileRequest> fileRequest =
+    FileRequest::Create(GetOwner(), lockedFile);
+
+  nsRefPtr<MetadataParameters> params = new MetadataParameters();
+  params->Init(true, false);
+
+  nsRefPtr<GetFileHelper> helper =
+    new GetFileHelper(lockedFile, fileRequest, params, this);
+
+  nsresult rv = helper->Enqueue();
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  fileRequest.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(PRInt64)
+FileHandle::GetFileId()
+{
+  return -1;
+}
+
+NS_IMETHODIMP_(mozilla::dom::indexedDB::FileInfo*)
+FileHandle::GetFileInfo()
+{
+  return nsnull;
+}
+
+nsresult
+GetFileHelper::GetSuccessResult(JSContext* aCx, jsval* aVal)
+{
+  nsCOMPtr<nsIDOMFile> domFile =
+    mFileHandle->CreateFileObject(mLockedFile, mParams->Size());
+
+  nsresult rv =
+    nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), domFile,
+                               &NS_GET_IID(nsIDOMFile), aVal);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/file/FileHandle.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_file_filehandle_h__
+#define mozilla_dom_file_filehandle_h__
+
+#include "FileCommon.h"
+
+#include "nsIDOMFileHandle.h"
+#include "nsIFile.h"
+
+#include "nsDOMEventTargetHelper.h"
+
+class nsIDOMFile;
+class nsIFileStorage;
+
+BEGIN_FILE_NAMESPACE
+
+class FileService;
+class LockedFile;
+class FinishHelper;
+class FileHelper;
+
+/**
+ * Must be subclassed. The subclass must implement CreateStream and
+ * CreateFileObject. Basically, every file storage implementation provides its
+ * own FileHandle implementation (for example IDBFileHandle provides IndexedDB
+ * specific implementation).
+ */
+class FileHandle : public nsDOMEventTargetHelper,
+                   public nsIDOMFileHandle
+{
+  friend class FileService;
+  friend class LockedFile;
+  friend class FinishHelper;
+  friend class FileHelper;
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIDOMFILEHANDLE
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileHandle, nsDOMEventTargetHelper)
+
+  const nsAString&
+  Name() const
+  {
+    return mName;
+  }
+
+  const nsAString&
+  Type() const
+  {
+    return mType;
+  }
+
+  virtual already_AddRefed<nsISupports>
+  CreateStream(nsIFile* aFile, bool aReadOnly) = 0;
+
+  virtual already_AddRefed<nsIDOMFile>
+  CreateFileObject(LockedFile* aLockedFile, PRUint32 aFileSize) = 0;
+
+protected:
+  FileHandle()
+  { }
+
+  ~FileHandle()
+  { }
+
+  nsCOMPtr<nsIFileStorage> mFileStorage;
+
+  nsString mName;
+  nsString mType;
+
+  nsCOMPtr<nsIFile> mFile;
+  nsString mFileName;
+
+  NS_DECL_EVENT_HANDLER(abort)
+  NS_DECL_EVENT_HANDLER(error)
+};
+
+END_FILE_NAMESPACE
+
+#endif // mozilla_dom_file_filehandle_h__
new file mode 100644
--- /dev/null
+++ b/dom/file/FileHelper.cpp
@@ -0,0 +1,209 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "FileHelper.h"
+
+#include "nsIFileStorage.h"
+
+#include "nsNetError.h"
+#include "nsProxyRelease.h"
+
+#include "FileHandle.h"
+#include "FileRequest.h"
+#include "FileService.h"
+
+USING_FILE_NAMESPACE
+
+namespace {
+
+LockedFile* gCurrentLockedFile = nsnull;
+
+} // anonymous namespace
+
+FileHelper::FileHelper(LockedFile* aLockedFile,
+                       FileRequest* aFileRequest)
+: mFileStorage(aLockedFile->mFileHandle->mFileStorage),
+  mLockedFile(aLockedFile),
+  mFileRequest(aFileRequest),
+  mResultCode(NS_OK),
+  mFinished(false)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+}
+
+FileHelper::~FileHelper()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(FileHelper, nsIRequestObserver)
+
+nsresult
+FileHelper::Enqueue()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  FileService* service = FileService::GetOrCreate();
+  NS_ENSURE_TRUE(service, NS_ERROR_FAILURE);
+
+  nsresult rv = service->Enqueue(mLockedFile, this);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (mLockedFile) {
+    mLockedFile->OnNewRequest();
+  }
+
+  return NS_OK;
+}
+
+nsresult
+FileHelper::AsyncRun(FileHelperListener* aListener)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // Assign the listener early, so we can use it synchronously if the code
+  // below fails.
+  mListener = aListener;
+
+  nsresult rv;
+
+  nsCOMPtr<nsISupports> stream;
+  if (mLockedFile->mRequestMode == LockedFile::PARALLEL) {
+    rv = mLockedFile->CreateParallelStream(getter_AddRefs(stream));
+  }
+  else {
+    rv = mLockedFile->GetOrCreateStream(getter_AddRefs(stream));
+  }
+
+  if (NS_SUCCEEDED(rv)) {
+    NS_ASSERTION(stream, "This should never be null!");
+
+    rv = DoAsyncRun(stream);
+  }
+
+  if (NS_FAILED(rv)) {
+    mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
+    Finish();
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileHelper::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileHelper::OnStopRequest(nsIRequest* aRequest, nsISupports* aCtxt,
+                          nsresult aStatus)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (NS_FAILED(aStatus)) {
+    mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
+  }
+
+  Finish();
+
+  return NS_OK;
+}
+
+void
+FileHelper::OnStreamProgress(PRUint64 aProgress, PRUint64 aProgressMax)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (mLockedFile->IsAborted()) {
+    NS_ASSERTION(mRequest, "Should have a request!\n");
+
+    nsresult rv = mRequest->Cancel(NS_BINDING_ABORTED);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Failed to cancel the request!");
+    }
+
+    return;
+  }
+
+  if (mFileRequest) {
+    mFileRequest->OnProgress(aProgress, aProgressMax);
+  }
+}
+
+// static
+LockedFile*
+FileHelper::GetCurrentLockedFile()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  return gCurrentLockedFile;
+}
+
+nsresult
+FileHelper::GetSuccessResult(JSContext* aCx,
+                             jsval* aVal)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  *aVal = JSVAL_VOID;
+  return NS_OK;
+}
+
+void
+FileHelper::ReleaseObjects()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  mFileStorage = nsnull;
+  mLockedFile = nsnull;
+  mFileRequest = nsnull;
+  mListener = nsnull;
+  mRequest = nsnull;
+}
+
+void
+FileHelper::Finish()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (mFinished) {
+    return;
+  }
+
+  mFinished = true;
+
+  if (mLockedFile->IsAborted()) {
+    // Always fire a "error" event with ABORT_ERR if the transaction was
+    // aborted, even if the request succeeded or failed with another error.
+    mResultCode = NS_ERROR_DOM_FILEHANDLE_ABORT_ERR;
+  }
+
+  LockedFile* oldLockedFile = gCurrentLockedFile;
+  gCurrentLockedFile = mLockedFile;
+
+  if (mFileRequest) {
+    nsresult rv = mFileRequest->NotifyHelperCompleted(this);
+    if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) {
+      mResultCode = rv;
+    }
+  }
+
+  NS_ASSERTION(gCurrentLockedFile == mLockedFile, "Should be unchanged!");
+  gCurrentLockedFile = oldLockedFile;
+
+  mLockedFile->OnRequestFinished();
+
+  mListener->OnFileHelperComplete(this);
+
+  ReleaseObjects();
+
+  NS_ASSERTION(!(mFileStorage || mLockedFile || mFileRequest || mListener ||
+                 mRequest), "Subclass didn't call FileHelper::ReleaseObjects!");
+
+}
new file mode 100644
--- /dev/null
+++ b/dom/file/FileHelper.h
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_file_filehelper_h__
+#define mozilla_dom_file_filehelper_h__
+
+#include "FileCommon.h"
+
+#include "nsIRequestObserver.h"
+
+class nsIFileStorage;
+
+BEGIN_FILE_NAMESPACE
+
+class FileHelper;
+class FileRequest;
+class FileOutputStreamWrapper;
+class LockedFile;
+
+class FileHelperListener
+{
+public:
+  NS_IMETHOD_(nsrefcnt)
+  AddRef() = 0;
+
+  NS_IMETHOD_(nsrefcnt)
+  Release() = 0;
+
+  virtual void
+  OnFileHelperComplete(FileHelper* aFileHelper) = 0;
+};
+
+/**
+ * Must be subclassed. The subclass must implement DoAsyncRun. It may then
+ * choose to implement GetSuccessResult to properly set the result of the
+ * success event. Call Enqueue to start the file operation.
+ */
+class FileHelper : public nsIRequestObserver
+{
+  friend class FileRequest;
+  friend class FileOutputStreamWrapper;
+
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIREQUESTOBSERVER
+
+  nsresult
+  Enqueue();
+
+  nsresult
+  AsyncRun(FileHelperListener* aListener);
+
+  void
+  OnStreamProgress(PRUint64 aProgress, PRUint64 aProgressMax);
+
+  void
+  OnStreamClose()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    Finish();
+  }
+
+  void
+  OnStreamDestroy()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    Finish();
+  }
+
+  static LockedFile*
+  GetCurrentLockedFile();
+
+protected:
+  FileHelper(LockedFile* aLockedFile, FileRequest* aRequest);
+
+  virtual ~FileHelper();
+
+  virtual nsresult
+  DoAsyncRun(nsISupports* aStream) = 0;
+
+  virtual nsresult
+  GetSuccessResult(JSContext* aCx, jsval* aVal);
+
+  virtual void
+  ReleaseObjects();
+
+  void
+  Finish();
+
+  nsCOMPtr<nsIFileStorage> mFileStorage;
+  nsRefPtr<LockedFile> mLockedFile;
+  nsRefPtr<FileRequest> mFileRequest;
+
+  nsRefPtr<FileHelperListener> mListener;
+  nsCOMPtr<nsIRequest> mRequest;
+
+private:
+  nsresult mResultCode;
+  bool mFinished;
+};
+
+END_FILE_NAMESPACE
+
+#endif // mozilla_dom_file_filehelper_h__
new file mode 100644
--- /dev/null
+++ b/dom/file/FileRequest.cpp
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "FileRequest.h"
+
+#include "nsIJSContextStack.h"
+#include "nsIPrivateDOMEvent.h"
+
+#include "nsContentUtils.h"
+#include "nsEventDispatcher.h"
+#include "nsDOMProgressEvent.h"
+
+#include "FileHelper.h"
+#include "LockedFile.h"
+
+USING_FILE_NAMESPACE
+
+FileRequest::FileRequest(nsIDOMWindow* aWindow)
+: DOMRequest(aWindow)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+}
+
+FileRequest::~FileRequest()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+}
+
+// static
+already_AddRefed<FileRequest>
+FileRequest::Create(nsIDOMWindow* aOwner,
+                    LockedFile* aLockedFile)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsRefPtr<FileRequest> request = new FileRequest(aOwner);
+  request->mLockedFile = aLockedFile;
+
+  return request.forget();
+}
+
+nsresult
+FileRequest::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  aVisitor.mCanHandle = true;
+  aVisitor.mParentTarget = mLockedFile;
+  return NS_OK;
+}
+
+nsresult
+FileRequest::NotifyHelperCompleted(FileHelper* aFileHelper)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsresult rv = aFileHelper->mResultCode;
+
+  // If the request failed then fire error event and return.
+  if (NS_FAILED(rv)) {
+    FireError(rv);
+    return NS_OK;
+  }
+
+  // Otherwise we need to get the result from the helper.
+  jsval result;
+
+  nsIScriptContext* sc = GetContextForEventHandlers(&rv);
+  NS_ENSURE_STATE(sc);
+
+  JSContext* cx = sc->GetNativeContext();
+  NS_ASSERTION(cx, "Failed to get a context!");
+
+  JSObject* global = sc->GetNativeGlobal();
+  NS_ASSERTION(global, "Failed to get global object!");
+
+  JSAutoRequest ar(cx);
+  JSAutoEnterCompartment ac;
+  if (ac.enter(cx, global)) {
+    rv = aFileHelper->GetSuccessResult(cx, &result);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("GetSuccessResult failed!");
+    }
+  }
+  else {
+    NS_WARNING("Failed to enter correct compartment!");
+    rv = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
+  }
+
+  if (NS_SUCCEEDED(rv)) {
+    FireSuccess(result);
+  }
+  else {
+    FireError(rv);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileRequest::GetLockedFile(nsIDOMLockedFile** aLockedFile)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsCOMPtr<nsIDOMLockedFile> lockedFile(mLockedFile);
+  lockedFile.forget(aLockedFile);
+  return NS_OK;
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(FileRequest)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FileRequest, DOMRequest)
+  // Don't need NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS because
+  // nsDOMEventTargetHelper does it for us.
+  NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(progress)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mLockedFile,
+                                                       nsIDOMLockedFile)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileRequest, DOMRequest)
+  NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(progress)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mLockedFile)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileRequest)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMFileRequest)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(FileRequest)
+NS_INTERFACE_MAP_END_INHERITING(DOMRequest)
+
+NS_IMPL_ADDREF_INHERITED(FileRequest, DOMRequest)
+NS_IMPL_RELEASE_INHERITED(FileRequest, DOMRequest)
+
+DOMCI_DATA(FileRequest, FileRequest)
+
+NS_IMPL_EVENT_HANDLER(FileRequest, progress);
+
+void
+FileRequest::FireProgressEvent(PRUint64 aLoaded, PRUint64 aTotal)
+{
+  if (NS_FAILED(CheckInnerWindowCorrectness())) {
+    return;
+  }
+
+  nsRefPtr<nsDOMProgressEvent> event = new nsDOMProgressEvent(nsnull, nsnull);
+  nsresult rv = event->InitProgressEvent(NS_LITERAL_STRING("progress"),
+                                         false, false, false, aLoaded, aTotal);
+  NS_ENSURE_SUCCESS(rv,);
+
+  rv = event->SetTrusted(true);
+  NS_ENSURE_SUCCESS(rv,);
+
+  bool dummy;
+  rv = DispatchEvent(static_cast<nsIDOMProgressEvent*>(event), &dummy);
+  NS_ENSURE_SUCCESS(rv,);
+}
+
+void
+FileRequest::RootResultVal()
+{
+  NS_ASSERTION(!mRooted, "Don't call me if already rooted!");
+  nsXPCOMCycleCollectionParticipant *participant;
+  CallQueryInterface(this, &participant);
+  nsContentUtils::HoldJSObjects(NS_CYCLE_COLLECTION_UPCAST(this, DOMRequest),
+                                participant);
+  mRooted = true;
+}
new file mode 100644
--- /dev/null
+++ b/dom/file/FileRequest.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_file_filerequest_h__
+#define mozilla_dom_file_filerequest_h__
+
+#include "FileCommon.h"
+
+#include "nsIDOMFileRequest.h"
+
+#include "DOMRequest.h"
+
+BEGIN_FILE_NAMESPACE
+
+class FileHelper;
+class LockedFile;
+
+class FileRequest : public mozilla::dom::DOMRequest,
+                    public nsIDOMFileRequest
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIDOMFILEREQUEST
+  NS_FORWARD_NSIDOMDOMREQUEST(DOMRequest::)
+  NS_FORWARD_NSIDOMEVENTTARGET_NOPREHANDLEEVENT(DOMRequest::)
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileRequest, DOMRequest)
+
+  static already_AddRefed<FileRequest>
+  Create(nsIDOMWindow* aOwner, LockedFile* aLockedFile);
+
+  // nsIDOMEventTarget
+  virtual nsresult
+  PreHandleEvent(nsEventChainPreVisitor& aVisitor);
+
+  void
+  OnProgress(PRUint64 aProgress, PRUint64 aProgressMax)
+  {
+    FireProgressEvent(aProgress, aProgressMax);
+  }
+
+  nsresult
+  NotifyHelperCompleted(FileHelper* aFileHelper);
+
+private:
+  FileRequest(nsIDOMWindow* aWindow);
+  ~FileRequest();
+
+  void
+  FireProgressEvent(PRUint64 aLoaded, PRUint64 aTotal);
+
+  virtual void
+  RootResultVal();
+
+  nsRefPtr<LockedFile> mLockedFile;
+
+  NS_DECL_EVENT_HANDLER(progress)
+};
+
+END_FILE_NAMESPACE
+
+#endif // mozilla_dom_file_filerequest_h__
new file mode 100644
--- /dev/null
+++ b/dom/file/FileService.cpp
@@ -0,0 +1,528 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "FileService.h"
+
+#include "nsIFile.h"
+#include "nsIFileStorage.h"
+#include "nsIObserverService.h"
+#include "nsIStreamTransportService.h"
+
+#include "nsNetCID.h"
+
+#include "FileHandle.h"
+#include "FileRequest.h"
+
+USING_FILE_NAMESPACE
+
+namespace {
+
+FileService* gInstance = nsnull;
+bool gShutdown = false;
+
+} // anonymous namespace
+
+FileService::FileService()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!gInstance, "More than one instance!");
+}
+
+FileService::~FileService()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!gInstance, "More than one instance!");
+}
+
+nsresult
+FileService::Init()
+{
+  mFileStorageInfos.Init();
+
+  nsresult rv;
+
+  mStreamTransportTarget =
+    do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIStreamTransportService> sts =
+    do_QueryInterface(mStreamTransportTarget);
+
+  rv = sts->RaiseThreadLimit();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+FileService::Cleanup()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsIThread* thread = NS_GetCurrentThread();
+  while (mFileStorageInfos.Count()) {
+    if (!NS_ProcessNextEvent(thread)) {
+      NS_ERROR("Failed to process next event!");
+      break;
+    }
+  }
+
+  // Make sure the service is still accessible while any generated callbacks
+  // are processed.
+  nsresult rv = NS_ProcessPendingEvents(thread);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!mCompleteCallbacks.IsEmpty()) {
+    // Run all callbacks manually now.
+    for (PRUint32 index = 0; index < mCompleteCallbacks.Length(); index++) {
+      mCompleteCallbacks[index].mCallback->Run();
+    }
+    mCompleteCallbacks.Clear();
+
+    // And make sure they get processed.
+    rv = NS_ProcessPendingEvents(thread);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
+
+// static
+FileService*
+FileService::GetOrCreate()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (gShutdown) {
+    NS_WARNING("Calling GetOrCreate() after shutdown!");
+    return nsnull;
+  }
+
+  if (!gInstance) {
+    nsRefPtr<FileService> service(new FileService);
+
+    nsresult rv = service->Init();
+    NS_ENSURE_SUCCESS(rv, nsnull);
+
+    nsCOMPtr<nsIObserverService> obs =
+      do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, nsnull);
+
+    rv = obs->AddObserver(service, "profile-before-change", false);
+    NS_ENSURE_SUCCESS(rv, nsnull);
+
+    // The observer service now owns us.
+    gInstance = service;
+  }
+
+  return gInstance;
+}
+
+// static
+FileService*
+FileService::Get()
+{
+  // Does not return an owning reference.
+  return gInstance;
+}
+
+// static
+void
+FileService::Shutdown()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  gShutdown = true;
+
+  if (gInstance) {
+    if (NS_FAILED(gInstance->Cleanup())) {
+      NS_WARNING("Failed to shutdown file service!");
+    }
+    gInstance = nsnull;
+  }
+}
+
+// static
+bool
+FileService::IsShuttingDown()
+{
+  return gShutdown;
+}
+
+nsresult
+FileService::Enqueue(LockedFile* aLockedFile, FileHelper* aFileHelper)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aLockedFile, "Null pointer!");
+
+  FileHandle* fileHandle = aLockedFile->mFileHandle;
+
+  if (fileHandle->mFileStorage->IsStorageInvalidated()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  nsISupports* storageId = fileHandle->mFileStorage->StorageId();
+  const nsAString& fileName = fileHandle->mFileName;
+  bool modeIsWrite = aLockedFile->mMode == LockedFile::READ_WRITE;
+
+  FileStorageInfo* fileStorageInfo;
+  if (!mFileStorageInfos.Get(storageId, &fileStorageInfo)) {
+    nsAutoPtr<FileStorageInfo> newFileStorageInfo(new FileStorageInfo());
+
+    mFileStorageInfos.Put(storageId, newFileStorageInfo);
+
+    fileStorageInfo = newFileStorageInfo.forget();
+  }
+
+  LockedFileQueue* existingLockedFileQueue =
+    fileStorageInfo->GetLockedFileQueue(aLockedFile);
+
+  if (existingLockedFileQueue) {
+    existingLockedFileQueue->Enqueue(aFileHelper);
+    return NS_OK;
+  }
+
+  bool lockedForReading = fileStorageInfo->IsFileLockedForReading(fileName);
+  bool lockedForWriting = fileStorageInfo->IsFileLockedForWriting(fileName);
+
+  if (modeIsWrite) {
+    if (!lockedForWriting) {
+      fileStorageInfo->LockFileForWriting(fileName);
+    }
+  }
+  else {
+    if (!lockedForReading) {
+      fileStorageInfo->LockFileForReading(fileName);
+    }
+  }
+
+  if (lockedForWriting || (lockedForReading && modeIsWrite)) {
+    fileStorageInfo->CreateDelayedEnqueueInfo(aLockedFile, aFileHelper);
+  }
+  else {
+    LockedFileQueue* lockedFileQueue =
+      fileStorageInfo->CreateLockedFileQueue(aLockedFile);
+
+    if (aFileHelper) {
+      // Enqueue() will queue the file helper if there's already something
+      // running. That can't fail, so no need to eventually remove
+      // fileStorageInfo from the hash table.
+      //
+      // If the file helper is free to run then AsyncRun() is called on the
+      // file helper. AsyncRun() is responsible for calling all necessary
+      // callbacks when something fails. We're propagating the error here,
+      // however there's no need to eventually remove fileStorageInfo from
+      // the hash table. Code behind AsyncRun() will take care of it. The last
+      // item in the code path is NotifyLockedFileCompleted() which removes
+      // fileStorageInfo from the hash table if there are no locked files for
+      // the file storage.
+      nsresult rv = lockedFileQueue->Enqueue(aFileHelper);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  }
+
+  return NS_OK;
+}
+
+void
+FileService::NotifyLockedFileCompleted(LockedFile* aLockedFile)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aLockedFile, "Null pointer!");
+
+  FileHandle* fileHandle = aLockedFile->mFileHandle;
+  nsISupports* storageId = fileHandle->mFileStorage->StorageId();
+
+  FileStorageInfo* fileStorageInfo;
+  if (!mFileStorageInfos.Get(storageId, &fileStorageInfo)) {
+    NS_ERROR("We don't know anyting about this locked file?!");
+    return;
+  }
+
+  fileStorageInfo->RemoveLockedFileQueue(aLockedFile);
+
+  if (!fileStorageInfo->HasRunningLockedFiles()) {
+    mFileStorageInfos.Remove(storageId);
+
+#ifdef DEBUG
+    storageId = nsnull;
+#endif
+
+     // See if we need to fire any complete callbacks.
+    PRUint32 index = 0;
+    while (index < mCompleteCallbacks.Length()) {
+      if (MaybeFireCallback(mCompleteCallbacks[index])) {
+        mCompleteCallbacks.RemoveElementAt(index);
+      }
+      else {
+        index++;
+      }
+    }
+  }
+}
+
+bool
+FileService::WaitForAllStoragesToComplete(
+                                nsTArray<nsCOMPtr<nsIFileStorage> >& aStorages,
+                                nsIRunnable* aCallback)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!aStorages.IsEmpty(), "No databases to wait on!");
+  NS_ASSERTION(aCallback, "Null pointer!");
+
+  StoragesCompleteCallback* callback = mCompleteCallbacks.AppendElement();
+  callback->mCallback = aCallback;
+  callback->mStorages.SwapElements(aStorages);
+
+  if (MaybeFireCallback(*callback)) {
+    mCompleteCallbacks.RemoveElementAt(mCompleteCallbacks.Length() - 1);
+  }
+
+  return true;
+}
+
+void
+FileService::AbortLockedFilesForStorage(nsIFileStorage* aFileStorage)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aFileStorage, "Null pointer!");
+
+  FileStorageInfo* fileStorageInfo;
+  if (!mFileStorageInfos.Get(aFileStorage->StorageId(), &fileStorageInfo)) {
+    return;
+  }
+
+  nsAutoTArray<nsRefPtr<LockedFile>, 10> lockedFiles;
+  fileStorageInfo->CollectRunningAndDelayedLockedFiles(aFileStorage,
+                                                       lockedFiles);
+
+  for (PRUint32 index = 0; index < lockedFiles.Length(); index++) {
+    lockedFiles[index]->Abort();
+  }
+}
+
+bool
+FileService::HasLockedFilesForStorage(nsIFileStorage* aFileStorage)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aFileStorage, "Null pointer!");
+
+  FileStorageInfo* fileStorageInfo;
+  if (!mFileStorageInfos.Get(aFileStorage->StorageId(), &fileStorageInfo)) {
+    return false;
+  }
+
+  return fileStorageInfo->HasRunningLockedFiles(aFileStorage);
+}
+
+NS_IMPL_ISUPPORTS1(FileService, nsIObserver)
+
+NS_IMETHODIMP
+FileService::Observe(nsISupports* aSubject, const char*  aTopic,
+                     const PRUnichar* aData)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!strcmp(aTopic, "profile-before-change"), "Wrong topic!");
+
+  Shutdown();
+
+  return NS_OK;
+}
+
+bool
+FileService::MaybeFireCallback(StoragesCompleteCallback& aCallback)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  for (PRUint32 index = 0; index < aCallback.mStorages.Length(); index++) {
+    if (mFileStorageInfos.Get(aCallback.mStorages[index]->StorageId(),
+                              nsnull)) {
+      return false;
+    }
+  }
+
+  aCallback.mCallback->Run();
+  return true;
+}
+
+FileService::LockedFileQueue::LockedFileQueue(LockedFile* aLockedFile)
+: mLockedFile(aLockedFile)
+{
+  NS_ASSERTION(aLockedFile, "Null pointer!");
+}
+
+NS_IMPL_THREADSAFE_ADDREF(FileService::LockedFileQueue)
+NS_IMPL_THREADSAFE_RELEASE(FileService::LockedFileQueue)
+
+nsresult
+FileService::LockedFileQueue::Enqueue(FileHelper* aFileHelper)
+{
+  mQueue.AppendElement(aFileHelper);
+
+  nsresult rv;
+  if (mLockedFile->mRequestMode == LockedFile::PARALLEL) {
+    rv = aFileHelper->AsyncRun(this);
+  }
+  else {
+    rv = ProcessQueue();
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+void
+FileService::LockedFileQueue::OnFileHelperComplete(FileHelper* aFileHelper)
+{
+  if (mLockedFile->mRequestMode == LockedFile::PARALLEL) {
+    PRInt32 index = mQueue.IndexOf(aFileHelper);
+    NS_ASSERTION(index != -1, "We don't know anything about this helper!");
+
+    mQueue.RemoveElementAt(index);
+  }
+  else {
+    NS_ASSERTION(mCurrentHelper == aFileHelper, "How can this happen?!");
+
+    mCurrentHelper = nsnull;
+
+    nsresult rv = ProcessQueue();
+    NS_ENSURE_SUCCESS(rv,);
+  }
+}
+
+nsresult
+FileService::LockedFileQueue::ProcessQueue()
+{
+  if (mQueue.IsEmpty() || mCurrentHelper) {
+    return NS_OK;
+  }
+
+  mCurrentHelper = mQueue[0];
+  mQueue.RemoveElementAt(0);
+
+  nsresult rv = mCurrentHelper->AsyncRun(this);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+FileService::LockedFileQueue*
+FileService::FileStorageInfo::CreateLockedFileQueue(LockedFile* aLockedFile)
+{
+  nsRefPtr<LockedFileQueue>* lockedFileQueue =
+    mLockedFileQueues.AppendElement();
+  *lockedFileQueue = new LockedFileQueue(aLockedFile);
+  return lockedFileQueue->get();
+}
+
+FileService::LockedFileQueue*
+FileService::FileStorageInfo::GetLockedFileQueue(LockedFile* aLockedFile)
+{
+  PRUint32 count = mLockedFileQueues.Length();
+  for (PRUint32 index = 0; index < count; index++) {
+    nsRefPtr<LockedFileQueue>& lockedFileQueue = mLockedFileQueues[index];
+    if (lockedFileQueue->mLockedFile == aLockedFile) {
+      return lockedFileQueue;
+    }
+  }
+  return nsnull;
+}
+
+void
+FileService::FileStorageInfo::RemoveLockedFileQueue(LockedFile* aLockedFile)
+{
+  PRUint32 lockedFileCount = mLockedFileQueues.Length();
+
+  // We can't just remove entries from lock hash tables, we have to rebuild
+  // them instead. Multiple LockedFile objects may lock the same file
+  // (one entry can represent multiple locks).
+
+  mFilesReading.Clear();
+  mFilesWriting.Clear();
+
+  for (PRUint32 index = 0, count = lockedFileCount; index < count; index++) {
+    LockedFile* lockedFile = mLockedFileQueues[index]->mLockedFile;
+    if (lockedFile == aLockedFile) {
+      NS_ASSERTION(count == lockedFileCount, "More than one match?!");
+
+      mLockedFileQueues.RemoveElementAt(index);
+      index--;
+      count--;
+
+      continue;
+    }
+
+    const nsAString& fileName = lockedFile->mFileHandle->mFileName;
+
+    if (lockedFile->mMode == LockedFile::READ_WRITE) {
+      if (!IsFileLockedForWriting(fileName)) {
+        LockFileForWriting(fileName);
+      }
+    }
+    else {
+      if (!IsFileLockedForReading(fileName)) {
+        LockFileForReading(fileName);
+      }
+    }
+  }
+
+  NS_ASSERTION(mLockedFileQueues.Length() == lockedFileCount - 1,
+               "Didn't find the locked file we were looking for!");
+
+  nsTArray<DelayedEnqueueInfo> delayedEnqueueInfos;
+  delayedEnqueueInfos.SwapElements(mDelayedEnqueueInfos);
+
+  for (PRUint32 index = 0; index < delayedEnqueueInfos.Length(); index++) {
+    DelayedEnqueueInfo& delayedEnqueueInfo = delayedEnqueueInfos[index];
+    if (NS_FAILED(gInstance->Enqueue(delayedEnqueueInfo.mLockedFile,
+                                     delayedEnqueueInfo.mFileHelper))) {
+      NS_WARNING("Enqueue failed!");
+    }
+  }
+}
+
+bool
+FileService::FileStorageInfo::HasRunningLockedFiles(
+                                                  nsIFileStorage* aFileStorage)
+{
+  for (PRUint32 index = 0; index < mLockedFileQueues.Length(); index++) {
+    LockedFile* lockedFile = mLockedFileQueues[index]->mLockedFile;
+    if (lockedFile->mFileHandle->mFileStorage == aFileStorage) {
+      return true;
+    }
+  }
+  return false;
+}
+
+FileService::DelayedEnqueueInfo*
+FileService::FileStorageInfo::CreateDelayedEnqueueInfo(LockedFile* aLockedFile,
+                                                       FileHelper* aFileHelper)
+{
+  DelayedEnqueueInfo* info = mDelayedEnqueueInfos.AppendElement();
+  info->mLockedFile = aLockedFile;
+  info->mFileHelper = aFileHelper;
+  return info;
+}
+
+void
+FileService::FileStorageInfo::CollectRunningAndDelayedLockedFiles(
+                                 nsIFileStorage* aFileStorage,
+                                 nsTArray<nsRefPtr<LockedFile> >& aLockedFiles)
+{
+  for (PRUint32 index = 0; index < mLockedFileQueues.Length(); index++) {
+    LockedFile* lockedFile = mLockedFileQueues[index]->mLockedFile;
+    if (lockedFile->mFileHandle->mFileStorage == aFileStorage) {
+      aLockedFiles.AppendElement(lockedFile);
+    }
+  }
+
+  for (PRUint32 index = 0; index < mDelayedEnqueueInfos.Length(); index++) {
+    LockedFile* lockedFile = mDelayedEnqueueInfos[index].mLockedFile;
+    if (lockedFile->mFileHandle->mFileStorage == aFileStorage) {
+      aLockedFiles.AppendElement(lockedFile);
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/file/FileService.h
@@ -0,0 +1,196 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_file_fileservice_h__
+#define mozilla_dom_file_fileservice_h__
+
+#include "FileCommon.h"
+
+#include "nsIObserver.h"
+
+#include "nsClassHashtable.h"
+
+#include "mozilla/dom/file/FileHelper.h"
+#include "mozilla/dom/file/LockedFile.h"
+
+BEGIN_FILE_NAMESPACE
+
+class FileService : public nsIObserver
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  // Returns a non-owning reference!
+  static FileService*
+  GetOrCreate();
+
+  // Returns a non-owning reference!
+  static FileService*
+  Get();
+
+  static void
+  Shutdown();
+
+  // Returns true if we've begun the shutdown process.
+  static bool
+  IsShuttingDown();
+
+  nsresult
+  Enqueue(LockedFile* aLockedFile, FileHelper* aFileHelper);
+
+  void
+  NotifyLockedFileCompleted(LockedFile* aLockedFile);
+
+  bool
+  WaitForAllStoragesToComplete(nsTArray<nsCOMPtr<nsIFileStorage> >& aStorages,
+                               nsIRunnable* aCallback);
+
+  void
+  AbortLockedFilesForStorage(nsIFileStorage* aFileStorage);
+
+  bool
+  HasLockedFilesForStorage(nsIFileStorage* aFileStorage);
+
+  nsIEventTarget*
+  StreamTransportTarget()
+  {
+    NS_ASSERTION(mStreamTransportTarget, "This should never be null!");
+    return mStreamTransportTarget;
+  }
+
+private:
+  class LockedFileQueue : public FileHelperListener
+  {
+    friend class FileService;
+
+  public:
+    NS_IMETHOD_(nsrefcnt)
+    AddRef();
+
+    NS_IMETHOD_(nsrefcnt)
+    Release();
+
+    inline nsresult
+    Enqueue(FileHelper* aFileHelper);
+
+    virtual void
+    OnFileHelperComplete(FileHelper* aFileHelper);
+
+  private:
+    inline
+    LockedFileQueue(LockedFile* aLockedFile);
+
+    nsresult
+    ProcessQueue();
+
+    nsAutoRefCnt mRefCnt;
+    NS_DECL_OWNINGTHREAD
+    nsRefPtr<LockedFile> mLockedFile;
+    nsTArray<nsRefPtr<FileHelper> > mQueue;
+    nsRefPtr<FileHelper> mCurrentHelper;
+  };
+
+  struct DelayedEnqueueInfo
+  {
+    nsRefPtr<LockedFile> mLockedFile;
+    nsRefPtr<FileHelper> mFileHelper;
+  };
+
+  class FileStorageInfo
+  {
+    friend class FileService;
+
+  public:
+    inline LockedFileQueue*
+    CreateLockedFileQueue(LockedFile* aLockedFile);
+
+    inline LockedFileQueue*
+    GetLockedFileQueue(LockedFile* aLockedFile);
+
+    void
+    RemoveLockedFileQueue(LockedFile* aLockedFile);
+
+    bool
+    HasRunningLockedFiles()
+    {
+      return !mLockedFileQueues.IsEmpty();
+    }
+
+    inline bool
+    HasRunningLockedFiles(nsIFileStorage* aFileStorage);
+
+    inline DelayedEnqueueInfo*
+    CreateDelayedEnqueueInfo(LockedFile* aLockedFile, FileHelper* aFileHelper);
+
+    inline void
+    CollectRunningAndDelayedLockedFiles(
+                                nsIFileStorage* aFileStorage,
+                                nsTArray<nsRefPtr<LockedFile> >& aLockedFiles);
+
+    void
+    LockFileForReading(const nsAString& aFileName)
+    {
+      mFilesReading.PutEntry(aFileName);
+    }
+
+    void
+    LockFileForWriting(const nsAString& aFileName)
+    {
+      mFilesWriting.PutEntry(aFileName);
+    }
+
+    bool
+    IsFileLockedForReading(const nsAString& aFileName)
+    {
+      return mFilesReading.Contains(aFileName);
+    }
+
+    bool
+    IsFileLockedForWriting(const nsAString& aFileName)
+    {
+      return mFilesWriting.Contains(aFileName);
+    }
+
+  private:
+    FileStorageInfo()
+    {
+      mFilesReading.Init();
+      mFilesWriting.Init();
+    }
+
+    nsTArray<nsRefPtr<LockedFileQueue> > mLockedFileQueues;
+    nsTArray<DelayedEnqueueInfo> mDelayedEnqueueInfos;
+    nsTHashtable<nsStringHashKey> mFilesReading;
+    nsTHashtable<nsStringHashKey> mFilesWriting;
+  };
+
+  struct StoragesCompleteCallback
+  {
+    nsTArray<nsCOMPtr<nsIFileStorage> > mStorages;
+    nsCOMPtr<nsIRunnable> mCallback;
+  };
+
+  FileService();
+  ~FileService();
+
+  nsresult
+  Init();
+
+  nsresult
+  Cleanup();
+
+  bool
+  MaybeFireCallback(StoragesCompleteCallback& aCallback);
+
+  nsCOMPtr<nsIEventTarget> mStreamTransportTarget;
+  nsClassHashtable<nsISupportsHashKey, FileStorageInfo> mFileStorageInfos;
+  nsTArray<StoragesCompleteCallback> mCompleteCallbacks;
+};
+
+END_FILE_NAMESPACE
+
+#endif /* mozilla_dom_file_fileservice_h__ */
new file mode 100644
--- /dev/null
+++ b/dom/file/FileStreamWrappers.cpp
@@ -0,0 +1,397 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "FileStreamWrappers.h"
+
+#include "nsIFileStorage.h"
+#include "nsISeekableStream.h"
+#include "nsIStandardFileStream.h"
+
+#include "FileHelper.h"
+
+USING_FILE_NAMESPACE
+
+namespace {
+
+class ProgressRunnable : public nsIRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIRUNNABLE
+
+  ProgressRunnable(FileHelper* aFileHelper,
+                   PRUint64 aProgress,
+                   PRUint64 aProgressMax)
+  : mFileHelper(aFileHelper),
+    mProgress(aProgress),
+    mProgressMax(aProgressMax)
+  {
+  }
+
+private:
+  nsRefPtr<FileHelper> mFileHelper;
+  PRUint64 mProgress;
+  PRUint64 mProgressMax;
+};
+
+class CloseRunnable : public nsIRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIRUNNABLE
+
+  CloseRunnable(FileHelper* aFileHelper)
+  : mFileHelper(aFileHelper)
+  { }
+
+private:
+  nsRefPtr<FileHelper> mFileHelper;
+};
+
+class DestroyRunnable : public nsIRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIRUNNABLE
+
+  DestroyRunnable(FileHelper* aFileHelper)
+  : mFileHelper(aFileHelper)
+  { }
+
+private:
+  nsRefPtr<FileHelper> mFileHelper;
+};
+
+} // anonymous namespace
+
+FileStreamWrapper::FileStreamWrapper(nsISupports* aFileStream,
+                                     FileHelper* aFileHelper,
+                                     PRUint64 aOffset,
+                                     PRUint64 aLimit,
+                                     PRUint32 aFlags)
+: mFileStream(aFileStream),
+  mFileHelper(aFileHelper),
+  mOffset(aOffset),
+  mLimit(aLimit),
+  mFlags(aFlags),
+  mFirstTime(true)
+{
+  NS_ASSERTION(mFileStream, "Must have a file stream!");
+  NS_ASSERTION(mFileHelper, "Must have a file helper!");
+}
+
+FileStreamWrapper::~FileStreamWrapper()
+{
+  if (mFlags & NOTIFY_DESTROY) {
+    if (NS_IsMainThread()) {
+      mFileHelper->OnStreamDestroy();
+    }
+    else {
+      nsCOMPtr<nsIRunnable> runnable =
+        new DestroyRunnable(mFileHelper);
+
+      nsresult rv = NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Failed to dispatch to the main thread!");
+      }
+    }
+  }
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS0(FileStreamWrapper)
+
+FileInputStreamWrapper::FileInputStreamWrapper(nsISupports* aFileStream,
+                                               FileHelper* aFileHelper,
+                                               PRUint64 aOffset,
+                                               PRUint64 aLimit,
+                                               PRUint32 aFlags)
+: FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags)
+{
+  mInputStream = do_QueryInterface(mFileStream);
+  NS_ASSERTION(mInputStream, "This should always succeed!");
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(FileInputStreamWrapper,
+                             FileStreamWrapper,
+                             nsIInputStream)
+
+NS_IMETHODIMP
+FileInputStreamWrapper::Close()
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mFlags & NOTIFY_CLOSE) {
+    nsCOMPtr<nsIRunnable> runnable = new CloseRunnable(mFileHelper);
+
+    if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
+      NS_WARNING("Failed to dispatch to the main thread!");
+    }
+  }
+
+  mOffset = 0;
+  mLimit = 0;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileInputStreamWrapper::Available(PRUint32* _retval)
+{
+  // Performing sync IO on the main thread is generally not allowed.
+  // However, the input stream wrapper is also used to track reads performed by
+  // other APIs like FileReader, XHR, etc.
+  // In that case nsInputStreamChannel::OpenContentStream() calls Available()
+  // before setting the content length property. This property is not important
+  // to perform reads properly, so we can just return NS_BASE_STREAM_CLOSED
+  // here. It causes OpenContentStream() to set the content length property to
+  // zero.
+
+  if (NS_IsMainThread()) {
+    return NS_BASE_STREAM_CLOSED;
+  }
+
+  return mInputStream->Available(_retval);
+}
+
+NS_IMETHODIMP
+FileInputStreamWrapper::Read(char* aBuf, PRUint32 aCount, PRUint32* _retval)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  nsresult rv;
+
+  if (mFirstTime) {
+    mFirstTime = false;
+
+    if (mOffset != LL_MAXUINT) {
+      nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
+      if (seekable) {
+        rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+    }
+
+    mOffset = 0;
+  }
+
+  PRUint64 max = mLimit - mOffset;
+  if (max == 0) {
+    *_retval = 0;
+    return NS_OK;
+  }
+
+  if (aCount > max) {
+    aCount = max;
+  }
+
+  rv = mInputStream->Read(aBuf, aCount, _retval);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mOffset += *_retval;
+
+  if (mFlags & NOTIFY_PROGRESS) {
+    nsCOMPtr<nsIRunnable> runnable =
+      new ProgressRunnable(mFileHelper, mOffset, mLimit);
+
+    rv = NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Failed to dispatch to the main thread!");
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileInputStreamWrapper::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
+                                     PRUint32 aCount, PRUint32* _retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+FileInputStreamWrapper::IsNonBlocking(bool* _retval)
+{
+  *_retval = false;
+  return NS_OK;
+}
+
+FileOutputStreamWrapper::FileOutputStreamWrapper(nsISupports* aFileStream,
+                                                 FileHelper* aFileHelper,
+                                                 PRUint64 aOffset,
+                                                 PRUint64 aLimit,
+                                                 PRUint32 aFlags)
+: FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags)
+#ifdef DEBUG
+, mWriteThread(nsnull)
+#endif
+{
+  mOutputStream = do_QueryInterface(mFileStream);
+  NS_ASSERTION(mOutputStream, "This should always succeed!");
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(FileOutputStreamWrapper,
+                             FileStreamWrapper,
+                             nsIOutputStream)
+
+NS_IMETHODIMP
+FileOutputStreamWrapper::Close()
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  nsresult rv = NS_OK;
+
+  if (!mFirstTime) {
+    // We must flush buffers of the stream on the same thread on which we wrote
+    // some data.
+    nsCOMPtr<nsIStandardFileStream> sstream = do_QueryInterface(mFileStream);
+    if (sstream) {
+      rv = sstream->FlushBuffers();
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Failed to flush buffers of the stream!");
+      }
+    }
+
+    NS_ASSERTION(PR_GetCurrentThread() == mWriteThread,
+                 "Unsetting thread locals on wrong thread!");
+    mFileHelper->mFileStorage->UnsetThreadLocals();
+  }
+
+  if (mFlags & NOTIFY_CLOSE) {
+    nsCOMPtr<nsIRunnable> runnable = new CloseRunnable(mFileHelper);
+
+    if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
+      NS_WARNING("Failed to dispatch to the main thread!");
+    }
+  }
+
+  mOffset = 0;
+  mLimit = 0;
+
+  return rv;
+}
+
+NS_IMETHODIMP
+FileOutputStreamWrapper::Write(const char* aBuf, PRUint32 aCount,
+                               PRUint32* _retval)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  nsresult rv;
+
+  if (mFirstTime) {
+    mFirstTime = false;
+
+#ifdef DEBUG
+    mWriteThread = PR_GetCurrentThread();
+#endif
+    mFileHelper->mFileStorage->SetThreadLocals();
+
+    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mOutputStream);
+    if (seekable) {
+      if (mOffset == LL_MAXUINT) {
+        rv = seekable->Seek(nsISeekableStream::NS_SEEK_END, 0);
+      }
+      else {
+        rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
+      }
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    mOffset = 0;
+  }
+
+  PRUint64 max = mLimit - mOffset;
+  if (max == 0) {
+    *_retval = 0;
+    return NS_OK;
+  }
+
+  if (aCount > max) {
+    aCount = max;
+  }
+
+  rv = mOutputStream->Write(aBuf, aCount, _retval);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mOffset += *_retval;
+
+  if (mFlags & NOTIFY_PROGRESS) {
+    nsCOMPtr<nsIRunnable> runnable =
+      new ProgressRunnable(mFileHelper, mOffset, mLimit);
+
+    NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileOutputStreamWrapper::Flush()
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileOutputStreamWrapper::WriteFrom(nsIInputStream* aFromStream,
+                                   PRUint32 aCount, PRUint32* _retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+FileOutputStreamWrapper::WriteSegments(nsReadSegmentFun aReader,
+                                       void* aClosure, PRUint32 aCount,
+                                       PRUint32* _retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+FileOutputStreamWrapper::IsNonBlocking(bool* _retval)
+{
+  *_retval = false;
+  return NS_OK;
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(ProgressRunnable, nsIRunnable)
+
+NS_IMETHODIMP
+ProgressRunnable::Run()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  mFileHelper->OnStreamProgress(mProgress, mProgressMax);
+  mFileHelper = nsnull;
+
+  return NS_OK;
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(CloseRunnable, nsIRunnable)
+
+NS_IMETHODIMP
+CloseRunnable::Run()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  mFileHelper->OnStreamClose();
+  mFileHelper = nsnull;
+
+  return NS_OK;
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(DestroyRunnable, nsIRunnable)
+
+NS_IMETHODIMP
+DestroyRunnable::Run()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  mFileHelper->OnStreamDestroy();
+  mFileHelper = nsnull;
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/file/FileStreamWrappers.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_file_filestreamwrappers_h__
+#define mozilla_dom_file_filestreamwrappers_h__
+
+#include "FileCommon.h"
+
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+
+BEGIN_FILE_NAMESPACE
+
+class FileHelper;
+
+class FileStreamWrapper : public nsISupports
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  FileStreamWrapper(nsISupports* aFileStream,
+                    FileHelper* aFileHelper,
+                    PRUint64 aOffset,
+                    PRUint64 aLimit,
+                    PRUint32 aFlags);
+
+  virtual ~FileStreamWrapper();
+
+  enum {
+    NOTIFY_PROGRESS = 1 << 0,
+    NOTIFY_CLOSE = 1 << 1,
+    NOTIFY_DESTROY = 1 << 2
+  };
+
+protected:
+  nsCOMPtr<nsISupports> mFileStream;
+  nsRefPtr<FileHelper> mFileHelper;
+  PRUint64 mOffset;
+  PRUint64 mLimit;
+  PRUint32 mFlags;
+  bool mFirstTime;
+};
+
+class FileInputStreamWrapper : public FileStreamWrapper,
+                               public nsIInputStream
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIINPUTSTREAM
+
+  FileInputStreamWrapper(nsISupports* aFileStream,
+                         FileHelper* aFileHelper,
+                         PRUint64 aOffset,
+                         PRUint64 aLimit,
+                         PRUint32 aFlags);
+
+protected:
+  virtual ~FileInputStreamWrapper()
+  { }
+
+private:
+  nsCOMPtr<nsIInputStream> mInputStream;
+};
+
+class FileOutputStreamWrapper : public FileStreamWrapper,
+                                public nsIOutputStream
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIOUTPUTSTREAM
+
+  FileOutputStreamWrapper(nsISupports* aFileStream,
+                         FileHelper* aFileHelper,
+                         PRUint64 aOffset,
+                         PRUint64 aLimit,
+                         PRUint32 aFlags);
+
+protected:
+  virtual ~FileOutputStreamWrapper()
+  { }
+
+private:
+  nsCOMPtr<nsIOutputStream> mOutputStream;
+#ifdef DEBUG
+  void* mWriteThread;
+#endif
+};
+
+END_FILE_NAMESPACE
+
+#endif // mozilla_dom_file_filestreamwrappers_h__
new file mode 100644
--- /dev/null
+++ b/dom/file/LockedFile.cpp
@@ -0,0 +1,1119 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "LockedFile.h"
+
+#include "nsIAppShell.h"
+#include "nsIDOMFile.h"
+#include "nsIFileStorage.h"
+#include "nsISeekableStream.h"
+
+#include "jsfriendapi.h"
+#include "nsCharsetAlias.h"
+#include "nsEventDispatcher.h"
+#include "nsNetUtil.h"
+#include "nsDOMClassInfoID.h"
+#include "nsDOMEvent.h"
+#include "nsJSUtils.h"
+#include "nsStringStream.h"
+#include "nsWidgetsCID.h"
+#include "xpcpublic.h"
+
+#include "AsyncHelper.h"
+#include "FileHandle.h"
+#include "FileHelper.h"
+#include "FileRequest.h"
+#include "FileService.h"
+#include "FileStreamWrappers.h"
+#include "MemoryStreams.h"
+#include "MetadataHelper.h"
+
+#define STREAM_COPY_BLOCK_SIZE 32768
+
+USING_FILE_NAMESPACE
+
+namespace {
+
+NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
+
+class ReadHelper : public FileHelper
+{
+public:
+  ReadHelper(LockedFile* aLockedFile,
+             FileRequest* aFileRequest,
+             PRUint64 aLocation,
+             PRUint64 aSize)
+  : FileHelper(aLockedFile, aFileRequest),
+    mLocation(aLocation), mSize(aSize)
+  {
+    NS_ASSERTION(mSize, "Passed zero size!");
+  }
+
+  nsresult
+  Init();
+
+  nsresult
+  DoAsyncRun(nsISupports* aStream);
+
+  nsresult
+  GetSuccessResult(JSContext* aCx, jsval* aVal);
+
+protected:
+  PRUint64 mLocation;
+  PRUint64 mSize;
+
+  nsRefPtr<MemoryOutputStream> mStream;
+};
+
+class ReadTextHelper : public ReadHelper
+{
+public:
+  ReadTextHelper(LockedFile* aLockedFile,
+                 FileRequest* aFileRequest,
+                 PRUint64 aLocation,
+                 PRUint64 aSize,
+                 const nsAString& aEncoding)
+  : ReadHelper(aLockedFile, aFileRequest, aLocation, aSize),
+    mEncoding(aEncoding)
+  { }
+
+  nsresult
+  GetSuccessResult(JSContext* aCx, jsval* aVal);
+
+private:
+  nsString mEncoding;
+};
+
+class WriteHelper : public FileHelper
+{
+public:
+  WriteHelper(LockedFile* aLockedFile,
+              FileRequest* aFileRequest,
+              PRUint64 aLocation,
+              nsIInputStream* aStream,
+              PRUint64 aLength)
+  : FileHelper(aLockedFile, aFileRequest),
+    mLocation(aLocation), mStream(aStream), mLength(aLength)
+  {
+    NS_ASSERTION(mLength, "Passed zero length!");
+  }
+
+  nsresult
+  DoAsyncRun(nsISupports* aStream);
+
+private:
+  PRUint64 mLocation;
+  nsCOMPtr<nsIInputStream> mStream;
+  PRUint64 mLength;
+};
+
+class TruncateHelper : public FileHelper
+{
+public:
+  TruncateHelper(LockedFile* aLockedFile,
+                 FileRequest* aFileRequest,
+                 PRUint64 aOffset)
+  : FileHelper(aLockedFile, aFileRequest),
+    mOffset(aOffset)
+  { }
+
+  nsresult
+  DoAsyncRun(nsISupports* aStream);
+
+private:
+  class AsyncTruncator : public AsyncHelper
+  {
+  public:
+    AsyncTruncator(nsISupports* aStream, PRInt64 aOffset)
+    : AsyncHelper(aStream),
+      mOffset(aOffset)
+    { }
+  protected:
+    nsresult
+    DoStreamWork(nsISupports* aStream);
+
+    PRUint64 mOffset;
+  };
+
+  PRUint64 mOffset;
+};
+
+class FlushHelper : public FileHelper
+{
+public:
+  FlushHelper(LockedFile* aLockedFile,
+               FileRequest* aFileRequest)
+  : FileHelper(aLockedFile, aFileRequest)
+  { }
+
+  nsresult
+  DoAsyncRun(nsISupports* aStream);
+
+private:
+  class AsyncFlusher : public AsyncHelper
+  {
+  public:
+    AsyncFlusher(nsISupports* aStream)
+    : AsyncHelper(aStream)
+    { }
+  protected:
+    nsresult
+    DoStreamWork(nsISupports* aStream);
+  };
+};
+
+class OpenStreamHelper : public FileHelper
+{
+public:
+  OpenStreamHelper(LockedFile* aLockedFile,
+                   bool aWholeFile,
+                   PRUint64 aStart,
+                   PRUint64 aLength)
+  : FileHelper(aLockedFile, nsnull),
+    mWholeFile(aWholeFile), mStart(aStart), mLength(aLength)
+  { }
+
+  nsresult
+  DoAsyncRun(nsISupports* aStream);
+
+  nsCOMPtr<nsIInputStream>&
+  Result()
+  {
+    return mStream;
+  }
+
+private:
+  bool mWholeFile;
+  PRUint64 mStart;
+  PRUint64 mLength;
+
+  nsCOMPtr<nsIInputStream> mStream;
+};
+
+already_AddRefed<nsDOMEvent>
+CreateGenericEvent(const nsAString& aType, bool aBubbles, bool aCancelable)
+{
+  nsRefPtr<nsDOMEvent> event(new nsDOMEvent(nsnull, nsnull));
+  nsresult rv = event->InitEvent(aType, aBubbles, aCancelable);
+  NS_ENSURE_SUCCESS(rv, nsnull);
+
+  rv = event->SetTrusted(true);
+  NS_ENSURE_SUCCESS(rv, nsnull);
+
+  return event.forget();
+}
+
+inline nsresult
+GetInputStreamForJSVal(const jsval& aValue, JSContext* aCx,
+                       nsIInputStream** aInputStream, PRUint64* aInputLength)
+{
+  nsresult rv;
+
+  if (!JSVAL_IS_PRIMITIVE(aValue)) {
+    JSObject* obj = JSVAL_TO_OBJECT(aValue);
+    if (JS_IsArrayBufferObject(obj, aCx)) {
+      char* data = reinterpret_cast<char*>(JS_GetArrayBufferData(obj, aCx));
+      PRUint32 length = JS_GetArrayBufferByteLength(obj, aCx);
+
+      rv = NS_NewByteInputStream(aInputStream, data, length,
+                                 NS_ASSIGNMENT_COPY);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      *aInputLength = length;
+
+      return NS_OK;
+    }
+
+    nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(
+      nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj));
+    if (blob) {
+      rv = blob->GetSize(aInputLength);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = blob->GetInternalStream(aInputStream);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      return NS_OK;
+    }
+  }
+
+  JSString* jsstr;
+  if (JSVAL_IS_STRING(aValue)) {
+    jsstr = JSVAL_TO_STRING(aValue);
+  }
+  else {
+    jsstr = JS_ValueToString(aCx, aValue);
+    NS_ENSURE_TRUE(jsstr, NS_ERROR_XPC_BAD_CONVERT_JS);
+  }
+
+  nsDependentJSString str;
+  if (!str.init(aCx, jsstr)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCString cstr;
+  CopyUTF16toUTF8(str, cstr);
+
+  nsCOMPtr<nsIInputStream> stream;
+  rv = NS_NewCStringInputStream(getter_AddRefs(stream), cstr);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  stream.forget(aInputStream);
+  *aInputLength = cstr.Length();
+
+  return NS_OK;
+}
+
+} // anonymous namespace
+
+// static
+already_AddRefed<LockedFile>
+LockedFile::Create(FileHandle* aFileHandle,
+                   Mode aMode,
+                   RequestMode aRequestMode)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsRefPtr<LockedFile> lockedFile = new LockedFile();
+
+  lockedFile->BindToOwner(aFileHandle);
+
+  lockedFile->mFileHandle = aFileHandle;
+  lockedFile->mMode = aMode;
+  lockedFile->mRequestMode = aRequestMode;
+
+  nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
+  NS_ENSURE_TRUE(appShell, nsnull);
+
+  nsresult rv = appShell->RunBeforeNextEvent(lockedFile);
+  NS_ENSURE_SUCCESS(rv, nsnull);
+
+  lockedFile->mCreating = true;
+
+  FileService* service = FileService::GetOrCreate();
+  NS_ENSURE_TRUE(service, nsnull);
+
+  rv = service->Enqueue(lockedFile, nsnull);
+  NS_ENSURE_SUCCESS(rv, nsnull);
+
+  return lockedFile.forget();
+}
+
+LockedFile::LockedFile()
+: mReadyState(INITIAL),
+  mMode(READ_ONLY),
+  mRequestMode(NORMAL),
+  mLocation(0),
+  mPendingRequests(0),
+  mAborted(false),
+  mCreating(false)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+}
+
+LockedFile::~LockedFile()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(LockedFile)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(LockedFile,
+                                                  nsDOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mFileHandle,
+                                                       nsIDOMEventTarget)
+  NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(complete)
+  NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(abort)
+  NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(error)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(LockedFile,
+                                                nsDOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFileHandle)
+  NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(complete)
+  NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(abort)
+  NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(error)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(LockedFile)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMLockedFile)
+  NS_INTERFACE_MAP_ENTRY(nsIRunnable)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(LockedFile)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
+
+NS_IMPL_ADDREF_INHERITED(LockedFile, nsDOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(LockedFile, nsDOMEventTargetHelper)
+
+DOMCI_DATA(LockedFile, LockedFile)
+
+NS_IMPL_EVENT_HANDLER(LockedFile, complete);
+NS_IMPL_EVENT_HANDLER(LockedFile, abort);
+NS_IMPL_EVENT_HANDLER(LockedFile, error);
+
+nsresult
+LockedFile::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  aVisitor.mCanHandle = true;
+  aVisitor.mParentTarget = mFileHandle;
+  return NS_OK;
+}
+
+void
+LockedFile::OnNewRequest()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  if (!mPendingRequests) {
+    NS_ASSERTION(mReadyState == INITIAL,
+                 "Reusing a locked file!");
+    mReadyState = LOADING;
+  }
+  ++mPendingRequests;
+}
+
+void
+LockedFile::OnRequestFinished()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(mPendingRequests, "Mismatched calls!");
+  --mPendingRequests;
+  if (!mPendingRequests) {
+    NS_ASSERTION(mAborted || mReadyState == LOADING,
+                 "Bad state!");
+    mReadyState = LockedFile::FINISHING;
+    Finish();
+  }
+}
+
+nsresult
+LockedFile::CreateParallelStream(nsISupports** aStream)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsIFileStorage* fileStorage = mFileHandle->mFileStorage;
+  if (fileStorage->IsStorageInvalidated()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  nsCOMPtr<nsISupports> stream =
+    mFileHandle->CreateStream(mFileHandle->mFile, mMode == READ_ONLY);
+  NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
+
+  mParallelStreams.AppendElement(stream);
+
+  stream.forget(aStream);
+  return NS_OK;
+}
+
+nsresult
+LockedFile::GetOrCreateStream(nsISupports** aStream)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsIFileStorage* fileStorage = mFileHandle->mFileStorage;
+  if (fileStorage->IsStorageInvalidated()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  if (!mStream) {
+    nsCOMPtr<nsISupports> stream =
+      mFileHandle->CreateStream(mFileHandle->mFile, mMode == READ_ONLY);
+    NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
+
+    stream.swap(mStream);
+  }
+
+  nsCOMPtr<nsISupports> stream(mStream);
+  stream.forget(aStream);
+
+  return NS_OK;
+}
+
+already_AddRefed<FileRequest>
+LockedFile::GenerateFileRequest()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  return FileRequest::Create(GetOwner(), this);
+}
+
+bool
+LockedFile::IsOpen() const
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // If we haven't started anything then we're open.
+  if (mReadyState == INITIAL) {
+    NS_ASSERTION(FileHelper::GetCurrentLockedFile() != this,
+                 "This should be some other locked file (or null)!");
+    return true;
+  }
+
+  // If we've already started then we need to check to see if we still have the
+  // mCreating flag set. If we do (i.e. we haven't returned to the event loop
+  // from the time we were created) then we are open. Otherwise check the
+  // currently running locked files to see if it's the same. We only allow other
+  // requests to be made if this locked file is currently running.
+  if (mReadyState == LOADING) {
+    if (mCreating) {
+      return true;
+    }
+
+    if (FileHelper::GetCurrentLockedFile() == this) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+NS_IMETHODIMP
+LockedFile::GetFileHandle(nsIDOMFileHandle** aFileHandle)
+{
+  nsCOMPtr<nsIDOMFileHandle> result(mFileHandle);
+  result.forget(aFileHandle);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LockedFile::GetMode(nsAString& aMode)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  switch (mMode) {
+   case READ_ONLY:
+     aMode.AssignLiteral("readonly");
+     break;
+   case READ_WRITE:
+     aMode.AssignLiteral("readwrite");
+     break;
+   default:
+     NS_NOTREACHED("Unknown mode!");
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LockedFile::GetActive(bool* aActive)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  *aActive = IsOpen();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LockedFile::GetLocation(PRUint64* aLocation)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  *aLocation = mLocation;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LockedFile::SetLocation(PRUint64 aLocation)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  mLocation = aLocation;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LockedFile::GetMetadata(const jsval& aParameters,
+                        JSContext* aCx,
+                        nsIDOMFileRequest** _retval)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (!IsOpen()) {
+    return NS_ERROR_DOM_FILEHANDLE_LOCKEDFILE_INACTIVE_ERR;
+  }
+
+  // Do nothing if the window is closed
+  if (!GetOwner()) {
+    return NS_OK;
+  }
+
+  nsRefPtr<MetadataParameters> params = new MetadataParameters();
+
+  // Get optional arguments.
+  if (!JSVAL_IS_VOID(aParameters) && !JSVAL_IS_NULL(aParameters)) {
+    nsresult rv = params->Init(aCx, &aParameters);
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+    if (!params->IsConfigured()) {
+      return NS_ERROR_TYPE_ERR;
+    }
+  }
+  else {
+    params->Init(true, true);
+  }
+
+  nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
+  NS_ENSURE_TRUE(fileRequest, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  nsRefPtr<MetadataHelper> helper =
+    new MetadataHelper(this, fileRequest, params);
+
+  nsresult rv = helper->Enqueue();
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  fileRequest.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LockedFile::ReadAsArrayBuffer(PRUint64 aSize,
+                              JSContext* aCx,
+                              nsIDOMFileRequest** _retval)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (!IsOpen()) {
+    return NS_ERROR_DOM_FILEHANDLE_LOCKEDFILE_INACTIVE_ERR;
+  }
+
+  if (!aSize) {
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  // Do nothing if the window is closed
+  if (!GetOwner()) {
+    return NS_OK;
+  }
+
+  nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
+  NS_ENSURE_TRUE(fileRequest, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  nsRefPtr<ReadHelper> helper =
+    new ReadHelper(this, fileRequest, mLocation, aSize);
+
+  nsresult rv = helper->Init();
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  rv = helper->Enqueue();
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  mLocation += aSize;
+
+  fileRequest.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LockedFile::ReadAsText(PRUint64 aSize,
+                       const nsAString& aEncoding,
+                       nsIDOMFileRequest** _retval)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (!IsOpen()) {
+    return NS_ERROR_DOM_FILEHANDLE_LOCKEDFILE_INACTIVE_ERR;
+  }
+
+  if (!aSize) {
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  // Do nothing if the window is closed
+  if (!GetOwner()) {
+    return NS_OK;
+  }
+
+  nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
+  NS_ENSURE_TRUE(fileRequest, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  nsRefPtr<ReadTextHelper> helper =
+    new ReadTextHelper(this, fileRequest, mLocation, aSize, aEncoding);
+
+  nsresult rv = helper->Init();
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  rv = helper->Enqueue();
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  mLocation += aSize;
+
+  fileRequest.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LockedFile::Write(const jsval& aValue,
+                  JSContext* aCx,
+                  nsIDOMFileRequest** _retval)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  return WriteOrAppend(aValue, aCx, _retval, false);
+}
+
+NS_IMETHODIMP
+LockedFile::Append(const jsval& aValue,
+                   JSContext* aCx,
+                   nsIDOMFileRequest** _retval)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  return WriteOrAppend(aValue, aCx, _retval, true);
+}
+
+NS_IMETHODIMP
+LockedFile::Truncate(nsIDOMFileRequest** _retval)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (!IsOpen()) {
+    return NS_ERROR_DOM_FILEHANDLE_LOCKEDFILE_INACTIVE_ERR;
+  }
+
+  if (mMode != READ_WRITE) {
+    return NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR;
+  }
+
+  // Do nothing if the window is closed
+  if (!GetOwner()) {
+    return NS_OK;
+  }
+
+  nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
+  NS_ENSURE_TRUE(fileRequest, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  nsRefPtr<TruncateHelper> helper =
+    new TruncateHelper(this, fileRequest, mLocation);
+
+  nsresult rv = helper->Enqueue();
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  fileRequest.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LockedFile::Flush(nsIDOMFileRequest** _retval)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (!IsOpen()) {
+    return NS_ERROR_DOM_FILEHANDLE_LOCKEDFILE_INACTIVE_ERR;
+  }
+
+  if (mMode != READ_WRITE) {
+    return NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR;
+  }
+
+  // Do nothing if the window is closed
+  if (!GetOwner()) {
+    return NS_OK;
+  }
+
+  nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
+  NS_ENSURE_TRUE(fileRequest, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  nsRefPtr<FlushHelper> helper = new FlushHelper(this, fileRequest);
+
+  nsresult rv = helper->Enqueue();
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  fileRequest.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LockedFile::Abort()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // We can't use IsOpen here since we need it to be possible to call Abort()
+  // even from outside of transaction callbacks.
+  if (mReadyState != LockedFile::INITIAL &&
+      mReadyState != LockedFile::LOADING) {
+    return NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR;
+  }
+
+  bool needToFinish = mReadyState == INITIAL;
+
+  mAborted = true;
+  mReadyState = DONE;
+
+  // Fire the abort event if there are no outstanding requests. Otherwise the
+  // abort event will be fired when all outstanding requests finish.
+  if (needToFinish) {
+    return Finish();
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LockedFile::Run()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // We're back at the event loop, no longer newborn.
+  mCreating = false;
+
+  // Maybe set the readyState to DONE if there were no requests generated.
+  if (mReadyState == INITIAL) {
+    mReadyState = DONE;
+
+    if (NS_FAILED(Finish())) {
+      NS_WARNING("Failed to finish!");
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
+LockedFile::OpenInputStream(bool aWholeFile, PRUint64 aStart, PRUint64 aLength,
+                            nsIInputStream** aResult)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(mRequestMode == PARALLEL,
+               "Don't call me in other than parallel mode!");
+
+  if (!IsOpen()) {
+    return NS_ERROR_DOM_FILEHANDLE_LOCKEDFILE_INACTIVE_ERR;
+  }
+
+  // Do nothing if the window is closed
+  if (!GetOwner()) {
+    return NS_OK;
+  }
+
+  nsRefPtr<OpenStreamHelper> helper =
+    new OpenStreamHelper(this, aWholeFile, aStart, aLength);
+
+  nsresult rv = helper->Enqueue();
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  nsCOMPtr<nsIInputStream>& result = helper->Result();
+  NS_ENSURE_TRUE(result, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+  
+  result.forget(aResult);
+  return NS_OK;
+}
+
+nsresult
+LockedFile::WriteOrAppend(const jsval& aValue,
+                          JSContext* aCx,
+                          nsIDOMFileRequest** _retval,
+                          bool aAppend)
+{
+  if (!IsOpen()) {
+    return NS_ERROR_DOM_FILEHANDLE_LOCKEDFILE_INACTIVE_ERR;
+  }
+
+  if (mMode != READ_WRITE) {
+    return NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR;
+  }
+
+  // Do nothing if the window is closed
+  if (!GetOwner()) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIInputStream> inputStream;
+  PRUint64 inputLength;
+  nsresult rv =
+    GetInputStreamForJSVal(aValue, aCx, getter_AddRefs(inputStream),
+                           &inputLength);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!inputLength) {
+    return NS_OK;
+  }
+
+  nsRefPtr<FileRequest> fileRequest = GenerateFileRequest();
+  NS_ENSURE_TRUE(fileRequest, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  PRUint64 location = aAppend ? LL_MAXUINT : mLocation;
+
+  nsRefPtr<WriteHelper> helper =
+    new WriteHelper(this, fileRequest, location, inputStream, inputLength);
+
+  rv = helper->Enqueue();
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+  if (aAppend) {
+    mLocation = LL_MAXUINT;
+  }
+  else {
+    mLocation += inputLength;
+  }
+
+  fileRequest.forget(_retval);
+  return NS_OK;
+}
+
+nsresult
+LockedFile::Finish()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsRefPtr<FinishHelper> helper(new FinishHelper(this));
+
+  FileService* service = FileService::Get();
+  NS_ASSERTION(service, "This should never be null");
+
+  nsIEventTarget* target = service->StreamTransportTarget();
+
+  nsresult rv = target->Dispatch(helper, NS_DISPATCH_NORMAL);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+FinishHelper::FinishHelper(LockedFile* aLockedFile)
+: mLockedFile(aLockedFile),
+  mAborted(aLockedFile->mAborted)
+{
+  mParallelStreams.SwapElements(aLockedFile->mParallelStreams);
+  mStream.swap(aLockedFile->mStream);
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(FinishHelper, nsIRunnable)
+
+NS_IMETHODIMP
+FinishHelper::Run()
+{
+  if (NS_IsMainThread()) {
+    mLockedFile->mReadyState = LockedFile::DONE;
+
+    FileService* service = FileService::Get();
+    if (service) {
+      service->NotifyLockedFileCompleted(mLockedFile);
+    }
+
+    nsCOMPtr<nsIDOMEvent> event;
+    if (mAborted) {
+      event = CreateGenericEvent(NS_LITERAL_STRING("abort"), true, false);
+    }
+    else {
+      event = CreateGenericEvent(NS_LITERAL_STRING("complete"), false, false);
+    }
+    NS_ENSURE_TRUE(event, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+
+    bool dummy;
+    if (NS_FAILED(mLockedFile->DispatchEvent(event, &dummy))) {
+      NS_WARNING("Dispatch failed!");
+    }
+
+    mLockedFile = nsnull;
+
+    return NS_OK;
+  }
+
+  nsIFileStorage* fileStorage = mLockedFile->mFileHandle->mFileStorage;
+  if (fileStorage->IsStorageInvalidated()) {
+    mAborted = true;
+  }
+
+  for (PRUint32 index = 0; index < mParallelStreams.Length(); index++) {
+    nsCOMPtr<nsIOutputStream> ostream =
+      do_QueryInterface(mParallelStreams[index]);
+
+    if (NS_FAILED(ostream->Close())) {
+      NS_WARNING("Failed to close stream!");
+    }
+
+    mParallelStreams[index] = nsnull;
+  }
+
+  if (mStream) {
+    nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(mStream);
+
+    if (NS_FAILED(ostream->Close())) {
+      NS_WARNING("Failed to close stream!");
+    }
+
+    mStream = nsnull;
+  }
+
+  return NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
+}
+
+nsresult
+ReadHelper::Init()
+{
+  mStream = MemoryOutputStream::Create(mSize);
+  NS_ENSURE_TRUE(mStream, NS_ERROR_FAILURE);
+
+  return NS_OK;
+}
+
+nsresult
+ReadHelper::DoAsyncRun(nsISupports* aStream)
+{
+  NS_ASSERTION(aStream, "Passed a null stream!");
+
+  PRUint32 flags = FileStreamWrapper::NOTIFY_PROGRESS;
+
+  nsCOMPtr<nsIInputStream> istream =
+    new FileInputStreamWrapper(aStream, this, mLocation, mSize, flags);
+
+  FileService* service = FileService::Get();
+  NS_ASSERTION(service, "This should never be null");
+
+  nsIEventTarget* target = service->StreamTransportTarget();
+
+  nsCOMPtr<nsIAsyncStreamCopier> copier;
+  nsresult rv =
+    NS_NewAsyncStreamCopier(getter_AddRefs(copier), istream, mStream, target,
+                            false, true, STREAM_COPY_BLOCK_SIZE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = copier->AsyncCopy(this, nsnull);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mRequest = do_QueryInterface(copier);
+
+  return NS_OK;
+}
+
+nsresult
+ReadHelper::GetSuccessResult(JSContext* aCx,
+                             jsval* aVal)
+{
+  JSObject *arrayBuffer;
+  nsresult rv =
+    nsContentUtils::CreateArrayBuffer(aCx, mStream->Data(), &arrayBuffer);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aVal = OBJECT_TO_JSVAL(arrayBuffer);
+
+  return NS_OK;
+}
+
+nsresult
+ReadTextHelper::GetSuccessResult(JSContext* aCx,
+                                 jsval* aVal)
+{
+  nsresult rv;
+
+  nsCString charsetGuess;
+  if (!mEncoding.IsEmpty()) {
+    CopyUTF16toUTF8(mEncoding, charsetGuess);
+  }
+  else {
+    const nsCString& data = mStream->Data();
+    PRUint32 dataLen = data.Length();
+    rv = nsContentUtils::GuessCharset(data.get(), dataLen, charsetGuess);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  nsCString charset;
+  rv = nsCharsetAlias::GetPreferred(charsetGuess, charset);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsString tmpString;
+  rv = nsContentUtils::ConvertStringFromCharset(charset, mStream->Data(),
+                                                tmpString);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!xpc::StringToJsval(aCx, tmpString, aVal)) {
+    NS_WARNING("Failed to convert string!");
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+WriteHelper::DoAsyncRun(nsISupports* aStream)
+{
+  NS_ASSERTION(aStream, "Passed a null stream!");
+
+  PRUint32 flags = FileStreamWrapper::NOTIFY_PROGRESS;
+
+  nsCOMPtr<nsIOutputStream> ostream =
+    new FileOutputStreamWrapper(aStream, this, mLocation, mLength, flags);
+
+  FileService* service = FileService::Get();
+  NS_ASSERTION(service, "This should never be null");
+
+  nsIEventTarget* target = service->StreamTransportTarget();
+
+  nsCOMPtr<nsIAsyncStreamCopier> copier;
+  nsresult rv =
+    NS_NewAsyncStreamCopier(getter_AddRefs(copier), mStream, ostream, target,
+                            true, false, STREAM_COPY_BLOCK_SIZE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = copier->AsyncCopy(this, nsnull);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mRequest = do_QueryInterface(copier);
+
+  return NS_OK;
+}
+
+nsresult
+TruncateHelper::DoAsyncRun(nsISupports* aStream)
+{
+  NS_ASSERTION(aStream, "Passed a null stream!");
+
+  nsRefPtr<AsyncTruncator> truncator = new AsyncTruncator(aStream, mOffset);
+
+  nsresult rv = truncator->AsyncWork(this, nsnull);
+  NS_ENSURE_SUCCESS(rv, rv);
+  
+  return NS_OK;
+}
+
+nsresult
+TruncateHelper::AsyncTruncator::DoStreamWork(nsISupports* aStream)
+{
+  nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(aStream);
+
+  nsresult rv = sstream->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = sstream->SetEOF();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+FlushHelper::DoAsyncRun(nsISupports* aStream)
+{
+  NS_ASSERTION(aStream, "Passed a null stream!");
+
+  nsRefPtr<AsyncFlusher> flusher = new AsyncFlusher(aStream);
+
+  nsresult rv = flusher->AsyncWork(this, nsnull);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+FlushHelper::AsyncFlusher::DoStreamWork(nsISupports* aStream)
+{
+  nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(aStream);
+
+  nsresult rv = ostream->Flush();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+OpenStreamHelper::DoAsyncRun(nsISupports* aStream)
+{
+  NS_ASSERTION(aStream, "Passed a null stream!");
+
+  PRUint32 flags = FileStreamWrapper::NOTIFY_CLOSE |
+                   FileStreamWrapper::NOTIFY_DESTROY;
+
+  mStream = mWholeFile ?
+    new FileInputStreamWrapper(aStream, this, 0, mLength, flags) :
+    new FileInputStreamWrapper(aStream, this, mStart, mLength, flags);
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/file/LockedFile.h
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_file_lockedfile_h__
+#define mozilla_dom_file_lockedfile_h__
+
+#include "FileCommon.h"
+
+#include "nsIDOMLockedFile.h"
+#include "nsIRunnable.h"
+
+#include "nsDOMEventTargetHelper.h"
+
+class nsIInputStream;
+
+BEGIN_FILE_NAMESPACE
+
+class FileHandle;
+class FileRequest;
+class MetadataHelper;
+
+class LockedFile : public nsDOMEventTargetHelper,
+                   public nsIDOMLockedFile,
+                   public nsIRunnable
+{
+  friend class FinishHelper;
+  friend class FileService;
+  friend class FileHelper;
+  friend class MetadataHelper;
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIDOMLOCKEDFILE
+  NS_DECL_NSIRUNNABLE
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(LockedFile, nsDOMEventTargetHelper)
+
+  enum Mode
+  {
+    READ_ONLY = 0,
+    READ_WRITE
+  };
+
+  enum RequestMode
+  {
+    NORMAL = 0, // Sequential
+    PARALLEL
+  };
+
+  enum ReadyState
+  {
+    INITIAL = 0,
+    LOADING,
+    FINISHING,
+    DONE
+  };
+
+  static already_AddRefed<LockedFile>
+  Create(FileHandle* aFileHandle,
+         Mode aMode,
+         RequestMode aRequestMode = NORMAL);
+
+  // nsIDOMEventTarget
+  virtual nsresult
+  PreHandleEvent(nsEventChainPreVisitor& aVisitor);
+
+  nsresult
+  CreateParallelStream(nsISupports** aStream);
+
+  nsresult
+  GetOrCreateStream(nsISupports** aStream);
+
+  bool
+  IsOpen() const;
+
+  bool
+  IsAborted() const
+  {
+    return mAborted;
+  }
+
+  FileHandle*
+  Handle() const
+  {
+    return mFileHandle;
+  }
+
+  nsresult
+  OpenInputStream(bool aWholeFile, PRUint64 aStart, PRUint64 aLength,
+                  nsIInputStream** aResult);
+
+private:
+  LockedFile();
+  ~LockedFile();
+
+  void
+  OnNewRequest();
+
+  void
+  OnRequestFinished();
+
+  inline already_AddRefed<FileRequest>
+  GenerateFileRequest();
+
+  nsresult
+  WriteOrAppend(const jsval& aValue, JSContext* aCx,
+                nsIDOMFileRequest** _retval, bool aAppend);
+
+  nsresult
+  Finish();
+
+  nsRefPtr<FileHandle> mFileHandle;
+  ReadyState mReadyState;
+  Mode mMode;
+  RequestMode mRequestMode;
+  PRUint64 mLocation;
+  PRUint32 mPendingRequests;
+
+  NS_DECL_EVENT_HANDLER(complete)
+  NS_DECL_EVENT_HANDLER(abort)
+  NS_DECL_EVENT_HANDLER(error)
+
+  nsTArray<nsCOMPtr<nsISupports> > mParallelStreams;
+  nsCOMPtr<nsISupports> mStream;
+
+  bool mAborted;
+  bool mCreating;
+};
+
+class FinishHelper : public nsIRunnable
+{
+  friend class LockedFile;
+
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIRUNNABLE
+
+private:
+  FinishHelper(LockedFile* aLockedFile);
+  ~FinishHelper()
+  { }
+
+  nsRefPtr<LockedFile> mLockedFile;
+  nsTArray<nsCOMPtr<nsISupports> > mParallelStreams;
+  nsCOMPtr<nsISupports> mStream;
+
+  bool mAborted;
+};
+
+END_FILE_NAMESPACE
+
+#endif // mozilla_dom_file_lockedfile_h__
new file mode 100644
--- /dev/null
+++ b/dom/file/Makefile.in
@@ -0,0 +1,58 @@
+# 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/.
+
+DEPTH            = ../..
+topsrcdir        = @top_srcdir@
+srcdir           = @srcdir@
+VPATH            = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE           = dom
+LIBRARY_NAME     = domfile_s
+XPIDL_MODULE     = dom_file
+LIBXUL_LIBRARY   = 1
+FORCE_STATIC_LIB = 1
+
+include $(topsrcdir)/dom/dom-config.mk
+
+EXPORTS_NAMESPACES = mozilla/dom/file
+
+CPPSRCS = \
+  AsyncHelper.cpp \
+  DOMFileHandle.cpp \
+  File.cpp \
+  FileHandle.cpp \
+  FileHelper.cpp \
+  FileRequest.cpp \
+  FileService.cpp \
+  FileStreamWrappers.cpp \
+  LockedFile.cpp \
+  MemoryStreams.cpp \
+  MetadataHelper.cpp \
+  $(NULL)
+
+EXPORTS = \
+  nsIFileStorage.h \
+  $(NULL)
+
+EXPORTS_mozilla/dom/file = \
+  DOMFileHandle.h \
+  File.h \
+  FileCommon.h \
+  FileHandle.h \
+  FileHelper.h \
+  FileService.h \
+  LockedFile.h \
+  $(NULL)
+
+XPIDLSRCS = \
+  nsIDOMFileHandle.idl \
+  nsIDOMFileRequest.idl \
+  nsIDOMLockedFile.idl \
+  $(NULL)
+
+TEST_DIRS += test
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/file/MemoryStreams.cpp
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "MemoryStreams.h"
+
+#include "nsStreamUtils.h"
+
+USING_FILE_NAMESPACE
+
+// static
+already_AddRefed<MemoryOutputStream>
+MemoryOutputStream::Create(PRUint64 aSize)
+{
+  NS_ASSERTION(aSize, "Passed zero size!");
+
+  NS_ENSURE_TRUE(aSize <= PR_UINT32_MAX, nsnull);
+
+  nsRefPtr<MemoryOutputStream> stream = new MemoryOutputStream();
+
+  char* dummy;
+  PRUint32 length = stream->mData.GetMutableData(&dummy, aSize, fallible_t());
+  NS_ENSURE_TRUE(length == aSize, nsnull);
+
+  return stream.forget();
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(MemoryOutputStream, nsIOutputStream)
+
+NS_IMETHODIMP
+MemoryOutputStream::Close()
+{
+  mData.Truncate(mOffset);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MemoryOutputStream::Write(const char* aBuf, PRUint32 aCount, PRUint32* _retval)
+{
+  return WriteSegments(NS_CopySegmentToBuffer, (char*)aBuf, aCount, _retval);
+}
+
+NS_IMETHODIMP
+MemoryOutputStream::Flush()
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MemoryOutputStream::WriteFrom(nsIInputStream* aFromStream, PRUint32 aCount,
+                              PRUint32* _retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MemoryOutputStream::WriteSegments(nsReadSegmentFun aReader, void* aClosure,
+                                  PRUint32 aCount, PRUint32* _retval)
+{
+  NS_ASSERTION(mData.Length() >= mOffset, "Bad stream state!");
+
+  PRUint32 maxCount = mData.Length() - mOffset;
+  if (maxCount == 0) {
+    *_retval = 0;
+    return NS_OK;
+  }
+
+  if (aCount > maxCount) {
+    aCount = maxCount;
+  }
+
+  nsresult rv = aReader(this, aClosure, mData.BeginWriting() + mOffset, 0,
+                        aCount, _retval);
+  if (NS_SUCCEEDED(rv)) {
+    NS_ASSERTION(*_retval <= aCount,
+                 "Reader should not read more than we asked it to read!");
+    mOffset += *_retval;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MemoryOutputStream::IsNonBlocking(bool* _retval)
+{
+  *_retval = false;
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/file/MemoryStreams.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_file_memorystreams_h__
+#define mozilla_dom_file_memorystreams_h__
+
+#include "FileCommon.h"
+
+#include "nsIOutputStream.h"
+
+BEGIN_FILE_NAMESPACE
+
+class MemoryOutputStream : public nsIOutputStream
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOUTPUTSTREAM
+
+  static already_AddRefed<MemoryOutputStream>
+  Create(PRUint64 aSize);
+
+
+  const nsCString&
+  Data() const
+  {
+    return mData;
+  }
+
+private:
+  MemoryOutputStream()
+  : mOffset(0)
+  { }
+
+  virtual ~MemoryOutputStream()
+  { }
+
+  nsCString mData;
+  PRUint64 mOffset;
+};
+
+END_FILE_NAMESPACE
+
+#endif // mozilla_dom_file_memorystreams_h__
new file mode 100644
--- /dev/null
+++ b/dom/file/MetadataHelper.cpp
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "MetadataHelper.h"
+
+#include "LockedFile.h"
+
+USING_FILE_NAMESPACE
+
+nsresult
+MetadataHelper::DoAsyncRun(nsISupports* aStream)
+{
+  bool readWrite = mLockedFile->mMode == LockedFile::READ_WRITE;
+
+  nsRefPtr<AsyncMetadataGetter> getter =
+    new AsyncMetadataGetter(aStream, mParams, readWrite);
+
+  nsresult rv = getter->AsyncWork(this, nsnull);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+MetadataHelper::GetSuccessResult(JSContext* aCx,
+                                 jsval* aVal)
+{
+  JSObject* obj = JS_NewObject(aCx, nsnull, nsnull, nsnull);
+  NS_ENSURE_TRUE(obj, NS_ERROR_OUT_OF_MEMORY);
+
+  if (mParams->SizeRequested()) {
+    jsval val;
+
+    if (mParams->Size() <= JSVAL_INT_MAX) {
+      val = INT_TO_JSVAL(mParams->Size());
+    }
+    else {
+      double size = mParams->Size();
+      if (!JS_NewNumberValue(aCx, size, &val)) {
+        return NS_ERROR_FAILURE;
+      }
+    }
+
+    if (!JS_DefineProperty(aCx, obj, "size", val, nsnull, nsnull,
+                           JSPROP_ENUMERATE)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  if (mParams->LastModifiedRequested()) {
+    double msec = mParams->LastModified();
+    JSObject *date = JS_NewDateObjectMsec(aCx, msec);
+    NS_ENSURE_TRUE(date, NS_ERROR_OUT_OF_MEMORY);
+
+    if (!JS_DefineProperty(aCx, obj, "lastModified", OBJECT_TO_JSVAL(date),
+                           nsnull, nsnull, JSPROP_ENUMERATE)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  *aVal = OBJECT_TO_JSVAL(obj);
+
+  return NS_OK;
+}
+
+nsresult
+MetadataHelper::AsyncMetadataGetter::DoStreamWork(nsISupports* aStream)
+{
+  nsresult rv;
+
+  if (mReadWrite) {
+    // Force a flush (so all pending writes are flushed to the disk and file
+    // metadata is updated too).
+
+    nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(aStream);
+    NS_ASSERTION(ostream, "This should always succeed!");
+
+    rv = ostream->Flush();
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  nsCOMPtr<nsIFileMetadata> metadata = do_QueryInterface(aStream);
+
+  if (mParams->SizeRequested()) {
+    PRInt64 size;
+    rv = metadata->GetSize(&size);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    NS_ENSURE_TRUE(size >= 0, NS_ERROR_FAILURE);
+
+    mParams->mSize = PRUint64(size);
+  }
+
+  if (mParams->LastModifiedRequested()) {
+    PRInt64 lastModified;
+    rv = metadata->GetLastModified(&lastModified);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mParams->mLastModified = lastModified;
+  }
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/file/MetadataHelper.h
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_file_metadatahelper_h__
+#define mozilla_dom_file_metadatahelper_h__
+
+#include "FileCommon.h"
+
+#include "nsIFileStreams.h"
+
+#include "DictionaryHelpers.h"
+
+#include "AsyncHelper.h"
+#include "FileHelper.h"
+
+class nsIFileStream;
+
+BEGIN_FILE_NAMESPACE
+
+class MetadataHelper;
+
+class MetadataParameters
+{
+  friend class MetadataHelper;
+
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MetadataParameters)
+
+  nsresult
+  Init(JSContext* aCx, const jsval* aVal)
+  {
+    return mConfig.Init(aCx, aVal);
+  }
+
+  void
+  Init(bool aRequestSize, bool aRequestLastModified)
+  {
+    mConfig.size = aRequestSize;
+    mConfig.lastModified = aRequestLastModified;
+  }
+
+  bool
+  IsConfigured() const
+  {
+    return mConfig.size || mConfig.lastModified;
+  }
+
+  bool
+  SizeRequested() const
+  {
+    return mConfig.size;
+  }
+
+  bool
+  LastModifiedRequested() const
+  {
+    return mConfig.lastModified;
+  }
+
+  PRUint64
+  Size() const
+  {
+    return mSize;
+  }
+
+  PRInt64
+  LastModified() const
+  {
+    return mLastModified;
+  }
+
+private:
+  DOMFileMetadataParameters mConfig;
+
+  PRUint64 mSize;
+  PRInt64 mLastModified;
+};
+
+class MetadataHelper : public FileHelper
+{
+public:
+  MetadataHelper(LockedFile* aLockedFile,
+                 FileRequest* aFileRequest,
+                 MetadataParameters* aParams)
+  : FileHelper(aLockedFile, aFileRequest),
+    mParams(aParams)
+  { }
+
+  nsresult
+  DoAsyncRun(nsISupports* aStream);
+
+  nsresult
+  GetSuccessResult(JSContext* aCx, jsval* aVal);
+
+protected:
+  class AsyncMetadataGetter : public AsyncHelper
+  {
+  public:
+    AsyncMetadataGetter(nsISupports* aStream, MetadataParameters* aParams,
+                        bool aReadWrite)
+    : AsyncHelper(aStream),
+      mParams(aParams), mReadWrite(aReadWrite)
+    { }
+
+  protected:
+    nsresult
+    DoStreamWork(nsISupports* aStream);
+
+  private:
+    nsRefPtr<MetadataParameters> mParams;
+    bool mReadWrite;
+  };
+
+  nsRefPtr<MetadataParameters> mParams;
+};
+
+END_FILE_NAMESPACE
+
+#endif // mozilla_dom_file_metadatahelper_h__
new file mode 100644
--- /dev/null
+++ b/dom/file/nsIDOMFileHandle.idl
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "nsISupports.idl"
+
+%{C++
+namespace mozilla {
+namespace dom {
+namespace indexedDB {
+class FileInfo;
+}
+}
+}
+%}
+
+[ptr] native FileInfo(mozilla::dom::indexedDB::FileInfo);
+
+interface nsIDOMEventListener;
+interface nsIDOMFileRequest;
+interface nsIDOMLockedFile;
+
+[scriptable, builtinclass, uuid(0dc9c73c-4e44-4430-8898-85f61a70b1d2)]
+interface nsIDOMFileHandle : nsISupports
+{
+  readonly attribute DOMString name;
+
+  readonly attribute DOMString type;
+
+  // mode can be either "readonly" or "readwrite"
+  [optional_argc]
+  nsIDOMLockedFile
+  open([optional /* "readonly" */] in DOMString mode);
+
+  nsIDOMFileRequest
+  getFile();
+
+  [notxpcom]
+  long long
+  getFileId();
+
+  [notxpcom]
+  FileInfo
+  getFileInfo();
+
+  attribute nsIDOMEventListener onabort;
+
+  attribute nsIDOMEventListener onerror;
+};
new file mode 100644
--- /dev/null
+++ b/dom/file/nsIDOMFileRequest.idl
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "nsIDOMDOMRequest.idl"
+
+interface nsIDOMEventListener;
+interface nsIDOMLockedFile;
+
+[scriptable, builtinclass, uuid(fe06b66e-fede-4d44-ab3a-403f60d6b593)]
+interface nsIDOMFileRequest : nsIDOMDOMRequest
+{
+  readonly attribute nsIDOMLockedFile lockedFile;
+
+  attribute nsIDOMEventListener onprogress;
+};
new file mode 100644
--- /dev/null
+++ b/dom/file/nsIDOMLockedFile.idl
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "nsISupports.idl"
+
+interface nsIDOMEventListener;
+interface nsIDOMFileHandle;
+interface nsIDOMFileRequest;
+
+dictionary DOMFileMetadataParameters
+{
+  boolean size;
+  boolean lastModified;
+};
+
+[scriptable, builtinclass, uuid(63055eeb-cc19-468b-bafa-7b7961796340)]
+interface nsIDOMLockedFile : nsISupports
+{
+  readonly attribute nsIDOMFileHandle fileHandle;
+
+  // "readonly" or "readwrite"
+  readonly attribute DOMString mode;
+
+  readonly attribute boolean active;
+
+  attribute unsigned long long location;
+
+  [implicit_jscontext]
+  nsIDOMFileRequest
+  getMetadata(/* DOMFileMetadataParameters */
+              [optional /* all */] in jsval parameters);
+
+  [implicit_jscontext]
+  nsIDOMFileRequest
+  readAsArrayBuffer(in unsigned long long size);
+
+  nsIDOMFileRequest
+  readAsText(in unsigned long long size,
+             [optional] in DOMString encoding);
+
+  [implicit_jscontext]
+  nsIDOMFileRequest
+  write(in jsval value);
+
+  [implicit_jscontext]
+  nsIDOMFileRequest
+  append(in jsval value);
+
+  nsIDOMFileRequest
+  truncate();
+
+  nsIDOMFileRequest
+  flush();
+
+  void
+  abort();
+
+  attribute nsIDOMEventListener oncomplete;
+
+  attribute nsIDOMEventListener onabort;
+
+  attribute nsIDOMEventListener onerror;
+};
new file mode 100644
--- /dev/null
+++ b/dom/file/nsIFileStorage.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef nsIFileStorage_h__
+#define nsIFileStorage_h__
+
+#include "nsISupports.h"
+
+#define NS_FILESTORAGE_IID \
+  {0xbba9c2ff, 0x85c9, 0x47c1, \
+  { 0xaf, 0xce, 0x0a, 0x7e, 0x6f, 0x21, 0x50, 0x95 } }
+
+class nsIFileStorage : public nsISupports
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_FILESTORAGE_IID)
+
+  virtual nsISupports*
+  StorageId() = 0;
+
+  virtual bool
+  IsStorageInvalidated() = 0;
+
+  virtual bool
+  IsStorageShuttingDown() = 0;
+
+  virtual void
+  SetThreadLocals() = 0;
+
+  virtual void
+  UnsetThreadLocals() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIFileStorage, NS_FILESTORAGE_IID)
+
+#define NS_DECL_NSIFILESTORAGE \
+  virtual nsISupports*         \
+  StorageId();                 \
+                               \
+  virtual bool                 \
+  IsStorageInvalidated();      \
+                               \
+  virtual bool                 \
+  IsStorageShuttingDown();     \
+                               \
+  virtual void                 \
+  SetThreadLocals();           \
+                               \
+  virtual void                 \
+  UnsetThreadLocals();
+
+#endif // nsIFileStorage_h__
new file mode 100644
--- /dev/null
+++ b/dom/file/test/Makefile.in
@@ -0,0 +1,33 @@
+# 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/.
+
+DEPTH            = ../../..
+topsrcdir        = @top_srcdir@
+srcdir           = @srcdir@
+VPATH            = @srcdir@
+relativesrcdir   = dom/file/test
+
+include $(DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/rules.mk
+
+TEST_FILES = \
+  helpers.js \
+  test_append_read_data.html \
+  test_getFileId.html \
+  test_lockedfile_lifetimes.html \
+  test_lockedfile_lifetimes_nested.html \
+  test_lockedfile_ordering.html \
+  test_overlapping_lockedfiles.html \
+  test_progress_events.html \
+  test_readonly_lockedfiles.html \
+  test_request_readyState.html \
+  test_stream_tracking.html \
+  test_success_events_after_abort.html \
+  test_truncate.html \
+  test_write_read_data.html \
+  $(NULL)
+
+libs:: $(TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/file/test/helpers.js
@@ -0,0 +1,220 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const IndexedDatabaseKey = "IDB";
+const DeviceStorageKey = "DS";
+
+var fileStorages = [
+  { key: IndexedDatabaseKey }
+//  { key: DeviceStorageKey }
+];
+
+var utils = SpecialPowers.getDOMWindowUtils(window);
+
+var testGenerator = testSteps();
+
+function runTest()
+{
+  allowIndexedDB();
+  allowUnlimitedQuota();
+
+  SimpleTest.waitForExplicitFinish();
+  testGenerator.next();
+}
+
+function finishTest()
+{
+  resetUnlimitedQuota();
+  resetIndexedDB();
+
+  SimpleTest.executeSoon(function() {
+    testGenerator.close();
+    SimpleTest.finish();
+  });
+}
+
+function grabEventAndContinueHandler(event)
+{
+  testGenerator.send(event);
+}
+
+function continueToNextStep()
+{
+  SimpleTest.executeSoon(function() {
+    testGenerator.next();
+  });
+}
+
+function errorHandler(event)
+{
+  ok(false, "indexedDB error, code " + event.target.errorCode);
+  finishTest();
+}
+
+function unexpectedSuccessHandler()
+{
+  ok(false, "Got success, but did not expect it!");
+  finishTest();
+}
+
+function ExpectError(name)
+{
+  this._name = name;
+}
+ExpectError.prototype = {
+  handleEvent: function(event)
+  {
+    is(event.type, "error", "Got an error event");
+    is(event.target.error.name, this._name, "Expected error was thrown.");
+    event.preventDefault();
+    event.stopPropagation();
+    grabEventAndContinueHandler(event);
+  }
+};
+
+function addPermission(type, allow, url)
+{
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+  let uri;
+  if (url) {
+    uri = Components.classes["@mozilla.org/network/io-service;1"]
+                    .getService(Components.interfaces.nsIIOService)
+                    .newURI(url, null, null);
+  }
+  else {
+    uri = SpecialPowers.getDocumentURIObject(window.document);
+  }
+
+  let permission;
+  if (allow) {
+    permission = Components.interfaces.nsIPermissionManager.ALLOW_ACTION;
+  }
+  else {
+    permission = Components.interfaces.nsIPermissionManager.DENY_ACTION;
+  }
+
+  Components.classes["@mozilla.org/permissionmanager;1"]
+            .getService(Components.interfaces.nsIPermissionManager)
+            .add(uri, type, permission);
+}
+
+function removePermission(permission, url)
+{
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+  let uri;
+  if (url) {
+    uri = Components.classes["@mozilla.org/network/io-service;1"]
+                    .getService(Components.interfaces.nsIIOService)
+                    .newURI(url, null, null);
+  }
+  else {
+    uri = SpecialPowers.getDocumentURIObject(window.document);
+  }
+
+  Components.classes["@mozilla.org/permissionmanager;1"]
+            .getService(Components.interfaces.nsIPermissionManager)
+            .remove(uri.host, permission);
+}
+
+function allowIndexedDB(url)
+{
+  addPermission("indexedDB", true, url);
+}
+
+function resetIndexedDB(url)
+{
+  removePermission("indexedDB", url);
+}
+
+function allowUnlimitedQuota(url)
+{
+  addPermission("indexedDB-unlimited", true, url);
+}
+
+function resetUnlimitedQuota(url)
+{
+  removePermission("indexedDB-unlimited", url);
+}
+
+function getFileHandle(fileStorageKey, name)
+{
+  var requestService = SpecialPowers.getDOMRequestService();
+  var request = requestService.createRequest(window);
+
+  switch (fileStorageKey) {
+    case IndexedDatabaseKey:
+      var dbname = window.location.pathname;
+      mozIndexedDB.open(dbname, 1).onsuccess = function(event) {
+        var db = event.target.result;
+        db.mozCreateFileHandle(name).onsuccess = function(event) {
+          var fileHandle = event.target.result;
+          requestService.fireSuccess(request, fileHandle);
+        }
+      }
+      break;
+
+    case DeviceStorageKey:
+      var dbname = window.location.pathname;
+      mozIndexedDB.open(dbname, 1).onsuccess = function(event) {
+        var db = event.target.result;
+        db.mozCreateFileHandle(name).onsuccess = function(event) {
+          var fileHandle = event.target.result;
+          requestService.fireSuccess(request, fileHandle);
+        }
+      }
+      break;
+  }
+
+  return request;
+}
+
+function getBuffer(size)
+{
+  let buffer = new ArrayBuffer(size);
+  is(buffer.byteLength, size, "Correct byte length");
+  return buffer;
+}
+
+function getRandomBuffer(size)
+{
+  let buffer = getBuffer(size);
+  let view = new Uint8Array(buffer);
+  for (let i = 0; i < size; i++) {
+    view[i] = parseInt(Math.random() * 255)
+  }
+  return buffer;
+}
+
+function compareBuffers(buffer1, buffer2)
+{
+  if (buffer1.byteLength != buffer2.byteLength) {
+    return false;
+  }
+  let view1 = new Uint8Array(buffer1);
+  let view2 = new Uint8Array(buffer2);
+  for (let i = 0; i < buffer1.byteLength; i++) {
+    if (view1[i] != view2[i]) {
+      return false;
+    }
+  }
+  return true;
+}
+
+function getBlob(type, buffer)
+{
+  return utils.getBlob([buffer], {type: type});
+}
+
+function getRandomBlob(size)
+{
+  return getBlob("binary/random", getRandomBuffer(size));
+}
+
+function getFileId(blob)
+{
+  return utils.getFileId(blob);
+}
new file mode 100644
--- /dev/null
+++ b/dom/file/test/test_append_read_data.html
@@ -0,0 +1,103 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>File Handle Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+  function testSteps()
+  {
+    const maxLocation = 18446744073709552000;
+
+    var testString = "Lorem ipsum his ponderum delicatissimi ne, at noster dolores urbanitas pro, cibo elaboraret no his. Ea dicunt maiorum usu. Ad appareat facilisis mediocritatem eos. Tale graeci mentitum in eos, hinc insolens at nam. Graecis nominavi aliquyam eu vix. Id solet assentior sadipscing pro. Et per atqui graecis, usu quot viris repudiandae ei, mollis evertitur an nam. At nam dolor ignota, liber labore omnesque ea mei, has movet voluptaria in. Vel an impetus omittantur. Vim movet option salutandi ex, ne mei ignota corrumpit. Mucius comprehensam id per. Est ea putant maiestatis.";
+    for (let i = 0; i < 5; i++) {
+      testString += testString;
+    }
+
+    var testBuffer = getRandomBuffer(100000);
+
+    var testBlob = getBlob("binary/random", testBuffer);
+
+    for each (let fileStorage in fileStorages) {
+      let request = getFileHandle(fileStorage.key, "test.txt");
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield;
+
+      let fileHandle = event.target.result;
+      fileHandle.onerror = errorHandler;
+
+      let location = 0;
+
+      let lockedFile = fileHandle.open("readwrite");
+      is(lockedFile.location, location, "Correct location");
+
+      request = lockedFile.append(testString);
+      is(lockedFile.location, maxLocation, "Correct location");
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      lockedFile.location = 0;
+      request = lockedFile.readAsText(testString.length);
+      location += testString.length
+      is(lockedFile.location, location, "Correct location");
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      let resultString = event.target.result;
+      ok(resultString == testString, "Correct string data");
+
+      request = lockedFile.append(testBuffer);
+      is(lockedFile.location, maxLocation, "Correct location");
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      lockedFile.location = location;
+      request = lockedFile.readAsArrayBuffer(testBuffer.byteLength);
+      location += testBuffer.byteLength;
+      is(lockedFile.location, location, "Correct location");
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      let resultBuffer = event.target.result;
+      ok(compareBuffers(resultBuffer, testBuffer), "Correct array buffer data");
+
+      request = lockedFile.append(testBlob);
+      is(lockedFile.location, maxLocation, "Correct location");
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      lockedFile.location = location;
+      request = lockedFile.readAsArrayBuffer(testBlob.size);
+      location += testBlob.size;
+      is(lockedFile.location, location, "Correct location");
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      resultBuffer = event.target.result;
+      ok(compareBuffers(resultBuffer, testBuffer), "Correct blob data");
+
+      request = lockedFile.getMetadata({ size: true });
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      let result = event.target.result;
+      is(result.size, location, "Correct size");
+    }
+
+    finishTest();
+    yield;
+  }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/file/test/test_getFileId.html
@@ -0,0 +1,31 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>File Handle Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+  function testSteps()
+  {
+    let id = getFileId(null);
+    ok(id == -1, "Correct id");
+
+    id = getFileId(getRandomBlob(100));
+    ok(id == -1, "Correct id");
+
+    finishTest();
+    yield;
+  }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/file/test/test_lockedfile_lifetimes.html
@@ -0,0 +1,49 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>File Handle Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+  function testSteps()
+  {
+    for each (let fileStorage in fileStorages) {
+      let request = getFileHandle(fileStorage.key, "test.txt");
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield;
+
+      let fileHandle = event.target.result;
+      fileHandle.onerror = errorHandler;
+
+      let lockedFile = fileHandle.open();
+      continueToNextStep();
+      yield;
+
+      try {
+        lockedFile.getMetadata({ size: true });
+        ok(false, "Should have thrown!");
+      }
+      catch (e) {
+        ok(e instanceof DOMException, "Got exception.");
+        is(e.name, "LockedFileInactiveError", "Good error.");
+        is(e.code, 0, "Good error code.");
+      }
+    }
+
+    finishTest();
+    yield;
+  }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/file/test/test_lockedfile_lifetimes_nested.html
@@ -0,0 +1,61 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>File Handle Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+  function testSteps()
+  {
+    for each (let fileStorage in fileStorages) {
+      let request = getFileHandle(fileStorage.key, "test.txt");
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield;
+
+      let fileHandle = event.target.result;
+      fileHandle.onerror = errorHandler;
+
+      let lockedFile = fileHandle.open();
+
+      let lockedFile2;
+
+      netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+      let thread = Components.classes["@mozilla.org/thread-manager;1"]
+                             .getService()
+                             .currentThread;
+
+      let eventHasRun;
+
+      thread.dispatch(function() {
+        eventHasRun = true;
+
+        lockedFile2 = fileHandle.open();
+      }, Components.interfaces.nsIThread.DISPATCH_NORMAL);
+
+      while (!eventHasRun) {
+        thread.processNextEvent(false);
+      }
+
+      ok(lockedFile2, "Non-null lockedFile2");
+
+      continueToNextStep();
+      yield;
+    }
+
+    finishTest();
+    yield;
+  }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/file/test/test_lockedfile_ordering.html
@@ -0,0 +1,54 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>File Handle Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+  function testSteps()
+  {
+    for each (let fileStorage in fileStorages) {
+      let request = getFileHandle(fileStorage.key, "test.txt");
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield;
+
+      let fileHandle = event.target.result;
+      fileHandle.onerror = errorHandler;
+
+      let lockedFile1 = fileHandle.open("readwrite");
+      let lockedFile2 = fileHandle.open("readwrite");
+
+      let request1 = lockedFile2.write("2");
+      let request2 = lockedFile1.write("1");
+
+      lockedFile1.oncomplete = grabEventAndContinueHandler;
+      lockedFile2.oncomplete = grabEventAndContinueHandler;
+
+      yield;
+      yield;
+
+      let lockedFile3 = fileHandle.open("readonly");
+      let request3 = lockedFile3.readAsText(1);
+      request3.onsuccess = grabEventAndContinueHandler;
+
+      event = yield;
+      is(event.target.result, "2", "Locked files were ordered properly.");
+    }
+
+    finishTest();
+    yield;
+  }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/file/test/test_overlapping_lockedfiles.html
@@ -0,0 +1,65 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>File Handle Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+  function testSteps()
+  {
+    for each (let fileStorage in fileStorages) {
+      let request = getFileHandle(fileStorage.key, "test.txt");
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield;
+
+      let fileHandle = event.target.result;
+      fileHandle.onerror = errorHandler;
+
+      for (let i = 0; i < 50; i++) {
+        let stepNumber = 0;
+
+        request = fileHandle.open("readwrite").append("string1");
+        request.onsuccess = function(event) {
+          is(stepNumber, 1, "This callback came first");
+          stepNumber++;
+          event.target.lockedFile.oncomplete = grabEventAndContinueHandler;
+        }
+
+        request = fileHandle.open("readwrite").append("string2");
+        request.onsuccess = function(event) {
+          is(stepNumber, 2, "This callback came second");
+          stepNumber++;
+          event.target.lockedFile.oncomplete = grabEventAndContinueHandler;
+        }
+
+        request = fileHandle.open("readwrite").append("string3");
+        request.onsuccess = function(event) {
+          is(stepNumber, 3, "This callback came third");
+          stepNumber++;
+          event.target.lockedFile.oncomplete = grabEventAndContinueHandler;
+        }
+
+        stepNumber++;
+        yield; yield; yield;;
+
+        is(stepNumber, 4, "All callbacks received");
+      }
+    }
+
+    finishTest();
+    yield;
+  }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/file/test/test_progress_events.html
@@ -0,0 +1,70 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>File Handle Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+  function testSteps()
+  {
+    var testBuffer = getRandomBuffer(100000);
+
+    for each (let fileStorage in fileStorages) {
+      let request = getFileHandle(fileStorage.key, "test.txt");
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield;
+
+      let fileHandle = event.target.result;
+      fileHandle.onerror = errorHandler;
+
+      let lockedFile = fileHandle.open("readwrite");
+
+      let sum = 0;
+
+      request = lockedFile.write(testBuffer);
+      request.onprogress = function(event) {
+        let loaded = event.loaded;
+        let total = event.total;
+        ok(loaded >= 0 && loaded <= total, "Correct loaded progress");
+        is(total, testBuffer.byteLength, "Correct total progress");
+        sum += event.loaded - sum;
+      }
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(sum, testBuffer.byteLength, "Correct loaded progress sum");
+
+      sum = 0;
+
+      lockedFile.location = 0;
+      request = lockedFile.readAsArrayBuffer(testBuffer.byteLength);
+      request.onprogress = function(event) {
+        let loaded = event.loaded;
+        let total = event.total;
+        ok(loaded >= 0 && loaded <= total, "Correct loaded progress");
+        is(total, testBuffer.byteLength, "Correct total progress");
+        sum += event.loaded - sum;
+      }
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(sum, testBuffer.byteLength, "Correct loaded progress sum");
+    }
+
+    finishTest();
+    yield;
+  }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/file/test/test_readonly_lockedfiles.html
@@ -0,0 +1,73 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>File Handle Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+  function testSteps()
+  {
+    for each (let fileStorage in fileStorages) {
+      let request = getFileHandle(fileStorage.key, "test.txt");
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield;
+
+      let fileHandle = event.target.result;
+      fileHandle.onerror = errorHandler;
+
+      request = fileHandle.open("readwrite").write({});
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.lockedFile.mode, "readwrite", "Correct mode");
+
+      try {
+        fileHandle.open().write({});
+        ok(false, "Writing to a readonly locked file should fail!");
+      }
+      catch (e) {
+        ok(true, "Writing to a readonly locked file failed");
+      }
+
+      try {
+        fileHandle.open().append({});
+        ok(false, "Appending to a readonly locked file should fail!");
+      }
+      catch (e) {
+        ok(true, "Appending to a readonly locked file failed");
+      }
+
+      try {
+        fileHandle.open().truncate({});
+        ok(false, "Truncating a readonly locked file should fail!");
+      }
+      catch (e) {
+        ok(true, "Truncating a readonly locked file failed");
+      }
+
+      try {
+        fileHandle.open().flush({});
+        ok(false, "Flushing a readonly locked file should fail!");
+      }
+      catch (e) {
+        ok(true, "Flushing a readonly locked file failed");
+      }
+    }
+
+    finishTest();
+    yield;
+  }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/file/test/test_request_readyState.html
@@ -0,0 +1,57 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>File Handle Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+  function testSteps()
+  {
+    for each (let fileStorage in fileStorages) {
+      let request = getFileHandle(fileStorage.key, "test.txt");
+      is(request.readyState, "pending", "Correct readyState");
+
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield;
+
+      is(request.readyState, "done", "Correct readyState");
+
+      let fileHandle = event.target.result;
+      fileHandle.onerror = errorHandler;
+
+      let lockedFile = fileHandle.open("readwrite");
+      request = lockedFile.write("string");
+      is(request.readyState, "pending", "Correct readyState");
+
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(request.readyState, "done", "Correct readyState");
+
+      lockedFile.location = 0;
+      request = lockedFile.readAsText(6);
+      request.onsuccess = grabEventAndContinueHandler;
+      is(request.readyState, "pending", "Correct readyState");
+      event = yield;
+
+      ok(event.target.result, "Got something");
+      is(request.readyState, "done", "Correct readyState");
+    }
+
+    finishTest();
+    yield;
+  }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/file/test/test_stream_tracking.html
@@ -0,0 +1,86 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>File Handle Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+  function testSteps()
+  {
+    var testBuffer = getRandomBuffer(100000);
+
+    for each (let fileStorage in fileStorages) {
+      let request = getFileHandle(fileStorage.key, "test.txt");
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield;
+
+      let fileHandle = event.target.result;
+      fileHandle.onerror = errorHandler;
+
+      let lockedFile = fileHandle.open("readwrite");
+
+      request = lockedFile.write(testBuffer);
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      request = fileHandle.getFile();
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      let file = event.target.result;
+
+      let resultBuffer1;
+      let resultBuffer2;
+
+      let reader1 = new FileReader();
+      reader1.readAsArrayBuffer(file);
+      reader1.onerror = errorHandler;
+      reader1.onload = function(event)
+      {
+        resultBuffer1 = event.target.result;
+
+        let reader = new FileReader();
+        try {
+          reader.readAsArrayBuffer(file);
+          ok(false, "Should have thrown!");
+        }
+        catch (e) {
+          ok(e instanceof DOMException, "Got exception.");
+          is(e.name, "LockedFileInactiveError", "Good error.");
+          is(e.code, 0, "Good error code.");
+        }
+      }
+
+      let reader2 = new FileReader();
+      reader2.readAsArrayBuffer(file);
+      reader2.onerror = errorHandler;
+      reader2.onload = function(event)
+      {
+        resultBuffer2 = event.target.result;
+      }
+
+      lockedFile = event.target.lockedFile;
+      lockedFile.oncomplete = grabEventAndContinueHandler;
+      yield;
+
+      ok(compareBuffers(resultBuffer1, testBuffer), "Correct data");
+      ok(compareBuffers(resultBuffer2, testBuffer), "Correct data");
+    }
+
+    finishTest();
+    yield;
+  }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/file/test/test_success_events_after_abort.html
@@ -0,0 +1,66 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>File Handle Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+  function testSteps()
+  {
+    for each (let fileStorage in fileStorages) {
+      let request = getFileHandle(fileStorage.key, "test.txt");
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield;
+
+      let fileHandle = event.target.result;
+      fileHandle.onerror = errorHandler;
+
+      let lockedFile = fileHandle.open();
+
+      lockedFile.oncomplete = unexpectedSuccessHandler;
+      lockedFile.onabort = grabEventAndContinueHandler;
+
+      let sawError = false;
+
+      request = lockedFile.getMetadata({ size: true });
+      request.onsuccess = unexpectedSuccessHandler;
+      request.onerror = function(event) {
+        is(event.target.error.name, "AbortError", "Good error");
+        sawError = true;
+        event.stopPropagation();
+      }
+
+      lockedFile.abort();
+
+      event = yield;
+
+      is(event.type, "abort", "Got abort event");
+      is(sawError, true, "Saw getMetadata() error");
+
+      // Make sure the success event isn't queued somehow.
+      netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+      var thread = Components.classes["@mozilla.org/thread-manager;1"]
+                             .getService(Components.interfaces.nsIThreadManager)
+                             .currentThread;
+      while (thread.hasPendingEvents()) {
+        thread.processNextEvent(false);
+      }
+    }  
+
+    finishTest();
+    yield;
+  }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/file/test/test_truncate.html
@@ -0,0 +1,61 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>File Handle Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+  function testSteps()
+  {
+    var testBuffer = getRandomBuffer(100000);
+
+    for each (let fileStorage in fileStorages) {
+      let request = getFileHandle(fileStorage.key, "test.bin");
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield;
+
+      let fileHandle = event.target.result;
+      fileHandle.onerror = errorHandler;
+
+      let lockedFile = fileHandle.open("readwrite");
+      request = lockedFile.write(testBuffer);
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(lockedFile.location, 100000, "Correct location");
+
+      for (let i = 0; i < 10; i++) {
+        let location = lockedFile.location - 10000
+        lockedFile.location = location;
+
+        request = lockedFile.truncate();
+        request.onsuccess = grabEventAndContinueHandler;
+        event = yield;
+
+        is(lockedFile.location, location, "Correct location");
+
+        request = lockedFile.getMetadata({ size: true });
+        request.onsuccess = grabEventAndContinueHandler;
+        event = yield;
+
+        is(event.target.result.size, location, "Correct size");
+      }
+    }
+
+    finishTest();
+    yield;
+  }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/file/test/test_write_read_data.html
@@ -0,0 +1,101 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>File Handle Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+  function testSteps()
+  {
+    var testString = "Lorem ipsum his ponderum delicatissimi ne, at noster dolores urbanitas pro, cibo elaboraret no his. Ea dicunt maiorum usu. Ad appareat facilisis mediocritatem eos. Tale graeci mentitum in eos, hinc insolens at nam. Graecis nominavi aliquyam eu vix. Id solet assentior sadipscing pro. Et per atqui graecis, usu quot viris repudiandae ei, mollis evertitur an nam. At nam dolor ignota, liber labore omnesque ea mei, has movet voluptaria in. Vel an impetus omittantur. Vim movet option salutandi ex, ne mei ignota corrumpit. Mucius comprehensam id per. Est ea putant maiestatis.";
+    for (let i = 0; i < 5; i++) {
+      testString += testString;
+    }
+
+    var testBuffer = getRandomBuffer(100000);
+
+    var testBlob = getBlob("binary/random", testBuffer);
+
+    for each (let fileStorage in fileStorages) {
+      let request = getFileHandle(fileStorage.key, "test.txt");
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield;
+
+      let fileHandle = event.target.result;
+      fileHandle.onerror = errorHandler;
+
+      let location = 0;
+
+      let lockedFile = fileHandle.open("readwrite");
+      is(lockedFile.location, location, "Correct location");
+
+      request = lockedFile.write(testString);
+      location += testString.length;
+      is(lockedFile.location, location, "Correct location");
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      lockedFile.location = 0;
+      request = lockedFile.readAsText(testString.length);
+      is(lockedFile.location, location, "Correct location");
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      let resultString = event.target.result;
+      ok(resultString == testString, "Correct string data");
+
+      request = lockedFile.write(testBuffer);
+      location += testBuffer.byteLength;
+      is(lockedFile.location, location, "Correct location");
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      lockedFile.location -= testBuffer.byteLength;
+      request = lockedFile.readAsArrayBuffer(testBuffer.byteLength);
+      is(lockedFile.location, location, "Correct location");
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      let resultBuffer = event.target.result;
+      ok(compareBuffers(resultBuffer, testBuffer), "Correct array buffer data");
+
+      request = lockedFile.write(testBlob);
+      location += testBlob.size;
+      is(lockedFile.location, location, "Correct location");
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      lockedFile.location -= testBlob.size;
+      request = lockedFile.readAsArrayBuffer(testBlob.size);
+      is(lockedFile.location, location, "Correct location");
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      resultBuffer = event.target.result;
+      ok(compareBuffers(resultBuffer, testBuffer), "Correct blob data");
+
+      request = lockedFile.getMetadata({ size: true });
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      let result = event.target.result;
+      is(result.size, location, "Correct size");
+    }
+
+    finishTest();
+    yield;
+  }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/FileStream.cpp
@@ -0,0 +1,321 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "FileStream.h"
+
+#include "nsIFile.h"
+
+#include "nsThreadUtils.h"
+#include "test_quota.h"
+
+USING_INDEXEDDB_NAMESPACE
+
+NS_IMPL_THREADSAFE_ADDREF(FileStream)
+NS_IMPL_THREADSAFE_RELEASE(FileStream)
+
+NS_INTERFACE_MAP_BEGIN(FileStream)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStandardFileStream)
+  NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
+  NS_INTERFACE_MAP_ENTRY(nsIInputStream)
+  NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
+  NS_INTERFACE_MAP_ENTRY(nsIStandardFileStream)
+  NS_INTERFACE_MAP_ENTRY(nsIFileMetadata)
+NS_INTERFACE_MAP_END
+
+NS_IMETHODIMP
+FileStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
+{
+  // TODO: Add support for 64 bit file sizes, bug 752431
+  NS_ENSURE_TRUE(aOffset <= PR_INT32_MAX, NS_ERROR_INVALID_ARG);
+
+  nsresult rv = DoPendingOpen();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!mQuotaFile) {
+    return NS_BASE_STREAM_CLOSED;
+  }
+
+  int whence;
+  switch (aWhence) {
+    case nsISeekableStream::NS_SEEK_SET:
+      whence = SEEK_SET;
+      break;
+    case nsISeekableStream::NS_SEEK_CUR:
+      whence = SEEK_CUR;
+      break;
+    case nsISeekableStream::NS_SEEK_END:
+      whence = SEEK_END;
+      break;
+    default:
+      return NS_ERROR_INVALID_ARG;
+  }
+
+  NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
+
+  int rc = sqlite3_quota_fseek(mQuotaFile, aOffset, whence);
+  NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileStream::Tell(PRInt64* aResult)
+{
+  nsresult rv = DoPendingOpen();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!mQuotaFile) {
+    return NS_BASE_STREAM_CLOSED;
+  }
+
+  NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
+
+  long rc = sqlite3_quota_ftell(mQuotaFile);
+  NS_ENSURE_TRUE(rc >= 0, NS_BASE_STREAM_OSERROR);
+
+  *aResult = rc;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileStream::SetEOF()
+{
+  PRInt64 pos;
+  nsresult rv = Tell(&pos);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
+
+  int rc = sqlite3_quota_ftruncate(mQuotaFile, pos);
+  NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
+
+  return NS_OK;
+}
+
+
+NS_IMETHODIMP
+FileStream::Close()
+{
+  CleanUpOpen();
+
+  if (mQuotaFile) {
+    NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
+
+    int rc = sqlite3_quota_fclose(mQuotaFile);
+    mQuotaFile = nsnull;
+
+    NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileStream::Available(PRUint32* aResult)
+{
+  nsresult rv = DoPendingOpen();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!mQuotaFile) {
+    return NS_BASE_STREAM_CLOSED;
+  }
+
+  NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
+
+  long rc = sqlite3_quota_file_available(mQuotaFile);
+  NS_ENSURE_TRUE(rc >= 0, NS_BASE_STREAM_OSERROR);
+
+  *aResult = rc;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileStream::Read(char* aBuf, PRUint32 aCount, PRUint32* aResult)
+{
+  nsresult rv = DoPendingOpen();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!mQuotaFile) {
+    return NS_BASE_STREAM_CLOSED;
+  }
+
+  NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
+
+  size_t bytesRead = sqlite3_quota_fread(aBuf, 1, aCount, mQuotaFile);
+  if (bytesRead < aCount && sqlite3_quota_ferror(mQuotaFile)) {
+    return NS_BASE_STREAM_OSERROR;
+  }
+
+  *aResult = bytesRead;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
+                         PRUint32 aCount, PRUint32* aResult)
+{
+  NS_NOTREACHED("Don't call me!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+FileStream::IsNonBlocking(bool *aNonBlocking)
+{
+  *aNonBlocking = false;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileStream::Write(const char* aBuf, PRUint32 aCount, PRUint32 *aResult)
+{
+  nsresult rv = DoPendingOpen();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!mQuotaFile) {
+    return NS_BASE_STREAM_CLOSED;
+  }
+
+  NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
+
+  size_t bytesWritten = sqlite3_quota_fwrite(aBuf, 1, aCount, mQuotaFile);
+  if (bytesWritten < aCount) {
+    return NS_BASE_STREAM_OSERROR;
+  }
+
+  *aResult = bytesWritten;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileStream::Flush()
+{
+  nsresult rv = DoPendingOpen();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!mQuotaFile) {
+    return NS_BASE_STREAM_CLOSED;
+  }
+
+  NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
+
+  int rc = sqlite3_quota_fflush(mQuotaFile, 1);
+  NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileStream::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+FileStream::WriteSegments(nsReadSegmentFun reader, void * closure, PRUint32 count, PRUint32 *_retval)
+{
+  NS_NOTREACHED("Don't call me!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+FileStream::Init(nsIFile* aFile, const nsAString& aMode, PRInt32 aFlags)
+{
+  NS_ASSERTION(!mQuotaFile && !mDeferredOpen, "Already initialized!");
+
+  nsresult rv = aFile->GetPath(mFilePath);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mMode = aMode;
+  mFlags = aFlags;
+ 
+  if (mFlags & nsIStandardFileStream::FLAGS_DEFER_OPEN) {
+    mDeferredOpen = true;
+    return NS_OK;
+  }
+
+  return DoOpen();
+}
+
+NS_IMETHODIMP
+FileStream::GetSize(PRInt64* _retval)
+{
+  nsresult rv = DoPendingOpen();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!mQuotaFile) {
+    return NS_BASE_STREAM_CLOSED;
+  }
+
+  NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
+
+  // TODO: Use sqlite3_quota_file_size() here, bug 760783
+  PRInt64 rc = sqlite3_quota_file_truesize(mQuotaFile);
+
+  NS_ASSERTION(rc >= 0, "The file is not under quota management!");
+
+  *_retval = rc;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileStream::GetLastModified(PRInt64* _retval)
+{
+  nsresult rv = DoPendingOpen();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!mQuotaFile) {
+    return NS_BASE_STREAM_CLOSED;
+  }
+
+  NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
+
+  time_t mtime;
+  int rc = sqlite3_quota_file_mtime(mQuotaFile, &mtime);
+  NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
+
+  *_retval = mtime * PR_MSEC_PER_SEC;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileStream::FlushBuffers()
+{
+  nsresult rv = DoPendingOpen();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!mQuotaFile) {
+    return NS_BASE_STREAM_CLOSED;
+  }
+
+  NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
+
+  int rc = sqlite3_quota_fflush(mQuotaFile, 0);
+  NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
+
+  return NS_OK;
+}
+
+nsresult
+FileStream::DoOpen()
+{
+  NS_ASSERTION(!mFilePath.IsEmpty(), "Must have a file path");
+
+  NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
+
+  quota_FILE* quotaFile =
+    sqlite3_quota_fopen(NS_ConvertUTF16toUTF8(mFilePath).get(),
+                        NS_ConvertUTF16toUTF8(mMode).get());
+
+  CleanUpOpen();
+
+  if (!quotaFile) {
+    return NS_BASE_STREAM_OSERROR;
+  }
+
+  mQuotaFile = quotaFile;
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/FileStream.h
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_indexeddb_filestream_h__
+#define mozilla_dom_indexeddb_filestream_h__
+
+#include "IndexedDatabase.h"
+
+#include "nsIFileStreams.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsISeekableStream.h"
+#include "nsIStandardFileStream.h"
+
+class nsIFile;
+struct quota_FILE;
+
+BEGIN_INDEXEDDB_NAMESPACE
+
+class FileStream : public nsISeekableStream,
+                   public nsIInputStream,
+                   public nsIOutputStream,
+                   public nsIStandardFileStream,
+                   public nsIFileMetadata
+{
+public:
+  FileStream()
+  : mFlags(0),
+    mDeferredOpen(false),
+    mQuotaFile(nsnull)
+  { }
+
+  virtual ~FileStream()
+  {
+    Close();
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISEEKABLESTREAM
+  NS_DECL_NSISTANDARDFILESTREAM
+  NS_DECL_NSIFILEMETADATA
+
+  // nsIInputStream
+  NS_IMETHOD
+  Close();
+
+  NS_IMETHOD
+  Available(PRUint32* _retval);
+
+  NS_IMETHOD
+  Read(char* aBuf, PRUint32 aCount, PRUint32* _retval);
+
+  NS_IMETHOD
+  ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, PRUint32 aCount,
+               PRUint32* _retval);
+
+  NS_IMETHOD
+  IsNonBlocking(bool* _retval);
+
+  // nsIOutputStream
+
+  // Close() already declared
+
+  NS_IMETHOD
+  Flush();
+
+  NS_IMETHOD
+  Write(const char* aBuf, PRUint32 aCount, PRUint32* _retval);
+
+  NS_IMETHOD
+  WriteFrom(nsIInputStream* aFromStream, PRUint32 aCount, PRUint32* _retval);
+
+  NS_IMETHOD
+  WriteSegments(nsReadSegmentFun aReader, void* aClosure, PRUint32 aCount,
+                PRUint32* _retval);
+
+  // IsNonBlocking() already declared
+
+protected:
+  /**
+   * Cleans up data prepared in Init.
+   */
+  void
+  CleanUpOpen()
+  {
+    mFilePath.Truncate();
+    mDeferredOpen = false;
+  }
+
+  /**
+   * Open the file. This is called either from Init
+   * or from DoPendingOpen (if FLAGS_DEFER_OPEN is used when initializing this
+   * stream). The default behavior of DoOpen is to open the file and save the
+   * file descriptor.
+   */
+  virtual nsresult
+  DoOpen();
+
+  /**
+   * If there is a pending open, do it now. It's important for this to be
+   * inlined since we do it in almost every stream API call.
+   */
+  nsresult
+  DoPendingOpen()
+  {
+    if (!mDeferredOpen) {
+      return NS_OK;
+    }
+
+    return DoOpen();
+  }
+
+  /**
+   * Data we need to do an open.
+   */
+  nsString mFilePath;
+  nsString mMode;
+
+  /**
+   * Flags describing our behavior.  See the IDL file for possible values.
+   */
+  PRInt32 mFlags;
+
+  /**
+   * Whether we have a pending open (see FLAGS_DEFER_OPEN in the IDL file).
+   */
+  bool mDeferredOpen;
+
+  /**
+   * File descriptor for opened file.
+   */
+  quota_FILE* mQuotaFile;
+};
+
+END_INDEXEDDB_NAMESPACE
+
+#endif // mozilla_dom_indexeddb_filestream_h__
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -996,17 +996,17 @@ nsresult
 ContinueObjectStoreHelper::GatherResultsFromStatement(
                                                mozIStorageStatement* aStatement)
 {
   // Figure out what kind of key we have next.
   nsresult rv = mKey.SetFromStatement(aStatement, 0);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 1, 2,
-    mDatabase->Manager(), mCloneReadInfo);
+    mDatabase, mCloneReadInfo);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 ContinueIndexHelper::BindArgumentsToStatement(mozIStorageStatement* aStatement)
 {
@@ -1066,13 +1066,13 @@ ContinueIndexObjectHelper::GatherResults
 {
   nsresult rv = mKey.SetFromStatement(aStatement, 0);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mObjectKey.SetFromStatement(aStatement, 1);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 2, 3,
-    mDatabase->Manager(), mCloneReadInfo);
+    mDatabase, mCloneReadInfo);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -16,16 +16,17 @@
 #include "nsJSUtils.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
 
 #include "AsyncConnectionHelper.h"
 #include "CheckQuotaHelper.h"
 #include "DatabaseInfo.h"
 #include "IDBEvents.h"
+#include "IDBFileHandle.h"
 #include "IDBIndex.h"
 #include "IDBObjectStore.h"
 #include "IDBTransaction.h"
 #include "IDBFactory.h"
 #include "IndexedDatabaseManager.h"
 #include "TransactionThreadPool.h"
 #include "DictionaryHelpers.h"
 
@@ -85,16 +86,63 @@ public:
   virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
                                   MOZ_OVERRIDE;
 
 private:
   // In-params.
   PRInt64 mObjectStoreId;
 };
 
+class CreateFileHelper : public AsyncConnectionHelper
+{
+public:
+  CreateFileHelper(IDBDatabase* aDatabase,
+                   IDBRequest* aRequest,
+                   const nsAString& aName,
+                   const nsAString& aType)
+  : AsyncConnectionHelper(aDatabase, aRequest),
+    mName(aName), mType(aType)
+  { }
+
+  ~CreateFileHelper()
+  { }
+
+  nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
+  nsresult GetSuccessResult(JSContext* aCx,
+                            jsval* aVal);
+  void ReleaseMainThreadObjects()
+  {
+    mFileInfo = nsnull;
+    AsyncConnectionHelper::ReleaseMainThreadObjects();
+  }
+
+  virtual ChildProcessSendResult MaybeSendResponseToChildProcess(
+                                                           nsresult aResultCode)
+                                                           MOZ_OVERRIDE
+  {
+    return Success_NotSent;
+  }
+
+  virtual nsresult UnpackResponseFromParentProcess(
+                                            const ResponseValue& aResponseValue)
+                                            MOZ_OVERRIDE
+  {
+    MOZ_NOT_REACHED("Should never get here!");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+private:
+  // In-params.
+  nsString mName;
+  nsString mType;
+
+  // Out-params.
+  nsRefPtr<FileInfo> mFileInfo;
+};
+
 NS_STACK_CLASS
 class AutoRemoveObjectStore
 {
 public:
   AutoRemoveObjectStore(DatabaseInfo* aInfo, const nsAString& aName)
   : mInfo(aInfo), mName(aName)
   { }
 
@@ -344,16 +392,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
   NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(versionchange)
 
   // Do some cleanup.
   tmp->OnUnlink();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBDatabase)
   NS_INTERFACE_MAP_ENTRY(nsIIDBDatabase)
+  NS_INTERFACE_MAP_ENTRY(nsIFileStorage)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBDatabase)
 NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache)
 
 NS_IMPL_ADDREF_INHERITED(IDBDatabase, IDBWrapperCache)
 NS_IMPL_RELEASE_INHERITED(IDBDatabase, IDBWrapperCache)
 
 DOMCI_DATA(IDBDatabase, IDBDatabase)
 
@@ -688,26 +737,87 @@ IDBDatabase::Transaction(const jsval& aS
     IDBTransaction::Create(this, storesToOpen, transactionMode, false);
   NS_ENSURE_TRUE(transaction, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   transaction.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+IDBDatabase::MozCreateFileHandle(const nsAString& aName,
+                                 const nsAString& aType,
+                                 nsIIDBRequest** _retval)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (IndexedDatabaseManager::IsShuttingDown()) {
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
+
+  if (mClosed) {
+    return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
+  }
+
+  nsRefPtr<IDBRequest> request = IDBRequest::Create(nsnull, this, nsnull);
+
+  nsRefPtr<CreateFileHelper> helper =
+    new CreateFileHelper(this, request, aName, aType);
+
+  IndexedDatabaseManager* manager = IndexedDatabaseManager::Get();
+  NS_ASSERTION(manager, "We should definitely have a manager here");
+
+  nsresult rv = helper->Dispatch(manager->IOThread());
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  request.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 IDBDatabase::Close()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   CloseInternal(false);
 
   NS_ASSERTION(mClosed, "Should have set the closed flag!");
   return NS_OK;
 }
 
+nsISupports*
+IDBDatabase::StorageId()
+{
+  return Id();
+}
+
+bool
+IDBDatabase::IsStorageInvalidated()
+{
+  return IsInvalidated();
+}
+
+bool
+IDBDatabase::IsStorageShuttingDown()
+{
+  return IndexedDatabaseManager::IsShuttingDown();
+}
+
+void
+IDBDatabase::SetThreadLocals()
+{
+  NS_ASSERTION(GetOwner(), "Should have owner!");
+  IndexedDatabaseManager::SetCurrentWindow(GetOwner());
+}
+
+void
+IDBDatabase::UnsetThreadLocals()
+{
+  IndexedDatabaseManager::SetCurrentWindow(nsnull);
+}
+
 nsresult
 IDBDatabase::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
 {
   NS_ENSURE_TRUE(aVisitor.mDOMEvent, NS_ERROR_UNEXPECTED);
 
   nsPIDOMWindow* owner = GetOwner();
   if (!owner) {
     return NS_OK;
@@ -841,8 +951,40 @@ DeleteObjectStoreHelper::DoDatabaseWork(
   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mObjectStoreId);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   rv = stmt->Execute();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   return NS_OK;
 }
+
+nsresult
+CreateFileHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
+{
+  FileManager* fileManager = mDatabase->Manager();
+
+  mFileInfo = fileManager->GetNewFileInfo();
+  NS_ENSURE_TRUE(mFileInfo, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
+  NS_ENSURE_TRUE(directory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  nsCOMPtr<nsIFile> file = fileManager->GetFileForId(directory, mFileInfo->Id());
+  NS_ENSURE_TRUE(file, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  nsresult rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  return NS_OK;
+}
+
+nsresult
+CreateFileHelper::GetSuccessResult(JSContext* aCx,
+                                   jsval* aVal)
+{
+  nsRefPtr<IDBFileHandle> fileHandle =
+    IDBFileHandle::Create(mDatabase, mName, mType, mFileInfo.forget());
+  NS_ENSURE_TRUE(fileHandle, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  return WrapNative(aCx, NS_ISUPPORTS_CAST(nsIDOMFileHandle*, fileHandle),
+                    aVal);
+}
--- a/dom/indexedDB/IDBDatabase.h
+++ b/dom/indexedDB/IDBDatabase.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_indexeddb_idbdatabase_h__
 #define mozilla_dom_indexeddb_idbdatabase_h__
 
 #include "mozilla/dom/indexedDB/IndexedDatabase.h"
 
 #include "nsIDocument.h"
+#include "nsIFileStorage.h"
 #include "nsIIDBDatabase.h"
 #include "nsDOMEventTargetHelper.h"
 #include "mozilla/dom/indexedDB/IDBWrapperCache.h"
 #include "mozilla/dom/indexedDB/FileManager.h"
 
 class nsIScriptContext;
 class nsPIDOMWindow;
 
@@ -26,24 +27,26 @@ class IDBIndex;
 class IDBObjectStore;
 class IDBTransaction;
 class IndexedDatabaseManager;
 class IndexedDBDatabaseChild;
 class IndexedDBDatabaseParent;
 struct ObjectStoreInfoGuts;
 
 class IDBDatabase : public IDBWrapperCache,
-                    public nsIIDBDatabase
+                    public nsIIDBDatabase,
+                    public nsIFileStorage
 {
   friend class AsyncConnectionHelper;
   friend class IndexedDatabaseManager;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIIDBDATABASE
+  NS_DECL_NSIFILESTORAGE
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBDatabase, IDBWrapperCache)
 
   static already_AddRefed<IDBDatabase>
   Create(IDBWrapperCache* aOwnerCache,
          already_AddRefed<DatabaseInfo> aDatabaseInfo,
          const nsACString& aASCIIOrigin,
          FileManager* aFileManager);
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/IDBFileHandle.cpp
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "IDBFileHandle.h"
+
+#include "nsIStandardFileStream.h"
+
+#include "nsDOMClassInfoID.h"
+
+#include "FileStream.h"
+#include "mozilla/dom/file/File.h"
+
+USING_INDEXEDDB_NAMESPACE
+
+namespace {
+
+inline
+already_AddRefed<nsIFile>
+GetFileFor(FileInfo* aFileInfo)
+
+{
+  FileManager* fileManager = aFileInfo->Manager();
+  nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
+  NS_ENSURE_TRUE(directory, nsnull);
+
+  nsCOMPtr<nsIFile> file = fileManager->GetFileForId(directory,
+                                                     aFileInfo->Id());
+  NS_ENSURE_TRUE(file, nsnull);
+
+  return file.forget();
+}
+
+} // anonymous namespace
+
+// static
+already_AddRefed<IDBFileHandle>
+IDBFileHandle::Create(IDBDatabase* aDatabase,
+                      const nsAString& aName,
+                      const nsAString& aType,
+                      already_AddRefed<FileInfo> aFileInfo)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsRefPtr<FileInfo> fileInfo(aFileInfo);
+  NS_ASSERTION(fileInfo, "Null pointer!");
+
+  nsRefPtr<IDBFileHandle> newFile = new IDBFileHandle();
+
+  newFile->BindToOwner(aDatabase);
+
+  newFile->mFileStorage = aDatabase;
+  newFile->mName = aName;
+  newFile->mType = aType;
+
+  newFile->mFile = GetFileFor(fileInfo);
+  NS_ENSURE_TRUE(newFile->mFile, nsnull);
+  newFile->mFileName.AppendInt(fileInfo->Id());
+
+  fileInfo.swap(newFile->mFileInfo);
+
+  return newFile.forget();
+}
+
+already_AddRefed<nsISupports>
+IDBFileHandle::CreateStream(nsIFile* aFile, bool aReadOnly)
+{
+  nsRefPtr<FileStream> stream = new FileStream();
+
+  nsString streamMode;
+  if (aReadOnly) {
+    streamMode.AssignLiteral("rb");
+  }
+  else {
+    streamMode.AssignLiteral("r+b");
+  }
+
+  nsresult rv = stream->Init(aFile, streamMode,
+                             nsIStandardFileStream::FLAGS_DEFER_OPEN);
+  NS_ENSURE_SUCCESS(rv, nsnull);
+
+  nsCOMPtr<nsISupports> result =
+    NS_ISUPPORTS_CAST(nsIStandardFileStream*, stream);
+  return result.forget();
+}
+
+already_AddRefed<nsIDOMFile>
+IDBFileHandle::CreateFileObject(mozilla::dom::file::LockedFile* aLockedFile,
+                                PRUint32 aFileSize)
+{
+  nsCOMPtr<nsIDOMFile> file = new mozilla::dom::file::File(
+    mName, mType, aFileSize, mFile, aLockedFile, mFileInfo);
+
+  return file.forget();
+}
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBFileHandle)
+  NS_INTERFACE_MAP_ENTRY(nsIIDBFileHandle)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBFileHandle)
+NS_INTERFACE_MAP_END_INHERITING(FileHandle)
+
+NS_IMPL_ADDREF_INHERITED(IDBFileHandle, FileHandle)
+NS_IMPL_RELEASE_INHERITED(IDBFileHandle, FileHandle)
+
+DOMCI_DATA(IDBFileHandle, IDBFileHandle)
+
+NS_IMETHODIMP
+IDBFileHandle::GetDatabase(nsIIDBDatabase** aDatabase)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsCOMPtr<nsIIDBDatabase> database = do_QueryInterface(mFileStorage);
+  NS_ASSERTION(database, "This should always succeed!");
+
+  database.forget(aDatabase);
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/IDBFileHandle.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_indexeddb_idbfilehandle_h__
+#define mozilla_dom_indexeddb_idbfilehandle_h__
+
+#include "IndexedDatabase.h"
+
+#include "nsIIDBFileHandle.h"
+
+#include "mozilla/dom/file/FileHandle.h"
+#include "mozilla/dom/indexedDB/FileInfo.h"
+
+BEGIN_INDEXEDDB_NAMESPACE
+
+class IDBFileHandle : public mozilla::dom::file::FileHandle,
+                      public nsIIDBFileHandle
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIIDBFILEHANDLE
+
+  NS_IMETHOD_(PRInt64)
+  GetFileId()
+  {
+    return mFileInfo->Id();
+  }
+
+  NS_IMETHOD_(FileInfo*)
+  GetFileInfo()
+  {
+    return mFileInfo;
+  }
+
+  static already_AddRefed<IDBFileHandle>
+  Create(IDBDatabase* aDatabase, const nsAString& aName,
+         const nsAString& aType, already_AddRefed<FileInfo> aFileInfo);
+
+  virtual already_AddRefed<nsISupports>
+  CreateStream(nsIFile* aFile, bool aReadOnly);
+
+  virtual already_AddRefed<nsIDOMFile>
+  CreateFileObject(mozilla::dom::file::LockedFile* aLockedFile,
+                   PRUint32 aFileSize);
+
+private:
+  IDBFileHandle()
+  { }
+
+  ~IDBFileHandle()
+  { }
+
+  nsRefPtr<FileInfo> mFileInfo;
+};
+
+END_INDEXEDDB_NAMESPACE
+
+#endif // mozilla_dom_indexeddb_idbfilehandle_h__
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -1167,17 +1167,17 @@ GetHelper::DoDatabaseWork(mozIStorageCon
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool hasResult;
   rv = stmt->ExecuteStep(&hasResult);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (hasResult) {
     rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
-      mDatabase->Manager(), mCloneReadInfo);
+      mDatabase, mCloneReadInfo);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult
 GetHelper::GetSuccessResult(JSContext* aCx,
@@ -1483,17 +1483,17 @@ GetAllHelper::DoDatabaseWork(mozIStorage
     if (mCloneReadInfos.Capacity() == mCloneReadInfos.Length()) {
       mCloneReadInfos.SetCapacity(mCloneReadInfos.Capacity() * 2);
     }
 
     StructuredCloneReadInfo* readInfo = mCloneReadInfos.AppendElement();
     NS_ASSERTION(readInfo, "This shouldn't fail!");
 
     rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
-      mDatabase->Manager(), *readInfo);
+      mDatabase, *readInfo);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   return NS_OK;
 }
 
 nsresult
@@ -2018,17 +2018,17 @@ OpenCursorHelper::DoDatabaseWork(mozISto
 
   rv = mKey.SetFromStatement(stmt, 0);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mObjectKey.SetFromStatement(stmt, 1);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 2, 3,
-    mDatabase->Manager(), mCloneReadInfo);
+    mDatabase, mCloneReadInfo);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Now we need to make the query to get the next match.
   nsCAutoString queryStart =
     NS_LITERAL_CSTRING("SELECT index_table.value, "
                        "index_table.object_data_key, object_data.data, "
                        "object_data.file_ids FROM ") +
     indexTable +
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -4,16 +4,17 @@
  * 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/. */
 
 #include "base/basictypes.h"
 
 #include "IDBObjectStore.h"
 
 #include "nsIJSContextStack.h"
+#include "nsIOutputStream.h"
 
 #include "jsfriendapi.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/storage.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfo.h"
 #include "nsDOMFile.h"
@@ -22,18 +23,20 @@
 #include "nsJSUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "snappy/snappy.h"
 #include "test_quota.h"
 #include "xpcpublic.h"
 
 #include "AsyncConnectionHelper.h"
+#include "FileStream.h"
 #include "IDBCursor.h"
 #include "IDBEvents.h"
+#include "IDBFileHandle.h"
 #include "IDBIndex.h"
 #include "IDBKeyRange.h"
 #include "IDBTransaction.h"
 #include "DatabaseInfo.h"
 #include "DictionaryHelpers.h"
 
 #include "ipc/IndexedDBChild.h"
 #include "ipc/IndexedDBParent.h"
@@ -905,17 +908,17 @@ IDBObjectStore::UpdateIndexes(IDBTransac
 }
 
 // static
 nsresult
 IDBObjectStore::GetStructuredCloneReadInfoFromStatement(
                                            mozIStorageStatement* aStatement,
                                            PRUint32 aDataIndex,
                                            PRUint32 aFileIdsIndex,
-                                           FileManager* aFileManager,
+                                           IDBDatabase* aDatabase,
                                            StructuredCloneReadInfo& aInfo)
 {
 #ifdef DEBUG
   {
     PRInt32 type;
     NS_ASSERTION(NS_SUCCEEDED(aStatement->GetTypeOfIndex(aDataIndex, &type)) &&
                  type == mozIStorageStatement::VALUE_TYPE_BLOB,
                  "Bad value type!");
@@ -951,36 +954,38 @@ IDBObjectStore::GetStructuredCloneReadIn
                    uncompressedLength)) {
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   bool isNull;
   rv = aStatement->GetIsNull(aFileIdsIndex, &isNull);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  if (isNull) {
-    return NS_OK;
+  if (!isNull) {
+    nsString ids;
+    rv = aStatement->GetString(aFileIdsIndex, ids);
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+    nsAutoTArray<PRInt64, 10> array;
+    rv = ConvertFileIdsToArray(ids, array);
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+    FileManager* fileManager = aDatabase->Manager();
+
+    for (PRUint32 i = 0; i < array.Length(); i++) {
+      const PRInt64& id = array[i];
+
+      nsRefPtr<FileInfo> fileInfo = fileManager->GetFileInfo(id);
+      NS_ASSERTION(fileInfo, "Null file info!");
+
+      aInfo.mFileInfos.AppendElement(fileInfo);
+    }
   }
 
-  nsString ids;
-  rv = aStatement->GetString(aFileIdsIndex, ids);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  nsAutoTArray<PRInt64, 10> array;
-  rv = ConvertFileIdsToArray(ids, array);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  for (PRUint32 i = 0; i < array.Length(); i++) {
-    const PRInt64& id = array.ElementAt(i);
-
-    nsRefPtr<FileInfo> fileInfo = aFileManager->GetFileInfo(id);
-    NS_ASSERTION(fileInfo, "Null file info!");
-
-    aInfo.mFileInfos.AppendElement(fileInfo);
-  }
+  aInfo.mDatabase = aDatabase;
 
   return NS_OK;
 }
 
 // static
 void
 IDBObjectStore::ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer)
 {
@@ -1097,35 +1102,71 @@ StructuredCloneReadString(JSStructuredCl
 
 JSObject*
 IDBObjectStore::StructuredCloneReadCallback(JSContext* aCx,
                                             JSStructuredCloneReader* aReader,
                                             uint32_t aTag,
                                             uint32_t aData,
                                             void* aClosure)
 {
-  if (aTag == SCTAG_DOM_BLOB || aTag == SCTAG_DOM_FILE) {
+  if (aTag == SCTAG_DOM_FILEHANDLE || aTag == SCTAG_DOM_BLOB ||
+      aTag == SCTAG_DOM_FILE) {
     StructuredCloneReadInfo* cloneReadInfo =
       reinterpret_cast<StructuredCloneReadInfo*>(aClosure);
 
     if (aData >= cloneReadInfo->mFileInfos.Length()) {
       NS_ERROR("Bad blob index!");
       return nsnull;
     }
 
     nsRefPtr<FileInfo> fileInfo = cloneReadInfo->mFileInfos[aData];
-    nsRefPtr<FileManager> fileManager = fileInfo->Manager();
+    IDBDatabase* database = cloneReadInfo->mDatabase;
+
+    if (aTag == SCTAG_DOM_FILEHANDLE) {
+      nsCString type;
+      if (!StructuredCloneReadString(aReader, type)) {
+        return nsnull;
+      }
+      NS_ConvertUTF8toUTF16 convType(type);
+
+      nsCString name;
+      if (!StructuredCloneReadString(aReader, name)) {
+        return nsnull;
+      }
+      NS_ConvertUTF8toUTF16 convName(name);
+
+      nsRefPtr<IDBFileHandle> fileHandle = IDBFileHandle::Create(database,
+        convName, convType, fileInfo.forget());
+
+      jsval wrappedFileHandle;
+      nsresult rv =
+        nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx),
+                                   static_cast<nsIDOMFileHandle*>(fileHandle),
+                                   &NS_GET_IID(nsIDOMFileHandle),
+                                   &wrappedFileHandle);
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Failed to wrap native!");
+        return nsnull;
+      }
+
+      return JSVAL_TO_OBJECT(wrappedFileHandle);
+    }
+
+    FileManager* fileManager = database->Manager();
+
     nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
     if (!directory) {
+      NS_WARNING("Failed to get directory!");
       return nsnull;
     }
 
     nsCOMPtr<nsIFile> nativeFile =
       fileManager->GetFileForId(directory, fileInfo->Id());
     if (!nativeFile) {
+      NS_WARNING("Failed to get file!");
       return nsnull;
     }
 
     PRUint64 size;
     if (!JS_ReadBytes(aReader, &size, sizeof(PRUint64))) {
       NS_WARNING("Failed to read size!");
       return nsnull;
     }
@@ -1204,56 +1245,131 @@ IDBObjectStore::StructuredCloneWriteCall
 
   nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
   nsContentUtils::XPConnect()->
     GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative));
 
   if (wrappedNative) {
     nsISupports* supports = wrappedNative->Native();
 
+    IDBTransaction* transaction = cloneWriteInfo->mTransaction;
+    FileManager* fileManager = transaction->Database()->Manager();
+
     nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports);
     if (blob) {
-      nsCOMPtr<nsIDOMFile> file = do_QueryInterface(blob);
+      // Check if it is a blob created from this db or the blob was already
+      // stored in this db
+
+      nsRefPtr<FileInfo> fileInfo = transaction->GetFileInfo(blob);
+      nsCOMPtr<nsIInputStream> inputStream;
+
+      if (!fileInfo) {
+        fileInfo = blob->GetFileInfo(fileManager);
+      }
+
+      if (!fileInfo) {
+        fileInfo = fileManager->GetNewFileInfo();
+        if (!fileInfo) {
+          NS_WARNING("Failed to get new file info!");
+          return false;
+        }
+
+        if (NS_FAILED(blob->GetInternalStream(getter_AddRefs(inputStream)))) {
+          NS_WARNING("Failed to get internal steam!");
+          return false;
+        }
+
+        transaction->AddFileInfo(blob, fileInfo);
+      }
 
       PRUint64 size;
       if (NS_FAILED(blob->GetSize(&size))) {
+        NS_WARNING("Failed to get size!");
         return false;
       }
       size = SwapBytes(size);
 
       nsString type;
       if (NS_FAILED(blob->GetType(type))) {
+        NS_WARNING("Failed to get type!");
         return false;
       }
       NS_ConvertUTF16toUTF8 convType(type);
       PRUint32 convTypeLength = SwapBytes(convType.Length());
 
+      nsCOMPtr<nsIDOMFile> file = do_QueryInterface(blob);
+
       if (!JS_WriteUint32Pair(aWriter, file ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB,
-                              cloneWriteInfo->mBlobs.Length()) ||
+                              cloneWriteInfo->mFiles.Length()) ||
           !JS_WriteBytes(aWriter, &size, sizeof(PRUint64)) ||
           !JS_WriteBytes(aWriter, &convTypeLength, sizeof(PRUint32)) ||
           !JS_WriteBytes(aWriter, convType.get(), convType.Length())) {
         return false;
       }
 
       if (file) {
         nsString name;
         if (NS_FAILED(file->GetName(name))) {
+          NS_WARNING("Failed to get name!");
           return false;
         }
         NS_ConvertUTF16toUTF8 convName(name);
         PRUint32 convNameLength = SwapBytes(convName.Length());
 
         if (!JS_WriteBytes(aWriter, &convNameLength, sizeof(PRUint32)) ||
             !JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
           return false;
         }
       }
 
-      cloneWriteInfo->mBlobs.AppendElement(blob);
+      StructuredCloneFile* cloneFile = cloneWriteInfo->mFiles.AppendElement();
+      cloneFile->mFile = blob.forget();
+      cloneFile->mFileInfo = fileInfo.forget();
+      cloneFile->mInputStream = inputStream.forget();
+
+      return true;
+    }
+
+    nsCOMPtr<nsIDOMFileHandle> fileHandle = do_QueryInterface(supports);
+    if (fileHandle) {
+      nsRefPtr<FileInfo> fileInfo = fileHandle->GetFileInfo();
+
+      // Throw when trying to store non IDB file handles or IDB file handles
+      // across databases.
+      if (!fileInfo || fileInfo->Manager() != fileManager) {
+        return false;
+      }
+
+      nsString type;
+      if (NS_FAILED(fileHandle->GetType(type))) {
+        NS_WARNING("Failed to get type!");
+        return false;
+      }
+      NS_ConvertUTF16toUTF8 convType(type);
+      PRUint32 convTypeLength = SwapBytes(convType.Length());
+
+      nsString name;
+      if (NS_FAILED(fileHandle->GetName(name))) {
+        NS_WARNING("Failed to get name!");
+        return false;
+      }
+      NS_ConvertUTF16toUTF8 convName(name);
+      PRUint32 convNameLength = SwapBytes(convName.Length());
+
+      if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILEHANDLE,
+                              cloneWriteInfo->mFiles.Length()) ||
+          !JS_WriteBytes(aWriter, &convTypeLength, sizeof(PRUint32)) ||
+          !JS_WriteBytes(aWriter, convType.get(), convType.Length()) ||
+          !JS_WriteBytes(aWriter, &convNameLength, sizeof(PRUint32)) ||
+          !JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
+        return false;
+      }
+
+      StructuredCloneFile* file = cloneWriteInfo->mFiles.AppendElement();
+      file->mFileInfo = fileInfo.forget();
 
       return true;
     }
   }
 
   // try using the runtime callbacks
   const JSStructuredCloneCallbacks* runtimeCallbacks =
     js::GetContextStructuredCloneCallbacks(aCx);
@@ -1460,16 +1576,17 @@ IDBObjectStore::GetAddInfo(JSContext* aC
             break;
           }
         }
       }
     }
   }
 
   aCloneWriteInfo.mOffsetToKeyProp = 0;
+  aCloneWriteInfo.mTransaction = mTransaction;
 
   // We guard on rv being a success because we need to run the property
   // deletion code below even if we should not be serializing the value
   if (NS_SUCCEEDED(rv) && 
       !IDBObjectStore::SerializeValue(aCx, aCloneWriteInfo, aValue)) {
     rv = NS_ERROR_DOM_DATA_CLONE_ERR;
   }
 
@@ -2374,36 +2491,40 @@ IDBObjectStore::Count(const jsval& aKey,
     return rv;
   }
 
   request.forget(_retval);
   return NS_OK;
 }
 
 inline nsresult
-CopyData(nsIInputStream* aStream, quota_FILE* aFile)
+CopyData(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream)
 {
+  nsresult rv;
+
   do {
     char copyBuffer[FILE_COPY_BUFFER_SIZE];
 
     PRUint32 numRead;
-    nsresult rv = aStream->Read(copyBuffer, FILE_COPY_BUFFER_SIZE, &numRead);
+    rv = aInputStream->Read(copyBuffer, FILE_COPY_BUFFER_SIZE, &numRead);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (numRead <= 0) {
       break;
     }
 
-    size_t numWrite = sqlite3_quota_fwrite(copyBuffer, 1, numRead, aFile);
+    PRUint32 numWrite;
+    rv = aOutputStream->Write(copyBuffer, numRead, &numWrite);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     NS_ENSURE_TRUE(numWrite == numRead, NS_ERROR_FAILURE);
   } while (true);
 
-  // Flush and sync
-  NS_ENSURE_TRUE(sqlite3_quota_fflush(aFile, 1) == 0,
-                 NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  rv = aOutputStream->Flush();
+  NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 void
 ObjectStoreHelper::ReleaseMainThreadObjects()
 {
   mObjectStore = nsnull;
@@ -2565,60 +2686,38 @@ AddHelper::DoDatabaseWork(mozIStorageCon
 
   // Handle blobs
   nsRefPtr<FileManager> fileManager = mDatabase->Manager();
   nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
   NS_ENSURE_TRUE(directory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsAutoString fileIds;
 
-  for (PRUint32 index = 0; index < mCloneWriteInfo.mBlobs.Length(); index++) {
-    nsCOMPtr<nsIDOMBlob>& domBlob = mCloneWriteInfo.mBlobs[index];
-
-    PRInt64 id = -1;
-
-    // Check if it is a blob created from this db or the blob was already
-    // stored in this db
-    nsRefPtr<FileInfo> fileInfo = domBlob->GetFileInfo(fileManager);
-    if (fileInfo) {
-      id = fileInfo->Id();
-    }
-
-    if (id == -1) {
-      fileInfo = fileManager->GetNewFileInfo();
-      NS_ENSURE_TRUE(fileInfo, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-      id = fileInfo->Id();
-
-      mTransaction->OnNewFileInfo(fileInfo);
-
+  PRUint32 length = mCloneWriteInfo.mFiles.Length();
+  for (PRUint32 index = 0; index < length; index++) {
+    StructuredCloneFile& cloneFile = mCloneWriteInfo.mFiles[index];
+
+    FileInfo* fileInfo = cloneFile.mFileInfo;
+    nsIInputStream* inputStream = cloneFile.mInputStream;
+
+    PRInt64 id = fileInfo->Id();
+    if (inputStream) {
       // Copy it
-      nsCOMPtr<nsIInputStream> inputStream;
-      rv = domBlob->GetInternalStream(getter_AddRefs(inputStream));
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-      nsCOMPtr<nsIFile> nativeFile = fileManager->GetFileForId(directory, id);
+      nsCOMPtr<nsIFile> nativeFile =
+        fileManager->GetFileForId(directory, id);
       NS_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-      nsString nativeFilePath;
-      rv = nativeFile->GetPath(nativeFilePath);
+      nsRefPtr<FileStream> outputStream = new FileStream();
+      rv = outputStream->Init(nativeFile, NS_LITERAL_STRING("wb"), 0);
       NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-      quota_FILE* file =
-        sqlite3_quota_fopen(NS_ConvertUTF16toUTF8(nativeFilePath).get(), "wb");
-      NS_ENSURE_TRUE(file, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-      rv = CopyData(inputStream, file);
-
-      NS_ENSURE_TRUE(sqlite3_quota_fclose(file) == 0,
-                     NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
+      rv = CopyData(inputStream, outputStream);
       NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-      domBlob->AddFileInfo(fileInfo);
+      cloneFile.mFile->AddFileInfo(fileInfo);
     }
 
     if (index) {
       fileIds.Append(NS_LITERAL_STRING(" "));
     }
     fileIds.AppendInt(id);
   }
 
@@ -2772,17 +2871,17 @@ GetHelper::DoDatabaseWork(mozIStorageCon
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool hasResult;
   rv = stmt->ExecuteStep(&hasResult);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (hasResult) {
     rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
-      mDatabase->Manager(), mCloneReadInfo);
+      mDatabase, mCloneReadInfo);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult
 GetHelper::GetSuccessResult(JSContext* aCx,
@@ -3082,17 +3181,17 @@ OpenCursorHelper::DoDatabaseWork(mozISto
     mKey.Unset();
     return NS_OK;
   }
 
   rv = mKey.SetFromStatement(stmt, 0);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2,
-    mDatabase->Manager(), mCloneReadInfo);
+    mDatabase, mCloneReadInfo);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Now we need to make the query to get the next match.
   keyRangeClause.Truncate();
   nsCAutoString continueToKeyRangeClause;
 
   NS_NAMED_LITERAL_CSTRING(currentKey, "current_key");
   NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
@@ -3456,17 +3555,17 @@ CreateIndexHelper::InsertDataFromObjectS
   }
 
   JSContext* cx = tlsEntry->Context();
   JSAutoRequest ar(cx);
 
   do {
     StructuredCloneReadInfo cloneReadInfo;
     rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2,
-      mDatabase->Manager(), cloneReadInfo);
+      mDatabase, cloneReadInfo);
     NS_ENSURE_SUCCESS(rv, rv);
 
     JSAutoStructuredCloneBuffer& buffer = cloneReadInfo.mCloneBuffer;
 
     JSStructuredCloneCallbacks callbacks = {
       IDBObjectStore::StructuredCloneReadCallback,
       nsnull,
       nsnull
@@ -3607,17 +3706,17 @@ GetAllHelper::DoDatabaseWork(mozIStorage
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
     }
 
     StructuredCloneReadInfo* readInfo = mCloneReadInfos.AppendElement();
     NS_ASSERTION(readInfo, "Shouldn't fail if SetCapacity succeeded!");
 
     rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
-      mDatabase->Manager(), *readInfo);
+      mDatabase, *readInfo);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   return NS_OK;
 }
 
 nsresult
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -68,17 +68,17 @@ public:
                 bool aOverwrite,
                 PRInt64 aObjectDataId,
                 const nsTArray<IndexUpdateInfo>& aUpdateInfoArray);
 
   static nsresult
   GetStructuredCloneReadInfoFromStatement(mozIStorageStatement* aStatement,
                                           PRUint32 aDataIndex,
                                           PRUint32 aFileIdsIndex,
-                                          FileManager* aFileManager,
+                                          IDBDatabase* aDatabase,
                                           StructuredCloneReadInfo& aInfo);
 
   static void
   ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer);
 
   static bool
   DeserializeValue(JSContext* aCx,
                    StructuredCloneReadInfo& aCloneReadInfo,
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -108,16 +108,18 @@ IDBTransaction::CreateInternal(IDBDataba
 
   transaction->mDatabase = aDatabase;
   transaction->mMode = aMode;
   transaction->mDatabaseInfo = aDatabase->Info();
   transaction->mObjectStoreNames.AppendElements(aObjectStoreNames);
 
   IndexedDBTransactionChild* actor = nsnull;
 
+  transaction->mCreatedFileInfos.Init();
+
   if (IndexedDatabaseManager::IsMainProcess()) {
     transaction->mCachedStatements.Init();
 
     if (aMode != IDBTransaction::VERSION_CHANGE) {
       TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate();
       NS_ENSURE_TRUE(pool, nsnull);
 
       pool->Dispatch(transaction, &gStartTransactionRunnable, false, nsnull);
@@ -465,20 +467,28 @@ IDBTransaction::GetOrCreateObjectStore(c
   retval = IDBObjectStore::Create(this, aObjectStoreInfo, mDatabaseInfo->id,
                                   aCreating);
 
   mCreatedObjectStores.AppendElement(retval);
 
   return retval.forget();
 }
 
+already_AddRefed<FileInfo>
+IDBTransaction::GetFileInfo(nsIDOMBlob* aBlob)
+{
+  nsRefPtr<FileInfo> fileInfo;
+  mCreatedFileInfos.Get(aBlob, getter_AddRefs(fileInfo));
+  return fileInfo.forget();
+}
+
 void
-IDBTransaction::OnNewFileInfo(FileInfo* aFileInfo)
+IDBTransaction::AddFileInfo(nsIDOMBlob* aBlob, FileInfo* aFileInfo)
 {
-  mCreatedFileInfos.AppendElement(aFileInfo);
+  mCreatedFileInfos.Put(aBlob, aFileInfo);
 }
 
 void
 IDBTransaction::ClearCreatedFileInfos()
 {
   mCreatedFileInfos.Clear();
 }
 
--- a/dom/indexedDB/IDBTransaction.h
+++ b/dom/indexedDB/IDBTransaction.h
@@ -147,17 +147,18 @@ public:
     return mDatabaseInfo;
   }
 
   already_AddRefed<IDBObjectStore>
   GetOrCreateObjectStore(const nsAString& aName,
                          ObjectStoreInfo* aObjectStoreInfo,
                          bool aCreating);
 
-  void OnNewFileInfo(FileInfo* aFileInfo);
+  already_AddRefed<FileInfo> GetFileInfo(nsIDOMBlob* aBlob);
+  void AddFileInfo(nsIDOMBlob* aBlob, FileInfo* aFileInfo);
 
   void ClearCreatedFileInfos();
 
   void
   SetActor(IndexedDBTransactionChild* aActorChild)
   {
     NS_ASSERTION(!aActorChild || !mActorChild, "Shouldn't have more than one!");
     mActorChild = aActorChild;
@@ -225,17 +226,17 @@ private:
   nsCOMPtr<mozIStorageConnection> mConnection;
 
   // Only touched on the database thread.
   PRUint32 mSavepointCount;
 
   nsTArray<nsRefPtr<IDBObjectStore> > mCreatedObjectStores;
 
   nsRefPtr<UpdateRefcountFunction> mUpdateFileRefcountFunction;
-  nsTArray<nsRefPtr<FileInfo> > mCreatedFileInfos;
+  nsRefPtrHashtable<nsISupportsHashKey, FileInfo> mCreatedFileInfos;
 
   IndexedDBTransactionChild* mActorChild;
   IndexedDBTransactionParent* mActorParent;
 
   nsresult mAbortCode;
   bool mCreating;
 
 #ifdef DEBUG
--- a/dom/indexedDB/IndexedDatabase.h
+++ b/dom/indexedDB/IndexedDatabase.h
@@ -23,37 +23,53 @@
 
 #define END_INDEXEDDB_NAMESPACE \
   } /* namespace indexedDB */ } /* namepsace dom */ } /* namespace mozilla */
 
 #define USING_INDEXEDDB_NAMESPACE \
   using namespace mozilla::dom::indexedDB;
 
 class nsIDOMBlob;
+class nsIInputStream;
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class FileInfo;
+class IDBDatabase;
+class IDBTransaction;
+
+template <class T>
+void SwapData(T& aData1, T& aData2)
+{
+  T temp = aData2;
+  aData2 = aData1;
+  aData1 = temp;
+}
 
 struct SerializedStructuredCloneReadInfo;
 
 struct StructuredCloneReadInfo
 {
+  // In IndexedDatabaseInlines.h
+  inline StructuredCloneReadInfo();
+
   void Swap(StructuredCloneReadInfo& aCloneReadInfo)
   {
     mCloneBuffer.swap(aCloneReadInfo.mCloneBuffer);
     mFileInfos.SwapElements(aCloneReadInfo.mFileInfos);
+    SwapData(mDatabase, aCloneReadInfo.mDatabase);
   }
 
   // In IndexedDatabaseInlines.h
   inline bool
   SetFromSerialized(const SerializedStructuredCloneReadInfo& aOther);
 
   JSAutoStructuredCloneBuffer mCloneBuffer;
   nsTArray<nsRefPtr<FileInfo> > mFileInfos;
+  IDBDatabase* mDatabase;
 };
 
 struct SerializedStructuredCloneReadInfo
 {
   SerializedStructuredCloneReadInfo()
   : data(nsnull), dataLength(0)
   { }
 
@@ -72,41 +88,61 @@ struct SerializedStructuredCloneReadInfo
     return *this;
   }
 
   // Make sure to update ipc/SerializationHelpers.h when changing members here!
   uint64_t* data;
   size_t dataLength;
 };
 
+struct StructuredCloneFile
+{
+  bool operator==(const StructuredCloneFile& aOther) const
+  {
+    return this->mFile == aOther.mFile &&
+           this->mFileInfo == aOther.mFileInfo &&
+           this->mInputStream == aOther.mInputStream;
+  }
+
+  nsCOMPtr<nsIDOMBlob> mFile;
+  nsRefPtr<FileInfo> mFileInfo;
+  nsCOMPtr<nsIInputStream> mInputStream;
+};
+
 struct SerializedStructuredCloneWriteInfo;
 
 struct StructuredCloneWriteInfo
 {
+  // In IndexedDatabaseInlines.h
+  inline StructuredCloneWriteInfo();
+
   void Swap(StructuredCloneWriteInfo& aCloneWriteInfo)
   {
     mCloneBuffer.swap(aCloneWriteInfo.mCloneBuffer);
-    mBlobs.SwapElements(aCloneWriteInfo.mBlobs);
-    mOffsetToKeyProp = aCloneWriteInfo.mOffsetToKeyProp;
+    mFiles.SwapElements(aCloneWriteInfo.mFiles);
+    SwapData(mTransaction, aCloneWriteInfo.mTransaction);
+    SwapData(mOffsetToKeyProp, aCloneWriteInfo.mOffsetToKeyProp);
   }
 
   bool operator==(const StructuredCloneWriteInfo& aOther) const
   {
     return this->mCloneBuffer.nbytes() == aOther.mCloneBuffer.nbytes() &&
            this->mCloneBuffer.data() == aOther.mCloneBuffer.data() &&
-           this->mBlobs == aOther.mBlobs &&
+           this->mFiles == aOther.mFiles &&
+           this->mTransaction == aOther.mTransaction &&
            this->mOffsetToKeyProp == aOther.mOffsetToKeyProp;
   }
 
   // In IndexedDatabaseInlines.h
   inline bool
   SetFromSerialized(const SerializedStructuredCloneWriteInfo& aOther);
 
   JSAutoStructuredCloneBuffer mCloneBuffer;
-  nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs;
+  nsTArray<StructuredCloneFile> mFiles;
+  IDBTransaction* mTransaction;
   PRUint64 mOffsetToKeyProp;
 };
 
 struct SerializedStructuredCloneWriteInfo
 {
   SerializedStructuredCloneWriteInfo()
   : data(nsnull), dataLength(0), offsetToKeyProp(0)
   { }
--- a/dom/indexedDB/IndexedDatabaseInlines.h
+++ b/dom/indexedDB/IndexedDatabaseInlines.h
@@ -6,33 +6,46 @@
 
 #ifndef mozilla_dom_indexeddb_indexeddatabase_h__
 #error Must include IndexedDatabase.h first
 #endif
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 inline
+StructuredCloneWriteInfo::StructuredCloneWriteInfo()
+: mTransaction(nsnull),
+  mOffsetToKeyProp(0)
+{
+}
+
+inline
 bool
 StructuredCloneWriteInfo::SetFromSerialized(
                                const SerializedStructuredCloneWriteInfo& aOther)
 {
   if (!aOther.dataLength) {
     mCloneBuffer.clear();
   }
   else if (!mCloneBuffer.copy(aOther.data, aOther.dataLength)) {
     return false;
   }
 
-  mBlobs.Clear();
+  mFiles.Clear();
   mOffsetToKeyProp = aOther.offsetToKeyProp;
   return true;
 }
 
 inline
+StructuredCloneReadInfo::StructuredCloneReadInfo()
+: mDatabase(nsnull)
+{
+}
+
+inline
 bool
 StructuredCloneReadInfo::SetFromSerialized(
                                 const SerializedStructuredCloneReadInfo& aOther)
 {
   if (aOther.dataLength &&
       !mCloneBuffer.copy(aOther.data, aOther.dataLength)) {
     return false;
   }
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -4,24 +4,26 @@
  * 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/. */
 
 #include "IndexedDatabaseManager.h"
 #include "DatabaseInfo.h"
 
 #include "nsIDOMScriptObjectFactory.h"
 #include "nsIFile.h"
+#include "nsIFileStorage.h"
 #include "nsILocalFile.h"
 #include "nsIObserverService.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsISHEntry.h"
 #include "nsISimpleEnumerator.h"
 #include "nsITimer.h"
 
+#include "mozilla/dom/file/FileService.h"
 #include "mozilla/LazyIdleThread.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/storage.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsContentUtils.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsThreadUtils.h"
@@ -51,16 +53,17 @@
 #define DEFAULT_QUOTA_MB 50
 
 // Preference that users can set to override DEFAULT_QUOTA_MB
 #define PREF_INDEXEDDB_QUOTA "dom.indexedDB.warningQuota"
 
 USING_INDEXEDDB_NAMESPACE
 using namespace mozilla::services;
 using mozilla::Preferences;
+using mozilla::dom::file::FileService;
 
 static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
 namespace {
 
 PRInt32 gShutdown = 0;
 PRInt32 gClosed = 0;
 
@@ -108,28 +111,29 @@ public:
 
     return NS_ERROR_FAILURE;
   }
 };
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(QuotaCallback, mozIStorageQuotaCallback)
 
 // Adds all databases in the hash to the given array.
+template <class T>
 PLDHashOperator
 EnumerateToTArray(const nsACString& aKey,
                   nsTArray<IDBDatabase*>* aValue,
                   void* aUserArg)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
   NS_ASSERTION(aValue, "Null pointer!");
   NS_ASSERTION(aUserArg, "Null pointer!");
 
-  nsTArray<IDBDatabase*>* array =
-    static_cast<nsTArray<IDBDatabase*>*>(aUserArg);
+  nsTArray<T>* array =
+    static_cast<nsTArray<T>*>(aUserArg);
 
   if (!array->AppendElements(*aValue)) {
     NS_WARNING("Out of memory!");
     return PL_DHASH_STOP;
   }
 
   return PL_DHASH_NEXT;
 }
@@ -513,52 +517,61 @@ IndexedDatabaseManager::IsClosed()
 
 void
 IndexedDatabaseManager::AbortCloseDatabasesForWindow(nsPIDOMWindow* aWindow)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aWindow, "Null pointer!");
 
   nsAutoTArray<IDBDatabase*, 50> liveDatabases;
-  mLiveDatabases.EnumerateRead(EnumerateToTArray, &liveDatabases);
+  mLiveDatabases.EnumerateRead(EnumerateToTArray<IDBDatabase*>,
+                               &liveDatabases);
 
+  FileService* service = FileService::Get();
   TransactionThreadPool* pool = TransactionThreadPool::Get();
 
   for (PRUint32 index = 0; index < liveDatabases.Length(); index++) {
     IDBDatabase*& database = liveDatabases[index];
     if (database->GetOwner() == aWindow) {
       if (NS_FAILED(database->Close())) {
         NS_WARNING("Failed to close database for dying window!");
       }
 
+      if (service) {
+        service->AbortLockedFilesForStorage(database);
+      }
+
       if (pool) {
         pool->AbortTransactionsForDatabase(database);
       }
     }
   }
 }
 
 bool
 IndexedDatabaseManager::HasOpenTransactions(nsPIDOMWindow* aWindow)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aWindow, "Null pointer!");
 
   nsAutoTArray<IDBDatabase*, 50> liveDatabases;
-  mLiveDatabases.EnumerateRead(EnumerateToTArray, &liveDatabases);
-
+  mLiveDatabases.EnumerateRead(EnumerateToTArray<IDBDatabase*>,
+                               &liveDatabases);
+  
+  FileService* service = FileService::Get();
   TransactionThreadPool* pool = TransactionThreadPool::Get();
-  if (!pool) {
+  if (!service && !pool) {
     return false;
   }
 
   for (PRUint32 index = 0; index < liveDatabases.Length(); index++) {
     IDBDatabase*& database = liveDatabases[index];
     if (database->GetOwner() == aWindow &&
-        pool->HasTransactionsForDatabase(database)) {
+        ((service && service->HasLockedFilesForStorage(database)) ||
+         (pool && pool->HasTransactionsForDatabase(database)))) {
       return true;
     }
   }
   
   return false;
 }
 
 void
@@ -582,31 +595,48 @@ IndexedDatabaseManager::OnDatabaseClosed
         NS_ASSERTION(op->mHelper, "How did we get rid of the helper before "
                      "removing the last database?");
         if (op->mDatabases.IsEmpty()) {
           // At this point, all databases are closed, so no new transactions
           // can be started.  There may, however, still be outstanding
           // transactions that have not completed.  We need to wait for those
           // before we dispatch the helper.
 
-          TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate();
-          if (!pool) {
-            NS_ERROR("IndexedDB is totally broken.");
-            return;
+          FileService* service = FileService::Get();
+          TransactionThreadPool* pool = TransactionThreadPool::Get();
+
+          PRUint32 count = !!service + !!pool;
+
+          nsRefPtr<WaitForTransactionsToFinishRunnable> runnable =
+            new WaitForTransactionsToFinishRunnable(op,
+                                                    NS_MAX<PRUint32>(count, 1));
+
+          if (!count) {
+            runnable->Run();
           }
+          else {
+            // Use the WaitForTransactionsToxFinishRunnable as the callback.
 
-          nsRefPtr<WaitForTransactionsToFinishRunnable> waitRunnable =
-            new WaitForTransactionsToFinishRunnable(op);
+            if (service) {
+              nsTArray<nsCOMPtr<nsIFileStorage> > array;
+              array.AppendElement(aDatabase);
+
+              if (!service->WaitForAllStoragesToComplete(array, runnable)) {
+                NS_WARNING("Failed to wait for storages to complete!");
+              }
+            }
 
-          nsAutoTArray<nsRefPtr<IDBDatabase>, 1> array;
-          array.AppendElement(aDatabase);
+            if (pool) {
+              nsTArray<nsRefPtr<IDBDatabase> > array;
+              array.AppendElement(aDatabase);
 
-          // Use the WaitForTransactionsToFinishRunnable as the callback.
-          if (!pool->WaitForAllDatabasesToComplete(array, waitRunnable)) {
-            NS_WARNING("Failed to wait for transaction to complete!");
+              if (!pool->WaitForAllDatabasesToComplete(array, runnable)) {
+                NS_WARNING("Failed to wait for databases to complete!");
+              }
+            }