1// Copyright 2013 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 <algorithm>
8#include <utility>
9
10#include "base/command_line.h"
11#include "base/prefs/pref_registry_simple.h"
12#include "base/prefs/testing_pref_service.h"
13#include "base/values.h"
14#include "chrome/browser/chromeos/drive/file_system_util.h"
15#include "chrome/browser/chromeos/file_manager/app_id.h"
16#include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
17#include "chrome/browser/chromeos/settings/cros_settings.h"
18#include "chrome/browser/chromeos/settings/device_settings_service.h"
19#include "chrome/browser/drive/drive_app_registry.h"
20#include "chrome/browser/extensions/extension_service.h"
21#include "chrome/browser/extensions/test_extension_system.h"
22#include "chrome/common/pref_names.h"
23#include "chrome/test/base/testing_profile.h"
24#include "content/public/test/test_browser_thread_bundle.h"
25#include "extensions/browser/extension_prefs.h"
26#include "extensions/browser/extension_system.h"
27#include "extensions/common/extension_builder.h"
28#include "google_apis/drive/drive_api_parser.h"
29#include "testing/gtest/include/gtest/gtest.h"
30#include "url/gurl.h"
31
32namespace file_manager {
33namespace file_tasks {
34namespace {
35
36// Registers the default task preferences. Used for testing
37// ChooseAndSetDefaultTask().
38void RegisterDefaultTaskPreferences(TestingPrefServiceSimple* pref_service) {
39  DCHECK(pref_service);
40
41  pref_service->registry()->RegisterDictionaryPref(
42      prefs::kDefaultTasksByMimeType);
43  pref_service->registry()->RegisterDictionaryPref(
44      prefs::kDefaultTasksBySuffix);
45}
46
47// Updates the default task preferences per the given dictionary values. Used
48// for testing ChooseAndSetDefaultTask.
49void UpdateDefaultTaskPreferences(TestingPrefServiceSimple* pref_service,
50                                  const base::DictionaryValue& mime_types,
51                                  const base::DictionaryValue& suffixes) {
52  DCHECK(pref_service);
53
54  pref_service->Set(prefs::kDefaultTasksByMimeType, mime_types);
55  pref_service->Set(prefs::kDefaultTasksBySuffix, suffixes);
56}
57
58}  // namespace
59
60TEST(FileManagerFileTasksTest,
61     FullTaskDescriptor_NonDriveAppWithIconAndDefault) {
62  FullTaskDescriptor full_descriptor(
63      TaskDescriptor("app-id",
64                     TASK_TYPE_FILE_BROWSER_HANDLER,
65                     "action-id"),
66      "task title",
67      GURL("http://example.com/icon.png"),
68      true /* is_default */);
69
70  const std::string task_id =
71      TaskDescriptorToId(full_descriptor.task_descriptor());
72  EXPECT_EQ("app-id|file|action-id", task_id);
73  EXPECT_EQ("http://example.com/icon.png", full_descriptor.icon_url().spec());
74  EXPECT_EQ("task title", full_descriptor.task_title());
75  EXPECT_TRUE(full_descriptor.is_default());
76}
77
78TEST(FileManagerFileTasksTest,
79     FullTaskDescriptor_DriveAppWithoutIconAndNotDefault) {
80  FullTaskDescriptor full_descriptor(
81      TaskDescriptor("app-id",
82                     TASK_TYPE_DRIVE_APP,
83                     "action-id"),
84      "task title",
85      GURL(),  // No icon URL.
86      false /* is_default */);
87
88  const std::string task_id =
89      TaskDescriptorToId(full_descriptor.task_descriptor());
90  EXPECT_EQ("app-id|drive|action-id", task_id);
91  EXPECT_TRUE(full_descriptor.icon_url().is_empty());
92  EXPECT_EQ("task title", full_descriptor.task_title());
93  EXPECT_FALSE(full_descriptor.is_default());
94}
95
96TEST(FileManagerFileTasksTest, MakeTaskID) {
97  EXPECT_EQ("app-id|file|action-id",
98            MakeTaskID("app-id", TASK_TYPE_FILE_BROWSER_HANDLER, "action-id"));
99  EXPECT_EQ("app-id|app|action-id",
100            MakeTaskID("app-id", TASK_TYPE_FILE_HANDLER, "action-id"));
101  EXPECT_EQ("app-id|drive|action-id",
102            MakeTaskID("app-id", TASK_TYPE_DRIVE_APP, "action-id"));
103}
104
105TEST(FileManagerFileTasksTest, TaskDescriptorToId) {
106  EXPECT_EQ("app-id|file|action-id",
107            TaskDescriptorToId(TaskDescriptor("app-id",
108                                              TASK_TYPE_FILE_BROWSER_HANDLER,
109                                              "action-id")));
110}
111
112TEST(FileManagerFileTasksTest, ParseTaskID_FileBrowserHandler) {
113  TaskDescriptor task;
114  EXPECT_TRUE(ParseTaskID("app-id|file|action-id", &task));
115  EXPECT_EQ("app-id", task.app_id);
116  EXPECT_EQ(TASK_TYPE_FILE_BROWSER_HANDLER, task.task_type);
117  EXPECT_EQ("action-id", task.action_id);
118}
119
120TEST(FileManagerFileTasksTest, ParseTaskID_FileHandler) {
121  TaskDescriptor task;
122  EXPECT_TRUE(ParseTaskID("app-id|app|action-id", &task));
123  EXPECT_EQ("app-id", task.app_id);
124  EXPECT_EQ(TASK_TYPE_FILE_HANDLER, task.task_type);
125  EXPECT_EQ("action-id", task.action_id);
126}
127
128TEST(FileManagerFileTasksTest, ParseTaskID_DriveApp) {
129  TaskDescriptor task;
130  EXPECT_TRUE(ParseTaskID("app-id|drive|action-id", &task));
131  EXPECT_EQ("app-id", task.app_id);
132  EXPECT_EQ(TASK_TYPE_DRIVE_APP, task.task_type);
133  EXPECT_EQ("action-id", task.action_id);
134}
135
136TEST(FileManagerFileTasksTest, ParseTaskID_Legacy) {
137  TaskDescriptor task;
138  // A legacy task ID only has two parts. The task type should be
139  // TASK_TYPE_FILE_BROWSER_HANDLER.
140  EXPECT_TRUE(ParseTaskID("app-id|action-id", &task));
141  EXPECT_EQ("app-id", task.app_id);
142  EXPECT_EQ(TASK_TYPE_FILE_BROWSER_HANDLER, task.task_type);
143  EXPECT_EQ("action-id", task.action_id);
144}
145
146TEST(FileManagerFileTasksTest, ParseTaskID_LegacyDrive) {
147  TaskDescriptor task;
148  // A legacy task ID only has two parts. For Drive app, the app ID is
149  // prefixed with "drive-app:".
150  EXPECT_TRUE(ParseTaskID("drive-app:app-id|action-id", &task));
151  EXPECT_EQ("app-id", task.app_id);
152  EXPECT_EQ(TASK_TYPE_DRIVE_APP, task.task_type);
153  EXPECT_EQ("action-id", task.action_id);
154}
155
156TEST(FileManagerFileTasksTest, ParseTaskID_Invalid) {
157  TaskDescriptor task;
158  EXPECT_FALSE(ParseTaskID("invalid", &task));
159}
160
161TEST(FileManagerFileTasksTest, ParseTaskID_UnknownTaskType) {
162  TaskDescriptor task;
163  EXPECT_FALSE(ParseTaskID("app-id|unknown|action-id", &task));
164}
165
166TEST(FileManagerFileTasksTest, FindDriveAppTasks) {
167  TestingProfile profile;
168  // For DriveAppRegistry, which checks CurrentlyOn(BrowserThread::UI).
169  content::TestBrowserThreadBundle thread_bundle;
170
171  // Foo.app can handle "text/plain" and "text/html"
172  scoped_ptr<google_apis::AppResource> foo_app(new google_apis::AppResource);
173  foo_app->set_product_id("foo_app_id");
174  foo_app->set_application_id("foo_app_id");
175  foo_app->set_name("Foo");
176  foo_app->set_object_type("foo_object_type");
177  ScopedVector<std::string> foo_mime_types;
178  foo_mime_types.push_back(new std::string("text/plain"));
179  foo_mime_types.push_back(new std::string("text/html"));
180  foo_app->set_primary_mimetypes(foo_mime_types.Pass());
181
182  // Bar.app can only handle "text/plain".
183  scoped_ptr<google_apis::AppResource> bar_app(new google_apis::AppResource);
184  bar_app->set_product_id("bar_app_id");
185  bar_app->set_application_id("bar_app_id");
186  bar_app->set_name("Bar");
187  bar_app->set_object_type("bar_object_type");
188  ScopedVector<std::string> bar_mime_types;
189  bar_mime_types.push_back(new std::string("text/plain"));
190  bar_app->set_primary_mimetypes(bar_mime_types.Pass());
191
192  // Prepare DriveAppRegistry from Foo.app and Bar.app.
193  ScopedVector<google_apis::AppResource> app_resources;
194  app_resources.push_back(foo_app.release());
195  app_resources.push_back(bar_app.release());
196  google_apis::AppList app_list;
197  app_list.set_items(app_resources.Pass());
198  drive::DriveAppRegistry drive_app_registry(NULL);
199  drive_app_registry.UpdateFromAppList(app_list);
200
201  // Find apps for a "text/plain" file. Foo.app and Bar.app should be found.
202  PathAndMimeTypeSet path_mime_set;
203  path_mime_set.insert(
204      std::make_pair(
205          drive::util::GetDriveMountPointPath(&profile).AppendASCII("foo.txt"),
206          "text/plain"));
207  std::vector<FullTaskDescriptor> tasks;
208  FindDriveAppTasks(drive_app_registry,
209                    path_mime_set,
210                    &tasks);
211  ASSERT_EQ(2U, tasks.size());
212  // Sort the app IDs, as the order is not guaranteed.
213  std::vector<std::string> app_ids;
214  app_ids.push_back(tasks[0].task_descriptor().app_id);
215  app_ids.push_back(tasks[1].task_descriptor().app_id);
216  std::sort(app_ids.begin(), app_ids.end());
217  // Confirm that both Foo.app and Bar.app are found.
218  EXPECT_EQ("bar_app_id", app_ids[0]);
219  EXPECT_EQ("foo_app_id", app_ids[1]);
220
221  // Find apps for "text/plain" and "text/html" files. Only Foo.app should be
222  // found.
223  path_mime_set.clear();
224  path_mime_set.insert(
225      std::make_pair(
226          drive::util::GetDriveMountPointPath(&profile).AppendASCII("foo.txt"),
227          "text/plain"));
228  path_mime_set.insert(
229      std::make_pair(
230          drive::util::GetDriveMountPointPath(&profile).AppendASCII("foo.html"),
231          "text/html"));
232  tasks.clear();
233  FindDriveAppTasks(drive_app_registry,
234                    path_mime_set,
235                    &tasks);
236  ASSERT_EQ(1U, tasks.size());
237  // Confirm that only Foo.app is found.
238  EXPECT_EQ("foo_app_id", tasks[0].task_descriptor().app_id);
239
240  // Add a "text/plain" file not on Drive. No tasks should be found.
241  path_mime_set.insert(
242      std::make_pair(base::FilePath::FromUTF8Unsafe("not_on_drive.txt"),
243                     "text/plain"));
244  tasks.clear();
245  FindDriveAppTasks(drive_app_registry,
246                    path_mime_set,
247                    &tasks);
248  // Confirm no tasks are found.
249  ASSERT_TRUE(tasks.empty());
250}
251
252// Test that the right task is chosen from multiple choices per mime types
253// and file extensions.
254TEST(FileManagerFileTasksTest, ChooseAndSetDefaultTask_MultipleTasks) {
255  TestingPrefServiceSimple pref_service;
256  RegisterDefaultTaskPreferences(&pref_service);
257
258  // Text.app and Nice.app were found for "foo.txt".
259  TaskDescriptor text_app_task("text-app-id",
260                               TASK_TYPE_FILE_HANDLER,
261                               "action-id");
262  TaskDescriptor nice_app_task("nice-app-id",
263                               TASK_TYPE_FILE_HANDLER,
264                               "action-id");
265  std::vector<FullTaskDescriptor> tasks;
266  tasks.push_back(FullTaskDescriptor(
267      text_app_task,
268      "Text.app",
269      GURL("http://example.com/text_app.png"),
270      false /* is_default */));
271  tasks.push_back(FullTaskDescriptor(
272      nice_app_task,
273      "Nice.app",
274      GURL("http://example.com/nice_app.png"),
275      false /* is_default */));
276  PathAndMimeTypeSet path_mime_set;
277  path_mime_set.insert(std::make_pair(
278      base::FilePath::FromUTF8Unsafe("foo.txt"),
279      "text/plain"));
280
281  // None of them should be chosen as default, as nothing is set in the
282  // preferences.
283  ChooseAndSetDefaultTask(pref_service, path_mime_set, &tasks);
284  EXPECT_FALSE(tasks[0].is_default());
285  EXPECT_FALSE(tasks[1].is_default());
286
287  // Set Text.app as default for "text/plain" in the preferences.
288  base::DictionaryValue empty;
289  base::DictionaryValue mime_types;
290  mime_types.SetStringWithoutPathExpansion(
291      "text/plain",
292      TaskDescriptorToId(text_app_task));
293  UpdateDefaultTaskPreferences(&pref_service, mime_types, empty);
294
295  // Text.app should be chosen as default.
296  ChooseAndSetDefaultTask(pref_service, path_mime_set, &tasks);
297  EXPECT_TRUE(tasks[0].is_default());
298  EXPECT_FALSE(tasks[1].is_default());
299
300  // Change it back to non-default for testing further.
301  tasks[0].set_is_default(false);
302
303  // Clear the preferences and make sure none of them are default.
304  UpdateDefaultTaskPreferences(&pref_service, empty, empty);
305  ChooseAndSetDefaultTask(pref_service, path_mime_set, &tasks);
306  EXPECT_FALSE(tasks[0].is_default());
307  EXPECT_FALSE(tasks[1].is_default());
308
309  // Set Nice.app as default for ".txt" in the preferences.
310  base::DictionaryValue suffixes;
311  suffixes.SetStringWithoutPathExpansion(
312      ".txt",
313      TaskDescriptorToId(nice_app_task));
314  UpdateDefaultTaskPreferences(&pref_service, empty, suffixes);
315
316  // Now Nice.app should be chosen as default.
317  ChooseAndSetDefaultTask(pref_service, path_mime_set, &tasks);
318  EXPECT_FALSE(tasks[0].is_default());
319  EXPECT_TRUE(tasks[1].is_default());
320}
321
322// Test that Files.app's internal file browser handler is chosen as default
323// even if nothing is set in the preferences.
324TEST(FileManagerFileTasksTest, ChooseAndSetDefaultTask_FallbackFileBrowser) {
325  TestingPrefServiceSimple pref_service;
326  RegisterDefaultTaskPreferences(&pref_service);
327
328  // Files.app's internal file browser handler was found for "foo.txt".
329  TaskDescriptor files_app_task(kFileManagerAppId,
330                                TASK_TYPE_FILE_BROWSER_HANDLER,
331                                "view-in-browser");
332  std::vector<FullTaskDescriptor> tasks;
333  tasks.push_back(FullTaskDescriptor(
334      files_app_task,
335      "View in browser",
336      GURL("http://example.com/some_icon.png"),
337      false /* is_default */));
338  PathAndMimeTypeSet path_mime_set;
339  path_mime_set.insert(std::make_pair(
340      base::FilePath::FromUTF8Unsafe("foo.txt"),
341      "text/plain"));
342
343  // The internal file browser handler should be chosen as default, as it's a
344  // fallback file browser handler.
345  ChooseAndSetDefaultTask(pref_service, path_mime_set, &tasks);
346  EXPECT_TRUE(tasks[0].is_default());
347}
348
349// Test using the test extension system, which needs lots of setup.
350class FileManagerFileTasksComplexTest : public testing::Test {
351 protected:
352  FileManagerFileTasksComplexTest()
353      : command_line_(CommandLine::NO_PROGRAM),
354        extension_service_(NULL) {
355    extensions::TestExtensionSystem* test_extension_system =
356        static_cast<extensions::TestExtensionSystem*>(
357            extensions::ExtensionSystem::Get(&test_profile_));
358    extension_service_ = test_extension_system->CreateExtensionService(
359        &command_line_,
360        base::FilePath()  /* install_directory */,
361        false  /* autoupdate_enabled*/);
362  }
363
364  content::TestBrowserThreadBundle thread_bundle_;
365  chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
366  chromeos::ScopedTestCrosSettings test_cros_settings_;
367  chromeos::ScopedTestUserManager test_user_manager_;
368  TestingProfile test_profile_;
369  CommandLine command_line_;
370  ExtensionService* extension_service_;  // Owned by test_profile_;
371};
372
373// The basic logic is similar to a test case for FindDriveAppTasks above.
374TEST_F(FileManagerFileTasksComplexTest, FindFileHandlerTasks) {
375  // Random IDs generated by
376  // % ruby -le 'print (0...32).to_a.map{(?a + rand(16)).chr}.join'
377  const char kFooId[] = "hhgbjpmdppecanaaogonaigmmifgpaph";
378  const char kBarId[] = "odlhccgofgkadkkhcmhgnhgahonahoca";
379  const char kEphemeralId[] = "opoomfdlbjcbjinalcjdjfoiikdeaoel";
380
381  // Foo.app can handle "text/plain" and "text/html".
382  extensions::ExtensionBuilder foo_app;
383  foo_app.SetManifest(extensions::DictionaryBuilder()
384                      .Set("name", "Foo")
385                      .Set("version", "1.0.0")
386                      .Set("manifest_version", 2)
387                      .Set("app",
388                           extensions::DictionaryBuilder()
389                           .Set("background",
390                                extensions::DictionaryBuilder()
391                                .Set("scripts",
392                                     extensions::ListBuilder()
393                                     .Append("background.js"))))
394                      .Set("file_handlers",
395                           extensions::DictionaryBuilder()
396                           .Set("text",
397                                extensions::DictionaryBuilder()
398                                .Set("title", "Text")
399                                .Set("types",
400                                     extensions::ListBuilder()
401                                     .Append("text/plain")
402                                     .Append("text/html")))));
403  foo_app.SetID(kFooId);
404  extension_service_->AddExtension(foo_app.Build().get());
405
406  // Bar.app can only handle "text/plain".
407  extensions::ExtensionBuilder bar_app;
408  bar_app.SetManifest(extensions::DictionaryBuilder()
409                      .Set("name", "Bar")
410                      .Set("version", "1.0.0")
411                      .Set("manifest_version", 2)
412                      .Set("app",
413                           extensions::DictionaryBuilder()
414                           .Set("background",
415                                extensions::DictionaryBuilder()
416                                .Set("scripts",
417                                     extensions::ListBuilder()
418                                     .Append("background.js"))))
419                      .Set("file_handlers",
420                           extensions::DictionaryBuilder()
421                           .Set("text",
422                                extensions::DictionaryBuilder()
423                                .Set("title", "Text")
424                                .Set("types",
425                                     extensions::ListBuilder()
426                                     .Append("text/plain")))));
427  bar_app.SetID(kBarId);
428  extension_service_->AddExtension(bar_app.Build().get());
429
430  // Ephemeral.app is an ephemeral app that can handle "text/plain".
431  // It should not ever be found as ephemeral apps cannot be file handlers.
432  extensions::ExtensionBuilder ephemeral_app;
433  ephemeral_app.SetManifest(
434      extensions::DictionaryBuilder()
435          .Set("name", "Ephemeral")
436          .Set("version", "1.0.0")
437          .Set("manifest_version", 2)
438          .Set("app",
439               extensions::DictionaryBuilder().Set(
440                   "background",
441                   extensions::DictionaryBuilder().Set(
442                       "scripts",
443                       extensions::ListBuilder().Append("background.js"))))
444          .Set("file_handlers",
445               extensions::DictionaryBuilder().Set(
446                   "text",
447                   extensions::DictionaryBuilder().Set("title", "Text").Set(
448                       "types",
449                       extensions::ListBuilder().Append("text/plain")))));
450  ephemeral_app.SetID(kEphemeralId);
451  scoped_refptr<extensions::Extension> built_ephemeral_app(
452      ephemeral_app.Build());
453  extension_service_->AddExtension(built_ephemeral_app.get());
454  extensions::ExtensionPrefs* extension_prefs =
455      extensions::ExtensionPrefs::Get(&test_profile_);
456  extension_prefs->OnExtensionInstalled(built_ephemeral_app.get(),
457                                        extensions::Extension::ENABLED,
458                                        syncer::StringOrdinal(),
459                                        extensions::kInstallFlagIsEphemeral,
460                                        std::string());
461
462  // Find apps for a "text/plain" file. Foo.app and Bar.app should be found.
463  PathAndMimeTypeSet path_mime_set;
464  path_mime_set.insert(
465      std::make_pair(
466          drive::util::GetDriveMountPointPath(&test_profile_).AppendASCII(
467              "foo.txt"),
468          "text/plain"));
469
470  std::vector<FullTaskDescriptor> tasks;
471  FindFileHandlerTasks(&test_profile_, path_mime_set, &tasks);
472  ASSERT_EQ(2U, tasks.size());
473  // Sort the app IDs, as the order is not guaranteed.
474  std::vector<std::string> app_ids;
475  app_ids.push_back(tasks[0].task_descriptor().app_id);
476  app_ids.push_back(tasks[1].task_descriptor().app_id);
477  std::sort(app_ids.begin(), app_ids.end());
478  // Confirm that both Foo.app and Bar.app are found.
479  EXPECT_EQ(kFooId, app_ids[0]);
480  EXPECT_EQ(kBarId, app_ids[1]);
481
482  // Find apps for "text/plain" and "text/html" files. Only Foo.app should be
483  // found.
484  path_mime_set.clear();
485  path_mime_set.insert(
486      std::make_pair(
487          drive::util::GetDriveMountPointPath(&test_profile_).AppendASCII(
488              "foo.txt"),
489          "text/plain"));
490  path_mime_set.insert(
491      std::make_pair(
492          drive::util::GetDriveMountPointPath(&test_profile_).AppendASCII(
493              "foo.html"),
494          "text/html"));
495  tasks.clear();
496  FindFileHandlerTasks(&test_profile_, path_mime_set, &tasks);
497  ASSERT_EQ(1U, tasks.size());
498  // Confirm that only Foo.app is found.
499  EXPECT_EQ(kFooId, tasks[0].task_descriptor().app_id);
500
501  // Add an "image/png" file. No tasks should be found.
502  path_mime_set.insert(
503      std::make_pair(base::FilePath::FromUTF8Unsafe("foo.png"),
504                     "image/png"));
505  tasks.clear();
506  FindFileHandlerTasks(&test_profile_, path_mime_set, &tasks);
507  // Confirm no tasks are found.
508  ASSERT_TRUE(tasks.empty());
509}
510
511// The basic logic is similar to a test case for FindDriveAppTasks above.
512TEST_F(FileManagerFileTasksComplexTest, FindFileBrowserHandlerTasks) {
513  // Copied from FindFileHandlerTasks test above.
514  const char kFooId[] = "hhgbjpmdppecanaaogonaigmmifgpaph";
515  const char kBarId[] = "odlhccgofgkadkkhcmhgnhgahonahoca";
516  const char kEphemeralId[] = "opoomfdlbjcbjinalcjdjfoiikdeaoel";
517
518  // Foo.app can handle ".txt" and ".html".
519  // This one is an extension, and has "file_browser_handlers"
520  extensions::ExtensionBuilder foo_app;
521  foo_app.SetManifest(extensions::DictionaryBuilder()
522                      .Set("name", "Foo")
523                      .Set("version", "1.0.0")
524                      .Set("manifest_version", 2)
525                      .Set("file_browser_handlers",
526                           extensions::ListBuilder()
527                           .Append(extensions::DictionaryBuilder()
528                                   .Set("id", "open")
529                                   .Set("default_title", "open")
530                                   .Set("file_filters",
531                                        extensions::ListBuilder()
532                                        .Append("filesystem:*.txt")
533                                        .Append("filesystem:*.html")))));
534  foo_app.SetID(kFooId);
535  extension_service_->AddExtension(foo_app.Build().get());
536
537  // Bar.app can only handle ".txt".
538  extensions::ExtensionBuilder bar_app;
539  bar_app.SetManifest(extensions::DictionaryBuilder()
540                      .Set("name", "Bar")
541                      .Set("version", "1.0.0")
542                      .Set("manifest_version", 2)
543                      .Set("file_browser_handlers",
544                           extensions::ListBuilder()
545                           .Append(extensions::DictionaryBuilder()
546                                   .Set("id", "open")
547                                   .Set("default_title", "open")
548                                   .Set("file_filters",
549                                        extensions::ListBuilder()
550                                        .Append("filesystem:*.txt")))));
551  bar_app.SetID(kBarId);
552  extension_service_->AddExtension(bar_app.Build().get());
553
554  // Ephemeral.app is an ephemeral app that can handle ".txt".
555  // It should not ever be found as ephemeral apps cannot be file browser
556  // handlers.
557  extensions::ExtensionBuilder ephemeral_app;
558  ephemeral_app.SetManifest(
559      extensions::DictionaryBuilder()
560          .Set("name", "Ephemeral")
561          .Set("version", "1.0.0")
562          .Set("manifest_version", 2)
563          .Set("file_browser_handlers",
564               extensions::ListBuilder().Append(
565                   extensions::DictionaryBuilder()
566                       .Set("id", "open")
567                       .Set("default_title", "open")
568                       .Set("file_filters",
569                            extensions::ListBuilder().Append(
570                                "filesystem:*.txt")))));
571  ephemeral_app.SetID(kEphemeralId);
572  scoped_refptr<extensions::Extension> built_ephemeral_app(
573      ephemeral_app.Build());
574  extension_service_->AddExtension(built_ephemeral_app.get());
575  extensions::ExtensionPrefs* extension_prefs =
576      extensions::ExtensionPrefs::Get(&test_profile_);
577  extension_prefs->OnExtensionInstalled(built_ephemeral_app.get(),
578                                        extensions::Extension::ENABLED,
579                                        syncer::StringOrdinal(),
580                                        extensions::kInstallFlagIsEphemeral,
581                                        std::string());
582
583  // Find apps for a ".txt" file. Foo.app and Bar.app should be found.
584  std::vector<GURL> file_urls;
585  file_urls.push_back(GURL("filesystem:chrome-extension://id/dir/foo.txt"));
586
587  std::vector<FullTaskDescriptor> tasks;
588  FindFileBrowserHandlerTasks(&test_profile_, file_urls, &tasks);
589  ASSERT_EQ(2U, tasks.size());
590  // Sort the app IDs, as the order is not guaranteed.
591  std::vector<std::string> app_ids;
592  app_ids.push_back(tasks[0].task_descriptor().app_id);
593  app_ids.push_back(tasks[1].task_descriptor().app_id);
594  std::sort(app_ids.begin(), app_ids.end());
595  // Confirm that both Foo.app and Bar.app are found.
596  EXPECT_EQ(kFooId, app_ids[0]);
597  EXPECT_EQ(kBarId, app_ids[1]);
598
599  // Find apps for ".txt" and ".html" files. Only Foo.app should be found.
600  file_urls.clear();
601  file_urls.push_back(GURL("filesystem:chrome-extension://id/dir/foo.txt"));
602  file_urls.push_back(GURL("filesystem:chrome-extension://id/dir/foo.html"));
603  tasks.clear();
604  FindFileBrowserHandlerTasks(&test_profile_, file_urls, &tasks);
605  ASSERT_EQ(1U, tasks.size());
606  // Confirm that only Foo.app is found.
607  EXPECT_EQ(kFooId, tasks[0].task_descriptor().app_id);
608
609  // Add an ".png" file. No tasks should be found.
610  file_urls.push_back(GURL("filesystem:chrome-extension://id/dir/foo.png"));
611  tasks.clear();
612  FindFileBrowserHandlerTasks(&test_profile_, file_urls, &tasks);
613  // Confirm no tasks are found.
614  ASSERT_TRUE(tasks.empty());
615}
616
617// Test that all kinds of apps (file handler, file browser handler, and Drive
618// app) are returned.
619TEST_F(FileManagerFileTasksComplexTest, FindAllTypesOfTasks) {
620  // kFooId and kBarId copied from FindFileHandlerTasks test above.
621  const char kFooId[] = "hhgbjpmdppecanaaogonaigmmifgpaph";
622  const char kBarId[] = "odlhccgofgkadkkhcmhgnhgahonahoca";
623  const char kBazId[] = "plifkpkakemokpflgbnnigcoldgcbdmc";
624
625  // Foo.app can handle "text/plain".
626  // This is a packaged app (file handler).
627  extensions::ExtensionBuilder foo_app;
628  foo_app.SetManifest(extensions::DictionaryBuilder()
629                      .Set("name", "Foo")
630                      .Set("version", "1.0.0")
631                      .Set("manifest_version", 2)
632                      .Set("app",
633                           extensions::DictionaryBuilder()
634                           .Set("background",
635                                extensions::DictionaryBuilder()
636                                .Set("scripts",
637                                     extensions::ListBuilder()
638                                     .Append("background.js"))))
639                      .Set("file_handlers",
640                           extensions::DictionaryBuilder()
641                           .Set("text",
642                                extensions::DictionaryBuilder()
643                                .Set("title", "Text")
644                                .Set("types",
645                                     extensions::ListBuilder()
646                                     .Append("text/plain")))));
647  foo_app.SetID(kFooId);
648  extension_service_->AddExtension(foo_app.Build().get());
649
650  // Bar.app can only handle ".txt".
651  // This is an extension (file browser handler).
652  extensions::ExtensionBuilder bar_app;
653  bar_app.SetManifest(extensions::DictionaryBuilder()
654                      .Set("name", "Bar")
655                      .Set("version", "1.0.0")
656                      .Set("manifest_version", 2)
657                      .Set("file_browser_handlers",
658                           extensions::ListBuilder()
659                           .Append(extensions::DictionaryBuilder()
660                                   .Set("id", "open")
661                                   .Set("default_title", "open")
662                                   .Set("file_filters",
663                                        extensions::ListBuilder()
664                                        .Append("filesystem:*.txt")))));
665  bar_app.SetID(kBarId);
666  extension_service_->AddExtension(bar_app.Build().get());
667
668  // Baz.app can handle "text/plain".
669  // This is a Drive app.
670  scoped_ptr<google_apis::AppResource> baz_app(new google_apis::AppResource);
671  baz_app->set_product_id("baz_app_id");
672  baz_app->set_application_id(kBazId);
673  baz_app->set_name("Baz");
674  baz_app->set_object_type("baz_object_type");
675  ScopedVector<std::string> baz_mime_types;
676  baz_mime_types.push_back(new std::string("text/plain"));
677  baz_app->set_primary_mimetypes(baz_mime_types.Pass());
678  // Set up DriveAppRegistry.
679  ScopedVector<google_apis::AppResource> app_resources;
680  app_resources.push_back(baz_app.release());
681  google_apis::AppList app_list;
682  app_list.set_items(app_resources.Pass());
683  drive::DriveAppRegistry drive_app_registry(NULL);
684  drive_app_registry.UpdateFromAppList(app_list);
685
686  // Find apps for "foo.txt". All apps should be found.
687  PathAndMimeTypeSet path_mime_set;
688  std::vector<GURL> file_urls;
689  path_mime_set.insert(
690      std::make_pair(
691          drive::util::GetDriveMountPointPath(&test_profile_).AppendASCII(
692              "foo.txt"),
693          "text/plain"));
694  file_urls.push_back(GURL("filesystem:chrome-extension://id/dir/foo.txt"));
695
696  std::vector<FullTaskDescriptor> tasks;
697  FindAllTypesOfTasks(&test_profile_,
698                      &drive_app_registry,
699                      path_mime_set,
700                      file_urls,
701                      &tasks);
702  ASSERT_EQ(3U, tasks.size());
703
704  // Sort the app IDs, as the order is not guaranteed.
705  std::vector<std::string> app_ids;
706  app_ids.push_back(tasks[0].task_descriptor().app_id);
707  app_ids.push_back(tasks[1].task_descriptor().app_id);
708  app_ids.push_back(tasks[2].task_descriptor().app_id);
709  std::sort(app_ids.begin(), app_ids.end());
710  // Confirm that all apps are found.
711  EXPECT_EQ(kFooId, app_ids[0]);
712  EXPECT_EQ(kBarId, app_ids[1]);
713  EXPECT_EQ(kBazId, app_ids[2]);
714}
715
716TEST_F(FileManagerFileTasksComplexTest, FindAllTypesOfTasks_GoogleDocument) {
717  // kFooId and kBarId copied from FindFileHandlerTasks test above.
718  const char kFooId[] = "hhgbjpmdppecanaaogonaigmmifgpaph";
719  const char kBarId[] = "odlhccgofgkadkkhcmhgnhgahonahoca";
720
721  // Foo.app can handle ".gdoc" files.
722  scoped_ptr<google_apis::AppResource> foo_app(new google_apis::AppResource);
723  foo_app->set_product_id("foo_app");
724  foo_app->set_application_id(kFooId);
725  foo_app->set_name("Foo");
726  foo_app->set_object_type("foo_object_type");
727  ScopedVector<std::string> foo_extensions;
728  foo_extensions.push_back(new std::string("gdoc"));  // Not ".gdoc"
729  foo_app->set_primary_file_extensions(foo_extensions.Pass());
730
731  // Prepare DriveAppRegistry from Foo.app.
732  ScopedVector<google_apis::AppResource> app_resources;
733  app_resources.push_back(foo_app.release());
734  google_apis::AppList app_list;
735  app_list.set_items(app_resources.Pass());
736  drive::DriveAppRegistry drive_app_registry(NULL);
737  drive_app_registry.UpdateFromAppList(app_list);
738
739  // Bar.app can handle ".gdoc" files.
740  // This is an extension (file browser handler).
741  extensions::ExtensionBuilder bar_app;
742  bar_app.SetManifest(extensions::DictionaryBuilder()
743                      .Set("name", "Bar")
744                      .Set("version", "1.0.0")
745                      .Set("manifest_version", 2)
746                      .Set("file_browser_handlers",
747                           extensions::ListBuilder()
748                           .Append(extensions::DictionaryBuilder()
749                                   .Set("id", "open")
750                                   .Set("default_title", "open")
751                                   .Set("file_filters",
752                                        extensions::ListBuilder()
753                                        .Append("filesystem:*.gdoc")))));
754  bar_app.SetID(kBarId);
755  extension_service_->AddExtension(bar_app.Build().get());
756
757  // Files.app can handle ".gdoc" files.
758  // The ID "kFileManagerAppId" used here is precisely the one that identifies
759  // the Chrome OS Files.app application.
760  extensions::ExtensionBuilder files_app;
761  files_app.SetManifest(extensions::DictionaryBuilder()
762                       .Set("name", "Files")
763                       .Set("version", "1.0.0")
764                       .Set("manifest_version", 2)
765                       .Set("file_browser_handlers",
766                            extensions::ListBuilder()
767                            .Append(extensions::DictionaryBuilder()
768                                    .Set("id", "open")
769                                    .Set("default_title", "open")
770                                    .Set("file_filters",
771                                         extensions::ListBuilder()
772                                         .Append("filesystem:*.gdoc")))));
773  files_app.SetID(kFileManagerAppId);
774  extension_service_->AddExtension(files_app.Build().get());
775
776  // Find apps for a ".gdoc file". Only the built-in handler of Files.apps
777  // should be found.
778  PathAndMimeTypeSet path_mime_set;
779  std::vector<GURL> file_urls;
780  path_mime_set.insert(
781      std::make_pair(
782          drive::util::GetDriveMountPointPath(&test_profile_).AppendASCII(
783              "foo.gdoc"),
784          "application/vnd.google-apps.document"));
785  file_urls.push_back(GURL("filesystem:chrome-extension://id/dir/foo.gdoc"));
786
787  std::vector<FullTaskDescriptor> tasks;
788  FindAllTypesOfTasks(&test_profile_,
789                      &drive_app_registry,
790                      path_mime_set,
791                      file_urls,
792                      &tasks);
793  ASSERT_EQ(1U, tasks.size());
794  EXPECT_EQ(kFileManagerAppId, tasks[0].task_descriptor().app_id);
795}
796
797}  // namespace file_tasks
798}  // namespace file_manager.
799