web_dialog_helper.cc 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  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 = content::WebContents::FromRenderFrameHost(
  32. 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, const std::vector<base::FilePath>& paths,
  48. const std::vector<std::string>& bookmarks)
  49. #else
  50. void OnOpenDialogDone(bool result, const std::vector<base::FilePath>& paths)
  51. #endif
  52. {
  53. std::vector<content::FileChooserFileInfo> file_info;
  54. if (result) {
  55. for (auto& path : paths) {
  56. content::FileChooserFileInfo info;
  57. info.file_path = path;
  58. info.display_name = path.BaseName().value();
  59. file_info.push_back(info);
  60. }
  61. if (render_frame_host_ && !paths.empty()) {
  62. auto browser_context = static_cast<atom::AtomBrowserContext*>(
  63. render_frame_host_->GetProcess()->GetBrowserContext());
  64. browser_context->prefs()->SetFilePath(prefs::kSelectFileLastDirectory,
  65. paths[0].DirName());
  66. }
  67. }
  68. OnFilesSelected(file_info);
  69. }
  70. #if defined(MAS_BUILD)
  71. void OnSaveDialogDone(bool result, const base::FilePath& path,
  72. const std::string& bookmark)
  73. #else
  74. void OnSaveDialogDone(bool result, const base::FilePath& path)
  75. #endif
  76. {
  77. std::vector<content::FileChooserFileInfo> file_info;
  78. if (result) {
  79. content::FileChooserFileInfo info;
  80. info.file_path = path;
  81. info.display_name = path.BaseName().value();
  82. file_info.push_back(info);
  83. }
  84. OnFilesSelected(file_info);
  85. }
  86. void OnFilesSelected(
  87. const std::vector<content::FileChooserFileInfo>& file_info) {
  88. if (render_frame_host_)
  89. render_frame_host_->FilesSelectedInChooser(file_info, mode_);
  90. }
  91. // content::WebContentsObserver:
  92. void RenderFrameHostChanged(content::RenderFrameHost* old_host,
  93. content::RenderFrameHost* new_host) override {
  94. if (old_host == render_frame_host_)
  95. render_frame_host_ = nullptr;
  96. }
  97. // content::WebContentsObserver:
  98. void RenderFrameDeleted(content::RenderFrameHost* deleted_host) override {
  99. if (deleted_host == render_frame_host_)
  100. render_frame_host_ = nullptr;
  101. }
  102. // content::WebContentsObserver:
  103. void WebContentsDestroyed() override {
  104. render_frame_host_ = nullptr;
  105. }
  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(
  124. ascii_type.begin(), 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),
  165. offscreen_(offscreen),
  166. weak_factory_(this) {
  167. }
  168. WebDialogHelper::~WebDialogHelper() {
  169. }
  170. void WebDialogHelper::RunFileChooser(
  171. content::RenderFrameHost* render_frame_host,
  172. const content::FileChooserParams& params) {
  173. std::vector<content::FileChooserFileInfo> result;
  174. file_dialog::DialogSettings settings;
  175. settings.force_detached = offscreen_;
  176. settings.filters = GetFileTypesFromAcceptType(params.accept_types);
  177. settings.parent_window = window_;
  178. settings.title = base::UTF16ToUTF8(params.title);
  179. scoped_refptr<FileSelectHelper> file_select_helper(
  180. new FileSelectHelper(render_frame_host, params.mode));
  181. if (params.mode == content::FileChooserParams::Save) {
  182. settings.default_path = params.default_file_name;
  183. file_select_helper->ShowSaveDialog(settings);
  184. } else {
  185. int flags = file_dialog::FILE_DIALOG_CREATE_DIRECTORY;
  186. switch (params.mode) {
  187. case content::FileChooserParams::OpenMultiple:
  188. flags |= file_dialog::FILE_DIALOG_MULTI_SELECTIONS;
  189. case content::FileChooserParams::Open:
  190. flags |= file_dialog::FILE_DIALOG_OPEN_FILE;
  191. flags |= file_dialog::FILE_DIALOG_TREAT_PACKAGE_APP_AS_DIRECTORY;
  192. break;
  193. case content::FileChooserParams::UploadFolder:
  194. flags |= file_dialog::FILE_DIALOG_OPEN_DIRECTORY;
  195. break;
  196. default:
  197. NOTREACHED();
  198. }
  199. AtomBrowserContext* browser_context = static_cast<AtomBrowserContext*>(
  200. window_->web_contents()->GetBrowserContext());
  201. if (!browser_context) {
  202. browser_context = static_cast<atom::AtomBrowserContext*>(
  203. render_frame_host->GetProcess()->GetBrowserContext());
  204. }
  205. settings.default_path = browser_context->prefs()->GetFilePath(
  206. prefs::kSelectFileLastDirectory).Append(params.default_file_name);
  207. settings.properties = flags;
  208. file_select_helper->ShowOpenDialog(settings);
  209. }
  210. }
  211. void WebDialogHelper::EnumerateDirectory(content::WebContents* web_contents,
  212. int request_id,
  213. const base::FilePath& dir) {
  214. int types = base::FileEnumerator::FILES |
  215. base::FileEnumerator::DIRECTORIES |
  216. base::FileEnumerator::INCLUDE_DOT_DOT;
  217. base::FileEnumerator file_enum(dir, false, types);
  218. base::FilePath path;
  219. std::vector<base::FilePath> paths;
  220. while (!(path = file_enum.Next()).empty())
  221. paths.push_back(path);
  222. web_contents->GetRenderViewHost()->DirectoryEnumerationFinished(
  223. request_id, paths);
  224. }
  225. } // namespace atom