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