Bug 1477919 [wpt PR 12154] - url: DecodeURLEscapeSequences() should not apply UTF...
[gecko.git] / widget / nsBaseFilePicker.cpp
blob9cbdf39d3f8b1e283fa29ce4379c140ea0f92991
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/. */
7 #include "nsCOMPtr.h"
8 #include "nsPIDOMWindow.h"
9 #include "nsIDocShell.h"
10 #include "nsIInterfaceRequestorUtils.h"
11 #include "nsIBaseWindow.h"
12 #include "nsIWidget.h"
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 "nsThreadUtils.h"
26 #include "nsBaseFilePicker.h"
28 using namespace mozilla::widget;
29 using namespace mozilla::dom;
31 #define FILEPICKER_TITLES "chrome://global/locale/filepicker.properties"
32 #define FILEPICKER_FILTERS "chrome://global/content/filepicker.properties"
34 namespace {
36 nsresult
37 LocalFileToDirectoryOrBlob(nsPIDOMWindowInner* aWindow,
38 bool aIsDirectory,
39 nsIFile* aFile,
40 nsISupports** aResult)
42 if (aIsDirectory) {
43 #ifdef DEBUG
44 bool isDir;
45 aFile->IsDirectory(&isDir);
46 MOZ_ASSERT(isDir);
47 #endif
49 RefPtr<Directory> directory = Directory::Create(aWindow, aFile);
50 MOZ_ASSERT(directory);
52 directory.forget(aResult);
53 return NS_OK;
56 RefPtr<File> file = File::CreateFromFile(aWindow, aFile);
57 file.forget(aResult);
58 return NS_OK;
61 } // anonymous namespace
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.
67 class nsBaseFilePicker::AsyncShowFilePicker : public mozilla::Runnable
69 public:
70 AsyncShowFilePicker(nsBaseFilePicker* aFilePicker,
71 nsIFilePickerShownCallback* aCallback)
72 : mozilla::Runnable("AsyncShowFilePicker")
73 , mFilePicker(aFilePicker)
74 , mCallback(aCallback)
78 NS_IMETHOD Run() override
80 NS_ASSERTION(NS_IsMainThread(),
81 "AsyncShowFilePicker should be on the main thread!");
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 int16_t result = nsIFilePicker::returnCancel;
87 nsresult rv = mFilePicker->Show(&result);
88 if (NS_FAILED(rv)) {
89 NS_ERROR("FilePicker's Show() implementation failed!");
92 if (mCallback) {
93 mCallback->Done(result);
95 return NS_OK;
98 private:
99 RefPtr<nsBaseFilePicker> mFilePicker;
100 RefPtr<nsIFilePickerShownCallback> mCallback;
103 class nsBaseFilePickerEnumerator : public nsISimpleEnumerator
105 public:
106 NS_DECL_ISUPPORTS
108 nsBaseFilePickerEnumerator(nsPIDOMWindowOuter* aParent,
109 nsISimpleEnumerator* iterator,
110 int16_t aMode)
111 : mIterator(iterator)
112 , mParent(aParent->GetCurrentInnerWindow())
113 , mMode(aMode)
116 NS_IMETHOD
117 GetNext(nsISupports** aResult) override
119 nsCOMPtr<nsISupports> tmp;
120 nsresult rv = mIterator->GetNext(getter_AddRefs(tmp));
121 NS_ENSURE_SUCCESS(rv, rv);
123 if (!tmp) {
124 return NS_OK;
127 nsCOMPtr<nsIFile> localFile = do_QueryInterface(tmp);
128 if (!localFile) {
129 return NS_ERROR_FAILURE;
132 return LocalFileToDirectoryOrBlob(mParent,
133 mMode == nsIFilePicker::modeGetFolder,
134 localFile,
135 aResult);
138 NS_IMETHOD
139 HasMoreElements(bool* aResult) override
141 return mIterator->HasMoreElements(aResult);
144 protected:
145 virtual ~nsBaseFilePickerEnumerator()
148 private:
149 nsCOMPtr<nsISimpleEnumerator> mIterator;
150 nsCOMPtr<nsPIDOMWindowInner> mParent;
151 int16_t mMode;
154 NS_IMPL_ISUPPORTS(nsBaseFilePickerEnumerator, nsISimpleEnumerator)
156 nsBaseFilePicker::nsBaseFilePicker()
157 : mAddToRecentDocs(true)
158 , mMode(nsIFilePicker::modeOpen)
163 nsBaseFilePicker::~nsBaseFilePicker()
168 NS_IMETHODIMP nsBaseFilePicker::Init(mozIDOMWindowProxy* aParent,
169 const nsAString& aTitle,
170 int16_t aMode)
172 MOZ_ASSERT(aParent, "Null parent passed to filepicker, no file "
173 "picker for you!");
175 mParent = nsPIDOMWindowOuter::From(aParent);
177 nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(mParent->GetOuterWindow());
178 NS_ENSURE_TRUE(widget, NS_ERROR_FAILURE);
181 mMode = aMode;
182 InitNative(widget, aTitle);
184 return NS_OK;
187 NS_IMETHODIMP
188 nsBaseFilePicker::Open(nsIFilePickerShownCallback *aCallback)
190 nsCOMPtr<nsIRunnable> filePickerEvent =
191 new AsyncShowFilePicker(this, aCallback);
192 return NS_DispatchToMainThread(filePickerEvent);
195 NS_IMETHODIMP
196 nsBaseFilePicker::AppendFilters(int32_t aFilterMask)
198 nsCOMPtr<nsIStringBundleService> stringService =
199 mozilla::services::GetStringBundleService();
200 if (!stringService)
201 return NS_ERROR_FAILURE;
203 nsCOMPtr<nsIStringBundle> titleBundle, filterBundle;
205 nsresult rv = stringService->CreateBundle(FILEPICKER_TITLES,
206 getter_AddRefs(titleBundle));
207 if (NS_FAILED(rv))
208 return NS_ERROR_FAILURE;
210 rv = stringService->CreateBundle(FILEPICKER_FILTERS, getter_AddRefs(filterBundle));
211 if (NS_FAILED(rv))
212 return NS_ERROR_FAILURE;
214 nsAutoString title;
215 nsAutoString filter;
217 if (aFilterMask & filterAll) {
218 titleBundle->GetStringFromName("allTitle", title);
219 filterBundle->GetStringFromName("allFilter", filter);
220 AppendFilter(title,filter);
222 if (aFilterMask & filterHTML) {
223 titleBundle->GetStringFromName("htmlTitle", title);
224 filterBundle->GetStringFromName("htmlFilter", filter);
225 AppendFilter(title,filter);
227 if (aFilterMask & filterText) {
228 titleBundle->GetStringFromName("textTitle", title);
229 filterBundle->GetStringFromName("textFilter", filter);
230 AppendFilter(title,filter);
232 if (aFilterMask & filterImages) {
233 titleBundle->GetStringFromName("imageTitle", title);
234 filterBundle->GetStringFromName("imageFilter", filter);
235 AppendFilter(title,filter);
237 if (aFilterMask & filterAudio) {
238 titleBundle->GetStringFromName("audioTitle", title);
239 filterBundle->GetStringFromName("audioFilter", filter);
240 AppendFilter(title,filter);
242 if (aFilterMask & filterVideo) {
243 titleBundle->GetStringFromName("videoTitle", title);
244 filterBundle->GetStringFromName("videoFilter", filter);
245 AppendFilter(title,filter);
247 if (aFilterMask & filterXML) {
248 titleBundle->GetStringFromName("xmlTitle", title);
249 filterBundle->GetStringFromName("xmlFilter", filter);
250 AppendFilter(title,filter);
252 if (aFilterMask & filterXUL) {
253 titleBundle->GetStringFromName("xulTitle", title);
254 filterBundle->GetStringFromName("xulFilter", filter);
255 AppendFilter(title, filter);
257 if (aFilterMask & filterApps) {
258 titleBundle->GetStringFromName("appsTitle", title);
259 // Pass the magic string "..apps" to the platform filepicker, which it
260 // should recognize and do the correct platform behavior for.
261 AppendFilter(title, NS_LITERAL_STRING("..apps"));
263 return NS_OK;
266 // Set the filter index
267 NS_IMETHODIMP nsBaseFilePicker::GetFilterIndex(int32_t *aFilterIndex)
269 *aFilterIndex = 0;
270 return NS_OK;
273 NS_IMETHODIMP nsBaseFilePicker::SetFilterIndex(int32_t aFilterIndex)
275 return NS_OK;
278 NS_IMETHODIMP nsBaseFilePicker::GetFiles(nsISimpleEnumerator **aFiles)
280 NS_ENSURE_ARG_POINTER(aFiles);
281 nsCOMArray <nsIFile> files;
282 nsresult rv;
284 // if we get into the base class, the platform
285 // doesn't implement GetFiles() yet.
286 // so we fake it.
287 nsCOMPtr <nsIFile> file;
288 rv = GetFile(getter_AddRefs(file));
289 NS_ENSURE_SUCCESS(rv,rv);
291 files.AppendObject(file);
293 return NS_NewArrayEnumerator(aFiles, files);
296 // Set the display directory
297 NS_IMETHODIMP nsBaseFilePicker::SetDisplayDirectory(nsIFile *aDirectory)
299 // if displaySpecialDirectory has been previously called, let's abort this
300 // operation.
301 if (!mDisplaySpecialDirectory.IsEmpty()) {
302 return NS_OK;
305 if (!aDirectory) {
306 mDisplayDirectory = nullptr;
307 return NS_OK;
309 nsCOMPtr<nsIFile> directory;
310 nsresult rv = aDirectory->Clone(getter_AddRefs(directory));
311 if (NS_FAILED(rv))
312 return rv;
313 mDisplayDirectory = do_QueryInterface(directory, &rv);
314 return rv;
317 // Get the display directory
318 NS_IMETHODIMP nsBaseFilePicker::GetDisplayDirectory(nsIFile **aDirectory)
320 *aDirectory = nullptr;
322 // if displaySpecialDirectory has been previously called, let's abort this
323 // operation.
324 if (!mDisplaySpecialDirectory.IsEmpty()) {
325 return NS_OK;
328 if (!mDisplayDirectory)
329 return NS_OK;
330 nsCOMPtr<nsIFile> directory;
331 nsresult rv = mDisplayDirectory->Clone(getter_AddRefs(directory));
332 if (NS_FAILED(rv)) {
333 return rv;
335 directory.forget(aDirectory);
336 return NS_OK;
339 // Set the display special directory
340 NS_IMETHODIMP nsBaseFilePicker::SetDisplaySpecialDirectory(const nsAString& aDirectory)
342 // if displayDirectory has been previously called, let's abort this operation.
343 if (mDisplayDirectory && mDisplaySpecialDirectory.IsEmpty()) {
344 return NS_OK;
347 mDisplaySpecialDirectory = aDirectory;
348 if (mDisplaySpecialDirectory.IsEmpty()) {
349 mDisplayDirectory = nullptr;
350 return NS_OK;
353 return NS_GetSpecialDirectory(NS_ConvertUTF16toUTF8(mDisplaySpecialDirectory).get(),
354 getter_AddRefs(mDisplayDirectory));
357 // Get the display special directory
358 NS_IMETHODIMP nsBaseFilePicker::GetDisplaySpecialDirectory(nsAString& aDirectory)
360 aDirectory = mDisplaySpecialDirectory;
361 return NS_OK;
364 NS_IMETHODIMP
365 nsBaseFilePicker::GetAddToRecentDocs(bool *aFlag)
367 *aFlag = mAddToRecentDocs;
368 return NS_OK;
371 NS_IMETHODIMP
372 nsBaseFilePicker::SetAddToRecentDocs(bool aFlag)
374 mAddToRecentDocs = aFlag;
375 return NS_OK;
378 NS_IMETHODIMP
379 nsBaseFilePicker::GetMode(int16_t* aMode)
381 *aMode = mMode;
382 return NS_OK;
385 NS_IMETHODIMP
386 nsBaseFilePicker::SetOkButtonLabel(const nsAString& aLabel)
388 mOkButtonLabel = aLabel;
389 return NS_OK;
392 NS_IMETHODIMP
393 nsBaseFilePicker::GetOkButtonLabel(nsAString& aLabel)
395 aLabel = mOkButtonLabel;
396 return NS_OK;
399 NS_IMETHODIMP
400 nsBaseFilePicker::GetDomFileOrDirectory(nsISupports** aValue)
402 nsCOMPtr<nsIFile> localFile;
403 nsresult rv = GetFile(getter_AddRefs(localFile));
404 NS_ENSURE_SUCCESS(rv, rv);
406 if (!localFile) {
407 *aValue = nullptr;
408 return NS_OK;
411 auto* innerParent = mParent ? mParent->GetCurrentInnerWindow() : nullptr;
413 return LocalFileToDirectoryOrBlob(innerParent,
414 mMode == nsIFilePicker::modeGetFolder,
415 localFile,
416 aValue);
419 NS_IMETHODIMP
420 nsBaseFilePicker::GetDomFileOrDirectoryEnumerator(nsISimpleEnumerator** aValue)
422 nsCOMPtr<nsISimpleEnumerator> iter;
423 nsresult rv = GetFiles(getter_AddRefs(iter));
424 NS_ENSURE_SUCCESS(rv, rv);
426 RefPtr<nsBaseFilePickerEnumerator> retIter =
427 new nsBaseFilePickerEnumerator(mParent, iter, mMode);
429 retIter.forget(aValue);
430 return NS_OK;