1 // Copyright (c) 2012 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 #include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h" 5 6 #include "ash/shell.h" 7 #include "base/bind.h" 8 #include "base/command_line.h" 9 #include "base/json/json_writer.h" 10 #include "base/logging.h" 11 #include "base/metrics/histogram.h" 12 #include "base/path_service.h" 13 #include "base/strings/string_util.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "base/threading/sequenced_worker_pool.h" 16 #include "base/values.h" 17 #include "chrome/browser/chromeos/drive/drive.pb.h" 18 #include "chrome/browser/chromeos/drive/drive_integration_service.h" 19 #include "chrome/browser/chromeos/drive/file_system.h" 20 #include "chrome/browser/chromeos/drive/file_system_util.h" 21 #include "chrome/browser/chromeos/extensions/file_manager/file_browser_handlers.h" 22 #include "chrome/browser/chromeos/extensions/file_manager/file_tasks.h" 23 #include "chrome/browser/chromeos/extensions/file_manager/fileapi_util.h" 24 #include "chrome/browser/chromeos/media/media_player.h" 25 #include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h" 26 #include "chrome/browser/extensions/crx_installer.h" 27 #include "chrome/browser/extensions/extension_install_prompt.h" 28 #include "chrome/browser/extensions/extension_service.h" 29 #include "chrome/browser/extensions/extension_system.h" 30 #include "chrome/browser/google_apis/task_util.h" 31 #include "chrome/browser/plugins/plugin_prefs.h" 32 #include "chrome/browser/profiles/profile.h" 33 #include "chrome/browser/profiles/profile_manager.h" 34 #include "chrome/browser/ui/browser.h" 35 #include "chrome/browser/ui/browser_finder.h" 36 #include "chrome/browser/ui/browser_iterator.h" 37 #include "chrome/browser/ui/browser_tabstrip.h" 38 #include "chrome/browser/ui/browser_window.h" 39 #include "chrome/browser/ui/extensions/application_launch.h" 40 #include "chrome/browser/ui/host_desktop.h" 41 #include "chrome/browser/ui/simple_message_box.h" 42 #include "chrome/browser/ui/tabs/tab_strip_model.h" 43 #include "chrome/common/chrome_paths.h" 44 #include "chrome/common/chrome_switches.h" 45 #include "chrome/common/extensions/api/file_browser_handlers/file_browser_handler.h" 46 #include "chrome/common/url_constants.h" 47 #include "chromeos/chromeos_switches.h" 48 #include "content/public/browser/browser_thread.h" 49 #include "content/public/browser/plugin_service.h" 50 #include "content/public/browser/storage_partition.h" 51 #include "content/public/browser/user_metrics.h" 52 #include "content/public/browser/web_contents.h" 53 #include "content/public/common/pepper_plugin_info.h" 54 #include "content/public/common/webplugininfo.h" 55 #include "grit/generated_resources.h" 56 #include "net/base/escape.h" 57 #include "net/base/mime_util.h" 58 #include "net/base/net_util.h" 59 #include "ui/base/l10n/l10n_util.h" 60 #include "ui/gfx/screen.h" 61 #include "webkit/browser/fileapi/file_system_backend.h" 62 #include "webkit/browser/fileapi/file_system_context.h" 63 #include "webkit/browser/fileapi/file_system_operation_runner.h" 64 #include "webkit/browser/fileapi/file_system_url.h" 65 #include "webkit/common/fileapi/file_system_util.h" 66 67 using base::DictionaryValue; 68 using base::ListValue; 69 using content::BrowserContext; 70 using content::BrowserThread; 71 using content::PluginService; 72 using content::UserMetricsAction; 73 using extensions::app_file_handler_util::FindFileHandlersForFiles; 74 using extensions::app_file_handler_util::PathAndMimeTypeSet; 75 using extensions::Extension; 76 using fileapi::FileSystemURL; 77 78 const char kFileBrowserDomain[] = "hhaomjibdihmijegdhdafkllkbggdgoj"; 79 80 const char kFileBrowserGalleryTaskId[] = "gallery"; 81 const char kFileBrowserMountArchiveTaskId[] = "mount-archive"; 82 const char kFileBrowserWatchTaskId[] = "watch"; 83 const char kFileBrowserPlayTaskId[] = "play"; 84 85 const char kVideoPlayerAppName[] = "videoplayer"; 86 87 namespace file_manager { 88 namespace util { 89 namespace { 90 91 const char kCRXExtension[] = ".crx"; 92 const char kPdfExtension[] = ".pdf"; 93 const char kSwfExtension[] = ".swf"; 94 // List of file extension we can open in tab. 95 const char* kBrowserSupportedExtensions[] = { 96 #if defined(GOOGLE_CHROME_BUILD) 97 ".pdf", ".swf", 98 #endif 99 ".bmp", ".jpg", ".jpeg", ".png", ".webp", ".gif", ".txt", ".html", ".htm", 100 ".mhtml", ".mht", ".svg" 101 }; 102 103 // Returns a file manager URL for the given |path|. 104 GURL GetFileManagerUrl(const char* path) { 105 return GURL(std::string("chrome-extension://") + kFileBrowserDomain + path); 106 } 107 108 bool IsSupportedBrowserExtension(const char* file_extension) { 109 for (size_t i = 0; i < arraysize(kBrowserSupportedExtensions); i++) { 110 if (base::strcasecmp(file_extension, kBrowserSupportedExtensions[i]) == 0) { 111 return true; 112 } 113 } 114 return false; 115 } 116 117 bool IsCRXFile(const char* file_extension) { 118 return base::strcasecmp(file_extension, kCRXExtension) == 0; 119 } 120 121 bool IsPepperPluginEnabled(Profile* profile, 122 const base::FilePath& plugin_path) { 123 content::PepperPluginInfo* pepper_info = 124 PluginService::GetInstance()->GetRegisteredPpapiPluginInfo(plugin_path); 125 if (!pepper_info) 126 return false; 127 128 scoped_refptr<PluginPrefs> plugin_prefs = PluginPrefs::GetForProfile(profile); 129 if (!plugin_prefs.get()) 130 return false; 131 132 return plugin_prefs->IsPluginEnabled(pepper_info->ToWebPluginInfo()); 133 } 134 135 bool IsPdfPluginEnabled(Profile* profile) { 136 base::FilePath plugin_path; 137 PathService::Get(chrome::FILE_PDF_PLUGIN, &plugin_path); 138 return IsPepperPluginEnabled(profile, plugin_path); 139 } 140 141 bool IsFlashPluginEnabled(Profile* profile) { 142 base::FilePath plugin_path( 143 CommandLine::ForCurrentProcess()->GetSwitchValueNative( 144 switches::kPpapiFlashPath)); 145 if (plugin_path.empty()) 146 PathService::Get(chrome::FILE_PEPPER_FLASH_PLUGIN, &plugin_path); 147 return IsPepperPluginEnabled(profile, plugin_path); 148 } 149 150 // Convert numeric dialog type to a string. 151 std::string GetDialogTypeAsString( 152 ui::SelectFileDialog::Type dialog_type) { 153 std::string type_str; 154 switch (dialog_type) { 155 case ui::SelectFileDialog::SELECT_NONE: 156 type_str = "full-page"; 157 break; 158 159 case ui::SelectFileDialog::SELECT_FOLDER: 160 type_str = "folder"; 161 break; 162 163 case ui::SelectFileDialog::SELECT_UPLOAD_FOLDER: 164 type_str = "upload-folder"; 165 break; 166 167 case ui::SelectFileDialog::SELECT_SAVEAS_FILE: 168 type_str = "saveas-file"; 169 break; 170 171 case ui::SelectFileDialog::SELECT_OPEN_FILE: 172 type_str = "open-file"; 173 break; 174 175 case ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE: 176 type_str = "open-multi-file"; 177 break; 178 179 default: 180 NOTREACHED(); 181 } 182 183 return type_str; 184 } 185 186 void OpenNewTab(Profile* profile, const GURL& url) { 187 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 188 Browser* browser = chrome::FindOrCreateTabbedBrowser( 189 profile ? profile : ProfileManager::GetDefaultProfileOrOffTheRecord(), 190 chrome::HOST_DESKTOP_TYPE_ASH); 191 chrome::AddSelectedTabWithURL(browser, url, content::PAGE_TRANSITION_LINK); 192 // If the current browser is not tabbed then the new tab will be created 193 // in a different browser. Make sure it is visible. 194 browser->window()->Show(); 195 } 196 197 // Shows a warning message box saying that the file could not be opened. 198 void ShowWarningMessageBox(Profile* profile, const base::FilePath& path) { 199 // TODO: if FindOrCreateTabbedBrowser creates a new browser the returned 200 // browser is leaked. 201 Browser* browser = 202 chrome::FindOrCreateTabbedBrowser(profile, 203 chrome::HOST_DESKTOP_TYPE_ASH); 204 chrome::ShowMessageBox( 205 browser->window()->GetNativeWindow(), 206 l10n_util::GetStringFUTF16( 207 IDS_FILE_BROWSER_ERROR_VIEWING_FILE_TITLE, 208 UTF8ToUTF16(path.BaseName().value())), 209 l10n_util::GetStringUTF16(IDS_FILE_BROWSER_ERROR_VIEWING_FILE), 210 chrome::MESSAGE_BOX_TYPE_WARNING); 211 } 212 213 void InstallCRX(Browser* browser, const base::FilePath& path) { 214 ExtensionService* service = 215 extensions::ExtensionSystem::Get(browser->profile())->extension_service(); 216 CHECK(service); 217 218 scoped_refptr<extensions::CrxInstaller> installer( 219 extensions::CrxInstaller::Create( 220 service, 221 scoped_ptr<ExtensionInstallPrompt>(new ExtensionInstallPrompt( 222 browser->profile(), NULL, NULL)))); 223 installer->set_error_on_unsupported_requirements(true); 224 installer->set_is_gallery_install(false); 225 installer->set_allow_silent_install(false); 226 installer->InstallCrx(path); 227 } 228 229 // Called when a crx file on Drive was downloaded. 230 void OnCRXDownloadCallback(Browser* browser, 231 drive::FileError error, 232 const base::FilePath& file, 233 scoped_ptr<drive::ResourceEntry> entry) { 234 if (error != drive::FILE_ERROR_OK) 235 return; 236 InstallCRX(browser, file); 237 } 238 239 // Grants file system access to the file browser. 240 bool GrantFileSystemAccessToFileBrowser(Profile* profile) { 241 // File browser always runs in the site for its extension id, so that is the 242 // site for which file access permissions should be granted. 243 GURL site = extensions::ExtensionSystem::Get(profile)->extension_service()-> 244 GetSiteForExtensionId(kFileBrowserDomain); 245 fileapi::ExternalFileSystemBackend* backend = 246 BrowserContext::GetStoragePartitionForSite(profile, site)-> 247 GetFileSystemContext()->external_backend(); 248 if (!backend) 249 return false; 250 backend->GrantFullAccessToExtension(GetFileBrowserUrl().host()); 251 return true; 252 } 253 254 // Opens the file specified by |url| with |task|. 255 void OpenFileWithTask(Profile* profile, 256 const file_tasks::TaskDescriptor& task, 257 const GURL& url) { 258 // If File Browser has not been open yet then it did not request access 259 // to the file system. Do it now. 260 if (!GrantFileSystemAccessToFileBrowser(profile)) 261 return; 262 263 fileapi::FileSystemContext* file_system_context = 264 fileapi_util::GetFileSystemContextForExtensionId( 265 profile, kFileBrowserDomain); 266 267 // We are executing the task on behalf of File Browser extension. 268 const GURL source_url = GetFileBrowserUrl(); 269 std::vector<FileSystemURL> urls; 270 urls.push_back(file_system_context->CrackURL(url)); 271 272 file_tasks::ExecuteFileTask( 273 profile, 274 source_url, 275 kFileBrowserDomain, 276 0, // no tab id 277 task, 278 urls, 279 file_tasks::FileTaskFinishedCallback()); 280 } 281 282 // Opens the file specified with |path|. Used to implement internal handlers 283 // of special action IDs such as "auto-open", "open", and "select". 284 void OpenFileWithInternalActionId(const base::FilePath& path, 285 const std::string& action_id) { 286 DCHECK(action_id == "auto-open" || 287 action_id == "open" || 288 action_id == "select"); 289 290 content::RecordAction(UserMetricsAction("ShowFileBrowserFullTab")); 291 Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord(); 292 293 GURL url; 294 if (!ConvertFileToFileSystemUrl(profile, path, kFileBrowserDomain, &url)) 295 return; 296 297 file_tasks::TaskDescriptor task(kFileBrowserDomain, 298 file_tasks::kFileBrowserHandlerTaskType, 299 action_id); 300 OpenFileWithTask(profile, task, url); 301 } 302 303 Browser* GetBrowserForUrl(GURL target_url) { 304 for (chrome::BrowserIterator it; !it.done(); it.Next()) { 305 Browser* browser = *it; 306 TabStripModel* tab_strip = browser->tab_strip_model(); 307 for (int idx = 0; idx < tab_strip->count(); idx++) { 308 content::WebContents* web_contents = tab_strip->GetWebContentsAt(idx); 309 const GURL& url = web_contents->GetLastCommittedURL(); 310 if (url == target_url) 311 return browser; 312 } 313 } 314 return NULL; 315 } 316 317 // Opens the file specified by |path| and |url| with a file handler, 318 // preferably the default handler for the type of the file. Returns false if 319 // no file handler is found. 320 bool OpenFileWithFileHandler(Profile* profile, 321 const base::FilePath& path, 322 const GURL& url, 323 const std::string& mime_type, 324 const std::string& default_task_id) { 325 ExtensionService* service = profile->GetExtensionService(); 326 if (!service) 327 return false; 328 329 PathAndMimeTypeSet files; 330 files.insert(std::make_pair(path, mime_type)); 331 const extensions::FileHandlerInfo* first_handler = NULL; 332 const extensions::Extension* extension_for_first_handler = NULL; 333 334 // If we find the default handler, we execute it immediately, but otherwise, 335 // we remember the first handler, and if there was no default handler, simply 336 // execute the first one. 337 for (ExtensionSet::const_iterator iter = service->extensions()->begin(); 338 iter != service->extensions()->end(); 339 ++iter) { 340 const Extension* extension = iter->get(); 341 342 // We don't support using hosted apps to open files. 343 if (!extension->is_platform_app()) 344 continue; 345 346 // We only support apps that specify "incognito: split" if in incognito 347 // mode. 348 if (profile->IsOffTheRecord() && 349 !service->IsIncognitoEnabled(extension->id())) 350 continue; 351 352 typedef std::vector<const extensions::FileHandlerInfo*> FileHandlerList; 353 FileHandlerList file_handlers = FindFileHandlersForFiles(*extension, files); 354 for (FileHandlerList::iterator i = file_handlers.begin(); 355 i != file_handlers.end(); ++i) { 356 const extensions::FileHandlerInfo* handler = *i; 357 std::string task_id = file_tasks::MakeTaskID( 358 extension->id(), 359 file_tasks::kFileHandlerTaskType, 360 handler->id); 361 if (task_id == default_task_id) { 362 file_tasks::TaskDescriptor task(extension->id(), 363 file_tasks::kFileHandlerTaskType, 364 handler->id); 365 OpenFileWithTask(profile, task, url); 366 return true; 367 368 } else if (!first_handler) { 369 first_handler = handler; 370 extension_for_first_handler = extension; 371 } 372 } 373 } 374 if (first_handler) { 375 file_tasks::TaskDescriptor task(extension_for_first_handler->id(), 376 file_tasks::kFileHandlerTaskType, 377 first_handler->id); 378 OpenFileWithTask(profile, task, url); 379 return true; 380 } 381 return false; 382 } 383 384 // Returns true if |action_id| indicates that the file currently being 385 // handled should be opened with the browser (i.e. should be opened with 386 // OpenFileWithBrowser()). 387 bool ShouldBeOpenedWithBrowser(const std::string& action_id) { 388 return (action_id == "view-pdf" || 389 action_id == "view-swf" || 390 action_id == "view-in-browser" || 391 action_id == "install-crx" || 392 action_id == "open-hosted-generic" || 393 action_id == "open-hosted-gdoc" || 394 action_id == "open-hosted-gsheet" || 395 action_id == "open-hosted-gslides"); 396 } 397 398 // Opens the file specified by |path| and |url| with the file browser handler 399 // specified by |handler|. Returns false if failed to open the file. 400 bool OpenFileWithFileBrowserHandler(Profile* profile, 401 const base::FilePath& path, 402 const FileBrowserHandler& handler, 403 const GURL& url) { 404 std::string extension_id = handler.extension_id(); 405 std::string action_id = handler.id(); 406 Browser* browser = chrome::FindLastActiveWithProfile(profile, 407 chrome::HOST_DESKTOP_TYPE_ASH); 408 409 // If there is no browsers for the profile, bail out. Return true so warning 410 // about file type not being supported is not displayed. 411 if (!browser) 412 return true; 413 414 // Some action IDs of the file manager's file browser handlers require the 415 // file to be directly opened with the browser. 416 if (extension_id == kFileBrowserDomain && 417 ShouldBeOpenedWithBrowser(action_id)) { 418 return OpenFileWithBrowser(browser, path); 419 } 420 421 file_tasks::TaskDescriptor task(extension_id, 422 file_tasks::kFileBrowserHandlerTaskType, 423 action_id); 424 OpenFileWithTask(profile, task, url); 425 return true; 426 } 427 428 // Opens the file specified by |path| with a handler (either of file browser 429 // handler or file handler, preferably the default handler for the type of 430 // the file), or opens the file with the browser. Returns false if failed to 431 // open the file. 432 bool OpenFileWithHandlerOrBrowser(Profile* profile, 433 const base::FilePath& path) { 434 GURL url; 435 if (!ConvertFileToFileSystemUrl(profile, path, kFileBrowserDomain, &url)) 436 return false; 437 438 std::string mime_type = GetMimeTypeForPath(path); 439 std::string default_task_id = file_tasks::GetDefaultTaskIdFromPrefs( 440 profile, mime_type, path.Extension()); 441 442 // We choose the file handler from the following in decreasing priority or 443 // fail if none support the file type: 444 // 1. default file browser handler 445 // 2. default file handler 446 // 3. a fallback handler (e.g. opening in the browser) 447 // 4. non-default file handler 448 // 5. non-default file browser handler 449 // Note that there can be at most one of default extension and default app. 450 const FileBrowserHandler* handler = 451 file_browser_handlers::FindFileBrowserHandlerForURLAndPath( 452 profile, url, path); 453 if (!handler) { 454 return OpenFileWithFileHandler( 455 profile, path, url, mime_type, default_task_id); 456 } 457 458 std::string handler_task_id = file_tasks::MakeTaskID( 459 handler->extension_id(), 460 file_tasks::kFileBrowserHandlerTaskType, 461 handler->id()); 462 if (handler_task_id != default_task_id && 463 !file_browser_handlers::IsFallbackFileBrowserHandler(handler) && 464 OpenFileWithFileHandler( 465 profile, path, url, mime_type, default_task_id)) { 466 return true; 467 } 468 return OpenFileWithFileBrowserHandler(profile, path, *handler, url); 469 } 470 471 // Reads the alternate URL from a GDoc file. When it fails, returns a file URL 472 // for |file_path| as fallback. 473 // Note that an alternate url is a URL to open a hosted document. 474 GURL ReadUrlFromGDocOnBlockingPool(const base::FilePath& file_path) { 475 GURL url = drive::util::ReadUrlFromGDocFile(file_path); 476 if (url.is_empty()) 477 url = net::FilePathToFileURL(file_path); 478 return url; 479 } 480 481 // Used to implement ViewItem(). 482 void ContinueViewItem(Profile* profile, 483 const base::FilePath& path, 484 base::PlatformFileError error) { 485 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 486 487 if (error == base::PLATFORM_FILE_OK) { 488 // A directory exists at |path|. Open it with the file manager. 489 OpenFileWithInternalActionId(path, "open"); 490 } else { 491 // |path| should be a file. Open it with a handler or the browser. 492 if (!OpenFileWithHandlerOrBrowser(profile, path)) 493 ShowWarningMessageBox(profile, path); 494 } 495 } 496 497 // Used to implement CheckIfDirectoryExists(). 498 void CheckIfDirectoryExistsOnIOThread( 499 scoped_refptr<fileapi::FileSystemContext> file_system_context, 500 const GURL& url, 501 const fileapi::FileSystemOperationRunner::StatusCallback& callback) { 502 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 503 504 fileapi::FileSystemURL file_system_url = file_system_context->CrackURL(url); 505 file_system_context->operation_runner()->DirectoryExists( 506 file_system_url, callback); 507 } 508 509 // Checks if a directory exists at |url|. 510 void CheckIfDirectoryExists( 511 scoped_refptr<fileapi::FileSystemContext> file_system_context, 512 const GURL& url, 513 const fileapi::FileSystemOperationRunner::StatusCallback& callback) { 514 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 515 516 BrowserThread::PostTask( 517 BrowserThread::IO, FROM_HERE, 518 base::Bind(&CheckIfDirectoryExistsOnIOThread, 519 file_system_context, 520 url, 521 google_apis::CreateRelayCallback(callback))); 522 } 523 524 } // namespace 525 526 GURL GetFileBrowserExtensionUrl() { 527 return GetFileManagerUrl("/"); 528 } 529 530 GURL GetFileBrowserUrl() { 531 return GetFileManagerUrl("/main.html"); 532 } 533 534 GURL GetMediaPlayerUrl() { 535 return GetFileManagerUrl("/mediaplayer.html"); 536 } 537 538 GURL GetActionChoiceUrl(const base::FilePath& virtual_path, 539 bool advanced_mode) { 540 std::string url = GetFileManagerUrl("/action_choice.html").spec(); 541 if (advanced_mode) 542 url += "?advanced-mode"; 543 url += "#/" + net::EscapeUrlEncodedData(virtual_path.value(), 544 false); // Space to %20 instead of +. 545 return GURL(url); 546 } 547 548 GURL ConvertRelativePathToFileSystemUrl(const base::FilePath& relative_path, 549 const std::string& extension_id) { 550 GURL base_url = fileapi::GetFileSystemRootURI( 551 Extension::GetBaseURLFromExtensionId(extension_id), 552 fileapi::kFileSystemTypeExternal); 553 return GURL(base_url.spec() + 554 net::EscapeUrlEncodedData(relative_path.AsUTF8Unsafe(), 555 false)); // Space to %20 instead of +. 556 } 557 558 bool ConvertFileToFileSystemUrl(Profile* profile, 559 const base::FilePath& full_file_path, 560 const std::string& extension_id, 561 GURL* url) { 562 base::FilePath relative_path; 563 if (!ConvertFileToRelativeFileSystemPath(profile, extension_id, 564 full_file_path, &relative_path)) { 565 return false; 566 } 567 *url = ConvertRelativePathToFileSystemUrl(relative_path, extension_id); 568 return true; 569 } 570 571 bool ConvertFileToRelativeFileSystemPath( 572 Profile* profile, 573 const std::string& extension_id, 574 const base::FilePath& full_file_path, 575 base::FilePath* virtual_path) { 576 ExtensionService* service = 577 extensions::ExtensionSystem::Get(profile)->extension_service(); 578 // May be NULL during unit_tests. 579 if (!service) 580 return false; 581 582 // File browser APIs are meant to be used only from extension context, so the 583 // extension's site is the one in whose file system context the virtual path 584 // should be found. 585 GURL site = service->GetSiteForExtensionId(extension_id); 586 fileapi::ExternalFileSystemBackend* backend = 587 BrowserContext::GetStoragePartitionForSite(profile, site)-> 588 GetFileSystemContext()->external_backend(); 589 if (!backend) 590 return false; 591 592 // Find if this file path is managed by the external backend. 593 if (!backend->GetVirtualPath(full_file_path, virtual_path)) 594 return false; 595 596 return true; 597 } 598 599 GURL GetFileBrowserUrlWithParams( 600 ui::SelectFileDialog::Type type, 601 const string16& title, 602 const base::FilePath& default_virtual_path, 603 const ui::SelectFileDialog::FileTypeInfo* file_types, 604 int file_type_index, 605 const base::FilePath::StringType& default_extension) { 606 DictionaryValue arg_value; 607 arg_value.SetString("type", GetDialogTypeAsString(type)); 608 arg_value.SetString("title", title); 609 arg_value.SetString("defaultPath", default_virtual_path.value()); 610 arg_value.SetString("defaultExtension", default_extension); 611 612 if (file_types) { 613 ListValue* types_list = new ListValue(); 614 for (size_t i = 0; i < file_types->extensions.size(); ++i) { 615 ListValue* extensions_list = new ListValue(); 616 for (size_t j = 0; j < file_types->extensions[i].size(); ++j) { 617 extensions_list->Append( 618 new base::StringValue(file_types->extensions[i][j])); 619 } 620 621 DictionaryValue* dict = new DictionaryValue(); 622 dict->Set("extensions", extensions_list); 623 624 if (i < file_types->extension_description_overrides.size()) { 625 string16 desc = file_types->extension_description_overrides[i]; 626 dict->SetString("description", desc); 627 } 628 629 // file_type_index is 1-based. 0 means no selection at all. 630 dict->SetBoolean("selected", 631 (static_cast<size_t>(file_type_index) == (i + 1))); 632 633 types_list->Set(i, dict); 634 } 635 arg_value.Set("typeList", types_list); 636 637 arg_value.SetBoolean("includeAllFiles", file_types->include_all_files); 638 } 639 640 // If the caller cannot handle Drive path, the file chooser dialog need to 641 // return resolved local native paths to the selected files. 642 arg_value.SetBoolean("shouldReturnLocalPath", 643 !file_types || !file_types->support_drive); 644 645 std::string json_args; 646 base::JSONWriter::Write(&arg_value, &json_args); 647 648 // kChromeUIFileManagerURL could not be used since query parameters are not 649 // supported for it. 650 std::string url = GetFileBrowserUrl().spec() + '?' + 651 net::EscapeUrlEncodedData(json_args, 652 false); // Space to %20 instead of +. 653 return GURL(url); 654 } 655 656 string16 GetTitleFromType(ui::SelectFileDialog::Type dialog_type) { 657 string16 title; 658 switch (dialog_type) { 659 case ui::SelectFileDialog::SELECT_NONE: 660 // Full page file manager doesn't need a title. 661 break; 662 663 case ui::SelectFileDialog::SELECT_FOLDER: 664 title = l10n_util::GetStringUTF16( 665 IDS_FILE_BROWSER_SELECT_FOLDER_TITLE); 666 break; 667 668 case ui::SelectFileDialog::SELECT_UPLOAD_FOLDER: 669 title = l10n_util::GetStringUTF16( 670 IDS_FILE_BROWSER_SELECT_UPLOAD_FOLDER_TITLE); 671 break; 672 673 case ui::SelectFileDialog::SELECT_SAVEAS_FILE: 674 title = l10n_util::GetStringUTF16( 675 IDS_FILE_BROWSER_SELECT_SAVEAS_FILE_TITLE); 676 break; 677 678 case ui::SelectFileDialog::SELECT_OPEN_FILE: 679 title = l10n_util::GetStringUTF16( 680 IDS_FILE_BROWSER_SELECT_OPEN_FILE_TITLE); 681 break; 682 683 case ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE: 684 title = l10n_util::GetStringUTF16( 685 IDS_FILE_BROWSER_SELECT_OPEN_MULTI_FILE_TITLE); 686 break; 687 688 default: 689 NOTREACHED(); 690 } 691 692 return title; 693 } 694 695 void ViewRemovableDrive(const base::FilePath& path) { 696 OpenFileWithInternalActionId(path, "auto-open"); 697 } 698 699 void OpenActionChoiceDialog(const base::FilePath& path, bool advanced_mode) { 700 const int kDialogWidth = 394; 701 // TODO(dgozman): remove 50, which is a title height once popup window 702 // will have no title. 703 const int kDialogHeight = 316 + 50; 704 705 Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord(); 706 707 base::FilePath virtual_path; 708 if (!ConvertFileToRelativeFileSystemPath(profile, kFileBrowserDomain, path, 709 &virtual_path)) 710 return; 711 GURL dialog_url = GetActionChoiceUrl(virtual_path, advanced_mode); 712 713 const gfx::Size screen = ash::Shell::GetScreen()->GetPrimaryDisplay().size(); 714 const gfx::Rect bounds((screen.width() - kDialogWidth) / 2, 715 (screen.height() - kDialogHeight) / 2, 716 kDialogWidth, 717 kDialogHeight); 718 719 Browser* browser = GetBrowserForUrl(dialog_url); 720 721 if (browser) { 722 browser->window()->Show(); 723 return; 724 } 725 726 ExtensionService* service = extensions::ExtensionSystem::Get( 727 profile ? profile : ProfileManager::GetDefaultProfileOrOffTheRecord())-> 728 extension_service(); 729 if (!service) 730 return; 731 732 const extensions::Extension* extension = 733 service->GetExtensionById(kFileBrowserDomain, false); 734 if (!extension) 735 return; 736 737 chrome::AppLaunchParams params(profile, extension, 738 extension_misc::LAUNCH_WINDOW, 739 NEW_FOREGROUND_TAB); 740 params.override_url = dialog_url; 741 params.override_bounds = bounds; 742 chrome::OpenApplication(params); 743 } 744 745 void ViewItem(const base::FilePath& path) { 746 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 747 748 Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord(); 749 GURL url; 750 if (!ConvertFileToFileSystemUrl(profile, path, kFileBrowserDomain, &url) || 751 !GrantFileSystemAccessToFileBrowser(profile)) { 752 ShowWarningMessageBox(profile, path); 753 return; 754 } 755 756 scoped_refptr<fileapi::FileSystemContext> file_system_context = 757 fileapi_util::GetFileSystemContextForExtensionId( 758 profile, kFileBrowserDomain); 759 760 CheckIfDirectoryExists(file_system_context, url, 761 base::Bind(&ContinueViewItem, profile, path)); 762 } 763 764 void ShowFileInFolder(const base::FilePath& path) { 765 // This action changes the selection so we do not reuse existing tabs. 766 OpenFileWithInternalActionId(path, "select"); 767 } 768 769 bool OpenFileWithBrowser(Browser* browser, const base::FilePath& path) { 770 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 771 772 Profile* profile = browser->profile(); 773 std::string file_extension = path.Extension(); 774 // For things supported natively by the browser, we should open it 775 // in a tab. 776 if (IsSupportedBrowserExtension(file_extension.data()) || 777 ShouldBeOpenedWithPlugin(profile, file_extension.data())) { 778 GURL page_url = net::FilePathToFileURL(path); 779 // Override drive resource to point to internal handler instead of file URL. 780 if (drive::util::IsUnderDriveMountPoint(path)) { 781 page_url = drive::util::FilePathToDriveURL( 782 drive::util::ExtractDrivePath(path)); 783 } 784 OpenNewTab(profile, page_url); 785 return true; 786 } 787 788 if (drive::util::HasGDocFileExtension(path)) { 789 if (drive::util::IsUnderDriveMountPoint(path)) { 790 // The file is on Google Docs. Open with drive URL. 791 GURL url = drive::util::FilePathToDriveURL( 792 drive::util::ExtractDrivePath(path)); 793 OpenNewTab(profile, url); 794 } else { 795 // The file is local (downloaded from an attachment or otherwise copied). 796 // Parse the file to extract the Docs url and open this url. 797 base::PostTaskAndReplyWithResult( 798 BrowserThread::GetBlockingPool(), 799 FROM_HERE, 800 base::Bind(&ReadUrlFromGDocOnBlockingPool, path), 801 base::Bind(&OpenNewTab, static_cast<Profile*>(NULL))); 802 } 803 return true; 804 } 805 806 if (IsCRXFile(file_extension.data())) { 807 if (drive::util::IsUnderDriveMountPoint(path)) { 808 drive::DriveIntegrationService* integration_service = 809 drive::DriveIntegrationServiceFactory::GetForProfile(profile); 810 if (!integration_service) 811 return false; 812 integration_service->file_system()->GetFileByPath( 813 drive::util::ExtractDrivePath(path), 814 base::Bind(&OnCRXDownloadCallback, browser)); 815 } else { 816 InstallCRX(browser, path); 817 } 818 return true; 819 } 820 821 // Failed to open the file of unknown type. 822 LOG(WARNING) << "Unknown file type: " << path.value(); 823 return false; 824 } 825 826 // If a bundled plugin is enabled, we should open pdf/swf files in a tab. 827 bool ShouldBeOpenedWithPlugin(Profile* profile, const char* file_extension) { 828 if (LowerCaseEqualsASCII(file_extension, kPdfExtension)) 829 return IsPdfPluginEnabled(profile); 830 if (LowerCaseEqualsASCII(file_extension, kSwfExtension)) 831 return IsFlashPluginEnabled(profile); 832 return false; 833 } 834 835 std::string GetMimeTypeForPath(const base::FilePath& file_path) { 836 const base::FilePath::StringType file_extension = 837 StringToLowerASCII(file_path.Extension()); 838 839 // TODO(thorogood): Rearchitect this call so it can run on the File thread; 840 // GetMimeTypeFromFile requires this on Linux. Right now, we use 841 // Chrome-level knowledge only. 842 std::string mime_type; 843 if (file_extension.empty() || 844 !net::GetWellKnownMimeTypeFromExtension(file_extension.substr(1), 845 &mime_type)) { 846 // If the file doesn't have an extension or its mime-type cannot be 847 // determined, then indicate that it has the empty mime-type. This will 848 // only be matched if the Web Intents accepts "*" or "*/*". 849 return ""; 850 } else { 851 return mime_type; 852 } 853 } 854 855 } // namespace util 856 } // namespace file_manager 857