nsBaseFilePicker.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  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/. */
  6. #include "nsCOMPtr.h"
  7. #include "nsPIDOMWindow.h"
  8. #include "nsIDocShell.h"
  9. #include "nsIInterfaceRequestorUtils.h"
  10. #include "nsIBaseWindow.h"
  11. #include "nsIWidget.h"
  12. #include "nsIStringBundle.h"
  13. #include "nsXPIDLString.h"
  14. #include "nsIServiceManager.h"
  15. #include "nsCOMArray.h"
  16. #include "nsIFile.h"
  17. #include "nsEnumeratorUtils.h"
  18. #include "mozilla/dom/Directory.h"
  19. #include "mozilla/dom/File.h"
  20. #include "mozilla/Services.h"
  21. #include "WidgetUtils.h"
  22. #include "nsThreadUtils.h"
  23. #include "nsBaseFilePicker.h"
  24. using namespace mozilla::widget;
  25. using namespace mozilla::dom;
  26. #define FILEPICKER_TITLES "chrome://global/locale/filepicker.properties"
  27. #define FILEPICKER_FILTERS "chrome://global/content/filepicker.properties"
  28. namespace {
  29. nsresult
  30. LocalFileToDirectoryOrBlob(nsPIDOMWindowInner* aWindow,
  31. bool aIsDirectory,
  32. nsIFile* aFile,
  33. nsISupports** aResult)
  34. {
  35. if (aIsDirectory) {
  36. #ifdef DEBUG
  37. bool isDir;
  38. aFile->IsDirectory(&isDir);
  39. MOZ_ASSERT(isDir);
  40. #endif
  41. RefPtr<Directory> directory = Directory::Create(aWindow, aFile);
  42. MOZ_ASSERT(directory);
  43. directory.forget(aResult);
  44. return NS_OK;
  45. }
  46. nsCOMPtr<nsIDOMBlob> blob = File::CreateFromFile(aWindow, aFile);
  47. blob.forget(aResult);
  48. return NS_OK;
  49. }
  50. } // anonymous namespace
  51. /**
  52. * A runnable to dispatch from the main thread to the main thread to display
  53. * the file picker while letting the showAsync method return right away.
  54. */
  55. class AsyncShowFilePicker : public mozilla::Runnable
  56. {
  57. public:
  58. AsyncShowFilePicker(nsIFilePicker *aFilePicker,
  59. nsIFilePickerShownCallback *aCallback) :
  60. mFilePicker(aFilePicker),
  61. mCallback(aCallback)
  62. {
  63. }
  64. NS_IMETHOD Run() override
  65. {
  66. NS_ASSERTION(NS_IsMainThread(),
  67. "AsyncShowFilePicker should be on the main thread!");
  68. // It's possible that some widget implementations require GUI operations
  69. // to be on the main thread, so that's why we're not dispatching to another
  70. // thread and calling back to the main after it's done.
  71. int16_t result = nsIFilePicker::returnCancel;
  72. nsresult rv = mFilePicker->Show(&result);
  73. if (NS_FAILED(rv)) {
  74. NS_ERROR("FilePicker's Show() implementation failed!");
  75. }
  76. if (mCallback) {
  77. mCallback->Done(result);
  78. }
  79. return NS_OK;
  80. }
  81. private:
  82. RefPtr<nsIFilePicker> mFilePicker;
  83. RefPtr<nsIFilePickerShownCallback> mCallback;
  84. };
  85. class nsBaseFilePickerEnumerator : public nsISimpleEnumerator
  86. {
  87. public:
  88. NS_DECL_ISUPPORTS
  89. nsBaseFilePickerEnumerator(nsPIDOMWindowOuter* aParent,
  90. nsISimpleEnumerator* iterator,
  91. int16_t aMode)
  92. : mIterator(iterator)
  93. , mParent(aParent->GetCurrentInnerWindow())
  94. , mMode(aMode)
  95. {}
  96. NS_IMETHOD
  97. GetNext(nsISupports** aResult) override
  98. {
  99. nsCOMPtr<nsISupports> tmp;
  100. nsresult rv = mIterator->GetNext(getter_AddRefs(tmp));
  101. NS_ENSURE_SUCCESS(rv, rv);
  102. if (!tmp) {
  103. return NS_OK;
  104. }
  105. nsCOMPtr<nsIFile> localFile = do_QueryInterface(tmp);
  106. if (!localFile) {
  107. return NS_ERROR_FAILURE;
  108. }
  109. return LocalFileToDirectoryOrBlob(mParent,
  110. mMode == nsIFilePicker::modeGetFolder,
  111. localFile,
  112. aResult);
  113. }
  114. NS_IMETHOD
  115. HasMoreElements(bool* aResult) override
  116. {
  117. return mIterator->HasMoreElements(aResult);
  118. }
  119. protected:
  120. virtual ~nsBaseFilePickerEnumerator()
  121. {}
  122. private:
  123. nsCOMPtr<nsISimpleEnumerator> mIterator;
  124. nsCOMPtr<nsPIDOMWindowInner> mParent;
  125. int16_t mMode;
  126. };
  127. NS_IMPL_ISUPPORTS(nsBaseFilePickerEnumerator, nsISimpleEnumerator)
  128. nsBaseFilePicker::nsBaseFilePicker()
  129. : mAddToRecentDocs(true)
  130. , mMode(nsIFilePicker::modeOpen)
  131. {
  132. }
  133. nsBaseFilePicker::~nsBaseFilePicker()
  134. {
  135. }
  136. NS_IMETHODIMP nsBaseFilePicker::Init(mozIDOMWindowProxy* aParent,
  137. const nsAString& aTitle,
  138. int16_t aMode,
  139. bool aRequireInteraction)
  140. {
  141. NS_PRECONDITION(aParent, "Null parent passed to filepicker, no file "
  142. "picker for you!");
  143. mParent = nsPIDOMWindowOuter::From(aParent);
  144. nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(mParent->GetOuterWindow());
  145. NS_ENSURE_TRUE(widget, NS_ERROR_FAILURE);
  146. mMode = aMode;
  147. InitNative(widget, aTitle);
  148. return NS_OK;
  149. }
  150. NS_IMETHODIMP
  151. nsBaseFilePicker::Open(nsIFilePickerShownCallback *aCallback)
  152. {
  153. nsCOMPtr<nsIRunnable> filePickerEvent =
  154. new AsyncShowFilePicker(this, aCallback);
  155. return NS_DispatchToMainThread(filePickerEvent);
  156. }
  157. NS_IMETHODIMP
  158. nsBaseFilePicker::AppendFilters(int32_t aFilterMask)
  159. {
  160. nsCOMPtr<nsIStringBundleService> stringService =
  161. mozilla::services::GetStringBundleService();
  162. if (!stringService)
  163. return NS_ERROR_FAILURE;
  164. nsCOMPtr<nsIStringBundle> titleBundle, filterBundle;
  165. nsresult rv = stringService->CreateBundle(FILEPICKER_TITLES,
  166. getter_AddRefs(titleBundle));
  167. if (NS_FAILED(rv))
  168. return NS_ERROR_FAILURE;
  169. rv = stringService->CreateBundle(FILEPICKER_FILTERS, getter_AddRefs(filterBundle));
  170. if (NS_FAILED(rv))
  171. return NS_ERROR_FAILURE;
  172. nsXPIDLString title;
  173. nsXPIDLString filter;
  174. if (aFilterMask & filterAll) {
  175. titleBundle->GetStringFromName(u"allTitle", getter_Copies(title));
  176. filterBundle->GetStringFromName(u"allFilter", getter_Copies(filter));
  177. AppendFilter(title,filter);
  178. }
  179. if (aFilterMask & filterHTML) {
  180. titleBundle->GetStringFromName(u"htmlTitle", getter_Copies(title));
  181. filterBundle->GetStringFromName(u"htmlFilter", getter_Copies(filter));
  182. AppendFilter(title,filter);
  183. }
  184. if (aFilterMask & filterText) {
  185. titleBundle->GetStringFromName(u"textTitle", getter_Copies(title));
  186. filterBundle->GetStringFromName(u"textFilter", getter_Copies(filter));
  187. AppendFilter(title,filter);
  188. }
  189. if (aFilterMask & filterImages) {
  190. titleBundle->GetStringFromName(u"imageTitle", getter_Copies(title));
  191. filterBundle->GetStringFromName(u"imageFilter", getter_Copies(filter));
  192. AppendFilter(title,filter);
  193. }
  194. if (aFilterMask & filterAudio) {
  195. titleBundle->GetStringFromName(u"audioTitle", getter_Copies(title));
  196. filterBundle->GetStringFromName(u"audioFilter", getter_Copies(filter));
  197. AppendFilter(title,filter);
  198. }
  199. if (aFilterMask & filterVideo) {
  200. titleBundle->GetStringFromName(u"videoTitle", getter_Copies(title));
  201. filterBundle->GetStringFromName(u"videoFilter", getter_Copies(filter));
  202. AppendFilter(title,filter);
  203. }
  204. if (aFilterMask & filterXML) {
  205. titleBundle->GetStringFromName(u"xmlTitle", getter_Copies(title));
  206. filterBundle->GetStringFromName(u"xmlFilter", getter_Copies(filter));
  207. AppendFilter(title,filter);
  208. }
  209. if (aFilterMask & filterXUL) {
  210. titleBundle->GetStringFromName(u"xulTitle", getter_Copies(title));
  211. filterBundle->GetStringFromName(u"xulFilter", getter_Copies(filter));
  212. AppendFilter(title, filter);
  213. }
  214. if (aFilterMask & filterApps) {
  215. titleBundle->GetStringFromName(u"appsTitle", getter_Copies(title));
  216. // Pass the magic string "..apps" to the platform filepicker, which it
  217. // should recognize and do the correct platform behavior for.
  218. AppendFilter(title, NS_LITERAL_STRING("..apps"));
  219. }
  220. return NS_OK;
  221. }
  222. // Set the filter index
  223. NS_IMETHODIMP nsBaseFilePicker::GetFilterIndex(int32_t *aFilterIndex)
  224. {
  225. *aFilterIndex = 0;
  226. return NS_OK;
  227. }
  228. NS_IMETHODIMP nsBaseFilePicker::SetFilterIndex(int32_t aFilterIndex)
  229. {
  230. return NS_OK;
  231. }
  232. NS_IMETHODIMP nsBaseFilePicker::GetFiles(nsISimpleEnumerator **aFiles)
  233. {
  234. NS_ENSURE_ARG_POINTER(aFiles);
  235. nsCOMArray <nsIFile> files;
  236. nsresult rv;
  237. // if we get into the base class, the platform
  238. // doesn't implement GetFiles() yet.
  239. // so we fake it.
  240. nsCOMPtr <nsIFile> file;
  241. rv = GetFile(getter_AddRefs(file));
  242. NS_ENSURE_SUCCESS(rv,rv);
  243. files.AppendObject(file);
  244. return NS_NewArrayEnumerator(aFiles, files);
  245. }
  246. // Set the display directory
  247. NS_IMETHODIMP nsBaseFilePicker::SetDisplayDirectory(nsIFile *aDirectory)
  248. {
  249. if (!aDirectory) {
  250. mDisplayDirectory = nullptr;
  251. return NS_OK;
  252. }
  253. nsCOMPtr<nsIFile> directory;
  254. nsresult rv = aDirectory->Clone(getter_AddRefs(directory));
  255. if (NS_FAILED(rv))
  256. return rv;
  257. mDisplayDirectory = do_QueryInterface(directory, &rv);
  258. return rv;
  259. }
  260. // Get the display directory
  261. NS_IMETHODIMP nsBaseFilePicker::GetDisplayDirectory(nsIFile **aDirectory)
  262. {
  263. *aDirectory = nullptr;
  264. if (!mDisplayDirectory)
  265. return NS_OK;
  266. nsCOMPtr<nsIFile> directory;
  267. nsresult rv = mDisplayDirectory->Clone(getter_AddRefs(directory));
  268. if (NS_FAILED(rv)) {
  269. return rv;
  270. }
  271. directory.forget(aDirectory);
  272. return NS_OK;
  273. }
  274. NS_IMETHODIMP
  275. nsBaseFilePicker::GetAddToRecentDocs(bool *aFlag)
  276. {
  277. *aFlag = mAddToRecentDocs;
  278. return NS_OK;
  279. }
  280. NS_IMETHODIMP
  281. nsBaseFilePicker::SetAddToRecentDocs(bool aFlag)
  282. {
  283. mAddToRecentDocs = aFlag;
  284. return NS_OK;
  285. }
  286. NS_IMETHODIMP
  287. nsBaseFilePicker::GetMode(int16_t* aMode)
  288. {
  289. *aMode = mMode;
  290. return NS_OK;
  291. }
  292. NS_IMETHODIMP
  293. nsBaseFilePicker::SetOkButtonLabel(const nsAString& aLabel)
  294. {
  295. mOkButtonLabel = aLabel;
  296. return NS_OK;
  297. }
  298. NS_IMETHODIMP
  299. nsBaseFilePicker::GetOkButtonLabel(nsAString& aLabel)
  300. {
  301. aLabel = mOkButtonLabel;
  302. return NS_OK;
  303. }
  304. NS_IMETHODIMP
  305. nsBaseFilePicker::GetDomFileOrDirectory(nsISupports** aValue)
  306. {
  307. nsCOMPtr<nsIFile> localFile;
  308. nsresult rv = GetFile(getter_AddRefs(localFile));
  309. NS_ENSURE_SUCCESS(rv, rv);
  310. if (!localFile) {
  311. *aValue = nullptr;
  312. return NS_OK;
  313. }
  314. auto* innerParent = mParent ? mParent->GetCurrentInnerWindow() : nullptr;
  315. return LocalFileToDirectoryOrBlob(innerParent,
  316. mMode == nsIFilePicker::modeGetFolder,
  317. localFile,
  318. aValue);
  319. }
  320. NS_IMETHODIMP
  321. nsBaseFilePicker::GetDomFileOrDirectoryEnumerator(nsISimpleEnumerator** aValue)
  322. {
  323. nsCOMPtr<nsISimpleEnumerator> iter;
  324. nsresult rv = GetFiles(getter_AddRefs(iter));
  325. NS_ENSURE_SUCCESS(rv, rv);
  326. RefPtr<nsBaseFilePickerEnumerator> retIter =
  327. new nsBaseFilePickerEnumerator(mParent, iter, mMode);
  328. retIter.forget(aValue);
  329. return NS_OK;
  330. }