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.
5 #include "chrome/browser/chromeos/file_manager/file_browser_handlers.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"
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;
50 namespace file_manager {
51 namespace file_browser_handlers {
54 // Returns process id of the process the extension is running in.
55 int ExtractProcessFromExtensionId(Profile* profile,
56 const std::string& extension_id) {
58 Extension::GetBaseURLFromExtensionId(extension_id);
59 extensions::ProcessManager* manager =
60 extensions::ExtensionSystem::Get(profile)->process_manager();
62 SiteInstance* site_instance = manager->GetSiteInstanceForURL(extension_url);
63 if (!site_instance || !site_instance->HasProcess())
65 content::RenderProcessHost* process = site_instance->GetProcess();
67 return process->GetID();
70 // Finds a file browser handler that matches |action_id|. Returns NULL if not
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();
81 if (handler_iter->get()->id() == action_id)
82 return handler_iter->get();
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 */);
95 // Finds file browser handlers that can handle the |selected_file_url|.
96 FileBrowserHandlerList FindFileBrowserHandlersForURL(
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.
103 return FileBrowserHandlerList();
105 // We need case-insensitive matching, and pattern in the handler is already
107 const GURL lowercase_url(EscapedUtf8ToLower(selected_file_url.spec()));
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))
118 FileBrowserHandler::List* handler_list =
119 FileBrowserHandler::GetHandlers(extension);
122 for (FileBrowserHandler::List::const_iterator handler_iter =
123 handler_list->begin();
124 handler_iter != handler_list->end();
126 const FileBrowserHandler* handler = handler_iter->get();
127 if (!handler->MatchesURL(lowercase_url))
130 results.push_back(handler_iter->get());
136 // This class is used to execute a file browser handler task. Here's how this
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.
146 // That said, the class itself does not execute a task. The task will be
147 // executed in JavaScript.
148 class FileBrowserHandlerExecutor {
150 FileBrowserHandlerExecutor(Profile* profile,
151 const Extension* extension,
152 const std::string& action_id);
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);
159 // This object is responsible to delete itself.
160 virtual ~FileBrowserHandlerExecutor();
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);
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,
178 extensions::ExtensionHost* host);
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,
188 scoped_refptr<const Extension> extension_;
189 const std::string action_id_;
190 file_tasks::FileTaskFinishedCallback done_;
191 base::WeakPtrFactory<FileBrowserHandlerExecutor> weak_ptr_factory_;
193 DISALLOW_COPY_AND_ASSIGN(FileBrowserHandlerExecutor);
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());
205 fileapi::ExternalFileSystemBackend* backend =
206 file_system_context_handler->external_backend();
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];
212 // Check if this file system entry exists first.
213 base::File::Info file_info;
215 base::FilePath local_path = url.path();
216 base::FilePath virtual_path = url.virtual_path();
218 bool is_drive_file = url.type() == fileapi::kFileSystemTypeDrive;
219 DCHECK(!is_drive_file || drive::util::IsUnderDriveMountPoint(local_path));
221 // If the file is under drive mount point, there is no actual file to be
222 // found on the url.path().
223 if (!is_drive_file) {
224 if (!base::PathExists(local_path) ||
225 base::IsLink(local_path) ||
226 !base::GetFileInfo(local_path, &file_info)) {
231 // Grant access to this particular file to target extension. This will
232 // ensure that the target extension can access only this FS entry and
233 // prevent from traversing FS hierarchy upward.
234 backend->GrantFileAccessToExtension(handler_extension->id(), virtual_path);
237 FileDefinition file_definition;
238 file_definition.virtual_path = virtual_path;
239 file_definition.is_directory = file_info.is_directory;
240 file_definition.absolute_path = local_path;
241 file_definition_list->push_back(file_definition);
244 return file_definition_list.Pass();
247 FileBrowserHandlerExecutor::FileBrowserHandlerExecutor(
249 const Extension* extension,
250 const std::string& action_id)
252 extension_(extension),
253 action_id_(action_id),
254 weak_ptr_factory_(this) {
257 FileBrowserHandlerExecutor::~FileBrowserHandlerExecutor() {}
259 void FileBrowserHandlerExecutor::Execute(
260 const std::vector<FileSystemURL>& file_urls,
261 const file_tasks::FileTaskFinishedCallback& done) {
264 // Get file system context for the extension to which onExecute event will be
265 // sent. The file access permissions will be granted to the extension in the
266 // file system context for the files in |file_urls|.
267 scoped_refptr<fileapi::FileSystemContext> file_system_context(
268 util::GetFileSystemContextForExtensionId(
269 profile_, extension_->id()));
271 BrowserThread::PostTaskAndReplyWithResult(
274 base::Bind(&SetupFileAccessPermissions,
278 base::Bind(&FileBrowserHandlerExecutor::ExecuteAfterSetupFileAccess,
279 weak_ptr_factory_.GetWeakPtr()));
282 void FileBrowserHandlerExecutor::ExecuteAfterSetupFileAccess(
283 scoped_ptr<FileDefinitionList> file_definition_list) {
284 // Outlives the conversion process, since bound to the callback.
285 const FileDefinitionList& file_definition_list_ref =
286 *file_definition_list.get();
287 file_manager::util::ConvertFileDefinitionListToEntryDefinitionList(
290 file_definition_list_ref,
291 base::Bind(&FileBrowserHandlerExecutor::ExecuteFileActionsOnUIThread,
292 weak_ptr_factory_.GetWeakPtr(),
293 base::Passed(&file_definition_list)));
296 void FileBrowserHandlerExecutor::ExecuteDoneOnUIThread(bool success) {
297 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
298 if (!done_.is_null())
301 ? extensions::api::file_browser_private::TASK_RESULT_MESSAGE_SENT
302 : extensions::api::file_browser_private::TASK_RESULT_FAILED);
306 void FileBrowserHandlerExecutor::ExecuteFileActionsOnUIThread(
307 scoped_ptr<FileDefinitionList> file_definition_list,
308 scoped_ptr<EntryDefinitionList> entry_definition_list) {
309 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
311 if (file_definition_list->empty() || entry_definition_list->empty()) {
312 ExecuteDoneOnUIThread(false);
316 int handler_pid = ExtractProcessFromExtensionId(profile_, extension_->id());
317 if (handler_pid <= 0 &&
318 !extensions::BackgroundInfo::HasLazyBackgroundPage(extension_.get())) {
319 ExecuteDoneOnUIThread(false);
323 if (handler_pid > 0) {
324 SetupPermissionsAndDispatchEvent(file_definition_list.Pass(),
325 entry_definition_list.Pass(),
329 // We have to wake the handler background page before we proceed.
330 extensions::LazyBackgroundTaskQueue* queue =
331 extensions::ExtensionSystem::Get(profile_)->
332 lazy_background_task_queue();
333 if (!queue->ShouldEnqueueTask(profile_, extension_.get())) {
334 ExecuteDoneOnUIThread(false);
337 queue->AddPendingTask(
341 &FileBrowserHandlerExecutor::SetupPermissionsAndDispatchEvent,
342 weak_ptr_factory_.GetWeakPtr(),
343 base::Passed(file_definition_list.Pass()),
344 base::Passed(entry_definition_list.Pass()),
349 void FileBrowserHandlerExecutor::SetupPermissionsAndDispatchEvent(
350 scoped_ptr<FileDefinitionList> file_definition_list,
351 scoped_ptr<EntryDefinitionList> entry_definition_list,
353 extensions::ExtensionHost* host) {
354 int handler_pid = host ? host->render_process_host()->GetID() :
357 if (handler_pid <= 0) {
358 ExecuteDoneOnUIThread(false);
362 extensions::EventRouter* event_router =
363 extensions::ExtensionSystem::Get(profile_)->event_router();
365 ExecuteDoneOnUIThread(false);
369 SetupHandlerHostFileAccessPermissions(
370 file_definition_list.get(), extension_.get(), handler_pid);
372 scoped_ptr<base::ListValue> event_args(new base::ListValue());
373 event_args->Append(new base::StringValue(action_id_));
374 base::DictionaryValue* details = new base::DictionaryValue();
375 event_args->Append(details);
376 // Get file definitions. These will be replaced with Entry instances by
377 // dispatchEvent() method from event_binding.js.
378 base::ListValue* file_entries = new base::ListValue();
379 details->Set("entries", file_entries);
381 for (EntryDefinitionList::const_iterator iter =
382 entry_definition_list->begin();
383 iter != entry_definition_list->end();
385 base::DictionaryValue* file_def = new base::DictionaryValue();
386 file_entries->Append(file_def);
387 file_def->SetString("fileSystemName", iter->file_system_name);
388 file_def->SetString("fileSystemRoot", iter->file_system_root_url);
389 file_def->SetString("fileFullPath",
390 "/" + iter->full_path.AsUTF8Unsafe());
391 file_def->SetBoolean("fileIsDirectory", iter->is_directory);
394 scoped_ptr<extensions::Event> event(new extensions::Event(
395 "fileBrowserHandler.onExecute", event_args.Pass()));
396 event->restrict_to_browser_context = profile_;
397 event_router->DispatchEventToExtension(extension_->id(), event.Pass());
399 ExecuteDoneOnUIThread(true);
402 void FileBrowserHandlerExecutor::SetupHandlerHostFileAccessPermissions(
403 FileDefinitionList* file_definition_list,
404 const Extension* extension,
406 const FileBrowserHandler* action = FindFileBrowserHandlerForActionId(
407 extension_, action_id_);
408 for (FileDefinitionList::const_iterator iter = file_definition_list->begin();
409 iter != file_definition_list->end();
413 if (action->CanRead()) {
414 content::ChildProcessSecurityPolicy::GetInstance()->GrantReadFile(
415 handler_pid, iter->absolute_path);
417 if (action->CanWrite()) {
418 content::ChildProcessSecurityPolicy::GetInstance()->
419 GrantCreateReadWriteFile(handler_pid, iter->absolute_path);
424 // Returns true if |extension_id| and |action_id| indicate that the file
425 // currently being handled should be opened with the browser. This function
426 // is used to handle certain action IDs of the file manager.
427 bool ShouldBeOpenedWithBrowser(const std::string& extension_id,
428 const std::string& action_id) {
430 return (extension_id == kFileManagerAppId &&
431 (action_id == "view-pdf" ||
432 action_id == "view-swf" ||
433 action_id == "view-in-browser" ||
434 action_id == "open-hosted-generic" ||
435 action_id == "open-hosted-gdoc" ||
436 action_id == "open-hosted-gsheet" ||
437 action_id == "open-hosted-gslides"));
440 // Opens the files specified by |file_urls| with the browser for |profile|.
441 // Returns true on success. It's a failure if no files are opened.
442 bool OpenFilesWithBrowser(Profile* profile,
443 const std::vector<FileSystemURL>& file_urls) {
445 for (size_t i = 0; i < file_urls.size(); ++i) {
446 const FileSystemURL& file_url = file_urls[i];
447 if (chromeos::FileSystemBackend::CanHandleURL(file_url)) {
448 const base::FilePath& file_path = file_url.path();
449 num_opened += util::OpenFileWithBrowser(profile, file_path);
452 return num_opened > 0;
457 bool ExecuteFileBrowserHandler(
459 const Extension* extension,
460 const std::string& action_id,
461 const std::vector<FileSystemURL>& file_urls,
462 const file_tasks::FileTaskFinishedCallback& done) {
463 // Forbid calling undeclared handlers.
464 if (!FindFileBrowserHandlerForActionId(extension, action_id))
467 // Some action IDs of the file manager's file browser handlers require the
468 // files to be directly opened with the browser.
469 if (ShouldBeOpenedWithBrowser(extension->id(), action_id)) {
470 const bool result = OpenFilesWithBrowser(profile, file_urls);
471 if (!done.is_null()) {
473 ? extensions::api::file_browser_private::TASK_RESULT_OPENED
474 : extensions::api::file_browser_private::TASK_RESULT_FAILED);
479 // The executor object will be self deleted on completion.
480 (new FileBrowserHandlerExecutor(
481 profile, extension, action_id))->Execute(file_urls, done);
485 bool IsFallbackFileBrowserHandler(const file_tasks::TaskDescriptor& task) {
486 return (task.task_type == file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER &&
487 (task.app_id == kFileManagerAppId ||
488 task.app_id == extension_misc::kQuickOfficeComponentExtensionId ||
489 task.app_id == extension_misc::kQuickOfficeInternalExtensionId ||
490 task.app_id == extension_misc::kQuickOfficeExtensionId));
493 FileBrowserHandlerList FindFileBrowserHandlers(
495 const std::vector<GURL>& file_list) {
496 FileBrowserHandlerList common_handlers;
497 for (std::vector<GURL>::const_iterator it = file_list.begin();
498 it != file_list.end(); ++it) {
499 FileBrowserHandlerList handlers =
500 FindFileBrowserHandlersForURL(profile, *it);
501 // If there is nothing to do for one file, the intersection of handlers
502 // for all files will be empty at the end, so no need to check further.
503 if (handlers.empty())
504 return FileBrowserHandlerList();
506 // For the very first file, just copy all the elements.
507 if (it == file_list.begin()) {
508 common_handlers = handlers;
510 // For all additional files, find intersection between the accumulated and
511 // file specific set.
512 FileBrowserHandlerList intersection;
513 std::set<const FileBrowserHandler*> common_handler_set(
514 common_handlers.begin(), common_handlers.end());
516 for (FileBrowserHandlerList::const_iterator itr = handlers.begin();
517 itr != handlers.end(); ++itr) {
518 if (ContainsKey(common_handler_set, *itr))
519 intersection.push_back(*itr);
522 std::swap(common_handlers, intersection);
523 if (common_handlers.empty())
524 return FileBrowserHandlerList();
528 return common_handlers;
531 } // namespace file_browser_handlers
532 } // namespace file_manager