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