Bug 1265767 - Subset of Blink FileSystem API - patch 2 - Entries, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Tue, 07 Jun 2016 00:55:16 +0200
changeset 300830 f4d2ec0d2be108759ec1b673f7c047254d0e0732
parent 300829 b0c48d9a4960e2680feddaacca538e39e5c9258e
child 300831 7af7af94985a7115d506316e0d19ae841c64446c
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 2 - Entries, r=smaug
dom/filesystem/compat/DirectoryEntry.cpp
dom/filesystem/compat/DirectoryEntry.h
dom/filesystem/compat/Entry.cpp
dom/filesystem/compat/Entry.h
dom/filesystem/compat/FileEntry.cpp
dom/filesystem/compat/FileEntry.h
dom/filesystem/compat/moz.build
dom/filesystem/compat/tests/mochitest.ini
dom/filesystem/compat/tests/moz.build
dom/filesystem/compat/tests/script_entries.js
dom/filesystem/compat/tests/test_basic.html
dom/html/HTMLInputElement.cpp
dom/html/HTMLInputElement.h
dom/webidl/DOMFileSystem.webidl
dom/webidl/HTMLInputElement.webidl
js/xpconnect/src/Sandbox.cpp
js/xpconnect/src/xpcprivate.h
--- a/dom/filesystem/compat/DirectoryEntry.cpp
+++ b/dom/filesystem/compat/DirectoryEntry.cpp
@@ -1,26 +1,52 @@
 /* -*- 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 "mozilla/dom/Directory.h"
 
 namespace mozilla {
 namespace dom {
 
-DirectoryEntry::DirectoryEntry(nsIGlobalObject* aGlobal)
+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)
-{}
+  , 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
+{
+  mDirectory->GetName(aName, aRv);
+}
+
+void
+DirectoryEntry::GetFullPath(nsAString& aPath, ErrorResult& aRv) const
+{
+  mDirectory->GetPath(aPath, aRv);
+}
+
 } // dom namespace
 } // mozilla namespace
--- a/dom/filesystem/compat/DirectoryEntry.h
+++ b/dom/filesystem/compat/DirectoryEntry.h
@@ -8,24 +8,41 @@
 #define mozilla_dom_DirectoryEntry_h
 
 #include "mozilla/dom/Entry.h"
 #include "mozilla/dom/DOMFileSystemBinding.h"
 
 namespace mozilla {
 namespace dom {
 
+class Directory;
+
 class DirectoryEntry final : public Entry
 {
 public:
-  explicit DirectoryEntry(nsIGlobalObject* aGlobalObject);
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DirectoryEntry, Entry)
+
+  DirectoryEntry(nsIGlobalObject* aGlobalObject, Directory* aDirectory);
 
   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>
   CreateReader(ErrorResult& aRv) const
   {
     aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
     return nullptr;
   }
 
   void
@@ -51,14 +68,16 @@ public:
                     const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback,
                     ErrorResult& aRv) const
   {
     aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
   }
 
 private:
   ~DirectoryEntry();
+
+  RefPtr<Directory> mDirectory;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_DirectoryEntry_h
--- a/dom/filesystem/compat/Entry.cpp
+++ b/dom/filesystem/compat/Entry.cpp
@@ -1,32 +1,56 @@
 /* -*- 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 "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_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)
+{
+  MOZ_ASSERT(aGlobalObject);
+
+  RefPtr<Entry> entry;
+  if (aFileOrDirectory.IsFile()) {
+    entry = new FileEntry(aGlobalObject, aFileOrDirectory.GetAsFile());
+  } else {
+    MOZ_ASSERT(aFileOrDirectory.IsDirectory());
+    entry = new DirectoryEntry(aGlobalObject,
+                               aFileOrDirectory.GetAsDirectory());
+  }
+
+  return entry.forget();
+}
+
 Entry::Entry(nsIGlobalObject* aGlobal)
   : mParent(aGlobal)
-{}
+{
+  MOZ_ASSERT(aGlobal);
+}
 
 Entry::~Entry()
 {}
 
 JSObject*
 Entry::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return EntryBinding::Wrap(aCx, this, aGivenProto);
--- a/dom/filesystem/compat/Entry.h
+++ b/dom/filesystem/compat/Entry.h
@@ -12,70 +12,66 @@
 #include "mozilla/dom/BindingDeclarations.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
 
 class DOMFileSystem;
+class OwningFileOrDirectory;
 
 class Entry
   : public nsISupports
   , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Entry)
 
-  explicit Entry(nsIGlobalObject* aGlobalObject);
+  static already_AddRefed<Entry>
+  Create(nsIGlobalObject* aGlobalObject,
+         const OwningFileOrDirectory& aFileOrDirectory);
 
   nsIGlobalObject*
   GetParentObject() const
   {
     return mParent;
   }
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  bool
-  GetIsFile(ErrorResult& aRv) const
+  virtual bool
+  IsFile() const
   {
-    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
     return false;
   }
 
-  bool
-  GetIsDirectory(ErrorResult& aRv) const
+  virtual bool
+  IsDirectory() const
   {
-    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
     return false;
   }
 
-  void
-  GetName(nsAString& aName, ErrorResult& aRv) const
-  {
-    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  }
+  virtual void
+  GetName(nsAString& aName, ErrorResult& aRv) const = 0;
 
-  void
-  GetFullPath(nsAString& aFullPath, ErrorResult& aRv) const
-  {
-    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  }
+  virtual void
+  GetFullPath(nsAString& aFullPath, ErrorResult& aRv) const = 0;
 
   DOMFileSystem*
   GetFilesystem(ErrorResult& aRv) const
   {
     aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
     return nullptr;
   }
 
 protected:
+  Entry(nsIGlobalObject* aGlobalObject);
   virtual ~Entry();
 
 private:
   nsCOMPtr<nsIGlobalObject> mParent;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/compat/FileEntry.cpp
+++ b/dom/filesystem/compat/FileEntry.cpp
@@ -1,26 +1,52 @@
 /* -*- 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 "mozilla/dom/File.h"
 
 namespace mozilla {
 namespace dom {
 
-FileEntry::FileEntry(nsIGlobalObject* aGlobal)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(FileEntry, Entry, mFile)
+
+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)
-{}
+  , mFile(aFile)
+{
+  MOZ_ASSERT(aGlobal);
+  MOZ_ASSERT(mFile);
+}
 
 FileEntry::~FileEntry()
 {}
 
 JSObject*
 FileEntry::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return FileEntryBinding::Wrap(aCx, this, aGivenProto);
 }
 
+void
+FileEntry::GetName(nsAString& aName, ErrorResult& aRv) const
+{
+  mFile->GetName(aName);
+}
+
+void
+FileEntry::GetFullPath(nsAString& aPath, ErrorResult& aRv) const
+{
+  mFile->GetPath(aPath);
+}
+
 } // dom namespace
 } // mozilla namespace
--- a/dom/filesystem/compat/FileEntry.h
+++ b/dom/filesystem/compat/FileEntry.h
@@ -2,49 +2,64 @@
 /* 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_FileEntry_h
 #define mozilla_dom_FileEntry_h
 
-#include "mozilla/Attributes.h"
-#include "mozilla/ErrorResult.h"
-#include "mozilla/dom/BindingDeclarations.h"
-#include "nsCycleCollectionParticipant.h"
-#include "nsWrapperCache.h"
+#include "mozilla/dom/Entry.h"
 
 namespace mozilla {
 namespace dom {
 
+class File;
+
 class FileEntry final : public Entry
 {
 public:
-  explicit FileEntry(nsIGlobalObject* aGlobalObject);
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileEntry, Entry)
+
+  FileEntry(nsIGlobalObject* aGlobalObject, File* aFile);
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
+  virtual bool
+  IsFile() const override
+  {
+    return true;
+  }
+
+  virtual void
+  GetName(nsAString& aName, ErrorResult& aRv) const override;
+
+  virtual void
+  GetFullPath(nsAString& aFullPath, ErrorResult& aRv) const override;
+
   void
   CreateWriter(VoidCallback& aSuccessCallback,
                const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback,
                ErrorResult& aRv) const
   {
     aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
   }
 
   void
-  File(BlobCallback& aSuccessCallback,
-       const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback,
-       ErrorResult& aRv) const
+  GetFile(BlobCallback& aSuccessCallback,
+          const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback,
+          ErrorResult& aRv) const
   {
     aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
   }
 
 private:
   ~FileEntry();
+
+  RefPtr<File> mFile;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_FileEntry_h
--- a/dom/filesystem/compat/moz.build
+++ b/dom/filesystem/compat/moz.build
@@ -1,14 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+TEST_DIRS += ['tests']
+
 EXPORTS.mozilla.dom += [
     'DirectoryEntry.h',
     'DirectoryReader.h',
     'DOMFileSystem.h',
     'Entry.h',
     'FileEntry.h',
 ]
 
new file mode 100644
--- /dev/null
+++ b/dom/filesystem/compat/tests/mochitest.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+support-files =
+  script_entries.js
+
+[test_basic.html]
new file mode 100644
--- /dev/null
+++ b/dom/filesystem/compat/tests/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+MOCHITEST_MANIFESTS += ['mochitest.ini']
new file mode 100644
--- /dev/null
+++ b/dom/filesystem/compat/tests/script_entries.js
@@ -0,0 +1,35 @@
+var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+Cu.importGlobalProperties(["File", "Directory"]);
+
+addMessageListener("entries.open", function (e) {
+  var tmpFile = Cc["@mozilla.org/file/directory_service;1"]
+                  .getService(Ci.nsIDirectoryService)
+                  .QueryInterface(Ci.nsIProperties)
+                  .get('TmpD', Ci.nsIFile)
+  tmpFile.append('file.txt');
+  tmpFile.createUnique(Components.interfaces.nsIFile.FILE_TYPE, 0o600);
+
+  var tmpDir = Cc["@mozilla.org/file/directory_service;1"]
+                  .getService(Ci.nsIDirectoryService)
+                  .QueryInterface(Ci.nsIProperties)
+                  .get('TmpD', Ci.nsIFile)
+
+  tmpDir.append('dir-test');
+  tmpDir.createUnique(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o700);
+
+  var file1 = tmpDir.clone();
+  file1.append('foo.txt');
+  file1.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o600);
+
+  var dir = tmpDir.clone();
+  dir.append('subdir');
+  dir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o700);
+
+  var file2 = dir.clone();
+  file2.append('bar.txt');
+  file2.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o600);
+
+  sendAsyncMessage("entries.opened", {
+    data: [ new Directory(tmpDir.path), new File(tmpFile) ]
+  });
+});
new file mode 100644
--- /dev/null
+++ b/dom/filesystem/compat/tests/test_basic.html
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Blink FileSystem API - subset</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<input id="entries" type="file"></input>
+<script type="application/javascript;version=1.7">
+
+var fileEntry;
+var directoryEntry;
+
+function setup_tests() {
+  SpecialPowers.pushPrefEnv({"set": [["dom.webkitBlink.dirPicker.enabled", true],
+                                     ["dom.webkitBlink.filesystem.enabled", true]]}, next);
+}
+
+function populate_entries() {
+  var url = SimpleTest.getTestFileURL("script_entries.js");
+  var script = SpecialPowers.loadChromeScript(url);
+
+  function onOpened(message) {
+    var entries = document.getElementById('entries');
+    SpecialPowers.wrap(entries).mozSetDndFilesAndDirectories(message.data);
+
+    script.destroy();
+    next();
+  }
+
+  script.addMessageListener("entries.opened", onOpened);
+  script.sendAsyncMessage("entries.open");
+}
+
+function test_entries() {
+  var entries = document.getElementById('entries');
+  ok("webkitEntries" in entries, "HTMLInputElement.webkitEntries");
+  is(entries.webkitEntries.length, 2, "HTMLInputElement.webkitEntries.length == 2");
+  is(entries.files.length, 1, "HTMLInputElement.files is still populated");
+
+  for (var i = 0; i < entries.webkitEntries.length; ++i) {
+    if (entries.webkitEntries[i].isFile) {
+      ok(!fileEntry, "We just want 1 fileEntry");
+      fileEntry = entries.webkitEntries[i];
+    } else {
+      ok(entries.webkitEntries[i].isDirectory, "If not a file, we have a directory.");
+      ok(!directoryEntry, "We just want 1 directoryEntry");
+      directoryEntry = entries.webkitEntries[i];
+    }
+  }
+
+  next();
+}
+
+function test_fileEntry() {
+  ok("name" in fileEntry, "We have a name.");
+  ok("fullPath" in fileEntry, "We have a fullPath.");
+  ok("filesystem" in fileEntry, "We have a filesystem.");
+
+  next();
+}
+
+function test_directoryEntry() {
+  ok("name" in directoryEntry, "We have a name.");
+  ok("fullPath" in directoryEntry, "We have a fullPath.");
+  ok("filesystem" in directoryEntry, "We have a filesystem.");
+
+  next();
+}
+
+var tests = [
+  setup_tests,
+  populate_entries,
+
+  test_entries,
+
+  test_fileEntry,
+  test_directoryEntry,
+];
+
+function next() {
+  if (!tests.length) {
+    SimpleTest.finish();
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
+
+SimpleTest.waitForExplicitFinish();
+next();
+</script>
+</body>
+</html>
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -74,16 +74,17 @@
 
 #include "nsRuleData.h"
 #include <algorithm>
 
 // input type=radio
 #include "nsIRadioGroupContainer.h"
 
 // input type=file
+#include "mozilla/dom/Entry.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"
@@ -1528,24 +1529,26 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
     tmp->mGetFilesRecursiveHelper->Traverse(cb);
   }
 
   if (tmp->mGetFilesNonRecursiveHelper) {
     tmp->mGetFilesNonRecursiveHelper->Traverse(cb);
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEntries)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLInputElement,
                                                 nsGenericHTMLFormElementWithState)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilesOrDirectories)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEntries)
   if (tmp->IsSingleLineTextControl(false)) {
     tmp->mInputData.mState->Unlink();
   }
 
   tmp->ClearGetFilesHelpers();
 
   //XXX should unlink more?
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@@ -3070,16 +3073,26 @@ HTMLInputElement::SetFiles(nsIDOMFileLis
       element->SetAsFile() = files->Item(i);
     }
   }
 
   AfterSetFilesOrDirectories(aSetValueChanged);
 }
 
 void
+HTMLInputElement::MozSetDndFilesAndDirectories(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories)
+{
+  if (Preferences::GetBool("dom.webkitBlink.filesystem.enabled", false)) {
+    UpdateEntries(aFilesOrDirectories);
+  }
+
+  SetFilesOrDirectories(aFilesOrDirectories, true);
+}
+
+void
 HTMLInputElement::AfterSetFilesOrDirectories(bool aSetValueChanged)
 {
   if (Preferences::GetBool("dom.webkitBlink.dirPicker.enabled", false) &&
       HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory)) {
     // This will call AfterSetFilesOrDirectoriesInternal eventually.
     ExploreDirectoryRecursively(aSetValueChanged);
     return;
   }
@@ -8178,12 +8191,36 @@ HTMLInputElement::ExploreDirectoryRecurs
     return;
   }
 
   RefPtr<AfterSetFilesOrDirectoriesCallback> callback =
     new AfterSetFilesOrDirectoriesCallback(this, aSetValueChanged);
   helper->AddCallback(callback);
 }
 
+void
+HTMLInputElement::UpdateEntries(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories)
+{
+  mEntries.Clear();
+
+  nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
+  MOZ_ASSERT(global);
+
+  for (uint32_t i = 0; i < aFilesOrDirectories.Length(); ++i) {
+    RefPtr<Entry> entry = Entry::Create(global, aFilesOrDirectories[i]);
+    MOZ_ASSERT(entry);
+
+    if (!mEntries.AppendElement(entry, fallible)) {
+      return;
+    }
+  }
+}
+
+void
+HTMLInputElement::GetWebkitEntries(nsTArray<RefPtr<Entry>>& aSequence)
+{
+  aSequence.AppendElements(mEntries);
+}
+
 } // namespace dom
 } // namespace mozilla
 
 #undef NS_ORIGINAL_CHECKED_VALUE
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -233,16 +233,18 @@ public:
   {
     return mFilesOrDirectories;
   }
 
   void SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories,
                              bool aSetValueChanged);
   void SetFiles(nsIDOMFileList* aFiles, bool aSetValueChanged);
 
+  void MozSetDndFilesAndDirectories(const nsTArray<OwningFileOrDirectory>& aSequence);
+
   // Called when a nsIFilePicker or a nsIColorPicker terminate.
   void PickerClosed();
 
   void SetCheckedChangedInternal(bool aCheckedChanged);
   bool GetCheckedChanged() const {
     return mCheckedChanged;
   }
   void AddedToRadioGroup();
@@ -711,20 +713,17 @@ public:
     return HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory);
   }
 
   void SetWebkitDirectoryAttr(bool aValue, ErrorResult& aRv)
   {
     SetHTMLBoolAttr(nsGkAtoms::webkitdirectory, aValue, aRv);
   }
 
-  void GetWebkitEntries(nsTArray<RefPtr<Entry>>& aSequence, ErrorResult& aRv)
-  {
-    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  }
+  void GetWebkitEntries(nsTArray<RefPtr<Entry>>& aSequence);
 
   bool IsFilesAndDirectoriesSupported() const;
 
   already_AddRefed<Promise> GetFilesAndDirectories(ErrorResult& aRv);
 
   already_AddRefed<Promise> GetFiles(bool aRecursiveFlag, ErrorResult& aRv);
 
   void ChooseDirectory(ErrorResult& aRv);
@@ -952,16 +951,18 @@ protected:
    */
   nsresult MaybeSubmitForm(nsPresContext* aPresContext);
 
   /**
    * Update mFileList with the currently selected file.
    */
   void UpdateFileList();
 
+  void UpdateEntries(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories);
+
   /**
    * Called after calling one of the SetFilesOrDirectories() functions.
    * This method can explore the directory recursively if needed.
    */
   void AfterSetFilesOrDirectories(bool aSetValueChanged);
   void AfterSetFilesOrDirectoriesInternal(bool aSetValueChanged);
 
   /**
@@ -1329,16 +1330,17 @@ protected:
 #ifndef MOZ_CHILD_PERMISSIONS
   /**
    * Hack for bug 1086684: Stash the .value when we're a file picker.
    */
   nsString mFirstFilePath;
 #endif
 
   RefPtr<FileList>  mFileList;
+  Sequence<RefPtr<Entry>> mEntries;
 
   nsString mStaticDocFileList;
 
   /**
    * The value of the input element when first initialized and it is updated
    * when the element is either changed through a script, focused or dispatches
    * a change event. This is to ensure correct future change event firing.
    * NB: This is ONLY applicable where the element is a text control. ie,
--- a/dom/webidl/DOMFileSystem.webidl
+++ b/dom/webidl/DOMFileSystem.webidl
@@ -1,20 +1,17 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/.
  */
 
 [NoInterfaceObject]
 interface Entry {
-    [GetterThrows]
     readonly attribute boolean isFile;
-
-    [GetterThrows]
     readonly attribute boolean isDirectory;
 
     [GetterThrows]
     readonly attribute DOMString name;
 
     [GetterThrows]
     readonly attribute DOMString fullPath;
 
@@ -80,17 +77,17 @@ callback interface BlobCallback {
 
 [NoInterfaceObject]
 interface FileEntry : Entry {
     // the successCallback should be a FileWriteCallback but this method is not
     // implemented.
     [Throws]
     void createWriter (VoidCallback successCallback, optional ErrorCallback errorCallback);
 
-    [Throws]
+    [Throws,BinaryName="GetFile"]
     void file (BlobCallback successCallback, optional ErrorCallback errorCallback);
 };
 
 [NoInterfaceObject]
 interface DOMFileSystem {
     [GetterThrows]
     readonly    attribute DOMString      name;
 
--- a/dom/webidl/HTMLInputElement.webidl
+++ b/dom/webidl/HTMLInputElement.webidl
@@ -155,16 +155,20 @@ partial interface HTMLInputElement {
 
   [ChromeOnly]
   void mozSetFileArray(sequence<File> files);
 
   // This method is meant to use for testing only.
   [ChromeOnly, Throws]
   void mozSetDirectory(DOMString directoryPath);
 
+  // This method is meant to use for testing only.
+  [ChromeOnly]
+  void mozSetDndFilesAndDirectories(sequence<(File or Directory)> list);
+
   // Number controls (<input type=number>) have an anonymous text control
   // (<input type=text>) in the anonymous shadow tree that they contain. On
   // such an anonymous text control this property provides access to the
   // number control that owns the text control. This is useful, for example,
   // in code that looks at the currently focused element to make decisions
   // about which IME to bring up. Such code needs to be able to check for any
   // owning number control since it probably wants to bring up a number pad
   // instead of the standard keyboard, even when the anonymous text control has
@@ -217,14 +221,14 @@ interface MozPhonetic {
   readonly attribute DOMString phonetic;
 };
 
 HTMLInputElement implements MozImageLoadingContent;
 HTMLInputElement implements MozPhonetic;
 
 // Webkit/Blink
 partial interface HTMLInputElement {
-  [Pref="dom.webkitBlink.filesystem.enabled", Cached, Constant, GetterThrows]
+  [Pref="dom.webkitBlink.filesystem.enabled", Cached, Constant]
   readonly attribute sequence<Entry> webkitEntries;
 
   [Pref="dom.webkitBlink.dirPicker.enabled", BinaryName="WebkitDirectoryAttr", SetterThrows]
           attribute boolean webkitdirectory;
 };
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -26,16 +26,17 @@
 #include "xpcprivate.h"
 #include "XPCWrapper.h"
 #include "XrayWrapper.h"
 #include "Crypto.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/cache/CacheStorage.h"
 #include "mozilla/dom/CSSBinding.h"
+#include "mozilla/dom/DirectoryBinding.h"
 #include "mozilla/dom/IndexedDatabaseManager.h"
 #include "mozilla/dom/Fetch.h"
 #include "mozilla/dom/FileBinding.h"
 #include "mozilla/dom/PromiseBinding.h"
 #include "mozilla/dom/RequestBinding.h"
 #include "mozilla/dom/ResponseBinding.h"
 #ifdef MOZ_WEBRTC
 #include "mozilla/dom/RTCIdentityProviderRegistrar.h"
@@ -904,16 +905,18 @@ xpc::GlobalProperties::Parse(JSContext* 
         } else if (!strcmp(name.ptr(), "URLSearchParams")) {
             URLSearchParams = true;
         } else if (!strcmp(name.ptr(), "atob")) {
             atob = true;
         } else if (!strcmp(name.ptr(), "btoa")) {
             btoa = true;
         } else if (!strcmp(name.ptr(), "Blob")) {
             Blob = true;
+        } else if (!strcmp(name.ptr(), "Directory")) {
+            Directory = true;
         } else if (!strcmp(name.ptr(), "File")) {
             File = true;
         } else if (!strcmp(name.ptr(), "crypto")) {
             crypto = true;
 #ifdef MOZ_WEBRTC
         } else if (!strcmp(name.ptr(), "rtcIdentityProvider")) {
             rtcIdentityProvider = true;
 #endif
@@ -969,16 +972,20 @@ xpc::GlobalProperties::Define(JSContext*
     if (btoa &&
         !JS_DefineFunction(cx, obj, "btoa", Btoa, 1, 0))
         return false;
 
     if (Blob &&
         !dom::BlobBinding::GetConstructorObject(cx, obj))
         return false;
 
+    if (Directory &&
+        !dom::DirectoryBinding::GetConstructorObject(cx, obj))
+        return false;
+
     if (File &&
         !dom::FileBinding::GetConstructorObject(cx, obj))
         return false;
 
     if (crypto && !SandboxCreateCrypto(cx, obj))
         return false;
 
 #ifdef MOZ_WEBRTC
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -3328,16 +3328,17 @@ struct GlobalProperties {
     bool XMLHttpRequest : 1;
     bool TextDecoder : 1;
     bool TextEncoder : 1;
     bool URL : 1;
     bool URLSearchParams : 1;
     bool atob : 1;
     bool btoa : 1;
     bool Blob : 1;
+    bool Directory : 1;
     bool File : 1;
     bool crypto : 1;
     bool rtcIdentityProvider : 1;
     bool fetch : 1;
     bool caches : 1;
     bool fileReader: 1;
 private:
     bool Define(JSContext* cx, JS::HandleObject obj);