Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / browser / chromeos / file_manager / file_tasks.cc
blob91eed8006c8a647409429c57b71219e73498f9a2
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/file_manager/file_tasks.h"
7 #include "apps/launcher.h"
8 #include "base/bind.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/strings/stringprintf.h"
11 #include "chrome/browser/chromeos/drive/file_system_util.h"
12 #include "chrome/browser/chromeos/drive/file_task_executor.h"
13 #include "chrome/browser/chromeos/file_manager/app_id.h"
14 #include "chrome/browser/chromeos/file_manager/file_browser_handlers.h"
15 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
16 #include "chrome/browser/chromeos/file_manager/open_util.h"
17 #include "chrome/browser/chromeos/fileapi/file_system_backend.h"
18 #include "chrome/browser/drive/drive_app_registry.h"
19 #include "chrome/browser/extensions/extension_service.h"
20 #include "chrome/browser/extensions/extension_tab_util.h"
21 #include "chrome/browser/extensions/extension_util.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
24 #include "chrome/common/extensions/api/file_browser_handlers/file_browser_handler.h"
25 #include "chrome/common/extensions/api/file_browser_private.h"
26 #include "chrome/common/pref_names.h"
27 #include "extensions/browser/extension_host.h"
28 #include "extensions/browser/extension_system.h"
29 #include "extensions/common/extension_set.h"
30 #include "google_apis/drive/gdata_wapi_parser.h"
31 #include "webkit/browser/fileapi/file_system_context.h"
32 #include "webkit/browser/fileapi/file_system_url.h"
34 using extensions::Extension;
35 using extensions::app_file_handler_util::FindFileHandlersForFiles;
36 using fileapi::FileSystemURL;
38 namespace file_manager {
39 namespace file_tasks {
41 namespace {
43 // The values "file" and "app" are confusing, but cannot be changed easily as
44 // these are used in default task IDs stored in preferences.
46 // TODO(satorux): We should rename them to "file_browser_handler" and
47 // "file_handler" respectively when switching from preferences to
48 // chrome.storage crbug.com/267359
49 const char kFileBrowserHandlerTaskType[] = "file";
50 const char kFileHandlerTaskType[] = "app";
51 const char kDriveAppTaskType[] = "drive";
53 // Drive apps always use the action ID.
54 const char kDriveAppActionID[] = "open-with";
56 // Converts a TaskType to a string.
57 std::string TaskTypeToString(TaskType task_type) {
58 switch (task_type) {
59 case TASK_TYPE_FILE_BROWSER_HANDLER:
60 return kFileBrowserHandlerTaskType;
61 case TASK_TYPE_FILE_HANDLER:
62 return kFileHandlerTaskType;
63 case TASK_TYPE_DRIVE_APP:
64 return kDriveAppTaskType;
65 case TASK_TYPE_UNKNOWN:
66 break;
68 NOTREACHED();
69 return "";
72 // Converts a string to a TaskType. Returns TASK_TYPE_UNKNOWN on error.
73 TaskType StringToTaskType(const std::string& str) {
74 if (str == kFileBrowserHandlerTaskType)
75 return TASK_TYPE_FILE_BROWSER_HANDLER;
76 if (str == kFileHandlerTaskType)
77 return TASK_TYPE_FILE_HANDLER;
78 if (str == kDriveAppTaskType)
79 return TASK_TYPE_DRIVE_APP;
80 return TASK_TYPE_UNKNOWN;
83 // Legacy Drive task extension prefix, used by CrackTaskID.
84 const char kDriveTaskExtensionPrefix[] = "drive-app:";
85 const size_t kDriveTaskExtensionPrefixLength =
86 arraysize(kDriveTaskExtensionPrefix) - 1;
88 // Returns true if path_mime_set contains a Google document.
89 bool ContainsGoogleDocument(const PathAndMimeTypeSet& path_mime_set) {
90 for (PathAndMimeTypeSet::const_iterator iter = path_mime_set.begin();
91 iter != path_mime_set.end(); ++iter) {
92 if (google_apis::ResourceEntry::ClassifyEntryKindByFileExtension(
93 iter->first) &
94 google_apis::ResourceEntry::KIND_OF_GOOGLE_DOCUMENT) {
95 return true;
98 return false;
101 // Leaves tasks handled by the file manger itself as is and removes all others.
102 void KeepOnlyFileManagerInternalTasks(std::vector<FullTaskDescriptor>* tasks) {
103 std::vector<FullTaskDescriptor> filtered;
104 for (size_t i = 0; i < tasks->size(); ++i) {
105 if ((*tasks)[i].task_descriptor().app_id == kFileManagerAppId)
106 filtered.push_back((*tasks)[i]);
108 tasks->swap(filtered);
111 // Finds a task that matches |app_id| and |action_id| from |task_list|.
112 // Returns a mutable iterator to the handler if found. Returns task_list->end()
113 // if not found.
114 std::vector<FullTaskDescriptor>::iterator
115 FindTaskForAppIdAndActionId(
116 std::vector<FullTaskDescriptor>* task_list,
117 const std::string& app_id,
118 const std::string& action_id) {
119 DCHECK(task_list);
121 std::vector<FullTaskDescriptor>::iterator iter = task_list->begin();
122 while (iter != task_list->end() &&
123 !(iter->task_descriptor().app_id == app_id &&
124 iter->task_descriptor().action_id == action_id)) {
125 ++iter;
127 return iter;
130 // Chooses a suitable video handeler and removes other internal video hander.
131 // Both "watch" and "gallery-video" actions are applicable which means that the
132 // selection is all videos. Showing them both is confusing, so we only keep
133 // the one that makes more sense ("watch" for single selection, "gallery"
134 // for multiple selection).
135 void ChooseSuitableVideoHandler(
136 const std::vector<GURL>& file_urls,
137 std::vector<FullTaskDescriptor>* task_list) {
138 std::vector<FullTaskDescriptor>::iterator video_player_iter =
139 FindTaskForAppIdAndActionId(task_list, kVideoPlayerAppId, "video");
140 std::vector<FullTaskDescriptor>::iterator gallery_video_iter =
141 FindTaskForAppIdAndActionId(
142 task_list, kFileManagerAppId, "gallery-video");
144 if (video_player_iter != task_list->end() &&
145 gallery_video_iter != task_list->end()) {
146 if (file_urls.size() == 1)
147 task_list->erase(gallery_video_iter);
148 else
149 task_list->erase(video_player_iter);
153 } // namespace
155 FullTaskDescriptor::FullTaskDescriptor(
156 const TaskDescriptor& task_descriptor,
157 const std::string& task_title,
158 const GURL& icon_url,
159 bool is_default)
160 : task_descriptor_(task_descriptor),
161 task_title_(task_title),
162 icon_url_(icon_url),
163 is_default_(is_default){
166 void UpdateDefaultTask(PrefService* pref_service,
167 const std::string& task_id,
168 const std::set<std::string>& suffixes,
169 const std::set<std::string>& mime_types) {
170 if (!pref_service)
171 return;
173 if (!mime_types.empty()) {
174 DictionaryPrefUpdate mime_type_pref(pref_service,
175 prefs::kDefaultTasksByMimeType);
176 for (std::set<std::string>::const_iterator iter = mime_types.begin();
177 iter != mime_types.end(); ++iter) {
178 base::StringValue* value = new base::StringValue(task_id);
179 mime_type_pref->SetWithoutPathExpansion(*iter, value);
183 if (!suffixes.empty()) {
184 DictionaryPrefUpdate mime_type_pref(pref_service,
185 prefs::kDefaultTasksBySuffix);
186 for (std::set<std::string>::const_iterator iter = suffixes.begin();
187 iter != suffixes.end(); ++iter) {
188 base::StringValue* value = new base::StringValue(task_id);
189 // Suffixes are case insensitive.
190 std::string lower_suffix = StringToLowerASCII(*iter);
191 mime_type_pref->SetWithoutPathExpansion(lower_suffix, value);
196 std::string GetDefaultTaskIdFromPrefs(const PrefService& pref_service,
197 const std::string& mime_type,
198 const std::string& suffix) {
199 VLOG(1) << "Looking for default for MIME type: " << mime_type
200 << " and suffix: " << suffix;
201 std::string task_id;
202 if (!mime_type.empty()) {
203 const base::DictionaryValue* mime_task_prefs =
204 pref_service.GetDictionary(prefs::kDefaultTasksByMimeType);
205 DCHECK(mime_task_prefs);
206 LOG_IF(ERROR, !mime_task_prefs) << "Unable to open MIME type prefs";
207 if (mime_task_prefs &&
208 mime_task_prefs->GetStringWithoutPathExpansion(mime_type, &task_id)) {
209 VLOG(1) << "Found MIME default handler: " << task_id;
210 return task_id;
214 const base::DictionaryValue* suffix_task_prefs =
215 pref_service.GetDictionary(prefs::kDefaultTasksBySuffix);
216 DCHECK(suffix_task_prefs);
217 LOG_IF(ERROR, !suffix_task_prefs) << "Unable to open suffix prefs";
218 std::string lower_suffix = StringToLowerASCII(suffix);
219 if (suffix_task_prefs)
220 suffix_task_prefs->GetStringWithoutPathExpansion(lower_suffix, &task_id);
221 VLOG_IF(1, !task_id.empty()) << "Found suffix default handler: " << task_id;
222 return task_id;
225 std::string MakeTaskID(const std::string& app_id,
226 TaskType task_type,
227 const std::string& action_id) {
228 return base::StringPrintf("%s|%s|%s",
229 app_id.c_str(),
230 TaskTypeToString(task_type).c_str(),
231 action_id.c_str());
234 std::string MakeDriveAppTaskId(const std::string& app_id) {
235 return MakeTaskID(app_id, TASK_TYPE_DRIVE_APP, kDriveAppActionID);
238 std::string TaskDescriptorToId(const TaskDescriptor& task_descriptor) {
239 return MakeTaskID(task_descriptor.app_id,
240 task_descriptor.task_type,
241 task_descriptor.action_id);
244 bool ParseTaskID(const std::string& task_id, TaskDescriptor* task) {
245 DCHECK(task);
247 std::vector<std::string> result;
248 int count = Tokenize(task_id, std::string("|"), &result);
250 // Parse a legacy task ID that only contain two parts. Drive tasks are
251 // identified by a prefix "drive-app:" on the extension ID. The legacy task
252 // IDs can be stored in preferences.
253 // TODO(satorux): We should get rid of this code: crbug.com/267359.
254 if (count == 2) {
255 if (StartsWithASCII(result[0], kDriveTaskExtensionPrefix, true)) {
256 task->task_type = TASK_TYPE_DRIVE_APP;
257 task->app_id = result[0].substr(kDriveTaskExtensionPrefixLength);
258 } else {
259 task->task_type = TASK_TYPE_FILE_BROWSER_HANDLER;
260 task->app_id = result[0];
263 task->action_id = result[1];
265 return true;
268 if (count != 3)
269 return false;
271 TaskType task_type = StringToTaskType(result[1]);
272 if (task_type == TASK_TYPE_UNKNOWN)
273 return false;
275 task->app_id = result[0];
276 task->task_type = task_type;
277 task->action_id = result[2];
279 return true;
282 bool ExecuteFileTask(Profile* profile,
283 const GURL& source_url,
284 const TaskDescriptor& task,
285 const std::vector<FileSystemURL>& file_urls,
286 const FileTaskFinishedCallback& done) {
287 // drive::FileTaskExecutor is responsible to handle drive tasks.
288 if (task.task_type == TASK_TYPE_DRIVE_APP) {
289 DCHECK_EQ(kDriveAppActionID, task.action_id);
290 drive::FileTaskExecutor* executor =
291 new drive::FileTaskExecutor(profile, task.app_id);
292 executor->Execute(file_urls, done);
293 return true;
296 // Get the extension.
297 ExtensionService* service =
298 extensions::ExtensionSystem::Get(profile)->extension_service();
299 const Extension* extension = service ?
300 service->GetExtensionById(task.app_id, false) : NULL;
301 if (!extension)
302 return false;
304 // Execute the task.
305 if (task.task_type == TASK_TYPE_FILE_BROWSER_HANDLER) {
306 return file_browser_handlers::ExecuteFileBrowserHandler(
307 profile,
308 extension,
309 task.action_id,
310 file_urls,
311 done);
312 } else if (task.task_type == TASK_TYPE_FILE_HANDLER) {
313 for (size_t i = 0; i != file_urls.size(); ++i) {
314 apps::LaunchPlatformAppWithFileHandler(
315 profile, extension, task.action_id, file_urls[i].path());
318 if (!done.is_null())
319 done.Run(extensions::api::file_browser_private::TASK_RESULT_MESSAGE_SENT);
320 return true;
322 NOTREACHED();
323 return false;
326 void FindDriveAppTasks(
327 const drive::DriveAppRegistry& drive_app_registry,
328 const PathAndMimeTypeSet& path_mime_set,
329 std::vector<FullTaskDescriptor>* result_list) {
330 DCHECK(result_list);
332 bool is_first = true;
333 typedef std::map<std::string, drive::DriveAppInfo> DriveAppInfoMap;
334 DriveAppInfoMap drive_app_map;
336 for (PathAndMimeTypeSet::const_iterator it = path_mime_set.begin();
337 it != path_mime_set.end(); ++it) {
338 const base::FilePath& file_path = it->first;
339 const std::string& mime_type = it->second;
340 // Return immediately if a file not on Drive is found, as Drive app tasks
341 // work only if all files are on Drive.
342 if (!drive::util::IsUnderDriveMountPoint(file_path))
343 return;
345 std::vector<drive::DriveAppInfo> app_info_list;
346 drive_app_registry.GetAppsForFile(file_path.Extension(),
347 mime_type,
348 &app_info_list);
350 if (is_first) {
351 // For the first file, we store all the info.
352 for (size_t j = 0; j < app_info_list.size(); ++j)
353 drive_app_map[app_info_list[j].app_id] = app_info_list[j];
354 } else {
355 // For remaining files, take the intersection with the current
356 // result, based on the app id.
357 std::set<std::string> app_id_set;
358 for (size_t j = 0; j < app_info_list.size(); ++j)
359 app_id_set.insert(app_info_list[j].app_id);
360 for (DriveAppInfoMap::iterator iter = drive_app_map.begin();
361 iter != drive_app_map.end();) {
362 if (app_id_set.count(iter->first) == 0) {
363 drive_app_map.erase(iter++);
364 } else {
365 ++iter;
370 is_first = false;
373 for (DriveAppInfoMap::const_iterator iter = drive_app_map.begin();
374 iter != drive_app_map.end(); ++iter) {
375 const drive::DriveAppInfo& app_info = iter->second;
376 TaskDescriptor descriptor(app_info.app_id,
377 TASK_TYPE_DRIVE_APP,
378 kDriveAppActionID);
379 GURL icon_url = drive::util::FindPreferredIcon(
380 app_info.app_icons,
381 drive::util::kPreferredIconSize);
382 result_list->push_back(
383 FullTaskDescriptor(descriptor,
384 app_info.app_name,
385 icon_url,
386 false /* is_default */));
390 void FindFileHandlerTasks(
391 Profile* profile,
392 const PathAndMimeTypeSet& path_mime_set,
393 std::vector<FullTaskDescriptor>* result_list) {
394 DCHECK(!path_mime_set.empty());
395 DCHECK(result_list);
397 ExtensionService* service = profile->GetExtensionService();
398 if (!service)
399 return;
401 for (extensions::ExtensionSet::const_iterator iter =
402 service->extensions()->begin();
403 iter != service->extensions()->end();
404 ++iter) {
405 const Extension* extension = iter->get();
407 // We don't support using hosted apps to open files.
408 if (!extension->is_platform_app())
409 continue;
411 if (profile->IsOffTheRecord() &&
412 !extensions::util::IsIncognitoEnabled(extension->id(), profile))
413 continue;
415 typedef std::vector<const extensions::FileHandlerInfo*> FileHandlerList;
416 FileHandlerList file_handlers =
417 FindFileHandlersForFiles(*extension, path_mime_set);
418 if (file_handlers.empty())
419 continue;
421 for (FileHandlerList::iterator i = file_handlers.begin();
422 i != file_handlers.end(); ++i) {
423 std::string task_id = file_tasks::MakeTaskID(
424 extension->id(), file_tasks::TASK_TYPE_FILE_HANDLER, (*i)->id);
426 GURL best_icon = extensions::ExtensionIconSource::GetIconURL(
427 extension,
428 drive::util::kPreferredIconSize,
429 ExtensionIconSet::MATCH_BIGGER,
430 false, // grayscale
431 NULL); // exists
433 result_list->push_back(FullTaskDescriptor(
434 TaskDescriptor(extension->id(),
435 file_tasks::TASK_TYPE_FILE_HANDLER,
436 (*i)->id),
437 (*i)->title,
438 best_icon,
439 false /* is_default */));
444 void FindFileBrowserHandlerTasks(
445 Profile* profile,
446 const std::vector<GURL>& file_urls,
447 std::vector<FullTaskDescriptor>* result_list) {
448 DCHECK(!file_urls.empty());
449 DCHECK(result_list);
451 file_browser_handlers::FileBrowserHandlerList common_tasks =
452 file_browser_handlers::FindFileBrowserHandlers(profile, file_urls);
453 if (common_tasks.empty())
454 return;
456 ExtensionService* service =
457 extensions::ExtensionSystem::Get(profile)->extension_service();
458 for (file_browser_handlers::FileBrowserHandlerList::const_iterator iter =
459 common_tasks.begin();
460 iter != common_tasks.end();
461 ++iter) {
462 const FileBrowserHandler* handler = *iter;
463 const std::string extension_id = handler->extension_id();
464 const Extension* extension = service->GetExtensionById(extension_id, false);
465 DCHECK(extension);
467 // TODO(zelidrag): Figure out how to expose icon URL that task defined in
468 // manifest instead of the default extension icon.
469 const GURL icon_url = extensions::ExtensionIconSource::GetIconURL(
470 extension,
471 extension_misc::EXTENSION_ICON_BITTY,
472 ExtensionIconSet::MATCH_BIGGER,
473 false, // grayscale
474 NULL); // exists
476 result_list->push_back(FullTaskDescriptor(
477 TaskDescriptor(extension_id,
478 file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER,
479 handler->id()),
480 handler->title(),
481 icon_url,
482 false /* is_default */));
486 void FindAllTypesOfTasks(
487 Profile* profile,
488 const drive::DriveAppRegistry* drive_app_registry,
489 const PathAndMimeTypeSet& path_mime_set,
490 const std::vector<GURL>& file_urls,
491 std::vector<FullTaskDescriptor>* result_list) {
492 DCHECK(profile);
493 DCHECK(result_list);
495 // Find Drive app tasks, if the drive app registry is present.
496 if (drive_app_registry)
497 FindDriveAppTasks(*drive_app_registry, path_mime_set, result_list);
499 // Find and append file handler tasks. We know there aren't duplicates
500 // because Drive apps and platform apps are entirely different kinds of
501 // tasks.
502 FindFileHandlerTasks(profile, path_mime_set, result_list);
504 // Find and append file browser handler tasks. We know there aren't
505 // duplicates because "file_browser_handlers" and "file_handlers" shouldn't
506 // be used in the same manifest.json.
507 FindFileBrowserHandlerTasks(profile, file_urls, result_list);
509 // Google documents can only be handled by internal handlers.
510 if (ContainsGoogleDocument(path_mime_set))
511 KeepOnlyFileManagerInternalTasks(result_list);
513 ChooseSuitableVideoHandler(file_urls, result_list);
515 ChooseAndSetDefaultTask(*profile->GetPrefs(), path_mime_set, result_list);
518 void ChooseAndSetDefaultTask(const PrefService& pref_service,
519 const PathAndMimeTypeSet& path_mime_set,
520 std::vector<FullTaskDescriptor>* tasks) {
521 // Collect the task IDs of default tasks from the preferences into a set.
522 std::set<std::string> default_task_ids;
523 for (PathAndMimeTypeSet::const_iterator it = path_mime_set.begin();
524 it != path_mime_set.end(); ++it) {
525 const base::FilePath& file_path = it->first;
526 const std::string& mime_type = it->second;
527 std::string task_id = file_tasks::GetDefaultTaskIdFromPrefs(
528 pref_service, mime_type, file_path.Extension());
529 default_task_ids.insert(task_id);
532 // Go through all the tasks from the beginning and see if there is any
533 // default task. If found, pick and set it as default and return.
534 for (size_t i = 0; i < tasks->size(); ++i) {
535 FullTaskDescriptor* task = &tasks->at(i);
536 DCHECK(!task->is_default());
537 const std::string task_id = TaskDescriptorToId(task->task_descriptor());
538 if (ContainsKey(default_task_ids, task_id)) {
539 task->set_is_default(true);
540 return;
544 // No default tasks found. If there is any fallback file browser handler,
545 // make it as default task, so it's selected by default.
546 for (size_t i = 0; i < tasks->size(); ++i) {
547 FullTaskDescriptor* task = &tasks->at(i);
548 DCHECK(!task->is_default());
549 if (file_browser_handlers::IsFallbackFileBrowserHandler(
550 task->task_descriptor())) {
551 task->set_is_default(true);
552 return;
557 } // namespace file_tasks
558 } // namespace file_manager