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