Bug 910412 - Filesystem API permission request and checks. r=dhylands
authorYuan Xulei <xyuan@mozilla.com>
Wed, 05 Mar 2014 11:24:19 +0800
changeset 190816 1b02211faa78a8df0ac159bae64abc99e1f70214
parent 190815 b1886a252ed6914c8d2c1887242d9b0ae6589a3d
child 190817 b83bb5aa1b252ab74c6129500ef5954963be1ad3
push idunknown
push userunknown
push dateunknown
reviewersdhylands
bugs910412
milestone30.0a1
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'