Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / file_manager / file_tasks.cc
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/file_manager/file_tasks.h"
6
7 #include "apps/launcher.h"
8 #include "base/bind.h"
9 #include "base/command_line.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/prefs/scoped_user_pref_update.h"
12 #include "base/strings/stringprintf.h"
13 #include "chrome/browser/chromeos/drive/file_system_util.h"
14 #include "chrome/browser/chromeos/drive/file_task_executor.h"
15 #include "chrome/browser/chromeos/file_manager/app_id.h"
16 #include "chrome/browser/chromeos/file_manager/file_browser_handlers.h"
17 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
18 #include "chrome/browser/chromeos/file_manager/open_util.h"
19 #include "chrome/browser/drive/drive_api_util.h"
20 #include "chrome/browser/drive/drive_app_registry.h"
21 #include "chrome/browser/extensions/extension_tab_util.h"
22 #include "chrome/browser/extensions/extension_util.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
25 #include "chrome/common/extensions/api/file_browser_handlers/file_browser_handler.h"
26 #include "chrome/common/extensions/api/file_browser_private.h"
27 #include "chrome/common/extensions/extension_constants.h"
28 #include "chrome/common/pref_names.h"
29 #include "chromeos/chromeos_switches.h"
30 #include "extensions/browser/extension_host.h"
31 #include "extensions/browser/extension_registry.h"
32 #include "extensions/browser/extension_system.h"
33 #include "extensions/browser/extension_util.h"
34 #include "extensions/common/constants.h"
35 #include "extensions/common/extension_set.h"
36 #include "webkit/browser/fileapi/file_system_url.h"
37
38 using extensions::Extension;
39 using extensions::app_file_handler_util::FindFileHandlersForFiles;
40 using fileapi::FileSystemURL;
41
42 namespace file_manager {
43 namespace file_tasks {
44
45 namespace {
46
47 // The values "file" and "app" are confusing, but cannot be changed easily as
48 // these are used in default task IDs stored in preferences.
49 const char kFileBrowserHandlerTaskType[] = "file";
50 const char kFileHandlerTaskType[] = "app";
51 const char kDriveAppTaskType[] = "drive";
52
53 // Drive apps always use the action ID.
54 const char kDriveAppActionID[] = "open-with";
55
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;
67   }
68   NOTREACHED();
69   return "";
70 }
71
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;
81 }
82
83 // Legacy Drive task extension prefix, used by CrackTaskID.
84 const char kDriveTaskExtensionPrefix[] = "drive-app:";
85 const size_t kDriveTaskExtensionPrefixLength =
86     arraysize(kDriveTaskExtensionPrefix) - 1;
87
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 (drive::util::HasHostedDocumentExtension(iter->first))
93       return true;
94   }
95   return false;
96 }
97
98 // Leaves tasks handled by the file manger itself as is and removes all others.
99 void KeepOnlyFileManagerInternalTasks(std::vector<FullTaskDescriptor>* tasks) {
100   std::vector<FullTaskDescriptor> filtered;
101   for (size_t i = 0; i < tasks->size(); ++i) {
102     if ((*tasks)[i].task_descriptor().app_id == kFileManagerAppId)
103       filtered.push_back((*tasks)[i]);
104   }
105   tasks->swap(filtered);
106 }
107
108 void ChooseSuitableGalleryHandler(std::vector<FullTaskDescriptor>* task_list) {
109   const bool disable_new_gallery =
110       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
111           chromeos::switches::kFileManagerEnableNewGallery) == "false";
112   std::vector<FullTaskDescriptor>::iterator it = task_list->begin();
113   while (it != task_list->end()) {
114     if (disable_new_gallery) {
115       if (it->task_descriptor().app_id == kGalleryAppId)
116         it = task_list->erase(it);
117       else
118         ++it;
119     } else {
120       if (it->task_descriptor().app_id == kFileManagerAppId &&
121           it->task_descriptor().action_id == "gallery") {
122         it = task_list->erase(it);
123       } else {
124         ++it;
125       }
126     }
127   }
128 }
129
130 // Returns true if the given task is a handler by built-in apps like Files.app
131 // itself or QuickOffice etc. They are used as the initial default app.
132 bool IsFallbackFileHandler(const file_tasks::TaskDescriptor& task) {
133   if (task.task_type != file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER &&
134       task.task_type != file_tasks::TASK_TYPE_FILE_HANDLER)
135     return false;
136
137   const char* kBuiltInApps[] = {
138     kFileManagerAppId,
139     kVideoPlayerAppId,
140     kGalleryAppId,
141     extension_misc::kQuickOfficeComponentExtensionId,
142     extension_misc::kQuickOfficeInternalExtensionId,
143     extension_misc::kQuickOfficeExtensionId,
144   };
145
146   for (size_t i = 0; i < arraysize(kBuiltInApps); ++i) {
147     if (task.app_id == kBuiltInApps[i])
148       return true;
149   }
150   return false;
151 }
152
153 }  // namespace
154
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) {
164 }
165
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;
172
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);
180     }
181   }
182
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 = base::StringToLowerASCII(*iter);
191       mime_type_pref->SetWithoutPathExpansion(lower_suffix, value);
192     }
193   }
194 }
195
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;
211     }
212   }
213
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 = base::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;
223 }
224
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());
232 }
233
234 std::string TaskDescriptorToId(const TaskDescriptor& task_descriptor) {
235   return MakeTaskID(task_descriptor.app_id,
236                     task_descriptor.task_type,
237                     task_descriptor.action_id);
238 }
239
240 bool ParseTaskID(const std::string& task_id, TaskDescriptor* task) {
241   DCHECK(task);
242
243   std::vector<std::string> result;
244   int count = Tokenize(task_id, std::string("|"), &result);
245
246   // Parse a legacy task ID that only contain two parts. Drive tasks are
247   // identified by a prefix "drive-app:" on the extension ID. The legacy task
248   // IDs can be stored in preferences.
249   if (count == 2) {
250     if (StartsWithASCII(result[0], kDriveTaskExtensionPrefix, true)) {
251       task->task_type = TASK_TYPE_DRIVE_APP;
252       task->app_id = result[0].substr(kDriveTaskExtensionPrefixLength);
253     } else {
254       task->task_type = TASK_TYPE_FILE_BROWSER_HANDLER;
255       task->app_id = result[0];
256     }
257
258     task->action_id = result[1];
259
260     return true;
261   }
262
263   if (count != 3)
264     return false;
265
266   TaskType task_type = StringToTaskType(result[1]);
267   if (task_type == TASK_TYPE_UNKNOWN)
268     return false;
269
270   task->app_id = result[0];
271   task->task_type = task_type;
272   task->action_id = result[2];
273
274   return true;
275 }
276
277 bool ExecuteFileTask(Profile* profile,
278                      const GURL& source_url,
279                      const TaskDescriptor& task,
280                      const std::vector<FileSystemURL>& file_urls,
281                      const FileTaskFinishedCallback& done) {
282   // drive::FileTaskExecutor is responsible to handle drive tasks.
283   if (task.task_type == TASK_TYPE_DRIVE_APP) {
284     DCHECK_EQ(kDriveAppActionID, task.action_id);
285     drive::FileTaskExecutor* executor =
286         new drive::FileTaskExecutor(profile, task.app_id);
287     executor->Execute(file_urls, done);
288     return true;
289   }
290
291   // Get the extension.
292   const Extension* extension = extensions::ExtensionRegistry::Get(
293       profile)->enabled_extensions().GetByID(task.app_id);
294   if (!extension)
295     return false;
296
297   // Execute the task.
298   if (task.task_type == TASK_TYPE_FILE_BROWSER_HANDLER) {
299     return file_browser_handlers::ExecuteFileBrowserHandler(
300         profile,
301         extension,
302         task.action_id,
303         file_urls,
304         done);
305   } else if (task.task_type == TASK_TYPE_FILE_HANDLER) {
306     std::vector<base::FilePath> paths;
307     for (size_t i = 0; i != file_urls.size(); ++i)
308       paths.push_back(file_urls[i].path());
309     apps::LaunchPlatformAppWithFileHandler(
310         profile, extension, task.action_id, paths);
311     if (!done.is_null())
312       done.Run(extensions::api::file_browser_private::TASK_RESULT_MESSAGE_SENT);
313     return true;
314   }
315   NOTREACHED();
316   return false;
317 }
318
319 void FindDriveAppTasks(
320     const drive::DriveAppRegistry& drive_app_registry,
321     const PathAndMimeTypeSet& path_mime_set,
322     std::vector<FullTaskDescriptor>* result_list) {
323   DCHECK(result_list);
324
325   bool is_first = true;
326   typedef std::map<std::string, drive::DriveAppInfo> DriveAppInfoMap;
327   DriveAppInfoMap drive_app_map;
328
329   for (PathAndMimeTypeSet::const_iterator it = path_mime_set.begin();
330        it != path_mime_set.end(); ++it) {
331     const base::FilePath& file_path = it->first;
332     const std::string& mime_type = it->second;
333     // Return immediately if a file not on Drive is found, as Drive app tasks
334     // work only if all files are on Drive.
335     if (!drive::util::IsUnderDriveMountPoint(file_path))
336       return;
337
338     std::vector<drive::DriveAppInfo> app_info_list;
339     drive_app_registry.GetAppsForFile(file_path.Extension(),
340                                       mime_type,
341                                       &app_info_list);
342
343     if (is_first) {
344       // For the first file, we store all the info.
345       for (size_t j = 0; j < app_info_list.size(); ++j)
346         drive_app_map[app_info_list[j].app_id] = app_info_list[j];
347     } else {
348       // For remaining files, take the intersection with the current
349       // result, based on the app id.
350       std::set<std::string> app_id_set;
351       for (size_t j = 0; j < app_info_list.size(); ++j)
352         app_id_set.insert(app_info_list[j].app_id);
353       for (DriveAppInfoMap::iterator iter = drive_app_map.begin();
354            iter != drive_app_map.end();) {
355         if (app_id_set.count(iter->first) == 0) {
356           drive_app_map.erase(iter++);
357         } else {
358           ++iter;
359         }
360       }
361     }
362
363     is_first = false;
364   }
365
366   for (DriveAppInfoMap::const_iterator iter = drive_app_map.begin();
367        iter != drive_app_map.end(); ++iter) {
368     const drive::DriveAppInfo& app_info = iter->second;
369     TaskDescriptor descriptor(app_info.app_id,
370                               TASK_TYPE_DRIVE_APP,
371                               kDriveAppActionID);
372     GURL icon_url = drive::util::FindPreferredIcon(
373         app_info.app_icons,
374         drive::util::kPreferredIconSize);
375     result_list->push_back(
376         FullTaskDescriptor(descriptor,
377                            app_info.app_name,
378                            icon_url,
379                            false /* is_default */));
380   }
381 }
382
383 void FindFileHandlerTasks(
384     Profile* profile,
385     const PathAndMimeTypeSet& path_mime_set,
386     std::vector<FullTaskDescriptor>* result_list) {
387   DCHECK(!path_mime_set.empty());
388   DCHECK(result_list);
389
390   const extensions::ExtensionSet& enabled_extensions =
391       extensions::ExtensionRegistry::Get(profile)->enabled_extensions();
392   for (extensions::ExtensionSet::const_iterator iter =
393            enabled_extensions.begin();
394        iter != enabled_extensions.end();
395        ++iter) {
396     const Extension* extension = iter->get();
397
398     // We don't support using hosted apps to open files.
399     if (!extension->is_platform_app())
400       continue;
401
402     // Ephemeral apps cannot be file handlers.
403     if (extensions::util::IsEphemeralApp(extension->id(), profile))
404       continue;
405
406     if (profile->IsOffTheRecord() &&
407         !extensions::util::IsIncognitoEnabled(extension->id(), profile))
408       continue;
409
410     typedef std::vector<const extensions::FileHandlerInfo*> FileHandlerList;
411     FileHandlerList file_handlers =
412         FindFileHandlersForFiles(*extension, path_mime_set);
413     if (file_handlers.empty())
414       continue;
415
416     // Only show the first matching handler from each app.
417     const extensions::FileHandlerInfo* file_handler = file_handlers.front();
418     std::string task_id = file_tasks::MakeTaskID(
419         extension->id(), file_tasks::TASK_TYPE_FILE_HANDLER, file_handler->id);
420
421     GURL best_icon = extensions::ExtensionIconSource::GetIconURL(
422         extension,
423         drive::util::kPreferredIconSize,
424         ExtensionIconSet::MATCH_BIGGER,
425         false,  // grayscale
426         NULL);  // exists
427
428     result_list->push_back(
429         FullTaskDescriptor(TaskDescriptor(extension->id(),
430                                           file_tasks::TASK_TYPE_FILE_HANDLER,
431                                           file_handler->id),
432                            extension->name(),
433                            best_icon,
434                            false /* is_default */));
435   }
436 }
437
438 void FindFileBrowserHandlerTasks(
439     Profile* profile,
440     const std::vector<GURL>& file_urls,
441     std::vector<FullTaskDescriptor>* result_list) {
442   DCHECK(!file_urls.empty());
443   DCHECK(result_list);
444
445   file_browser_handlers::FileBrowserHandlerList common_tasks =
446       file_browser_handlers::FindFileBrowserHandlers(profile, file_urls);
447   if (common_tasks.empty())
448     return;
449
450   const extensions::ExtensionSet& enabled_extensions =
451       extensions::ExtensionRegistry::Get(profile)->enabled_extensions();
452   for (file_browser_handlers::FileBrowserHandlerList::const_iterator iter =
453            common_tasks.begin();
454        iter != common_tasks.end();
455        ++iter) {
456     const FileBrowserHandler* handler = *iter;
457     const std::string extension_id = handler->extension_id();
458     const Extension* extension = enabled_extensions.GetByID(extension_id);
459     DCHECK(extension);
460
461     // TODO(zelidrag): Figure out how to expose icon URL that task defined in
462     // manifest instead of the default extension icon.
463     const GURL icon_url = extensions::ExtensionIconSource::GetIconURL(
464         extension,
465         extension_misc::EXTENSION_ICON_BITTY,
466         ExtensionIconSet::MATCH_BIGGER,
467         false,  // grayscale
468         NULL);  // exists
469
470     result_list->push_back(FullTaskDescriptor(
471         TaskDescriptor(extension_id,
472                        file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER,
473                        handler->id()),
474         handler->title(),
475         icon_url,
476         false /* is_default */));
477   }
478 }
479
480 void FindAllTypesOfTasks(
481     Profile* profile,
482     const drive::DriveAppRegistry* drive_app_registry,
483     const PathAndMimeTypeSet& path_mime_set,
484     const std::vector<GURL>& file_urls,
485     std::vector<FullTaskDescriptor>* result_list) {
486   DCHECK(profile);
487   DCHECK(result_list);
488
489   // Find Drive app tasks, if the drive app registry is present.
490   if (drive_app_registry)
491     FindDriveAppTasks(*drive_app_registry, path_mime_set, result_list);
492
493   // Find and append file handler tasks. We know there aren't duplicates
494   // because Drive apps and platform apps are entirely different kinds of
495   // tasks.
496   FindFileHandlerTasks(profile, path_mime_set, result_list);
497
498   // Find and append file browser handler tasks. We know there aren't
499   // duplicates because "file_browser_handlers" and "file_handlers" shouldn't
500   // be used in the same manifest.json.
501   FindFileBrowserHandlerTasks(profile, file_urls, result_list);
502
503   // Google documents can only be handled by internal handlers.
504   if (ContainsGoogleDocument(path_mime_set))
505     KeepOnlyFileManagerInternalTasks(result_list);
506
507   ChooseSuitableGalleryHandler(result_list);
508   ChooseAndSetDefaultTask(*profile->GetPrefs(), path_mime_set, result_list);
509 }
510
511 void ChooseAndSetDefaultTask(const PrefService& pref_service,
512                              const PathAndMimeTypeSet& path_mime_set,
513                              std::vector<FullTaskDescriptor>* tasks) {
514   // Collect the task IDs of default tasks from the preferences into a set.
515   std::set<std::string> default_task_ids;
516   for (PathAndMimeTypeSet::const_iterator it = path_mime_set.begin();
517        it != path_mime_set.end(); ++it) {
518     const base::FilePath& file_path = it->first;
519     const std::string& mime_type = it->second;
520     std::string task_id = file_tasks::GetDefaultTaskIdFromPrefs(
521         pref_service, mime_type, file_path.Extension());
522     default_task_ids.insert(task_id);
523   }
524
525   // Go through all the tasks from the beginning and see if there is any
526   // default task. If found, pick and set it as default and return.
527   for (size_t i = 0; i < tasks->size(); ++i) {
528     FullTaskDescriptor* task = &tasks->at(i);
529     DCHECK(!task->is_default());
530     const std::string task_id = TaskDescriptorToId(task->task_descriptor());
531     if (ContainsKey(default_task_ids, task_id)) {
532       task->set_is_default(true);
533       return;
534     }
535   }
536
537   // No default tasks found. If there is any fallback file browser handler,
538   // make it as default task, so it's selected by default.
539   for (size_t i = 0; i < tasks->size(); ++i) {
540     FullTaskDescriptor* task = &tasks->at(i);
541     DCHECK(!task->is_default());
542     if (IsFallbackFileHandler(task->task_descriptor())) {
543       task->set_is_default(true);
544       return;
545     }
546   }
547 }
548
549 }  // namespace file_tasks
550 }  // namespace file_manager