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/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/file_manager/app_id.h"
     13 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
     14 #include "chrome/browser/chromeos/file_manager/open_with_browser.h"
     15 #include "chrome/browser/chromeos/fileapi/file_system_backend.h"
     16 #include "chrome/browser/extensions/extension_service.h"
     17 #include "chrome/browser/extensions/extension_util.h"
     18 #include "chrome/browser/profiles/profile.h"
     19 #include "chrome/browser/ui/browser_finder.h"
     20 #include "chrome/common/extensions/api/file_browser_handlers/file_browser_handler.h"
     21 #include "chrome/common/extensions/api/file_browser_private.h"
     22 #include "chrome/common/extensions/extension_constants.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 "extensions/browser/event_router.h"
     29 #include "extensions/browser/extension_host.h"
     30 #include "extensions/browser/extension_system.h"
     31 #include "extensions/browser/lazy_background_task_queue.h"
     32 #include "extensions/common/extension_set.h"
     33 #include "extensions/common/manifest_handlers/background_info.h"
     34 #include "net/base/escape.h"
     35 #include "webkit/browser/fileapi/file_system_context.h"
     36 #include "webkit/browser/fileapi/file_system_url.h"
     37 #include "webkit/common/fileapi/file_system_info.h"
     38 #include "webkit/common/fileapi/file_system_util.h"
     39 
     40 using content::BrowserThread;
     41 using content::ChildProcessSecurityPolicy;
     42 using content::SiteInstance;
     43 using content::WebContents;
     44 using extensions::Extension;
     45 using fileapi::FileSystemURL;
     46 using file_manager::util::EntryDefinition;
     47 using file_manager::util::EntryDefinitionList;
     48 using file_manager::util::FileDefinition;
     49 using file_manager::util::FileDefinitionList;
     50 
     51 namespace file_manager {
     52 namespace file_browser_handlers {
     53 namespace {
     54 
     55 // Returns process id of the process the extension is running in.
     56 int ExtractProcessFromExtensionId(Profile* profile,
     57                                   const std::string& extension_id) {
     58   GURL extension_url =
     59       Extension::GetBaseURLFromExtensionId(extension_id);
     60   extensions::ProcessManager* manager =
     61     extensions::ExtensionSystem::Get(profile)->process_manager();
     62 
     63   SiteInstance* site_instance = manager->GetSiteInstanceForURL(extension_url);
     64   if (!site_instance || !site_instance->HasProcess())
     65     return -1;
     66   content::RenderProcessHost* process = site_instance->GetProcess();
     67 
     68   return process->GetID();
     69 }
     70 
     71 // Finds a file browser handler that matches |action_id|. Returns NULL if not
     72 // found.
     73 const FileBrowserHandler* FindFileBrowserHandlerForActionId(
     74     const Extension* extension,
     75     const std::string& action_id) {
     76   FileBrowserHandler::List* handler_list =
     77       FileBrowserHandler::GetHandlers(extension);
     78   for (FileBrowserHandler::List::const_iterator handler_iter =
     79            handler_list->begin();
     80        handler_iter != handler_list->end();
     81        ++handler_iter) {
     82     if (handler_iter->get()->id() == action_id)
     83       return handler_iter->get();
     84   }
     85   return NULL;
     86 }
     87 
     88 std::string EscapedUtf8ToLower(const std::string& str) {
     89   base::string16 utf16 = base::UTF8ToUTF16(
     90       net::UnescapeURLComponent(str, net::UnescapeRule::NORMAL));
     91   return net::EscapeUrlEncodedData(
     92       base::UTF16ToUTF8(base::i18n::ToLower(utf16)),
     93       false /* do not replace space with plus */);
     94 }
     95 
     96 // Finds file browser handlers that can handle the |selected_file_url|.
     97 FileBrowserHandlerList FindFileBrowserHandlersForURL(
     98     Profile* profile,
     99     const GURL& selected_file_url) {
    100   ExtensionService* service =
    101       extensions::ExtensionSystem::Get(profile)->extension_service();
    102   // In unit-tests, we may not have an ExtensionService.
    103   if (!service)
    104     return FileBrowserHandlerList();
    105 
    106   // We need case-insensitive matching, and pattern in the handler is already
    107   // in lower case.
    108   const GURL lowercase_url(EscapedUtf8ToLower(selected_file_url.spec()));
    109 
    110   FileBrowserHandlerList results;
    111   for (extensions::ExtensionSet::const_iterator iter =
    112            service->extensions()->begin();
    113        iter != service->extensions()->end(); ++iter) {
    114     const Extension* extension = iter->get();
    115     if (profile->IsOffTheRecord() &&
    116         !extensions::util::IsIncognitoEnabled(extension->id(), profile))
    117       continue;
    118 
    119     FileBrowserHandler::List* handler_list =
    120         FileBrowserHandler::GetHandlers(extension);
    121     if (!handler_list)
    122       continue;
    123     for (FileBrowserHandler::List::const_iterator handler_iter =
    124              handler_list->begin();
    125          handler_iter != handler_list->end();
    126          ++handler_iter) {
    127       const FileBrowserHandler* handler = handler_iter->get();
    128       if (!handler->MatchesURL(lowercase_url))
    129         continue;
    130 
    131       results.push_back(handler_iter->get());
    132     }
    133   }
    134   return results;
    135 }
    136 
    137 // This class is used to execute a file browser handler task. Here's how this
    138 // works:
    139 //
    140 // 1) Open the "external" file system
    141 // 2) Set up permissions for the target files on the external file system.
    142 // 3) Raise onExecute event with the action ID and entries of the target
    143 //    files. The event will launch the file browser handler if not active.
    144 // 4) In the file browser handler, onExecute event is handled and executes the
    145 //    task in JavaScript.
    146 //
    147 // That said, the class itself does not execute a task. The task will be
    148 // executed in JavaScript.
    149 class FileBrowserHandlerExecutor {
    150  public:
    151   FileBrowserHandlerExecutor(Profile* profile,
    152                              const Extension* extension,
    153                              const std::string& action_id);
    154 
    155   // Executes the task for each file. |done| will be run with the result.
    156   void Execute(const std::vector<FileSystemURL>& file_urls,
    157                const file_tasks::FileTaskFinishedCallback& done);
    158 
    159  private:
    160   // This object is responsible to delete itself.
    161   virtual ~FileBrowserHandlerExecutor();
    162 
    163   // Checks legitimacy of file url and grants file RO access permissions from
    164   // handler (target) extension and its renderer process.
    165   static scoped_ptr<FileDefinitionList> SetupFileAccessPermissions(
    166       scoped_refptr<fileapi::FileSystemContext> file_system_context_handler,
    167       const scoped_refptr<const Extension>& handler_extension,
    168       const std::vector<FileSystemURL>& file_urls);
    169 
    170   void ExecuteDoneOnUIThread(bool success);
    171   void ExecuteAfterSetupFileAccess(scoped_ptr<FileDefinitionList> file_list);
    172   void ExecuteFileActionsOnUIThread(
    173       scoped_ptr<FileDefinitionList> file_definition_list,
    174       scoped_ptr<EntryDefinitionList> entry_definition_list);
    175   void SetupPermissionsAndDispatchEvent(
    176       scoped_ptr<FileDefinitionList> file_definition_list,
    177       scoped_ptr<EntryDefinitionList> entry_definition_list,
    178       int handler_pid_in,
    179       extensions::ExtensionHost* host);
    180 
    181   // Registers file permissions from |handler_host_permissions_| with
    182   // ChildProcessSecurityPolicy for process with id |handler_pid|.
    183   void SetupHandlerHostFileAccessPermissions(
    184       FileDefinitionList* file_definition_list,
    185       const Extension* extension,
    186       int handler_pid);
    187 
    188   Profile* profile_;
    189   scoped_refptr<const Extension> extension_;
    190   const std::string action_id_;
    191   file_tasks::FileTaskFinishedCallback done_;
    192   base::WeakPtrFactory<FileBrowserHandlerExecutor> weak_ptr_factory_;
    193 
    194   DISALLOW_COPY_AND_ASSIGN(FileBrowserHandlerExecutor);
    195 };
    196 
    197 // static
    198 scoped_ptr<FileDefinitionList>
    199 FileBrowserHandlerExecutor::SetupFileAccessPermissions(
    200     scoped_refptr<fileapi::FileSystemContext> file_system_context_handler,
    201     const scoped_refptr<const Extension>& handler_extension,
    202     const std::vector<FileSystemURL>& file_urls) {
    203   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
    204   DCHECK(handler_extension.get());
    205 
    206   fileapi::ExternalFileSystemBackend* backend =
    207       file_system_context_handler->external_backend();
    208 
    209   scoped_ptr<FileDefinitionList> file_definition_list(new FileDefinitionList);
    210   for (size_t i = 0; i < file_urls.size(); ++i) {
    211     const FileSystemURL& url = file_urls[i];
    212 
    213     // Check if this file system entry exists first.
    214     base::File::Info file_info;
    215 
    216     base::FilePath local_path = url.path();
    217     base::FilePath virtual_path = url.virtual_path();
    218 
    219     const bool is_drive_file = url.type() == fileapi::kFileSystemTypeDrive;
    220     DCHECK(!is_drive_file || drive::util::IsUnderDriveMountPoint(local_path));
    221 
    222     const bool is_native_file =
    223         url.type() == fileapi::kFileSystemTypeNativeLocal ||
    224         url.type() == fileapi::kFileSystemTypeRestrictedNativeLocal;
    225 
    226     // If the file is from a physical volume, actual file must be found.
    227     if (is_native_file) {
    228       if (!base::PathExists(local_path) ||
    229           base::IsLink(local_path) ||
    230           !base::GetFileInfo(local_path, &file_info)) {
    231         continue;
    232       }
    233     }
    234 
    235     // Grant access to this particular file to target extension. This will
    236     // ensure that the target extension can access only this FS entry and
    237     // prevent from traversing FS hierarchy upward.
    238     backend->GrantFileAccessToExtension(handler_extension->id(), virtual_path);
    239 
    240     // Output values.
    241     FileDefinition file_definition;
    242     file_definition.virtual_path = virtual_path;
    243     file_definition.is_directory = file_info.is_directory;
    244     file_definition.absolute_path = local_path;
    245     file_definition_list->push_back(file_definition);
    246   }
    247 
    248   return file_definition_list.Pass();
    249 }
    250 
    251 FileBrowserHandlerExecutor::FileBrowserHandlerExecutor(
    252     Profile* profile,
    253     const Extension* extension,
    254     const std::string& action_id)
    255     : profile_(profile),
    256       extension_(extension),
    257       action_id_(action_id),
    258       weak_ptr_factory_(this) {
    259 }
    260 
    261 FileBrowserHandlerExecutor::~FileBrowserHandlerExecutor() {}
    262 
    263 void FileBrowserHandlerExecutor::Execute(
    264     const std::vector<FileSystemURL>& file_urls,
    265     const file_tasks::FileTaskFinishedCallback& done) {
    266   done_ = done;
    267 
    268   // Get file system context for the extension to which onExecute event will be
    269   // sent. The file access permissions will be granted to the extension in the
    270   // file system context for the files in |file_urls|.
    271   scoped_refptr<fileapi::FileSystemContext> file_system_context(
    272       util::GetFileSystemContextForExtensionId(
    273           profile_, extension_->id()));
    274 
    275   BrowserThread::PostTaskAndReplyWithResult(
    276       BrowserThread::FILE,
    277       FROM_HERE,
    278       base::Bind(&SetupFileAccessPermissions,
    279                  file_system_context,
    280                  extension_,
    281                  file_urls),
    282       base::Bind(&FileBrowserHandlerExecutor::ExecuteAfterSetupFileAccess,
    283                  weak_ptr_factory_.GetWeakPtr()));
    284 }
    285 
    286 void FileBrowserHandlerExecutor::ExecuteAfterSetupFileAccess(
    287     scoped_ptr<FileDefinitionList> file_definition_list) {
    288   // Outlives the conversion process, since bound to the callback.
    289   const FileDefinitionList& file_definition_list_ref =
    290       *file_definition_list.get();
    291   file_manager::util::ConvertFileDefinitionListToEntryDefinitionList(
    292       profile_,
    293       extension_->id(),
    294       file_definition_list_ref,
    295       base::Bind(&FileBrowserHandlerExecutor::ExecuteFileActionsOnUIThread,
    296                  weak_ptr_factory_.GetWeakPtr(),
    297                  base::Passed(&file_definition_list)));
    298 }
    299 
    300 void FileBrowserHandlerExecutor::ExecuteDoneOnUIThread(bool success) {
    301   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    302   if (!done_.is_null())
    303     done_.Run(
    304         success
    305             ? extensions::api::file_browser_private::TASK_RESULT_MESSAGE_SENT
    306             : extensions::api::file_browser_private::TASK_RESULT_FAILED);
    307   delete this;
    308 }
    309 
    310 void FileBrowserHandlerExecutor::ExecuteFileActionsOnUIThread(
    311     scoped_ptr<FileDefinitionList> file_definition_list,
    312     scoped_ptr<EntryDefinitionList> entry_definition_list) {
    313   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    314 
    315   if (file_definition_list->empty() || entry_definition_list->empty()) {
    316     ExecuteDoneOnUIThread(false);
    317     return;
    318   }
    319 
    320   int handler_pid = ExtractProcessFromExtensionId(profile_, extension_->id());
    321   if (handler_pid <= 0 &&
    322       !extensions::BackgroundInfo::HasLazyBackgroundPage(extension_.get())) {
    323     ExecuteDoneOnUIThread(false);
    324     return;
    325   }
    326 
    327   if (handler_pid > 0) {
    328     SetupPermissionsAndDispatchEvent(file_definition_list.Pass(),
    329                                      entry_definition_list.Pass(),
    330                                      handler_pid,
    331                                      NULL);
    332   } else {
    333     // We have to wake the handler background page before we proceed.
    334     extensions::LazyBackgroundTaskQueue* queue =
    335         extensions::ExtensionSystem::Get(profile_)->
    336         lazy_background_task_queue();
    337     if (!queue->ShouldEnqueueTask(profile_, extension_.get())) {
    338       ExecuteDoneOnUIThread(false);
    339       return;
    340     }
    341     queue->AddPendingTask(
    342         profile_,
    343         extension_->id(),
    344         base::Bind(
    345             &FileBrowserHandlerExecutor::SetupPermissionsAndDispatchEvent,
    346             weak_ptr_factory_.GetWeakPtr(),
    347             base::Passed(file_definition_list.Pass()),
    348             base::Passed(entry_definition_list.Pass()),
    349             handler_pid));
    350   }
    351 }
    352 
    353 void FileBrowserHandlerExecutor::SetupPermissionsAndDispatchEvent(
    354     scoped_ptr<FileDefinitionList> file_definition_list,
    355     scoped_ptr<EntryDefinitionList> entry_definition_list,
    356     int handler_pid_in,
    357     extensions::ExtensionHost* host) {
    358   int handler_pid = host ? host->render_process_host()->GetID() :
    359       handler_pid_in;
    360 
    361   if (handler_pid <= 0) {
    362     ExecuteDoneOnUIThread(false);
    363     return;
    364   }
    365 
    366   extensions::EventRouter* router = extensions::EventRouter::Get(profile_);
    367   if (!router) {
    368     ExecuteDoneOnUIThread(false);
    369     return;
    370   }
    371 
    372   SetupHandlerHostFileAccessPermissions(
    373       file_definition_list.get(), extension_.get(), handler_pid);
    374 
    375   scoped_ptr<base::ListValue> event_args(new base::ListValue());
    376   event_args->Append(new base::StringValue(action_id_));
    377   base::DictionaryValue* details = new base::DictionaryValue();
    378   event_args->Append(details);
    379   // Get file definitions. These will be replaced with Entry instances by
    380   // dispatchEvent() method from event_binding.js.
    381   base::ListValue* file_entries = new base::ListValue();
    382   details->Set("entries", file_entries);
    383 
    384   for (EntryDefinitionList::const_iterator iter =
    385            entry_definition_list->begin();
    386        iter != entry_definition_list->end();
    387        ++iter) {
    388     base::DictionaryValue* file_def = new base::DictionaryValue();
    389     file_entries->Append(file_def);
    390     file_def->SetString("fileSystemName", iter->file_system_name);
    391     file_def->SetString("fileSystemRoot", iter->file_system_root_url);
    392     file_def->SetString("fileFullPath",
    393                         "/" + iter->full_path.AsUTF8Unsafe());
    394     file_def->SetBoolean("fileIsDirectory", iter->is_directory);
    395   }
    396 
    397   scoped_ptr<extensions::Event> event(new extensions::Event(
    398       "fileBrowserHandler.onExecute", event_args.Pass()));
    399   event->restrict_to_browser_context = profile_;
    400   router->DispatchEventToExtension(extension_->id(), event.Pass());
    401 
    402   ExecuteDoneOnUIThread(true);
    403 }
    404 
    405 void FileBrowserHandlerExecutor::SetupHandlerHostFileAccessPermissions(
    406     FileDefinitionList* file_definition_list,
    407     const Extension* extension,
    408     int handler_pid) {
    409   const FileBrowserHandler* action = FindFileBrowserHandlerForActionId(
    410       extension_, action_id_);
    411   for (FileDefinitionList::const_iterator iter = file_definition_list->begin();
    412        iter != file_definition_list->end();
    413        ++iter) {
    414     if (!action)
    415       continue;
    416     if (action->CanRead()) {
    417       content::ChildProcessSecurityPolicy::GetInstance()->GrantReadFile(
    418           handler_pid, iter->absolute_path);
    419     }
    420     if (action->CanWrite()) {
    421       content::ChildProcessSecurityPolicy::GetInstance()->
    422           GrantCreateReadWriteFile(handler_pid, iter->absolute_path);
    423     }
    424   }
    425 }
    426 
    427 // Returns true if |extension_id| and |action_id| indicate that the file
    428 // currently being handled should be opened with the browser. This function
    429 // is used to handle certain action IDs of the file manager.
    430 bool ShouldBeOpenedWithBrowser(const std::string& extension_id,
    431                                const std::string& action_id) {
    432 
    433   return (extension_id == kFileManagerAppId &&
    434           (action_id == "view-pdf" ||
    435            action_id == "view-swf" ||
    436            action_id == "view-in-browser" ||
    437            action_id == "open-hosted-generic" ||
    438            action_id == "open-hosted-gdoc" ||
    439            action_id == "open-hosted-gsheet" ||
    440            action_id == "open-hosted-gslides"));
    441 }
    442 
    443 // Opens the files specified by |file_urls| with the browser for |profile|.
    444 // Returns true on success. It's a failure if no files are opened.
    445 bool OpenFilesWithBrowser(Profile* profile,
    446                           const std::vector<FileSystemURL>& file_urls) {
    447   int num_opened = 0;
    448   for (size_t i = 0; i < file_urls.size(); ++i) {
    449     const FileSystemURL& file_url = file_urls[i];
    450     if (chromeos::FileSystemBackend::CanHandleURL(file_url)) {
    451       const base::FilePath& file_path = file_url.path();
    452       num_opened += util::OpenFileWithBrowser(profile, file_path);
    453     }
    454   }
    455   return num_opened > 0;
    456 }
    457 
    458 }  // namespace
    459 
    460 bool ExecuteFileBrowserHandler(
    461     Profile* profile,
    462     const Extension* extension,
    463     const std::string& action_id,
    464     const std::vector<FileSystemURL>& file_urls,
    465     const file_tasks::FileTaskFinishedCallback& done) {
    466   // Forbid calling undeclared handlers.
    467   if (!FindFileBrowserHandlerForActionId(extension, action_id))
    468     return false;
    469 
    470   // Some action IDs of the file manager's file browser handlers require the
    471   // files to be directly opened with the browser.
    472   if (ShouldBeOpenedWithBrowser(extension->id(), action_id)) {
    473     const bool result = OpenFilesWithBrowser(profile, file_urls);
    474     if (result && !done.is_null())
    475       done.Run(extensions::api::file_browser_private::TASK_RESULT_OPENED);
    476     return result;
    477   }
    478 
    479   // The executor object will be self deleted on completion.
    480   (new FileBrowserHandlerExecutor(
    481       profile, extension, action_id))->Execute(file_urls, done);
    482   return true;
    483 }
    484 
    485 bool IsFallbackFileBrowserHandler(const file_tasks::TaskDescriptor& task) {
    486   return ((task.task_type == file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER ||
    487            task.task_type == file_tasks::TASK_TYPE_FILE_HANDLER) &&
    488           (task.app_id == kFileManagerAppId ||
    489            task.app_id == kVideoPlayerAppId ||
    490            task.app_id == kGalleryAppId ||
    491            task.app_id == extension_misc::kQuickOfficeComponentExtensionId ||
    492            task.app_id == extension_misc::kQuickOfficeInternalExtensionId ||
    493            task.app_id == extension_misc::kQuickOfficeExtensionId));
    494 }
    495 
    496 FileBrowserHandlerList FindFileBrowserHandlers(
    497     Profile* profile,
    498     const std::vector<GURL>& file_list) {
    499   FileBrowserHandlerList common_handlers;
    500   for (std::vector<GURL>::const_iterator it = file_list.begin();
    501        it != file_list.end(); ++it) {
    502     FileBrowserHandlerList handlers =
    503         FindFileBrowserHandlersForURL(profile, *it);
    504     // If there is nothing to do for one file, the intersection of handlers
    505     // for all files will be empty at the end, so no need to check further.
    506     if (handlers.empty())
    507       return FileBrowserHandlerList();
    508 
    509     // For the very first file, just copy all the elements.
    510     if (it == file_list.begin()) {
    511       common_handlers = handlers;
    512     } else {
    513       // For all additional files, find intersection between the accumulated and
    514       // file specific set.
    515       FileBrowserHandlerList intersection;
    516       std::set<const FileBrowserHandler*> common_handler_set(
    517           common_handlers.begin(), common_handlers.end());
    518 
    519       for (FileBrowserHandlerList::const_iterator itr = handlers.begin();
    520            itr != handlers.end(); ++itr) {
    521         if (ContainsKey(common_handler_set, *itr))
    522           intersection.push_back(*itr);
    523       }
    524 
    525       std::swap(common_handlers, intersection);
    526       if (common_handlers.empty())
    527         return FileBrowserHandlerList();
    528     }
    529   }
    530 
    531   return common_handlers;
    532 }
    533 
    534 }  // namespace file_browser_handlers
    535 }  // namespace file_manager
    536