Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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