Bug 1265767 - Subset of Blink FileSystem API - patch 5 - DOMFileSystem, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Tue, 07 Jun 2016 00:55:17 +0200
changeset 300833 56fe0bfb503ce68e0bf0c058ecd32126ec8e2438
parent 300832 d22217515a1e6a08dc023d75361853c84d0275c8
child 300834 8ff81864d0021cfd072b3eca5c3225085a90926b
push id19599
push usercbook@mozilla.com
push dateWed, 08 Jun 2016 10:16:21 +0000
treeherderfx-team@81f4cc3f6f4c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1265767
milestone50.0a1
Bug 1265767 - Subset of Blink FileSystem API - patch 5 - DOMFileSystem, r=smaug
dom/filesystem/compat/CallbackRunnables.h
dom/filesystem/compat/DOMFileSystem.cpp
dom/filesystem/compat/DOMFileSystem.h
dom/filesystem/compat/DirectoryEntry.cpp
dom/filesystem/compat/DirectoryEntry.h
dom/filesystem/compat/DirectoryReader.cpp
dom/filesystem/compat/DirectoryReader.h
dom/filesystem/compat/Entry.cpp
dom/filesystem/compat/Entry.h
dom/filesystem/compat/ErrorCallbackRunnable.h
dom/filesystem/compat/FileEntry.cpp
dom/filesystem/compat/FileEntry.h
dom/filesystem/compat/RootDirectoryEntry.cpp
dom/filesystem/compat/RootDirectoryEntry.h
dom/filesystem/compat/RootDirectoryReader.cpp
dom/filesystem/compat/RootDirectoryReader.h
dom/filesystem/compat/moz.build
dom/filesystem/compat/tests/test_basic.html
dom/html/HTMLInputElement.cpp
dom/webidl/DOMFileSystem.webidl
rename from dom/filesystem/compat/ErrorCallbackRunnable.h
rename to dom/filesystem/compat/CallbackRunnables.h
--- a/dom/filesystem/compat/ErrorCallbackRunnable.h
+++ b/dom/filesystem/compat/CallbackRunnables.h
@@ -11,19 +11,19 @@
 #include "nsPIDOMWindow.h"
 
 namespace mozilla {
 namespace dom {
 
 class ErrorCallbackRunnable final : public Runnable
 {
 public:
-  explicit ErrorCallbackRunnable(nsIGlobalObject* aGlobalObject,
-                                 ErrorCallback* aCallback,
-                                 nsresult aError = NS_ERROR_DOM_NOT_SUPPORTED_ERR)
+  ErrorCallbackRunnable(nsIGlobalObject* aGlobalObject,
+                        ErrorCallback* aCallback,
+                        nsresult aError = NS_ERROR_DOM_NOT_SUPPORTED_ERR)
     : mGlobal(aGlobalObject)
     , mCallback(aCallback)
     , mError(aError)
   {
     MOZ_ASSERT(aGlobalObject);
     MOZ_ASSERT(aCallback);
     MOZ_ASSERT(NS_FAILED(aError));
   }
@@ -42,12 +42,33 @@ public:
   }
 
 private:
   nsCOMPtr<nsIGlobalObject> mGlobal;
   RefPtr<ErrorCallback> mCallback;
   nsresult mError;
 };
 
+class EmptyEntriesCallbackRunnable final : public Runnable
+{
+public:
+  explicit EmptyEntriesCallbackRunnable(EntriesCallback* aCallback)
+    : mCallback(aCallback)
+  {
+    MOZ_ASSERT(aCallback);
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    Sequence<OwningNonNull<Entry>> sequence;
+    mCallback->HandleEvent(sequence);
+    return NS_OK;
+  }
+
+private:
+  RefPtr<EntriesCallback> mCallback;
+};
+
 } // dom namespace
 } // mozilla namespace
 
-#endif // mozilla_dom_ErrorCallbackRunnable_h
+#endif // mozilla_dom_CallbackRunnables_h
--- a/dom/filesystem/compat/DOMFileSystem.cpp
+++ b/dom/filesystem/compat/DOMFileSystem.cpp
@@ -1,37 +1,76 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMFileSystem.h"
+#include "RootDirectoryEntry.h"
 #include "mozilla/dom/DOMFileSystemBinding.h"
+#include "nsContentUtils.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMFileSystem, mParent)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMFileSystem, mParent, mRoot)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMFileSystem)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMFileSystem)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMFileSystem)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-DOMFileSystem::DOMFileSystem(nsIGlobalObject* aGlobal)
+/* static */ already_AddRefed<DOMFileSystem>
+DOMFileSystem::Create(nsIGlobalObject* aGlobalObject)
+
+{
+  MOZ_ASSERT(aGlobalObject);
+
+
+  nsID id;
+  nsresult rv = nsContentUtils::GenerateUUIDInPlace(id);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
+
+  char chars[NSID_LENGTH];
+  id.ToProvidedString(chars);
+
+  // Any fileSystem has an unique ID. We use UUID, but our generator produces
+  // UUID in this format '{' + UUID + '}'. We remove them with these +1 and -2.
+  nsAutoCString name(Substring(chars + 1, chars + NSID_LENGTH - 2));
+
+  RefPtr<DOMFileSystem> fs =
+    new DOMFileSystem(aGlobalObject, NS_ConvertUTF8toUTF16(name));
+
+  return fs.forget();
+}
+
+DOMFileSystem::DOMFileSystem(nsIGlobalObject* aGlobal,
+                             const nsAString& aName)
   : mParent(aGlobal)
-{}
+  , mName(aName)
+{
+  MOZ_ASSERT(aGlobal);
+}
 
 DOMFileSystem::~DOMFileSystem()
 {}
 
 JSObject*
 DOMFileSystem::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return DOMFileSystemBinding::Wrap(aCx, this, aGivenProto);
 }
 
+void
+DOMFileSystem::CreateRoot(const Sequence<RefPtr<Entry>>& aEntries)
+{
+  MOZ_ASSERT(!mRoot);
+  mRoot = new RootDirectoryEntry(mParent, aEntries, this);
+}
+
 } // dom namespace
 } // mozilla namespace
--- a/dom/filesystem/compat/DOMFileSystem.h
+++ b/dom/filesystem/compat/DOMFileSystem.h
@@ -14,51 +14,60 @@
 #include "nsWrapperCache.h"
 
 class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
 class DirectoryEntry;
+class Entry;
+class OwningFileOrDirectory;
 
 class DOMFileSystem final
   : public nsISupports
   , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMFileSystem)
 
-  explicit DOMFileSystem(nsIGlobalObject* aGlobalObject);
+  static already_AddRefed<DOMFileSystem>
+  Create(nsIGlobalObject* aGlobalObject);
 
   nsIGlobalObject*
   GetParentObject() const
   {
     return mParent;
   }
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void
-  GetName(nsAString& aName, ErrorResult& aRv) const
+  GetName(nsAString& aName) const
   {
-    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+    aName = mName;
   }
 
-  already_AddRefed<DirectoryEntry>
-  GetRoot(ErrorResult& aRv) const
+  DirectoryEntry*
+  Root() const
   {
-    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-    return nullptr;
+    return mRoot;
   }
 
+  void
+  CreateRoot(const Sequence<RefPtr<Entry>>& aEntries);
+
 private:
+  explicit DOMFileSystem(nsIGlobalObject* aGlobalObject,
+                         const nsAString& aName);
   ~DOMFileSystem();
 
   nsCOMPtr<nsIGlobalObject> mParent;
+  RefPtr<DirectoryEntry> mRoot;
+  nsString mName;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_DOMFileSystem_h
--- a/dom/filesystem/compat/DirectoryEntry.cpp
+++ b/dom/filesystem/compat/DirectoryEntry.cpp
@@ -1,65 +1,69 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DirectoryEntry.h"
+#include "CallbackRunnables.h"
 #include "DirectoryReader.h"
-#include "ErrorCallbackRunnable.h"
 #include "mozilla/dom/Directory.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(DirectoryEntry, Entry, mDirectory)
 
 NS_IMPL_ADDREF_INHERITED(DirectoryEntry, Entry)
 NS_IMPL_RELEASE_INHERITED(DirectoryEntry, Entry)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DirectoryEntry)
 NS_INTERFACE_MAP_END_INHERITING(Entry)
 
 DirectoryEntry::DirectoryEntry(nsIGlobalObject* aGlobal,
-                               Directory* aDirectory)
-  : Entry(aGlobal)
+                               Directory* aDirectory,
+                               DOMFileSystem* aFileSystem)
+  : Entry(aGlobal, aFileSystem)
   , mDirectory(aDirectory)
 {
   MOZ_ASSERT(aGlobal);
-  MOZ_ASSERT(mDirectory);
 }
 
 DirectoryEntry::~DirectoryEntry()
 {}
 
 JSObject*
 DirectoryEntry::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return DirectoryEntryBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 DirectoryEntry::GetName(nsAString& aName, ErrorResult& aRv) const
 {
+  MOZ_ASSERT(mDirectory);
   mDirectory->GetName(aName, aRv);
 }
 
 void
 DirectoryEntry::GetFullPath(nsAString& aPath, ErrorResult& aRv) const
 {
+  MOZ_ASSERT(mDirectory);
   mDirectory->GetPath(aPath, aRv);
 }
 
 already_AddRefed<DirectoryReader>
 DirectoryEntry::CreateReader() const
 {
+  MOZ_ASSERT(mDirectory);
+
   RefPtr<DirectoryReader> reader =
-    new DirectoryReader(GetParentObject(), mDirectory);
+    new DirectoryReader(GetParentObject(), Filesystem(), mDirectory);
   return reader.forget();
 }
 
 void
 DirectoryEntry::RemoveRecursively(VoidCallback& aSuccessCallback,
                                   const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback) const
 {
   if (aErrorCallback.WasPassed()) {
--- a/dom/filesystem/compat/DirectoryEntry.h
+++ b/dom/filesystem/compat/DirectoryEntry.h
@@ -10,40 +10,41 @@
 #include "mozilla/dom/Entry.h"
 #include "mozilla/dom/DOMFileSystemBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 class Directory;
 
-class DirectoryEntry final : public Entry
+class DirectoryEntry : public Entry
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DirectoryEntry, Entry)
 
-  DirectoryEntry(nsIGlobalObject* aGlobalObject, Directory* aDirectory);
+  DirectoryEntry(nsIGlobalObject* aGlobalObject, Directory* aDirectory,
+                 DOMFileSystem* aFileSystem);
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   virtual bool
   IsDirectory() const override
   {
     return true;
   }
 
   virtual void
   GetName(nsAString& aName, ErrorResult& aRv) const override;
 
   virtual void
   GetFullPath(nsAString& aFullPath, ErrorResult& aRv) const override;
 
-  already_AddRefed<DirectoryReader>
+  virtual already_AddRefed<DirectoryReader>
   CreateReader() const;
 
   void
   GetFile(const nsAString& aPath, const FileSystemFlags& aFlag,
           const Optional<OwningNonNull<EntryCallback>>& aSuccessCallback,
           const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback,
           ErrorResult& aRv) const
   {
@@ -58,18 +59,19 @@ public:
   {
     aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
   }
 
   void
   RemoveRecursively(VoidCallback& aSuccessCallback,
                     const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback) const;
 
+protected:
+  virtual ~DirectoryEntry();
+
 private:
-  ~DirectoryEntry();
-
   RefPtr<Directory> mDirectory;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_DirectoryEntry_h
--- a/dom/filesystem/compat/DirectoryReader.cpp
+++ b/dom/filesystem/compat/DirectoryReader.cpp
@@ -1,62 +1,45 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DirectoryReader.h"
+#include "CallbackRunnables.h"
 #include "FileEntry.h"
 #include "mozilla/dom/FileBinding.h"
 #include "mozilla/dom/Directory.h"
 #include "mozilla/dom/DirectoryBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "nsIGlobalObject.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
-class EmptyEntriesCallbackRunnable final : public Runnable
-{
-public:
-  EmptyEntriesCallbackRunnable(EntriesCallback* aCallback)
-    : mCallback(aCallback)
-  {
-    MOZ_ASSERT(aCallback);
-  }
-
-  NS_IMETHOD
-  Run() override
-  {
-    Sequence<OwningNonNull<Entry>> sequence;
-    mCallback->HandleEvent(sequence);
-    return NS_OK;
-  }
-
-private:
-  RefPtr<EntriesCallback> mCallback;
-};
-
 class PromiseHandler final : public PromiseNativeHandler
 {
 public:
   NS_DECL_ISUPPORTS
 
   PromiseHandler(nsIGlobalObject* aGlobalObject,
+                 DOMFileSystem* aFileSystem,
                  EntriesCallback* aSuccessCallback,
                  ErrorCallback* aErrorCallback)
     : mGlobal(aGlobalObject)
+    , mFileSystem(aFileSystem)
     , mSuccessCallback(aSuccessCallback)
     , mErrorCallback(aErrorCallback)
   {
     MOZ_ASSERT(aGlobalObject);
+    MOZ_ASSERT(aFileSystem);
     MOZ_ASSERT(aSuccessCallback);
   }
 
   virtual void
   ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
   {
     if(NS_WARN_IF(!aValue.isObject())) {
       return;
@@ -83,28 +66,29 @@ public:
       if(NS_WARN_IF(!value.isObject())) {
         return;
       }
 
       JS::Rooted<JSObject*> valueObj(aCx, &value.toObject());
 
       RefPtr<File> file;
       if (NS_SUCCEEDED(UNWRAP_OBJECT(File, valueObj, file))) {
-        RefPtr<FileEntry> entry = new FileEntry(mGlobal, file);
+        RefPtr<FileEntry> entry = new FileEntry(mGlobal, file, mFileSystem);
         sequence[i] = entry;
         continue;
       }
 
       RefPtr<Directory> directory;
       if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Directory, valueObj,
                                              directory)))) {
         return;
       }
 
-      RefPtr<DirectoryEntry> entry = new DirectoryEntry(mGlobal, directory);
+      RefPtr<DirectoryEntry> entry =
+        new DirectoryEntry(mGlobal, directory, mFileSystem);
       sequence[i] = entry;
     }
 
     mSuccessCallback->HandleEvent(sequence);
   }
 
   virtual void
   RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
@@ -117,58 +101,64 @@ public:
       NS_WARN_IF(NS_FAILED(rv));
     }
   }
 
 private:
   ~PromiseHandler() {}
 
   nsCOMPtr<nsIGlobalObject> mGlobal;
+  RefPtr<DOMFileSystem> mFileSystem;
   RefPtr<EntriesCallback> mSuccessCallback;
   RefPtr<ErrorCallback> mErrorCallback;
 };
 
 NS_IMPL_ISUPPORTS0(PromiseHandler);
 
 } // anonymous namespace
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DirectoryReader, mParent, mDirectory)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DirectoryReader, mParent, mDirectory,
+                                      mFileSystem)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(DirectoryReader)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DirectoryReader)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DirectoryReader)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 DirectoryReader::DirectoryReader(nsIGlobalObject* aGlobal,
+                                 DOMFileSystem* aFileSystem,
                                  Directory* aDirectory)
   : mParent(aGlobal)
+  , mFileSystem(aFileSystem)
   , mDirectory(aDirectory)
   , mAlreadyRead(false)
 {
   MOZ_ASSERT(aGlobal);
-  MOZ_ASSERT(aDirectory);
+  MOZ_ASSERT(aFileSystem);
 }
 
 DirectoryReader::~DirectoryReader()
 {}
 
 JSObject*
 DirectoryReader::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return DirectoryReaderBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 DirectoryReader::ReadEntries(EntriesCallback& aSuccessCallback,
                              const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback,
                              ErrorResult& aRv)
 {
+  MOZ_ASSERT(mDirectory);
+
   if (mAlreadyRead) {
     RefPtr<EmptyEntriesCallbackRunnable> runnable =
       new EmptyEntriesCallbackRunnable(&aSuccessCallback);
     aRv = NS_DispatchToMainThread(runnable);
     NS_WARN_IF(aRv.Failed());
     return;
   }
 
@@ -186,16 +176,16 @@ DirectoryReader::ReadEntries(EntriesCall
       aRv = NS_DispatchToMainThread(runnable);
       NS_WARN_IF(aRv.Failed());
     }
 
     return;
   }
 
   RefPtr<PromiseHandler> handler =
-    new PromiseHandler(GetParentObject(), &aSuccessCallback,
+    new PromiseHandler(GetParentObject(), mFileSystem, &aSuccessCallback,
                        aErrorCallback.WasPassed()
                          ? &aErrorCallback.Value() : nullptr);
   promise->AppendNativeHandler(handler);
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/filesystem/compat/DirectoryReader.h
+++ b/dom/filesystem/compat/DirectoryReader.h
@@ -14,46 +14,50 @@
 #include "nsWrapperCache.h"
 
 class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
 class Directory;
+class DOMFileSystem;
 
-class DirectoryReader final
+class DirectoryReader
   : public nsISupports
   , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DirectoryReader)
 
   explicit DirectoryReader(nsIGlobalObject* aGlobalObject,
+                           DOMFileSystem* aFileSystem,
                            Directory* aDirectory);
 
   nsIGlobalObject*
   GetParentObject() const
   {
     return mParent;
   }
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  void
+  virtual void
   ReadEntries(EntriesCallback& aSuccessCallback,
               const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback,
               ErrorResult& aRv);
 
+protected:
+  virtual ~DirectoryReader();
+
 private:
-  ~DirectoryReader();
-
   nsCOMPtr<nsIGlobalObject> mParent;
+  RefPtr<DOMFileSystem> mFileSystem;
   RefPtr<Directory> mDirectory;
 
   bool mAlreadyRead;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/filesystem/compat/Entry.cpp
+++ b/dom/filesystem/compat/Entry.cpp
@@ -7,48 +7,55 @@
 #include "Entry.h"
 #include "DirectoryEntry.h"
 #include "FileEntry.h"
 #include "mozilla/dom/UnionTypes.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Entry, mParent)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Entry, mParent, mFileSystem)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Entry)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Entry)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Entry)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 /* static */ already_AddRefed<Entry>
 Entry::Create(nsIGlobalObject* aGlobalObject,
-              const OwningFileOrDirectory& aFileOrDirectory)
+              const OwningFileOrDirectory& aFileOrDirectory,
+              DOMFileSystem* aFileSystem)
 {
   MOZ_ASSERT(aGlobalObject);
+  MOZ_ASSERT(aFileSystem);
 
   RefPtr<Entry> entry;
   if (aFileOrDirectory.IsFile()) {
-    entry = new FileEntry(aGlobalObject, aFileOrDirectory.GetAsFile());
+    entry = new FileEntry(aGlobalObject,
+                          aFileOrDirectory.GetAsFile(),
+                          aFileSystem);
   } else {
     MOZ_ASSERT(aFileOrDirectory.IsDirectory());
     entry = new DirectoryEntry(aGlobalObject,
-                               aFileOrDirectory.GetAsDirectory());
+                               aFileOrDirectory.GetAsDirectory(),
+                               aFileSystem);
   }
 
   return entry.forget();
 }
 
-Entry::Entry(nsIGlobalObject* aGlobal)
+Entry::Entry(nsIGlobalObject* aGlobal, DOMFileSystem* aFileSystem)
   : mParent(aGlobal)
+  , mFileSystem(aFileSystem)
 {
   MOZ_ASSERT(aGlobal);
+  MOZ_ASSERT(aFileSystem);
 }
 
 Entry::~Entry()
 {}
 
 JSObject*
 Entry::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
--- a/dom/filesystem/compat/Entry.h
+++ b/dom/filesystem/compat/Entry.h
@@ -24,17 +24,18 @@ class Entry
   , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Entry)
 
   static already_AddRefed<Entry>
   Create(nsIGlobalObject* aGlobalObject,
-         const OwningFileOrDirectory& aFileOrDirectory);
+         const OwningFileOrDirectory& aFileOrDirectory,
+         DOMFileSystem* aFileSystem);
 
   nsIGlobalObject*
   GetParentObject() const
   {
     return mParent;
   }
 
   virtual JSObject*
@@ -54,26 +55,27 @@ public:
 
   virtual void
   GetName(nsAString& aName, ErrorResult& aRv) const = 0;
 
   virtual void
   GetFullPath(nsAString& aFullPath, ErrorResult& aRv) const = 0;
 
   DOMFileSystem*
-  GetFilesystem(ErrorResult& aRv) const
+  Filesystem() const
   {
-    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-    return nullptr;
+    return mFileSystem;
   }
 
 protected:
-  Entry(nsIGlobalObject* aGlobalObject);
+  Entry(nsIGlobalObject* aGlobalObject,
+        DOMFileSystem* aFileSystem);
   virtual ~Entry();
 
 private:
   nsCOMPtr<nsIGlobalObject> mParent;
+  RefPtr<DOMFileSystem> mFileSystem;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Entry_h
--- a/dom/filesystem/compat/FileEntry.cpp
+++ b/dom/filesystem/compat/FileEntry.cpp
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FileEntry.h"
-#include "ErrorCallbackRunnable.h"
+#include "CallbackRunnables.h"
 #include "mozilla/dom/File.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 class BlobCallbackRunnable final : public Runnable
@@ -42,18 +42,19 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(FileE
 
 NS_IMPL_ADDREF_INHERITED(FileEntry, Entry)
 NS_IMPL_RELEASE_INHERITED(FileEntry, Entry)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileEntry)
 NS_INTERFACE_MAP_END_INHERITING(Entry)
 
 FileEntry::FileEntry(nsIGlobalObject* aGlobal,
-                     File* aFile)
-  : Entry(aGlobal)
+                     File* aFile,
+                     DOMFileSystem* aFileSystem)
+  : Entry(aGlobal, aFileSystem)
   , mFile(aFile)
 {
   MOZ_ASSERT(aGlobal);
   MOZ_ASSERT(mFile);
 }
 
 FileEntry::~FileEntry()
 {}
--- a/dom/filesystem/compat/FileEntry.h
+++ b/dom/filesystem/compat/FileEntry.h
@@ -15,17 +15,18 @@ namespace dom {
 class File;
 
 class FileEntry final : public Entry
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileEntry, Entry)
 
-  FileEntry(nsIGlobalObject* aGlobalObject, File* aFile);
+  FileEntry(nsIGlobalObject* aGlobalObject, File* aFile,
+            DOMFileSystem* aFileSystem);
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   virtual bool
   IsFile() const override
   {
     return true;
new file mode 100644
--- /dev/null
+++ b/dom/filesystem/compat/RootDirectoryEntry.cpp
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RootDirectoryEntry.h"
+#include "RootDirectoryReader.h"
+#include "mozilla/dom/FileSystemUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(RootDirectoryEntry, DirectoryEntry, mEntries)
+
+NS_IMPL_ADDREF_INHERITED(RootDirectoryEntry, DirectoryEntry)
+NS_IMPL_RELEASE_INHERITED(RootDirectoryEntry, DirectoryEntry)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(RootDirectoryEntry)
+NS_INTERFACE_MAP_END_INHERITING(DirectoryEntry)
+
+RootDirectoryEntry::RootDirectoryEntry(nsIGlobalObject* aGlobal,
+                                       const Sequence<RefPtr<Entry>>& aEntries,
+                                       DOMFileSystem* aFileSystem)
+  : DirectoryEntry(aGlobal, nullptr, aFileSystem)
+  , mEntries(aEntries)
+{
+  MOZ_ASSERT(aGlobal);
+}
+
+RootDirectoryEntry::~RootDirectoryEntry()
+{}
+
+void
+RootDirectoryEntry::GetName(nsAString& aName, ErrorResult& aRv) const
+{
+  aName.Truncate();
+}
+
+void
+RootDirectoryEntry::GetFullPath(nsAString& aPath, ErrorResult& aRv) const
+{
+  aPath.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
+}
+
+already_AddRefed<DirectoryReader>
+RootDirectoryEntry::CreateReader() const
+{
+  RefPtr<DirectoryReader> reader =
+    new RootDirectoryReader(GetParentObject(), Filesystem(), mEntries);
+  return reader.forget();
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/filesystem/compat/RootDirectoryEntry.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_RootDirectoryEntry_h
+#define mozilla_dom_RootDirectoryEntry_h
+
+#include "mozilla/dom/DirectoryEntry.h"
+
+namespace mozilla {
+namespace dom {
+
+class RootDirectoryEntry final : public DirectoryEntry
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(RootDirectoryEntry, DirectoryEntry)
+
+  RootDirectoryEntry(nsIGlobalObject* aGlobalObject,
+                     const Sequence<RefPtr<Entry>>& aEntries,
+                     DOMFileSystem* aFileSystem);
+
+  virtual void
+  GetName(nsAString& aName, ErrorResult& aRv) const override;
+
+  virtual void
+  GetFullPath(nsAString& aFullPath, ErrorResult& aRv) const override;
+
+  virtual already_AddRefed<DirectoryReader>
+  CreateReader() const override;
+
+private:
+  ~RootDirectoryEntry();
+
+  Sequence<RefPtr<Entry>> mEntries;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_RootDirectoryEntry_h
new file mode 100644
--- /dev/null
+++ b/dom/filesystem/compat/RootDirectoryReader.cpp
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RootDirectoryReader.h"
+#include "CallbackRunnables.h"
+#include "nsIGlobalObject.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+
+class EntriesCallbackRunnable final : public Runnable
+{
+public:
+  EntriesCallbackRunnable(EntriesCallback* aCallback,
+                          const Sequence<RefPtr<Entry>>& aEntries)
+    : mCallback(aCallback)
+    , mEntries(aEntries)
+  {
+    MOZ_ASSERT(aCallback);
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    Sequence<OwningNonNull<Entry>> entries;
+    for (uint32_t i = 0; i < mEntries.Length(); ++i) {
+      if (!entries.AppendElement(mEntries[i].forget(), fallible)) {
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
+    }
+
+    mCallback->HandleEvent(entries);
+    return NS_OK;
+  }
+
+private:
+  RefPtr<EntriesCallback> mCallback;
+  Sequence<RefPtr<Entry>> mEntries;
+};
+
+} // anonymous namespace
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(RootDirectoryReader, DirectoryReader, mEntries)
+
+NS_IMPL_ADDREF_INHERITED(RootDirectoryReader, DirectoryReader)
+NS_IMPL_RELEASE_INHERITED(RootDirectoryReader, DirectoryReader)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(RootDirectoryReader)
+NS_INTERFACE_MAP_END_INHERITING(DirectoryReader)
+
+RootDirectoryReader::RootDirectoryReader(nsIGlobalObject* aGlobal,
+                                         DOMFileSystem* aFileSystem,
+                                         const Sequence<RefPtr<Entry>>& aEntries)
+  : DirectoryReader(aGlobal, aFileSystem, nullptr)
+  , mEntries(aEntries)
+  , mAlreadyRead(false)
+{
+  MOZ_ASSERT(aGlobal);
+  MOZ_ASSERT(aFileSystem);
+}
+
+RootDirectoryReader::~RootDirectoryReader()
+{}
+
+void
+RootDirectoryReader::ReadEntries(EntriesCallback& aSuccessCallback,
+                                 const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback,
+                                 ErrorResult& aRv)
+{
+  if (mAlreadyRead) {
+    RefPtr<EmptyEntriesCallbackRunnable> runnable =
+      new EmptyEntriesCallbackRunnable(&aSuccessCallback);
+    aRv = NS_DispatchToMainThread(runnable);
+    NS_WARN_IF(aRv.Failed());
+    return;
+  }
+
+  // This object can be used only once.
+  mAlreadyRead = true;
+
+  RefPtr<EntriesCallbackRunnable> runnable =
+    new EntriesCallbackRunnable(&aSuccessCallback, mEntries);
+  aRv = NS_DispatchToMainThread(runnable);
+  NS_WARN_IF(aRv.Failed());
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/filesystem/compat/RootDirectoryReader.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_RootDirectoryReader_h
+#define mozilla_dom_RootDirectoryReader_h
+
+#include "DirectoryReader.h"
+
+namespace mozilla {
+namespace dom {
+
+class RootDirectoryReader final : public DirectoryReader
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(RootDirectoryReader, DirectoryReader)
+
+  explicit RootDirectoryReader(nsIGlobalObject* aGlobalObject,
+                               DOMFileSystem* aFileSystem,
+                               const Sequence<RefPtr<Entry>>& aEntries);
+
+  virtual void
+  ReadEntries(EntriesCallback& aSuccessCallback,
+              const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback,
+              ErrorResult& aRv) override;
+
+private:
+  ~RootDirectoryReader();
+
+  Sequence<RefPtr<Entry>> mEntries;
+  bool mAlreadyRead;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_RootDirectoryReader_h
--- a/dom/filesystem/compat/moz.build
+++ b/dom/filesystem/compat/moz.build
@@ -15,13 +15,15 @@ EXPORTS.mozilla.dom += [
 ]
 
 UNIFIED_SOURCES += [
     'DirectoryEntry.cpp',
     'DirectoryReader.cpp',
     'DOMFileSystem.cpp',
     'Entry.cpp',
     'FileEntry.cpp',
+    'RootDirectoryEntry.cpp',
+    'RootDirectoryReader.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 include('/ipc/chromium/chromium-config.mozbuild')
--- a/dom/filesystem/compat/tests/test_basic.html
+++ b/dom/filesystem/compat/tests/test_basic.html
@@ -111,28 +111,50 @@ function test_directoryEntry_createReade
       ok(false, "Something when wrong!");
     });
 
   }, function() {
     ok(false, "Something when wrong!");
   });
 }
 
+function test_filesystem() {
+  is(fileEntry.filesystem, directoryEntry.filesystem, "FileSystem object is shared.");
+
+  var fs = fileEntry.filesystem;
+  ok(fs.name, "FileSystem.name exists.");
+  ok(fs.root, "FileSystem has a root.");
+
+  is(fs.root.name, "", "FileSystem.root.name must be an empty string.");
+  is(fs.root.fullPath, "/", "FileSystem.root.fullPath must be '/'");
+
+  reader = fs.root.createReader();
+  reader.readEntries(function(a) {
+    ok(Array.isArray(a), "We want an array.");
+    is(a.length, 2, "reader.readyEntries returns 2 elements.");
+    next();
+  }, function() {
+    ok(false, "Something when wrong!");
+  });
+}
+
 var tests = [
   setup_tests,
   populate_entries,
 
   test_entries,
 
   test_fileEntry,
   test_fileEntry_file,
   test_fileEntry_createWriter,
 
   test_directoryEntry,
   test_directoryEntry_createReader,
+
+  test_filesystem,
 ];
 
 function next() {
   if (!tests.length) {
     SimpleTest.finish();
     return;
   }
 
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -75,16 +75,17 @@
 #include "nsRuleData.h"
 #include <algorithm>
 
 // input type=radio
 #include "nsIRadioGroupContainer.h"
 
 // input type=file
 #include "mozilla/dom/Entry.h"
+#include "mozilla/dom/DOMFileSystem.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileList.h"
 #include "nsIFile.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsIContentPrefService.h"
 #include "nsIMIMEService.h"
@@ -8199,24 +8200,36 @@ HTMLInputElement::ExploreDirectoryRecurs
 void
 HTMLInputElement::UpdateEntries(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories)
 {
   mEntries.Clear();
 
   nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
   MOZ_ASSERT(global);
 
+  RefPtr<DOMFileSystem> fs = DOMFileSystem::Create(global);
+  if (NS_WARN_IF(!fs)) {
+    return;
+  }
+
+  Sequence<RefPtr<Entry>> entries;
   for (uint32_t i = 0; i < aFilesOrDirectories.Length(); ++i) {
-    RefPtr<Entry> entry = Entry::Create(global, aFilesOrDirectories[i]);
+    RefPtr<Entry> entry = Entry::Create(global, aFilesOrDirectories[i], fs);
     MOZ_ASSERT(entry);
 
-    if (!mEntries.AppendElement(entry, fallible)) {
+    if (!entries.AppendElement(entry, fallible)) {
       return;
     }
   }
+
+  // The root fileSystem is a DirectoryEntry object that contains only the
+  // dropped fileEntry and directoryEntry objects.
+  fs->CreateRoot(entries);
+
+  mEntries.SwapElements(entries);
 }
 
 void
 HTMLInputElement::GetWebkitEntries(nsTArray<RefPtr<Entry>>& aSequence)
 {
   aSequence.AppendElements(mEntries);
 }
 
--- a/dom/webidl/DOMFileSystem.webidl
+++ b/dom/webidl/DOMFileSystem.webidl
@@ -10,17 +10,16 @@ interface Entry {
     readonly attribute boolean isDirectory;
 
     [GetterThrows]
     readonly attribute DOMString name;
 
     [GetterThrows]
     readonly attribute DOMString fullPath;
 
-    [GetterThrows]
     readonly attribute DOMFileSystem filesystem;
 
 /** Not implemented:
  *  void getMetadata(MetadataCallback successCallback, optional ErrorCallback errorCallback);
  *  void moveTo(DirectoryEntry parent, optional DOMString? name, optional EntryCallback successCallback, optional ErrorCallback errorCallback);
  *  void copyTo(DirectoryEntry parent, optional DOMString? name, optional EntryCallback successCallback, optional ErrorCallback errorCallback);
  *  DOMString toURL();
  *  void remove(VoidCallback successCallback, optional ErrorCallback errorCallback);
@@ -86,14 +85,11 @@ interface FileEntry : Entry {
     void createWriter (VoidCallback successCallback, optional ErrorCallback errorCallback);
 
     [BinaryName="GetFile"]
     void file (BlobCallback successCallback, optional ErrorCallback errorCallback);
 };
 
 [NoInterfaceObject]
 interface DOMFileSystem {
-    [GetterThrows]
     readonly    attribute DOMString      name;
-
-    [GetterThrows]
     readonly    attribute DirectoryEntry root;
 };