michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsIBaseWindow.h" michael@0: #include "nsIWidget.h" michael@0: michael@0: #include "nsIStringBundle.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsCOMArray.h" michael@0: #include "nsIFile.h" michael@0: #include "nsDOMFile.h" michael@0: #include "nsEnumeratorUtils.h" michael@0: #include "mozilla/Services.h" michael@0: #include "WidgetUtils.h" michael@0: #include "nsThreadUtils.h" michael@0: michael@0: #include "nsBaseFilePicker.h" michael@0: michael@0: using namespace mozilla::widget; michael@0: michael@0: #define FILEPICKER_TITLES "chrome://global/locale/filepicker.properties" michael@0: #define FILEPICKER_FILTERS "chrome://global/content/filepicker.properties" michael@0: michael@0: /** michael@0: * A runnable to dispatch from the main thread to the main thread to display michael@0: * the file picker while letting the showAsync method return right away. michael@0: */ michael@0: class AsyncShowFilePicker : public nsRunnable michael@0: { michael@0: public: michael@0: AsyncShowFilePicker(nsIFilePicker *aFilePicker, michael@0: nsIFilePickerShownCallback *aCallback) : michael@0: mFilePicker(aFilePicker), michael@0: mCallback(aCallback) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), michael@0: "AsyncShowFilePicker should be on the main thread!"); michael@0: michael@0: // It's possible that some widget implementations require GUI operations michael@0: // to be on the main thread, so that's why we're not dispatching to another michael@0: // thread and calling back to the main after it's done. michael@0: int16_t result = nsIFilePicker::returnCancel; michael@0: nsresult rv = mFilePicker->Show(&result); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("FilePicker's Show() implementation failed!"); michael@0: } michael@0: michael@0: if (mCallback) { michael@0: mCallback->Done(result); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mFilePicker; michael@0: nsRefPtr mCallback; michael@0: }; michael@0: michael@0: class nsBaseFilePickerEnumerator : public nsISimpleEnumerator michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: nsBaseFilePickerEnumerator(nsISimpleEnumerator* iterator) michael@0: : mIterator(iterator) michael@0: {} michael@0: michael@0: virtual ~nsBaseFilePickerEnumerator() michael@0: {} michael@0: michael@0: NS_IMETHOD michael@0: GetNext(nsISupports** aResult) michael@0: { michael@0: nsCOMPtr tmp; michael@0: nsresult rv = mIterator->GetNext(getter_AddRefs(tmp)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!tmp) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr localFile = do_QueryInterface(tmp); michael@0: if (!localFile) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsCOMPtr domFile = new nsDOMFileFile(localFile); michael@0: domFile.forget(aResult); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHOD michael@0: HasMoreElements(bool* aResult) michael@0: { michael@0: return mIterator->HasMoreElements(aResult); michael@0: } michael@0: michael@0: private: michael@0: nsCOMPtr mIterator; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsBaseFilePickerEnumerator, nsISimpleEnumerator) michael@0: michael@0: nsBaseFilePicker::nsBaseFilePicker() michael@0: : mAddToRecentDocs(true) michael@0: , mMode(nsIFilePicker::modeOpen) michael@0: { michael@0: michael@0: } michael@0: michael@0: nsBaseFilePicker::~nsBaseFilePicker() michael@0: { michael@0: michael@0: } michael@0: michael@0: NS_IMETHODIMP nsBaseFilePicker::Init(nsIDOMWindow *aParent, michael@0: const nsAString& aTitle, michael@0: int16_t aMode) michael@0: { michael@0: NS_PRECONDITION(aParent, "Null parent passed to filepicker, no file " michael@0: "picker for you!"); michael@0: nsCOMPtr widget = WidgetUtils::DOMWindowToWidget(aParent); michael@0: NS_ENSURE_TRUE(widget, NS_ERROR_FAILURE); michael@0: michael@0: mMode = aMode; michael@0: InitNative(widget, aTitle); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseFilePicker::Open(nsIFilePickerShownCallback *aCallback) michael@0: { michael@0: nsCOMPtr filePickerEvent = michael@0: new AsyncShowFilePicker(this, aCallback); michael@0: return NS_DispatchToMainThread(filePickerEvent); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseFilePicker::AppendFilters(int32_t aFilterMask) michael@0: { michael@0: nsCOMPtr stringService = michael@0: mozilla::services::GetStringBundleService(); michael@0: if (!stringService) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr titleBundle, filterBundle; michael@0: michael@0: nsresult rv = stringService->CreateBundle(FILEPICKER_TITLES, michael@0: getter_AddRefs(titleBundle)); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: rv = stringService->CreateBundle(FILEPICKER_FILTERS, getter_AddRefs(filterBundle)); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsXPIDLString title; michael@0: nsXPIDLString filter; michael@0: michael@0: if (aFilterMask & filterAll) { michael@0: titleBundle->GetStringFromName(MOZ_UTF16("allTitle"), getter_Copies(title)); michael@0: filterBundle->GetStringFromName(MOZ_UTF16("allFilter"), getter_Copies(filter)); michael@0: AppendFilter(title,filter); michael@0: } michael@0: if (aFilterMask & filterHTML) { michael@0: titleBundle->GetStringFromName(MOZ_UTF16("htmlTitle"), getter_Copies(title)); michael@0: filterBundle->GetStringFromName(MOZ_UTF16("htmlFilter"), getter_Copies(filter)); michael@0: AppendFilter(title,filter); michael@0: } michael@0: if (aFilterMask & filterText) { michael@0: titleBundle->GetStringFromName(MOZ_UTF16("textTitle"), getter_Copies(title)); michael@0: filterBundle->GetStringFromName(MOZ_UTF16("textFilter"), getter_Copies(filter)); michael@0: AppendFilter(title,filter); michael@0: } michael@0: if (aFilterMask & filterImages) { michael@0: titleBundle->GetStringFromName(MOZ_UTF16("imageTitle"), getter_Copies(title)); michael@0: filterBundle->GetStringFromName(MOZ_UTF16("imageFilter"), getter_Copies(filter)); michael@0: AppendFilter(title,filter); michael@0: } michael@0: if (aFilterMask & filterAudio) { michael@0: titleBundle->GetStringFromName(MOZ_UTF16("audioTitle"), getter_Copies(title)); michael@0: filterBundle->GetStringFromName(MOZ_UTF16("audioFilter"), getter_Copies(filter)); michael@0: AppendFilter(title,filter); michael@0: } michael@0: if (aFilterMask & filterVideo) { michael@0: titleBundle->GetStringFromName(MOZ_UTF16("videoTitle"), getter_Copies(title)); michael@0: filterBundle->GetStringFromName(MOZ_UTF16("videoFilter"), getter_Copies(filter)); michael@0: AppendFilter(title,filter); michael@0: } michael@0: if (aFilterMask & filterXML) { michael@0: titleBundle->GetStringFromName(MOZ_UTF16("xmlTitle"), getter_Copies(title)); michael@0: filterBundle->GetStringFromName(MOZ_UTF16("xmlFilter"), getter_Copies(filter)); michael@0: AppendFilter(title,filter); michael@0: } michael@0: if (aFilterMask & filterXUL) { michael@0: titleBundle->GetStringFromName(MOZ_UTF16("xulTitle"), getter_Copies(title)); michael@0: filterBundle->GetStringFromName(MOZ_UTF16("xulFilter"), getter_Copies(filter)); michael@0: AppendFilter(title, filter); michael@0: } michael@0: if (aFilterMask & filterApps) { michael@0: titleBundle->GetStringFromName(MOZ_UTF16("appsTitle"), getter_Copies(title)); michael@0: // Pass the magic string "..apps" to the platform filepicker, which it michael@0: // should recognize and do the correct platform behavior for. michael@0: AppendFilter(title, NS_LITERAL_STRING("..apps")); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Set the filter index michael@0: NS_IMETHODIMP nsBaseFilePicker::GetFilterIndex(int32_t *aFilterIndex) michael@0: { michael@0: *aFilterIndex = 0; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsBaseFilePicker::SetFilterIndex(int32_t aFilterIndex) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsBaseFilePicker::GetFiles(nsISimpleEnumerator **aFiles) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aFiles); michael@0: nsCOMArray files; michael@0: nsresult rv; michael@0: michael@0: // if we get into the base class, the platform michael@0: // doesn't implement GetFiles() yet. michael@0: // so we fake it. michael@0: nsCOMPtr file; michael@0: rv = GetFile(getter_AddRefs(file)); michael@0: NS_ENSURE_SUCCESS(rv,rv); michael@0: michael@0: files.AppendObject(file); michael@0: michael@0: return NS_NewArrayEnumerator(aFiles, files); michael@0: } michael@0: michael@0: // Set the display directory michael@0: NS_IMETHODIMP nsBaseFilePicker::SetDisplayDirectory(nsIFile *aDirectory) michael@0: { michael@0: if (!aDirectory) { michael@0: mDisplayDirectory = nullptr; michael@0: return NS_OK; michael@0: } michael@0: nsCOMPtr directory; michael@0: nsresult rv = aDirectory->Clone(getter_AddRefs(directory)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: mDisplayDirectory = do_QueryInterface(directory, &rv); michael@0: return rv; michael@0: } michael@0: michael@0: // Get the display directory michael@0: NS_IMETHODIMP nsBaseFilePicker::GetDisplayDirectory(nsIFile **aDirectory) michael@0: { michael@0: *aDirectory = nullptr; michael@0: if (!mDisplayDirectory) michael@0: return NS_OK; michael@0: nsCOMPtr directory; michael@0: nsresult rv = mDisplayDirectory->Clone(getter_AddRefs(directory)); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: directory.forget(aDirectory); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseFilePicker::GetAddToRecentDocs(bool *aFlag) michael@0: { michael@0: *aFlag = mAddToRecentDocs; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseFilePicker::SetAddToRecentDocs(bool aFlag) michael@0: { michael@0: mAddToRecentDocs = aFlag; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseFilePicker::GetMode(int16_t* aMode) michael@0: { michael@0: *aMode = mMode; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseFilePicker::GetDomfile(nsIDOMFile** aDomfile) michael@0: { michael@0: nsCOMPtr localFile; michael@0: nsresult rv = GetFile(getter_AddRefs(localFile)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!localFile) { michael@0: *aDomfile = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsRefPtr domFile = new nsDOMFileFile(localFile); michael@0: domFile.forget(aDomfile); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBaseFilePicker::GetDomfiles(nsISimpleEnumerator** aDomfiles) michael@0: { michael@0: nsCOMPtr iter; michael@0: nsresult rv = GetFiles(getter_AddRefs(iter)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsRefPtr retIter = michael@0: new nsBaseFilePickerEnumerator(iter); michael@0: michael@0: retIter.forget(aDomfiles); michael@0: return NS_OK; michael@0: } michael@0: