1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
8 #include "nsPIDOMWindow.h"
9 #include "nsIInterfaceRequestorUtils.h"
10 #include "nsIWidget.h"
12 #include "nsIStringBundle.h"
14 #include "nsCOMArray.h"
16 #include "nsEnumeratorUtils.h"
17 #include "mozilla/dom/Directory.h"
18 #include "mozilla/dom/File.h"
19 #include "mozilla/Components.h"
20 #include "WidgetUtils.h"
21 #include "nsSimpleEnumerator.h"
22 #include "nsThreadUtils.h"
24 #include "nsBaseFilePicker.h"
26 using namespace mozilla::widget
;
27 using namespace mozilla::dom
;
29 #define FILEPICKER_TITLES "chrome://global/locale/filepicker.properties"
30 #define FILEPICKER_FILTERS "chrome://global/content/filepicker.properties"
34 nsresult
LocalFileToDirectoryOrBlob(nsPIDOMWindowInner
* aWindow
,
35 bool aIsDirectory
, nsIFile
* aFile
,
36 nsISupports
** aResult
) {
42 aFile
->IsDirectory(&isDir
);
46 RefPtr
<Directory
> directory
= Directory::Create(aWindow
->AsGlobal(), aFile
);
47 MOZ_ASSERT(directory
);
49 directory
.forget(aResult
);
53 RefPtr
<File
> file
= File::CreateFromFile(aWindow
->AsGlobal(), aFile
);
54 if (NS_WARN_IF(!file
)) {
55 return NS_ERROR_FAILURE
;
62 } // anonymous namespace
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.
68 class nsBaseFilePicker::AsyncShowFilePicker
: public mozilla::Runnable
{
70 AsyncShowFilePicker(nsBaseFilePicker
* aFilePicker
,
71 nsIFilePickerShownCallback
* aCallback
)
72 : mozilla::Runnable("AsyncShowFilePicker"),
73 mFilePicker(aFilePicker
),
74 mCallback(aCallback
) {}
76 NS_IMETHOD
Run() override
{
77 NS_ASSERTION(NS_IsMainThread(),
78 "AsyncShowFilePicker should be on the main thread!");
80 // It's possible that some widget implementations require GUI operations
81 // to be on the main thread, so that's why we're not dispatching to another
82 // thread and calling back to the main after it's done.
83 nsIFilePicker::ResultCode result
= nsIFilePicker::returnCancel
;
84 nsresult rv
= mFilePicker
->Show(&result
);
86 NS_ERROR("FilePicker's Show() implementation failed!");
90 mCallback
->Done(result
);
96 RefPtr
<nsBaseFilePicker
> mFilePicker
;
97 RefPtr
<nsIFilePickerShownCallback
> mCallback
;
100 class nsBaseFilePickerEnumerator
: public nsSimpleEnumerator
{
102 nsBaseFilePickerEnumerator(nsPIDOMWindowOuter
* aParent
,
103 nsISimpleEnumerator
* iterator
,
104 nsIFilePicker::Mode aMode
)
105 : mIterator(iterator
),
106 mParent(aParent
->GetCurrentInnerWindow()),
109 const nsID
& DefaultInterface() override
{ return NS_GET_IID(nsIFile
); }
112 GetNext(nsISupports
** aResult
) override
{
113 nsCOMPtr
<nsISupports
> tmp
;
114 nsresult rv
= mIterator
->GetNext(getter_AddRefs(tmp
));
115 NS_ENSURE_SUCCESS(rv
, rv
);
121 nsCOMPtr
<nsIFile
> localFile
= do_QueryInterface(tmp
);
123 return NS_ERROR_FAILURE
;
127 return NS_ERROR_FAILURE
;
130 return LocalFileToDirectoryOrBlob(
131 mParent
, mMode
== nsIFilePicker::modeGetFolder
, localFile
, aResult
);
135 HasMoreElements(bool* aResult
) override
{
136 return mIterator
->HasMoreElements(aResult
);
140 nsCOMPtr
<nsISimpleEnumerator
> mIterator
;
141 nsCOMPtr
<nsPIDOMWindowInner
> mParent
;
142 nsIFilePicker::Mode mMode
;
145 nsBaseFilePicker::nsBaseFilePicker()
146 : mAddToRecentDocs(true), mMode(nsIFilePicker::modeOpen
) {}
148 nsBaseFilePicker::~nsBaseFilePicker() = default;
150 NS_IMETHODIMP
nsBaseFilePicker::Init(mozIDOMWindowProxy
* aParent
,
151 const nsAString
& aTitle
,
152 nsIFilePicker::Mode aMode
) {
154 "Null parent passed to filepicker, no file "
157 mParent
= nsPIDOMWindowOuter::From(aParent
);
159 nsCOMPtr
<nsIWidget
> widget
= WidgetUtils::DOMWindowToWidget(mParent
);
160 NS_ENSURE_TRUE(widget
, NS_ERROR_FAILURE
);
163 InitNative(widget
, aTitle
);
169 nsBaseFilePicker::Open(nsIFilePickerShownCallback
* aCallback
) {
170 nsCOMPtr
<nsIRunnable
> filePickerEvent
=
171 new AsyncShowFilePicker(this, aCallback
);
172 return NS_DispatchToMainThread(filePickerEvent
);
176 nsBaseFilePicker::AppendFilters(int32_t aFilterMask
) {
177 nsCOMPtr
<nsIStringBundleService
> stringService
=
178 mozilla::components::StringBundle::Service();
179 if (!stringService
) return NS_ERROR_FAILURE
;
181 nsCOMPtr
<nsIStringBundle
> titleBundle
, filterBundle
;
183 nsresult rv
= stringService
->CreateBundle(FILEPICKER_TITLES
,
184 getter_AddRefs(titleBundle
));
185 if (NS_FAILED(rv
)) return NS_ERROR_FAILURE
;
187 rv
= stringService
->CreateBundle(FILEPICKER_FILTERS
,
188 getter_AddRefs(filterBundle
));
189 if (NS_FAILED(rv
)) return NS_ERROR_FAILURE
;
194 if (aFilterMask
& filterAll
) {
195 titleBundle
->GetStringFromName("allTitle", title
);
196 filterBundle
->GetStringFromName("allFilter", filter
);
197 AppendFilter(title
, filter
);
199 if (aFilterMask
& filterHTML
) {
200 titleBundle
->GetStringFromName("htmlTitle", title
);
201 filterBundle
->GetStringFromName("htmlFilter", filter
);
202 AppendFilter(title
, filter
);
204 if (aFilterMask
& filterText
) {
205 titleBundle
->GetStringFromName("textTitle", title
);
206 filterBundle
->GetStringFromName("textFilter", filter
);
207 AppendFilter(title
, filter
);
209 if (aFilterMask
& filterImages
) {
210 titleBundle
->GetStringFromName("imageTitle", title
);
211 filterBundle
->GetStringFromName("imageFilter", filter
);
212 AppendFilter(title
, filter
);
214 if (aFilterMask
& filterAudio
) {
215 titleBundle
->GetStringFromName("audioTitle", title
);
216 filterBundle
->GetStringFromName("audioFilter", filter
);
217 AppendFilter(title
, filter
);
219 if (aFilterMask
& filterVideo
) {
220 titleBundle
->GetStringFromName("videoTitle", title
);
221 filterBundle
->GetStringFromName("videoFilter", filter
);
222 AppendFilter(title
, filter
);
224 if (aFilterMask
& filterXML
) {
225 titleBundle
->GetStringFromName("xmlTitle", title
);
226 filterBundle
->GetStringFromName("xmlFilter", filter
);
227 AppendFilter(title
, filter
);
229 if (aFilterMask
& filterXUL
) {
230 titleBundle
->GetStringFromName("xulTitle", title
);
231 filterBundle
->GetStringFromName("xulFilter", filter
);
232 AppendFilter(title
, filter
);
234 if (aFilterMask
& filterApps
) {
235 titleBundle
->GetStringFromName("appsTitle", title
);
236 // Pass the magic string "..apps" to the platform filepicker, which it
237 // should recognize and do the correct platform behavior for.
238 AppendFilter(title
, u
"..apps"_ns
);
243 NS_IMETHODIMP
nsBaseFilePicker::AppendRawFilter(const nsAString
& aFilter
) {
244 mRawFilters
.AppendElement(aFilter
);
248 NS_IMETHODIMP
nsBaseFilePicker::GetCapture(
249 nsIFilePicker::CaptureTarget
* aCapture
) {
250 *aCapture
= nsIFilePicker::CaptureTarget::captureNone
;
254 NS_IMETHODIMP
nsBaseFilePicker::SetCapture(
255 nsIFilePicker::CaptureTarget aCapture
) {
259 // Set the filter index
260 NS_IMETHODIMP
nsBaseFilePicker::GetFilterIndex(int32_t* aFilterIndex
) {
265 NS_IMETHODIMP
nsBaseFilePicker::SetFilterIndex(int32_t aFilterIndex
) {
269 NS_IMETHODIMP
nsBaseFilePicker::GetFiles(nsISimpleEnumerator
** aFiles
) {
270 NS_ENSURE_ARG_POINTER(aFiles
);
271 nsCOMArray
<nsIFile
> files
;
274 // if we get into the base class, the platform
275 // doesn't implement GetFiles() yet.
277 nsCOMPtr
<nsIFile
> file
;
278 rv
= GetFile(getter_AddRefs(file
));
279 NS_ENSURE_SUCCESS(rv
, rv
);
281 files
.AppendObject(file
);
283 return NS_NewArrayEnumerator(aFiles
, files
, NS_GET_IID(nsIFile
));
286 // Set the display directory
287 NS_IMETHODIMP
nsBaseFilePicker::SetDisplayDirectory(nsIFile
* aDirectory
) {
288 // if displaySpecialDirectory has been previously called, let's abort this
290 if (!mDisplaySpecialDirectory
.IsEmpty()) {
295 mDisplayDirectory
= nullptr;
298 nsCOMPtr
<nsIFile
> directory
;
299 nsresult rv
= aDirectory
->Clone(getter_AddRefs(directory
));
300 if (NS_FAILED(rv
)) return rv
;
301 mDisplayDirectory
= directory
;
305 // Get the display directory
306 NS_IMETHODIMP
nsBaseFilePicker::GetDisplayDirectory(nsIFile
** aDirectory
) {
307 *aDirectory
= nullptr;
309 // if displaySpecialDirectory has been previously called, let's abort this
311 if (!mDisplaySpecialDirectory
.IsEmpty()) {
315 if (!mDisplayDirectory
) return NS_OK
;
316 nsCOMPtr
<nsIFile
> directory
;
317 nsresult rv
= mDisplayDirectory
->Clone(getter_AddRefs(directory
));
321 directory
.forget(aDirectory
);
325 // Set the display special directory
326 NS_IMETHODIMP
nsBaseFilePicker::SetDisplaySpecialDirectory(
327 const nsAString
& aDirectory
) {
328 // if displayDirectory has been previously called, let's abort this operation.
329 if (mDisplayDirectory
&& mDisplaySpecialDirectory
.IsEmpty()) {
333 mDisplaySpecialDirectory
= aDirectory
;
334 if (mDisplaySpecialDirectory
.IsEmpty()) {
335 mDisplayDirectory
= nullptr;
339 return NS_GetSpecialDirectory(
340 NS_ConvertUTF16toUTF8(mDisplaySpecialDirectory
).get(),
341 getter_AddRefs(mDisplayDirectory
));
344 // Get the display special directory
345 NS_IMETHODIMP
nsBaseFilePicker::GetDisplaySpecialDirectory(
346 nsAString
& aDirectory
) {
347 aDirectory
= mDisplaySpecialDirectory
;
352 nsBaseFilePicker::GetAddToRecentDocs(bool* aFlag
) {
353 *aFlag
= mAddToRecentDocs
;
358 nsBaseFilePicker::SetAddToRecentDocs(bool aFlag
) {
359 mAddToRecentDocs
= aFlag
;
364 nsBaseFilePicker::GetMode(nsIFilePicker::Mode
* aMode
) {
370 nsBaseFilePicker::SetOkButtonLabel(const nsAString
& aLabel
) {
371 mOkButtonLabel
= aLabel
;
376 nsBaseFilePicker::GetOkButtonLabel(nsAString
& aLabel
) {
377 aLabel
= mOkButtonLabel
;
382 nsBaseFilePicker::GetDomFileOrDirectory(nsISupports
** aValue
) {
383 nsCOMPtr
<nsIFile
> localFile
;
384 nsresult rv
= GetFile(getter_AddRefs(localFile
));
385 NS_ENSURE_SUCCESS(rv
, rv
);
392 auto* innerParent
= mParent
? mParent
->GetCurrentInnerWindow() : nullptr;
395 return NS_ERROR_FAILURE
;
398 return LocalFileToDirectoryOrBlob(
399 innerParent
, mMode
== nsIFilePicker::modeGetFolder
, localFile
, aValue
);
403 nsBaseFilePicker::GetDomFileOrDirectoryEnumerator(
404 nsISimpleEnumerator
** aValue
) {
405 nsCOMPtr
<nsISimpleEnumerator
> iter
;
406 nsresult rv
= GetFiles(getter_AddRefs(iter
));
407 NS_ENSURE_SUCCESS(rv
, rv
);
409 RefPtr
<nsBaseFilePickerEnumerator
> retIter
=
410 new nsBaseFilePickerEnumerator(mParent
, iter
, mMode
);
412 retIter
.forget(aValue
);