Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/extensions/extension_file_browser_private_api.h"
      6 
      7 #include "base/base64.h"
      8 #include "base/command_line.h"
      9 #include "base/json/json_writer.h"
     10 #include "base/logging.h"
     11 #include "base/memory/singleton.h"
     12 #include "base/stringprintf.h"
     13 #include "base/string_util.h"
     14 #include "base/task.h"
     15 #include "base/time.h"
     16 #include "base/values.h"
     17 #include "chrome/browser/profiles/profile.h"
     18 #include "chrome/browser/extensions/extension_event_router.h"
     19 #include "chrome/browser/extensions/extension_function_dispatcher.h"
     20 #include "chrome/browser/extensions/extension_process_manager.h"
     21 #include "chrome/browser/extensions/extension_service.h"
     22 #include "chrome/browser/extensions/extension_tabs_module.h"
     23 #include "chrome/browser/extensions/file_manager_util.h"
     24 #include "chrome/browser/prefs/pref_service.h"
     25 #include "chrome/browser/prefs/scoped_user_pref_update.h"
     26 #include "chrome/browser/ui/browser.h"
     27 #include "chrome/browser/ui/views/html_dialog_view.h"
     28 #include "chrome/browser/ui/webui/extension_icon_source.h"
     29 #include "chrome/common/chrome_switches.h"
     30 #include "chrome/common/extensions/extension.h"
     31 #include "chrome/common/extensions/file_browser_handler.h"
     32 #include "chrome/common/pref_names.h"
     33 #include "content/browser/browser_thread.h"
     34 #include "content/browser/child_process_security_policy.h"
     35 #include "content/browser/renderer_host/render_process_host.h"
     36 #include "content/browser/renderer_host/render_view_host.h"
     37 #include "content/browser/tab_contents/tab_contents.h"
     38 #include "googleurl/src/gurl.h"
     39 #include "grit/generated_resources.h"
     40 #include "webkit/fileapi/file_system_context.h"
     41 #include "webkit/fileapi/file_system_mount_point_provider.h"
     42 #include "webkit/fileapi/file_system_operation.h"
     43 #include "webkit/fileapi/file_system_operation_context.h"
     44 #include "webkit/fileapi/file_system_path_manager.h"
     45 #include "webkit/fileapi/file_system_types.h"
     46 #include "webkit/fileapi/file_system_util.h"
     47 #include "webkit/fileapi/file_system_file_util.h"
     48 #include "webkit/fileapi/local_file_system_file_util.h"
     49 #include "ui/base/l10n/l10n_util.h"
     50 
     51 // Error messages.
     52 const char kFileError[] = "File error %d";
     53 const char kInvalidFileUrl[] = "Invalid file URL";
     54 
     55 // Internal task ids.
     56 const char kEnqueueTaskId[] = "enqueue";
     57 
     58 const int kReadOnlyFilePermissions = base::PLATFORM_FILE_OPEN |
     59                                      base::PLATFORM_FILE_READ |
     60                                      base::PLATFORM_FILE_EXCLUSIVE_READ |
     61                                      base::PLATFORM_FILE_ASYNC;
     62 
     63 const int kReadWriteFilePermissions = base::PLATFORM_FILE_OPEN |
     64                                       base::PLATFORM_FILE_CREATE |
     65                                       base::PLATFORM_FILE_OPEN_ALWAYS |
     66                                       base::PLATFORM_FILE_CREATE_ALWAYS |
     67                                       base::PLATFORM_FILE_READ |
     68                                       base::PLATFORM_FILE_WRITE |
     69                                       base::PLATFORM_FILE_EXCLUSIVE_READ |
     70                                       base::PLATFORM_FILE_EXCLUSIVE_WRITE |
     71                                       base::PLATFORM_FILE_ASYNC |
     72                                       base::PLATFORM_FILE_TRUNCATE |
     73                                       base::PLATFORM_FILE_WRITE_ATTRIBUTES;
     74 
     75 typedef std::pair<int, const FileBrowserHandler* > LastUsedHandler;
     76 typedef std::vector<LastUsedHandler> LastUsedHandlerList;
     77 
     78 typedef std::vector<const FileBrowserHandler*> ActionList;
     79 
     80 
     81 // Breaks down task_id that is used between getFileTasks() and executeTask() on
     82 // its building blocks. task_id field the following structure:
     83 //     <extension-id>|<task-action-id>
     84 // Currently, the only supported task-type is of 'context'.
     85 bool CrackTaskIdentifier(const std::string& task_id,
     86                          std::string* target_extension_id,
     87                          std::string* action_id) {
     88   std::vector<std::string> result;
     89   int count = Tokenize(task_id, std::string("|"), &result);
     90   if (count != 2)
     91     return false;
     92   *target_extension_id = result[0];
     93   *action_id = result[1];
     94   return true;
     95 }
     96 
     97 std::string MakeTaskID(const char* extension_id,
     98                        const char*  action_id) {
     99   return base::StringPrintf("%s|%s", extension_id, action_id);
    100 }
    101 
    102 bool GetFileBrowserHandlers(Profile* profile,
    103                            const GURL& selected_file_url,
    104                            ActionList* results) {
    105   ExtensionService* service = profile->GetExtensionService();
    106   if (!service)
    107     return false;  // In unit-tests, we may not have an ExtensionService.
    108 
    109   for (ExtensionList::const_iterator iter = service->extensions()->begin();
    110        iter != service->extensions()->end();
    111        ++iter) {
    112     const Extension* extension = iter->get();
    113     if (!extension->file_browser_handlers())
    114       continue;
    115 
    116     for (Extension::FileBrowserHandlerList::const_iterator action_iter =
    117              extension->file_browser_handlers()->begin();
    118         action_iter != extension->file_browser_handlers()->end();
    119         ++action_iter) {
    120       const FileBrowserHandler* action = action_iter->get();
    121       if (!action->MatchesURL(selected_file_url))
    122         continue;
    123 
    124       results->push_back(action_iter->get());
    125     }
    126   }
    127   return true;
    128 }
    129 
    130 bool SortByLastUsedTimestampDesc(const LastUsedHandler& a,
    131                                  const LastUsedHandler& b) {
    132   return a.first > b.first;
    133 }
    134 
    135 // TODO(zelidrag): Wire this with ICU to make this sort I18N happy.
    136 bool SortByTaskName(const LastUsedHandler& a, const LastUsedHandler& b) {
    137   return base::strcasecmp(a.second->title().data(),
    138                           b.second->title().data()) > 0;
    139 }
    140 
    141 // Given the list of selected files, returns array of context menu tasks
    142 // that are shared
    143 bool FindCommonTasks(Profile* profile,
    144                      ListValue* files_list,
    145                      LastUsedHandlerList* named_action_list) {
    146   named_action_list->clear();
    147   ActionList common_tasks;
    148   for (size_t i = 0; i < files_list->GetSize(); ++i) {
    149     std::string file_url;
    150     if (!files_list->GetString(i, &file_url))
    151       return false;
    152 
    153     ActionList file_actions;
    154     if (!GetFileBrowserHandlers(profile, GURL(file_url), &file_actions))
    155       return false;
    156     // If there is nothing to do for one file, the intersection of tasks for all
    157     // files will be empty at the end.
    158     if (!file_actions.size()) {
    159       common_tasks.clear();
    160       return true;
    161     }
    162     // For the very first file, just copy elements.
    163     if (i == 0) {
    164       common_tasks.insert(common_tasks.begin(),
    165                           file_actions.begin(),
    166                           file_actions.end());
    167       std::sort(common_tasks.begin(), common_tasks.end());
    168     } else if (common_tasks.size()) {
    169       // For all additional files, find intersection between the accumulated
    170       // and file specific set.
    171       std::sort(file_actions.begin(), file_actions.end());
    172       ActionList intersection(common_tasks.size());
    173       ActionList::iterator intersection_end =
    174           std::set_intersection(common_tasks.begin(),
    175                                 common_tasks.end(),
    176                                 file_actions.begin(),
    177                                 file_actions.end(),
    178                                 intersection.begin());
    179       common_tasks.clear();
    180       common_tasks.insert(common_tasks.begin(),
    181                           intersection.begin(),
    182                           intersection_end);
    183       std::sort(common_tasks.begin(), common_tasks.end());
    184     }
    185   }
    186 
    187   const DictionaryValue* prefs_tasks =
    188       profile->GetPrefs()->GetDictionary(prefs::kLastUsedFileBrowserHandlers);
    189   for (ActionList::const_iterator iter = common_tasks.begin();
    190        iter != common_tasks.end(); ++iter) {
    191     // Get timestamp of when this task was used last time.
    192     int last_used_timestamp = 0;
    193     prefs_tasks->GetInteger(MakeTaskID((*iter)->extension_id().data(),
    194                                        (*iter)->id().data()),
    195                              &last_used_timestamp);
    196     named_action_list->push_back(LastUsedHandler(last_used_timestamp, *iter));
    197   }
    198   // Sort by the last used descending.
    199   std::sort(named_action_list->begin(), named_action_list->end(),
    200             SortByLastUsedTimestampDesc);
    201   if (named_action_list->size() > 1) {
    202     // Sort the rest by name.
    203     std::sort(named_action_list->begin() + 1, named_action_list->end(),
    204               SortByTaskName);
    205   }
    206   return true;
    207 }
    208 
    209 // Update file handler usage stats.
    210 void UpdateFileHandlerUsageStats(Profile* profile, const std::string& task_id) {
    211   if (!profile || !profile->GetPrefs())
    212     return;
    213   DictionaryPrefUpdate prefs_usage_update(profile->GetPrefs(),
    214       prefs::kLastUsedFileBrowserHandlers);
    215   prefs_usage_update->SetWithoutPathExpansion(task_id,
    216       new FundamentalValue(
    217           static_cast<int>(base::Time::Now().ToInternalValue()/
    218                            base::Time::kMicrosecondsPerSecond)));
    219 }
    220 
    221 
    222 class LocalFileSystemCallbackDispatcher
    223     : public fileapi::FileSystemCallbackDispatcher {
    224  public:
    225   explicit LocalFileSystemCallbackDispatcher(
    226       RequestLocalFileSystemFunction* function,
    227       Profile* profile,
    228       int child_id,
    229       scoped_refptr<const Extension> extension)
    230       : function_(function),
    231         profile_(profile),
    232         child_id_(child_id),
    233         extension_(extension)  {
    234     DCHECK(function_);
    235   }
    236 
    237   // fileapi::FileSystemCallbackDispatcher overrides.
    238   virtual void DidSucceed() OVERRIDE {
    239     NOTREACHED();
    240   }
    241 
    242   virtual void DidGetLocalPath(const FilePath& local_path) {
    243     NOTREACHED();
    244   }
    245 
    246   virtual void DidReadMetadata(const base::PlatformFileInfo& info,
    247                                const FilePath& unused) OVERRIDE {
    248     NOTREACHED();
    249   }
    250 
    251   virtual void DidReadDirectory(
    252       const std::vector<base::FileUtilProxy::Entry>& entries,
    253       bool has_more) OVERRIDE {
    254     NOTREACHED();
    255   }
    256 
    257   virtual void DidWrite(int64 bytes, bool complete) OVERRIDE {
    258     NOTREACHED();
    259   }
    260 
    261   virtual void DidOpenFileSystem(const std::string& name,
    262                                  const GURL& root_path) OVERRIDE {
    263     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    264     // Set up file permission access.
    265     if (!SetupFileSystemAccessPermissions()) {
    266       DidFail(base::PLATFORM_FILE_ERROR_SECURITY);
    267       return;
    268     }
    269 
    270     BrowserThread::PostTask(
    271         BrowserThread::UI, FROM_HERE,
    272         NewRunnableMethod(function_,
    273             &RequestLocalFileSystemFunction::RespondSuccessOnUIThread,
    274             name,
    275             root_path));
    276   }
    277 
    278   virtual void DidFail(base::PlatformFileError error_code) OVERRIDE {
    279     BrowserThread::PostTask(
    280         BrowserThread::UI, FROM_HERE,
    281         NewRunnableMethod(function_,
    282             &RequestLocalFileSystemFunction::RespondFailedOnUIThread,
    283             error_code));
    284   }
    285 
    286  private:
    287 
    288   // Grants file system access permissions to file browser component.
    289   bool SetupFileSystemAccessPermissions() {
    290     if (!extension_.get())
    291       return false;
    292 
    293     // Make sure that only component extension can access the entire
    294     // local file system.
    295     if (extension_->location() != Extension::COMPONENT
    296 #ifndef NDEBUG
    297       && !CommandLine::ForCurrentProcess()->HasSwitch(
    298           switches::kExposePrivateExtensionApi)
    299 #endif
    300         ) {
    301       NOTREACHED() << "Private method access by non-component extension "
    302                    << extension_->id();
    303       return false;
    304     }
    305 
    306     fileapi::FileSystemPathManager* path_manager =
    307         profile_->GetFileSystemContext()->path_manager();
    308     fileapi::ExternalFileSystemMountPointProvider* provider =
    309         path_manager->external_provider();
    310     if (!provider)
    311       return false;
    312 
    313     // Grant full access to File API from this component extension.
    314     provider->GrantFullAccessToExtension(extension_->id());
    315 
    316     // Grant R/W file permissions to the renderer hosting component
    317     // extension for all paths exposed by our local file system provider.
    318     std::vector<FilePath> root_dirs = provider->GetRootDirectories();
    319     for (std::vector<FilePath>::iterator iter = root_dirs.begin();
    320          iter != root_dirs.end();
    321          ++iter) {
    322       ChildProcessSecurityPolicy::GetInstance()->GrantPermissionsForFile(
    323           child_id_, *iter, kReadWriteFilePermissions);
    324     }
    325     return true;
    326   }
    327 
    328   RequestLocalFileSystemFunction* function_;
    329   Profile* profile_;
    330   // Renderer process id.
    331   int child_id_;
    332   // Extension source URL.
    333   scoped_refptr<const Extension> extension_;
    334   DISALLOW_COPY_AND_ASSIGN(LocalFileSystemCallbackDispatcher);
    335 };
    336 
    337 void RequestLocalFileSystemFunction::RequestOnFileThread(
    338     const GURL& source_url, int child_id) {
    339   fileapi::FileSystemOperation* operation =
    340       new fileapi::FileSystemOperation(
    341           new LocalFileSystemCallbackDispatcher(
    342               this,
    343               profile(),
    344               child_id,
    345               GetExtension()),
    346           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
    347           profile()->GetFileSystemContext(),
    348           NULL);
    349   GURL origin_url = source_url.GetOrigin();
    350   operation->OpenFileSystem(origin_url, fileapi::kFileSystemTypeExternal,
    351                             false);     // create
    352 }
    353 
    354 bool RequestLocalFileSystemFunction::RunImpl() {
    355   if (!dispatcher() || !dispatcher()->render_view_host() ||
    356       !dispatcher()->render_view_host()->process())
    357     return false;
    358 
    359   BrowserThread::PostTask(
    360       BrowserThread::FILE, FROM_HERE,
    361       NewRunnableMethod(this,
    362           &RequestLocalFileSystemFunction::RequestOnFileThread,
    363           source_url_,
    364           dispatcher()->render_view_host()->process()->id()));
    365   // Will finish asynchronously.
    366   return true;
    367 }
    368 
    369 void RequestLocalFileSystemFunction::RespondSuccessOnUIThread(
    370     const std::string& name, const GURL& root_path) {
    371   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    372   result_.reset(new DictionaryValue());
    373   DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get());
    374   dict->SetString("name", name);
    375   dict->SetString("path", root_path.spec());
    376   dict->SetInteger("error", base::PLATFORM_FILE_OK);
    377   SendResponse(true);
    378 }
    379 
    380 void RequestLocalFileSystemFunction::RespondFailedOnUIThread(
    381     base::PlatformFileError error_code) {
    382   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    383   error_ = base::StringPrintf(kFileError, static_cast<int>(error_code));
    384   SendResponse(false);
    385 }
    386 
    387 bool GetFileTasksFileBrowserFunction::RunImpl() {
    388   ListValue* files_list = NULL;
    389   if (!args_->GetList(0, &files_list))
    390     return false;
    391 
    392   ListValue* result_list = new ListValue();
    393   result_.reset(result_list);
    394 
    395   LastUsedHandlerList common_tasks;
    396   if (!FindCommonTasks(profile_, files_list, &common_tasks))
    397     return false;
    398 
    399   ExtensionService* service = profile_->GetExtensionService();
    400   for (LastUsedHandlerList::const_iterator iter = common_tasks.begin();
    401        iter != common_tasks.end();
    402        ++iter) {
    403     const std::string extension_id = iter->second->extension_id();
    404     const Extension* extension = service->GetExtensionById(extension_id, false);
    405     CHECK(extension);
    406     DictionaryValue* task = new DictionaryValue();
    407     task->SetString("taskId", MakeTaskID(extension_id.data(),
    408                                          iter->second->id().data()));
    409     task->SetString("title", iter->second->title());
    410     // TODO(zelidrag): Figure out how to expose icon URL that task defined in
    411     // manifest instead of the default extension icon.
    412     GURL icon =
    413         ExtensionIconSource::GetIconURL(extension,
    414                                         Extension::EXTENSION_ICON_BITTY,
    415                                         ExtensionIconSet::MATCH_BIGGER,
    416                                         false);     // grayscale
    417     task->SetString("iconUrl", icon.spec());
    418     result_list->Append(task);
    419   }
    420 
    421   // TODO(zelidrag, serya): Add intent content tasks to result_list once we
    422   // implement that API.
    423   SendResponse(true);
    424   return true;
    425 }
    426 
    427 class ExecuteTasksFileSystemCallbackDispatcher
    428     : public fileapi::FileSystemCallbackDispatcher {
    429  public:
    430   explicit ExecuteTasksFileSystemCallbackDispatcher(
    431       ExecuteTasksFileBrowserFunction* function,
    432       Profile* profile,
    433       int child_id,
    434       const GURL& source_url,
    435       scoped_refptr<const Extension> extension,
    436       const std::string task_id,
    437       const std::vector<GURL>& file_urls)
    438       : function_(function),
    439         profile_(profile),
    440         source_url_(source_url),
    441         extension_(extension),
    442         task_id_(task_id),
    443         origin_file_urls_(file_urls) {
    444     DCHECK(function_);
    445   }
    446 
    447   // fileapi::FileSystemCallbackDispatcher overrides.
    448   virtual void DidSucceed() OVERRIDE {
    449     NOTREACHED();
    450   }
    451 
    452   virtual void DidGetLocalPath(const FilePath& local_path) {
    453     NOTREACHED();
    454   }
    455 
    456   virtual void DidReadMetadata(const base::PlatformFileInfo& info,
    457                                const FilePath& unused) OVERRIDE {
    458     NOTREACHED();
    459   }
    460 
    461   virtual void DidReadDirectory(
    462       const std::vector<base::FileUtilProxy::Entry>& entries,
    463       bool has_more) OVERRIDE {
    464     NOTREACHED();
    465   }
    466 
    467   virtual void DidWrite(int64 bytes, bool complete) OVERRIDE {
    468     NOTREACHED();
    469   }
    470 
    471   virtual void DidOpenFileSystem(const std::string& file_system_name,
    472                                  const GURL& file_system_root) OVERRIDE {
    473     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    474     ExecuteTasksFileBrowserFunction::FileDefinitionList file_list;
    475     for (std::vector<GURL>::iterator iter = origin_file_urls_.begin();
    476          iter != origin_file_urls_.end();
    477          ++iter) {
    478       // Set up file permission access.
    479       ExecuteTasksFileBrowserFunction::FileDefinition file;
    480       if (!SetupFileAccessPermissions(*iter, &file.target_file_url,
    481                                       &file.virtual_path, &file.is_directory)) {
    482         continue;
    483       }
    484       file_list.push_back(file);
    485     }
    486     if (file_list.empty()) {
    487       BrowserThread::PostTask(
    488           BrowserThread::UI, FROM_HERE,
    489           NewRunnableMethod(function_,
    490               &ExecuteTasksFileBrowserFunction::ExecuteFailedOnUIThread));
    491       return;
    492     }
    493 
    494     BrowserThread::PostTask(
    495         BrowserThread::UI, FROM_HERE,
    496         NewRunnableMethod(function_,
    497             &ExecuteTasksFileBrowserFunction::ExecuteFileActionsOnUIThread,
    498             task_id_,
    499             file_system_name,
    500             file_system_root,
    501             file_list));
    502   }
    503 
    504   virtual void DidFail(base::PlatformFileError error_code) OVERRIDE {
    505     LOG(WARNING) << "Local file system cant be resolved";
    506     BrowserThread::PostTask(
    507         BrowserThread::UI, FROM_HERE,
    508         NewRunnableMethod(function_,
    509             &ExecuteTasksFileBrowserFunction::ExecuteFailedOnUIThread));
    510   }
    511 
    512  private:
    513   // Checks legitimacy of file url and grants file RO access permissions from
    514   // handler (target) extension and its renderer process.
    515   bool SetupFileAccessPermissions(const GURL& origin_file_url,
    516      GURL* target_file_url, FilePath* file_path, bool* is_directory) {
    517 
    518     if (!extension_.get())
    519       return false;
    520 
    521     GURL file_origin_url;
    522     FilePath virtual_path;
    523     fileapi::FileSystemType type;
    524     if (!CrackFileSystemURL(origin_file_url, &file_origin_url, &type,
    525                             &virtual_path)) {
    526       return false;
    527     }
    528 
    529     if (type != fileapi::kFileSystemTypeExternal)
    530       return false;
    531 
    532     fileapi::FileSystemPathManager* path_manager =
    533         profile_->GetFileSystemContext()->path_manager();
    534     if (!path_manager->IsAccessAllowed(file_origin_url,
    535                                        type,
    536                                        virtual_path)) {
    537       return false;
    538     }
    539 
    540     // Make sure this url really being used by the right caller extension.
    541     if (source_url_.GetOrigin() != file_origin_url) {
    542       DidFail(base::PLATFORM_FILE_ERROR_SECURITY);
    543       return false;
    544     }
    545 
    546     FilePath root_path =
    547         path_manager->ValidateFileSystemRootAndGetPathOnFileThread(
    548           file_origin_url,
    549           fileapi::kFileSystemTypeExternal,
    550           virtual_path,
    551           false);     // create
    552     FilePath final_file_path = root_path.Append(virtual_path);
    553 
    554     // Check if this file system entry exists first.
    555     base::PlatformFileInfo file_info;
    556     FilePath platform_path;
    557     fileapi::FileSystemOperationContext file_system_operation_context(
    558         profile_->GetFileSystemContext(),
    559         fileapi::LocalFileSystemFileUtil::GetInstance());
    560     if (base::PLATFORM_FILE_OK !=
    561             fileapi::FileSystemFileUtil::GetInstance()->GetFileInfo(
    562                 &file_system_operation_context, final_file_path, &file_info,
    563                 &platform_path)) {
    564       return false;
    565     }
    566 
    567     fileapi::ExternalFileSystemMountPointProvider* external_provider =
    568         path_manager->external_provider();
    569     if (!external_provider)
    570       return false;
    571 
    572     // TODO(zelidrag): Let's just prevent all symlinks for now. We don't want a
    573     // USB drive content to point to something in the rest of the file system.
    574     // Ideally, we should permit symlinks within the boundary of the same
    575     // virtual mount point.
    576     if (file_info.is_symbolic_link)
    577       return false;
    578 
    579     // Get task details.
    580     std::string target_extension_id;
    581     std::string action_id;
    582     if (!CrackTaskIdentifier(task_id_, &target_extension_id,
    583                              &action_id)) {
    584       return false;
    585     }
    586 
    587     // TODO(zelidrag): Add explicit R/W + R/O permissions for non-component
    588     // extensions.
    589 
    590     // Get target extension's process.
    591     RenderProcessHost* target_host =
    592         profile_->GetExtensionProcessManager()->GetExtensionProcess(
    593             target_extension_id);
    594     if (!target_host)
    595       return false;
    596 
    597     // Grant R/O access permission to non-component extension and R/W to
    598     // component extensions.
    599     ChildProcessSecurityPolicy::GetInstance()->GrantPermissionsForFile(
    600         target_host->id(), final_file_path,
    601         extension_->location() != Extension::COMPONENT ?
    602             kReadOnlyFilePermissions : kReadWriteFilePermissions);
    603 
    604     // Grant access to this particular file to target extension. This will
    605     // ensure that the target extension can access only this FS entry and
    606     // prevent from traversing FS hierarchy upward.
    607     external_provider->GrantFileAccessToExtension(target_extension_id,
    608                                                   virtual_path);
    609 
    610     // Output values.
    611     GURL target_origin_url(Extension::GetBaseURLFromExtensionId(
    612         target_extension_id));
    613     GURL base_url = fileapi::GetFileSystemRootURI(target_origin_url,
    614         fileapi::kFileSystemTypeExternal);
    615     *target_file_url = GURL(base_url.spec() + virtual_path.value());
    616     FilePath root(FILE_PATH_LITERAL("/"));
    617     *file_path = root.Append(virtual_path);
    618     *is_directory = file_info.is_directory;
    619     return true;
    620   }
    621 
    622   ExecuteTasksFileBrowserFunction* function_;
    623   Profile* profile_;
    624   // Extension source URL.
    625   GURL source_url_;
    626   scoped_refptr<const Extension> extension_;
    627   std::string task_id_;
    628   std::vector<GURL> origin_file_urls_;
    629   DISALLOW_COPY_AND_ASSIGN(ExecuteTasksFileSystemCallbackDispatcher);
    630 };
    631 
    632 bool ExecuteTasksFileBrowserFunction::RunImpl() {
    633   // First param is task id that was to the extension with getFileTasks call.
    634   std::string task_id;
    635   if (!args_->GetString(0, &task_id) || !task_id.size())
    636     return false;
    637 
    638   // The second param is the list of files that need to be executed with this
    639   // task.
    640   ListValue* files_list = NULL;
    641   if (!args_->GetList(1, &files_list))
    642     return false;
    643 
    644   if (!files_list->GetSize())
    645     return true;
    646 
    647   return InitiateFileTaskExecution(task_id, files_list);
    648 }
    649 
    650 bool ExecuteTasksFileBrowserFunction::InitiateFileTaskExecution(
    651     const std::string& task_id, ListValue* files_list) {
    652   std::vector<GURL> file_urls;
    653   for (size_t i = 0; i < files_list->GetSize(); i++) {
    654     std::string origin_file_url;
    655     if (!files_list->GetString(i, &origin_file_url)) {
    656       error_ = kInvalidFileUrl;
    657       return false;
    658     }
    659     file_urls.push_back(GURL(origin_file_url));
    660   }
    661   // Get local file system instance on file thread.
    662   BrowserThread::PostTask(
    663       BrowserThread::FILE, FROM_HERE,
    664       NewRunnableMethod(this,
    665           &ExecuteTasksFileBrowserFunction::RequestFileEntryOnFileThread,
    666           source_url_,
    667           task_id,
    668           file_urls));
    669   result_.reset(new FundamentalValue(true));
    670   return true;
    671 }
    672 
    673 void ExecuteTasksFileBrowserFunction::RequestFileEntryOnFileThread(
    674     const GURL& source_url, const std::string& task_id,
    675     const std::vector<GURL>& file_urls) {
    676   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    677   fileapi::FileSystemOperation* operation =
    678       new fileapi::FileSystemOperation(
    679           new ExecuteTasksFileSystemCallbackDispatcher(
    680               this,
    681               profile(),
    682               dispatcher()->render_view_host()->process()->id(),
    683               source_url,
    684               GetExtension(),
    685               task_id,
    686               file_urls),
    687           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
    688           profile()->GetFileSystemContext(),
    689           NULL);
    690   GURL origin_url = source_url.GetOrigin();
    691   operation->OpenFileSystem(origin_url, fileapi::kFileSystemTypeExternal,
    692                             false);     // create
    693 }
    694 
    695 void ExecuteTasksFileBrowserFunction::ExecuteFailedOnUIThread() {
    696   SendResponse(false);
    697 }
    698 
    699 
    700 void ExecuteTasksFileBrowserFunction::ExecuteFileActionsOnUIThread(
    701     const std::string& task_id,
    702     const std::string& file_system_name,
    703     const GURL& file_system_root,
    704     const FileDefinitionList& file_list) {
    705   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    706   ExtensionService* service = profile_->GetExtensionService();
    707   if (!service)
    708     return;
    709   // Get task details.
    710   std::string handler_extension_id;
    711   std::string action_id;
    712   if (!CrackTaskIdentifier(task_id, &handler_extension_id,
    713                            &action_id)) {
    714     LOG(WARNING) << "Invalid task " << task_id;
    715     SendResponse(false);
    716     return;
    717   }
    718 
    719   const Extension* extension = service->GetExtensionById(handler_extension_id,
    720                                                          false);
    721   if (!extension) {
    722     SendResponse(false);
    723     return;
    724   }
    725 
    726   ExtensionEventRouter* event_router = profile_->GetExtensionEventRouter();
    727   if (!event_router) {
    728     SendResponse(false);
    729     return;
    730   }
    731 
    732   scoped_ptr<ListValue> event_args(new ListValue());
    733   event_args->Append(Value::CreateStringValue(action_id));
    734   DictionaryValue* details = new DictionaryValue();
    735   event_args->Append(details);
    736   // Get file definitions. These will be replaced with Entry instances by
    737   // chromeHidden.Event.dispatchJSON() method from even_binding.js.
    738   ListValue* files_urls = new ListValue();
    739   details->Set("entries", files_urls);
    740   for (FileDefinitionList::const_iterator iter = file_list.begin();
    741        iter != file_list.end();
    742        ++iter) {
    743     DictionaryValue* file_def = new DictionaryValue();
    744     files_urls->Append(file_def);
    745     file_def->SetString("fileSystemName", file_system_name);
    746     file_def->SetString("fileSystemRoot", file_system_root.spec());
    747     file_def->SetString("fileFullPath", iter->virtual_path.value());
    748     file_def->SetBoolean("fileIsDirectory", iter->is_directory);
    749   }
    750   // Get tab id.
    751   Browser* browser = GetCurrentBrowser();
    752   if (browser) {
    753     TabContents* contents = browser->GetSelectedTabContents();
    754     if (contents)
    755       details->SetInteger("tab_id", ExtensionTabUtil::GetTabId(contents));
    756   }
    757 
    758   UpdateFileHandlerUsageStats(profile_, task_id);
    759 
    760   std::string json_args;
    761   base::JSONWriter::Write(event_args.get(), false, &json_args);
    762   event_router->DispatchEventToExtension(
    763       handler_extension_id, std::string("fileBrowserHandler.onExecute"),
    764       json_args, profile_,
    765       GURL());
    766   SendResponse(true);
    767 }
    768 
    769 FileDialogFunction::FileDialogFunction() {
    770 }
    771 
    772 FileDialogFunction::~FileDialogFunction() {
    773 }
    774 
    775 // static
    776 FileDialogFunction::Callback
    777 FileDialogFunction::Callback::null_(NULL, NULL, NULL);
    778 
    779 // static
    780 FileDialogFunction::Callback::Map FileDialogFunction::Callback::map_;
    781 
    782 // static
    783 void FileDialogFunction::Callback::Add(int32 tab_id,
    784                                      SelectFileDialog::Listener* listener,
    785                                      HtmlDialogView* dialog,
    786                                      void* params) {
    787   if (map_.find(tab_id) == map_.end()) {
    788     map_.insert(std::make_pair(tab_id, Callback(listener, dialog, params)));
    789   } else {
    790     DLOG_ASSERT("FileDialogFunction::AddCallback tab_id already present");
    791   }
    792 }
    793 
    794 // static
    795 void FileDialogFunction::Callback::Remove(int32 tab_id) {
    796   map_.erase(tab_id);
    797 }
    798 
    799 // static
    800 const FileDialogFunction::Callback&
    801 FileDialogFunction::Callback::Find(int32 tab_id) {
    802   Callback::Map::const_iterator it = map_.find(tab_id);
    803   return (it == map_.end()) ? null_ : it->second;
    804 }
    805 
    806 
    807 int32 FileDialogFunction::GetTabId() const {
    808   return dispatcher()->delegate()->associated_tab_contents()->
    809     controller().session_id().id();
    810 }
    811 
    812 const FileDialogFunction::Callback& FileDialogFunction::GetCallback() const {
    813   if (!dispatcher() || !dispatcher()->delegate() ||
    814       !dispatcher()->delegate()->associated_tab_contents()) {
    815     return Callback::null();
    816   }
    817   return Callback::Find(GetTabId());
    818 }
    819 
    820 void FileDialogFunction::CloseDialog(HtmlDialogView* dialog) {
    821   DCHECK(dialog);
    822   TabContents* contents = dispatcher()->delegate()->associated_tab_contents();
    823   if (contents)
    824     dialog->CloseContents(contents);
    825 }
    826 
    827 // GetFileSystemRootPathOnFileThread can only be called from the file thread,
    828 // so here we are. This function takes a vector of virtual paths, converts
    829 // them to local paths and calls GetLocalPathsResponseOnUIThread with the
    830 // result vector, on the UI thread.
    831 void FileDialogFunction::GetLocalPathsOnFileThread(const UrlList& file_urls,
    832                                                    const std::string& task_id) {
    833   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    834   FilePathList selected_files;
    835 
    836   // FilePath(virtual_path) doesn't work on win, so limit this to ChromeOS.
    837 #if defined(OS_CHROMEOS)
    838   GURL origin_url = source_url().GetOrigin();
    839   fileapi::FileSystemPathManager* path_manager =
    840       profile()->GetFileSystemContext()->path_manager();
    841 
    842   size_t len = file_urls.size();
    843   selected_files.reserve(len);
    844   for (size_t i = 0; i < len; ++i) {
    845     const GURL& file_url = file_urls[i];
    846     GURL file_origin_url;
    847     FilePath virtual_path;
    848     fileapi::FileSystemType type;
    849     if (!CrackFileSystemURL(file_url, &file_origin_url, &type,
    850                             &virtual_path)) {
    851       continue;
    852     }
    853     if (type != fileapi::kFileSystemTypeExternal) {
    854       NOTREACHED();
    855       continue;
    856     }
    857     FilePath root = path_manager->ValidateFileSystemRootAndGetPathOnFileThread(
    858         origin_url,
    859         fileapi::kFileSystemTypeExternal,
    860         FilePath(virtual_path),
    861         false);
    862     if (!root.empty()) {
    863       selected_files.push_back(root.Append(virtual_path));
    864     } else {
    865       LOG(WARNING) << "GetLocalPathsOnFileThread failed "
    866                    << file_url.spec();
    867     }
    868   }
    869 #endif
    870 
    871   if (!selected_files.empty()) {
    872     BrowserThread::PostTask(
    873         BrowserThread::UI, FROM_HERE,
    874         NewRunnableMethod(this,
    875             &FileDialogFunction::GetLocalPathsResponseOnUIThread,
    876             selected_files, task_id));
    877   }
    878 }
    879 
    880 bool SelectFileFunction::RunImpl() {
    881   if (args_->GetSize() != 2) {
    882     return false;
    883   }
    884   std::string file_url;
    885   args_->GetString(0, &file_url);
    886   UrlList file_paths;
    887   file_paths.push_back(GURL(file_url));
    888 
    889   BrowserThread::PostTask(
    890       BrowserThread::FILE, FROM_HERE,
    891       NewRunnableMethod(this,
    892           &SelectFileFunction::GetLocalPathsOnFileThread,
    893           file_paths, std::string()));
    894 
    895   return true;
    896 }
    897 
    898 void SelectFileFunction::GetLocalPathsResponseOnUIThread(
    899     const FilePathList& files, const std::string& task_id) {
    900   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    901   if (files.size() != 1) {
    902     SendResponse(false);
    903     return;
    904   }
    905   int index;
    906   args_->GetInteger(1, &index);
    907   const Callback& callback = GetCallback();
    908   DCHECK(!callback.IsNull());
    909   if (!callback.IsNull()) {
    910     // Must do this before callback, as the callback may delete listeners
    911     // waiting for window close.
    912     CloseDialog(callback.dialog());
    913     callback.listener()->FileSelected(files[0],
    914                                       index,
    915                                       callback.params());
    916   }
    917   SendResponse(true);
    918 }
    919 
    920 
    921 ViewFilesFunction::ViewFilesFunction() {
    922 }
    923 
    924 ViewFilesFunction::~ViewFilesFunction() {
    925 }
    926 
    927 bool ViewFilesFunction::RunImpl() {
    928   if (args_->GetSize() < 1) {
    929     return false;
    930   }
    931 
    932   ListValue* path_list = NULL;
    933   args_->GetList(0, &path_list);
    934   DCHECK(path_list);
    935 
    936   std::string internal_task_id;
    937   args_->GetString(1, &internal_task_id);
    938 
    939   std::string virtual_path;
    940   size_t len = path_list->GetSize();
    941   UrlList file_urls;
    942   file_urls.reserve(len);
    943   for (size_t i = 0; i < len; ++i) {
    944     path_list->GetString(i, &virtual_path);
    945     file_urls.push_back(GURL(virtual_path));
    946   }
    947 
    948   BrowserThread::PostTask(
    949       BrowserThread::FILE, FROM_HERE,
    950       NewRunnableMethod(this,
    951           &ViewFilesFunction::GetLocalPathsOnFileThread,
    952           file_urls, internal_task_id));
    953 
    954   return true;
    955 }
    956 
    957 void ViewFilesFunction::GetLocalPathsResponseOnUIThread(
    958     const FilePathList& files, const std::string& internal_task_id) {
    959   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    960   for (FilePathList::const_iterator iter = files.begin();
    961        iter != files.end();
    962        ++iter) {
    963     FileManagerUtil::ViewItem(*iter, internal_task_id == kEnqueueTaskId);
    964   }
    965   UpdateFileHandlerUsageStats(profile_, internal_task_id);
    966   SendResponse(true);
    967 }
    968 
    969 SelectFilesFunction::SelectFilesFunction() {
    970 }
    971 
    972 SelectFilesFunction::~SelectFilesFunction() {
    973 }
    974 
    975 bool SelectFilesFunction::RunImpl() {
    976   if (args_->GetSize() != 1) {
    977     return false;
    978   }
    979 
    980   ListValue* path_list = NULL;
    981   args_->GetList(0, &path_list);
    982   DCHECK(path_list);
    983 
    984   std::string virtual_path;
    985   size_t len = path_list->GetSize();
    986   UrlList file_urls;
    987   file_urls.reserve(len);
    988   for (size_t i = 0; i < len; ++i) {
    989     path_list->GetString(i, &virtual_path);
    990     file_urls.push_back(GURL(virtual_path));
    991   }
    992 
    993   BrowserThread::PostTask(
    994       BrowserThread::FILE, FROM_HERE,
    995       NewRunnableMethod(this,
    996           &SelectFilesFunction::GetLocalPathsOnFileThread,
    997           file_urls, std::string()));
    998 
    999   return true;
   1000 }
   1001 
   1002 void SelectFilesFunction::GetLocalPathsResponseOnUIThread(
   1003     const FilePathList& files, const std::string& internal_task_id) {
   1004   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1005 
   1006   const Callback& callback = GetCallback();
   1007   DCHECK(!callback.IsNull());
   1008   if (!callback.IsNull()) {
   1009     // Must do this before callback, as the callback may delete listeners
   1010     // waiting for window close.
   1011     CloseDialog(callback.dialog());
   1012     callback.listener()->MultiFilesSelected(files, callback.params());
   1013   }
   1014   SendResponse(true);
   1015 }
   1016 
   1017 bool CancelFileDialogFunction::RunImpl() {
   1018   const Callback& callback = GetCallback();
   1019   DCHECK(!callback.IsNull());
   1020   if (!callback.IsNull()) {
   1021     // Must do this before callback, as the callback may delete listeners
   1022     // waiting for window close.
   1023     CloseDialog(callback.dialog());
   1024     callback.listener()->FileSelectionCanceled(callback.params());
   1025   }
   1026   SendResponse(true);
   1027   return true;
   1028 }
   1029 
   1030 bool FileDialogStringsFunction::RunImpl() {
   1031   result_.reset(new DictionaryValue());
   1032   DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get());
   1033 
   1034 #define SET_STRING(ns, id) \
   1035   dict->SetString(#id, l10n_util::GetStringUTF16(ns##_##id))
   1036 
   1037   SET_STRING(IDS, LOCALE_FMT_DATE_SHORT);
   1038   SET_STRING(IDS, LOCALE_MONTHS_SHORT);
   1039   SET_STRING(IDS, LOCALE_DAYS_SHORT);
   1040 
   1041   SET_STRING(IDS_FILE_BROWSER, BODY_FONT_FAMILY);
   1042   SET_STRING(IDS_FILE_BROWSER, BODY_FONT_SIZE);
   1043 
   1044   SET_STRING(IDS_FILE_BROWSER, ROOT_DIRECTORY_LABEL);
   1045   SET_STRING(IDS_FILE_BROWSER, DOWNLOADS_DIRECTORY_LABEL);
   1046   SET_STRING(IDS_FILE_BROWSER, MEDIA_DIRECTORY_LABEL);
   1047   SET_STRING(IDS_FILE_BROWSER, NAME_COLUMN_LABEL);
   1048   SET_STRING(IDS_FILE_BROWSER, SIZE_COLUMN_LABEL);
   1049   SET_STRING(IDS_FILE_BROWSER, DATE_COLUMN_LABEL);
   1050   SET_STRING(IDS_FILE_BROWSER, PREVIEW_COLUMN_LABEL);
   1051 
   1052   SET_STRING(IDS_FILE_BROWSER, ERROR_CREATING_FOLDER);
   1053   SET_STRING(IDS_FILE_BROWSER, ERROR_INVALID_FOLDER_CHARACTER);
   1054   SET_STRING(IDS_FILE_BROWSER, ERROR_INVALID_FILE_CHARACTER);
   1055   SET_STRING(IDS_FILE_BROWSER, NEW_FOLDER_PROMPT);
   1056   SET_STRING(IDS_FILE_BROWSER, NEW_FOLDER_BUTTON_LABEL);
   1057   SET_STRING(IDS_FILE_BROWSER, FILENAME_LABEL);
   1058 
   1059   SET_STRING(IDS_FILE_BROWSER, EJECT_BUTTON);
   1060   SET_STRING(IDS_FILE_BROWSER, IMAGE_DIMENSIONS);
   1061   SET_STRING(IDS_FILE_BROWSER, VOLUME_LABEL);
   1062   SET_STRING(IDS_FILE_BROWSER, READ_ONLY);
   1063 
   1064   SET_STRING(IDS_FILE_BROWSER, ERROR_RENAMING);
   1065   SET_STRING(IDS_FILE_BROWSER, RENAME_PROMPT);
   1066   SET_STRING(IDS_FILE_BROWSER, RENAME_BUTTON_LABEL);
   1067 
   1068   SET_STRING(IDS_FILE_BROWSER, ERROR_DELETING);
   1069   SET_STRING(IDS_FILE_BROWSER, DELETE_BUTTON_LABEL);
   1070 
   1071   SET_STRING(IDS_FILE_BROWSER, ERROR_MOVING);
   1072   SET_STRING(IDS_FILE_BROWSER, MOVE_BUTTON_LABEL);
   1073 
   1074   SET_STRING(IDS_FILE_BROWSER, ERROR_PASTING);
   1075   SET_STRING(IDS_FILE_BROWSER, PASTE_BUTTON_LABEL);
   1076 
   1077   SET_STRING(IDS_FILE_BROWSER, COPY_BUTTON_LABEL);
   1078   SET_STRING(IDS_FILE_BROWSER, CUT_BUTTON_LABEL);
   1079 
   1080   SET_STRING(IDS_FILE_BROWSER, DEVICE_TYPE_FLASH);
   1081   SET_STRING(IDS_FILE_BROWSER, DEVICE_TYPE_HDD);
   1082   SET_STRING(IDS_FILE_BROWSER, DEVICE_TYPE_OPTICAL);
   1083   SET_STRING(IDS_FILE_BROWSER, DEVICE_TYPE_UNDEFINED);
   1084 
   1085   SET_STRING(IDS_FILE_BROWSER, CANCEL_LABEL);
   1086   SET_STRING(IDS_FILE_BROWSER, OPEN_LABEL);
   1087   SET_STRING(IDS_FILE_BROWSER, SAVE_LABEL);
   1088 
   1089   SET_STRING(IDS_FILE_BROWSER, SELECT_FOLDER_TITLE);
   1090   SET_STRING(IDS_FILE_BROWSER, SELECT_OPEN_FILE_TITLE);
   1091   SET_STRING(IDS_FILE_BROWSER, SELECT_OPEN_MULTI_FILE_TITLE);
   1092   SET_STRING(IDS_FILE_BROWSER, SELECT_SAVEAS_FILE_TITLE);
   1093 
   1094   SET_STRING(IDS_FILE_BROWSER, COMPUTING_SELECTION);
   1095   SET_STRING(IDS_FILE_BROWSER, NOTHING_SELECTED);
   1096   SET_STRING(IDS_FILE_BROWSER, ONE_FILE_SELECTED);
   1097   SET_STRING(IDS_FILE_BROWSER, MANY_FILES_SELECTED);
   1098 
   1099   // FILEBROWSER, without the underscore, is from the old school codebase.
   1100   // TODO(rginda): Move these into IDS_FILE_BROWSER post M12.
   1101   SET_STRING(IDS_FILEBROWSER, CONFIRM_DELETE);
   1102 
   1103   SET_STRING(IDS_FILEBROWSER, ENQUEUE);
   1104 #undef SET_STRING
   1105 
   1106   // TODO(serya): Create a new string in .grd file for this one in M13.
   1107   dict->SetString("PREVIEW_IMAGE",
   1108       l10n_util::GetStringUTF16(IDS_CERT_MANAGER_VIEW_CERT_BUTTON));
   1109   dict->SetString("PLAY_MEDIA",
   1110       l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLAY));
   1111 
   1112   return true;
   1113 }
   1114