OLD | NEW |
(Empty) | |
| 1 // 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 |
| 7 #include "app/l10n_util.h" |
| 8 #include "base/file_util.h" |
| 9 #include "base/string_split.h" |
| 10 #include "base/string_util.h" |
| 11 #include "base/utf_string_conversions.h" |
| 12 #include "net/base/mime_util.h" |
| 13 #include "chrome/browser/platform_util.h" |
| 14 #include "chrome/browser/profile.h" |
| 15 #include "chrome/browser/renderer_host/render_view_host.h" |
| 16 #include "chrome/browser/renderer_host/render_widget_host_view.h" |
| 17 #include "chrome/browser/shell_dialogs.h" |
| 18 #include "chrome/browser/tab_contents/tab_contents.h" |
| 19 #include "chrome/browser/tab_contents/tab_contents_view.h" |
| 20 #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 |
| 25 FileSelectHelper::FileSelectHelper(Profile* profile) |
| 26 : profile_(profile), |
| 27 render_view_host_(NULL), |
| 28 select_file_dialog_(), |
| 29 dialog_type_(SelectFileDialog::SELECT_OPEN_FILE) { |
| 30 } |
| 31 |
| 32 FileSelectHelper::~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 |
| 45 void 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 |
| 64 void 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 |
| 76 void 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 |
| 88 void 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 |
| 97 void 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 |
| 107 void 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 |
| 122 SelectFileDialog::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 |
| 188 void FileSelectHelper::RunFileChooser( |
| 189 RenderViewHost* render_view_host, |
| 190 const ViewHostMsg_RunFileChooser_Params ¶ms) { |
| 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 |
| 236 void 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 } |
OLD | NEW |