blob: 8dc8132a2affed270c7fe7ddf0eb5bd599d33f75 [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"
dcheng4af48582016-04-19 00:29:3515#include "base/memory/ptr_util.h"
[email protected]1988e1c2013-02-28 20:27:4216#include "base/strings/string_split.h"
[email protected]d8830562013-06-10 22:01:5417#include "base/strings/string_util.h"
[email protected]112158af2013-06-07 23:46:1818#include "base/strings/utf_string_conversions.h"
fdoray9e269762016-12-21 20:21:0719#include "base/task_scheduler/post_task.h"
avi6846aef2015-12-26 01:09:3820#include "build/build_config.h"
hironoa4a32af22014-11-07 03:17:2921#include "chrome/browser/browser_process.h"
[email protected]ba70d082010-09-10 16:54:4922#include "chrome/browser/platform_util.h"
[email protected]8ecad5e2010-12-02 21:18:3323#include "chrome/browser/profiles/profile.h"
hironoa4a32af22014-11-07 03:17:2924#include "chrome/browser/profiles/profile_manager.h"
[email protected]d9898912011-04-15 21:10:0025#include "chrome/browser/ui/browser.h"
26#include "chrome/browser/ui/browser_list.h"
[email protected]6e1fcd12012-07-02 17:14:2027#include "chrome/browser/ui/chrome_select_file_policy.h"
[email protected]af39f002014-08-22 10:18:1828#include "chrome/grit/generated_resources.h"
[email protected]6a1c98e02012-10-24 21:49:4329#include "content/public/browser/browser_thread.h"
[email protected]6c2381d2011-10-19 02:52:5330#include "content/public/browser/notification_details.h"
31#include "content/public/browser/notification_source.h"
[email protected]0d6e9bd2011-10-18 04:29:1632#include "content/public/browser/notification_types.h"
nasko35169af2016-06-10 18:47:1333#include "content/public/browser/render_frame_host.h"
34#include "content/public/browser/render_process_host.h"
[email protected]9c1662b2012-03-06 15:44:3335#include "content/public/browser/render_view_host.h"
avif9ab5d942015-10-15 14:05:4436#include "content/public/browser/render_widget_host.h"
[email protected]5626b0892012-02-20 14:46:5837#include "content/public/browser/render_widget_host_view.h"
rdevlin.croninbfbc11d92015-06-12 23:26:0838#include "content/public/browser/storage_partition.h"
[email protected]33f8ad52012-05-22 18:10:1339#include "content/public/browser/web_contents.h"
hirono570357bd2014-10-08 12:39:2740#include "content/public/common/file_chooser_file_info.h"
[email protected]8caadeb2011-11-22 02:45:2341#include "content/public/common/file_chooser_params.h"
asankaee261f32015-10-27 21:04:2842#include "net/base/filename_util.h"
[email protected]b3841c502011-03-09 01:21:3143#include "net/base/mime_util.h"
[email protected]c051a1b2011-01-21 23:30:1744#include "ui/base/l10n/l10n_util.h"
[email protected]4344a3c2013-01-17 23:49:2045#include "ui/shell_dialogs/selected_file_info.h"
[email protected]ba70d082010-09-10 16:54:4946
hirono275bfbe2014-10-21 01:46:1747#if defined(OS_CHROMEOS)
48#include "chrome/browser/chromeos/file_manager/fileapi_util.h"
49#include "content/public/browser/site_instance.h"
50#endif
51
asankaee261f32015-10-27 21:04:2852#if defined(FULL_SAFE_BROWSING)
asankaef1f38722016-05-20 16:17:4153#include "chrome/browser/safe_browsing/download_protection_service.h"
54#include "chrome/browser/safe_browsing/safe_browsing_service.h"
asankaee261f32015-10-27 21:04:2855#endif
56
[email protected]631bb742011-11-02 11:29:3957using content::BrowserThread;
[email protected]33f8ad52012-05-22 18:10:1358using content::FileChooserParams;
[email protected]eaabba22012-03-07 15:02:1159using content::RenderViewHost;
60using content::RenderWidgetHost;
[email protected]ea049a02011-12-25 21:37:0961using content::WebContents;
[email protected]631bb742011-11-02 11:29:3962
[email protected]600ea402011-04-12 00:01:5163namespace {
64
65// There is only one file-selection happening at any given time,
66// so we allocate an enumeration ID for that purpose. All IDs from
67// the renderer must start at 0 and increase.
[email protected]459fba82011-10-13 02:48:5068const int kFileSelectEnumerationId = -1;
69
[email protected]53f04c82012-07-26 02:31:0970// Converts a list of FilePaths to a list of ui::SelectedFileInfo.
71std::vector<ui::SelectedFileInfo> FilePathListToSelectedFileInfoList(
[email protected]650b2d52013-02-10 03:41:4572 const std::vector<base::FilePath>& paths) {
[email protected]ddb034b2012-06-26 20:31:3973 std::vector<ui::SelectedFileInfo> selected_files;
[email protected]fb11b6a42012-03-14 07:25:1274 for (size_t i = 0; i < paths.size(); ++i) {
75 selected_files.push_back(
[email protected]53f04c82012-07-26 02:31:0976 ui::SelectedFileInfo(paths[i], paths[i]));
[email protected]fb11b6a42012-03-14 07:25:1277 }
78 return selected_files;
[email protected]600ea402011-04-12 00:01:5179}
80
erikchen22de64a32014-10-10 18:27:4381void DeleteFiles(const std::vector<base::FilePath>& paths) {
82 for (auto& file_path : paths)
83 base::DeleteFile(file_path, false);
84}
85
hironoa4a32af22014-11-07 03:17:2986bool IsValidProfile(Profile* profile) {
87 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
jsbell0d496dc2016-10-28 22:23:5788 // No profile manager in unit tests.
89 if (!g_browser_process->profile_manager())
90 return true;
hironoa4a32af22014-11-07 03:17:2991 return g_browser_process->profile_manager()->IsValidProfile(profile);
92}
93
asankaef1f38722016-05-20 16:17:4194#if defined(FULL_SAFE_BROWSING)
95
96bool IsDownloadAllowedBySafeBrowsing(
97 safe_browsing::DownloadProtectionService::DownloadCheckResult result) {
98 using Result = safe_browsing::DownloadProtectionService::DownloadCheckResult;
99 switch (result) {
100 // Only allow downloads that are marked as SAFE or UNKNOWN by SafeBrowsing.
101 // All other types are going to be blocked. UNKNOWN could be the result of a
102 // failed safe browsing ping.
103 case Result::UNKNOWN:
104 case Result::SAFE:
105 return true;
106
107 case Result::DANGEROUS:
108 case Result::UNCOMMON:
109 case Result::DANGEROUS_HOST:
110 case Result::POTENTIALLY_UNWANTED:
111 return false;
112 }
113 NOTREACHED();
114 return false;
115}
116
117void InterpretSafeBrowsingVerdict(
118 const base::Callback<void(bool)>& recipient,
119 safe_browsing::DownloadProtectionService::DownloadCheckResult result) {
120 recipient.Run(IsDownloadAllowedBySafeBrowsing(result));
121}
122
123#endif
124
[email protected]fb11b6a42012-03-14 07:25:12125} // namespace
126
[email protected]485a5272011-04-12 00:49:29127struct FileSelectHelper::ActiveDirectoryEnumeration {
[email protected]d45f7512011-06-21 21:18:27128 ActiveDirectoryEnumeration() : rvh_(NULL) {}
[email protected]485a5272011-04-12 00:49:29129
dcheng4af48582016-04-19 00:29:35130 std::unique_ptr<DirectoryListerDispatchDelegate> delegate_;
131 std::unique_ptr<net::DirectoryLister> lister_;
[email protected]485a5272011-04-12 00:49:29132 RenderViewHost* rvh_;
[email protected]650b2d52013-02-10 03:41:45133 std::vector<base::FilePath> results_;
[email protected]485a5272011-04-12 00:49:29134};
135
[email protected]ba70d082010-09-10 16:54:49136FileSelectHelper::FileSelectHelper(Profile* profile)
137 : profile_(profile),
nasko35169af2016-06-10 18:47:13138 render_frame_host_(nullptr),
139 web_contents_(nullptr),
[email protected]ba70d082010-09-10 16:54:49140 select_file_dialog_(),
[email protected]9f054aa12011-09-29 19:13:45141 select_file_types_(),
[email protected]bfcf1e92013-07-11 04:37:25142 dialog_type_(ui::SelectFileDialog::SELECT_OPEN_FILE),
nasko35169af2016-06-10 18:47:13143 dialog_mode_(FileChooserParams::Open) {}
[email protected]ba70d082010-09-10 16:54:49144
145FileSelectHelper::~FileSelectHelper() {
146 // There may be pending file dialogs, we need to tell them that we've gone
147 // away so they don't try and call back to us.
148 if (select_file_dialog_.get())
149 select_file_dialog_->ListenerDestroyed();
150
[email protected]600ea402011-04-12 00:01:51151 // Stop any pending directory enumeration, prevent a callback, and free
152 // allocated memory.
153 std::map<int, ActiveDirectoryEnumeration*>::iterator iter;
154 for (iter = directory_enumerations_.begin();
155 iter != directory_enumerations_.end();
156 ++iter) {
[email protected]05a814182011-04-27 19:50:34157 iter->second->lister_.reset();
[email protected]600ea402011-04-12 00:01:51158 delete iter->second;
[email protected]ba70d082010-09-10 16:54:49159 }
160}
161
[email protected]23827ec2012-08-10 22:08:08162void FileSelectHelper::DirectoryListerDispatchDelegate::OnListFile(
163 const net::DirectoryLister::DirectoryListerData& data) {
164 parent_->OnListFile(id_, data);
165}
166
167void FileSelectHelper::DirectoryListerDispatchDelegate::OnListDone(int error) {
168 parent_->OnListDone(id_, error);
169}
170
[email protected]650b2d52013-02-10 03:41:45171void FileSelectHelper::FileSelected(const base::FilePath& path,
[email protected]ba70d082010-09-10 16:54:49172 int index, void* params) {
[email protected]53f04c82012-07-26 02:31:09173 FileSelectedWithExtraInfo(ui::SelectedFileInfo(path, path), index, params);
[email protected]fb11b6a42012-03-14 07:25:12174}
175
176void FileSelectHelper::FileSelectedWithExtraInfo(
[email protected]ddb034b2012-06-26 20:31:39177 const ui::SelectedFileInfo& file,
[email protected]fb11b6a42012-03-14 07:25:12178 int index,
179 void* params) {
jsbell0d496dc2016-10-28 22:23:57180 if (IsValidProfile(profile_)) {
181 base::FilePath path = file.file_path;
182 if (dialog_mode_ != FileChooserParams::UploadFolder)
183 path = path.DirName();
184 profile_->set_last_selected_directory(path);
185 }
[email protected]ba70d082010-09-10 16:54:49186
nasko35169af2016-06-10 18:47:13187 if (!render_frame_host_) {
erikchen22de64a32014-10-10 18:27:43188 RunFileChooserEnd();
189 return;
190 }
191
[email protected]650b2d52013-02-10 03:41:45192 const base::FilePath& path = file.local_path;
[email protected]6bedbef2013-07-31 06:33:49193 if (dialog_type_ == ui::SelectFileDialog::SELECT_UPLOAD_FOLDER) {
nasko35169af2016-06-10 18:47:13194 StartNewEnumeration(path, kFileSelectEnumerationId,
195 render_frame_host_->GetRenderViewHost());
[email protected]ba70d082010-09-10 16:54:49196 return;
197 }
198
[email protected]ddb034b2012-06-26 20:31:39199 std::vector<ui::SelectedFileInfo> files;
[email protected]fb11b6a42012-03-14 07:25:12200 files.push_back(file);
[email protected]9f054aa12011-09-29 19:13:45201
jam1c5a91492016-02-24 20:47:53202#if defined(OS_MACOSX)
erikchen22de64a32014-10-10 18:27:43203 content::BrowserThread::PostTask(
204 content::BrowserThread::FILE_USER_BLOCKING,
205 FROM_HERE,
206 base::Bind(&FileSelectHelper::ProcessSelectedFilesMac, this, files));
207#else
naskof6a80ac2016-06-29 02:37:05208 NotifyRenderFrameHostAndEnd(files);
jam1c5a91492016-02-24 20:47:53209#endif // defined(OS_MACOSX)
[email protected]ba70d082010-09-10 16:54:49210}
211
[email protected]650b2d52013-02-10 03:41:45212void FileSelectHelper::MultiFilesSelected(
213 const std::vector<base::FilePath>& files,
214 void* params) {
[email protected]ddb034b2012-06-26 20:31:39215 std::vector<ui::SelectedFileInfo> selected_files =
[email protected]53f04c82012-07-26 02:31:09216 FilePathListToSelectedFileInfoList(files);
217
[email protected]fb11b6a42012-03-14 07:25:12218 MultiFilesSelectedWithExtraInfo(selected_files, params);
219}
220
221void FileSelectHelper::MultiFilesSelectedWithExtraInfo(
[email protected]ddb034b2012-06-26 20:31:39222 const std::vector<ui::SelectedFileInfo>& files,
[email protected]fb11b6a42012-03-14 07:25:12223 void* params) {
jsbell0d496dc2016-10-28 22:23:57224 if (!files.empty() && IsValidProfile(profile_)) {
225 base::FilePath path = files[0].file_path;
226 if (dialog_mode_ != FileChooserParams::UploadFolder)
227 path = path.DirName();
228 profile_->set_last_selected_directory(path);
229 }
jam1c5a91492016-02-24 20:47:53230#if defined(OS_MACOSX)
erikchen22de64a32014-10-10 18:27:43231 content::BrowserThread::PostTask(
232 content::BrowserThread::FILE_USER_BLOCKING,
233 FROM_HERE,
234 base::Bind(&FileSelectHelper::ProcessSelectedFilesMac, this, files));
235#else
naskof6a80ac2016-06-29 02:37:05236 NotifyRenderFrameHostAndEnd(files);
jam1c5a91492016-02-24 20:47:53237#endif // defined(OS_MACOSX)
[email protected]ba70d082010-09-10 16:54:49238}
239
240void FileSelectHelper::FileSelectionCanceled(void* params) {
naskof6a80ac2016-06-29 02:37:05241 NotifyRenderFrameHostAndEnd(std::vector<ui::SelectedFileInfo>());
[email protected]ba70d082010-09-10 16:54:49242}
243
[email protected]650b2d52013-02-10 03:41:45244void FileSelectHelper::StartNewEnumeration(const base::FilePath& path,
[email protected]600ea402011-04-12 00:01:51245 int request_id,
246 RenderViewHost* render_view_host) {
dcheng4af48582016-04-19 00:29:35247 std::unique_ptr<ActiveDirectoryEnumeration> entry(
248 new ActiveDirectoryEnumeration);
[email protected]600ea402011-04-12 00:01:51249 entry->rvh_ = render_view_host;
250 entry->delegate_.reset(new DirectoryListerDispatchDelegate(this, request_id));
olli.syrjala3396ad32015-04-24 07:56:41251 entry->lister_.reset(new net::DirectoryLister(
252 path, net::DirectoryLister::NO_SORT_RECURSIVE, entry->delegate_.get()));
fdoray9e269762016-12-21 20:21:07253 if (!entry->lister_->Start(
254 base::CreateTaskRunnerWithTraits(
255 base::TaskTraits()
256 .WithShutdownBehavior(
257 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)
258 .MayBlock())
259 .get())) {
[email protected]600ea402011-04-12 00:01:51260 if (request_id == kFileSelectEnumerationId)
261 FileSelectionCanceled(NULL);
262 else
263 render_view_host->DirectoryEnumerationFinished(request_id,
264 entry->results_);
265 } else {
266 directory_enumerations_[request_id] = entry.release();
267 }
[email protected]ba70d082010-09-10 16:54:49268}
269
270void FileSelectHelper::OnListFile(
[email protected]600ea402011-04-12 00:01:51271 int id,
[email protected]ba70d082010-09-10 16:54:49272 const net::DirectoryLister::DirectoryListerData& data) {
[email protected]600ea402011-04-12 00:01:51273 ActiveDirectoryEnumeration* entry = directory_enumerations_[id];
274
[email protected]2c718a0d2014-03-24 02:42:22275 // Directory upload only cares about files.
[email protected]25a4c1c2013-06-08 04:53:36276 if (data.info.IsDirectory())
[email protected]2c718a0d2014-03-24 02:42:22277 return;
278
279 entry->results_.push_back(data.path);
[email protected]ba70d082010-09-10 16:54:49280}
281
[email protected]600ea402011-04-12 00:01:51282void FileSelectHelper::OnListDone(int id, int error) {
283 // This entry needs to be cleaned up when this function is done.
dcheng4af48582016-04-19 00:29:35284 std::unique_ptr<ActiveDirectoryEnumeration> entry(
285 directory_enumerations_[id]);
[email protected]600ea402011-04-12 00:01:51286 directory_enumerations_.erase(id);
287 if (!entry->rvh_)
[email protected]ba70d082010-09-10 16:54:49288 return;
[email protected]ba70d082010-09-10 16:54:49289 if (error) {
290 FileSelectionCanceled(NULL);
291 return;
292 }
[email protected]fb11b6a42012-03-14 07:25:12293
[email protected]ddb034b2012-06-26 20:31:39294 std::vector<ui::SelectedFileInfo> selected_files =
[email protected]53f04c82012-07-26 02:31:09295 FilePathListToSelectedFileInfoList(entry->results_);
[email protected]fb11b6a42012-03-14 07:25:12296
hirono275bfbe2014-10-21 01:46:17297 if (id == kFileSelectEnumerationId) {
naskof6a80ac2016-06-29 02:37:05298 NotifyRenderFrameHostAndEnd(selected_files);
hirono275bfbe2014-10-21 01:46:17299 } else {
[email protected]600ea402011-04-12 00:01:51300 entry->rvh_->DirectoryEnumerationFinished(id, entry->results_);
hirono275bfbe2014-10-21 01:46:17301 EnumerateDirectoryEnd();
302 }
[email protected]ba70d082010-09-10 16:54:49303}
304
naskof6a80ac2016-06-29 02:37:05305void FileSelectHelper::NotifyRenderFrameHostAndEnd(
erikchen22de64a32014-10-10 18:27:43306 const std::vector<ui::SelectedFileInfo>& files) {
nasko35169af2016-06-10 18:47:13307 if (!render_frame_host_) {
hirono275bfbe2014-10-21 01:46:17308 RunFileChooserEnd();
309 return;
310 }
311
312#if defined(OS_CHROMEOS)
313 if (!files.empty()) {
hironoa4a32af22014-11-07 03:17:29314 if (!IsValidProfile(profile_)) {
315 RunFileChooserEnd();
316 return;
317 }
hirono275bfbe2014-10-21 01:46:17318 // Converts |files| into FileChooserFileInfo with handling of non-native
319 // files.
nasko35169af2016-06-10 18:47:13320 content::SiteInstance* site_instance =
321 render_frame_host_->GetSiteInstance();
rdevlin.croninbfbc11d92015-06-12 23:26:08322 storage::FileSystemContext* file_system_context =
nasko35169af2016-06-10 18:47:13323 content::BrowserContext::GetStoragePartition(profile_, site_instance)
324 ->GetFileSystemContext();
hirono275bfbe2014-10-21 01:46:17325 file_manager::util::ConvertSelectedFileInfoListToFileChooserFileInfoList(
nasko35169af2016-06-10 18:47:13326 file_system_context, site_instance->GetSiteURL(), files,
naskof6a80ac2016-06-29 02:37:05327 base::Bind(
328 &FileSelectHelper::NotifyRenderFrameHostAndEndAfterConversion,
329 this));
hirono275bfbe2014-10-21 01:46:17330 return;
331 }
332#endif // defined(OS_CHROMEOS)
333
334 std::vector<content::FileChooserFileInfo> chooser_files;
335 for (const auto& file : files) {
336 content::FileChooserFileInfo chooser_file;
337 chooser_file.file_path = file.local_path;
338 chooser_file.display_name = file.display_name;
339 chooser_files.push_back(chooser_file);
340 }
hironocf350cb2014-10-23 08:37:18341
naskof6a80ac2016-06-29 02:37:05342 NotifyRenderFrameHostAndEndAfterConversion(chooser_files);
hironocf350cb2014-10-23 08:37:18343}
344
naskof6a80ac2016-06-29 02:37:05345void FileSelectHelper::NotifyRenderFrameHostAndEndAfterConversion(
hironocf350cb2014-10-23 08:37:18346 const std::vector<content::FileChooserFileInfo>& list) {
nasko35169af2016-06-10 18:47:13347 if (render_frame_host_)
348 render_frame_host_->FilesSelectedInChooser(list, dialog_mode_);
erikchen22de64a32014-10-10 18:27:43349
350 // No members should be accessed from here on.
351 RunFileChooserEnd();
352}
353
354void FileSelectHelper::DeleteTemporaryFiles() {
355 BrowserThread::PostTask(BrowserThread::FILE,
356 FROM_HERE,
357 base::Bind(&DeleteFiles, temporary_files_));
358 temporary_files_.clear();
359}
360
naskof6a80ac2016-06-29 02:37:05361void FileSelectHelper::CleanUp() {
lazyboy1cf60af2015-05-15 22:17:05362 if (!temporary_files_.empty()) {
363 DeleteTemporaryFiles();
364
365 // Now that the temporary files have been scheduled for deletion, there
366 // is no longer any reason to keep this instance around.
367 Release();
368 }
369}
370
dcheng4af48582016-04-19 00:29:35371std::unique_ptr<ui::SelectFileDialog::FileTypeInfo>
[email protected]92f54082012-07-31 01:43:14372FileSelectHelper::GetFileTypesFromAcceptType(
[email protected]d2065e062013-12-12 23:49:52373 const std::vector<base::string16>& accept_types) {
dcheng4af48582016-04-19 00:29:35374 std::unique_ptr<ui::SelectFileDialog::FileTypeInfo> base_file_type(
[email protected]479cce782012-09-15 20:15:53375 new ui::SelectFileDialog::FileTypeInfo());
[email protected]ba70d082010-09-10 16:54:49376 if (accept_types.empty())
dchenge73d8520c2015-12-27 01:19:09377 return base_file_type;
[email protected]ba70d082010-09-10 16:54:49378
[email protected]ba70d082010-09-10 16:54:49379 // Create FileTypeInfo and pre-allocate for the first extension list.
dcheng4af48582016-04-19 00:29:35380 std::unique_ptr<ui::SelectFileDialog::FileTypeInfo> file_type(
[email protected]479cce782012-09-15 20:15:53381 new ui::SelectFileDialog::FileTypeInfo(*base_file_type));
[email protected]ba70d082010-09-10 16:54:49382 file_type->include_all_files = true;
383 file_type->extensions.resize(1);
[email protected]650b2d52013-02-10 03:41:45384 std::vector<base::FilePath::StringType>* extensions =
385 &file_type->extensions.back();
[email protected]ba70d082010-09-10 16:54:49386
[email protected]f9a4c41a2012-05-30 00:05:32387 // Find the corresponding extensions.
[email protected]ba70d082010-09-10 16:54:49388 int valid_type_count = 0;
389 int description_id = 0;
[email protected]3314c2b12011-11-02 08:05:46390 for (size_t i = 0; i < accept_types.size(); ++i) {
[email protected]74f778e2014-03-14 21:11:46391 std::string ascii_type = base::UTF16ToASCII(accept_types[i]);
[email protected]f9a4c41a2012-05-30 00:05:32392 if (!IsAcceptTypeValid(ascii_type))
393 continue;
[email protected]ba70d082010-09-10 16:54:49394
395 size_t old_extension_size = extensions->size();
[email protected]f9a4c41a2012-05-30 00:05:32396 if (ascii_type[0] == '.') {
397 // If the type starts with a period it is assumed to be a file extension
398 // so we just have to add it to the list.
[email protected]650b2d52013-02-10 03:41:45399 base::FilePath::StringType ext(ascii_type.begin(), ascii_type.end());
[email protected]f9a4c41a2012-05-30 00:05:32400 extensions->push_back(ext.substr(1));
[email protected]ba70d082010-09-10 16:54:49401 } else {
[email protected]4a66fa0e2012-09-10 06:45:20402 if (ascii_type == "image/*")
403 description_id = IDS_IMAGE_FILES;
404 else if (ascii_type == "audio/*")
405 description_id = IDS_AUDIO_FILES;
406 else if (ascii_type == "video/*")
407 description_id = IDS_VIDEO_FILES;
408
[email protected]f9a4c41a2012-05-30 00:05:32409 net::GetExtensionsForMimeType(ascii_type, extensions);
[email protected]ba70d082010-09-10 16:54:49410 }
411
412 if (extensions->size() > old_extension_size)
413 valid_type_count++;
414 }
415
[email protected]cbcd12ed2010-12-16 23:42:57416 // If no valid extension is added, bail out.
417 if (valid_type_count == 0)
dchenge73d8520c2015-12-27 01:19:09418 return base_file_type;
[email protected]cbcd12ed2010-12-16 23:42:57419
[email protected]ba70d082010-09-10 16:54:49420 // Use a generic description "Custom Files" if either of the following is
421 // true:
422 // 1) There're multiple types specified, like "audio/*,video/*"
423 // 2) There're multiple extensions for a MIME type without parameter, like
424 // "ehtml,shtml,htm,html" for "text/html". On Windows, the select file
425 // dialog uses the first extension in the list to form the description,
426 // like "EHTML Files". This is not what we want.
427 if (valid_type_count > 1 ||
428 (valid_type_count == 1 && description_id == 0 && extensions->size() > 1))
429 description_id = IDS_CUSTOM_FILES;
430
431 if (description_id) {
432 file_type->extension_description_overrides.push_back(
433 l10n_util::GetStringUTF16(description_id));
434 }
435
dchenge73d8520c2015-12-27 01:19:09436 return file_type;
[email protected]ba70d082010-09-10 16:54:49437}
438
[email protected]33f8ad52012-05-22 18:10:13439// static
nasko35169af2016-06-10 18:47:13440void FileSelectHelper::RunFileChooser(
441 content::RenderFrameHost* render_frame_host,
442 const FileChooserParams& params) {
443 Profile* profile = Profile::FromBrowserContext(
444 render_frame_host->GetProcess()->GetBrowserContext());
[email protected]33f8ad52012-05-22 18:10:13445 // FileSelectHelper will keep itself alive until it sends the result message.
446 scoped_refptr<FileSelectHelper> file_select_helper(
447 new FileSelectHelper(profile));
asanka1cbf49812015-11-03 18:28:15448 file_select_helper->RunFileChooser(
ricea86fa1dd2016-09-13 05:59:45449 render_frame_host, base::MakeUnique<content::FileChooserParams>(params));
[email protected]33f8ad52012-05-22 18:10:13450}
451
452// static
453void FileSelectHelper::EnumerateDirectory(content::WebContents* tab,
454 int request_id,
[email protected]650b2d52013-02-10 03:41:45455 const base::FilePath& path) {
[email protected]33f8ad52012-05-22 18:10:13456 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
457 // FileSelectHelper will keep itself alive until it sends the result message.
458 scoped_refptr<FileSelectHelper> file_select_helper(
459 new FileSelectHelper(profile));
460 file_select_helper->EnumerateDirectory(
461 request_id, tab->GetRenderViewHost(), path);
462}
463
dcheng4af48582016-04-19 00:29:35464void FileSelectHelper::RunFileChooser(
nasko35169af2016-06-10 18:47:13465 content::RenderFrameHost* render_frame_host,
dcheng4af48582016-04-19 00:29:35466 std::unique_ptr<FileChooserParams> params) {
nasko35169af2016-06-10 18:47:13467 DCHECK(!render_frame_host_);
[email protected]ea049a02011-12-25 21:37:09468 DCHECK(!web_contents_);
asanka1cbf49812015-11-03 18:28:15469 DCHECK(params->default_file_name.empty() ||
470 params->mode == FileChooserParams::Save)
asankaee261f32015-10-27 21:04:28471 << "The default_file_name parameter should only be specified for Save "
472 "file choosers";
asanka1cbf49812015-11-03 18:28:15473 DCHECK(params->default_file_name == params->default_file_name.BaseName())
asankaee261f32015-10-27 21:04:28474 << "The default_file_name parameter should not contain path separators";
475
nasko35169af2016-06-10 18:47:13476 render_frame_host_ = render_frame_host;
477 web_contents_ = WebContents::FromRenderFrameHost(render_frame_host);
[email protected]ba70d082010-09-10 16:54:49478 notification_registrar_.RemoveAll();
lazyboy1cf60af2015-05-15 22:17:05479 content::WebContentsObserver::Observe(web_contents_);
[email protected]432115822011-07-10 15:52:27480 notification_registrar_.Add(
avif9ab5d942015-10-15 14:05:44481 this, content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
nasko35169af2016-06-10 18:47:13482 content::Source<RenderWidgetHost>(
483 render_frame_host_->GetRenderViewHost()->GetWidget()));
[email protected]9f054aa12011-09-29 19:13:45484
485 BrowserThread::PostTask(
486 BrowserThread::FILE, FROM_HERE,
asanka1cbf49812015-11-03 18:28:15487 base::Bind(&FileSelectHelper::GetFileTypesOnFileThread, this,
488 base::Passed(&params)));
[email protected]9f054aa12011-09-29 19:13:45489
490 // Because this class returns notifications to the RenderViewHost, it is
491 // difficult for callers to know how long to keep a reference to this
492 // instance. We AddRef() here to keep the instance alive after we return
493 // to the caller, until the last callback is received from the file dialog.
494 // At that point, we must call RunFileChooserEnd().
495 AddRef();
496}
497
asanka1cbf49812015-11-03 18:28:15498void FileSelectHelper::GetFileTypesOnFileThread(
dcheng4af48582016-04-19 00:29:35499 std::unique_ptr<FileChooserParams> params) {
asanka1cbf49812015-11-03 18:28:15500 select_file_types_ = GetFileTypesFromAcceptType(params->accept_types);
hirono4d4d3392016-02-04 05:03:50501 select_file_types_->allowed_paths =
502 params->need_local_path ? ui::SelectFileDialog::FileTypeInfo::NATIVE_PATH
503 : ui::SelectFileDialog::FileTypeInfo::ANY_PATH;
[email protected]9f054aa12011-09-29 19:13:45504
505 BrowserThread::PostTask(
506 BrowserThread::UI, FROM_HERE,
asanka1cbf49812015-11-03 18:28:15507 base::Bind(&FileSelectHelper::GetSanitizedFilenameOnUIThread, this,
508 base::Passed(&params)));
[email protected]9f054aa12011-09-29 19:13:45509}
510
asanka1cbf49812015-11-03 18:28:15511void FileSelectHelper::GetSanitizedFilenameOnUIThread(
dcheng4af48582016-04-19 00:29:35512 std::unique_ptr<FileChooserParams> params) {
asanka1cbf49812015-11-03 18:28:15513 base::FilePath default_file_path = profile_->last_selected_directory().Append(
514 GetSanitizedFileName(params->default_file_name));
asanka1cbf49812015-11-03 18:28:15515#if defined(FULL_SAFE_BROWSING)
asankaef1f38722016-05-20 16:17:41516 CheckDownloadRequestWithSafeBrowsing(default_file_path, std::move(params));
517#else
dchenge73d8520c2015-12-27 01:19:09518 RunFileChooserOnUIThread(default_file_path, std::move(params));
asankaef1f38722016-05-20 16:17:41519#endif
asanka1cbf49812015-11-03 18:28:15520}
521
522#if defined(FULL_SAFE_BROWSING)
asankaef1f38722016-05-20 16:17:41523void FileSelectHelper::CheckDownloadRequestWithSafeBrowsing(
524 const base::FilePath& default_file_path,
525 std::unique_ptr<FileChooserParams> params) {
526 safe_browsing::SafeBrowsingService* sb_service =
527 g_browser_process->safe_browsing_service();
528
529 if (!sb_service || !sb_service->download_protection_service() ||
530 !sb_service->download_protection_service()->enabled()) {
531 RunFileChooserOnUIThread(default_file_path, std::move(params));
asanka1cbf49812015-11-03 18:28:15532 return;
533 }
534
asankaef1f38722016-05-20 16:17:41535 std::vector<base::FilePath::StringType> alternate_extensions;
536 if (select_file_types_) {
537 for (const auto& extensions_list : select_file_types_->extensions) {
538 for (const auto& extension_in_list : extensions_list) {
539 base::FilePath::StringType extension =
540 default_file_path.ReplaceExtension(extension_in_list)
541 .FinalExtension();
542 alternate_extensions.push_back(extension);
543 }
544 }
545 }
546
547 GURL requestor_url = params->requestor;
548 sb_service->download_protection_service()->CheckPPAPIDownloadRequest(
jialiulaf664d302017-01-06 01:34:49549 requestor_url,
550 render_frame_host_? render_frame_host_->GetLastCommittedURL() : GURL(),
551 WebContents::FromRenderFrameHost(render_frame_host_),
552 default_file_path, alternate_extensions, profile_,
asankaef1f38722016-05-20 16:17:41553 base::Bind(&InterpretSafeBrowsingVerdict,
554 base::Bind(&FileSelectHelper::ProceedWithSafeBrowsingVerdict,
555 this, default_file_path, base::Passed(&params))));
556}
557
558void FileSelectHelper::ProceedWithSafeBrowsingVerdict(
559 const base::FilePath& default_file_path,
560 std::unique_ptr<content::FileChooserParams> params,
561 bool allowed_by_safe_browsing) {
562 if (!allowed_by_safe_browsing) {
naskof6a80ac2016-06-29 02:37:05563 NotifyRenderFrameHostAndEnd(std::vector<ui::SelectedFileInfo>());
asankaef1f38722016-05-20 16:17:41564 return;
565 }
566 RunFileChooserOnUIThread(default_file_path, std::move(params));
asanka1cbf49812015-11-03 18:28:15567}
568#endif
569
[email protected]9f054aa12011-09-29 19:13:45570void FileSelectHelper::RunFileChooserOnUIThread(
asanka1cbf49812015-11-03 18:28:15571 const base::FilePath& default_file_path,
dcheng4af48582016-04-19 00:29:35572 std::unique_ptr<FileChooserParams> params) {
asanka1cbf49812015-11-03 18:28:15573 DCHECK(params);
nasko35169af2016-06-10 18:47:13574 if (!render_frame_host_ || !web_contents_ || !IsValidProfile(profile_) ||
575 !web_contents_->GetNativeView()) {
[email protected]b95b08d2011-12-15 20:23:16576 // If the renderer was destroyed before we started, just cancel the
577 // operation.
578 RunFileChooserEnd();
[email protected]9f054aa12011-09-29 19:13:45579 return;
[email protected]b95b08d2011-12-15 20:23:16580 }
[email protected]ba70d082010-09-10 16:54:49581
[email protected]92f54082012-07-31 01:43:14582 select_file_dialog_ = ui::SelectFileDialog::Create(
[email protected]6e1fcd12012-07-02 17:14:20583 this, new ChromeSelectFilePolicy(web_contents_));
dcheng319a21952014-08-26 22:52:40584 if (!select_file_dialog_.get())
[email protected]6910cb242014-03-20 14:33:01585 return;
[email protected]ba70d082010-09-10 16:54:49586
asanka1cbf49812015-11-03 18:28:15587 dialog_mode_ = params->mode;
588 switch (params->mode) {
[email protected]33f8ad52012-05-22 18:10:13589 case FileChooserParams::Open:
[email protected]92f54082012-07-31 01:43:14590 dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
[email protected]ba70d082010-09-10 16:54:49591 break;
[email protected]33f8ad52012-05-22 18:10:13592 case FileChooserParams::OpenMultiple:
[email protected]92f54082012-07-31 01:43:14593 dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
[email protected]ba70d082010-09-10 16:54:49594 break;
[email protected]6bedbef2013-07-31 06:33:49595 case FileChooserParams::UploadFolder:
596 dialog_type_ = ui::SelectFileDialog::SELECT_UPLOAD_FOLDER;
[email protected]ba70d082010-09-10 16:54:49597 break;
[email protected]33f8ad52012-05-22 18:10:13598 case FileChooserParams::Save:
[email protected]92f54082012-07-31 01:43:14599 dialog_type_ = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
[email protected]ba70d082010-09-10 16:54:49600 break;
601 default:
[email protected]92f54082012-07-31 01:43:14602 // Prevent warning.
603 dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
[email protected]ba70d082010-09-10 16:54:49604 NOTREACHED();
605 }
[email protected]4e9149a2012-08-15 20:43:59606
nasko35169af2016-06-10 18:47:13607 gfx::NativeWindow owning_window =
608 platform_util::GetTopLevel(web_contents_->GetNativeView());
[email protected]d9898912011-04-15 21:10:00609
[email protected]2d02a2002012-09-18 21:47:56610#if defined(OS_ANDROID)
611 // Android needs the original MIME types and an additional capture value.
[email protected]d2065e062013-12-12 23:49:52612 std::pair<std::vector<base::string16>, bool> accept_types =
asanka1cbf49812015-11-03 18:28:15613 std::make_pair(params->accept_types, params->capture);
[email protected]2d02a2002012-09-18 21:47:56614#endif
615
[email protected]9f054aa12011-09-29 19:13:45616 select_file_dialog_->SelectFile(
asanka1cbf49812015-11-03 18:28:15617 dialog_type_, params->title, default_file_path, select_file_types_.get(),
[email protected]007b3f82013-04-09 08:46:45618 select_file_types_.get() && !select_file_types_->extensions.empty()
619 ? 1
620 : 0, // 1-based index of default extension to show.
621 base::FilePath::StringType(),
[email protected]9f054aa12011-09-29 19:13:45622 owning_window,
[email protected]b8452fa2012-06-15 01:41:41623#if defined(OS_ANDROID)
[email protected]2d02a2002012-09-18 21:47:56624 &accept_types);
[email protected]b8452fa2012-06-15 01:41:41625#else
[email protected]9f054aa12011-09-29 19:13:45626 NULL);
[email protected]b8452fa2012-06-15 01:41:41627#endif
[email protected]9f054aa12011-09-29 19:13:45628
629 select_file_types_.reset();
630}
631
632// This method is called when we receive the last callback from the file
633// chooser dialog. Perform any cleanup and release the reference we added
634// in RunFileChooser().
635void FileSelectHelper::RunFileChooserEnd() {
erikchen22de64a32014-10-10 18:27:43636 // If there are temporary files, then this instance needs to stick around
637 // until web_contents_ is destroyed, so that this instance can delete the
638 // temporary files.
639 if (!temporary_files_.empty())
640 return;
641
nasko35169af2016-06-10 18:47:13642 render_frame_host_ = nullptr;
643 web_contents_ = nullptr;
[email protected]9f054aa12011-09-29 19:13:45644 Release();
[email protected]ba70d082010-09-10 16:54:49645}
646
[email protected]600ea402011-04-12 00:01:51647void FileSelectHelper::EnumerateDirectory(int request_id,
648 RenderViewHost* render_view_host,
[email protected]650b2d52013-02-10 03:41:45649 const base::FilePath& path) {
[email protected]9f054aa12011-09-29 19:13:45650 // Because this class returns notifications to the RenderViewHost, it is
651 // difficult for callers to know how long to keep a reference to this
652 // instance. We AddRef() here to keep the instance alive after we return
653 // to the caller, until the last callback is received from the enumeration
654 // code. At that point, we must call EnumerateDirectoryEnd().
655 AddRef();
[email protected]600ea402011-04-12 00:01:51656 StartNewEnumeration(path, request_id, render_view_host);
657}
658
[email protected]9f054aa12011-09-29 19:13:45659// This method is called when we receive the last callback from the enumeration
660// code. Perform any cleanup and release the reference we added in
661// EnumerateDirectory().
662void FileSelectHelper::EnumerateDirectoryEnd() {
663 Release();
664}
665
[email protected]432115822011-07-10 15:52:27666void FileSelectHelper::Observe(int type,
[email protected]6c2381d2011-10-19 02:52:53667 const content::NotificationSource& source,
668 const content::NotificationDetails& details) {
thestig52835792016-06-13 21:08:09669 DCHECK_EQ(content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED, type);
thestig52835792016-06-13 21:08:09670 render_frame_host_ = nullptr;
[email protected]ba70d082010-09-10 16:54:49671}
[email protected]f9a4c41a2012-05-30 00:05:32672
naskof6a80ac2016-06-29 02:37:05673void FileSelectHelper::RenderFrameHostChanged(
674 content::RenderFrameHost* old_host,
675 content::RenderFrameHost* new_host) {
nasko5e61b75f2016-07-01 22:35:05676 if (old_host == render_frame_host_)
677 render_frame_host_ = nullptr;
naskof6a80ac2016-06-29 02:37:05678}
679
680void FileSelectHelper::RenderFrameDeleted(
681 content::RenderFrameHost* render_frame_host) {
nasko5e61b75f2016-07-01 22:35:05682 if (render_frame_host == render_frame_host_)
683 render_frame_host_ = nullptr;
lazyboy1cf60af2015-05-15 22:17:05684}
685
686void FileSelectHelper::WebContentsDestroyed() {
naskof6a80ac2016-06-29 02:37:05687 render_frame_host_ = nullptr;
lazyboy1cf60af2015-05-15 22:17:05688 web_contents_ = nullptr;
naskof6a80ac2016-06-29 02:37:05689 CleanUp();
lazyboy1cf60af2015-05-15 22:17:05690}
691
[email protected]f9a4c41a2012-05-30 00:05:32692// static
693bool FileSelectHelper::IsAcceptTypeValid(const std::string& accept_type) {
694 // TODO(raymes): This only does some basic checks, extend to test more cases.
695 // A 1 character accept type will always be invalid (either a "." in the case
696 // of an extension or a "/" in the case of a MIME type).
697 std::string unused;
698 if (accept_type.length() <= 1 ||
brettwfce8d192015-08-10 19:07:51699 base::ToLowerASCII(accept_type) != accept_type ||
[email protected]8af69c6c2014-03-03 19:05:31700 base::TrimWhitespaceASCII(accept_type, base::TRIM_ALL, &unused) !=
701 base::TRIM_NONE) {
[email protected]f9a4c41a2012-05-30 00:05:32702 return false;
703 }
704 return true;
705}
asankaee261f32015-10-27 21:04:28706
707// static
708base::FilePath FileSelectHelper::GetSanitizedFileName(
709 const base::FilePath& suggested_filename) {
710 if (suggested_filename.empty())
711 return base::FilePath();
712 return net::GenerateFileName(
713 GURL(), std::string(), std::string(), suggested_filename.AsUTF8Unsafe(),
714 std::string(), l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME));
715}