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