blob: a1b31cde3df2b8d4deee974904d29ee2a7f0c043 [file] [log] [blame]
[email protected]ba70d082010-09-10 16:54:491// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// 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>
8
[email protected]ba70d082010-09-10 16:54:499#include "app/l10n_util.h"
10#include "base/file_util.h"
11#include "base/string_split.h"
12#include "base/string_util.h"
13#include "base/utf_string_conversions.h"
14#include "net/base/mime_util.h"
15#include "chrome/browser/platform_util.h"
[email protected]8ecad5e2010-12-02 21:18:3316#include "chrome/browser/profiles/profile.h"
[email protected]ba70d082010-09-10 16:54:4917#include "chrome/browser/renderer_host/render_view_host.h"
18#include "chrome/browser/renderer_host/render_widget_host_view.h"
19#include "chrome/browser/shell_dialogs.h"
[email protected]ba70d082010-09-10 16:54:4920#include "chrome/common/notification_details.h"
21#include "chrome/common/notification_source.h"
22#include "chrome/common/render_messages_params.h"
23#include "grit/generated_resources.h"
24
25FileSelectHelper::FileSelectHelper(Profile* profile)
26 : profile_(profile),
27 render_view_host_(NULL),
28 select_file_dialog_(),
29 dialog_type_(SelectFileDialog::SELECT_OPEN_FILE) {
30}
31
32FileSelectHelper::~FileSelectHelper() {
33 // There may be pending file dialogs, we need to tell them that we've gone
34 // away so they don't try and call back to us.
35 if (select_file_dialog_.get())
36 select_file_dialog_->ListenerDestroyed();
37
38 // Stop any pending directory enumeration and prevent a callback.
39 if (directory_lister_.get()) {
40 directory_lister_->set_delegate(NULL);
41 directory_lister_->Cancel();
42 }
43}
44
45void FileSelectHelper::FileSelected(const FilePath& path,
46 int index, void* params) {
47 if (!render_view_host_)
48 return;
49
50 profile_->set_last_selected_directory(path.DirName());
51
52 if (dialog_type_ == SelectFileDialog::SELECT_FOLDER) {
53 DirectorySelected(path);
54 return;
55 }
56
57 std::vector<FilePath> files;
58 files.push_back(path);
59 render_view_host_->FilesSelectedInChooser(files);
60 // We are done with this showing of the dialog.
61 render_view_host_ = NULL;
62}
63
64void FileSelectHelper::MultiFilesSelected(const std::vector<FilePath>& files,
65 void* params) {
66 if (!files.empty())
67 profile_->set_last_selected_directory(files[0].DirName());
68 if (!render_view_host_)
69 return;
70
71 render_view_host_->FilesSelectedInChooser(files);
72 // We are done with this showing of the dialog.
73 render_view_host_ = NULL;
74}
75
76void FileSelectHelper::FileSelectionCanceled(void* params) {
77 if (!render_view_host_)
78 return;
79
80 // If the user cancels choosing a file to upload we pass back an
81 // empty vector.
82 render_view_host_->FilesSelectedInChooser(std::vector<FilePath>());
83
84 // We are done with this showing of the dialog.
85 render_view_host_ = NULL;
86}
87
88void FileSelectHelper::DirectorySelected(const FilePath& path) {
89 directory_lister_ = new net::DirectoryLister(path,
90 true,
91 net::DirectoryLister::NO_SORT,
92 this);
93 if (!directory_lister_->Start())
94 FileSelectionCanceled(NULL);
95}
96
97void FileSelectHelper::OnListFile(
98 const net::DirectoryLister::DirectoryListerData& data) {
99 // Directory upload only cares about files. This util call just checks
100 // the flags in the structure; there's no file I/O going on.
101 if (file_util::FileEnumerator::IsDirectory(data.info))
102 return;
103
104 directory_lister_results_.push_back(data.path);
105}
106
107void FileSelectHelper::OnListDone(int error) {
108 if (!render_view_host_)
109 return;
110
111 if (error) {
112 FileSelectionCanceled(NULL);
113 return;
114 }
115
116 render_view_host_->FilesSelectedInChooser(directory_lister_results_);
117 render_view_host_ = NULL;
118 directory_lister_ = NULL;
119 directory_lister_results_.clear();
120}
121
122SelectFileDialog::FileTypeInfo* FileSelectHelper::GetFileTypesFromAcceptType(
123 const string16& accept_types) {
124 if (accept_types.empty())
125 return NULL;
126
127 // Split the accept-type string on commas.
128 std::vector<string16> mime_types;
129 base::SplitStringUsingSubstr(accept_types, ASCIIToUTF16(","), &mime_types);
130 if (mime_types.empty())
131 return NULL;
132
133 // Create FileTypeInfo and pre-allocate for the first extension list.
134 scoped_ptr<SelectFileDialog::FileTypeInfo> file_type(
135 new SelectFileDialog::FileTypeInfo());
136 file_type->include_all_files = true;
137 file_type->extensions.resize(1);
138 std::vector<FilePath::StringType>* extensions = &file_type->extensions.back();
139
140 // Find the correspondinge extensions.
141 int valid_type_count = 0;
142 int description_id = 0;
143 for (size_t i = 0; i < mime_types.size(); ++i) {
144 string16 mime_type = mime_types[i];
145 std::string ascii_mime_type = StringToLowerASCII(UTF16ToASCII(mime_type));
146
147 TrimWhitespace(ascii_mime_type, TRIM_ALL, &ascii_mime_type);
148 if (ascii_mime_type.empty())
149 continue;
150
151 size_t old_extension_size = extensions->size();
152 if (ascii_mime_type == "image/*") {
153 description_id = IDS_IMAGE_FILES;
154 net::GetImageExtensions(extensions);
155 } else if (ascii_mime_type == "audio/*") {
156 description_id = IDS_AUDIO_FILES;
157 net::GetAudioExtensions(extensions);
158 } else if (ascii_mime_type == "video/*") {
159 description_id = IDS_VIDEO_FILES;
160 net::GetVideoExtensions(extensions);
161 } else {
162 net::GetExtensionsForMimeType(ascii_mime_type, extensions);
163 }
164
165 if (extensions->size() > old_extension_size)
166 valid_type_count++;
167 }
168
169 // Use a generic description "Custom Files" if either of the following is
170 // true:
171 // 1) There're multiple types specified, like "audio/*,video/*"
172 // 2) There're multiple extensions for a MIME type without parameter, like
173 // "ehtml,shtml,htm,html" for "text/html". On Windows, the select file
174 // dialog uses the first extension in the list to form the description,
175 // like "EHTML Files". This is not what we want.
176 if (valid_type_count > 1 ||
177 (valid_type_count == 1 && description_id == 0 && extensions->size() > 1))
178 description_id = IDS_CUSTOM_FILES;
179
180 if (description_id) {
181 file_type->extension_description_overrides.push_back(
182 l10n_util::GetStringUTF16(description_id));
183 }
184
185 return file_type.release();
186}
187
188void FileSelectHelper::RunFileChooser(
189 RenderViewHost* render_view_host,
190 const ViewHostMsg_RunFileChooser_Params &params) {
191 DCHECK(!render_view_host_);
192 render_view_host_ = render_view_host;
193 notification_registrar_.RemoveAll();
194 notification_registrar_.Add(this,
195 NotificationType::RENDER_WIDGET_HOST_DESTROYED,
196 Source<RenderViewHost>(render_view_host));
197
198 if (!select_file_dialog_.get())
199 select_file_dialog_ = SelectFileDialog::Create(this);
200
201 switch (params.mode) {
202 case ViewHostMsg_RunFileChooser_Params::Open:
203 dialog_type_ = SelectFileDialog::SELECT_OPEN_FILE;
204 break;
205 case ViewHostMsg_RunFileChooser_Params::OpenMultiple:
206 dialog_type_ = SelectFileDialog::SELECT_OPEN_MULTI_FILE;
207 break;
208 case ViewHostMsg_RunFileChooser_Params::OpenFolder:
209 dialog_type_ = SelectFileDialog::SELECT_FOLDER;
210 break;
211 case ViewHostMsg_RunFileChooser_Params::Save:
212 dialog_type_ = SelectFileDialog::SELECT_SAVEAS_FILE;
213 break;
214 default:
215 dialog_type_ = SelectFileDialog::SELECT_OPEN_FILE; // Prevent warning.
216 NOTREACHED();
217 }
218 scoped_ptr<SelectFileDialog::FileTypeInfo> file_types(
219 GetFileTypesFromAcceptType(params.accept_types));
220 FilePath default_file_name = params.default_file_name;
221 if (default_file_name.empty())
222 default_file_name = profile_->last_selected_directory();
223
224 gfx::NativeWindow owning_window =
225 platform_util::GetTopLevel(render_view_host_->view()->GetNativeView());
226 select_file_dialog_->SelectFile(dialog_type_,
227 params.title,
228 default_file_name,
229 file_types.get(),
230 0,
231 FILE_PATH_LITERAL(""),
232 owning_window,
233 NULL);
234}
235
236void FileSelectHelper::Observe(NotificationType type,
237 const NotificationSource& source,
238 const NotificationDetails& details) {
239 DCHECK(type == NotificationType::RENDER_WIDGET_HOST_DESTROYED);
240 DCHECK(Details<RenderViewHost>(details).ptr() == render_view_host_);
241 render_view_host_ = NULL;
242}