Bug 1842773 - Part 34: Enable test262 tests. r=sfink
[gecko.git] / widget / nsBaseFilePicker.cpp
blobc216f3d55caf999ea709b5bd1369768250db4854
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 "nsIInterfaceRequestorUtils.h"
10 #include "nsIWidget.h"
12 #include "nsIStringBundle.h"
13 #include "nsString.h"
14 #include "nsCOMArray.h"
15 #include "nsIFile.h"
16 #include "nsEnumeratorUtils.h"
17 #include "mozilla/dom/Directory.h"
18 #include "mozilla/dom/File.h"
19 #include "mozilla/dom/Promise.h"
20 #include "mozilla/Components.h"
21 #include "WidgetUtils.h"
22 #include "nsSimpleEnumerator.h"
23 #include "nsThreadUtils.h"
25 #include "nsBaseFilePicker.h"
27 using namespace mozilla::widget;
28 using namespace mozilla::dom;
29 using mozilla::ErrorResult;
31 #define FILEPICKER_TITLES "chrome://global/locale/filepicker.properties"
32 #define FILEPICKER_FILTERS "chrome://global/content/filepicker.properties"
34 namespace {
36 nsresult LocalFileToDirectoryOrBlob(nsPIDOMWindowInner* aWindow,
37 bool aIsDirectory, nsIFile* aFile,
38 nsISupports** aResult) {
39 MOZ_ASSERT(aWindow);
41 if (aIsDirectory) {
42 #ifdef DEBUG
43 bool isDir;
44 aFile->IsDirectory(&isDir);
45 MOZ_ASSERT(isDir);
46 #endif
48 RefPtr<Directory> directory = Directory::Create(aWindow->AsGlobal(), aFile);
49 MOZ_ASSERT(directory);
51 directory.forget(aResult);
52 return NS_OK;
55 RefPtr<File> file = File::CreateFromFile(aWindow->AsGlobal(), aFile);
56 if (NS_WARN_IF(!file)) {
57 return NS_ERROR_FAILURE;
60 file.forget(aResult);
61 return NS_OK;
64 } // anonymous namespace
66 #ifndef XP_WIN
67 /**
68 * A runnable to dispatch from the main thread to the main thread to display
69 * the file picker while letting the showAsync method return right away.
71 * Not needed on Windows, where nsFilePicker::Open() is fully async.
73 class nsBaseFilePicker::AsyncShowFilePicker : public mozilla::Runnable {
74 public:
75 AsyncShowFilePicker(nsBaseFilePicker* aFilePicker,
76 nsIFilePickerShownCallback* aCallback)
77 : mozilla::Runnable("AsyncShowFilePicker"),
78 mFilePicker(aFilePicker),
79 mCallback(aCallback) {}
81 NS_IMETHOD Run() override {
82 NS_ASSERTION(NS_IsMainThread(),
83 "AsyncShowFilePicker should be on the main thread!");
85 // It's possible that some widget implementations require GUI operations
86 // to be on the main thread, so that's why we're not dispatching to another
87 // thread and calling back to the main after it's done.
88 nsIFilePicker::ResultCode result = nsIFilePicker::returnCancel;
89 nsresult rv = mFilePicker->Show(&result);
90 if (NS_FAILED(rv)) {
91 NS_ERROR("FilePicker's Show() implementation failed!");
94 if (mCallback) {
95 mCallback->Done(result);
97 return NS_OK;
100 private:
101 RefPtr<nsBaseFilePicker> mFilePicker;
102 RefPtr<nsIFilePickerShownCallback> mCallback;
104 #endif
106 class nsBaseFilePickerEnumerator : public nsSimpleEnumerator {
107 public:
108 nsBaseFilePickerEnumerator(nsPIDOMWindowOuter* aParent,
109 nsISimpleEnumerator* iterator,
110 nsIFilePicker::Mode aMode)
111 : mIterator(iterator),
112 mParent(aParent->GetCurrentInnerWindow()),
113 mMode(aMode) {}
115 const nsID& DefaultInterface() override { return NS_GET_IID(nsIFile); }
117 NS_IMETHOD
118 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 if (!mParent) {
133 return NS_ERROR_FAILURE;
136 return LocalFileToDirectoryOrBlob(
137 mParent, mMode == nsIFilePicker::modeGetFolder, localFile, aResult);
140 NS_IMETHOD
141 HasMoreElements(bool* aResult) override {
142 return mIterator->HasMoreElements(aResult);
145 private:
146 nsCOMPtr<nsISimpleEnumerator> mIterator;
147 nsCOMPtr<nsPIDOMWindowInner> mParent;
148 nsIFilePicker::Mode mMode;
151 nsBaseFilePicker::nsBaseFilePicker()
152 : mAddToRecentDocs(true), mMode(nsIFilePicker::modeOpen) {}
154 nsBaseFilePicker::~nsBaseFilePicker() = default;
156 NS_IMETHODIMP nsBaseFilePicker::Init(
157 mozIDOMWindowProxy* aParent, const nsAString& aTitle,
158 nsIFilePicker::Mode aMode,
159 mozilla::dom::BrowsingContext* aBrowsingContext) {
160 MOZ_ASSERT(aParent,
161 "Null parent passed to filepicker, no file "
162 "picker for you!");
164 mParent = nsPIDOMWindowOuter::From(aParent);
166 nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(mParent);
167 NS_ENSURE_TRUE(widget, NS_ERROR_FAILURE);
169 mBrowsingContext = aBrowsingContext;
170 mMode = aMode;
171 InitNative(widget, aTitle);
173 return NS_OK;
176 NS_IMETHODIMP
177 nsBaseFilePicker::IsModeSupported(nsIFilePicker::Mode aMode, JSContext* aCx,
178 Promise** aPromise) {
179 MOZ_ASSERT(aCx);
180 MOZ_ASSERT(aPromise);
182 nsIGlobalObject* globalObject = xpc::CurrentNativeGlobal(aCx);
183 if (NS_WARN_IF(!globalObject)) {
184 return NS_ERROR_FAILURE;
187 ErrorResult result;
188 RefPtr<Promise> promise = Promise::Create(globalObject, result);
189 if (NS_WARN_IF(result.Failed())) {
190 return result.StealNSResult();
193 promise->MaybeResolve(true);
194 promise.forget(aPromise);
196 return NS_OK;
199 #ifndef XP_WIN
200 NS_IMETHODIMP
201 nsBaseFilePicker::Open(nsIFilePickerShownCallback* aCallback) {
202 nsCOMPtr<nsIRunnable> filePickerEvent =
203 new AsyncShowFilePicker(this, aCallback);
204 return NS_DispatchToMainThread(filePickerEvent);
206 #endif
208 NS_IMETHODIMP
209 nsBaseFilePicker::Close() {
210 NS_WARNING("Unimplemented nsFilePicker::Close");
211 return NS_OK;
214 NS_IMETHODIMP
215 nsBaseFilePicker::AppendFilters(int32_t aFilterMask) {
216 nsCOMPtr<nsIStringBundleService> stringService =
217 mozilla::components::StringBundle::Service();
218 if (!stringService) return NS_ERROR_FAILURE;
220 nsCOMPtr<nsIStringBundle> titleBundle, filterBundle;
222 nsresult rv = stringService->CreateBundle(FILEPICKER_TITLES,
223 getter_AddRefs(titleBundle));
224 if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
226 rv = stringService->CreateBundle(FILEPICKER_FILTERS,
227 getter_AddRefs(filterBundle));
228 if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
230 nsAutoString title;
231 nsAutoString filter;
233 if (aFilterMask & filterAll) {
234 titleBundle->GetStringFromName("allTitle", title);
235 filterBundle->GetStringFromName("allFilter", filter);
236 AppendFilter(title, filter);
238 if (aFilterMask & filterHTML) {
239 titleBundle->GetStringFromName("htmlTitle", title);
240 filterBundle->GetStringFromName("htmlFilter", filter);
241 AppendFilter(title, filter);
243 if (aFilterMask & filterText) {
244 titleBundle->GetStringFromName("textTitle", title);
245 filterBundle->GetStringFromName("textFilter", filter);
246 AppendFilter(title, filter);
248 if (aFilterMask & filterImages) {
249 titleBundle->GetStringFromName("imageTitle", title);
250 filterBundle->GetStringFromName("imageFilter", filter);
251 AppendFilter(title, filter);
253 if (aFilterMask & filterAudio) {
254 titleBundle->GetStringFromName("audioTitle", title);
255 filterBundle->GetStringFromName("audioFilter", filter);
256 AppendFilter(title, filter);
258 if (aFilterMask & filterVideo) {
259 titleBundle->GetStringFromName("videoTitle", title);
260 filterBundle->GetStringFromName("videoFilter", filter);
261 AppendFilter(title, filter);
263 if (aFilterMask & filterXML) {
264 titleBundle->GetStringFromName("xmlTitle", title);
265 filterBundle->GetStringFromName("xmlFilter", filter);
266 AppendFilter(title, filter);
268 if (aFilterMask & filterXUL) {
269 titleBundle->GetStringFromName("xulTitle", title);
270 filterBundle->GetStringFromName("xulFilter", filter);
271 AppendFilter(title, filter);
273 if (aFilterMask & filterApps) {
274 titleBundle->GetStringFromName("appsTitle", title);
275 // Pass the magic string "..apps" to the platform filepicker, which it
276 // should recognize and do the correct platform behavior for.
277 AppendFilter(title, u"..apps"_ns);
279 if (aFilterMask & filterPDF) {
280 titleBundle->GetStringFromName("pdfTitle", title);
281 filterBundle->GetStringFromName("pdfFilter", filter);
282 AppendFilter(title, filter);
284 return NS_OK;
287 NS_IMETHODIMP nsBaseFilePicker::AppendRawFilter(const nsAString& aFilter) {
288 mRawFilters.AppendElement(aFilter);
289 return NS_OK;
292 NS_IMETHODIMP nsBaseFilePicker::GetCapture(
293 nsIFilePicker::CaptureTarget* aCapture) {
294 *aCapture = nsIFilePicker::CaptureTarget::captureNone;
295 return NS_OK;
298 NS_IMETHODIMP nsBaseFilePicker::SetCapture(
299 nsIFilePicker::CaptureTarget aCapture) {
300 return NS_OK;
303 // Set the filter index
304 NS_IMETHODIMP nsBaseFilePicker::GetFilterIndex(int32_t* aFilterIndex) {
305 *aFilterIndex = 0;
306 return NS_OK;
309 NS_IMETHODIMP nsBaseFilePicker::SetFilterIndex(int32_t aFilterIndex) {
310 return NS_OK;
313 NS_IMETHODIMP nsBaseFilePicker::GetFiles(nsISimpleEnumerator** aFiles) {
314 NS_ENSURE_ARG_POINTER(aFiles);
315 nsCOMArray<nsIFile> files;
316 nsresult rv;
318 // if we get into the base class, the platform
319 // doesn't implement GetFiles() yet.
320 // so we fake it.
321 nsCOMPtr<nsIFile> file;
322 rv = GetFile(getter_AddRefs(file));
323 NS_ENSURE_SUCCESS(rv, rv);
325 files.AppendObject(file);
327 return NS_NewArrayEnumerator(aFiles, files, NS_GET_IID(nsIFile));
330 // Set the display directory
331 NS_IMETHODIMP nsBaseFilePicker::SetDisplayDirectory(nsIFile* aDirectory) {
332 // if displaySpecialDirectory has been previously called, let's abort this
333 // operation.
334 if (!mDisplaySpecialDirectory.IsEmpty()) {
335 return NS_OK;
338 if (!aDirectory) {
339 mDisplayDirectory = nullptr;
340 return NS_OK;
342 nsCOMPtr<nsIFile> directory;
343 nsresult rv = aDirectory->Clone(getter_AddRefs(directory));
344 if (NS_FAILED(rv)) return rv;
345 mDisplayDirectory = directory;
346 return NS_OK;
349 // Get the display directory
350 NS_IMETHODIMP nsBaseFilePicker::GetDisplayDirectory(nsIFile** aDirectory) {
351 *aDirectory = nullptr;
353 // if displaySpecialDirectory has been previously called, let's abort this
354 // operation.
355 if (!mDisplaySpecialDirectory.IsEmpty()) {
356 return NS_OK;
359 if (!mDisplayDirectory) return NS_OK;
360 nsCOMPtr<nsIFile> directory;
361 nsresult rv = mDisplayDirectory->Clone(getter_AddRefs(directory));
362 if (NS_FAILED(rv)) {
363 return rv;
365 directory.forget(aDirectory);
366 return NS_OK;
369 // Set the display special directory
370 NS_IMETHODIMP nsBaseFilePicker::SetDisplaySpecialDirectory(
371 const nsAString& aDirectory) {
372 // if displayDirectory has been previously called, let's abort this operation.
373 if (mDisplayDirectory && mDisplaySpecialDirectory.IsEmpty()) {
374 return NS_OK;
377 mDisplaySpecialDirectory = aDirectory;
378 if (mDisplaySpecialDirectory.IsEmpty()) {
379 mDisplayDirectory = nullptr;
380 return NS_OK;
383 return ResolveSpecialDirectory(aDirectory);
386 nsresult nsBaseFilePicker::ResolveSpecialDirectory(
387 const nsAString& aSpecialDirectory) {
388 // Only perform special-directory name resolution in the parent process.
389 // (Subclasses of `nsBaseFilePicker` used in other processes must override
390 // this function.)
391 MOZ_ASSERT(XRE_IsParentProcess());
392 return NS_GetSpecialDirectory(NS_ConvertUTF16toUTF8(aSpecialDirectory).get(),
393 getter_AddRefs(mDisplayDirectory));
396 // Get the display special directory
397 NS_IMETHODIMP nsBaseFilePicker::GetDisplaySpecialDirectory(
398 nsAString& aDirectory) {
399 aDirectory = mDisplaySpecialDirectory;
400 return NS_OK;
403 NS_IMETHODIMP
404 nsBaseFilePicker::GetAddToRecentDocs(bool* aFlag) {
405 *aFlag = mAddToRecentDocs;
406 return NS_OK;
409 NS_IMETHODIMP
410 nsBaseFilePicker::SetAddToRecentDocs(bool aFlag) {
411 mAddToRecentDocs = aFlag;
412 return NS_OK;
415 NS_IMETHODIMP
416 nsBaseFilePicker::GetMode(nsIFilePicker::Mode* aMode) {
417 *aMode = mMode;
418 return NS_OK;
421 NS_IMETHODIMP
422 nsBaseFilePicker::SetOkButtonLabel(const nsAString& aLabel) {
423 mOkButtonLabel = aLabel;
424 return NS_OK;
427 NS_IMETHODIMP
428 nsBaseFilePicker::GetOkButtonLabel(nsAString& aLabel) {
429 aLabel = mOkButtonLabel;
430 return NS_OK;
433 NS_IMETHODIMP
434 nsBaseFilePicker::GetDomFileOrDirectory(nsISupports** aValue) {
435 nsCOMPtr<nsIFile> localFile;
436 nsresult rv = GetFile(getter_AddRefs(localFile));
437 NS_ENSURE_SUCCESS(rv, rv);
439 if (!localFile) {
440 *aValue = nullptr;
441 return NS_OK;
444 auto* innerParent = mParent ? mParent->GetCurrentInnerWindow() : nullptr;
446 if (!innerParent) {
447 return NS_ERROR_FAILURE;
450 return LocalFileToDirectoryOrBlob(
451 innerParent, mMode == nsIFilePicker::modeGetFolder, localFile, aValue);
454 NS_IMETHODIMP
455 nsBaseFilePicker::GetDomFileOrDirectoryEnumerator(
456 nsISimpleEnumerator** aValue) {
457 nsCOMPtr<nsISimpleEnumerator> iter;
458 nsresult rv = GetFiles(getter_AddRefs(iter));
459 NS_ENSURE_SUCCESS(rv, rv);
461 RefPtr<nsBaseFilePickerEnumerator> retIter =
462 new nsBaseFilePickerEnumerator(mParent, iter, mMode);
464 retIter.forget(aValue);
465 return NS_OK;