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