blob: 37f85fb988cf812b61d7bde14e6b0fd6d29c3aaf [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"
[email protected]ba70d082010-09-10 16:54:4916#include "chrome/browser/platform_util.h"
[email protected]8ecad5e2010-12-02 21:18:3317#include "chrome/browser/profiles/profile.h"
[email protected]d9898912011-04-15 21:10:0018#include "chrome/browser/ui/browser.h"
19#include "chrome/browser/ui/browser_list.h"
[email protected]6e1fcd12012-07-02 17:14:2020#include "chrome/browser/ui/chrome_select_file_policy.h"
[email protected]af39f002014-08-22 10:18:1821#include "chrome/grit/generated_resources.h"
[email protected]6a1c98e02012-10-24 21:49:4322#include "content/public/browser/browser_thread.h"
[email protected]6c2381d2011-10-19 02:52:5323#include "content/public/browser/notification_details.h"
24#include "content/public/browser/notification_source.h"
[email protected]0d6e9bd2011-10-18 04:29:1625#include "content/public/browser/notification_types.h"
[email protected]9c1662b2012-03-06 15:44:3326#include "content/public/browser/render_view_host.h"
[email protected]5626b0892012-02-20 14:46:5827#include "content/public/browser/render_widget_host_view.h"
[email protected]33f8ad52012-05-22 18:10:1328#include "content/public/browser/web_contents.h"
hirono570357bd2014-10-08 12:39:2729#include "content/public/common/file_chooser_file_info.h"
[email protected]8caadeb2011-11-22 02:45:2330#include "content/public/common/file_chooser_params.h"
[email protected]b3841c502011-03-09 01:21:3131#include "net/base/mime_util.h"
[email protected]c051a1b2011-01-21 23:30:1732#include "ui/base/l10n/l10n_util.h"
[email protected]4344a3c2013-01-17 23:49:2033#include "ui/shell_dialogs/selected_file_info.h"
[email protected]ba70d082010-09-10 16:54:4934
hirono275bfbe2014-10-21 01:46:1735#if defined(OS_CHROMEOS)
36#include "chrome/browser/chromeos/file_manager/fileapi_util.h"
37#include "content/public/browser/site_instance.h"
38#endif
39
[email protected]631bb742011-11-02 11:29:3940using content::BrowserThread;
[email protected]33f8ad52012-05-22 18:10:1341using content::FileChooserParams;
[email protected]eaabba22012-03-07 15:02:1142using content::RenderViewHost;
43using content::RenderWidgetHost;
[email protected]ea049a02011-12-25 21:37:0944using content::WebContents;
[email protected]631bb742011-11-02 11:29:3945
[email protected]600ea402011-04-12 00:01:5146namespace {
47
48// There is only one file-selection happening at any given time,
49// so we allocate an enumeration ID for that purpose. All IDs from
50// the renderer must start at 0 and increase.
[email protected]459fba82011-10-13 02:48:5051const int kFileSelectEnumerationId = -1;
52
[email protected]53f04c82012-07-26 02:31:0953// Converts a list of FilePaths to a list of ui::SelectedFileInfo.
54std::vector<ui::SelectedFileInfo> FilePathListToSelectedFileInfoList(
[email protected]650b2d52013-02-10 03:41:4555 const std::vector<base::FilePath>& paths) {
[email protected]ddb034b2012-06-26 20:31:3956 std::vector<ui::SelectedFileInfo> selected_files;
[email protected]fb11b6a42012-03-14 07:25:1257 for (size_t i = 0; i < paths.size(); ++i) {
58 selected_files.push_back(
[email protected]53f04c82012-07-26 02:31:0959 ui::SelectedFileInfo(paths[i], paths[i]));
[email protected]fb11b6a42012-03-14 07:25:1260 }
61 return selected_files;
[email protected]600ea402011-04-12 00:01:5162}
63
erikchen22de64a32014-10-10 18:27:4364void DeleteFiles(const std::vector<base::FilePath>& paths) {
65 for (auto& file_path : paths)
66 base::DeleteFile(file_path, false);
67}
68
[email protected]fb11b6a42012-03-14 07:25:1269} // namespace
70
[email protected]485a5272011-04-12 00:49:2971struct FileSelectHelper::ActiveDirectoryEnumeration {
[email protected]d45f7512011-06-21 21:18:2772 ActiveDirectoryEnumeration() : rvh_(NULL) {}
[email protected]485a5272011-04-12 00:49:2973
74 scoped_ptr<DirectoryListerDispatchDelegate> delegate_;
[email protected]05a814182011-04-27 19:50:3475 scoped_ptr<net::DirectoryLister> lister_;
[email protected]485a5272011-04-12 00:49:2976 RenderViewHost* rvh_;
[email protected]650b2d52013-02-10 03:41:4577 std::vector<base::FilePath> results_;
[email protected]485a5272011-04-12 00:49:2978};
79
[email protected]ba70d082010-09-10 16:54:4980FileSelectHelper::FileSelectHelper(Profile* profile)
81 : profile_(profile),
82 render_view_host_(NULL),
[email protected]ea049a02011-12-25 21:37:0983 web_contents_(NULL),
[email protected]ba70d082010-09-10 16:54:4984 select_file_dialog_(),
[email protected]9f054aa12011-09-29 19:13:4585 select_file_types_(),
[email protected]bfcf1e92013-07-11 04:37:2586 dialog_type_(ui::SelectFileDialog::SELECT_OPEN_FILE),
87 dialog_mode_(FileChooserParams::Open) {
[email protected]ba70d082010-09-10 16:54:4988}
89
90FileSelectHelper::~FileSelectHelper() {
91 // There may be pending file dialogs, we need to tell them that we've gone
92 // away so they don't try and call back to us.
93 if (select_file_dialog_.get())
94 select_file_dialog_->ListenerDestroyed();
95
[email protected]600ea402011-04-12 00:01:5196 // Stop any pending directory enumeration, prevent a callback, and free
97 // allocated memory.
98 std::map<int, ActiveDirectoryEnumeration*>::iterator iter;
99 for (iter = directory_enumerations_.begin();
100 iter != directory_enumerations_.end();
101 ++iter) {
[email protected]05a814182011-04-27 19:50:34102 iter->second->lister_.reset();
[email protected]600ea402011-04-12 00:01:51103 delete iter->second;
[email protected]ba70d082010-09-10 16:54:49104 }
105}
106
[email protected]23827ec2012-08-10 22:08:08107void FileSelectHelper::DirectoryListerDispatchDelegate::OnListFile(
108 const net::DirectoryLister::DirectoryListerData& data) {
109 parent_->OnListFile(id_, data);
110}
111
112void FileSelectHelper::DirectoryListerDispatchDelegate::OnListDone(int error) {
113 parent_->OnListDone(id_, error);
114}
115
[email protected]650b2d52013-02-10 03:41:45116void FileSelectHelper::FileSelected(const base::FilePath& path,
[email protected]ba70d082010-09-10 16:54:49117 int index, void* params) {
[email protected]53f04c82012-07-26 02:31:09118 FileSelectedWithExtraInfo(ui::SelectedFileInfo(path, path), index, params);
[email protected]fb11b6a42012-03-14 07:25:12119}
120
121void FileSelectHelper::FileSelectedWithExtraInfo(
[email protected]ddb034b2012-06-26 20:31:39122 const ui::SelectedFileInfo& file,
[email protected]fb11b6a42012-03-14 07:25:12123 int index,
124 void* params) {
[email protected]53f04c82012-07-26 02:31:09125 profile_->set_last_selected_directory(file.file_path.DirName());
[email protected]ba70d082010-09-10 16:54:49126
erikchen22de64a32014-10-10 18:27:43127 if (!render_view_host_) {
128 RunFileChooserEnd();
129 return;
130 }
131
[email protected]650b2d52013-02-10 03:41:45132 const base::FilePath& path = file.local_path;
[email protected]6bedbef2013-07-31 06:33:49133 if (dialog_type_ == ui::SelectFileDialog::SELECT_UPLOAD_FOLDER) {
[email protected]600ea402011-04-12 00:01:51134 StartNewEnumeration(path, kFileSelectEnumerationId, render_view_host_);
[email protected]ba70d082010-09-10 16:54:49135 return;
136 }
137
[email protected]ddb034b2012-06-26 20:31:39138 std::vector<ui::SelectedFileInfo> files;
[email protected]fb11b6a42012-03-14 07:25:12139 files.push_back(file);
[email protected]9f054aa12011-09-29 19:13:45140
erikchen22de64a32014-10-10 18:27:43141#if defined(OS_MACOSX) && !defined(OS_IOS)
142 content::BrowserThread::PostTask(
143 content::BrowserThread::FILE_USER_BLOCKING,
144 FROM_HERE,
145 base::Bind(&FileSelectHelper::ProcessSelectedFilesMac, this, files));
146#else
147 NotifyRenderViewHostAndEnd(files);
148#endif // defined(OS_MACOSX) && !defined(OS_IOS)
[email protected]ba70d082010-09-10 16:54:49149}
150
[email protected]650b2d52013-02-10 03:41:45151void FileSelectHelper::MultiFilesSelected(
152 const std::vector<base::FilePath>& files,
153 void* params) {
[email protected]ddb034b2012-06-26 20:31:39154 std::vector<ui::SelectedFileInfo> selected_files =
[email protected]53f04c82012-07-26 02:31:09155 FilePathListToSelectedFileInfoList(files);
156
[email protected]fb11b6a42012-03-14 07:25:12157 MultiFilesSelectedWithExtraInfo(selected_files, params);
158}
159
160void FileSelectHelper::MultiFilesSelectedWithExtraInfo(
[email protected]ddb034b2012-06-26 20:31:39161 const std::vector<ui::SelectedFileInfo>& files,
[email protected]fb11b6a42012-03-14 07:25:12162 void* params) {
[email protected]ba70d082010-09-10 16:54:49163 if (!files.empty())
[email protected]53f04c82012-07-26 02:31:09164 profile_->set_last_selected_directory(files[0].file_path.DirName());
[email protected]ba70d082010-09-10 16:54:49165
erikchen22de64a32014-10-10 18:27:43166#if defined(OS_MACOSX) && !defined(OS_IOS)
167 content::BrowserThread::PostTask(
168 content::BrowserThread::FILE_USER_BLOCKING,
169 FROM_HERE,
170 base::Bind(&FileSelectHelper::ProcessSelectedFilesMac, this, files));
171#else
172 NotifyRenderViewHostAndEnd(files);
173#endif // defined(OS_MACOSX) && !defined(OS_IOS)
[email protected]ba70d082010-09-10 16:54:49174}
175
176void FileSelectHelper::FileSelectionCanceled(void* params) {
erikchen22de64a32014-10-10 18:27:43177 NotifyRenderViewHostAndEnd(std::vector<ui::SelectedFileInfo>());
[email protected]ba70d082010-09-10 16:54:49178}
179
[email protected]650b2d52013-02-10 03:41:45180void FileSelectHelper::StartNewEnumeration(const base::FilePath& path,
[email protected]600ea402011-04-12 00:01:51181 int request_id,
182 RenderViewHost* render_view_host) {
183 scoped_ptr<ActiveDirectoryEnumeration> entry(new ActiveDirectoryEnumeration);
184 entry->rvh_ = render_view_host;
185 entry->delegate_.reset(new DirectoryListerDispatchDelegate(this, request_id));
[email protected]05a814182011-04-27 19:50:34186 entry->lister_.reset(new net::DirectoryLister(path,
187 true,
188 net::DirectoryLister::NO_SORT,
189 entry->delegate_.get()));
[email protected]600ea402011-04-12 00:01:51190 if (!entry->lister_->Start()) {
191 if (request_id == kFileSelectEnumerationId)
192 FileSelectionCanceled(NULL);
193 else
194 render_view_host->DirectoryEnumerationFinished(request_id,
195 entry->results_);
196 } else {
197 directory_enumerations_[request_id] = entry.release();
198 }
[email protected]ba70d082010-09-10 16:54:49199}
200
201void FileSelectHelper::OnListFile(
[email protected]600ea402011-04-12 00:01:51202 int id,
[email protected]ba70d082010-09-10 16:54:49203 const net::DirectoryLister::DirectoryListerData& data) {
[email protected]600ea402011-04-12 00:01:51204 ActiveDirectoryEnumeration* entry = directory_enumerations_[id];
205
[email protected]2c718a0d2014-03-24 02:42:22206 // Directory upload only cares about files.
[email protected]25a4c1c2013-06-08 04:53:36207 if (data.info.IsDirectory())
[email protected]2c718a0d2014-03-24 02:42:22208 return;
209
210 entry->results_.push_back(data.path);
[email protected]ba70d082010-09-10 16:54:49211}
212
[email protected]600ea402011-04-12 00:01:51213void FileSelectHelper::OnListDone(int id, int error) {
214 // This entry needs to be cleaned up when this function is done.
215 scoped_ptr<ActiveDirectoryEnumeration> entry(directory_enumerations_[id]);
216 directory_enumerations_.erase(id);
217 if (!entry->rvh_)
[email protected]ba70d082010-09-10 16:54:49218 return;
[email protected]ba70d082010-09-10 16:54:49219 if (error) {
220 FileSelectionCanceled(NULL);
221 return;
222 }
[email protected]fb11b6a42012-03-14 07:25:12223
[email protected]ddb034b2012-06-26 20:31:39224 std::vector<ui::SelectedFileInfo> selected_files =
[email protected]53f04c82012-07-26 02:31:09225 FilePathListToSelectedFileInfoList(entry->results_);
[email protected]fb11b6a42012-03-14 07:25:12226
hirono275bfbe2014-10-21 01:46:17227 if (id == kFileSelectEnumerationId) {
228 NotifyRenderViewHostAndEnd(selected_files);
229 } else {
[email protected]600ea402011-04-12 00:01:51230 entry->rvh_->DirectoryEnumerationFinished(id, entry->results_);
hirono275bfbe2014-10-21 01:46:17231 EnumerateDirectoryEnd();
232 }
[email protected]ba70d082010-09-10 16:54:49233}
234
erikchen22de64a32014-10-10 18:27:43235void FileSelectHelper::NotifyRenderViewHostAndEnd(
236 const std::vector<ui::SelectedFileInfo>& files) {
hirono275bfbe2014-10-21 01:46:17237 if (!render_view_host_) {
238 RunFileChooserEnd();
239 return;
240 }
241
242#if defined(OS_CHROMEOS)
243 if (!files.empty()) {
244 // Converts |files| into FileChooserFileInfo with handling of non-native
245 // files.
246 file_manager::util::ConvertSelectedFileInfoListToFileChooserFileInfoList(
247 file_manager::util::GetFileSystemContextForRenderViewHost(
248 profile_, render_view_host_),
249 web_contents_->GetSiteInstance()->GetSiteURL(),
250 files,
251 base::Bind(
hironocf350cb2014-10-23 08:37:18252 &FileSelectHelper::NotifyRenderViewHostAndEndAfterConversion,
hirono275bfbe2014-10-21 01:46:17253 this));
254 return;
255 }
256#endif // defined(OS_CHROMEOS)
257
258 std::vector<content::FileChooserFileInfo> chooser_files;
259 for (const auto& file : files) {
260 content::FileChooserFileInfo chooser_file;
261 chooser_file.file_path = file.local_path;
262 chooser_file.display_name = file.display_name;
263 chooser_files.push_back(chooser_file);
264 }
hironocf350cb2014-10-23 08:37:18265
266 NotifyRenderViewHostAndEndAfterConversion(chooser_files);
267}
268
269void FileSelectHelper::NotifyRenderViewHostAndEndAfterConversion(
270 const std::vector<content::FileChooserFileInfo>& list) {
271 if (render_view_host_)
272 render_view_host_->FilesSelectedInChooser(list, dialog_mode_);
erikchen22de64a32014-10-10 18:27:43273
274 // No members should be accessed from here on.
275 RunFileChooserEnd();
276}
277
erikchen22de64a32014-10-10 18:27:43278void FileSelectHelper::DeleteTemporaryFiles() {
279 BrowserThread::PostTask(BrowserThread::FILE,
280 FROM_HERE,
281 base::Bind(&DeleteFiles, temporary_files_));
282 temporary_files_.clear();
283}
284
[email protected]479cce782012-09-15 20:15:53285scoped_ptr<ui::SelectFileDialog::FileTypeInfo>
[email protected]92f54082012-07-31 01:43:14286FileSelectHelper::GetFileTypesFromAcceptType(
[email protected]d2065e062013-12-12 23:49:52287 const std::vector<base::string16>& accept_types) {
[email protected]479cce782012-09-15 20:15:53288 scoped_ptr<ui::SelectFileDialog::FileTypeInfo> base_file_type(
289 new ui::SelectFileDialog::FileTypeInfo());
[email protected]ba70d082010-09-10 16:54:49290 if (accept_types.empty())
[email protected]479cce782012-09-15 20:15:53291 return base_file_type.Pass();
[email protected]ba70d082010-09-10 16:54:49292
[email protected]ba70d082010-09-10 16:54:49293 // Create FileTypeInfo and pre-allocate for the first extension list.
[email protected]92f54082012-07-31 01:43:14294 scoped_ptr<ui::SelectFileDialog::FileTypeInfo> file_type(
[email protected]479cce782012-09-15 20:15:53295 new ui::SelectFileDialog::FileTypeInfo(*base_file_type));
[email protected]ba70d082010-09-10 16:54:49296 file_type->include_all_files = true;
297 file_type->extensions.resize(1);
[email protected]650b2d52013-02-10 03:41:45298 std::vector<base::FilePath::StringType>* extensions =
299 &file_type->extensions.back();
[email protected]ba70d082010-09-10 16:54:49300
[email protected]f9a4c41a2012-05-30 00:05:32301 // Find the corresponding extensions.
[email protected]ba70d082010-09-10 16:54:49302 int valid_type_count = 0;
303 int description_id = 0;
[email protected]3314c2b12011-11-02 08:05:46304 for (size_t i = 0; i < accept_types.size(); ++i) {
[email protected]74f778e2014-03-14 21:11:46305 std::string ascii_type = base::UTF16ToASCII(accept_types[i]);
[email protected]f9a4c41a2012-05-30 00:05:32306 if (!IsAcceptTypeValid(ascii_type))
307 continue;
[email protected]ba70d082010-09-10 16:54:49308
309 size_t old_extension_size = extensions->size();
[email protected]f9a4c41a2012-05-30 00:05:32310 if (ascii_type[0] == '.') {
311 // If the type starts with a period it is assumed to be a file extension
312 // so we just have to add it to the list.
[email protected]650b2d52013-02-10 03:41:45313 base::FilePath::StringType ext(ascii_type.begin(), ascii_type.end());
[email protected]f9a4c41a2012-05-30 00:05:32314 extensions->push_back(ext.substr(1));
[email protected]ba70d082010-09-10 16:54:49315 } else {
[email protected]4a66fa0e2012-09-10 06:45:20316 if (ascii_type == "image/*")
317 description_id = IDS_IMAGE_FILES;
318 else if (ascii_type == "audio/*")
319 description_id = IDS_AUDIO_FILES;
320 else if (ascii_type == "video/*")
321 description_id = IDS_VIDEO_FILES;
322
[email protected]f9a4c41a2012-05-30 00:05:32323 net::GetExtensionsForMimeType(ascii_type, extensions);
[email protected]ba70d082010-09-10 16:54:49324 }
325
326 if (extensions->size() > old_extension_size)
327 valid_type_count++;
328 }
329
[email protected]cbcd12ed2010-12-16 23:42:57330 // If no valid extension is added, bail out.
331 if (valid_type_count == 0)
[email protected]479cce782012-09-15 20:15:53332 return base_file_type.Pass();
[email protected]cbcd12ed2010-12-16 23:42:57333
[email protected]ba70d082010-09-10 16:54:49334 // Use a generic description "Custom Files" if either of the following is
335 // true:
336 // 1) There're multiple types specified, like "audio/*,video/*"
337 // 2) There're multiple extensions for a MIME type without parameter, like
338 // "ehtml,shtml,htm,html" for "text/html". On Windows, the select file
339 // dialog uses the first extension in the list to form the description,
340 // like "EHTML Files". This is not what we want.
341 if (valid_type_count > 1 ||
342 (valid_type_count == 1 && description_id == 0 && extensions->size() > 1))
343 description_id = IDS_CUSTOM_FILES;
344
345 if (description_id) {
346 file_type->extension_description_overrides.push_back(
347 l10n_util::GetStringUTF16(description_id));
348 }
349
[email protected]479cce782012-09-15 20:15:53350 return file_type.Pass();
[email protected]ba70d082010-09-10 16:54:49351}
352
[email protected]33f8ad52012-05-22 18:10:13353// static
354void FileSelectHelper::RunFileChooser(content::WebContents* tab,
355 const FileChooserParams& params) {
356 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
357 // FileSelectHelper will keep itself alive until it sends the result message.
358 scoped_refptr<FileSelectHelper> file_select_helper(
359 new FileSelectHelper(profile));
360 file_select_helper->RunFileChooser(tab->GetRenderViewHost(), tab, params);
361}
362
363// static
364void FileSelectHelper::EnumerateDirectory(content::WebContents* tab,
365 int request_id,
[email protected]650b2d52013-02-10 03:41:45366 const base::FilePath& path) {
[email protected]33f8ad52012-05-22 18:10:13367 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
368 // FileSelectHelper will keep itself alive until it sends the result message.
369 scoped_refptr<FileSelectHelper> file_select_helper(
370 new FileSelectHelper(profile));
371 file_select_helper->EnumerateDirectory(
372 request_id, tab->GetRenderViewHost(), path);
373}
374
375void FileSelectHelper::RunFileChooser(RenderViewHost* render_view_host,
376 content::WebContents* web_contents,
377 const FileChooserParams& params) {
[email protected]ba70d082010-09-10 16:54:49378 DCHECK(!render_view_host_);
[email protected]ea049a02011-12-25 21:37:09379 DCHECK(!web_contents_);
[email protected]ba70d082010-09-10 16:54:49380 render_view_host_ = render_view_host;
[email protected]ea049a02011-12-25 21:37:09381 web_contents_ = web_contents;
[email protected]ba70d082010-09-10 16:54:49382 notification_registrar_.RemoveAll();
erikchen22de64a32014-10-10 18:27:43383 notification_registrar_.Add(this,
384 content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
385 content::Source<WebContents>(web_contents_));
[email protected]432115822011-07-10 15:52:27386 notification_registrar_.Add(
erikchen22de64a32014-10-10 18:27:43387 this,
388 content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
[email protected]6c2381d2011-10-19 02:52:53389 content::Source<RenderWidgetHost>(render_view_host_));
[email protected]9f054aa12011-09-29 19:13:45390 notification_registrar_.Add(
[email protected]ea049a02011-12-25 21:37:09391 this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
392 content::Source<WebContents>(web_contents_));
[email protected]9f054aa12011-09-29 19:13:45393
394 BrowserThread::PostTask(
395 BrowserThread::FILE, FROM_HERE,
396 base::Bind(&FileSelectHelper::RunFileChooserOnFileThread, this, params));
397
398 // Because this class returns notifications to the RenderViewHost, it is
399 // difficult for callers to know how long to keep a reference to this
400 // instance. We AddRef() here to keep the instance alive after we return
401 // to the caller, until the last callback is received from the file dialog.
402 // At that point, we must call RunFileChooserEnd().
403 AddRef();
404}
405
406void FileSelectHelper::RunFileChooserOnFileThread(
[email protected]33f8ad52012-05-22 18:10:13407 const FileChooserParams& params) {
[email protected]479cce782012-09-15 20:15:53408 select_file_types_ = GetFileTypesFromAcceptType(params.accept_types);
hironoe9f2732b2014-10-22 08:06:41409 select_file_types_->support_drive = !params.need_local_path;
[email protected]9f054aa12011-09-29 19:13:45410
411 BrowserThread::PostTask(
412 BrowserThread::UI, FROM_HERE,
413 base::Bind(&FileSelectHelper::RunFileChooserOnUIThread, this, params));
414}
415
416void FileSelectHelper::RunFileChooserOnUIThread(
[email protected]33f8ad52012-05-22 18:10:13417 const FileChooserParams& params) {
[email protected]ea049a02011-12-25 21:37:09418 if (!render_view_host_ || !web_contents_) {
[email protected]b95b08d2011-12-15 20:23:16419 // If the renderer was destroyed before we started, just cancel the
420 // operation.
421 RunFileChooserEnd();
[email protected]9f054aa12011-09-29 19:13:45422 return;
[email protected]b95b08d2011-12-15 20:23:16423 }
[email protected]ba70d082010-09-10 16:54:49424
[email protected]92f54082012-07-31 01:43:14425 select_file_dialog_ = ui::SelectFileDialog::Create(
[email protected]6e1fcd12012-07-02 17:14:20426 this, new ChromeSelectFilePolicy(web_contents_));
dcheng319a21952014-08-26 22:52:40427 if (!select_file_dialog_.get())
[email protected]6910cb242014-03-20 14:33:01428 return;
[email protected]ba70d082010-09-10 16:54:49429
[email protected]bfcf1e92013-07-11 04:37:25430 dialog_mode_ = params.mode;
[email protected]ba70d082010-09-10 16:54:49431 switch (params.mode) {
[email protected]33f8ad52012-05-22 18:10:13432 case FileChooserParams::Open:
[email protected]92f54082012-07-31 01:43:14433 dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
[email protected]ba70d082010-09-10 16:54:49434 break;
[email protected]33f8ad52012-05-22 18:10:13435 case FileChooserParams::OpenMultiple:
[email protected]92f54082012-07-31 01:43:14436 dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
[email protected]ba70d082010-09-10 16:54:49437 break;
[email protected]6bedbef2013-07-31 06:33:49438 case FileChooserParams::UploadFolder:
439 dialog_type_ = ui::SelectFileDialog::SELECT_UPLOAD_FOLDER;
[email protected]ba70d082010-09-10 16:54:49440 break;
[email protected]33f8ad52012-05-22 18:10:13441 case FileChooserParams::Save:
[email protected]92f54082012-07-31 01:43:14442 dialog_type_ = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
[email protected]ba70d082010-09-10 16:54:49443 break;
444 default:
[email protected]92f54082012-07-31 01:43:14445 // Prevent warning.
446 dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
[email protected]ba70d082010-09-10 16:54:49447 NOTREACHED();
448 }
[email protected]4e9149a2012-08-15 20:43:59449
[email protected]650b2d52013-02-10 03:41:45450 base::FilePath default_file_name = params.default_file_name.IsAbsolute() ?
[email protected]4e9149a2012-08-15 20:43:59451 params.default_file_name :
452 profile_->last_selected_directory().Append(params.default_file_name);
[email protected]ba70d082010-09-10 16:54:49453
454 gfx::NativeWindow owning_window =
[email protected]9f76c1e2012-03-05 15:15:58455 platform_util::GetTopLevel(render_view_host_->GetView()->GetNativeView());
[email protected]d9898912011-04-15 21:10:00456
[email protected]2d02a2002012-09-18 21:47:56457#if defined(OS_ANDROID)
458 // Android needs the original MIME types and an additional capture value.
[email protected]d2065e062013-12-12 23:49:52459 std::pair<std::vector<base::string16>, bool> accept_types =
[email protected]b7b4beb2013-07-09 14:06:50460 std::make_pair(params.accept_types, params.capture);
[email protected]2d02a2002012-09-18 21:47:56461#endif
462
[email protected]9f054aa12011-09-29 19:13:45463 select_file_dialog_->SelectFile(
464 dialog_type_,
465 params.title,
466 default_file_name,
467 select_file_types_.get(),
[email protected]007b3f82013-04-09 08:46:45468 select_file_types_.get() && !select_file_types_->extensions.empty()
469 ? 1
470 : 0, // 1-based index of default extension to show.
471 base::FilePath::StringType(),
[email protected]9f054aa12011-09-29 19:13:45472 owning_window,
[email protected]b8452fa2012-06-15 01:41:41473#if defined(OS_ANDROID)
[email protected]2d02a2002012-09-18 21:47:56474 &accept_types);
[email protected]b8452fa2012-06-15 01:41:41475#else
[email protected]9f054aa12011-09-29 19:13:45476 NULL);
[email protected]b8452fa2012-06-15 01:41:41477#endif
[email protected]9f054aa12011-09-29 19:13:45478
479 select_file_types_.reset();
480}
481
482// This method is called when we receive the last callback from the file
483// chooser dialog. Perform any cleanup and release the reference we added
484// in RunFileChooser().
485void FileSelectHelper::RunFileChooserEnd() {
erikchen22de64a32014-10-10 18:27:43486 // If there are temporary files, then this instance needs to stick around
487 // until web_contents_ is destroyed, so that this instance can delete the
488 // temporary files.
489 if (!temporary_files_.empty())
490 return;
491
[email protected]9f054aa12011-09-29 19:13:45492 render_view_host_ = NULL;
[email protected]ea049a02011-12-25 21:37:09493 web_contents_ = NULL;
[email protected]9f054aa12011-09-29 19:13:45494 Release();
[email protected]ba70d082010-09-10 16:54:49495}
496
[email protected]600ea402011-04-12 00:01:51497void FileSelectHelper::EnumerateDirectory(int request_id,
498 RenderViewHost* render_view_host,
[email protected]650b2d52013-02-10 03:41:45499 const base::FilePath& path) {
[email protected]9f054aa12011-09-29 19:13:45500
501 // Because this class returns notifications to the RenderViewHost, it is
502 // difficult for callers to know how long to keep a reference to this
503 // instance. We AddRef() here to keep the instance alive after we return
504 // to the caller, until the last callback is received from the enumeration
505 // code. At that point, we must call EnumerateDirectoryEnd().
506 AddRef();
[email protected]600ea402011-04-12 00:01:51507 StartNewEnumeration(path, request_id, render_view_host);
508}
509
[email protected]9f054aa12011-09-29 19:13:45510// This method is called when we receive the last callback from the enumeration
511// code. Perform any cleanup and release the reference we added in
512// EnumerateDirectory().
513void FileSelectHelper::EnumerateDirectoryEnd() {
514 Release();
515}
516
[email protected]432115822011-07-10 15:52:27517void FileSelectHelper::Observe(int type,
[email protected]6c2381d2011-10-19 02:52:53518 const content::NotificationSource& source,
519 const content::NotificationDetails& details) {
[email protected]9f054aa12011-09-29 19:13:45520 switch (type) {
521 case content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED: {
[email protected]6c2381d2011-10-19 02:52:53522 DCHECK(content::Source<RenderWidgetHost>(source).ptr() ==
523 render_view_host_);
[email protected]9f054aa12011-09-29 19:13:45524 render_view_host_ = NULL;
525 break;
526 }
527
[email protected]ea049a02011-12-25 21:37:09528 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: {
529 DCHECK(content::Source<WebContents>(source).ptr() == web_contents_);
530 web_contents_ = NULL;
[email protected]9f054aa12011-09-29 19:13:45531 }
532
erikchen22de64a32014-10-10 18:27:43533 // Intentional fall through.
534 case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED:
535 if (!temporary_files_.empty()) {
536 DeleteTemporaryFiles();
537
538 // Now that the temporary files have been scheduled for deletion, there
539 // is no longer any reason to keep this instance around.
540 Release();
541 }
542
543 break;
544
[email protected]9f054aa12011-09-29 19:13:45545 default:
546 NOTREACHED();
547 }
[email protected]ba70d082010-09-10 16:54:49548}
[email protected]f9a4c41a2012-05-30 00:05:32549
550// static
551bool FileSelectHelper::IsAcceptTypeValid(const std::string& accept_type) {
552 // TODO(raymes): This only does some basic checks, extend to test more cases.
553 // A 1 character accept type will always be invalid (either a "." in the case
554 // of an extension or a "/" in the case of a MIME type).
555 std::string unused;
556 if (accept_type.length() <= 1 ||
[email protected]cb1f4ac2014-08-07 16:55:42557 base::StringToLowerASCII(accept_type) != accept_type ||
[email protected]8af69c6c2014-03-03 19:05:31558 base::TrimWhitespaceASCII(accept_type, base::TRIM_ALL, &unused) !=
559 base::TRIM_NONE) {
[email protected]f9a4c41a2012-05-30 00:05:32560 return false;
561 }
562 return true;
563}