web_dialog_helper.cc 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. // Copyright (c) 2014 GitHub, Inc. All rights reserved.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "atom/browser/web_dialog_helper.h"
  5. #include <string>
  6. #include <utility>
  7. #include <vector>
  8. #include "atom/browser/atom_browser_context.h"
  9. #include "atom/browser/native_window.h"
  10. #include "atom/browser/ui/file_dialog.h"
  11. #include "base/bind.h"
  12. #include "base/files/file_enumerator.h"
  13. #include "base/files/file_path.h"
  14. #include "base/strings/utf_string_conversions.h"
  15. #include "chrome/common/pref_names.h"
  16. #include "components/prefs/pref_service.h"
  17. #include "content/public/browser/file_select_listener.h"
  18. #include "content/public/browser/render_frame_host.h"
  19. #include "content/public/browser/render_process_host.h"
  20. #include "content/public/browser/render_view_host.h"
  21. #include "content/public/browser/web_contents.h"
  22. #include "net/base/mime_util.h"
  23. #include "ui/shell_dialogs/selected_file_info.h"
  24. using blink::mojom::FileChooserFileInfo;
  25. using blink::mojom::FileChooserFileInfoPtr;
  26. using blink::mojom::FileChooserParams;
  27. namespace {
  28. class FileSelectHelper : public base::RefCounted<FileSelectHelper>,
  29. public content::WebContentsObserver {
  30. public:
  31. FileSelectHelper(content::RenderFrameHost* render_frame_host,
  32. std::unique_ptr<content::FileSelectListener> listener,
  33. blink::mojom::FileChooserParams::Mode mode)
  34. : render_frame_host_(render_frame_host),
  35. listener_(std::move(listener)),
  36. mode_(mode) {
  37. auto* web_contents =
  38. content::WebContents::FromRenderFrameHost(render_frame_host);
  39. content::WebContentsObserver::Observe(web_contents);
  40. }
  41. void ShowOpenDialog(const file_dialog::DialogSettings& settings) {
  42. auto callback = base::Bind(&FileSelectHelper::OnOpenDialogDone, this);
  43. file_dialog::ShowOpenDialog(settings, callback);
  44. }
  45. void ShowSaveDialog(const file_dialog::DialogSettings& settings) {
  46. auto callback = base::Bind(&FileSelectHelper::OnSaveDialogDone, this);
  47. file_dialog::ShowSaveDialog(settings, callback);
  48. }
  49. private:
  50. friend class base::RefCounted<FileSelectHelper>;
  51. ~FileSelectHelper() override {}
  52. #if defined(MAS_BUILD)
  53. void OnOpenDialogDone(bool result,
  54. const std::vector<base::FilePath>& paths,
  55. const std::vector<std::string>& bookmarks)
  56. #else
  57. void OnOpenDialogDone(bool result, const std::vector<base::FilePath>& paths)
  58. #endif
  59. {
  60. std::vector<FileChooserFileInfoPtr> file_info;
  61. if (result) {
  62. for (auto& path : paths) {
  63. file_info.push_back(FileChooserFileInfo::NewNativeFile(
  64. blink::mojom::NativeFileInfo::New(
  65. path, path.BaseName().AsUTF16Unsafe())));
  66. }
  67. if (render_frame_host_ && !paths.empty()) {
  68. auto* browser_context = static_cast<atom::AtomBrowserContext*>(
  69. render_frame_host_->GetProcess()->GetBrowserContext());
  70. browser_context->prefs()->SetFilePath(prefs::kSelectFileLastDirectory,
  71. paths[0].DirName());
  72. }
  73. }
  74. OnFilesSelected(std::move(file_info));
  75. }
  76. #if defined(MAS_BUILD)
  77. void OnSaveDialogDone(bool result,
  78. const base::FilePath& path,
  79. const std::string& bookmark)
  80. #else
  81. void OnSaveDialogDone(bool result, const base::FilePath& path)
  82. #endif
  83. {
  84. std::vector<FileChooserFileInfoPtr> file_info;
  85. if (result) {
  86. file_info.push_back(
  87. FileChooserFileInfo::NewNativeFile(blink::mojom::NativeFileInfo::New(
  88. path, path.BaseName().AsUTF16Unsafe())));
  89. }
  90. OnFilesSelected(std::move(file_info));
  91. }
  92. void OnFilesSelected(std::vector<FileChooserFileInfoPtr> file_info) {
  93. if (listener_) {
  94. listener_->FileSelected(std::move(file_info), base::FilePath(), mode_);
  95. listener_.reset();
  96. }
  97. render_frame_host_ = nullptr;
  98. Release();
  99. }
  100. // content::WebContentsObserver:
  101. void RenderFrameHostChanged(content::RenderFrameHost* old_host,
  102. content::RenderFrameHost* new_host) override {
  103. if (old_host == render_frame_host_)
  104. render_frame_host_ = nullptr;
  105. }
  106. // content::WebContentsObserver:
  107. void RenderFrameDeleted(content::RenderFrameHost* deleted_host) override {
  108. if (deleted_host == render_frame_host_)
  109. render_frame_host_ = nullptr;
  110. }
  111. // content::WebContentsObserver:
  112. void WebContentsDestroyed() override { render_frame_host_ = nullptr; }
  113. content::RenderFrameHost* render_frame_host_;
  114. std::unique_ptr<content::FileSelectListener> listener_;
  115. blink::mojom::FileChooserParams::Mode mode_;
  116. };
  117. file_dialog::Filters GetFileTypesFromAcceptType(
  118. const std::vector<base::string16>& accept_types) {
  119. file_dialog::Filters filters;
  120. if (accept_types.empty())
  121. return filters;
  122. std::vector<base::FilePath::StringType> extensions;
  123. int valid_type_count = 0;
  124. std::string description;
  125. for (const auto& accept_type : accept_types) {
  126. std::string ascii_type = base::UTF16ToASCII(accept_type);
  127. auto old_extension_size = extensions.size();
  128. if (ascii_type[0] == '.') {
  129. // If the type starts with a period it is assumed to be a file extension,
  130. // like `.txt`, // so we just have to add it to the list.
  131. base::FilePath::StringType extension(ascii_type.begin(),
  132. ascii_type.end());
  133. // Skip the first character.
  134. extensions.push_back(extension.substr(1));
  135. } else {
  136. if (ascii_type == "image/*")
  137. description = "Image Files";
  138. else if (ascii_type == "audio/*")
  139. description = "Audio Files";
  140. else if (ascii_type == "video/*")
  141. description = "Video Files";
  142. // For MIME Type, `audio/*, video/*, image/*
  143. net::GetExtensionsForMimeType(ascii_type, &extensions);
  144. }
  145. if (extensions.size() > old_extension_size)
  146. valid_type_count++;
  147. }
  148. // If no valid exntesion is added, return empty filters.
  149. if (extensions.empty())
  150. return filters;
  151. filters.push_back(file_dialog::Filter());
  152. if (valid_type_count > 1 || (valid_type_count == 1 && description.empty()))
  153. description = "Custom Files";
  154. DCHECK(!description.empty());
  155. filters[0].first = description;
  156. for (const auto& extension : extensions) {
  157. #if defined(OS_WIN)
  158. filters[0].second.push_back(base::UTF16ToASCII(extension));
  159. #else
  160. filters[0].second.push_back(extension);
  161. #endif
  162. }
  163. // Allow all files when extension is specified.
  164. filters.push_back(file_dialog::Filter());
  165. filters.back().first = "All Files";
  166. filters.back().second.push_back("*");
  167. return filters;
  168. }
  169. } // namespace
  170. namespace atom {
  171. WebDialogHelper::WebDialogHelper(NativeWindow* window, bool offscreen)
  172. : window_(window), offscreen_(offscreen), weak_factory_(this) {}
  173. WebDialogHelper::~WebDialogHelper() {}
  174. void WebDialogHelper::RunFileChooser(
  175. content::RenderFrameHost* render_frame_host,
  176. std::unique_ptr<content::FileSelectListener> listener,
  177. const blink::mojom::FileChooserParams& params) {
  178. file_dialog::DialogSettings settings;
  179. settings.force_detached = offscreen_;
  180. settings.filters = GetFileTypesFromAcceptType(params.accept_types);
  181. settings.parent_window = window_;
  182. settings.title = base::UTF16ToUTF8(params.title);
  183. scoped_refptr<FileSelectHelper> file_select_helper(new FileSelectHelper(
  184. render_frame_host, std::move(listener), params.mode));
  185. if (params.mode == FileChooserParams::Mode::kSave) {
  186. settings.default_path = params.default_file_name;
  187. file_select_helper->ShowSaveDialog(settings);
  188. } else {
  189. int flags = file_dialog::FILE_DIALOG_CREATE_DIRECTORY;
  190. switch (params.mode) {
  191. case FileChooserParams::Mode::kOpenMultiple:
  192. flags |= file_dialog::FILE_DIALOG_MULTI_SELECTIONS;
  193. FALLTHROUGH;
  194. case FileChooserParams::Mode::kOpen:
  195. flags |= file_dialog::FILE_DIALOG_OPEN_FILE;
  196. flags |= file_dialog::FILE_DIALOG_TREAT_PACKAGE_APP_AS_DIRECTORY;
  197. break;
  198. case FileChooserParams::Mode::kUploadFolder:
  199. flags |= file_dialog::FILE_DIALOG_OPEN_DIRECTORY;
  200. break;
  201. default:
  202. NOTREACHED();
  203. }
  204. auto* browser_context = static_cast<atom::AtomBrowserContext*>(
  205. render_frame_host->GetProcess()->GetBrowserContext());
  206. settings.default_path = browser_context->prefs()
  207. ->GetFilePath(prefs::kSelectFileLastDirectory)
  208. .Append(params.default_file_name);
  209. settings.properties = flags;
  210. file_select_helper->ShowOpenDialog(settings);
  211. }
  212. }
  213. void WebDialogHelper::EnumerateDirectory(
  214. content::WebContents* web_contents,
  215. std::unique_ptr<content::FileSelectListener> listener,
  216. const base::FilePath& dir) {
  217. int types = base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES |
  218. base::FileEnumerator::INCLUDE_DOT_DOT;
  219. base::FileEnumerator file_enum(dir, false, types);
  220. base::FilePath path;
  221. std::vector<FileChooserFileInfoPtr> file_info;
  222. while (!(path = file_enum.Next()).empty()) {
  223. file_info.push_back(FileChooserFileInfo::NewNativeFile(
  224. blink::mojom::NativeFileInfo::New(path, base::string16())));
  225. }
  226. listener->FileSelected(std::move(file_info), dir,
  227. FileChooserParams::Mode::kUploadFolder);
  228. }
  229. } // namespace atom