blob: 72f121ba9addd4291dac5fe63e0abff9b22ffb59 [file] [log] [blame]
[email protected]5626b0892012-02-20 14:46:581// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]ba70d082010-09-10 16:54:492// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/file_select_helper.h"
6
avi6846aef2015-12-26 01:09:387#include <stddef.h>
8
[email protected]5ac950b2010-12-09 21:34:259#include <string>
[email protected]b7b4beb2013-07-09 14:06:5010#include <utility>
[email protected]5ac950b2010-12-09 21:34:2511
[email protected]9f054aa12011-09-29 19:13:4512#include "base/bind.h"
[email protected]25a4c1c2013-06-08 04:53:3613#include "base/files/file_enumerator.h"
thestig18dfb7a52014-08-26 10:44:0414#include "base/files/file_util.h"
[email protected]1988e1c2013-02-28 20:27:4215#include "base/strings/string_split.h"
[email protected]d8830562013-06-10 22:01:5416#include "base/strings/string_util.h"
[email protected]112158af2013-06-07 23:46:1817#include "base/strings/utf_string_conversions.h"
avi6846aef2015-12-26 01:09:3818#include "build/build_config.h"
hironoa4a32af22014-11-07 03:17:2919#include "chrome/browser/browser_process.h"
[email protected]ba70d082010-09-10 16:54:4920#include "chrome/browser/platform_util.h"
[email protected]8ecad5e2010-12-02 21:18:3321#include "chrome/browser/profiles/profile.h"
hironoa4a32af22014-11-07 03:17:2922#include "chrome/browser/profiles/profile_manager.h"
[email protected]d9898912011-04-15 21:10:0023#include "chrome/browser/ui/browser.h"
24#include "chrome/browser/ui/browser_list.h"
[email protected]6e1fcd12012-07-02 17:14:2025#include "chrome/browser/ui/chrome_select_file_policy.h"
[email protected]af39f002014-08-22 10:18:1826#include "chrome/grit/generated_resources.h"
[email protected]6a1c98e02012-10-24 21:49:4327#include "content/public/browser/browser_thread.h"
[email protected]6c2381d2011-10-19 02:52:5328#include "content/public/browser/notification_details.h"
29#include "content/public/browser/notification_source.h"
[email protected]0d6e9bd2011-10-18 04:29:1630#include "content/public/browser/notification_types.h"
[email protected]9c1662b2012-03-06 15:44:3331#include "content/public/browser/render_view_host.h"
avif9ab5d942015-10-15 14:05:4432#include "content/public/browser/render_widget_host.h"
[email protected]5626b0892012-02-20 14:46:5833#include "content/public/browser/render_widget_host_view.h"
rdevlin.croninbfbc11d92015-06-12 23:26:0834#include "content/public/browser/storage_partition.h"
[email protected]33f8ad52012-05-22 18:10:1335#include "content/public/browser/web_contents.h"
hirono570357bd2014-10-08 12:39:2736#include "content/public/common/file_chooser_file_info.h"
[email protected]8caadeb2011-11-22 02:45:2337#include "content/public/common/file_chooser_params.h"
asankaee261f32015-10-27 21:04:2838#include "net/base/filename_util.h"
[email protected]b3841c502011-03-09 01:21:3139#include "net/base/mime_util.h"
[email protected]c051a1b2011-01-21 23:30:1740#include "ui/base/l10n/l10n_util.h"
[email protected]4344a3c2013-01-17 23:49:2041#include "ui/shell_dialogs/selected_file_info.h"
[email protected]ba70d082010-09-10 16:54:4942
hirono275bfbe2014-10-21 01:46:1743#if defined(OS_CHROMEOS)
44#include "chrome/browser/chromeos/file_manager/fileapi_util.h"
45#include "content/public/browser/site_instance.h"
46#endif
47
asankaee261f32015-10-27 21:04:2848#if defined(FULL_SAFE_BROWSING)
49#include "chrome/browser/safe_browsing/unverified_download_policy.h"
50#endif
51
[email protected]631bb742011-11-02 11:29:3952using content::BrowserThread;
[email protected]33f8ad52012-05-22 18:10:1353using content::FileChooserParams;
[email protected]eaabba22012-03-07 15:02:1154using content::RenderViewHost;
55using content::RenderWidgetHost;
[email protected]ea049a02011-12-25 21:37:0956using content::WebContents;
[email protected]631bb742011-11-02 11:29:3957
[email protected]600ea402011-04-12 00:01:5158namespace {
59
60// There is only one file-selection happening at any given time,
61// so we allocate an enumeration ID for that purpose. All IDs from
62// the renderer must start at 0 and increase.
[email protected]459fba82011-10-13 02:48:5063const int kFileSelectEnumerationId = -1;
64
[email protected]53f04c82012-07-26 02:31:0965// Converts a list of FilePaths to a list of ui::SelectedFileInfo.
66std::vector<ui::SelectedFileInfo> FilePathListToSelectedFileInfoList(
[email protected]650b2d52013-02-10 03:41:4567 const std::vector<base::FilePath>& paths) {
[email protected]ddb034b2012-06-26 20:31:3968 std::vector<ui::SelectedFileInfo> selected_files;
[email protected]fb11b6a42012-03-14 07:25:1269 for (size_t i = 0; i < paths.size(); ++i) {
70 selected_files.push_back(
[email protected]53f04c82012-07-26 02:31:0971 ui::SelectedFileInfo(paths[i], paths[i]));
[email protected]fb11b6a42012-03-14 07:25:1272 }
73 return selected_files;
[email protected]600ea402011-04-12 00:01:5174}
75
erikchen22de64a32014-10-10 18:27:4376void DeleteFiles(const std::vector<base::FilePath>& paths) {
77 for (auto& file_path : paths)
78 base::DeleteFile(file_path, false);
79}
80
hironoa4a32af22014-11-07 03:17:2981bool IsValidProfile(Profile* profile) {
82 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
83 return g_browser_process->profile_manager()->IsValidProfile(profile);
84}
85
[email protected]fb11b6a42012-03-14 07:25:1286} // namespace
87
[email protected]485a5272011-04-12 00:49:2988struct FileSelectHelper::ActiveDirectoryEnumeration {
[email protected]d45f7512011-06-21 21:18:2789 ActiveDirectoryEnumeration() : rvh_(NULL) {}
[email protected]485a5272011-04-12 00:49:2990
91 scoped_ptr<DirectoryListerDispatchDelegate> delegate_;
[email protected]05a814182011-04-27 19:50:3492 scoped_ptr<net::DirectoryLister> lister_;
[email protected]485a5272011-04-12 00:49:2993 RenderViewHost* rvh_;
[email protected]650b2d52013-02-10 03:41:4594 std::vector<base::FilePath> results_;
[email protected]485a5272011-04-12 00:49:2995};
96
[email protected]ba70d082010-09-10 16:54:4997FileSelectHelper::FileSelectHelper(Profile* profile)
98 : profile_(profile),
99 render_view_host_(NULL),
[email protected]ea049a02011-12-25 21:37:09100 web_contents_(NULL),
[email protected]ba70d082010-09-10 16:54:49101 select_file_dialog_(),
[email protected]9f054aa12011-09-29 19:13:45102 select_file_types_(),
[email protected]bfcf1e92013-07-11 04:37:25103 dialog_type_(ui::SelectFileDialog::SELECT_OPEN_FILE),
104 dialog_mode_(FileChooserParams::Open) {
[email protected]ba70d082010-09-10 16:54:49105}
106
107FileSelectHelper::~FileSelectHelper() {
108 // There may be pending file dialogs, we need to tell them that we've gone
109 // away so they don't try and call back to us.
110 if (select_file_dialog_.get())
111 select_file_dialog_->ListenerDestroyed();
112
[email protected]600ea402011-04-12 00:01:51113 // Stop any pending directory enumeration, prevent a callback, and free
114 // allocated memory.
115 std::map<int, ActiveDirectoryEnumeration*>::iterator iter;
116 for (iter = directory_enumerations_.begin();
117 iter != directory_enumerations_.end();
118 ++iter) {
[email protected]05a814182011-04-27 19:50:34119 iter->second->lister_.reset();
[email protected]600ea402011-04-12 00:01:51120 delete iter->second;
[email protected]ba70d082010-09-10 16:54:49121 }
122}
123
[email protected]23827ec2012-08-10 22:08:08124void FileSelectHelper::DirectoryListerDispatchDelegate::OnListFile(
125 const net::DirectoryLister::DirectoryListerData& data) {
126 parent_->OnListFile(id_, data);
127}
128
129void FileSelectHelper::DirectoryListerDispatchDelegate::OnListDone(int error) {
130 parent_->OnListDone(id_, error);
131}
132
[email protected]650b2d52013-02-10 03:41:45133void FileSelectHelper::FileSelected(const base::FilePath& path,
[email protected]ba70d082010-09-10 16:54:49134 int index, void* params) {
[email protected]53f04c82012-07-26 02:31:09135 FileSelectedWithExtraInfo(ui::SelectedFileInfo(path, path), index, params);
[email protected]fb11b6a42012-03-14 07:25:12136}
137
138void FileSelectHelper::FileSelectedWithExtraInfo(
[email protected]ddb034b2012-06-26 20:31:39139 const ui::SelectedFileInfo& file,
[email protected]fb11b6a42012-03-14 07:25:12140 int index,
141 void* params) {
hironoa4a32af22014-11-07 03:17:29142 if (IsValidProfile(profile_))
143 profile_->set_last_selected_directory(file.file_path.DirName());
[email protected]ba70d082010-09-10 16:54:49144
erikchen22de64a32014-10-10 18:27:43145 if (!render_view_host_) {
146 RunFileChooserEnd();
147 return;
148 }
149
[email protected]650b2d52013-02-10 03:41:45150 const base::FilePath& path = file.local_path;
[email protected]6bedbef2013-07-31 06:33:49151 if (dialog_type_ == ui::SelectFileDialog::SELECT_UPLOAD_FOLDER) {
[email protected]600ea402011-04-12 00:01:51152 StartNewEnumeration(path, kFileSelectEnumerationId, render_view_host_);
[email protected]ba70d082010-09-10 16:54:49153 return;
154 }
155
[email protected]ddb034b2012-06-26 20:31:39156 std::vector<ui::SelectedFileInfo> files;
[email protected]fb11b6a42012-03-14 07:25:12157 files.push_back(file);
[email protected]9f054aa12011-09-29 19:13:45158
erikchen22de64a32014-10-10 18:27:43159#if defined(OS_MACOSX) && !defined(OS_IOS)
160 content::BrowserThread::PostTask(
161 content::BrowserThread::FILE_USER_BLOCKING,
162 FROM_HERE,
163 base::Bind(&FileSelectHelper::ProcessSelectedFilesMac, this, files));
164#else
165 NotifyRenderViewHostAndEnd(files);
166#endif // defined(OS_MACOSX) && !defined(OS_IOS)
[email protected]ba70d082010-09-10 16:54:49167}
168
[email protected]650b2d52013-02-10 03:41:45169void FileSelectHelper::MultiFilesSelected(
170 const std::vector<base::FilePath>& files,
171 void* params) {
[email protected]ddb034b2012-06-26 20:31:39172 std::vector<ui::SelectedFileInfo> selected_files =
[email protected]53f04c82012-07-26 02:31:09173 FilePathListToSelectedFileInfoList(files);
174
[email protected]fb11b6a42012-03-14 07:25:12175 MultiFilesSelectedWithExtraInfo(selected_files, params);
176}
177
178void FileSelectHelper::MultiFilesSelectedWithExtraInfo(
[email protected]ddb034b2012-06-26 20:31:39179 const std::vector<ui::SelectedFileInfo>& files,
[email protected]fb11b6a42012-03-14 07:25:12180 void* params) {
hironoa4a32af22014-11-07 03:17:29181 if (!files.empty() && IsValidProfile(profile_))
[email protected]53f04c82012-07-26 02:31:09182 profile_->set_last_selected_directory(files[0].file_path.DirName());
[email protected]ba70d082010-09-10 16:54:49183
erikchen22de64a32014-10-10 18:27:43184#if defined(OS_MACOSX) && !defined(OS_IOS)
185 content::BrowserThread::PostTask(
186 content::BrowserThread::FILE_USER_BLOCKING,
187 FROM_HERE,
188 base::Bind(&FileSelectHelper::ProcessSelectedFilesMac, this, files));
189#else
190 NotifyRenderViewHostAndEnd(files);
191#endif // defined(OS_MACOSX) && !defined(OS_IOS)
[email protected]ba70d082010-09-10 16:54:49192}
193
194void FileSelectHelper::FileSelectionCanceled(void* params) {
erikchen22de64a32014-10-10 18:27:43195 NotifyRenderViewHostAndEnd(std::vector<ui::SelectedFileInfo>());
[email protected]ba70d082010-09-10 16:54:49196}
197
[email protected]650b2d52013-02-10 03:41:45198void FileSelectHelper::StartNewEnumeration(const base::FilePath& path,
[email protected]600ea402011-04-12 00:01:51199 int request_id,
200 RenderViewHost* render_view_host) {
201 scoped_ptr<ActiveDirectoryEnumeration> entry(new ActiveDirectoryEnumeration);
202 entry->rvh_ = render_view_host;
203 entry->delegate_.reset(new DirectoryListerDispatchDelegate(this, request_id));
olli.syrjala3396ad32015-04-24 07:56:41204 entry->lister_.reset(new net::DirectoryLister(
205 path, net::DirectoryLister::NO_SORT_RECURSIVE, entry->delegate_.get()));
[email protected]600ea402011-04-12 00:01:51206 if (!entry->lister_->Start()) {
207 if (request_id == kFileSelectEnumerationId)
208 FileSelectionCanceled(NULL);
209 else
210 render_view_host->DirectoryEnumerationFinished(request_id,
211 entry->results_);
212 } else {
213 directory_enumerations_[request_id] = entry.release();
214 }
[email protected]ba70d082010-09-10 16:54:49215}
216
217void FileSelectHelper::OnListFile(
[email protected]600ea402011-04-12 00:01:51218 int id,
[email protected]ba70d082010-09-10 16:54:49219 const net::DirectoryLister::DirectoryListerData& data) {
[email protected]600ea402011-04-12 00:01:51220 ActiveDirectoryEnumeration* entry = directory_enumerations_[id];
221
[email protected]2c718a0d2014-03-24 02:42:22222 // Directory upload only cares about files.
[email protected]25a4c1c2013-06-08 04:53:36223 if (data.info.IsDirectory())
[email protected]2c718a0d2014-03-24 02:42:22224 return;
225
226 entry->results_.push_back(data.path);
[email protected]ba70d082010-09-10 16:54:49227}
228
[email protected]600ea402011-04-12 00:01:51229void FileSelectHelper::OnListDone(int id, int error) {
230 // This entry needs to be cleaned up when this function is done.
231 scoped_ptr<ActiveDirectoryEnumeration> entry(directory_enumerations_[id]);
232 directory_enumerations_.erase(id);
233 if (!entry->rvh_)
[email protected]ba70d082010-09-10 16:54:49234 return;
[email protected]ba70d082010-09-10 16:54:49235 if (error) {
236 FileSelectionCanceled(NULL);
237 return;
238 }
[email protected]fb11b6a42012-03-14 07:25:12239
[email protected]ddb034b2012-06-26 20:31:39240 std::vector<ui::SelectedFileInfo> selected_files =
[email protected]53f04c82012-07-26 02:31:09241 FilePathListToSelectedFileInfoList(entry->results_);
[email protected]fb11b6a42012-03-14 07:25:12242
hirono275bfbe2014-10-21 01:46:17243 if (id == kFileSelectEnumerationId) {
244 NotifyRenderViewHostAndEnd(selected_files);
245 } else {
[email protected]600ea402011-04-12 00:01:51246 entry->rvh_->DirectoryEnumerationFinished(id, entry->results_);
hirono275bfbe2014-10-21 01:46:17247 EnumerateDirectoryEnd();
248 }
[email protected]ba70d082010-09-10 16:54:49249}
250
erikchen22de64a32014-10-10 18:27:43251void FileSelectHelper::NotifyRenderViewHostAndEnd(
252 const std::vector<ui::SelectedFileInfo>& files) {
hirono275bfbe2014-10-21 01:46:17253 if (!render_view_host_) {
254 RunFileChooserEnd();
255 return;
256 }
257
258#if defined(OS_CHROMEOS)
259 if (!files.empty()) {
hironoa4a32af22014-11-07 03:17:29260 if (!IsValidProfile(profile_)) {
261 RunFileChooserEnd();
262 return;
263 }
hirono275bfbe2014-10-21 01:46:17264 // Converts |files| into FileChooserFileInfo with handling of non-native
265 // files.
rdevlin.croninbfbc11d92015-06-12 23:26:08266 storage::FileSystemContext* file_system_context =
267 content::BrowserContext::GetStoragePartition(
268 profile_, render_view_host_->GetSiteInstance())->
269 GetFileSystemContext();
hirono275bfbe2014-10-21 01:46:17270 file_manager::util::ConvertSelectedFileInfoListToFileChooserFileInfoList(
rdevlin.croninbfbc11d92015-06-12 23:26:08271 file_system_context,
hirono275bfbe2014-10-21 01:46:17272 web_contents_->GetSiteInstance()->GetSiteURL(),
273 files,
274 base::Bind(
hironocf350cb2014-10-23 08:37:18275 &FileSelectHelper::NotifyRenderViewHostAndEndAfterConversion,
hirono275bfbe2014-10-21 01:46:17276 this));
277 return;
278 }
279#endif // defined(OS_CHROMEOS)
280
281 std::vector<content::FileChooserFileInfo> chooser_files;
282 for (const auto& file : files) {
283 content::FileChooserFileInfo chooser_file;
284 chooser_file.file_path = file.local_path;
285 chooser_file.display_name = file.display_name;
286 chooser_files.push_back(chooser_file);
287 }
hironocf350cb2014-10-23 08:37:18288
289 NotifyRenderViewHostAndEndAfterConversion(chooser_files);
290}
291
292void FileSelectHelper::NotifyRenderViewHostAndEndAfterConversion(
293 const std::vector<content::FileChooserFileInfo>& list) {
294 if (render_view_host_)
295 render_view_host_->FilesSelectedInChooser(list, dialog_mode_);
erikchen22de64a32014-10-10 18:27:43296
297 // No members should be accessed from here on.
298 RunFileChooserEnd();
299}
300
301void FileSelectHelper::DeleteTemporaryFiles() {
302 BrowserThread::PostTask(BrowserThread::FILE,
303 FROM_HERE,
304 base::Bind(&DeleteFiles, temporary_files_));
305 temporary_files_.clear();
306}
307
lazyboy1cf60af2015-05-15 22:17:05308void FileSelectHelper::CleanUpOnRenderViewHostChange() {
309 if (!temporary_files_.empty()) {
310 DeleteTemporaryFiles();
311
312 // Now that the temporary files have been scheduled for deletion, there
313 // is no longer any reason to keep this instance around.
314 Release();
315 }
316}
317
[email protected]479cce782012-09-15 20:15:53318scoped_ptr<ui::SelectFileDialog::FileTypeInfo>
[email protected]92f54082012-07-31 01:43:14319FileSelectHelper::GetFileTypesFromAcceptType(
[email protected]d2065e062013-12-12 23:49:52320 const std::vector<base::string16>& accept_types) {
[email protected]479cce782012-09-15 20:15:53321 scoped_ptr<ui::SelectFileDialog::FileTypeInfo> base_file_type(
322 new ui::SelectFileDialog::FileTypeInfo());
[email protected]ba70d082010-09-10 16:54:49323 if (accept_types.empty())
[email protected]479cce782012-09-15 20:15:53324 return base_file_type.Pass();
[email protected]ba70d082010-09-10 16:54:49325
[email protected]ba70d082010-09-10 16:54:49326 // Create FileTypeInfo and pre-allocate for the first extension list.
[email protected]92f54082012-07-31 01:43:14327 scoped_ptr<ui::SelectFileDialog::FileTypeInfo> file_type(
[email protected]479cce782012-09-15 20:15:53328 new ui::SelectFileDialog::FileTypeInfo(*base_file_type));
[email protected]ba70d082010-09-10 16:54:49329 file_type->include_all_files = true;
330 file_type->extensions.resize(1);
[email protected]650b2d52013-02-10 03:41:45331 std::vector<base::FilePath::StringType>* extensions =
332 &file_type->extensions.back();
[email protected]ba70d082010-09-10 16:54:49333
[email protected]f9a4c41a2012-05-30 00:05:32334 // Find the corresponding extensions.
[email protected]ba70d082010-09-10 16:54:49335 int valid_type_count = 0;
336 int description_id = 0;
[email protected]3314c2b12011-11-02 08:05:46337 for (size_t i = 0; i < accept_types.size(); ++i) {
[email protected]74f778e2014-03-14 21:11:46338 std::string ascii_type = base::UTF16ToASCII(accept_types[i]);
[email protected]f9a4c41a2012-05-30 00:05:32339 if (!IsAcceptTypeValid(ascii_type))
340 continue;
[email protected]ba70d082010-09-10 16:54:49341
342 size_t old_extension_size = extensions->size();
[email protected]f9a4c41a2012-05-30 00:05:32343 if (ascii_type[0] == '.') {
344 // If the type starts with a period it is assumed to be a file extension
345 // so we just have to add it to the list.
[email protected]650b2d52013-02-10 03:41:45346 base::FilePath::StringType ext(ascii_type.begin(), ascii_type.end());
[email protected]f9a4c41a2012-05-30 00:05:32347 extensions->push_back(ext.substr(1));
[email protected]ba70d082010-09-10 16:54:49348 } else {
[email protected]4a66fa0e2012-09-10 06:45:20349 if (ascii_type == "image/*")
350 description_id = IDS_IMAGE_FILES;
351 else if (ascii_type == "audio/*")
352 description_id = IDS_AUDIO_FILES;
353 else if (ascii_type == "video/*")
354 description_id = IDS_VIDEO_FILES;
355
[email protected]f9a4c41a2012-05-30 00:05:32356 net::GetExtensionsForMimeType(ascii_type, extensions);
[email protected]ba70d082010-09-10 16:54:49357 }
358
359 if (extensions->size() > old_extension_size)
360 valid_type_count++;
361 }
362
[email protected]cbcd12ed2010-12-16 23:42:57363 // If no valid extension is added, bail out.
364 if (valid_type_count == 0)
[email protected]479cce782012-09-15 20:15:53365 return base_file_type.Pass();
[email protected]cbcd12ed2010-12-16 23:42:57366
[email protected]ba70d082010-09-10 16:54:49367 // Use a generic description "Custom Files" if either of the following is
368 // true:
369 // 1) There're multiple types specified, like "audio/*,video/*"
370 // 2) There're multiple extensions for a MIME type without parameter, like
371 // "ehtml,shtml,htm,html" for "text/html". On Windows, the select file
372 // dialog uses the first extension in the list to form the description,
373 // like "EHTML Files". This is not what we want.
374 if (valid_type_count > 1 ||
375 (valid_type_count == 1 && description_id == 0 && extensions->size() > 1))
376 description_id = IDS_CUSTOM_FILES;
377
378 if (description_id) {
379 file_type->extension_description_overrides.push_back(
380 l10n_util::GetStringUTF16(description_id));
381 }
382
[email protected]479cce782012-09-15 20:15:53383 return file_type.Pass();
[email protected]ba70d082010-09-10 16:54:49384}
385
[email protected]33f8ad52012-05-22 18:10:13386// static
387void FileSelectHelper::RunFileChooser(content::WebContents* tab,
388 const FileChooserParams& params) {
389 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
390 // FileSelectHelper will keep itself alive until it sends the result message.
391 scoped_refptr<FileSelectHelper> file_select_helper(
392 new FileSelectHelper(profile));
asanka1cbf49812015-11-03 18:28:15393 file_select_helper->RunFileChooser(
394 tab->GetRenderViewHost(), tab,
395 make_scoped_ptr(new content::FileChooserParams(params)));
[email protected]33f8ad52012-05-22 18:10:13396}
397
398// static
399void FileSelectHelper::EnumerateDirectory(content::WebContents* tab,
400 int request_id,
[email protected]650b2d52013-02-10 03:41:45401 const base::FilePath& path) {
[email protected]33f8ad52012-05-22 18:10:13402 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
403 // FileSelectHelper will keep itself alive until it sends the result message.
404 scoped_refptr<FileSelectHelper> file_select_helper(
405 new FileSelectHelper(profile));
406 file_select_helper->EnumerateDirectory(
407 request_id, tab->GetRenderViewHost(), path);
408}
409
410void FileSelectHelper::RunFileChooser(RenderViewHost* render_view_host,
411 content::WebContents* web_contents,
asanka1cbf49812015-11-03 18:28:15412 scoped_ptr<FileChooserParams> params) {
[email protected]ba70d082010-09-10 16:54:49413 DCHECK(!render_view_host_);
[email protected]ea049a02011-12-25 21:37:09414 DCHECK(!web_contents_);
asanka1cbf49812015-11-03 18:28:15415 DCHECK(params->default_file_name.empty() ||
416 params->mode == FileChooserParams::Save)
asankaee261f32015-10-27 21:04:28417 << "The default_file_name parameter should only be specified for Save "
418 "file choosers";
asanka1cbf49812015-11-03 18:28:15419 DCHECK(params->default_file_name == params->default_file_name.BaseName())
asankaee261f32015-10-27 21:04:28420 << "The default_file_name parameter should not contain path separators";
421
[email protected]ba70d082010-09-10 16:54:49422 render_view_host_ = render_view_host;
[email protected]ea049a02011-12-25 21:37:09423 web_contents_ = web_contents;
[email protected]ba70d082010-09-10 16:54:49424 notification_registrar_.RemoveAll();
lazyboy1cf60af2015-05-15 22:17:05425 content::WebContentsObserver::Observe(web_contents_);
[email protected]432115822011-07-10 15:52:27426 notification_registrar_.Add(
avif9ab5d942015-10-15 14:05:44427 this, content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
428 content::Source<RenderWidgetHost>(render_view_host_->GetWidget()));
[email protected]9f054aa12011-09-29 19:13:45429
430 BrowserThread::PostTask(
431 BrowserThread::FILE, FROM_HERE,
asanka1cbf49812015-11-03 18:28:15432 base::Bind(&FileSelectHelper::GetFileTypesOnFileThread, this,
433 base::Passed(&params)));
[email protected]9f054aa12011-09-29 19:13:45434
435 // Because this class returns notifications to the RenderViewHost, it is
436 // difficult for callers to know how long to keep a reference to this
437 // instance. We AddRef() here to keep the instance alive after we return
438 // to the caller, until the last callback is received from the file dialog.
439 // At that point, we must call RunFileChooserEnd().
440 AddRef();
441}
442
asanka1cbf49812015-11-03 18:28:15443void FileSelectHelper::GetFileTypesOnFileThread(
444 scoped_ptr<FileChooserParams> params) {
445 select_file_types_ = GetFileTypesFromAcceptType(params->accept_types);
446 select_file_types_->support_drive = !params->need_local_path;
[email protected]9f054aa12011-09-29 19:13:45447
448 BrowserThread::PostTask(
449 BrowserThread::UI, FROM_HERE,
asanka1cbf49812015-11-03 18:28:15450 base::Bind(&FileSelectHelper::GetSanitizedFilenameOnUIThread, this,
451 base::Passed(&params)));
[email protected]9f054aa12011-09-29 19:13:45452}
453
asanka1cbf49812015-11-03 18:28:15454void FileSelectHelper::GetSanitizedFilenameOnUIThread(
455 scoped_ptr<FileChooserParams> params) {
456 base::FilePath default_file_path = profile_->last_selected_directory().Append(
457 GetSanitizedFileName(params->default_file_name));
458
459#if defined(FULL_SAFE_BROWSING)
460 // Note that FileChooserParams::requestor is not considered a trusted field
461 // since it's provided by the renderer and not validated browserside.
462 if (params->mode == FileChooserParams::Save &&
463 !params->default_file_name.empty()) {
464 GURL requestor = params->requestor;
465 safe_browsing::CheckUnverifiedDownloadPolicy(
466 requestor, default_file_path,
467 base::Bind(&FileSelectHelper::ApplyUnverifiedDownloadPolicy, this,
468 default_file_path, base::Passed(&params)));
469 return;
470 }
471#endif
472
473 RunFileChooserOnUIThread(default_file_path, params.Pass());
474}
475
476#if defined(FULL_SAFE_BROWSING)
477void FileSelectHelper::ApplyUnverifiedDownloadPolicy(
478 const base::FilePath& default_path,
479 scoped_ptr<FileChooserParams> params,
480 safe_browsing::UnverifiedDownloadPolicy policy) {
481 DCHECK(params);
482 if (policy == safe_browsing::UnverifiedDownloadPolicy::DISALLOWED) {
483 NotifyRenderViewHostAndEnd(std::vector<ui::SelectedFileInfo>());
484 return;
485 }
486
487 RunFileChooserOnUIThread(default_path, params.Pass());
488}
489#endif
490
[email protected]9f054aa12011-09-29 19:13:45491void FileSelectHelper::RunFileChooserOnUIThread(
asanka1cbf49812015-11-03 18:28:15492 const base::FilePath& default_file_path,
493 scoped_ptr<FileChooserParams> params) {
494 DCHECK(params);
avi20e83192015-11-16 20:11:48495 if (!render_view_host_ || !web_contents_ || !IsValidProfile(profile_) ||
496 !render_view_host_->GetWidget()->GetView()) {
[email protected]b95b08d2011-12-15 20:23:16497 // If the renderer was destroyed before we started, just cancel the
498 // operation.
499 RunFileChooserEnd();
[email protected]9f054aa12011-09-29 19:13:45500 return;
[email protected]b95b08d2011-12-15 20:23:16501 }
[email protected]ba70d082010-09-10 16:54:49502
[email protected]92f54082012-07-31 01:43:14503 select_file_dialog_ = ui::SelectFileDialog::Create(
[email protected]6e1fcd12012-07-02 17:14:20504 this, new ChromeSelectFilePolicy(web_contents_));
dcheng319a21952014-08-26 22:52:40505 if (!select_file_dialog_.get())
[email protected]6910cb242014-03-20 14:33:01506 return;
[email protected]ba70d082010-09-10 16:54:49507
asanka1cbf49812015-11-03 18:28:15508 dialog_mode_ = params->mode;
509 switch (params->mode) {
[email protected]33f8ad52012-05-22 18:10:13510 case FileChooserParams::Open:
[email protected]92f54082012-07-31 01:43:14511 dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
[email protected]ba70d082010-09-10 16:54:49512 break;
[email protected]33f8ad52012-05-22 18:10:13513 case FileChooserParams::OpenMultiple:
[email protected]92f54082012-07-31 01:43:14514 dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
[email protected]ba70d082010-09-10 16:54:49515 break;
[email protected]6bedbef2013-07-31 06:33:49516 case FileChooserParams::UploadFolder:
517 dialog_type_ = ui::SelectFileDialog::SELECT_UPLOAD_FOLDER;
[email protected]ba70d082010-09-10 16:54:49518 break;
[email protected]33f8ad52012-05-22 18:10:13519 case FileChooserParams::Save:
[email protected]92f54082012-07-31 01:43:14520 dialog_type_ = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
[email protected]ba70d082010-09-10 16:54:49521 break;
522 default:
[email protected]92f54082012-07-31 01:43:14523 // Prevent warning.
524 dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
[email protected]ba70d082010-09-10 16:54:49525 NOTREACHED();
526 }
[email protected]4e9149a2012-08-15 20:43:59527
avif9ab5d942015-10-15 14:05:44528 gfx::NativeWindow owning_window = platform_util::GetTopLevel(
529 render_view_host_->GetWidget()->GetView()->GetNativeView());
[email protected]d9898912011-04-15 21:10:00530
[email protected]2d02a2002012-09-18 21:47:56531#if defined(OS_ANDROID)
532 // Android needs the original MIME types and an additional capture value.
[email protected]d2065e062013-12-12 23:49:52533 std::pair<std::vector<base::string16>, bool> accept_types =
asanka1cbf49812015-11-03 18:28:15534 std::make_pair(params->accept_types, params->capture);
[email protected]2d02a2002012-09-18 21:47:56535#endif
536
[email protected]9f054aa12011-09-29 19:13:45537 select_file_dialog_->SelectFile(
asanka1cbf49812015-11-03 18:28:15538 dialog_type_, params->title, default_file_path, select_file_types_.get(),
[email protected]007b3f82013-04-09 08:46:45539 select_file_types_.get() && !select_file_types_->extensions.empty()
540 ? 1
541 : 0, // 1-based index of default extension to show.
542 base::FilePath::StringType(),
[email protected]9f054aa12011-09-29 19:13:45543 owning_window,
[email protected]b8452fa2012-06-15 01:41:41544#if defined(OS_ANDROID)
[email protected]2d02a2002012-09-18 21:47:56545 &accept_types);
[email protected]b8452fa2012-06-15 01:41:41546#else
[email protected]9f054aa12011-09-29 19:13:45547 NULL);
[email protected]b8452fa2012-06-15 01:41:41548#endif
[email protected]9f054aa12011-09-29 19:13:45549
550 select_file_types_.reset();
551}
552
553// This method is called when we receive the last callback from the file
554// chooser dialog. Perform any cleanup and release the reference we added
555// in RunFileChooser().
556void FileSelectHelper::RunFileChooserEnd() {
erikchen22de64a32014-10-10 18:27:43557 // If there are temporary files, then this instance needs to stick around
558 // until web_contents_ is destroyed, so that this instance can delete the
559 // temporary files.
560 if (!temporary_files_.empty())
561 return;
562
[email protected]9f054aa12011-09-29 19:13:45563 render_view_host_ = NULL;
[email protected]ea049a02011-12-25 21:37:09564 web_contents_ = NULL;
[email protected]9f054aa12011-09-29 19:13:45565 Release();
[email protected]ba70d082010-09-10 16:54:49566}
567
[email protected]600ea402011-04-12 00:01:51568void FileSelectHelper::EnumerateDirectory(int request_id,
569 RenderViewHost* render_view_host,
[email protected]650b2d52013-02-10 03:41:45570 const base::FilePath& path) {
[email protected]9f054aa12011-09-29 19:13:45571
572 // Because this class returns notifications to the RenderViewHost, it is
573 // difficult for callers to know how long to keep a reference to this
574 // instance. We AddRef() here to keep the instance alive after we return
575 // to the caller, until the last callback is received from the enumeration
576 // code. At that point, we must call EnumerateDirectoryEnd().
577 AddRef();
[email protected]600ea402011-04-12 00:01:51578 StartNewEnumeration(path, request_id, render_view_host);
579}
580
[email protected]9f054aa12011-09-29 19:13:45581// This method is called when we receive the last callback from the enumeration
582// code. Perform any cleanup and release the reference we added in
583// EnumerateDirectory().
584void FileSelectHelper::EnumerateDirectoryEnd() {
585 Release();
586}
587
[email protected]432115822011-07-10 15:52:27588void FileSelectHelper::Observe(int type,
[email protected]6c2381d2011-10-19 02:52:53589 const content::NotificationSource& source,
590 const content::NotificationDetails& details) {
[email protected]9f054aa12011-09-29 19:13:45591 switch (type) {
592 case content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED: {
[email protected]6c2381d2011-10-19 02:52:53593 DCHECK(content::Source<RenderWidgetHost>(source).ptr() ==
avif9ab5d942015-10-15 14:05:44594 render_view_host_->GetWidget());
[email protected]9f054aa12011-09-29 19:13:45595 render_view_host_ = NULL;
596 break;
597 }
[email protected]9f054aa12011-09-29 19:13:45598 default:
599 NOTREACHED();
600 }
[email protected]ba70d082010-09-10 16:54:49601}
[email protected]f9a4c41a2012-05-30 00:05:32602
lazyboy1cf60af2015-05-15 22:17:05603void FileSelectHelper::RenderViewHostChanged(RenderViewHost* old_host,
604 RenderViewHost* new_host) {
605 CleanUpOnRenderViewHostChange();
606}
607
608void FileSelectHelper::WebContentsDestroyed() {
609 web_contents_ = nullptr;
610 CleanUpOnRenderViewHostChange();
611}
612
[email protected]f9a4c41a2012-05-30 00:05:32613// static
614bool FileSelectHelper::IsAcceptTypeValid(const std::string& accept_type) {
615 // TODO(raymes): This only does some basic checks, extend to test more cases.
616 // A 1 character accept type will always be invalid (either a "." in the case
617 // of an extension or a "/" in the case of a MIME type).
618 std::string unused;
619 if (accept_type.length() <= 1 ||
brettwfce8d192015-08-10 19:07:51620 base::ToLowerASCII(accept_type) != accept_type ||
[email protected]8af69c6c2014-03-03 19:05:31621 base::TrimWhitespaceASCII(accept_type, base::TRIM_ALL, &unused) !=
622 base::TRIM_NONE) {
[email protected]f9a4c41a2012-05-30 00:05:32623 return false;
624 }
625 return true;
626}
asankaee261f32015-10-27 21:04:28627
628// static
629base::FilePath FileSelectHelper::GetSanitizedFileName(
630 const base::FilePath& suggested_filename) {
631 if (suggested_filename.empty())
632 return base::FilePath();
633 return net::GenerateFileName(
634 GURL(), std::string(), std::string(), suggested_filename.AsUTF8Unsafe(),
635 std::string(), l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME));
636}