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