/src/mozilla-central/widget/nsBaseFilePicker.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
2 | | * |
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 "nsCOMPtr.h" |
8 | | #include "nsPIDOMWindow.h" |
9 | | #include "nsIDocShell.h" |
10 | | #include "nsIInterfaceRequestorUtils.h" |
11 | | #include "nsIBaseWindow.h" |
12 | | #include "nsIWidget.h" |
13 | | |
14 | | #include "nsIStringBundle.h" |
15 | | #include "nsString.h" |
16 | | #include "nsIServiceManager.h" |
17 | | #include "nsCOMArray.h" |
18 | | #include "nsIFile.h" |
19 | | #include "nsEnumeratorUtils.h" |
20 | | #include "mozilla/dom/Directory.h" |
21 | | #include "mozilla/dom/File.h" |
22 | | #include "mozilla/Services.h" |
23 | | #include "WidgetUtils.h" |
24 | | #include "nsSimpleEnumerator.h" |
25 | | #include "nsThreadUtils.h" |
26 | | |
27 | | #include "nsBaseFilePicker.h" |
28 | | |
29 | | using namespace mozilla::widget; |
30 | | using namespace mozilla::dom; |
31 | | |
32 | 0 | #define FILEPICKER_TITLES "chrome://global/locale/filepicker.properties" |
33 | 0 | #define FILEPICKER_FILTERS "chrome://global/content/filepicker.properties" |
34 | | |
35 | | namespace { |
36 | | |
37 | | nsresult |
38 | | LocalFileToDirectoryOrBlob(nsPIDOMWindowInner* aWindow, |
39 | | bool aIsDirectory, |
40 | | nsIFile* aFile, |
41 | | nsISupports** aResult) |
42 | 0 | { |
43 | 0 | if (aIsDirectory) { |
44 | | #ifdef DEBUG |
45 | | bool isDir; |
46 | | aFile->IsDirectory(&isDir); |
47 | | MOZ_ASSERT(isDir); |
48 | | #endif |
49 | |
|
50 | 0 | RefPtr<Directory> directory = Directory::Create(aWindow, aFile); |
51 | 0 | MOZ_ASSERT(directory); |
52 | 0 |
|
53 | 0 | directory.forget(aResult); |
54 | 0 | return NS_OK; |
55 | 0 | } |
56 | 0 |
|
57 | 0 | RefPtr<File> file = File::CreateFromFile(aWindow, aFile); |
58 | 0 | file.forget(aResult); |
59 | 0 | return NS_OK; |
60 | 0 | } |
61 | | |
62 | | } // anonymous namespace |
63 | | |
64 | | /** |
65 | | * A runnable to dispatch from the main thread to the main thread to display |
66 | | * the file picker while letting the showAsync method return right away. |
67 | | */ |
68 | | class nsBaseFilePicker::AsyncShowFilePicker : public mozilla::Runnable |
69 | | { |
70 | | public: |
71 | | AsyncShowFilePicker(nsBaseFilePicker* aFilePicker, |
72 | | nsIFilePickerShownCallback* aCallback) |
73 | | : mozilla::Runnable("AsyncShowFilePicker") |
74 | | , mFilePicker(aFilePicker) |
75 | | , mCallback(aCallback) |
76 | 0 | { |
77 | 0 | } |
78 | | |
79 | | NS_IMETHOD Run() override |
80 | 0 | { |
81 | 0 | NS_ASSERTION(NS_IsMainThread(), |
82 | 0 | "AsyncShowFilePicker should be on the main thread!"); |
83 | 0 |
|
84 | 0 | // It's possible that some widget implementations require GUI operations |
85 | 0 | // to be on the main thread, so that's why we're not dispatching to another |
86 | 0 | // thread and calling back to the main after it's done. |
87 | 0 | int16_t result = nsIFilePicker::returnCancel; |
88 | 0 | nsresult rv = mFilePicker->Show(&result); |
89 | 0 | if (NS_FAILED(rv)) { |
90 | 0 | NS_ERROR("FilePicker's Show() implementation failed!"); |
91 | 0 | } |
92 | 0 |
|
93 | 0 | if (mCallback) { |
94 | 0 | mCallback->Done(result); |
95 | 0 | } |
96 | 0 | return NS_OK; |
97 | 0 | } |
98 | | |
99 | | private: |
100 | | RefPtr<nsBaseFilePicker> mFilePicker; |
101 | | RefPtr<nsIFilePickerShownCallback> mCallback; |
102 | | }; |
103 | | |
104 | | class nsBaseFilePickerEnumerator : public nsSimpleEnumerator |
105 | | { |
106 | | public: |
107 | | nsBaseFilePickerEnumerator(nsPIDOMWindowOuter* aParent, |
108 | | nsISimpleEnumerator* iterator, |
109 | | int16_t aMode) |
110 | | : mIterator(iterator) |
111 | | , mParent(aParent->GetCurrentInnerWindow()) |
112 | | , mMode(aMode) |
113 | 0 | {} |
114 | | |
115 | 0 | const nsID& DefaultInterface() override { return NS_GET_IID(nsIFile); } |
116 | | |
117 | | NS_IMETHOD |
118 | | GetNext(nsISupports** aResult) override |
119 | 0 | { |
120 | 0 | nsCOMPtr<nsISupports> tmp; |
121 | 0 | nsresult rv = mIterator->GetNext(getter_AddRefs(tmp)); |
122 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
123 | 0 |
|
124 | 0 | if (!tmp) { |
125 | 0 | return NS_OK; |
126 | 0 | } |
127 | 0 | |
128 | 0 | nsCOMPtr<nsIFile> localFile = do_QueryInterface(tmp); |
129 | 0 | if (!localFile) { |
130 | 0 | return NS_ERROR_FAILURE; |
131 | 0 | } |
132 | 0 | |
133 | 0 | return LocalFileToDirectoryOrBlob(mParent, |
134 | 0 | mMode == nsIFilePicker::modeGetFolder, |
135 | 0 | localFile, |
136 | 0 | aResult); |
137 | 0 | } |
138 | | |
139 | | NS_IMETHOD |
140 | | HasMoreElements(bool* aResult) override |
141 | 0 | { |
142 | 0 | return mIterator->HasMoreElements(aResult); |
143 | 0 | } |
144 | | |
145 | | private: |
146 | | nsCOMPtr<nsISimpleEnumerator> mIterator; |
147 | | nsCOMPtr<nsPIDOMWindowInner> mParent; |
148 | | int16_t mMode; |
149 | | }; |
150 | | |
151 | | nsBaseFilePicker::nsBaseFilePicker() |
152 | | : mAddToRecentDocs(true) |
153 | | , mMode(nsIFilePicker::modeOpen) |
154 | 0 | { |
155 | 0 |
|
156 | 0 | } |
157 | | |
158 | | nsBaseFilePicker::~nsBaseFilePicker() |
159 | 0 | { |
160 | 0 |
|
161 | 0 | } |
162 | | |
163 | | NS_IMETHODIMP nsBaseFilePicker::Init(mozIDOMWindowProxy* aParent, |
164 | | const nsAString& aTitle, |
165 | | int16_t aMode) |
166 | 0 | { |
167 | 0 | MOZ_ASSERT(aParent, "Null parent passed to filepicker, no file " |
168 | 0 | "picker for you!"); |
169 | 0 |
|
170 | 0 | mParent = nsPIDOMWindowOuter::From(aParent); |
171 | 0 |
|
172 | 0 | nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(mParent->GetOuterWindow()); |
173 | 0 | NS_ENSURE_TRUE(widget, NS_ERROR_FAILURE); |
174 | 0 |
|
175 | 0 |
|
176 | 0 | mMode = aMode; |
177 | 0 | InitNative(widget, aTitle); |
178 | 0 |
|
179 | 0 | return NS_OK; |
180 | 0 | } |
181 | | |
182 | | NS_IMETHODIMP |
183 | | nsBaseFilePicker::Open(nsIFilePickerShownCallback *aCallback) |
184 | 0 | { |
185 | 0 | nsCOMPtr<nsIRunnable> filePickerEvent = |
186 | 0 | new AsyncShowFilePicker(this, aCallback); |
187 | 0 | return NS_DispatchToMainThread(filePickerEvent); |
188 | 0 | } |
189 | | |
190 | | NS_IMETHODIMP |
191 | | nsBaseFilePicker::AppendFilters(int32_t aFilterMask) |
192 | 0 | { |
193 | 0 | nsCOMPtr<nsIStringBundleService> stringService = |
194 | 0 | mozilla::services::GetStringBundleService(); |
195 | 0 | if (!stringService) |
196 | 0 | return NS_ERROR_FAILURE; |
197 | 0 | |
198 | 0 | nsCOMPtr<nsIStringBundle> titleBundle, filterBundle; |
199 | 0 |
|
200 | 0 | nsresult rv = stringService->CreateBundle(FILEPICKER_TITLES, |
201 | 0 | getter_AddRefs(titleBundle)); |
202 | 0 | if (NS_FAILED(rv)) |
203 | 0 | return NS_ERROR_FAILURE; |
204 | 0 | |
205 | 0 | rv = stringService->CreateBundle(FILEPICKER_FILTERS, getter_AddRefs(filterBundle)); |
206 | 0 | if (NS_FAILED(rv)) |
207 | 0 | return NS_ERROR_FAILURE; |
208 | 0 | |
209 | 0 | nsAutoString title; |
210 | 0 | nsAutoString filter; |
211 | 0 |
|
212 | 0 | if (aFilterMask & filterAll) { |
213 | 0 | titleBundle->GetStringFromName("allTitle", title); |
214 | 0 | filterBundle->GetStringFromName("allFilter", filter); |
215 | 0 | AppendFilter(title,filter); |
216 | 0 | } |
217 | 0 | if (aFilterMask & filterHTML) { |
218 | 0 | titleBundle->GetStringFromName("htmlTitle", title); |
219 | 0 | filterBundle->GetStringFromName("htmlFilter", filter); |
220 | 0 | AppendFilter(title,filter); |
221 | 0 | } |
222 | 0 | if (aFilterMask & filterText) { |
223 | 0 | titleBundle->GetStringFromName("textTitle", title); |
224 | 0 | filterBundle->GetStringFromName("textFilter", filter); |
225 | 0 | AppendFilter(title,filter); |
226 | 0 | } |
227 | 0 | if (aFilterMask & filterImages) { |
228 | 0 | titleBundle->GetStringFromName("imageTitle", title); |
229 | 0 | filterBundle->GetStringFromName("imageFilter", filter); |
230 | 0 | AppendFilter(title,filter); |
231 | 0 | } |
232 | 0 | if (aFilterMask & filterAudio) { |
233 | 0 | titleBundle->GetStringFromName("audioTitle", title); |
234 | 0 | filterBundle->GetStringFromName("audioFilter", filter); |
235 | 0 | AppendFilter(title,filter); |
236 | 0 | } |
237 | 0 | if (aFilterMask & filterVideo) { |
238 | 0 | titleBundle->GetStringFromName("videoTitle", title); |
239 | 0 | filterBundle->GetStringFromName("videoFilter", filter); |
240 | 0 | AppendFilter(title,filter); |
241 | 0 | } |
242 | 0 | if (aFilterMask & filterXML) { |
243 | 0 | titleBundle->GetStringFromName("xmlTitle", title); |
244 | 0 | filterBundle->GetStringFromName("xmlFilter", filter); |
245 | 0 | AppendFilter(title,filter); |
246 | 0 | } |
247 | 0 | if (aFilterMask & filterXUL) { |
248 | 0 | titleBundle->GetStringFromName("xulTitle", title); |
249 | 0 | filterBundle->GetStringFromName("xulFilter", filter); |
250 | 0 | AppendFilter(title, filter); |
251 | 0 | } |
252 | 0 | if (aFilterMask & filterApps) { |
253 | 0 | titleBundle->GetStringFromName("appsTitle", title); |
254 | 0 | // Pass the magic string "..apps" to the platform filepicker, which it |
255 | 0 | // should recognize and do the correct platform behavior for. |
256 | 0 | AppendFilter(title, NS_LITERAL_STRING("..apps")); |
257 | 0 | } |
258 | 0 | return NS_OK; |
259 | 0 | } |
260 | | |
261 | | // Set the filter index |
262 | | NS_IMETHODIMP nsBaseFilePicker::GetFilterIndex(int32_t *aFilterIndex) |
263 | 0 | { |
264 | 0 | *aFilterIndex = 0; |
265 | 0 | return NS_OK; |
266 | 0 | } |
267 | | |
268 | | NS_IMETHODIMP nsBaseFilePicker::SetFilterIndex(int32_t aFilterIndex) |
269 | 0 | { |
270 | 0 | return NS_OK; |
271 | 0 | } |
272 | | |
273 | | NS_IMETHODIMP nsBaseFilePicker::GetFiles(nsISimpleEnumerator **aFiles) |
274 | 0 | { |
275 | 0 | NS_ENSURE_ARG_POINTER(aFiles); |
276 | 0 | nsCOMArray <nsIFile> files; |
277 | 0 | nsresult rv; |
278 | 0 |
|
279 | 0 | // if we get into the base class, the platform |
280 | 0 | // doesn't implement GetFiles() yet. |
281 | 0 | // so we fake it. |
282 | 0 | nsCOMPtr <nsIFile> file; |
283 | 0 | rv = GetFile(getter_AddRefs(file)); |
284 | 0 | NS_ENSURE_SUCCESS(rv,rv); |
285 | 0 |
|
286 | 0 | files.AppendObject(file); |
287 | 0 |
|
288 | 0 | return NS_NewArrayEnumerator(aFiles, files, NS_GET_IID(nsIFile)); |
289 | 0 | } |
290 | | |
291 | | // Set the display directory |
292 | | NS_IMETHODIMP nsBaseFilePicker::SetDisplayDirectory(nsIFile *aDirectory) |
293 | 0 | { |
294 | 0 | // if displaySpecialDirectory has been previously called, let's abort this |
295 | 0 | // operation. |
296 | 0 | if (!mDisplaySpecialDirectory.IsEmpty()) { |
297 | 0 | return NS_OK; |
298 | 0 | } |
299 | 0 | |
300 | 0 | if (!aDirectory) { |
301 | 0 | mDisplayDirectory = nullptr; |
302 | 0 | return NS_OK; |
303 | 0 | } |
304 | 0 | nsCOMPtr<nsIFile> directory; |
305 | 0 | nsresult rv = aDirectory->Clone(getter_AddRefs(directory)); |
306 | 0 | if (NS_FAILED(rv)) |
307 | 0 | return rv; |
308 | 0 | mDisplayDirectory = do_QueryInterface(directory, &rv); |
309 | 0 | return rv; |
310 | 0 | } |
311 | | |
312 | | // Get the display directory |
313 | | NS_IMETHODIMP nsBaseFilePicker::GetDisplayDirectory(nsIFile **aDirectory) |
314 | 0 | { |
315 | 0 | *aDirectory = nullptr; |
316 | 0 |
|
317 | 0 | // if displaySpecialDirectory has been previously called, let's abort this |
318 | 0 | // operation. |
319 | 0 | if (!mDisplaySpecialDirectory.IsEmpty()) { |
320 | 0 | return NS_OK; |
321 | 0 | } |
322 | 0 | |
323 | 0 | if (!mDisplayDirectory) |
324 | 0 | return NS_OK; |
325 | 0 | nsCOMPtr<nsIFile> directory; |
326 | 0 | nsresult rv = mDisplayDirectory->Clone(getter_AddRefs(directory)); |
327 | 0 | if (NS_FAILED(rv)) { |
328 | 0 | return rv; |
329 | 0 | } |
330 | 0 | directory.forget(aDirectory); |
331 | 0 | return NS_OK; |
332 | 0 | } |
333 | | |
334 | | // Set the display special directory |
335 | | NS_IMETHODIMP nsBaseFilePicker::SetDisplaySpecialDirectory(const nsAString& aDirectory) |
336 | 0 | { |
337 | 0 | // if displayDirectory has been previously called, let's abort this operation. |
338 | 0 | if (mDisplayDirectory && mDisplaySpecialDirectory.IsEmpty()) { |
339 | 0 | return NS_OK; |
340 | 0 | } |
341 | 0 | |
342 | 0 | mDisplaySpecialDirectory = aDirectory; |
343 | 0 | if (mDisplaySpecialDirectory.IsEmpty()) { |
344 | 0 | mDisplayDirectory = nullptr; |
345 | 0 | return NS_OK; |
346 | 0 | } |
347 | 0 | |
348 | 0 | return NS_GetSpecialDirectory(NS_ConvertUTF16toUTF8(mDisplaySpecialDirectory).get(), |
349 | 0 | getter_AddRefs(mDisplayDirectory)); |
350 | 0 | } |
351 | | |
352 | | // Get the display special directory |
353 | | NS_IMETHODIMP nsBaseFilePicker::GetDisplaySpecialDirectory(nsAString& aDirectory) |
354 | 0 | { |
355 | 0 | aDirectory = mDisplaySpecialDirectory; |
356 | 0 | return NS_OK; |
357 | 0 | } |
358 | | |
359 | | NS_IMETHODIMP |
360 | | nsBaseFilePicker::GetAddToRecentDocs(bool *aFlag) |
361 | 0 | { |
362 | 0 | *aFlag = mAddToRecentDocs; |
363 | 0 | return NS_OK; |
364 | 0 | } |
365 | | |
366 | | NS_IMETHODIMP |
367 | | nsBaseFilePicker::SetAddToRecentDocs(bool aFlag) |
368 | 0 | { |
369 | 0 | mAddToRecentDocs = aFlag; |
370 | 0 | return NS_OK; |
371 | 0 | } |
372 | | |
373 | | NS_IMETHODIMP |
374 | | nsBaseFilePicker::GetMode(int16_t* aMode) |
375 | 0 | { |
376 | 0 | *aMode = mMode; |
377 | 0 | return NS_OK; |
378 | 0 | } |
379 | | |
380 | | NS_IMETHODIMP |
381 | | nsBaseFilePicker::SetOkButtonLabel(const nsAString& aLabel) |
382 | 0 | { |
383 | 0 | mOkButtonLabel = aLabel; |
384 | 0 | return NS_OK; |
385 | 0 | } |
386 | | |
387 | | NS_IMETHODIMP |
388 | | nsBaseFilePicker::GetOkButtonLabel(nsAString& aLabel) |
389 | 0 | { |
390 | 0 | aLabel = mOkButtonLabel; |
391 | 0 | return NS_OK; |
392 | 0 | } |
393 | | |
394 | | NS_IMETHODIMP |
395 | | nsBaseFilePicker::GetDomFileOrDirectory(nsISupports** aValue) |
396 | 0 | { |
397 | 0 | nsCOMPtr<nsIFile> localFile; |
398 | 0 | nsresult rv = GetFile(getter_AddRefs(localFile)); |
399 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
400 | 0 |
|
401 | 0 | if (!localFile) { |
402 | 0 | *aValue = nullptr; |
403 | 0 | return NS_OK; |
404 | 0 | } |
405 | 0 | |
406 | 0 | auto* innerParent = mParent ? mParent->GetCurrentInnerWindow() : nullptr; |
407 | 0 |
|
408 | 0 | return LocalFileToDirectoryOrBlob(innerParent, |
409 | 0 | mMode == nsIFilePicker::modeGetFolder, |
410 | 0 | localFile, |
411 | 0 | aValue); |
412 | 0 | } |
413 | | |
414 | | NS_IMETHODIMP |
415 | | nsBaseFilePicker::GetDomFileOrDirectoryEnumerator(nsISimpleEnumerator** aValue) |
416 | 0 | { |
417 | 0 | nsCOMPtr<nsISimpleEnumerator> iter; |
418 | 0 | nsresult rv = GetFiles(getter_AddRefs(iter)); |
419 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
420 | 0 |
|
421 | 0 | RefPtr<nsBaseFilePickerEnumerator> retIter = |
422 | 0 | new nsBaseFilePickerEnumerator(mParent, iter, mMode); |
423 | 0 |
|
424 | 0 | retIter.forget(aValue); |
425 | 0 | return NS_OK; |
426 | 0 | } |