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 
      5 #include "chrome/browser/chromeos/extensions/file_manager/file_browser_handlers.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/file_util.h"
      9 #include "base/i18n/case_conversion.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "chrome/browser/chromeos/drive/file_system_util.h"
     12 #include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
     13 #include "chrome/browser/chromeos/extensions/file_manager/fileapi_util.h"
     14 #include "chrome/browser/chromeos/fileapi/file_system_backend.h"
     15 #include "chrome/browser/extensions/event_router.h"
     16 #include "chrome/browser/extensions/extension_host.h"
     17 #include "chrome/browser/extensions/extension_service.h"
     18 #include "chrome/browser/extensions/extension_system.h"
     19 #include "chrome/browser/extensions/lazy_background_task_queue.h"
     20 #include "chrome/browser/profiles/profile.h"
     21 #include "chrome/common/extensions/api/file_browser_handlers/file_browser_handler.h"
     22 #include "chrome/common/extensions/background_info.h"
     23 #include "content/public/browser/browser_thread.h"
     24 #include "content/public/browser/child_process_security_policy.h"
     25 #include "content/public/browser/render_process_host.h"
     26 #include "content/public/browser/site_instance.h"
     27 #include "content/public/browser/web_contents.h"
     28 #include "net/base/escape.h"
     29 #include "webkit/browser/fileapi/file_system_context.h"
     30 #include "webkit/browser/fileapi/file_system_url.h"
     31 
     32 using content::BrowserThread;
     33 using content::ChildProcessSecurityPolicy;
     34 using content::SiteInstance;
     35 using content::WebContents;
     36 using extensions::Extension;
     37 using fileapi::FileSystemURL;
     38 
     39 namespace file_manager {
     40 namespace file_browser_handlers {
     41 namespace {
     42 
     43 // Returns process id of the process the extension is running in.
     44 int ExtractProcessFromExtensionId(Profile* profile,
     45                                   const std::string& extension_id) {
     46   GURL extension_url =
     47       Extension::GetBaseURLFromExtensionId(extension_id);
     48   ExtensionProcessManager* manager =
     49     extensions::ExtensionSystem::Get(profile)->process_manager();
     50 
     51   SiteInstance* site_instance = manager->GetSiteInstanceForURL(extension_url);
     52   if (!site_instance || !site_instance->HasProcess())
     53     return -1;
     54   content::RenderProcessHost* process = site_instance->GetProcess();
     55 
     56   return process->GetID();
     57 }
     58 
     59 // Finds a file browser handler that matches |action_id|. Returns NULL if not
     60 // found.
     61 const FileBrowserHandler* FindFileBrowserHandlerForActionId(
     62     const Extension* extension,
     63     const std::string& action_id) {
     64   FileBrowserHandler::List* handler_list =
     65       FileBrowserHandler::GetHandlers(extension);
     66   for (FileBrowserHandler::List::const_iterator handler_iter =
     67            handler_list->begin();
     68        handler_iter != handler_list->end();
     69        ++handler_iter) {
     70     if (handler_iter->get()->id() == action_id)
     71       return handler_iter->get();
     72   }
     73   return NULL;
     74 }
     75 
     76 std::string EscapedUtf8ToLower(const std::string& str) {
     77   string16 utf16 = UTF8ToUTF16(
     78       net::UnescapeURLComponent(str, net::UnescapeRule::NORMAL));
     79   return net::EscapeUrlEncodedData(
     80       UTF16ToUTF8(base::i18n::ToLower(utf16)),
     81       false /* do not replace space with plus */);
     82 }
     83 
     84 // Finds file browser handlers that can handle the |selected_file_url|.
     85 FileBrowserHandlerList FindFileBrowserHandlersForURL(
     86     Profile* profile,
     87     const GURL& selected_file_url) {
     88   ExtensionService* service =
     89       extensions::ExtensionSystem::Get(profile)->extension_service();
     90   // In unit-tests, we may not have an ExtensionService.
     91   if (!service)
     92     return FileBrowserHandlerList();
     93 
     94   // We need case-insensitive matching, and pattern in the handler is already
     95   // in lower case.
     96   const GURL lowercase_url(EscapedUtf8ToLower(selected_file_url.spec()));
     97 
     98   FileBrowserHandlerList results;
     99   for (ExtensionSet::const_iterator iter = service->extensions()->begin();
    100        iter != service->extensions()->end();
    101        ++iter) {
    102     const Extension* extension = iter->get();
    103     if (profile->IsOffTheRecord() &&
    104         !service->IsIncognitoEnabled(extension->id()))
    105       continue;
    106 
    107     FileBrowserHandler::List* handler_list =
    108         FileBrowserHandler::GetHandlers(extension);
    109     if (!handler_list)
    110       continue;
    111     for (FileBrowserHandler::List::const_iterator handler_iter =
    112              handler_list->begin();
    113          handler_iter != handler_list->end();
    114          ++handler_iter) {
    115       const FileBrowserHandler* handler = handler_iter->get();
    116       if (!handler->MatchesURL(lowercase_url))
    117         continue;
    118 
    119       results.push_back(handler_iter->get());
    120     }
    121   }
    122   return results;
    123 }
    124 
    125 // Finds a file browser handler that matches |extension_id| and |action_id|
    126 // from |handler_list|.  Returns a mutable iterator to the handler if
    127 // found. Returns handler_list->end() if not found.
    128 FileBrowserHandlerList::iterator
    129 FindFileBrowserHandlerForExtensionIdAndActionId(
    130     FileBrowserHandlerList* handler_list,
    131     const std::string& extension_id,
    132     const std::string& action_id) {
    133   DCHECK(handler_list);
    134 
    135   FileBrowserHandlerList::iterator iter = handler_list->begin();
    136   while (iter != handler_list->end() &&
    137          !((*iter)->extension_id() == extension_id &&
    138            (*iter)->id() == action_id)) {
    139     ++iter;
    140   }
    141   return iter;
    142 }
    143 
    144 // This class is used to execute a file browser handler task. Here's how this
    145 // works:
    146 //
    147 // 1) Open the "external" file system
    148 // 2) Set up permissions for the target files on the external file system.
    149 // 3) Raise onExecute event with the action ID and entries of the target
    150 //    files. The event will launch the file browser handler if not active.
    151 // 4) In the file browser handler, onExecute event is handled and executes the
    152 //    task in JavaScript.
    153 //
    154 // That said, the class itself does not execute a task. The task will be
    155 // executed in JavaScript.
    156 class FileBrowserHandlerExecutor {
    157  public:
    158   FileBrowserHandlerExecutor(Profile* profile,
    159                              const Extension* extension,
    160                              int32 tab_id,
    161                              const std::string& action_id);
    162 
    163   // Executes the task for each file. |done| will be run with the result.
    164   void Execute(const std::vector<FileSystemURL>& file_urls,
    165                const file_tasks::FileTaskFinishedCallback& done);
    166 
    167  private:
    168   // This object is responsible to delete itself.
    169   virtual ~FileBrowserHandlerExecutor();
    170 
    171   struct FileDefinition {
    172     FileDefinition();
    173     ~FileDefinition();
    174 
    175     base::FilePath virtual_path;
    176     base::FilePath absolute_path;
    177     bool is_directory;
    178   };
    179 
    180   typedef std::vector<FileDefinition> FileDefinitionList;
    181 
    182   // Checks legitimacy of file url and grants file RO access permissions from
    183   // handler (target) extension and its renderer process.
    184   static FileDefinitionList SetupFileAccessPermissions(
    185       scoped_refptr<fileapi::FileSystemContext> file_system_context_handler,
    186       const scoped_refptr<const Extension>& handler_extension,
    187       const std::vector<FileSystemURL>& file_urls);
    188 
    189   void DidOpenFileSystem(const std::vector<FileSystemURL>& file_urls,
    190                          base::PlatformFileError result,
    191                          const std::string& file_system_name,
    192                          const GURL& file_system_root);
    193 
    194   void ExecuteDoneOnUIThread(bool success);
    195   void ExecuteFileActionsOnUIThread(const std::string& file_system_name,
    196                                     const GURL& file_system_root,
    197                                     const FileDefinitionList& file_list);
    198   void SetupPermissionsAndDispatchEvent(const std::string& file_system_name,
    199                                         const GURL& file_system_root,
    200                                         const FileDefinitionList& file_list,
    201                                         int handler_pid_in,
    202                                         extensions::ExtensionHost* host);
    203 
    204   // Registers file permissions from |handler_host_permissions_| with
    205   // ChildProcessSecurityPolicy for process with id |handler_pid|.
    206   void SetupHandlerHostFileAccessPermissions(
    207       const FileDefinitionList& file_list,
    208       const Extension* extension,
    209       int handler_pid);
    210 
    211   Profile* profile_;
    212   scoped_refptr<const Extension> extension_;
    213   int32 tab_id_;
    214   const std::string action_id_;
    215   file_tasks::FileTaskFinishedCallback done_;
    216   base::WeakPtrFactory<FileBrowserHandlerExecutor> weak_ptr_factory_;
    217 
    218   DISALLOW_COPY_AND_ASSIGN(FileBrowserHandlerExecutor);
    219 };
    220 
    221 FileBrowserHandlerExecutor::FileDefinition::FileDefinition()
    222     : is_directory(false) {
    223 }
    224 
    225 FileBrowserHandlerExecutor::FileDefinition::~FileDefinition() {
    226 }
    227 
    228 // static
    229 FileBrowserHandlerExecutor::FileDefinitionList
    230 FileBrowserHandlerExecutor::SetupFileAccessPermissions(
    231     scoped_refptr<fileapi::FileSystemContext> file_system_context_handler,
    232     const scoped_refptr<const Extension>& handler_extension,
    233     const std::vector<FileSystemURL>& file_urls) {
    234   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    235   DCHECK(handler_extension.get());
    236 
    237   fileapi::ExternalFileSystemBackend* backend =
    238       file_system_context_handler->external_backend();
    239 
    240   FileDefinitionList file_list;
    241   for (size_t i = 0; i < file_urls.size(); ++i) {
    242     const FileSystemURL& url = file_urls[i];
    243 
    244     // Check if this file system entry exists first.
    245     base::PlatformFileInfo file_info;
    246 
    247     base::FilePath local_path = url.path();
    248     base::FilePath virtual_path = url.virtual_path();
    249 
    250     bool is_drive_file = url.type() == fileapi::kFileSystemTypeDrive;
    251     DCHECK(!is_drive_file || drive::util::IsUnderDriveMountPoint(local_path));
    252 
    253     // If the file is under drive mount point, there is no actual file to be
    254     // found on the url.path().
    255     if (!is_drive_file) {
    256       if (!base::PathExists(local_path) ||
    257           file_util::IsLink(local_path) ||
    258           !file_util::GetFileInfo(local_path, &file_info)) {
    259         continue;
    260       }
    261     }
    262 
    263     // Grant access to this particular file to target extension. This will
    264     // ensure that the target extension can access only this FS entry and
    265     // prevent from traversing FS hierarchy upward.
    266     backend->GrantFileAccessToExtension(
    267         handler_extension->id(), virtual_path);
    268 
    269     // Output values.
    270     FileDefinition file;
    271     file.virtual_path = virtual_path;
    272     file.is_directory = file_info.is_directory;
    273     file.absolute_path = local_path;
    274     file_list.push_back(file);
    275   }
    276   return file_list;
    277 }
    278 
    279 FileBrowserHandlerExecutor::FileBrowserHandlerExecutor(
    280     Profile* profile,
    281     const Extension* extension,
    282     int32 tab_id,
    283     const std::string& action_id)
    284     : profile_(profile),
    285       extension_(extension),
    286       tab_id_(tab_id),
    287       action_id_(action_id),
    288       weak_ptr_factory_(this) {
    289 }
    290 
    291 FileBrowserHandlerExecutor::~FileBrowserHandlerExecutor() {}
    292 
    293 void FileBrowserHandlerExecutor::Execute(
    294     const std::vector<FileSystemURL>& file_urls,
    295     const file_tasks::FileTaskFinishedCallback& done) {
    296   done_ = done;
    297 
    298   // Get file system context for the extension to which onExecute event will be
    299   // sent. The file access permissions will be granted to the extension in the
    300   // file system context for the files in |file_urls|.
    301   fileapi_util::GetFileSystemContextForExtensionId(
    302       profile_, extension_->id())->OpenFileSystem(
    303           Extension::GetBaseURLFromExtensionId(extension_->id()).GetOrigin(),
    304           fileapi::kFileSystemTypeExternal,
    305           fileapi::OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
    306           base::Bind(&FileBrowserHandlerExecutor::DidOpenFileSystem,
    307                      weak_ptr_factory_.GetWeakPtr(),
    308                      file_urls));
    309 }
    310 
    311 void FileBrowserHandlerExecutor::DidOpenFileSystem(
    312     const std::vector<FileSystemURL>& file_urls,
    313     base::PlatformFileError result,
    314     const std::string& file_system_name,
    315     const GURL& file_system_root) {
    316   if (result != base::PLATFORM_FILE_OK) {
    317     ExecuteDoneOnUIThread(false);
    318     return;
    319   }
    320 
    321   scoped_refptr<fileapi::FileSystemContext> file_system_context(
    322       fileapi_util::GetFileSystemContextForExtensionId(
    323           profile_, extension_->id()));
    324   BrowserThread::PostTaskAndReplyWithResult(
    325       BrowserThread::FILE,
    326       FROM_HERE,
    327       base::Bind(&SetupFileAccessPermissions,
    328                  file_system_context,
    329                  extension_,
    330                  file_urls),
    331       base::Bind(&FileBrowserHandlerExecutor::ExecuteFileActionsOnUIThread,
    332                  weak_ptr_factory_.GetWeakPtr(),
    333                  file_system_name,
    334                  file_system_root));
    335 }
    336 
    337 void FileBrowserHandlerExecutor::ExecuteDoneOnUIThread(bool success) {
    338   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    339   if (!done_.is_null())
    340     done_.Run(success);
    341   delete this;
    342 }
    343 
    344 void FileBrowserHandlerExecutor::ExecuteFileActionsOnUIThread(
    345     const std::string& file_system_name,
    346     const GURL& file_system_root,
    347     const FileDefinitionList& file_list) {
    348   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    349 
    350   if (file_list.empty()) {
    351     ExecuteDoneOnUIThread(false);
    352     return;
    353   }
    354 
    355   int handler_pid = ExtractProcessFromExtensionId(profile_, extension_->id());
    356   if (handler_pid <= 0 &&
    357       !extensions::BackgroundInfo::HasLazyBackgroundPage(extension_.get())) {
    358     ExecuteDoneOnUIThread(false);
    359     return;
    360   }
    361 
    362   if (handler_pid > 0) {
    363     SetupPermissionsAndDispatchEvent(file_system_name, file_system_root,
    364                                      file_list, handler_pid, NULL);
    365   } else {
    366     // We have to wake the handler background page before we proceed.
    367     extensions::LazyBackgroundTaskQueue* queue =
    368         extensions::ExtensionSystem::Get(profile_)->
    369         lazy_background_task_queue();
    370     if (!queue->ShouldEnqueueTask(profile_, extension_.get())) {
    371       ExecuteDoneOnUIThread(false);
    372       return;
    373     }
    374     queue->AddPendingTask(
    375         profile_, extension_->id(),
    376         base::Bind(
    377             &FileBrowserHandlerExecutor::SetupPermissionsAndDispatchEvent,
    378             weak_ptr_factory_.GetWeakPtr(),
    379             file_system_name,
    380             file_system_root,
    381             file_list,
    382             handler_pid));
    383   }
    384 }
    385 
    386 void FileBrowserHandlerExecutor::SetupPermissionsAndDispatchEvent(
    387     const std::string& file_system_name,
    388     const GURL& file_system_root,
    389     const FileDefinitionList& file_list,
    390     int handler_pid_in,
    391     extensions::ExtensionHost* host) {
    392   int handler_pid = host ? host->render_process_host()->GetID() :
    393       handler_pid_in;
    394 
    395   if (handler_pid <= 0) {
    396     ExecuteDoneOnUIThread(false);
    397     return;
    398   }
    399 
    400   extensions::EventRouter* event_router =
    401       extensions::ExtensionSystem::Get(profile_)->event_router();
    402   if (!event_router) {
    403     ExecuteDoneOnUIThread(false);
    404     return;
    405   }
    406 
    407   SetupHandlerHostFileAccessPermissions(
    408       file_list, extension_.get(), handler_pid);
    409 
    410   scoped_ptr<ListValue> event_args(new ListValue());
    411   event_args->Append(new base::StringValue(action_id_));
    412   DictionaryValue* details = new DictionaryValue();
    413   event_args->Append(details);
    414   // Get file definitions. These will be replaced with Entry instances by
    415   // dispatchEvent() method from event_binding.js.
    416   ListValue* file_entries = new ListValue();
    417   details->Set("entries", file_entries);
    418   for (FileDefinitionList::const_iterator iter = file_list.begin();
    419        iter != file_list.end();
    420        ++iter) {
    421     DictionaryValue* file_def = new DictionaryValue();
    422     file_entries->Append(file_def);
    423     file_def->SetString("fileSystemName", file_system_name);
    424     file_def->SetString("fileSystemRoot", file_system_root.spec());
    425     base::FilePath root(FILE_PATH_LITERAL("/"));
    426     base::FilePath full_path = root.Append(iter->virtual_path);
    427     file_def->SetString("fileFullPath", full_path.value());
    428     file_def->SetBoolean("fileIsDirectory", iter->is_directory);
    429   }
    430 
    431   details->SetInteger("tab_id", tab_id_);
    432 
    433   scoped_ptr<extensions::Event> event(new extensions::Event(
    434       "fileBrowserHandler.onExecute", event_args.Pass()));
    435   event->restrict_to_profile = profile_;
    436   event_router->DispatchEventToExtension(extension_->id(), event.Pass());
    437 
    438   ExecuteDoneOnUIThread(true);
    439 }
    440 
    441 void FileBrowserHandlerExecutor::SetupHandlerHostFileAccessPermissions(
    442     const FileDefinitionList& file_list,
    443     const Extension* extension,
    444     int handler_pid) {
    445   const FileBrowserHandler* action = FindFileBrowserHandlerForActionId(
    446       extension_, action_id_);
    447   for (FileDefinitionList::const_iterator iter = file_list.begin();
    448        iter != file_list.end();
    449        ++iter) {
    450     if (!action)
    451       continue;
    452     if (action->CanRead()) {
    453       content::ChildProcessSecurityPolicy::GetInstance()->GrantReadFile(
    454           handler_pid, iter->absolute_path);
    455     }
    456     if (action->CanWrite()) {
    457       content::ChildProcessSecurityPolicy::GetInstance()->
    458           GrantCreateReadWriteFile(handler_pid, iter->absolute_path);
    459     }
    460   }
    461 }
    462 
    463 }  // namespace
    464 
    465 bool ExecuteFileBrowserHandler(
    466     Profile* profile,
    467     const Extension* extension,
    468     int32 tab_id,
    469     const std::string& action_id,
    470     const std::vector<FileSystemURL>& file_urls,
    471     const file_tasks::FileTaskFinishedCallback& done) {
    472   // Forbid calling undeclared handlers.
    473   if (!FindFileBrowserHandlerForActionId(extension, action_id))
    474     return false;
    475 
    476   // The executor object will be self deleted on completion.
    477   (new FileBrowserHandlerExecutor(
    478       profile, extension, tab_id, action_id))->Execute(file_urls, done);
    479   return true;
    480 }
    481 
    482 bool IsFallbackFileBrowserHandler(const FileBrowserHandler* handler) {
    483   const std::string& extension_id = handler->extension_id();
    484   return (extension_id == kFileBrowserDomain ||
    485           extension_id == extension_misc::kQuickOfficeComponentExtensionId ||
    486           extension_id == extension_misc::kQuickOfficeDevExtensionId ||
    487           extension_id == extension_misc::kQuickOfficeExtensionId);
    488 }
    489 
    490 FileBrowserHandlerList FindDefaultFileBrowserHandlers(
    491     Profile* profile,
    492     const std::vector<base::FilePath>& file_list,
    493     const FileBrowserHandlerList& common_handlers) {
    494   FileBrowserHandlerList default_handlers;
    495 
    496   std::set<std::string> default_ids;
    497   for (std::vector<base::FilePath>::const_iterator it = file_list.begin();
    498        it != file_list.end(); ++it) {
    499     std::string task_id = file_tasks::GetDefaultTaskIdFromPrefs(
    500         profile, "", it->Extension());
    501     if (!task_id.empty())
    502       default_ids.insert(task_id);
    503   }
    504 
    505   const FileBrowserHandler* fallback_handler = NULL;
    506   // Convert the default task IDs collected above to one of the handler pointers
    507   // from common_handlers.
    508   for (size_t i = 0; i < common_handlers.size(); ++i) {
    509     const FileBrowserHandler* handler = common_handlers[i];
    510     std::string task_id = file_tasks::MakeTaskID(
    511         handler->extension_id(),
    512         file_tasks::kFileBrowserHandlerTaskType,
    513         handler->id());
    514     std::set<std::string>::iterator default_iter = default_ids.find(task_id);
    515     if (default_iter != default_ids.end()) {
    516       default_handlers.push_back(handler);
    517       continue;
    518     }
    519 
    520     // Remember the first fallback handler.
    521     if (!fallback_handler && IsFallbackFileBrowserHandler(handler))
    522       fallback_handler = handler;
    523   }
    524 
    525   // If there are no default handlers found, use fallback as default.
    526   if (fallback_handler && default_handlers.empty())
    527     default_handlers.push_back(fallback_handler);
    528 
    529   return default_handlers;
    530 }
    531 
    532 FileBrowserHandlerList FindCommonFileBrowserHandlers(
    533     Profile* profile,
    534     const std::vector<GURL>& file_list) {
    535   FileBrowserHandlerList common_handlers;
    536   for (std::vector<GURL>::const_iterator it = file_list.begin();
    537        it != file_list.end(); ++it) {
    538     FileBrowserHandlerList handlers =
    539         FindFileBrowserHandlersForURL(profile, *it);
    540     // If there is nothing to do for one file, the intersection of handlers
    541     // for all files will be empty at the end, so no need to check further.
    542     if (handlers.empty())
    543       return FileBrowserHandlerList();
    544 
    545     // For the very first file, just copy all the elements.
    546     if (it == file_list.begin()) {
    547       common_handlers = handlers;
    548     } else {
    549       // For all additional files, find intersection between the accumulated and
    550       // file specific set.
    551       FileBrowserHandlerList intersection;
    552       std::set_intersection(common_handlers.begin(), common_handlers.end(),
    553                             handlers.begin(), handlers.end(),
    554                             std::back_inserter(intersection));
    555       common_handlers = intersection;
    556       if (common_handlers.empty())
    557         return FileBrowserHandlerList();
    558     }
    559   }
    560 
    561   FileBrowserHandlerList::iterator watch_iter =
    562       FindFileBrowserHandlerForExtensionIdAndActionId(
    563           &common_handlers, kFileBrowserDomain, kFileBrowserWatchTaskId);
    564   FileBrowserHandlerList::iterator gallery_iter =
    565       FindFileBrowserHandlerForExtensionIdAndActionId(
    566           &common_handlers, kFileBrowserDomain, kFileBrowserGalleryTaskId);
    567   if (watch_iter != common_handlers.end() &&
    568       gallery_iter != common_handlers.end()) {
    569     // Both "watch" and "gallery" actions are applicable which means that the
    570     // selection is all videos. Showing them both is confusing, so we only keep
    571     // the one that makes more sense ("watch" for single selection, "gallery"
    572     // for multiple selection).
    573     if (file_list.size() == 1)
    574       common_handlers.erase(gallery_iter);
    575     else
    576       common_handlers.erase(watch_iter);
    577   }
    578 
    579   return common_handlers;
    580 }
    581 
    582 const FileBrowserHandler* FindFileBrowserHandlerForURLAndPath(
    583     Profile* profile,
    584     const GURL& url,
    585     const base::FilePath& file_path) {
    586   std::vector<GURL> file_urls;
    587   file_urls.push_back(url);
    588 
    589   FileBrowserHandlerList common_handlers =
    590       FindCommonFileBrowserHandlers(profile, file_urls);
    591   if (common_handlers.empty())
    592     return NULL;
    593 
    594   std::vector<base::FilePath> file_paths;
    595   file_paths.push_back(file_path);
    596 
    597   FileBrowserHandlerList default_handlers =
    598       FindDefaultFileBrowserHandlers(profile, file_paths, common_handlers);
    599 
    600   // If there's none, or more than one, then we don't have a canonical default.
    601   if (!default_handlers.empty()) {
    602     // There should not be multiple default handlers for a single URL.
    603     DCHECK_EQ(1u, default_handlers.size());
    604 
    605     return *default_handlers.begin();
    606   }
    607 
    608   // If there are no default handlers, use first handler in the list (file
    609   // manager does the same in this situation).  TODO(tbarzic): This is not so
    610   // optimal behaviour.
    611   return *common_handlers.begin();
    612 }
    613 
    614 }  // namespace file_browser_handlers
    615 }  // namespace file_manager
    616