Bug 1258490 - Implement file.webkitrelativepath, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Fri, 13 May 2016 13:11:38 +0200
changeset 336355 bbe22e01384ccb3ae1043c6d090c2beaae632de0
parent 336354 65a7900758687be24170ee0bacbccf86d4b5e857
child 336356 247e8e95837d13b5fd58fb3a1aaee964564ae308
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1258490
milestone49.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 1258490 - Implement file.webkitrelativepath, r=smaug
dom/base/File.cpp
dom/base/File.h
dom/filesystem/CreateFileTask.cpp
dom/filesystem/Directory.cpp
dom/filesystem/Directory.h
dom/filesystem/GetDirectoryListingTask.cpp
dom/filesystem/GetDirectoryListingTask.h
dom/filesystem/GetFileOrDirectoryTask.cpp
dom/filesystem/GetFilesTask.cpp
dom/filesystem/GetFilesTask.h
dom/filesystem/PFileSystemParams.ipdlh
dom/filesystem/PFileSystemRequest.ipdl
dom/filesystem/moz.build
dom/filesystem/tests/filesystem_commons.js
dom/filesystem/tests/script_fileList.js
dom/filesystem/tests/test_basic.html
dom/html/HTMLInputElement.cpp
dom/html/nsFormSubmission.cpp
dom/indexedDB/FileSnapshot.h
dom/ipc/Blob.cpp
dom/webidl/File.webidl
dom/workers/WorkerPrefs.h
--- a/dom/base/File.cpp
+++ b/dom/base/File.cpp
@@ -460,19 +460,25 @@ File::WrapObject(JSContext* aCx, JS::Han
 
 void
 File::GetName(nsAString& aFileName) const
 {
   mImpl->GetName(aFileName);
 }
 
 void
-File::GetPath(nsAString& aPath, ErrorResult& aRv)
+File::GetPath(nsAString& aPath) const
 {
-  mImpl->GetPath(aPath, aRv);
+  mImpl->GetPath(aPath);
+}
+
+void
+File::SetPath(const nsAString& aPath)
+{
+  mImpl->SetPath(aPath);
 }
 
 Date
 File::GetLastModifiedDate(ErrorResult& aRv)
 {
   int64_t value = GetLastModified(aRv);
   if (aRv.Failed()) {
     return Date();
@@ -680,23 +686,30 @@ NS_IMPL_ISUPPORTS_INHERITED0(BlobImplFil
 void
 BlobImplBase::GetName(nsAString& aName) const
 {
   NS_ASSERTION(mIsFile, "Should only be called on files");
   aName = mName;
 }
 
 void
-BlobImplBase::GetPath(nsAString& aPath, ErrorResult& aRv)
+BlobImplBase::GetPath(nsAString& aPath) const
 {
   NS_ASSERTION(mIsFile, "Should only be called on files");
   aPath = mPath;
 }
 
 void
+BlobImplBase::SetPath(const nsAString& aPath)
+{
+  NS_ASSERTION(mIsFile, "Should only be called on files");
+  mPath = aPath;
+}
+
+void
 BlobImplBase::GetMozFullPath(nsAString& aFileName, ErrorResult& aRv) const
 {
   NS_ASSERTION(mIsFile, "Should only be called on files");
 
   aFileName.Truncate();
 
   if (NS_IsMainThread()) {
     if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
@@ -980,25 +993,16 @@ BlobImplFile::GetInternalStream(nsIInput
     aRv = NS_NewLocalFileInputStream(aStream, mFile, -1, -1, sFileStreamFlags);
     return;
   }
 
   aRv = NS_NewPartialLocalFileInputStream(aStream, mFile, mStart, mLength,
                                           -1, -1, sFileStreamFlags);
 }
 
-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;
-}
-
 ////////////////////////////////////////////////////////////////////////////
 // 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
@@ -231,17 +231,23 @@ public:
               ErrorResult& aRv);
 
   void GetName(nsAString& aName) const;
 
   int64_t GetLastModified(ErrorResult& aRv);
 
   Date GetLastModifiedDate(ErrorResult& aRv);
 
-  void GetPath(nsAString& aName, ErrorResult& aRv);
+  // GetPath and SetPath are currently used only for the webkitRelativePath
+  // attribute and they are only used when this File object is created from a
+  // Directory, generated by a Directory Picker.
+
+  void GetPath(nsAString& aName) const;
+
+  void SetPath(const nsAString& aName);
 
   void GetMozFullPath(nsAString& aFilename, ErrorResult& aRv) const;
 
   void GetMozFullPathInternal(nsAString& aName, ErrorResult& aRv) const;
 
 protected:
   virtual bool HasFileInterface() const override { return true; }
 
@@ -259,17 +265,19 @@ class BlobImpl : public nsISupports
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(BLOBIMPL_IID)
   NS_DECL_THREADSAFE_ISUPPORTS
 
   BlobImpl() {}
 
   virtual void GetName(nsAString& aName) const = 0;
 
-  virtual void GetPath(nsAString& aName, ErrorResult& aRv) = 0;
+  virtual void GetPath(nsAString& aName) const = 0;
+
+  virtual void SetPath(const nsAString& aName) = 0;
 
   virtual int64_t GetLastModified(ErrorResult& aRv) = 0;
 
   virtual void SetLastModified(int64_t aLastModified) = 0;
 
   virtual void GetMozFullPath(nsAString& aName, ErrorResult& aRv) const = 0;
 
   virtual void GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) const = 0;
@@ -395,17 +403,19 @@ public:
     NS_ASSERTION(aLength != UINT64_MAX,
                  "Must know length when creating slice");
     // Ensure non-null mContentType by default
     mContentType.SetIsVoid(false);
   }
 
   virtual void GetName(nsAString& aName) const override;
 
-  virtual void GetPath(nsAString& aName, ErrorResult& aRv) override;
+  virtual void GetPath(nsAString& aName) const override;
+
+  virtual void SetPath(const nsAString& aName) override;
 
   virtual int64_t GetLastModified(ErrorResult& aRv) override;
 
   virtual void SetLastModified(int64_t aLastModified) override;
 
   virtual void GetMozFullPath(nsAString& aName, ErrorResult& aRv) const override;
 
   virtual void GetMozFullPathInternal(nsAString& aFileName,
@@ -700,18 +710,16 @@ public:
   virtual void GetType(nsAString& aType) override;
   virtual int64_t GetLastModified(ErrorResult& aRv) override;
   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);
-
   // 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/filesystem/CreateFileTask.cpp
+++ b/dom/filesystem/CreateFileTask.cpp
@@ -235,17 +235,17 @@ CreateFileTaskParent::GetSuccessRequestR
   AssertIsOnBackgroundThread();
 
   nsAutoString path;
   aRv = mTargetPath->GetPath(path);
   if (NS_WARN_IF(aRv.Failed())) {
     return FileSystemDirectoryResponse();
   }
 
-  return FileSystemFileResponse(path);
+  return FileSystemFileResponse(path, EmptyString());
 }
 
 nsresult
 CreateFileTaskParent::IOWork()
 {
   class MOZ_RAII AutoClose final
   {
   public:
--- a/dom/filesystem/Directory.cpp
+++ b/dom/filesystem/Directory.cpp
@@ -8,16 +8,17 @@
 
 #include "CreateDirectoryTask.h"
 #include "CreateFileTask.h"
 #include "FileSystemPermissionRequest.h"
 #include "GetDirectoryListingTask.h"
 #include "GetFileOrDirectoryTask.h"
 #include "GetFilesTask.h"
 #include "RemoveTask.h"
+#include "WorkerPrivate.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"
 
@@ -117,16 +118,32 @@ Directory::DeviceStorageEnabled(JSContex
 {
   if (!NS_IsMainThread()) {
     return false;
   }
 
   return Preferences::GetBool("device.storage.enabled", false);
 }
 
+/* static */ bool
+Directory::WebkitBlinkDirectoryPickerEnabled(JSContext* aCx, JSObject* aObj)
+{
+  if (NS_IsMainThread()) {
+    return Preferences::GetBool("dom.webkitBlink.dirPicker.enabled", false);
+  }
+
+  workers::WorkerPrivate* workerPrivate =
+    workers::GetWorkerPrivateFromContext(aCx);
+  if (!workerPrivate) {
+    return false;
+  }
+
+  return workerPrivate->WebkitBlinkDirectoryPickerEnabled();
+}
+
 /* static */ already_AddRefed<Promise>
 Directory::GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv)
 {
   // Only exposed for DeviceStorage.
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aFileSystem);
 
   nsCOMPtr<nsIFile> path;
@@ -415,17 +432,17 @@ already_AddRefed<Promise>
 Directory::GetFilesAndDirectories(ErrorResult& aRv)
 {
   RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   RefPtr<GetDirectoryListingTaskChild> task =
-    GetDirectoryListingTaskChild::Create(fs, mFile, mFilters, aRv);
+    GetDirectoryListingTaskChild::Create(fs, this, mFile, mFilters, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
 
@@ -435,17 +452,17 @@ Directory::GetFiles(bool aRecursiveFlag,
   ErrorResult rv;
   RefPtr<FileSystemBase> fs = GetFileSystem(rv);
   if (NS_WARN_IF(rv.Failed())) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
   RefPtr<GetFilesTaskChild> task =
-    GetFilesTaskChild::Create(fs, mFile, aRecursiveFlag, rv);
+    GetFilesTaskChild::Create(fs, this, mFile, aRecursiveFlag, rv);
   if (NS_WARN_IF(rv.Failed())) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
   FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
--- a/dom/filesystem/Directory.h
+++ b/dom/filesystem/Directory.h
@@ -51,16 +51,19 @@ public:
   };
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Directory)
 
   static bool
   DeviceStorageEnabled(JSContext* aCx, JSObject* aObj);
 
+  static bool
+  WebkitBlinkDirectoryPickerEnabled(JSContext* aCx, JSObject* aObj);
+
   static already_AddRefed<Promise>
   GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv);
 
   static already_AddRefed<Directory>
   Create(nsISupports* aParent, nsIFile* aDirectory,
          FileSystemBase* aFileSystem = 0);
 
   // ========= Begin WebIDL bindings. ===========
--- a/dom/filesystem/GetDirectoryListingTask.cpp
+++ b/dom/filesystem/GetDirectoryListingTask.cpp
@@ -22,25 +22,28 @@ namespace mozilla {
 namespace dom {
 
 /**
  * GetDirectoryListingTaskChild
  */
 
 /* static */ already_AddRefed<GetDirectoryListingTaskChild>
 GetDirectoryListingTaskChild::Create(FileSystemBase* aFileSystem,
+                                     Directory* aDirectory,
                                      nsIFile* aTargetPath,
                                      const nsAString& aFilters,
                                      ErrorResult& aRv)
 {
   MOZ_ASSERT(aFileSystem);
+  MOZ_ASSERT(aDirectory);
   aFileSystem->AssertIsOnOwningThread();
 
   RefPtr<GetDirectoryListingTaskChild> task =
-    new GetDirectoryListingTaskChild(aFileSystem, aTargetPath, aFilters);
+    new GetDirectoryListingTaskChild(aFileSystem, aDirectory, aTargetPath,
+                                     aFilters);
 
   // aTargetPath can be null. In this case SetError will be called.
 
   nsCOMPtr<nsIGlobalObject> globalObject =
     do_QueryInterface(aFileSystem->GetParentObject());
   if (NS_WARN_IF(!globalObject)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
@@ -50,19 +53,21 @@ GetDirectoryListingTaskChild::Create(Fil
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
 GetDirectoryListingTaskChild::GetDirectoryListingTaskChild(FileSystemBase* aFileSystem,
+                                                           Directory* aDirectory,
                                                            nsIFile* aTargetPath,
                                                            const nsAString& aFilters)
   : FileSystemTaskChildBase(aFileSystem)
+  , mDirectory(aDirectory)
   , mTargetPath(aTargetPath)
   , mFilters(aFilters)
 {
   MOZ_ASSERT(aFileSystem);
   aFileSystem->AssertIsOnOwningThread();
 }
 
 GetDirectoryListingTaskChild::~GetDirectoryListingTaskChild()
@@ -137,16 +142,25 @@ GetDirectoryListingTaskChild::HandlerCal
   if (HasError()) {
     mPromise->MaybeReject(mErrorValue);
     mPromise = nullptr;
     return;
   }
 
   size_t count = mTargetData.Length();
 
+  nsAutoString directoryPath;
+  ErrorResult error;
+  mDirectory->GetPath(directoryPath, error);
+  if (NS_WARN_IF(error.Failed())) {
+    mPromise->MaybeReject(error.StealNSResult());
+    mPromise = nullptr;
+    return;
+  }
+
   Sequence<OwningFileOrDirectory> listing;
 
   if (!listing.SetLength(count, mozilla::fallible_t())) {
     mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
     mPromise = nullptr;
     return;
   }
 
@@ -183,16 +197,29 @@ GetDirectoryListingTaskChild::HandlerCal
       listing[i].SetAsDirectory() = directory;
     } else {
       MOZ_ASSERT(mTargetData[i].mType == Directory::FileOrDirectoryPath::eFilePath);
 
       RefPtr<File> file =
         File::CreateFromFile(mFileSystem->GetParentObject(), path);
       MOZ_ASSERT(file);
 
+      nsAutoString filePath;
+      filePath.Assign(directoryPath);
+
+      // This is specific for unix root filesystem.
+      if (!directoryPath.EqualsLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL)) {
+        filePath.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
+      }
+
+      nsAutoString name;
+      file->GetName(name);
+      filePath.Append(name);
+      file->SetPath(filePath);
+
       listing[i].SetAsFile() = file;
     }
   }
 
   mPromise->MaybeResolve(listing);
   mPromise = nullptr;
 }
 
--- a/dom/filesystem/GetDirectoryListingTask.h
+++ b/dom/filesystem/GetDirectoryListingTask.h
@@ -17,47 +17,50 @@ namespace dom {
 
 class BlobImpl;
 
 class GetDirectoryListingTaskChild final : public FileSystemTaskChildBase
 {
 public:
   static already_AddRefed<GetDirectoryListingTaskChild>
   Create(FileSystemBase* aFileSystem,
+         Directory* aDirectory,
          nsIFile* aTargetPath,
          const nsAString& aFilters,
          ErrorResult& aRv);
 
   virtual
   ~GetDirectoryListingTaskChild();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
 
 private:
   // If aDirectoryOnly is set, we should ensure that the target is a directory.
   GetDirectoryListingTaskChild(FileSystemBase* aFileSystem,
+                               Directory* aDirectory,
                                nsIFile* aTargetPath,
                                const nsAString& aFilters);
 
   virtual FileSystemParams
   GetRequestParams(const nsString& aSerializedDOMPath,
                    ErrorResult& aRv) const override;
 
   virtual void
   SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                           ErrorResult& aRv) override;
 
   virtual void
   HandlerCallback() override;
 
   RefPtr<Promise> mPromise;
+  RefPtr<Directory> mDirectory;
   nsCOMPtr<nsIFile> mTargetPath;
   nsString mFilters;
 
   // We cannot store File or Directory objects bacause this object is created
   // on a different thread and File and Directory are not thread-safe.
   FallibleTArray<Directory::FileOrDirectoryPath> mTargetData;
 };
 
--- a/dom/filesystem/GetFileOrDirectoryTask.cpp
+++ b/dom/filesystem/GetFileOrDirectoryTask.cpp
@@ -213,17 +213,17 @@ GetFileOrDirectoryTaskParent::GetSuccess
   if (NS_WARN_IF(aRv.Failed())) {
     return FileSystemDirectoryResponse();
   }
 
   if (mIsDirectory) {
     return FileSystemDirectoryResponse(path);
   }
 
-  return FileSystemFileResponse(path);
+  return FileSystemFileResponse(path, EmptyString());
 }
 
 nsresult
 GetFileOrDirectoryTaskParent::IOWork()
 {
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Only call from parent process!");
   MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
--- a/dom/filesystem/GetFilesTask.cpp
+++ b/dom/filesystem/GetFilesTask.cpp
@@ -22,51 +22,57 @@ namespace mozilla {
 namespace dom {
 
 /**
  * GetFilesTaskChild
  */
 
 /* static */ already_AddRefed<GetFilesTaskChild>
 GetFilesTaskChild::Create(FileSystemBase* aFileSystem,
+                          Directory* aDirectory,
                           nsIFile* aTargetPath,
                           bool aRecursiveFlag,
                           ErrorResult& aRv)
 {
   MOZ_ASSERT(aFileSystem);
+  MOZ_ASSERT(aDirectory);
   aFileSystem->AssertIsOnOwningThread();
 
   nsCOMPtr<nsIGlobalObject> globalObject =
     do_QueryInterface(aFileSystem->GetParentObject());
   if (NS_WARN_IF(!globalObject)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   RefPtr<GetFilesTaskChild> task =
-    new GetFilesTaskChild(aFileSystem, aTargetPath, aRecursiveFlag);
+    new GetFilesTaskChild(aFileSystem, aDirectory, aTargetPath,
+                          aRecursiveFlag);
 
   // aTargetPath can be null. In this case SetError will be called.
 
   task->mPromise = Promise::Create(globalObject, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
 GetFilesTaskChild::GetFilesTaskChild(FileSystemBase* aFileSystem,
+                                     Directory* aDirectory,
                                      nsIFile* aTargetPath,
                                      bool aRecursiveFlag)
   : FileSystemTaskChildBase(aFileSystem)
+  , mDirectory(aDirectory)
   , mTargetPath(aTargetPath)
   , mRecursiveFlag(aRecursiveFlag)
 {
   MOZ_ASSERT(aFileSystem);
+  MOZ_ASSERT(aDirectory);
   aFileSystem->AssertIsOnOwningThread();
 }
 
 GetFilesTaskChild::~GetFilesTaskChild()
 {
   mFileSystem->AssertIsOnOwningThread();
 }
 
@@ -84,17 +90,24 @@ GetFilesTaskChild::GetRequestParams(cons
   mFileSystem->AssertIsOnOwningThread();
 
   nsAutoString path;
   aRv = mTargetPath->GetPath(path);
   if (NS_WARN_IF(aRv.Failed())) {
     return FileSystemGetFilesParams();
   }
 
-  return FileSystemGetFilesParams(aSerializedDOMPath, path, mRecursiveFlag);
+  nsAutoString domPath;
+  mDirectory->GetPath(domPath, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return FileSystemGetFilesParams();
+  }
+
+  return FileSystemGetFilesParams(aSerializedDOMPath, path, domPath,
+                                  mRecursiveFlag);
 }
 
 void
 GetFilesTaskChild::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                                            ErrorResult& aRv)
 {
   mFileSystem->AssertIsOnOwningThread();
   MOZ_ASSERT(aValue.type() ==
@@ -104,17 +117,18 @@ GetFilesTaskChild::SetSuccessRequestResu
 
   if (!mTargetData.SetLength(r.data().Length(), mozilla::fallible_t())) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
   for (uint32_t i = 0; i < r.data().Length(); ++i) {
     const FileSystemFileResponse& data = r.data()[i];
-    mTargetData[i] = data.realPath();
+    mTargetData[i].mRealPath = data.realPath();
+    mTargetData[i].mDOMPath = data.domPath();
   }
 }
 
 void
 GetFilesTaskChild::HandlerCallback()
 {
   mFileSystem->AssertIsOnOwningThread();
   if (mFileSystem->IsShutdown()) {
@@ -135,17 +149,17 @@ GetFilesTaskChild::HandlerCallback()
   if (!listing.SetLength(count, mozilla::fallible_t())) {
     mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
     mPromise = nullptr;
     return;
   }
 
   for (unsigned i = 0; i < count; i++) {
     nsCOMPtr<nsIFile> path;
-    NS_ConvertUTF16toUTF8 fullPath(mTargetData[i]);
+    NS_ConvertUTF16toUTF8 fullPath(mTargetData[i].mRealPath);
     nsresult rv = NS_NewNativeLocalFile(fullPath, true, getter_AddRefs(path));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
       mPromise = nullptr;
       return;
     }
 
 #ifdef DEBUG
@@ -159,16 +173,17 @@ GetFilesTaskChild::HandlerCallback()
     }
 
     MOZ_ASSERT(FileSystemUtils::IsDescendantPath(rootPath, path));
 #endif
 
     RefPtr<File> file =
       File::CreateFromFile(mFileSystem->GetParentObject(), path);
     MOZ_ASSERT(file);
+    file->SetPath(mTargetData[i].mDOMPath);
 
     listing[i] = file;
   }
 
   mPromise->MaybeResolve(listing);
   mPromise = nullptr;
 }
 
@@ -203,16 +218,17 @@ GetFilesTaskParent::Create(FileSystemBas
 
   return task.forget();
 }
 
 GetFilesTaskParent::GetFilesTaskParent(FileSystemBase* aFileSystem,
                                        const FileSystemGetFilesParams& aParam,
                                        FileSystemRequestParent* aParent)
   : FileSystemTaskParentBase(aFileSystem, aParam, aParent)
+  , mDirectoryDOMPath(aParam.domPath())
   , mRecursiveFlag(aParam.recursiveFlag())
 {
   MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aFileSystem);
 }
 
 FileSystemResponseValue
@@ -226,17 +242,18 @@ GetFilesTaskParent::GetSuccessRequestRes
   if (!inputs.SetLength(mTargetData.Length(), mozilla::fallible_t())) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     FileSystemFilesResponse response;
     return response;
   }
 
   for (unsigned i = 0; i < mTargetData.Length(); i++) {
     FileSystemFileResponse fileData;
-    fileData.realPath() = mTargetData[i];
+    fileData.realPath() = mTargetData[i].mRealPath;
+    fileData.domPath() = mTargetData[i].mDOMPath;
     inputs[i] = fileData;
   }
 
   FileSystemFilesResponse response;
   response.data().SwapElements(inputs);
   return response;
 }
 
@@ -257,26 +274,26 @@ GetFilesTaskParent::IOWork()
     return rv;
   }
 
   if (!exists) {
     return NS_OK;
   }
 
   // Get isDirectory.
-  rv = ExploreDirectory(mTargetPath);
+  rv = ExploreDirectory(mDirectoryDOMPath, mTargetPath);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
-GetFilesTaskParent::ExploreDirectory(nsIFile* aPath)
+GetFilesTaskParent::ExploreDirectory(const nsAString& aDOMPath, nsIFile* aPath)
 {
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Only call from parent process!");
   MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
   MOZ_ASSERT(aPath);
 
   bool isDir;
   nsresult rv = aPath->IsDirectory(&isDir);
@@ -315,37 +332,53 @@ GetFilesTaskParent::ExploreDirectory(nsI
     }
 
     if (NS_WARN_IF(NS_FAILED(currFile->IsFile(&isFile)) ||
                    NS_FAILED(currFile->IsDirectory(&isDir))) ||
         !(isFile || isDir)) {
       continue;
     }
 
+    nsAutoString domPath;
+    domPath.Assign(aDOMPath);
+
+    // This is specific for unix root filesystem.
+    if (!aDOMPath.EqualsLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL)) {
+      domPath.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
+    }
+
+    nsAutoString leafName;
+    if (NS_WARN_IF(NS_FAILED(currFile->GetLeafName(leafName)))) {
+      continue;
+    }
+    domPath.Append(leafName);
+
     if (isFile) {
-      nsAutoString path;
-      if (NS_WARN_IF(NS_FAILED(currFile->GetPath(path)))) {
+      FileData data;
+      data.mDOMPath.Append(domPath);
+
+      if (NS_WARN_IF(NS_FAILED(currFile->GetPath(data.mRealPath)))) {
         continue;
       }
 
-      if (!mTargetData.AppendElement(path, fallible)) {
+      if (!mTargetData.AppendElement(data, fallible)) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
 
       continue;
     }
 
     MOZ_ASSERT(isDir);
 
     if (!mRecursiveFlag) {
       continue;
     }
 
     // Recursive.
-    rv = ExploreDirectory(currFile);
+    rv = ExploreDirectory(domPath, currFile);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   return NS_OK;
 }
 
--- a/dom/filesystem/GetFilesTask.h
+++ b/dom/filesystem/GetFilesTask.h
@@ -17,52 +17,60 @@ namespace dom {
 
 class BlobImpl;
 
 class GetFilesTaskChild final : public FileSystemTaskChildBase
 {
 public:
   static already_AddRefed<GetFilesTaskChild>
   Create(FileSystemBase* aFileSystem,
+         Directory* aDirectory,
          nsIFile* aTargetPath,
          bool aRecursiveFlag,
          ErrorResult& aRv);
 
   virtual
   ~GetFilesTaskChild();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
 
 private:
   // If aDirectoryOnly is set, we should ensure that the target is a directory.
   GetFilesTaskChild(FileSystemBase* aFileSystem,
+                    Directory* aDirectory,
                     nsIFile* aTargetPath,
                     bool aRecursiveFlag);
 
   virtual FileSystemParams
   GetRequestParams(const nsString& aSerializedDOMPath,
                    ErrorResult& aRv) const override;
 
   virtual void
   SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                           ErrorResult& aRv) override;
 
   virtual void
   HandlerCallback() override;
 
   RefPtr<Promise> mPromise;
+  RefPtr<Directory> mDirectory;
   nsCOMPtr<nsIFile> mTargetPath;
   bool mRecursiveFlag;
 
-  // We store the fullpath of Files.
-  FallibleTArray<nsString> mTargetData;
+  // We store the fullpath and the dom path of Files.
+  struct FileData {
+    nsString mRealPath;
+    nsString mDOMPath;
+  };
+
+  FallibleTArray<FileData> mTargetData;
 };
 
 class GetFilesTaskParent final : public FileSystemTaskParentBase
 {
 public:
   static already_AddRefed<GetFilesTaskParent>
   Create(FileSystemBase* aFileSystem,
          const FileSystemGetFilesParams& aParam,
@@ -79,21 +87,27 @@ private:
 
   virtual FileSystemResponseValue
   GetSuccessRequestResult(ErrorResult& aRv) const override;
 
   virtual nsresult
   IOWork() override;
 
   nsresult
-  ExploreDirectory(nsIFile* aPath);
+  ExploreDirectory(const nsAString& aDOMPath, nsIFile* aPath);
 
+  nsString mDirectoryDOMPath;
   nsCOMPtr<nsIFile> mTargetPath;
   bool mRecursiveFlag;
 
-  // We store the fullpath of Files.
-  FallibleTArray<nsString> mTargetData;
+  // We store the fullpath and the dom path of Files.
+  struct FileData {
+    nsString mRealPath;
+    nsString mDOMPath;
+  };
+
+  FallibleTArray<FileData> mTargetData;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_GetFilesTask_h
--- a/dom/filesystem/PFileSystemParams.ipdlh
+++ b/dom/filesystem/PFileSystemParams.ipdlh
@@ -43,16 +43,17 @@ struct FileSystemGetDirectoryListingPara
   // each nsString that it contains.
   nsString filters;
 };
 
 struct FileSystemGetFilesParams
 {
   nsString filesystem;
   nsString realPath;
+  nsString domPath;
   bool recursiveFlag;
 };
 
 struct FileSystemGetFileOrDirectoryParams
 {
   nsString filesystem;
   nsString realPath;
 };
--- a/dom/filesystem/PFileSystemRequest.ipdl
+++ b/dom/filesystem/PFileSystemRequest.ipdl
@@ -8,16 +8,17 @@ include protocol PBackground;
 include protocol PBlob;
 
 namespace mozilla {
 namespace dom {
 
 struct FileSystemFileResponse
 {
   nsString realPath;
+  nsString domPath;
 };
 
 struct FileSystemDirectoryResponse
 {
   nsString realPath;
 };
 
 struct FileSystemDirectoryListingResponseFile
--- a/dom/filesystem/moz.build
+++ b/dom/filesystem/moz.build
@@ -39,12 +39,13 @@ IPDL_SOURCES += [
     'PFileSystemParams.ipdlh',
     'PFileSystemRequest.ipdl',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 LOCAL_INCLUDES += [
     '/dom/base',
+    '/dom/workers',
 ]
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wshadow']
--- a/dom/filesystem/tests/filesystem_commons.js
+++ b/dom/filesystem/tests/filesystem_commons.js
@@ -1,10 +1,11 @@
 function setup_tests(aNext) {
-  SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true]]}, aNext);
+  SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true],
+                                     ["dom.webkitBlink.dirPicker.enabled", true]]}, aNext);
 }
 
 function test_basic(aDirectory, aNext) {
   ok(aDirectory, "Directory exists.");
   ok(aDirectory instanceof Directory, "We have a directory.");
   is(aDirectory.path, '/' + aDirectory.name, "directory.path must be '/'+name");
   aNext();
 }
@@ -16,16 +17,20 @@ function test_getFilesAndDirectories(aDi
         for (var i = 0; i < data.length; ++i) {
           ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories");
           if (data[i] instanceof Directory) {
             isnot(data[i].name, '/', "Subdirectory should be called with the leafname");
             isnot(data[i].path, '/', "Subdirectory path should be called with the leafname");
             isnot(data[i].path, dir.path, "Subdirectory path should contain the parent path.");
             is(data[i].path, dir.path + '/' + data[i].name, "Subdirectory path should be called parentdir.path + '/' + leafname");
           }
+
+          if (data[i] instanceof File) {
+            is(data[i].webkitRelativePath, dir.path + '/' + data[i].name, "File.webkitRelativePath should be called: parentdir.path + '/' + file.name");
+          }
         }
       }
     );
   }
 
   aDirectory.getFilesAndDirectories().then(
     function(data) {
       ok(data.length, "We should have some data.");
@@ -34,31 +39,38 @@ function test_getFilesAndDirectories(aDi
         ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories: " + data[i].name);
         if (data[i] instanceof Directory) {
           isnot(data[i].name, '/', "Subdirectory should be called with the leafname");
           is(data[i].path, aDirectory.path + '/' + data[i].name, "Subdirectory path should be called parentdir.path + '/' + leafname");
           if (aRecursive) {
             promises.push(checkSubDir(data[i]));
           }
         }
+
+        if (data[i] instanceof File) {
+          is(data[i].webkitRelativePath, aDirectory.path + '/' + data[i].name, "File.webkitRelativePath should be called '/' + file.name");
+        }
       }
 
       return Promise.all(promises);
     },
     function() {
       ok(false, "Something when wrong");
     }
   ).then(aNext);
 }
 
 function test_getFiles(aDirectory, aRecursive, aNext) {
   aDirectory.getFiles(aRecursive).then(
     function(data) {
       for (var i = 0; i < data.length; ++i) {
-        ok (data[i] instanceof File, "File: " + data[i].name);
+        ok(data[i] instanceof File, "File: " + data[i].name);
+        ok(data[i].webkitRelativePath.indexOf(aDirectory.path) == 0 &&
+           data[i].webkitRelativePath.indexOf('/' + data[i].name) + ('/' + data[i].name).length == data[i].webkitRelativePath.length,
+           "File.webkitRelativePath should be called dir.path + '/' + file.name: " + data[i].webkitRelativePath);
       }
     },
     function() {
       ok(false, "Something when wrong");
     }
   ).then(aNext);
 }
 
--- a/dom/filesystem/tests/script_fileList.js
+++ b/dom/filesystem/tests/script_fileList.js
@@ -63,8 +63,20 @@ addMessageListener("dir.open", function 
       testFile = createTestFile();
       break;
   }
 
   sendAsyncMessage("dir.opened", {
     dir: testFile.path
   });
 });
+
+addMessageListener("file.open", function (e) {
+  var testFile = Cc["@mozilla.org/file/directory_service;1"]
+                   .getService(Ci.nsIDirectoryService)
+                   .QueryInterface(Ci.nsIProperties)
+                   .get("ProfD", Ci.nsIFile);
+  testFile.append("prefs.js");
+
+  sendAsyncMessage("file.opened", {
+    file: new File(testFile)
+  });
+});
--- a/dom/filesystem/tests/test_basic.html
+++ b/dom/filesystem/tests/test_basic.html
@@ -30,32 +30,55 @@ function create_fileList(aPath) {
       next();
     });
   }
 
   script.addMessageListener("dir.opened", onOpened);
   script.sendAsyncMessage("dir.open", { path: aPath });
 }
 
+function test_simpleFilePicker(aPath) {
+  var url = SimpleTest.getTestFileURL("script_fileList.js");
+  var script = SpecialPowers.loadChromeScript(url);
+
+  function onOpened(message) {
+    var fileList = document.getElementById('fileList');
+    SpecialPowers.wrap(fileList).mozSetFileArray([message.file]);
+
+    is(fileList.files.length, 1, "we want 1 element");
+    ok(fileList.files[0] instanceof File, "we want 1 file");
+    ok("webkitRelativePath" in fileList.files[0], "we have webkitRelativePath attribute");
+    is(fileList.files[0].webkitRelativePath, "", "No webkit relative path for normal filePicker");
+
+    script.destroy();
+    next();
+  }
+
+  script.addMessageListener("file.opened", onOpened);
+  script.sendAsyncMessage("file.open");
+}
+
 var tests = [
   function() { setup_tests(next); },
 
   function() { create_fileList('ProfD') },
   function() { test_basic(directory, next); },
   function() { test_getFilesAndDirectories(directory, true, next); },
   function() { test_getFiles(directory, false, next); },
   function() { test_getFiles(directory, true, next); },
 
   function() { create_fileList('test') },
   function() { test_getFiles_recursiveComparison(directory, next); },
 
   function() { create_fileList('root'); },
   function() { test_basic(directory, next); },
   function() { test_getFilesAndDirectories(directory, false, next); },
   function() { test_getFiles(directory, false, next); },
+
+  test_simpleFilePicker
 ];
 
 function next() {
   if (!tests.length) {
     SimpleTest.finish();
     return;
   }
 
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -6,16 +6,17 @@
 
 #include "mozilla/dom/HTMLInputElement.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/Date.h"
 #include "mozilla/dom/Directory.h"
+#include "mozilla/dom/FileSystemUtils.h"
 #include "nsAttrValueInlines.h"
 #include "nsCRTGlue.h"
 
 #include "nsIDOMHTMLInputElement.h"
 #include "nsITextControlElement.h"
 #include "nsIDOMNSEditableElement.h"
 #include "nsIRadioVisitor.h"
 #include "nsIPhonetic.h"
@@ -283,17 +284,28 @@ class HTMLInputElementState final : publ
 
     void SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& aArray)
     {
       mBlobImplsOrDirectoryPaths.Clear();
       for (uint32_t i = 0; i < aArray.Length(); ++i) {
         if (aArray[i].IsFile()) {
           BlobImplOrDirectoryPath* data = mBlobImplsOrDirectoryPaths.AppendElement();
 
-          data->mBlobImpl = aArray[i].GetAsFile()->Impl();
+          RefPtr<File> file = aArray[i].GetAsFile();
+
+          nsAutoString name;
+          file->GetName(name);
+
+          nsAutoString path;
+          path.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
+          path.Append(name);
+
+          file->SetPath(path);
+
+          data->mBlobImpl = file->Impl();
           data->mType = 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;
           }
--- a/dom/html/nsFormSubmission.cpp
+++ b/dom/html/nsFormSubmission.cpp
@@ -31,21 +31,23 @@
 #include "nsIMIMEService.h"
 #include "nsIConsoleService.h"
 #include "nsIScriptError.h"
 #include "nsIStringBundle.h"
 #include "nsCExternalHandlerService.h"
 #include "nsIFileStreams.h"
 #include "nsContentUtils.h"
 
+#include "mozilla/dom/Directory.h"
 #include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/dom/File.h"
 
 using namespace mozilla;
 using mozilla::dom::Blob;
+using mozilla::dom::Directory;
 using mozilla::dom::EncodingUtils;
 using mozilla::dom::File;
 
 static void
 SendJSWarning(nsIDocument* aDocument,
               const char* aWarningName,
               const char16_t** aWarningArgs, uint32_t aWarningArgsLen)
 {
@@ -459,31 +461,29 @@ nsFSMultipartFormData::AddNameBlobOrNull
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString filename;
   nsAutoCString contentType;
   nsCOMPtr<nsIInputStream> fileStream;
 
   if (aBlob) {
     nsAutoString filename16;
-    RetrieveFileName(aBlob, filename16);
 
-    ErrorResult error;
-    nsAutoString filepath16;
     RefPtr<File> file = aBlob->ToFile();
     if (file) {
-      file->GetPath(filepath16, error);
-      if (NS_WARN_IF(error.Failed())) {
-        return error.StealNSResult();
+      nsAutoString path;
+      file->GetPath(path);
+      if (Directory::WebkitBlinkDirectoryPickerEnabled(nullptr, nullptr) &&
+          !path.IsEmpty()) {
+        filename16 = path;
       }
-    }
 
-    if (!filepath16.IsEmpty()) {
-      // File.path includes trailing "/"
-      filename16 = filepath16 + filename16;
+      if (filename16.IsEmpty()) {
+        RetrieveFileName(aBlob, filename16);
+      }
     }
 
     rv = EncodeVal(filename16, filename, true);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Get content type
     nsAutoString contentType16;
     aBlob->GetType(contentType16);
@@ -492,16 +492,17 @@ nsFSMultipartFormData::AddNameBlobOrNull
     }
 
     contentType.Adopt(nsLinebreakConverter::
                       ConvertLineBreaks(NS_ConvertUTF16toUTF8(contentType16).get(),
                                         nsLinebreakConverter::eLinebreakAny,
                                         nsLinebreakConverter::eLinebreakSpace));
 
     // Get input stream
+    ErrorResult error;
     aBlob->GetInternalStream(getter_AddRefs(fileStream), error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
 
     if (fileStream) {
       // Create buffered stream (for efficiency)
       nsCOMPtr<nsIInputStream> bufferedStream;
--- a/dom/indexedDB/FileSnapshot.h
+++ b/dom/indexedDB/FileSnapshot.h
@@ -56,19 +56,25 @@ private:
   // BlobImpl
   virtual void
   GetName(nsAString& aName) const override
   {
     mBlobImpl->GetName(aName);
   }
 
   virtual void
-  GetPath(nsAString& aPath, ErrorResult& aRv) override
+  GetPath(nsAString& aPath) const override
   {
-    mBlobImpl->GetPath(aPath, aRv);
+    mBlobImpl->GetPath(aPath);
+  }
+
+  virtual void
+  SetPath(const nsAString& aPath) override
+  {
+    mBlobImpl->SetPath(aPath);
   }
 
   virtual int64_t
   GetLastModified(ErrorResult& aRv) override
   {
     return mBlobImpl->GetLastModified(aRv);
   }
 
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -1919,17 +1919,20 @@ public:
   NoteDyingActor();
 
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual void
   GetName(nsAString& aName) const override;
 
   virtual void
-  GetPath(nsAString& aPath, ErrorResult& aRv) override;
+  GetPath(nsAString& aPath) const override;
+
+  virtual void
+  SetPath(const nsAString& aPath) override;
 
   virtual int64_t
   GetLastModified(ErrorResult& aRv) override;
 
   virtual void
   SetLastModified(int64_t aLastModified) override;
 
   virtual void
@@ -2585,19 +2588,26 @@ void
 BlobParent::
 RemoteBlobImpl::GetName(nsAString& aName) const
 {
   mBlobImpl->GetName(aName);
 }
 
 void
 BlobParent::
-RemoteBlobImpl::GetPath(nsAString& aPath, ErrorResult& aRv)
+RemoteBlobImpl::GetPath(nsAString& aPath) const
 {
-  mBlobImpl->GetPath(aPath, aRv);
+  mBlobImpl->GetPath(aPath);
+}
+
+void
+BlobParent::
+RemoteBlobImpl::SetPath(const nsAString& aPath)
+{
+  mBlobImpl->SetPath(aPath);
 }
 
 int64_t
 BlobParent::
 RemoteBlobImpl::GetLastModified(ErrorResult& aRv)
 {
   return mBlobImpl->GetLastModified(aRv);
 }
--- a/dom/webidl/File.webidl
+++ b/dom/webidl/File.webidl
@@ -39,14 +39,14 @@ dictionary ChromeFilePropertyBag : FileP
 };
 
 // Mozilla extensions
 partial interface File {
 
   [GetterThrows]
   readonly attribute Date lastModifiedDate;
 
-  [GetterThrows, ChromeOnly]
-  readonly attribute DOMString path;
+  [BinaryName="path", Func="mozilla::dom::Directory::WebkitBlinkDirectoryPickerEnabled"]
+  readonly attribute DOMString webkitRelativePath;
 
   [GetterThrows, ChromeOnly]
   readonly attribute DOMString mozFullPath;
 };
--- a/dom/workers/WorkerPrefs.h
+++ b/dom/workers/WorkerPrefs.h
@@ -30,16 +30,17 @@ WORKER_SIMPLE_PREF("dom.performance.enab
 WORKER_SIMPLE_PREF("dom.webnotifications.enabled", DOMWorkerNotificationEnabled, DOM_WORKERNOTIFICATION)
 WORKER_SIMPLE_PREF("dom.webnotifications.serviceworker.enabled", DOMServiceWorkerNotificationEnabled, DOM_SERVICEWORKERNOTIFICATION)
 WORKER_SIMPLE_PREF("dom.serviceWorkers.enabled", ServiceWorkersEnabled, SERVICEWORKERS_ENABLED)
 WORKER_SIMPLE_PREF("dom.serviceWorkers.testing.enabled", ServiceWorkersTestingEnabled, SERVICEWORKERS_TESTING_ENABLED)
 WORKER_SIMPLE_PREF("dom.serviceWorkers.openWindow.enabled", OpenWindowEnabled, OPEN_WINDOW_ENABLED)
 WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED)
 WORKER_SIMPLE_PREF("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED)
 WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED)
+WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK)
 WORKER_PREF("dom.workers.latestJSVersion", JSVersionChanged)
 WORKER_PREF("intl.accept_languages", PrefLanguagesChanged)
 WORKER_PREF("general.appname.override", AppNameOverrideChanged)
 WORKER_PREF("general.appversion.override", AppVersionOverrideChanged)
 WORKER_PREF("general.platform.override", PlatformOverrideChanged)
 #ifdef JS_GC_ZEAL
 WORKER_PREF("dom.workers.options.gcZeal", LoadGCZealOptions)
 #endif