Bug 1344415 - r=haik, a=al
authorAndrea Marchesini <amarchesini@mozilla.com>
Fri, 24 Mar 2017 15:15:10 +0100
changeset 395391 076416de288687037559442ec1dbe2987752da66
parent 395390 e4c10d4f6b3ec4101824421749b104b17e942f0b
child 395392 009991b7cc4d0b98344564c71a4215830a97a0d4
push id1468
push userasasaki@mozilla.com
push dateMon, 05 Jun 2017 19:31:07 +0000
treeherdermozilla-release@0641fc6ee9d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershaik, al
bugs1344415
milestone54.0a2
Bug 1344415 - r=haik, a=al Entries API gives access to the filesystem. With this patch we allow that only to patches, previously shared by a FilePicker or a DataTransfer event.
dom/base/nsContentUtils.cpp
dom/filesystem/FileSystemRequestParent.cpp
dom/filesystem/FileSystemSecurity.cpp
dom/filesystem/FileSystemSecurity.h
dom/filesystem/FileSystemTaskBase.h
dom/filesystem/FileSystemUtils.cpp
dom/filesystem/FileSystemUtils.h
dom/filesystem/GetDirectoryListingTask.cpp
dom/filesystem/GetDirectoryListingTask.h
dom/filesystem/GetFileOrDirectoryTask.cpp
dom/filesystem/GetFileOrDirectoryTask.h
dom/filesystem/GetFilesTask.cpp
dom/filesystem/GetFilesTask.h
dom/filesystem/compat/tests/test_basic.html
dom/filesystem/compat/tests/test_formSubmission.html
dom/filesystem/compat/tests/test_no_dnd.html
dom/filesystem/moz.build
dom/filesystem/tests/filesystem_commons.js
dom/ipc/ContentParent.cpp
dom/ipc/FilePickerParent.cpp
ipc/glue/BackgroundParentImpl.cpp
ipc/glue/BackgroundParentImpl.h
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -37,16 +37,17 @@
 #include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/CustomElementRegistry.h"
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/DOMTypes.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/FileSystemSecurity.h"
 #include "mozilla/dom/FileBlobImpl.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLTemplateElement.h"
 #include "mozilla/dom/HTMLContentElement.h"
 #include "mozilla/dom/HTMLShadowElement.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/dom/Promise.h"
@@ -7884,16 +7885,29 @@ nsContentUtils::TransferableToIPCTransfe
 
                 Shmem dataAsShmem = ConvertToShmem(aChild, aParent, data);
                 item->data() = dataAsShmem;
               }
 
               continue;
             }
 
+            if (aParent) {
+              bool isDir = false;
+              if (NS_SUCCEEDED(file->IsDirectory(&isDir)) && isDir) {
+                nsAutoString path;
+                if (NS_WARN_IF(NS_FAILED(file->GetPath(path)))) {
+                  continue;
+                }
+
+                RefPtr<FileSystemSecurity> fss = FileSystemSecurity::GetOrCreate();
+                fss->GrantAccessToContentProcess(aParent->ChildID(), path);
+              }
+            }
+
             blobImpl = new FileBlobImpl(file);
 
             IgnoredErrorResult rv;
 
             // Ensure that file data is cached no that the content process
             // has this data available to it when passed over:
             blobImpl->GetSize(rv);
             if (NS_WARN_IF(rv.Failed())) {
--- a/dom/filesystem/FileSystemRequestParent.cpp
+++ b/dom/filesystem/FileSystemRequestParent.cpp
@@ -5,18 +5,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/PFileSystemParams.h"
 
 #include "GetDirectoryListingTask.h"
 #include "GetFileOrDirectoryTask.h"
 
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/FileSystemBase.h"
+#include "mozilla/dom/FileSystemSecurity.h"
 #include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/Unused.h"
+#include "nsProxyRelease.h"
 
 using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace dom {
 
 FileSystemRequestParent::FileSystemRequestParent()
   : mDestroyed(false)
@@ -64,24 +68,114 @@ FileSystemRequestParent::Initialize(cons
   if (NS_WARN_IF(!mTask || !mFileSystem)) {
     // Should never reach here.
     return false;
   }
 
   return true;
 }
 
+namespace {
+
+class CheckPermissionRunnable final : public Runnable
+{
+public:
+  CheckPermissionRunnable(already_AddRefed<ContentParent> aParent,
+                          FileSystemRequestParent* aActor,
+                          FileSystemTaskParentBase* aTask,
+                          const nsAString& aPath)
+    : mContentParent(aParent)
+    , mActor(aActor)
+    , mTask(aTask)
+    , mPath(aPath)
+    , mBackgroundEventTarget(NS_GetCurrentThread())
+  {
+    AssertIsInMainProcess();
+    AssertIsOnBackgroundThread();
+
+    MOZ_ASSERT(mContentParent);
+    MOZ_ASSERT(mActor);
+    MOZ_ASSERT(mTask);
+    MOZ_ASSERT(mBackgroundEventTarget);
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    if (NS_IsMainThread()) {
+      auto raii = mozilla::MakeScopeExit([&] { mContentParent = nullptr; });
+
+
+      if (!mozilla::Preferences::GetBool("dom.filesystem.pathcheck.disabled", false)) {
+        RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
+        if (NS_WARN_IF(!fss ||
+                       !fss->ContentProcessHasAccessTo(mContentParent->ChildID(),
+                                                       mPath))) {
+          mContentParent->KillHard("This path is not allowed.");
+          return NS_OK;
+        }
+      }
+
+      return mBackgroundEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+    }
+
+    AssertIsOnBackgroundThread();
+
+    // It can happen that this actor has been destroyed in the meantime we were
+    // on the main-thread.
+    if (!mActor->Destroyed()) {
+      mTask->Start();
+    }
+
+    return NS_OK;
+  }
+
+private:
+  ~CheckPermissionRunnable()
+  {
+     NS_ProxyRelease(mBackgroundEventTarget, mActor.forget());
+  }
+
+  RefPtr<ContentParent> mContentParent;
+  RefPtr<FileSystemRequestParent> mActor;
+  RefPtr<FileSystemTaskParentBase> mTask;
+  const nsString mPath;
+
+  nsCOMPtr<nsIEventTarget> mBackgroundEventTarget;
+};
+
+} // anonymous
+
 void
 FileSystemRequestParent::Start()
 {
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(mFileSystem);
   MOZ_ASSERT(mTask);
 
-  mTask->Start();
+  nsAutoString path;
+  if (NS_WARN_IF(NS_FAILED(mTask->GetTargetPath(path)))) {
+    Unused << Send__delete__(this, FileSystemErrorResponse(NS_ERROR_DOM_SECURITY_ERR));
+    return;
+  }
+
+  RefPtr<ContentParent> parent = BackgroundParent::GetContentParent(Manager());
+
+  // If the ContentParent is null we are dealing with a same-process actor.
+  if (!parent) {
+    mTask->Start();
+    return;
+  }
+
+  RefPtr<Runnable> runnable =
+    new CheckPermissionRunnable(parent.forget(), this, mTask, path);
+  NS_DispatchToMainThread(runnable);
 }
 
 void
 FileSystemRequestParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!mDestroyed);
 
new file mode 100644
--- /dev/null
+++ b/dom/filesystem/FileSystemSecurity.cpp
@@ -0,0 +1,107 @@
+/* -*- 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 "FileSystemSecurity.h"
+#include "FileSystemUtils.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/StaticPtr.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+
+StaticRefPtr<FileSystemSecurity> gFileSystemSecurity;
+
+} // anonymous
+
+/* static */ already_AddRefed<FileSystemSecurity>
+FileSystemSecurity::Get()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+
+  RefPtr<FileSystemSecurity> service = gFileSystemSecurity.get();
+  return service.forget();
+}
+
+/* static */ already_AddRefed<FileSystemSecurity>
+FileSystemSecurity::GetOrCreate()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+
+  if (!gFileSystemSecurity) {
+    gFileSystemSecurity = new FileSystemSecurity();
+    ClearOnShutdown(&gFileSystemSecurity);
+  }
+
+  RefPtr<FileSystemSecurity> service = gFileSystemSecurity.get();
+  return service.forget();
+}
+
+FileSystemSecurity::FileSystemSecurity()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+}
+
+FileSystemSecurity::~FileSystemSecurity()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+}
+
+void
+FileSystemSecurity::GrantAccessToContentProcess(ContentParentId aId,
+                                                const nsAString& aDirectoryPath)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+
+  nsTArray<nsString>* paths;
+  if (!mPaths.Get(aId, &paths)) {
+    paths = new nsTArray<nsString>();
+    mPaths.Put(aId, paths);
+  } else if (paths->Contains(aDirectoryPath)) {
+    return;
+  }
+
+  paths->AppendElement(aDirectoryPath);
+}
+
+void
+FileSystemSecurity::Forget(ContentParentId aId)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+
+  mPaths.Remove(aId);
+}
+
+bool
+FileSystemSecurity::ContentProcessHasAccessTo(ContentParentId aId,
+                                              const nsAString& aPath)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+
+  nsTArray<nsString>* paths;
+  if (!mPaths.Get(aId, &paths)) {
+    return false;
+  }
+
+  for (uint32_t i = 0, len = paths->Length(); i < len; ++i) {
+    if (FileSystemUtils::IsDescendantPath(paths->ElementAt(i), aPath)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/filesystem/FileSystemSecurity.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_FileSystemSecurity_h
+#define mozilla_dom_FileSystemSecurity_h
+
+#include "mozilla/dom/ipc/IdType.h"
+#include "nsClassHashtable.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace dom {
+
+class FileSystemSecurity final
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(FileSystemSecurity)
+
+  static already_AddRefed<FileSystemSecurity>
+  Get();
+
+  static already_AddRefed<FileSystemSecurity>
+  GetOrCreate();
+
+  void
+  GrantAccessToContentProcess(ContentParentId aId,
+                              const nsAString& aDirectoryPath);
+
+  void
+  Forget(ContentParentId aId);
+
+  bool
+  ContentProcessHasAccessTo(ContentParentId aId, const nsAString& aPath);
+
+private:
+  FileSystemSecurity();
+  ~FileSystemSecurity();
+
+  nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mPaths;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_FileSystemSecurity_h
--- a/dom/filesystem/FileSystemTaskBase.h
+++ b/dom/filesystem/FileSystemTaskBase.h
@@ -237,16 +237,19 @@ public:
   MainThreadWork();
 
   bool
   HasError() const { return NS_FAILED(mErrorValue); }
 
   NS_IMETHOD
   Run() override;
 
+  virtual nsresult
+  GetTargetPath(nsAString& aPath) const = 0;
+
 private:
   /*
    * Wrap the task result to FileSystemResponseValue for sending it through IPC.
    * It will be called when the task is completed and we need to
    * send the task result back to the content. This runs on the PBackground
    * thread.
    */
   FileSystemResponseValue
--- a/dom/filesystem/FileSystemUtils.cpp
+++ b/dom/filesystem/FileSystemUtils.cpp
@@ -15,34 +15,22 @@ bool
 TokenizerIgnoreNothing(char16_t /* aChar */)
 {
   return false;
 }
 
 } // anonymous namespace
 
 /* static */ bool
-FileSystemUtils::IsDescendantPath(nsIFile* aFile,
-                                  nsIFile* aDescendantFile)
+FileSystemUtils::IsDescendantPath(const nsAString& aPath,
+                                  const nsAString& aDescendantPath)
 {
-  nsAutoString path;
-  nsresult rv = aFile->GetPath(path);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return false;
-  }
-
-  nsAutoString descendantPath;
-  rv = aDescendantFile->GetPath(descendantPath);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return false;
-  }
-
   // Check the sub-directory path to see if it has the parent path as prefix.
-  if (descendantPath.Length() <= path.Length() ||
-      !StringBeginsWith(descendantPath, path)) {
+  if (!aDescendantPath.Equals(aPath) &&
+      !StringBeginsWith(aDescendantPath, aPath)) {
     return false;
   }
 
   return true;
 }
 
 /* static */ bool
 FileSystemUtils::IsValidRelativeDOMPath(const nsAString& aPath,
--- a/dom/filesystem/FileSystemUtils.h
+++ b/dom/filesystem/FileSystemUtils.h
@@ -21,17 +21,18 @@ namespace dom {
  */
 class FileSystemUtils
 {
 public:
   /*
    * Return true if aDescendantPath is a descendant of aPath.
    */
   static bool
-  IsDescendantPath(nsIFile* aPath, nsIFile* aDescendantPath);
+  IsDescendantPath(const nsAString& aPath,
+                   const nsAString& aDescendantPath);
 
   /**
    * Return true if this is valid DOMPath. It also splits the path in
    * subdirectories and stores them in aParts.
    */
   static bool
   IsValidRelativeDOMPath(const nsAString& aPath,
                          nsTArray<nsString>& aParts);
--- a/dom/filesystem/GetDirectoryListingTask.cpp
+++ b/dom/filesystem/GetDirectoryListingTask.cpp
@@ -375,10 +375,16 @@ GetDirectoryListingTaskParent::IOWork()
 
     if (!mTargetData.AppendElement(element, fallible)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
   return NS_OK;
 }
 
+nsresult
+GetDirectoryListingTaskParent::GetTargetPath(nsAString& aPath) const
+{
+  return mTargetPath->GetPath(aPath);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/GetDirectoryListingTask.h
+++ b/dom/filesystem/GetDirectoryListingTask.h
@@ -64,16 +64,19 @@ class GetDirectoryListingTaskParent fina
 {
 public:
   static already_AddRefed<GetDirectoryListingTaskParent>
   Create(FileSystemBase* aFileSystem,
          const FileSystemGetDirectoryListingParams& aParam,
          FileSystemRequestParent* aParent,
          ErrorResult& aRv);
 
+  nsresult
+  GetTargetPath(nsAString& aPath) const override;
+
 private:
   GetDirectoryListingTaskParent(FileSystemBase* aFileSystem,
                                 const FileSystemGetDirectoryListingParams& aParam,
                                 FileSystemRequestParent* aParent);
 
   virtual FileSystemResponseValue
   GetSuccessRequestResult(ErrorResult& aRv) const override;
 
--- a/dom/filesystem/GetFileOrDirectoryTask.cpp
+++ b/dom/filesystem/GetFileOrDirectoryTask.cpp
@@ -265,10 +265,16 @@ GetFileOrDirectoryTaskParent::IOWork()
 
   if (!mFileSystem->IsSafeFile(mTargetPath)) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   return NS_OK;
 }
 
+nsresult
+GetFileOrDirectoryTaskParent::GetTargetPath(nsAString& aPath) const
+{
+  return mTargetPath->GetPath(aPath);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/GetFileOrDirectoryTask.h
+++ b/dom/filesystem/GetFileOrDirectoryTask.h
@@ -57,16 +57,19 @@ class GetFileOrDirectoryTaskParent final
 {
 public:
   static already_AddRefed<GetFileOrDirectoryTaskParent>
   Create(FileSystemBase* aFileSystem,
          const FileSystemGetFileOrDirectoryParams& aParam,
          FileSystemRequestParent* aParent,
          ErrorResult& aRv);
 
+  nsresult
+  GetTargetPath(nsAString& aPath) const override;
+
 protected:
   virtual FileSystemResponseValue
   GetSuccessRequestResult(ErrorResult& aRv) const override;
 
   virtual nsresult
   IOWork() override;
 
 private:
--- a/dom/filesystem/GetFilesTask.cpp
+++ b/dom/filesystem/GetFilesTask.cpp
@@ -248,10 +248,16 @@ GetFilesTaskParent::IOWork()
   rv = ExploreDirectory(mDirectoryDOMPath, mTargetPath);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
+nsresult
+GetFilesTaskParent::GetTargetPath(nsAString& aPath) const
+{
+  return mTargetPath->GetPath(aPath);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/GetFilesTask.h
+++ b/dom/filesystem/GetFilesTask.h
@@ -66,16 +66,19 @@ class GetFilesTaskParent final : public 
 {
 public:
   static already_AddRefed<GetFilesTaskParent>
   Create(FileSystemBase* aFileSystem,
          const FileSystemGetFilesParams& aParam,
          FileSystemRequestParent* aParent,
          ErrorResult& aRv);
 
+  nsresult
+  GetTargetPath(nsAString& aPath) const override;
+
 private:
   GetFilesTaskParent(FileSystemBase* aFileSystem,
                      const FileSystemGetFilesParams& aParam,
                      FileSystemRequestParent* aParent);
 
   virtual FileSystemResponseValue
   GetSuccessRequestResult(ErrorResult& aRv) const override;
 
--- a/dom/filesystem/compat/tests/test_basic.html
+++ b/dom/filesystem/compat/tests/test_basic.html
@@ -11,16 +11,17 @@
 <script type="application/javascript">
 
 var fileEntry;
 var directoryEntry;
 var script;
 
 function setup_tests() {
   SpecialPowers.pushPrefEnv({"set": [["dom.webkitBlink.dirPicker.enabled", true],
+                                     ["dom.filesystem.pathcheck.disabled", true],
                                      ["dom.webkitBlink.filesystem.enabled", true]]}, next);
 }
 
 function populate_entries() {
   var url = SimpleTest.getTestFileURL("script_entries.js");
   script = SpecialPowers.loadChromeScript(url);
 
   function onOpened(message) {
--- a/dom/filesystem/compat/tests/test_formSubmission.html
+++ b/dom/filesystem/compat/tests/test_formSubmission.html
@@ -27,16 +27,17 @@ function setup_tests() {
   iframe = document.getElementById("target_iframe");
   iframe.onload = function() {
     info("Frame loaded!");
     next();
   }
 
   SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true],
                                      ["dom.webkitBlink.dirPicker.enabled", true],
+                                     ["dom.filesystem.pathcheck.disabled", true],
                                      ["dom.webkitBlink.filesystem.enabled", true]]}, next);
 }
 
 function populate_entries(webkitDirectory) {
   if (input) {
     form.removeChild(input);
   }
 
--- a/dom/filesystem/compat/tests/test_no_dnd.html
+++ b/dom/filesystem/compat/tests/test_no_dnd.html
@@ -11,16 +11,17 @@
 
 var fileEntry;
 var directoryEntry;
 var script;
 var entries;
 
 function setup_tests() {
   SpecialPowers.pushPrefEnv({"set": [["dom.webkitBlink.dirPicker.enabled", true],
+                                     ["dom.filesystem.pathcheck.disabled", true],
                                      ["dom.webkitBlink.filesystem.enabled", true]]}, next);
 }
 
 function populate_entries() {
   entries = document.createElement('input');
   entries.setAttribute('type', 'file');
   document.body.appendChild(entries);
 
--- a/dom/filesystem/moz.build
+++ b/dom/filesystem/moz.build
@@ -10,26 +10,28 @@ with Files("**"):
 DIRS += ['compat']
 
 TEST_DIRS += ['tests']
 
 EXPORTS.mozilla.dom += [
     'Directory.h',
     'FileSystemBase.h',
     'FileSystemRequestParent.h',
+    'FileSystemSecurity.h',
     'FileSystemTaskBase.h',
     'FileSystemUtils.h',
     'GetFilesHelper.h',
     'OSFileSystem.h',
 ]
 
 UNIFIED_SOURCES += [
     'Directory.cpp',
     'FileSystemBase.cpp',
     'FileSystemRequestParent.cpp',
+    'FileSystemSecurity.cpp',
     'FileSystemTaskBase.cpp',
     'FileSystemUtils.cpp',
     'GetDirectoryListingTask.cpp',
     'GetFileOrDirectoryTask.cpp',
     'GetFilesHelper.cpp',
     'GetFilesTask.cpp',
     'OSFileSystem.cpp',
 ]
--- a/dom/filesystem/tests/filesystem_commons.js
+++ b/dom/filesystem/tests/filesystem_commons.js
@@ -6,16 +6,17 @@ function createRelativePath(parentDir, d
   let path = createPath(parentDir, dirOrFile);
   is(path[0], "/", "The full path should start with '/'");
   return path.substring(1);
 }
 
 function setup_tests(aNext) {
   SimpleTest.requestLongerTimeout(2);
   SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true],
+                                     ["dom.filesystem.pathcheck.disabled", 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();
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -36,16 +36,17 @@
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/DataStorage.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileCreatorHelper.h"
+#include "mozilla/dom/FileSystemSecurity.h"
 #include "mozilla/dom/ExternalHelperAppParent.h"
 #include "mozilla/dom/GetFilesHelper.h"
 #include "mozilla/dom/GeolocationBinding.h"
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/dom/Notification.h"
 #include "mozilla/dom/PContentBridgeParent.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
 #include "mozilla/dom/PCycleCollectWithLogsParent.h"
@@ -1678,16 +1679,21 @@ ContentParent::ActorDestroy(ActorDestroy
   // finish waiting in the xpcom-shutdown/profile-before-change observer.
   mIPCOpen = false;
 
   if (mHangMonitorActor) {
     ProcessHangMonitor::RemoveProcess(mHangMonitorActor);
     mHangMonitorActor = nullptr;
   }
 
+  RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
+  if (fss) {
+    fss->Forget(ChildID());
+  }
+
   if (why == NormalShutdown && !mCalledClose) {
     // If we shut down normally but haven't called Close, assume somebody
     // else called Close on us. In that case, we still need to call
     // ShutDownProcess below to perform other necessary clean up.
     mCalledClose = true;
   }
 
   // Make sure we always clean up.
--- a/dom/ipc/FilePickerParent.cpp
+++ b/dom/ipc/FilePickerParent.cpp
@@ -8,16 +8,17 @@
 #include "nsComponentManagerUtils.h"
 #include "nsNetCID.h"
 #include "nsIDocument.h"
 #include "nsIDOMWindow.h"
 #include "nsIFile.h"
 #include "nsISimpleEnumerator.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/FileBlobImpl.h"
+#include "mozilla/dom/FileSystemSecurity.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 
 using mozilla::Unused;
 using namespace mozilla::dom;
 
@@ -140,32 +141,39 @@ void
 FilePickerParent::IORunnable::Destroy()
 {
   mFilePickerParent = nullptr;
 }
 
 void
 FilePickerParent::SendFilesOrDirectories(const nsTArray<BlobImplOrString>& aData)
 {
+  nsIContentParent* parent = TabParent::GetFrom(Manager())->Manager();
+
   if (mMode == nsIFilePicker::modeGetFolder) {
     MOZ_ASSERT(aData.Length() <= 1);
     if (aData.IsEmpty()) {
       Unused << Send__delete__(this, void_t(), mResult);
       return;
     }
 
     MOZ_ASSERT(aData[0].mType == BlobImplOrString::eDirectoryPath);
 
+    // Let's inform the security singleton about the given access of this tab on
+    // this directory path.
+    RefPtr<FileSystemSecurity> fss = FileSystemSecurity::GetOrCreate();
+    fss->GrantAccessToContentProcess(parent->ChildID(),
+                                     aData[0].mDirectoryPath);
+
     InputDirectory input;
     input.directoryPath() = aData[0].mDirectoryPath;
     Unused << Send__delete__(this, input, mResult);
     return;
   }
 
-  nsIContentParent* parent = TabParent::GetFrom(Manager())->Manager();
   InfallibleTArray<PBlobParent*> blobs;
 
   for (unsigned i = 0; i < aData.Length(); i++) {
     MOZ_ASSERT(aData[i].mType == BlobImplOrString::eBlobImpl);
     BlobParent* blobParent = parent->GetOrCreateActorForBlobImpl(aData[i].mBlobImpl);
     if (blobParent) {
       blobs.AppendElement(blobParent);
     }
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -766,19 +766,26 @@ BackgroundParentImpl::AllocPFileSystemRe
   AssertIsOnBackgroundThread();
 
   RefPtr<FileSystemRequestParent> result = new FileSystemRequestParent();
 
   if (NS_WARN_IF(!result->Initialize(aParams))) {
     return nullptr;
   }
 
-  result->Start();
+  return result.forget().take();
+}
 
-  return result.forget().take();
+mozilla::ipc::IPCResult
+BackgroundParentImpl::RecvPFileSystemRequestConstructor(
+                                               PFileSystemRequestParent* aActor,
+                                               const FileSystemParams& params)
+{
+  static_cast<FileSystemRequestParent*>(aActor)->Start();
+  return IPC_OK();
 }
 
 bool
 BackgroundParentImpl::DeallocPFileSystemRequestParent(
                                               PFileSystemRequestParent* aDoomed)
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -182,16 +182,20 @@ protected:
   AllocPQuotaParent() override;
 
   virtual bool
   DeallocPQuotaParent(PQuotaParent* aActor) override;
 
   virtual PFileSystemRequestParent*
   AllocPFileSystemRequestParent(const FileSystemParams&) override;
 
+  virtual mozilla::ipc::IPCResult
+  RecvPFileSystemRequestConstructor(PFileSystemRequestParent* actor,
+                                    const FileSystemParams& params) override;
+
   virtual bool
   DeallocPFileSystemRequestParent(PFileSystemRequestParent*) override;
 
   // Gamepad API Background IPC
   virtual PGamepadEventChannelParent*
   AllocPGamepadEventChannelParent() override;
 
   virtual bool