dom/filesystem/Directory.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/filesystem/Directory.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,284 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "mozilla/dom/Directory.h"
    1.11 +
    1.12 +#include "CreateDirectoryTask.h"
    1.13 +#include "CreateFileTask.h"
    1.14 +#include "FileSystemPermissionRequest.h"
    1.15 +#include "GetFileOrDirectoryTask.h"
    1.16 +#include "RemoveTask.h"
    1.17 +
    1.18 +#include "nsCharSeparatedTokenizer.h"
    1.19 +#include "nsString.h"
    1.20 +#include "mozilla/dom/DirectoryBinding.h"
    1.21 +#include "mozilla/dom/FileSystemBase.h"
    1.22 +#include "mozilla/dom/FileSystemUtils.h"
    1.23 +#include "mozilla/dom/UnionTypes.h"
    1.24 +
    1.25 +// Resolve the name collision of Microsoft's API name with macros defined in
    1.26 +// Windows header files. Undefine the macro of CreateDirectory to avoid
    1.27 +// Directory#CreateDirectory being replaced by Directory#CreateDirectoryW.
    1.28 +#ifdef CreateDirectory
    1.29 +#undef CreateDirectory
    1.30 +#endif
    1.31 +// Undefine the macro of CreateFile to avoid Directory#CreateFile being replaced
    1.32 +// by Directory#CreateFileW.
    1.33 +#ifdef CreateFile
    1.34 +#undef CreateFile
    1.35 +#endif
    1.36 +
    1.37 +namespace mozilla {
    1.38 +namespace dom {
    1.39 +
    1.40 +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(Directory)
    1.41 +NS_IMPL_CYCLE_COLLECTING_ADDREF(Directory)
    1.42 +NS_IMPL_CYCLE_COLLECTING_RELEASE(Directory)
    1.43 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Directory)
    1.44 +  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    1.45 +  NS_INTERFACE_MAP_ENTRY(nsISupports)
    1.46 +NS_INTERFACE_MAP_END
    1.47 +
    1.48 +// static
    1.49 +already_AddRefed<Promise>
    1.50 +Directory::GetRoot(FileSystemBase* aFileSystem)
    1.51 +{
    1.52 +  nsRefPtr<GetFileOrDirectoryTask> task = new GetFileOrDirectoryTask(
    1.53 +    aFileSystem, EmptyString(), true);
    1.54 +  FileSystemPermissionRequest::RequestForTask(task);
    1.55 +  return task->GetPromise();
    1.56 +}
    1.57 +
    1.58 +Directory::Directory(FileSystemBase* aFileSystem,
    1.59 +                     const nsAString& aPath)
    1.60 +  : mFileSystem(aFileSystem)
    1.61 +  , mPath(aPath)
    1.62 +{
    1.63 +  MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
    1.64 +  // Remove the trailing "/".
    1.65 +  mPath.Trim(FILESYSTEM_DOM_PATH_SEPARATOR, false, true);
    1.66 +
    1.67 +  SetIsDOMBinding();
    1.68 +}
    1.69 +
    1.70 +Directory::~Directory()
    1.71 +{
    1.72 +}
    1.73 +
    1.74 +nsPIDOMWindow*
    1.75 +Directory::GetParentObject() const
    1.76 +{
    1.77 +  return mFileSystem->GetWindow();
    1.78 +}
    1.79 +
    1.80 +JSObject*
    1.81 +Directory::WrapObject(JSContext* aCx)
    1.82 +{
    1.83 +  return DirectoryBinding::Wrap(aCx, this);
    1.84 +}
    1.85 +
    1.86 +void
    1.87 +Directory::GetName(nsString& aRetval) const
    1.88 +{
    1.89 +  aRetval.Truncate();
    1.90 +
    1.91 +  if (mPath.IsEmpty()) {
    1.92 +    aRetval = mFileSystem->GetRootName();
    1.93 +    return;
    1.94 +  }
    1.95 +
    1.96 +  aRetval = Substring(mPath,
    1.97 +                      mPath.RFindChar(FileSystemUtils::kSeparatorChar) + 1);
    1.98 +}
    1.99 +
   1.100 +already_AddRefed<Promise>
   1.101 +Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions)
   1.102 +{
   1.103 +  nsresult error = NS_OK;
   1.104 +  nsString realPath;
   1.105 +  nsRefPtr<nsIDOMBlob> blobData;
   1.106 +  InfallibleTArray<uint8_t> arrayData;
   1.107 +  bool replace = (aOptions.mIfExists == CreateIfExistsMode::Replace);
   1.108 +
   1.109 +  // Get the file content.
   1.110 +  if (aOptions.mData.WasPassed()) {
   1.111 +    auto& data = aOptions.mData.Value();
   1.112 +    if (data.IsString()) {
   1.113 +      NS_ConvertUTF16toUTF8 str(data.GetAsString());
   1.114 +      arrayData.AppendElements(reinterpret_cast<const uint8_t *>(str.get()),
   1.115 +                               str.Length());
   1.116 +    } else if (data.IsArrayBuffer()) {
   1.117 +      ArrayBuffer& buffer = data.GetAsArrayBuffer();
   1.118 +      buffer.ComputeLengthAndData();
   1.119 +      arrayData.AppendElements(buffer.Data(), buffer.Length());
   1.120 +    } else if (data.IsArrayBufferView()){
   1.121 +      ArrayBufferView& view = data.GetAsArrayBufferView();
   1.122 +      view.ComputeLengthAndData();
   1.123 +      arrayData.AppendElements(view.Data(), view.Length());
   1.124 +    } else {
   1.125 +      blobData = data.GetAsBlob();
   1.126 +    }
   1.127 +  }
   1.128 +
   1.129 +  if (!DOMPathToRealPath(aPath, realPath)) {
   1.130 +    error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
   1.131 +  }
   1.132 +
   1.133 +  nsRefPtr<CreateFileTask> task = new CreateFileTask(mFileSystem, realPath,
   1.134 +    blobData, arrayData, replace);
   1.135 +  task->SetError(error);
   1.136 +  FileSystemPermissionRequest::RequestForTask(task);
   1.137 +  return task->GetPromise();
   1.138 +}
   1.139 +
   1.140 +already_AddRefed<Promise>
   1.141 +Directory::CreateDirectory(const nsAString& aPath)
   1.142 +{
   1.143 +  nsresult error = NS_OK;
   1.144 +  nsString realPath;
   1.145 +  if (!DOMPathToRealPath(aPath, realPath)) {
   1.146 +    error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
   1.147 +  }
   1.148 +  nsRefPtr<CreateDirectoryTask> task = new CreateDirectoryTask(
   1.149 +    mFileSystem, realPath);
   1.150 +  task->SetError(error);
   1.151 +  FileSystemPermissionRequest::RequestForTask(task);
   1.152 +  return task->GetPromise();
   1.153 +}
   1.154 +
   1.155 +already_AddRefed<Promise>
   1.156 +Directory::Get(const nsAString& aPath)
   1.157 +{
   1.158 +  nsresult error = NS_OK;
   1.159 +  nsString realPath;
   1.160 +  if (!DOMPathToRealPath(aPath, realPath)) {
   1.161 +    error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
   1.162 +  }
   1.163 +  nsRefPtr<GetFileOrDirectoryTask> task = new GetFileOrDirectoryTask(
   1.164 +    mFileSystem, realPath, false);
   1.165 +  task->SetError(error);
   1.166 +  FileSystemPermissionRequest::RequestForTask(task);
   1.167 +  return task->GetPromise();
   1.168 +}
   1.169 +
   1.170 +already_AddRefed<Promise>
   1.171 +Directory::Remove(const StringOrFileOrDirectory& aPath)
   1.172 +{
   1.173 +  return RemoveInternal(aPath, false);
   1.174 +}
   1.175 +
   1.176 +already_AddRefed<Promise>
   1.177 +Directory::RemoveDeep(const StringOrFileOrDirectory& aPath)
   1.178 +{
   1.179 +  return RemoveInternal(aPath, true);
   1.180 +}
   1.181 +
   1.182 +already_AddRefed<Promise>
   1.183 +Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive)
   1.184 +{
   1.185 +  nsresult error = NS_OK;
   1.186 +  nsString realPath;
   1.187 +  nsCOMPtr<nsIDOMFile> file;
   1.188 +
   1.189 +  // Check and get the target path.
   1.190 +
   1.191 +  if (aPath.IsFile()) {
   1.192 +    file = aPath.GetAsFile();
   1.193 +    goto parameters_check_done;
   1.194 +  }
   1.195 +
   1.196 +  if (aPath.IsString()) {
   1.197 +    if (!DOMPathToRealPath(aPath.GetAsString(), realPath)) {
   1.198 +      error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
   1.199 +    }
   1.200 +    goto parameters_check_done;
   1.201 +  }
   1.202 +
   1.203 +  if (!mFileSystem->IsSafeDirectory(&aPath.GetAsDirectory())) {
   1.204 +    error = NS_ERROR_DOM_SECURITY_ERR;
   1.205 +    goto parameters_check_done;
   1.206 +  }
   1.207 +
   1.208 +  realPath = aPath.GetAsDirectory().mPath;
   1.209 +  // The target must be a descendant of this directory.
   1.210 +  if (!FileSystemUtils::IsDescendantPath(mPath, realPath)) {
   1.211 +    error = NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
   1.212 +  }
   1.213 +
   1.214 +parameters_check_done:
   1.215 +
   1.216 +  nsRefPtr<RemoveTask> task = new RemoveTask(mFileSystem, mPath, file, realPath,
   1.217 +    aRecursive);
   1.218 +  task->SetError(error);
   1.219 +  FileSystemPermissionRequest::RequestForTask(task);
   1.220 +  return task->GetPromise();
   1.221 +}
   1.222 +
   1.223 +FileSystemBase*
   1.224 +Directory::GetFileSystem() const
   1.225 +{
   1.226 +  return mFileSystem.get();
   1.227 +}
   1.228 +
   1.229 +bool
   1.230 +Directory::DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const
   1.231 +{
   1.232 +  aRealPath.Truncate();
   1.233 +
   1.234 +  nsString relativePath;
   1.235 +  relativePath = aPath;
   1.236 +
   1.237 +  // Trim white spaces.
   1.238 +  static const char kWhitespace[] = "\b\t\r\n ";
   1.239 +  relativePath.Trim(kWhitespace);
   1.240 +
   1.241 +  if (!IsValidRelativePath(relativePath)) {
   1.242 +    return false;
   1.243 +  }
   1.244 +
   1.245 +  aRealPath = mPath + NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR) +
   1.246 +    relativePath;
   1.247 +
   1.248 +  return true;
   1.249 +}
   1.250 +
   1.251 +// static
   1.252 +bool
   1.253 +Directory::IsValidRelativePath(const nsString& aPath)
   1.254 +{
   1.255 +  // We don't allow empty relative path to access the root.
   1.256 +  if (aPath.IsEmpty()) {
   1.257 +    return false;
   1.258 +  }
   1.259 +
   1.260 +  // Leading and trailing "/" are not allowed.
   1.261 +  if (aPath.First() == FileSystemUtils::kSeparatorChar ||
   1.262 +      aPath.Last() == FileSystemUtils::kSeparatorChar) {
   1.263 +    return false;
   1.264 +  }
   1.265 +
   1.266 +  NS_NAMED_LITERAL_STRING(kCurrentDir, ".");
   1.267 +  NS_NAMED_LITERAL_STRING(kParentDir, "..");
   1.268 +
   1.269 +  // Split path and check each path component.
   1.270 +  nsCharSeparatedTokenizer tokenizer(aPath, FileSystemUtils::kSeparatorChar);
   1.271 +  while (tokenizer.hasMoreTokens()) {
   1.272 +    nsDependentSubstring pathComponent = tokenizer.nextToken();
   1.273 +    // The path containing empty components, such as "foo//bar", is invalid.
   1.274 +    // We don't allow paths, such as "../foo", "foo/./bar" and "foo/../bar",
   1.275 +    // to walk up the directory.
   1.276 +    if (pathComponent.IsEmpty() ||
   1.277 +        pathComponent.Equals(kCurrentDir) ||
   1.278 +        pathComponent.Equals(kParentDir)) {
   1.279 +      return false;
   1.280 +    }
   1.281 +  }
   1.282 +
   1.283 +  return true;
   1.284 +}
   1.285 +
   1.286 +} // namespace dom
   1.287 +} // namespace mozilla

mercurial