dom/filesystem/Directory.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "mozilla/dom/Directory.h"
     9 #include "CreateDirectoryTask.h"
    10 #include "CreateFileTask.h"
    11 #include "FileSystemPermissionRequest.h"
    12 #include "GetFileOrDirectoryTask.h"
    13 #include "RemoveTask.h"
    15 #include "nsCharSeparatedTokenizer.h"
    16 #include "nsString.h"
    17 #include "mozilla/dom/DirectoryBinding.h"
    18 #include "mozilla/dom/FileSystemBase.h"
    19 #include "mozilla/dom/FileSystemUtils.h"
    20 #include "mozilla/dom/UnionTypes.h"
    22 // Resolve the name collision of Microsoft's API name with macros defined in
    23 // Windows header files. Undefine the macro of CreateDirectory to avoid
    24 // Directory#CreateDirectory being replaced by Directory#CreateDirectoryW.
    25 #ifdef CreateDirectory
    26 #undef CreateDirectory
    27 #endif
    28 // Undefine the macro of CreateFile to avoid Directory#CreateFile being replaced
    29 // by Directory#CreateFileW.
    30 #ifdef CreateFile
    31 #undef CreateFile
    32 #endif
    34 namespace mozilla {
    35 namespace dom {
    37 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(Directory)
    38 NS_IMPL_CYCLE_COLLECTING_ADDREF(Directory)
    39 NS_IMPL_CYCLE_COLLECTING_RELEASE(Directory)
    40 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Directory)
    41   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    42   NS_INTERFACE_MAP_ENTRY(nsISupports)
    43 NS_INTERFACE_MAP_END
    45 // static
    46 already_AddRefed<Promise>
    47 Directory::GetRoot(FileSystemBase* aFileSystem)
    48 {
    49   nsRefPtr<GetFileOrDirectoryTask> task = new GetFileOrDirectoryTask(
    50     aFileSystem, EmptyString(), true);
    51   FileSystemPermissionRequest::RequestForTask(task);
    52   return task->GetPromise();
    53 }
    55 Directory::Directory(FileSystemBase* aFileSystem,
    56                      const nsAString& aPath)
    57   : mFileSystem(aFileSystem)
    58   , mPath(aPath)
    59 {
    60   MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
    61   // Remove the trailing "/".
    62   mPath.Trim(FILESYSTEM_DOM_PATH_SEPARATOR, false, true);
    64   SetIsDOMBinding();
    65 }
    67 Directory::~Directory()
    68 {
    69 }
    71 nsPIDOMWindow*
    72 Directory::GetParentObject() const
    73 {
    74   return mFileSystem->GetWindow();
    75 }
    77 JSObject*
    78 Directory::WrapObject(JSContext* aCx)
    79 {
    80   return DirectoryBinding::Wrap(aCx, this);
    81 }
    83 void
    84 Directory::GetName(nsString& aRetval) const
    85 {
    86   aRetval.Truncate();
    88   if (mPath.IsEmpty()) {
    89     aRetval = mFileSystem->GetRootName();
    90     return;
    91   }
    93   aRetval = Substring(mPath,
    94                       mPath.RFindChar(FileSystemUtils::kSeparatorChar) + 1);
    95 }
    97 already_AddRefed<Promise>
    98 Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions)
    99 {
   100   nsresult error = NS_OK;
   101   nsString realPath;
   102   nsRefPtr<nsIDOMBlob> blobData;
   103   InfallibleTArray<uint8_t> arrayData;
   104   bool replace = (aOptions.mIfExists == CreateIfExistsMode::Replace);
   106   // Get the file content.
   107   if (aOptions.mData.WasPassed()) {
   108     auto& data = aOptions.mData.Value();
   109     if (data.IsString()) {
   110       NS_ConvertUTF16toUTF8 str(data.GetAsString());
   111       arrayData.AppendElements(reinterpret_cast<const uint8_t *>(str.get()),
   112                                str.Length());
   113     } else if (data.IsArrayBuffer()) {
   114       ArrayBuffer& buffer = data.GetAsArrayBuffer();
   115       buffer.ComputeLengthAndData();
   116       arrayData.AppendElements(buffer.Data(), buffer.Length());
   117     } else if (data.IsArrayBufferView()){
   118       ArrayBufferView& view = data.GetAsArrayBufferView();
   119       view.ComputeLengthAndData();
   120       arrayData.AppendElements(view.Data(), view.Length());
   121     } else {
   122       blobData = data.GetAsBlob();
   123     }
   124   }
   126   if (!DOMPathToRealPath(aPath, realPath)) {
   127     error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
   128   }
   130   nsRefPtr<CreateFileTask> task = new CreateFileTask(mFileSystem, realPath,
   131     blobData, arrayData, replace);
   132   task->SetError(error);
   133   FileSystemPermissionRequest::RequestForTask(task);
   134   return task->GetPromise();
   135 }
   137 already_AddRefed<Promise>
   138 Directory::CreateDirectory(const nsAString& aPath)
   139 {
   140   nsresult error = NS_OK;
   141   nsString realPath;
   142   if (!DOMPathToRealPath(aPath, realPath)) {
   143     error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
   144   }
   145   nsRefPtr<CreateDirectoryTask> task = new CreateDirectoryTask(
   146     mFileSystem, realPath);
   147   task->SetError(error);
   148   FileSystemPermissionRequest::RequestForTask(task);
   149   return task->GetPromise();
   150 }
   152 already_AddRefed<Promise>
   153 Directory::Get(const nsAString& aPath)
   154 {
   155   nsresult error = NS_OK;
   156   nsString realPath;
   157   if (!DOMPathToRealPath(aPath, realPath)) {
   158     error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
   159   }
   160   nsRefPtr<GetFileOrDirectoryTask> task = new GetFileOrDirectoryTask(
   161     mFileSystem, realPath, false);
   162   task->SetError(error);
   163   FileSystemPermissionRequest::RequestForTask(task);
   164   return task->GetPromise();
   165 }
   167 already_AddRefed<Promise>
   168 Directory::Remove(const StringOrFileOrDirectory& aPath)
   169 {
   170   return RemoveInternal(aPath, false);
   171 }
   173 already_AddRefed<Promise>
   174 Directory::RemoveDeep(const StringOrFileOrDirectory& aPath)
   175 {
   176   return RemoveInternal(aPath, true);
   177 }
   179 already_AddRefed<Promise>
   180 Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive)
   181 {
   182   nsresult error = NS_OK;
   183   nsString realPath;
   184   nsCOMPtr<nsIDOMFile> file;
   186   // Check and get the target path.
   188   if (aPath.IsFile()) {
   189     file = aPath.GetAsFile();
   190     goto parameters_check_done;
   191   }
   193   if (aPath.IsString()) {
   194     if (!DOMPathToRealPath(aPath.GetAsString(), realPath)) {
   195       error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
   196     }
   197     goto parameters_check_done;
   198   }
   200   if (!mFileSystem->IsSafeDirectory(&aPath.GetAsDirectory())) {
   201     error = NS_ERROR_DOM_SECURITY_ERR;
   202     goto parameters_check_done;
   203   }
   205   realPath = aPath.GetAsDirectory().mPath;
   206   // The target must be a descendant of this directory.
   207   if (!FileSystemUtils::IsDescendantPath(mPath, realPath)) {
   208     error = NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
   209   }
   211 parameters_check_done:
   213   nsRefPtr<RemoveTask> task = new RemoveTask(mFileSystem, mPath, file, realPath,
   214     aRecursive);
   215   task->SetError(error);
   216   FileSystemPermissionRequest::RequestForTask(task);
   217   return task->GetPromise();
   218 }
   220 FileSystemBase*
   221 Directory::GetFileSystem() const
   222 {
   223   return mFileSystem.get();
   224 }
   226 bool
   227 Directory::DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const
   228 {
   229   aRealPath.Truncate();
   231   nsString relativePath;
   232   relativePath = aPath;
   234   // Trim white spaces.
   235   static const char kWhitespace[] = "\b\t\r\n ";
   236   relativePath.Trim(kWhitespace);
   238   if (!IsValidRelativePath(relativePath)) {
   239     return false;
   240   }
   242   aRealPath = mPath + NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR) +
   243     relativePath;
   245   return true;
   246 }
   248 // static
   249 bool
   250 Directory::IsValidRelativePath(const nsString& aPath)
   251 {
   252   // We don't allow empty relative path to access the root.
   253   if (aPath.IsEmpty()) {
   254     return false;
   255   }
   257   // Leading and trailing "/" are not allowed.
   258   if (aPath.First() == FileSystemUtils::kSeparatorChar ||
   259       aPath.Last() == FileSystemUtils::kSeparatorChar) {
   260     return false;
   261   }
   263   NS_NAMED_LITERAL_STRING(kCurrentDir, ".");
   264   NS_NAMED_LITERAL_STRING(kParentDir, "..");
   266   // Split path and check each path component.
   267   nsCharSeparatedTokenizer tokenizer(aPath, FileSystemUtils::kSeparatorChar);
   268   while (tokenizer.hasMoreTokens()) {
   269     nsDependentSubstring pathComponent = tokenizer.nextToken();
   270     // The path containing empty components, such as "foo//bar", is invalid.
   271     // We don't allow paths, such as "../foo", "foo/./bar" and "foo/../bar",
   272     // to walk up the directory.
   273     if (pathComponent.IsEmpty() ||
   274         pathComponent.Equals(kCurrentDir) ||
   275         pathComponent.Equals(kParentDir)) {
   276       return false;
   277     }
   278   }
   280   return true;
   281 }
   283 } // namespace dom
   284 } // namespace mozilla

mercurial