/src/mozilla-central/dom/ipc/FilePickerParent.cpp
Line | Count | Source (jump to first uncovered line) |
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 "FilePickerParent.h" |
8 | | #include "nsComponentManagerUtils.h" |
9 | | #include "nsNetCID.h" |
10 | | #include "nsIDocument.h" |
11 | | #include "nsIDOMWindow.h" |
12 | | #include "nsIFile.h" |
13 | | #include "nsISimpleEnumerator.h" |
14 | | #include "mozilla/Unused.h" |
15 | | #include "mozilla/dom/FileBlobImpl.h" |
16 | | #include "mozilla/dom/FileSystemSecurity.h" |
17 | | #include "mozilla/dom/ContentParent.h" |
18 | | #include "mozilla/dom/Element.h" |
19 | | #include "mozilla/dom/TabParent.h" |
20 | | #include "mozilla/dom/IPCBlobUtils.h" |
21 | | |
22 | | using mozilla::Unused; |
23 | | using namespace mozilla::dom; |
24 | | |
25 | | NS_IMPL_ISUPPORTS(FilePickerParent::FilePickerShownCallback, |
26 | | nsIFilePickerShownCallback); |
27 | | |
28 | | NS_IMETHODIMP |
29 | | FilePickerParent::FilePickerShownCallback::Done(int16_t aResult) |
30 | 0 | { |
31 | 0 | if (mFilePickerParent) { |
32 | 0 | mFilePickerParent->Done(aResult); |
33 | 0 | } |
34 | 0 | return NS_OK; |
35 | 0 | } |
36 | | |
37 | | void |
38 | | FilePickerParent::FilePickerShownCallback::Destroy() |
39 | 0 | { |
40 | 0 | mFilePickerParent = nullptr; |
41 | 0 | } |
42 | | |
43 | | FilePickerParent::~FilePickerParent() |
44 | 0 | { |
45 | 0 | } |
46 | | |
47 | | // We run code in three places: |
48 | | // 1. The main thread calls Dispatch() to start the runnable. |
49 | | // 2. The stream transport thread stat()s the file in Run() and then dispatches |
50 | | // the same runnable on the main thread. |
51 | | // 3. The main thread sends the results over IPC. |
52 | | FilePickerParent::IORunnable::IORunnable(FilePickerParent* aFPParent, |
53 | | nsTArray<nsCOMPtr<nsIFile>>& aFiles, |
54 | | bool aIsDirectory) |
55 | | : mozilla::Runnable("dom::FilePickerParent::IORunnable") |
56 | | , mFilePickerParent(aFPParent) |
57 | | , mIsDirectory(aIsDirectory) |
58 | 0 | { |
59 | 0 | mFiles.SwapElements(aFiles); |
60 | 0 | MOZ_ASSERT_IF(aIsDirectory, mFiles.Length() == 1); |
61 | 0 | } |
62 | | |
63 | | bool |
64 | | FilePickerParent::IORunnable::Dispatch() |
65 | 0 | { |
66 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
67 | 0 |
|
68 | 0 | mEventTarget = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); |
69 | 0 | if (!mEventTarget) { |
70 | 0 | return false; |
71 | 0 | } |
72 | 0 | |
73 | 0 | nsresult rv = mEventTarget->Dispatch(this, NS_DISPATCH_NORMAL); |
74 | 0 | return NS_SUCCEEDED(rv); |
75 | 0 | } |
76 | | |
77 | | NS_IMETHODIMP |
78 | | FilePickerParent::IORunnable::Run() |
79 | 0 | { |
80 | 0 | // If we're on the main thread, then that means we're done. Just send the |
81 | 0 | // results. |
82 | 0 | if (NS_IsMainThread()) { |
83 | 0 | if (mFilePickerParent) { |
84 | 0 | mFilePickerParent->SendFilesOrDirectories(mResults); |
85 | 0 | } |
86 | 0 | return NS_OK; |
87 | 0 | } |
88 | 0 |
|
89 | 0 | // We're not on the main thread, so do the IO. |
90 | 0 |
|
91 | 0 | for (uint32_t i = 0; i < mFiles.Length(); ++i) { |
92 | 0 | if (mIsDirectory) { |
93 | 0 | nsAutoString path; |
94 | 0 | nsresult rv = mFiles[i]->GetPath(path); |
95 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
96 | 0 | continue; |
97 | 0 | } |
98 | 0 | |
99 | 0 | BlobImplOrString* data = mResults.AppendElement(); |
100 | 0 | data->mType = BlobImplOrString::eDirectoryPath; |
101 | 0 | data->mDirectoryPath = path; |
102 | 0 | continue; |
103 | 0 | } |
104 | 0 | |
105 | 0 | RefPtr<BlobImpl> blobImpl = new FileBlobImpl(mFiles[i]); |
106 | 0 |
|
107 | 0 | ErrorResult error; |
108 | 0 | blobImpl->GetSize(error); |
109 | 0 | if (NS_WARN_IF(error.Failed())) { |
110 | 0 | error.SuppressException(); |
111 | 0 | continue; |
112 | 0 | } |
113 | 0 | |
114 | 0 | blobImpl->GetLastModified(error); |
115 | 0 | if (NS_WARN_IF(error.Failed())) { |
116 | 0 | error.SuppressException(); |
117 | 0 | continue; |
118 | 0 | } |
119 | 0 | |
120 | 0 | BlobImplOrString* data = mResults.AppendElement(); |
121 | 0 | data->mType = BlobImplOrString::eBlobImpl; |
122 | 0 | data->mBlobImpl = blobImpl; |
123 | 0 | } |
124 | 0 |
|
125 | 0 | // Dispatch ourselves back on the main thread. |
126 | 0 | if (NS_FAILED(NS_DispatchToMainThread(this))) { |
127 | 0 | // It's hard to see how we can recover gracefully in this case. The child |
128 | 0 | // process is waiting for an IPC, but that can only happen on the main |
129 | 0 | // thread. |
130 | 0 | MOZ_CRASH(); |
131 | 0 | } |
132 | 0 |
|
133 | 0 | return NS_OK; |
134 | 0 | } |
135 | | |
136 | | void |
137 | | FilePickerParent::IORunnable::Destroy() |
138 | 0 | { |
139 | 0 | mFilePickerParent = nullptr; |
140 | 0 | } |
141 | | |
142 | | void |
143 | | FilePickerParent::SendFilesOrDirectories(const nsTArray<BlobImplOrString>& aData) |
144 | 0 | { |
145 | 0 | nsIContentParent* parent = TabParent::GetFrom(Manager())->Manager(); |
146 | 0 |
|
147 | 0 | if (mMode == nsIFilePicker::modeGetFolder) { |
148 | 0 | MOZ_ASSERT(aData.Length() <= 1); |
149 | 0 | if (aData.IsEmpty()) { |
150 | 0 | Unused << Send__delete__(this, void_t(), mResult); |
151 | 0 | return; |
152 | 0 | } |
153 | 0 | |
154 | 0 | MOZ_ASSERT(aData[0].mType == BlobImplOrString::eDirectoryPath); |
155 | 0 |
|
156 | 0 | // Let's inform the security singleton about the given access of this tab on |
157 | 0 | // this directory path. |
158 | 0 | RefPtr<FileSystemSecurity> fss = FileSystemSecurity::GetOrCreate(); |
159 | 0 | fss->GrantAccessToContentProcess(parent->ChildID(), |
160 | 0 | aData[0].mDirectoryPath); |
161 | 0 |
|
162 | 0 | InputDirectory input; |
163 | 0 | input.directoryPath() = aData[0].mDirectoryPath; |
164 | 0 | Unused << Send__delete__(this, input, mResult); |
165 | 0 | return; |
166 | 0 | } |
167 | 0 | |
168 | 0 | InfallibleTArray<IPCBlob> ipcBlobs; |
169 | 0 |
|
170 | 0 | for (unsigned i = 0; i < aData.Length(); i++) { |
171 | 0 | IPCBlob ipcBlob; |
172 | 0 |
|
173 | 0 | MOZ_ASSERT(aData[i].mType == BlobImplOrString::eBlobImpl); |
174 | 0 | nsresult rv = IPCBlobUtils::Serialize(aData[i].mBlobImpl, parent, ipcBlob); |
175 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
176 | 0 | break; |
177 | 0 | } |
178 | 0 | |
179 | 0 | ipcBlobs.AppendElement(ipcBlob); |
180 | 0 | } |
181 | 0 |
|
182 | 0 | InputBlobs inblobs; |
183 | 0 | inblobs.blobs().SwapElements(ipcBlobs); |
184 | 0 |
|
185 | 0 | Unused << Send__delete__(this, inblobs, mResult); |
186 | 0 | } |
187 | | |
188 | | void |
189 | | FilePickerParent::Done(int16_t aResult) |
190 | 0 | { |
191 | 0 | mResult = aResult; |
192 | 0 |
|
193 | 0 | if (mResult != nsIFilePicker::returnOK) { |
194 | 0 | Unused << Send__delete__(this, void_t(), mResult); |
195 | 0 | return; |
196 | 0 | } |
197 | 0 | |
198 | 0 | nsTArray<nsCOMPtr<nsIFile>> files; |
199 | 0 | if (mMode == nsIFilePicker::modeOpenMultiple) { |
200 | 0 | nsCOMPtr<nsISimpleEnumerator> iter; |
201 | 0 | NS_ENSURE_SUCCESS_VOID(mFilePicker->GetFiles(getter_AddRefs(iter))); |
202 | 0 |
|
203 | 0 | nsCOMPtr<nsISupports> supports; |
204 | 0 | bool loop = true; |
205 | 0 | while (NS_SUCCEEDED(iter->HasMoreElements(&loop)) && loop) { |
206 | 0 | iter->GetNext(getter_AddRefs(supports)); |
207 | 0 | if (supports) { |
208 | 0 | nsCOMPtr<nsIFile> file = do_QueryInterface(supports); |
209 | 0 | MOZ_ASSERT(file); |
210 | 0 | files.AppendElement(file); |
211 | 0 | } |
212 | 0 | } |
213 | 0 | } else { |
214 | 0 | nsCOMPtr<nsIFile> file; |
215 | 0 | mFilePicker->GetFile(getter_AddRefs(file)); |
216 | 0 | if (file) { |
217 | 0 | files.AppendElement(file); |
218 | 0 | } |
219 | 0 | } |
220 | 0 |
|
221 | 0 | if (files.IsEmpty()) { |
222 | 0 | Unused << Send__delete__(this, void_t(), mResult); |
223 | 0 | return; |
224 | 0 | } |
225 | 0 | |
226 | 0 | MOZ_ASSERT(!mRunnable); |
227 | 0 | mRunnable = new IORunnable(this, files, mMode == nsIFilePicker::modeGetFolder); |
228 | 0 |
|
229 | 0 | // Dispatch to background thread to do I/O: |
230 | 0 | if (!mRunnable->Dispatch()) { |
231 | 0 | Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel); |
232 | 0 | } |
233 | 0 | } |
234 | | |
235 | | bool |
236 | | FilePickerParent::CreateFilePicker() |
237 | 0 | { |
238 | 0 | mFilePicker = do_CreateInstance("@mozilla.org/filepicker;1"); |
239 | 0 | if (!mFilePicker) { |
240 | 0 | return false; |
241 | 0 | } |
242 | 0 | |
243 | 0 | Element* element = TabParent::GetFrom(Manager())->GetOwnerElement(); |
244 | 0 | if (!element) { |
245 | 0 | return false; |
246 | 0 | } |
247 | 0 | |
248 | 0 | nsCOMPtr<mozIDOMWindowProxy> window = element->OwnerDoc()->GetWindow(); |
249 | 0 | if (!window) { |
250 | 0 | return false; |
251 | 0 | } |
252 | 0 | |
253 | 0 | return NS_SUCCEEDED(mFilePicker->Init(window, mTitle, mMode)); |
254 | 0 | } |
255 | | |
256 | | mozilla::ipc::IPCResult |
257 | | FilePickerParent::RecvOpen(const int16_t& aSelectedType, |
258 | | const bool& aAddToRecentDocs, |
259 | | const nsString& aDefaultFile, |
260 | | const nsString& aDefaultExtension, |
261 | | InfallibleTArray<nsString>&& aFilters, |
262 | | InfallibleTArray<nsString>&& aFilterNames, |
263 | | const nsString& aDisplayDirectory, |
264 | | const nsString& aDisplaySpecialDirectory, |
265 | | const nsString& aOkButtonLabel) |
266 | 0 | { |
267 | 0 | if (!CreateFilePicker()) { |
268 | 0 | Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel); |
269 | 0 | return IPC_OK(); |
270 | 0 | } |
271 | 0 |
|
272 | 0 | mFilePicker->SetAddToRecentDocs(aAddToRecentDocs); |
273 | 0 |
|
274 | 0 | for (uint32_t i = 0; i < aFilters.Length(); ++i) { |
275 | 0 | mFilePicker->AppendFilter(aFilterNames[i], aFilters[i]); |
276 | 0 | } |
277 | 0 |
|
278 | 0 | mFilePicker->SetDefaultString(aDefaultFile); |
279 | 0 | mFilePicker->SetDefaultExtension(aDefaultExtension); |
280 | 0 | mFilePicker->SetFilterIndex(aSelectedType); |
281 | 0 | mFilePicker->SetOkButtonLabel(aOkButtonLabel); |
282 | 0 |
|
283 | 0 | if (!aDisplayDirectory.IsEmpty()) { |
284 | 0 | nsCOMPtr<nsIFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); |
285 | 0 | if (localFile) { |
286 | 0 | localFile->InitWithPath(aDisplayDirectory); |
287 | 0 | mFilePicker->SetDisplayDirectory(localFile); |
288 | 0 | } |
289 | 0 | } else if (!aDisplaySpecialDirectory.IsEmpty()) { |
290 | 0 | mFilePicker->SetDisplaySpecialDirectory(aDisplaySpecialDirectory); |
291 | 0 | } |
292 | 0 |
|
293 | 0 | mCallback = new FilePickerShownCallback(this); |
294 | 0 |
|
295 | 0 | mFilePicker->Open(mCallback); |
296 | 0 | return IPC_OK(); |
297 | 0 | } |
298 | | |
299 | | void |
300 | | FilePickerParent::ActorDestroy(ActorDestroyReason aWhy) |
301 | 0 | { |
302 | 0 | if (mCallback) { |
303 | 0 | mCallback->Destroy(); |
304 | 0 | mCallback = nullptr; |
305 | 0 | } |
306 | 0 | if (mRunnable) { |
307 | 0 | mRunnable->Destroy(); |
308 | 0 | mRunnable = nullptr; |
309 | 0 | } |
310 | 0 | } |