1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=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 "GetDirectoryListingTask.h"
9 #include "HTMLSplitOnSpacesTokenizer.h"
11 #include "mozilla/dom/FileBlobImpl.h"
12 #include "mozilla/dom/FileSystemBase.h"
13 #include "mozilla/dom/FileSystemUtils.h"
14 #include "mozilla/dom/IPCBlobUtils.h"
15 #include "mozilla/dom/PFileSystemParams.h"
16 #include "mozilla/dom/Promise.h"
17 #include "mozilla/dom/UnionTypes.h"
18 #include "mozilla/ipc/BackgroundParent.h"
22 namespace mozilla::dom
{
25 * GetDirectoryListingTaskChild
29 already_AddRefed
<GetDirectoryListingTaskChild
>
30 GetDirectoryListingTaskChild::Create(FileSystemBase
* aFileSystem
,
31 Directory
* aDirectory
,
33 const nsAString
& aFilters
,
35 MOZ_ASSERT(aFileSystem
);
36 MOZ_ASSERT(aDirectory
);
37 aFileSystem
->AssertIsOnOwningThread();
39 nsCOMPtr
<nsIGlobalObject
> globalObject
= aFileSystem
->GetParentObject();
40 MOZ_ASSERT(globalObject
);
42 RefPtr
<GetDirectoryListingTaskChild
> task
= new GetDirectoryListingTaskChild(
43 globalObject
, aFileSystem
, aDirectory
, aTargetPath
, aFilters
);
45 // aTargetPath can be null. In this case SetError will be called.
47 task
->mPromise
= Promise::Create(globalObject
, aRv
);
48 if (NS_WARN_IF(aRv
.Failed())) {
55 GetDirectoryListingTaskChild::GetDirectoryListingTaskChild(
56 nsIGlobalObject
* aGlobalObject
, FileSystemBase
* aFileSystem
,
57 Directory
* aDirectory
, nsIFile
* aTargetPath
, const nsAString
& aFilters
)
58 : FileSystemTaskChildBase(aGlobalObject
, aFileSystem
),
59 mDirectory(aDirectory
),
60 mTargetPath(aTargetPath
),
62 MOZ_ASSERT(aFileSystem
);
63 aFileSystem
->AssertIsOnOwningThread();
66 GetDirectoryListingTaskChild::~GetDirectoryListingTaskChild() {
67 mFileSystem
->AssertIsOnOwningThread();
70 already_AddRefed
<Promise
> GetDirectoryListingTaskChild::GetPromise() {
71 mFileSystem
->AssertIsOnOwningThread();
72 return RefPtr
<Promise
>(mPromise
).forget();
75 FileSystemParams
GetDirectoryListingTaskChild::GetRequestParams(
76 const nsString
& aSerializedDOMPath
, ErrorResult
& aRv
) const {
77 mFileSystem
->AssertIsOnOwningThread();
79 // this is the real path.
81 aRv
= mTargetPath
->GetPath(path
);
82 if (NS_WARN_IF(aRv
.Failed())) {
83 return FileSystemGetDirectoryListingParams();
86 // this is the dom path.
87 nsAutoString directoryPath
;
88 mDirectory
->GetPath(directoryPath
, aRv
);
89 if (NS_WARN_IF(aRv
.Failed())) {
90 return FileSystemGetDirectoryListingParams();
93 return FileSystemGetDirectoryListingParams(aSerializedDOMPath
, path
,
94 directoryPath
, mFilters
);
97 void GetDirectoryListingTaskChild::SetSuccessRequestResult(
98 const FileSystemResponseValue
& aValue
, ErrorResult
& aRv
) {
99 mFileSystem
->AssertIsOnOwningThread();
100 MOZ_ASSERT(aValue
.type() ==
101 FileSystemResponseValue::TFileSystemDirectoryListingResponse
);
103 FileSystemDirectoryListingResponse r
= aValue
;
104 for (uint32_t i
= 0; i
< r
.data().Length(); ++i
) {
105 const FileSystemDirectoryListingResponseData
& data
= r
.data()[i
];
107 OwningFileOrDirectory
* ofd
= mTargetData
.AppendElement(fallible
);
109 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
113 if (data
.type() == FileSystemDirectoryListingResponseData::
114 TFileSystemDirectoryListingResponseFile
) {
115 const FileSystemDirectoryListingResponseFile
& d
=
116 data
.get_FileSystemDirectoryListingResponseFile();
118 RefPtr
<BlobImpl
> blobImpl
= IPCBlobUtils::Deserialize(d
.blob());
119 MOZ_ASSERT(blobImpl
);
121 nsCOMPtr
<nsIGlobalObject
> globalObject
= mFileSystem
->GetParentObject();
122 MOZ_ASSERT(globalObject
);
124 RefPtr
<File
> file
= File::Create(globalObject
, blobImpl
);
127 ofd
->SetAsFile() = file
;
129 MOZ_ASSERT(data
.type() ==
130 FileSystemDirectoryListingResponseData::
131 TFileSystemDirectoryListingResponseDirectory
);
132 const FileSystemDirectoryListingResponseDirectory
& d
=
133 data
.get_FileSystemDirectoryListingResponseDirectory();
135 nsCOMPtr
<nsIFile
> path
;
136 aRv
= NS_NewLocalFile(d
.directoryRealPath(), true, getter_AddRefs(path
));
137 if (NS_WARN_IF(aRv
.Failed())) {
141 RefPtr
<Directory
> directory
=
142 Directory::Create(mFileSystem
->GetParentObject(), path
, mFileSystem
);
143 MOZ_ASSERT(directory
);
145 ofd
->SetAsDirectory() = directory
;
150 void GetDirectoryListingTaskChild::HandlerCallback() {
151 mFileSystem
->AssertIsOnOwningThread();
153 if (mFileSystem
->IsShutdown()) {
159 mPromise
->MaybeReject(mErrorValue
);
164 mPromise
->MaybeResolve(mTargetData
);
169 * GetDirectoryListingTaskParent
173 already_AddRefed
<GetDirectoryListingTaskParent
>
174 GetDirectoryListingTaskParent::Create(
175 FileSystemBase
* aFileSystem
,
176 const FileSystemGetDirectoryListingParams
& aParam
,
177 FileSystemRequestParent
* aParent
, ErrorResult
& aRv
) {
178 MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
179 mozilla::ipc::AssertIsOnBackgroundThread();
180 MOZ_ASSERT(aFileSystem
);
182 RefPtr
<GetDirectoryListingTaskParent
> task
=
183 new GetDirectoryListingTaskParent(aFileSystem
, aParam
, aParent
);
185 aRv
= NS_NewLocalFile(aParam
.realPath(), true,
186 getter_AddRefs(task
->mTargetPath
));
187 if (NS_WARN_IF(aRv
.Failed())) {
191 return task
.forget();
194 GetDirectoryListingTaskParent::GetDirectoryListingTaskParent(
195 FileSystemBase
* aFileSystem
,
196 const FileSystemGetDirectoryListingParams
& aParam
,
197 FileSystemRequestParent
* aParent
)
198 : FileSystemTaskParentBase(aFileSystem
, aParam
, aParent
),
199 mDOMPath(aParam
.domPath()),
200 mFilters(aParam
.filters()) {
201 MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
202 mozilla::ipc::AssertIsOnBackgroundThread();
203 MOZ_ASSERT(aFileSystem
);
206 FileSystemResponseValue
GetDirectoryListingTaskParent::GetSuccessRequestResult(
207 ErrorResult
& aRv
) const {
208 mozilla::ipc::AssertIsOnBackgroundThread();
210 nsTArray
<FileSystemDirectoryListingResponseData
> inputs
;
212 for (unsigned i
= 0; i
< mTargetData
.Length(); i
++) {
213 if (mTargetData
[i
].mType
== FileOrDirectoryPath::eFilePath
) {
214 nsCOMPtr
<nsIFile
> path
;
216 NS_NewLocalFile(mTargetData
[i
].mPath
, true, getter_AddRefs(path
));
217 if (NS_WARN_IF(NS_FAILED(rv
))) {
221 FileSystemDirectoryListingResponseFile fileData
;
222 RefPtr
<BlobImpl
> blobImpl
= new FileBlobImpl(path
);
224 nsAutoString filePath
;
225 filePath
.Assign(mDOMPath
);
227 // This is specific for unix root filesystem.
228 if (!mDOMPath
.EqualsLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL
)) {
229 filePath
.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL
);
233 blobImpl
->GetName(name
);
234 filePath
.Append(name
);
235 blobImpl
->SetDOMPath(filePath
);
238 rv
= IPCBlobUtils::Serialize(blobImpl
, ipcBlob
);
239 if (NS_WARN_IF(NS_FAILED(rv
))) {
243 fileData
.blob() = ipcBlob
;
244 inputs
.AppendElement(fileData
);
246 MOZ_ASSERT(mTargetData
[i
].mType
== FileOrDirectoryPath::eDirectoryPath
);
247 FileSystemDirectoryListingResponseDirectory directoryData
;
248 directoryData
.directoryRealPath() = mTargetData
[i
].mPath
;
249 inputs
.AppendElement(directoryData
);
253 FileSystemDirectoryListingResponse response
;
254 response
.data() = std::move(inputs
);
258 nsresult
GetDirectoryListingTaskParent::IOWork() {
259 MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
260 MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
262 if (mFileSystem
->IsShutdown()) {
263 return NS_ERROR_FAILURE
;
267 nsresult rv
= mTargetPath
->Exists(&exists
);
268 if (NS_WARN_IF(NS_FAILED(rv
))) {
273 if (!mFileSystem
->ShouldCreateDirectory()) {
274 return NS_ERROR_DOM_FILE_NOT_FOUND_ERR
;
277 rv
= mTargetPath
->Create(nsIFile::DIRECTORY_TYPE
, 0777);
278 if (NS_WARN_IF(NS_FAILED(rv
))) {
285 rv
= mTargetPath
->IsDirectory(&isDir
);
286 if (NS_WARN_IF(NS_FAILED(rv
))) {
291 return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR
;
294 nsCOMPtr
<nsIDirectoryEnumerator
> entries
;
295 rv
= mTargetPath
->GetDirectoryEntries(getter_AddRefs(entries
));
296 if (NS_WARN_IF(NS_FAILED(rv
))) {
300 bool filterOutSensitive
= false;
302 HTMLSplitOnSpacesTokenizer
tokenizer(mFilters
, ';');
304 while (tokenizer
.hasMoreTokens()) {
305 token
= tokenizer
.nextToken();
306 if (token
.EqualsLiteral("filter-out-sensitive")) {
307 filterOutSensitive
= true;
309 MOZ_CRASH("Unrecognized filter");
315 nsCOMPtr
<nsIFile
> currFile
;
316 if (NS_WARN_IF(NS_FAILED(entries
->GetNextFile(getter_AddRefs(currFile
)))) ||
320 bool isLink
, isSpecial
, isFile
;
321 if (NS_WARN_IF(NS_FAILED(currFile
->IsSymlink(&isLink
)) ||
322 NS_FAILED(currFile
->IsSpecial(&isSpecial
))) ||
323 // Although we allow explicit individual selection of symlinks via the
324 // file picker, we do not process symlinks in directory traversal. Our
325 // specific policy decision is documented at
326 // https://bugzilla.mozilla.org/show_bug.cgi?id=1813299#c20
327 isLink
|| isSpecial
) {
330 if (NS_WARN_IF(NS_FAILED(currFile
->IsFile(&isFile
)) ||
331 NS_FAILED(currFile
->IsDirectory(&isDir
))) ||
332 !(isFile
|| isDir
)) {
336 if (filterOutSensitive
) {
338 if (NS_WARN_IF(NS_FAILED(currFile
->IsHidden(&isHidden
))) || isHidden
) {
341 nsAutoString leafName
;
342 if (NS_WARN_IF(NS_FAILED(currFile
->GetLeafName(leafName
)))) {
345 if (leafName
[0] == char16_t('.')) {
351 if (NS_WARN_IF(NS_FAILED(currFile
->GetPath(path
)))) {
355 FileOrDirectoryPath element
;
356 element
.mPath
= path
;
357 element
.mType
= isDir
? FileOrDirectoryPath::eDirectoryPath
358 : FileOrDirectoryPath::eFilePath
;
360 if (!mTargetData
.AppendElement(element
, fallible
)) {
361 return NS_ERROR_OUT_OF_MEMORY
;
367 nsresult
GetDirectoryListingTaskParent::GetTargetPath(nsAString
& aPath
) const {
368 return mTargetPath
->GetPath(aPath
);
371 } // namespace mozilla::dom