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_manager/file_browser_handlers.
h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/file_util.h" | |
9 #include "base/i18n/case_conversion.h" | |
10 #include "base/strings/utf_string_conversions.h" | |
11 #include "chrome/browser/chromeos/drive/file_system_util.h" | |
12 #include "chrome/browser/chromeos/extensions/file_manager/app_id.h" | |
13 #include "chrome/browser/chromeos/extensions/file_manager/fileapi_util.h" | |
14 #include "chrome/browser/chromeos/extensions/file_manager/open_with_browser.h" | |
15 #include "chrome/browser/chromeos/fileapi/file_system_backend.h" | |
16 #include "chrome/browser/extensions/event_router.h" | |
17 #include "chrome/browser/extensions/extension_host.h" | |
18 #include "chrome/browser/extensions/extension_service.h" | |
19 #include "chrome/browser/extensions/extension_system.h" | |
20 #include "chrome/browser/extensions/lazy_background_task_queue.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_handle
r.h" | |
24 #include "chrome/common/extensions/background_info.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 "net/base/escape.h" | |
31 #include "webkit/browser/fileapi/file_system_context.h" | |
32 #include "webkit/browser/fileapi/file_system_url.h" | |
33 | |
34 using content::BrowserThread; | |
35 using content::ChildProcessSecurityPolicy; | |
36 using content::SiteInstance; | |
37 using content::WebContents; | |
38 using extensions::Extension; | |
39 using fileapi::FileSystemURL; | |
40 | |
41 namespace file_manager { | |
42 namespace file_browser_handlers { | |
43 namespace { | |
44 | |
45 // Returns process id of the process the extension is running in. | |
46 int ExtractProcessFromExtensionId(Profile* profile, | |
47 const std::string& extension_id) { | |
48 GURL extension_url = | |
49 Extension::GetBaseURLFromExtensionId(extension_id); | |
50 ExtensionProcessManager* manager = | |
51 extensions::ExtensionSystem::Get(profile)->process_manager(); | |
52 | |
53 SiteInstance* site_instance = manager->GetSiteInstanceForURL(extension_url); | |
54 if (!site_instance || !site_instance->HasProcess()) | |
55 return -1; | |
56 content::RenderProcessHost* process = site_instance->GetProcess(); | |
57 | |
58 return process->GetID(); | |
59 } | |
60 | |
61 // Finds a file browser handler that matches |action_id|. Returns NULL if not | |
62 // found. | |
63 const FileBrowserHandler* FindFileBrowserHandlerForActionId( | |
64 const Extension* extension, | |
65 const std::string& action_id) { | |
66 FileBrowserHandler::List* handler_list = | |
67 FileBrowserHandler::GetHandlers(extension); | |
68 for (FileBrowserHandler::List::const_iterator handler_iter = | |
69 handler_list->begin(); | |
70 handler_iter != handler_list->end(); | |
71 ++handler_iter) { | |
72 if (handler_iter->get()->id() == action_id) | |
73 return handler_iter->get(); | |
74 } | |
75 return NULL; | |
76 } | |
77 | |
78 std::string EscapedUtf8ToLower(const std::string& str) { | |
79 string16 utf16 = UTF8ToUTF16( | |
80 net::UnescapeURLComponent(str, net::UnescapeRule::NORMAL)); | |
81 return net::EscapeUrlEncodedData( | |
82 UTF16ToUTF8(base::i18n::ToLower(utf16)), | |
83 false /* do not replace space with plus */); | |
84 } | |
85 | |
86 // Finds file browser handlers that can handle the |selected_file_url|. | |
87 FileBrowserHandlerList FindFileBrowserHandlersForURL( | |
88 Profile* profile, | |
89 const GURL& selected_file_url) { | |
90 ExtensionService* service = | |
91 extensions::ExtensionSystem::Get(profile)->extension_service(); | |
92 // In unit-tests, we may not have an ExtensionService. | |
93 if (!service) | |
94 return FileBrowserHandlerList(); | |
95 | |
96 // We need case-insensitive matching, and pattern in the handler is already | |
97 // in lower case. | |
98 const GURL lowercase_url(EscapedUtf8ToLower(selected_file_url.spec())); | |
99 | |
100 FileBrowserHandlerList results; | |
101 for (ExtensionSet::const_iterator iter = service->extensions()->begin(); | |
102 iter != service->extensions()->end(); | |
103 ++iter) { | |
104 const Extension* extension = iter->get(); | |
105 if (profile->IsOffTheRecord() && | |
106 !service->IsIncognitoEnabled(extension->id())) | |
107 continue; | |
108 | |
109 FileBrowserHandler::List* handler_list = | |
110 FileBrowserHandler::GetHandlers(extension); | |
111 if (!handler_list) | |
112 continue; | |
113 for (FileBrowserHandler::List::const_iterator handler_iter = | |
114 handler_list->begin(); | |
115 handler_iter != handler_list->end(); | |
116 ++handler_iter) { | |
117 const FileBrowserHandler* handler = handler_iter->get(); | |
118 if (!handler->MatchesURL(lowercase_url)) | |
119 continue; | |
120 | |
121 results.push_back(handler_iter->get()); | |
122 } | |
123 } | |
124 return results; | |
125 } | |
126 | |
127 // Finds a file browser handler that matches |extension_id| and |action_id| | |
128 // from |handler_list|. Returns a mutable iterator to the handler if | |
129 // found. Returns handler_list->end() if not found. | |
130 FileBrowserHandlerList::iterator | |
131 FindFileBrowserHandlerForExtensionIdAndActionId( | |
132 FileBrowserHandlerList* handler_list, | |
133 const std::string& extension_id, | |
134 const std::string& action_id) { | |
135 DCHECK(handler_list); | |
136 | |
137 FileBrowserHandlerList::iterator iter = handler_list->begin(); | |
138 while (iter != handler_list->end() && | |
139 !((*iter)->extension_id() == extension_id && | |
140 (*iter)->id() == action_id)) { | |
141 ++iter; | |
142 } | |
143 return iter; | |
144 } | |
145 | |
146 // This class is used to execute a file browser handler task. Here's how this | |
147 // works: | |
148 // | |
149 // 1) Open the "external" file system | |
150 // 2) Set up permissions for the target files on the external file system. | |
151 // 3) Raise onExecute event with the action ID and entries of the target | |
152 // files. The event will launch the file browser handler if not active. | |
153 // 4) In the file browser handler, onExecute event is handled and executes the | |
154 // task in JavaScript. | |
155 // | |
156 // That said, the class itself does not execute a task. The task will be | |
157 // executed in JavaScript. | |
158 class FileBrowserHandlerExecutor { | |
159 public: | |
160 FileBrowserHandlerExecutor(Profile* profile, | |
161 const Extension* extension, | |
162 int32 tab_id, | |
163 const std::string& action_id); | |
164 | |
165 // Executes the task for each file. |done| will be run with the result. | |
166 void Execute(const std::vector<FileSystemURL>& file_urls, | |
167 const file_tasks::FileTaskFinishedCallback& done); | |
168 | |
169 private: | |
170 // This object is responsible to delete itself. | |
171 virtual ~FileBrowserHandlerExecutor(); | |
172 | |
173 struct FileDefinition { | |
174 FileDefinition(); | |
175 ~FileDefinition(); | |
176 | |
177 base::FilePath virtual_path; | |
178 base::FilePath absolute_path; | |
179 bool is_directory; | |
180 }; | |
181 | |
182 typedef std::vector<FileDefinition> FileDefinitionList; | |
183 | |
184 // Checks legitimacy of file url and grants file RO access permissions from | |
185 // handler (target) extension and its renderer process. | |
186 static FileDefinitionList SetupFileAccessPermissions( | |
187 scoped_refptr<fileapi::FileSystemContext> file_system_context_handler, | |
188 const scoped_refptr<const Extension>& handler_extension, | |
189 const std::vector<FileSystemURL>& file_urls); | |
190 | |
191 void DidOpenFileSystem(const std::vector<FileSystemURL>& file_urls, | |
192 base::PlatformFileError result, | |
193 const std::string& file_system_name, | |
194 const GURL& file_system_root); | |
195 | |
196 void ExecuteDoneOnUIThread(bool success); | |
197 void ExecuteFileActionsOnUIThread(const std::string& file_system_name, | |
198 const GURL& file_system_root, | |
199 const FileDefinitionList& file_list); | |
200 void SetupPermissionsAndDispatchEvent(const std::string& file_system_name, | |
201 const GURL& file_system_root, | |
202 const FileDefinitionList& file_list, | |
203 int handler_pid_in, | |
204 extensions::ExtensionHost* host); | |
205 | |
206 // Registers file permissions from |handler_host_permissions_| with | |
207 // ChildProcessSecurityPolicy for process with id |handler_pid|. | |
208 void SetupHandlerHostFileAccessPermissions( | |
209 const FileDefinitionList& file_list, | |
210 const Extension* extension, | |
211 int handler_pid); | |
212 | |
213 Profile* profile_; | |
214 scoped_refptr<const Extension> extension_; | |
215 int32 tab_id_; | |
216 const std::string action_id_; | |
217 file_tasks::FileTaskFinishedCallback done_; | |
218 base::WeakPtrFactory<FileBrowserHandlerExecutor> weak_ptr_factory_; | |
219 | |
220 DISALLOW_COPY_AND_ASSIGN(FileBrowserHandlerExecutor); | |
221 }; | |
222 | |
223 FileBrowserHandlerExecutor::FileDefinition::FileDefinition() | |
224 : is_directory(false) { | |
225 } | |
226 | |
227 FileBrowserHandlerExecutor::FileDefinition::~FileDefinition() { | |
228 } | |
229 | |
230 // static | |
231 FileBrowserHandlerExecutor::FileDefinitionList | |
232 FileBrowserHandlerExecutor::SetupFileAccessPermissions( | |
233 scoped_refptr<fileapi::FileSystemContext> file_system_context_handler, | |
234 const scoped_refptr<const Extension>& handler_extension, | |
235 const std::vector<FileSystemURL>& file_urls) { | |
236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
237 DCHECK(handler_extension.get()); | |
238 | |
239 fileapi::ExternalFileSystemBackend* backend = | |
240 file_system_context_handler->external_backend(); | |
241 | |
242 FileDefinitionList file_list; | |
243 for (size_t i = 0; i < file_urls.size(); ++i) { | |
244 const FileSystemURL& url = file_urls[i]; | |
245 | |
246 // Check if this file system entry exists first. | |
247 base::PlatformFileInfo file_info; | |
248 | |
249 base::FilePath local_path = url.path(); | |
250 base::FilePath virtual_path = url.virtual_path(); | |
251 | |
252 bool is_drive_file = url.type() == fileapi::kFileSystemTypeDrive; | |
253 DCHECK(!is_drive_file || drive::util::IsUnderDriveMountPoint(local_path)); | |
254 | |
255 // If the file is under drive mount point, there is no actual file to be | |
256 // found on the url.path(). | |
257 if (!is_drive_file) { | |
258 if (!base::PathExists(local_path) || | |
259 file_util::IsLink(local_path) || | |
260 !file_util::GetFileInfo(local_path, &file_info)) { | |
261 continue; | |
262 } | |
263 } | |
264 | |
265 // Grant access to this particular file to target extension. This will | |
266 // ensure that the target extension can access only this FS entry and | |
267 // prevent from traversing FS hierarchy upward. | |
268 backend->GrantFileAccessToExtension( | |
269 handler_extension->id(), virtual_path); | |
270 | |
271 // Output values. | |
272 FileDefinition file; | |
273 file.virtual_path = virtual_path; | |
274 file.is_directory = file_info.is_directory; | |
275 file.absolute_path = local_path; | |
276 file_list.push_back(file); | |
277 } | |
278 return file_list; | |
279 } | |
280 | |
281 FileBrowserHandlerExecutor::FileBrowserHandlerExecutor( | |
282 Profile* profile, | |
283 const Extension* extension, | |
284 int32 tab_id, | |
285 const std::string& action_id) | |
286 : profile_(profile), | |
287 extension_(extension), | |
288 tab_id_(tab_id), | |
289 action_id_(action_id), | |
290 weak_ptr_factory_(this) { | |
291 } | |
292 | |
293 FileBrowserHandlerExecutor::~FileBrowserHandlerExecutor() {} | |
294 | |
295 void FileBrowserHandlerExecutor::Execute( | |
296 const std::vector<FileSystemURL>& file_urls, | |
297 const file_tasks::FileTaskFinishedCallback& done) { | |
298 done_ = done; | |
299 | |
300 // Get file system context for the extension to which onExecute event will be | |
301 // sent. The file access permissions will be granted to the extension in the | |
302 // file system context for the files in |file_urls|. | |
303 util::GetFileSystemContextForExtensionId( | |
304 profile_, extension_->id())->OpenFileSystem( | |
305 Extension::GetBaseURLFromExtensionId(extension_->id()).GetOrigin(), | |
306 fileapi::kFileSystemTypeExternal, | |
307 fileapi::OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT, | |
308 base::Bind(&FileBrowserHandlerExecutor::DidOpenFileSystem, | |
309 weak_ptr_factory_.GetWeakPtr(), | |
310 file_urls)); | |
311 } | |
312 | |
313 void FileBrowserHandlerExecutor::DidOpenFileSystem( | |
314 const std::vector<FileSystemURL>& file_urls, | |
315 base::PlatformFileError result, | |
316 const std::string& file_system_name, | |
317 const GURL& file_system_root) { | |
318 if (result != base::PLATFORM_FILE_OK) { | |
319 ExecuteDoneOnUIThread(false); | |
320 return; | |
321 } | |
322 | |
323 scoped_refptr<fileapi::FileSystemContext> file_system_context( | |
324 util::GetFileSystemContextForExtensionId( | |
325 profile_, extension_->id())); | |
326 BrowserThread::PostTaskAndReplyWithResult( | |
327 BrowserThread::FILE, | |
328 FROM_HERE, | |
329 base::Bind(&SetupFileAccessPermissions, | |
330 file_system_context, | |
331 extension_, | |
332 file_urls), | |
333 base::Bind(&FileBrowserHandlerExecutor::ExecuteFileActionsOnUIThread, | |
334 weak_ptr_factory_.GetWeakPtr(), | |
335 file_system_name, | |
336 file_system_root)); | |
337 } | |
338 | |
339 void FileBrowserHandlerExecutor::ExecuteDoneOnUIThread(bool success) { | |
340 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
341 if (!done_.is_null()) | |
342 done_.Run(success); | |
343 delete this; | |
344 } | |
345 | |
346 void FileBrowserHandlerExecutor::ExecuteFileActionsOnUIThread( | |
347 const std::string& file_system_name, | |
348 const GURL& file_system_root, | |
349 const FileDefinitionList& file_list) { | |
350 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
351 | |
352 if (file_list.empty()) { | |
353 ExecuteDoneOnUIThread(false); | |
354 return; | |
355 } | |
356 | |
357 int handler_pid = ExtractProcessFromExtensionId(profile_, extension_->id()); | |
358 if (handler_pid <= 0 && | |
359 !extensions::BackgroundInfo::HasLazyBackgroundPage(extension_.get())) { | |
360 ExecuteDoneOnUIThread(false); | |
361 return; | |
362 } | |
363 | |
364 if (handler_pid > 0) { | |
365 SetupPermissionsAndDispatchEvent(file_system_name, file_system_root, | |
366 file_list, handler_pid, NULL); | |
367 } else { | |
368 // We have to wake the handler background page before we proceed. | |
369 extensions::LazyBackgroundTaskQueue* queue = | |
370 extensions::ExtensionSystem::Get(profile_)-> | |
371 lazy_background_task_queue(); | |
372 if (!queue->ShouldEnqueueTask(profile_, extension_.get())) { | |
373 ExecuteDoneOnUIThread(false); | |
374 return; | |
375 } | |
376 queue->AddPendingTask( | |
377 profile_, extension_->id(), | |
378 base::Bind( | |
379 &FileBrowserHandlerExecutor::SetupPermissionsAndDispatchEvent, | |
380 weak_ptr_factory_.GetWeakPtr(), | |
381 file_system_name, | |
382 file_system_root, | |
383 file_list, | |
384 handler_pid)); | |
385 } | |
386 } | |
387 | |
388 void FileBrowserHandlerExecutor::SetupPermissionsAndDispatchEvent( | |
389 const std::string& file_system_name, | |
390 const GURL& file_system_root, | |
391 const FileDefinitionList& file_list, | |
392 int handler_pid_in, | |
393 extensions::ExtensionHost* host) { | |
394 int handler_pid = host ? host->render_process_host()->GetID() : | |
395 handler_pid_in; | |
396 | |
397 if (handler_pid <= 0) { | |
398 ExecuteDoneOnUIThread(false); | |
399 return; | |
400 } | |
401 | |
402 extensions::EventRouter* event_router = | |
403 extensions::ExtensionSystem::Get(profile_)->event_router(); | |
404 if (!event_router) { | |
405 ExecuteDoneOnUIThread(false); | |
406 return; | |
407 } | |
408 | |
409 SetupHandlerHostFileAccessPermissions( | |
410 file_list, extension_.get(), handler_pid); | |
411 | |
412 scoped_ptr<ListValue> event_args(new ListValue()); | |
413 event_args->Append(new base::StringValue(action_id_)); | |
414 DictionaryValue* details = new DictionaryValue(); | |
415 event_args->Append(details); | |
416 // Get file definitions. These will be replaced with Entry instances by | |
417 // dispatchEvent() method from event_binding.js. | |
418 ListValue* file_entries = new ListValue(); | |
419 details->Set("entries", file_entries); | |
420 for (FileDefinitionList::const_iterator iter = file_list.begin(); | |
421 iter != file_list.end(); | |
422 ++iter) { | |
423 DictionaryValue* file_def = new DictionaryValue(); | |
424 file_entries->Append(file_def); | |
425 file_def->SetString("fileSystemName", file_system_name); | |
426 file_def->SetString("fileSystemRoot", file_system_root.spec()); | |
427 base::FilePath root(FILE_PATH_LITERAL("/")); | |
428 base::FilePath full_path = root.Append(iter->virtual_path); | |
429 file_def->SetString("fileFullPath", full_path.value()); | |
430 file_def->SetBoolean("fileIsDirectory", iter->is_directory); | |
431 } | |
432 | |
433 details->SetInteger("tab_id", tab_id_); | |
434 | |
435 scoped_ptr<extensions::Event> event(new extensions::Event( | |
436 "fileBrowserHandler.onExecute", event_args.Pass())); | |
437 event->restrict_to_profile = profile_; | |
438 event_router->DispatchEventToExtension(extension_->id(), event.Pass()); | |
439 | |
440 ExecuteDoneOnUIThread(true); | |
441 } | |
442 | |
443 void FileBrowserHandlerExecutor::SetupHandlerHostFileAccessPermissions( | |
444 const FileDefinitionList& file_list, | |
445 const Extension* extension, | |
446 int handler_pid) { | |
447 const FileBrowserHandler* action = FindFileBrowserHandlerForActionId( | |
448 extension_, action_id_); | |
449 for (FileDefinitionList::const_iterator iter = file_list.begin(); | |
450 iter != file_list.end(); | |
451 ++iter) { | |
452 if (!action) | |
453 continue; | |
454 if (action->CanRead()) { | |
455 content::ChildProcessSecurityPolicy::GetInstance()->GrantReadFile( | |
456 handler_pid, iter->absolute_path); | |
457 } | |
458 if (action->CanWrite()) { | |
459 content::ChildProcessSecurityPolicy::GetInstance()-> | |
460 GrantCreateReadWriteFile(handler_pid, iter->absolute_path); | |
461 } | |
462 } | |
463 } | |
464 | |
465 // Returns true if |extension_id| and |action_id| indicate that the file | |
466 // currently being handled should be opened with the browser. This function | |
467 // is used to handle certain action IDs of the file manager. | |
468 bool ShouldBeOpenedWithBrowser(const std::string& extension_id, | |
469 const std::string& action_id) { | |
470 | |
471 return (extension_id == kFileManagerAppId && | |
472 (action_id == "view-pdf" || | |
473 action_id == "view-swf" || | |
474 action_id == "view-in-browser" || | |
475 action_id == "install-crx" || | |
476 action_id == "open-hosted-generic" || | |
477 action_id == "open-hosted-gdoc" || | |
478 action_id == "open-hosted-gsheet" || | |
479 action_id == "open-hosted-gslides")); | |
480 } | |
481 | |
482 // Opens the files specified by |file_urls| with the browser for |profile|. | |
483 // Returns true on success. It's a failure if no files are opened. | |
484 bool OpenFilesWithBrowser(Profile* profile, | |
485 const std::vector<FileSystemURL>& file_urls) { | |
486 int num_opened = 0; | |
487 for (size_t i = 0; i < file_urls.size(); ++i) { | |
488 const FileSystemURL& file_url = file_urls[i]; | |
489 if (chromeos::FileSystemBackend::CanHandleURL(file_url)) { | |
490 const base::FilePath& file_path = file_url.path(); | |
491 num_opened += util::OpenFileWithBrowser(profile, file_path); | |
492 } | |
493 } | |
494 return num_opened > 0; | |
495 } | |
496 | |
497 } // namespace | |
498 | |
499 bool ExecuteFileBrowserHandler( | |
500 Profile* profile, | |
501 const Extension* extension, | |
502 int32 tab_id, | |
503 const std::string& action_id, | |
504 const std::vector<FileSystemURL>& file_urls, | |
505 const file_tasks::FileTaskFinishedCallback& done) { | |
506 // Forbid calling undeclared handlers. | |
507 if (!FindFileBrowserHandlerForActionId(extension, action_id)) | |
508 return false; | |
509 | |
510 // Some action IDs of the file manager's file browser handlers require the | |
511 // files to be directly opened with the browser. | |
512 if (ShouldBeOpenedWithBrowser(extension->id(), action_id)) { | |
513 return OpenFilesWithBrowser(profile, file_urls); | |
514 } | |
515 | |
516 // The executor object will be self deleted on completion. | |
517 (new FileBrowserHandlerExecutor( | |
518 profile, extension, tab_id, action_id))->Execute(file_urls, done); | |
519 return true; | |
520 } | |
521 | |
522 bool IsFallbackFileBrowserHandler(const file_tasks::TaskDescriptor& task) { | |
523 return (task.task_type == file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER && | |
524 (task.app_id == kFileManagerAppId || | |
525 task.app_id == extension_misc::kQuickOfficeComponentExtensionId || | |
526 task.app_id == extension_misc::kQuickOfficeDevExtensionId || | |
527 task.app_id == extension_misc::kQuickOfficeExtensionId)); | |
528 } | |
529 | |
530 FileBrowserHandlerList FindDefaultFileBrowserHandlers( | |
531 const PrefService& pref_service, | |
532 const std::vector<base::FilePath>& file_list, | |
533 const FileBrowserHandlerList& common_handlers) { | |
534 FileBrowserHandlerList default_handlers; | |
535 | |
536 std::set<std::string> default_ids; | |
537 for (std::vector<base::FilePath>::const_iterator it = file_list.begin(); | |
538 it != file_list.end(); ++it) { | |
539 std::string task_id = file_tasks::GetDefaultTaskIdFromPrefs( | |
540 pref_service, "", it->Extension()); | |
541 if (!task_id.empty()) | |
542 default_ids.insert(task_id); | |
543 } | |
544 | |
545 const FileBrowserHandler* fallback_handler = NULL; | |
546 // Convert the default task IDs collected above to one of the handler pointers | |
547 // from common_handlers. | |
548 for (size_t i = 0; i < common_handlers.size(); ++i) { | |
549 const FileBrowserHandler* handler = common_handlers[i]; | |
550 const file_tasks::TaskDescriptor task_descriptor( | |
551 handler->extension_id(), | |
552 file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER, | |
553 handler->id()); | |
554 const std::string task_id = | |
555 file_tasks::TaskDescriptorToId(task_descriptor); | |
556 std::set<std::string>::iterator default_iter = default_ids.find(task_id); | |
557 if (default_iter != default_ids.end()) { | |
558 default_handlers.push_back(handler); | |
559 continue; | |
560 } | |
561 | |
562 // Remember the first fallback handler. | |
563 if (!fallback_handler && IsFallbackFileBrowserHandler(task_descriptor)) | |
564 fallback_handler = handler; | |
565 } | |
566 | |
567 // If there are no default handlers found, use fallback as default. | |
568 if (fallback_handler && default_handlers.empty()) | |
569 default_handlers.push_back(fallback_handler); | |
570 | |
571 return default_handlers; | |
572 } | |
573 | |
574 FileBrowserHandlerList FindCommonFileBrowserHandlers( | |
575 Profile* profile, | |
576 const std::vector<GURL>& file_list) { | |
577 FileBrowserHandlerList common_handlers; | |
578 for (std::vector<GURL>::const_iterator it = file_list.begin(); | |
579 it != file_list.end(); ++it) { | |
580 FileBrowserHandlerList handlers = | |
581 FindFileBrowserHandlersForURL(profile, *it); | |
582 // If there is nothing to do for one file, the intersection of handlers | |
583 // for all files will be empty at the end, so no need to check further. | |
584 if (handlers.empty()) | |
585 return FileBrowserHandlerList(); | |
586 | |
587 // For the very first file, just copy all the elements. | |
588 if (it == file_list.begin()) { | |
589 common_handlers = handlers; | |
590 } else { | |
591 // For all additional files, find intersection between the accumulated and | |
592 // file specific set. | |
593 FileBrowserHandlerList intersection; | |
594 std::set_intersection(common_handlers.begin(), common_handlers.end(), | |
595 handlers.begin(), handlers.end(), | |
596 std::back_inserter(intersection)); | |
597 common_handlers = intersection; | |
598 if (common_handlers.empty()) | |
599 return FileBrowserHandlerList(); | |
600 } | |
601 } | |
602 | |
603 // "watch" and "gallery" are defined in the file manager's manifest.json. | |
604 FileBrowserHandlerList::iterator watch_iter = | |
605 FindFileBrowserHandlerForExtensionIdAndActionId( | |
606 &common_handlers, kFileManagerAppId, "watch"); | |
607 FileBrowserHandlerList::iterator gallery_iter = | |
608 FindFileBrowserHandlerForExtensionIdAndActionId( | |
609 &common_handlers, kFileManagerAppId, "gallery"); | |
610 if (watch_iter != common_handlers.end() && | |
611 gallery_iter != common_handlers.end()) { | |
612 // Both "watch" and "gallery" actions are applicable which means that the | |
613 // selection is all videos. Showing them both is confusing, so we only keep | |
614 // the one that makes more sense ("watch" for single selection, "gallery" | |
615 // for multiple selection). | |
616 if (file_list.size() == 1) | |
617 common_handlers.erase(gallery_iter); | |
618 else | |
619 common_handlers.erase(watch_iter); | |
620 } | |
621 | |
622 return common_handlers; | |
623 } | |
624 | |
625 const FileBrowserHandler* FindFileBrowserHandlerForURLAndPath( | |
626 Profile* profile, | |
627 const GURL& url, | |
628 const base::FilePath& file_path) { | |
629 std::vector<GURL> file_urls; | |
630 file_urls.push_back(url); | |
631 | |
632 FileBrowserHandlerList common_handlers = | |
633 FindCommonFileBrowserHandlers(profile, file_urls); | |
634 if (common_handlers.empty()) | |
635 return NULL; | |
636 | |
637 std::vector<base::FilePath> file_paths; | |
638 file_paths.push_back(file_path); | |
639 | |
640 FileBrowserHandlerList default_handlers = | |
641 FindDefaultFileBrowserHandlers(*profile->GetPrefs(), | |
642 file_paths, | |
643 common_handlers); | |
644 | |
645 // If there's none, or more than one, then we don't have a canonical default. | |
646 if (!default_handlers.empty()) { | |
647 // There should not be multiple default handlers for a single URL. | |
648 DCHECK_EQ(1u, default_handlers.size()); | |
649 | |
650 return *default_handlers.begin(); | |
651 } | |
652 | |
653 // If there are no default handlers, use first handler in the list (file | |
654 // manager does the same in this situation). TODO(tbarzic): This is not so | |
655 // optimal behaviour. | |
656 return *common_handlers.begin(); | |
657 } | |
658 | |
659 } // namespace file_browser_handlers | |
660 } // namespace file_manager | |
OLD | NEW |