OLD | NEW |
| (Empty) |
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/extensions/file_handler_util.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/file_util.h" | |
9 #include "base/i18n/case_conversion.h" | |
10 #include "base/json/json_writer.h" | |
11 #include "base/string_util.h" | |
12 #include "base/stringprintf.h" | |
13 #include "base/utf_string_conversions.h" | |
14 #include "chrome/browser/chromeos/drive/drive_file_system_util.h" | |
15 #include "chrome/browser/chromeos/drive/drive_task_executor.h" | |
16 #include "chrome/browser/chromeos/extensions/file_browser_handler.h" | |
17 #include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.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" | |
24 #include "chrome/browser/extensions/platform_app_launcher.h" | |
25 #include "chrome/browser/prefs/scoped_user_pref_update.h" | |
26 #include "chrome/browser/profiles/profile.h" | |
27 #include "chrome/browser/profiles/profile_manager.h" | |
28 #include "chrome/browser/ui/browser.h" | |
29 #include "chrome/browser/ui/browser_finder.h" | |
30 #include "chrome/browser/ui/browser_tabstrip.h" | |
31 #include "chrome/browser/ui/host_desktop.h" | |
32 #include "chrome/common/extensions/background_info.h" | |
33 #include "chrome/common/pref_names.h" | |
34 #include "content/public/browser/browser_thread.h" | |
35 #include "content/public/browser/child_process_security_policy.h" | |
36 #include "content/public/browser/render_process_host.h" | |
37 #include "content/public/browser/site_instance.h" | |
38 #include "content/public/browser/storage_partition.h" | |
39 #include "content/public/browser/web_contents.h" | |
40 #include "net/base/escape.h" | |
41 #include "webkit/chromeos/fileapi/cros_mount_point_provider.h" | |
42 #include "webkit/fileapi/file_system_context.h" | |
43 #include "webkit/fileapi/file_system_url.h" | |
44 #include "webkit/fileapi/file_system_util.h" | |
45 #include "webkit/fileapi/isolated_context.h" | |
46 | |
47 using content::BrowserContext; | |
48 using content::BrowserThread; | |
49 using content::ChildProcessSecurityPolicy; | |
50 using content::SiteInstance; | |
51 using content::WebContents; | |
52 using extensions::Extension; | |
53 using fileapi::FileSystemURL; | |
54 | |
55 namespace file_handler_util { | |
56 | |
57 const char kTaskFile[] = "file"; | |
58 const char kTaskDrive[] = "drive"; | |
59 const char kTaskApp[] = "app"; | |
60 | |
61 namespace { | |
62 | |
63 // Legacy Drive task extension prefix, used by CrackTaskID. | |
64 const char kDriveTaskExtensionPrefix[] = "drive-app:"; | |
65 const size_t kDriveTaskExtensionPrefixLength = | |
66 arraysize(kDriveTaskExtensionPrefix) - 1; | |
67 | |
68 typedef std::set<const FileBrowserHandler*> FileBrowserHandlerSet; | |
69 | |
70 const int kReadWriteFilePermissions = base::PLATFORM_FILE_OPEN | | |
71 base::PLATFORM_FILE_CREATE | | |
72 base::PLATFORM_FILE_OPEN_ALWAYS | | |
73 base::PLATFORM_FILE_CREATE_ALWAYS | | |
74 base::PLATFORM_FILE_OPEN_TRUNCATED | | |
75 base::PLATFORM_FILE_READ | | |
76 base::PLATFORM_FILE_WRITE | | |
77 base::PLATFORM_FILE_EXCLUSIVE_READ | | |
78 base::PLATFORM_FILE_EXCLUSIVE_WRITE | | |
79 base::PLATFORM_FILE_ASYNC | | |
80 base::PLATFORM_FILE_WRITE_ATTRIBUTES; | |
81 | |
82 const int kReadOnlyFilePermissions = base::PLATFORM_FILE_OPEN | | |
83 base::PLATFORM_FILE_READ | | |
84 base::PLATFORM_FILE_EXCLUSIVE_READ | | |
85 base::PLATFORM_FILE_ASYNC; | |
86 | |
87 const char kFileBrowserExtensionId[] = "hhaomjibdihmijegdhdafkllkbggdgoj"; | |
88 | |
89 // Returns process id of the process the extension is running in. | |
90 int ExtractProcessFromExtensionId(Profile* profile, | |
91 const std::string& extension_id) { | |
92 GURL extension_url = | |
93 Extension::GetBaseURLFromExtensionId(extension_id); | |
94 ExtensionProcessManager* manager = | |
95 extensions::ExtensionSystem::Get(profile)->process_manager(); | |
96 | |
97 SiteInstance* site_instance = manager->GetSiteInstanceForURL(extension_url); | |
98 if (!site_instance || !site_instance->HasProcess()) | |
99 return -1; | |
100 content::RenderProcessHost* process = site_instance->GetProcess(); | |
101 | |
102 return process->GetID(); | |
103 } | |
104 | |
105 bool IsBuiltinTask(const FileBrowserHandler* task) { | |
106 return (task->extension_id() == kFileBrowserExtensionId || | |
107 task->extension_id() == | |
108 extension_misc::kQuickOfficeComponentExtensionId || | |
109 task->extension_id() == extension_misc::kQuickOfficeDevExtensionId || | |
110 task->extension_id() == extension_misc::kQuickOfficeExtensionId); | |
111 } | |
112 | |
113 bool MatchesAllURLs(const FileBrowserHandler* handler) { | |
114 const std::set<URLPattern>& patterns = | |
115 handler->file_url_patterns().patterns(); | |
116 for (std::set<URLPattern>::const_iterator it = patterns.begin(); | |
117 it != patterns.end(); | |
118 ++it) { | |
119 if (it->match_all_urls()) | |
120 return true; | |
121 } | |
122 return false; | |
123 } | |
124 | |
125 const FileBrowserHandler* FindFileBrowserHandler(const Extension* extension, | |
126 const std::string& action_id) { | |
127 FileBrowserHandler::List* handler_list = | |
128 FileBrowserHandler::GetHandlers(extension); | |
129 for (FileBrowserHandler::List::const_iterator action_iter = | |
130 handler_list->begin(); | |
131 action_iter != handler_list->end(); | |
132 ++action_iter) { | |
133 if (action_iter->get()->id() == action_id) | |
134 return action_iter->get(); | |
135 } | |
136 return NULL; | |
137 } | |
138 | |
139 unsigned int GetAccessPermissionsForFileBrowserHandler( | |
140 const Extension* extension, | |
141 const std::string& action_id) { | |
142 const FileBrowserHandler* action = | |
143 FindFileBrowserHandler(extension, action_id); | |
144 if (!action) | |
145 return 0; | |
146 unsigned int result = 0; | |
147 if (action->CanRead()) | |
148 result |= kReadOnlyFilePermissions; | |
149 if (action->CanWrite()) | |
150 result |= kReadWriteFilePermissions; | |
151 // TODO(tbarzic): We don't handle Create yet. | |
152 return result; | |
153 } | |
154 | |
155 std::string EscapedUtf8ToLower(const std::string& str) { | |
156 string16 utf16 = UTF8ToUTF16( | |
157 net::UnescapeURLComponent(str, net::UnescapeRule::NORMAL)); | |
158 return net::EscapeUrlEncodedData( | |
159 UTF16ToUTF8(base::i18n::ToLower(utf16)), | |
160 false /* do not replace space with plus */); | |
161 } | |
162 | |
163 bool GetFileBrowserHandlers(Profile* profile, | |
164 const GURL& selected_file_url, | |
165 FileBrowserHandlerSet* results) { | |
166 ExtensionService* service = | |
167 extensions::ExtensionSystem::Get(profile)->extension_service(); | |
168 if (!service) | |
169 return false; // In unit-tests, we may not have an ExtensionService. | |
170 | |
171 // We need case-insensitive matching, and pattern in the handler is already | |
172 // in lower case. | |
173 const GURL lowercase_url(EscapedUtf8ToLower(selected_file_url.spec())); | |
174 | |
175 for (ExtensionSet::const_iterator iter = service->extensions()->begin(); | |
176 iter != service->extensions()->end(); | |
177 ++iter) { | |
178 const Extension* extension = *iter; | |
179 if (profile->IsOffTheRecord() && | |
180 !service->IsIncognitoEnabled(extension->id())) | |
181 continue; | |
182 | |
183 FileBrowserHandler::List* handler_list = | |
184 FileBrowserHandler::GetHandlers(extension); | |
185 if (!handler_list) | |
186 continue; | |
187 for (FileBrowserHandler::List::const_iterator action_iter = | |
188 handler_list->begin(); | |
189 action_iter != handler_list->end(); | |
190 ++action_iter) { | |
191 const FileBrowserHandler* action = action_iter->get(); | |
192 if (!action->MatchesURL(lowercase_url)) | |
193 continue; | |
194 | |
195 results->insert(action_iter->get()); | |
196 } | |
197 } | |
198 return true; | |
199 } | |
200 | |
201 } // namespace | |
202 | |
203 void UpdateDefaultTask(Profile* profile, | |
204 const std::string& task_id, | |
205 const std::set<std::string>& suffixes, | |
206 const std::set<std::string>& mime_types) { | |
207 if (!profile || !profile->GetPrefs()) | |
208 return; | |
209 | |
210 if (!mime_types.empty()) { | |
211 DictionaryPrefUpdate mime_type_pref(profile->GetPrefs(), | |
212 prefs::kDefaultTasksByMimeType); | |
213 for (std::set<std::string>::const_iterator iter = mime_types.begin(); | |
214 iter != mime_types.end(); ++iter) { | |
215 base::StringValue* value = new base::StringValue(task_id); | |
216 mime_type_pref->SetWithoutPathExpansion(*iter, value); | |
217 } | |
218 } | |
219 | |
220 if (!suffixes.empty()) { | |
221 DictionaryPrefUpdate mime_type_pref(profile->GetPrefs(), | |
222 prefs::kDefaultTasksBySuffix); | |
223 for (std::set<std::string>::const_iterator iter = suffixes.begin(); | |
224 iter != suffixes.end(); ++iter) { | |
225 base::StringValue* value = new base::StringValue(task_id); | |
226 // Suffixes are case insensitive. | |
227 std::string lower_suffix = StringToLowerASCII(*iter); | |
228 mime_type_pref->SetWithoutPathExpansion(lower_suffix, value); | |
229 } | |
230 } | |
231 } | |
232 | |
233 std::string GetDefaultTaskIdFromPrefs(Profile* profile, | |
234 const std::string& mime_type, | |
235 const std::string& suffix) { | |
236 VLOG(1) << "Looking for default for MIME type: " << mime_type | |
237 << " and suffix: " << suffix; | |
238 std::string task_id; | |
239 if (!mime_type.empty()) { | |
240 const DictionaryValue* mime_task_prefs = | |
241 profile->GetPrefs()->GetDictionary(prefs::kDefaultTasksByMimeType); | |
242 DCHECK(mime_task_prefs); | |
243 LOG_IF(ERROR, !mime_task_prefs) << "Unable to open MIME type prefs"; | |
244 if (mime_task_prefs && | |
245 mime_task_prefs->GetStringWithoutPathExpansion(mime_type, &task_id)) { | |
246 VLOG(1) << "Found MIME default handler: " << task_id; | |
247 return task_id; | |
248 } | |
249 } | |
250 | |
251 const DictionaryValue* suffix_task_prefs = | |
252 profile->GetPrefs()->GetDictionary(prefs::kDefaultTasksBySuffix); | |
253 DCHECK(suffix_task_prefs); | |
254 LOG_IF(ERROR, !suffix_task_prefs) << "Unable to open suffix prefs"; | |
255 std::string lower_suffix = StringToLowerASCII(suffix); | |
256 if (suffix_task_prefs) | |
257 suffix_task_prefs->GetStringWithoutPathExpansion(lower_suffix, &task_id); | |
258 VLOG_IF(1, !task_id.empty()) << "Found suffix default handler: " << task_id; | |
259 return task_id; | |
260 } | |
261 | |
262 int GetReadWritePermissions() { | |
263 return kReadWriteFilePermissions; | |
264 } | |
265 | |
266 int GetReadOnlyPermissions() { | |
267 return kReadOnlyFilePermissions; | |
268 } | |
269 | |
270 std::string MakeTaskID(const std::string& extension_id, | |
271 const std::string& task_type, | |
272 const std::string& action_id) { | |
273 DCHECK(task_type == kTaskFile || | |
274 task_type == kTaskDrive || | |
275 task_type == kTaskApp); | |
276 return base::StringPrintf("%s|%s|%s", | |
277 extension_id.c_str(), | |
278 task_type.c_str(), | |
279 action_id.c_str()); | |
280 } | |
281 | |
282 // Breaks down task_id that is used between getFileTasks() and executeTask() on | |
283 // its building blocks. task_id field the following structure: | |
284 // <extension-id>|<task-type>|<task-action-id> | |
285 bool CrackTaskID(const std::string& task_id, | |
286 std::string* extension_id, | |
287 std::string* task_type, | |
288 std::string* action_id) { | |
289 std::vector<std::string> result; | |
290 int count = Tokenize(task_id, std::string("|"), &result); | |
291 | |
292 // Parse historic task_id parameters that only contain two parts. Drive tasks | |
293 // are identified by a prefix "drive-app:" on the extension ID. | |
294 if (count == 2) { | |
295 if (StartsWithASCII(result[0], kDriveTaskExtensionPrefix, true)) { | |
296 if (task_type) | |
297 *task_type = kTaskDrive; | |
298 | |
299 if (extension_id) | |
300 *extension_id = result[0].substr(kDriveTaskExtensionPrefixLength); | |
301 } else { | |
302 if (task_type) | |
303 *task_type = kTaskFile; | |
304 | |
305 if (extension_id) | |
306 *extension_id = result[0]; | |
307 } | |
308 | |
309 if (action_id) | |
310 *action_id = result[1]; | |
311 | |
312 return true; | |
313 } | |
314 | |
315 if (count != 3) | |
316 return false; | |
317 | |
318 if (extension_id) | |
319 *extension_id = result[0]; | |
320 | |
321 if (task_type) { | |
322 *task_type = result[1]; | |
323 DCHECK(*task_type == kTaskFile || | |
324 *task_type == kTaskDrive || | |
325 *task_type == kTaskApp); | |
326 } | |
327 | |
328 if (action_id) | |
329 *action_id = result[2]; | |
330 | |
331 return true; | |
332 } | |
333 | |
334 // Find a specific handler in the handler list. | |
335 FileBrowserHandlerSet::iterator FindHandler( | |
336 FileBrowserHandlerSet* handler_set, | |
337 const std::string& extension_id, | |
338 const std::string& id) { | |
339 FileBrowserHandlerSet::iterator iter = handler_set->begin(); | |
340 while (iter != handler_set->end() && | |
341 !((*iter)->extension_id() == extension_id && | |
342 (*iter)->id() == id)) { | |
343 iter++; | |
344 } | |
345 return iter; | |
346 } | |
347 | |
348 // Given the list of selected files, returns array of file action tasks | |
349 // that are shared between them. | |
350 void FindDefaultTasks(Profile* profile, | |
351 const std::vector<base::FilePath>& files_list, | |
352 const FileBrowserHandlerSet& common_tasks, | |
353 FileBrowserHandlerSet* default_tasks) { | |
354 DCHECK(default_tasks); | |
355 default_tasks->clear(); | |
356 | |
357 std::set<std::string> default_ids; | |
358 for (std::vector<base::FilePath>::const_iterator it = files_list.begin(); | |
359 it != files_list.end(); ++it) { | |
360 std::string task_id = file_handler_util::GetDefaultTaskIdFromPrefs( | |
361 profile, "", it->Extension()); | |
362 if (!task_id.empty()) | |
363 default_ids.insert(task_id); | |
364 } | |
365 | |
366 const FileBrowserHandler* builtin_task = NULL; | |
367 // Convert the default task IDs collected above to one of the handler pointers | |
368 // from common_tasks. | |
369 for (FileBrowserHandlerSet::const_iterator task_iter = common_tasks.begin(); | |
370 task_iter != common_tasks.end(); ++task_iter) { | |
371 std::string task_id = MakeTaskID((*task_iter)->extension_id(), kTaskFile, | |
372 (*task_iter)->id()); | |
373 std::set<std::string>::iterator default_iter = default_ids.find(task_id); | |
374 if (default_iter != default_ids.end()) { | |
375 default_tasks->insert(*task_iter); | |
376 continue; | |
377 } | |
378 | |
379 // If it's a built in task, remember it. If there are no default tasks among | |
380 // common tasks, builtin task will be used as a fallback. | |
381 // Note that builtin tasks are not overlapping, so there can be at most one | |
382 // builtin tasks for each set of files. | |
383 if (IsBuiltinTask(*task_iter)) | |
384 builtin_task = *task_iter; | |
385 } | |
386 | |
387 // If there are no default tasks found, use builtin task (if found) as a | |
388 // default. | |
389 if (builtin_task && default_tasks->empty()) | |
390 default_tasks->insert(builtin_task); | |
391 } | |
392 | |
393 // Given the list of selected files, returns array of context menu tasks | |
394 // that are shared | |
395 bool FindCommonTasks(Profile* profile, | |
396 const std::vector<GURL>& files_list, | |
397 FileBrowserHandlerSet* common_tasks) { | |
398 DCHECK(common_tasks); | |
399 common_tasks->clear(); | |
400 | |
401 FileBrowserHandlerSet common_task_set; | |
402 std::set<std::string> default_task_ids; | |
403 for (std::vector<GURL>::const_iterator it = files_list.begin(); | |
404 it != files_list.end(); ++it) { | |
405 FileBrowserHandlerSet file_actions; | |
406 if (!GetFileBrowserHandlers(profile, *it, &file_actions)) | |
407 return false; | |
408 // If there is nothing to do for one file, the intersection of tasks for all | |
409 // files will be empty at the end, and so will the default tasks. | |
410 if (file_actions.empty()) | |
411 return true; | |
412 | |
413 // For the very first file, just copy all the elements. | |
414 if (it == files_list.begin()) { | |
415 common_task_set = file_actions; | |
416 } else { | |
417 // For all additional files, find intersection between the accumulated and | |
418 // file specific set. | |
419 FileBrowserHandlerSet intersection; | |
420 std::set_intersection(common_task_set.begin(), common_task_set.end(), | |
421 file_actions.begin(), file_actions.end(), | |
422 std::inserter(intersection, | |
423 intersection.begin())); | |
424 common_task_set = intersection; | |
425 if (common_task_set.empty()) | |
426 return true; | |
427 } | |
428 } | |
429 | |
430 FileBrowserHandlerSet::iterator watch_iter = FindHandler( | |
431 &common_task_set, kFileBrowserDomain, kFileBrowserWatchTaskId); | |
432 FileBrowserHandlerSet::iterator gallery_iter = FindHandler( | |
433 &common_task_set, kFileBrowserDomain, kFileBrowserGalleryTaskId); | |
434 if (watch_iter != common_task_set.end() && | |
435 gallery_iter != common_task_set.end()) { | |
436 // Both "watch" and "gallery" actions are applicable which means that the | |
437 // selection is all videos. Showing them both is confusing, so we only keep | |
438 // the one that makes more sense ("watch" for single selection, "gallery" | |
439 // for multiple selection). | |
440 if (files_list.size() == 1) | |
441 common_task_set.erase(gallery_iter); | |
442 else | |
443 common_task_set.erase(watch_iter); | |
444 } | |
445 | |
446 common_tasks->swap(common_task_set); | |
447 return true; | |
448 } | |
449 | |
450 bool GetTaskForURLAndPath(Profile* profile, | |
451 const GURL& url, | |
452 const base::FilePath& file_path, | |
453 const FileBrowserHandler** handler) { | |
454 std::vector<GURL> file_urls; | |
455 file_urls.push_back(url); | |
456 | |
457 FileBrowserHandlerSet default_tasks; | |
458 FileBrowserHandlerSet common_tasks; | |
459 if (!FindCommonTasks(profile, file_urls, &common_tasks)) | |
460 return false; | |
461 | |
462 if (common_tasks.empty()) | |
463 return false; | |
464 | |
465 std::vector<base::FilePath> file_paths; | |
466 file_paths.push_back(file_path); | |
467 | |
468 FindDefaultTasks(profile, file_paths, common_tasks, &default_tasks); | |
469 | |
470 // If there's none, or more than one, then we don't have a canonical default. | |
471 if (!default_tasks.empty()) { | |
472 // There should not be multiple default tasks for a single URL. | |
473 DCHECK_EQ(1u, default_tasks.size()); | |
474 | |
475 *handler = *default_tasks.begin(); | |
476 return true; | |
477 } | |
478 | |
479 // If there are no default tasks, use first task in the list (file manager | |
480 // does the same in this situation). | |
481 // TODO(tbarzic): This is so not optimal behaviour. | |
482 *handler = *common_tasks.begin(); | |
483 return true; | |
484 } | |
485 | |
486 class ExtensionTaskExecutor : public FileTaskExecutor { | |
487 public: | |
488 // FileTaskExecutor overrides. | |
489 virtual bool ExecuteAndNotify(const std::vector<FileSystemURL>& file_urls, | |
490 const FileTaskFinishedCallback& done) OVERRIDE; | |
491 | |
492 private: | |
493 // FileTaskExecutor is the only class allowed to create one. | |
494 friend class FileTaskExecutor; | |
495 | |
496 ExtensionTaskExecutor(Profile* profile, | |
497 const GURL& source_url, | |
498 const std::string& file_browser_id, | |
499 int32 tab_id, | |
500 const std::string& extension_id, | |
501 const std::string& action_id); | |
502 virtual ~ExtensionTaskExecutor(); | |
503 | |
504 struct FileDefinition { | |
505 FileDefinition(); | |
506 ~FileDefinition(); | |
507 | |
508 base::FilePath virtual_path; | |
509 base::FilePath absolute_path; | |
510 bool is_directory; | |
511 }; | |
512 | |
513 typedef std::vector<FileDefinition> FileDefinitionList; | |
514 class ExecuteTasksFileSystemCallbackDispatcher; | |
515 void RequestFileEntryOnFileThread( | |
516 scoped_refptr<fileapi::FileSystemContext> file_system_context_handler, | |
517 const GURL& handler_base_url, | |
518 const scoped_refptr<const extensions::Extension>& handler, | |
519 int handler_pid, | |
520 const std::vector<FileSystemURL>& file_urls); | |
521 | |
522 void ExecuteDoneOnUIThread(bool success); | |
523 void ExecuteFileActionsOnUIThread(const std::string& file_system_name, | |
524 const GURL& file_system_root, | |
525 const FileDefinitionList& file_list, | |
526 int handler_pid); | |
527 void SetupPermissionsAndDispatchEvent(const std::string& file_system_name, | |
528 const GURL& file_system_root, | |
529 const FileDefinitionList& file_list, | |
530 int handler_pid_in, | |
531 extensions::ExtensionHost* host); | |
532 | |
533 // Registers file permissions from |handler_host_permissions_| with | |
534 // ChildProcessSecurityPolicy for process with id |handler_pid|. | |
535 void SetupHandlerHostFileAccessPermissions( | |
536 const FileDefinitionList& file_list, | |
537 const Extension* extension, | |
538 int handler_pid); | |
539 | |
540 int32 tab_id_; | |
541 const std::string action_id_; | |
542 FileTaskFinishedCallback done_; | |
543 }; | |
544 | |
545 class AppTaskExecutor : public FileTaskExecutor { | |
546 public: | |
547 // FileTaskExecutor overrides. | |
548 virtual bool ExecuteAndNotify(const std::vector<FileSystemURL>& file_urls, | |
549 const FileTaskFinishedCallback& done) OVERRIDE; | |
550 | |
551 private: | |
552 // FileTaskExecutor is the only class allowed to create one. | |
553 friend class FileTaskExecutor; | |
554 | |
555 AppTaskExecutor(Profile* profile, | |
556 const GURL& source_url, | |
557 const std::string& file_browser_id, | |
558 const std::string& extension_id, | |
559 const std::string& action_id); | |
560 virtual ~AppTaskExecutor(); | |
561 | |
562 const std::string extension_id_; | |
563 const std::string action_id_; | |
564 }; | |
565 | |
566 // static | |
567 FileTaskExecutor* FileTaskExecutor::Create(Profile* profile, | |
568 const GURL& source_url, | |
569 const std::string& file_browser_id, | |
570 int32 tab_id, | |
571 const std::string& extension_id, | |
572 const std::string& task_type, | |
573 const std::string& action_id) { | |
574 if (task_type == kTaskFile) | |
575 return new ExtensionTaskExecutor(profile, | |
576 source_url, | |
577 file_browser_id, | |
578 tab_id, | |
579 extension_id, | |
580 action_id); | |
581 | |
582 if (task_type == kTaskDrive) | |
583 return new drive::DriveTaskExecutor(profile, | |
584 extension_id, // really app_id | |
585 action_id); | |
586 | |
587 if (task_type == kTaskApp) | |
588 return new AppTaskExecutor(profile, | |
589 source_url, | |
590 file_browser_id, | |
591 extension_id, | |
592 action_id); | |
593 | |
594 NOTREACHED(); | |
595 return NULL; | |
596 } | |
597 | |
598 FileTaskExecutor::FileTaskExecutor(Profile* profile, | |
599 const GURL& source_url, | |
600 const std::string& file_browser_id, | |
601 const std::string& extension_id) | |
602 : profile_(profile), | |
603 source_url_(source_url), | |
604 file_browser_id_(file_browser_id), | |
605 extension_id_(extension_id) { | |
606 } | |
607 | |
608 FileTaskExecutor::~FileTaskExecutor() { | |
609 } | |
610 | |
611 bool FileTaskExecutor::Execute(const std::vector<FileSystemURL>& file_urls) { | |
612 return ExecuteAndNotify(file_urls, FileTaskFinishedCallback()); | |
613 } | |
614 | |
615 bool FileTaskExecutor::FileBrowserHasAccessPermissionForFiles( | |
616 const std::vector<FileSystemURL>& files) { | |
617 // Check if the file browser extension has permissions for the files in its | |
618 // file system context. | |
619 GURL site = extensions::ExtensionSystem::Get(profile())->extension_service()-> | |
620 GetSiteForExtensionId(file_browser_id_); | |
621 fileapi::ExternalFileSystemMountPointProvider* external_provider = | |
622 BrowserContext::GetStoragePartitionForSite(profile(), site)-> | |
623 GetFileSystemContext()->external_provider(); | |
624 | |
625 if (!external_provider) | |
626 return false; | |
627 | |
628 for (size_t i = 0; i < files.size(); ++i) { | |
629 // Make sure this url really being used by the right caller extension. | |
630 if (source_url_.GetOrigin() != files[i].origin()) | |
631 return false; | |
632 | |
633 if (!chromeos::CrosMountPointProvider::CanHandleURL(files[i]) || | |
634 !external_provider->IsAccessAllowed(files[i])) { | |
635 return false; | |
636 } | |
637 } | |
638 | |
639 return true; | |
640 } | |
641 | |
642 // TODO(kaznacheev): Remove this method and inline its implementation at the | |
643 // only place where it is used (DriveTaskExecutor::OnAppAuthorized) | |
644 Browser* FileTaskExecutor::GetBrowser() const { | |
645 return chrome::FindOrCreateTabbedBrowser( | |
646 profile_ ? profile_ : ProfileManager::GetDefaultProfileOrOffTheRecord(), | |
647 chrome::HOST_DESKTOP_TYPE_ASH); | |
648 } | |
649 | |
650 const Extension* FileTaskExecutor::GetExtension() { | |
651 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
652 | |
653 ExtensionService* service = | |
654 extensions::ExtensionSystem::Get(profile())->extension_service(); | |
655 return service ? service->GetExtensionById(extension_id_, false) : | |
656 NULL; | |
657 } | |
658 | |
659 ExtensionTaskExecutor::FileDefinition::FileDefinition() : is_directory(false) { | |
660 } | |
661 | |
662 ExtensionTaskExecutor::FileDefinition::~FileDefinition() { | |
663 } | |
664 | |
665 class ExtensionTaskExecutor::ExecuteTasksFileSystemCallbackDispatcher { | |
666 public: | |
667 static fileapi::FileSystemContext::OpenFileSystemCallback CreateCallback( | |
668 ExtensionTaskExecutor* executor, | |
669 scoped_refptr<fileapi::FileSystemContext> file_system_context_handler, | |
670 scoped_refptr<const Extension> handler_extension, | |
671 int handler_pid, | |
672 const std::string& action_id, | |
673 const std::vector<FileSystemURL>& file_urls) { | |
674 return base::Bind( | |
675 &ExecuteTasksFileSystemCallbackDispatcher::DidOpenFileSystem, | |
676 base::Owned(new ExecuteTasksFileSystemCallbackDispatcher( | |
677 executor, file_system_context_handler, handler_extension, | |
678 handler_pid, action_id, file_urls))); | |
679 } | |
680 | |
681 void DidOpenFileSystem(base::PlatformFileError result, | |
682 const std::string& file_system_name, | |
683 const GURL& file_system_root) { | |
684 if (result != base::PLATFORM_FILE_OK) { | |
685 DidFail(result); | |
686 return; | |
687 } | |
688 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
689 ExtensionTaskExecutor::FileDefinitionList file_list; | |
690 for (std::vector<FileSystemURL>::iterator iter = urls_.begin(); | |
691 iter != urls_.end(); | |
692 ++iter) { | |
693 // Set up file permission access. | |
694 ExtensionTaskExecutor::FileDefinition file; | |
695 if (!SetupFileAccessPermissions(*iter, &file)) | |
696 continue; | |
697 file_list.push_back(file); | |
698 } | |
699 if (file_list.empty()) { | |
700 BrowserThread::PostTask( | |
701 BrowserThread::UI, FROM_HERE, | |
702 base::Bind( | |
703 &ExtensionTaskExecutor::ExecuteDoneOnUIThread, | |
704 executor_, | |
705 false)); | |
706 return; | |
707 } | |
708 | |
709 BrowserThread::PostTask( | |
710 BrowserThread::UI, FROM_HERE, | |
711 base::Bind( | |
712 &ExtensionTaskExecutor::ExecuteFileActionsOnUIThread, | |
713 executor_, | |
714 file_system_name, | |
715 file_system_root, | |
716 file_list, | |
717 handler_pid_)); | |
718 } | |
719 | |
720 void DidFail(base::PlatformFileError error_code) { | |
721 BrowserThread::PostTask( | |
722 BrowserThread::UI, FROM_HERE, | |
723 base::Bind( | |
724 &ExtensionTaskExecutor::ExecuteDoneOnUIThread, | |
725 executor_, | |
726 false)); | |
727 } | |
728 | |
729 private: | |
730 ExecuteTasksFileSystemCallbackDispatcher( | |
731 ExtensionTaskExecutor* executor, | |
732 scoped_refptr<fileapi::FileSystemContext> file_system_context_handler, | |
733 const scoped_refptr<const Extension>& handler_extension, | |
734 int handler_pid, | |
735 const std::string& action_id, | |
736 const std::vector<FileSystemURL>& file_urls) | |
737 : executor_(executor), | |
738 file_system_context_handler_(file_system_context_handler), | |
739 handler_extension_(handler_extension), | |
740 handler_pid_(handler_pid), | |
741 action_id_(action_id), | |
742 urls_(file_urls) { | |
743 DCHECK(executor_); | |
744 } | |
745 | |
746 // Checks legitimacy of file url and grants file RO access permissions from | |
747 // handler (target) extension and its renderer process. | |
748 bool SetupFileAccessPermissions(const FileSystemURL& url, | |
749 FileDefinition* file) { | |
750 if (!handler_extension_.get()) | |
751 return false; | |
752 | |
753 if (handler_pid_ == 0) | |
754 return false; | |
755 | |
756 fileapi::ExternalFileSystemMountPointProvider* external_provider_handler = | |
757 file_system_context_handler_->external_provider(); | |
758 | |
759 // Check if this file system entry exists first. | |
760 base::PlatformFileInfo file_info; | |
761 | |
762 base::FilePath local_path = url.path(); | |
763 base::FilePath virtual_path = url.virtual_path(); | |
764 | |
765 bool is_drive_file = url.type() == fileapi::kFileSystemTypeDrive; | |
766 DCHECK(!is_drive_file || drive::util::IsUnderDriveMountPoint(local_path)); | |
767 | |
768 // If the file is under gdata mount point, there is no actual file to be | |
769 // found on the url.path(). | |
770 if (!is_drive_file) { | |
771 if (!file_util::PathExists(local_path) || | |
772 file_util::IsLink(local_path) || | |
773 !file_util::GetFileInfo(local_path, &file_info)) { | |
774 return false; | |
775 } | |
776 } | |
777 | |
778 // Grant access to this particular file to target extension. This will | |
779 // ensure that the target extension can access only this FS entry and | |
780 // prevent from traversing FS hierarchy upward. | |
781 external_provider_handler->GrantFileAccessToExtension( | |
782 handler_extension_->id(), virtual_path); | |
783 | |
784 // Output values. | |
785 file->virtual_path = virtual_path; | |
786 file->is_directory = file_info.is_directory; | |
787 file->absolute_path = local_path; | |
788 return true; | |
789 } | |
790 | |
791 ExtensionTaskExecutor* executor_; | |
792 scoped_refptr<fileapi::FileSystemContext> file_system_context_handler_; | |
793 scoped_refptr<const Extension> handler_extension_; | |
794 int handler_pid_; | |
795 std::string action_id_; | |
796 std::vector<FileSystemURL> urls_; | |
797 DISALLOW_COPY_AND_ASSIGN(ExecuteTasksFileSystemCallbackDispatcher); | |
798 }; | |
799 | |
800 ExtensionTaskExecutor::ExtensionTaskExecutor( | |
801 Profile* profile, | |
802 const GURL& source_url, | |
803 const std::string& file_browser_id, | |
804 int tab_id, | |
805 const std::string& extension_id, | |
806 const std::string& action_id) | |
807 : FileTaskExecutor(profile, source_url, file_browser_id, extension_id), | |
808 tab_id_(tab_id), | |
809 action_id_(action_id) { | |
810 } | |
811 | |
812 ExtensionTaskExecutor::~ExtensionTaskExecutor() {} | |
813 | |
814 bool ExtensionTaskExecutor::ExecuteAndNotify( | |
815 const std::vector<FileSystemURL>& file_urls, | |
816 const FileTaskFinishedCallback& done) { | |
817 if (!FileBrowserHasAccessPermissionForFiles(file_urls)) | |
818 return false; | |
819 | |
820 scoped_refptr<const Extension> handler = GetExtension(); | |
821 if (!handler.get()) | |
822 return false; | |
823 | |
824 int handler_pid = ExtractProcessFromExtensionId(profile(), handler->id()); | |
825 if (handler_pid <= 0) { | |
826 if (!extensions::BackgroundInfo::HasLazyBackgroundPage(handler)) | |
827 return false; | |
828 } | |
829 | |
830 done_ = done; | |
831 | |
832 // Get file system context for the extension to which onExecute event will be | |
833 // send. The file access permissions will be granted to the extension in the | |
834 // file system context for the files in |file_urls|. | |
835 GURL site = extensions::ExtensionSystem::Get(profile())->extension_service()-> | |
836 GetSiteForExtensionId(handler->id()); | |
837 scoped_refptr<fileapi::FileSystemContext> file_system_context_handler = | |
838 BrowserContext::GetStoragePartitionForSite(profile(), site)-> | |
839 GetFileSystemContext(); | |
840 | |
841 BrowserThread::PostTask( | |
842 BrowserThread::FILE, FROM_HERE, | |
843 base::Bind( | |
844 &ExtensionTaskExecutor::RequestFileEntryOnFileThread, | |
845 this, | |
846 file_system_context_handler, | |
847 Extension::GetBaseURLFromExtensionId(handler->id()), | |
848 handler, | |
849 handler_pid, | |
850 file_urls)); | |
851 return true; | |
852 } | |
853 | |
854 void ExtensionTaskExecutor::RequestFileEntryOnFileThread( | |
855 scoped_refptr<fileapi::FileSystemContext> file_system_context_handler, | |
856 const GURL& handler_base_url, | |
857 const scoped_refptr<const Extension>& handler, | |
858 int handler_pid, | |
859 const std::vector<FileSystemURL>& file_urls) { | |
860 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
861 GURL origin_url = handler_base_url.GetOrigin(); | |
862 file_system_context_handler->OpenFileSystem( | |
863 origin_url, fileapi::kFileSystemTypeExternal, false, // create | |
864 ExecuteTasksFileSystemCallbackDispatcher::CreateCallback( | |
865 this, | |
866 file_system_context_handler, | |
867 handler, | |
868 handler_pid, | |
869 action_id_, | |
870 file_urls)); | |
871 } | |
872 | |
873 void ExtensionTaskExecutor::ExecuteDoneOnUIThread(bool success) { | |
874 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
875 if (!done_.is_null()) | |
876 done_.Run(success); | |
877 done_.Reset(); | |
878 } | |
879 | |
880 void ExtensionTaskExecutor::ExecuteFileActionsOnUIThread( | |
881 const std::string& file_system_name, | |
882 const GURL& file_system_root, | |
883 const FileDefinitionList& file_list, | |
884 int handler_pid) { | |
885 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
886 | |
887 const Extension* extension = GetExtension(); | |
888 if (!extension) { | |
889 ExecuteDoneOnUIThread(false); | |
890 return; | |
891 } | |
892 | |
893 if (handler_pid > 0) { | |
894 SetupPermissionsAndDispatchEvent(file_system_name, file_system_root, | |
895 file_list, handler_pid, NULL); | |
896 } else { | |
897 // We have to wake the handler background page before we proceed. | |
898 extensions::LazyBackgroundTaskQueue* queue = | |
899 extensions::ExtensionSystem::Get(profile())-> | |
900 lazy_background_task_queue(); | |
901 if (!queue->ShouldEnqueueTask(profile(), extension)) { | |
902 ExecuteDoneOnUIThread(false); | |
903 return; | |
904 } | |
905 queue->AddPendingTask( | |
906 profile(), extension_id(), | |
907 base::Bind(&ExtensionTaskExecutor::SetupPermissionsAndDispatchEvent, | |
908 this, file_system_name, file_system_root, file_list, | |
909 handler_pid)); | |
910 } | |
911 } | |
912 | |
913 void ExtensionTaskExecutor::SetupPermissionsAndDispatchEvent( | |
914 const std::string& file_system_name, | |
915 const GURL& file_system_root, | |
916 const FileDefinitionList& file_list, | |
917 int handler_pid_in, | |
918 extensions::ExtensionHost* host) { | |
919 int handler_pid = host ? host->render_process_host()->GetID() : | |
920 handler_pid_in; | |
921 | |
922 if (handler_pid <= 0) { | |
923 ExecuteDoneOnUIThread(false); | |
924 return; | |
925 } | |
926 | |
927 extensions::EventRouter* event_router = | |
928 extensions::ExtensionSystem::Get(profile())->event_router(); | |
929 if (!event_router) { | |
930 ExecuteDoneOnUIThread(false); | |
931 return; | |
932 } | |
933 | |
934 const Extension* extension = GetExtension(); | |
935 if (!extension) { | |
936 ExecuteDoneOnUIThread(false); | |
937 return; | |
938 } | |
939 | |
940 SetupHandlerHostFileAccessPermissions(file_list, extension, handler_pid); | |
941 | |
942 scoped_ptr<ListValue> event_args(new ListValue()); | |
943 event_args->Append(new base::StringValue(action_id_)); | |
944 DictionaryValue* details = new DictionaryValue(); | |
945 event_args->Append(details); | |
946 // Get file definitions. These will be replaced with Entry instances by | |
947 // chromeHidden.Event.dispatchEvent() method from event_binding.js. | |
948 ListValue* files_urls = new ListValue(); | |
949 details->Set("entries", files_urls); | |
950 for (FileDefinitionList::const_iterator iter = file_list.begin(); | |
951 iter != file_list.end(); | |
952 ++iter) { | |
953 DictionaryValue* file_def = new DictionaryValue(); | |
954 files_urls->Append(file_def); | |
955 file_def->SetString("fileSystemName", file_system_name); | |
956 file_def->SetString("fileSystemRoot", file_system_root.spec()); | |
957 base::FilePath root(FILE_PATH_LITERAL("/")); | |
958 base::FilePath full_path = root.Append(iter->virtual_path); | |
959 file_def->SetString("fileFullPath", full_path.value()); | |
960 file_def->SetBoolean("fileIsDirectory", iter->is_directory); | |
961 } | |
962 | |
963 details->SetInteger("tab_id", tab_id_); | |
964 | |
965 scoped_ptr<extensions::Event> event(new extensions::Event( | |
966 "fileBrowserHandler.onExecute", event_args.Pass())); | |
967 event->restrict_to_profile = profile(); | |
968 event_router->DispatchEventToExtension(extension_id(), event.Pass()); | |
969 | |
970 ExecuteDoneOnUIThread(true); | |
971 } | |
972 | |
973 void ExtensionTaskExecutor::SetupHandlerHostFileAccessPermissions( | |
974 const FileDefinitionList& file_list, | |
975 const Extension* extension, | |
976 int handler_pid) { | |
977 for (FileDefinitionList::const_iterator iter = file_list.begin(); | |
978 iter != file_list.end(); | |
979 ++iter) { | |
980 content::ChildProcessSecurityPolicy::GetInstance()->GrantPermissionsForFile( | |
981 handler_pid, | |
982 iter->absolute_path, | |
983 GetAccessPermissionsForFileBrowserHandler(extension, action_id_)); | |
984 } | |
985 } | |
986 | |
987 AppTaskExecutor::AppTaskExecutor( | |
988 Profile* profile, | |
989 const GURL& source_url, | |
990 const std::string& file_browser_id, | |
991 const std::string& extension_id, | |
992 const std::string& action_id) | |
993 : FileTaskExecutor(profile, source_url, file_browser_id, extension_id), | |
994 action_id_(action_id) { | |
995 } | |
996 | |
997 AppTaskExecutor::~AppTaskExecutor() {} | |
998 | |
999 bool AppTaskExecutor::ExecuteAndNotify( | |
1000 const std::vector<FileSystemURL>& file_urls, | |
1001 const FileTaskFinishedCallback& done) { | |
1002 if (!FileBrowserHasAccessPermissionForFiles(file_urls)) | |
1003 return false; | |
1004 | |
1005 for (size_t i = 0; i != file_urls.size(); ++i) { | |
1006 extensions::LaunchPlatformAppWithFileHandler(profile(), GetExtension(), | |
1007 action_id_, file_urls[i].path()); | |
1008 } | |
1009 | |
1010 if (!done.is_null()) | |
1011 done.Run(true); | |
1012 return true; | |
1013 } | |
1014 | |
1015 } // namespace file_handler_util | |
OLD | NEW |