Bug 910412 - Filesystem API permission request and checks. r=dhylands
authorYuan Xulei <xyuan@mozilla.com>
Wed, 05 Mar 2014 11:24:19 +0800
changeset 173625 1b02211faa78a8df0ac159bae64abc99e1f70214
parent 173624 b1886a252ed6914c8d2c1887242d9b0ae6589a3d
child 173626 b83bb5aa1b252ab74c6129500ef5954963be1ad3
push id26414
push userkwierso@gmail.com
push dateSat, 15 Mar 2014 02:34:24 +0000
treeherdermozilla-central@092d63342910 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdhylands
bugs910412
milestone30.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 910412 - Filesystem API permission request and checks. r=dhylands
dom/filesystem/CreateDirectoryTask.cpp
dom/filesystem/CreateDirectoryTask.h
dom/filesystem/DeviceStorageFileSystem.cpp
dom/filesystem/DeviceStorageFileSystem.h
dom/filesystem/Directory.cpp
dom/filesystem/FileSystemBase.cpp
dom/filesystem/FileSystemBase.h
dom/filesystem/FileSystemPermissionRequest.cpp
dom/filesystem/FileSystemPermissionRequest.h
dom/filesystem/FileSystemRequestParent.cpp
dom/filesystem/FileSystemTaskBase.cpp
dom/filesystem/FileSystemTaskBase.h
dom/filesystem/FileSystemUtils.cpp
dom/filesystem/FileSystemUtils.h
dom/filesystem/GetFileOrDirectoryTask.cpp
dom/filesystem/GetFileOrDirectoryTask.h
dom/filesystem/moz.build
--- a/dom/filesystem/CreateDirectoryTask.cpp
+++ b/dom/filesystem/CreateDirectoryTask.cpp
@@ -126,10 +126,16 @@ CreateDirectoryTask::HandlerCallback()
     mPromise = nullptr;
     return;
   }
   nsRefPtr<Directory> dir = new Directory(mFileSystem, mTargetRealPath);
   mPromise->MaybeResolve(dir);
   mPromise = nullptr;
 }
 
+void
+CreateDirectoryTask::GetPermissionAccessType(nsCString& aAccess) const
+{
+  aAccess.AssignLiteral("create");
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/CreateDirectoryTask.h
+++ b/dom/filesystem/CreateDirectoryTask.h
@@ -26,16 +26,19 @@ public:
                       FileSystemRequestParent* aParent);
 
   virtual
   ~CreateDirectoryTask();
 
   already_AddRefed<Promise>
   GetPromise();
 
+  virtual void
+  GetPermissionAccessType(nsCString& aAccess) const MOZ_OVERRIDE;
+
 protected:
   virtual FileSystemParams
   GetRequestParams(const nsString& aFileSystem) const MOZ_OVERRIDE;
 
   virtual FileSystemResponseValue
   GetSuccessRequestResult() const MOZ_OVERRIDE;
 
   virtual void
--- a/dom/filesystem/DeviceStorageFileSystem.cpp
+++ b/dom/filesystem/DeviceStorageFileSystem.cpp
@@ -2,51 +2,73 @@
 /* vim: set ts=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 "mozilla/dom/DeviceStorageFileSystem.h"
 
 #include "DeviceStorage.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/Directory.h"
 #include "mozilla/dom/FileSystemUtils.h"
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
+#include "nsDeviceStorage.h"
 #include "nsIFile.h"
 #include "nsPIDOMWindow.h"
 
 namespace mozilla {
 namespace dom {
 
 DeviceStorageFileSystem::DeviceStorageFileSystem(
   const nsAString& aStorageType,
   const nsAString& aStorageName)
   : mDeviceStorage(nullptr)
 {
+  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+
   mStorageType = aStorageType;
   mStorageName = aStorageName;
 
   // Generate the string representation of the file system.
   mString.AppendLiteral("devicestorage-");
   mString.Append(mStorageType);
   mString.AppendLiteral("-");
   mString.Append(mStorageName);
 
+  mIsTesting =
+    mozilla::Preferences::GetBool("device.storage.prompt.testing", false);
+
+  // Get the permission name required to access the file system.
+  nsresult rv =
+    DeviceStorageTypeChecker::GetPermissionForType(mStorageType, mPermission);
+  NS_WARN_IF(NS_FAILED(rv));
+
   // Get the local path of the file system root.
   // Since the child process is not allowed to access the file system, we only
   // do this from the parent process.
   if (!FileSystemUtils::IsParentProcess()) {
     return;
   }
   nsCOMPtr<nsIFile> rootFile;
   DeviceStorageFile::GetRootDirectoryForType(aStorageType,
                                              aStorageName,
                                              getter_AddRefs(rootFile));
 
   NS_WARN_IF(!rootFile || NS_FAILED(rootFile->GetPath(mLocalRootPath)));
+  FileSystemUtils::LocalPathToNormalizedPath(mLocalRootPath,
+    mNormalizedLocalRootPath);
+
+  // DeviceStorageTypeChecker is a singleton object and must be initialized on
+  // the main thread. We initialize it here so that we can use it on the worker
+  // thread.
+  DebugOnly<DeviceStorageTypeChecker*> typeChecker
+    = DeviceStorageTypeChecker::CreateOrGet();
+  MOZ_ASSERT(typeChecker);
 }
 
 DeviceStorageFileSystem::~DeviceStorageFileSystem()
 {
 }
 
 void
 DeviceStorageFileSystem::Init(nsDOMDeviceStorage* aDeviceStorage)
@@ -91,10 +113,34 @@ DeviceStorageFileSystem::GetLocalFile(co
 }
 
 const nsAString&
 DeviceStorageFileSystem::GetRootName() const
 {
   return mStorageName;
 }
 
+bool
+DeviceStorageFileSystem::IsSafeFile(nsIFile* aFile) const
+{
+  MOZ_ASSERT(FileSystemUtils::IsParentProcess(),
+             "Should be on parent process!");
+  MOZ_ASSERT(aFile);
+
+  // Check if this file belongs to this storage.
+  nsAutoString path;
+  if (NS_FAILED(aFile->GetPath(path))) {
+    return false;
+  }
+  FileSystemUtils::LocalPathToNormalizedPath(path, path);
+  if (!FileSystemUtils::IsDescendantPath(mNormalizedLocalRootPath, path)) {
+    return false;
+  }
+
+  // Check if the file type is compatible with the storage type.
+  DeviceStorageTypeChecker* typeChecker
+    = DeviceStorageTypeChecker::CreateOrGet();
+  MOZ_ASSERT(typeChecker);
+  return typeChecker->Check(mStorageType, aFile);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/DeviceStorageFileSystem.h
+++ b/dom/filesystem/DeviceStorageFileSystem.h
@@ -33,25 +33,30 @@ public:
   virtual nsPIDOMWindow*
   GetWindow() const MOZ_OVERRIDE;
 
   virtual already_AddRefed<nsIFile>
   GetLocalFile(const nsAString& aRealPath) const MOZ_OVERRIDE;
 
   virtual const nsAString&
   GetRootName() const MOZ_OVERRIDE;
+
+  virtual bool
+  IsSafeFile(nsIFile* aFile) const MOZ_OVERRIDE;
+
 private:
   virtual
   ~DeviceStorageFileSystem();
 
   nsString mStorageType;
   nsString mStorageName;
 
   // The local path of the root. Only available in the parent process.
   // In the child process, we don't use it and its value should be empty.
   nsString mLocalRootPath;
+  nsString mNormalizedLocalRootPath;
   nsDOMDeviceStorage* mDeviceStorage;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_DeviceStorageFileSystem_h
--- a/dom/filesystem/Directory.cpp
+++ b/dom/filesystem/Directory.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=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 "mozilla/dom/Directory.h"
 
 #include "CreateDirectoryTask.h"
+#include "FileSystemPermissionRequest.h"
 #include "GetFileOrDirectoryTask.h"
 
 #include "nsCharSeparatedTokenizer.h"
 #include "nsString.h"
 #include "mozilla/dom/DirectoryBinding.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemUtils.h"
 
@@ -34,17 +35,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 NS_INTERFACE_MAP_END
 
 // static
 already_AddRefed<Promise>
 Directory::GetRoot(FileSystemBase* aFileSystem)
 {
   nsRefPtr<GetFileOrDirectoryTask> task = new GetFileOrDirectoryTask(
     aFileSystem, EmptyString(), true);
-  task->Start();
+  FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
 
 Directory::Directory(FileSystemBase* aFileSystem,
                      const nsAString& aPath)
   : mFileSystem(aFileSystem)
   , mPath(aPath)
 {
@@ -91,32 +92,32 @@ Directory::CreateDirectory(const nsAStri
   nsresult error = NS_OK;
   nsString realPath;
   if (!DOMPathToRealPath(aPath, realPath)) {
     error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
   }
   nsRefPtr<CreateDirectoryTask> task = new CreateDirectoryTask(
     mFileSystem, realPath);
   task->SetError(error);
-  task->Start();
+  FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
 
 already_AddRefed<Promise>
 Directory::Get(const nsAString& aPath)
 {
   nsresult error = NS_OK;
   nsString realPath;
   if (!DOMPathToRealPath(aPath, realPath)) {
     error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
   }
   nsRefPtr<GetFileOrDirectoryTask> task = new GetFileOrDirectoryTask(
     mFileSystem, realPath, false);
   task->SetError(error);
-  task->Start();
+  FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
 
 bool
 Directory::DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const
 {
   aRealPath.Truncate();
 
--- a/dom/filesystem/FileSystemBase.cpp
+++ b/dom/filesystem/FileSystemBase.cpp
@@ -37,16 +37,17 @@ FileSystemBase::FromString(const nsAStri
       new DeviceStorageFileSystem(storageType, storageName);
     return f.forget();
   }
   return nullptr;
 }
 
 FileSystemBase::FileSystemBase()
   : mShutdown(false)
+  , mIsTesting(false)
 {
 }
 
 FileSystemBase::~FileSystemBase()
 {
 }
 
 void
@@ -56,10 +57,16 @@ FileSystemBase::Shutdown()
 }
 
 nsPIDOMWindow*
 FileSystemBase::GetWindow() const
 {
   return nullptr;
 }
 
+bool
+FileSystemBase::IsSafeFile(nsIFile* aFile) const
+{
+  return false;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/FileSystemBase.h
+++ b/dom/filesystem/FileSystemBase.h
@@ -52,21 +52,44 @@ public:
   virtual const nsAString&
   GetRootName() const = 0;
 
   bool
   IsShutdown() const
   {
     return mShutdown;
   }
+
+  virtual bool
+  IsSafeFile(nsIFile* aFile) const;
+
+  /*
+   * Get the permission name required to access this file system.
+   */
+  const nsCString&
+  GetPermission() const
+  {
+    return mPermission;
+  }
+
+  bool
+  IsTesting() const
+  {
+    return mIsTesting;
+  }
 protected:
   virtual ~FileSystemBase();
 
   // The string representation of the file system.
   nsString mString;
 
   bool mShutdown;
+
+  // The permission name required to access the file system.
+  nsCString mPermission;
+
+  bool mIsTesting;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_FileSystemBase_h
new file mode 100644
--- /dev/null
+++ b/dom/filesystem/FileSystemPermissionRequest.cpp
@@ -0,0 +1,190 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "FileSystemPermissionRequest.h"
+
+#include "mozilla/dom/FileSystemBase.h"
+#include "mozilla/dom/FileSystemTaskBase.h"
+#include "mozilla/dom/FileSystemUtils.h"
+#include "mozilla/dom/TabChild.h"
+#include "nsIDocument.h"
+#include "nsPIDOMWindow.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_ISUPPORTS2(FileSystemPermissionRequest, nsIRunnable, nsIContentPermissionRequest)
+
+// static
+void
+FileSystemPermissionRequest::RequestForTask(FileSystemTaskBase* aTask)
+{
+  MOZ_ASSERT(aTask, "aTask should not be null!");
+  MOZ_ASSERT(NS_IsMainThread());
+  nsRefPtr<FileSystemPermissionRequest> request =
+    new FileSystemPermissionRequest(aTask);
+  NS_DispatchToCurrentThread(request);
+}
+
+FileSystemPermissionRequest::FileSystemPermissionRequest(
+  FileSystemTaskBase* aTask)
+  : mTask(aTask)
+{
+  MOZ_ASSERT(mTask, "aTask should not be null!");
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mTask->GetPermissionAccessType(mPermissionAccess);
+
+  nsRefPtr<FileSystemBase> filesystem = mTask->GetFileSystem();
+  if (!filesystem) {
+    return;
+  }
+
+  mPermissionType = filesystem->GetPermission();
+
+  mWindow = filesystem->GetWindow();
+  if (!mWindow) {
+    return;
+  }
+
+  nsCOMPtr<nsIDocument> doc = mWindow->GetDoc();
+  if (!doc) {
+    return;
+  }
+
+  mPrincipal = doc->NodePrincipal();
+}
+
+FileSystemPermissionRequest::~FileSystemPermissionRequest()
+{
+}
+
+bool
+FileSystemPermissionRequest::Recv__delete__(const bool& aAllow,
+               const InfallibleTArray<PermissionChoice>& aChoices)
+{
+  MOZ_ASSERT(aChoices.IsEmpty(),
+             "FileSystemPermissionRequest doesn't support permission choice");
+  if (aAllow) {
+    Allow(JS::UndefinedHandleValue);
+  } else {
+    Cancel();
+  }
+  return true;
+}
+
+void
+FileSystemPermissionRequest::IPDLRelease()
+{
+  Release();
+}
+
+NS_IMETHODIMP
+FileSystemPermissionRequest::GetTypes(nsIArray** aTypes)
+{
+  nsTArray<nsString> emptyOptions;
+  return CreatePermissionArray(mPermissionType,
+                               mPermissionAccess,
+                               emptyOptions,
+                               aTypes);
+}
+
+NS_IMETHODIMP
+FileSystemPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal)
+{
+  NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileSystemPermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow)
+{
+  NS_IF_ADDREF(*aRequestingWindow = mWindow);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileSystemPermissionRequest::GetElement(nsIDOMElement** aRequestingElement)
+{
+  *aRequestingElement = nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileSystemPermissionRequest::Cancel()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  mTask->SetError(NS_ERROR_DOM_SECURITY_ERR);
+  mTask->Start();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileSystemPermissionRequest::Allow(JS::HandleValue aChoices)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aChoices.isUndefined());
+  mTask->Start();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileSystemPermissionRequest::Run()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsRefPtr<FileSystemBase> filesystem = mTask->GetFileSystem();
+  if (!filesystem) {
+    Cancel();
+    return NS_OK;
+  }
+
+  if (filesystem->IsTesting()) {
+    Allow(JS::UndefinedHandleValue);
+    return NS_OK;
+  }
+
+  if (FileSystemUtils::IsParentProcess()) {
+    nsCOMPtr<nsIContentPermissionPrompt> prompt
+      = do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
+    if (!prompt || NS_FAILED(prompt->Prompt(this))) {
+      Cancel();
+    }
+    return NS_OK;
+  }
+
+  if (!mWindow) {
+    Cancel();
+    return NS_OK;
+  }
+
+  // because owner implements nsITabChild, we can assume that it is
+  // the one and only TabChild.
+  TabChild* child = TabChild::GetFrom(mWindow->GetDocShell());
+  if (!child) {
+    Cancel();
+    return NS_OK;
+  }
+
+  // Retain a reference so the object isn't deleted without IPDL's
+  // knowledge. Corresponding release occurs in
+  // DeallocPContentPermissionRequest.
+  AddRef();
+
+  nsTArray<PermissionRequest> permArray;
+  nsTArray<nsString> emptyOptions;
+  permArray.AppendElement(PermissionRequest(mPermissionType,
+                                            mPermissionAccess,
+                                            emptyOptions));
+  child->SendPContentPermissionRequestConstructor(
+    this, permArray, IPC::Principal(mPrincipal));
+
+  Sendprompt();
+  return NS_OK;
+}
+
+} /* namespace dom */
+} /* namespace mozilla */
new file mode 100644
--- /dev/null
+++ b/dom/filesystem/FileSystemPermissionRequest.h
@@ -0,0 +1,61 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=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_FileSystemPermissionRequest_h
+#define mozilla_dom_FileSystemPermissionRequest_h
+
+#include "PCOMContentPermissionRequestChild.h"
+#include "nsAutoPtr.h"
+#include "nsContentPermissionHelper.h"
+#include "nsIRunnable.h"
+
+class nsCString;
+class nsPIDOMWindow;
+
+namespace mozilla {
+namespace dom {
+
+class FileSystemTaskBase;
+
+class FileSystemPermissionRequest MOZ_FINAL
+  : public nsIContentPermissionRequest
+  , public nsIRunnable
+  , public PCOMContentPermissionRequestChild
+{
+public:
+  // Request permission for the given task.
+  static void
+  RequestForTask(FileSystemTaskBase* aTask);
+
+  // Overrides PCOMContentPermissionRequestChild
+
+  virtual void
+  IPDLRelease() MOZ_OVERRIDE;
+
+  bool
+  Recv__delete__(const bool& aAllow,
+    const InfallibleTArray<PermissionChoice>& aChoices) MOZ_OVERRIDE;
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSICONTENTPERMISSIONREQUEST
+  NS_DECL_NSIRUNNABLE
+private:
+  FileSystemPermissionRequest(FileSystemTaskBase* aTask);
+
+  virtual
+  ~FileSystemPermissionRequest();
+
+  nsCString mPermissionType;
+  nsCString mPermissionAccess;
+  nsRefPtr<FileSystemTaskBase> mTask;
+  nsCOMPtr<nsPIDOMWindow> mWindow;
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_FileSystemPermissionRequest_h
--- a/dom/filesystem/FileSystemRequestParent.cpp
+++ b/dom/filesystem/FileSystemRequestParent.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "mozilla/dom/FileSystemRequestParent.h"
 
 #include "CreateDirectoryTask.h"
 #include "GetFileOrDirectoryTask.h"
 
+#include "mozilla/AppProcessChecker.h"
 #include "mozilla/dom/FileSystemBase.h"
 
 namespace mozilla {
 namespace dom {
 
 FileSystemRequestParent::FileSystemRequestParent()
 {
 }
@@ -46,16 +47,32 @@ FileSystemRequestParent::Dispatch(Conten
     }
   }
 
   if (NS_WARN_IF(!task || !mFileSystem)) {
     // Should never reach here.
     return false;
   }
 
+  if (!mFileSystem->IsTesting()) {
+    // Check the content process permission.
+
+    nsCString access;
+    task->GetPermissionAccessType(access);
+
+    nsAutoCString permissionName;
+    permissionName = mFileSystem->GetPermission();
+    permissionName.AppendLiteral("-");
+    permissionName.Append(access);
+
+    if (!AssertAppProcessPermission(aParent, permissionName.get())) {
+      return false;
+    }
+  }
+
   task->Start();
   return true;
 }
 
 void
 FileSystemRequestParent::ActorDestroy(ActorDestroyReason why)
 {
   if (!mFileSystem) {
--- a/dom/filesystem/FileSystemTaskBase.cpp
+++ b/dom/filesystem/FileSystemTaskBase.cpp
@@ -39,16 +39,22 @@ FileSystemTaskBase::FileSystemTaskBase(F
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
 }
 
 FileSystemTaskBase::~FileSystemTaskBase()
 {
 }
 
+FileSystemBase*
+FileSystemTaskBase::GetFileSystem() const
+{
+  return mFileSystem.get();
+}
+
 void
 FileSystemTaskBase::Start()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
 
   if (HasError()) {
     NS_DispatchToMainThread(this);
     return;
@@ -166,17 +172,18 @@ FileSystemTaskBase::GetBlobParent(nsIDOM
   return cp->GetOrCreateActorForBlob(aFile);
 }
 
 void
 FileSystemTaskBase::SetError(const nsresult& aErrorValue)
 {
   uint16_t module = NS_ERROR_GET_MODULE(aErrorValue);
   if (module == NS_ERROR_MODULE_DOM_FILESYSTEM ||
-      module == NS_ERROR_MODULE_DOM_FILE) {
+      module == NS_ERROR_MODULE_DOM_FILE ||
+      module == NS_ERROR_MODULE_DOM) {
     mErrorValue = aErrorValue;
     return;
   }
 
   switch (aErrorValue) {
     case NS_OK:
       mErrorValue = NS_OK;
       return;
--- a/dom/filesystem/FileSystemTaskBase.h
+++ b/dom/filesystem/FileSystemTaskBase.h
@@ -123,16 +123,24 @@ public:
 
   /*
    * The error codes are defined in xpcom/base/ErrorList.h and their
    * corresponding error name and message are defined in dom/base/domerr.msg.
    */
   void
   SetError(const nsresult& aErrorCode);
 
+  FileSystemBase*
+  GetFileSystem() const;
+
+  /*
+   * Get the type of permission access required to perform this task.
+   */
+  virtual void
+  GetPermissionAccessType(nsCString& aAccess) const = 0;
 
   NS_DECL_NSIRUNNABLE
 protected:
   /*
    * To create a task to handle the page content request.
    */
   FileSystemTaskBase(FileSystemBase* aFileSystem);
 
--- a/dom/filesystem/FileSystemUtils.cpp
+++ b/dom/filesystem/FileSystemUtils.cpp
@@ -44,15 +44,33 @@ FileSystemUtils::NormalizedPathToLocalPa
       *cur = char16_t('\\');
   }
 #endif
   aLocal = result;
 }
 
 // static
 bool
+FileSystemUtils::IsDescendantPath(const nsAString& aPath,
+                                  const nsAString& aDescendantPath)
+{
+  // The descendant path should begin with its ancestor path.
+  nsAutoString prefix;
+  prefix = aPath + NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR);
+
+  // Check the sub-directory path to see if it has the parent path as prefix.
+  if (aDescendantPath.Length() < prefix.Length() ||
+      !StringBeginsWith(aDescendantPath, prefix)) {
+    return false;
+  }
+
+  return true;
+}
+
+// static
+bool
 FileSystemUtils::IsParentProcess()
 {
   return XRE_GetProcessType() == GeckoProcessType_Default;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/FileSystemUtils.h
+++ b/dom/filesystem/FileSystemUtils.h
@@ -29,16 +29,23 @@ public:
 
   /*
    * Convert the normalized path separator "/" to the system dependent path
    * separator, which is "/" on Mac and Linux, and "\" on Windows.
    */
   static void
   NormalizedPathToLocalPath(const nsAString& aNorm, nsAString& aLocal);
 
+  /*
+   * Return true if aDescendantPath is a descendant of aPath. Both aPath and
+   * aDescendantPath are absolute DOM path.
+   */
+  static bool
+  IsDescendantPath(const nsAString& aPath, const nsAString& aDescendantPath);
+
   static bool
   IsParentProcess();
 
   static const char16_t kSeparatorChar = char16_t('/');
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/GetFileOrDirectoryTask.cpp
+++ b/dom/filesystem/GetFileOrDirectoryTask.cpp
@@ -171,16 +171,20 @@ GetFileOrDirectoryTask::Work()
     return rv;
   }
 
   if (!isFile) {
     // Neither directory or file.
     return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
   }
 
+  if (!mFileSystem->IsSafeFile(file)) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
   mTargetFile = new nsDOMFileFile(file);
 
   return NS_OK;
 }
 
 void
 GetFileOrDirectoryTask::HandlerCallback()
 {
@@ -204,10 +208,16 @@ GetFileOrDirectoryTask::HandlerCallback(
     mPromise = nullptr;
     return;
   }
 
   mPromise->MaybeResolve(mTargetFile);
   mPromise = nullptr;
 }
 
+void
+GetFileOrDirectoryTask::GetPermissionAccessType(nsCString& aAccess) const
+{
+  aAccess.AssignLiteral("read");
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/GetFileOrDirectoryTask.h
+++ b/dom/filesystem/GetFileOrDirectoryTask.h
@@ -26,16 +26,18 @@ public:
                          FileSystemRequestParent* aParent);
 
   virtual
   ~GetFileOrDirectoryTask();
 
   already_AddRefed<Promise>
   GetPromise();
 
+  virtual void
+  GetPermissionAccessType(nsCString& aAccess) const MOZ_OVERRIDE;
 protected:
   virtual FileSystemParams
   GetRequestParams(const nsString& aFileSystem) const MOZ_OVERRIDE;
 
   virtual FileSystemResponseValue
   GetSuccessRequestResult() const MOZ_OVERRIDE;
 
   virtual void
--- a/dom/filesystem/moz.build
+++ b/dom/filesystem/moz.build
@@ -13,16 +13,17 @@ EXPORTS.mozilla.dom += [
     'FileSystemUtils.h',
 ]
 
 SOURCES += [
     'CreateDirectoryTask.cpp',
     'DeviceStorageFileSystem.cpp',
     'Directory.cpp',
     'FileSystemBase.cpp',
+    'FileSystemPermissionRequest.cpp',
     'FileSystemRequestParent.cpp',
     'FileSystemTaskBase.cpp',
     'FileSystemUtils.cpp',
     'GetFileOrDirectoryTask.cpp',
 ]
 
 FINAL_LIBRARY = 'gklayout'