Source code

Revision control

Other Tools

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