blob: 9ceb4384d916d5217865911c35347938545f0b4e [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]ba70d082010-09-10 16:54:4911#include "base/file_util.h"
[email protected]25a4c1c2013-06-08 04:53:3612#include "base/files/file_enumerator.h"
[email protected]459fba82011-10-13 02:48:5013#include "base/platform_file.h"
[email protected]1988e1c2013-02-28 20:27:4214#include "base/strings/string_split.h"
[email protected]d8830562013-06-10 22:01:5415#include "base/strings/string_util.h"
[email protected]112158af2013-06-07 23:46:1816#include "base/strings/utf_string_conversions.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"
[email protected]d9898912011-04-15 21:10:0019#include "chrome/browser/ui/browser.h"
20#include "chrome/browser/ui/browser_list.h"
[email protected]6e1fcd12012-07-02 17:14:2021#include "chrome/browser/ui/chrome_select_file_policy.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"
[email protected]8caadeb2011-11-22 02:45:2329#include "content/public/common/file_chooser_params.h"
[email protected]ba70d082010-09-10 16:54:4930#include "grit/generated_resources.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
[email protected]631bb742011-11-02 11:29:3935using content::BrowserThread;
[email protected]33f8ad52012-05-22 18:10:1336using content::FileChooserParams;
[email protected]eaabba22012-03-07 15:02:1137using content::RenderViewHost;
38using content::RenderWidgetHost;
[email protected]ea049a02011-12-25 21:37:0939using content::WebContents;
[email protected]631bb742011-11-02 11:29:3940
[email protected]600ea402011-04-12 00:01:5141namespace {
42
43// There is only one file-selection happening at any given time,
44// so we allocate an enumeration ID for that purpose. All IDs from
45// the renderer must start at 0 and increase.
[email protected]459fba82011-10-13 02:48:5046const int kFileSelectEnumerationId = -1;
47
48void NotifyRenderViewHost(RenderViewHost* render_view_host,
[email protected]ddb034b2012-06-26 20:31:3949 const std::vector<ui::SelectedFileInfo>& files,
[email protected]92f54082012-07-31 01:43:1450 ui::SelectFileDialog::Type dialog_type) {
[email protected]459fba82011-10-13 02:48:5051 const int kReadFilePermissions =
52 base::PLATFORM_FILE_OPEN |
53 base::PLATFORM_FILE_READ |
54 base::PLATFORM_FILE_EXCLUSIVE_READ |
55 base::PLATFORM_FILE_ASYNC;
56
57 const int kWriteFilePermissions =
[email protected]3c688fac2011-10-14 02:29:1458 base::PLATFORM_FILE_CREATE |
59 base::PLATFORM_FILE_CREATE_ALWAYS |
60 base::PLATFORM_FILE_OPEN |
[email protected]459fba82011-10-13 02:48:5061 base::PLATFORM_FILE_OPEN_ALWAYS |
[email protected]3c688fac2011-10-14 02:29:1462 base::PLATFORM_FILE_OPEN_TRUNCATED |
[email protected]459fba82011-10-13 02:48:5063 base::PLATFORM_FILE_WRITE |
64 base::PLATFORM_FILE_WRITE_ATTRIBUTES |
65 base::PLATFORM_FILE_ASYNC;
66
67 int permissions = kReadFilePermissions;
[email protected]92f54082012-07-31 01:43:1468 if (dialog_type == ui::SelectFileDialog::SELECT_SAVEAS_FILE)
[email protected]459fba82011-10-13 02:48:5069 permissions = kWriteFilePermissions;
70 render_view_host->FilesSelectedInChooser(files, permissions);
71}
[email protected]fb11b6a42012-03-14 07:25:1272
[email protected]53f04c82012-07-26 02:31:0973// Converts a list of FilePaths to a list of ui::SelectedFileInfo.
74std::vector<ui::SelectedFileInfo> FilePathListToSelectedFileInfoList(
[email protected]650b2d52013-02-10 03:41:4575 const std::vector<base::FilePath>& paths) {
[email protected]ddb034b2012-06-26 20:31:3976 std::vector<ui::SelectedFileInfo> selected_files;
[email protected]fb11b6a42012-03-14 07:25:1277 for (size_t i = 0; i < paths.size(); ++i) {
78 selected_files.push_back(
[email protected]53f04c82012-07-26 02:31:0979 ui::SelectedFileInfo(paths[i], paths[i]));
[email protected]fb11b6a42012-03-14 07:25:1280 }
81 return selected_files;
[email protected]600ea402011-04-12 00:01:5182}
83
[email protected]fb11b6a42012-03-14 07:25:1284} // namespace
85
[email protected]485a5272011-04-12 00:49:2986struct FileSelectHelper::ActiveDirectoryEnumeration {
[email protected]d45f7512011-06-21 21:18:2787 ActiveDirectoryEnumeration() : rvh_(NULL) {}
[email protected]485a5272011-04-12 00:49:2988
89 scoped_ptr<DirectoryListerDispatchDelegate> delegate_;
[email protected]05a814182011-04-27 19:50:3490 scoped_ptr<net::DirectoryLister> lister_;
[email protected]485a5272011-04-12 00:49:2991 RenderViewHost* rvh_;
[email protected]650b2d52013-02-10 03:41:4592 std::vector<base::FilePath> results_;
[email protected]485a5272011-04-12 00:49:2993};
94
[email protected]ba70d082010-09-10 16:54:4995FileSelectHelper::FileSelectHelper(Profile* profile)
96 : profile_(profile),
97 render_view_host_(NULL),
[email protected]ea049a02011-12-25 21:37:0998 web_contents_(NULL),
[email protected]ba70d082010-09-10 16:54:4999 select_file_dialog_(),
[email protected]9f054aa12011-09-29 19:13:45100 select_file_types_(),
[email protected]92f54082012-07-31 01:43:14101 dialog_type_(ui::SelectFileDialog::SELECT_OPEN_FILE) {
[email protected]ba70d082010-09-10 16:54:49102}
103
104FileSelectHelper::~FileSelectHelper() {
105 // There may be pending file dialogs, we need to tell them that we've gone
106 // away so they don't try and call back to us.
107 if (select_file_dialog_.get())
108 select_file_dialog_->ListenerDestroyed();
109
[email protected]600ea402011-04-12 00:01:51110 // Stop any pending directory enumeration, prevent a callback, and free
111 // allocated memory.
112 std::map<int, ActiveDirectoryEnumeration*>::iterator iter;
113 for (iter = directory_enumerations_.begin();
114 iter != directory_enumerations_.end();
115 ++iter) {
[email protected]05a814182011-04-27 19:50:34116 iter->second->lister_.reset();
[email protected]600ea402011-04-12 00:01:51117 delete iter->second;
[email protected]ba70d082010-09-10 16:54:49118 }
119}
120
[email protected]23827ec2012-08-10 22:08:08121void FileSelectHelper::DirectoryListerDispatchDelegate::OnListFile(
122 const net::DirectoryLister::DirectoryListerData& data) {
123 parent_->OnListFile(id_, data);
124}
125
126void FileSelectHelper::DirectoryListerDispatchDelegate::OnListDone(int error) {
127 parent_->OnListDone(id_, error);
128}
129
[email protected]650b2d52013-02-10 03:41:45130void FileSelectHelper::FileSelected(const base::FilePath& path,
[email protected]ba70d082010-09-10 16:54:49131 int index, void* params) {
[email protected]53f04c82012-07-26 02:31:09132 FileSelectedWithExtraInfo(ui::SelectedFileInfo(path, path), index, params);
[email protected]fb11b6a42012-03-14 07:25:12133}
134
135void FileSelectHelper::FileSelectedWithExtraInfo(
[email protected]ddb034b2012-06-26 20:31:39136 const ui::SelectedFileInfo& file,
[email protected]fb11b6a42012-03-14 07:25:12137 int index,
138 void* params) {
[email protected]ba70d082010-09-10 16:54:49139 if (!render_view_host_)
140 return;
141
[email protected]53f04c82012-07-26 02:31:09142 profile_->set_last_selected_directory(file.file_path.DirName());
[email protected]ba70d082010-09-10 16:54:49143
[email protected]650b2d52013-02-10 03:41:45144 const base::FilePath& path = file.local_path;
[email protected]92f54082012-07-31 01:43:14145 if (dialog_type_ == ui::SelectFileDialog::SELECT_FOLDER) {
[email protected]600ea402011-04-12 00:01:51146 StartNewEnumeration(path, kFileSelectEnumerationId, render_view_host_);
[email protected]ba70d082010-09-10 16:54:49147 return;
148 }
149
[email protected]ddb034b2012-06-26 20:31:39150 std::vector<ui::SelectedFileInfo> files;
[email protected]fb11b6a42012-03-14 07:25:12151 files.push_back(file);
[email protected]459fba82011-10-13 02:48:50152 NotifyRenderViewHost(render_view_host_, files, dialog_type_);
[email protected]9f054aa12011-09-29 19:13:45153
[email protected]3a29a6e2011-08-24 18:26:21154 // No members should be accessed from here on.
[email protected]9f054aa12011-09-29 19:13:45155 RunFileChooserEnd();
[email protected]ba70d082010-09-10 16:54:49156}
157
[email protected]650b2d52013-02-10 03:41:45158void FileSelectHelper::MultiFilesSelected(
159 const std::vector<base::FilePath>& files,
160 void* params) {
[email protected]ddb034b2012-06-26 20:31:39161 std::vector<ui::SelectedFileInfo> selected_files =
[email protected]53f04c82012-07-26 02:31:09162 FilePathListToSelectedFileInfoList(files);
163
[email protected]fb11b6a42012-03-14 07:25:12164 MultiFilesSelectedWithExtraInfo(selected_files, params);
165}
166
167void FileSelectHelper::MultiFilesSelectedWithExtraInfo(
[email protected]ddb034b2012-06-26 20:31:39168 const std::vector<ui::SelectedFileInfo>& files,
[email protected]fb11b6a42012-03-14 07:25:12169 void* params) {
[email protected]ba70d082010-09-10 16:54:49170 if (!files.empty())
[email protected]53f04c82012-07-26 02:31:09171 profile_->set_last_selected_directory(files[0].file_path.DirName());
[email protected]ba70d082010-09-10 16:54:49172 if (!render_view_host_)
173 return;
174
[email protected]459fba82011-10-13 02:48:50175 NotifyRenderViewHost(render_view_host_, files, dialog_type_);
[email protected]9f054aa12011-09-29 19:13:45176
[email protected]3a29a6e2011-08-24 18:26:21177 // No members should be accessed from here on.
[email protected]9f054aa12011-09-29 19:13:45178 RunFileChooserEnd();
[email protected]ba70d082010-09-10 16:54:49179}
180
181void FileSelectHelper::FileSelectionCanceled(void* params) {
182 if (!render_view_host_)
183 return;
184
185 // If the user cancels choosing a file to upload we pass back an
186 // empty vector.
[email protected]459fba82011-10-13 02:48:50187 NotifyRenderViewHost(
[email protected]ddb034b2012-06-26 20:31:39188 render_view_host_, std::vector<ui::SelectedFileInfo>(),
[email protected]fb11b6a42012-03-14 07:25:12189 dialog_type_);
[email protected]ba70d082010-09-10 16:54:49190
[email protected]3a29a6e2011-08-24 18:26:21191 // No members should be accessed from here on.
[email protected]9f054aa12011-09-29 19:13:45192 RunFileChooserEnd();
[email protected]ba70d082010-09-10 16:54:49193}
194
[email protected]650b2d52013-02-10 03:41:45195void FileSelectHelper::StartNewEnumeration(const base::FilePath& path,
[email protected]600ea402011-04-12 00:01:51196 int request_id,
197 RenderViewHost* render_view_host) {
198 scoped_ptr<ActiveDirectoryEnumeration> entry(new ActiveDirectoryEnumeration);
199 entry->rvh_ = render_view_host;
200 entry->delegate_.reset(new DirectoryListerDispatchDelegate(this, request_id));
[email protected]05a814182011-04-27 19:50:34201 entry->lister_.reset(new net::DirectoryLister(path,
202 true,
203 net::DirectoryLister::NO_SORT,
204 entry->delegate_.get()));
[email protected]600ea402011-04-12 00:01:51205 if (!entry->lister_->Start()) {
206 if (request_id == kFileSelectEnumerationId)
207 FileSelectionCanceled(NULL);
208 else
209 render_view_host->DirectoryEnumerationFinished(request_id,
210 entry->results_);
211 } else {
212 directory_enumerations_[request_id] = entry.release();
213 }
[email protected]ba70d082010-09-10 16:54:49214}
215
216void FileSelectHelper::OnListFile(
[email protected]600ea402011-04-12 00:01:51217 int id,
[email protected]ba70d082010-09-10 16:54:49218 const net::DirectoryLister::DirectoryListerData& data) {
[email protected]600ea402011-04-12 00:01:51219 ActiveDirectoryEnumeration* entry = directory_enumerations_[id];
220
[email protected]9897e092011-02-04 22:09:11221 // Directory upload returns directories via a "." file, so that
222 // empty directories are included. This util call just checks
[email protected]ba70d082010-09-10 16:54:49223 // the flags in the structure; there's no file I/O going on.
[email protected]25a4c1c2013-06-08 04:53:36224 if (data.info.IsDirectory())
[email protected]600ea402011-04-12 00:01:51225 entry->results_.push_back(data.path.Append(FILE_PATH_LITERAL(".")));
[email protected]9897e092011-02-04 22:09:11226 else
[email protected]600ea402011-04-12 00:01:51227 entry->results_.push_back(data.path);
[email protected]ba70d082010-09-10 16:54:49228}
229
[email protected]600ea402011-04-12 00:01:51230void FileSelectHelper::OnListDone(int id, int error) {
231 // This entry needs to be cleaned up when this function is done.
232 scoped_ptr<ActiveDirectoryEnumeration> entry(directory_enumerations_[id]);
233 directory_enumerations_.erase(id);
234 if (!entry->rvh_)
[email protected]ba70d082010-09-10 16:54:49235 return;
[email protected]ba70d082010-09-10 16:54:49236 if (error) {
237 FileSelectionCanceled(NULL);
238 return;
239 }
[email protected]fb11b6a42012-03-14 07:25:12240
[email protected]ddb034b2012-06-26 20:31:39241 std::vector<ui::SelectedFileInfo> selected_files =
[email protected]53f04c82012-07-26 02:31:09242 FilePathListToSelectedFileInfoList(entry->results_);
[email protected]fb11b6a42012-03-14 07:25:12243
[email protected]600ea402011-04-12 00:01:51244 if (id == kFileSelectEnumerationId)
[email protected]fb11b6a42012-03-14 07:25:12245 NotifyRenderViewHost(entry->rvh_, selected_files, dialog_type_);
[email protected]600ea402011-04-12 00:01:51246 else
247 entry->rvh_->DirectoryEnumerationFinished(id, entry->results_);
[email protected]9f054aa12011-09-29 19:13:45248
249 EnumerateDirectoryEnd();
[email protected]ba70d082010-09-10 16:54:49250}
251
[email protected]479cce782012-09-15 20:15:53252scoped_ptr<ui::SelectFileDialog::FileTypeInfo>
[email protected]92f54082012-07-31 01:43:14253FileSelectHelper::GetFileTypesFromAcceptType(
[email protected]3314c2b12011-11-02 08:05:46254 const std::vector<string16>& accept_types) {
[email protected]479cce782012-09-15 20:15:53255 scoped_ptr<ui::SelectFileDialog::FileTypeInfo> base_file_type(
256 new ui::SelectFileDialog::FileTypeInfo());
[email protected]599538b2013-02-20 14:15:14257 base_file_type->support_drive = true;
[email protected]ba70d082010-09-10 16:54:49258 if (accept_types.empty())
[email protected]479cce782012-09-15 20:15:53259 return base_file_type.Pass();
[email protected]ba70d082010-09-10 16:54:49260
[email protected]ba70d082010-09-10 16:54:49261 // Create FileTypeInfo and pre-allocate for the first extension list.
[email protected]92f54082012-07-31 01:43:14262 scoped_ptr<ui::SelectFileDialog::FileTypeInfo> file_type(
[email protected]479cce782012-09-15 20:15:53263 new ui::SelectFileDialog::FileTypeInfo(*base_file_type));
[email protected]ba70d082010-09-10 16:54:49264 file_type->include_all_files = true;
265 file_type->extensions.resize(1);
[email protected]650b2d52013-02-10 03:41:45266 std::vector<base::FilePath::StringType>* extensions =
267 &file_type->extensions.back();
[email protected]ba70d082010-09-10 16:54:49268
[email protected]f9a4c41a2012-05-30 00:05:32269 // Find the corresponding extensions.
[email protected]ba70d082010-09-10 16:54:49270 int valid_type_count = 0;
271 int description_id = 0;
[email protected]3314c2b12011-11-02 08:05:46272 for (size_t i = 0; i < accept_types.size(); ++i) {
[email protected]f9a4c41a2012-05-30 00:05:32273 std::string ascii_type = UTF16ToASCII(accept_types[i]);
274 if (!IsAcceptTypeValid(ascii_type))
275 continue;
[email protected]ba70d082010-09-10 16:54:49276
277 size_t old_extension_size = extensions->size();
[email protected]f9a4c41a2012-05-30 00:05:32278 if (ascii_type[0] == '.') {
279 // If the type starts with a period it is assumed to be a file extension
280 // so we just have to add it to the list.
[email protected]650b2d52013-02-10 03:41:45281 base::FilePath::StringType ext(ascii_type.begin(), ascii_type.end());
[email protected]f9a4c41a2012-05-30 00:05:32282 extensions->push_back(ext.substr(1));
[email protected]ba70d082010-09-10 16:54:49283 } else {
[email protected]4a66fa0e2012-09-10 06:45:20284 if (ascii_type == "image/*")
285 description_id = IDS_IMAGE_FILES;
286 else if (ascii_type == "audio/*")
287 description_id = IDS_AUDIO_FILES;
288 else if (ascii_type == "video/*")
289 description_id = IDS_VIDEO_FILES;
290
[email protected]f9a4c41a2012-05-30 00:05:32291 net::GetExtensionsForMimeType(ascii_type, extensions);
[email protected]ba70d082010-09-10 16:54:49292 }
293
294 if (extensions->size() > old_extension_size)
295 valid_type_count++;
296 }
297
[email protected]cbcd12ed2010-12-16 23:42:57298 // If no valid extension is added, bail out.
299 if (valid_type_count == 0)
[email protected]479cce782012-09-15 20:15:53300 return base_file_type.Pass();
[email protected]cbcd12ed2010-12-16 23:42:57301
[email protected]ba70d082010-09-10 16:54:49302 // Use a generic description "Custom Files" if either of the following is
303 // true:
304 // 1) There're multiple types specified, like "audio/*,video/*"
305 // 2) There're multiple extensions for a MIME type without parameter, like
306 // "ehtml,shtml,htm,html" for "text/html". On Windows, the select file
307 // dialog uses the first extension in the list to form the description,
308 // like "EHTML Files". This is not what we want.
309 if (valid_type_count > 1 ||
310 (valid_type_count == 1 && description_id == 0 && extensions->size() > 1))
311 description_id = IDS_CUSTOM_FILES;
312
313 if (description_id) {
314 file_type->extension_description_overrides.push_back(
315 l10n_util::GetStringUTF16(description_id));
316 }
317
[email protected]479cce782012-09-15 20:15:53318 return file_type.Pass();
[email protected]ba70d082010-09-10 16:54:49319}
320
[email protected]33f8ad52012-05-22 18:10:13321// static
322void FileSelectHelper::RunFileChooser(content::WebContents* tab,
323 const FileChooserParams& params) {
324 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
325 // FileSelectHelper will keep itself alive until it sends the result message.
326 scoped_refptr<FileSelectHelper> file_select_helper(
327 new FileSelectHelper(profile));
328 file_select_helper->RunFileChooser(tab->GetRenderViewHost(), tab, params);
329}
330
331// static
332void FileSelectHelper::EnumerateDirectory(content::WebContents* tab,
333 int request_id,
[email protected]650b2d52013-02-10 03:41:45334 const base::FilePath& path) {
[email protected]33f8ad52012-05-22 18:10:13335 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
336 // FileSelectHelper will keep itself alive until it sends the result message.
337 scoped_refptr<FileSelectHelper> file_select_helper(
338 new FileSelectHelper(profile));
339 file_select_helper->EnumerateDirectory(
340 request_id, tab->GetRenderViewHost(), path);
341}
342
343void FileSelectHelper::RunFileChooser(RenderViewHost* render_view_host,
344 content::WebContents* web_contents,
345 const FileChooserParams& params) {
[email protected]ba70d082010-09-10 16:54:49346 DCHECK(!render_view_host_);
[email protected]ea049a02011-12-25 21:37:09347 DCHECK(!web_contents_);
[email protected]ba70d082010-09-10 16:54:49348 render_view_host_ = render_view_host;
[email protected]ea049a02011-12-25 21:37:09349 web_contents_ = web_contents;
[email protected]ba70d082010-09-10 16:54:49350 notification_registrar_.RemoveAll();
[email protected]432115822011-07-10 15:52:27351 notification_registrar_.Add(
352 this, content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
[email protected]6c2381d2011-10-19 02:52:53353 content::Source<RenderWidgetHost>(render_view_host_));
[email protected]9f054aa12011-09-29 19:13:45354 notification_registrar_.Add(
[email protected]ea049a02011-12-25 21:37:09355 this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
356 content::Source<WebContents>(web_contents_));
[email protected]9f054aa12011-09-29 19:13:45357
358 BrowserThread::PostTask(
359 BrowserThread::FILE, FROM_HERE,
360 base::Bind(&FileSelectHelper::RunFileChooserOnFileThread, this, params));
361
362 // Because this class returns notifications to the RenderViewHost, it is
363 // difficult for callers to know how long to keep a reference to this
364 // instance. We AddRef() here to keep the instance alive after we return
365 // to the caller, until the last callback is received from the file dialog.
366 // At that point, we must call RunFileChooserEnd().
367 AddRef();
368}
369
370void FileSelectHelper::RunFileChooserOnFileThread(
[email protected]33f8ad52012-05-22 18:10:13371 const FileChooserParams& params) {
[email protected]479cce782012-09-15 20:15:53372 select_file_types_ = GetFileTypesFromAcceptType(params.accept_types);
[email protected]9f054aa12011-09-29 19:13:45373
374 BrowserThread::PostTask(
375 BrowserThread::UI, FROM_HERE,
376 base::Bind(&FileSelectHelper::RunFileChooserOnUIThread, this, params));
377}
378
379void FileSelectHelper::RunFileChooserOnUIThread(
[email protected]33f8ad52012-05-22 18:10:13380 const FileChooserParams& params) {
[email protected]ea049a02011-12-25 21:37:09381 if (!render_view_host_ || !web_contents_) {
[email protected]b95b08d2011-12-15 20:23:16382 // If the renderer was destroyed before we started, just cancel the
383 // operation.
384 RunFileChooserEnd();
[email protected]9f054aa12011-09-29 19:13:45385 return;
[email protected]b95b08d2011-12-15 20:23:16386 }
[email protected]ba70d082010-09-10 16:54:49387
[email protected]92f54082012-07-31 01:43:14388 select_file_dialog_ = ui::SelectFileDialog::Create(
[email protected]6e1fcd12012-07-02 17:14:20389 this, new ChromeSelectFilePolicy(web_contents_));
[email protected]ba70d082010-09-10 16:54:49390
391 switch (params.mode) {
[email protected]33f8ad52012-05-22 18:10:13392 case FileChooserParams::Open:
[email protected]92f54082012-07-31 01:43:14393 dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
[email protected]ba70d082010-09-10 16:54:49394 break;
[email protected]33f8ad52012-05-22 18:10:13395 case FileChooserParams::OpenMultiple:
[email protected]92f54082012-07-31 01:43:14396 dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
[email protected]ba70d082010-09-10 16:54:49397 break;
[email protected]33f8ad52012-05-22 18:10:13398 case FileChooserParams::OpenFolder:
[email protected]92f54082012-07-31 01:43:14399 dialog_type_ = ui::SelectFileDialog::SELECT_FOLDER;
[email protected]ba70d082010-09-10 16:54:49400 break;
[email protected]33f8ad52012-05-22 18:10:13401 case FileChooserParams::Save:
[email protected]92f54082012-07-31 01:43:14402 dialog_type_ = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
[email protected]ba70d082010-09-10 16:54:49403 break;
404 default:
[email protected]92f54082012-07-31 01:43:14405 // Prevent warning.
406 dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
[email protected]ba70d082010-09-10 16:54:49407 NOTREACHED();
408 }
[email protected]4e9149a2012-08-15 20:43:59409
[email protected]650b2d52013-02-10 03:41:45410 base::FilePath default_file_name = params.default_file_name.IsAbsolute() ?
[email protected]4e9149a2012-08-15 20:43:59411 params.default_file_name :
412 profile_->last_selected_directory().Append(params.default_file_name);
[email protected]ba70d082010-09-10 16:54:49413
414 gfx::NativeWindow owning_window =
[email protected]9f76c1e2012-03-05 15:15:58415 platform_util::GetTopLevel(render_view_host_->GetView()->GetNativeView());
[email protected]d9898912011-04-15 21:10:00416
[email protected]2d02a2002012-09-18 21:47:56417#if defined(OS_ANDROID)
418 // Android needs the original MIME types and an additional capture value.
[email protected]b7b4beb2013-07-09 14:06:50419 std::pair<std::vector<string16>, bool> accept_types =
420 std::make_pair(params.accept_types, params.capture);
[email protected]2d02a2002012-09-18 21:47:56421#endif
422
[email protected]9f054aa12011-09-29 19:13:45423 select_file_dialog_->SelectFile(
424 dialog_type_,
425 params.title,
426 default_file_name,
427 select_file_types_.get(),
[email protected]007b3f82013-04-09 08:46:45428 select_file_types_.get() && !select_file_types_->extensions.empty()
429 ? 1
430 : 0, // 1-based index of default extension to show.
431 base::FilePath::StringType(),
[email protected]9f054aa12011-09-29 19:13:45432 owning_window,
[email protected]b8452fa2012-06-15 01:41:41433#if defined(OS_ANDROID)
[email protected]2d02a2002012-09-18 21:47:56434 &accept_types);
[email protected]b8452fa2012-06-15 01:41:41435#else
[email protected]9f054aa12011-09-29 19:13:45436 NULL);
[email protected]b8452fa2012-06-15 01:41:41437#endif
[email protected]9f054aa12011-09-29 19:13:45438
439 select_file_types_.reset();
440}
441
442// This method is called when we receive the last callback from the file
443// chooser dialog. Perform any cleanup and release the reference we added
444// in RunFileChooser().
445void FileSelectHelper::RunFileChooserEnd() {
446 render_view_host_ = NULL;
[email protected]ea049a02011-12-25 21:37:09447 web_contents_ = NULL;
[email protected]9f054aa12011-09-29 19:13:45448 Release();
[email protected]ba70d082010-09-10 16:54:49449}
450
[email protected]600ea402011-04-12 00:01:51451void FileSelectHelper::EnumerateDirectory(int request_id,
452 RenderViewHost* render_view_host,
[email protected]650b2d52013-02-10 03:41:45453 const base::FilePath& path) {
[email protected]9f054aa12011-09-29 19:13:45454
455 // Because this class returns notifications to the RenderViewHost, it is
456 // difficult for callers to know how long to keep a reference to this
457 // instance. We AddRef() here to keep the instance alive after we return
458 // to the caller, until the last callback is received from the enumeration
459 // code. At that point, we must call EnumerateDirectoryEnd().
460 AddRef();
[email protected]600ea402011-04-12 00:01:51461 StartNewEnumeration(path, request_id, render_view_host);
462}
463
[email protected]9f054aa12011-09-29 19:13:45464// This method is called when we receive the last callback from the enumeration
465// code. Perform any cleanup and release the reference we added in
466// EnumerateDirectory().
467void FileSelectHelper::EnumerateDirectoryEnd() {
468 Release();
469}
470
[email protected]432115822011-07-10 15:52:27471void FileSelectHelper::Observe(int type,
[email protected]6c2381d2011-10-19 02:52:53472 const content::NotificationSource& source,
473 const content::NotificationDetails& details) {
[email protected]9f054aa12011-09-29 19:13:45474 switch (type) {
475 case content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED: {
[email protected]6c2381d2011-10-19 02:52:53476 DCHECK(content::Source<RenderWidgetHost>(source).ptr() ==
477 render_view_host_);
[email protected]9f054aa12011-09-29 19:13:45478 render_view_host_ = NULL;
479 break;
480 }
481
[email protected]ea049a02011-12-25 21:37:09482 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: {
483 DCHECK(content::Source<WebContents>(source).ptr() == web_contents_);
484 web_contents_ = NULL;
[email protected]9f054aa12011-09-29 19:13:45485 break;
486 }
487
488 default:
489 NOTREACHED();
490 }
[email protected]ba70d082010-09-10 16:54:49491}
[email protected]f9a4c41a2012-05-30 00:05:32492
493// static
494bool FileSelectHelper::IsAcceptTypeValid(const std::string& accept_type) {
495 // TODO(raymes): This only does some basic checks, extend to test more cases.
496 // A 1 character accept type will always be invalid (either a "." in the case
497 // of an extension or a "/" in the case of a MIME type).
498 std::string unused;
499 if (accept_type.length() <= 1 ||
500 StringToLowerASCII(accept_type) != accept_type ||
501 TrimWhitespaceASCII(accept_type, TRIM_ALL, &unused) != TRIM_NONE) {
502 return false;
503 }
504 return true;
505}