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