blob: bbfae1b59754632b77b6d69b04b514c4af6ecc0a [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
[email protected]5ac950b2010-12-09 21:34:257#include <string>
[email protected]b7b4beb2013-07-09 14:06:508#include <utility>
[email protected]5ac950b2010-12-09 21:34:259
[email protected]9f054aa12011-09-29 19:13:4510#include "base/bind.h"
[email protected]25a4c1c2013-06-08 04:53:3611#include "base/files/file_enumerator.h"
thestig18dfb7a52014-08-26 10:44:0412#include "base/files/file_util.h"
[email protected]1988e1c2013-02-28 20:27:4213#include "base/strings/string_split.h"
[email protected]d8830562013-06-10 22:01:5414#include "base/strings/string_util.h"
[email protected]112158af2013-06-07 23:46:1815#include "base/strings/utf_string_conversions.h"
hironoa4a32af22014-11-07 03:17:2916#include "chrome/browser/browser_process.h"
[email protected]ba70d082010-09-10 16:54:4917#include "chrome/browser/platform_util.h"
[email protected]8ecad5e2010-12-02 21:18:3318#include "chrome/browser/profiles/profile.h"
hironoa4a32af22014-11-07 03:17:2919#include "chrome/browser/profiles/profile_manager.h"
[email protected]d9898912011-04-15 21:10:0020#include "chrome/browser/ui/browser.h"
21#include "chrome/browser/ui/browser_list.h"
[email protected]6e1fcd12012-07-02 17:14:2022#include "chrome/browser/ui/chrome_select_file_policy.h"
[email protected]af39f002014-08-22 10:18:1823#include "chrome/grit/generated_resources.h"
[email protected]6a1c98e02012-10-24 21:49:4324#include "content/public/browser/browser_thread.h"
[email protected]6c2381d2011-10-19 02:52:5325#include "content/public/browser/notification_details.h"
26#include "content/public/browser/notification_source.h"
[email protected]0d6e9bd2011-10-18 04:29:1627#include "content/public/browser/notification_types.h"
[email protected]9c1662b2012-03-06 15:44:3328#include "content/public/browser/render_view_host.h"
[email protected]5626b0892012-02-20 14:46:5829#include "content/public/browser/render_widget_host_view.h"
rdevlin.croninbfbc11d92015-06-12 23:26:0830#include "content/public/browser/storage_partition.h"
[email protected]33f8ad52012-05-22 18:10:1331#include "content/public/browser/web_contents.h"
hirono570357bd2014-10-08 12:39:2732#include "content/public/common/file_chooser_file_info.h"
[email protected]8caadeb2011-11-22 02:45:2333#include "content/public/common/file_chooser_params.h"
[email protected]b3841c502011-03-09 01:21:3134#include "net/base/mime_util.h"
[email protected]c051a1b2011-01-21 23:30:1735#include "ui/base/l10n/l10n_util.h"
[email protected]4344a3c2013-01-17 23:49:2036#include "ui/shell_dialogs/selected_file_info.h"
[email protected]ba70d082010-09-10 16:54:4937
hirono275bfbe2014-10-21 01:46:1738#if defined(OS_CHROMEOS)
39#include "chrome/browser/chromeos/file_manager/fileapi_util.h"
40#include "content/public/browser/site_instance.h"
41#endif
42
[email protected]631bb742011-11-02 11:29:3943using content::BrowserThread;
[email protected]33f8ad52012-05-22 18:10:1344using content::FileChooserParams;
[email protected]eaabba22012-03-07 15:02:1145using content::RenderViewHost;
46using content::RenderWidgetHost;
[email protected]ea049a02011-12-25 21:37:0947using content::WebContents;
[email protected]631bb742011-11-02 11:29:3948
[email protected]600ea402011-04-12 00:01:5149namespace {
50
51// There is only one file-selection happening at any given time,
52// so we allocate an enumeration ID for that purpose. All IDs from
53// the renderer must start at 0 and increase.
[email protected]459fba82011-10-13 02:48:5054const int kFileSelectEnumerationId = -1;
55
[email protected]53f04c82012-07-26 02:31:0956// Converts a list of FilePaths to a list of ui::SelectedFileInfo.
57std::vector<ui::SelectedFileInfo> FilePathListToSelectedFileInfoList(
[email protected]650b2d52013-02-10 03:41:4558 const std::vector<base::FilePath>& paths) {
[email protected]ddb034b2012-06-26 20:31:3959 std::vector<ui::SelectedFileInfo> selected_files;
[email protected]fb11b6a42012-03-14 07:25:1260 for (size_t i = 0; i < paths.size(); ++i) {
61 selected_files.push_back(
[email protected]53f04c82012-07-26 02:31:0962 ui::SelectedFileInfo(paths[i], paths[i]));
[email protected]fb11b6a42012-03-14 07:25:1263 }
64 return selected_files;
[email protected]600ea402011-04-12 00:01:5165}
66
erikchen22de64a32014-10-10 18:27:4367void DeleteFiles(const std::vector<base::FilePath>& paths) {
68 for (auto& file_path : paths)
69 base::DeleteFile(file_path, false);
70}
71
hironoa4a32af22014-11-07 03:17:2972bool IsValidProfile(Profile* profile) {
73 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
74 return g_browser_process->profile_manager()->IsValidProfile(profile);
75}
76
[email protected]fb11b6a42012-03-14 07:25:1277} // namespace
78
[email protected]485a5272011-04-12 00:49:2979struct FileSelectHelper::ActiveDirectoryEnumeration {
[email protected]d45f7512011-06-21 21:18:2780 ActiveDirectoryEnumeration() : rvh_(NULL) {}
[email protected]485a5272011-04-12 00:49:2981
82 scoped_ptr<DirectoryListerDispatchDelegate> delegate_;
[email protected]05a814182011-04-27 19:50:3483 scoped_ptr<net::DirectoryLister> lister_;
[email protected]485a5272011-04-12 00:49:2984 RenderViewHost* rvh_;
[email protected]650b2d52013-02-10 03:41:4585 std::vector<base::FilePath> results_;
[email protected]485a5272011-04-12 00:49:2986};
87
[email protected]ba70d082010-09-10 16:54:4988FileSelectHelper::FileSelectHelper(Profile* profile)
89 : profile_(profile),
90 render_view_host_(NULL),
[email protected]ea049a02011-12-25 21:37:0991 web_contents_(NULL),
[email protected]ba70d082010-09-10 16:54:4992 select_file_dialog_(),
[email protected]9f054aa12011-09-29 19:13:4593 select_file_types_(),
[email protected]bfcf1e92013-07-11 04:37:2594 dialog_type_(ui::SelectFileDialog::SELECT_OPEN_FILE),
95 dialog_mode_(FileChooserParams::Open) {
[email protected]ba70d082010-09-10 16:54:4996}
97
98FileSelectHelper::~FileSelectHelper() {
99 // There may be pending file dialogs, we need to tell them that we've gone
100 // away so they don't try and call back to us.
101 if (select_file_dialog_.get())
102 select_file_dialog_->ListenerDestroyed();
103
[email protected]600ea402011-04-12 00:01:51104 // Stop any pending directory enumeration, prevent a callback, and free
105 // allocated memory.
106 std::map<int, ActiveDirectoryEnumeration*>::iterator iter;
107 for (iter = directory_enumerations_.begin();
108 iter != directory_enumerations_.end();
109 ++iter) {
[email protected]05a814182011-04-27 19:50:34110 iter->second->lister_.reset();
[email protected]600ea402011-04-12 00:01:51111 delete iter->second;
[email protected]ba70d082010-09-10 16:54:49112 }
113}
114
[email protected]23827ec2012-08-10 22:08:08115void FileSelectHelper::DirectoryListerDispatchDelegate::OnListFile(
116 const net::DirectoryLister::DirectoryListerData& data) {
117 parent_->OnListFile(id_, data);
118}
119
120void FileSelectHelper::DirectoryListerDispatchDelegate::OnListDone(int error) {
121 parent_->OnListDone(id_, error);
122}
123
[email protected]650b2d52013-02-10 03:41:45124void FileSelectHelper::FileSelected(const base::FilePath& path,
[email protected]ba70d082010-09-10 16:54:49125 int index, void* params) {
[email protected]53f04c82012-07-26 02:31:09126 FileSelectedWithExtraInfo(ui::SelectedFileInfo(path, path), index, params);
[email protected]fb11b6a42012-03-14 07:25:12127}
128
129void FileSelectHelper::FileSelectedWithExtraInfo(
[email protected]ddb034b2012-06-26 20:31:39130 const ui::SelectedFileInfo& file,
[email protected]fb11b6a42012-03-14 07:25:12131 int index,
132 void* params) {
hironoa4a32af22014-11-07 03:17:29133 if (IsValidProfile(profile_))
134 profile_->set_last_selected_directory(file.file_path.DirName());
[email protected]ba70d082010-09-10 16:54:49135
erikchen22de64a32014-10-10 18:27:43136 if (!render_view_host_) {
137 RunFileChooserEnd();
138 return;
139 }
140
[email protected]650b2d52013-02-10 03:41:45141 const base::FilePath& path = file.local_path;
[email protected]6bedbef2013-07-31 06:33:49142 if (dialog_type_ == ui::SelectFileDialog::SELECT_UPLOAD_FOLDER) {
[email protected]600ea402011-04-12 00:01:51143 StartNewEnumeration(path, kFileSelectEnumerationId, render_view_host_);
[email protected]ba70d082010-09-10 16:54:49144 return;
145 }
146
[email protected]ddb034b2012-06-26 20:31:39147 std::vector<ui::SelectedFileInfo> files;
[email protected]fb11b6a42012-03-14 07:25:12148 files.push_back(file);
[email protected]9f054aa12011-09-29 19:13:45149
erikchen22de64a32014-10-10 18:27:43150#if defined(OS_MACOSX) && !defined(OS_IOS)
151 content::BrowserThread::PostTask(
152 content::BrowserThread::FILE_USER_BLOCKING,
153 FROM_HERE,
154 base::Bind(&FileSelectHelper::ProcessSelectedFilesMac, this, files));
155#else
156 NotifyRenderViewHostAndEnd(files);
157#endif // defined(OS_MACOSX) && !defined(OS_IOS)
[email protected]ba70d082010-09-10 16:54:49158}
159
[email protected]650b2d52013-02-10 03:41:45160void FileSelectHelper::MultiFilesSelected(
161 const std::vector<base::FilePath>& files,
162 void* params) {
[email protected]ddb034b2012-06-26 20:31:39163 std::vector<ui::SelectedFileInfo> selected_files =
[email protected]53f04c82012-07-26 02:31:09164 FilePathListToSelectedFileInfoList(files);
165
[email protected]fb11b6a42012-03-14 07:25:12166 MultiFilesSelectedWithExtraInfo(selected_files, params);
167}
168
169void FileSelectHelper::MultiFilesSelectedWithExtraInfo(
[email protected]ddb034b2012-06-26 20:31:39170 const std::vector<ui::SelectedFileInfo>& files,
[email protected]fb11b6a42012-03-14 07:25:12171 void* params) {
hironoa4a32af22014-11-07 03:17:29172 if (!files.empty() && IsValidProfile(profile_))
[email protected]53f04c82012-07-26 02:31:09173 profile_->set_last_selected_directory(files[0].file_path.DirName());
[email protected]ba70d082010-09-10 16:54:49174
erikchen22de64a32014-10-10 18:27:43175#if defined(OS_MACOSX) && !defined(OS_IOS)
176 content::BrowserThread::PostTask(
177 content::BrowserThread::FILE_USER_BLOCKING,
178 FROM_HERE,
179 base::Bind(&FileSelectHelper::ProcessSelectedFilesMac, this, files));
180#else
181 NotifyRenderViewHostAndEnd(files);
182#endif // defined(OS_MACOSX) && !defined(OS_IOS)
[email protected]ba70d082010-09-10 16:54:49183}
184
185void FileSelectHelper::FileSelectionCanceled(void* params) {
erikchen22de64a32014-10-10 18:27:43186 NotifyRenderViewHostAndEnd(std::vector<ui::SelectedFileInfo>());
[email protected]ba70d082010-09-10 16:54:49187}
188
[email protected]650b2d52013-02-10 03:41:45189void FileSelectHelper::StartNewEnumeration(const base::FilePath& path,
[email protected]600ea402011-04-12 00:01:51190 int request_id,
191 RenderViewHost* render_view_host) {
192 scoped_ptr<ActiveDirectoryEnumeration> entry(new ActiveDirectoryEnumeration);
193 entry->rvh_ = render_view_host;
194 entry->delegate_.reset(new DirectoryListerDispatchDelegate(this, request_id));
olli.syrjala3396ad32015-04-24 07:56:41195 entry->lister_.reset(new net::DirectoryLister(
196 path, net::DirectoryLister::NO_SORT_RECURSIVE, entry->delegate_.get()));
[email protected]600ea402011-04-12 00:01:51197 if (!entry->lister_->Start()) {
198 if (request_id == kFileSelectEnumerationId)
199 FileSelectionCanceled(NULL);
200 else
201 render_view_host->DirectoryEnumerationFinished(request_id,
202 entry->results_);
203 } else {
204 directory_enumerations_[request_id] = entry.release();
205 }
[email protected]ba70d082010-09-10 16:54:49206}
207
208void FileSelectHelper::OnListFile(
[email protected]600ea402011-04-12 00:01:51209 int id,
[email protected]ba70d082010-09-10 16:54:49210 const net::DirectoryLister::DirectoryListerData& data) {
[email protected]600ea402011-04-12 00:01:51211 ActiveDirectoryEnumeration* entry = directory_enumerations_[id];
212
[email protected]2c718a0d2014-03-24 02:42:22213 // Directory upload only cares about files.
[email protected]25a4c1c2013-06-08 04:53:36214 if (data.info.IsDirectory())
[email protected]2c718a0d2014-03-24 02:42:22215 return;
216
217 entry->results_.push_back(data.path);
[email protected]ba70d082010-09-10 16:54:49218}
219
[email protected]600ea402011-04-12 00:01:51220void FileSelectHelper::OnListDone(int id, int error) {
221 // This entry needs to be cleaned up when this function is done.
222 scoped_ptr<ActiveDirectoryEnumeration> entry(directory_enumerations_[id]);
223 directory_enumerations_.erase(id);
224 if (!entry->rvh_)
[email protected]ba70d082010-09-10 16:54:49225 return;
[email protected]ba70d082010-09-10 16:54:49226 if (error) {
227 FileSelectionCanceled(NULL);
228 return;
229 }
[email protected]fb11b6a42012-03-14 07:25:12230
[email protected]ddb034b2012-06-26 20:31:39231 std::vector<ui::SelectedFileInfo> selected_files =
[email protected]53f04c82012-07-26 02:31:09232 FilePathListToSelectedFileInfoList(entry->results_);
[email protected]fb11b6a42012-03-14 07:25:12233
hirono275bfbe2014-10-21 01:46:17234 if (id == kFileSelectEnumerationId) {
235 NotifyRenderViewHostAndEnd(selected_files);
236 } else {
[email protected]600ea402011-04-12 00:01:51237 entry->rvh_->DirectoryEnumerationFinished(id, entry->results_);
hirono275bfbe2014-10-21 01:46:17238 EnumerateDirectoryEnd();
239 }
[email protected]ba70d082010-09-10 16:54:49240}
241
erikchen22de64a32014-10-10 18:27:43242void FileSelectHelper::NotifyRenderViewHostAndEnd(
243 const std::vector<ui::SelectedFileInfo>& files) {
hirono275bfbe2014-10-21 01:46:17244 if (!render_view_host_) {
245 RunFileChooserEnd();
246 return;
247 }
248
249#if defined(OS_CHROMEOS)
250 if (!files.empty()) {
hironoa4a32af22014-11-07 03:17:29251 if (!IsValidProfile(profile_)) {
252 RunFileChooserEnd();
253 return;
254 }
hirono275bfbe2014-10-21 01:46:17255 // Converts |files| into FileChooserFileInfo with handling of non-native
256 // files.
rdevlin.croninbfbc11d92015-06-12 23:26:08257 storage::FileSystemContext* file_system_context =
258 content::BrowserContext::GetStoragePartition(
259 profile_, render_view_host_->GetSiteInstance())->
260 GetFileSystemContext();
hirono275bfbe2014-10-21 01:46:17261 file_manager::util::ConvertSelectedFileInfoListToFileChooserFileInfoList(
rdevlin.croninbfbc11d92015-06-12 23:26:08262 file_system_context,
hirono275bfbe2014-10-21 01:46:17263 web_contents_->GetSiteInstance()->GetSiteURL(),
264 files,
265 base::Bind(
hironocf350cb2014-10-23 08:37:18266 &FileSelectHelper::NotifyRenderViewHostAndEndAfterConversion,
hirono275bfbe2014-10-21 01:46:17267 this));
268 return;
269 }
270#endif // defined(OS_CHROMEOS)
271
272 std::vector<content::FileChooserFileInfo> chooser_files;
273 for (const auto& file : files) {
274 content::FileChooserFileInfo chooser_file;
275 chooser_file.file_path = file.local_path;
276 chooser_file.display_name = file.display_name;
277 chooser_files.push_back(chooser_file);
278 }
hironocf350cb2014-10-23 08:37:18279
280 NotifyRenderViewHostAndEndAfterConversion(chooser_files);
281}
282
283void FileSelectHelper::NotifyRenderViewHostAndEndAfterConversion(
284 const std::vector<content::FileChooserFileInfo>& list) {
285 if (render_view_host_)
286 render_view_host_->FilesSelectedInChooser(list, dialog_mode_);
erikchen22de64a32014-10-10 18:27:43287
288 // No members should be accessed from here on.
289 RunFileChooserEnd();
290}
291
292void FileSelectHelper::DeleteTemporaryFiles() {
293 BrowserThread::PostTask(BrowserThread::FILE,
294 FROM_HERE,
295 base::Bind(&DeleteFiles, temporary_files_));
296 temporary_files_.clear();
297}
298
lazyboy1cf60af2015-05-15 22:17:05299void FileSelectHelper::CleanUpOnRenderViewHostChange() {
300 if (!temporary_files_.empty()) {
301 DeleteTemporaryFiles();
302
303 // Now that the temporary files have been scheduled for deletion, there
304 // is no longer any reason to keep this instance around.
305 Release();
306 }
307}
308
[email protected]479cce782012-09-15 20:15:53309scoped_ptr<ui::SelectFileDialog::FileTypeInfo>
[email protected]92f54082012-07-31 01:43:14310FileSelectHelper::GetFileTypesFromAcceptType(
[email protected]d2065e062013-12-12 23:49:52311 const std::vector<base::string16>& accept_types) {
[email protected]479cce782012-09-15 20:15:53312 scoped_ptr<ui::SelectFileDialog::FileTypeInfo> base_file_type(
313 new ui::SelectFileDialog::FileTypeInfo());
[email protected]ba70d082010-09-10 16:54:49314 if (accept_types.empty())
[email protected]479cce782012-09-15 20:15:53315 return base_file_type.Pass();
[email protected]ba70d082010-09-10 16:54:49316
[email protected]ba70d082010-09-10 16:54:49317 // Create FileTypeInfo and pre-allocate for the first extension list.
[email protected]92f54082012-07-31 01:43:14318 scoped_ptr<ui::SelectFileDialog::FileTypeInfo> file_type(
[email protected]479cce782012-09-15 20:15:53319 new ui::SelectFileDialog::FileTypeInfo(*base_file_type));
[email protected]ba70d082010-09-10 16:54:49320 file_type->include_all_files = true;
321 file_type->extensions.resize(1);
[email protected]650b2d52013-02-10 03:41:45322 std::vector<base::FilePath::StringType>* extensions =
323 &file_type->extensions.back();
[email protected]ba70d082010-09-10 16:54:49324
[email protected]f9a4c41a2012-05-30 00:05:32325 // Find the corresponding extensions.
[email protected]ba70d082010-09-10 16:54:49326 int valid_type_count = 0;
327 int description_id = 0;
[email protected]3314c2b12011-11-02 08:05:46328 for (size_t i = 0; i < accept_types.size(); ++i) {
[email protected]74f778e2014-03-14 21:11:46329 std::string ascii_type = base::UTF16ToASCII(accept_types[i]);
[email protected]f9a4c41a2012-05-30 00:05:32330 if (!IsAcceptTypeValid(ascii_type))
331 continue;
[email protected]ba70d082010-09-10 16:54:49332
333 size_t old_extension_size = extensions->size();
[email protected]f9a4c41a2012-05-30 00:05:32334 if (ascii_type[0] == '.') {
335 // If the type starts with a period it is assumed to be a file extension
336 // so we just have to add it to the list.
[email protected]650b2d52013-02-10 03:41:45337 base::FilePath::StringType ext(ascii_type.begin(), ascii_type.end());
[email protected]f9a4c41a2012-05-30 00:05:32338 extensions->push_back(ext.substr(1));
[email protected]ba70d082010-09-10 16:54:49339 } else {
[email protected]4a66fa0e2012-09-10 06:45:20340 if (ascii_type == "image/*")
341 description_id = IDS_IMAGE_FILES;
342 else if (ascii_type == "audio/*")
343 description_id = IDS_AUDIO_FILES;
344 else if (ascii_type == "video/*")
345 description_id = IDS_VIDEO_FILES;
346
[email protected]f9a4c41a2012-05-30 00:05:32347 net::GetExtensionsForMimeType(ascii_type, extensions);
[email protected]ba70d082010-09-10 16:54:49348 }
349
350 if (extensions->size() > old_extension_size)
351 valid_type_count++;
352 }
353
[email protected]cbcd12ed2010-12-16 23:42:57354 // If no valid extension is added, bail out.
355 if (valid_type_count == 0)
[email protected]479cce782012-09-15 20:15:53356 return base_file_type.Pass();
[email protected]cbcd12ed2010-12-16 23:42:57357
[email protected]ba70d082010-09-10 16:54:49358 // Use a generic description "Custom Files" if either of the following is
359 // true:
360 // 1) There're multiple types specified, like "audio/*,video/*"
361 // 2) There're multiple extensions for a MIME type without parameter, like
362 // "ehtml,shtml,htm,html" for "text/html". On Windows, the select file
363 // dialog uses the first extension in the list to form the description,
364 // like "EHTML Files". This is not what we want.
365 if (valid_type_count > 1 ||
366 (valid_type_count == 1 && description_id == 0 && extensions->size() > 1))
367 description_id = IDS_CUSTOM_FILES;
368
369 if (description_id) {
370 file_type->extension_description_overrides.push_back(
371 l10n_util::GetStringUTF16(description_id));
372 }
373
[email protected]479cce782012-09-15 20:15:53374 return file_type.Pass();
[email protected]ba70d082010-09-10 16:54:49375}
376
[email protected]33f8ad52012-05-22 18:10:13377// static
378void FileSelectHelper::RunFileChooser(content::WebContents* tab,
379 const FileChooserParams& params) {
380 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
381 // FileSelectHelper will keep itself alive until it sends the result message.
382 scoped_refptr<FileSelectHelper> file_select_helper(
383 new FileSelectHelper(profile));
384 file_select_helper->RunFileChooser(tab->GetRenderViewHost(), tab, params);
385}
386
387// static
388void FileSelectHelper::EnumerateDirectory(content::WebContents* tab,
389 int request_id,
[email protected]650b2d52013-02-10 03:41:45390 const base::FilePath& path) {
[email protected]33f8ad52012-05-22 18:10:13391 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
392 // FileSelectHelper will keep itself alive until it sends the result message.
393 scoped_refptr<FileSelectHelper> file_select_helper(
394 new FileSelectHelper(profile));
395 file_select_helper->EnumerateDirectory(
396 request_id, tab->GetRenderViewHost(), path);
397}
398
399void FileSelectHelper::RunFileChooser(RenderViewHost* render_view_host,
400 content::WebContents* web_contents,
401 const FileChooserParams& params) {
[email protected]ba70d082010-09-10 16:54:49402 DCHECK(!render_view_host_);
[email protected]ea049a02011-12-25 21:37:09403 DCHECK(!web_contents_);
[email protected]ba70d082010-09-10 16:54:49404 render_view_host_ = render_view_host;
[email protected]ea049a02011-12-25 21:37:09405 web_contents_ = web_contents;
[email protected]ba70d082010-09-10 16:54:49406 notification_registrar_.RemoveAll();
lazyboy1cf60af2015-05-15 22:17:05407 content::WebContentsObserver::Observe(web_contents_);
[email protected]432115822011-07-10 15:52:27408 notification_registrar_.Add(
erikchen22de64a32014-10-10 18:27:43409 this,
410 content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
[email protected]6c2381d2011-10-19 02:52:53411 content::Source<RenderWidgetHost>(render_view_host_));
[email protected]9f054aa12011-09-29 19:13:45412
413 BrowserThread::PostTask(
414 BrowserThread::FILE, FROM_HERE,
415 base::Bind(&FileSelectHelper::RunFileChooserOnFileThread, this, params));
416
417 // Because this class returns notifications to the RenderViewHost, it is
418 // difficult for callers to know how long to keep a reference to this
419 // instance. We AddRef() here to keep the instance alive after we return
420 // to the caller, until the last callback is received from the file dialog.
421 // At that point, we must call RunFileChooserEnd().
422 AddRef();
423}
424
425void FileSelectHelper::RunFileChooserOnFileThread(
[email protected]33f8ad52012-05-22 18:10:13426 const FileChooserParams& params) {
[email protected]479cce782012-09-15 20:15:53427 select_file_types_ = GetFileTypesFromAcceptType(params.accept_types);
hironoe9f2732b2014-10-22 08:06:41428 select_file_types_->support_drive = !params.need_local_path;
[email protected]9f054aa12011-09-29 19:13:45429
430 BrowserThread::PostTask(
431 BrowserThread::UI, FROM_HERE,
432 base::Bind(&FileSelectHelper::RunFileChooserOnUIThread, this, params));
433}
434
435void FileSelectHelper::RunFileChooserOnUIThread(
[email protected]33f8ad52012-05-22 18:10:13436 const FileChooserParams& params) {
hironoa4a32af22014-11-07 03:17:29437 if (!render_view_host_ || !web_contents_ || !IsValidProfile(profile_)) {
[email protected]b95b08d2011-12-15 20:23:16438 // If the renderer was destroyed before we started, just cancel the
439 // operation.
440 RunFileChooserEnd();
[email protected]9f054aa12011-09-29 19:13:45441 return;
[email protected]b95b08d2011-12-15 20:23:16442 }
[email protected]ba70d082010-09-10 16:54:49443
[email protected]92f54082012-07-31 01:43:14444 select_file_dialog_ = ui::SelectFileDialog::Create(
[email protected]6e1fcd12012-07-02 17:14:20445 this, new ChromeSelectFilePolicy(web_contents_));
dcheng319a21952014-08-26 22:52:40446 if (!select_file_dialog_.get())
[email protected]6910cb242014-03-20 14:33:01447 return;
[email protected]ba70d082010-09-10 16:54:49448
[email protected]bfcf1e92013-07-11 04:37:25449 dialog_mode_ = params.mode;
[email protected]ba70d082010-09-10 16:54:49450 switch (params.mode) {
[email protected]33f8ad52012-05-22 18:10:13451 case FileChooserParams::Open:
[email protected]92f54082012-07-31 01:43:14452 dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
[email protected]ba70d082010-09-10 16:54:49453 break;
[email protected]33f8ad52012-05-22 18:10:13454 case FileChooserParams::OpenMultiple:
[email protected]92f54082012-07-31 01:43:14455 dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
[email protected]ba70d082010-09-10 16:54:49456 break;
[email protected]6bedbef2013-07-31 06:33:49457 case FileChooserParams::UploadFolder:
458 dialog_type_ = ui::SelectFileDialog::SELECT_UPLOAD_FOLDER;
[email protected]ba70d082010-09-10 16:54:49459 break;
[email protected]33f8ad52012-05-22 18:10:13460 case FileChooserParams::Save:
[email protected]92f54082012-07-31 01:43:14461 dialog_type_ = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
[email protected]ba70d082010-09-10 16:54:49462 break;
463 default:
[email protected]92f54082012-07-31 01:43:14464 // Prevent warning.
465 dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
[email protected]ba70d082010-09-10 16:54:49466 NOTREACHED();
467 }
[email protected]4e9149a2012-08-15 20:43:59468
[email protected]650b2d52013-02-10 03:41:45469 base::FilePath default_file_name = params.default_file_name.IsAbsolute() ?
[email protected]4e9149a2012-08-15 20:43:59470 params.default_file_name :
471 profile_->last_selected_directory().Append(params.default_file_name);
[email protected]ba70d082010-09-10 16:54:49472
473 gfx::NativeWindow owning_window =
[email protected]9f76c1e2012-03-05 15:15:58474 platform_util::GetTopLevel(render_view_host_->GetView()->GetNativeView());
[email protected]d9898912011-04-15 21:10:00475
[email protected]2d02a2002012-09-18 21:47:56476#if defined(OS_ANDROID)
477 // Android needs the original MIME types and an additional capture value.
[email protected]d2065e062013-12-12 23:49:52478 std::pair<std::vector<base::string16>, bool> accept_types =
[email protected]b7b4beb2013-07-09 14:06:50479 std::make_pair(params.accept_types, params.capture);
[email protected]2d02a2002012-09-18 21:47:56480#endif
481
[email protected]9f054aa12011-09-29 19:13:45482 select_file_dialog_->SelectFile(
483 dialog_type_,
484 params.title,
485 default_file_name,
486 select_file_types_.get(),
[email protected]007b3f82013-04-09 08:46:45487 select_file_types_.get() && !select_file_types_->extensions.empty()
488 ? 1
489 : 0, // 1-based index of default extension to show.
490 base::FilePath::StringType(),
[email protected]9f054aa12011-09-29 19:13:45491 owning_window,
[email protected]b8452fa2012-06-15 01:41:41492#if defined(OS_ANDROID)
[email protected]2d02a2002012-09-18 21:47:56493 &accept_types);
[email protected]b8452fa2012-06-15 01:41:41494#else
[email protected]9f054aa12011-09-29 19:13:45495 NULL);
[email protected]b8452fa2012-06-15 01:41:41496#endif
[email protected]9f054aa12011-09-29 19:13:45497
498 select_file_types_.reset();
499}
500
501// This method is called when we receive the last callback from the file
502// chooser dialog. Perform any cleanup and release the reference we added
503// in RunFileChooser().
504void FileSelectHelper::RunFileChooserEnd() {
erikchen22de64a32014-10-10 18:27:43505 // If there are temporary files, then this instance needs to stick around
506 // until web_contents_ is destroyed, so that this instance can delete the
507 // temporary files.
508 if (!temporary_files_.empty())
509 return;
510
[email protected]9f054aa12011-09-29 19:13:45511 render_view_host_ = NULL;
[email protected]ea049a02011-12-25 21:37:09512 web_contents_ = NULL;
[email protected]9f054aa12011-09-29 19:13:45513 Release();
[email protected]ba70d082010-09-10 16:54:49514}
515
[email protected]600ea402011-04-12 00:01:51516void FileSelectHelper::EnumerateDirectory(int request_id,
517 RenderViewHost* render_view_host,
[email protected]650b2d52013-02-10 03:41:45518 const base::FilePath& path) {
[email protected]9f054aa12011-09-29 19:13:45519
520 // Because this class returns notifications to the RenderViewHost, it is
521 // difficult for callers to know how long to keep a reference to this
522 // instance. We AddRef() here to keep the instance alive after we return
523 // to the caller, until the last callback is received from the enumeration
524 // code. At that point, we must call EnumerateDirectoryEnd().
525 AddRef();
[email protected]600ea402011-04-12 00:01:51526 StartNewEnumeration(path, request_id, render_view_host);
527}
528
[email protected]9f054aa12011-09-29 19:13:45529// This method is called when we receive the last callback from the enumeration
530// code. Perform any cleanup and release the reference we added in
531// EnumerateDirectory().
532void FileSelectHelper::EnumerateDirectoryEnd() {
533 Release();
534}
535
[email protected]432115822011-07-10 15:52:27536void FileSelectHelper::Observe(int type,
[email protected]6c2381d2011-10-19 02:52:53537 const content::NotificationSource& source,
538 const content::NotificationDetails& details) {
[email protected]9f054aa12011-09-29 19:13:45539 switch (type) {
540 case content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED: {
[email protected]6c2381d2011-10-19 02:52:53541 DCHECK(content::Source<RenderWidgetHost>(source).ptr() ==
542 render_view_host_);
[email protected]9f054aa12011-09-29 19:13:45543 render_view_host_ = NULL;
544 break;
545 }
[email protected]9f054aa12011-09-29 19:13:45546 default:
547 NOTREACHED();
548 }
[email protected]ba70d082010-09-10 16:54:49549}
[email protected]f9a4c41a2012-05-30 00:05:32550
lazyboy1cf60af2015-05-15 22:17:05551void FileSelectHelper::RenderViewHostChanged(RenderViewHost* old_host,
552 RenderViewHost* new_host) {
553 CleanUpOnRenderViewHostChange();
554}
555
556void FileSelectHelper::WebContentsDestroyed() {
557 web_contents_ = nullptr;
558 CleanUpOnRenderViewHostChange();
559}
560
[email protected]f9a4c41a2012-05-30 00:05:32561// static
562bool FileSelectHelper::IsAcceptTypeValid(const std::string& accept_type) {
563 // TODO(raymes): This only does some basic checks, extend to test more cases.
564 // A 1 character accept type will always be invalid (either a "." in the case
565 // of an extension or a "/" in the case of a MIME type).
566 std::string unused;
567 if (accept_type.length() <= 1 ||
brettwfce8d192015-08-10 19:07:51568 base::ToLowerASCII(accept_type) != accept_type ||
[email protected]8af69c6c2014-03-03 19:05:31569 base::TrimWhitespaceASCII(accept_type, base::TRIM_ALL, &unused) !=
570 base::TRIM_NONE) {
[email protected]f9a4c41a2012-05-30 00:05:32571 return false;
572 }
573 return true;
574}