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/. */
7 #include "FilePickerParent.h"
8 #include "nsComponentManagerUtils.h"
10 #include "mozilla/dom/Document.h"
12 #include "nsISimpleEnumerator.h"
13 #include "mozilla/Unused.h"
14 #include "mozilla/dom/BrowserParent.h"
15 #include "mozilla/dom/CanonicalBrowsingContext.h"
16 #include "mozilla/dom/ContentParent.h"
17 #include "mozilla/dom/Element.h"
18 #include "mozilla/dom/FileBlobImpl.h"
19 #include "mozilla/dom/FileSystemSecurity.h"
20 #include "mozilla/dom/IPCBlobUtils.h"
22 using mozilla::Unused
;
23 using namespace mozilla::dom
;
25 NS_IMPL_ISUPPORTS(FilePickerParent::FilePickerShownCallback
,
26 nsIFilePickerShownCallback
);
29 FilePickerParent::FilePickerShownCallback::Done(
30 nsIFilePicker::ResultCode aResult
) {
31 if (mFilePickerParent
) {
32 mFilePickerParent
->Done(aResult
);
37 void FilePickerParent::FilePickerShownCallback::Destroy() {
38 mFilePickerParent
= nullptr;
41 FilePickerParent::~FilePickerParent() = default;
43 // We run code in three places:
44 // 1. The main thread calls Dispatch() to start the runnable.
45 // 2. The stream transport thread stat()s the file in Run() and then dispatches
46 // the same runnable on the main thread.
47 // 3. The main thread sends the results over IPC.
48 FilePickerParent::IORunnable::IORunnable(FilePickerParent
* aFPParent
,
49 nsTArray
<nsCOMPtr
<nsIFile
>>&& aFiles
,
51 : mozilla::Runnable("dom::FilePickerParent::IORunnable"),
52 mFilePickerParent(aFPParent
),
53 mFiles(std::move(aFiles
)),
54 mIsDirectory(aIsDirectory
) {
55 MOZ_ASSERT_IF(aIsDirectory
, mFiles
.Length() == 1);
58 bool FilePickerParent::IORunnable::Dispatch() {
59 MOZ_ASSERT(NS_IsMainThread());
61 mEventTarget
= do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
);
66 nsresult rv
= mEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
);
67 return NS_SUCCEEDED(rv
);
71 FilePickerParent::IORunnable::Run() {
72 // If we're on the main thread, then that means we're done. Just send the
74 if (NS_IsMainThread()) {
75 if (mFilePickerParent
) {
76 mFilePickerParent
->SendFilesOrDirectories(mResults
);
81 // We're not on the main thread, so do the IO.
83 for (uint32_t i
= 0; i
< mFiles
.Length(); ++i
) {
86 nsresult rv
= mFiles
[i
]->GetPath(path
);
87 if (NS_WARN_IF(NS_FAILED(rv
))) {
91 BlobImplOrString
* data
= mResults
.AppendElement();
92 data
->mType
= BlobImplOrString::eDirectoryPath
;
93 data
->mDirectoryPath
= path
;
97 RefPtr
<BlobImpl
> blobImpl
= new FileBlobImpl(mFiles
[i
]);
100 blobImpl
->GetSize(error
);
101 if (NS_WARN_IF(error
.Failed())) {
102 error
.SuppressException();
106 blobImpl
->GetLastModified(error
);
107 if (NS_WARN_IF(error
.Failed())) {
108 error
.SuppressException();
112 BlobImplOrString
* data
= mResults
.AppendElement();
113 data
->mType
= BlobImplOrString::eBlobImpl
;
114 data
->mBlobImpl
= blobImpl
;
117 // Dispatch ourselves back on the main thread.
118 if (NS_FAILED(NS_DispatchToMainThread(this))) {
119 // It's hard to see how we can recover gracefully in this case. The child
120 // process is waiting for an IPC, but that can only happen on the main
128 void FilePickerParent::IORunnable::Destroy() { mFilePickerParent
= nullptr; }
130 void FilePickerParent::SendFilesOrDirectories(
131 const nsTArray
<BlobImplOrString
>& aData
) {
132 ContentParent
* parent
= BrowserParent::GetFrom(Manager())->Manager();
134 if (mMode
== nsIFilePicker::modeGetFolder
) {
135 MOZ_ASSERT(aData
.Length() <= 1);
136 if (aData
.IsEmpty()) {
137 Unused
<< Send__delete__(this, void_t(), mResult
);
141 MOZ_ASSERT(aData
[0].mType
== BlobImplOrString::eDirectoryPath
);
143 // Let's inform the security singleton about the given access of this tab on
144 // this directory path.
145 RefPtr
<FileSystemSecurity
> fss
= FileSystemSecurity::GetOrCreate();
146 fss
->GrantAccessToContentProcess(parent
->ChildID(),
147 aData
[0].mDirectoryPath
);
149 InputDirectory input
;
150 input
.directoryPath() = aData
[0].mDirectoryPath
;
151 Unused
<< Send__delete__(this, input
, mResult
);
155 nsTArray
<IPCBlob
> ipcBlobs
;
157 for (unsigned i
= 0; i
< aData
.Length(); i
++) {
160 MOZ_ASSERT(aData
[i
].mType
== BlobImplOrString::eBlobImpl
);
161 nsresult rv
= IPCBlobUtils::Serialize(aData
[i
].mBlobImpl
, ipcBlob
);
162 if (NS_WARN_IF(NS_FAILED(rv
))) {
166 ipcBlobs
.AppendElement(ipcBlob
);
170 inblobs
.blobs() = std::move(ipcBlobs
);
172 Unused
<< Send__delete__(this, inblobs
, mResult
);
175 void FilePickerParent::Done(nsIFilePicker::ResultCode aResult
) {
178 if (mResult
!= nsIFilePicker::returnOK
) {
179 Unused
<< Send__delete__(this, void_t(), mResult
);
183 nsTArray
<nsCOMPtr
<nsIFile
>> files
;
184 if (mMode
== nsIFilePicker::modeOpenMultiple
) {
185 nsCOMPtr
<nsISimpleEnumerator
> iter
;
186 NS_ENSURE_SUCCESS_VOID(mFilePicker
->GetFiles(getter_AddRefs(iter
)));
188 nsCOMPtr
<nsISupports
> supports
;
190 while (NS_SUCCEEDED(iter
->HasMoreElements(&loop
)) && loop
) {
191 iter
->GetNext(getter_AddRefs(supports
));
193 nsCOMPtr
<nsIFile
> file
= do_QueryInterface(supports
);
195 files
.AppendElement(file
);
199 nsCOMPtr
<nsIFile
> file
;
200 mFilePicker
->GetFile(getter_AddRefs(file
));
202 files
.AppendElement(file
);
206 if (files
.IsEmpty()) {
207 Unused
<< Send__delete__(this, void_t(), mResult
);
211 MOZ_ASSERT(!mRunnable
);
212 mRunnable
= new IORunnable(this, std::move(files
),
213 mMode
== nsIFilePicker::modeGetFolder
);
215 // Dispatch to background thread to do I/O:
216 if (!mRunnable
->Dispatch()) {
217 Unused
<< Send__delete__(this, void_t(), nsIFilePicker::returnCancel
);
221 bool FilePickerParent::CreateFilePicker() {
222 mFilePicker
= do_CreateInstance("@mozilla.org/filepicker;1");
227 auto* browserParent
= BrowserParent::GetFrom(Manager());
228 auto* browsingContext
= browserParent
->GetBrowsingContext();
229 Element
* element
= browserParent
->GetOwnerElement();
234 nsCOMPtr
<mozIDOMWindowProxy
> window
= element
->OwnerDoc()->GetWindow();
240 mFilePicker
->Init(window
, mTitle
, mMode
, browsingContext
));
243 mozilla::ipc::IPCResult
FilePickerParent::RecvOpen(
244 const int16_t& aSelectedType
, const bool& aAddToRecentDocs
,
245 const nsString
& aDefaultFile
, const nsString
& aDefaultExtension
,
246 nsTArray
<nsString
>&& aFilters
, nsTArray
<nsString
>&& aFilterNames
,
247 nsTArray
<nsString
>&& aRawFilters
, const nsString
& aDisplayDirectory
,
248 const nsString
& aDisplaySpecialDirectory
, const nsString
& aOkButtonLabel
,
249 const nsIFilePicker::CaptureTarget
& aCapture
) {
250 if (!CreateFilePicker()) {
251 Unused
<< Send__delete__(this, void_t(), nsIFilePicker::returnCancel
);
255 mFilePicker
->SetAddToRecentDocs(aAddToRecentDocs
);
257 for (uint32_t i
= 0; i
< aFilters
.Length(); ++i
) {
258 mFilePicker
->AppendFilter(aFilterNames
[i
], aFilters
[i
]);
261 for (uint32_t i
= 0; i
< aRawFilters
.Length(); ++i
) {
262 mFilePicker
->AppendRawFilter(aRawFilters
[i
]);
265 mFilePicker
->SetDefaultString(aDefaultFile
);
266 mFilePicker
->SetDefaultExtension(aDefaultExtension
);
267 mFilePicker
->SetFilterIndex(aSelectedType
);
268 mFilePicker
->SetOkButtonLabel(aOkButtonLabel
);
269 mFilePicker
->SetCapture(aCapture
);
271 if (!aDisplayDirectory
.IsEmpty()) {
272 nsCOMPtr
<nsIFile
> localFile
= do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
);
274 localFile
->InitWithPath(aDisplayDirectory
);
275 mFilePicker
->SetDisplayDirectory(localFile
);
277 } else if (!aDisplaySpecialDirectory
.IsEmpty()) {
278 mFilePicker
->SetDisplaySpecialDirectory(aDisplaySpecialDirectory
);
281 MOZ_ASSERT(!mCallback
);
282 mCallback
= new FilePickerShownCallback(this);
284 mFilePicker
->Open(mCallback
);
288 mozilla::ipc::IPCResult
FilePickerParent::RecvClose() {
290 mFilePicker
->Close();
295 void FilePickerParent::ActorDestroy(ActorDestroyReason aWhy
) {
297 mCallback
->Destroy();
301 mRunnable
->Destroy();