Home | History | Annotate | Download | only in file_manager
      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