| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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/ui/shell_dialogs.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/file_path.h" | |
| 9 #include "base/json/json_reader.h" | |
| 10 #include "base/memory/scoped_ptr.h" | |
| 11 #include "base/string_util.h" | |
| 12 #include "base/sys_string_conversions.h" | |
| 13 #include "base/utf_string_conversions.h" | |
| 14 #include "base/values.h" | |
| 15 #include "chrome/browser/profiles/profile_manager.h" | |
| 16 #include "chrome/browser/ui/browser.h" | |
| 17 #include "chrome/browser/ui/browser_dialogs.h" | |
| 18 #include "chrome/browser/ui/browser_list.h" | |
| 19 #include "chrome/browser/ui/views/html_dialog_view.h" | |
| 20 #include "chrome/browser/ui/webui/html_dialog_ui.h" | |
| 21 #include "chrome/common/url_constants.h" | |
| 22 #include "content/browser/tab_contents/tab_contents.h" | |
| 23 #include "content/public/browser/browser_thread.h" | |
| 24 #include "grit/generated_resources.h" | |
| 25 #include "ui/base/l10n/l10n_util.h" | |
| 26 #include "views/window/non_client_view.h" | |
| 27 | |
| 28 using content::BrowserThread; | |
| 29 | |
| 30 namespace { | |
| 31 | |
| 32 const char kKeyNamePath[] = "path"; | |
| 33 const int kSaveCompletePageIndex = 2; | |
| 34 | |
| 35 } // namespace | |
| 36 | |
| 37 // Implementation of SelectFileDialog that shows an UI for choosing a file | |
| 38 // or folder. | |
| 39 class SelectFileDialogImpl : public SelectFileDialog { | |
| 40 public: | |
| 41 explicit SelectFileDialogImpl(Listener* listener); | |
| 42 | |
| 43 // BaseShellDialog implementation. | |
| 44 virtual bool IsRunning(gfx::NativeWindow parent_window) const; | |
| 45 virtual void ListenerDestroyed(); | |
| 46 | |
| 47 protected: | |
| 48 // SelectFileDialog implementation. | |
| 49 // |params| is user data we pass back via the Listener interface. | |
| 50 virtual void SelectFileImpl(Type type, | |
| 51 const string16& title, | |
| 52 const FilePath& default_path, | |
| 53 const FileTypeInfo* file_types, | |
| 54 int file_type_index, | |
| 55 const FilePath::StringType& default_extension, | |
| 56 gfx::NativeWindow owning_window, | |
| 57 void* params); | |
| 58 virtual bool HasMultipleFileTypeChoicesImpl(); | |
| 59 | |
| 60 private: | |
| 61 virtual ~SelectFileDialogImpl(); | |
| 62 | |
| 63 class FileBrowseDelegate : public HtmlDialogUIDelegate { | |
| 64 public: | |
| 65 FileBrowseDelegate(SelectFileDialogImpl* owner, | |
| 66 Type type, | |
| 67 const std::wstring& title, | |
| 68 const FilePath& default_path, | |
| 69 const FileTypeInfo* file_types, | |
| 70 int file_type_index, | |
| 71 const FilePath::StringType& default_extension, | |
| 72 gfx::NativeWindow parent, | |
| 73 void* params); | |
| 74 | |
| 75 // Owner of this FileBrowseDelegate. | |
| 76 scoped_refptr<SelectFileDialogImpl> owner_; | |
| 77 | |
| 78 // Parent window. | |
| 79 gfx::NativeWindow parent_; | |
| 80 | |
| 81 // The type of dialog we are showing the user. | |
| 82 Type type_; | |
| 83 | |
| 84 // The dialog title. | |
| 85 string16 title_; | |
| 86 | |
| 87 // Default path of the file dialog. | |
| 88 FilePath default_path_; | |
| 89 | |
| 90 // The file filters. | |
| 91 FileTypeInfo file_types_; | |
| 92 | |
| 93 // The index of the default selected file filter. | |
| 94 // Note: This starts from 1, not 0. | |
| 95 int file_type_index_; | |
| 96 | |
| 97 // Default extension to be added to file if user does not type one. | |
| 98 FilePath::StringType default_extension_; | |
| 99 | |
| 100 // Associated user data. | |
| 101 void* params_; | |
| 102 | |
| 103 private: | |
| 104 ~FileBrowseDelegate(); | |
| 105 | |
| 106 // Overridden from HtmlDialogUI::Delegate: | |
| 107 virtual bool IsDialogModal() const OVERRIDE; | |
| 108 virtual string16 GetDialogTitle() const OVERRIDE; | |
| 109 virtual GURL GetDialogContentURL() const OVERRIDE; | |
| 110 virtual void GetWebUIMessageHandlers( | |
| 111 std::vector<WebUIMessageHandler*>* handlers) const OVERRIDE; | |
| 112 virtual void GetDialogSize(gfx::Size* size) const OVERRIDE; | |
| 113 virtual std::string GetDialogArgs() const OVERRIDE; | |
| 114 virtual void OnDialogClosed(const std::string& json_retval) OVERRIDE; | |
| 115 virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) | |
| 116 OVERRIDE { | |
| 117 } | |
| 118 virtual bool ShouldShowDialogTitle() const OVERRIDE { return true; } | |
| 119 virtual bool HandleContextMenu(const ContextMenuParams& params) OVERRIDE { | |
| 120 return true; | |
| 121 } | |
| 122 | |
| 123 DISALLOW_COPY_AND_ASSIGN(FileBrowseDelegate); | |
| 124 }; | |
| 125 | |
| 126 class FileBrowseDelegateHandler : public WebUIMessageHandler { | |
| 127 public: | |
| 128 explicit FileBrowseDelegateHandler(FileBrowseDelegate* delegate); | |
| 129 | |
| 130 // WebUIMessageHandler implementation. | |
| 131 virtual void RegisterMessages() OVERRIDE; | |
| 132 | |
| 133 // Callback for the "setDialogTitle" message. | |
| 134 void HandleSetDialogTitle(const ListValue* args); | |
| 135 | |
| 136 private: | |
| 137 FileBrowseDelegate* delegate_; | |
| 138 | |
| 139 DISALLOW_COPY_AND_ASSIGN(FileBrowseDelegateHandler); | |
| 140 }; | |
| 141 | |
| 142 // Notification from FileBrowseDelegate when file browse UI is dismissed. | |
| 143 void OnDialogClosed(FileBrowseDelegate* delegate, const std::string& json); | |
| 144 | |
| 145 // The set of all parent windows for which we are currently running dialogs. | |
| 146 std::set<gfx::NativeWindow> parents_; | |
| 147 | |
| 148 // The set of all FileBrowseDelegate that we are currently running. | |
| 149 std::set<FileBrowseDelegate*> delegates_; | |
| 150 | |
| 151 DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImpl); | |
| 152 }; | |
| 153 | |
| 154 // static | |
| 155 SelectFileDialog* SelectFileDialog::Create(Listener* listener) { | |
| 156 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 157 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 158 return new SelectFileDialogImpl(listener); | |
| 159 } | |
| 160 | |
| 161 SelectFileDialogImpl::SelectFileDialogImpl(Listener* listener) | |
| 162 : SelectFileDialog(listener) { | |
| 163 } | |
| 164 | |
| 165 SelectFileDialogImpl::~SelectFileDialogImpl() { | |
| 166 // All dialogs should be dismissed by now. | |
| 167 DCHECK(parents_.empty() && delegates_.empty()); | |
| 168 } | |
| 169 | |
| 170 bool SelectFileDialogImpl::IsRunning(gfx::NativeWindow parent_window) const { | |
| 171 return parent_window && parents_.find(parent_window) != parents_.end(); | |
| 172 } | |
| 173 | |
| 174 void SelectFileDialogImpl::ListenerDestroyed() { | |
| 175 listener_ = NULL; | |
| 176 } | |
| 177 | |
| 178 void SelectFileDialogImpl::SelectFileImpl( | |
| 179 Type type, | |
| 180 const string16& title, | |
| 181 const FilePath& default_path, | |
| 182 const FileTypeInfo* file_types, | |
| 183 int file_type_index, | |
| 184 const FilePath::StringType& default_extension, | |
| 185 gfx::NativeWindow owning_window, | |
| 186 void* params) { | |
| 187 std::wstring title_string; | |
| 188 if (title.empty()) { | |
| 189 int string_id; | |
| 190 switch (type) { | |
| 191 case SELECT_FOLDER: | |
| 192 string_id = IDS_SELECT_FOLDER_DIALOG_TITLE; | |
| 193 break; | |
| 194 case SELECT_OPEN_FILE: | |
| 195 case SELECT_OPEN_MULTI_FILE: | |
| 196 string_id = IDS_OPEN_FILE_DIALOG_TITLE; | |
| 197 break; | |
| 198 case SELECT_SAVEAS_FILE: | |
| 199 string_id = IDS_SAVE_AS_DIALOG_TITLE; | |
| 200 break; | |
| 201 default: | |
| 202 NOTREACHED(); | |
| 203 return; | |
| 204 } | |
| 205 title_string = UTF16ToWide(l10n_util::GetStringUTF16(string_id)); | |
| 206 } else { | |
| 207 title_string = UTF16ToWide(title); | |
| 208 } | |
| 209 | |
| 210 if (owning_window) | |
| 211 parents_.insert(owning_window); | |
| 212 | |
| 213 FileBrowseDelegate* file_browse_delegate = new FileBrowseDelegate(this, | |
| 214 type, title_string, default_path, file_types, file_type_index, | |
| 215 default_extension, owning_window, params); | |
| 216 delegates_.insert(file_browse_delegate); | |
| 217 | |
| 218 Browser* browser = BrowserList::FindBrowserWithWindow(owning_window); | |
| 219 // As SelectFile may be invoked after a delay, it is entirely possible for | |
| 220 // it be invoked when no browser is around. Silently ignore this case. | |
| 221 if (browser) | |
| 222 browser->BrowserShowHtmlDialog(file_browse_delegate, owning_window); | |
| 223 } | |
| 224 | |
| 225 bool SelectFileDialogImpl::HasMultipleFileTypeChoicesImpl() { | |
| 226 return file_types_.extensions.size() > 1; | |
| 227 } | |
| 228 | |
| 229 void SelectFileDialogImpl::OnDialogClosed(FileBrowseDelegate* delegate, | |
| 230 const std::string& json) { | |
| 231 // Nothing to do if listener_ is gone. | |
| 232 | |
| 233 if (!listener_) | |
| 234 return; | |
| 235 | |
| 236 bool notification_fired = false; | |
| 237 | |
| 238 if (!json.empty()) { | |
| 239 scoped_ptr<Value> value(base::JSONReader::Read(json, false)); | |
| 240 if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY)) { | |
| 241 // Bad json value returned. | |
| 242 NOTREACHED(); | |
| 243 } else { | |
| 244 const DictionaryValue* dict = static_cast<DictionaryValue*>(value.get()); | |
| 245 if (delegate->type_ == SELECT_OPEN_FILE || | |
| 246 delegate->type_ == SELECT_SAVEAS_FILE || | |
| 247 delegate->type_ == SELECT_FOLDER) { | |
| 248 std::string path_string; | |
| 249 if (dict->HasKey(kKeyNamePath) && | |
| 250 dict->GetString(kKeyNamePath, &path_string)) { | |
| 251 #if defined(OS_WIN) | |
| 252 FilePath path(base::SysUTF8ToWide(path_string)); | |
| 253 #else | |
| 254 FilePath path( | |
| 255 base::SysWideToNativeMB(base::SysUTF8ToWide(path_string))); | |
| 256 #endif | |
| 257 listener_-> | |
| 258 FileSelected(path, kSaveCompletePageIndex, delegate->params_); | |
| 259 notification_fired = true; | |
| 260 } | |
| 261 } else if (delegate->type_ == SELECT_OPEN_MULTI_FILE) { | |
| 262 ListValue* paths_value = NULL; | |
| 263 if (dict->HasKey(kKeyNamePath) && | |
| 264 dict->GetList(kKeyNamePath, &paths_value) && | |
| 265 paths_value) { | |
| 266 std::vector<FilePath> paths; | |
| 267 paths.reserve(paths_value->GetSize()); | |
| 268 for (size_t i = 0; i < paths_value->GetSize(); ++i) { | |
| 269 std::string path_string; | |
| 270 if (paths_value->GetString(i, &path_string) && | |
| 271 !path_string.empty()) { | |
| 272 #if defined(OS_WIN) | |
| 273 FilePath path(base::SysUTF8ToWide(path_string)); | |
| 274 #else | |
| 275 FilePath path( | |
| 276 base::SysWideToNativeMB(base::SysUTF8ToWide(path_string))); | |
| 277 #endif | |
| 278 paths.push_back(path); | |
| 279 } | |
| 280 } | |
| 281 | |
| 282 listener_->MultiFilesSelected(paths, delegate->params_); | |
| 283 notification_fired = true; | |
| 284 } | |
| 285 } else { | |
| 286 NOTREACHED(); | |
| 287 } | |
| 288 } | |
| 289 } | |
| 290 | |
| 291 // Always notify listener when dialog is dismissed. | |
| 292 if (!notification_fired) | |
| 293 listener_->FileSelectionCanceled(delegate->params_); | |
| 294 | |
| 295 parents_.erase(delegate->parent_); | |
| 296 delegates_.erase(delegate); | |
| 297 } | |
| 298 | |
| 299 SelectFileDialogImpl::FileBrowseDelegate::FileBrowseDelegate( | |
| 300 SelectFileDialogImpl* owner, | |
| 301 Type type, | |
| 302 const std::wstring& title, | |
| 303 const FilePath& default_path, | |
| 304 const FileTypeInfo* file_types, | |
| 305 int file_type_index, | |
| 306 const FilePath::StringType& default_extension, | |
| 307 gfx::NativeWindow parent, | |
| 308 void* params) | |
| 309 : owner_(owner), | |
| 310 parent_(parent), | |
| 311 type_(type), | |
| 312 title_(WideToUTF16Hack(title)), | |
| 313 default_path_(default_path), | |
| 314 file_type_index_(file_type_index), | |
| 315 default_extension_(default_extension), | |
| 316 params_(params) { | |
| 317 if (file_types) | |
| 318 file_types_ = *file_types; | |
| 319 else | |
| 320 file_types_.include_all_files = true; | |
| 321 } | |
| 322 | |
| 323 SelectFileDialogImpl::FileBrowseDelegate::~FileBrowseDelegate() { | |
| 324 } | |
| 325 | |
| 326 bool SelectFileDialogImpl::FileBrowseDelegate::IsDialogModal() const { | |
| 327 return true; | |
| 328 } | |
| 329 | |
| 330 string16 SelectFileDialogImpl::FileBrowseDelegate::GetDialogTitle() const { | |
| 331 return title_; | |
| 332 } | |
| 333 | |
| 334 GURL SelectFileDialogImpl::FileBrowseDelegate::GetDialogContentURL() const { | |
| 335 #if defined(USE_AURA) | |
| 336 // TODO(saintlou): The current SelectFileDialogImpl assumes chromeos==1 | |
| 337 std::string url_string; | |
| 338 #else | |
| 339 std::string url_string(chrome::kChromeUIFileBrowseURL); | |
| 340 #endif | |
| 341 return GURL(url_string); | |
| 342 } | |
| 343 | |
| 344 void SelectFileDialogImpl::FileBrowseDelegate::GetWebUIMessageHandlers( | |
| 345 std::vector<WebUIMessageHandler*>* handlers) const { | |
| 346 handlers->push_back(new FileBrowseDelegateHandler( | |
| 347 const_cast<FileBrowseDelegate*>(this))); | |
| 348 return; | |
| 349 } | |
| 350 | |
| 351 void SelectFileDialogImpl::FileBrowseDelegate::GetDialogSize( | |
| 352 gfx::Size* size) const { | |
| 353 size->SetSize(320, 240); | |
| 354 } | |
| 355 | |
| 356 std::string SelectFileDialogImpl::FileBrowseDelegate::GetDialogArgs() const { | |
| 357 #if defined(USE_AURA) | |
| 358 // TODO(saintlou): The current SelectFileDialogImpl does not seem to work | |
| 359 // when chromeos==0. | |
| 360 return std::string(); | |
| 361 #else | |
| 362 // SelectFile inputs as json. | |
| 363 // { | |
| 364 // "type" : "open", // (or "open_multiple", "save", "folder" | |
| 365 // "all_files" : true, | |
| 366 // "file_types" : { | |
| 367 // "exts" : [ ["htm", "html"], ["txt"] ], | |
| 368 // "desc" : [ "HTML files", "Text files" ], | |
| 369 // }, | |
| 370 // "file_type_index" : 1, // 1-based file type index. | |
| 371 // } | |
| 372 // See browser/ui/shell_dialogs.h for more details. | |
| 373 | |
| 374 std::string type_string; | |
| 375 switch (type_) { | |
| 376 case SELECT_FOLDER: | |
| 377 type_string = "folder"; | |
| 378 break; | |
| 379 case SELECT_OPEN_FILE: | |
| 380 type_string = "open"; | |
| 381 break; | |
| 382 case SELECT_OPEN_MULTI_FILE: | |
| 383 type_string = "open_multiple"; | |
| 384 break; | |
| 385 case SELECT_SAVEAS_FILE: | |
| 386 type_string = "save"; | |
| 387 break; | |
| 388 default: | |
| 389 NOTREACHED(); | |
| 390 return std::string(); | |
| 391 } | |
| 392 | |
| 393 std::string exts_list; | |
| 394 std::string desc_list; | |
| 395 for (size_t i = 0; i < file_types_.extensions.size(); ++i) { | |
| 396 DCHECK(!file_types_.extensions[i].empty()); | |
| 397 | |
| 398 std::string exts; | |
| 399 for (size_t j = 0; j < file_types_.extensions[i].size(); ++j) { | |
| 400 if (!exts.empty()) | |
| 401 exts.append(","); | |
| 402 base::StringAppendF(&exts, "\"%s\"", | |
| 403 file_types_.extensions[i][j].c_str()); | |
| 404 } | |
| 405 | |
| 406 if (!exts_list.empty()) | |
| 407 exts_list.append(","); | |
| 408 base::StringAppendF(&exts_list, "[%s]", exts.c_str()); | |
| 409 | |
| 410 std::string desc; | |
| 411 if (i < file_types_.extension_description_overrides.size()) { | |
| 412 desc = UTF16ToUTF8(file_types_.extension_description_overrides[i]); | |
| 413 } else { | |
| 414 #if defined(OS_WIN) | |
| 415 desc = WideToUTF8(file_types_.extensions[i][0]); | |
| 416 #elif defined(OS_POSIX) | |
| 417 desc = file_types_.extensions[i][0]; | |
| 418 #else | |
| 419 NOTIMPLEMENTED(); | |
| 420 #endif | |
| 421 } | |
| 422 | |
| 423 if (!desc_list.empty()) | |
| 424 desc_list.append(","); | |
| 425 base::StringAppendF(&desc_list, "\"%s\"", desc.c_str()); | |
| 426 } | |
| 427 | |
| 428 std::string filename = default_path_.BaseName().value(); | |
| 429 | |
| 430 return StringPrintf("{" | |
| 431 "\"type\":\"%s\"," | |
| 432 "\"all_files\":%s," | |
| 433 "\"current_file\":\"%s\"," | |
| 434 "\"file_types\":{\"exts\":[%s],\"desc\":[%s]}," | |
| 435 "\"file_type_index\":%d" | |
| 436 "}", | |
| 437 type_string.c_str(), | |
| 438 file_types_.include_all_files ? "true" : "false", | |
| 439 filename.c_str(), | |
| 440 exts_list.c_str(), | |
| 441 desc_list.c_str(), | |
| 442 file_type_index_); | |
| 443 #endif | |
| 444 } | |
| 445 | |
| 446 void SelectFileDialogImpl::FileBrowseDelegate::OnDialogClosed( | |
| 447 const std::string& json_retval) { | |
| 448 owner_->OnDialogClosed(this, json_retval); | |
| 449 delete this; | |
| 450 return; | |
| 451 } | |
| 452 | |
| 453 SelectFileDialogImpl::FileBrowseDelegateHandler::FileBrowseDelegateHandler( | |
| 454 FileBrowseDelegate* delegate) | |
| 455 : delegate_(delegate) { | |
| 456 } | |
| 457 | |
| 458 void SelectFileDialogImpl::FileBrowseDelegateHandler::RegisterMessages() { | |
| 459 web_ui_->RegisterMessageCallback("setDialogTitle", | |
| 460 base::Bind(&FileBrowseDelegateHandler::HandleSetDialogTitle, this)); | |
| 461 } | |
| 462 | |
| 463 void SelectFileDialogImpl::FileBrowseDelegateHandler::HandleSetDialogTitle( | |
| 464 const ListValue* args) { | |
| 465 #if !defined(USE_AURA) | |
| 466 // TODO(saintlou): The current SelectFileDialogImpl does not seem to work | |
| 467 // when chromeos==0. | |
| 468 std::wstring new_title = UTF16ToWideHack(ExtractStringValue(args)); | |
| 469 if (new_title != delegate_->title_) { | |
| 470 delegate_->title_ = new_title; | |
| 471 | |
| 472 // Notify the containing view about the title change. | |
| 473 // The current HtmlDialogUIDelegate and HtmlDialogView does not support | |
| 474 // dynamic title change. We hijacked the mechanism between HTMLDialogUI | |
| 475 // and HtmlDialogView to get the HtmlDialogView and forced it to update | |
| 476 // its title. | |
| 477 // TODO(xiyuan): Change this when the infrastructure is improved. | |
| 478 HtmlDialogUIDelegate** delegate = HtmlDialogUI::GetPropertyAccessor(). | |
| 479 GetProperty(web_ui_->tab_contents()->property_bag()); | |
| 480 HtmlDialogView* containing_view = static_cast<HtmlDialogView*>(*delegate); | |
| 481 DCHECK(containing_view); | |
| 482 | |
| 483 containing_view->GetWindow()->UpdateWindowTitle(); | |
| 484 containing_view->GetWindow()->non_client_view()->SchedulePaint(); | |
| 485 } | |
| 486 #endif | |
| 487 } | |
| OLD | NEW |