blob: 46c553886602bd4a2d376d238183f9fd18a02fe4 [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"
shahriar.rostami4d55d8312016-02-25 22:44:2819#include "base/threading/worker_pool.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);
88 return g_browser_process->profile_manager()->IsValidProfile(profile);
89}
90
asankaef1f38722016-05-20 16:17:4191#if defined(FULL_SAFE_BROWSING)
92
93bool IsDownloadAllowedBySafeBrowsing(
94 safe_browsing::DownloadProtectionService::DownloadCheckResult result) {
95 using Result = safe_browsing::DownloadProtectionService::DownloadCheckResult;
96 switch (result) {
97 // Only allow downloads that are marked as SAFE or UNKNOWN by SafeBrowsing.
98 // All other types are going to be blocked. UNKNOWN could be the result of a
99 // failed safe browsing ping.
100 case Result::UNKNOWN:
101 case Result::SAFE:
102 return true;
103
104 case Result::DANGEROUS:
105 case Result::UNCOMMON:
106 case Result::DANGEROUS_HOST:
107 case Result::POTENTIALLY_UNWANTED:
108 return false;
109 }
110 NOTREACHED();
111 return false;
112}
113
114void InterpretSafeBrowsingVerdict(
115 const base::Callback<void(bool)>& recipient,
116 safe_browsing::DownloadProtectionService::DownloadCheckResult result) {
117 recipient.Run(IsDownloadAllowedBySafeBrowsing(result));
118}
119
120#endif
121
[email protected]fb11b6a42012-03-14 07:25:12122} // namespace
123
[email protected]485a5272011-04-12 00:49:29124struct FileSelectHelper::ActiveDirectoryEnumeration {
[email protected]d45f7512011-06-21 21:18:27125 ActiveDirectoryEnumeration() : rvh_(NULL) {}
[email protected]485a5272011-04-12 00:49:29126
dcheng4af48582016-04-19 00:29:35127 std::unique_ptr<DirectoryListerDispatchDelegate> delegate_;
128 std::unique_ptr<net::DirectoryLister> lister_;
[email protected]485a5272011-04-12 00:49:29129 RenderViewHost* rvh_;
[email protected]650b2d52013-02-10 03:41:45130 std::vector<base::FilePath> results_;
[email protected]485a5272011-04-12 00:49:29131};
132
[email protected]ba70d082010-09-10 16:54:49133FileSelectHelper::FileSelectHelper(Profile* profile)
134 : profile_(profile),
nasko35169af2016-06-10 18:47:13135 render_frame_host_(nullptr),
136 web_contents_(nullptr),
[email protected]ba70d082010-09-10 16:54:49137 select_file_dialog_(),
[email protected]9f054aa12011-09-29 19:13:45138 select_file_types_(),
[email protected]bfcf1e92013-07-11 04:37:25139 dialog_type_(ui::SelectFileDialog::SELECT_OPEN_FILE),
nasko35169af2016-06-10 18:47:13140 dialog_mode_(FileChooserParams::Open) {}
[email protected]ba70d082010-09-10 16:54:49141
142FileSelectHelper::~FileSelectHelper() {
143 // There may be pending file dialogs, we need to tell them that we've gone
144 // away so they don't try and call back to us.
145 if (select_file_dialog_.get())
146 select_file_dialog_->ListenerDestroyed();
147
[email protected]600ea402011-04-12 00:01:51148 // Stop any pending directory enumeration, prevent a callback, and free
149 // allocated memory.
150 std::map<int, ActiveDirectoryEnumeration*>::iterator iter;
151 for (iter = directory_enumerations_.begin();
152 iter != directory_enumerations_.end();
153 ++iter) {
[email protected]05a814182011-04-27 19:50:34154 iter->second->lister_.reset();
[email protected]600ea402011-04-12 00:01:51155 delete iter->second;
[email protected]ba70d082010-09-10 16:54:49156 }
157}
158
[email protected]23827ec2012-08-10 22:08:08159void FileSelectHelper::DirectoryListerDispatchDelegate::OnListFile(
160 const net::DirectoryLister::DirectoryListerData& data) {
161 parent_->OnListFile(id_, data);
162}
163
164void FileSelectHelper::DirectoryListerDispatchDelegate::OnListDone(int error) {
165 parent_->OnListDone(id_, error);
166}
167
[email protected]650b2d52013-02-10 03:41:45168void FileSelectHelper::FileSelected(const base::FilePath& path,
[email protected]ba70d082010-09-10 16:54:49169 int index, void* params) {
[email protected]53f04c82012-07-26 02:31:09170 FileSelectedWithExtraInfo(ui::SelectedFileInfo(path, path), index, params);
[email protected]fb11b6a42012-03-14 07:25:12171}
172
173void FileSelectHelper::FileSelectedWithExtraInfo(
[email protected]ddb034b2012-06-26 20:31:39174 const ui::SelectedFileInfo& file,
[email protected]fb11b6a42012-03-14 07:25:12175 int index,
176 void* params) {
hironoa4a32af22014-11-07 03:17:29177 if (IsValidProfile(profile_))
178 profile_->set_last_selected_directory(file.file_path.DirName());
[email protected]ba70d082010-09-10 16:54:49179
nasko35169af2016-06-10 18:47:13180 if (!render_frame_host_) {
erikchen22de64a32014-10-10 18:27:43181 RunFileChooserEnd();
182 return;
183 }
184
[email protected]650b2d52013-02-10 03:41:45185 const base::FilePath& path = file.local_path;
[email protected]6bedbef2013-07-31 06:33:49186 if (dialog_type_ == ui::SelectFileDialog::SELECT_UPLOAD_FOLDER) {
nasko35169af2016-06-10 18:47:13187 StartNewEnumeration(path, kFileSelectEnumerationId,
188 render_frame_host_->GetRenderViewHost());
[email protected]ba70d082010-09-10 16:54:49189 return;
190 }
191
[email protected]ddb034b2012-06-26 20:31:39192 std::vector<ui::SelectedFileInfo> files;
[email protected]fb11b6a42012-03-14 07:25:12193 files.push_back(file);
[email protected]9f054aa12011-09-29 19:13:45194
jam1c5a91492016-02-24 20:47:53195#if defined(OS_MACOSX)
erikchen22de64a32014-10-10 18:27:43196 content::BrowserThread::PostTask(
197 content::BrowserThread::FILE_USER_BLOCKING,
198 FROM_HERE,
199 base::Bind(&FileSelectHelper::ProcessSelectedFilesMac, this, files));
200#else
naskof6a80ac2016-06-29 02:37:05201 NotifyRenderFrameHostAndEnd(files);
jam1c5a91492016-02-24 20:47:53202#endif // defined(OS_MACOSX)
[email protected]ba70d082010-09-10 16:54:49203}
204
[email protected]650b2d52013-02-10 03:41:45205void FileSelectHelper::MultiFilesSelected(
206 const std::vector<base::FilePath>& files,
207 void* params) {
[email protected]ddb034b2012-06-26 20:31:39208 std::vector<ui::SelectedFileInfo> selected_files =
[email protected]53f04c82012-07-26 02:31:09209 FilePathListToSelectedFileInfoList(files);
210
[email protected]fb11b6a42012-03-14 07:25:12211 MultiFilesSelectedWithExtraInfo(selected_files, params);
212}
213
214void FileSelectHelper::MultiFilesSelectedWithExtraInfo(
[email protected]ddb034b2012-06-26 20:31:39215 const std::vector<ui::SelectedFileInfo>& files,
[email protected]fb11b6a42012-03-14 07:25:12216 void* params) {
hironoa4a32af22014-11-07 03:17:29217 if (!files.empty() && IsValidProfile(profile_))
[email protected]53f04c82012-07-26 02:31:09218 profile_->set_last_selected_directory(files[0].file_path.DirName());
[email protected]ba70d082010-09-10 16:54:49219
jam1c5a91492016-02-24 20:47:53220#if defined(OS_MACOSX)
erikchen22de64a32014-10-10 18:27:43221 content::BrowserThread::PostTask(
222 content::BrowserThread::FILE_USER_BLOCKING,
223 FROM_HERE,
224 base::Bind(&FileSelectHelper::ProcessSelectedFilesMac, this, files));
225#else
naskof6a80ac2016-06-29 02:37:05226 NotifyRenderFrameHostAndEnd(files);
jam1c5a91492016-02-24 20:47:53227#endif // defined(OS_MACOSX)
[email protected]ba70d082010-09-10 16:54:49228}
229
230void FileSelectHelper::FileSelectionCanceled(void* params) {
naskof6a80ac2016-06-29 02:37:05231 NotifyRenderFrameHostAndEnd(std::vector<ui::SelectedFileInfo>());
[email protected]ba70d082010-09-10 16:54:49232}
233
[email protected]650b2d52013-02-10 03:41:45234void FileSelectHelper::StartNewEnumeration(const base::FilePath& path,
[email protected]600ea402011-04-12 00:01:51235 int request_id,
236 RenderViewHost* render_view_host) {
dcheng4af48582016-04-19 00:29:35237 std::unique_ptr<ActiveDirectoryEnumeration> entry(
238 new ActiveDirectoryEnumeration);
[email protected]600ea402011-04-12 00:01:51239 entry->rvh_ = render_view_host;
240 entry->delegate_.reset(new DirectoryListerDispatchDelegate(this, request_id));
olli.syrjala3396ad32015-04-24 07:56:41241 entry->lister_.reset(new net::DirectoryLister(
242 path, net::DirectoryLister::NO_SORT_RECURSIVE, entry->delegate_.get()));
shahriar.rostamia8c06daf2016-02-12 00:07:04243 if (!entry->lister_->Start(base::WorkerPool::GetTaskRunner(true).get())) {
[email protected]600ea402011-04-12 00:01:51244 if (request_id == kFileSelectEnumerationId)
245 FileSelectionCanceled(NULL);
246 else
247 render_view_host->DirectoryEnumerationFinished(request_id,
248 entry->results_);
249 } else {
250 directory_enumerations_[request_id] = entry.release();
251 }
[email protected]ba70d082010-09-10 16:54:49252}
253
254void FileSelectHelper::OnListFile(
[email protected]600ea402011-04-12 00:01:51255 int id,
[email protected]ba70d082010-09-10 16:54:49256 const net::DirectoryLister::DirectoryListerData& data) {
[email protected]600ea402011-04-12 00:01:51257 ActiveDirectoryEnumeration* entry = directory_enumerations_[id];
258
[email protected]2c718a0d2014-03-24 02:42:22259 // Directory upload only cares about files.
[email protected]25a4c1c2013-06-08 04:53:36260 if (data.info.IsDirectory())
[email protected]2c718a0d2014-03-24 02:42:22261 return;
262
263 entry->results_.push_back(data.path);
[email protected]ba70d082010-09-10 16:54:49264}
265
[email protected]600ea402011-04-12 00:01:51266void FileSelectHelper::OnListDone(int id, int error) {
267 // This entry needs to be cleaned up when this function is done.
dcheng4af48582016-04-19 00:29:35268 std::unique_ptr<ActiveDirectoryEnumeration> entry(
269 directory_enumerations_[id]);
[email protected]600ea402011-04-12 00:01:51270 directory_enumerations_.erase(id);
271 if (!entry->rvh_)
[email protected]ba70d082010-09-10 16:54:49272 return;
[email protected]ba70d082010-09-10 16:54:49273 if (error) {
274 FileSelectionCanceled(NULL);
275 return;
276 }
[email protected]fb11b6a42012-03-14 07:25:12277
[email protected]ddb034b2012-06-26 20:31:39278 std::vector<ui::SelectedFileInfo> selected_files =
[email protected]53f04c82012-07-26 02:31:09279 FilePathListToSelectedFileInfoList(entry->results_);
[email protected]fb11b6a42012-03-14 07:25:12280
hirono275bfbe2014-10-21 01:46:17281 if (id == kFileSelectEnumerationId) {
naskof6a80ac2016-06-29 02:37:05282 NotifyRenderFrameHostAndEnd(selected_files);
hirono275bfbe2014-10-21 01:46:17283 } else {
[email protected]600ea402011-04-12 00:01:51284 entry->rvh_->DirectoryEnumerationFinished(id, entry->results_);
hirono275bfbe2014-10-21 01:46:17285 EnumerateDirectoryEnd();
286 }
[email protected]ba70d082010-09-10 16:54:49287}
288
naskof6a80ac2016-06-29 02:37:05289void FileSelectHelper::NotifyRenderFrameHostAndEnd(
erikchen22de64a32014-10-10 18:27:43290 const std::vector<ui::SelectedFileInfo>& files) {
nasko35169af2016-06-10 18:47:13291 if (!render_frame_host_) {
hirono275bfbe2014-10-21 01:46:17292 RunFileChooserEnd();
293 return;
294 }
295
296#if defined(OS_CHROMEOS)
297 if (!files.empty()) {
hironoa4a32af22014-11-07 03:17:29298 if (!IsValidProfile(profile_)) {
299 RunFileChooserEnd();
300 return;
301 }
hirono275bfbe2014-10-21 01:46:17302 // Converts |files| into FileChooserFileInfo with handling of non-native
303 // files.
nasko35169af2016-06-10 18:47:13304 content::SiteInstance* site_instance =
305 render_frame_host_->GetSiteInstance();
rdevlin.croninbfbc11d92015-06-12 23:26:08306 storage::FileSystemContext* file_system_context =
nasko35169af2016-06-10 18:47:13307 content::BrowserContext::GetStoragePartition(profile_, site_instance)
308 ->GetFileSystemContext();
hirono275bfbe2014-10-21 01:46:17309 file_manager::util::ConvertSelectedFileInfoListToFileChooserFileInfoList(
nasko35169af2016-06-10 18:47:13310 file_system_context, site_instance->GetSiteURL(), files,
naskof6a80ac2016-06-29 02:37:05311 base::Bind(
312 &FileSelectHelper::NotifyRenderFrameHostAndEndAfterConversion,
313 this));
hirono275bfbe2014-10-21 01:46:17314 return;
315 }
316#endif // defined(OS_CHROMEOS)
317
318 std::vector<content::FileChooserFileInfo> chooser_files;
319 for (const auto& file : files) {
320 content::FileChooserFileInfo chooser_file;
321 chooser_file.file_path = file.local_path;
322 chooser_file.display_name = file.display_name;
323 chooser_files.push_back(chooser_file);
324 }
hironocf350cb2014-10-23 08:37:18325
naskof6a80ac2016-06-29 02:37:05326 NotifyRenderFrameHostAndEndAfterConversion(chooser_files);
hironocf350cb2014-10-23 08:37:18327}
328
naskof6a80ac2016-06-29 02:37:05329void FileSelectHelper::NotifyRenderFrameHostAndEndAfterConversion(
hironocf350cb2014-10-23 08:37:18330 const std::vector<content::FileChooserFileInfo>& list) {
nasko35169af2016-06-10 18:47:13331 if (render_frame_host_)
332 render_frame_host_->FilesSelectedInChooser(list, dialog_mode_);
erikchen22de64a32014-10-10 18:27:43333
334 // No members should be accessed from here on.
335 RunFileChooserEnd();
336}
337
338void FileSelectHelper::DeleteTemporaryFiles() {
339 BrowserThread::PostTask(BrowserThread::FILE,
340 FROM_HERE,
341 base::Bind(&DeleteFiles, temporary_files_));
342 temporary_files_.clear();
343}
344
naskof6a80ac2016-06-29 02:37:05345void FileSelectHelper::CleanUp() {
lazyboy1cf60af2015-05-15 22:17:05346 if (!temporary_files_.empty()) {
347 DeleteTemporaryFiles();
348
349 // Now that the temporary files have been scheduled for deletion, there
350 // is no longer any reason to keep this instance around.
351 Release();
352 }
353}
354
dcheng4af48582016-04-19 00:29:35355std::unique_ptr<ui::SelectFileDialog::FileTypeInfo>
[email protected]92f54082012-07-31 01:43:14356FileSelectHelper::GetFileTypesFromAcceptType(
[email protected]d2065e062013-12-12 23:49:52357 const std::vector<base::string16>& accept_types) {
dcheng4af48582016-04-19 00:29:35358 std::unique_ptr<ui::SelectFileDialog::FileTypeInfo> base_file_type(
[email protected]479cce782012-09-15 20:15:53359 new ui::SelectFileDialog::FileTypeInfo());
[email protected]ba70d082010-09-10 16:54:49360 if (accept_types.empty())
dchenge73d8520c2015-12-27 01:19:09361 return base_file_type;
[email protected]ba70d082010-09-10 16:54:49362
[email protected]ba70d082010-09-10 16:54:49363 // Create FileTypeInfo and pre-allocate for the first extension list.
dcheng4af48582016-04-19 00:29:35364 std::unique_ptr<ui::SelectFileDialog::FileTypeInfo> file_type(
[email protected]479cce782012-09-15 20:15:53365 new ui::SelectFileDialog::FileTypeInfo(*base_file_type));
[email protected]ba70d082010-09-10 16:54:49366 file_type->include_all_files = true;
367 file_type->extensions.resize(1);
[email protected]650b2d52013-02-10 03:41:45368 std::vector<base::FilePath::StringType>* extensions =
369 &file_type->extensions.back();
[email protected]ba70d082010-09-10 16:54:49370
[email protected]f9a4c41a2012-05-30 00:05:32371 // Find the corresponding extensions.
[email protected]ba70d082010-09-10 16:54:49372 int valid_type_count = 0;
373 int description_id = 0;
[email protected]3314c2b12011-11-02 08:05:46374 for (size_t i = 0; i < accept_types.size(); ++i) {
[email protected]74f778e2014-03-14 21:11:46375 std::string ascii_type = base::UTF16ToASCII(accept_types[i]);
[email protected]f9a4c41a2012-05-30 00:05:32376 if (!IsAcceptTypeValid(ascii_type))
377 continue;
[email protected]ba70d082010-09-10 16:54:49378
379 size_t old_extension_size = extensions->size();
[email protected]f9a4c41a2012-05-30 00:05:32380 if (ascii_type[0] == '.') {
381 // If the type starts with a period it is assumed to be a file extension
382 // so we just have to add it to the list.
[email protected]650b2d52013-02-10 03:41:45383 base::FilePath::StringType ext(ascii_type.begin(), ascii_type.end());
[email protected]f9a4c41a2012-05-30 00:05:32384 extensions->push_back(ext.substr(1));
[email protected]ba70d082010-09-10 16:54:49385 } else {
[email protected]4a66fa0e2012-09-10 06:45:20386 if (ascii_type == "image/*")
387 description_id = IDS_IMAGE_FILES;
388 else if (ascii_type == "audio/*")
389 description_id = IDS_AUDIO_FILES;
390 else if (ascii_type == "video/*")
391 description_id = IDS_VIDEO_FILES;
392
[email protected]f9a4c41a2012-05-30 00:05:32393 net::GetExtensionsForMimeType(ascii_type, extensions);
[email protected]ba70d082010-09-10 16:54:49394 }
395
396 if (extensions->size() > old_extension_size)
397 valid_type_count++;
398 }
399
[email protected]cbcd12ed2010-12-16 23:42:57400 // If no valid extension is added, bail out.
401 if (valid_type_count == 0)
dchenge73d8520c2015-12-27 01:19:09402 return base_file_type;
[email protected]cbcd12ed2010-12-16 23:42:57403
[email protected]ba70d082010-09-10 16:54:49404 // Use a generic description "Custom Files" if either of the following is
405 // true:
406 // 1) There're multiple types specified, like "audio/*,video/*"
407 // 2) There're multiple extensions for a MIME type without parameter, like
408 // "ehtml,shtml,htm,html" for "text/html". On Windows, the select file
409 // dialog uses the first extension in the list to form the description,
410 // like "EHTML Files". This is not what we want.
411 if (valid_type_count > 1 ||
412 (valid_type_count == 1 && description_id == 0 && extensions->size() > 1))
413 description_id = IDS_CUSTOM_FILES;
414
415 if (description_id) {
416 file_type->extension_description_overrides.push_back(
417 l10n_util::GetStringUTF16(description_id));
418 }
419
dchenge73d8520c2015-12-27 01:19:09420 return file_type;
[email protected]ba70d082010-09-10 16:54:49421}
422
[email protected]33f8ad52012-05-22 18:10:13423// static
nasko35169af2016-06-10 18:47:13424void FileSelectHelper::RunFileChooser(
425 content::RenderFrameHost* render_frame_host,
426 const FileChooserParams& params) {
427 Profile* profile = Profile::FromBrowserContext(
428 render_frame_host->GetProcess()->GetBrowserContext());
[email protected]33f8ad52012-05-22 18:10:13429 // FileSelectHelper will keep itself alive until it sends the result message.
430 scoped_refptr<FileSelectHelper> file_select_helper(
431 new FileSelectHelper(profile));
asanka1cbf49812015-11-03 18:28:15432 file_select_helper->RunFileChooser(
nasko35169af2016-06-10 18:47:13433 render_frame_host,
dcheng4af48582016-04-19 00:29:35434 base::WrapUnique(new content::FileChooserParams(params)));
[email protected]33f8ad52012-05-22 18:10:13435}
436
437// static
438void FileSelectHelper::EnumerateDirectory(content::WebContents* tab,
439 int request_id,
[email protected]650b2d52013-02-10 03:41:45440 const base::FilePath& path) {
[email protected]33f8ad52012-05-22 18:10:13441 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
442 // FileSelectHelper will keep itself alive until it sends the result message.
443 scoped_refptr<FileSelectHelper> file_select_helper(
444 new FileSelectHelper(profile));
445 file_select_helper->EnumerateDirectory(
446 request_id, tab->GetRenderViewHost(), path);
447}
448
dcheng4af48582016-04-19 00:29:35449void FileSelectHelper::RunFileChooser(
nasko35169af2016-06-10 18:47:13450 content::RenderFrameHost* render_frame_host,
dcheng4af48582016-04-19 00:29:35451 std::unique_ptr<FileChooserParams> params) {
nasko35169af2016-06-10 18:47:13452 DCHECK(!render_frame_host_);
[email protected]ea049a02011-12-25 21:37:09453 DCHECK(!web_contents_);
asanka1cbf49812015-11-03 18:28:15454 DCHECK(params->default_file_name.empty() ||
455 params->mode == FileChooserParams::Save)
asankaee261f32015-10-27 21:04:28456 << "The default_file_name parameter should only be specified for Save "
457 "file choosers";
asanka1cbf49812015-11-03 18:28:15458 DCHECK(params->default_file_name == params->default_file_name.BaseName())
asankaee261f32015-10-27 21:04:28459 << "The default_file_name parameter should not contain path separators";
460
nasko35169af2016-06-10 18:47:13461 render_frame_host_ = render_frame_host;
462 web_contents_ = WebContents::FromRenderFrameHost(render_frame_host);
[email protected]ba70d082010-09-10 16:54:49463 notification_registrar_.RemoveAll();
lazyboy1cf60af2015-05-15 22:17:05464 content::WebContentsObserver::Observe(web_contents_);
[email protected]432115822011-07-10 15:52:27465 notification_registrar_.Add(
avif9ab5d942015-10-15 14:05:44466 this, content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
nasko35169af2016-06-10 18:47:13467 content::Source<RenderWidgetHost>(
468 render_frame_host_->GetRenderViewHost()->GetWidget()));
[email protected]9f054aa12011-09-29 19:13:45469
470 BrowserThread::PostTask(
471 BrowserThread::FILE, FROM_HERE,
asanka1cbf49812015-11-03 18:28:15472 base::Bind(&FileSelectHelper::GetFileTypesOnFileThread, this,
473 base::Passed(&params)));
[email protected]9f054aa12011-09-29 19:13:45474
475 // Because this class returns notifications to the RenderViewHost, it is
476 // difficult for callers to know how long to keep a reference to this
477 // instance. We AddRef() here to keep the instance alive after we return
478 // to the caller, until the last callback is received from the file dialog.
479 // At that point, we must call RunFileChooserEnd().
480 AddRef();
481}
482
asanka1cbf49812015-11-03 18:28:15483void FileSelectHelper::GetFileTypesOnFileThread(
dcheng4af48582016-04-19 00:29:35484 std::unique_ptr<FileChooserParams> params) {
asanka1cbf49812015-11-03 18:28:15485 select_file_types_ = GetFileTypesFromAcceptType(params->accept_types);
hirono4d4d3392016-02-04 05:03:50486 select_file_types_->allowed_paths =
487 params->need_local_path ? ui::SelectFileDialog::FileTypeInfo::NATIVE_PATH
488 : ui::SelectFileDialog::FileTypeInfo::ANY_PATH;
[email protected]9f054aa12011-09-29 19:13:45489
490 BrowserThread::PostTask(
491 BrowserThread::UI, FROM_HERE,
asanka1cbf49812015-11-03 18:28:15492 base::Bind(&FileSelectHelper::GetSanitizedFilenameOnUIThread, this,
493 base::Passed(&params)));
[email protected]9f054aa12011-09-29 19:13:45494}
495
asanka1cbf49812015-11-03 18:28:15496void FileSelectHelper::GetSanitizedFilenameOnUIThread(
dcheng4af48582016-04-19 00:29:35497 std::unique_ptr<FileChooserParams> params) {
asanka1cbf49812015-11-03 18:28:15498 base::FilePath default_file_path = profile_->last_selected_directory().Append(
499 GetSanitizedFileName(params->default_file_name));
asanka1cbf49812015-11-03 18:28:15500#if defined(FULL_SAFE_BROWSING)
asankaef1f38722016-05-20 16:17:41501 CheckDownloadRequestWithSafeBrowsing(default_file_path, std::move(params));
502#else
dchenge73d8520c2015-12-27 01:19:09503 RunFileChooserOnUIThread(default_file_path, std::move(params));
asankaef1f38722016-05-20 16:17:41504#endif
asanka1cbf49812015-11-03 18:28:15505}
506
507#if defined(FULL_SAFE_BROWSING)
asankaef1f38722016-05-20 16:17:41508void FileSelectHelper::CheckDownloadRequestWithSafeBrowsing(
509 const base::FilePath& default_file_path,
510 std::unique_ptr<FileChooserParams> params) {
511 safe_browsing::SafeBrowsingService* sb_service =
512 g_browser_process->safe_browsing_service();
513
514 if (!sb_service || !sb_service->download_protection_service() ||
515 !sb_service->download_protection_service()->enabled()) {
516 RunFileChooserOnUIThread(default_file_path, std::move(params));
asanka1cbf49812015-11-03 18:28:15517 return;
518 }
519
asankaef1f38722016-05-20 16:17:41520 std::vector<base::FilePath::StringType> alternate_extensions;
521 if (select_file_types_) {
522 for (const auto& extensions_list : select_file_types_->extensions) {
523 for (const auto& extension_in_list : extensions_list) {
524 base::FilePath::StringType extension =
525 default_file_path.ReplaceExtension(extension_in_list)
526 .FinalExtension();
527 alternate_extensions.push_back(extension);
528 }
529 }
530 }
531
532 GURL requestor_url = params->requestor;
533 sb_service->download_protection_service()->CheckPPAPIDownloadRequest(
534 requestor_url, default_file_path, alternate_extensions,
535 base::Bind(&InterpretSafeBrowsingVerdict,
536 base::Bind(&FileSelectHelper::ProceedWithSafeBrowsingVerdict,
537 this, default_file_path, base::Passed(&params))));
538}
539
540void FileSelectHelper::ProceedWithSafeBrowsingVerdict(
541 const base::FilePath& default_file_path,
542 std::unique_ptr<content::FileChooserParams> params,
543 bool allowed_by_safe_browsing) {
544 if (!allowed_by_safe_browsing) {
naskof6a80ac2016-06-29 02:37:05545 NotifyRenderFrameHostAndEnd(std::vector<ui::SelectedFileInfo>());
asankaef1f38722016-05-20 16:17:41546 return;
547 }
548 RunFileChooserOnUIThread(default_file_path, std::move(params));
asanka1cbf49812015-11-03 18:28:15549}
550#endif
551
[email protected]9f054aa12011-09-29 19:13:45552void FileSelectHelper::RunFileChooserOnUIThread(
asanka1cbf49812015-11-03 18:28:15553 const base::FilePath& default_file_path,
dcheng4af48582016-04-19 00:29:35554 std::unique_ptr<FileChooserParams> params) {
asanka1cbf49812015-11-03 18:28:15555 DCHECK(params);
nasko35169af2016-06-10 18:47:13556 if (!render_frame_host_ || !web_contents_ || !IsValidProfile(profile_) ||
557 !web_contents_->GetNativeView()) {
[email protected]b95b08d2011-12-15 20:23:16558 // If the renderer was destroyed before we started, just cancel the
559 // operation.
560 RunFileChooserEnd();
[email protected]9f054aa12011-09-29 19:13:45561 return;
[email protected]b95b08d2011-12-15 20:23:16562 }
[email protected]ba70d082010-09-10 16:54:49563
[email protected]92f54082012-07-31 01:43:14564 select_file_dialog_ = ui::SelectFileDialog::Create(
[email protected]6e1fcd12012-07-02 17:14:20565 this, new ChromeSelectFilePolicy(web_contents_));
dcheng319a21952014-08-26 22:52:40566 if (!select_file_dialog_.get())
[email protected]6910cb242014-03-20 14:33:01567 return;
[email protected]ba70d082010-09-10 16:54:49568
asanka1cbf49812015-11-03 18:28:15569 dialog_mode_ = params->mode;
570 switch (params->mode) {
[email protected]33f8ad52012-05-22 18:10:13571 case FileChooserParams::Open:
[email protected]92f54082012-07-31 01:43:14572 dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
[email protected]ba70d082010-09-10 16:54:49573 break;
[email protected]33f8ad52012-05-22 18:10:13574 case FileChooserParams::OpenMultiple:
[email protected]92f54082012-07-31 01:43:14575 dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
[email protected]ba70d082010-09-10 16:54:49576 break;
[email protected]6bedbef2013-07-31 06:33:49577 case FileChooserParams::UploadFolder:
578 dialog_type_ = ui::SelectFileDialog::SELECT_UPLOAD_FOLDER;
[email protected]ba70d082010-09-10 16:54:49579 break;
[email protected]33f8ad52012-05-22 18:10:13580 case FileChooserParams::Save:
[email protected]92f54082012-07-31 01:43:14581 dialog_type_ = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
[email protected]ba70d082010-09-10 16:54:49582 break;
583 default:
[email protected]92f54082012-07-31 01:43:14584 // Prevent warning.
585 dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
[email protected]ba70d082010-09-10 16:54:49586 NOTREACHED();
587 }
[email protected]4e9149a2012-08-15 20:43:59588
nasko35169af2016-06-10 18:47:13589 gfx::NativeWindow owning_window =
590 platform_util::GetTopLevel(web_contents_->GetNativeView());
[email protected]d9898912011-04-15 21:10:00591
[email protected]2d02a2002012-09-18 21:47:56592#if defined(OS_ANDROID)
593 // Android needs the original MIME types and an additional capture value.
[email protected]d2065e062013-12-12 23:49:52594 std::pair<std::vector<base::string16>, bool> accept_types =
asanka1cbf49812015-11-03 18:28:15595 std::make_pair(params->accept_types, params->capture);
[email protected]2d02a2002012-09-18 21:47:56596#endif
597
[email protected]9f054aa12011-09-29 19:13:45598 select_file_dialog_->SelectFile(
asanka1cbf49812015-11-03 18:28:15599 dialog_type_, params->title, default_file_path, select_file_types_.get(),
[email protected]007b3f82013-04-09 08:46:45600 select_file_types_.get() && !select_file_types_->extensions.empty()
601 ? 1
602 : 0, // 1-based index of default extension to show.
603 base::FilePath::StringType(),
[email protected]9f054aa12011-09-29 19:13:45604 owning_window,
[email protected]b8452fa2012-06-15 01:41:41605#if defined(OS_ANDROID)
[email protected]2d02a2002012-09-18 21:47:56606 &accept_types);
[email protected]b8452fa2012-06-15 01:41:41607#else
[email protected]9f054aa12011-09-29 19:13:45608 NULL);
[email protected]b8452fa2012-06-15 01:41:41609#endif
[email protected]9f054aa12011-09-29 19:13:45610
611 select_file_types_.reset();
612}
613
614// This method is called when we receive the last callback from the file
615// chooser dialog. Perform any cleanup and release the reference we added
616// in RunFileChooser().
617void FileSelectHelper::RunFileChooserEnd() {
erikchen22de64a32014-10-10 18:27:43618 // If there are temporary files, then this instance needs to stick around
619 // until web_contents_ is destroyed, so that this instance can delete the
620 // temporary files.
621 if (!temporary_files_.empty())
622 return;
623
nasko35169af2016-06-10 18:47:13624 render_frame_host_ = nullptr;
625 web_contents_ = nullptr;
[email protected]9f054aa12011-09-29 19:13:45626 Release();
[email protected]ba70d082010-09-10 16:54:49627}
628
[email protected]600ea402011-04-12 00:01:51629void FileSelectHelper::EnumerateDirectory(int request_id,
630 RenderViewHost* render_view_host,
[email protected]650b2d52013-02-10 03:41:45631 const base::FilePath& path) {
[email protected]9f054aa12011-09-29 19:13:45632 // Because this class returns notifications to the RenderViewHost, it is
633 // difficult for callers to know how long to keep a reference to this
634 // instance. We AddRef() here to keep the instance alive after we return
635 // to the caller, until the last callback is received from the enumeration
636 // code. At that point, we must call EnumerateDirectoryEnd().
637 AddRef();
[email protected]600ea402011-04-12 00:01:51638 StartNewEnumeration(path, request_id, render_view_host);
639}
640
[email protected]9f054aa12011-09-29 19:13:45641// This method is called when we receive the last callback from the enumeration
642// code. Perform any cleanup and release the reference we added in
643// EnumerateDirectory().
644void FileSelectHelper::EnumerateDirectoryEnd() {
645 Release();
646}
647
[email protected]432115822011-07-10 15:52:27648void FileSelectHelper::Observe(int type,
[email protected]6c2381d2011-10-19 02:52:53649 const content::NotificationSource& source,
650 const content::NotificationDetails& details) {
thestig52835792016-06-13 21:08:09651 DCHECK_EQ(content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED, type);
thestig52835792016-06-13 21:08:09652 render_frame_host_ = nullptr;
[email protected]ba70d082010-09-10 16:54:49653}
[email protected]f9a4c41a2012-05-30 00:05:32654
naskof6a80ac2016-06-29 02:37:05655void FileSelectHelper::RenderFrameHostChanged(
656 content::RenderFrameHost* old_host,
657 content::RenderFrameHost* new_host) {
nasko5e61b75f2016-07-01 22:35:05658 if (old_host == render_frame_host_)
659 render_frame_host_ = nullptr;
naskof6a80ac2016-06-29 02:37:05660}
661
662void FileSelectHelper::RenderFrameDeleted(
663 content::RenderFrameHost* render_frame_host) {
nasko5e61b75f2016-07-01 22:35:05664 if (render_frame_host == render_frame_host_)
665 render_frame_host_ = nullptr;
lazyboy1cf60af2015-05-15 22:17:05666}
667
668void FileSelectHelper::WebContentsDestroyed() {
naskof6a80ac2016-06-29 02:37:05669 render_frame_host_ = nullptr;
lazyboy1cf60af2015-05-15 22:17:05670 web_contents_ = nullptr;
naskof6a80ac2016-06-29 02:37:05671 CleanUp();
lazyboy1cf60af2015-05-15 22:17:05672}
673
[email protected]f9a4c41a2012-05-30 00:05:32674// static
675bool FileSelectHelper::IsAcceptTypeValid(const std::string& accept_type) {
676 // TODO(raymes): This only does some basic checks, extend to test more cases.
677 // A 1 character accept type will always be invalid (either a "." in the case
678 // of an extension or a "/" in the case of a MIME type).
679 std::string unused;
680 if (accept_type.length() <= 1 ||
brettwfce8d192015-08-10 19:07:51681 base::ToLowerASCII(accept_type) != accept_type ||
[email protected]8af69c6c2014-03-03 19:05:31682 base::TrimWhitespaceASCII(accept_type, base::TRIM_ALL, &unused) !=
683 base::TRIM_NONE) {
[email protected]f9a4c41a2012-05-30 00:05:32684 return false;
685 }
686 return true;
687}
asankaee261f32015-10-27 21:04:28688
689// static
690base::FilePath FileSelectHelper::GetSanitizedFileName(
691 const base::FilePath& suggested_filename) {
692 if (suggested_filename.empty())
693 return base::FilePath();
694 return net::GenerateFileName(
695 GURL(), std::string(), std::string(), suggested_filename.AsUTF8Unsafe(),
696 std::string(), l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME));
697}