• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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'.
CrackTaskIdentifier(const std::string & task_id,std::string * target_extension_id,std::string * action_id)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 
MakeTaskID(const char * extension_id,const char * action_id)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 
GetFileBrowserHandlers(Profile * profile,const GURL & selected_file_url,ActionList * results)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 
SortByLastUsedTimestampDesc(const LastUsedHandler & a,const LastUsedHandler & b)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.
SortByTaskName(const LastUsedHandler & a,const LastUsedHandler & b)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
FindCommonTasks(Profile * profile,ListValue * files_list,LastUsedHandlerList * named_action_list)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.
UpdateFileHandlerUsageStats(Profile * profile,const std::string & task_id)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:
LocalFileSystemCallbackDispatcher(RequestLocalFileSystemFunction * function,Profile * profile,int child_id,scoped_refptr<const Extension> extension)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.
DidSucceed()238   virtual void DidSucceed() OVERRIDE {
239     NOTREACHED();
240   }
241 
DidGetLocalPath(const FilePath & local_path)242   virtual void DidGetLocalPath(const FilePath& local_path) {
243     NOTREACHED();
244   }
245 
DidReadMetadata(const base::PlatformFileInfo & info,const FilePath & unused)246   virtual void DidReadMetadata(const base::PlatformFileInfo& info,
247                                const FilePath& unused) OVERRIDE {
248     NOTREACHED();
249   }
250 
DidReadDirectory(const std::vector<base::FileUtilProxy::Entry> & entries,bool has_more)251   virtual void DidReadDirectory(
252       const std::vector<base::FileUtilProxy::Entry>& entries,
253       bool has_more) OVERRIDE {
254     NOTREACHED();
255   }
256 
DidWrite(int64 bytes,bool complete)257   virtual void DidWrite(int64 bytes, bool complete) OVERRIDE {
258     NOTREACHED();
259   }
260 
DidOpenFileSystem(const std::string & name,const GURL & root_path)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 
DidFail(base::PlatformFileError error_code)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.
SetupFileSystemAccessPermissions()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 
RequestOnFileThread(const GURL & source_url,int child_id)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 
RunImpl()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 
RespondSuccessOnUIThread(const std::string & name,const GURL & root_path)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 
RespondFailedOnUIThread(base::PlatformFileError error_code)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 
RunImpl()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:
ExecuteTasksFileSystemCallbackDispatcher(ExecuteTasksFileBrowserFunction * function,Profile * profile,int child_id,const GURL & source_url,scoped_refptr<const Extension> extension,const std::string task_id,const std::vector<GURL> & file_urls)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.
DidSucceed()448   virtual void DidSucceed() OVERRIDE {
449     NOTREACHED();
450   }
451 
DidGetLocalPath(const FilePath & local_path)452   virtual void DidGetLocalPath(const FilePath& local_path) {
453     NOTREACHED();
454   }
455 
DidReadMetadata(const base::PlatformFileInfo & info,const FilePath & unused)456   virtual void DidReadMetadata(const base::PlatformFileInfo& info,
457                                const FilePath& unused) OVERRIDE {
458     NOTREACHED();
459   }
460 
DidReadDirectory(const std::vector<base::FileUtilProxy::Entry> & entries,bool has_more)461   virtual void DidReadDirectory(
462       const std::vector<base::FileUtilProxy::Entry>& entries,
463       bool has_more) OVERRIDE {
464     NOTREACHED();
465   }
466 
DidWrite(int64 bytes,bool complete)467   virtual void DidWrite(int64 bytes, bool complete) OVERRIDE {
468     NOTREACHED();
469   }
470 
DidOpenFileSystem(const std::string & file_system_name,const GURL & file_system_root)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 
DidFail(base::PlatformFileError error_code)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.
SetupFileAccessPermissions(const GURL & origin_file_url,GURL * target_file_url,FilePath * file_path,bool * is_directory)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 
RunImpl()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 
InitiateFileTaskExecution(const std::string & task_id,ListValue * files_list)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 
RequestFileEntryOnFileThread(const GURL & source_url,const std::string & task_id,const std::vector<GURL> & file_urls)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 
ExecuteFailedOnUIThread()695 void ExecuteTasksFileBrowserFunction::ExecuteFailedOnUIThread() {
696   SendResponse(false);
697 }
698 
699 
ExecuteFileActionsOnUIThread(const std::string & task_id,const std::string & file_system_name,const GURL & file_system_root,const FileDefinitionList & file_list)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 
FileDialogFunction()769 FileDialogFunction::FileDialogFunction() {
770 }
771 
~FileDialogFunction()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
Add(int32 tab_id,SelectFileDialog::Listener * listener,HtmlDialogView * dialog,void * params)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
Remove(int32 tab_id)795 void FileDialogFunction::Callback::Remove(int32 tab_id) {
796   map_.erase(tab_id);
797 }
798 
799 // static
800 const FileDialogFunction::Callback&
Find(int32 tab_id)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 
GetTabId() const807 int32 FileDialogFunction::GetTabId() const {
808   return dispatcher()->delegate()->associated_tab_contents()->
809     controller().session_id().id();
810 }
811 
GetCallback() const812 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 
CloseDialog(HtmlDialogView * dialog)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.
GetLocalPathsOnFileThread(const UrlList & file_urls,const std::string & task_id)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 
RunImpl()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 
GetLocalPathsResponseOnUIThread(const FilePathList & files,const std::string & task_id)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 
ViewFilesFunction()921 ViewFilesFunction::ViewFilesFunction() {
922 }
923 
~ViewFilesFunction()924 ViewFilesFunction::~ViewFilesFunction() {
925 }
926 
RunImpl()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 
GetLocalPathsResponseOnUIThread(const FilePathList & files,const std::string & internal_task_id)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 
SelectFilesFunction()969 SelectFilesFunction::SelectFilesFunction() {
970 }
971 
~SelectFilesFunction()972 SelectFilesFunction::~SelectFilesFunction() {
973 }
974 
RunImpl()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 
GetLocalPathsResponseOnUIThread(const FilePathList & files,const std::string & internal_task_id)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 
RunImpl()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 
RunImpl()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