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 int16_t 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
, int16_t aMode
)
104 : mIterator(iterator
),
105 mParent(aParent
->GetCurrentInnerWindow()),
108 const nsID
& DefaultInterface() override
{ return NS_GET_IID(nsIFile
); }
111 GetNext(nsISupports
** aResult
) override
{
112 nsCOMPtr
<nsISupports
> tmp
;
113 nsresult rv
= mIterator
->GetNext(getter_AddRefs(tmp
));
114 NS_ENSURE_SUCCESS(rv
, rv
);
120 nsCOMPtr
<nsIFile
> localFile
= do_QueryInterface(tmp
);
122 return NS_ERROR_FAILURE
;
126 return NS_ERROR_FAILURE
;
129 return LocalFileToDirectoryOrBlob(
130 mParent
, mMode
== nsIFilePicker::modeGetFolder
, localFile
, aResult
);
134 HasMoreElements(bool* aResult
) override
{
135 return mIterator
->HasMoreElements(aResult
);
139 nsCOMPtr
<nsISimpleEnumerator
> mIterator
;
140 nsCOMPtr
<nsPIDOMWindowInner
> mParent
;
144 nsBaseFilePicker::nsBaseFilePicker()
145 : mAddToRecentDocs(true), mMode(nsIFilePicker::modeOpen
) {}
147 nsBaseFilePicker::~nsBaseFilePicker() = default;
149 NS_IMETHODIMP
nsBaseFilePicker::Init(mozIDOMWindowProxy
* aParent
,
150 const nsAString
& aTitle
, int16_t aMode
) {
152 "Null parent passed to filepicker, no file "
155 mParent
= nsPIDOMWindowOuter::From(aParent
);
157 nsCOMPtr
<nsIWidget
> widget
= WidgetUtils::DOMWindowToWidget(mParent
);
158 NS_ENSURE_TRUE(widget
, NS_ERROR_FAILURE
);
161 InitNative(widget
, aTitle
);
167 nsBaseFilePicker::Open(nsIFilePickerShownCallback
* aCallback
) {
168 nsCOMPtr
<nsIRunnable
> filePickerEvent
=
169 new AsyncShowFilePicker(this, aCallback
);
170 return NS_DispatchToMainThread(filePickerEvent
);
174 nsBaseFilePicker::AppendFilters(int32_t aFilterMask
) {
175 nsCOMPtr
<nsIStringBundleService
> stringService
=
176 mozilla::components::StringBundle::Service();
177 if (!stringService
) return NS_ERROR_FAILURE
;
179 nsCOMPtr
<nsIStringBundle
> titleBundle
, filterBundle
;
181 nsresult rv
= stringService
->CreateBundle(FILEPICKER_TITLES
,
182 getter_AddRefs(titleBundle
));
183 if (NS_FAILED(rv
)) return NS_ERROR_FAILURE
;
185 rv
= stringService
->CreateBundle(FILEPICKER_FILTERS
,
186 getter_AddRefs(filterBundle
));
187 if (NS_FAILED(rv
)) return NS_ERROR_FAILURE
;
192 if (aFilterMask
& filterAll
) {
193 titleBundle
->GetStringFromName("allTitle", title
);
194 filterBundle
->GetStringFromName("allFilter", filter
);
195 AppendFilter(title
, filter
);
197 if (aFilterMask
& filterHTML
) {
198 titleBundle
->GetStringFromName("htmlTitle", title
);
199 filterBundle
->GetStringFromName("htmlFilter", filter
);
200 AppendFilter(title
, filter
);
202 if (aFilterMask
& filterText
) {
203 titleBundle
->GetStringFromName("textTitle", title
);
204 filterBundle
->GetStringFromName("textFilter", filter
);
205 AppendFilter(title
, filter
);
207 if (aFilterMask
& filterImages
) {
208 titleBundle
->GetStringFromName("imageTitle", title
);
209 filterBundle
->GetStringFromName("imageFilter", filter
);
210 AppendFilter(title
, filter
);
212 if (aFilterMask
& filterAudio
) {
213 titleBundle
->GetStringFromName("audioTitle", title
);
214 filterBundle
->GetStringFromName("audioFilter", filter
);
215 AppendFilter(title
, filter
);
217 if (aFilterMask
& filterVideo
) {
218 titleBundle
->GetStringFromName("videoTitle", title
);
219 filterBundle
->GetStringFromName("videoFilter", filter
);
220 AppendFilter(title
, filter
);
222 if (aFilterMask
& filterXML
) {
223 titleBundle
->GetStringFromName("xmlTitle", title
);
224 filterBundle
->GetStringFromName("xmlFilter", filter
);
225 AppendFilter(title
, filter
);
227 if (aFilterMask
& filterXUL
) {
228 titleBundle
->GetStringFromName("xulTitle", title
);
229 filterBundle
->GetStringFromName("xulFilter", filter
);
230 AppendFilter(title
, filter
);
232 if (aFilterMask
& filterApps
) {
233 titleBundle
->GetStringFromName("appsTitle", title
);
234 // Pass the magic string "..apps" to the platform filepicker, which it
235 // should recognize and do the correct platform behavior for.
236 AppendFilter(title
, u
"..apps"_ns
);
241 NS_IMETHODIMP
nsBaseFilePicker::AppendRawFilter(const nsAString
& aFilter
) {
242 mRawFilters
.AppendElement(aFilter
);
246 NS_IMETHODIMP
nsBaseFilePicker::GetCapture(int16_t* aCapture
) {
251 NS_IMETHODIMP
nsBaseFilePicker::SetCapture(int16_t aCapture
) { return NS_OK
; }
253 // Set the filter index
254 NS_IMETHODIMP
nsBaseFilePicker::GetFilterIndex(int32_t* aFilterIndex
) {
259 NS_IMETHODIMP
nsBaseFilePicker::SetFilterIndex(int32_t aFilterIndex
) {
263 NS_IMETHODIMP
nsBaseFilePicker::GetFiles(nsISimpleEnumerator
** aFiles
) {
264 NS_ENSURE_ARG_POINTER(aFiles
);
265 nsCOMArray
<nsIFile
> files
;
268 // if we get into the base class, the platform
269 // doesn't implement GetFiles() yet.
271 nsCOMPtr
<nsIFile
> file
;
272 rv
= GetFile(getter_AddRefs(file
));
273 NS_ENSURE_SUCCESS(rv
, rv
);
275 files
.AppendObject(file
);
277 return NS_NewArrayEnumerator(aFiles
, files
, NS_GET_IID(nsIFile
));
280 // Set the display directory
281 NS_IMETHODIMP
nsBaseFilePicker::SetDisplayDirectory(nsIFile
* aDirectory
) {
282 // if displaySpecialDirectory has been previously called, let's abort this
284 if (!mDisplaySpecialDirectory
.IsEmpty()) {
289 mDisplayDirectory
= nullptr;
292 nsCOMPtr
<nsIFile
> directory
;
293 nsresult rv
= aDirectory
->Clone(getter_AddRefs(directory
));
294 if (NS_FAILED(rv
)) return rv
;
295 mDisplayDirectory
= directory
;
299 // Get the display directory
300 NS_IMETHODIMP
nsBaseFilePicker::GetDisplayDirectory(nsIFile
** aDirectory
) {
301 *aDirectory
= nullptr;
303 // if displaySpecialDirectory has been previously called, let's abort this
305 if (!mDisplaySpecialDirectory
.IsEmpty()) {
309 if (!mDisplayDirectory
) return NS_OK
;
310 nsCOMPtr
<nsIFile
> directory
;
311 nsresult rv
= mDisplayDirectory
->Clone(getter_AddRefs(directory
));
315 directory
.forget(aDirectory
);
319 // Set the display special directory
320 NS_IMETHODIMP
nsBaseFilePicker::SetDisplaySpecialDirectory(
321 const nsAString
& aDirectory
) {
322 // if displayDirectory has been previously called, let's abort this operation.
323 if (mDisplayDirectory
&& mDisplaySpecialDirectory
.IsEmpty()) {
327 mDisplaySpecialDirectory
= aDirectory
;
328 if (mDisplaySpecialDirectory
.IsEmpty()) {
329 mDisplayDirectory
= nullptr;
333 return NS_GetSpecialDirectory(
334 NS_ConvertUTF16toUTF8(mDisplaySpecialDirectory
).get(),
335 getter_AddRefs(mDisplayDirectory
));
338 // Get the display special directory
339 NS_IMETHODIMP
nsBaseFilePicker::GetDisplaySpecialDirectory(
340 nsAString
& aDirectory
) {
341 aDirectory
= mDisplaySpecialDirectory
;
346 nsBaseFilePicker::GetAddToRecentDocs(bool* aFlag
) {
347 *aFlag
= mAddToRecentDocs
;
352 nsBaseFilePicker::SetAddToRecentDocs(bool aFlag
) {
353 mAddToRecentDocs
= aFlag
;
358 nsBaseFilePicker::GetMode(int16_t* aMode
) {
364 nsBaseFilePicker::SetOkButtonLabel(const nsAString
& aLabel
) {
365 mOkButtonLabel
= aLabel
;
370 nsBaseFilePicker::GetOkButtonLabel(nsAString
& aLabel
) {
371 aLabel
= mOkButtonLabel
;
376 nsBaseFilePicker::GetDomFileOrDirectory(nsISupports
** aValue
) {
377 nsCOMPtr
<nsIFile
> localFile
;
378 nsresult rv
= GetFile(getter_AddRefs(localFile
));
379 NS_ENSURE_SUCCESS(rv
, rv
);
386 auto* innerParent
= mParent
? mParent
->GetCurrentInnerWindow() : nullptr;
389 return NS_ERROR_FAILURE
;
392 return LocalFileToDirectoryOrBlob(
393 innerParent
, mMode
== nsIFilePicker::modeGetFolder
, localFile
, aValue
);
397 nsBaseFilePicker::GetDomFileOrDirectoryEnumerator(
398 nsISimpleEnumerator
** aValue
) {
399 nsCOMPtr
<nsISimpleEnumerator
> iter
;
400 nsresult rv
= GetFiles(getter_AddRefs(iter
));
401 NS_ENSURE_SUCCESS(rv
, rv
);
403 RefPtr
<nsBaseFilePickerEnumerator
> retIter
=
404 new nsBaseFilePickerEnumerator(mParent
, iter
, mMode
);
406 retIter
.forget(aValue
);