Line data Source code
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
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "GetFilesHelper.h"
8 : #include "mozilla/dom/ContentChild.h"
9 : #include "mozilla/dom/ContentParent.h"
10 : #include "mozilla/dom/FileBlobImpl.h"
11 : #include "mozilla/dom/IPCBlobUtils.h"
12 : #include "mozilla/ipc/IPCStreamUtils.h"
13 : #include "FileSystemUtils.h"
14 : #include "nsProxyRelease.h"
15 :
16 : namespace mozilla {
17 : namespace dom {
18 :
19 : namespace {
20 :
21 : // This class is used in the DTOR of GetFilesHelper to release resources in the
22 : // correct thread.
23 0 : class ReleaseRunnable final : public Runnable
24 : {
25 : public:
26 : static void
27 0 : MaybeReleaseOnMainThread(nsTArray<RefPtr<Promise>>& aPromises,
28 : nsTArray<RefPtr<GetFilesCallback>>& aCallbacks,
29 : Sequence<RefPtr<File>>& aFiles,
30 : already_AddRefed<nsIGlobalObject> aGlobal)
31 : {
32 0 : nsCOMPtr<nsIGlobalObject> global(aGlobal);
33 0 : if (NS_IsMainThread()) {
34 0 : return;
35 : }
36 :
37 : RefPtr<ReleaseRunnable> runnable =
38 0 : new ReleaseRunnable(aPromises, aCallbacks, aFiles, global.forget());
39 0 : FileSystemUtils::DispatchRunnable(nullptr, runnable.forget());
40 : }
41 :
42 : NS_IMETHOD
43 0 : Run() override
44 : {
45 0 : MOZ_ASSERT(NS_IsMainThread());
46 :
47 0 : mPromises.Clear();
48 0 : mCallbacks.Clear();
49 0 : mFiles.Clear();
50 0 : mGlobal = nullptr;
51 :
52 0 : return NS_OK;
53 : }
54 :
55 : private:
56 0 : ReleaseRunnable(nsTArray<RefPtr<Promise>>& aPromises,
57 : nsTArray<RefPtr<GetFilesCallback>>& aCallbacks,
58 : Sequence<RefPtr<File>>& aFiles,
59 : already_AddRefed<nsIGlobalObject> aGlobal)
60 0 : : Runnable("dom::ReleaseRunnable")
61 : {
62 0 : mPromises.SwapElements(aPromises);
63 0 : mCallbacks.SwapElements(aCallbacks);
64 0 : mFiles.SwapElements(aFiles);
65 0 : mGlobal = aGlobal;
66 0 : }
67 :
68 : nsTArray<RefPtr<Promise>> mPromises;
69 : nsTArray<RefPtr<GetFilesCallback>> mCallbacks;
70 : Sequence<RefPtr<File>> mFiles;
71 : nsCOMPtr<nsIGlobalObject> mGlobal;
72 : };
73 :
74 : } // anonymous
75 :
76 : ///////////////////////////////////////////////////////////////////////////////
77 : // GetFilesHelper Base class
78 :
79 : already_AddRefed<GetFilesHelper>
80 0 : GetFilesHelper::Create(nsIGlobalObject* aGlobal,
81 : const nsTArray<OwningFileOrDirectory>& aFilesOrDirectory,
82 : bool aRecursiveFlag, ErrorResult& aRv)
83 : {
84 0 : RefPtr<GetFilesHelper> helper;
85 :
86 0 : if (XRE_IsParentProcess()) {
87 0 : helper = new GetFilesHelper(aGlobal, aRecursiveFlag);
88 : } else {
89 0 : helper = new GetFilesHelperChild(aGlobal, aRecursiveFlag);
90 : }
91 :
92 0 : nsAutoString directoryPath;
93 :
94 0 : for (uint32_t i = 0; i < aFilesOrDirectory.Length(); ++i) {
95 0 : const OwningFileOrDirectory& data = aFilesOrDirectory[i];
96 0 : if (data.IsFile()) {
97 0 : if (!helper->mFiles.AppendElement(data.GetAsFile(), fallible)) {
98 0 : aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
99 0 : return nullptr;
100 : }
101 : } else {
102 0 : MOZ_ASSERT(data.IsDirectory());
103 :
104 : // We support the upload of only 1 top-level directory from our
105 : // directory picker. This means that we cannot have more than 1
106 : // Directory object in aFilesOrDirectory array.
107 0 : MOZ_ASSERT(directoryPath.IsEmpty());
108 :
109 0 : RefPtr<Directory> directory = data.GetAsDirectory();
110 0 : MOZ_ASSERT(directory);
111 :
112 0 : aRv = directory->GetFullRealPath(directoryPath);
113 0 : if (NS_WARN_IF(aRv.Failed())) {
114 0 : return nullptr;
115 : }
116 : }
117 : }
118 :
119 : // No directories to explore.
120 0 : if (directoryPath.IsEmpty()) {
121 0 : helper->mListingCompleted = true;
122 0 : return helper.forget();
123 : }
124 :
125 0 : MOZ_ASSERT(helper->mFiles.IsEmpty());
126 0 : helper->SetDirectoryPath(directoryPath);
127 :
128 0 : helper->Work(aRv);
129 0 : if (NS_WARN_IF(aRv.Failed())) {
130 0 : return nullptr;
131 : }
132 :
133 0 : return helper.forget();
134 : }
135 :
136 0 : GetFilesHelper::GetFilesHelper(nsIGlobalObject* aGlobal, bool aRecursiveFlag)
137 : : Runnable("GetFilesHelper")
138 : , GetFilesHelperBase(aRecursiveFlag)
139 : , mGlobal(aGlobal)
140 : , mListingCompleted(false)
141 : , mErrorResult(NS_OK)
142 : , mMutex("GetFilesHelper::mMutex")
143 0 : , mCanceled(false)
144 : {
145 0 : }
146 :
147 0 : GetFilesHelper::~GetFilesHelper()
148 : {
149 0 : ReleaseRunnable::MaybeReleaseOnMainThread(mPromises, mCallbacks, mFiles,
150 0 : mGlobal.forget());
151 0 : }
152 :
153 : void
154 0 : GetFilesHelper::AddPromise(Promise* aPromise)
155 : {
156 0 : MOZ_ASSERT(aPromise);
157 :
158 : // Still working.
159 0 : if (!mListingCompleted) {
160 0 : mPromises.AppendElement(aPromise);
161 0 : return;
162 : }
163 :
164 0 : MOZ_ASSERT(mPromises.IsEmpty());
165 0 : ResolveOrRejectPromise(aPromise);
166 : }
167 :
168 : void
169 0 : GetFilesHelper::AddCallback(GetFilesCallback* aCallback)
170 : {
171 0 : MOZ_ASSERT(aCallback);
172 :
173 : // Still working.
174 0 : if (!mListingCompleted) {
175 0 : mCallbacks.AppendElement(aCallback);
176 0 : return;
177 : }
178 :
179 0 : MOZ_ASSERT(mCallbacks.IsEmpty());
180 0 : RunCallback(aCallback);
181 : }
182 :
183 : void
184 0 : GetFilesHelper::Unlink()
185 : {
186 0 : mGlobal = nullptr;
187 0 : mFiles.Clear();
188 0 : mPromises.Clear();
189 0 : mCallbacks.Clear();
190 :
191 : {
192 0 : MutexAutoLock lock(mMutex);
193 0 : mCanceled = true;
194 : }
195 :
196 0 : Cancel();
197 0 : }
198 :
199 : void
200 0 : GetFilesHelper::Traverse(nsCycleCollectionTraversalCallback &cb)
201 : {
202 0 : GetFilesHelper* tmp = this;
203 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal);
204 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFiles);
205 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromises);
206 0 : }
207 :
208 : void
209 0 : GetFilesHelper::Work(ErrorResult& aRv)
210 : {
211 : nsCOMPtr<nsIEventTarget> target =
212 0 : do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
213 0 : MOZ_ASSERT(target);
214 :
215 0 : aRv = target->Dispatch(this, NS_DISPATCH_NORMAL);
216 0 : }
217 :
218 : NS_IMETHODIMP
219 0 : GetFilesHelper::Run()
220 : {
221 0 : MOZ_ASSERT(!mDirectoryPath.IsEmpty());
222 0 : MOZ_ASSERT(!mListingCompleted);
223 :
224 : // First step is to retrieve the list of file paths.
225 : // This happens in the I/O thread.
226 0 : if (!NS_IsMainThread()) {
227 0 : RunIO();
228 :
229 : // If this operation has been canceled, we don't have to go back to
230 : // main-thread.
231 0 : if (IsCanceled()) {
232 0 : return NS_OK;
233 : }
234 :
235 0 : RefPtr<Runnable> runnable = this;
236 0 : return FileSystemUtils::DispatchRunnable(nullptr, runnable.forget());
237 : }
238 :
239 : // We are here, but we should not do anything on this thread because, in the
240 : // meantime, the operation has been canceled.
241 0 : if (IsCanceled()) {
242 0 : return NS_OK;
243 : }
244 :
245 0 : RunMainThread();
246 :
247 0 : OperationCompleted();
248 0 : return NS_OK;
249 : }
250 :
251 : void
252 0 : GetFilesHelper::OperationCompleted()
253 : {
254 : // We mark the operation as completed here.
255 0 : mListingCompleted = true;
256 :
257 : // Let's process the pending promises.
258 0 : nsTArray<RefPtr<Promise>> promises;
259 0 : promises.SwapElements(mPromises);
260 :
261 0 : for (uint32_t i = 0; i < promises.Length(); ++i) {
262 0 : ResolveOrRejectPromise(promises[i]);
263 : }
264 :
265 : // Let's process the pending callbacks.
266 0 : nsTArray<RefPtr<GetFilesCallback>> callbacks;
267 0 : callbacks.SwapElements(mCallbacks);
268 :
269 0 : for (uint32_t i = 0; i < callbacks.Length(); ++i) {
270 0 : RunCallback(callbacks[i]);
271 : }
272 0 : }
273 :
274 : void
275 0 : GetFilesHelper::RunIO()
276 : {
277 0 : MOZ_ASSERT(!NS_IsMainThread());
278 0 : MOZ_ASSERT(!mDirectoryPath.IsEmpty());
279 0 : MOZ_ASSERT(!mListingCompleted);
280 :
281 0 : nsCOMPtr<nsIFile> file;
282 0 : mErrorResult = NS_NewLocalFile(mDirectoryPath, true, getter_AddRefs(file));
283 0 : if (NS_WARN_IF(NS_FAILED(mErrorResult))) {
284 0 : return;
285 : }
286 :
287 0 : nsAutoString leafName;
288 0 : mErrorResult = file->GetLeafName(leafName);
289 0 : if (NS_WARN_IF(NS_FAILED(mErrorResult))) {
290 0 : return;
291 : }
292 :
293 0 : nsAutoString domPath;
294 0 : domPath.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
295 0 : domPath.Append(leafName);
296 :
297 0 : mErrorResult = ExploreDirectory(domPath, file);
298 : }
299 :
300 : void
301 0 : GetFilesHelper::RunMainThread()
302 : {
303 0 : MOZ_ASSERT(NS_IsMainThread());
304 0 : MOZ_ASSERT(!mDirectoryPath.IsEmpty());
305 0 : MOZ_ASSERT(!mListingCompleted);
306 :
307 : // If there is an error, do nothing.
308 0 : if (NS_FAILED(mErrorResult)) {
309 0 : return;
310 : }
311 :
312 : // Create the sequence of Files.
313 0 : for (uint32_t i = 0; i < mTargetBlobImplArray.Length(); ++i) {
314 0 : RefPtr<File> domFile = File::Create(mGlobal, mTargetBlobImplArray[i]);
315 0 : MOZ_ASSERT(domFile);
316 :
317 0 : if (!mFiles.AppendElement(domFile, fallible)) {
318 0 : mErrorResult = NS_ERROR_OUT_OF_MEMORY;
319 0 : mFiles.Clear();
320 0 : return;
321 : }
322 : }
323 : }
324 :
325 : nsresult
326 0 : GetFilesHelperBase::ExploreDirectory(const nsAString& aDOMPath, nsIFile* aFile)
327 : {
328 0 : MOZ_ASSERT(!NS_IsMainThread());
329 0 : MOZ_ASSERT(aFile);
330 :
331 : // We check if this operation has to be terminated at each recursion.
332 0 : if (IsCanceled()) {
333 0 : return NS_OK;
334 : }
335 :
336 0 : nsresult rv = AddExploredDirectory(aFile);
337 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
338 0 : return rv;
339 : }
340 :
341 0 : nsCOMPtr<nsISimpleEnumerator> entries;
342 0 : rv = aFile->GetDirectoryEntries(getter_AddRefs(entries));
343 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
344 0 : return rv;
345 : }
346 :
347 : for (;;) {
348 0 : bool hasMore = false;
349 0 : if (NS_WARN_IF(NS_FAILED(entries->HasMoreElements(&hasMore))) || !hasMore) {
350 0 : break;
351 : }
352 :
353 0 : nsCOMPtr<nsISupports> supp;
354 0 : if (NS_WARN_IF(NS_FAILED(entries->GetNext(getter_AddRefs(supp))))) {
355 0 : break;
356 : }
357 :
358 0 : nsCOMPtr<nsIFile> currFile = do_QueryInterface(supp);
359 0 : MOZ_ASSERT(currFile);
360 :
361 : bool isLink, isSpecial, isFile, isDir;
362 0 : if (NS_WARN_IF(NS_FAILED(currFile->IsSymlink(&isLink)) ||
363 0 : NS_FAILED(currFile->IsSpecial(&isSpecial))) ||
364 : isSpecial) {
365 0 : continue;
366 : }
367 :
368 0 : if (NS_WARN_IF(NS_FAILED(currFile->IsFile(&isFile)) ||
369 0 : NS_FAILED(currFile->IsDirectory(&isDir))) ||
370 0 : !(isFile || isDir)) {
371 0 : continue;
372 : }
373 :
374 : // We don't want to explore loops of links.
375 0 : if (isDir && isLink && !ShouldFollowSymLink(currFile)) {
376 0 : continue;
377 : }
378 :
379 : // The new domPath
380 0 : nsAutoString domPath;
381 0 : domPath.Assign(aDOMPath);
382 0 : if (!aDOMPath.EqualsLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL)) {
383 0 : domPath.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
384 : }
385 :
386 0 : nsAutoString leafName;
387 0 : if (NS_WARN_IF(NS_FAILED(currFile->GetLeafName(leafName)))) {
388 0 : continue;
389 : }
390 0 : domPath.Append(leafName);
391 :
392 0 : if (isFile) {
393 0 : RefPtr<BlobImpl> blobImpl = new FileBlobImpl(currFile);
394 0 : blobImpl->SetDOMPath(domPath);
395 :
396 0 : if (!mTargetBlobImplArray.AppendElement(blobImpl, fallible)) {
397 0 : return NS_ERROR_OUT_OF_MEMORY;
398 : }
399 :
400 0 : continue;
401 : }
402 :
403 0 : MOZ_ASSERT(isDir);
404 0 : if (!mRecursiveFlag) {
405 0 : continue;
406 : }
407 :
408 : // Recursive.
409 0 : rv = ExploreDirectory(domPath, currFile);
410 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
411 0 : return rv;
412 : }
413 0 : }
414 :
415 0 : return NS_OK;
416 : }
417 :
418 : nsresult
419 0 : GetFilesHelperBase::AddExploredDirectory(nsIFile* aDir)
420 : {
421 : nsresult rv;
422 :
423 : #ifdef DEBUG
424 : bool isDir;
425 0 : rv = aDir->IsDirectory(&isDir);
426 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
427 0 : return rv;
428 : }
429 :
430 0 : MOZ_ASSERT(isDir, "Why are we here?");
431 : #endif
432 :
433 : bool isLink;
434 0 : rv = aDir->IsSymlink(&isLink);
435 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
436 0 : return rv;
437 : }
438 :
439 0 : nsAutoString path;
440 0 : if (!isLink) {
441 0 : rv = aDir->GetPath(path);
442 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
443 0 : return rv;
444 : }
445 : } else {
446 0 : rv = aDir->GetTarget(path);
447 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
448 0 : return rv;
449 : }
450 : }
451 :
452 0 : mExploredDirectories.PutEntry(path);
453 0 : return NS_OK;
454 : }
455 :
456 : bool
457 0 : GetFilesHelperBase::ShouldFollowSymLink(nsIFile* aDir)
458 : {
459 : #ifdef DEBUG
460 : bool isLink, isDir;
461 0 : if (NS_WARN_IF(NS_FAILED(aDir->IsSymlink(&isLink)) ||
462 : NS_FAILED(aDir->IsDirectory(&isDir)))) {
463 0 : return false;
464 : }
465 :
466 0 : MOZ_ASSERT(isLink && isDir, "Why are we here?");
467 : #endif
468 :
469 0 : nsAutoString targetPath;
470 0 : if (NS_WARN_IF(NS_FAILED(aDir->GetTarget(targetPath)))) {
471 0 : return false;
472 : }
473 :
474 0 : return !mExploredDirectories.Contains(targetPath);
475 : }
476 :
477 : void
478 0 : GetFilesHelper::ResolveOrRejectPromise(Promise* aPromise)
479 : {
480 0 : MOZ_ASSERT(NS_IsMainThread());
481 0 : MOZ_ASSERT(mListingCompleted);
482 0 : MOZ_ASSERT(aPromise);
483 :
484 : // Error propagation.
485 0 : if (NS_FAILED(mErrorResult)) {
486 0 : aPromise->MaybeReject(mErrorResult);
487 0 : return;
488 : }
489 :
490 0 : aPromise->MaybeResolve(mFiles);
491 : }
492 :
493 : void
494 0 : GetFilesHelper::RunCallback(GetFilesCallback* aCallback)
495 : {
496 0 : MOZ_ASSERT(NS_IsMainThread());
497 0 : MOZ_ASSERT(mListingCompleted);
498 0 : MOZ_ASSERT(aCallback);
499 :
500 0 : aCallback->Callback(mErrorResult, mFiles);
501 0 : }
502 :
503 : ///////////////////////////////////////////////////////////////////////////////
504 : // GetFilesHelperChild class
505 :
506 : void
507 0 : GetFilesHelperChild::Work(ErrorResult& aRv)
508 : {
509 0 : ContentChild* cc = ContentChild::GetSingleton();
510 0 : if (NS_WARN_IF(!cc)) {
511 0 : aRv.Throw(NS_ERROR_FAILURE);
512 0 : return;
513 : }
514 :
515 0 : aRv = nsContentUtils::GenerateUUIDInPlace(mUUID);
516 0 : if (NS_WARN_IF(aRv.Failed())) {
517 0 : return;
518 : }
519 :
520 0 : mPendingOperation = true;
521 0 : cc->CreateGetFilesRequest(mDirectoryPath, mRecursiveFlag, mUUID, this);
522 : }
523 :
524 : void
525 0 : GetFilesHelperChild::Cancel()
526 : {
527 0 : if (!mPendingOperation) {
528 0 : return;
529 : }
530 :
531 0 : ContentChild* cc = ContentChild::GetSingleton();
532 0 : if (NS_WARN_IF(!cc)) {
533 0 : return;
534 : }
535 :
536 0 : mPendingOperation = false;
537 0 : cc->DeleteGetFilesRequest(mUUID, this);
538 : }
539 :
540 : bool
541 0 : GetFilesHelperChild::AppendBlobImpl(BlobImpl* aBlobImpl)
542 : {
543 0 : MOZ_ASSERT(mPendingOperation);
544 0 : MOZ_ASSERT(aBlobImpl);
545 0 : MOZ_ASSERT(aBlobImpl->IsFile());
546 :
547 0 : RefPtr<File> file = File::Create(mGlobal, aBlobImpl);
548 0 : MOZ_ASSERT(file);
549 :
550 0 : return mFiles.AppendElement(file, fallible);
551 : }
552 :
553 : void
554 0 : GetFilesHelperChild::Finished(nsresult aError)
555 : {
556 0 : MOZ_ASSERT(mPendingOperation);
557 0 : MOZ_ASSERT(NS_SUCCEEDED(mErrorResult));
558 :
559 0 : mPendingOperation = false;
560 0 : mErrorResult = aError;
561 :
562 0 : OperationCompleted();
563 0 : }
564 :
565 : ///////////////////////////////////////////////////////////////////////////////
566 : // GetFilesHelperParent class
567 :
568 0 : class GetFilesHelperParentCallback final : public GetFilesCallback
569 : {
570 : public:
571 0 : explicit GetFilesHelperParentCallback(GetFilesHelperParent* aParent)
572 0 : : mParent(aParent)
573 : {
574 0 : MOZ_ASSERT(aParent);
575 0 : }
576 :
577 : void
578 0 : Callback(nsresult aStatus, const Sequence<RefPtr<File>>& aFiles) override
579 : {
580 0 : if (NS_FAILED(aStatus)) {
581 0 : mParent->mContentParent->SendGetFilesResponseAndForget(mParent->mUUID,
582 0 : GetFilesResponseFailure(aStatus));
583 0 : return;
584 : }
585 :
586 0 : GetFilesResponseSuccess success;
587 :
588 0 : nsTArray<IPCBlob>& ipcBlobs = success.blobs();
589 0 : ipcBlobs.SetLength(aFiles.Length());
590 :
591 0 : for (uint32_t i = 0; i < aFiles.Length(); ++i) {
592 0 : nsresult rv = IPCBlobUtils::Serialize(aFiles[i]->Impl(),
593 0 : mParent->mContentParent,
594 0 : ipcBlobs[i]);
595 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
596 0 : mParent->mContentParent->SendGetFilesResponseAndForget(mParent->mUUID,
597 0 : GetFilesResponseFailure(NS_ERROR_OUT_OF_MEMORY));
598 0 : return;
599 : }
600 : }
601 :
602 0 : mParent->mContentParent->SendGetFilesResponseAndForget(mParent->mUUID,
603 0 : success);
604 : }
605 :
606 : private:
607 : // Raw pointer because this callback is kept alive by this parent object.
608 : GetFilesHelperParent* mParent;
609 : };
610 :
611 0 : GetFilesHelperParent::GetFilesHelperParent(const nsID& aUUID,
612 : ContentParent* aContentParent,
613 0 : bool aRecursiveFlag)
614 : : GetFilesHelper(nullptr, aRecursiveFlag)
615 : , mContentParent(aContentParent)
616 0 : , mUUID(aUUID)
617 0 : {}
618 :
619 0 : GetFilesHelperParent::~GetFilesHelperParent()
620 : {
621 : NS_ReleaseOnMainThread(
622 0 : "GetFilesHelperParent::mContentParent", mContentParent.forget());
623 0 : }
624 :
625 : /* static */ already_AddRefed<GetFilesHelperParent>
626 0 : GetFilesHelperParent::Create(const nsID& aUUID, const nsAString& aDirectoryPath,
627 : bool aRecursiveFlag, ContentParent* aContentParent,
628 : ErrorResult& aRv)
629 : {
630 0 : MOZ_ASSERT(aContentParent);
631 :
632 : RefPtr<GetFilesHelperParent> helper =
633 0 : new GetFilesHelperParent(aUUID, aContentParent, aRecursiveFlag);
634 0 : helper->SetDirectoryPath(aDirectoryPath);
635 :
636 0 : helper->Work(aRv);
637 0 : if (NS_WARN_IF(aRv.Failed())) {
638 0 : return nullptr;
639 : }
640 :
641 : RefPtr<GetFilesHelperParentCallback> callback =
642 0 : new GetFilesHelperParentCallback(helper);
643 0 : helper->AddCallback(callback);
644 :
645 0 : return helper.forget();
646 : }
647 :
648 : } // dom namespace
649 : } // mozilla namespace
|