web_dialog_helper.cc 8.6 KB

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