Backed out 8 changesets (bug 1173320) for adding dom/filesystem/tests/test_basic.html without making it work on Android
authorPhil Ringnalda <philringnalda@gmail.com>
Sat, 19 Mar 2016 19:44:22 -0700
changeset 289524 de7baace275ad30bb1eae8e5fe85a9a8f5d1e779
parent 289523 8461e2ffc339629b431d93be4c8bc59db9327de6
child 289525 f14898695ee0dd14615914f3e1401f17df57fdd7
push id18270
push userryanvm@gmail.com
push dateSun, 20 Mar 2016 04:26:12 +0000
treeherderfx-team@f14898695ee0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1173320
milestone48.0a1
backs out7e3a105b9160c3ea312c8739bee6073dc409fab1
094819fbb07fecf582f8281dfd99f4ba71995ceb
23b0e55ff1db18caef396b2fa64e1b25b9a7dc44
8bf8e2fcedad3e92c25533087400e106d58bf3aa
3de390c6f47f7650bc5dcb0d1fde80660ff97a28
fb298010e12ee2e6ce20d83775563dbdd30965bc
f1a965c2f7969c8121e313226792de83efacd160
176128ba757f02042c701813352a4120bbd77660
Backed out 8 changesets (bug 1173320) for adding dom/filesystem/tests/test_basic.html without making it work on Android Backed out changeset 7e3a105b9160 (bug 1173320) Backed out changeset 094819fbb07f (bug 1173320) Backed out changeset 23b0e55ff1db (bug 1173320) Backed out changeset 8bf8e2fcedad (bug 1173320) Backed out changeset 3de390c6f47f (bug 1173320) Backed out changeset fb298010e12e (bug 1173320) Backed out changeset f1a965c2f796 (bug 1173320) Backed out changeset 176128ba757f (bug 1173320)
dom/base/File.cpp
dom/base/File.h
dom/base/FileList.cpp
dom/base/FileList.h
dom/base/StructuredCloneHolder.cpp
dom/base/nsContentUtils.cpp
dom/base/test/script_postmessages_fileList.js
dom/base/test/test_postMessages.html
dom/devicestorage/DeviceStorage.h
dom/devicestorage/DeviceStorageStatics.cpp
dom/devicestorage/nsDeviceStorage.cpp
dom/devicestorage/test/devicestorage_common.js
dom/devicestorage/test/test_fs_createDirectory.html
dom/devicestorage/test/test_overrideDir.html
dom/events/DataTransfer.cpp
dom/events/DataTransfer.h
dom/filehandle/ActorsChild.cpp
dom/filesystem/CreateDirectoryTask.cpp
dom/filesystem/CreateDirectoryTask.h
dom/filesystem/CreateFileTask.cpp
dom/filesystem/CreateFileTask.h
dom/filesystem/DeviceStorageFileSystem.cpp
dom/filesystem/DeviceStorageFileSystem.h
dom/filesystem/Directory.cpp
dom/filesystem/Directory.h
dom/filesystem/FileSystemBase.cpp
dom/filesystem/FileSystemBase.h
dom/filesystem/FileSystemPermissionRequest.cpp
dom/filesystem/FileSystemRequestParent.cpp
dom/filesystem/FileSystemTaskBase.cpp
dom/filesystem/FileSystemTaskBase.h
dom/filesystem/FileSystemUtils.cpp
dom/filesystem/FileSystemUtils.h
dom/filesystem/GetDirectoryListingTask.cpp
dom/filesystem/GetDirectoryListingTask.h
dom/filesystem/GetFileOrDirectoryTask.cpp
dom/filesystem/GetFileOrDirectoryTask.h
dom/filesystem/OSFileSystem.cpp
dom/filesystem/OSFileSystem.h
dom/filesystem/PFileSystemRequest.ipdl
dom/filesystem/RemoveTask.cpp
dom/filesystem/RemoveTask.h
dom/filesystem/moz.build
dom/filesystem/tests/mochitest.ini
dom/filesystem/tests/moz.build
dom/filesystem/tests/script_fileList.js
dom/filesystem/tests/test_basic.html
dom/html/HTMLInputElement.cpp
dom/html/HTMLInputElement.h
dom/indexedDB/FileSnapshot.h
dom/indexedDB/IDBObjectStore.cpp
dom/ipc/Blob.cpp
dom/ipc/BlobChild.h
dom/ipc/DOMTypes.ipdlh
dom/ipc/FilePickerParent.cpp
dom/ipc/FilePickerParent.h
dom/ipc/PContent.ipdl
dom/ipc/PFilePicker.ipdl
dom/webidl/Directory.webidl
dom/webidl/FileList.webidl
dom/webidl/HTMLInputElement.webidl
embedding/browser/nsDocShellTreeOwner.cpp
widget/nsBaseFilePicker.cpp
widget/nsFilePickerProxy.cpp
widget/nsFilePickerProxy.h
--- a/dom/base/File.cpp
+++ b/dom/base/File.cpp
@@ -222,16 +222,22 @@ Blob::Blob(nsISupports* aParent, BlobImp
 }
 
 bool
 Blob::IsFile() const
 {
   return mImpl->IsFile();
 }
 
+bool
+Blob::IsDirectory() const
+{
+  return mImpl->IsDirectory();
+}
+
 const nsTArray<RefPtr<BlobImpl>>*
 Blob::GetSubBlobImpls() const
 {
   return mImpl->GetSubBlobImpls();
 }
 
 already_AddRefed<File>
 Blob::ToFile()
@@ -409,20 +415,21 @@ File::Create(nsISupports* aParent, BlobI
   MOZ_ASSERT(aImpl->IsFile());
 
   return new File(aParent, aImpl);
 }
 
 /* static */ already_AddRefed<File>
 File::Create(nsISupports* aParent, const nsAString& aName,
              const nsAString& aContentType, uint64_t aLength,
-             int64_t aLastModifiedDate)
+             int64_t aLastModifiedDate, BlobDirState aDirState)
 {
   RefPtr<File> file = new File(aParent,
-    new BlobImplBase(aName, aContentType, aLength, aLastModifiedDate));
+    new BlobImplBase(aName, aContentType, aLength, aLastModifiedDate,
+                     aDirState));
   return file.forget();
 }
 
 /* static */ already_AddRefed<File>
 File::CreateMemoryFile(nsISupports* aParent, void* aMemoryBuffer,
                        uint64_t aLength, const nsAString& aName,
                        const nsAString& aContentType,
                        int64_t aLastModifiedDate)
@@ -934,16 +941,27 @@ void
 BlobImplFile::SetPath(const nsAString& aPath)
 {
   MOZ_ASSERT(aPath.IsEmpty() ||
              aPath[aPath.Length() - 1] == char16_t('/'),
              "Path must end with a path separator");
   mPath = aPath;
 }
 
+void
+BlobImplFile::LookupAndCacheIsDirectory()
+{
+  MOZ_ASSERT(mIsFile,
+             "This should only be called when this object has been created "
+             "from an nsIFile to note that the nsIFile is a directory");
+  bool isDir;
+  mFile->IsDirectory(&isDir);
+  mDirState = isDir ? BlobDirState::eIsDir : BlobDirState::eIsNotDir;
+}
+
 ////////////////////////////////////////////////////////////////////////////
 // EmptyBlobImpl implementation
 
 NS_IMPL_ISUPPORTS_INHERITED0(EmptyBlobImpl, BlobImpl)
 
 already_AddRefed<BlobImpl>
 EmptyBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
                            const nsAString& aContentType,
--- a/dom/base/File.h
+++ b/dom/base/File.h
@@ -39,16 +39,28 @@ namespace dom {
 
 struct BlobPropertyBag;
 struct ChromeFilePropertyBag;
 struct FilePropertyBag;
 class BlobImpl;
 class File;
 class OwningArrayBufferOrArrayBufferViewOrBlobOrString;
 
+/**
+ * Used to indicate when a Blob/BlobImpl that was created from an nsIFile
+ * (when IsFile() will return true) was from an nsIFile for which
+ * nsIFile::IsDirectory() returned true. This is a tri-state to enable us to
+ * assert that the state is always set when callers request it.
+ */
+enum BlobDirState : uint32_t {
+  eIsDir,
+  eIsNotDir,
+  eUnknownIfDir
+};
+
 class Blob : public nsIDOMBlob
            , public nsIXHRSendable
            , public nsIMutable
            , public nsSupportsWeakReference
            , public nsWrapperCache
 {
 public:
   NS_DECL_NSIDOMBLOB
@@ -84,23 +96,32 @@ public:
 
   BlobImpl* Impl() const
   {
     return mImpl;
   }
 
   bool IsFile() const;
 
+  /**
+   * This may return true if the Blob was created from an nsIFile that is a
+   * directory.
+   */
+  bool IsDirectory() const;
+
   const nsTArray<RefPtr<BlobImpl>>* GetSubBlobImpls() const;
 
   // This method returns null if this Blob is not a File; it returns
   // the same object in case this Blob already implements the File interface;
   // otherwise it returns a new File object with the same BlobImpl.
   already_AddRefed<File> ToFile();
 
+  // XXXjwatt Consider having a ToDirectory() method. The need for a FileSystem
+  // object complicates that though.
+
   // This method creates a new File object with the given name and the same
   // BlobImpl.
   already_AddRefed<File> ToFile(const nsAString& aName,
                                 ErrorResult& aRv) const;
 
   already_AddRefed<Blob>
   CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType,
               ErrorResult& aRv);
@@ -168,17 +189,17 @@ public:
   // Note: BlobImpl must be a File in order to use this method.
   // Check impl->IsFile().
   static File*
   Create(nsISupports* aParent, BlobImpl* aImpl);
 
   static already_AddRefed<File>
   Create(nsISupports* aParent, const nsAString& aName,
          const nsAString& aContentType, uint64_t aLength,
-         int64_t aLastModifiedDate);
+         int64_t aLastModifiedDate, BlobDirState aDirState);
 
   // The returned File takes ownership of aMemoryBuffer. aMemoryBuffer will be
   // freed by free so it must be allocated by malloc or something
   // compatible with it.
   static already_AddRefed<File>
   CreateMemoryFile(nsISupports* aParent, void* aMemoryBuffer, uint64_t aLength,
                    const nsAString& aName, const nsAString& aContentType,
                    int64_t aLastModifiedDate);
@@ -313,78 +334,105 @@ public:
   virtual bool IsMemoryFile() const = 0;
 
   virtual bool IsSizeUnknown() const = 0;
 
   virtual bool IsDateUnknown() const = 0;
 
   virtual bool IsFile() const = 0;
 
+  /**
+   * Called when this BlobImpl was created from an nsIFile in order to call
+   * nsIFile::IsDirectory() and cache the result so that when the BlobImpl is
+   * copied to another process that informaton is available.
+   * nsIFile::IsDirectory() does synchronous I/O, and BlobImpl objects may be
+   * created on the main thread or in a non-chrome process (where I/O is not
+   * allowed). Do not call this on a non-chrome process, and preferably do not
+   * call it on the main thread.
+   *
+   * Not all creators of BlobImplFile will call this method, in which case
+   * calling IsDirectory will MOZ_ASSERT.
+   */
+  virtual void LookupAndCacheIsDirectory() = 0;
+  virtual void SetIsDirectory(bool aIsDir) = 0;
+  virtual bool IsDirectory() const = 0;
+
+  /**
+   * Prefer IsDirectory(). This exists to help consumer code pass on state from
+   * one BlobImpl when creating another.
+   */
+  virtual BlobDirState GetDirState() const = 0;
+
   // True if this implementation can be sent to other threads.
   virtual bool MayBeClonedToOtherThreads() const
   {
     return true;
   }
 
 protected:
   virtual ~BlobImpl() {}
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(BlobImpl, BLOBIMPL_IID)
 
 class BlobImplBase : public BlobImpl
 {
 public:
   BlobImplBase(const nsAString& aName, const nsAString& aContentType,
-               uint64_t aLength, int64_t aLastModifiedDate)
+               uint64_t aLength, int64_t aLastModifiedDate,
+               BlobDirState aDirState = BlobDirState::eUnknownIfDir)
     : mIsFile(true)
     , mImmutable(false)
+    , mDirState(aDirState)
     , mContentType(aContentType)
     , mName(aName)
     , mStart(0)
     , mLength(aLength)
     , mLastModificationDate(aLastModifiedDate)
     , mSerialNumber(NextSerialNumber())
   {
     // Ensure non-null mContentType by default
     mContentType.SetIsVoid(false);
   }
 
   BlobImplBase(const nsAString& aName, const nsAString& aContentType,
                uint64_t aLength)
     : mIsFile(true)
     , mImmutable(false)
+    , mDirState(BlobDirState::eUnknownIfDir)
     , mContentType(aContentType)
     , mName(aName)
     , mStart(0)
     , mLength(aLength)
     , mLastModificationDate(INT64_MAX)
     , mSerialNumber(NextSerialNumber())
   {
     // Ensure non-null mContentType by default
     mContentType.SetIsVoid(false);
   }
 
   BlobImplBase(const nsAString& aContentType, uint64_t aLength)
     : mIsFile(false)
     , mImmutable(false)
+    , mDirState(BlobDirState::eUnknownIfDir)
     , mContentType(aContentType)
     , mStart(0)
     , mLength(aLength)
     , mLastModificationDate(INT64_MAX)
     , mSerialNumber(NextSerialNumber())
   {
     // Ensure non-null mContentType by default
     mContentType.SetIsVoid(false);
   }
 
   BlobImplBase(const nsAString& aContentType, uint64_t aStart,
                uint64_t aLength)
     : mIsFile(false)
     , mImmutable(false)
+    , mDirState(BlobDirState::eUnknownIfDir)
     , mContentType(aContentType)
     , mStart(aStart)
     , mLength(aLength)
     , mLastModificationDate(INT64_MAX)
     , mSerialNumber(NextSerialNumber())
   {
     NS_ASSERTION(aLength != UINT64_MAX,
                  "Must know length when creating slice");
@@ -465,16 +513,46 @@ public:
     return mIsFile && mLastModificationDate == INT64_MAX;
   }
 
   virtual bool IsFile() const override
   {
     return mIsFile;
   }
 
+  virtual void LookupAndCacheIsDirectory() override
+  {
+    MOZ_ASSERT(false, "Why is this being called on a non-BlobImplFile?");
+  }
+
+  virtual void SetIsDirectory(bool aIsDir) override
+  {
+    MOZ_ASSERT(mIsFile,
+               "This should only be called when this object has been created "
+               "from an nsIFile to note that the nsIFile is a directory");
+    mDirState = aIsDir ? BlobDirState::eIsDir : BlobDirState::eIsNotDir;
+  }
+
+  /**
+   * Returns true if the nsIFile that this object wraps is a directory.
+   */
+  virtual bool IsDirectory() const override
+  {
+    MOZ_ASSERT(mDirState != BlobDirState::eUnknownIfDir,
+               "Must only be used by callers for whom the code paths are "
+               "know to call LookupAndCacheIsDirectory() or "
+               "SetIsDirectory()");
+    return mDirState == BlobDirState::eIsDir;
+  }
+
+  virtual BlobDirState GetDirState() const override
+  {
+    return mDirState;
+  }
+
   virtual bool IsSizeUnknown() const override
   {
     return mLength == UINT64_MAX;
   }
 
 protected:
   virtual ~BlobImplBase() {}
 
@@ -482,16 +560,17 @@ protected:
    * Returns a new, effectively-unique serial number. This should be used
    * by implementations to obtain a serial number for GetSerialNumber().
    * The implementation is thread safe.
    */
   static uint64_t NextSerialNumber();
 
   bool mIsFile;
   bool mImmutable;
+  BlobDirState mDirState;
 
   nsString mContentType;
   nsString mName;
   nsString mPath; // The path relative to a directory chosen by the user
 
   uint64_t mStart;
   uint64_t mLength;
 
@@ -506,17 +585,18 @@ protected:
  */
 class BlobImplMemory final : public BlobImplBase
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   BlobImplMemory(void* aMemoryBuffer, uint64_t aLength, const nsAString& aName,
                  const nsAString& aContentType, int64_t aLastModifiedDate)
-    : BlobImplBase(aName, aContentType, aLength, aLastModifiedDate)
+    : BlobImplBase(aName, aContentType, aLength, aLastModifiedDate,
+                   BlobDirState::eIsNotDir)
     , mDataOwner(new DataOwner(aMemoryBuffer, aLength))
   {
     NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");
   }
 
   BlobImplMemory(void* aMemoryBuffer, uint64_t aLength,
                  const nsAString& aContentType)
     : BlobImplBase(aContentType, aLength)
@@ -707,16 +787,18 @@ public:
   virtual void SetLastModified(int64_t aLastModified) override;
   virtual void GetMozFullPathInternal(nsAString& aFullPath,
                                       ErrorResult& aRv) const override;
   virtual void GetInternalStream(nsIInputStream** aInputStream,
                                  ErrorResult& aRv) override;
 
   void SetPath(const nsAString& aFullPath);
 
+  virtual void LookupAndCacheIsDirectory() override;
+
   // We always have size and date for this kind of blob.
   virtual bool IsSizeUnknown() const override { return false; }
   virtual bool IsDateUnknown() const override { return false; }
 
 protected:
   virtual ~BlobImplFile() {
     if (mFile && mIsTemporary) {
       NS_WARNING("In temporary ~BlobImplFile");
--- a/dom/base/FileList.cpp
+++ b/dom/base/FileList.cpp
@@ -1,127 +1,48 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=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 "mozilla/dom/Directory.h"
 #include "mozilla/dom/FileList.h"
 #include "mozilla/dom/FileListBinding.h"
 #include "mozilla/dom/File.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileList, mFilesOrDirectories, mParent)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileList, mFiles, mParent)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileList)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFileList)
   NS_INTERFACE_MAP_ENTRY(nsIDOMFileList)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(FileList)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(FileList)
 
 JSObject*
 FileList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return mozilla::dom::FileListBinding::Wrap(aCx, this, aGivenProto);
 }
 
-void
-FileList::Append(File* aFile)
-{
-  OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
-  element->SetAsFile() = aFile;
-}
-
-void
-FileList::Append(Directory* aDirectory)
-{
-  OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
-  element->SetAsDirectory() = aDirectory;
-}
-
 NS_IMETHODIMP
 FileList::GetLength(uint32_t* aLength)
 {
   *aLength = Length();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-FileList::Item(uint32_t aIndex, nsISupports** aValue)
-{
-  if (aIndex >= mFilesOrDirectories.Length()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  if (mFilesOrDirectories[aIndex].IsFile()) {
-    nsCOMPtr<nsIDOMBlob> file = mFilesOrDirectories[aIndex].GetAsFile();
-    file.forget(aValue);
-    return NS_OK;
-  }
-
-  MOZ_ASSERT(mFilesOrDirectories[aIndex].IsDirectory());
-  RefPtr<Directory> directory = mFilesOrDirectories[aIndex].GetAsDirectory();
-  directory.forget(aValue);
-  return NS_OK;
-}
-
-void
-FileList::Item(uint32_t aIndex, Nullable<OwningFileOrDirectory>& aValue,
-               ErrorResult& aRv) const
-{
-  if (aIndex >= mFilesOrDirectories.Length()) {
-    aValue.SetNull();
-    return;
-  }
-
-  aValue.SetValue(mFilesOrDirectories[aIndex]);
-}
-
-void
-FileList::IndexedGetter(uint32_t aIndex, bool& aFound,
-                        Nullable<OwningFileOrDirectory>& aFileOrDirectory,
-                        ErrorResult& aRv) const
+FileList::Item(uint32_t aIndex, nsISupports** aFile)
 {
-  aFound = aIndex < mFilesOrDirectories.Length();
-  Item(aIndex, aFileOrDirectory, aRv);
-}
-
-void
-FileList::ToSequence(Sequence<OwningFileOrDirectory>& aSequence,
-                     ErrorResult& aRv) const
-{
-  MOZ_ASSERT(aSequence.IsEmpty());
-  if (mFilesOrDirectories.IsEmpty()) {
-    return;
-  }
-
-  if (!aSequence.SetLength(mFilesOrDirectories.Length(),
-                           mozilla::fallible_t())) {
-    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-    return;
-  }
-
-  for (uint32_t i = 0; i < mFilesOrDirectories.Length(); ++i) {
-    aSequence[i] = mFilesOrDirectories[i];
-  }
-}
-
-bool
-FileList::ClonableToDifferentThreadOrProcess() const
-{
-  for (uint32_t i = 0; i < mFilesOrDirectories.Length(); ++i) {
-    if (mFilesOrDirectories[i].IsDirectory()) {
-      return false;
-    }
-  }
-
-  return true;
+  nsCOMPtr<nsIDOMBlob> file = Item(aIndex);
+  file.forget(aFile);
+  return NS_OK;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/FileList.h
+++ b/dom/base/FileList.h
@@ -3,17 +3,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/. */
 
 #ifndef mozilla_dom_FileList_h
 #define mozilla_dom_FileList_h
 
 #include "mozilla/dom/BindingDeclarations.h"
-#include "mozilla/dom/UnionTypes.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDOMFileList.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
 
 class BlobImpls;
@@ -35,32 +34,34 @@ public:
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   nsISupports* GetParentObject()
   {
     return mParent;
   }
 
-  void Append(File* aFile);
-  void Append(Directory* aDirectory);
+  bool Append(File* aFile)
+  {
+    return mFiles.AppendElement(aFile);
+  }
 
   bool Remove(uint32_t aIndex)
   {
-    if (aIndex < mFilesOrDirectories.Length()) {
-      mFilesOrDirectories.RemoveElementAt(aIndex);
+    if (aIndex < mFiles.Length()) {
+      mFiles.RemoveElementAt(aIndex);
       return true;
     }
 
     return false;
   }
 
   void Clear()
   {
-    return mFilesOrDirectories.Clear();
+    return mFiles.Clear();
   }
 
   static FileList* FromSupports(nsISupports* aSupports)
   {
 #ifdef DEBUG
     {
       nsCOMPtr<nsIDOMFileList> list_qi = do_QueryInterface(aSupports);
 
@@ -70,43 +71,38 @@ public:
       NS_ASSERTION(list_qi == static_cast<nsIDOMFileList*>(aSupports),
                    "Uh, fix QI!");
     }
 #endif
 
     return static_cast<FileList*>(aSupports);
   }
 
-  const OwningFileOrDirectory& UnsafeItem(uint32_t aIndex) const
+  File* Item(uint32_t aIndex)
   {
-    MOZ_ASSERT(aIndex < Length());
-    return mFilesOrDirectories[aIndex];
+    return mFiles.SafeElementAt(aIndex);
   }
 
-  void Item(uint32_t aIndex,
-            Nullable<OwningFileOrDirectory>& aFileOrDirectory,
-            ErrorResult& aRv) const;
-
-  void IndexedGetter(uint32_t aIndex, bool& aFound,
-                     Nullable<OwningFileOrDirectory>& aFileOrDirectory,
-                     ErrorResult& aRv) const;
-
-  uint32_t Length() const
+  File* IndexedGetter(uint32_t aIndex, bool& aFound)
   {
-    return mFilesOrDirectories.Length();
+    aFound = aIndex < mFiles.Length();
+    if (!aFound) {
+      return nullptr;
+    }
+    return mFiles.ElementAt(aIndex);
   }
 
-  void ToSequence(Sequence<OwningFileOrDirectory>& aSequence,
-                  ErrorResult& aRv) const;
-
-  bool ClonableToDifferentThreadOrProcess() const;
+  uint32_t Length()
+  {
+    return mFiles.Length();
+  }
 
 private:
   ~FileList() {}
 
-  nsTArray<OwningFileOrDirectory> mFilesOrDirectories;
+  nsTArray<RefPtr<File>> mFiles;
   nsCOMPtr<nsISupports> mParent;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_FileList_h
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "StructuredCloneHolder.h"
 
 #include "ImageContainer.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/CryptoKey.h"
-#include "mozilla/dom/Directory.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileList.h"
 #include "mozilla/dom/FileListBinding.h"
 #include "mozilla/dom/FormData.h"
 #include "mozilla/dom/ImageBitmap.h"
 #include "mozilla/dom/ImageBitmapBinding.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/ImageDataBinding.h"
@@ -719,139 +718,94 @@ ReadFileList(JSContext* aCx,
 {
   MOZ_ASSERT(aCx);
   MOZ_ASSERT(aReader);
 
   JS::Rooted<JS::Value> val(aCx);
   {
     RefPtr<FileList> fileList = new FileList(aHolder->ParentDuringRead());
 
-    // |aCount| is the number of Files or Directory for this FileList.
+    uint32_t tag, offset;
+    // Offset is the index of the blobImpl from which we can find the blobImpl
+    // for this FileList.
+    if (!JS_ReadUint32Pair(aReader, &tag, &offset)) {
+      return nullptr;
+    }
+
+    MOZ_ASSERT(tag == 0);
+
+    // |aCount| is the number of BlobImpls to use from the |offset|.
     for (uint32_t i = 0; i < aCount; ++i) {
-      uint32_t tagOrDirectoryType, indexOrLengthOfString;
-      if (!JS_ReadUint32Pair(aReader, &tagOrDirectoryType,
-                             &indexOrLengthOfString)) {
+      uint32_t index = offset + i;
+      MOZ_ASSERT(index < aHolder->BlobImpls().Length());
+
+      RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[index];
+      MOZ_ASSERT(blobImpl->IsFile());
+
+      ErrorResult rv;
+      blobImpl = EnsureBlobForBackgroundManager(blobImpl, nullptr, rv);
+      if (NS_WARN_IF(rv.Failed())) {
+        rv.SuppressException();
         return nullptr;
       }
 
-      MOZ_ASSERT(tagOrDirectoryType == SCTAG_DOM_BLOB ||
-                 tagOrDirectoryType == Directory::eDOMRootDirectory ||
-                 tagOrDirectoryType == Directory::eNotDOMRootDirectory);
-
-      if (tagOrDirectoryType == SCTAG_DOM_BLOB) {
-        MOZ_ASSERT(indexOrLengthOfString < aHolder->BlobImpls().Length());
-
-        RefPtr<BlobImpl> blobImpl =
-          aHolder->BlobImpls()[indexOrLengthOfString];
-        MOZ_ASSERT(blobImpl->IsFile());
+      MOZ_ASSERT(blobImpl);
 
-        ErrorResult rv;
-        blobImpl = EnsureBlobForBackgroundManager(blobImpl, nullptr, rv);
-        if (NS_WARN_IF(rv.Failed())) {
-          rv.SuppressException();
-          return nullptr;
-        }
-
-        RefPtr<File> file =
-          File::Create(aHolder->ParentDuringRead(), blobImpl);
-        MOZ_ASSERT(file);
-
-        fileList->Append(file);
-        continue;
-      }
-
-      nsAutoString path;
-      path.SetLength(indexOrLengthOfString);
-      size_t charSize = sizeof(nsString::char_type);
-      if (!JS_ReadBytes(aReader, (void*) path.BeginWriting(),
-                        indexOrLengthOfString * charSize)) {
+      RefPtr<File> file = File::Create(aHolder->ParentDuringRead(), blobImpl);
+      if (!fileList->Append(file)) {
         return nullptr;
       }
-
-      nsCOMPtr<nsIFile> file;
-      nsresult rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true,
-                                          getter_AddRefs(file));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return nullptr;
-      }
-
-      RefPtr<Directory> directory =
-        Directory::Create(aHolder->ParentDuringRead(), file,
-                          (Directory::DirectoryType) tagOrDirectoryType);
-      fileList->Append(directory);
     }
 
     if (!ToJSValue(aCx, fileList, &val)) {
       return nullptr;
     }
   }
 
   return &val.toObject();
 }
 
 // The format of the FileList serialization is:
 // - pair of ints: SCTAG_DOM_FILELIST, Length of the FileList
-// - for each element of the FileList:
-//   - if it's a blob:
-//    - pair of ints: SCTAG_DOM_BLOB, index of the BlobImpl in the array
-//       mBlobImplArray.
-//   - else:
-//     - pair of ints: 0/1 is root, string length
-//     - value string
+// - pair of ints: 0, The offset of the BlobImpl array
 bool
 WriteFileList(JSStructuredCloneWriter* aWriter,
               FileList* aFileList,
               StructuredCloneHolder* aHolder)
 {
   MOZ_ASSERT(aWriter);
   MOZ_ASSERT(aFileList);
   MOZ_ASSERT(aHolder);
 
+  // A FileList is serialized writing the X number of elements and the offset
+  // from mBlobImplArray. The Read will take X elements from mBlobImplArray
+  // starting from the offset.
   if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST,
-                          aFileList->Length())) {
+                          aFileList->Length()) ||
+      !JS_WriteUint32Pair(aWriter, 0,
+                          aHolder->BlobImpls().Length())) {
     return false;
   }
 
   ErrorResult rv;
   nsTArray<RefPtr<BlobImpl>> blobImpls;
 
   for (uint32_t i = 0; i < aFileList->Length(); ++i) {
-    const OwningFileOrDirectory& data = aFileList->UnsafeItem(i);
-
-    if (data.IsFile()) {
-      RefPtr<BlobImpl> blobImpl =
-        EnsureBlobForBackgroundManager(data.GetAsFile()->Impl(), nullptr, rv);
-      if (NS_WARN_IF(rv.Failed())) {
-        rv.SuppressException();
-        return false;
-      }
-
-      if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
-                              aHolder->BlobImpls().Length())) {
-        return false;
-      }
-
-      aHolder->BlobImpls().AppendElement(blobImpl);
-      continue;
+    RefPtr<BlobImpl> blobImpl =
+      EnsureBlobForBackgroundManager(aFileList->Item(i)->Impl(), nullptr, rv);
+    if (NS_WARN_IF(rv.Failed())) {
+      rv.SuppressException();
+      return false;
     }
 
-    MOZ_ASSERT(data.IsDirectory());
-
-    nsAutoString path;
-    data.GetAsDirectory()->GetFullRealPath(path);
-
-    size_t charSize = sizeof(nsString::char_type);
-    if (!JS_WriteUint32Pair(aWriter,
-                            (uint32_t)data.GetAsDirectory()->Type(),
-                            path.Length()) ||
-        !JS_WriteBytes(aWriter, path.get(), path.Length() * charSize)) {
-      return false;
-    }
+    MOZ_ASSERT(blobImpl);
+    blobImpls.AppendElement(blobImpl);
   }
 
+  aHolder->BlobImpls().AppendElements(blobImpls);
   return true;
 }
 
 // Read the WriteFormData for the format.
 JSObject*
 ReadFormData(JSContext* aCx,
              JSStructuredCloneReader* aReader,
              uint32_t aCount,
@@ -1045,19 +999,17 @@ StructuredCloneHolder::CustomWriteHandle
     if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob))) {
       return WriteBlob(aWriter, blob, this);
     }
   }
 
   // See if this is a FileList object.
   {
     FileList* fileList = nullptr;
-    if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList)) &&
-        (mSupportedContext == SameProcessSameThread ||
-         fileList->ClonableToDifferentThreadOrProcess())) {
+    if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList))) {
       return WriteFileList(aWriter, fileList, this);
     }
   }
 
   // See if this is a FormData object.
   {
     FormData* formData = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(FormData, aObj, formData))) {
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -7620,16 +7620,17 @@ nsContentUtils::TransferableToIPCTransfe
             }
 
             blobImpl = new BlobImplFile(file, false);
             ErrorResult rv;
             // Ensure that file data is cached no that the content process
             // has this data available to it when passed over:
             blobImpl->GetSize(rv);
             blobImpl->GetLastModified(rv);
+            blobImpl->LookupAndCacheIsDirectory();
           } else {
             if (aInSyncMessage) {
               // Can't do anything.
               continue;
             }
             blobImpl = do_QueryInterface(data);
           }
           if (blobImpl) {
--- a/dom/base/test/script_postmessages_fileList.js
+++ b/dom/base/test/script_postmessages_fileList.js
@@ -1,25 +1,15 @@
 var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 Cu.importGlobalProperties(["File"]);
 
+var testFile = Cc["@mozilla.org/file/directory_service;1"]
+                 .getService(Ci.nsIDirectoryService)
+                 .QueryInterface(Ci.nsIProperties)
+                 .get("ProfD", Ci.nsIFile);
+testFile.append("prefs.js");
+
 addMessageListener("file.open", function () {
-  var testFile = Cc["@mozilla.org/file/directory_service;1"]
-                   .getService(Ci.nsIDirectoryService)
-                   .QueryInterface(Ci.nsIProperties)
-                   .get("ProfD", Ci.nsIFile);
-  testFile.append("prefs.js");
-
   sendAsyncMessage("file.opened", {
     file: new File(testFile)
   });
 });
 
-addMessageListener("dir.open", function () {
-  var testFile = Cc["@mozilla.org/file/directory_service;1"]
-                   .getService(Ci.nsIDirectoryService)
-                   .QueryInterface(Ci.nsIProperties)
-                   .get("ProfD", Ci.nsIFile);
-
-  sendAsyncMessage("dir.opened", {
-    dir: testFile.path
-  });
-});
--- a/dom/base/test/test_postMessages.html
+++ b/dom/base/test/test_postMessages.html
@@ -56,69 +56,48 @@ function compare(a, b) {
   }
 
   if (type != 'null') {
     is (a.toSource(), b.toSource(), 'Matching using toSource()');
   }
 }
 
 var clonableObjects = [
-  { crossThreads: true, data: 'hello world' },
-  { crossThreads: true, data: 123 },
-  { crossThreads: true, data: null },
-  { crossThreads: true, data: true },
-  { crossThreads: true, data: new Date() },
-  { crossThreads: true, data: [ 1, 'test', true, new Date() ] },
-  { crossThreads: true, data: { a: true, b:  null, c: new Date(), d: [ true, false, {} ] } },
-  { crossThreads: true, data: new Blob([123], { type: 'plain/text' }) },
-  { crossThreads: true, data: new ImageData(2, 2) },
+  'hello world',
+  123,
+  null,
+  true,
+  new Date(),
+  [ 1, 'test', true, new Date() ],
+  { a: true, b:  null, c: new Date(), d: [ true, false, {} ] },
+  new Blob([123], { type: 'plain/text' }),
+  new ImageData(2, 2),
 ];
 
-function create_fileList_forFile() {
+function create_fileList() {
   var url = SimpleTest.getTestFileURL("script_postmessages_fileList.js");
   var script = SpecialPowers.loadChromeScript(url);
 
   function onOpened(message) {
     var fileList = document.getElementById('fileList');
     SpecialPowers.wrap(fileList).mozSetFileArray([message.file]);
 
     // Just a simple test
     var domFile = fileList.files[0];
     is(domFile.name, "prefs.js", "fileName should be prefs.js");
 
-    clonableObjects.push({ crossThreads: true, data: fileList.files });
+    clonableObjects.push(fileList.files);
     script.destroy();
     next();
   }
 
   script.addMessageListener("file.opened", onOpened);
   script.sendAsyncMessage("file.open");
 }
 
-function create_fileList_forDir() {
-  var url = SimpleTest.getTestFileURL("script_postmessages_fileList.js");
-  var script = SpecialPowers.loadChromeScript(url);
-
-  function onOpened(message) {
-    var fileList = document.getElementById('fileList');
-    SpecialPowers.wrap(fileList).mozSetDirectory(message.dir);
-
-    // Just a simple test
-    is(fileList.files.length, 1, "Filelist has 1 element");
-    ok(fileList.files[0] instanceof Directory, "We have a directory.");
-
-    clonableObjects.push({ crossThreads: false, data: fileList.files });
-    script.destroy();
-    next();
-  }
-
-  script.addMessageListener("dir.opened", onOpened);
-  script.sendAsyncMessage("dir.open");
-}
-
 function runTests(obj) {
   ok(('clonableObjects' in obj) &&
      ('transferableObjects' in obj) &&
      (obj.clonableObjects || obj.transferableObjects), "We must run some test!");
 
   // cloning tests
   new Promise(function(resolve, reject) {
     if (!obj.clonableObjects) {
@@ -129,26 +108,18 @@ function runTests(obj) {
     var clonableObjectsId = 0;
     function runClonableTest() {
       if (clonableObjectsId >= clonableObjects.length) {
         resolve();
         return;
       }
 
       var object = clonableObjects[clonableObjectsId++];
-
-      // If this test requires a cross-thread structured clone algorithm, maybe
-      // we have to skip it.
-      if (!object.crossThread && obj.crossThread) {
-        runClonableTest();
-        return;
-      }
-
-      obj.send(object.data, []).then(function(received) {
-        compare(received.data, object.data);
+      obj.send(object, []).then(function(received) {
+        compare(received.data, object);
         runClonableTest();
       });
     }
 
     runClonableTest();
   })
 
   // transfering tests
@@ -227,17 +198,16 @@ function test_windowToWindow() {
     let tmp = resolve;
     resolve = null;
     tmp({ data: e.data, ports: e.ports });
   }
 
   runTests({
     clonableObjects: true,
     transferableObjects: true,
-    crossThread: false,
     send: function(what, ports) {
       return new Promise(function(r, rr) {
         resolve = r;
 
         try {
           postMessage(what, '*', ports);
         } catch(e) {
           resolve = null;
@@ -281,17 +251,16 @@ function test_windowToIframeURL(url) {
   }
 
   var ifr = document.createElement('iframe');
   ifr.src = url;
   ifr.onload = function() {
     runTests({
       clonableObjects: true,
       transferableObjects: true,
-      crossThread: false,
       send: function(what, ports) {
         return new Promise(function(r, rr) {
           resolve = r;
           try {
             ifr.contentWindow.postMessage(what, '*', ports);
           } catch(e) {
             resolve = null;
             rr();
@@ -329,17 +298,16 @@ function test_workers() {
       let tmp = resolve;
       resolve = null;
       tmp({ data: e.data, ports: e.ports });
     }
 
     runTests({
       clonableObjects: true,
       transferableObjects: true,
-      crossThread: true,
       send: function(what, ports) {
         return new Promise(function(r, rr) {
           resolve = r;
           try {
             w.postMessage(what, ports);
           } catch(e) {
             resolve = null;
             rr();
@@ -373,17 +341,16 @@ function test_broadcastChannel() {
     let tmp = resolve;
     resolve = null;
     tmp({ data: e.data, ports: [] });
   }
 
   runTests({
     clonableObjects: true,
     transferableObjects: false,
-    crossThread: true,
     send: function(what, ports) {
       return new Promise(function(r, rr) {
         if (ports.length) {
           rr();
           return;
         }
 
         resolve = r;
@@ -419,17 +386,16 @@ function test_broadcastChannel_inWorkers
       let tmp = resolve;
       resolve = null;
       tmp({ data: e.data, ports: e.ports });
     }
 
     runTests({
       clonableObjects: true,
       transferableObjects: false,
-      crossThread: true,
       send: function(what, ports) {
         return new Promise(function(r, rr) {
           if (ports.length) {
             rr();
             return;
           }
 
           resolve = r;
@@ -461,17 +427,16 @@ function test_messagePort() {
     let tmp = resolve;
     resolve = null;
     tmp({ data: e.data, ports: e.ports });
   }
 
   runTests({
     clonableObjects: true,
     transferableObjects: true,
-    crossThread: true,
     send: function(what, ports) {
       return new Promise(function(r, rr) {
         resolve = r;
         try {
           mc.port1.postMessage(what, ports);
         } catch(e) {
           resolve = null;
           rr();
@@ -507,17 +472,16 @@ function test_messagePort_inWorkers() {
       let tmp = resolve;
       resolve = null;
       tmp({ data: e.data, ports: e.ports });
     }
 
     runTests({
       clonableObjects: true,
       transferableObjects: true,
-      crossThread: true,
       send: function(what, ports) {
         return new Promise(function(r, rr) {
           resolve = r;
           try {
             mc.port1.postMessage(what, ports);
           } catch(e) {
             resolve = null;
             rr();
@@ -529,18 +493,17 @@ function test_messagePort_inWorkers() {
         onmessage = null;
         next();
       }
     });
   }
 }
 
 var tests = [
-  create_fileList_forFile,
-  create_fileList_forDir,
+  create_fileList,
 
   test_windowToWindow,
   test_windowToIframe,
   test_windowToCrossOriginIframe,
 
   test_workers,
 
   test_broadcastChannel,
--- a/dom/devicestorage/DeviceStorage.h
+++ b/dom/devicestorage/DeviceStorage.h
@@ -96,19 +96,18 @@ public:
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
   bool IsAvailable();
   void GetFullPath(nsAString& aFullPath);
 
   // we want to make sure that the names of file can't reach
   // outside of the type of storage the user asked for.
-  bool IsSafePath() const;
-  bool ValidateAndSplitPath(const nsAString& aPath,
-                            nsTArray<nsString>* aParts = nullptr) const;
+  bool IsSafePath();
+  bool IsSafePath(const nsAString& aPath);
 
   void Dump(const char* label);
 
   nsresult Remove();
   nsresult Write(nsIInputStream* aInputStream);
   nsresult Write(InfallibleTArray<uint8_t>& bits);
   nsresult Append(nsIInputStream* aInputStream);
   nsresult Append(nsIInputStream* aInputStream,
@@ -133,16 +132,17 @@ public:
 
   nsresult CalculateSizeAndModifiedDate();
   nsresult CalculateMimeType();
   nsresult CreateFileDescriptor(mozilla::ipc::FileDescriptor& aFileDescriptor);
 
 private:
   ~DeviceStorageFile() {}
   void Init();
+  void NormalizeFilePath();
   void AppendRelativePath(const nsAString& aPath);
   void AccumDirectoryUsage(nsIFile* aFile,
                            uint64_t* aPicturesSoFar,
                            uint64_t* aVideosSoFar,
                            uint64_t* aMusicSoFar,
                            uint64_t* aTotalSoFar);
 };
 
--- a/dom/devicestorage/DeviceStorageStatics.cpp
+++ b/dom/devicestorage/DeviceStorageStatics.cpp
@@ -577,35 +577,35 @@ DeviceStorageStatics::Deregister()
 void
 DeviceStorageStatics::ResetOverrideRootDir()
 {
   MOZ_ASSERT(NS_IsMainThread());
   sMutex.AssertCurrentThreadOwns();
   nsCOMPtr<nsIFile> f;
   DS_LOG_INFO("");
 
-  // For users running on desktop, it's convenient to be able to override
-  // all of the directories to point to a single tree, much like what happens
-  // on a real device.
-  const nsAdoptingString& overrideRootDir =
-    mozilla::Preferences::GetString(kPrefOverrideRootDir);
-  if (overrideRootDir && !overrideRootDir.IsEmpty()) {
-    NS_NewLocalFile(overrideRootDir, false, getter_AddRefs(f));
-  }
-
-  if (!f && Preferences::GetBool(kPrefTesting, false)) {
+  if (Preferences::GetBool(kPrefTesting, false)) {
     DS_LOG_INFO("temp");
     nsCOMPtr<nsIProperties> dirService
       = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
     MOZ_ASSERT(dirService);
     dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
     if (f) {
       f->AppendRelativeNativePath(
         NS_LITERAL_CSTRING("device-storage-testing"));
     }
+  } else {
+    // For users running on desktop, it's convenient to be able to override
+    // all of the directories to point to a single tree, much like what happens
+    // on a real device.
+    const nsAdoptingString& overrideRootDir =
+      mozilla::Preferences::GetString(kPrefOverrideRootDir);
+    if (overrideRootDir && !overrideRootDir.IsEmpty()) {
+      NS_NewLocalFile(overrideRootDir, false, getter_AddRefs(f));
+    }
   }
 
   if (f) {
     if (XRE_IsParentProcess()) {
       // Only the parent process can create directories. In testing, because
       // the preference is updated after startup, its entirely possible that
       // the preference updated notification will be received by a child
       // prior to the parent.
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -25,17 +25,16 @@
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/LazyIdleThread.h"
 #include "mozilla/Scoped.h"
 #include "mozilla/Services.h"
 #include "mozilla/ipc/BackgroundUtils.h" // for PrincipalInfoToPrincipal
 
 #include "nsArrayUtils.h"
 #include "nsAutoPtr.h"
-#include "nsCharSeparatedTokenizer.h"
 #include "nsGlobalWindow.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIFile.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsNetUtil.h"
 #include "nsIOutputStream.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIPrincipal.h"
@@ -72,45 +71,20 @@
 #define DEFAULT_THREAD_TIMEOUT_MS 30000
 #define STORAGE_CHANGE_EVENT "change"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::ipc;
 
-namespace mozilla
-{
+namespace mozilla {
   MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, PR_Close);
 } // namespace mozilla
 
-namespace {
-
-void
-NormalizeFilePath(nsAString& aPath)
-{
-#if defined(XP_WIN)
-  char16_t* cur = aPath.BeginWriting();
-  char16_t* end = aPath.EndWriting();
-  for (; cur < end; ++cur) {
-    if (char16_t('\\') == *cur) {
-      *cur = FILESYSTEM_DOM_PATH_SEPARATOR_CHAR;
-    }
-  }
-#endif
-}
-
-bool
-TokenizerIgnoreNothing(char16_t /* aChar */)
-{
-  return false;
-}
-
-} // anonymous namespace
-
 StaticAutoPtr<DeviceStorageUsedSpaceCache>
   DeviceStorageUsedSpaceCache::sDeviceStorageUsedSpaceCache;
 
 DeviceStorageUsedSpaceCache::DeviceStorageUsedSpaceCache()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mIOThread = new LazyIdleThread(
@@ -531,33 +505,32 @@ DeviceStorageFile::DeviceStorageFile(con
   , mLength(UINT64_MAX)
   , mLastModifiedDate(UINT64_MAX)
 {
   Init();
   AppendRelativePath(mRootDir);
   if (!mPath.EqualsLiteral("")) {
     AppendRelativePath(mPath);
   }
-
-  NormalizeFilePath(mPath);
+  NormalizeFilePath();
 }
 
 DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
                                      const nsAString& aStorageName,
                                      const nsAString& aPath)
   : mStorageType(aStorageType)
   , mStorageName(aStorageName)
   , mPath(aPath)
   , mEditable(false)
   , mLength(UINT64_MAX)
   , mLastModifiedDate(UINT64_MAX)
 {
   Init();
   AppendRelativePath(aPath);
-  NormalizeFilePath(mPath);
+  NormalizeFilePath();
 }
 
 DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
                                      const nsAString& aStorageName)
   : mStorageType(aStorageType)
   , mStorageName(aStorageName)
   , mEditable(false)
   , mLength(UINT64_MAX)
@@ -756,100 +729,85 @@ DeviceStorageFile::CreateUnique(const ns
   }
 
   return dsf.forget();
 }
 
 void
 DeviceStorageFile::SetPath(const nsAString& aPath) {
   mPath.Assign(aPath);
-  NormalizeFilePath(mPath);
+  NormalizeFilePath();
 }
 
 void
 DeviceStorageFile::SetEditable(bool aEditable) {
   mEditable = aEditable;
 }
 
 // we want to make sure that the names of file can't reach
 // outside of the type of storage the user asked for.
 bool
-DeviceStorageFile::IsSafePath() const
+DeviceStorageFile::IsSafePath()
 {
-  return ValidateAndSplitPath(mRootDir) && ValidateAndSplitPath(mPath);
+  return IsSafePath(mRootDir) && IsSafePath(mPath);
 }
 
 bool
-DeviceStorageFile::ValidateAndSplitPath(const nsAString& aPath,
-                                        nsTArray<nsString>* aParts) const
+DeviceStorageFile::IsSafePath(const nsAString& aPath)
 {
   nsAString::const_iterator start, end;
   aPath.BeginReading(start);
   aPath.EndReading(end);
 
   // if the path is a '~' or starts with '~/', return false.
   NS_NAMED_LITERAL_STRING(tilde, "~");
   NS_NAMED_LITERAL_STRING(tildeSlash, "~/");
   if (aPath.Equals(tilde) ||
       StringBeginsWith(aPath, tildeSlash)) {
     NS_WARNING("Path name starts with tilde!");
     return false;
-  }
-
-  NS_NAMED_LITERAL_STRING(kCurrentDir, ".");
-  NS_NAMED_LITERAL_STRING(kParentDir, "..");
-
-  // Split path and check each path component.
-  nsCharSeparatedTokenizerTemplate<TokenizerIgnoreNothing>
-    tokenizer(aPath, FILESYSTEM_DOM_PATH_SEPARATOR_CHAR);
-
-  while (tokenizer.hasMoreTokens()) {
-    nsDependentSubstring pathComponent = tokenizer.nextToken();
-    // The path containing empty components, such as "foo//bar", is invalid.
-    // We don't allow paths, such as "../foo", "foo/./bar" and "foo/../bar",
-    // to walk up the directory.
-    if (pathComponent.IsEmpty() ||
-        pathComponent.Equals(kCurrentDir) ||
-        pathComponent.Equals(kParentDir)) {
+   }
+  // split on /.  if any token is "", ., or .., return false.
+  NS_ConvertUTF16toUTF8 cname(aPath);
+  char* buffer = cname.BeginWriting();
+  const char* token;
+
+  while ((token = nsCRT::strtok(buffer, "/", &buffer))) {
+    if (PL_strcmp(token, "") == 0 ||
+        PL_strcmp(token, ".") == 0 ||
+        PL_strcmp(token, "..") == 0 ) {
       return false;
     }
-
-    if (aParts) {
-      aParts->AppendElement(pathComponent);
-    }
   }
   return true;
 }
 
 void
-DeviceStorageFile::AppendRelativePath(const nsAString& aPath)
-{
+DeviceStorageFile::NormalizeFilePath() {
+  FileSystemUtils::LocalPathToNormalizedPath(mPath, mPath);
+}
+
+void
+DeviceStorageFile::AppendRelativePath(const nsAString& aPath) {
   if (!mFile) {
     return;
   }
-
-  nsTArray<nsString> parts;
-
-  if (!ValidateAndSplitPath(aPath, &parts)) {
+  if (!IsSafePath(aPath)) {
     // All of the APIs (in the child) do checks to verify that the path is
     // valid and return PERMISSION_DENIED if a non-safe path is entered.
     // This check is done in the parent and prevents a compromised
     // child from bypassing the check. It shouldn't be possible for this
     // code path to be taken with a non-compromised child.
     NS_WARNING("Unsafe path detected - ignoring");
     NS_WARNING(NS_LossyConvertUTF16toASCII(aPath).get());
     return;
   }
-
-  for (uint32_t i = 0; i < parts.Length(); ++i) {
-    nsresult rv = mFile->AppendRelativePath(parts[i]);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return;
-    }
-  }
+  nsString localPath;
+  FileSystemUtils::NormalizedPathToLocalPath(aPath, localPath);
+  mFile->AppendRelativePath(localPath);
 }
 
 nsresult
 DeviceStorageFile::CreateFileDescriptor(FileDescriptor& aFileDescriptor)
 {
   if (!mFile) {
     return NS_ERROR_FAILURE;
   }
--- a/dom/devicestorage/test/devicestorage_common.js
+++ b/dom/devicestorage/test/devicestorage_common.js
@@ -15,30 +15,22 @@ Object.defineProperty(Array.prototype, "
     this.length = from < 0 ? this.length + from : from;
     return this.push.apply(this, rest);
   }
 });
 
 function devicestorage_setup(callback) {
   SimpleTest.waitForExplicitFinish();
 
-  const Cc = SpecialPowers.Cc;
-  const Ci = SpecialPowers.Ci;
-  var directoryService = Cc["@mozilla.org/file/directory_service;1"]
-                           .getService(Ci.nsIProperties);
-  var f = directoryService.get("TmpD", Ci.nsIFile);
-  f.appendRelativePath("device-storage-testing");
-
   let script = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('remove_testing_directory.js'));
 
   script.addMessageListener('directory-removed', function listener () {
     script.removeMessageListener('directory-removed', listener);
     var prefs = [["device.storage.enabled", true],
                  ["device.storage.testing", true],
-                 ["device.storage.overrideRootDir", f.path],
                  ["device.storage.prompt.testing", true]];
     SpecialPowers.pushPrefEnv({"set": prefs}, callback);
   });
 }
 
 function getRandomBuffer() {
   var size = 1024;
   var buffer = new ArrayBuffer(size);
--- a/dom/devicestorage/test/test_fs_createDirectory.html
+++ b/dom/devicestorage/test/test_fs_createDirectory.html
@@ -29,24 +29,24 @@ var gTestCount = 0;
 var gPath = '';
 var gName = '';
 
 function testCreateDirectory(rootDir, path) {
   rootDir.createDirectory(path).then(createDirectorySuccess, cbError);
 }
 
 function createDirectorySuccess(d) {
-  is(d.name, gName, "Failed to create directory: name mismatch.");
+  ok(d.name === gName, "Failed to create directory: name mismatch.");
 
   // Get the new created directory from the root.
   gRoot.get(gPath).then(getSuccess, cbError);
 }
 
 function getSuccess(d) {
-  is(d.name, gName, "Should get directory - " + (gPath || "[root]") + ".");
+  ok(d.name === gName, "Should get directory - " + (gPath || "[root]") + ".");
   switch (gTestCount) {
     case 0:
       gRoot = d;
       // Create a new directory under the root.
       gName = gPath = randomFilename(12);
       testCreateDirectory(gRoot, gName);
       break;
     case 1:
--- a/dom/devicestorage/test/test_overrideDir.html
+++ b/dom/devicestorage/test/test_overrideDir.html
@@ -144,17 +144,18 @@ try {
   const Ci = SpecialPowers.Ci;
   var directoryService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
   var f = directoryService.get("TmpD", Ci.nsIFile);
   f.appendRelativePath("device-storage-sdcard");
   try {
     // The remove will fail if the directory doesn't exist, which is fine.
     f.remove(true);
   } catch (e) {}
-  SpecialPowers.pushPrefEnv({'set': [["device.storage.overrideRootDir", f.path]]},
+  SpecialPowers.pushPrefEnv({'set': [["device.storage.overrideRootDir", f.path],
+                                     ["device.storage.testing", false]]},
     function() {
       startTest();
     });
 } catch(e) {}
 
 });
 
 </script>
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -44,25 +44,25 @@ ImplCycleCollectionTraverse(nsCycleColle
 {
   ImplCycleCollectionTraverse(aCallback, aField.mData, aName, aFlags);
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(DataTransfer)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFiles)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mItems)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragTarget)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragImage)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DataTransfer)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFiles)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mItems)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragImage)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(DataTransfer)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(DataTransfer)
@@ -276,38 +276,36 @@ DataTransfer::GetMozUserCancelled(bool* 
 {
   *aUserCancelled = MozUserCancelled();
   return NS_OK;
 }
 
 FileList*
 DataTransfer::GetFiles(ErrorResult& aRv)
 {
-  return GetFileListInternal(aRv, nsContentUtils::SubjectPrincipal());
+  return GetFilesInternal(aRv, nsContentUtils::SubjectPrincipal());
 }
 
 FileList*
-DataTransfer::GetFileListInternal(ErrorResult& aRv,
-                                  nsIPrincipal* aSubjectPrincipal)
+DataTransfer::GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal)
 {
   if (mEventMessage != eDrop &&
       mEventMessage != eLegacyDragDrop &&
       mEventMessage != ePaste) {
     return nullptr;
   }
 
-  if (!mFileList) {
-    mFileList = new FileList(static_cast<nsIDOMDataTransfer*>(this));
+  if (!mFiles) {
+    mFiles = new FileList(static_cast<nsIDOMDataTransfer*>(this));
 
     uint32_t count = mItems.Length();
 
     for (uint32_t i = 0; i < count; i++) {
       nsCOMPtr<nsIVariant> variant;
-      aRv = GetDataAtInternal(NS_ConvertUTF8toUTF16(kFileMime), i,
-                              aSubjectPrincipal, getter_AddRefs(variant));
+      aRv = GetDataAtInternal(NS_ConvertUTF8toUTF16(kFileMime), i, aSubjectPrincipal, getter_AddRefs(variant));
       if (aRv.Failed()) {
         return nullptr;
       }
 
       if (!variant)
         continue;
 
       nsCOMPtr<nsISupports> supports;
@@ -335,28 +333,31 @@ DataTransfer::GetFileListInternal(ErrorR
         }
 
         MOZ_ASSERT(blobImpl->IsFile());
 
         domFile = File::Create(GetParentObject(), blobImpl);
         MOZ_ASSERT(domFile);
       }
 
-      mFileList->Append(domFile);
+      if (!mFiles->Append(domFile)) {
+        aRv.Throw(NS_ERROR_FAILURE);
+        return nullptr;
+      }
     }
   }
 
-  return mFileList;
+  return mFiles;
 }
 
 NS_IMETHODIMP
 DataTransfer::GetFiles(nsIDOMFileList** aFileList)
 {
   ErrorResult rv;
-  NS_IF_ADDREF(*aFileList = GetFileListInternal(rv, nsContentUtils::GetSystemPrincipal()));
+  NS_IF_ADDREF(*aFileList = GetFilesInternal(rv, nsContentUtils::GetSystemPrincipal()));
   return rv.StealNSResult();
 }
 
 already_AddRefed<DOMStringList>
 DataTransfer::Types()
 {
   RefPtr<DOMStringList> types = new DOMStringList();
   if (mItems.Length()) {
@@ -848,27 +849,58 @@ DataTransfer::GetFilesAndDirectories(Err
     return nullptr;
   }
 
   RefPtr<Promise> p = Promise::Create(global, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  if (!mFileList) {
+  if (!mFiles) {
     GetFiles(aRv);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
   }
 
   Sequence<OwningFileOrDirectory> filesAndDirsSeq;
-  mFileList->ToSequence(filesAndDirsSeq, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
+
+  if (mFiles && mFiles->Length()) {
+    if (!filesAndDirsSeq.SetLength(mFiles->Length(), mozilla::fallible_t())) {
+      p->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
+      return p.forget();
+    }
+
+    for (uint32_t i = 0; i < mFiles->Length(); ++i) {
+      if (mFiles->Item(i)->Impl()->IsDirectory()) {
+#if defined(ANDROID) || defined(MOZ_B2G)
+        MOZ_ASSERT(false,
+                   "Directory picking should have been redirected to normal "
+                   "file picking for platforms that don't have a directory "
+                   "picker");
+#endif
+        nsAutoString path;
+        mFiles->Item(i)->GetMozFullPathInternal(path, aRv);
+        if (aRv.Failed()) {
+          return nullptr;
+        }
+        int32_t leafSeparatorIndex = path.RFind(FILE_PATH_SEPARATOR);
+        nsDependentSubstring dirname = Substring(path, 0, leafSeparatorIndex);
+        nsDependentSubstring basename = Substring(path, leafSeparatorIndex);
+
+        RefPtr<OSFileSystem> fs = new OSFileSystem(dirname);
+        fs->Init(parentNode->OwnerDoc()->GetInnerWindow());
+
+        RefPtr<Directory> directory = new Directory(fs, basename);
+        directory->SetContentFilters(NS_LITERAL_STRING("filter-out-sensitive"));
+        filesAndDirsSeq[i].SetAsDirectory() = directory;
+      } else {
+        filesAndDirsSeq[i].SetAsFile() = mFiles->Item(i);
+      }
+    }
   }
 
   p->MaybeResolve(filesAndDirsSeq);
 
   return p.forget();
 }
 
 void
--- a/dom/events/DataTransfer.h
+++ b/dom/events/DataTransfer.h
@@ -247,17 +247,17 @@ protected:
   // caches the formats that exist in the clipboard
   void CacheExternalClipboardFormats();
 
   // fills in the data field of aItem with the data from the drag service or
   // clipboard for a given index.
   void FillInExternalData(TransferItem& aItem, uint32_t aIndex);
 
 
-  FileList* GetFileListInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal);
+  FileList* GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal);
   nsresult GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex,
                              nsIPrincipal* aSubjectPrincipal, nsIVariant** aData);
   nsresult SetDataAtInternal(const nsAString& aFormat, nsIVariant* aData, uint32_t aIndex,
                              nsIPrincipal* aSubjectPrincipal);
 
   friend class ContentParent;
   void FillAllExternalData();
 
@@ -295,19 +295,18 @@ protected:
 
   // Indicates which clipboard type to use for clipboard operations. Ignored for
   // drag and drop.
   int32_t mClipboardType;
 
   // array of items, each containing an array of format->data pairs
   nsTArray<nsTArray<TransferItem> > mItems;
 
-  // array of files and directories, containing only the files present in the
-  // dataTransfer
-  RefPtr<FileList> mFileList;
+  // array of files, containing only the files present in the dataTransfer
+  RefPtr<FileList> mFiles;
 
   // the target of the drag. The drag and dragend events will fire at this.
   nsCOMPtr<mozilla::dom::Element> mDragTarget;
 
   // the custom drag image and coordinates within the image. If mDragImage is
   // null, the default image is created from the drag target.
   nsCOMPtr<mozilla::dom::Element> mDragImage;
   uint32_t mDragImageX;
--- a/dom/filehandle/ActorsChild.cpp
+++ b/dom/filehandle/ActorsChild.cpp
@@ -313,17 +313,18 @@ ConvertActorToFile(FileHandleBase* aFile
   MOZ_ASSERT(size.type() == FileRequestSize::Tuint64_t);
 
   const FileRequestLastModified& lastModified = metadata.lastModified();
   MOZ_ASSERT(lastModified.type() == FileRequestLastModified::Tint64_t);
 
   actor->SetMysteryBlobInfo(mutableFile->Name(),
                             mutableFile->Type(),
                             size.get_uint64_t(),
-                            lastModified.get_int64_t());
+                            lastModified.get_int64_t(),
+                            BlobDirState::eUnknownIfDir);
 
   RefPtr<BlobImpl> blobImpl = actor->GetBlobImpl();
   MOZ_ASSERT(blobImpl);
 
   RefPtr<File> file = mutableFile->CreateFileFor(blobImpl, aFileHandle);
   return file.forget();
 }
 
--- a/dom/filesystem/CreateDirectoryTask.cpp
+++ b/dom/filesystem/CreateDirectoryTask.cpp
@@ -11,188 +11,125 @@
 #include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "nsIFile.h"
 #include "nsStringGlue.h"
 
 namespace mozilla {
 namespace dom {
 
-/* static */ already_AddRefed<CreateDirectoryTask>
-CreateDirectoryTask::Create(FileSystemBase* aFileSystem,
-                            nsIFile* aTargetPath,
-                            ErrorResult& aRv)
+CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem,
+                                         const nsAString& aPath,
+                                         ErrorResult& aRv)
+  : FileSystemTaskBase(aFileSystem)
+  , mTargetRealPath(aPath)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
-
-  RefPtr<CreateDirectoryTask> task =
-    new CreateDirectoryTask(aFileSystem, aTargetPath);
-
-  // aTargetPath can be null. In this case SetError will be called.
-
   nsCOMPtr<nsIGlobalObject> globalObject =
-    do_QueryInterface(aFileSystem->GetParentObject());
-  if (NS_WARN_IF(!globalObject)) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
+    do_QueryInterface(aFileSystem->GetWindow());
+  if (!globalObject) {
+    return;
   }
-
-  task->mPromise = Promise::Create(globalObject, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  return task.forget();
+  mPromise = Promise::Create(globalObject, aRv);
 }
 
-/* static */ already_AddRefed<CreateDirectoryTask>
-CreateDirectoryTask::Create(FileSystemBase* aFileSystem,
-                            const FileSystemCreateDirectoryParams& aParam,
-                            FileSystemRequestParent* aParent,
-                            ErrorResult& aRv)
+CreateDirectoryTask::CreateDirectoryTask(
+  FileSystemBase* aFileSystem,
+  const FileSystemCreateDirectoryParams& aParam,
+  FileSystemRequestParent* aParent)
+  : FileSystemTaskBase(aFileSystem, aParam, aParent)
 {
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  MOZ_ASSERT(XRE_IsParentProcess(),
+             "Only call from parent process!");
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
-
-  RefPtr<CreateDirectoryTask> task =
-    new CreateDirectoryTask(aFileSystem, aParam, aParent);
-
-  NS_ConvertUTF16toUTF8 path(aParam.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  return task.forget();
-}
-
-CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem,
-                                         nsIFile* aTargetPath)
-  : FileSystemTaskBase(aFileSystem)
-  , mTargetPath(aTargetPath)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-}
-
-CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem,
-                                         const FileSystemCreateDirectoryParams& aParam,
-                                         FileSystemRequestParent* aParent)
-  : FileSystemTaskBase(aFileSystem, aParam, aParent)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
+  mTargetRealPath = aParam.realPath();
 }
 
 CreateDirectoryTask::~CreateDirectoryTask()
 {
   MOZ_ASSERT(!mPromise || NS_IsMainThread(),
              "mPromise should be released on main thread!");
 }
 
 already_AddRefed<Promise>
 CreateDirectoryTask::GetPromise()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   return RefPtr<Promise>(mPromise).forget();
 }
 
 FileSystemParams
-CreateDirectoryTask::GetRequestParams(const nsString& aSerializedDOMPath,
-                                      ErrorResult& aRv) const
+CreateDirectoryTask::GetRequestParams(const nsString& aFileSystem) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-
-  nsAutoString path;
-  aRv = mTargetPath->GetPath(path);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return FileSystemCreateDirectoryParams();
-  }
-
-  return FileSystemCreateDirectoryParams(aSerializedDOMPath, path);
+  return FileSystemCreateDirectoryParams(aFileSystem, mTargetRealPath);
 }
 
 FileSystemResponseValue
-CreateDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const
+CreateDirectoryTask::GetSuccessRequestResult() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-
-  nsAutoString path;
-  aRv = mTargetPath->GetPath(path);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return FileSystemDirectoryResponse();
-  }
-
-  return FileSystemDirectoryResponse(path);
+  return FileSystemDirectoryResponse(mTargetRealPath);
 }
 
 void
-CreateDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                                             ErrorResult& aRv)
+CreateDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   FileSystemDirectoryResponse r = aValue;
-
-  NS_ConvertUTF16toUTF8 path(r.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
-  NS_WARN_IF(aRv.Failed());
+  mTargetRealPath = r.realPath();
 }
 
 nsresult
 CreateDirectoryTask::Work()
 {
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Only call from parent process!");
   MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
 
   if (mFileSystem->IsShutdown()) {
     return NS_ERROR_FAILURE;
   }
 
+  nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(mTargetRealPath);
+  if (!file) {
+    return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
+  }
+
   bool fileExists;
-  nsresult rv = mTargetPath->Exists(&fileExists);
+  nsresult rv = file->Exists(&fileExists);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (fileExists) {
     return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR;
   }
 
-  rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0770);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  return NS_OK;
+  rv = file->Create(nsIFile::DIRECTORY_TYPE, 0770);
+  return rv;
 }
 
 void
 CreateDirectoryTask::HandlerCallback()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   if (mFileSystem->IsShutdown()) {
     mPromise = nullptr;
     return;
   }
 
   if (HasError()) {
     mPromise->MaybeReject(mErrorValue);
     mPromise = nullptr;
     return;
   }
-  RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
-                                            mTargetPath,
-                                            Directory::eNotDOMRootDirectory,
-                                            mFileSystem);
-  MOZ_ASSERT(dir);
-
+  RefPtr<Directory> dir = new Directory(mFileSystem, mTargetRealPath);
   mPromise->MaybeResolve(dir);
   mPromise = nullptr;
 }
 
 void
 CreateDirectoryTask::GetPermissionAccessType(nsCString& aAccess) const
 {
   aAccess.AssignLiteral("create");
--- a/dom/filesystem/CreateDirectoryTask.h
+++ b/dom/filesystem/CreateDirectoryTask.h
@@ -11,65 +11,53 @@
 #include "nsAutoPtr.h"
 #include "mozilla/ErrorResult.h"
 
 namespace mozilla {
 namespace dom {
 
 class Promise;
 
-class CreateDirectoryTask final : public FileSystemTaskBase
+class CreateDirectoryTask final
+  : public FileSystemTaskBase
 {
 public:
-  static already_AddRefed<CreateDirectoryTask>
-  Create(FileSystemBase* aFileSystem,
-         nsIFile* aTargetPath,
-         ErrorResult& aRv);
-
-  static already_AddRefed<CreateDirectoryTask>
-  Create(FileSystemBase* aFileSystem,
-         const FileSystemCreateDirectoryParams& aParam,
-         FileSystemRequestParent* aParent,
-         ErrorResult& aRv);
+  CreateDirectoryTask(FileSystemBase* aFileSystem,
+                      const nsAString& aPath,
+                      ErrorResult& aRv);
+  CreateDirectoryTask(FileSystemBase* aFileSystem,
+                      const FileSystemCreateDirectoryParams& aParam,
+                      FileSystemRequestParent* aParent);
 
   virtual
   ~CreateDirectoryTask();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
 
 protected:
   virtual FileSystemParams
-  GetRequestParams(const nsString& aSerializedDOMPath,
-                   ErrorResult& aRv) const override;
+  GetRequestParams(const nsString& aFileSystem) const override;
 
   virtual FileSystemResponseValue
-  GetSuccessRequestResult(ErrorResult& aRv) const override;
+  GetSuccessRequestResult() const override;
 
   virtual void
-  SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                          ErrorResult& aRv) override;
+  SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
 
   virtual nsresult
   Work() override;
 
   virtual void
   HandlerCallback() override;
 
 private:
-  CreateDirectoryTask(FileSystemBase* aFileSystem,
-                      nsIFile* aTargetPath);
-
-  CreateDirectoryTask(FileSystemBase* aFileSystem,
-                      const FileSystemCreateDirectoryParams& aParam,
-                      FileSystemRequestParent* aParent);
-
   RefPtr<Promise> mPromise;
-  nsCOMPtr<nsIFile> mTargetPath;
+  nsString mTargetRealPath;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_CreateDirectoryTask_h
--- a/dom/filesystem/CreateFileTask.cpp
+++ b/dom/filesystem/CreateFileTask.cpp
@@ -20,125 +20,78 @@
 #include "nsIOutputStream.h"
 #include "nsStringGlue.h"
 
 namespace mozilla {
 namespace dom {
 
 uint32_t CreateFileTask::sOutputBufferSize = 0;
 
-/* static */ already_AddRefed<CreateFileTask>
-CreateFileTask::Create(FileSystemBase* aFileSystem,
-                       nsIFile* aTargetPath,
-                       Blob* aBlobData,
-                       InfallibleTArray<uint8_t>& aArrayData,
-                       bool aReplace,
-                       ErrorResult& aRv)
+CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
+                               const nsAString& aPath,
+                               Blob* aBlobData,
+                               InfallibleTArray<uint8_t>& aArrayData,
+                               bool replace,
+                               ErrorResult& aRv)
+  : FileSystemTaskBase(aFileSystem)
+  , mTargetRealPath(aPath)
+  , mReplace(replace)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
-
-  RefPtr<CreateFileTask> task =
-    new CreateFileTask(aFileSystem, aTargetPath, aReplace);
-
-  // aTargetPath can be null. In this case SetError will be called.
-
-  task->GetOutputBufferSize();
-
+  GetOutputBufferSize();
   if (aBlobData) {
     if (XRE_IsParentProcess()) {
-      aBlobData->GetInternalStream(getter_AddRefs(task->mBlobStream), aRv);
-      if (NS_WARN_IF(aRv.Failed())) {
-        return nullptr;
-      }
+      aBlobData->GetInternalStream(getter_AddRefs(mBlobStream), aRv);
+      NS_WARN_IF(aRv.Failed());
     } else {
-      task->mBlobData = aBlobData;
+      mBlobData = aBlobData;
     }
   }
-
-  task->mArrayData.SwapElements(aArrayData);
-
+  mArrayData.SwapElements(aArrayData);
   nsCOMPtr<nsIGlobalObject> globalObject =
-    do_QueryInterface(aFileSystem->GetParentObject());
-  if (NS_WARN_IF(!globalObject)) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
-  }
-
-  task->mPromise = Promise::Create(globalObject, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
+    do_QueryInterface(aFileSystem->GetWindow());
+  if (!globalObject) {
+    return;
   }
-
-  return task.forget();
-}
-
-/* static */ already_AddRefed<CreateFileTask>
-CreateFileTask::Create(FileSystemBase* aFileSystem,
-                       const FileSystemCreateFileParams& aParam,
-                       FileSystemRequestParent* aParent,
-                       ErrorResult& aRv)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-
-  RefPtr<CreateFileTask> task =
-    new CreateFileTask(aFileSystem, aParam, aParent);
-
-  task->GetOutputBufferSize();
-
-  NS_ConvertUTF16toUTF8 path(aParam.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  task->mReplace = aParam.replace();
-
-  auto& data = aParam.data();
-
-  if (data.type() == FileSystemFileDataValue::TArrayOfuint8_t) {
-    task->mArrayData = data;
-    return task.forget();
-  }
-
-  BlobParent* bp = static_cast<BlobParent*>(static_cast<PBlobParent*>(data));
-  RefPtr<BlobImpl> blobImpl = bp->GetBlobImpl();
-  MOZ_ASSERT(blobImpl, "blobData should not be null.");
-
-  ErrorResult rv;
-  blobImpl->GetInternalStream(getter_AddRefs(task->mBlobStream), rv);
-  if (NS_WARN_IF(rv.Failed())) {
-    rv.SuppressException();
-  }
-
-  return task.forget();
-}
-
-CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
-                               nsIFile* aTargetPath,
-                               bool aReplace)
-  : FileSystemTaskBase(aFileSystem)
-  , mTargetPath(aTargetPath)
-  , mReplace(aReplace)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
+  mPromise = Promise::Create(globalObject, aRv);
 }
 
 CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
                                const FileSystemCreateFileParams& aParam,
                                FileSystemRequestParent* aParent)
   : FileSystemTaskBase(aFileSystem, aParam, aParent)
   , mReplace(false)
 {
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  MOZ_ASSERT(XRE_IsParentProcess(),
+             "Only call from parent process!");
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
+  GetOutputBufferSize();
+
+  mTargetRealPath = aParam.realPath();
+
+  mReplace = aParam.replace();
+
+  auto& data = aParam.data();
+
+  if (data.type() == FileSystemFileDataValue::TArrayOfuint8_t) {
+    mArrayData = data;
+    return;
+  }
+
+  BlobParent* bp = static_cast<BlobParent*>(static_cast<PBlobParent*>(data));
+  RefPtr<BlobImpl> blobImpl = bp->GetBlobImpl();
+  MOZ_ASSERT(blobImpl, "blobData should not be null.");
+
+  ErrorResult rv;
+  blobImpl->GetInternalStream(getter_AddRefs(mBlobStream), rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    rv.SuppressException();
+  }
 }
 
 CreateFileTask::~CreateFileTask()
 {
   MOZ_ASSERT((!mPromise && !mBlobData) || NS_IsMainThread(),
              "mPromise and mBlobData should be released on main thread!");
 
   if (mBlobStream) {
@@ -149,132 +102,129 @@ CreateFileTask::~CreateFileTask()
 already_AddRefed<Promise>
 CreateFileTask::GetPromise()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   return RefPtr<Promise>(mPromise).forget();
 }
 
 FileSystemParams
-CreateFileTask::GetRequestParams(const nsString& aSerializedDOMPath,
-                                 ErrorResult& aRv) const
+CreateFileTask::GetRequestParams(const nsString& aFileSystem) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   FileSystemCreateFileParams param;
-  param.filesystem() = aSerializedDOMPath;
-
-  aRv = mTargetPath->GetPath(param.realPath());
-  if (NS_WARN_IF(aRv.Failed())) {
-    return param;
-  }
-
+  param.filesystem() = aFileSystem;
+  param.realPath() = mTargetRealPath;
   param.replace() = mReplace;
   if (mBlobData) {
-    BlobChild* actor =
-      ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlobData);
+    BlobChild* actor
+      = ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlobData);
     if (actor) {
       param.data() = actor;
     }
   } else {
     param.data() = mArrayData;
   }
   return param;
 }
 
 FileSystemResponseValue
-CreateFileTask::GetSuccessRequestResult(ErrorResult& aRv) const
+CreateFileTask::GetSuccessRequestResult() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   BlobParent* actor = GetBlobParent(mTargetBlobImpl);
   if (!actor) {
     return FileSystemErrorResponse(NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR);
   }
   FileSystemFileResponse response;
   response.blobParent() = actor;
   return response;
 }
 
 void
-CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                                        ErrorResult& aRv)
+CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   FileSystemFileResponse r = aValue;
   BlobChild* actor = static_cast<BlobChild*>(r.blobChild());
   mTargetBlobImpl = actor->GetBlobImpl();
 }
 
 nsresult
 CreateFileTask::Work()
 {
-  class MOZ_RAII AutoClose final
+  class AutoClose
   {
   public:
     explicit AutoClose(nsIOutputStream* aStream)
       : mStream(aStream)
     {
       MOZ_ASSERT(aStream);
     }
 
     ~AutoClose()
     {
       mStream->Close();
     }
-
   private:
     nsCOMPtr<nsIOutputStream> mStream;
   };
 
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Only call from parent process!");
   MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
 
   if (mFileSystem->IsShutdown()) {
     return NS_ERROR_FAILURE;
   }
 
-  if (!mFileSystem->IsSafeFile(mTargetPath)) {
+  nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(mTargetRealPath);
+  if (!file) {
+    return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
+  }
+
+  if (!mFileSystem->IsSafeFile(file)) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   bool exists = false;
-  nsresult rv = mTargetPath->Exists(&exists);
+  nsresult rv = file->Exists(&exists);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (exists) {
     bool isFile = false;
-    rv = mTargetPath->IsFile(&isFile);
+    rv = file->IsFile(&isFile);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     if (!isFile) {
       return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
     }
 
     if (!mReplace) {
       return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR;
     }
 
     // Remove the old file before creating.
-    rv = mTargetPath->Remove(false);
+    rv = file->Remove(false);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
-  rv = mTargetPath->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
+  rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<nsIOutputStream> outputStream;
-  rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mTargetPath);
+  rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   AutoClose acOutputStream(outputStream);
 
   nsCOMPtr<nsIOutputStream> bufferedOutputStream;
   rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
@@ -307,17 +257,17 @@ CreateFileTask::Work()
 
     mBlobStream->Close();
     mBlobStream = nullptr;
 
     if (mFileSystem->IsShutdown()) {
       return NS_ERROR_FAILURE;
     }
 
-    mTargetBlobImpl = new BlobImplFile(mTargetPath);
+    mTargetBlobImpl = new BlobImplFile(file);
     return NS_OK;
   }
 
   // Write file content from array data.
 
   uint32_t written;
   rv = bufferedOutputStream->Write(
     reinterpret_cast<char*>(mArrayData.Elements()),
@@ -326,17 +276,17 @@ CreateFileTask::Work()
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (mArrayData.Length() != written) {
     return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR;
   }
 
-  mTargetBlobImpl = new BlobImplFile(mTargetPath);
+  mTargetBlobImpl = new BlobImplFile(file);
   return NS_OK;
 }
 
 void
 CreateFileTask::HandlerCallback()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   if (mFileSystem->IsShutdown()) {
@@ -347,18 +297,17 @@ CreateFileTask::HandlerCallback()
 
   if (HasError()) {
     mPromise->MaybeReject(mErrorValue);
     mPromise = nullptr;
     mBlobData = nullptr;
     return;
   }
 
-  RefPtr<Blob> blob = Blob::Create(mFileSystem->GetParentObject(),
-                                   mTargetBlobImpl);
+  RefPtr<Blob> blob = Blob::Create(mFileSystem->GetWindow(), mTargetBlobImpl);
   mPromise->MaybeResolve(blob);
   mPromise = nullptr;
   mBlobData = nullptr;
 }
 
 void
 CreateFileTask::GetPermissionAccessType(nsCString& aAccess) const
 {
--- a/dom/filesystem/CreateFileTask.h
+++ b/dom/filesystem/CreateFileTask.h
@@ -15,75 +15,62 @@ class nsIInputStream;
 
 namespace mozilla {
 namespace dom {
 
 class Blob;
 class BlobImpl;
 class Promise;
 
-class CreateFileTask final : public FileSystemTaskBase
+class CreateFileTask final
+  : public FileSystemTaskBase
 {
 public:
-  static already_AddRefed<CreateFileTask>
-  Create(FileSystemBase* aFileSystem,
-         nsIFile* aFile,
-         Blob* aBlobData,
-         InfallibleTArray<uint8_t>& aArrayData,
-         bool replace,
-         ErrorResult& aRv);
-
-  static already_AddRefed<CreateFileTask>
-  Create(FileSystemBase* aFileSystem,
-         const FileSystemCreateFileParams& aParam,
-         FileSystemRequestParent* aParent,
-         ErrorResult& aRv);
+  CreateFileTask(FileSystemBase* aFileSystem,
+                 const nsAString& aPath,
+                 Blob* aBlobData,
+                 InfallibleTArray<uint8_t>& aArrayData,
+                 bool replace,
+                 ErrorResult& aRv);
+  CreateFileTask(FileSystemBase* aFileSystem,
+                 const FileSystemCreateFileParams& aParam,
+                 FileSystemRequestParent* aParent);
 
   virtual
   ~CreateFileTask();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
 
 protected:
   virtual FileSystemParams
-  GetRequestParams(const nsString& aSerializedDOMPath,
-                   ErrorResult& aRv) const override;
+  GetRequestParams(const nsString& aFileSystem) const override;
 
   virtual FileSystemResponseValue
-  GetSuccessRequestResult(ErrorResult& aRv) const override;
+  GetSuccessRequestResult() const override;
 
   virtual void
-  SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                          ErrorResult& aRv) override;
+  SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
 
   virtual nsresult
   Work() override;
 
   virtual void
   HandlerCallback() override;
 
 private:
-  CreateFileTask(FileSystemBase* aFileSystem,
-                 nsIFile* aFile,
-                 bool aReplace);
-
-  CreateFileTask(FileSystemBase* aFileSystem,
-                 const FileSystemCreateFileParams& aParam,
-                 FileSystemRequestParent* aParent);
-
   void
   GetOutputBufferSize() const;
 
   static uint32_t sOutputBufferSize;
   RefPtr<Promise> mPromise;
-  nsCOMPtr<nsIFile> mTargetPath;
+  nsString mTargetRealPath;
 
   // Not thread-safe and should be released on main thread.
   RefPtr<Blob> mBlobData;
 
   nsCOMPtr<nsIInputStream> mBlobStream;
   InfallibleTArray<uint8_t> mArrayData;
   bool mReplace;
 
--- a/dom/filesystem/DeviceStorageFileSystem.cpp
+++ b/dom/filesystem/DeviceStorageFileSystem.cpp
@@ -16,44 +16,54 @@
 #include "nsDeviceStorage.h"
 #include "nsIFile.h"
 #include "nsPIDOMWindow.h"
 #include "nsGlobalWindow.h"
 
 namespace mozilla {
 namespace dom {
 
-DeviceStorageFileSystem::DeviceStorageFileSystem(const nsAString& aStorageType,
-                                                 const nsAString& aStorageName)
+DeviceStorageFileSystem::DeviceStorageFileSystem(
+  const nsAString& aStorageType,
+  const nsAString& aStorageName)
   : mWindowId(0)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
 
   mStorageType = aStorageType;
   mStorageName = aStorageName;
 
+  // Generate the string representation of the file system.
+  mString.AppendLiteral("devicestorage-");
+  mString.Append(mStorageType);
+  mString.Append('-');
+  mString.Append(mStorageName);
+
   mRequiresPermissionChecks =
     !mozilla::Preferences::GetBool("device.storage.prompt.testing", false);
 
   // Get the permission name required to access the file system.
   nsresult rv =
     DeviceStorageTypeChecker::GetPermissionForType(mStorageType, mPermission);
   NS_WARN_IF(NS_FAILED(rv));
 
   // Get the local path of the file system root.
+  // Since the child process is not allowed to access the file system, we only
+  // do this from the parent process.
+  if (!XRE_IsParentProcess()) {
+    return;
+  }
   nsCOMPtr<nsIFile> rootFile;
   DeviceStorageFile::GetRootDirectoryForType(aStorageType,
                                              aStorageName,
                                              getter_AddRefs(rootFile));
 
   NS_WARN_IF(!rootFile || NS_FAILED(rootFile->GetPath(mLocalRootPath)));
-
-  if (!XRE_IsParentProcess()) {
-    return;
-  }
+  FileSystemUtils::LocalPathToNormalizedPath(mLocalRootPath,
+    mNormalizedLocalRootPath);
 
   // DeviceStorageTypeChecker is a singleton object and must be initialized on
   // the main thread. We initialize it here so that we can use it on the worker
   // thread.
   DebugOnly<DeviceStorageTypeChecker*> typeChecker
     = DeviceStorageTypeChecker::CreateOrGet();
   MOZ_ASSERT(typeChecker);
 }
@@ -74,18 +84,18 @@ DeviceStorageFileSystem::Init(nsDOMDevic
 
 void
 DeviceStorageFileSystem::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   mShutdown = true;
 }
 
-nsISupports*
-DeviceStorageFileSystem::GetParentObject() const
+nsPIDOMWindowInner*
+DeviceStorageFileSystem::GetWindow() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
   MOZ_ASSERT_IF(!mShutdown, window);
   return window ? window->AsInner() : nullptr;
 }
 
 void
@@ -96,62 +106,37 @@ DeviceStorageFileSystem::GetRootName(nsA
 
 bool
 DeviceStorageFileSystem::IsSafeFile(nsIFile* aFile) const
 {
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Should be on parent process!");
   MOZ_ASSERT(aFile);
 
-  nsCOMPtr<nsIFile> rootPath;
-  nsresult rv = NS_NewLocalFile(GetLocalRootPath(), false,
-                                getter_AddRefs(rootPath));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
+  // Check if this file belongs to this storage.
+  nsAutoString path;
+  if (NS_FAILED(aFile->GetPath(path))) {
     return false;
   }
-
-  // Check if this file belongs to this storage.
-  if (NS_WARN_IF(!FileSystemUtils::IsDescendantPath(rootPath, aFile))) {
+  if (!LocalPathToRealPath(path, path)) {
     return false;
   }
 
   // Check if the file type is compatible with the storage type.
   DeviceStorageTypeChecker* typeChecker
     = DeviceStorageTypeChecker::CreateOrGet();
   MOZ_ASSERT(typeChecker);
   return typeChecker->Check(mStorageType, aFile);
 }
 
 bool
 DeviceStorageFileSystem::IsSafeDirectory(Directory* aDir) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aDir);
-
-  ErrorResult rv;
-  RefPtr<FileSystemBase> fs = aDir->GetFileSystem(rv);
-  if (NS_WARN_IF(rv.Failed())) {
-    rv.SuppressException();
-    return false;
-  }
-
-  nsAutoString fsSerialization;
-  fs->SerializeDOMPath(fsSerialization);
-
-  nsAutoString thisSerialization;
-  SerializeDOMPath(thisSerialization);
-
+  RefPtr<FileSystemBase> fs = aDir->GetFileSystem();
+  MOZ_ASSERT(fs);
   // Check if the given directory is from this storage.
-  return fsSerialization == thisSerialization;
-}
-
-void
-DeviceStorageFileSystem::SerializeDOMPath(nsAString& aString) const
-{
-  // Generate the string representation of the file system.
-  aString.AssignLiteral("devicestorage-");
-  aString.Append(mStorageType);
-  aString.Append('-');
-  aString.Append(mStorageName);
+  return fs->ToString() == mString;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/DeviceStorageFileSystem.h
+++ b/dom/filesystem/DeviceStorageFileSystem.h
@@ -25,31 +25,27 @@ public:
   void
   Init(nsDOMDeviceStorage* aDeviceStorage);
 
   // Overrides FileSystemBase
 
   virtual void
   Shutdown() override;
 
-  virtual nsISupports*
-  GetParentObject() const override;
+  virtual nsPIDOMWindowInner*
+  GetWindow() const override;
 
   virtual void
   GetRootName(nsAString& aRetval) const override;
 
   virtual bool
   IsSafeFile(nsIFile* aFile) const override;
 
   virtual bool
   IsSafeDirectory(Directory* aDir) const override;
-
-  virtual void
-  SerializeDOMPath(nsAString& aSerializedString) const override;
-
 private:
   virtual
   ~DeviceStorageFileSystem();
 
   nsString mStorageType;
   nsString mStorageName;
 
   uint64_t mWindowId;
--- a/dom/filesystem/Directory.cpp
+++ b/dom/filesystem/Directory.cpp
@@ -13,205 +13,112 @@
 #include "GetFileOrDirectoryTask.h"
 #include "RemoveTask.h"
 
 #include "nsCharSeparatedTokenizer.h"
 #include "nsString.h"
 #include "mozilla/dom/DirectoryBinding.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemUtils.h"
-#include "mozilla/dom/OSFileSystem.h"
 
 // Resolve the name collision of Microsoft's API name with macros defined in
 // Windows header files. Undefine the macro of CreateDirectory to avoid
 // Directory#CreateDirectory being replaced by Directory#CreateDirectoryW.
 #ifdef CreateDirectory
 #undef CreateDirectory
 #endif
 // Undefine the macro of CreateFile to avoid Directory#CreateFile being replaced
 // by Directory#CreateFileW.
 #ifdef CreateFile
 #undef CreateFile
 #endif
 
 namespace mozilla {
 namespace dom {
 
-namespace {
-
-bool
-TokenizerIgnoreNothing(char16_t /* aChar */)
-{
-  return false;
-}
-
-bool
-IsValidRelativeDOMPath(const nsString& aPath, nsTArray<nsString>& aParts)
-{
-  // We don't allow empty relative path to access the root.
-  if (aPath.IsEmpty()) {
-    return false;
-  }
-
-  // Leading and trailing "/" are not allowed.
-  if (aPath.First() == FILESYSTEM_DOM_PATH_SEPARATOR_CHAR ||
-      aPath.Last() == FILESYSTEM_DOM_PATH_SEPARATOR_CHAR) {
-    return false;
-  }
-
-  NS_NAMED_LITERAL_STRING(kCurrentDir, ".");
-  NS_NAMED_LITERAL_STRING(kParentDir, "..");
-
-  // Split path and check each path component.
-  nsCharSeparatedTokenizerTemplate<TokenizerIgnoreNothing>
-    tokenizer(aPath, FILESYSTEM_DOM_PATH_SEPARATOR_CHAR);
-
-  while (tokenizer.hasMoreTokens()) {
-    nsDependentSubstring pathComponent = tokenizer.nextToken();
-    // The path containing empty components, such as "foo//bar", is invalid.
-    // We don't allow paths, such as "../foo", "foo/./bar" and "foo/../bar",
-    // to walk up the directory.
-    if (pathComponent.IsEmpty() ||
-        pathComponent.Equals(kCurrentDir) ||
-        pathComponent.Equals(kParentDir)) {
-      return false;
-    }
-
-    aParts.AppendElement(pathComponent);
-  }
-
-  return true;
-}
-
-} // anonymous namespace
-
 NS_IMPL_CYCLE_COLLECTION_CLASS(Directory)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Directory)
-  if (tmp->mFileSystem) {
-    tmp->mFileSystem->Unlink();
-    tmp->mFileSystem = nullptr;
-  }
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
+  tmp->mFileSystem->Unlink();
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Directory)
-  if (tmp->mFileSystem) {
-    tmp->mFileSystem->Traverse(cb);
-  }
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
+  tmp->mFileSystem->Traverse(cb);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Directory)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Directory)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Directory)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Directory)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 // static
 already_AddRefed<Promise>
 Directory::GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv)
 {
-  MOZ_ASSERT(aFileSystem);
-
-  nsCOMPtr<nsIFile> path;
-  aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(aFileSystem->GetLocalRootPath()),
-                              true, getter_AddRefs(path));
-  if (NS_WARN_IF(aRv.Failed())) {
+  RefPtr<GetFileOrDirectoryTask> task = new GetFileOrDirectoryTask(
+    aFileSystem, EmptyString(), true, aRv);
+  if (aRv.Failed()) {
     return nullptr;
   }
-
-  RefPtr<GetFileOrDirectoryTask> task =
-    GetFileOrDirectoryTask::Create(aFileSystem, path, eDOMRootDirectory, true, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
   FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
 
-/* static */ already_AddRefed<Directory>
-Directory::Create(nsISupports* aParent, nsIFile* aFile,
-                  DirectoryType aType, FileSystemBase* aFileSystem)
+Directory::Directory(FileSystemBase* aFileSystem,
+                     const nsAString& aPath)
+  : mFileSystem(aFileSystem)
+  , mPath(aPath)
 {
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aParent);
-  MOZ_ASSERT(aFile);
-
-#ifdef DEBUG
-  bool isDir;
-  nsresult rv = aFile->IsDirectory(&isDir);
-  MOZ_ASSERT(NS_SUCCEEDED(rv) && isDir);
-#endif
-
-  RefPtr<Directory> directory =
-    new Directory(aParent, aFile, aType, aFileSystem);
-  return directory.forget();
-}
-
-Directory::Directory(nsISupports* aParent,
-                     nsIFile* aFile,
-                     DirectoryType aType,
-                     FileSystemBase* aFileSystem)
-  : mParent(aParent)
-  , mFileSystem(aFileSystem)
-  , mFile(aFile)
-  , mType(aType)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aFile);
-
-  // aFileSystem can be null. In this case we create a OSFileSystem when needed.
+  MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
+  // Remove the trailing "/".
+  mPath.Trim(FILESYSTEM_DOM_PATH_SEPARATOR, false, true);
 }
 
 Directory::~Directory()
 {
 }
 
-nsISupports*
+nsPIDOMWindowInner*
 Directory::GetParentObject() const
 {
-  return mParent;
+  return mFileSystem->GetWindow();
 }
 
 JSObject*
 Directory::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return DirectoryBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
-Directory::GetName(nsAString& aRetval, ErrorResult& aRv)
+Directory::GetName(nsAString& aRetval) const
 {
   aRetval.Truncate();
 
-  if (mType == eDOMRootDirectory) {
-    RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
-    if (NS_WARN_IF(aRv.Failed())) {
-      return;
-    }
-
-    fs->GetRootName(aRetval);
+  if (mPath.IsEmpty()) {
+    mFileSystem->GetRootName(aRetval);
     return;
   }
 
-  aRv = mFile->GetLeafName(aRetval);
-  NS_WARN_IF(aRv.Failed());
+  aRetval = Substring(mPath,
+                      mPath.RFindChar(FileSystemUtils::kSeparatorChar) + 1);
 }
 
 already_AddRefed<Promise>
 Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions,
                       ErrorResult& aRv)
 {
+  nsresult error = NS_OK;
+  nsAutoString realPath;
   RefPtr<Blob> blobData;
   InfallibleTArray<uint8_t> arrayData;
   bool replace = (aOptions.mIfExists == CreateIfExistsMode::Replace);
 
   // Get the file content.
   if (aOptions.mData.WasPassed()) {
     auto& data = aOptions.mData.Value();
     if (data.IsString()) {
@@ -226,75 +133,61 @@ Directory::CreateFile(const nsAString& a
       const ArrayBufferView& view = data.GetAsArrayBufferView();
       view.ComputeLengthAndData();
       arrayData.AppendElements(view.Data(), view.Length());
     } else {
       blobData = data.GetAsBlob();
     }
   }
 
-  nsCOMPtr<nsIFile> realPath;
-  nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
-
-  RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
+  if (!DOMPathToRealPath(aPath, realPath)) {
+    error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
   }
 
   RefPtr<CreateFileTask> task =
-    CreateFileTask::Create(fs, realPath, blobData, arrayData, replace, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
+    new CreateFileTask(mFileSystem, realPath, blobData, arrayData, replace, aRv);
+  if (aRv.Failed()) {
     return nullptr;
   }
-
   task->SetError(error);
   FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
 
 already_AddRefed<Promise>
 Directory::CreateDirectory(const nsAString& aPath, ErrorResult& aRv)
 {
-  nsCOMPtr<nsIFile> realPath;
-  nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
-
-  RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
+  nsresult error = NS_OK;
+  nsAutoString realPath;
+  if (!DOMPathToRealPath(aPath, realPath)) {
+    error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
+  }
+  RefPtr<CreateDirectoryTask> task = new CreateDirectoryTask(
+    mFileSystem, realPath, aRv);
+  if (aRv.Failed()) {
     return nullptr;
   }
-
-  RefPtr<CreateDirectoryTask> task =
-    CreateDirectoryTask::Create(fs, realPath, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
   task->SetError(error);
   FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
 
 already_AddRefed<Promise>
 Directory::Get(const nsAString& aPath, ErrorResult& aRv)
 {
-  nsCOMPtr<nsIFile> realPath;
-  nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
-
-  RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
+  nsresult error = NS_OK;
+  nsAutoString realPath;
+  if (!DOMPathToRealPath(aPath, realPath)) {
+    error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
+  }
+  RefPtr<GetFileOrDirectoryTask> task = new GetFileOrDirectoryTask(
+    mFileSystem, realPath, false, aRv);
+  if (aRv.Failed()) {
     return nullptr;
   }
-
-  RefPtr<GetFileOrDirectoryTask> task =
-    GetFileOrDirectoryTask::Create(fs, realPath, eNotDOMRootDirectory, false,
-                                   aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
   task->SetError(error);
   FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
 
 already_AddRefed<Promise>
 Directory::Remove(const StringOrFileOrDirectory& aPath, ErrorResult& aRv)
 {
@@ -307,152 +200,135 @@ Directory::RemoveDeep(const StringOrFile
   return RemoveInternal(aPath, true, aRv);
 }
 
 already_AddRefed<Promise>
 Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive,
                           ErrorResult& aRv)
 {
   nsresult error = NS_OK;
-  nsCOMPtr<nsIFile> realPath;
+  nsAutoString realPath;
   RefPtr<BlobImpl> blob;
 
   // Check and get the target path.
 
-  RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
   if (aPath.IsFile()) {
     blob = aPath.GetAsFile().Impl();
   } else if (aPath.IsString()) {
-    error = DOMPathToRealPath(aPath.GetAsString(), getter_AddRefs(realPath));
-  } else if (!fs->IsSafeDirectory(&aPath.GetAsDirectory())) {
+    if (!DOMPathToRealPath(aPath.GetAsString(), realPath)) {
+      error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
+    }
+  } else if (!mFileSystem->IsSafeDirectory(&aPath.GetAsDirectory())) {
     error = NS_ERROR_DOM_SECURITY_ERR;
   } else {
-    realPath = aPath.GetAsDirectory().mFile;
+    realPath = aPath.GetAsDirectory().mPath;
     // The target must be a descendant of this directory.
-    if (!FileSystemUtils::IsDescendantPath(mFile, realPath)) {
+    if (!FileSystemUtils::IsDescendantPath(mPath, realPath)) {
       error = NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
     }
   }
 
-  RefPtr<RemoveTask> task =
-    RemoveTask::Create(fs, mFile, blob, realPath, aRecursive, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
+  RefPtr<RemoveTask> task = new RemoveTask(mFileSystem, mPath, blob, realPath,
+    aRecursive, aRv);
+  if (aRv.Failed()) {
     return nullptr;
   }
   task->SetError(error);
   FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
 
 void
-Directory::GetPath(nsAString& aRetval, ErrorResult& aRv)
+Directory::GetPath(nsAString& aRetval) const
 {
-  if (mType == eDOMRootDirectory) {
-    aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
+  if (mPath.IsEmpty()) {
+    // The Directory ctor removes any trailing '/'; this is the root directory.
+    aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR);
   } else {
-    // TODO: this should be a bit different...
-    GetName(aRetval, aRv);
+    aRetval = mPath;
   }
 }
 
-nsresult
-Directory::GetFullRealPath(nsAString& aPath)
+already_AddRefed<Promise>
+Directory::GetFilesAndDirectories()
 {
-  nsresult rv = mFile->GetPath(aPath);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-already_AddRefed<Promise>
-Directory::GetFilesAndDirectories(ErrorResult& aRv)
-{
-  RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
+  ErrorResult rv;
   RefPtr<GetDirectoryListingTask> task =
-    GetDirectoryListingTask::Create(fs, mFile, mType, mFilters, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
+    new GetDirectoryListingTask(mFileSystem, mPath, mFilters, rv);
+  if (NS_WARN_IF(rv.Failed())) {
     return nullptr;
   }
 
   FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
 
 void
 Directory::SetContentFilters(const nsAString& aFilters)
 {
   mFilters = aFilters;
 }
 
 FileSystemBase*
-Directory::GetFileSystem(ErrorResult& aRv)
+Directory::GetFileSystem() const
 {
-  if (!mFileSystem) {
-    nsCOMPtr<nsIFile> parent;
-    aRv = mFile->GetParent(getter_AddRefs(parent));
-    if (NS_WARN_IF(aRv.Failed())) {
-      return nullptr;
-    }
-
-    // Parent can be null if mFile is pointing to the top directory.
-    if (!parent) {
-      parent = mFile;
-    }
-
-    nsAutoString path;
-    aRv = parent->GetPath(path);
-    if (NS_WARN_IF(aRv.Failed())) {
-      return nullptr;
-    }
-
-    RefPtr<OSFileSystem> fs = new OSFileSystem(path);
-    fs->Init(mParent);
-
-    mFileSystem = fs;
-  }
-
-  return mFileSystem;
+  return mFileSystem.get();
 }
 
-nsresult
-Directory::DOMPathToRealPath(const nsAString& aPath, nsIFile** aFile) const
+bool
+Directory::DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const
 {
+  aRealPath.Truncate();
+
   nsString relativePath;
   relativePath = aPath;
 
   // Trim white spaces.
   static const char kWhitespace[] = "\b\t\r\n ";
   relativePath.Trim(kWhitespace);
 
-  nsTArray<nsString> parts;
-  if (!IsValidRelativeDOMPath(relativePath, parts)) {
-    return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
+  if (!IsValidRelativePath(relativePath)) {
+    return false;
+  }
+
+  aRealPath = mPath + NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR) +
+    relativePath;
+
+  return true;
+}
+
+// static
+bool
+Directory::IsValidRelativePath(const nsString& aPath)
+{
+  // We don't allow empty relative path to access the root.
+  if (aPath.IsEmpty()) {
+    return false;
   }
 
-  nsCOMPtr<nsIFile> file;
-  nsresult rv = mFile->Clone(getter_AddRefs(file));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
+  // Leading and trailing "/" are not allowed.
+  if (aPath.First() == FileSystemUtils::kSeparatorChar ||
+      aPath.Last() == FileSystemUtils::kSeparatorChar) {
+    return false;
   }
 
-  for (uint32_t i = 0; i < parts.Length(); ++i) {
-    rv = file->AppendRelativePath(parts[i]);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
+  NS_NAMED_LITERAL_STRING(kCurrentDir, ".");
+  NS_NAMED_LITERAL_STRING(kParentDir, "..");
+
+  // Split path and check each path component.
+  nsCharSeparatedTokenizer tokenizer(aPath, FileSystemUtils::kSeparatorChar);
+  while (tokenizer.hasMoreTokens()) {
+    nsDependentSubstring pathComponent = tokenizer.nextToken();
+    // The path containing empty components, such as "foo//bar", is invalid.
+    // We don't allow paths, such as "../foo", "foo/./bar" and "foo/../bar",
+    // to walk up the directory.
+    if (pathComponent.IsEmpty() ||
+        pathComponent.Equals(kCurrentDir) ||
+        pathComponent.Equals(kParentDir)) {
+      return false;
     }
   }
 
-  file.forget(aFile);
-  return NS_OK;
+  return true;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/Directory.h
+++ b/dom/filesystem/Directory.h
@@ -8,16 +8,17 @@
 #define mozilla_dom_Directory_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/File.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
+#include "nsPIDOMWindow.h"
 #include "nsWrapperCache.h"
 
 // Resolve the name collision of Microsoft's API name with macros defined in
 // Windows header files. Undefine the macro of CreateDirectory to avoid
 // Directory#CreateDirectory being replaced by Directory#CreateDirectoryW.
 #ifdef CreateDirectory
 #undef CreateDirectory
 #endif
@@ -35,58 +36,35 @@ class FileSystemBase;
 class Promise;
 class StringOrFileOrDirectory;
 
 class Directory final
   : public nsISupports
   , public nsWrapperCache
 {
 public:
-  struct BlobImplOrDirectoryPath
-  {
-    RefPtr<BlobImpl> mBlobImpl;
-    nsString mDirectoryPath;
-
-    enum {
-      eBlobImpl,
-      eDirectoryPath
-    } mType;
-  };
-
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Directory)
 
+public:
   static already_AddRefed<Promise>
   GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv);
 
-  enum DirectoryType {
-    // When a directory is selected using a HTMLInputElement, that will be the
-    // DOM root directory and its name will be '/'. All the sub directory will
-    // be called with they real name. We use this enum to mark what we must
-    // consider the '/' of this DOM filesystem.
-    eDOMRootDirectory,
-
-    // All the sub directories of the '/' will be marked using this other value.
-    eNotDOMRootDirectory
-  };
-
-  static already_AddRefed<Directory>
-  Create(nsISupports* aParent, nsIFile* aDirectory,
-         DirectoryType aType, FileSystemBase* aFileSystem = 0);
+  Directory(FileSystemBase* aFileSystem, const nsAString& aPath);
 
   // ========= Begin WebIDL bindings. ===========
 
-  nsISupports*
+  nsPIDOMWindowInner*
   GetParentObject() const;
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void
-  GetName(nsAString& aRetval, ErrorResult& aRv);
+  GetName(nsAString& aRetval) const;
 
   already_AddRefed<Promise>
   CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions,
              ErrorResult& aRv);
 
   already_AddRefed<Promise>
   CreateDirectory(const nsAString& aPath, ErrorResult& aRv);
 
@@ -97,23 +75,20 @@ public:
   Remove(const StringOrFileOrDirectory& aPath, ErrorResult& aRv);
 
   already_AddRefed<Promise>
   RemoveDeep(const StringOrFileOrDirectory& aPath, ErrorResult& aRv);
 
   // From https://microsoftedge.github.io/directory-upload/proposal.html#directory-interface :
 
   void
-  GetPath(nsAString& aRetval, ErrorResult& aRv);
-
-  nsresult
-  GetFullRealPath(nsAString& aPath);
+  GetPath(nsAString& aRetval) const;
 
   already_AddRefed<Promise>
-  GetFilesAndDirectories(ErrorResult& aRv);
+  GetFilesAndDirectories();
 
   // =========== End WebIDL bindings.============
 
   /**
    * Sets a semi-colon separated list of filters to filter-in or filter-out
    * certain types of files when the contents of this directory are requested
    * via a GetFilesAndDirectories() call.
    *
@@ -133,43 +108,35 @@ public:
    * functionality would allow us to apply the 'accept' attribute from
    * <input type=file directory accept="..."> to the results of a directory
    * picker operation.
    */
   void
   SetContentFilters(const nsAString& aFilters);
 
   FileSystemBase*
-  GetFileSystem(ErrorResult& aRv);
+  GetFileSystem() const;
+private:
+  ~Directory();
 
-  DirectoryType Type() const
-  {
-    return mType;
-  }
-
-private:
-  Directory(nsISupports* aParent,
-            nsIFile* aFile, DirectoryType aType,
-            FileSystemBase* aFileSystem = nullptr);
-  ~Directory();
+  static bool
+  IsValidRelativePath(const nsString& aPath);
 
   /*
    * Convert relative DOM path to the absolute real path.
+   * @return true if succeed. false if the DOM path is invalid.
    */
-  nsresult
-  DOMPathToRealPath(const nsAString& aPath, nsIFile** aFile) const;
+  bool
+  DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const;
 
   already_AddRefed<Promise>
   RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive,
                  ErrorResult& aRv);
 
-  nsCOMPtr<nsISupports> mParent;
   RefPtr<FileSystemBase> mFileSystem;
-  nsCOMPtr<nsIFile> mFile;
-  DirectoryType mType;
-
+  nsString mPath;
   nsString mFilters;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Directory_h
--- a/dom/filesystem/FileSystemBase.cpp
+++ b/dom/filesystem/FileSystemBase.cpp
@@ -10,17 +10,17 @@
 #include "nsCharSeparatedTokenizer.h"
 #include "OSFileSystem.h"
 
 namespace mozilla {
 namespace dom {
 
 // static
 already_AddRefed<FileSystemBase>
-FileSystemBase::DeserializeDOMPath(const nsAString& aString)
+FileSystemBase::FromString(const nsAString& aString)
 {
   if (StringBeginsWith(aString, NS_LITERAL_STRING("devicestorage-"))) {
     // The string representation of devicestorage file system is of the format:
     // devicestorage-StorageType-StorageName
 
     nsCharSeparatedTokenizer tokenizer(aString, char16_t('-'));
     tokenizer.nextToken();
 
@@ -33,17 +33,16 @@ FileSystemBase::DeserializeDOMPath(const
     if (tokenizer.hasMoreTokens()) {
       storageName = tokenizer.nextToken();
     }
 
     RefPtr<DeviceStorageFileSystem> f =
       new DeviceStorageFileSystem(storageType, storageName);
     return f.forget();
   }
-
   return RefPtr<OSFileSystem>(new OSFileSystem(aString)).forget();
 }
 
 FileSystemBase::FileSystemBase()
   : mShutdown(false)
   , mRequiresPermissionChecks(true)
 {
 }
@@ -53,51 +52,77 @@ FileSystemBase::~FileSystemBase()
 }
 
 void
 FileSystemBase::Shutdown()
 {
   mShutdown = true;
 }
 
-nsISupports*
-FileSystemBase::GetParentObject() const
+nsPIDOMWindowInner*
+FileSystemBase::GetWindow() const
 {
   return nullptr;
 }
 
+already_AddRefed<nsIFile>
+FileSystemBase::GetLocalFile(const nsAString& aRealPath) const
+{
+  MOZ_ASSERT(XRE_IsParentProcess(),
+             "Should be on parent process!");
+  nsAutoString localPath;
+  FileSystemUtils::NormalizedPathToLocalPath(aRealPath, localPath);
+  localPath = mLocalRootPath + localPath;
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = NS_NewLocalFile(localPath, false, getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
+  return file.forget();
+}
+
 bool
-FileSystemBase::GetRealPath(BlobImpl* aFile, nsIFile** aPath) const
+FileSystemBase::GetRealPath(BlobImpl* aFile, nsAString& aRealPath) const
 {
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Should be on parent process!");
   MOZ_ASSERT(aFile, "aFile Should not be null.");
 
+  aRealPath.Truncate();
+
   nsAutoString filePath;
   ErrorResult rv;
   aFile->GetMozFullPathInternal(filePath, rv);
   if (NS_WARN_IF(rv.Failed())) {
     return false;
   }
 
-  rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(filePath),
-                             true, aPath);
-  if (NS_WARN_IF(rv.Failed())) {
-    return false;
-  }
-
-  return true;
+  return LocalPathToRealPath(filePath, aRealPath);
 }
 
 bool
 FileSystemBase::IsSafeFile(nsIFile* aFile) const
 {
   return false;
 }
 
 bool
 FileSystemBase::IsSafeDirectory(Directory* aDir) const
 {
   return false;
 }
 
+bool
+FileSystemBase::LocalPathToRealPath(const nsAString& aLocalPath,
+                                    nsAString& aRealPath) const
+{
+  nsAutoString path;
+  FileSystemUtils::LocalPathToNormalizedPath(aLocalPath, path);
+  if (!FileSystemUtils::IsDescendantPath(mNormalizedLocalRootPath, path)) {
+    aRealPath.Truncate();
+    return false;
+  }
+  aRealPath = Substring(path, mNormalizedLocalRootPath.Length());
+  return true;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/FileSystemBase.h
+++ b/dom/filesystem/FileSystemBase.h
@@ -5,42 +5,53 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_FileSystemBase_h
 #define mozilla_dom_FileSystemBase_h
 
 #include "nsAutoPtr.h"
 #include "nsString.h"
 
+class nsPIDOMWindowInner;
+
 namespace mozilla {
 namespace dom {
 
 class BlobImpl;
 class Directory;
 
 class FileSystemBase
 {
   NS_INLINE_DECL_REFCOUNTING(FileSystemBase)
 public:
 
   // Create file system object from its string representation.
   static already_AddRefed<FileSystemBase>
-  DeserializeDOMPath(const nsAString& aString);
+  FromString(const nsAString& aString);
 
   FileSystemBase();
 
   virtual void
   Shutdown();
 
-  // SerializeDOMPath the FileSystem to string.
-  virtual void
-  SerializeDOMPath(nsAString& aOutput) const = 0;
+  // Get the string representation of the file system.
+  const nsString&
+  ToString() const
+  {
+    return mString;
+  }
 
-  virtual nsISupports*
-  GetParentObject() const;
+  virtual nsPIDOMWindowInner*
+  GetWindow() const;
+
+  /**
+   * Create nsIFile object from the given real path (absolute DOM path).
+   */
+  already_AddRefed<nsIFile>
+  GetLocalFile(const nsAString& aRealPath) const;
 
   /*
    * Get the virtual name of the root directory. This name will be exposed to
    * the content page.
    */
   virtual void
   GetRootName(nsAString& aRetval) const = 0;
 
@@ -57,18 +68,23 @@ public:
   }
 
   virtual bool
   IsSafeFile(nsIFile* aFile) const;
 
   virtual bool
   IsSafeDirectory(Directory* aDir) const;
 
+  /*
+   * Get the real path (absolute DOM path) of the DOM file in the file system.
+   * If succeeded, returns true. Otherwise, returns false and set aRealPath to
+   * empty string.
+   */
   bool
-  GetRealPath(BlobImpl* aFile, nsIFile** aPath) const;
+  GetRealPath(BlobImpl* aFile, nsAString& aRealPath) const;
 
   /*
    * Get the permission name required to access this file system.
    */
   const nsCString&
   GetPermission() const
   {
     return mPermission;
@@ -82,22 +98,31 @@ public:
 
   // CC methods
   virtual void Unlink() {}
   virtual void Traverse(nsCycleCollectionTraversalCallback &cb) {}
 
 protected:
   virtual ~FileSystemBase();
 
+  bool
+  LocalPathToRealPath(const nsAString& aLocalPath, nsAString& aRealPath) const;
+
   // The local path of the root (i.e. the OS path, with OS path separators, of
   // the OS directory that acts as the root of this OSFileSystem).
   // Only available in the parent process.
   // In the child process, we don't use it and its value should be empty.
   nsString mLocalRootPath;
 
+  // The same, but with path separators normalized to "/".
+  nsString mNormalizedLocalRootPath;
+
+  // The string representation of the file system.
+  nsString mString;
+
   bool mShutdown;
 
   // The permission name required to access the file system.
   nsCString mPermission;
 
   bool mRequiresPermissionChecks;
 };
 
--- a/dom/filesystem/FileSystemPermissionRequest.cpp
+++ b/dom/filesystem/FileSystemPermissionRequest.cpp
@@ -10,47 +10,47 @@
 #include "mozilla/dom/FileSystemUtils.h"
 #include "nsIDocument.h"
 #include "nsPIDOMWindow.h"
 #include "nsContentPermissionHelper.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_ISUPPORTS(FileSystemPermissionRequest, nsIRunnable,
-                  nsIContentPermissionRequest)
+NS_IMPL_ISUPPORTS(FileSystemPermissionRequest, nsIRunnable, nsIContentPermissionRequest)
 
 // static
 void
 FileSystemPermissionRequest::RequestForTask(FileSystemTaskBase* aTask)
 {
   MOZ_ASSERT(aTask, "aTask should not be null!");
   MOZ_ASSERT(NS_IsMainThread());
   RefPtr<FileSystemPermissionRequest> request =
     new FileSystemPermissionRequest(aTask);
   NS_DispatchToCurrentThread(request);
 }
 
-FileSystemPermissionRequest::FileSystemPermissionRequest(FileSystemTaskBase* aTask)
+FileSystemPermissionRequest::FileSystemPermissionRequest(
+  FileSystemTaskBase* aTask)
   : mTask(aTask)
 {
   MOZ_ASSERT(mTask, "aTask should not be null!");
   MOZ_ASSERT(NS_IsMainThread());
 
   mTask->GetPermissionAccessType(mPermissionAccess);
 
   RefPtr<FileSystemBase> filesystem = mTask->GetFileSystem();
   if (!filesystem) {
     return;
   }
 
   mPermissionType = filesystem->GetPermission();
 
-  mWindow = do_QueryInterface(filesystem->GetParentObject());
-  if (NS_WARN_IF(!mWindow)) {
+  mWindow = filesystem->GetWindow();
+  if (!mWindow) {
     return;
   }
 
   nsCOMPtr<nsIDocument> doc = mWindow->GetDoc();
   if (!doc) {
     return;
   }
 
--- a/dom/filesystem/FileSystemRequestParent.cpp
+++ b/dom/filesystem/FileSystemRequestParent.cpp
@@ -23,32 +23,27 @@ FileSystemRequestParent::FileSystemReque
 
 FileSystemRequestParent::~FileSystemRequestParent()
 {
 }
 
 #define FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(name)                         \
     case FileSystemParams::TFileSystem##name##Params: {                        \
       const FileSystem##name##Params& p = aParams;                             \
-      mFileSystem = FileSystemBase::DeserializeDOMPath(p.filesystem());        \
-      task = name##Task::Create(mFileSystem, p, this, rv);                     \
-      if (NS_WARN_IF(rv.Failed())) {                                           \
-        return false;                                                          \
-      }                                                                        \
+      mFileSystem = FileSystemBase::FromString(p.filesystem());                \
+      task = new name##Task(mFileSystem, p, this);                             \
       break;                                                                   \
     }
 
 bool
 FileSystemRequestParent::Dispatch(ContentParent* aParent,
                                   const FileSystemParams& aParams)
 {
   MOZ_ASSERT(aParent, "aParent should not be null.");
   RefPtr<FileSystemTaskBase> task;
-  ErrorResult rv;
-
   switch (aParams.type()) {
 
     FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateDirectory)
     FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateFile)
     FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetDirectoryListing)
     FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetFileOrDirectory)
     FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(Remove)
 
--- a/dom/filesystem/FileSystemTaskBase.cpp
+++ b/dom/filesystem/FileSystemTaskBase.cpp
@@ -18,17 +18,17 @@
 #include "mozilla/unused.h"
 #include "nsProxyRelease.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
-class FileSystemReleaseRunnable final : public nsRunnable
+class FileSystemReleaseRunnable : public nsRunnable
 {
 public:
   explicit FileSystemReleaseRunnable(RefPtr<FileSystemBase>& aDoomed)
     : mDoomed(nullptr)
   {
     aDoomed.swap(mDoomed);
   }
 
@@ -101,32 +101,22 @@ FileSystemTaskBase::Start()
     return;
   }
 
   // Run in child process.
   if (mFileSystem->IsShutdown()) {
     return;
   }
 
-  nsAutoString serialization;
-  mFileSystem->SerializeDOMPath(serialization);
-
-  ErrorResult rv;
-  FileSystemParams params = GetRequestParams(serialization, rv);
-  if (NS_WARN_IF(rv.Failed())) {
-    return;
-  }
-
   // Retain a reference so the task object isn't deleted without IPDL's
   // knowledge. The reference will be released by
   // mozilla::dom::ContentChild::DeallocPFileSystemRequestChild.
   NS_ADDREF_THIS();
-
   ContentChild::GetSingleton()->SendPFileSystemRequestConstructor(this,
-                                                                  params);
+    GetRequestParams(mFileSystem->ToString()));
 }
 
 NS_IMETHODIMP
 FileSystemTaskBase::Run()
 {
   if (!NS_IsMainThread()) {
     // Run worker thread tasks
     nsresult rv = Work();
@@ -159,42 +149,34 @@ FileSystemTaskBase::HandleResult()
 }
 
 FileSystemResponseValue
 FileSystemTaskBase::GetRequestResult() const
 {
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Only call from parent process!");
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  if (!HasError()) {
-    ErrorResult rv;
-    FileSystemResponseValue value = GetSuccessRequestResult(rv);
-    if (NS_WARN_IF(rv.Failed())) {
-      return FileSystemErrorResponse(rv.StealNSResult());
-    }
-
-    return value;
+  if (HasError()) {
+    return FileSystemErrorResponse(mErrorValue);
+  } else {
+    return GetSuccessRequestResult();
   }
-
-  return FileSystemErrorResponse(mErrorValue);
 }
 
 void
 FileSystemTaskBase::SetRequestResult(const FileSystemResponseValue& aValue)
 {
   MOZ_ASSERT(!XRE_IsParentProcess(),
              "Only call from child process!");
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   if (aValue.type() == FileSystemResponseValue::TFileSystemErrorResponse) {
     FileSystemErrorResponse r = aValue;
     mErrorValue = r.error();
   } else {
-    ErrorResult rv;
-    SetSuccessRequestResult(aValue, rv);
-    mErrorValue = rv.StealNSResult();
+    SetSuccessRequestResult(aValue);
   }
 }
 
 bool
 FileSystemTaskBase::Recv__delete__(const FileSystemResponseValue& aValue)
 {
   SetRequestResult(aValue);
   HandlerCallback();
--- a/dom/filesystem/FileSystemTaskBase.h
+++ b/dom/filesystem/FileSystemTaskBase.h
@@ -171,37 +171,35 @@ protected:
 
   /*
    * Wrap the task parameter to FileSystemParams for sending it through IPC.
    * It will be called when we need to forward a task from the child process to
    * the prarent process.
    * @param filesystem The string representation of the file system.
    */
   virtual FileSystemParams
-  GetRequestParams(const nsString& aSerializedDOMPath,
-                   ErrorResult& aRv) const = 0;
+  GetRequestParams(const nsString& aFileSystem) const = 0;
 
   /*
    * Wrap the task success result to FileSystemResponseValue for sending it
    * through IPC.
    * It will be called when the task is completed successfully and we need to
    * send the task success result back to the child process.
    */
   virtual FileSystemResponseValue
-  GetSuccessRequestResult(ErrorResult& aRv) const = 0;
+  GetSuccessRequestResult() const = 0;
 
   /*
    * Unwrap the IPC message to get the task success result.
    * It will be called when the task is completed successfully and an IPC
    * message is received in the child process and we want to get the task
    * success result.
    */
   virtual void
-  SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                          ErrorResult& aRv) = 0;
+  SetSuccessRequestResult(const FileSystemResponseValue& aValue) = 0;
 
   bool
   HasError() const { return mErrorValue != NS_OK; }
 
   // Overrides PFileSystemRequestChild
   virtual bool
   Recv__delete__(const FileSystemResponseValue& value) override;
 
--- a/dom/filesystem/FileSystemUtils.cpp
+++ b/dom/filesystem/FileSystemUtils.cpp
@@ -1,38 +1,69 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=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 "mozilla/dom/FileSystemUtils.h"
 
+#include "nsXULAppAPI.h"
+
 namespace mozilla {
 namespace dom {
 
-/* static */ bool
-FileSystemUtils::IsDescendantPath(nsIFile* aFile,
-                                  nsIFile* aDescendantFile)
+// static
+void
+FileSystemUtils::LocalPathToNormalizedPath(const nsAString& aLocal,
+                                           nsAString& aNorm)
 {
-  nsAutoString path;
-  nsresult rv = aFile->GetPath(path);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return false;
+  nsString result;
+  result = aLocal;
+#if defined(XP_WIN)
+  char16_t* cur = result.BeginWriting();
+  char16_t* end = result.EndWriting();
+  for (; cur < end; ++cur) {
+    if (char16_t('\\') == *cur)
+      *cur = char16_t('/');
   }
+#endif
+  aNorm = result;
+}
 
-  nsAutoString descendantPath;
-  rv = aDescendantFile->GetPath(descendantPath);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return false;
+// static
+void
+FileSystemUtils::NormalizedPathToLocalPath(const nsAString& aNorm,
+                                           nsAString& aLocal)
+{
+  nsString result;
+  result = aNorm;
+#if defined(XP_WIN)
+  char16_t* cur = result.BeginWriting();
+  char16_t* end = result.EndWriting();
+  for (; cur < end; ++cur) {
+    if (char16_t('/') == *cur)
+      *cur = char16_t('\\');
   }
+#endif
+  aLocal = result;
+}
+
+// static
+bool
+FileSystemUtils::IsDescendantPath(const nsAString& aPath,
+                                  const nsAString& aDescendantPath)
+{
+  // The descendant path should begin with its ancestor path.
+  nsAutoString prefix;
+  prefix = aPath + NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR);
 
   // Check the sub-directory path to see if it has the parent path as prefix.
-  if (descendantPath.Length() <= path.Length() ||
-      !StringBeginsWith(descendantPath, path)) {
+  if (aDescendantPath.Length() < prefix.Length() ||
+      !StringBeginsWith(aDescendantPath, prefix)) {
     return false;
   }
 
   return true;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/FileSystemUtils.h
+++ b/dom/filesystem/FileSystemUtils.h
@@ -2,34 +2,49 @@
 /* vim: set ts=8 sts=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_FileSystemUtils_h
 #define mozilla_dom_FileSystemUtils_h
 
-class nsIFile;
+#include "nsString.h"
 
 namespace mozilla {
 namespace dom {
 
-#define FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL "/"
-#define FILESYSTEM_DOM_PATH_SEPARATOR_CHAR '/'
+#define FILESYSTEM_DOM_PATH_SEPARATOR "/"
 
 /*
  * This class is for error handling.
  * All methods in this class are static.
  */
 class FileSystemUtils
 {
 public:
   /*
-   * Return true if aDescendantPath is a descendant of aPath.
+   * Convert the path separator to "/".
+   */
+  static void
+  LocalPathToNormalizedPath(const nsAString& aLocal, nsAString& aNorm);
+
+  /*
+   * Convert the normalized path separator "/" to the system dependent path
+   * separator, which is "/" on Mac and Linux, and "\" on Windows.
+   */
+  static void
+  NormalizedPathToLocalPath(const nsAString& aNorm, nsAString& aLocal);
+
+  /*
+   * Return true if aDescendantPath is a descendant of aPath. Both aPath and
+   * aDescendantPath are absolute DOM path.
    */
   static bool
-  IsDescendantPath(nsIFile* aPath, nsIFile* aDescendantPath);
+  IsDescendantPath(const nsAString& aPath, const nsAString& aDescendantPath);
+
+  static const char16_t kSeparatorChar = char16_t('/');
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_FileSystemUtils_h
--- a/dom/filesystem/GetDirectoryListingTask.cpp
+++ b/dom/filesystem/GetDirectoryListingTask.cpp
@@ -3,102 +3,56 @@
 /* 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 "GetDirectoryListingTask.h"
 
 #include "HTMLSplitOnSpacesTokenizer.h"
 #include "js/Value.h"
+#include "mozilla/dom/Directory.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "nsIFile.h"
 #include "nsStringGlue.h"
 
 namespace mozilla {
 namespace dom {
 
-/* static */ already_AddRefed<GetDirectoryListingTask>
-GetDirectoryListingTask::Create(FileSystemBase* aFileSystem,
-                                nsIFile* aTargetPath,
-                                Directory::DirectoryType aType,
-                                const nsAString& aFilters,
-                                ErrorResult& aRv)
+GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem,
+                                                 const nsAString& aTargetPath,
+                                                 const nsAString& aFilters,
+                                                 ErrorResult& aRv)
+  : FileSystemTaskBase(aFileSystem)
+  , mTargetRealPath(aTargetPath)
+  , mFilters(aFilters)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
-
-  RefPtr<GetDirectoryListingTask> task =
-    new GetDirectoryListingTask(aFileSystem, aTargetPath, aType, aFilters);
-
-  // aTargetPath can be null. In this case SetError will be called.
-
   nsCOMPtr<nsIGlobalObject> globalObject =
-    do_QueryInterface(aFileSystem->GetParentObject());
-  if (NS_WARN_IF(!globalObject)) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
-  }
-
-  task->mPromise = Promise::Create(globalObject, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
+    do_QueryInterface(aFileSystem->GetWindow());
+  if (!globalObject) {
+    return;
   }
-
-  return task.forget();
-}
-
-/* static */ already_AddRefed<GetDirectoryListingTask>
-GetDirectoryListingTask::Create(FileSystemBase* aFileSystem,
-                                const FileSystemGetDirectoryListingParams& aParam,
-                                FileSystemRequestParent* aParent,
-                                ErrorResult& aRv)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-
-  RefPtr<GetDirectoryListingTask> task =
-    new GetDirectoryListingTask(aFileSystem, aParam, aParent);
-
-  NS_ConvertUTF16toUTF8 path(aParam.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  task->mType = aParam.isRoot()
-                  ? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory;
-  return task.forget();
-}
-
-GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem,
-                                                 nsIFile* aTargetPath,
-                                                 Directory::DirectoryType aType,
-                                                 const nsAString& aFilters)
-  : FileSystemTaskBase(aFileSystem)
-  , mTargetPath(aTargetPath)
-  , mFilters(aFilters)
-  , mType(aType)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
+  mPromise = Promise::Create(globalObject, aRv);
 }
 
 GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem,
                                                  const FileSystemGetDirectoryListingParams& aParam,
                                                  FileSystemRequestParent* aParent)
   : FileSystemTaskBase(aFileSystem, aParam, aParent)
+  , mTargetRealPath(aParam.realPath())
   , mFilters(aParam.filters())
 {
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  MOZ_ASSERT(XRE_IsParentProcess(),
+             "Only call from parent process!");
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
 }
 
 GetDirectoryListingTask::~GetDirectoryListingTask()
 {
   MOZ_ASSERT(!mPromise || NS_IsMainThread(),
              "mPromise should be released on main thread!");
@@ -107,134 +61,106 @@ GetDirectoryListingTask::~GetDirectoryLi
 already_AddRefed<Promise>
 GetDirectoryListingTask::GetPromise()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   return RefPtr<Promise>(mPromise).forget();
 }
 
 FileSystemParams
-GetDirectoryListingTask::GetRequestParams(const nsString& aSerializedDOMPath,
-                                          ErrorResult& aRv) const
+GetDirectoryListingTask::GetRequestParams(const nsString& aFileSystem) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-
-  nsAutoString path;
-  aRv = mTargetPath->GetPath(path);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return FileSystemGetDirectoryListingParams();
-  }
-
-  return FileSystemGetDirectoryListingParams(aSerializedDOMPath, path,
-                                             mType == Directory::eDOMRootDirectory,
+  return FileSystemGetDirectoryListingParams(aFileSystem, mTargetRealPath,
                                              mFilters);
 }
 
 FileSystemResponseValue
-GetDirectoryListingTask::GetSuccessRequestResult(ErrorResult& aRv) const
+GetDirectoryListingTask::GetSuccessRequestResult() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
 
   InfallibleTArray<PBlobParent*> blobs;
 
-  nsTArray<FileSystemDirectoryListingResponseData> inputs;
-
-  for (unsigned i = 0; i < mTargetData.Length(); i++) {
-    if (mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl) {
-      BlobParent* blobParent = GetBlobParent(mTargetData[i].mBlobImpl);
-      if (!blobParent) {
-        continue;
-      }
-
-      FileSystemDirectoryListingResponseBlob blobData;
-      blobData.blobParent() = blobParent;
-      inputs.AppendElement(blobData);
-    } else {
-      MOZ_ASSERT(mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath);
-      FileSystemDirectoryListingResponseDirectory directoryData;
-      directoryData.directoryRealPath() = mTargetData[i].mDirectoryPath;
-      inputs.AppendElement(directoryData);
+  for (unsigned i = 0; i < mTargetBlobImpls.Length(); i++) {
+    BlobParent* blobParent = GetBlobParent(mTargetBlobImpls[i]);
+    if (blobParent) {
+      blobs.AppendElement(blobParent);
     }
   }
-
   FileSystemDirectoryListingResponse response;
-  response.data().SwapElements(inputs);
+  response.blobsParent().SwapElements(blobs);
   return response;
 }
 
 void
-GetDirectoryListingTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                                                 ErrorResult& aRv)
+GetDirectoryListingTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aValue.type() ==
                FileSystemResponseValue::TFileSystemDirectoryListingResponse);
 
   FileSystemDirectoryListingResponse r = aValue;
-  for (uint32_t i = 0; i < r.data().Length(); ++i) {
-    const FileSystemDirectoryListingResponseData& data = r.data()[i];
-
-    if (data.type() == FileSystemDirectoryListingResponseData::TFileSystemDirectoryListingResponseBlob) {
-      PBlobChild* blob = data.get_FileSystemDirectoryListingResponseBlob().blobChild();
+  nsTArray<PBlobChild*>& blobs = r.blobsChild();
 
-      Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement();
-      element->mType = Directory::BlobImplOrDirectoryPath::eBlobImpl;
-      element->mBlobImpl = static_cast<BlobChild*>(blob)->GetBlobImpl();
-    } else {
-      MOZ_ASSERT(data.type() == FileSystemDirectoryListingResponseData::TFileSystemDirectoryListingResponseDirectory);
-
-      Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement();
-      element->mType = Directory::BlobImplOrDirectoryPath::eDirectoryPath;
-      element->mDirectoryPath = data.get_FileSystemDirectoryListingResponseDirectory().directoryRealPath();
-    }
+  for (unsigned i = 0; i < blobs.Length(); i++) {
+    mTargetBlobImpls.AppendElement(static_cast<BlobChild*>(blobs[i])->GetBlobImpl());
   }
 }
 
 nsresult
 GetDirectoryListingTask::Work()
 {
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Only call from parent process!");
   MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
 
   if (mFileSystem->IsShutdown()) {
     return NS_ERROR_FAILURE;
   }
 
+  // Whether we want to get the root directory.
+  bool getRoot = mTargetRealPath.IsEmpty();
+
+  nsCOMPtr<nsIFile> dir = mFileSystem->GetLocalFile(mTargetRealPath);
+  if (!dir) {
+    return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
+  }
+
   bool exists;
-  nsresult rv = mTargetPath->Exists(&exists);
+  nsresult rv = dir->Exists(&exists);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (!exists) {
-    if (mType == Directory::eNotDOMRootDirectory) {
+    if (!getRoot) {
       return NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
     }
 
     // If the root directory doesn't exit, create it.
-    rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
+    rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0777);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   // Get isDirectory.
   bool isDir;
-  rv = mTargetPath->IsDirectory(&isDir);
+  rv = dir->IsDirectory(&isDir);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (!isDir) {
     return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
   }
 
   nsCOMPtr<nsISimpleEnumerator> entries;
-  rv = mTargetPath->GetDirectoryEntries(getter_AddRefs(entries));
+  rv = dir->GetDirectoryEntries(getter_AddRefs(entries));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   bool filterOutSensitive = false;
   {
     HTMLSplitOnSpacesTokenizer tokenizer(mFilters, ';');
     nsAutoString token;
@@ -255,17 +181,17 @@ GetDirectoryListingTask::Work()
     }
     nsCOMPtr<nsISupports> supp;
     if (NS_WARN_IF(NS_FAILED(entries->GetNext(getter_AddRefs(supp))))) {
       break;
     }
 
     nsCOMPtr<nsIFile> currFile = do_QueryInterface(supp);
     MOZ_ASSERT(currFile);
-
+    
     bool isLink, isSpecial, isFile;
     if (NS_WARN_IF(NS_FAILED(currFile->IsSymlink(&isLink)) ||
                    NS_FAILED(currFile->IsSpecial(&isSpecial))) ||
         isLink || isSpecial) {
       continue;
     }
     if (NS_WARN_IF(NS_FAILED(currFile->IsFile(&isFile)) ||
                    NS_FAILED(currFile->IsDirectory(&isDir))) ||
@@ -282,32 +208,19 @@ GetDirectoryListingTask::Work()
       if (NS_WARN_IF(NS_FAILED(currFile->GetLeafName(leafName)))) {
         continue;
       }
       if (leafName[0] == char16_t('.')) {
         continue;
       }
     }
 
-    if (isDir) {
-      nsAutoString path;
-      if (NS_WARN_IF(NS_FAILED(currFile->GetPath(path)))) {
-        continue;
-      }
-
-      Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement();
-      element->mType = Directory::BlobImplOrDirectoryPath::eDirectoryPath;
-      element->mDirectoryPath = path;
-    } else {
-      BlobImplFile* impl = new BlobImplFile(currFile);
-
-      Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement();
-      element->mType = Directory::BlobImplOrDirectoryPath::eBlobImpl;
-      element->mBlobImpl = impl;
-    }
+    BlobImplFile* impl = new BlobImplFile(currFile);
+    impl->LookupAndCacheIsDirectory();
+    mTargetBlobImpls.AppendElement(impl);
   }
   return NS_OK;
 }
 
 void
 GetDirectoryListingTask::HandlerCallback()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
@@ -317,65 +230,47 @@ GetDirectoryListingTask::HandlerCallback
   }
 
   if (HasError()) {
     mPromise->MaybeReject(mErrorValue);
     mPromise = nullptr;
     return;
   }
 
-  size_t count = mTargetData.Length();
+  size_t count = mTargetBlobImpls.Length();
 
   Sequence<OwningFileOrDirectory> listing;
 
   if (!listing.SetLength(count, mozilla::fallible_t())) {
-    mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
+    mPromise->MaybeReject(NS_ERROR_FAILURE);
     mPromise = nullptr;
     return;
   }
 
   for (unsigned i = 0; i < count; i++) {
-    if (mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath) {
-      nsCOMPtr<nsIFile> directoryPath;
-      NS_ConvertUTF16toUTF8 path(mTargetData[i].mDirectoryPath);
-      nsresult rv = NS_NewNativeLocalFile(path, true,
-                                          getter_AddRefs(directoryPath));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        mPromise->MaybeReject(rv);
-        mPromise = nullptr;
-        return;
-      }
-
+    if (mTargetBlobImpls[i]->IsDirectory()) {
+      nsAutoString name;
+      mTargetBlobImpls[i]->GetName(name);
+      nsAutoString path(mTargetRealPath);
+      path.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR);
+      path.Append(name);
 #ifdef DEBUG
-      nsCOMPtr<nsIFile> rootPath;
-      rv = NS_NewLocalFile(mFileSystem->GetLocalRootPath(), false,
-                           getter_AddRefs(rootPath));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        mPromise->MaybeReject(rv);
-        mPromise = nullptr;
-        return;
+      if (XRE_IsParentProcess()) {
+        nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(path);
+        bool exist;
+        file->Exists(&exist);
+        MOZ_ASSERT(exist);
       }
-
-      MOZ_ASSERT(FileSystemUtils::IsDescendantPath(rootPath, directoryPath));
 #endif
-
-      RefPtr<Directory> directory =
-        Directory::Create(mFileSystem->GetParentObject(),
-                          directoryPath,
-                          Directory::eNotDOMRootDirectory,
-                          mFileSystem);
-      MOZ_ASSERT(directory);
-
+      RefPtr<Directory> directory = new Directory(mFileSystem, path);
       // Propogate mFilter onto sub-Directory object:
       directory->SetContentFilters(mFilters);
       listing[i].SetAsDirectory() = directory;
     } else {
-      MOZ_ASSERT(mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl);
-      listing[i].SetAsFile() =
-        File::Create(mFileSystem->GetParentObject(), mTargetData[i].mBlobImpl);
+      listing[i].SetAsFile() = File::Create(mFileSystem->GetWindow(), mTargetBlobImpls[i]);
     }
   }
 
   mPromise->MaybeResolve(listing);
   mPromise = nullptr;
 }
 
 void
--- a/dom/filesystem/GetDirectoryListingTask.h
+++ b/dom/filesystem/GetDirectoryListingTask.h
@@ -2,85 +2,68 @@
 /* vim: set ts=8 sts=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_GetDirectoryListing_h
 #define mozilla_dom_GetDirectoryListing_h
 
-#include "mozilla/dom/Directory.h"
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "mozilla/ErrorResult.h"
 #include "nsAutoPtr.h"
 
 namespace mozilla {
 namespace dom {
 
 class BlobImpl;
 
-class GetDirectoryListingTask final : public FileSystemTaskBase
+class GetDirectoryListingTask final
+  : public FileSystemTaskBase
 {
 public:
-  static already_AddRefed<GetDirectoryListingTask>
-  Create(FileSystemBase* aFileSystem,
-         nsIFile* aTargetPath,
-         Directory::DirectoryType aType,
-         const nsAString& aFilters,
-         ErrorResult& aRv);
-
-  static already_AddRefed<GetDirectoryListingTask>
-  Create(FileSystemBase* aFileSystem,
-         const FileSystemGetDirectoryListingParams& aParam,
-         FileSystemRequestParent* aParent,
-         ErrorResult& aRv);
+  // If aDirectoryOnly is set, we should ensure that the target is a directory.
+  GetDirectoryListingTask(FileSystemBase* aFileSystem,
+                          const nsAString& aTargetPath,
+                          const nsAString& aFilters,
+                          ErrorResult& aRv);
+  GetDirectoryListingTask(FileSystemBase* aFileSystem,
+                          const FileSystemGetDirectoryListingParams& aParam,
+                          FileSystemRequestParent* aParent);
 
   virtual
   ~GetDirectoryListingTask();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
-
-private:
-  // If aDirectoryOnly is set, we should ensure that the target is a directory.
-  GetDirectoryListingTask(FileSystemBase* aFileSystem,
-                          nsIFile* aTargetPath,
-                          Directory::DirectoryType aType,
-                          const nsAString& aFilters);
-
-  GetDirectoryListingTask(FileSystemBase* aFileSystem,
-                          const FileSystemGetDirectoryListingParams& aParam,
-                          FileSystemRequestParent* aParent);
-
+protected:
   virtual FileSystemParams
-  GetRequestParams(const nsString& aSerializedDOMPath,
-                   ErrorResult& aRv) const override;
+  GetRequestParams(const nsString& aFileSystem) const override;
 
   virtual FileSystemResponseValue
-  GetSuccessRequestResult(ErrorResult& aRv) const override;
+  GetSuccessRequestResult() const override;
 
   virtual void
-  SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                          ErrorResult& aRv) override;
+  SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
 
   virtual nsresult
   Work() override;
 
   virtual void
   HandlerCallback() override;
 
+private:
   RefPtr<Promise> mPromise;
-  nsCOMPtr<nsIFile> mTargetPath;
+  nsString mTargetRealPath;
   nsString mFilters;
-  Directory::DirectoryType mType;
 
   // We cannot store File or Directory objects bacause this object is created
   // on a different thread and File and Directory are not thread-safe.
-  nsTArray<Directory::BlobImplOrDirectoryPath> mTargetData;
+  nsTArray<RefPtr<BlobImpl>> mTargetBlobImpls;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_GetDirectoryListing_h
--- a/dom/filesystem/GetFileOrDirectoryTask.cpp
+++ b/dom/filesystem/GetFileOrDirectoryTask.cpp
@@ -2,180 +2,114 @@
 /* vim: set ts=8 sts=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 "GetFileOrDirectoryTask.h"
 
 #include "js/Value.h"
+#include "mozilla/dom/Directory.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "nsIFile.h"
 #include "nsStringGlue.h"
 
 namespace mozilla {
 namespace dom {
 
-/* static */ already_AddRefed<GetFileOrDirectoryTask>
-GetFileOrDirectoryTask::Create(FileSystemBase* aFileSystem,
-                               nsIFile* aTargetPath,
-                               Directory::DirectoryType aType,
-                               bool aDirectoryOnly,
-                               ErrorResult& aRv)
+GetFileOrDirectoryTask::GetFileOrDirectoryTask(
+  FileSystemBase* aFileSystem,
+  const nsAString& aTargetPath,
+  bool aDirectoryOnly,
+  ErrorResult& aRv)
+  : FileSystemTaskBase(aFileSystem)
+  , mTargetRealPath(aTargetPath)
+  , mIsDirectory(aDirectoryOnly)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
-
-  RefPtr<GetFileOrDirectoryTask> task =
-    new GetFileOrDirectoryTask(aFileSystem, aTargetPath, aType, aDirectoryOnly);
-
-  // aTargetPath can be null. In this case SetError will be called.
-
   nsCOMPtr<nsIGlobalObject> globalObject =
-    do_QueryInterface(aFileSystem->GetParentObject());
-  if (NS_WARN_IF(!globalObject)) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
+    do_QueryInterface(aFileSystem->GetWindow());
+  if (!globalObject) {
+    return;
   }
-
-  task->mPromise = Promise::Create(globalObject, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  return task.forget();
+  mPromise = Promise::Create(globalObject, aRv);
 }
 
-/* static */ already_AddRefed<GetFileOrDirectoryTask>
-GetFileOrDirectoryTask::Create(FileSystemBase* aFileSystem,
-                               const FileSystemGetFileOrDirectoryParams& aParam,
-                               FileSystemRequestParent* aParent,
-                               ErrorResult& aRv)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-
-  RefPtr<GetFileOrDirectoryTask> task =
-    new GetFileOrDirectoryTask(aFileSystem, aParam, aParent);
-
-  NS_ConvertUTF16toUTF8 path(aParam.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  task->mType = aParam.isRoot()
-                  ? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory;
-  return task.forget();
-}
-
-GetFileOrDirectoryTask::GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
-                                               nsIFile* aTargetPath,
-                                               Directory::DirectoryType aType,
-                                               bool aDirectoryOnly)
-  : FileSystemTaskBase(aFileSystem)
-  , mTargetPath(aTargetPath)
-  , mIsDirectory(aDirectoryOnly)
-  , mType(aType)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-}
-
-GetFileOrDirectoryTask::GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
-                                               const FileSystemGetFileOrDirectoryParams& aParam,
-                                               FileSystemRequestParent* aParent)
+GetFileOrDirectoryTask::GetFileOrDirectoryTask(
+  FileSystemBase* aFileSystem,
+  const FileSystemGetFileOrDirectoryParams& aParam,
+  FileSystemRequestParent* aParent)
   : FileSystemTaskBase(aFileSystem, aParam, aParent)
   , mIsDirectory(false)
 {
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  MOZ_ASSERT(XRE_IsParentProcess(),
+             "Only call from parent process!");
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
+  mTargetRealPath = aParam.realPath();
 }
 
 GetFileOrDirectoryTask::~GetFileOrDirectoryTask()
 {
   MOZ_ASSERT(!mPromise || NS_IsMainThread(),
              "mPromise should be released on main thread!");
 }
 
 already_AddRefed<Promise>
 GetFileOrDirectoryTask::GetPromise()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   return RefPtr<Promise>(mPromise).forget();
 }
 
 FileSystemParams
-GetFileOrDirectoryTask::GetRequestParams(const nsString& aSerializedDOMPath,
-                                         ErrorResult& aRv) const
+GetFileOrDirectoryTask::GetRequestParams(const nsString& aFileSystem) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-
-  nsAutoString path;
-  aRv = mTargetPath->GetPath(path);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return FileSystemGetFileOrDirectoryParams();
-  }
-
-  return FileSystemGetFileOrDirectoryParams(aSerializedDOMPath, path,
-                                            mType == Directory::eDOMRootDirectory);
+  return FileSystemGetFileOrDirectoryParams(aFileSystem, mTargetRealPath);
 }
 
 FileSystemResponseValue
-GetFileOrDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const
+GetFileOrDirectoryTask::GetSuccessRequestResult() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   if (mIsDirectory) {
-    nsAutoString path;
-    aRv = mTargetPath->GetPath(path);
-    if (NS_WARN_IF(aRv.Failed())) {
-      return FileSystemDirectoryResponse();
-    }
-
-    return FileSystemDirectoryResponse(path);
+    return FileSystemDirectoryResponse(mTargetRealPath);
   }
 
   BlobParent* actor = GetBlobParent(mTargetBlobImpl);
   if (!actor) {
     return FileSystemErrorResponse(NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR);
   }
   FileSystemFileResponse response;
   response.blobParent() = actor;
   return response;
 }
 
 void
-GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                                                ErrorResult& aRv)
+GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   switch (aValue.type()) {
     case FileSystemResponseValue::TFileSystemFileResponse: {
       FileSystemFileResponse r = aValue;
       BlobChild* actor = static_cast<BlobChild*>(r.blobChild());
       mTargetBlobImpl = actor->GetBlobImpl();
       mIsDirectory = false;
       break;
     }
     case FileSystemResponseValue::TFileSystemDirectoryResponse: {
       FileSystemDirectoryResponse r = aValue;
-
-      NS_ConvertUTF16toUTF8 path(r.realPath());
-      aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
-      if (NS_WARN_IF(aRv.Failed())) {
-        return;
-      }
-
+      mTargetRealPath = r.realPath();
       mIsDirectory = true;
       break;
     }
     default: {
       NS_RUNTIMEABORT("not reached");
       break;
     }
   }
@@ -188,66 +122,73 @@ GetFileOrDirectoryTask::Work()
              "Only call from parent process!");
   MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
 
   if (mFileSystem->IsShutdown()) {
     return NS_ERROR_FAILURE;
   }
 
   // Whether we want to get the root directory.
+  bool getRoot = mTargetRealPath.IsEmpty();
+
+  nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(mTargetRealPath);
+  if (!file) {
+    return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
+  }
+
   bool exists;
-  nsresult rv = mTargetPath->Exists(&exists);
+  nsresult rv = file->Exists(&exists);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (!exists) {
-    if (mType == Directory::eNotDOMRootDirectory) {
+    if (!getRoot) {
       return NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
     }
 
     // If the root directory doesn't exit, create it.
-    rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
+    rv = file->Create(nsIFile::DIRECTORY_TYPE, 0777);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   // Get isDirectory.
-  rv = mTargetPath->IsDirectory(&mIsDirectory);
+  rv = file->IsDirectory(&mIsDirectory);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (mIsDirectory) {
     return NS_OK;
   }
 
   // Check if the root is a directory.
-  if (mType == Directory::eDOMRootDirectory) {
+  if (getRoot) {
     return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
   }
 
   bool isFile;
   // Get isFile
-  rv = mTargetPath->IsFile(&isFile);
+  rv = file->IsFile(&isFile);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (!isFile) {
     // Neither directory or file.
     return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
   }
 
-  if (!mFileSystem->IsSafeFile(mTargetPath)) {
+  if (!mFileSystem->IsSafeFile(file)) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  mTargetBlobImpl = new BlobImplFile(mTargetPath);
+  mTargetBlobImpl = new BlobImplFile(file);
 
   return NS_OK;
 }
 
 void
 GetFileOrDirectoryTask::HandlerCallback()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
@@ -258,29 +199,23 @@ GetFileOrDirectoryTask::HandlerCallback(
 
   if (HasError()) {
     mPromise->MaybeReject(mErrorValue);
     mPromise = nullptr;
     return;
   }
 
   if (mIsDirectory) {
-    RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
-                                              mTargetPath,
-                                              mType,
-                                              mFileSystem);
-    MOZ_ASSERT(dir);
-
+    RefPtr<Directory> dir = new Directory(mFileSystem, mTargetRealPath);
     mPromise->MaybeResolve(dir);
     mPromise = nullptr;
     return;
   }
 
-  RefPtr<Blob> blob = Blob::Create(mFileSystem->GetParentObject(),
-                                   mTargetBlobImpl);
+  RefPtr<Blob> blob = Blob::Create(mFileSystem->GetWindow(), mTargetBlobImpl);
   mPromise->MaybeResolve(blob);
   mPromise = nullptr;
 }
 
 void
 GetFileOrDirectoryTask::GetPermissionAccessType(nsCString& aAccess) const
 {
   aAccess.AssignLiteral("read");
--- a/dom/filesystem/GetFileOrDirectoryTask.h
+++ b/dom/filesystem/GetFileOrDirectoryTask.h
@@ -2,85 +2,67 @@
 /* vim: set ts=8 sts=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_GetFileOrDirectory_h
 #define mozilla_dom_GetFileOrDirectory_h
 
-#include "mozilla/dom/Directory.h"
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "nsAutoPtr.h"
 #include "mozilla/ErrorResult.h"
 
 namespace mozilla {
 namespace dom {
 
 class BlobImpl;
 
-class GetFileOrDirectoryTask final : public FileSystemTaskBase
+class GetFileOrDirectoryTask final
+  : public FileSystemTaskBase
 {
 public:
-  static already_AddRefed<GetFileOrDirectoryTask>
-  Create(FileSystemBase* aFileSystem,
-         nsIFile* aTargetPath,
-         Directory::DirectoryType aType,
-         bool aDirectoryOnly,
-         ErrorResult& aRv);
-
-  static already_AddRefed<GetFileOrDirectoryTask>
-  Create(FileSystemBase* aFileSystem,
-         const FileSystemGetFileOrDirectoryParams& aParam,
-         FileSystemRequestParent* aParent,
-         ErrorResult& aRv);
+  // If aDirectoryOnly is set, we should ensure that the target is a directory.
+  GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
+                         const nsAString& aTargetPath,
+                         bool aDirectoryOnly,
+                         ErrorResult& aRv);
+  GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
+                         const FileSystemGetFileOrDirectoryParams& aParam,
+                         FileSystemRequestParent* aParent);
 
   virtual
   ~GetFileOrDirectoryTask();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
 protected:
   virtual FileSystemParams
-  GetRequestParams(const nsString& aSerializedDOMPath,
-                   ErrorResult& aRv) const override;
+  GetRequestParams(const nsString& aFileSystem) const override;
 
   virtual FileSystemResponseValue
-  GetSuccessRequestResult(ErrorResult& aRv) const override;
+  GetSuccessRequestResult() const override;
 
   virtual void
-  SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                          ErrorResult& aRv) override;
+  SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
 
   virtual nsresult
   Work() override;
 
   virtual void
   HandlerCallback() override;
 
 private:
-  // If aDirectoryOnly is set, we should ensure that the target is a directory.
-  GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
-                         nsIFile* aTargetPath,
-                         Directory::DirectoryType aType,
-                         bool aDirectoryOnly);
-
-  GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
-                         const FileSystemGetFileOrDirectoryParams& aParam,
-                         FileSystemRequestParent* aParent);
-
   RefPtr<Promise> mPromise;
-  nsCOMPtr<nsIFile> mTargetPath;
-
+  nsString mTargetRealPath;
   // Whether we get a directory.
   bool mIsDirectory;
-  Directory::DirectoryType mType;
 
   // This cannot be a File bacause this object is created on a different
   // thread and File is not thread-safe. Let's use the BlobImpl instead.
   RefPtr<BlobImpl> mTargetBlobImpl;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/OSFileSystem.cpp
+++ b/dom/filesystem/OSFileSystem.cpp
@@ -4,62 +4,61 @@
  * 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 "mozilla/dom/OSFileSystem.h"
 
 #include "mozilla/dom/Directory.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileSystemUtils.h"
-#include "nsIGlobalObject.h"
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
 #include "nsIFile.h"
+#include "nsPIDOMWindow.h"
 
 namespace mozilla {
 namespace dom {
 
 OSFileSystem::OSFileSystem(const nsAString& aRootDir)
 {
   mLocalRootPath = aRootDir;
+  FileSystemUtils::LocalPathToNormalizedPath(mLocalRootPath,
+                                             mNormalizedLocalRootPath);
 
   // Non-mobile devices don't have the concept of separate permissions to
   // access different parts of devices storage like Pictures, or Videos, etc.
   mRequiresPermissionChecks = false;
 
+  mString = mLocalRootPath;
+
 #ifdef DEBUG
   mPermission.AssignLiteral("never-used");
 #endif
 }
 
 void
-OSFileSystem::Init(nsISupports* aParent)
+OSFileSystem::Init(nsPIDOMWindowInner* aWindow)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(!mParent, "No duple Init() calls");
-  MOZ_ASSERT(aParent);
-  mParent = aParent;
-
-#ifdef DEBUG
-  nsCOMPtr<nsIGlobalObject> obj = do_QueryInterface(aParent);
-  MOZ_ASSERT(obj);
-#endif
+  MOZ_ASSERT(!mWindow, "No duple Init() calls");
+  MOZ_ASSERT(aWindow);
+  mWindow = aWindow;
 }
 
-nsISupports*
-OSFileSystem::GetParentObject() const
+nsPIDOMWindowInner*
+OSFileSystem::GetWindow() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  return mParent;
+  return mWindow;
 }
 
 void
 OSFileSystem::GetRootName(nsAString& aRetval) const
 {
-  aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
+  return aRetval.AssignLiteral("/");
 }
 
 bool
 OSFileSystem::IsSafeFile(nsIFile* aFile) const
 {
   // The concept of "safe files" is specific to the Device Storage API where
   // files are only "safe" if they're of a type that is appropriate for the
   // area of device storage that is being used.
@@ -75,26 +74,20 @@ OSFileSystem::IsSafeDirectory(Directory*
   // storage that it is being used with.
   MOZ_CRASH("Don't use OSFileSystem with the Device Storage API");
   return true;
 }
 
 void
 OSFileSystem::Unlink()
 {
-  mParent = nullptr;
+  mWindow = nullptr;
 }
 
 void
 OSFileSystem::Traverse(nsCycleCollectionTraversalCallback &cb)
 {
   OSFileSystem* tmp = this;
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent);
-}
-
-void
-OSFileSystem::SerializeDOMPath(nsAString& aOutput) const
-{
-  aOutput = mLocalRootPath;
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/OSFileSystem.h
+++ b/dom/filesystem/OSFileSystem.h
@@ -13,41 +13,38 @@ namespace mozilla {
 namespace dom {
 
 class OSFileSystem final : public FileSystemBase
 {
 public:
   explicit OSFileSystem(const nsAString& aRootDir);
 
   void
-  Init(nsISupports* aParent);
+  Init(nsPIDOMWindowInner* aWindow);
 
   // Overrides FileSystemBase
 
-  virtual nsISupports*
-  GetParentObject() const override;
+  virtual nsPIDOMWindowInner*
+  GetWindow() const override;
 
   virtual void
   GetRootName(nsAString& aRetval) const override;
 
   virtual bool
   IsSafeFile(nsIFile* aFile) const override;
 
   virtual bool
   IsSafeDirectory(Directory* aDir) const override;
 
-  virtual void
-  SerializeDOMPath(nsAString& aOutput) const override;
-
   // CC methods
   virtual void Unlink() override;
   virtual void Traverse(nsCycleCollectionTraversalCallback &cb) override;
 
 private:
   virtual ~OSFileSystem() {}
 
-   nsCOMPtr<nsISupports> mParent;
+   nsCOMPtr<nsPIDOMWindowInner> mWindow;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_OSFileSystem_h
--- a/dom/filesystem/PFileSystemRequest.ipdl
+++ b/dom/filesystem/PFileSystemRequest.ipdl
@@ -15,36 +15,19 @@ struct FileSystemFileResponse
   PBlob blob;
 };
 
 struct FileSystemDirectoryResponse
 {
   nsString realPath;
 };
 
-struct FileSystemDirectoryListingResponseBlob
-{
-  PBlob blob;
-};
-
-struct FileSystemDirectoryListingResponseDirectory
-{
-  // This is the full real path for the directory that we are sending via IPC.
-  nsString directoryRealPath;
-};
-
-union FileSystemDirectoryListingResponseData
-{
-  FileSystemDirectoryListingResponseBlob;
-  FileSystemDirectoryListingResponseDirectory;
-};
-
 struct FileSystemDirectoryListingResponse
 {
-  FileSystemDirectoryListingResponseData[] data;
+  PBlob[] blobs;
 };
 
 struct FileSystemErrorResponse
 {
   nsresult error;
 };
 
 struct FileSystemBooleanResponse
--- a/dom/filesystem/RemoveTask.cpp
+++ b/dom/filesystem/RemoveTask.cpp
@@ -13,175 +13,111 @@
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "nsIFile.h"
 #include "nsStringGlue.h"
 
 namespace mozilla {
 namespace dom {
 
-/* static */ already_AddRefed<RemoveTask>
-RemoveTask::Create(FileSystemBase* aFileSystem,
-                   nsIFile* aDirPath,
-                   BlobImpl* aTargetBlob,
-                   nsIFile* aTargetPath,
-                   bool aRecursive,
-                   ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-  MOZ_ASSERT(aDirPath);
-
-  RefPtr<RemoveTask> task =
-    new RemoveTask(aFileSystem, aDirPath, aTargetBlob, aTargetPath, aRecursive);
-
-  // aTargetPath can be null. In this case SetError will be called.
-
-  nsCOMPtr<nsIGlobalObject> globalObject =
-    do_QueryInterface(aFileSystem->GetParentObject());
-  if (NS_WARN_IF(!globalObject)) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
-  }
-
-  task->mPromise = Promise::Create(globalObject, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  return task.forget();
-}
-
-/* static */ already_AddRefed<RemoveTask>
-RemoveTask::Create(FileSystemBase* aFileSystem,
-                   const FileSystemRemoveParams& aParam,
-                   FileSystemRequestParent* aParent,
-                   ErrorResult& aRv)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-
-  RefPtr<RemoveTask> task =
-    new RemoveTask(aFileSystem, aParam, aParent);
-
-  NS_ConvertUTF16toUTF8 directoryPath(aParam.directory());
-  aRv = NS_NewNativeLocalFile(directoryPath, true,
-                              getter_AddRefs(task->mDirPath));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  task->mRecursive = aParam.recursive();
-
-  const FileSystemPathOrFileValue& target = aParam.target();
-
-  if (target.type() == FileSystemPathOrFileValue::TnsString) {
-    NS_ConvertUTF16toUTF8 path(target);
-    aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
-    if (NS_WARN_IF(aRv.Failed())) {
-      return nullptr;
-    }
-
-    return task.forget();
-  }
-
-  BlobParent* bp = static_cast<BlobParent*>(static_cast<PBlobParent*>(target));
-  task->mTargetBlobImpl = bp->GetBlobImpl();
-  MOZ_ASSERT(task->mTargetBlobImpl);
-
-  return task.forget();
-}
-
 RemoveTask::RemoveTask(FileSystemBase* aFileSystem,
-                       nsIFile* aDirPath,
+                       const nsAString& aDirPath,
                        BlobImpl* aTargetBlob,
-                       nsIFile* aTargetPath,
-                       bool aRecursive)
+                       const nsAString& aTargetPath,
+                       bool aRecursive,
+                       ErrorResult& aRv)
   : FileSystemTaskBase(aFileSystem)
-  , mDirPath(aDirPath)
+  , mDirRealPath(aDirPath)
   , mTargetBlobImpl(aTargetBlob)
-  , mTargetPath(aTargetPath)
+  , mTargetRealPath(aTargetPath)
   , mRecursive(aRecursive)
   , mReturnValue(false)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
-  MOZ_ASSERT(aDirPath);
+  nsCOMPtr<nsIGlobalObject> globalObject =
+    do_QueryInterface(aFileSystem->GetWindow());
+  if (!globalObject) {
+    return;
+  }
+  mPromise = Promise::Create(globalObject, aRv);
 }
 
 RemoveTask::RemoveTask(FileSystemBase* aFileSystem,
                        const FileSystemRemoveParams& aParam,
                        FileSystemRequestParent* aParent)
   : FileSystemTaskBase(aFileSystem, aParam, aParent)
   , mRecursive(false)
   , mReturnValue(false)
 {
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  MOZ_ASSERT(XRE_IsParentProcess(),
+             "Only call from parent process!");
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
+
+  mDirRealPath = aParam.directory();
+
+  mRecursive = aParam.recursive();
+
+  const FileSystemPathOrFileValue& target = aParam.target();
+
+  if (target.type() == FileSystemPathOrFileValue::TnsString) {
+    mTargetRealPath = target;
+    return;
+  }
+
+  BlobParent* bp = static_cast<BlobParent*>(static_cast<PBlobParent*>(target));
+  mTargetBlobImpl = bp->GetBlobImpl();
+  MOZ_ASSERT(mTargetBlobImpl);
 }
 
 RemoveTask::~RemoveTask()
 {
   MOZ_ASSERT(!mPromise || NS_IsMainThread(),
              "mPromise should be released on main thread!");
 }
 
 already_AddRefed<Promise>
 RemoveTask::GetPromise()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   return RefPtr<Promise>(mPromise).forget();
 }
 
 FileSystemParams
-RemoveTask::GetRequestParams(const nsString& aSerializedDOMPath,
-                             ErrorResult& aRv) const
+RemoveTask::GetRequestParams(const nsString& aFileSystem) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   FileSystemRemoveParams param;
-  param.filesystem() = aSerializedDOMPath;
-
-  aRv = mDirPath->GetPath(param.directory());
-  if (NS_WARN_IF(aRv.Failed())) {
-    return param;
-  }
-
+  param.filesystem() = aFileSystem;
+  param.directory() = mDirRealPath;
   param.recursive() = mRecursive;
   if (mTargetBlobImpl) {
-    RefPtr<Blob> blob = Blob::Create(mFileSystem->GetParentObject(),
-                                     mTargetBlobImpl);
+    RefPtr<Blob> blob = Blob::Create(mFileSystem->GetWindow(),
+                                       mTargetBlobImpl);
     BlobChild* actor
       = ContentChild::GetSingleton()->GetOrCreateActorForBlob(blob);
     if (actor) {
       param.target() = actor;
     }
   } else {
-    nsAutoString path;
-    aRv = mTargetPath->GetPath(path);
-    if (NS_WARN_IF(aRv.Failed())) {
-      return param;
-    }
-
-    param.target() = path;
+    param.target() = mTargetRealPath;
   }
   return param;
 }
 
 FileSystemResponseValue
-RemoveTask::GetSuccessRequestResult(ErrorResult& aRv) const
+RemoveTask::GetSuccessRequestResult() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   return FileSystemBooleanResponse(mReturnValue);
 }
 
 void
-RemoveTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                                    ErrorResult& aRv)
+RemoveTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   FileSystemBooleanResponse r = aValue;
   mReturnValue = r.success();
 }
 
 nsresult
 RemoveTask::Work()
@@ -189,49 +125,53 @@ RemoveTask::Work()
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Only call from parent process!");
   MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
 
   if (mFileSystem->IsShutdown()) {
     return NS_ERROR_FAILURE;
   }
 
-  // Get the path if a File is passed as the target.
+  // Get the DOM path if a File is passed as the target.
   if (mTargetBlobImpl) {
-    if (!mFileSystem->GetRealPath(mTargetBlobImpl,
-                                  getter_AddRefs(mTargetPath))) {
+    if (!mFileSystem->GetRealPath(mTargetBlobImpl, mTargetRealPath)) {
       return NS_ERROR_DOM_SECURITY_ERR;
     }
-    if (!FileSystemUtils::IsDescendantPath(mDirPath, mTargetPath)) {
+    if (!FileSystemUtils::IsDescendantPath(mDirRealPath, mTargetRealPath)) {
       return NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
     }
   }
 
+  nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(mTargetRealPath);
+  if (!file) {
+    return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
+  }
+
   bool exists = false;
-  nsresult rv = mTargetPath->Exists(&exists);
+  nsresult rv = file->Exists(&exists);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (!exists) {
     mReturnValue = false;
     return NS_OK;
   }
 
   bool isFile = false;
-  rv = mTargetPath->IsFile(&isFile);
+  rv = file->IsFile(&isFile);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  if (isFile && !mFileSystem->IsSafeFile(mTargetPath)) {
+  if (isFile && !mFileSystem->IsSafeFile(file)) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  rv = mTargetPath->Remove(mRecursive);
+  rv = file->Remove(mRecursive);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   mReturnValue = true;
 
   return NS_OK;
 }
--- a/dom/filesystem/RemoveTask.h
+++ b/dom/filesystem/RemoveTask.h
@@ -12,78 +12,62 @@
 #include "mozilla/ErrorResult.h"
 
 namespace mozilla {
 namespace dom {
 
 class BlobImpl;
 class Promise;
 
-class RemoveTask final : public FileSystemTaskBase
+class RemoveTask final
+  : public FileSystemTaskBase
 {
 public:
-  static already_AddRefed<RemoveTask>
-  Create(FileSystemBase* aFileSystem,
-         nsIFile* aDirPath,
-         BlobImpl* aTargetBlob,
-         nsIFile* aTargetPath,
-         bool aRecursive,
-         ErrorResult& aRv);
-
-  static already_AddRefed<RemoveTask>
-  Create(FileSystemBase* aFileSystem,
-         const FileSystemRemoveParams& aParam,
-         FileSystemRequestParent* aParent,
-         ErrorResult& aRv);
+  RemoveTask(FileSystemBase* aFileSystem,
+             const nsAString& aDirPath,
+             BlobImpl* aTargetBlob,
+             const nsAString& aTargetPath,
+             bool aRecursive,
+             ErrorResult& aRv);
+  RemoveTask(FileSystemBase* aFileSystem,
+             const FileSystemRemoveParams& aParam,
+             FileSystemRequestParent* aParent);
 
   virtual
   ~RemoveTask();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
 
 protected:
   virtual FileSystemParams
-  GetRequestParams(const nsString& aSerializedDOMPath,
-                   ErrorResult& aRv) const override;
+  GetRequestParams(const nsString& aFileSystem) const override;
 
   virtual FileSystemResponseValue
-  GetSuccessRequestResult(ErrorResult& aRv) const override;
+  GetSuccessRequestResult() const override;
 
   virtual void
-  SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                          ErrorResult& aRv) override;
+  SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
 
   virtual nsresult
   Work() override;
 
   virtual void
   HandlerCallback() override;
 
 private:
-  RemoveTask(FileSystemBase* aFileSystem,
-             nsIFile* aDirPath,
-             BlobImpl* aTargetBlob,
-             nsIFile* aTargetPath,
-             bool aRecursive);
-
-  RemoveTask(FileSystemBase* aFileSystem,
-             const FileSystemRemoveParams& aParam,
-             FileSystemRequestParent* aParent);
-
   RefPtr<Promise> mPromise;
-  nsCOMPtr<nsIFile> mDirPath;
-
+  nsString mDirRealPath;
   // This cannot be a File because this object will be used on a different
   // thread and File is not thread-safe. Let's use the BlobImpl instead.
   RefPtr<BlobImpl> mTargetBlobImpl;
-  nsCOMPtr<nsIFile> mTargetPath;
+  nsString mTargetRealPath;
   bool mRecursive;
   bool mReturnValue;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_RemoveTask_h
--- a/dom/filesystem/moz.build
+++ b/dom/filesystem/moz.build
@@ -1,16 +1,14 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-TEST_DIRS += ['tests']
-
 EXPORTS.mozilla.dom += [
     'DeviceStorageFileSystem.h',
     'Directory.h',
     'FileSystemBase.h',
     'FileSystemRequestParent.h',
     'FileSystemTaskBase.h',
     'FileSystemUtils.h',
     'OSFileSystem.h',
deleted file mode 100644
--- a/dom/filesystem/tests/mochitest.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[DEFAULT]
-support-files =
-  script_fileList.js
-
-[test_basic.html]
deleted file mode 100644
--- a/dom/filesystem/tests/moz.build
+++ /dev/null
@@ -1,7 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-MOCHITEST_MANIFESTS += ['mochitest.ini']
deleted file mode 100644
--- a/dom/filesystem/tests/script_fileList.js
+++ /dev/null
@@ -1,25 +0,0 @@
-var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-Cu.importGlobalProperties(["File"]);
-
-addMessageListener("file.open", function () {
-  var testFile = Cc["@mozilla.org/file/directory_service;1"]
-                   .getService(Ci.nsIDirectoryService)
-                   .QueryInterface(Ci.nsIProperties)
-                   .get("ProfD", Ci.nsIFile);
-  testFile.append("prefs.js");
-
-  sendAsyncMessage("file.opened", {
-    file: new File(testFile)
-  });
-});
-
-addMessageListener("dir.open", function () {
-  var testFile = Cc["@mozilla.org/file/directory_service;1"]
-                   .getService(Ci.nsIDirectoryService)
-                   .QueryInterface(Ci.nsIProperties)
-                   .get("ProfD", Ci.nsIFile);
-
-  sendAsyncMessage("dir.opened", {
-    dir: testFile.path
-  });
-});
deleted file mode 100644
--- a/dom/filesystem/tests/test_basic.html
+++ /dev/null
@@ -1,103 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test for Directory API</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-
-<body>
-<input id="fileList" type="file"></input>
-<script type="application/javascript;version=1.7">
-
-var directory;
-
-function create_fileList() {
-  var url = SimpleTest.getTestFileURL("script_fileList.js");
-  var script = SpecialPowers.loadChromeScript(url);
-
-  function onOpened(message) {
-    var fileList = document.getElementById('fileList');
-    SpecialPowers.wrap(fileList).mozSetDirectory(message.dir);
-
-    // Just a simple test
-    is(fileList.files.length, 1, "Filelist has 1 element");
-    ok(fileList.files[0] instanceof Directory, "We have a directory.");
-
-    directory = fileList.files[0];
-
-    script.destroy();
-    next();
-  }
-
-  script.addMessageListener("dir.opened", onOpened);
-  script.sendAsyncMessage("dir.open");
-}
-
-function test_basic() {
-  ok(directory, "Directory exists.");
-  ok(directory instanceof Directory, "We have a directory.");
-  is(directory.name, '/', "directory.name must be '/'");
-  is(directory.path, '/', "directory.path must be '/'");
-  next();
-}
-
-function checkSubDir(dir) {
-  dir.getFilesAndDirectories().then(
-    function(data) {
-      for (var i = 0; i < data.length; ++i) {
-        ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories");
-        if (data[i] instanceof Directory) {
-          isnot(data[i].name, '/', "Subdirectory should be called with the leafname");
-          isnot(data[i].path, '/', "Subdirectory path should be called with the leafname");
-        }
-      }
-    }
-  );
-}
-
-function getFilesAndDirectories() {
-  directory.getFilesAndDirectories().then(
-    function(data) {
-      ok(data.length, "We should have some data.");
-      var promises = [];
-      for (var i = 0; i < data.length; ++i) {
-        ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories");
-        if (data[i] instanceof Directory) {
-          isnot(data[i].name, '/', "Subdirectory should be called with the leafname");
-          isnot(data[i].path, '/', "Subdirectory path should be called with the leafname");
-          promises.push(checkSubDir(data[i]));
-        }
-      }
-
-      return Promise.all(promises);
-    },
-    function() {
-      ok(false, "Something when wrong");
-    }
-  ).then(next);
-}
-
-var tests = [
-  create_fileList,
-
-  test_basic,
-
-  getFilesAndDirectories,
-];
-
-function next() {
-  if (!tests.length) {
-    SimpleTest.finish();
-    return;
-  }
-
-  var test = tests.shift();
-  test();
-}
-
-SimpleTest.waitForExplicitFinish();
-next();
-</script>
-</body>
-</html>
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -6,16 +6,18 @@
 
 #include "mozilla/dom/HTMLInputElement.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/Date.h"
 #include "mozilla/dom/Directory.h"
+#include "mozilla/dom/FileSystemUtils.h"
+#include "mozilla/dom/OSFileSystem.h"
 #include "nsAttrValueInlines.h"
 #include "nsCRTGlue.h"
 
 #include "nsIDOMHTMLInputElement.h"
 #include "nsITextControlElement.h"
 #include "nsIDOMNSEditableElement.h"
 #include "nsIRadioVisitor.h"
 #include "nsIPhonetic.h"
@@ -244,89 +246,40 @@ class HTMLInputElementState final : publ
       return mValue;
     }
 
     void SetValue(const nsAString& aValue)
     {
       mValue = aValue;
     }
 
-    void
-    GetFilesOrDirectories(nsPIDOMWindowInner* aWindow,
-                          nsTArray<OwningFileOrDirectory>& aResult) const
+    const nsTArray<RefPtr<BlobImpl>>& GetBlobImpls()
     {
-      for (uint32_t i = 0; i < mBlobImplsOrDirectoryPaths.Length(); ++i) {
-        if (mBlobImplsOrDirectoryPaths[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl) {
-          RefPtr<File> file =
-            File::Create(aWindow,
-                         mBlobImplsOrDirectoryPaths[i].mBlobImpl);
-          MOZ_ASSERT(file);
-
-          OwningFileOrDirectory* element = aResult.AppendElement();
-          element->SetAsFile() = file;
-        } else {
-          MOZ_ASSERT(mBlobImplsOrDirectoryPaths[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath);
-
-          nsCOMPtr<nsIFile> file;
-          NS_ConvertUTF16toUTF8 path(mBlobImplsOrDirectoryPaths[i].mDirectoryPath);
-          nsresult rv = NS_NewNativeLocalFile(path, true, getter_AddRefs(file));
-          if (NS_WARN_IF(NS_FAILED(rv))) {
-            continue;
-          }
-
-          RefPtr<Directory> directory = Directory::Create(aWindow, file,
-                                                          Directory::eDOMRootDirectory);
-          MOZ_ASSERT(directory);
-
-          OwningFileOrDirectory* element = aResult.AppendElement();
-          element->SetAsDirectory() = directory;
-        }
-      }
-    }
-
-    void SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& aArray)
+      return mBlobImpls;
+    }
+
+    void SetBlobImpls(const nsTArray<RefPtr<File>>& aFile)
     {
-      mBlobImplsOrDirectoryPaths.Clear();
-      for (uint32_t i = 0; i < aArray.Length(); ++i) {
-        if (aArray[i].IsFile()) {
-          Directory::BlobImplOrDirectoryPath* data =
-            mBlobImplsOrDirectoryPaths.AppendElement();
-
-          data->mBlobImpl = aArray[i].GetAsFile()->Impl();
-          data->mType = Directory::BlobImplOrDirectoryPath::eBlobImpl;
-        } else {
-          MOZ_ASSERT(aArray[i].IsDirectory());
-          nsAutoString fullPath;
-          nsresult rv = aArray[i].GetAsDirectory()->GetFullRealPath(fullPath);
-          if (NS_WARN_IF(NS_FAILED(rv))) {
-            continue;
-          }
-
-          Directory::BlobImplOrDirectoryPath* data =
-            mBlobImplsOrDirectoryPaths.AppendElement();
-
-          data->mDirectoryPath = fullPath;
-          data->mType = Directory::BlobImplOrDirectoryPath::eDirectoryPath;
-        }
+      mBlobImpls.Clear();
+      for (uint32_t i = 0, len = aFile.Length(); i < len; ++i) {
+        mBlobImpls.AppendElement(aFile[i]->Impl());
       }
     }
 
     HTMLInputElementState()
       : mValue()
       , mChecked(false)
       , mCheckedSet(false)
     {}
 
   protected:
     ~HTMLInputElementState() {}
 
     nsString mValue;
-
-    nsTArray<Directory::BlobImplOrDirectoryPath> mBlobImplsOrDirectoryPaths;
-
+    nsTArray<RefPtr<BlobImpl>> mBlobImpls;
     bool mChecked;
     bool mCheckedSet;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(HTMLInputElementState, NS_INPUT_ELEMENT_STATE_IID)
 
 NS_IMPL_ISUPPORTS(HTMLInputElementState, HTMLInputElementState)
 
@@ -382,92 +335,60 @@ UploadLastDir::ContentPrefCallback::Hand
   // HandleCompletion is always called (even with HandleError was called),
   // so we don't need to do anything special here.
   return NS_OK;
 }
 
 namespace {
 
 /**
- * This may return nullptr if the DOM File's implementation of
+ * This may return nullptr if aDomFile's implementation of
  * File::mozFullPathInternal does not successfully return a non-empty
  * string that is a valid path. This can happen on Firefox OS, for example,
  * where the file picker can create Blobs.
  */
 static already_AddRefed<nsIFile>
-DOMFileOrDirectoryToLocalFile(const OwningFileOrDirectory& aData)
+DOMFileToLocalFile(File* aDomFile)
 {
   nsString path;
-
-  if (aData.IsFile()) {
-    ErrorResult rv;
-    aData.GetAsFile()->GetMozFullPathInternal(path, rv);
-    if (rv.Failed() || path.IsEmpty()) {
-      rv.SuppressException();
-      return nullptr;
-    }
-  } else {
-    MOZ_ASSERT(aData.IsDirectory());
-    aData.GetAsDirectory()->GetFullRealPath(path);
+  ErrorResult rv;
+  aDomFile->GetMozFullPathInternal(path, rv);
+  if (rv.Failed() || path.IsEmpty()) {
+    rv.SuppressException();
+    return nullptr;
   }
 
   nsCOMPtr<nsIFile> localFile;
-  nsresult rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true,
-                                      getter_AddRefs(localFile));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
+  rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true,
+                             getter_AddRefs(localFile));
+  if (NS_WARN_IF(rv.Failed())) {
+    rv.SuppressException();
     return nullptr;
   }
 
   return localFile.forget();
 }
 
-void
-GetDOMFileOrDirectoryName(const OwningFileOrDirectory& aData,
-                          nsAString& aName)
-{
-  if (aData.IsFile()) {
-    aData.GetAsFile()->GetName(aName);
-  } else {
-    MOZ_ASSERT(aData.IsDirectory());
-    ErrorResult rv;
-    aData.GetAsDirectory()->GetName(aName, rv);
-    NS_WARN_IF(rv.Failed());
-  }
-}
-
-void
-GetDOMFileOrDirectoryPath(const OwningFileOrDirectory& aData,
-                          nsAString& aPath,
-                          ErrorResult& aRv)
-{
-  if (aData.IsFile()) {
-    aData.GetAsFile()->GetMozFullPathInternal(aPath, aRv);
-  } else {
-    MOZ_ASSERT(aData.IsDirectory());
-    aData.GetAsDirectory()->GetFullRealPath(aPath);
-  }
-}
-
 } // namespace
 
 
 NS_IMETHODIMP
 HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult)
 {
   mInput->PickerClosed();
 
   if (aResult == nsIFilePicker::returnCancel) {
     return NS_OK;
   }
 
   int16_t mode;
   mFilePicker->GetMode(&mode);
 
   // Collect new selected filenames
-  nsTArray<OwningFileOrDirectory> newFilesOrDirectories;
+  nsTArray<RefPtr<File>> newFiles;
   if (mode == static_cast<int16_t>(nsIFilePicker::modeOpenMultiple)) {
     nsCOMPtr<nsISimpleEnumerator> iter;
     nsresult rv =
       mFilePicker->GetDomFileOrDirectoryEnumerator(getter_AddRefs(iter));
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!iter) {
       return NS_OK;
@@ -476,63 +397,51 @@ HTMLInputElement::nsFilePickerShownCallb
     nsCOMPtr<nsISupports> tmp;
     bool hasMore = true;
 
     while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
       iter->GetNext(getter_AddRefs(tmp));
       nsCOMPtr<nsIDOMBlob> domBlob = do_QueryInterface(tmp);
       MOZ_ASSERT(domBlob,
                  "Null file object from FilePicker's file enumerator?");
-      if (!domBlob) {
-        continue;
+      if (domBlob) {
+        newFiles.AppendElement(static_cast<File*>(domBlob.get()));
       }
-
-      OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement();
-      element->SetAsFile() = static_cast<File*>(domBlob.get());
     }
   } else {
     MOZ_ASSERT(mode == static_cast<int16_t>(nsIFilePicker::modeOpen) ||
                mode == static_cast<int16_t>(nsIFilePicker::modeGetFolder));
     nsCOMPtr<nsISupports> tmp;
     nsresult rv = mFilePicker->GetDomFileOrDirectory(getter_AddRefs(tmp));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(tmp);
     if (blob) {
       RefPtr<File> file = static_cast<Blob*>(blob.get())->ToFile();
-      MOZ_ASSERT(file);
-
-      OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement();
-      element->SetAsFile() = file;
-    } else if (tmp) {
-      RefPtr<Directory> directory = static_cast<Directory*>(tmp.get());
-      OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement();
-      element->SetAsDirectory() = directory;
-    }
-  }
-
-  if (newFilesOrDirectories.IsEmpty()) {
+      newFiles.AppendElement(file);
+    }
+  }
+
+  if (newFiles.IsEmpty()) {
     return NS_OK;
   }
 
   // Store the last used directory using the content pref service:
-  nsCOMPtr<nsIFile> file =
-    DOMFileOrDirectoryToLocalFile(newFilesOrDirectories[0]);
-
+  nsCOMPtr<nsIFile> file = DOMFileToLocalFile(newFiles[0]);
   if (file) {
     nsCOMPtr<nsIFile> lastUsedDir;
     file->GetParent(getter_AddRefs(lastUsedDir));
     HTMLInputElement::gUploadLastDir->StoreLastUsedDirectory(
       mInput->OwnerDoc(), lastUsedDir);
   }
 
   // The text control frame (if there is one) isn't going to send a change
   // event because it will think this is done by a script.
   // So, we can safely send one by ourself.
-  mInput->SetFilesOrDirectories(newFilesOrDirectories, true);
+  mInput->SetFiles(newFiles, true);
   return nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(),
                                               static_cast<nsIDOMHTMLInputElement*>(mInput.get()),
                                               NS_LITERAL_STRING("change"), true,
                                               false);
 }
 
 NS_IMPL_ISUPPORTS(HTMLInputElement::nsFilePickerShownCallback,
                   nsIFilePickerShownCallback)
@@ -758,42 +667,48 @@ HTMLInputElement::InitFilePicker(FilePic
     SetFilePickerFiltersFromAccept(filePicker);
   } else {
     filePicker->AppendFilters(nsIFilePicker::filterAll);
   }
 
   // Set default directry and filename
   nsAutoString defaultName;
 
-  const nsTArray<OwningFileOrDirectory>& oldFiles =
-    GetFilesOrDirectoriesInternal();
+  const nsTArray<RefPtr<File>>& oldFiles = GetFilesInternal();
 
   nsCOMPtr<nsIFilePickerShownCallback> callback =
     new HTMLInputElement::nsFilePickerShownCallback(this, filePicker);
 
   if (!oldFiles.IsEmpty() &&
       aType != FILE_PICKER_DIRECTORY) {
     nsString path;
 
-    nsCOMPtr<nsIFile> localFile = DOMFileOrDirectoryToLocalFile(oldFiles[0]);
-    if (localFile) {
+    ErrorResult error;
+    oldFiles[0]->GetMozFullPathInternal(path, error);
+    if (NS_WARN_IF(error.Failed())) {
+      return error.StealNSResult();
+    }
+
+    nsCOMPtr<nsIFile> localFile;
+    rv = NS_NewLocalFile(path, false, getter_AddRefs(localFile));
+
+    if (NS_SUCCEEDED(rv)) {
       nsCOMPtr<nsIFile> parentFile;
-      nsresult rv = localFile->GetParent(getter_AddRefs(parentFile));
+      rv = localFile->GetParent(getter_AddRefs(parentFile));
       if (NS_SUCCEEDED(rv)) {
         filePicker->SetDisplayDirectory(parentFile);
       }
     }
 
     // Unfortunately nsIFilePicker doesn't allow multiple files to be
     // default-selected, so only select something by default if exactly
     // one file was selected before.
     if (oldFiles.Length() == 1) {
       nsAutoString leafName;
-      GetDOMFileOrDirectoryName(oldFiles[0], leafName);
-
+      oldFiles[0]->GetName(leafName);
       if (!leafName.IsEmpty()) {
         filePicker->SetDefaultString(leafName);
       }
     }
 
     rv = filePicker->Open(callback);
     if (NS_SUCCEEDED(rv)) {
       mPickerRunning = true;
@@ -836,17 +751,17 @@ UploadLastDir::FetchDirectoryAndDisplayP
   NS_PRECONDITION(aDoc, "aDoc is null");
   NS_PRECONDITION(aFilePicker, "aFilePicker is null");
   NS_PRECONDITION(aFpCallback, "aFpCallback is null");
 
   nsIURI* docURI = aDoc->GetDocumentURI();
   NS_PRECONDITION(docURI, "docURI is null");
 
   nsCOMPtr<nsILoadContext> loadContext = aDoc->GetLoadContext();
-  nsCOMPtr<nsIContentPrefCallback2> prefCallback =
+  nsCOMPtr<nsIContentPrefCallback2> prefCallback = 
     new UploadLastDir::ContentPrefCallback(aFilePicker, aFpCallback);
 
 #ifdef MOZ_B2G
   if (XRE_IsContentProcess()) {
     prefCallback->HandleCompletion(nsIContentPrefCallback2::COMPLETE_ERROR);
     return NS_OK;
   }
 #endif
@@ -1015,26 +930,26 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLInput
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLInputElement,
                                                   nsGenericHTMLFormElementWithState)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mValidity)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
   if (tmp->IsSingleLineTextControl(false)) {
     tmp->mInputData.mState->Traverse(cb);
   }
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilesOrDirectories)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFiles)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilesAndDirectoriesPromise)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLInputElement,
                                                 nsGenericHTMLFormElementWithState)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilesOrDirectories)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFiles)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilesAndDirectoriesPromise)
   if (tmp->IsSingleLineTextControl(false)) {
     tmp->mInputData.mState->Unlink();
   }
   //XXX should unlink more?
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
@@ -1083,18 +998,18 @@ HTMLInputElement::Clone(mozilla::dom::No
       }
       break;
     case VALUE_MODE_FILENAME:
       if (it->OwnerDoc()->IsStaticDocument()) {
         // We're going to be used in print preview.  Since the doc is static
         // we can just grab the pretty string and use it as wallpaper
         GetDisplayFileName(it->mStaticDocFileList);
       } else {
-        it->mFilesOrDirectories.Clear();
-        it->mFilesOrDirectories.AppendElements(mFilesOrDirectories);
+        it->mFiles.Clear();
+        it->mFiles.AppendElements(mFiles);
       }
       break;
     case VALUE_MODE_DEFAULT_ON:
       if (mCheckedChanged) {
         // We no longer have our original checked state.  Set our
         // checked state on the clone.
         it->DoSetChecked(mChecked, false, true);
       }
@@ -1522,34 +1437,34 @@ HTMLInputElement::GetValueInternal(nsASt
 
     case VALUE_MODE_FILENAME:
       if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
 #ifndef MOZ_CHILD_PERMISSIONS
         aValue.Assign(mFirstFilePath);
 #else
         // XXX We'd love to assert that this can't happen, but some mochitests
         // use SpecialPowers to circumvent our more sane security model.
-        if (!mFilesOrDirectories.IsEmpty()) {
+        if (!mFiles.IsEmpty()) {
           ErrorResult rv;
-          GetDOMFileOrDirectoryPath(mFilesOrDirectories[0], aValue, rv);
+          mFiles[0]->GetMozFullPath(aValue, rv);
           if (NS_WARN_IF(rv.Failed())) {
             return rv.StealNSResult();
           }
           return NS_OK;
         }
         else {
           aValue.Truncate();
         }
 #endif
       } else {
         // Just return the leaf name
-        if (mFilesOrDirectories.IsEmpty()) {
+        if (mFiles.IsEmpty()) {
           aValue.Truncate();
         } else {
-          GetDOMFileOrDirectoryName(mFilesOrDirectories[0], aValue);
+          mFiles[0]->GetName(aValue);
         }
       }
 
       return NS_OK;
 
     case VALUE_MODE_DEFAULT:
       // Treat defaultValue as value.
       GetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue);
@@ -1574,18 +1489,18 @@ HTMLInputElement::IsValueEmpty() const
   GetValueInternal(value);
 
   return value.IsEmpty();
 }
 
 void
 HTMLInputElement::ClearFiles(bool aSetValueChanged)
 {
-  nsTArray<OwningFileOrDirectory> data;
-  SetFilesOrDirectories(data, aSetValueChanged);
+  nsTArray<RefPtr<File>> files;
+  SetFiles(files, aSetValueChanged);
 }
 
 /* static */ Decimal
 HTMLInputElement::StringToDecimal(const nsAString& aValue)
 {
   if (!IsASCII(aValue)) {
     return Decimal::nan();
   }
@@ -2145,19 +2060,19 @@ HTMLInputElement::FlushFrames()
     GetComposedDoc()->FlushPendingNotifications(Flush_Frames);
   }
 }
 
 void
 HTMLInputElement::MozGetFileNameArray(nsTArray<nsString>& aArray,
                                       ErrorResult& aRv)
 {
-  for (uint32_t i = 0; i < mFilesOrDirectories.Length(); i++) {
-    nsAutoString str;
-    GetDOMFileOrDirectoryPath(mFilesOrDirectories[i], str, aRv);
+  for (uint32_t i = 0; i < mFiles.Length(); i++) {
+    nsString str;
+    mFiles[i]->GetMozFullPathInternal(str, aRv);
     if (NS_WARN_IF(aRv.Failed())) {
       return;
     }
 
     aArray.AppendElement(str);
   }
 }
 
@@ -2197,77 +2112,66 @@ HTMLInputElement::MozGetFileNameArray(ui
 void
 HTMLInputElement::MozSetFileArray(const Sequence<OwningNonNull<File>>& aFiles)
 {
   nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
   MOZ_ASSERT(global);
   if (!global) {
     return;
   }
-
-  nsTArray<OwningFileOrDirectory> files;
+  nsTArray<RefPtr<File>> files;
   for (uint32_t i = 0; i < aFiles.Length(); ++i) {
     RefPtr<File> file = File::Create(global, aFiles[i].get()->Impl());
     MOZ_ASSERT(file);
 
-    OwningFileOrDirectory* element = files.AppendElement();
-    element->SetAsFile() = file;
-  }
-
-  SetFilesOrDirectories(files, true);
+    files.AppendElement(file);
+  }
+  SetFiles(files, true);
 }
 
 void
-HTMLInputElement::MozSetFileNameArray(const Sequence<nsString>& aFileNames,
-                                      ErrorResult& aRv)
+HTMLInputElement::MozSetFileNameArray(const Sequence< nsString >& aFileNames, ErrorResult& aRv)
 {
   if (XRE_IsContentProcess()) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return;
   }
 
-  nsTArray<OwningFileOrDirectory> files;
+  nsTArray<RefPtr<File>> files;
   for (uint32_t i = 0; i < aFileNames.Length(); ++i) {
     nsCOMPtr<nsIFile> file;
 
     if (StringBeginsWith(aFileNames[i], NS_LITERAL_STRING("file:"),
                          nsASCIICaseInsensitiveStringComparator())) {
       // Converts the URL string into the corresponding nsIFile if possible
       // A local file will be created if the URL string begins with file://
       NS_GetFileFromURLSpec(NS_ConvertUTF16toUTF8(aFileNames[i]),
                             getter_AddRefs(file));
     }
 
     if (!file) {
       // this is no "file://", try as local file
       NS_NewLocalFile(aFileNames[i], false, getter_AddRefs(file));
     }
 
-    if (!file) {
+    if (file) {
+      nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
+      RefPtr<File> domFile = File::CreateFromFile(global, file);
+      files.AppendElement(domFile);
+    } else {
       continue; // Not much we can do if the file doesn't exist
     }
 
-    nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
-    if (!global) {
-      aRv.Throw(NS_ERROR_FAILURE);
-      return;
-    }
-
-    RefPtr<File> domFile = File::CreateFromFile(global, file);
-
-    OwningFileOrDirectory* element = files.AppendElement();
-    element->SetAsFile() = domFile;
-  }
-
-  SetFilesOrDirectories(files, true);
+  }
+
+  SetFiles(files, true);
 }
 
 NS_IMETHODIMP
-HTMLInputElement::MozSetFileNameArray(const char16_t** aFileNames,
-                                      uint32_t aLength)
+HTMLInputElement::MozSetFileNameArray(const char16_t** aFileNames, uint32_t aLength)
 {
   if (!nsContentUtils::IsCallerChrome()) {
     // setting the value of a "FILE" input widget requires chrome privilege
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   Sequence<nsString> list;
   nsString* names = list.AppendElements(aLength, fallible);
@@ -2279,44 +2183,16 @@ HTMLInputElement::MozSetFileNameArray(co
     names[i].Rebind(filename, nsCharTraits<char16_t>::length(filename));
   }
 
   ErrorResult rv;
   MozSetFileNameArray(list, rv);
   return rv.StealNSResult();
 }
 
-void
-HTMLInputElement::MozSetDirectory(const nsAString& aDirectoryPath,
-                                  ErrorResult& aRv)
-{
-  nsCOMPtr<nsIFile> file;
-  NS_ConvertUTF16toUTF8 path(aDirectoryPath);
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(file));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return;
-  }
-
-  nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
-  if (NS_WARN_IF(!window)) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  RefPtr<Directory> directory = Directory::Create(window, file,
-                                                  Directory::eDOMRootDirectory);
-  MOZ_ASSERT(directory);
-
-  nsTArray<OwningFileOrDirectory> array;
-  OwningFileOrDirectory* element = array.AppendElement();
-  element->SetAsDirectory() = directory;
-
-  SetFilesOrDirectories(array, true);
-}
-
 bool
 HTMLInputElement::MozIsTextField(bool aExcludePassword)
 {
   // TODO: temporary until bug 773205 is fixed.
   if (IsExperimentalMobileType(mType)) {
     return false;
   }
 
@@ -2498,74 +2374,74 @@ HTMLInputElement::GetPlaceholderVisibili
 void
 HTMLInputElement::GetDisplayFileName(nsAString& aValue) const
 {
   if (OwnerDoc()->IsStaticDocument()) {
     aValue = mStaticDocFileList;
     return;
   }
 
-  if (mFilesOrDirectories.Length() == 1) {
-    GetDOMFileOrDirectoryName(mFilesOrDirectories[0], aValue);
+  if (mFiles.Length() == 1) {
+    mFiles[0]->GetName(aValue);
     return;
   }
 
   nsXPIDLString value;
 
-  if (mFilesOrDirectories.IsEmpty()) {
+  if (mFiles.IsEmpty()) {
     if (HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) {
       nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
                                          "NoFilesSelected", value);
     } else {
       nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
                                          "NoFileSelected", value);
     }
   } else {
     nsString count;
-    count.AppendInt(int(mFilesOrDirectories.Length()));
+    count.AppendInt(int(mFiles.Length()));
 
     const char16_t* params[] = { count.get() };
     nsContentUtils::FormatLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
                                           "XFilesSelected", params, value);
   }
 
   aValue = value;
 }
 
 void
-HTMLInputElement::SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories,
-                                        bool aSetValueChanged)
-{
-  mFilesOrDirectories.Clear();
-  mFilesOrDirectories.AppendElements(aFilesOrDirectories);
-
-  AfterSetFilesOrDirectories(aSetValueChanged);
+HTMLInputElement::SetFiles(const nsTArray<RefPtr<File>>& aFiles,
+                           bool aSetValueChanged)
+{
+  mFiles.Clear();
+  mFiles.AppendElements(aFiles);
+
+  AfterSetFiles(aSetValueChanged);
 }
 
 void
 HTMLInputElement::SetFiles(nsIDOMFileList* aFiles,
                            bool aSetValueChanged)
 {
   RefPtr<FileList> files = static_cast<FileList*>(aFiles);
-  mFilesOrDirectories.Clear();
+  mFiles.Clear();
 
   if (aFiles) {
     uint32_t listLength;
     aFiles->GetLength(&listLength);
     for (uint32_t i = 0; i < listLength; i++) {
-      OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
-      *element = files->UnsafeItem(i);
-    }
-  }
-
-  AfterSetFilesOrDirectories(aSetValueChanged);
+      RefPtr<File> file = files->Item(i);
+      mFiles.AppendElement(file);
+    }
+  }
+
+  AfterSetFiles(aSetValueChanged);
 }
 
 void
-HTMLInputElement::AfterSetFilesOrDirectories(bool aSetValueChanged)
+HTMLInputElement::AfterSetFiles(bool aSetValueChanged)
 {
   // No need to flush here, if there's no frame at this point we
   // don't need to force creation of one just to tell it about this
   // new value.  We just want the display to update as needed.
   nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
   if (formControlFrame) {
     nsAutoString readableValue;
     GetDisplayFileName(readableValue);
@@ -2573,21 +2449,21 @@ HTMLInputElement::AfterSetFilesOrDirecto
   }
 
 #ifndef MOZ_CHILD_PERMISSIONS
   // Grab the full path here for any chrome callers who access our .value via a
   // CPOW. This path won't be called from a CPOW meaning the potential sync IPC
   // call under GetMozFullPath won't be rejected for not being urgent.
   // XXX Protected by the ifndef because the blob code doesn't allow us to send
   // this message in b2g.
-  if (mFilesOrDirectories.IsEmpty()) {
+  if (mFiles.IsEmpty()) {
     mFirstFilePath.Truncate();
   } else {
     ErrorResult rv;
-    GetDOMFileOrDirectoryPath(mFilesOrDirectories[0], mFirstFilePath, rv);
+    mFiles[0]->GetMozFullPath(mFirstFilePath, rv);
     if (NS_WARN_IF(rv.Failed())) {
       rv.SuppressException();
     }
   }
 #endif
 
   UpdateFileList();
 
@@ -2651,37 +2527,34 @@ HTMLInputElement::HandleNumberControlSpi
     // or else we've lost our frame. Either way, stop the timer and don't do
     // anything else.
     input->StopNumberControlSpinnerSpin();
   } else {
     input->StepNumberControlForUserEvent(input->mNumberControlSpinnerSpinsUp ? 1 : -1);
   }
 }
 
-void
+nsresult
 HTMLInputElement::UpdateFileList()
 {
   if (mFileList) {
     mFileList->Clear();
 
-    const nsTArray<OwningFileOrDirectory>& array =
-      GetFilesOrDirectoriesInternal();
-
-    for (uint32_t i = 0; i < array.Length(); ++i) {
-      if (array[i].IsFile()) {
-        mFileList->Append(array[i].GetAsFile());
-      } else {
-        MOZ_ASSERT(array[i].IsDirectory());
-        mFileList->Append(array[i].GetAsDirectory());
+    const nsTArray<RefPtr<File>>& files = GetFilesInternal();
+    for (uint32_t i = 0; i < files.Length(); ++i) {
+      if (!mFileList->Append(files[i])) {
+        return NS_ERROR_FAILURE;
       }
     }
   }
 
   // Make sure we (lazily) create a new Promise for GetFilesAndDirectories:
   mFilesAndDirectoriesPromise = nullptr;
+
+  return NS_OK;
 }
 
 nsresult
 HTMLInputElement::SetValueInternal(const nsAString& aValue, uint32_t aFlags)
 {
   NS_PRECONDITION(GetValueMode() != VALUE_MODE_FILENAME,
                   "Don't call SetValueInternal for file inputs");
 
@@ -4110,17 +3983,17 @@ HTMLInputElement::PostHandleEvent(EventC
             if (mouseEvent->button == WidgetMouseEvent::eLeftButton &&
                 !(mouseEvent->IsShift() || mouseEvent->IsControl() ||
                   mouseEvent->IsAlt() || mouseEvent->IsMeta() ||
                   mouseEvent->IsAltGraph() || mouseEvent->IsFn() ||
                   mouseEvent->IsOS())) {
               nsNumberControlFrame* numberControlFrame =
                 do_QueryFrame(GetPrimaryFrame());
               if (numberControlFrame) {
-                if (aVisitor.mEvent->mMessage == eMouseDown &&
+                if (aVisitor.mEvent->mMessage == eMouseDown && 
                     IsMutable()) {
                   switch (numberControlFrame->GetSpinButtonForPointerEvent(
                             aVisitor.mEvent->AsMouseEvent())) {
                   case nsNumberControlFrame::eSpinButtonUp:
                     StepNumberControlForUserEvent(1);
                     mNumberControlSpinnerSpinsUp = true;
                     StartNumberControlSpinnerSpin();
                     aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
@@ -4661,17 +4534,17 @@ HTMLInputElement::GetValueAsDate(const n
  *  Where maxday is the number of days in the month 'month' and year 'year'
  */
 
   if (aValue.Length() < 10) {
     return false;
   }
 
   uint32_t endOfYearOffset = aValue.Length() - 6;
-
+  
   if (aValue[endOfYearOffset]     != '-' ||
       aValue[endOfYearOffset + 3] != '-') {
     return false;
   }
 
   if (!DigitSubStringToNumber(aValue, 0, endOfYearOffset, aYear) ||
       *aYear < 1) {
     return false;
@@ -5029,48 +4902,55 @@ HTMLInputElement::GetFilesAndDirectories
     return nullptr;
   }
 
   RefPtr<Promise> p = Promise::Create(global, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  const nsTArray<OwningFileOrDirectory>& filesAndDirs =
-    GetFilesOrDirectoriesInternal();
+  const nsTArray<RefPtr<File>>& filesAndDirs = GetFilesInternal();
 
   Sequence<OwningFileOrDirectory> filesAndDirsSeq;
 
-  if (!filesAndDirsSeq.SetLength(filesAndDirs.Length(),
-                                 mozilla::fallible_t())) {
+  if (!filesAndDirsSeq.SetLength(filesAndDirs.Length(), mozilla::fallible_t())) {
     p->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
     return p.forget();
   }
 
   for (uint32_t i = 0; i < filesAndDirs.Length(); ++i) {
-    if (filesAndDirs[i].IsDirectory()) {
+    if (filesAndDirs[i]->IsDirectory()) {
 #if defined(ANDROID) || defined(MOZ_B2G)
       MOZ_ASSERT(false,
                  "Directory picking should have been redirected to normal "
                  "file picking for platforms that don't have a directory "
                  "picker");
 #endif
-
-      RefPtr<Directory> directory = filesAndDirs[i].GetAsDirectory();
-
+      nsAutoString path;
+      filesAndDirs[i]->GetMozFullPathInternal(path, aRv);
+      if (aRv.Failed()) {
+        return nullptr;
+      }
+      int32_t leafSeparatorIndex = path.RFind(FILE_PATH_SEPARATOR);
+      nsDependentSubstring dirname = Substring(path, 0, leafSeparatorIndex);
+
+      RefPtr<OSFileSystem> fs = new OSFileSystem(dirname);
+      fs->Init(OwnerDoc()->GetInnerWindow());
+
+      nsAutoString dompath(NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR));
+      dompath.Append(Substring(path, leafSeparatorIndex + 1));
+      RefPtr<Directory> directory = new Directory(fs, dompath);
       // In future we could refactor SetFilePickerFiltersFromAccept to return a
       // semicolon separated list of file extensions and include that in the
       // filter string passed here.
       directory->SetContentFilters(NS_LITERAL_STRING("filter-out-sensitive"));
       filesAndDirsSeq[i].SetAsDirectory() = directory;
     } else {
-      MOZ_ASSERT(filesAndDirs[i].IsFile());
-
       // This file was directly selected by the user, so don't filter it.
-      filesAndDirsSeq[i].SetAsFile() = filesAndDirs[i].GetAsFile();
+      filesAndDirsSeq[i].SetAsFile() = filesAndDirs[i];
     }
   }
 
   p->MaybeResolve(filesAndDirsSeq);
 
   // Cache the Promise so that repeat getFilesAndDirectories() calls return
   // the same Promise and array of File and Directory objects until the user
   // picks different files/directories:
@@ -5694,28 +5574,23 @@ HTMLInputElement::SubmitNamesValues(nsFo
   }
 
   //
   // Submit file if its input type=file and this encoding method accepts files
   //
   if (mType == NS_FORM_INPUT_FILE) {
     // Submit files
 
-    const nsTArray<OwningFileOrDirectory>& files =
-      GetFilesOrDirectoriesInternal();
-
-    bool hasBlobs = false;
+    const nsTArray<RefPtr<File>>& files = GetFilesInternal();
+
     for (uint32_t i = 0; i < files.Length(); ++i) {
-      if (files[i].IsFile()) {
-        hasBlobs = true;
-        aFormSubmission->AddNameBlobOrNullPair(name, files[i].GetAsFile());
-      }
-    }
-
-    if (!hasBlobs) {
+      aFormSubmission->AddNameBlobOrNullPair(name, files[i]);
+    }
+
+    if (files.IsEmpty()) {
       aFormSubmission->AddNameBlobOrNullPair(name, nullptr);
     }
 
     return NS_OK;
   }
 
   if (mType == NS_FORM_INPUT_HIDDEN && name.EqualsLiteral("_charset_")) {
     nsCString charset;
@@ -5739,19 +5614,19 @@ HTMLInputElement::SaveState()
   switch (GetValueMode()) {
     case VALUE_MODE_DEFAULT_ON:
       if (mCheckedChanged) {
         inputState = new HTMLInputElementState();
         inputState->SetChecked(mChecked);
       }
       break;
     case VALUE_MODE_FILENAME:
-      if (!mFilesOrDirectories.IsEmpty()) {
+      if (!mFiles.IsEmpty()) {
         inputState = new HTMLInputElementState();
-        inputState->SetFilesOrDirectories(mFilesOrDirectories);
+        inputState->SetBlobImpls(mFiles);
       }
       break;
     case VALUE_MODE_VALUE:
     case VALUE_MODE_DEFAULT:
       // VALUE_MODE_DEFAULT shouldn't have their value saved except 'hidden',
       // mType shouldn't be NS_FORM_INPUT_PASSWORD and value should have changed.
       if ((GetValueMode() == VALUE_MODE_DEFAULT &&
            mType != NS_FORM_INPUT_HIDDEN) ||
@@ -5910,17 +5785,17 @@ HTMLInputElement::AddStates(EventStates 
                                        NS_EVENT_STATE_FOCUSRING));
     if (!focusStates.IsEmpty()) {
       HTMLInputElement* ownerNumberControl = GetOwnerNumberControl();
       if (ownerNumberControl) {
         ownerNumberControl->AddStates(focusStates);
       }
     }
   }
-  nsGenericHTMLFormElementWithState::AddStates(aStates);
+  nsGenericHTMLFormElementWithState::AddStates(aStates);                          
 }
 
 void
 HTMLInputElement::RemoveStates(EventStates aStates)
 {
   if (mType == NS_FORM_INPUT_TEXT) {
     EventStates focusStates(aStates & (NS_EVENT_STATE_FOCUS |
                                        NS_EVENT_STATE_FOCUSRING));
@@ -5947,23 +5822,30 @@ HTMLInputElement::RestoreState(nsPresSta
       case VALUE_MODE_DEFAULT_ON:
         if (inputState->IsCheckedSet()) {
           restoredCheckedState = true;
           DoSetChecked(inputState->GetChecked(), true, true);
         }
         break;
       case VALUE_MODE_FILENAME:
         {
-          nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
-          if (window) {
-            nsTArray<OwningFileOrDirectory> array;
-            inputState->GetFilesOrDirectories(window, array);
-
-            SetFilesOrDirectories(array, true);
+          const nsTArray<RefPtr<BlobImpl>>& blobImpls = inputState->GetBlobImpls();
+
+          nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
+          MOZ_ASSERT(global);
+
+          nsTArray<RefPtr<File>> files;
+          for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
+            RefPtr<File> file = File::Create(global, blobImpls[i]);
+            MOZ_ASSERT(file);
+
+            files.AppendElement(file);
           }
+
+          SetFiles(files, true);
         }
         break;
       case VALUE_MODE_VALUE:
       case VALUE_MODE_DEFAULT:
         if (GetValueMode() == VALUE_MODE_DEFAULT &&
             mType != NS_FORM_INPUT_HIDDEN) {
           break;
         }
@@ -6463,25 +6345,25 @@ HTMLInputElement::IsValueMissing() const
 
   if (!IsMutable()) {
     return false;
   }
 
   switch (GetValueMode()) {
     case VALUE_MODE_VALUE:
       return IsValueEmpty();
-
     case VALUE_MODE_FILENAME:
-      return GetFilesOrDirectoriesInternal().IsEmpty();
-
+    {
+      const nsTArray<RefPtr<File>>& files = GetFilesInternal();
+      return files.IsEmpty();
+    }
     case VALUE_MODE_DEFAULT_ON:
       // This should not be used for type radio.
       // See the MOZ_ASSERT at the beginning of the method.
       return !mChecked;
-
     case VALUE_MODE_DEFAULT:
     default:
       return false;
   }
 }
 
 bool
 HTMLInputElement::HasTypeMismatch() const
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -15,17 +15,16 @@
 #include "nsITimer.h"
 #include "nsIPhonetic.h"
 #include "nsIDOMNSEditableElement.h"
 #include "nsCOMPtr.h"
 #include "nsIConstraintValidation.h"
 #include "mozilla/dom/HTMLFormElement.h" // for HasEverTriedInvalidSubmit()
 #include "mozilla/dom/HTMLInputElementBinding.h"
 #include "mozilla/dom/Promise.h"
-#include "mozilla/dom/UnionTypes.h"
 #include "nsIFilePicker.h"
 #include "nsIContentPrefService2.h"
 #include "mozilla/Decimal.h"
 #include "nsContentUtils.h"
 #include "nsTextEditorState.h"
 
 class nsIRadioGroupContainer;
 class nsIRadioVisitor;
@@ -216,23 +215,22 @@ public:
   NS_IMETHOD_(void) UpdatePlaceholderVisibility(bool aNotify) override;
   NS_IMETHOD_(bool) GetPlaceholderVisibility() override;
   NS_IMETHOD_(void) InitializeKeyboardEventListeners() override;
   NS_IMETHOD_(void) OnValueChanged(bool aNotify) override;
   NS_IMETHOD_(bool) HasCachedSelection() override;
 
   void GetDisplayFileName(nsAString& aFileName) const;
 
-  const nsTArray<OwningFileOrDirectory>& GetFilesOrDirectoriesInternal() const
+  const nsTArray<RefPtr<File>>& GetFilesInternal() const
   {
-    return mFilesOrDirectories;
+    return mFiles;
   }
 
-  void SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories,
-                             bool aSetValueChanged);
+  void SetFiles(const nsTArray<RefPtr<File>>& aFiles, bool aSetValueChanged);
   void SetFiles(nsIDOMFileList* aFiles, bool aSetValueChanged);
 
   // Called when a nsIFilePicker or a nsIColorPicker terminate.
   void PickerClosed();
 
   void SetCheckedChangedInternal(bool aCheckedChanged);
   bool GetCheckedChanged() const {
     return mCheckedChanged;
@@ -719,17 +717,16 @@ public:
   nsIControllers* GetControllers(ErrorResult& aRv);
 
   int32_t GetTextLength(ErrorResult& aRv);
 
   void MozGetFileNameArray(nsTArray<nsString>& aFileNames, ErrorResult& aRv);
 
   void MozSetFileNameArray(const Sequence< nsString >& aFileNames, ErrorResult& aRv);
   void MozSetFileArray(const Sequence<OwningNonNull<File>>& aFiles);
-  void MozSetDirectory(const nsAString& aDirectoryPath, ErrorResult& aRv);
 
   HTMLInputElement* GetOwnerNumberControl();
 
   void StartNumberControlSpinnerSpin();
   void StopNumberControlSpinnerSpin();
   void StepNumberControlForUserEvent(int32_t aDirection);
 
   /**
@@ -920,22 +917,22 @@ protected:
    * MaybeSubmitForm looks for a submit input or a single text control
    * and submits the form if either is present.
    */
   nsresult MaybeSubmitForm(nsPresContext* aPresContext);
 
   /**
    * Update mFileList with the currently selected file.
    */
-  void UpdateFileList();
+  nsresult UpdateFileList();
 
   /**
-   * Called after calling one of the SetFilesOrDirectories() functions.
+   * Called after calling one of the SetFiles() functions.
    */
-  void AfterSetFilesOrDirectories(bool aSetValueChanged);
+  void AfterSetFiles(bool aSetValueChanged);
 
   /**
    * Determine whether the editor needs to be initialized explicitly for
    * a particular event.
    */
   bool NeedToInitializeEditorForEvent(EventChainPreVisitor& aVisitor) const;
 
   /**
@@ -1264,26 +1261,26 @@ protected:
     char16_t*               mValue;
     /**
      * The state of the text editor associated with the text/password input
      */
     nsTextEditorState*       mState;
   } mInputData;
 
   /**
-   * The value of the input if it is a file input. This is the list of files or
-   * directories DOM objects used when uploading a file. It is vital that this
-   * is kept separate from mValue so that it won't be possible to 'leak' the
-   * value from a text-input to a file-input. Additionally, the logic for this
-   * value is kept as simple as possible to avoid accidental errors where the
-   * wrong filename is used.  Therefor the list of filenames is always owned by
-   * this member, never by the frame. Whenever the frame wants to change the
-   * filename it has to call SetFilesOrDirectories to update this member.
+   * The value of the input if it is a file input. This is the list of filenames
+   * used when uploading a file. It is vital that this is kept separate from
+   * mValue so that it won't be possible to 'leak' the value from a text-input
+   * to a file-input. Additionally, the logic for this value is kept as simple
+   * as possible to avoid accidental errors where the wrong filename is used.
+   * Therefor the list of filenames is always owned by this member, never by
+   * the frame. Whenever the frame wants to change the filename it has to call
+   * SetFileNames to update this member.
    */
-  nsTArray<OwningFileOrDirectory> mFilesOrDirectories;
+  nsTArray<RefPtr<File>> mFiles;
 
 #ifndef MOZ_CHILD_PERMISSIONS
   /**
    * Hack for bug 1086684: Stash the .value when we're a file picker.
    */
   nsString mFirstFilePath;
 #endif
 
--- a/dom/indexedDB/FileSnapshot.h
+++ b/dom/indexedDB/FileSnapshot.h
@@ -182,16 +182,40 @@ private:
   }
 
   virtual bool
   IsFile() const override
   {
     return mBlobImpl->IsFile();
   }
 
+  virtual void
+  LookupAndCacheIsDirectory() override
+  {
+    mBlobImpl->LookupAndCacheIsDirectory();
+  }
+
+  virtual void
+  SetIsDirectory(bool aIsDir) override
+  {
+    return mBlobImpl->SetIsDirectory(aIsDir);
+  }
+
+  virtual bool
+  IsDirectory() const override
+  {
+    return mBlobImpl->IsDirectory();
+  }
+
+  virtual BlobDirState
+  GetDirState() const override
+  {
+    return mBlobImpl->GetDirState();
+  }
+
   virtual bool
   MayBeClonedToOtherThreads() const override
   {
     return false;
   }
 
   // PIBlobImplSnapshot
   virtual BlobImpl*
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -439,17 +439,18 @@ ResolveMysteryFile(BlobImpl* aImpl,
                    const nsString& aName,
                    const nsString& aContentType,
                    uint64_t aSize,
                    uint64_t aLastModifiedDate)
 {
   BlobChild* actor = ActorFromRemoteBlobImpl(aImpl);
   if (actor) {
     return actor->SetMysteryBlobInfo(aName, aContentType,
-                                     aSize, aLastModifiedDate);
+                                     aSize, aLastModifiedDate,
+                                     BlobDirState::eUnknownIfDir);
   }
   return true;
 }
 
 bool
 ResolveMysteryBlob(BlobImpl* aImpl,
                    const nsString& aContentType,
                    uint64_t aSize)
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -665,17 +665,18 @@ public:
     : BlobImplBase(aContentType, 0)
   {
     mImmutable = true;
   }
 
   EmptyBlobImpl(const nsAString& aName,
                 const nsAString& aContentType,
                 int64_t aLastModifiedDate)
-    : BlobImplBase(aName, aContentType, 0, aLastModifiedDate)
+    : BlobImplBase(aName, aContentType, 0, aLastModifiedDate,
+                   BlobDirState::eIsNotDir)
   {
     mImmutable = true;
   }
 
 private:
   virtual already_AddRefed<BlobImpl>
   CreateSlice(uint64_t /* aStart */,
               uint64_t aLength,
@@ -710,22 +711,24 @@ private:
 };
 
 struct MOZ_STACK_CLASS CreateBlobImplMetadata final
 {
   nsString mContentType;
   nsString mName;
   uint64_t mLength;
   int64_t mLastModifiedDate;
+  BlobDirState mDirState;
   bool mHasRecursed;
   const bool mIsSameProcessActor;
 
   explicit CreateBlobImplMetadata(bool aIsSameProcessActor)
     : mLength(0)
     , mLastModifiedDate(0)
+    , mDirState(BlobDirState::eUnknownIfDir)
     , mHasRecursed(false)
     , mIsSameProcessActor(aIsSameProcessActor)
   {
     MOZ_COUNT_CTOR(CreateBlobImplMetadata);
 
     mName.SetIsVoid(true);
   }
 
@@ -957,16 +960,17 @@ CreateBlobImpl(const ParentBlobConstruct
       ASSERT_UNLESS_FUZZING();
       return nullptr;
     }
 
     metadata.mContentType = params.contentType();
     metadata.mName = params.name();
     metadata.mLength = params.length();
     metadata.mLastModifiedDate = params.modDate();
+    metadata.mDirState = BlobDirState(params.dirState());
   }
 
   RefPtr<BlobImpl> blobImpl =
     CreateBlobImplFromBlobData(aBlobData, metadata);
   return blobImpl.forget();
 }
 
 void
@@ -1766,16 +1770,17 @@ protected:
 public:
   // For File.
   RemoteBlobImpl(BlobChild* aActor,
                  BlobImpl* aRemoteBlobImpl,
                  const nsAString& aName,
                  const nsAString& aContentType,
                  uint64_t aLength,
                  int64_t aModDate,
+                 BlobDirState aDirState,
                  bool aIsSameProcessBlob);
 
   // For Blob.
   RemoteBlobImpl(BlobChild* aActor,
                  BlobImpl* aRemoteBlobImpl,
                  const nsAString& aContentType,
                  uint64_t aLength,
                  bool aIsSameProcessBlob);
@@ -2043,16 +2048,28 @@ public:
   IsSizeUnknown() const override;
 
   virtual bool
   IsDateUnknown() const override;
 
   virtual bool
   IsFile() const override;
 
+  virtual void
+  LookupAndCacheIsDirectory() override;
+
+  virtual void
+  SetIsDirectory(bool aIsDir) override;
+
+  virtual bool
+  IsDirectory() const override;
+
+  virtual BlobDirState
+  GetDirState() const override;
+
   virtual bool
   MayBeClonedToOtherThreads() const override;
 
   virtual BlobChild*
   GetBlobChild() override;
 
   virtual BlobParent*
   GetBlobParent() override;
@@ -2074,18 +2091,19 @@ private:
 
 BlobChild::
 RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
                                BlobImpl* aRemoteBlobImpl,
                                const nsAString& aName,
                                const nsAString& aContentType,
                                uint64_t aLength,
                                int64_t aModDate,
+                               BlobDirState aDirState,
                                bool aIsSameProcessBlob)
-  : BlobImplBase(aName, aContentType, aLength, aModDate)
+  : BlobImplBase(aName, aContentType, aLength, aModDate, aDirState)
   , mIsSlice(false)
 {
   if (aIsSameProcessBlob) {
     MOZ_ASSERT(aRemoteBlobImpl);
     mSameProcessBlobImpl = aRemoteBlobImpl;
     MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
   } else {
     mDifferentProcessBlobImpl = aRemoteBlobImpl;
@@ -2791,16 +2809,44 @@ RemoteBlobImpl::IsDateUnknown() const
 
 bool
 BlobParent::
 RemoteBlobImpl::IsFile() const
 {
   return mBlobImpl->IsFile();
 }
 
+void
+BlobParent::
+RemoteBlobImpl::LookupAndCacheIsDirectory()
+{
+  return mBlobImpl->LookupAndCacheIsDirectory();
+}
+
+void
+BlobParent::
+RemoteBlobImpl::SetIsDirectory(bool aIsDir)
+{
+  return mBlobImpl->SetIsDirectory(aIsDir);
+}
+
+bool
+BlobParent::
+RemoteBlobImpl::IsDirectory() const
+{
+  return mBlobImpl->IsDirectory();
+}
+
+BlobDirState
+BlobParent::
+RemoteBlobImpl::GetDirState() const
+{
+  return mBlobImpl->GetDirState();
+}
+
 bool
 BlobParent::
 RemoteBlobImpl::MayBeClonedToOtherThreads() const
 {
   return mBlobImpl->MayBeClonedToOtherThreads();
 }
 
 BlobChild*
@@ -2982,17 +3028,18 @@ BlobChild::CommonInit(BlobChild* aOther,
   if (otherImpl->IsFile()) {
     nsString name;
     otherImpl->GetName(name);
 
     int64_t modDate = otherImpl->GetLastModified(rv);
     MOZ_ASSERT(!rv.Failed());
 
     remoteBlob = new RemoteBlobImpl(this, otherImpl, name, contentType, length,
-                                    modDate, false /* SameProcessBlobImpl */);
+                                    modDate, otherImpl->GetDirState(),
+                                    false /* SameProcessBlobImpl */);
   } else {
     remoteBlob = new RemoteBlobImpl(this, otherImpl, contentType, length,
                                     false /* SameProcessBlobImpl */);
   }
 
   // This RemoteBlob must be kept alive untill RecvCreatedFromKnownBlob is
   // called because the parent will send this notification and we must be able
   // to manage it.
@@ -3034,16 +3081,17 @@ BlobChild::CommonInit(const ChildBlobCon
       const FileBlobConstructorParams& params =
         blobParams.get_FileBlobConstructorParams();
       remoteBlob = new RemoteBlobImpl(this,
                                       nullptr,
                                       params.name(),
                                       params.contentType(),
                                       params.length(),
                                       params.modDate(),
+                                      BlobDirState(params.dirState()),
                                       false /* SameProcessBlobImpl */);
       break;
     }
 
     case AnyBlobConstructorParams::TSameProcessBlobConstructorParams: {
       MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
 
       const SameProcessBlobConstructorParams& params =
@@ -3069,16 +3117,17 @@ BlobChild::CommonInit(const ChildBlobCon
 
         remoteBlob =
           new RemoteBlobImpl(this,
                              blobImpl,
                              name,
                              contentType,
                              size,
                              lastModifiedDate,
+                             blobImpl->GetDirState(),
                              true /* SameProcessBlobImpl */);
       } else {
         remoteBlob = new RemoteBlobImpl(this, blobImpl, contentType, size,
                                         true /* SameProcessBlobImpl */);
       }
 
       break;
     }
@@ -3249,17 +3298,18 @@ BlobChild::GetOrCreateFromImpl(ChildMana
     if (aBlobImpl->IsFile()) {
       nsString name;
       aBlobImpl->GetName(name);
 
       int64_t modDate = aBlobImpl->GetLastModified(rv);
       MOZ_ASSERT(!rv.Failed());
 
       blobParams =
-        FileBlobConstructorParams(name, contentType, length, modDate, blobData);
+        FileBlobConstructorParams(name, contentType, length, modDate,
+                                  aBlobImpl->GetDirState(), blobData);
     } else {
       blobParams = NormalBlobConstructorParams(contentType, length, blobData);
     }
   }
 
   BlobChild* actor = new BlobChild(aManager, aBlobImpl);
 
   ParentBlobConstructorParams params(blobParams);
@@ -3422,29 +3472,31 @@ BlobChild::GetBlobImpl()
 
   return blobImpl.forget();
 }
 
 bool
 BlobChild::SetMysteryBlobInfo(const nsString& aName,
                               const nsString& aContentType,
                               uint64_t aLength,
-                              int64_t aLastModifiedDate)
+                              int64_t aLastModifiedDate,
+                              BlobDirState aDirState)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mBlobImpl);
   MOZ_ASSERT(mRemoteBlobImpl);
   MOZ_ASSERT(aLastModifiedDate != INT64_MAX);
 
   mBlobImpl->SetLazyData(aName, aContentType, aLength, aLastModifiedDate);
 
   FileBlobConstructorParams params(aName,
                                    aContentType,
                                    aLength,
                                    aLastModifiedDate,
+                                   aDirState,
                                    void_t() /* optionalBlobData */);
   return SendResolveMystery(params);
 }
 
 bool
 BlobChild::SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength)
 {
   AssertIsOnOwningThread();
@@ -3797,17 +3849,17 @@ BlobParent::GetOrCreateFromImpl(ParentMa
         nsString name;
         aBlobImpl->GetName(name);
 
         int64_t modDate = aBlobImpl->GetLastModified(rv);
         MOZ_ASSERT(!rv.Failed());
 
         blobParams =
           FileBlobConstructorParams(name, contentType, length, modDate,
-                                    void_t());
+                                    aBlobImpl->GetDirState(), void_t());
       } else {
         blobParams = NormalBlobConstructorParams(contentType, length, void_t());
       }
     }
   }
 
   nsID id;
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(gUUIDGenerator->GenerateUUIDInPlace(&id)));
--- a/dom/ipc/BlobChild.h
+++ b/dom/ipc/BlobChild.h
@@ -26,16 +26,18 @@ class PBackgroundChild;
 namespace dom {
 
 class Blob;
 class BlobImpl;
 class ContentChild;
 class nsIContentChild;
 class PBlobStreamChild;
 
+enum BlobDirState : uint32_t;
+
 class BlobChild final
   : public PBlobChild
 {
   typedef mozilla::ipc::PBackgroundChild PBackgroundChild;
 
   class RemoteBlobImpl;
   friend class RemoteBlobImpl;
 
@@ -110,17 +112,18 @@ public:
   already_AddRefed<BlobImpl>
   GetBlobImpl();
 
   // Use this for files.
   bool
   SetMysteryBlobInfo(const nsString& aName,
                      const nsString& aContentType,
                      uint64_t aLength,
-                     int64_t aLastModifiedDate);
+                     int64_t aLastModifiedDate,
+                     BlobDirState aDirState);
 
   // Use this for non-file blobs.
   bool
   SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength);
 
   void
   AssertIsOnOwningThread() const
 #ifdef DEBUG
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -68,16 +68,17 @@ struct NormalBlobConstructorParams
 };
 
 struct FileBlobConstructorParams
 {
   nsString name;
   nsString contentType;
   uint64_t length;
   int64_t modDate;
+  uint32_t dirState;
 
   // This must be of type BlobData in a child->parent message, and will always
   // be of type void_t in a parent->child message.
   OptionalBlobData optionalBlobData;
 };
 
 struct SlicedBlobConstructorParams
 {
--- a/dom/ipc/FilePickerParent.cpp
+++ b/dom/ipc/FilePickerParent.cpp
@@ -48,178 +48,128 @@ FilePickerParent::~FilePickerParent()
 // GetOrCreateActorForBlob, which will cause problems for the child
 // process. This runnable stat()s the file off the main thread.
 //
 // We run code in three places:
 // 1. The main thread calls Dispatch() to start the runnable.
 // 2. The stream transport thread stat()s the file in Run() and then dispatches
 // the same runnable on the main thread.
 // 3. The main thread sends the results over IPC.
-FilePickerParent::IORunnable::IORunnable(FilePickerParent *aFPParent,
-                                         nsTArray<nsCOMPtr<nsIFile>>& aFiles,
-                                         bool aIsDirectory)
+FilePickerParent::FileSizeAndDateRunnable::FileSizeAndDateRunnable(FilePickerParent *aFPParent,
+                                                                   nsTArray<RefPtr<BlobImpl>>& aBlobs)
  : mFilePickerParent(aFPParent)
- , mIsDirectory(aIsDirectory)
 {
-  mFiles.SwapElements(aFiles);
-  MOZ_ASSERT_IF(aIsDirectory, mFiles.Length() == 1);
+  mBlobs.SwapElements(aBlobs);
 }
 
 bool
-FilePickerParent::IORunnable::Dispatch()
+FilePickerParent::FileSizeAndDateRunnable::Dispatch()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mEventTarget = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
   if (!mEventTarget) {
     return false;
   }
 
   nsresult rv = mEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
   return NS_SUCCEEDED(rv);
 }
 
 NS_IMETHODIMP
-FilePickerParent::IORunnable::Run()
+FilePickerParent::FileSizeAndDateRunnable::Run()
 {
   // If we're on the main thread, then that means we're done. Just send the
   // results.
   if (NS_IsMainThread()) {
     if (mFilePickerParent) {
-      mFilePickerParent->SendFilesOrDirectories(mResults);
+      mFilePickerParent->SendFiles(mBlobs);
     }
     return NS_OK;
   }
 
-  // We're not on the main thread, so do the IO.
-
-  for (uint32_t i = 0; i < mFiles.Length(); ++i) {
-    if (mIsDirectory) {
-      nsAutoString path;
-      nsresult rv = mFiles[i]->GetPath(path);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        continue;
-      }
-
-      BlobImplOrString* data = mResults.AppendElement();
-      data->mType = BlobImplOrString::eDirectoryPath;
-      data->mDirectoryPath = path;
-      continue;
-    }
-
-    RefPtr<BlobImpl> blobImpl = new BlobImplFile(mFiles[i]);
-
-    ErrorResult error;
-    blobImpl->GetSize(error);
-    if (NS_WARN_IF(error.Failed())) {
-      continue;
-    }
-
-    blobImpl->GetLastModified(error);
-    if (NS_WARN_IF(error.Failed())) {
-      continue;
-    }
-
-    BlobImplOrString* data = mResults.AppendElement();
-    data->mType = BlobImplOrString::eBlobImpl;
-    data->mBlobImpl = blobImpl;
+  // We're not on the main thread, so do the stat().
+  for (unsigned i = 0; i < mBlobs.Length(); i++) {
+    ErrorResult rv;
+    mBlobs[i]->GetSize(rv);
+    mBlobs[i]->GetLastModified(rv);
+    mBlobs[i]->LookupAndCacheIsDirectory();
   }
 
   // Dispatch ourselves back on the main thread.
   if (NS_FAILED(NS_DispatchToMainThread(this))) {
     // It's hard to see how we can recover gracefully in this case. The child
     // process is waiting for an IPC, but that can only happen on the main
     // thread.
     MOZ_CRASH();
   }
-
   return NS_OK;
 }
 
 void
-FilePickerParent::IORunnable::Destroy()
+FilePickerParent::FileSizeAndDateRunnable::Destroy()
 {
   mFilePickerParent = nullptr;
 }
 
 void
-FilePickerParent::SendFilesOrDirectories(const nsTArray<BlobImplOrString>& aData)
+FilePickerParent::SendFiles(const nsTArray<RefPtr<BlobImpl>>& aBlobs)
 {
-  if (mMode == nsIFilePicker::modeGetFolder) {
-    MOZ_ASSERT(aData.Length() <= 1);
-    if (aData.IsEmpty()) {
-      Unused << Send__delete__(this, void_t(), mResult);
-      return;
-    }
-
-    MOZ_ASSERT(aData[0].mType == BlobImplOrString::eDirectoryPath);
-
-    InputDirectory input;
-    input.directoryPath() = aData[0].mDirectoryPath;
-    Unused << Send__delete__(this, input, mResult);
-    return;
-  }
-
   nsIContentParent* parent = TabParent::GetFrom(Manager())->Manager();
   InfallibleTArray<PBlobParent*> blobs;
 
-  for (unsigned i = 0; i < aData.Length(); i++) {
-    MOZ_ASSERT(aData[i].mType == BlobImplOrString::eBlobImpl);
-    BlobParent* blobParent = parent->GetOrCreateActorForBlobImpl(aData[i].mBlobImpl);
+  for (unsigned i = 0; i < aBlobs.Length(); i++) {
+    BlobParent* blobParent = parent->GetOrCreateActorForBlobImpl(aBlobs[i]);
     if (blobParent) {
       blobs.AppendElement(blobParent);
     }
   }
 
-  InputBlobs inblobs;
+  InputFiles inblobs;
   inblobs.blobsParent().SwapElements(blobs);
   Unused << Send__delete__(this, inblobs, mResult);
 }
 
 void
 FilePickerParent::Done(int16_t aResult)
 {
   mResult = aResult;
 
   if (mResult != nsIFilePicker::returnOK) {
     Unused << Send__delete__(this, void_t(), mResult);
     return;
   }
 
-  nsTArray<nsCOMPtr<nsIFile>> files;
+  nsTArray<RefPtr<BlobImpl>> blobs;
   if (mMode == nsIFilePicker::modeOpenMultiple) {
     nsCOMPtr<nsISimpleEnumerator> iter;
     NS_ENSURE_SUCCESS_VOID(mFilePicker->GetFiles(getter_AddRefs(iter)));
 
     nsCOMPtr<nsISupports> supports;
     bool loop = true;
     while (NS_SUCCEEDED(iter->HasMoreElements(&loop)) && loop) {
       iter->GetNext(getter_AddRefs(supports));
       if (supports) {
         nsCOMPtr<nsIFile> file = do_QueryInterface(supports);
-        MOZ_ASSERT(file);
-        files.AppendElement(file);
+
+        RefPtr<BlobImpl> blobimpl = new BlobImplFile(file);
+        blobs.AppendElement(blobimpl);
       }
     }
   } else {
     nsCOMPtr<nsIFile> file;
     mFilePicker->GetFile(getter_AddRefs(file));
     if (file) {
-      files.AppendElement(file);
+      RefPtr<BlobImpl> blobimpl = new BlobImplFile(file);
+      blobs.AppendElement(blobimpl);
     }
   }
 
-  if (files.IsEmpty()) {
-    Unused << Send__delete__(this, void_t(), mResult);
-    return;
-  }
-
   MOZ_ASSERT(!mRunnable);
-  mRunnable = new IORunnable(this, files, mMode == nsIFilePicker::modeGetFolder);
-
+  mRunnable = new FileSizeAndDateRunnable(this, blobs);
   // Dispatch to background thread to do I/O:
   if (!mRunnable->Dispatch()) {
     Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel);
   }
 }
 
 bool
 FilePickerParent::CreateFilePicker()
--- a/dom/ipc/FilePickerParent.h
+++ b/dom/ipc/FilePickerParent.h
@@ -9,46 +9,32 @@
 
 #include "nsIEventTarget.h"
 #include "nsIFilePicker.h"
 #include "nsCOMArray.h"
 #include "nsThreadUtils.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/PFilePickerParent.h"
 
-class nsIFile;
-
 namespace mozilla {
 namespace dom {
 
 class FilePickerParent : public PFilePickerParent
 {
  public:
   FilePickerParent(const nsString& aTitle,
                    const int16_t& aMode)
   : mTitle(aTitle)
   , mMode(aMode)
   {}
 
   virtual ~FilePickerParent();
 
   void Done(int16_t aResult);
-
-  struct BlobImplOrString
-  {
-    RefPtr<BlobImpl> mBlobImpl;
-    nsString mDirectoryPath;
-
-    enum {
-      eBlobImpl,
-      eDirectoryPath
-    } mType;
-  };
-
-  void SendFilesOrDirectories(const nsTArray<BlobImplOrString>& aData);
+  void SendFiles(const nsTArray<RefPtr<BlobImpl>>& aDomBlobs);
 
   virtual bool RecvOpen(const int16_t& aSelectedType,
                         const bool& aAddToRecentDocs,
                         const nsString& aDefaultFile,
                         const nsString& aDefaultExtension,
                         InfallibleTArray<nsString>&& aFilters,
                         InfallibleTArray<nsString>&& aFilterNames,
                         const nsString& aDisplayDirectory) override;
@@ -70,36 +56,31 @@ class FilePickerParent : public PFilePic
   private:
     virtual ~FilePickerShownCallback() {}
     FilePickerParent* mFilePickerParent;
   };
 
  private:
   bool CreateFilePicker();
 
-  // This runnable is used to do some I/O operation on a separate thread.
-  class IORunnable : public nsRunnable
+  class FileSizeAndDateRunnable : public nsRunnable
   {
     FilePickerParent* mFilePickerParent;
-    nsTArray<nsCOMPtr<nsIFile>> mFiles;
-    nsTArray<BlobImplOrString> mResults;
+    nsTArray<RefPtr<BlobImpl>> mBlobs;
     nsCOMPtr<nsIEventTarget> mEventTarget;
-    bool mIsDirectory;
 
   public:
-    IORunnable(FilePickerParent *aFPParent,
-               nsTArray<nsCOMPtr<nsIFile>>& aFiles,
-               bool aIsDirectory);
-
+    FileSizeAndDateRunnable(FilePickerParent *aFPParent,
+                            nsTArray<RefPtr<BlobImpl>>& aBlobs);
     bool Dispatch();
     NS_IMETHOD Run();
     void Destroy();
   };
 
-  RefPtr<IORunnable> mRunnable;
+  RefPtr<FileSizeAndDateRunnable> mRunnable;
   RefPtr<FilePickerShownCallback> mCallback;
   nsCOMPtr<nsIFilePicker> mFilePicker;
 
   nsString mTitle;
   int16_t mMode;
   int16_t mResult;
 };
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -284,34 +284,32 @@ struct FileSystemCreateFileParams
   FileSystemFileDataValue data;
   bool replace;
 };
 
 struct FileSystemGetDirectoryListingParams
 {
   nsString filesystem;
   nsString realPath;
-  bool isRoot;
   // 'filters' could be an array rather than a semicolon separated string
   // (we'd then use InfallibleTArray<nsString> internally), but that is
   // wasteful.  E10s requires us to pass the filters over as a string anyway,
   // so avoiding using an array avoids serialization on the side passing the
   // filters.  Since an nsString can share its buffer when copied,
   // using that instead of InfallibleTArray<nsString> makes copying the filters
   // around in any given process a bit more efficient too, since copying a
   // single nsString is cheaper than copying InfallibleTArray member data and
   // each nsString that it contains.
   nsString filters;
 };
 
 struct FileSystemGetFileOrDirectoryParams
 {
   nsString filesystem;
   nsString realPath;
-  bool isRoot;
 };
 
 union FileSystemPathOrFileValue
 {
   nsString;
   PBlob;
 };
 
--- a/dom/ipc/PFilePicker.ipdl
+++ b/dom/ipc/PFilePicker.ipdl
@@ -7,40 +7,34 @@
 include protocol PBlob;
 include protocol PBrowser;
 
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 
 namespace mozilla {
 namespace dom {
 
-struct InputBlobs
+struct InputFiles
 {
   PBlob[] blobs;
 };
 
-struct InputDirectory
+union MaybeInputFiles
 {
-  nsString directoryPath;
-};
-
-union MaybeInputData
-{
-  InputBlobs;
-  InputDirectory;
+  InputFiles;
   void_t;
 };
 
 protocol PFilePicker
 {
   manager PBrowser;
 
 parent:
     async Open(int16_t selectedType, bool addToRecentDocs, nsString defaultFile,
                nsString defaultExtension, nsString[] filters, nsString[] filterNames,
                nsString displayDirectory);
 
 child:
-    async __delete__(MaybeInputData data, int16_t result);
+    async __delete__(MaybeInputFiles files, int16_t result);
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/webidl/Directory.webidl
+++ b/dom/webidl/Directory.webidl
@@ -15,17 +15,16 @@
  * http://w3c.github.io/filesystem-api/#idl-def-Directory
  * https://microsoftedge.github.io/directory-upload/proposal.html#directory-interface
  */
 [Exposed=Window]
 interface Directory {
   /*
    * The leaf name of the directory.
    */
-  [Throws]
   readonly attribute DOMString name;
 
   /*
    * Creates a new file or replaces an existing file with given data. The file
    * should be a descendent of current directory.
    *
    * @param path The relative path of the new file to current directory.
    * @param options It has two optional properties, 'ifExists' and 'data'.
@@ -101,23 +100,21 @@ partial interface Directory {
 
   /*
    * The path of the Directory (includes both its basename and leafname).
    * The path begins with the name of the ancestor Directory that was
    * originally exposed to content (say via a directory picker) and traversed
    * to obtain this Directory.  Full filesystem paths are not exposed to
    * unprivilaged content.
    */
-  [Throws]
   readonly attribute DOMString path;
 
   /*
    * Getter for the immediate children of this directory.
    */
-  [Throws]
   Promise<sequence<(File or Directory)>> getFilesAndDirectories();
 };
 
 enum CreateIfExistsMode { "replace", "fail" };
 
 dictionary CreateFileOptions {
   CreateIfExistsMode ifExists = "fail";
   (DOMString or Blob or ArrayBuffer or ArrayBufferView) data;
--- a/dom/webidl/FileList.webidl
+++ b/dom/webidl/FileList.webidl
@@ -6,13 +6,11 @@
  * The origin of this IDL file is
  * http://dev.w3.org/2006/webapi/FileAPI/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface FileList {
-  [Throws]
-  getter (File or Directory)? item(unsigned long index);
-
+  getter File? item(unsigned long index);
   readonly attribute unsigned long length;
 };
--- a/dom/webidl/HTMLInputElement.webidl
+++ b/dom/webidl/HTMLInputElement.webidl
@@ -150,20 +150,16 @@ partial interface HTMLInputElement {
   sequence<DOMString> mozGetFileNameArray();
 
   [ChromeOnly, Throws]
   void mozSetFileNameArray(sequence<DOMString> fileNames);
 
   [ChromeOnly]
   void mozSetFileArray(sequence<File> files);
 
-  // This method is meant to use for testing only.
-  [ChromeOnly, Throws]
-  void mozSetDirectory(DOMString directoryPath);
-
   // Number controls (<input type=number>) have an anonymous text control
   // (<input type=text>) in the anonymous shadow tree that they contain. On
   // such an anonymous text control this property provides access to the
   // number control that owns the text control. This is useful, for example,
   // in code that looks at the currently focused element to make decisions
   // about which IME to bring up. Such code needs to be able to check for any
   // owning number control since it probably wants to bring up a number pad
   // instead of the standard keyboard, even when the anonymous text control has
--- a/embedding/browser/nsDocShellTreeOwner.cpp
+++ b/embedding/browser/nsDocShellTreeOwner.cpp
@@ -1181,26 +1181,26 @@ DefaultTooltipTextProvider::GetNodeText(
                                                  getter_Copies(outText));
                 } else {
                   rv = bundle->GetStringFromName(MOZ_UTF16("NoFileSelected"),
                                                  getter_Copies(outText));
                 }
                 NS_ENSURE_SUCCESS(rv, rv);
               } else {
                 FileList* fl = static_cast<FileList*>(fileList.get());
-                fl->UnsafeItem(0).GetAsFile()->GetName(outText);
+                fl->Item(0)->GetName(outText);
 
                 // For UX and performance (jank) reasons we cap the number of
                 // files that we list in the tooltip to 20 plus a "and xxx more"
                 // line, or to 21 if exactly 21 files were picked.
                 const uint32_t TRUNCATED_FILE_COUNT = 20;
                 uint32_t count = std::min(listLength, TRUNCATED_FILE_COUNT);
                 for (uint32_t i = 1; i < count; ++i) {
                   nsString fileName;
-                  fl->UnsafeItem(i).GetAsFile()->GetName(fileName);
+                  fl->Item(i)->GetName(fileName);
                   outText.Append(NS_LITERAL_STRING("\n"));
                   outText.Append(fileName);
                 }
               }
             } else if (NS_SUCCEEDED(currElement->GetAttribute(NS_LITERAL_STRING("title"), outText)) &&
                        outText.Length()) {
               found = true;
             }
--- a/widget/nsBaseFilePicker.cpp
+++ b/widget/nsBaseFilePicker.cpp
@@ -12,60 +12,29 @@
 #include "nsIWidget.h"
 
 #include "nsIStringBundle.h"
 #include "nsXPIDLString.h"
 #include "nsIServiceManager.h"
 #include "nsCOMArray.h"
 #include "nsIFile.h"
 #include "nsEnumeratorUtils.h"
-#include "mozilla/dom/Directory.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/Services.h"
 #include "WidgetUtils.h"
 #include "nsThreadUtils.h"
 
 #include "nsBaseFilePicker.h"
 
 using namespace mozilla::widget;
 using namespace mozilla::dom;
 
 #define FILEPICKER_TITLES "chrome://global/locale/filepicker.properties"
 #define FILEPICKER_FILTERS "chrome://global/content/filepicker.properties"
 
-namespace {
-
-nsresult
-LocalFileToDirectoryOrBlob(nsPIDOMWindowInner* aWindow,
-                           bool aIsDirectory,
-                           nsIFile* aFile,
-                           nsISupports** aResult)
-{
-  if (aIsDirectory) {
-#ifdef DEBUG
-    bool isDir;
-    aFile->IsDirectory(&isDir);
-    MOZ_ASSERT(isDir);
-#endif
-
-    RefPtr<Directory> directory =
-      Directory::Create(aWindow, aFile, Directory::eDOMRootDirectory);
-    MOZ_ASSERT(directory);
-
-    directory.forget(aResult);
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIDOMBlob> blob = File::CreateFromFile(aWindow, aFile);
-  blob.forget(aResult);
-  return NS_OK;
-}
-
-} // anonymous namespace
-
 /**
  * A runnable to dispatch from the main thread to the main thread to display
  * the file picker while letting the showAsync method return right away.
 */
 class AsyncShowFilePicker : public nsRunnable
 {
 public:
   AsyncShowFilePicker(nsIFilePicker *aFilePicker,
@@ -100,19 +69,19 @@ private:
   RefPtr<nsIFilePickerShownCallback> mCallback;
 };
 
 class nsBaseFilePickerEnumerator : public nsISimpleEnumerator
 {
 public:
   NS_DECL_ISUPPORTS
 
-  nsBaseFilePickerEnumerator(nsPIDOMWindowOuter* aParent,
-                             nsISimpleEnumerator* iterator,
-                             int16_t aMode)
+  explicit nsBaseFilePickerEnumerator(nsPIDOMWindowOuter* aParent,
+                                      nsISimpleEnumerator* iterator,
+                                      int16_t aMode)
     : mIterator(iterator)
     , mParent(aParent->GetCurrentInnerWindow())
     , mMode(aMode)
   {}
 
   NS_IMETHOD
   GetNext(nsISupports** aResult) override
   {
@@ -124,20 +93,42 @@ public:
       return NS_OK;
     }
 
     nsCOMPtr<nsIFile> localFile = do_QueryInterface(tmp);
     if (!localFile) {
       return NS_ERROR_FAILURE;
     }
 
-    return LocalFileToDirectoryOrBlob(mParent,
-                                      mMode == nsIFilePicker::modeGetFolder,
-                                      localFile,
-                                      aResult);
+    RefPtr<File> domFile = File::CreateFromFile(mParent, localFile);
+
+    // Right now we're on the main thread of the chrome process. We need
+    // to call SetIsDirectory on the BlobImpl, but it's preferable not to
+    // call nsIFile::IsDirectory to determine what argument to pass since
+    // IsDirectory does synchronous I/O. It's true that since we've just
+    // been called synchronously directly after nsIFilePicker::Show blocked
+    // the main thread while the picker was being shown and the OS did file
+    // system access, doing more I/O to stat the selected files probably
+    // wouldn't be the end of the world. However, we can simply check
+    // mMode and avoid calling IsDirectory.
+    //
+    // In future we may take advantage of OS X's ability to allow both
+    // files and directories to be picked at the same time, so we do assert
+    // in debug builds that the mMode trick produces the correct results.
+    // If we do add that support maybe it's better to use IsDirectory
+    // directly, but in an nsRunnable punted off to a background thread.
+#ifdef DEBUG
+    bool isDir;
+    localFile->IsDirectory(&isDir);
+    MOZ_ASSERT(isDir == (mMode == nsIFilePicker::modeGetFolder));
+#endif
+    domFile->Impl()->SetIsDirectory(mMode == nsIFilePicker::modeGetFolder);
+
+    nsCOMPtr<nsIDOMBlob>(domFile).forget(aResult);
+    return NS_OK;
   }
 
   NS_IMETHOD
   HasMoreElements(bool* aResult) override
   {
     return mIterator->HasMoreElements(aResult);
   }
 
@@ -353,20 +344,20 @@ nsBaseFilePicker::GetDomFileOrDirectory(
 
   if (!localFile) {
     *aValue = nullptr;
     return NS_OK;
   }
 
   auto* innerParent = mParent ? mParent->GetCurrentInnerWindow() : nullptr;
 
-  return LocalFileToDirectoryOrBlob(innerParent,
-                                    mMode == nsIFilePicker::modeGetFolder,
-                                    localFile,
-                                    aValue);
+  RefPtr<File> domFile = File::CreateFromFile(innerParent, localFile);
+  domFile->Impl()->SetIsDirectory(mMode == nsIFilePicker::modeGetFolder);
+  nsCOMPtr<nsIDOMBlob>(domFile).forget(aValue);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsBaseFilePicker::GetDomFileOrDirectoryEnumerator(nsISimpleEnumerator** aValue)
 {
   nsCOMPtr<nsISimpleEnumerator> iter;
   nsresult rv = GetFiles(getter_AddRefs(iter));
   NS_ENSURE_SUCCESS(rv, rv);
--- a/widget/nsFilePickerProxy.cpp
+++ b/widget/nsFilePickerProxy.cpp
@@ -2,17 +2,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/. */
 
 #include "nsFilePickerProxy.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIFile.h"
-#include "mozilla/dom/Directory.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 
 using namespace mozilla::dom;
 
 NS_IMPL_ISUPPORTS(nsFilePickerProxy, nsIFilePicker)
 
@@ -138,53 +137,37 @@ nsFilePickerProxy::Open(nsIFilePickerSho
 
   SendOpen(mSelectedType, mAddToRecentDocs, mDefault, mDefaultExtension,
            mFilters, mFilterNames, displayDirectory);
 
   return NS_OK;
 }
 
 bool
-nsFilePickerProxy::Recv__delete__(const MaybeInputData& aData,
+nsFilePickerProxy::Recv__delete__(const MaybeInputFiles& aFiles,
                                   const int16_t& aResult)
 {
-  if (aData.type() == MaybeInputData::TInputBlobs) {
-    const InfallibleTArray<PBlobChild*>& blobs = aData.get_InputBlobs().blobsChild();
+  if (aFiles.type() == MaybeInputFiles::TInputFiles) {
+    const InfallibleTArray<PBlobChild*>& blobs = aFiles.get_InputFiles().blobsChild();
     for (uint32_t i = 0; i < blobs.Length(); ++i) {
       BlobChild* actor = static_cast<BlobChild*>(blobs[i]);
       RefPtr<BlobImpl> blobImpl = actor->GetBlobImpl();
       NS_ENSURE_TRUE(blobImpl, true);
 
       if (!blobImpl->IsFile()) {
         return true;
       }
 
       nsPIDOMWindowInner* inner =
         mParent ? mParent->GetCurrentInnerWindow() : nullptr;
       RefPtr<File> file = File::Create(inner, blobImpl);
       MOZ_ASSERT(file);
 
-      OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
-      element->SetAsFile() = file;
+      mFilesOrDirectories.AppendElement(file);
     }
-  } else if (aData.type() == MaybeInputData::TInputDirectory) {
-    nsCOMPtr<nsIFile> file;
-    NS_ConvertUTF16toUTF8 path(aData.get_InputDirectory().directoryPath());
-    nsresult rv = NS_NewNativeLocalFile(path, true, getter_AddRefs(file));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return true;
-    }
-
-    RefPtr<Directory> directory =
-      Directory::Create(mParent->GetCurrentInnerWindow(), file,
-                        Directory::eDOMRootDirectory);
-    MOZ_ASSERT(directory);
-
-    OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
-    element->SetAsDirectory() = directory;
   }
 
   if (mCallback) {
     mCallback->Done(aResult);
     mCallback = nullptr;
   }
 
   return true;
@@ -194,81 +177,62 @@ NS_IMETHODIMP
 nsFilePickerProxy::GetDomFileOrDirectory(nsISupports** aValue)
 {
   *aValue = nullptr;
   if (mFilesOrDirectories.IsEmpty()) {
     return NS_OK;
   }
 
   MOZ_ASSERT(mFilesOrDirectories.Length() == 1);
-
-  if (mFilesOrDirectories[0].IsFile()) {
-    nsCOMPtr<nsIDOMBlob> blob = mFilesOrDirectories[0].GetAsFile().get();
-    blob.forget(aValue);
-    return NS_OK;
-  }
-
-  MOZ_ASSERT(mFilesOrDirectories[0].IsDirectory());
-  RefPtr<Directory> directory = mFilesOrDirectories[0].GetAsDirectory();
-  directory.forget(aValue);
+  nsCOMPtr<nsIDOMBlob> blob = mFilesOrDirectories[0].get();
+  blob.forget(aValue);
   return NS_OK;
 }
 
 namespace {
 
 class SimpleEnumerator final : public nsISimpleEnumerator
 {
 public:
   NS_DECL_ISUPPORTS
 
-  explicit
-  SimpleEnumerator(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories)
-    : mFilesOrDirectories(aFilesOrDirectories)
+  explicit SimpleEnumerator(const nsTArray<RefPtr<File>>& aFiles)
+    : mFiles(aFiles)
     , mIndex(0)
   {}
 
   NS_IMETHOD
   HasMoreElements(bool* aRetvalue) override
   {
     MOZ_ASSERT(aRetvalue);
-    *aRetvalue = mIndex < mFilesOrDirectories.Length();
+    *aRetvalue = mIndex < mFiles.Length();
     return NS_OK;
   }
 
   NS_IMETHOD
-  GetNext(nsISupports** aValue) override
+  GetNext(nsISupports** aSupports) override
   {
-    NS_ENSURE_TRUE(mIndex < mFilesOrDirectories.Length(), NS_ERROR_FAILURE);
-
-    uint32_t index = mIndex++;
+    NS_ENSURE_TRUE(mIndex < mFiles.Length(), NS_ERROR_FAILURE);
 
-    if (mFilesOrDirectories[index].IsFile()) {
-      nsCOMPtr<nsIDOMBlob> blob = mFilesOrDirectories[index].GetAsFile().get();
-      blob.forget(aValue);
-      return NS_OK;
-    }
-
-    MOZ_ASSERT(mFilesOrDirectories[index].IsDirectory());
-    RefPtr<Directory> directory = mFilesOrDirectories[index].GetAsDirectory();
-    directory.forget(aValue);
+    nsCOMPtr<nsIDOMBlob> blob = mFiles[mIndex++].get();
+    blob.forget(aSupports);
     return NS_OK;
   }
 
 private:
   ~SimpleEnumerator()
   {}
 
-  nsTArray<mozilla::dom::OwningFileOrDirectory> mFilesOrDirectories;
+  nsTArray<RefPtr<File>> mFiles;
   uint32_t mIndex;
 };
 
 NS_IMPL_ISUPPORTS(SimpleEnumerator, nsISimpleEnumerator)
 
 } // namespace
 
 NS_IMETHODIMP
 nsFilePickerProxy::GetDomFileOrDirectoryEnumerator(nsISimpleEnumerator** aDomfiles)
 {
-  RefPtr<SimpleEnumerator> enumerator =
-    new SimpleEnumerator(mFilesOrDirectories);
+  RefPtr<SimpleEnumerator> enumerator = new SimpleEnumerator(mFilesOrDirectories);
   enumerator.forget(aDomfiles);
   return NS_OK;
 }
--- a/widget/nsFilePickerProxy.h
+++ b/widget/nsFilePickerProxy.h
@@ -8,22 +8,27 @@
 
 #include "nsBaseFilePicker.h"
 #include "nsString.h"
 #include "nsIURI.h"
 #include "nsTArray.h"
 #include "nsCOMArray.h"
 
 #include "mozilla/dom/PFilePickerChild.h"
-#include "mozilla/dom/UnionTypes.h"
 
 class nsIWidget;
 class nsIFile;
 class nsPIDOMWindowInner;
 
+namespace mozilla {
+namespace dom {
+class File;
+} // namespace dom
+} // namespace mozilla
+
 /**
   This class creates a proxy file picker to be used in content processes.
   The file picker just collects the initialization data and when Show() is
   called, remotes everything to the chrome process which in turn can show a
   platform specific file picker.
 */
 class nsFilePickerProxy : public nsBaseFilePicker,
                           public mozilla::dom::PFilePickerChild
@@ -49,23 +54,23 @@ public:
     NS_IMETHODIMP GetDomFileOrDirectory(nsISupports** aValue) override;
     NS_IMETHODIMP GetDomFileOrDirectoryEnumerator(nsISimpleEnumerator** aValue) override;
 
     NS_IMETHODIMP Show(int16_t* aReturn) override;
     NS_IMETHODIMP Open(nsIFilePickerShownCallback* aCallback) override;
 
     // PFilePickerChild
     virtual bool
-    Recv__delete__(const MaybeInputData& aData, const int16_t& aResult) override;
+    Recv__delete__(const MaybeInputFiles& aFiles, const int16_t& aResult) override;
 
 private:
     ~nsFilePickerProxy();
     void InitNative(nsIWidget*, const nsAString&) override;
 
-    nsTArray<mozilla::dom::OwningFileOrDirectory> mFilesOrDirectories;
+    nsTArray<RefPtr<mozilla::dom::File>> mFilesOrDirectories;
     nsCOMPtr<nsIFilePickerShownCallback> mCallback;
 
     int16_t   mSelectedType;
     nsString  mFile;
     nsString  mDefault;
     nsString  mDefaultExtension;
 
     InfallibleTArray<nsString> mFilters;