Bug 1173320 - patch 1/8 - Implement Directory object as string and not as BlobImpl, r=smaug
☠☠ backed out by bd8284e36c7c ☠ ☠
authorAndrea Marchesini <amarchesini@mozilla.com>
Sat, 19 Mar 2016 14:32:18 +0100
changeset 329320 ac58a56021acde418bcf6b9175e757ca90880356
parent 329319 1a47789d4d15826865858d4fd7100f7db73e67f3
child 329321 bf65b38f759ede2f241278b83f3d46213c2ad302
push id1146
push userCallek@gmail.com
push dateMon, 25 Jul 2016 16:35:44 +0000
treeherdermozilla-release@a55778f9cd5a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1173320
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1173320 - patch 1/8 - Implement Directory object as string and not as BlobImpl, r=smaug
dom/base/File.cpp
dom/base/File.h
dom/base/nsContentUtils.cpp
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/Directory.cpp
dom/filesystem/Directory.h
dom/filesystem/FileSystemBase.cpp
dom/filesystem/FileSystemBase.h
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/PFileSystemRequest.ipdl
dom/filesystem/RemoveTask.cpp
dom/filesystem/RemoveTask.h
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
widget/nsBaseFilePicker.cpp
widget/nsFilePickerProxy.cpp
widget/nsFilePickerProxy.h
--- a/dom/base/File.cpp
+++ b/dom/base/File.cpp
@@ -222,22 +222,16 @@ 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()
@@ -415,21 +409,20 @@ 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, BlobDirState aDirState)
+             int64_t aLastModifiedDate)
 {
   RefPtr<File> file = new File(aParent,
-    new BlobImplBase(aName, aContentType, aLength, aLastModifiedDate,
-                     aDirState));
+    new BlobImplBase(aName, aContentType, aLength, aLastModifiedDate));
   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)
@@ -941,27 +934,16 @@ 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,28 +39,16 @@ 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
@@ -96,32 +84,23 @@ 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);
@@ -189,17 +168,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, BlobDirState aDirState);
+         int64_t aLastModifiedDate);
 
   // 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);
@@ -334,105 +313,78 @@ 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,
-               BlobDirState aDirState = BlobDirState::eUnknownIfDir)
+               uint64_t aLength, int64_t aLastModifiedDate)
     : 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");
@@ -513,46 +465,16 @@ 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() {}
 
@@ -560,17 +482,16 @@ 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;
 
@@ -585,18 +506,17 @@ 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,
-                   BlobDirState::eIsNotDir)
+    : BlobImplBase(aName, aContentType, aLength, aLastModifiedDate)
     , 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)
@@ -787,18 +707,16 @@ 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/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -7620,17 +7620,16 @@ 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/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(mFiles)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList)
   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(mFiles)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList)
   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,36 +276,38 @@ DataTransfer::GetMozUserCancelled(bool* 
 {
   *aUserCancelled = MozUserCancelled();
   return NS_OK;
 }
 
 FileList*
 DataTransfer::GetFiles(ErrorResult& aRv)
 {
-  return GetFilesInternal(aRv, nsContentUtils::SubjectPrincipal());
+  return GetFileListInternal(aRv, nsContentUtils::SubjectPrincipal());
 }
 
 FileList*
-DataTransfer::GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal)
+DataTransfer::GetFileListInternal(ErrorResult& aRv,
+                                  nsIPrincipal* aSubjectPrincipal)
 {
   if (mEventMessage != eDrop &&
       mEventMessage != eLegacyDragDrop &&
       mEventMessage != ePaste) {
     return nullptr;
   }
 
-  if (!mFiles) {
-    mFiles = new FileList(static_cast<nsIDOMDataTransfer*>(this));
+  if (!mFileList) {
+    mFileList = 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;
@@ -333,31 +335,31 @@ DataTransfer::GetFilesInternal(ErrorResu
         }
 
         MOZ_ASSERT(blobImpl->IsFile());
 
         domFile = File::Create(GetParentObject(), blobImpl);
         MOZ_ASSERT(domFile);
       }
 
-      if (!mFiles->Append(domFile)) {
+      if (!mFileList->Append(domFile)) {
         aRv.Throw(NS_ERROR_FAILURE);
         return nullptr;
       }
     }
   }
 
-  return mFiles;
+  return mFileList;
 }
 
 NS_IMETHODIMP
 DataTransfer::GetFiles(nsIDOMFileList** aFileList)
 {
   ErrorResult rv;
-  NS_IF_ADDREF(*aFileList = GetFilesInternal(rv, nsContentUtils::GetSystemPrincipal()));
+  NS_IF_ADDREF(*aFileList = GetFileListInternal(rv, nsContentUtils::GetSystemPrincipal()));
   return rv.StealNSResult();
 }
 
 already_AddRefed<DOMStringList>
 DataTransfer::Types()
 {
   RefPtr<DOMStringList> types = new DOMStringList();
   if (mItems.Length()) {
@@ -849,57 +851,33 @@ DataTransfer::GetFilesAndDirectories(Err
     return nullptr;
   }
 
   RefPtr<Promise> p = Promise::Create(global, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  if (!mFiles) {
+  if (!mFileList) {
     GetFiles(aRv);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
   }
 
   Sequence<OwningFileOrDirectory> filesAndDirsSeq;
 
-  if (mFiles && mFiles->Length()) {
-    if (!filesAndDirsSeq.SetLength(mFiles->Length(), mozilla::fallible_t())) {
+  if (mFileList && mFileList->Length()) {
+    if (!filesAndDirsSeq.SetLength(mFileList->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);
-      }
+    for (uint32_t i = 0; i < mFileList->Length(); ++i) {
+      filesAndDirsSeq[i].SetAsFile() = mFileList->Item(i);
     }
   }
 
   p->MaybeResolve(filesAndDirsSeq);
 
   return p.forget();
 }
 
--- 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* GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal);
+  FileList* GetFileListInternal(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,18 +295,19 @@ 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, containing only the files present in the dataTransfer
-  RefPtr<FileList> mFiles;
+  // array of files and directories, containing only the files present in the
+  // dataTransfer
+  RefPtr<FileList> mFileList;
 
   // 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,18 +313,17 @@ 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(),
-                            BlobDirState::eUnknownIfDir);
+                            lastModified.get_int64_t());
 
   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,125 +11,188 @@
 #include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "nsIFile.h"
 #include "nsStringGlue.h"
 
 namespace mozilla {
 namespace dom {
 
-CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem,
-                                         const nsAString& aPath,
-                                         ErrorResult& aRv)
-  : FileSystemTaskBase(aFileSystem)
-  , mTargetRealPath(aPath)
+/* static */ already_AddRefed<CreateDirectoryTask>
+CreateDirectoryTask::Create(FileSystemBase* aFileSystem,
+                            nsIFile* aTargetPath,
+                            ErrorResult& aRv)
 {
   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->GetWindow());
-  if (!globalObject) {
-    return;
+  if (NS_WARN_IF(!globalObject)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
   }
-  mPromise = Promise::Create(globalObject, aRv);
+
+  task->mPromise = Promise::Create(globalObject, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return task.forget();
 }
 
-CreateDirectoryTask::CreateDirectoryTask(
-  FileSystemBase* aFileSystem,
-  const FileSystemCreateDirectoryParams& aParam,
-  FileSystemRequestParent* aParent)
+/* static */ already_AddRefed<CreateDirectoryTask>
+CreateDirectoryTask::Create(FileSystemBase* aFileSystem,
+                            const FileSystemCreateDirectoryParams& 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<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(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& aFileSystem) const
+CreateDirectoryTask::GetRequestParams(const nsString& aSerializedDOMPath,
+                                      ErrorResult& aRv) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  return FileSystemCreateDirectoryParams(aFileSystem, mTargetRealPath);
+
+  nsAutoString path;
+  aRv = mTargetPath->GetPath(path);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return FileSystemCreateDirectoryParams();
+  }
+
+  return FileSystemCreateDirectoryParams(aSerializedDOMPath, path);
 }
 
 FileSystemResponseValue
-CreateDirectoryTask::GetSuccessRequestResult() const
+CreateDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  return FileSystemDirectoryResponse(mTargetRealPath);
+
+  nsAutoString path;
+  aRv = mTargetPath->GetPath(path);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return FileSystemDirectoryResponse();
+  }
+
+  return FileSystemDirectoryResponse(path);
 }
 
 void
-CreateDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
+CreateDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                                             ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   FileSystemDirectoryResponse r = aValue;
-  mTargetRealPath = r.realPath();
+
+  NS_ConvertUTF16toUTF8 path(r.realPath());
+  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
+  NS_WARN_IF(aRv.Failed());
 }
 
 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 = file->Exists(&fileExists);
+  nsresult rv = mTargetPath->Exists(&fileExists);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (fileExists) {
     return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR;
   }
 
-  rv = file->Create(nsIFile::DIRECTORY_TYPE, 0770);
-  return rv;
+  rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0770);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
 }
 
 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 = new Directory(mFileSystem, mTargetRealPath);
+  RefPtr<Directory> dir = Directory::Create(mFileSystem->GetWindow(),
+                                            mTargetPath,
+                                            Directory::eNotDOMRootDirectory,
+                                            mFileSystem);
+  MOZ_ASSERT(dir);
+
   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,53 +11,65 @@
 #include "nsAutoPtr.h"
 #include "mozilla/ErrorResult.h"
 
 namespace mozilla {
 namespace dom {
 
 class Promise;
 
-class CreateDirectoryTask final
-  : public FileSystemTaskBase
+class CreateDirectoryTask final : public FileSystemTaskBase
 {
 public:
-  CreateDirectoryTask(FileSystemBase* aFileSystem,
-                      const nsAString& aPath,
-                      ErrorResult& aRv);
-  CreateDirectoryTask(FileSystemBase* aFileSystem,
-                      const FileSystemCreateDirectoryParams& aParam,
-                      FileSystemRequestParent* aParent);
+  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);
 
   virtual
   ~CreateDirectoryTask();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
 
 protected:
   virtual FileSystemParams
-  GetRequestParams(const nsString& aFileSystem) const override;
+  GetRequestParams(const nsString& aSerializedDOMPath,
+                   ErrorResult& aRv) const override;
 
   virtual FileSystemResponseValue
-  GetSuccessRequestResult() const override;
+  GetSuccessRequestResult(ErrorResult& aRv) const override;
 
   virtual void
-  SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
+  SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                          ErrorResult& aRv) 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;
-  nsString mTargetRealPath;
+  nsCOMPtr<nsIFile> mTargetPath;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_CreateDirectoryTask_h
--- a/dom/filesystem/CreateFileTask.cpp
+++ b/dom/filesystem/CreateFileTask.cpp
@@ -20,78 +20,125 @@
 #include "nsIOutputStream.h"
 #include "nsStringGlue.h"
 
 namespace mozilla {
 namespace dom {
 
 uint32_t CreateFileTask::sOutputBufferSize = 0;
 
-CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
-                               const nsAString& aPath,
-                               Blob* aBlobData,
-                               InfallibleTArray<uint8_t>& aArrayData,
-                               bool replace,
-                               ErrorResult& aRv)
-  : FileSystemTaskBase(aFileSystem)
-  , mTargetRealPath(aPath)
-  , mReplace(replace)
+/* static */ already_AddRefed<CreateFileTask>
+CreateFileTask::Create(FileSystemBase* aFileSystem,
+                       nsIFile* aTargetPath,
+                       Blob* aBlobData,
+                       InfallibleTArray<uint8_t>& aArrayData,
+                       bool aReplace,
+                       ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
-  GetOutputBufferSize();
+
+  RefPtr<CreateFileTask> task =
+    new CreateFileTask(aFileSystem, aTargetPath, aReplace);
+
+  // aTargetPath can be null. In this case SetError will be called.
+
+  task->GetOutputBufferSize();
+
   if (aBlobData) {
     if (XRE_IsParentProcess()) {
-      aBlobData->GetInternalStream(getter_AddRefs(mBlobStream), aRv);
-      NS_WARN_IF(aRv.Failed());
+      aBlobData->GetInternalStream(getter_AddRefs(task->mBlobStream), aRv);
+      if (NS_WARN_IF(aRv.Failed())) {
+        return nullptr;
+      }
     } else {
-      mBlobData = aBlobData;
+      task->mBlobData = aBlobData;
     }
   }
-  mArrayData.SwapElements(aArrayData);
+
+  task->mArrayData.SwapElements(aArrayData);
+
   nsCOMPtr<nsIGlobalObject> globalObject =
     do_QueryInterface(aFileSystem->GetWindow());
-  if (!globalObject) {
-    return;
+  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<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;
   }
-  mPromise = Promise::Create(globalObject, aRv);
+
+  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);
 }
 
 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) {
@@ -102,50 +149,57 @@ 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& aFileSystem) const
+CreateFileTask::GetRequestParams(const nsString& aSerializedDOMPath,
+                                 ErrorResult& aRv) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   FileSystemCreateFileParams param;
-  param.filesystem() = aFileSystem;
-  param.realPath() = mTargetRealPath;
+  param.filesystem() = aSerializedDOMPath;
+
+  aRv = mTargetPath->GetPath(param.realPath());
+  if (NS_WARN_IF(aRv.Failed())) {
+    return param;
+  }
+
   param.replace() = mReplace;
   if (mBlobData) {
     BlobChild* actor
       = ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlobData);
     if (actor) {
       param.data() = actor;
     }
   } else {
     param.data() = mArrayData;
   }
   return param;
 }
 
 FileSystemResponseValue
-CreateFileTask::GetSuccessRequestResult() const
+CreateFileTask::GetSuccessRequestResult(ErrorResult& aRv) 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)
+CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                                        ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   FileSystemFileResponse r = aValue;
   BlobChild* actor = static_cast<BlobChild*>(r.blobChild());
   mTargetBlobImpl = actor->GetBlobImpl();
 }
 
 nsresult
@@ -171,60 +225,55 @@ CreateFileTask::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;
-  }
-
-  if (!mFileSystem->IsSafeFile(file)) {
+  if (!mFileSystem->IsSafeFile(mTargetPath)) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   bool exists = false;
-  nsresult rv = file->Exists(&exists);
+  nsresult rv = mTargetPath->Exists(&exists);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (exists) {
     bool isFile = false;
-    rv = file->IsFile(&isFile);
+    rv = mTargetPath->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 = file->Remove(false);
+    rv = mTargetPath->Remove(false);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
-  rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
+  rv = mTargetPath->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<nsIOutputStream> outputStream;
-  rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file);
+  rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mTargetPath);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   AutoClose acOutputStream(outputStream);
 
   nsCOMPtr<nsIOutputStream> bufferedOutputStream;
   rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
@@ -257,17 +306,17 @@ CreateFileTask::Work()
 
     mBlobStream->Close();
     mBlobStream = nullptr;
 
     if (mFileSystem->IsShutdown()) {
       return NS_ERROR_FAILURE;
     }
 
-    mTargetBlobImpl = new BlobImplFile(file);
+    mTargetBlobImpl = new BlobImplFile(mTargetPath);
     return NS_OK;
   }
 
   // Write file content from array data.
 
   uint32_t written;
   rv = bufferedOutputStream->Write(
     reinterpret_cast<char*>(mArrayData.Elements()),
@@ -276,17 +325,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(file);
+  mTargetBlobImpl = new BlobImplFile(mTargetPath);
   return NS_OK;
 }
 
 void
 CreateFileTask::HandlerCallback()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   if (mFileSystem->IsShutdown()) {
--- a/dom/filesystem/CreateFileTask.h
+++ b/dom/filesystem/CreateFileTask.h
@@ -15,62 +15,75 @@ class nsIInputStream;
 
 namespace mozilla {
 namespace dom {
 
 class Blob;
 class BlobImpl;
 class Promise;
 
-class CreateFileTask final
-  : public FileSystemTaskBase
+class CreateFileTask final : public FileSystemTaskBase
 {
 public:
-  CreateFileTask(FileSystemBase* aFileSystem,
-                 const nsAString& aPath,
-                 Blob* aBlobData,
-                 InfallibleTArray<uint8_t>& aArrayData,
-                 bool replace,
-                 ErrorResult& aRv);
-  CreateFileTask(FileSystemBase* aFileSystem,
-                 const FileSystemCreateFileParams& aParam,
-                 FileSystemRequestParent* aParent);
+  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);
 
   virtual
   ~CreateFileTask();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
 
 protected:
   virtual FileSystemParams
-  GetRequestParams(const nsString& aFileSystem) const override;
+  GetRequestParams(const nsString& aSerializedDOMPath,
+                   ErrorResult& aRv) const override;
 
   virtual FileSystemResponseValue
-  GetSuccessRequestResult() const override;
+  GetSuccessRequestResult(ErrorResult& aRv) const override;
 
   virtual void
-  SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
+  SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                          ErrorResult& aRv) 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;
-  nsString mTargetRealPath;
+  nsCOMPtr<nsIFile> mTargetPath;
 
   // 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
@@ -41,27 +41,27 @@ DeviceStorageFileSystem::DeviceStorageFi
     !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();
@@ -127,16 +127,22 @@ DeviceStorageFileSystem::IsSafeFile(nsIF
   return typeChecker->Check(mStorageType, aFile);
 }
 
 bool
 DeviceStorageFileSystem::IsSafeDirectory(Directory* aDir) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aDir);
-  RefPtr<FileSystemBase> fs = aDir->GetFileSystem();
-  MOZ_ASSERT(fs);
+
+  ErrorResult rv;
+  RefPtr<FileSystemBase> fs = aDir->GetFileSystem(rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    rv.SuppressException();
+    return false;
+  }
+
   // Check if the given directory is from this storage.
   return fs->ToString() == mString;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/Directory.cpp
+++ b/dom/filesystem/Directory.cpp
@@ -13,16 +13,17 @@
 #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
@@ -32,93 +33,137 @@
 #endif
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(Directory)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Directory)
-  tmp->mFileSystem->Unlink();
+  if (tmp->mFileSystem) {
+    tmp->mFileSystem->Unlink();
+    tmp->mFileSystem = nullptr;
+  }
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Directory)
-  tmp->mFileSystem->Traverse(cb);
+  if (tmp->mFileSystem) {
+    tmp->mFileSystem->Traverse(cb);
+  }
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
   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)
 {
-  RefPtr<GetFileOrDirectoryTask> task = new GetFileOrDirectoryTask(
-    aFileSystem, EmptyString(), true, aRv);
-  if (aRv.Failed()) {
+  MOZ_ASSERT(aFileSystem);
+
+  nsCOMPtr<nsIFile> path;
+  aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(aFileSystem->GetLocalRootPath()),
+                              true, getter_AddRefs(path));
+  if (NS_WARN_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();
 }
 
-Directory::Directory(FileSystemBase* aFileSystem,
-                     const nsAString& aPath)
-  : mFileSystem(aFileSystem)
-  , mPath(aPath)
+/* static */ already_AddRefed<Directory>
+Directory::Create(nsPIDOMWindowInner* aWindow, nsIFile* aFile,
+                  DirectoryType aType, FileSystemBase* aFileSystem)
 {
-  MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
-  // Remove the trailing "/".
-  mPath.Trim(FILESYSTEM_DOM_PATH_SEPARATOR, false, true);
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aFile);
+
+#ifdef DEBUG
+  bool isDir;
+  nsresult rv = aFile->IsDirectory(&isDir);
+  MOZ_ASSERT(NS_SUCCEEDED(rv) && isDir);
+#endif
+
+  RefPtr<Directory> directory =
+    new Directory(aWindow, aFile, aType, aFileSystem);
+  return directory.forget();
+}
+
+Directory::Directory(nsPIDOMWindowInner* aWindow,
+                     nsIFile* aFile,
+                     DirectoryType aType,
+                     FileSystemBase* aFileSystem)
+  : mWindow(aWindow)
+  , 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.
 }
 
 Directory::~Directory()
 {
 }
 
 nsPIDOMWindowInner*
 Directory::GetParentObject() const
 {
-  return mFileSystem->GetWindow();
+  return mWindow;
 }
 
 JSObject*
 Directory::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return DirectoryBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
-Directory::GetName(nsAString& aRetval) const
+Directory::GetName(nsAString& aRetval, ErrorResult& aRv)
 {
   aRetval.Truncate();
 
-  if (mPath.IsEmpty()) {
-    mFileSystem->GetRootName(aRetval);
+  if (mType == eDOMRootDirectory) {
+    RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+
+    fs->GetRootName(aRetval);
     return;
   }
 
-  aRetval = Substring(mPath,
-                      mPath.RFindChar(FileSystemUtils::kSeparatorChar) + 1);
+  aRv = mFile->GetLeafName(aRetval);
+  NS_WARN_IF(aRv.Failed());
 }
 
 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()) {
@@ -133,61 +178,75 @@ Directory::CreateFile(const nsAString& a
       const ArrayBufferView& view = data.GetAsArrayBufferView();
       view.ComputeLengthAndData();
       arrayData.AppendElements(view.Data(), view.Length());
     } else {
       blobData = data.GetAsBlob();
     }
   }
 
-  if (!DOMPathToRealPath(aPath, realPath)) {
-    error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
+  nsCOMPtr<nsIFile> realPath;
+  nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
+
+  RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
   }
 
   RefPtr<CreateFileTask> task =
-    new CreateFileTask(mFileSystem, realPath, blobData, arrayData, replace, aRv);
-  if (aRv.Failed()) {
+    CreateFileTask::Create(fs, realPath, blobData, arrayData, replace, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
+
   task->SetError(error);
   FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
 
 already_AddRefed<Promise>
 Directory::CreateDirectory(const nsAString& aPath, ErrorResult& aRv)
 {
-  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()) {
+  nsCOMPtr<nsIFile> realPath;
+  nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
+
+  RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
+  if (NS_WARN_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)
 {
-  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()) {
+  nsCOMPtr<nsIFile> realPath;
+  nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
+
+  RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
+  if (NS_WARN_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)
 {
@@ -200,104 +259,153 @@ 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;
-  nsAutoString realPath;
+  nsCOMPtr<nsIFile> 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()) {
-    if (!DOMPathToRealPath(aPath.GetAsString(), realPath)) {
-      error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
-    }
-  } else if (!mFileSystem->IsSafeDirectory(&aPath.GetAsDirectory())) {
+    error = DOMPathToRealPath(aPath.GetAsString(), getter_AddRefs(realPath));
+  } else if (!fs->IsSafeDirectory(&aPath.GetAsDirectory())) {
     error = NS_ERROR_DOM_SECURITY_ERR;
   } else {
-    realPath = aPath.GetAsDirectory().mPath;
+    realPath = aPath.GetAsDirectory().mFile;
     // The target must be a descendant of this directory.
-    if (!FileSystemUtils::IsDescendantPath(mPath, realPath)) {
+    if (!FileSystemUtils::IsDescendantPath(mFile, realPath)) {
       error = NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
     }
   }
 
-  RefPtr<RemoveTask> task = new RemoveTask(mFileSystem, mPath, blob, realPath,
-    aRecursive, aRv);
-  if (aRv.Failed()) {
+  RefPtr<RemoveTask> task =
+    RemoveTask::Create(fs, mFile, blob, realPath, aRecursive, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
   task->SetError(error);
   FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
 
 void
-Directory::GetPath(nsAString& aRetval) const
+Directory::GetPath(nsAString& aRetval, ErrorResult& aRv)
 {
-  if (mPath.IsEmpty()) {
-    // The Directory ctor removes any trailing '/'; this is the root directory.
+  if (mType == eDOMRootDirectory) {
     aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR);
   } else {
-    aRetval = mPath;
+    // TODO: this should be a bit different...
+    GetName(aRetval, aRv);
   }
 }
 
-already_AddRefed<Promise>
-Directory::GetFilesAndDirectories()
+nsresult
+Directory::GetFullRealPath(nsAString& aPath)
 {
-  ErrorResult rv;
+  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;
+  }
+
   RefPtr<GetDirectoryListingTask> task =
-    new GetDirectoryListingTask(mFileSystem, mPath, mFilters, rv);
-  if (NS_WARN_IF(rv.Failed())) {
+    GetDirectoryListingTask::Create(fs, mFile, mType, mFilters, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
 
 void
 Directory::SetContentFilters(const nsAString& aFilters)
 {
   mFilters = aFilters;
 }
 
 FileSystemBase*
-Directory::GetFileSystem() const
+Directory::GetFileSystem(ErrorResult& aRv)
 {
-  return mFileSystem.get();
+  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(mWindow);
+
+    mFileSystem = fs;
+  }
+
+  return mFileSystem;
 }
 
-bool
-Directory::DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const
+nsresult
+Directory::DOMPathToRealPath(const nsAString& aPath, nsIFile** aFile) const
 {
-  aRealPath.Truncate();
-
   nsString relativePath;
   relativePath = aPath;
 
   // Trim white spaces.
   static const char kWhitespace[] = "\b\t\r\n ";
   relativePath.Trim(kWhitespace);
 
   if (!IsValidRelativePath(relativePath)) {
-    return false;
+    return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
   }
 
-  aRealPath = mPath + NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR) +
-    relativePath;
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = mFile->Clone(getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
-  return true;
+  rv = file->AppendRelativePath(relativePath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  file.forget(aFile);
+  return NS_OK;
 }
 
 // static
 bool
 Directory::IsValidRelativePath(const nsString& aPath)
 {
   // We don't allow empty relative path to access the root.
   if (aPath.IsEmpty()) {
--- a/dom/filesystem/Directory.h
+++ b/dom/filesystem/Directory.h
@@ -36,35 +36,58 @@ 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);
 
-  Directory(FileSystemBase* aFileSystem, const nsAString& aPath);
+  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(nsPIDOMWindowInner* aWindow, nsIFile* aDirectory,
+         DirectoryType aType, FileSystemBase* aFileSystem = 0);
 
   // ========= Begin WebIDL bindings. ===========
 
   nsPIDOMWindowInner*
   GetParentObject() const;
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void
-  GetName(nsAString& aRetval) const;
+  GetName(nsAString& aRetval, ErrorResult& aRv);
 
   already_AddRefed<Promise>
   CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions,
              ErrorResult& aRv);
 
   already_AddRefed<Promise>
   CreateDirectory(const nsAString& aPath, ErrorResult& aRv);
 
@@ -75,20 +98,23 @@ 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) const;
+  GetPath(nsAString& aRetval, ErrorResult& aRv);
+
+  nsresult
+  GetFullRealPath(nsAString& aPath);
 
   already_AddRefed<Promise>
-  GetFilesAndDirectories();
+  GetFilesAndDirectories(ErrorResult& aRv);
 
   // =========== 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.
    *
@@ -108,35 +134,41 @@ 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() const;
+  GetFileSystem(ErrorResult& aRv);
+
 private:
+  Directory(nsPIDOMWindowInner* aWindow,
+            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.
    */
-  bool
-  DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const;
+  nsresult
+  DOMPathToRealPath(const nsAString& aPath, nsIFile** aFile) const;
 
   already_AddRefed<Promise>
   RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive,
                  ErrorResult& aRv);
 
+  nsCOMPtr<nsPIDOMWindowInner> mWindow;
   RefPtr<FileSystemBase> mFileSystem;
-  nsString mPath;
+  nsCOMPtr<nsIFile> mFile;
+  DirectoryType mType;
+
   nsString mFilters;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Directory_h
--- a/dom/filesystem/FileSystemBase.cpp
+++ b/dom/filesystem/FileSystemBase.cpp
@@ -63,44 +63,65 @@ FileSystemBase::GetWindow() const
   return nullptr;
 }
 
 already_AddRefed<nsIFile>
 FileSystemBase::GetLocalFile(const nsAString& aRealPath) const
 {
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Should be on parent process!");
+
+  // Let's convert the input path to /path
   nsAutoString localPath;
-  FileSystemUtils::NormalizedPathToLocalPath(aRealPath, localPath);
-  localPath = mLocalRootPath + localPath;
+  if (!aRealPath.IsEmpty() &&
+      !StringBeginsWith(aRealPath,
+                        NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR))) {
+    localPath.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR);
+  }
+  localPath.Append(aRealPath);
+
+  // We have to normalize the path string in order to follow the separator
+  // schema of this OS.
+  nsAutoString normalizedPath;
+  FileSystemUtils::NormalizedPathToLocalPath(localPath, normalizedPath);
+
+  // The full path is mLocalRootPath + normalizedPath.
+  nsAutoString path(mLocalRootPath);
+  path.Append(normalizedPath);
+
   nsCOMPtr<nsIFile> file;
-  nsresult rv = NS_NewLocalFile(localPath, false, getter_AddRefs(file));
+  nsresult rv = NS_NewLocalFile(path, false, getter_AddRefs(file));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
+
   return file.forget();
 }
 
 bool
-FileSystemBase::GetRealPath(BlobImpl* aFile, nsAString& aRealPath) const
+FileSystemBase::GetRealPath(BlobImpl* aFile, nsIFile** aPath) 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;
   }
 
-  return LocalPathToRealPath(filePath, aRealPath);
+  rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(filePath),
+                             true, aPath);
+  if (NS_WARN_IF(rv.Failed())) {
+    return false;
+  }
+
+  return true;
 }
 
 bool
 FileSystemBase::IsSafeFile(nsIFile* aFile) const
 {
   return false;
 }
 
@@ -115,14 +136,15 @@ FileSystemBase::LocalPathToRealPath(cons
                                     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
@@ -68,23 +68,18 @@ 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, nsAString& aRealPath) const;
+  GetRealPath(BlobImpl* aFile, nsIFile** aPath) const;
 
   /*
    * Get the permission name required to access this file system.
    */
   const nsCString&
   GetPermission() const
   {
     return mPermission;
--- a/dom/filesystem/FileSystemRequestParent.cpp
+++ b/dom/filesystem/FileSystemRequestParent.cpp
@@ -24,26 +24,31 @@ FileSystemRequestParent::FileSystemReque
 FileSystemRequestParent::~FileSystemRequestParent()
 {
 }
 
 #define FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(name)                         \
     case FileSystemParams::TFileSystem##name##Params: {                        \
       const FileSystem##name##Params& p = aParams;                             \
       mFileSystem = FileSystemBase::FromString(p.filesystem());                \
-      task = new name##Task(mFileSystem, p, this);                             \
+      task = name##Task::Create(mFileSystem, p, this, rv);                     \
+      if (NS_WARN_IF(rv.Failed())) {                                           \
+        return false;                                                          \
+      }                                                                        \
       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
@@ -101,22 +101,29 @@ FileSystemTaskBase::Start()
     return;
   }
 
   // Run in child process.
   if (mFileSystem->IsShutdown()) {
     return;
   }
 
+  ErrorResult rv;
+  FileSystemParams params = GetRequestParams(mFileSystem->ToString(), 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,
-    GetRequestParams(mFileSystem->ToString()));
+                                                                  params);
 }
 
 NS_IMETHODIMP
 FileSystemTaskBase::Run()
 {
   if (!NS_IsMainThread()) {
     // Run worker thread tasks
     nsresult rv = Work();
@@ -149,34 +156,42 @@ 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()) {
-    return FileSystemErrorResponse(mErrorValue);
-  } else {
-    return GetSuccessRequestResult();
+  if (!HasError()) {
+    ErrorResult rv;
+    FileSystemResponseValue value = GetSuccessRequestResult(rv);
+    if (NS_WARN_IF(rv.Failed())) {
+      return FileSystemErrorResponse(rv.StealNSResult());
+    }
+
+    return value;
   }
+
+  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 {
-    SetSuccessRequestResult(aValue);
+    ErrorResult rv;
+    SetSuccessRequestResult(aValue, rv);
+    mErrorValue = rv.StealNSResult();
   }
 }
 
 bool
 FileSystemTaskBase::Recv__delete__(const FileSystemResponseValue& aValue)
 {
   SetRequestResult(aValue);
   HandlerCallback();
--- a/dom/filesystem/FileSystemTaskBase.h
+++ b/dom/filesystem/FileSystemTaskBase.h
@@ -171,35 +171,37 @@ 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& aFileSystem) const = 0;
+  GetRequestParams(const nsString& aSerializedDOMPath,
+                   ErrorResult& aRv) 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() const = 0;
+  GetSuccessRequestResult(ErrorResult& aRv) 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) = 0;
+  SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                          ErrorResult& aRv) = 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
@@ -60,10 +60,36 @@ FileSystemUtils::IsDescendantPath(const 
   if (aDescendantPath.Length() < prefix.Length() ||
       !StringBeginsWith(aDescendantPath, prefix)) {
     return false;
   }
 
   return true;
 }
 
+// static
+bool
+FileSystemUtils::IsDescendantPath(nsIFile* aFile,
+                                  nsIFile* aDescendantFile)
+{
+  nsAutoString path;
+  nsresult rv = aFile->GetPath(path);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  nsAutoString descendantPath;
+  rv = aDescendantFile->GetPath(descendantPath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  // Check the sub-directory path to see if it has the parent path as prefix.
+  if (descendantPath.Length() <= path.Length() ||
+      !StringBeginsWith(descendantPath, path)) {
+    return false;
+  }
+
+  return true;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/FileSystemUtils.h
+++ b/dom/filesystem/FileSystemUtils.h
@@ -30,16 +30,22 @@ public:
   /*
    * 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.
+   */
+  static bool
+  IsDescendantPath(nsIFile* aPath, nsIFile* aDescendantPath);
+
+  /*
    * Return true if aDescendantPath is a descendant of aPath. Both aPath and
    * aDescendantPath are absolute DOM path.
    */
   static bool
   IsDescendantPath(const nsAString& aPath, const nsAString& aDescendantPath);
 
   static const char16_t kSeparatorChar = char16_t('/');
 };
--- a/dom/filesystem/GetDirectoryListingTask.cpp
+++ b/dom/filesystem/GetDirectoryListingTask.cpp
@@ -3,56 +3,102 @@
 /* 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 {
 
-GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem,
-                                                 const nsAString& aTargetPath,
-                                                 const nsAString& aFilters,
-                                                 ErrorResult& aRv)
-  : FileSystemTaskBase(aFileSystem)
-  , mTargetRealPath(aTargetPath)
-  , mFilters(aFilters)
+/* static */ already_AddRefed<GetDirectoryListingTask>
+GetDirectoryListingTask::Create(FileSystemBase* aFileSystem,
+                                nsIFile* aTargetPath,
+                                Directory::DirectoryType aType,
+                                const nsAString& aFilters,
+                                ErrorResult& aRv)
 {
   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->GetWindow());
-  if (!globalObject) {
-    return;
+  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;
   }
-  mPromise = Promise::Create(globalObject, aRv);
+
+  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::eNotRootDirectory;
+  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);
 }
 
 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!");
@@ -61,106 +107,143 @@ 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& aFileSystem) const
+GetDirectoryListingTask::GetRequestParams(const nsString& aSerializedDOMPath,
+                                          ErrorResult& aRv) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  return FileSystemGetDirectoryListingParams(aFileSystem, mTargetRealPath,
+
+  nsAutoString path;
+  aRv = mTargetPath->GetPath(path);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return FileSystemGetDirectoryListingParams();
+  }
+
+  return FileSystemGetDirectoryListingParams(aSerializedDOMPath, path,
+                                             mType == Directory::eDOMRootDirectory,
                                              mFilters);
 }
 
+void
+GetDirectoryListingTask::CreateNormalizedRelativePath(const nsAString& aPath,
+                                                      nsAString& aRelativePath) const
+{
+  uint32_t rootPathLen = mFileSystem->GetLocalRootPath().Length();
+  FileSystemUtils::LocalPathToNormalizedPath(
+    Substring(aPath, rootPathLen, aPath.Length() - rootPathLen), aRelativePath);
+}
+
 FileSystemResponseValue
-GetDirectoryListingTask::GetSuccessRequestResult() const
+GetDirectoryListingTask::GetSuccessRequestResult(ErrorResult& aRv) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
 
   InfallibleTArray<PBlobParent*> blobs;
 
-  for (unsigned i = 0; i < mTargetBlobImpls.Length(); i++) {
-    BlobParent* blobParent = GetBlobParent(mTargetBlobImpls[i]);
-    if (blobParent) {
-      blobs.AppendElement(blobParent);
+  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);
     }
   }
+
   FileSystemDirectoryListingResponse response;
-  response.blobsParent().SwapElements(blobs);
+  response.data().SwapElements(inputs);
   return response;
 }
 
 void
-GetDirectoryListingTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
+GetDirectoryListingTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                                                 ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aValue.type() ==
                FileSystemResponseValue::TFileSystemDirectoryListingResponse);
 
   FileSystemDirectoryListingResponse r = aValue;
-  nsTArray<PBlobChild*>& blobs = r.blobsChild();
+  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();
 
-  for (unsigned i = 0; i < blobs.Length(); i++) {
-    mTargetBlobImpls.AppendElement(static_cast<BlobChild*>(blobs[i])->GetBlobImpl());
+      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();
+    }
   }
 }
 
 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 = dir->Exists(&exists);
+  nsresult rv = mTargetPath->Exists(&exists);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (!exists) {
-    if (!getRoot) {
+    if (mType == Directory::eNotRootDirectory) {
       return NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
     }
 
     // If the root directory doesn't exit, create it.
-    rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0777);
+    rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   // Get isDirectory.
   bool isDir;
-  rv = dir->IsDirectory(&isDir);
+  rv = mTargetPath->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 = dir->GetDirectoryEntries(getter_AddRefs(entries));
+  rv = mTargetPath->GetDirectoryEntries(getter_AddRefs(entries));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   bool filterOutSensitive = false;
   {
     HTMLSplitOnSpacesTokenizer tokenizer(mFilters, ';');
     nsAutoString token;
@@ -181,17 +264,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))) ||
@@ -208,19 +291,32 @@ GetDirectoryListingTask::Work()
       if (NS_WARN_IF(NS_FAILED(currFile->GetLeafName(leafName)))) {
         continue;
       }
       if (leafName[0] == char16_t('.')) {
         continue;
       }
     }
 
-    BlobImplFile* impl = new BlobImplFile(currFile);
-    impl->LookupAndCacheIsDirectory();
-    mTargetBlobImpls.AppendElement(impl);
+    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;
+    }
   }
   return NS_OK;
 }
 
 void
 GetDirectoryListingTask::HandlerCallback()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
@@ -230,47 +326,86 @@ GetDirectoryListingTask::HandlerCallback
   }
 
   if (HasError()) {
     mPromise->MaybeReject(mErrorValue);
     mPromise = nullptr;
     return;
   }
 
-  size_t count = mTargetBlobImpls.Length();
+  size_t count = mTargetData.Length();
 
   Sequence<OwningFileOrDirectory> listing;
 
   if (!listing.SetLength(count, mozilla::fallible_t())) {
     mPromise->MaybeReject(NS_ERROR_FAILURE);
     mPromise = nullptr;
     return;
   }
 
   for (unsigned i = 0; i < count; i++) {
-    if (mTargetBlobImpls[i]->IsDirectory()) {
-      nsAutoString name;
-      mTargetBlobImpls[i]->GetName(name);
-      nsAutoString path(mTargetRealPath);
-      path.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR);
-      path.Append(name);
+    if (mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath) {
 #ifdef DEBUG
       if (XRE_IsParentProcess()) {
-        nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(path);
+        nsCOMPtr<nsIFile> file;
+        nsresult rv = NS_NewLocalFile(mTargetData[i].mDirectoryPath, false,
+                                      getter_AddRefs(file));
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          mPromise->MaybeReject(rv);
+          mPromise = nullptr;
+          return;
+        }
+
         bool exist;
-        file->Exists(&exist);
-        MOZ_ASSERT(exist);
+        rv = file->Exists(&exist);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          mPromise->MaybeReject(rv);
+          mPromise = nullptr;
+          return;
+        }
+
+        // We cannot assert here because the file can be done in the meantime.
+        NS_ASSERTION(exist, "The file doesn't exist anymore?!?");
+
+        nsAutoString normalizedLocalRootPath;
+        FileSystemUtils::NormalizedPathToLocalPath(mFileSystem->GetLocalRootPath(),
+                                                   normalizedLocalRootPath);
+
+        nsAutoString directoryPath;
+        FileSystemUtils::NormalizedPathToLocalPath(mTargetData[i].mDirectoryPath,
+                                                   directoryPath);
+
+        MOZ_ASSERT(FileSystemUtils::IsDescendantPath(normalizedLocalRootPath,
+                                                     directoryPath));
       }
 #endif
-      RefPtr<Directory> directory = new Directory(mFileSystem, path);
+
+      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;
+      }
+
+      RefPtr<Directory> directory = Directory::Create(mFileSystem->GetWindow(),
+                                                      directoryPath,
+                                                      Directory::eNotRootDirectory,
+                                                      mFileSystem);
+      MOZ_ASSERT(directory);
+
       // Propogate mFilter onto sub-Directory object:
       directory->SetContentFilters(mFilters);
       listing[i].SetAsDirectory() = directory;
     } else {
-      listing[i].SetAsFile() = File::Create(mFileSystem->GetWindow(), mTargetBlobImpls[i]);
+      MOZ_ASSERT(mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl);
+      listing[i].SetAsFile() =
+        File::Create(mFileSystem->GetWindow(), mTargetData[i].mBlobImpl);
     }
   }
 
   mPromise->MaybeResolve(listing);
   mPromise = nullptr;
 }
 
 void
--- a/dom/filesystem/GetDirectoryListingTask.h
+++ b/dom/filesystem/GetDirectoryListingTask.h
@@ -2,68 +2,89 @@
 /* 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:
-  // 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);
+  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);
 
   virtual
   ~GetDirectoryListingTask();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
-protected:
+
+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);
+
   virtual FileSystemParams
-  GetRequestParams(const nsString& aFileSystem) const override;
+  GetRequestParams(const nsString& aSerializedDOMPath,
+                   ErrorResult& aRv) const override;
 
   virtual FileSystemResponseValue
-  GetSuccessRequestResult() const override;
+  GetSuccessRequestResult(ErrorResult& aRv) const override;
 
   virtual void
-  SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
+  SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                          ErrorResult& aRv) override;
 
   virtual nsresult
   Work() override;
 
   virtual void
   HandlerCallback() override;
 
-private:
+  void
+  CreateNormalizedRelativePath(const nsAString& aPath,
+                               nsAString& aRelativePath) const;
+
   RefPtr<Promise> mPromise;
-  nsString mTargetRealPath;
+  nsCOMPtr<nsIFile> mTargetPath;
   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<RefPtr<BlobImpl>> mTargetBlobImpls;
+  nsTArray<Directory::BlobImplOrDirectoryPath> mTargetData;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_GetDirectoryListing_h
--- a/dom/filesystem/GetFileOrDirectoryTask.cpp
+++ b/dom/filesystem/GetFileOrDirectoryTask.cpp
@@ -2,114 +2,180 @@
 /* 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 {
 
-GetFileOrDirectoryTask::GetFileOrDirectoryTask(
-  FileSystemBase* aFileSystem,
-  const nsAString& aTargetPath,
-  bool aDirectoryOnly,
-  ErrorResult& aRv)
-  : FileSystemTaskBase(aFileSystem)
-  , mTargetRealPath(aTargetPath)
-  , mIsDirectory(aDirectoryOnly)
+/* static */ already_AddRefed<GetFileOrDirectoryTask>
+GetFileOrDirectoryTask::Create(FileSystemBase* aFileSystem,
+                               nsIFile* aTargetPath,
+                               Directory::DirectoryType aType,
+                               bool aDirectoryOnly,
+                               ErrorResult& aRv)
 {
   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->GetWindow());
-  if (!globalObject) {
-    return;
+  if (NS_WARN_IF(!globalObject)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
   }
-  mPromise = Promise::Create(globalObject, aRv);
+
+  task->mPromise = Promise::Create(globalObject, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return task.forget();
 }
 
-GetFileOrDirectoryTask::GetFileOrDirectoryTask(
-  FileSystemBase* aFileSystem,
-  const FileSystemGetFileOrDirectoryParams& aParam,
-  FileSystemRequestParent* aParent)
+/* 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::eNotRootDirectory;
+  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)
   : 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& aFileSystem) const
+GetFileOrDirectoryTask::GetRequestParams(const nsString& aSerializedDOMPath,
+                                         ErrorResult& aRv) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  return FileSystemGetFileOrDirectoryParams(aFileSystem, mTargetRealPath);
+
+  nsAutoString path;
+  aRv = mTargetPath->GetPath(path);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return FileSystemGetFileOrDirectoryParams();
+  }
+
+  return FileSystemGetFileOrDirectoryParams(aSerializedDOMPath, path,
+                                            mType == Directory::eDOMRootDirectory);
 }
 
 FileSystemResponseValue
-GetFileOrDirectoryTask::GetSuccessRequestResult() const
+GetFileOrDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   if (mIsDirectory) {
-    return FileSystemDirectoryResponse(mTargetRealPath);
+    nsAutoString path;
+    aRv = mTargetPath->GetPath(path);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return FileSystemDirectoryResponse();
+    }
+
+    return FileSystemDirectoryResponse(path);
   }
 
   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)
+GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                                                ErrorResult& aRv)
 {
   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;
-      mTargetRealPath = r.realPath();
+
+      NS_ConvertUTF16toUTF8 path(r.realPath());
+      aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
+      if (NS_WARN_IF(aRv.Failed())) {
+        return;
+      }
+
       mIsDirectory = true;
       break;
     }
     default: {
       NS_RUNTIMEABORT("not reached");
       break;
     }
   }
@@ -122,73 +188,66 @@ 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 = file->Exists(&exists);
+  nsresult rv = mTargetPath->Exists(&exists);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (!exists) {
-    if (!getRoot) {
+    if (mType == Directory::eNotRootDirectory) {
       return NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
     }
 
     // If the root directory doesn't exit, create it.
-    rv = file->Create(nsIFile::DIRECTORY_TYPE, 0777);
+    rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   // Get isDirectory.
-  rv = file->IsDirectory(&mIsDirectory);
+  rv = mTargetPath->IsDirectory(&mIsDirectory);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (mIsDirectory) {
     return NS_OK;
   }
 
   // Check if the root is a directory.
-  if (getRoot) {
+  if (mType == Directory::eDOMRootDirectory) {
     return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
   }
 
   bool isFile;
   // Get isFile
-  rv = file->IsFile(&isFile);
+  rv = mTargetPath->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(file)) {
+  if (!mFileSystem->IsSafeFile(mTargetPath)) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  mTargetBlobImpl = new BlobImplFile(file);
+  mTargetBlobImpl = new BlobImplFile(mTargetPath);
 
   return NS_OK;
 }
 
 void
 GetFileOrDirectoryTask::HandlerCallback()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
@@ -199,17 +258,22 @@ GetFileOrDirectoryTask::HandlerCallback(
 
   if (HasError()) {
     mPromise->MaybeReject(mErrorValue);
     mPromise = nullptr;
     return;
   }
 
   if (mIsDirectory) {
-    RefPtr<Directory> dir = new Directory(mFileSystem, mTargetRealPath);
+    RefPtr<Directory> dir = Directory::Create(mFileSystem->GetWindow(),
+                                              mTargetPath,
+                                              mType,
+                                              mFileSystem);
+    MOZ_ASSERT(dir);
+
     mPromise->MaybeResolve(dir);
     mPromise = nullptr;
     return;
   }
 
   RefPtr<Blob> blob = Blob::Create(mFileSystem->GetWindow(), mTargetBlobImpl);
   mPromise->MaybeResolve(blob);
   mPromise = nullptr;
--- a/dom/filesystem/GetFileOrDirectoryTask.h
+++ b/dom/filesystem/GetFileOrDirectoryTask.h
@@ -2,67 +2,85 @@
 /* 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:
-  // 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);
+  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);
 
   virtual
   ~GetFileOrDirectoryTask();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
 protected:
   virtual FileSystemParams
-  GetRequestParams(const nsString& aFileSystem) const override;
+  GetRequestParams(const nsString& aSerializedDOMPath,
+                   ErrorResult& aRv) const override;
 
   virtual FileSystemResponseValue
-  GetSuccessRequestResult() const override;
+  GetSuccessRequestResult(ErrorResult& aRv) const override;
 
   virtual void
-  SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
+  SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                          ErrorResult& aRv) 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;
-  nsString mTargetRealPath;
+  nsCOMPtr<nsIFile> mTargetPath;
+
   // 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/PFileSystemRequest.ipdl
+++ b/dom/filesystem/PFileSystemRequest.ipdl
@@ -15,19 +15,36 @@ 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
 {
-  PBlob[] blobs;
+  FileSystemDirectoryListingResponseData[] data;
 };
 
 struct FileSystemErrorResponse
 {
   nsresult error;
 };
 
 struct FileSystemBooleanResponse
--- a/dom/filesystem/RemoveTask.cpp
+++ b/dom/filesystem/RemoveTask.cpp
@@ -13,111 +13,175 @@
 #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->GetWindow());
+  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,
-                       const nsAString& aDirPath,
+                       nsIFile* aDirPath,
                        BlobImpl* aTargetBlob,
-                       const nsAString& aTargetPath,
-                       bool aRecursive,
-                       ErrorResult& aRv)
+                       nsIFile* aTargetPath,
+                       bool aRecursive)
   : FileSystemTaskBase(aFileSystem)
-  , mDirRealPath(aDirPath)
+  , mDirPath(aDirPath)
   , mTargetBlobImpl(aTargetBlob)
-  , mTargetRealPath(aTargetPath)
+  , mTargetPath(aTargetPath)
   , mRecursive(aRecursive)
   , mReturnValue(false)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
-  nsCOMPtr<nsIGlobalObject> globalObject =
-    do_QueryInterface(aFileSystem->GetWindow());
-  if (!globalObject) {
-    return;
-  }
-  mPromise = Promise::Create(globalObject, aRv);
+  MOZ_ASSERT(aDirPath);
 }
 
 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& aFileSystem) const
+RemoveTask::GetRequestParams(const nsString& aSerializedDOMPath,
+                             ErrorResult& aRv) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   FileSystemRemoveParams param;
-  param.filesystem() = aFileSystem;
-  param.directory() = mDirRealPath;
+  param.filesystem() = aSerializedDOMPath;
+
+  aRv = mDirPath->GetPath(param.directory());
+  if (NS_WARN_IF(aRv.Failed())) {
+    return param;
+  }
+
   param.recursive() = mRecursive;
   if (mTargetBlobImpl) {
     RefPtr<Blob> blob = Blob::Create(mFileSystem->GetWindow(),
-                                       mTargetBlobImpl);
+                                     mTargetBlobImpl);
     BlobChild* actor
       = ContentChild::GetSingleton()->GetOrCreateActorForBlob(blob);
     if (actor) {
       param.target() = actor;
     }
   } else {
-    param.target() = mTargetRealPath;
+    nsAutoString path;
+    aRv = mTargetPath->GetPath(path);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return param;
+    }
+
+    param.target() = path;
   }
   return param;
 }
 
 FileSystemResponseValue
-RemoveTask::GetSuccessRequestResult() const
+RemoveTask::GetSuccessRequestResult(ErrorResult& aRv) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   return FileSystemBooleanResponse(mReturnValue);
 }
 
 void
-RemoveTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
+RemoveTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                                    ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   FileSystemBooleanResponse r = aValue;
   mReturnValue = r.success();
 }
 
 nsresult
 RemoveTask::Work()
@@ -125,53 +189,49 @@ 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 DOM path if a File is passed as the target.
+  // Get the path if a File is passed as the target.
   if (mTargetBlobImpl) {
-    if (!mFileSystem->GetRealPath(mTargetBlobImpl, mTargetRealPath)) {
+    if (!mFileSystem->GetRealPath(mTargetBlobImpl,
+                                  getter_AddRefs(mTargetPath))) {
       return NS_ERROR_DOM_SECURITY_ERR;
     }
-    if (!FileSystemUtils::IsDescendantPath(mDirRealPath, mTargetRealPath)) {
+    if (!FileSystemUtils::IsDescendantPath(mDirPath, mTargetPath)) {
       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 = file->Exists(&exists);
+  nsresult rv = mTargetPath->Exists(&exists);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (!exists) {
     mReturnValue = false;
     return NS_OK;
   }
 
   bool isFile = false;
-  rv = file->IsFile(&isFile);
+  rv = mTargetPath->IsFile(&isFile);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  if (isFile && !mFileSystem->IsSafeFile(file)) {
+  if (isFile && !mFileSystem->IsSafeFile(mTargetPath)) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  rv = file->Remove(mRecursive);
+  rv = mTargetPath->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,62 +12,78 @@
 #include "mozilla/ErrorResult.h"
 
 namespace mozilla {
 namespace dom {
 
 class BlobImpl;
 class Promise;
 
-class RemoveTask final
-  : public FileSystemTaskBase
+class RemoveTask final : public FileSystemTaskBase
 {
 public:
-  RemoveTask(FileSystemBase* aFileSystem,
-             const nsAString& aDirPath,
-             BlobImpl* aTargetBlob,
-             const nsAString& aTargetPath,
-             bool aRecursive,
-             ErrorResult& aRv);
-  RemoveTask(FileSystemBase* aFileSystem,
-             const FileSystemRemoveParams& aParam,
-             FileSystemRequestParent* aParent);
+  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);
 
   virtual
   ~RemoveTask();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
 
 protected:
   virtual FileSystemParams
-  GetRequestParams(const nsString& aFileSystem) const override;
+  GetRequestParams(const nsString& aSerializedDOMPath,
+                   ErrorResult& aRv) const override;
 
   virtual FileSystemResponseValue
-  GetSuccessRequestResult() const override;
+  GetSuccessRequestResult(ErrorResult& aRv) const override;
 
   virtual void
-  SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
+  SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                          ErrorResult& aRv) 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;
-  nsString mDirRealPath;
+  nsCOMPtr<nsIFile> mDirPath;
+
   // 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;
-  nsString mTargetRealPath;
+  nsCOMPtr<nsIFile> mTargetPath;
   bool mRecursive;
   bool mReturnValue;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_RemoveTask_h
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -246,40 +246,89 @@ class HTMLInputElementState final : publ
       return mValue;
     }
 
     void SetValue(const nsAString& aValue)
     {
       mValue = aValue;
     }
 
-    const nsTArray<RefPtr<BlobImpl>>& GetBlobImpls()
+    void
+    GetFilesOrDirectories(nsPIDOMWindowInner* aWindow,
+                          nsTArray<OwningFileOrDirectory>& aResult) const
     {
-      return mBlobImpls;
-    }
-
-    void SetBlobImpls(const nsTArray<RefPtr<File>>& aFile)
+      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)
     {
-      mBlobImpls.Clear();
-      for (uint32_t i = 0, len = aFile.Length(); i < len; ++i) {
-        mBlobImpls.AppendElement(aFile[i]->Impl());
+      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;
+        }
       }
     }
 
     HTMLInputElementState()
       : mValue()
       , mChecked(false)
       , mCheckedSet(false)
     {}
 
   protected:
     ~HTMLInputElementState() {}
 
     nsString mValue;
-    nsTArray<RefPtr<BlobImpl>> mBlobImpls;
+
+    nsTArray<Directory::BlobImplOrDirectoryPath> mBlobImplsOrDirectoryPaths;
+
     bool mChecked;
     bool mCheckedSet;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(HTMLInputElementState, NS_INPUT_ELEMENT_STATE_IID)
 
 NS_IMPL_ISUPPORTS(HTMLInputElementState, HTMLInputElementState)
 
@@ -335,60 +384,92 @@ 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 aDomFile's implementation of
+ * This may return nullptr if the DOM File'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>
-DOMFileToLocalFile(File* aDomFile)
+DOMFileOrDirectoryToLocalFile(const OwningFileOrDirectory& aData)
 {
   nsString path;
-  ErrorResult rv;
-  aDomFile->GetMozFullPathInternal(path, rv);
-  if (rv.Failed() || path.IsEmpty()) {
-    rv.SuppressException();
-    return nullptr;
+
+  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);
   }
 
   nsCOMPtr<nsIFile> localFile;
-  rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true,
-                             getter_AddRefs(localFile));
-  if (NS_WARN_IF(rv.Failed())) {
-    rv.SuppressException();
+  nsresult rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true,
+                                      getter_AddRefs(localFile));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     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<RefPtr<File>> newFiles;
+  nsTArray<OwningFileOrDirectory> newFilesOrDirectories;
   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;
@@ -397,51 +478,63 @@ 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) {
-        newFiles.AppendElement(static_cast<File*>(domBlob.get()));
+      if (!domBlob) {
+        continue;
       }
+
+      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();
-      newFiles.AppendElement(file);
-    }
-  }
-
-  if (newFiles.IsEmpty()) {
+      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()) {
     return NS_OK;
   }
 
   // Store the last used directory using the content pref service:
-  nsCOMPtr<nsIFile> file = DOMFileToLocalFile(newFiles[0]);
+  nsCOMPtr<nsIFile> file =
+    DOMFileOrDirectoryToLocalFile(newFilesOrDirectories[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->SetFiles(newFiles, true);
+  mInput->SetFilesOrDirectories(newFilesOrDirectories, true);
   return nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(),
                                               static_cast<nsIDOMHTMLInputElement*>(mInput.get()),
                                               NS_LITERAL_STRING("change"), true,
                                               false);
 }
 
 NS_IMPL_ISUPPORTS(HTMLInputElement::nsFilePickerShownCallback,
                   nsIFilePickerShownCallback)
@@ -667,48 +760,42 @@ HTMLInputElement::InitFilePicker(FilePic
     SetFilePickerFiltersFromAccept(filePicker);
   } else {
     filePicker->AppendFilters(nsIFilePicker::filterAll);
   }
 
   // Set default directry and filename
   nsAutoString defaultName;
 
-  const nsTArray<RefPtr<File>>& oldFiles = GetFilesInternal();
+  const nsTArray<OwningFileOrDirectory>& oldFiles =
+    GetFilesOrDirectoriesInternal();
 
   nsCOMPtr<nsIFilePickerShownCallback> callback =
     new HTMLInputElement::nsFilePickerShownCallback(this, filePicker);
 
   if (!oldFiles.IsEmpty() &&
       aType != FILE_PICKER_DIRECTORY) {
     nsString path;
 
-    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> localFile = DOMFileOrDirectoryToLocalFile(oldFiles[0]);
+    if (localFile) {
       nsCOMPtr<nsIFile> parentFile;
-      rv = localFile->GetParent(getter_AddRefs(parentFile));
+      nsresult 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;
-      oldFiles[0]->GetName(leafName);
+      GetDOMFileOrDirectoryName(oldFiles[0], leafName);
+
       if (!leafName.IsEmpty()) {
         filePicker->SetDefaultString(leafName);
       }
     }
 
     rv = filePicker->Open(callback);
     if (NS_SUCCEEDED(rv)) {
       mPickerRunning = true;
@@ -751,17 +838,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
@@ -930,26 +1017,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(mFiles)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilesOrDirectories)
   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(mFiles)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilesOrDirectories)
   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
 
@@ -998,18 +1085,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->mFiles.Clear();
-        it->mFiles.AppendElements(mFiles);
+        it->mFilesOrDirectories.Clear();
+        it->mFilesOrDirectories.AppendElements(mFilesOrDirectories);
       }
       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);
       }
@@ -1437,34 +1524,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 (!mFiles.IsEmpty()) {
+        if (!mFilesOrDirectories.IsEmpty()) {
           ErrorResult rv;
-          mFiles[0]->GetMozFullPath(aValue, rv);
+          GetDOMFileOrDirectoryPath(mFilesOrDirectories[0], 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 (mFiles.IsEmpty()) {
+        if (mFilesOrDirectories.IsEmpty()) {
           aValue.Truncate();
         } else {
-          mFiles[0]->GetName(aValue);
+          GetDOMFileOrDirectoryName(mFilesOrDirectories[0], aValue);
         }
       }
 
       return NS_OK;
 
     case VALUE_MODE_DEFAULT:
       // Treat defaultValue as value.
       GetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue);
@@ -1489,18 +1576,18 @@ HTMLInputElement::IsValueEmpty() const
   GetValueInternal(value);
 
   return value.IsEmpty();
 }
 
 void
 HTMLInputElement::ClearFiles(bool aSetValueChanged)
 {
-  nsTArray<RefPtr<File>> files;
-  SetFiles(files, aSetValueChanged);
+  nsTArray<OwningFileOrDirectory> data;
+  SetFilesOrDirectories(data, aSetValueChanged);
 }
 
 /* static */ Decimal
 HTMLInputElement::StringToDecimal(const nsAString& aValue)
 {
   if (!IsASCII(aValue)) {
     return Decimal::nan();
   }
@@ -2060,19 +2147,19 @@ HTMLInputElement::FlushFrames()
     GetComposedDoc()->FlushPendingNotifications(Flush_Frames);
   }
 }
 
 void
 HTMLInputElement::MozGetFileNameArray(nsTArray<nsString>& aArray,
                                       ErrorResult& aRv)
 {
-  for (uint32_t i = 0; i < mFiles.Length(); i++) {
-    nsString str;
-    mFiles[i]->GetMozFullPathInternal(str, aRv);
+  for (uint32_t i = 0; i < mFilesOrDirectories.Length(); i++) {
+    nsAutoString str;
+    GetDOMFileOrDirectoryPath(mFilesOrDirectories[i], str, aRv);
     if (NS_WARN_IF(aRv.Failed())) {
       return;
     }
 
     aArray.AppendElement(str);
   }
 }
 
@@ -2112,66 +2199,77 @@ HTMLInputElement::MozGetFileNameArray(ui
 void
 HTMLInputElement::MozSetFileArray(const Sequence<OwningNonNull<File>>& aFiles)
 {
   nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
   MOZ_ASSERT(global);
   if (!global) {
     return;
   }
-  nsTArray<RefPtr<File>> files;
+
+  nsTArray<OwningFileOrDirectory> files;
   for (uint32_t i = 0; i < aFiles.Length(); ++i) {
     RefPtr<File> file = File::Create(global, aFiles[i].get()->Impl());
     MOZ_ASSERT(file);
 
-    files.AppendElement(file);
-  }
-  SetFiles(files, true);
+    OwningFileOrDirectory* element = files.AppendElement();
+    element->SetAsFile() = file;
+  }
+
+  SetFilesOrDirectories(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<RefPtr<File>> files;
+  nsTArray<OwningFileOrDirectory> 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) {
-      nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
-      RefPtr<File> domFile = File::CreateFromFile(global, file);
-      files.AppendElement(domFile);
-    } else {
+    if (!file) {
       continue; // Not much we can do if the file doesn't exist
     }
 
-  }
-
-  SetFiles(files, true);
+    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);
 }
 
 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);
@@ -2374,74 +2472,76 @@ HTMLInputElement::GetPlaceholderVisibili
 void
 HTMLInputElement::GetDisplayFileName(nsAString& aValue) const
 {
   if (OwnerDoc()->IsStaticDocument()) {
     aValue = mStaticDocFileList;
     return;
   }
 
-  if (mFiles.Length() == 1) {
-    mFiles[0]->GetName(aValue);
+  if (mFilesOrDirectories.Length() == 1) {
+    GetDOMFileOrDirectoryName(mFilesOrDirectories[0], aValue);
     return;
   }
 
   nsXPIDLString value;
 
-  if (mFiles.IsEmpty()) {
+  if (mFilesOrDirectories.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(mFiles.Length()));
+    count.AppendInt(int(mFilesOrDirectories.Length()));
 
     const char16_t* params[] = { count.get() };
     nsContentUtils::FormatLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
                                           "XFilesSelected", params, value);
   }
 
   aValue = value;
 }
 
 void
-HTMLInputElement::SetFiles(const nsTArray<RefPtr<File>>& aFiles,
-                           bool aSetValueChanged)
-{
-  mFiles.Clear();
-  mFiles.AppendElements(aFiles);
-
-  AfterSetFiles(aSetValueChanged);
+HTMLInputElement::SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories,
+                                        bool aSetValueChanged)
+{
+  mFilesOrDirectories.Clear();
+  mFilesOrDirectories.AppendElements(aFilesOrDirectories);
+
+  AfterSetFilesOrDirectories(aSetValueChanged);
 }
 
 void
 HTMLInputElement::SetFiles(nsIDOMFileList* aFiles,
                            bool aSetValueChanged)
 {
   RefPtr<FileList> files = static_cast<FileList*>(aFiles);
-  mFiles.Clear();
+  mFilesOrDirectories.Clear();
 
   if (aFiles) {
     uint32_t listLength;
     aFiles->GetLength(&listLength);
     for (uint32_t i = 0; i < listLength; i++) {
       RefPtr<File> file = files->Item(i);
-      mFiles.AppendElement(file);
-    }
-  }
-
-  AfterSetFiles(aSetValueChanged);
+
+      OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
+      element->SetAsFile() = file;
+    }
+  }
+
+  AfterSetFilesOrDirectories(aSetValueChanged);
 }
 
 void
-HTMLInputElement::AfterSetFiles(bool aSetValueChanged)
+HTMLInputElement::AfterSetFilesOrDirectories(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);
@@ -2449,21 +2549,21 @@ HTMLInputElement::AfterSetFiles(bool aSe
   }
 
 #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 (mFiles.IsEmpty()) {
+  if (mFilesOrDirectories.IsEmpty()) {
     mFirstFilePath.Truncate();
   } else {
     ErrorResult rv;
-    mFiles[0]->GetMozFullPath(mFirstFilePath, rv);
+    GetDOMFileOrDirectoryPath(mFilesOrDirectories[0], mFirstFilePath, rv);
     if (NS_WARN_IF(rv.Failed())) {
       rv.SuppressException();
     }
   }
 #endif
 
   UpdateFileList();
 
@@ -2533,19 +2633,21 @@ HTMLInputElement::HandleNumberControlSpi
 }
 
 nsresult
 HTMLInputElement::UpdateFileList()
 {
   if (mFileList) {
     mFileList->Clear();
 
-    const nsTArray<RefPtr<File>>& files = GetFilesInternal();
-    for (uint32_t i = 0; i < files.Length(); ++i) {
-      if (!mFileList->Append(files[i])) {
+    const nsTArray<OwningFileOrDirectory>& array =
+      GetFilesOrDirectoriesInternal();
+
+    for (uint32_t i = 0; i < array.Length(); ++i) {
+      if (array[i].IsFile() && !mFileList->Append(array[i].GetAsFile())) {
         return NS_ERROR_FAILURE;
       }
     }
   }
 
   // Make sure we (lazily) create a new Promise for GetFilesAndDirectories:
   mFilesAndDirectoriesPromise = nullptr;
 
@@ -3983,17 +4085,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;
@@ -4534,17 +4636,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;
@@ -4902,55 +5004,48 @@ HTMLInputElement::GetFilesAndDirectories
     return nullptr;
   }
 
   RefPtr<Promise> p = Promise::Create(global, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  const nsTArray<RefPtr<File>>& filesAndDirs = GetFilesInternal();
+  const nsTArray<OwningFileOrDirectory>& filesAndDirs =
+    GetFilesOrDirectoriesInternal();
 
   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
-      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);
+
+      RefPtr<Directory> directory = filesAndDirs[i].GetAsDirectory();
+
       // 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];
+      filesAndDirsSeq[i].SetAsFile() = filesAndDirs[i].GetAsFile();
     }
   }
 
   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:
@@ -5574,23 +5669,28 @@ 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<RefPtr<File>>& files = GetFilesInternal();
-
+    const nsTArray<OwningFileOrDirectory>& files =
+      GetFilesOrDirectoriesInternal();
+
+    bool hasBlobs = false;
     for (uint32_t i = 0; i < files.Length(); ++i) {
-      aFormSubmission->AddNameBlobOrNullPair(name, files[i]);
-    }
-
-    if (files.IsEmpty()) {
+      if (files[i].IsFile()) {
+        hasBlobs = true;
+        aFormSubmission->AddNameBlobOrNullPair(name, files[i].GetAsFile());
+      }
+    }
+
+    if (!hasBlobs) {
       aFormSubmission->AddNameBlobOrNullPair(name, nullptr);
     }
 
     return NS_OK;
   }
 
   if (mType == NS_FORM_INPUT_HIDDEN && name.EqualsLiteral("_charset_")) {
     nsCString charset;
@@ -5614,19 +5714,19 @@ HTMLInputElement::SaveState()
   switch (GetValueMode()) {
     case VALUE_MODE_DEFAULT_ON:
       if (mCheckedChanged) {
         inputState = new HTMLInputElementState();
         inputState->SetChecked(mChecked);
       }
       break;
     case VALUE_MODE_FILENAME:
-      if (!mFiles.IsEmpty()) {
+      if (!mFilesOrDirectories.IsEmpty()) {
         inputState = new HTMLInputElementState();
-        inputState->SetBlobImpls(mFiles);
+        inputState->SetFilesOrDirectories(mFilesOrDirectories);
       }
       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) ||
@@ -5785,17 +5885,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));
@@ -5822,30 +5922,23 @@ HTMLInputElement::RestoreState(nsPresSta
       case VALUE_MODE_DEFAULT_ON:
         if (inputState->IsCheckedSet()) {
           restoredCheckedState = true;
           DoSetChecked(inputState->GetChecked(), true, true);
         }
         break;
       case VALUE_MODE_FILENAME:
         {
-          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);
+          nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
+          if (window) {
+            nsTArray<OwningFileOrDirectory> array;
+            inputState->GetFilesOrDirectories(window, array);
+
+            SetFilesOrDirectories(array, true);
           }
-
-          SetFiles(files, true);
         }
         break;
       case VALUE_MODE_VALUE:
       case VALUE_MODE_DEFAULT:
         if (GetValueMode() == VALUE_MODE_DEFAULT &&
             mType != NS_FORM_INPUT_HIDDEN) {
           break;
         }
@@ -6345,25 +6438,25 @@ HTMLInputElement::IsValueMissing() const
 
   if (!IsMutable()) {
     return false;
   }
 
   switch (GetValueMode()) {
     case VALUE_MODE_VALUE:
       return IsValueEmpty();
+
     case VALUE_MODE_FILENAME:
-    {
-      const nsTArray<RefPtr<File>>& files = GetFilesInternal();
-      return files.IsEmpty();
-    }
+      return GetFilesOrDirectoriesInternal().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,16 +15,17 @@
 #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;
@@ -215,22 +216,23 @@ 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<RefPtr<File>>& GetFilesInternal() const
+  const nsTArray<OwningFileOrDirectory>& GetFilesOrDirectoriesInternal() const
   {
-    return mFiles;
+    return mFilesOrDirectories;
   }
 
-  void SetFiles(const nsTArray<RefPtr<File>>& aFiles, bool aSetValueChanged);
+  void SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories,
+                             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;
@@ -920,19 +922,19 @@ protected:
   nsresult MaybeSubmitForm(nsPresContext* aPresContext);
 
   /**
    * Update mFileList with the currently selected file.
    */
   nsresult UpdateFileList();
 
   /**
-   * Called after calling one of the SetFiles() functions.
+   * Called after calling one of the SetFilesOrDirectories() functions.
    */
-  void AfterSetFiles(bool aSetValueChanged);
+  void AfterSetFilesOrDirectories(bool aSetValueChanged);
 
   /**
    * Determine whether the editor needs to be initialized explicitly for
    * a particular event.
    */
   bool NeedToInitializeEditorForEvent(EventChainPreVisitor& aVisitor) const;
 
   /**
@@ -1261,26 +1263,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 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.
+   * 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.
    */
-  nsTArray<RefPtr<File>> mFiles;
+  nsTArray<OwningFileOrDirectory> mFilesOrDirectories;
 
 #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,40 +182,16 @@ 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,18 +439,17 @@ 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,
-                                     BlobDirState::eUnknownIfDir);
+                                     aSize, aLastModifiedDate);
   }
   return true;
 }
 
 bool
 ResolveMysteryBlob(BlobImpl* aImpl,
                    const nsString& aContentType,
                    uint64_t aSize)
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -665,18 +665,17 @@ public:
     : BlobImplBase(aContentType, 0)
   {
     mImmutable = true;
   }
 
   EmptyBlobImpl(const nsAString& aName,
                 const nsAString& aContentType,
                 int64_t aLastModifiedDate)
-    : BlobImplBase(aName, aContentType, 0, aLastModifiedDate,
-                   BlobDirState::eIsNotDir)
+    : BlobImplBase(aName, aContentType, 0, aLastModifiedDate)
   {
     mImmutable = true;
   }
 
 private:
   virtual already_AddRefed<BlobImpl>
   CreateSlice(uint64_t /* aStart */,
               uint64_t aLength,
@@ -711,24 +710,22 @@ 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);
   }
 
@@ -960,17 +957,16 @@ 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
@@ -1770,17 +1766,16 @@ 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);
@@ -2048,28 +2043,16 @@ 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;
@@ -2091,19 +2074,18 @@ 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, aDirState)
+  : BlobImplBase(aName, aContentType, aLength, aModDate)
   , mIsSlice(false)
 {
   if (aIsSameProcessBlob) {
     MOZ_ASSERT(aRemoteBlobImpl);
     mSameProcessBlobImpl = aRemoteBlobImpl;
     MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
   } else {
     mDifferentProcessBlobImpl = aRemoteBlobImpl;
@@ -2809,44 +2791,16 @@ 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*
@@ -3028,18 +2982,17 @@ 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, otherImpl->GetDirState(),
-                                    false /* SameProcessBlobImpl */);
+                                    modDate, 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.
@@ -3081,17 +3034,16 @@ 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 =
@@ -3117,17 +3069,16 @@ 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;
     }
@@ -3298,18 +3249,17 @@ 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,
-                                  aBlobImpl->GetDirState(), blobData);
+        FileBlobConstructorParams(name, contentType, length, modDate, blobData);
     } else {
       blobParams = NormalBlobConstructorParams(contentType, length, blobData);
     }
   }
 
   BlobChild* actor = new BlobChild(aManager, aBlobImpl);
 
   ParentBlobConstructorParams params(blobParams);
@@ -3472,31 +3422,29 @@ BlobChild::GetBlobImpl()
 
   return blobImpl.forget();
 }
 
 bool
 BlobChild::SetMysteryBlobInfo(const nsString& aName,
                               const nsString& aContentType,
                               uint64_t aLength,
-                              int64_t aLastModifiedDate,
-                              BlobDirState aDirState)
+                              int64_t aLastModifiedDate)
 {
   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();
@@ -3849,17 +3797,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,
-                                    aBlobImpl->GetDirState(), void_t());
+                                    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,18 +26,16 @@ 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;
 
@@ -112,18 +110,17 @@ public:
   already_AddRefed<BlobImpl>
   GetBlobImpl();
 
   // Use this for files.
   bool
   SetMysteryBlobInfo(const nsString& aName,
                      const nsString& aContentType,
                      uint64_t aLength,
-                     int64_t aLastModifiedDate,
-                     BlobDirState aDirState);
+                     int64_t aLastModifiedDate);
 
   // 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,17 +68,16 @@ 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,128 +48,178 @@ 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::FileSizeAndDateRunnable::FileSizeAndDateRunnable(FilePickerParent *aFPParent,
-                                                                   nsTArray<RefPtr<BlobImpl>>& aBlobs)
+FilePickerParent::IORunnable::IORunnable(FilePickerParent *aFPParent,
+                                         nsTArray<nsCOMPtr<nsIFile>>& aFiles,
+                                         bool aIsDirectory)
  : mFilePickerParent(aFPParent)
+ , mIsDirectory(aIsDirectory)
 {
-  mBlobs.SwapElements(aBlobs);
+  mFiles.SwapElements(aFiles);
+  MOZ_ASSERT_IF(aIsDirectory, mFiles.Length() == 1);
 }
 
 bool
-FilePickerParent::FileSizeAndDateRunnable::Dispatch()
+FilePickerParent::IORunnable::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::FileSizeAndDateRunnable::Run()
+FilePickerParent::IORunnable::Run()
 {
   // If we're on the main thread, then that means we're done. Just send the
   // results.
   if (NS_IsMainThread()) {
     if (mFilePickerParent) {
-      mFilePickerParent->SendFiles(mBlobs);
+      mFilePickerParent->SendFilesOrDirectories(mResults);
     }
     return NS_OK;
   }
 
-  // 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();
+  // 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;
   }
 
   // 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::FileSizeAndDateRunnable::Destroy()
+FilePickerParent::IORunnable::Destroy()
 {
   mFilePickerParent = nullptr;
 }
 
 void
-FilePickerParent::SendFiles(const nsTArray<RefPtr<BlobImpl>>& aBlobs)
+FilePickerParent::SendFilesOrDirectories(const nsTArray<BlobImplOrString>& aData)
 {
+  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 < aBlobs.Length(); i++) {
-    BlobParent* blobParent = parent->GetOrCreateActorForBlobImpl(aBlobs[i]);
+  for (unsigned i = 0; i < aData.Length(); i++) {
+    MOZ_ASSERT(aData[i].mType == BlobImplOrString::eBlobImpl);
+    BlobParent* blobParent = parent->GetOrCreateActorForBlobImpl(aData[i].mBlobImpl);
     if (blobParent) {
       blobs.AppendElement(blobParent);
     }
   }
 
-  InputFiles inblobs;
+  InputBlobs 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<RefPtr<BlobImpl>> blobs;
+  nsTArray<nsCOMPtr<nsIFile>> files;
   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);
-
-        RefPtr<BlobImpl> blobimpl = new BlobImplFile(file);
-        blobs.AppendElement(blobimpl);
+        MOZ_ASSERT(file);
+        files.AppendElement(file);
       }
     }
   } else {
     nsCOMPtr<nsIFile> file;
     mFilePicker->GetFile(getter_AddRefs(file));
     if (file) {
-      RefPtr<BlobImpl> blobimpl = new BlobImplFile(file);
-      blobs.AppendElement(blobimpl);
+      files.AppendElement(file);
     }
   }
 
+  if (files.IsEmpty()) {
+    Unused << Send__delete__(this, void_t(), mResult);
+    return;
+  }
+
   MOZ_ASSERT(!mRunnable);
-  mRunnable = new FileSizeAndDateRunnable(this, blobs);
+  mRunnable = new IORunnable(this, files, mMode == nsIFilePicker::modeGetFolder);
+
   // 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,32 +9,46 @@
 
 #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);
-  void SendFiles(const nsTArray<RefPtr<BlobImpl>>& aDomBlobs);
+
+  struct BlobImplOrString
+  {
+    RefPtr<BlobImpl> mBlobImpl;
+    nsString mDirectoryPath;
+
+    enum {
+      eBlobImpl,
+      eDirectoryPath
+    } mType;
+  };
+
+  void SendFilesOrDirectories(const nsTArray<BlobImplOrString>& aData);
 
   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;
@@ -56,31 +70,36 @@ class FilePickerParent : public PFilePic
   private:
     virtual ~FilePickerShownCallback() {}
     FilePickerParent* mFilePickerParent;
   };
 
  private:
   bool CreateFilePicker();
 
-  class FileSizeAndDateRunnable : public nsRunnable
+  // This runnable is used to do some I/O operation on a separate thread.
+  class IORunnable : public nsRunnable
   {
     FilePickerParent* mFilePickerParent;
-    nsTArray<RefPtr<BlobImpl>> mBlobs;
+    nsTArray<nsCOMPtr<nsIFile>> mFiles;
+    nsTArray<BlobImplOrString> mResults;
     nsCOMPtr<nsIEventTarget> mEventTarget;
+    bool mIsDirectory;
 
   public:
-    FileSizeAndDateRunnable(FilePickerParent *aFPParent,
-                            nsTArray<RefPtr<BlobImpl>>& aBlobs);
+    IORunnable(FilePickerParent *aFPParent,
+               nsTArray<nsCOMPtr<nsIFile>>& aFiles,
+               bool aIsDirectory);
+
     bool Dispatch();
     NS_IMETHOD Run();
     void Destroy();
   };
 
-  RefPtr<FileSizeAndDateRunnable> mRunnable;
+  RefPtr<IORunnable> 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,32 +284,34 @@ 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,34 +7,40 @@
 include protocol PBlob;
 include protocol PBrowser;
 
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 
 namespace mozilla {
 namespace dom {
 
-struct InputFiles
+struct InputBlobs
 {
   PBlob[] blobs;
 };
 
-union MaybeInputFiles
+struct InputDirectory
 {
-  InputFiles;
+  nsString directoryPath;
+};
+
+union MaybeInputData
+{
+  InputBlobs;
+  InputDirectory;
   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__(MaybeInputFiles files, int16_t result);
+    async __delete__(MaybeInputData data, int16_t result);
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/webidl/Directory.webidl
+++ b/dom/webidl/Directory.webidl
@@ -15,16 +15,17 @@
  * 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'.
@@ -100,21 +101,23 @@ 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/widget/nsBaseFilePicker.cpp
+++ b/widget/nsBaseFilePicker.cpp
@@ -12,29 +12,60 @@
 #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,
@@ -69,19 +100,19 @@ private:
   RefPtr<nsIFilePickerShownCallback> mCallback;
 };
 
 class nsBaseFilePickerEnumerator : public nsISimpleEnumerator
 {
 public:
   NS_DECL_ISUPPORTS
 
-  explicit nsBaseFilePickerEnumerator(nsPIDOMWindowOuter* aParent,
-                                      nsISimpleEnumerator* iterator,
-                                      int16_t aMode)
+  nsBaseFilePickerEnumerator(nsPIDOMWindowOuter* aParent,
+                             nsISimpleEnumerator* iterator,
+                             int16_t aMode)
     : mIterator(iterator)
     , mParent(aParent->GetCurrentInnerWindow())
     , mMode(aMode)
   {}
 
   NS_IMETHOD
   GetNext(nsISupports** aResult) override
   {
@@ -93,42 +124,20 @@ public:
       return NS_OK;
     }
 
     nsCOMPtr<nsIFile> localFile = do_QueryInterface(tmp);
     if (!localFile) {
       return NS_ERROR_FAILURE;
     }
 
-    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;
+    return LocalFileToDirectoryOrBlob(mParent,
+                                      mMode == nsIFilePicker::modeGetFolder,
+                                      localFile,
+                                      aResult);
   }
 
   NS_IMETHOD
   HasMoreElements(bool* aResult) override
   {
     return mIterator->HasMoreElements(aResult);
   }
 
@@ -344,20 +353,20 @@ nsBaseFilePicker::GetDomFileOrDirectory(
 
   if (!localFile) {
     *aValue = nullptr;
     return NS_OK;
   }
 
   auto* innerParent = mParent ? mParent->GetCurrentInnerWindow() : nullptr;
 
-  RefPtr<File> domFile = File::CreateFromFile(innerParent, localFile);
-  domFile->Impl()->SetIsDirectory(mMode == nsIFilePicker::modeGetFolder);
-  nsCOMPtr<nsIDOMBlob>(domFile).forget(aValue);
-  return NS_OK;
+  return LocalFileToDirectoryOrBlob(innerParent,
+                                    mMode == nsIFilePicker::modeGetFolder,
+                                    localFile,
+                                    aValue);
 }
 
 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,16 +2,17 @@
  *
  * 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)
 
@@ -137,37 +138,53 @@ nsFilePickerProxy::Open(nsIFilePickerSho
 
   SendOpen(mSelectedType, mAddToRecentDocs, mDefault, mDefaultExtension,
            mFilters, mFilterNames, displayDirectory);
 
   return NS_OK;
 }
 
 bool
-nsFilePickerProxy::Recv__delete__(const MaybeInputFiles& aFiles,
+nsFilePickerProxy::Recv__delete__(const MaybeInputData& aData,
                                   const int16_t& aResult)
 {
-  if (aFiles.type() == MaybeInputFiles::TInputFiles) {
-    const InfallibleTArray<PBlobChild*>& blobs = aFiles.get_InputFiles().blobsChild();
+  if (aData.type() == MaybeInputData::TInputBlobs) {
+    const InfallibleTArray<PBlobChild*>& blobs = aData.get_InputBlobs().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);
 
-      mFilesOrDirectories.AppendElement(file);
+      OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
+      element->SetAsFile() = 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;
@@ -177,62 +194,81 @@ NS_IMETHODIMP
 nsFilePickerProxy::GetDomFileOrDirectory(nsISupports** aValue)
 {
   *aValue = nullptr;
   if (mFilesOrDirectories.IsEmpty()) {
     return NS_OK;
   }
 
   MOZ_ASSERT(mFilesOrDirectories.Length() == 1);
-  nsCOMPtr<nsIDOMBlob> blob = mFilesOrDirectories[0].get();
-  blob.forget(aValue);
+
+  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);
   return NS_OK;
 }
 
 namespace {
 
 class SimpleEnumerator final : public nsISimpleEnumerator
 {
 public:
   NS_DECL_ISUPPORTS
 
-  explicit SimpleEnumerator(const nsTArray<RefPtr<File>>& aFiles)
-    : mFiles(aFiles)
+  explicit
+  SimpleEnumerator(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories)
+    : mFilesOrDirectories(aFilesOrDirectories)
     , mIndex(0)
   {}
 
   NS_IMETHOD
   HasMoreElements(bool* aRetvalue) override
   {
     MOZ_ASSERT(aRetvalue);
-    *aRetvalue = mIndex < mFiles.Length();
+    *aRetvalue = mIndex < mFilesOrDirectories.Length();
     return NS_OK;
   }
 
   NS_IMETHOD
-  GetNext(nsISupports** aSupports) override
+  GetNext(nsISupports** aValue) override
   {
-    NS_ENSURE_TRUE(mIndex < mFiles.Length(), NS_ERROR_FAILURE);
+    NS_ENSURE_TRUE(mIndex < mFilesOrDirectories.Length(), NS_ERROR_FAILURE);
+
+    uint32_t index = mIndex++;
 
-    nsCOMPtr<nsIDOMBlob> blob = mFiles[mIndex++].get();
-    blob.forget(aSupports);
+    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);
     return NS_OK;
   }
 
 private:
   ~SimpleEnumerator()
   {}
 
-  nsTArray<RefPtr<File>> mFiles;
+  nsTArray<mozilla::dom::OwningFileOrDirectory> mFilesOrDirectories;
   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,27 +8,22 @@
 
 #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
@@ -54,23 +49,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 MaybeInputFiles& aFiles, const int16_t& aResult) override;
+    Recv__delete__(const MaybeInputData& aData, const int16_t& aResult) override;
 
 private:
     ~nsFilePickerProxy();
     void InitNative(nsIWidget*, const nsAString&) override;
 
-    nsTArray<RefPtr<mozilla::dom::File>> mFilesOrDirectories;
+    nsTArray<mozilla::dom::OwningFileOrDirectory> mFilesOrDirectories;
     nsCOMPtr<nsIFilePickerShownCallback> mCallback;
 
     int16_t   mSelectedType;
     nsString  mFile;
     nsString  mDefault;
     nsString  mDefaultExtension;
 
     InfallibleTArray<nsString> mFilters;