dom/filesystem/Directory.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial