blob: 609cf4ca13cba579b01bf0176e739cc89163f34f [file] [log] [blame]
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +01001// 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/extensions/file_manager/file_tasks.h"
6
Ben Murdochbb1529c2013-08-08 10:24:53 +01007#include "apps/launcher.h"
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +01008#include "base/bind.h"
9#include "base/file_util.h"
10#include "base/i18n/case_conversion.h"
11#include "base/strings/string_util.h"
12#include "base/strings/stringprintf.h"
13#include "base/strings/utf_string_conversions.h"
14#include "chrome/browser/chromeos/drive/file_system_util.h"
15#include "chrome/browser/chromeos/drive/file_task_executor.h"
16#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
17#include "chrome/browser/chromeos/fileapi/file_system_backend.h"
18#include "chrome/browser/extensions/event_router.h"
19#include "chrome/browser/extensions/extension_host.h"
20#include "chrome/browser/extensions/extension_service.h"
21#include "chrome/browser/extensions/extension_system.h"
22#include "chrome/browser/extensions/extension_tab_util.h"
23#include "chrome/browser/extensions/lazy_background_task_queue.h"
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010024#include "chrome/browser/prefs/scoped_user_pref_update.h"
25#include "chrome/browser/profiles/profile.h"
26#include "chrome/browser/ui/browser.h"
27#include "chrome/browser/ui/browser_tabstrip.h"
28#include "chrome/browser/ui/host_desktop.h"
29#include "chrome/common/extensions/api/file_browser_handlers/file_browser_handler.h"
30#include "chrome/common/extensions/background_info.h"
31#include "chrome/common/pref_names.h"
32#include "content/public/browser/browser_thread.h"
33#include "content/public/browser/child_process_security_policy.h"
34#include "content/public/browser/render_process_host.h"
35#include "content/public/browser/site_instance.h"
36#include "content/public/browser/storage_partition.h"
37#include "content/public/browser/web_contents.h"
38#include "net/base/escape.h"
39#include "webkit/browser/fileapi/file_system_context.h"
40#include "webkit/browser/fileapi/file_system_url.h"
41#include "webkit/browser/fileapi/isolated_context.h"
42#include "webkit/common/fileapi/file_system_util.h"
43
44using content::BrowserContext;
45using content::BrowserThread;
46using content::ChildProcessSecurityPolicy;
47using content::SiteInstance;
48using content::WebContents;
49using extensions::Extension;
50using fileapi::FileSystemURL;
51
52namespace file_manager {
53namespace file_tasks {
54
55const char kTaskFile[] = "file";
56const char kTaskDrive[] = "drive";
57const char kTaskApp[] = "app";
58
59namespace {
60
61// Legacy Drive task extension prefix, used by CrackTaskID.
62const char kDriveTaskExtensionPrefix[] = "drive-app:";
63const size_t kDriveTaskExtensionPrefixLength =
64 arraysize(kDriveTaskExtensionPrefix) - 1;
65
66// Returns process id of the process the extension is running in.
67int ExtractProcessFromExtensionId(Profile* profile,
68 const std::string& extension_id) {
69 GURL extension_url =
70 Extension::GetBaseURLFromExtensionId(extension_id);
71 ExtensionProcessManager* manager =
72 extensions::ExtensionSystem::Get(profile)->process_manager();
73
74 SiteInstance* site_instance = manager->GetSiteInstanceForURL(extension_url);
75 if (!site_instance || !site_instance->HasProcess())
76 return -1;
77 content::RenderProcessHost* process = site_instance->GetProcess();
78
79 return process->GetID();
80}
81
82const FileBrowserHandler* FindFileBrowserHandler(const Extension* extension,
83 const std::string& action_id) {
84 FileBrowserHandler::List* handler_list =
85 FileBrowserHandler::GetHandlers(extension);
86 for (FileBrowserHandler::List::const_iterator action_iter =
87 handler_list->begin();
88 action_iter != handler_list->end();
89 ++action_iter) {
90 if (action_iter->get()->id() == action_id)
91 return action_iter->get();
92 }
93 return NULL;
94}
95
96std::string EscapedUtf8ToLower(const std::string& str) {
97 string16 utf16 = UTF8ToUTF16(
98 net::UnescapeURLComponent(str, net::UnescapeRule::NORMAL));
99 return net::EscapeUrlEncodedData(
100 UTF16ToUTF8(base::i18n::ToLower(utf16)),
101 false /* do not replace space with plus */);
102}
103
104bool GetFileBrowserHandlers(Profile* profile,
105 const GURL& selected_file_url,
106 FileBrowserHandlerList* results) {
107 ExtensionService* service =
108 extensions::ExtensionSystem::Get(profile)->extension_service();
109 if (!service)
110 return false; // In unit-tests, we may not have an ExtensionService.
111
112 // We need case-insensitive matching, and pattern in the handler is already
113 // in lower case.
114 const GURL lowercase_url(EscapedUtf8ToLower(selected_file_url.spec()));
115
116 for (ExtensionSet::const_iterator iter = service->extensions()->begin();
117 iter != service->extensions()->end();
118 ++iter) {
119 const Extension* extension = iter->get();
120 if (profile->IsOffTheRecord() &&
121 !service->IsIncognitoEnabled(extension->id()))
122 continue;
123
124 FileBrowserHandler::List* handler_list =
125 FileBrowserHandler::GetHandlers(extension);
126 if (!handler_list)
127 continue;
128 for (FileBrowserHandler::List::const_iterator action_iter =
129 handler_list->begin();
130 action_iter != handler_list->end();
131 ++action_iter) {
132 const FileBrowserHandler* action = action_iter->get();
133 if (!action->MatchesURL(lowercase_url))
134 continue;
135
136 results->push_back(action_iter->get());
137 }
138 }
139 return true;
140}
141
142fileapi::FileSystemContext* GetFileSystemContextForExtension(
143 Profile* profile,
144 const std::string& extension_id) {
145 GURL site = extensions::ExtensionSystem::Get(profile)->
146 extension_service()->GetSiteForExtensionId(extension_id);
147 return BrowserContext::GetStoragePartitionForSite(profile, site)->
148 GetFileSystemContext();
149}
150
151// Checks if the file browser extension has permissions for the files in its
152// file system context.
153bool FileBrowserHasAccessPermissionForFiles(
154 Profile* profile,
155 const GURL& source_url,
156 const std::string& file_browser_id,
157 const std::vector<FileSystemURL>& files) {
158 fileapi::ExternalFileSystemBackend* backend =
159 GetFileSystemContextForExtension(profile, file_browser_id)->
160 external_backend();
161 if (!backend)
162 return false;
163
164 for (size_t i = 0; i < files.size(); ++i) {
165 // Make sure this url really being used by the right caller extension.
166 if (source_url.GetOrigin() != files[i].origin())
167 return false;
168
169 if (!chromeos::FileSystemBackend::CanHandleURL(files[i]) ||
170 !backend->IsAccessAllowed(files[i])) {
171 return false;
172 }
173 }
174
175 return true;
176}
177
178} // namespace
179
180bool IsFallbackTask(const FileBrowserHandler* task) {
181 return (task->extension_id() == kFileBrowserDomain ||
182 task->extension_id() ==
183 extension_misc::kQuickOfficeComponentExtensionId ||
184 task->extension_id() == extension_misc::kQuickOfficeDevExtensionId ||
185 task->extension_id() == extension_misc::kQuickOfficeExtensionId);
186}
187
188void UpdateDefaultTask(Profile* profile,
189 const std::string& task_id,
190 const std::set<std::string>& suffixes,
191 const std::set<std::string>& mime_types) {
192 if (!profile || !profile->GetPrefs())
193 return;
194
195 if (!mime_types.empty()) {
196 DictionaryPrefUpdate mime_type_pref(profile->GetPrefs(),
197 prefs::kDefaultTasksByMimeType);
198 for (std::set<std::string>::const_iterator iter = mime_types.begin();
199 iter != mime_types.end(); ++iter) {
200 base::StringValue* value = new base::StringValue(task_id);
201 mime_type_pref->SetWithoutPathExpansion(*iter, value);
202 }
203 }
204
205 if (!suffixes.empty()) {
206 DictionaryPrefUpdate mime_type_pref(profile->GetPrefs(),
207 prefs::kDefaultTasksBySuffix);
208 for (std::set<std::string>::const_iterator iter = suffixes.begin();
209 iter != suffixes.end(); ++iter) {
210 base::StringValue* value = new base::StringValue(task_id);
211 // Suffixes are case insensitive.
212 std::string lower_suffix = StringToLowerASCII(*iter);
213 mime_type_pref->SetWithoutPathExpansion(lower_suffix, value);
214 }
215 }
216}
217
218std::string GetDefaultTaskIdFromPrefs(Profile* profile,
219 const std::string& mime_type,
220 const std::string& suffix) {
221 VLOG(1) << "Looking for default for MIME type: " << mime_type
222 << " and suffix: " << suffix;
223 std::string task_id;
224 if (!mime_type.empty()) {
225 const DictionaryValue* mime_task_prefs =
226 profile->GetPrefs()->GetDictionary(prefs::kDefaultTasksByMimeType);
227 DCHECK(mime_task_prefs);
228 LOG_IF(ERROR, !mime_task_prefs) << "Unable to open MIME type prefs";
229 if (mime_task_prefs &&
230 mime_task_prefs->GetStringWithoutPathExpansion(mime_type, &task_id)) {
231 VLOG(1) << "Found MIME default handler: " << task_id;
232 return task_id;
233 }
234 }
235
236 const DictionaryValue* suffix_task_prefs =
237 profile->GetPrefs()->GetDictionary(prefs::kDefaultTasksBySuffix);
238 DCHECK(suffix_task_prefs);
239 LOG_IF(ERROR, !suffix_task_prefs) << "Unable to open suffix prefs";
240 std::string lower_suffix = StringToLowerASCII(suffix);
241 if (suffix_task_prefs)
242 suffix_task_prefs->GetStringWithoutPathExpansion(lower_suffix, &task_id);
243 VLOG_IF(1, !task_id.empty()) << "Found suffix default handler: " << task_id;
244 return task_id;
245}
246
247std::string MakeTaskID(const std::string& extension_id,
248 const std::string& task_type,
249 const std::string& action_id) {
250 DCHECK(task_type == kTaskFile ||
251 task_type == kTaskDrive ||
252 task_type == kTaskApp);
253 return base::StringPrintf("%s|%s|%s",
254 extension_id.c_str(),
255 task_type.c_str(),
256 action_id.c_str());
257}
258
259bool CrackTaskID(const std::string& task_id,
260 std::string* extension_id,
261 std::string* task_type,
262 std::string* action_id) {
263 std::vector<std::string> result;
264 int count = Tokenize(task_id, std::string("|"), &result);
265
266 // Parse historic task_id parameters that only contain two parts. Drive tasks
267 // are identified by a prefix "drive-app:" on the extension ID.
268 if (count == 2) {
269 if (StartsWithASCII(result[0], kDriveTaskExtensionPrefix, true)) {
270 if (task_type)
271 *task_type = kTaskDrive;
272
273 if (extension_id)
274 *extension_id = result[0].substr(kDriveTaskExtensionPrefixLength);
275 } else {
276 if (task_type)
277 *task_type = kTaskFile;
278
279 if (extension_id)
280 *extension_id = result[0];
281 }
282
283 if (action_id)
284 *action_id = result[1];
285
286 return true;
287 }
288
289 if (count != 3)
290 return false;
291
292 if (extension_id)
293 *extension_id = result[0];
294
295 if (task_type) {
296 *task_type = result[1];
297 DCHECK(*task_type == kTaskFile ||
298 *task_type == kTaskDrive ||
299 *task_type == kTaskApp);
300 }
301
302 if (action_id)
303 *action_id = result[2];
304
305 return true;
306}
307
308// Find a specific handler in the handler list.
309FileBrowserHandlerList::iterator FindHandler(
310 FileBrowserHandlerList* handler_list,
311 const std::string& extension_id,
312 const std::string& id) {
313 FileBrowserHandlerList::iterator iter = handler_list->begin();
314 while (iter != handler_list->end() &&
315 !((*iter)->extension_id() == extension_id &&
316 (*iter)->id() == id)) {
317 ++iter;
318 }
319 return iter;
320}
321
322// Given the list of selected files, returns array of file action tasks
323// that are shared between them.
324void FindDefaultTasks(Profile* profile,
325 const std::vector<base::FilePath>& files_list,
326 const FileBrowserHandlerList& common_tasks,
327 FileBrowserHandlerList* default_tasks) {
328 DCHECK(default_tasks);
329 default_tasks->clear();
330
331 std::set<std::string> default_ids;
332 for (std::vector<base::FilePath>::const_iterator it = files_list.begin();
333 it != files_list.end(); ++it) {
334 std::string task_id = file_tasks::GetDefaultTaskIdFromPrefs(
335 profile, "", it->Extension());
336 if (!task_id.empty())
337 default_ids.insert(task_id);
338 }
339
340 const FileBrowserHandler* fallback_task = NULL;
341 // Convert the default task IDs collected above to one of the handler pointers
342 // from common_tasks.
343 for (FileBrowserHandlerList::const_iterator task_iter = common_tasks.begin();
344 task_iter != common_tasks.end(); ++task_iter) {
345 std::string task_id = MakeTaskID((*task_iter)->extension_id(), kTaskFile,
346 (*task_iter)->id());
347 std::set<std::string>::iterator default_iter = default_ids.find(task_id);
348 if (default_iter != default_ids.end()) {
349 default_tasks->push_back(*task_iter);
350 continue;
351 }
352
353 // Remember the first fallback task.
354 if (!fallback_task && IsFallbackTask(*task_iter))
355 fallback_task = *task_iter;
356 }
357
358 // If there are no default tasks found, use fallback as default.
359 if (fallback_task && default_tasks->empty())
360 default_tasks->push_back(fallback_task);
361}
362
363// Given the list of selected files, returns array of context menu tasks
364// that are shared
365bool FindCommonTasks(Profile* profile,
366 const std::vector<GURL>& files_list,
367 FileBrowserHandlerList* common_tasks) {
368 DCHECK(common_tasks);
369 common_tasks->clear();
370
371 FileBrowserHandlerList common_task_list;
372 std::set<std::string> default_task_ids;
373 for (std::vector<GURL>::const_iterator it = files_list.begin();
374 it != files_list.end(); ++it) {
375 FileBrowserHandlerList file_actions;
376 if (!GetFileBrowserHandlers(profile, *it, &file_actions))
377 return false;
378 // If there is nothing to do for one file, the intersection of tasks for all
379 // files will be empty at the end, and so will the default tasks.
380 if (file_actions.empty())
381 return true;
382
383 // For the very first file, just copy all the elements.
384 if (it == files_list.begin()) {
385 common_task_list = file_actions;
386 } else {
387 // For all additional files, find intersection between the accumulated and
388 // file specific set.
389 FileBrowserHandlerList intersection;
390 std::set_intersection(common_task_list.begin(), common_task_list.end(),
391 file_actions.begin(), file_actions.end(),
392 std::back_inserter(intersection));
393 common_task_list = intersection;
394 if (common_task_list.empty())
395 return true;
396 }
397 }
398
399 FileBrowserHandlerList::iterator watch_iter = FindHandler(
400 &common_task_list, kFileBrowserDomain, kFileBrowserWatchTaskId);
401 FileBrowserHandlerList::iterator gallery_iter = FindHandler(
402 &common_task_list, kFileBrowserDomain, kFileBrowserGalleryTaskId);
403 if (watch_iter != common_task_list.end() &&
404 gallery_iter != common_task_list.end()) {
405 // Both "watch" and "gallery" actions are applicable which means that the
406 // selection is all videos. Showing them both is confusing, so we only keep
407 // the one that makes more sense ("watch" for single selection, "gallery"
408 // for multiple selection).
409 if (files_list.size() == 1)
410 common_task_list.erase(gallery_iter);
411 else
412 common_task_list.erase(watch_iter);
413 }
414
415 common_tasks->swap(common_task_list);
416 return true;
417}
418
419bool GetTaskForURLAndPath(Profile* profile,
420 const GURL& url,
421 const base::FilePath& file_path,
422 const FileBrowserHandler** handler) {
423 std::vector<GURL> file_urls;
424 file_urls.push_back(url);
425
426 FileBrowserHandlerList default_tasks;
427 FileBrowserHandlerList common_tasks;
428 if (!FindCommonTasks(profile, file_urls, &common_tasks))
429 return false;
430
431 if (common_tasks.empty())
432 return false;
433
434 std::vector<base::FilePath> file_paths;
435 file_paths.push_back(file_path);
436
437 FindDefaultTasks(profile, file_paths, common_tasks, &default_tasks);
438
439 // If there's none, or more than one, then we don't have a canonical default.
440 if (!default_tasks.empty()) {
441 // There should not be multiple default tasks for a single URL.
442 DCHECK_EQ(1u, default_tasks.size());
443
444 *handler = *default_tasks.begin();
445 return true;
446 }
447
448 // If there are no default tasks, use first task in the list (file manager
449 // does the same in this situation).
450 // TODO(tbarzic): This is so not optimal behaviour.
451 *handler = *common_tasks.begin();
452 return true;
453}
454
455// ExtensionTaskExecutor executes tasks with kTaskFile type.
456class ExtensionTaskExecutor {
457 public:
458 ExtensionTaskExecutor(Profile* profile,
459 const Extension* extension,
460 int32 tab_id,
461 const std::string& action_id);
462
463 // Executes the task for each file. |done| will be run with the result.
464 void Execute(const std::vector<FileSystemURL>& file_urls,
465 const FileTaskFinishedCallback& done);
466
467 private:
468 // This object is responsible to delete itself.
469 virtual ~ExtensionTaskExecutor();
470
471 struct FileDefinition {
472 FileDefinition();
473 ~FileDefinition();
474
475 base::FilePath virtual_path;
476 base::FilePath absolute_path;
477 bool is_directory;
478 };
479
480 typedef std::vector<FileDefinition> FileDefinitionList;
481
482 // Checks legitimacy of file url and grants file RO access permissions from
483 // handler (target) extension and its renderer process.
484 static FileDefinitionList SetupFileAccessPermissions(
485 scoped_refptr<fileapi::FileSystemContext> file_system_context_handler,
486 const scoped_refptr<const Extension>& handler_extension,
487 const std::vector<FileSystemURL>& file_urls);
488
489 void DidOpenFileSystem(const std::vector<FileSystemURL>& file_urls,
490 base::PlatformFileError result,
491 const std::string& file_system_name,
492 const GURL& file_system_root);
493
494 void ExecuteDoneOnUIThread(bool success);
495 void ExecuteFileActionsOnUIThread(const std::string& file_system_name,
496 const GURL& file_system_root,
497 const FileDefinitionList& file_list);
498 void SetupPermissionsAndDispatchEvent(const std::string& file_system_name,
499 const GURL& file_system_root,
500 const FileDefinitionList& file_list,
501 int handler_pid_in,
502 extensions::ExtensionHost* host);
503
504 // Registers file permissions from |handler_host_permissions_| with
505 // ChildProcessSecurityPolicy for process with id |handler_pid|.
506 void SetupHandlerHostFileAccessPermissions(
507 const FileDefinitionList& file_list,
508 const Extension* extension,
509 int handler_pid);
510
511 Profile* profile_;
512 scoped_refptr<const Extension> extension_;
513 int32 tab_id_;
514 const std::string action_id_;
515 FileTaskFinishedCallback done_;
516 base::WeakPtrFactory<ExtensionTaskExecutor> weak_ptr_factory_;
517
518 DISALLOW_COPY_AND_ASSIGN(ExtensionTaskExecutor);
519};
520
521bool ExecuteFileTask(Profile* profile,
522 const GURL& source_url,
523 const std::string& file_browser_id,
524 int32 tab_id,
525 const std::string& extension_id,
526 const std::string& task_type,
527 const std::string& action_id,
528 const std::vector<FileSystemURL>& file_urls,
529 const FileTaskFinishedCallback& done) {
530 if (!FileBrowserHasAccessPermissionForFiles(profile, source_url,
531 file_browser_id, file_urls))
532 return false;
533
534 // drive::FileTaskExecutor is responsible to handle drive tasks.
535 if (task_type == kTaskDrive) {
536 DCHECK_EQ("open-with", action_id);
537 drive::FileTaskExecutor* executor =
538 new drive::FileTaskExecutor(profile, extension_id);
539 executor->Execute(file_urls, done);
540 return true;
541 }
542
543 // Get the extension.
544 ExtensionService* service =
545 extensions::ExtensionSystem::Get(profile)->extension_service();
546 const Extension* extension = service ?
547 service->GetExtensionById(extension_id, false) : NULL;
548 if (!extension)
549 return false;
550
551 // Execute the task.
552 if (task_type == kTaskFile) {
553 // Forbid calling undeclared handlers.
554 if (!FindFileBrowserHandler(extension, action_id))
555 return false;
556
557 (new ExtensionTaskExecutor(
558 profile, extension, tab_id, action_id))->Execute(file_urls, done);
559 return true;
560 } else if (task_type == kTaskApp) {
561 for (size_t i = 0; i != file_urls.size(); ++i) {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100562 apps::LaunchPlatformAppWithFileHandler(
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100563 profile, extension, action_id, file_urls[i].path());
564 }
565
566 if (!done.is_null())
567 done.Run(true);
568 return true;
569 }
570 NOTREACHED();
571 return false;
572}
573
574ExtensionTaskExecutor::FileDefinition::FileDefinition() : is_directory(false) {
575}
576
577ExtensionTaskExecutor::FileDefinition::~FileDefinition() {
578}
579
580// static
581ExtensionTaskExecutor::FileDefinitionList
582ExtensionTaskExecutor::SetupFileAccessPermissions(
583 scoped_refptr<fileapi::FileSystemContext> file_system_context_handler,
584 const scoped_refptr<const Extension>& handler_extension,
585 const std::vector<FileSystemURL>& file_urls) {
586 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
587 DCHECK(handler_extension.get());
588
589 fileapi::ExternalFileSystemBackend* backend =
590 file_system_context_handler->external_backend();
591
592 FileDefinitionList file_list;
593 for (size_t i = 0; i < file_urls.size(); ++i) {
594 const FileSystemURL& url = file_urls[i];
595
596 // Check if this file system entry exists first.
597 base::PlatformFileInfo file_info;
598
599 base::FilePath local_path = url.path();
600 base::FilePath virtual_path = url.virtual_path();
601
602 bool is_drive_file = url.type() == fileapi::kFileSystemTypeDrive;
603 DCHECK(!is_drive_file || drive::util::IsUnderDriveMountPoint(local_path));
604
605 // If the file is under drive mount point, there is no actual file to be
606 // found on the url.path().
607 if (!is_drive_file) {
608 if (!base::PathExists(local_path) ||
609 file_util::IsLink(local_path) ||
610 !file_util::GetFileInfo(local_path, &file_info)) {
611 continue;
612 }
613 }
614
615 // Grant access to this particular file to target extension. This will
616 // ensure that the target extension can access only this FS entry and
617 // prevent from traversing FS hierarchy upward.
618 backend->GrantFileAccessToExtension(
619 handler_extension->id(), virtual_path);
620
621 // Output values.
622 FileDefinition file;
623 file.virtual_path = virtual_path;
624 file.is_directory = file_info.is_directory;
625 file.absolute_path = local_path;
626 file_list.push_back(file);
627 }
628 return file_list;
629}
630
631ExtensionTaskExecutor::ExtensionTaskExecutor(
632 Profile* profile,
633 const Extension* extension,
634 int32 tab_id,
635 const std::string& action_id)
636 : profile_(profile),
637 extension_(extension),
638 tab_id_(tab_id),
639 action_id_(action_id),
640 weak_ptr_factory_(this) {
641}
642
643ExtensionTaskExecutor::~ExtensionTaskExecutor() {}
644
645void ExtensionTaskExecutor::Execute(const std::vector<FileSystemURL>& file_urls,
646 const FileTaskFinishedCallback& done) {
647 done_ = done;
648
649 // Get file system context for the extension to which onExecute event will be
650 // sent. The file access permissions will be granted to the extension in the
651 // file system context for the files in |file_urls|.
652 GetFileSystemContextForExtension(profile_, extension_->id())->OpenFileSystem(
653 Extension::GetBaseURLFromExtensionId(extension_->id()).GetOrigin(),
654 fileapi::kFileSystemTypeExternal,
655 fileapi::OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
656 base::Bind(&ExtensionTaskExecutor::DidOpenFileSystem,
657 weak_ptr_factory_.GetWeakPtr(),
658 file_urls));
659}
660
661void ExtensionTaskExecutor::DidOpenFileSystem(
662 const std::vector<FileSystemURL>& file_urls,
663 base::PlatformFileError result,
664 const std::string& file_system_name,
665 const GURL& file_system_root) {
666 if (result != base::PLATFORM_FILE_OK) {
667 ExecuteDoneOnUIThread(false);
668 return;
669 }
670
671 scoped_refptr<fileapi::FileSystemContext> file_system_context(
672 GetFileSystemContextForExtension(profile_, extension_->id()));
673 BrowserThread::PostTaskAndReplyWithResult(
674 BrowserThread::FILE,
675 FROM_HERE,
676 base::Bind(&SetupFileAccessPermissions,
677 file_system_context,
678 extension_,
679 file_urls),
680 base::Bind(&ExtensionTaskExecutor::ExecuteFileActionsOnUIThread,
681 weak_ptr_factory_.GetWeakPtr(),
682 file_system_name,
683 file_system_root));
684}
685
686void ExtensionTaskExecutor::ExecuteDoneOnUIThread(bool success) {
687 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
688 if (!done_.is_null())
689 done_.Run(success);
690 delete this;
691}
692
693void ExtensionTaskExecutor::ExecuteFileActionsOnUIThread(
694 const std::string& file_system_name,
695 const GURL& file_system_root,
696 const FileDefinitionList& file_list) {
697 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
698
699 if (file_list.empty()) {
700 ExecuteDoneOnUIThread(false);
701 return;
702 }
703
704 int handler_pid = ExtractProcessFromExtensionId(profile_, extension_->id());
705 if (handler_pid <= 0 &&
706 !extensions::BackgroundInfo::HasLazyBackgroundPage(extension_.get())) {
707 ExecuteDoneOnUIThread(false);
708 return;
709 }
710
711 if (handler_pid > 0) {
712 SetupPermissionsAndDispatchEvent(file_system_name, file_system_root,
713 file_list, handler_pid, NULL);
714 } else {
715 // We have to wake the handler background page before we proceed.
716 extensions::LazyBackgroundTaskQueue* queue =
717 extensions::ExtensionSystem::Get(profile_)->
718 lazy_background_task_queue();
719 if (!queue->ShouldEnqueueTask(profile_, extension_.get())) {
720 ExecuteDoneOnUIThread(false);
721 return;
722 }
723 queue->AddPendingTask(
724 profile_, extension_->id(),
725 base::Bind(&ExtensionTaskExecutor::SetupPermissionsAndDispatchEvent,
726 weak_ptr_factory_.GetWeakPtr(),
727 file_system_name,
728 file_system_root,
729 file_list,
730 handler_pid));
731 }
732}
733
734void ExtensionTaskExecutor::SetupPermissionsAndDispatchEvent(
735 const std::string& file_system_name,
736 const GURL& file_system_root,
737 const FileDefinitionList& file_list,
738 int handler_pid_in,
739 extensions::ExtensionHost* host) {
740 int handler_pid = host ? host->render_process_host()->GetID() :
741 handler_pid_in;
742
743 if (handler_pid <= 0) {
744 ExecuteDoneOnUIThread(false);
745 return;
746 }
747
748 extensions::EventRouter* event_router =
749 extensions::ExtensionSystem::Get(profile_)->event_router();
750 if (!event_router) {
751 ExecuteDoneOnUIThread(false);
752 return;
753 }
754
755 SetupHandlerHostFileAccessPermissions(
756 file_list, extension_.get(), handler_pid);
757
758 scoped_ptr<ListValue> event_args(new ListValue());
759 event_args->Append(new base::StringValue(action_id_));
760 DictionaryValue* details = new DictionaryValue();
761 event_args->Append(details);
762 // Get file definitions. These will be replaced with Entry instances by
763 // dispatchEvent() method from event_binding.js.
764 ListValue* files_urls = new ListValue();
765 details->Set("entries", files_urls);
766 for (FileDefinitionList::const_iterator iter = file_list.begin();
767 iter != file_list.end();
768 ++iter) {
769 DictionaryValue* file_def = new DictionaryValue();
770 files_urls->Append(file_def);
771 file_def->SetString("fileSystemName", file_system_name);
772 file_def->SetString("fileSystemRoot", file_system_root.spec());
773 base::FilePath root(FILE_PATH_LITERAL("/"));
774 base::FilePath full_path = root.Append(iter->virtual_path);
775 file_def->SetString("fileFullPath", full_path.value());
776 file_def->SetBoolean("fileIsDirectory", iter->is_directory);
777 }
778
779 details->SetInteger("tab_id", tab_id_);
780
781 scoped_ptr<extensions::Event> event(new extensions::Event(
782 "fileBrowserHandler.onExecute", event_args.Pass()));
783 event->restrict_to_profile = profile_;
784 event_router->DispatchEventToExtension(extension_->id(), event.Pass());
785
786 ExecuteDoneOnUIThread(true);
787}
788
789void ExtensionTaskExecutor::SetupHandlerHostFileAccessPermissions(
790 const FileDefinitionList& file_list,
791 const Extension* extension,
792 int handler_pid) {
793 const FileBrowserHandler* action = FindFileBrowserHandler(extension_,
794 action_id_);
795 for (FileDefinitionList::const_iterator iter = file_list.begin();
796 iter != file_list.end();
797 ++iter) {
798 if (!action)
799 continue;
800 if (action->CanRead()) {
801 content::ChildProcessSecurityPolicy::GetInstance()->GrantReadFile(
802 handler_pid, iter->absolute_path);
803 }
804 if (action->CanWrite()) {
805 content::ChildProcessSecurityPolicy::GetInstance()->
806 GrantCreateReadWriteFile(handler_pid, iter->absolute_path);
807 }
808 }
809}
810
811} // namespace file_tasks
812} // namespace file_manager