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