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 "extensions/common/extension_api.h" 6 7 #include <string> 8 #include <vector> 9 10 #include "base/file_util.h" 11 #include "base/files/file_path.h" 12 #include "base/json/json_reader.h" 13 #include "base/json/json_writer.h" 14 #include "base/memory/ref_counted.h" 15 #include "base/memory/scoped_ptr.h" 16 #include "base/path_service.h" 17 #include "base/strings/stringprintf.h" 18 #include "base/values.h" 19 #include "chrome/common/chrome_paths.h" 20 #include "chrome/common/extensions/extension_test_util.h" 21 #include "chrome/common/extensions/features/api_feature.h" 22 #include "chrome/common/extensions/features/base_feature_provider.h" 23 #include "chrome/common/extensions/features/simple_feature.h" 24 #include "extensions/common/extension.h" 25 #include "extensions/common/extension_builder.h" 26 #include "extensions/common/manifest.h" 27 #include "extensions/common/manifest_constants.h" 28 #include "extensions/common/value_builder.h" 29 #include "testing/gtest/include/gtest/gtest.h" 30 31 namespace extensions { 32 33 using extension_test_util::BuildExtension; 34 35 SimpleFeature* CreateAPIFeature() { 36 return new APIFeature(); 37 } 38 39 TEST(ExtensionAPITest, Creation) { 40 ExtensionAPI* shared_instance = ExtensionAPI::GetSharedInstance(); 41 EXPECT_EQ(shared_instance, ExtensionAPI::GetSharedInstance()); 42 43 scoped_ptr<ExtensionAPI> new_instance( 44 ExtensionAPI::CreateWithDefaultConfiguration()); 45 EXPECT_NE(new_instance.get(), 46 scoped_ptr<ExtensionAPI>( 47 ExtensionAPI::CreateWithDefaultConfiguration()).get()); 48 49 ExtensionAPI empty_instance; 50 51 struct { 52 ExtensionAPI* api; 53 bool expect_populated; 54 } test_data[] = { 55 { shared_instance, true }, 56 { new_instance.get(), true }, 57 { &empty_instance, false } 58 }; 59 60 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { 61 EXPECT_EQ(test_data[i].expect_populated, 62 test_data[i].api->GetSchema("bookmarks.create") != NULL); 63 } 64 } 65 66 TEST(ExtensionAPITest, SplitDependencyName) { 67 struct { 68 std::string input; 69 std::string expected_feature_type; 70 std::string expected_feature_name; 71 } test_data[] = { 72 { "", "api", "" }, // assumes "api" when no type is present 73 { "foo", "api", "foo" }, 74 { "foo:", "foo", "" }, 75 { ":foo", "", "foo" }, 76 { "foo:bar", "foo", "bar" }, 77 { "foo:bar.baz", "foo", "bar.baz" } 78 }; 79 80 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { 81 std::string feature_type; 82 std::string feature_name; 83 ExtensionAPI::SplitDependencyName(test_data[i].input, &feature_type, 84 &feature_name); 85 EXPECT_EQ(test_data[i].expected_feature_type, feature_type) << i; 86 EXPECT_EQ(test_data[i].expected_feature_name, feature_name) << i; 87 } 88 } 89 90 TEST(ExtensionAPITest, IsPrivileged) { 91 scoped_ptr<ExtensionAPI> extension_api( 92 ExtensionAPI::CreateWithDefaultConfiguration()); 93 94 EXPECT_FALSE(extension_api->IsPrivileged("runtime.connect")); 95 EXPECT_FALSE(extension_api->IsPrivileged("runtime.onConnect")); 96 EXPECT_FALSE(extension_api->IsPrivileged("runtime.lastError")); 97 98 // Exists, but privileged. 99 EXPECT_TRUE(extension_api->IsPrivileged("extension.getViews")); 100 EXPECT_TRUE(extension_api->IsPrivileged("history.search")); 101 102 // Whole APIs that are unprivileged. 103 EXPECT_FALSE(extension_api->IsPrivileged("app.getDetails")); 104 EXPECT_FALSE(extension_api->IsPrivileged("app.isInstalled")); 105 EXPECT_FALSE(extension_api->IsPrivileged("storage.local")); 106 EXPECT_FALSE(extension_api->IsPrivileged("storage.local.onChanged")); 107 EXPECT_FALSE(extension_api->IsPrivileged("storage.local.set")); 108 EXPECT_FALSE(extension_api->IsPrivileged("storage.local.MAX_ITEMS")); 109 EXPECT_FALSE(extension_api->IsPrivileged("storage.set")); 110 } 111 112 TEST(ExtensionAPITest, IsPrivilegedFeatures) { 113 struct { 114 std::string api_full_name; 115 bool expect_is_privilged; 116 } test_data[] = { 117 { "test1", false }, 118 { "test1.foo", true }, 119 { "test2", true }, 120 { "test2.foo", false }, 121 { "test2.bar", false }, 122 { "test2.baz", true }, 123 { "test3", false }, 124 { "test3.foo", true }, 125 { "test4", false } 126 }; 127 128 base::FilePath api_features_path; 129 PathService::Get(chrome::DIR_TEST_DATA, &api_features_path); 130 api_features_path = api_features_path.AppendASCII("extensions") 131 .AppendASCII("extension_api_unittest") 132 .AppendASCII("privileged_api_features.json"); 133 134 std::string api_features_str; 135 ASSERT_TRUE(base::ReadFileToString( 136 api_features_path, &api_features_str)) << "privileged_api_features.json"; 137 138 scoped_ptr<base::DictionaryValue> value(static_cast<base::DictionaryValue*>( 139 base::JSONReader::Read(api_features_str))); 140 BaseFeatureProvider api_feature_provider(*value, CreateAPIFeature); 141 142 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { 143 ExtensionAPI api; 144 api.RegisterDependencyProvider("api", &api_feature_provider); 145 EXPECT_EQ(test_data[i].expect_is_privilged, 146 api.IsPrivileged(test_data[i].api_full_name)) << i; 147 } 148 } 149 150 TEST(ExtensionAPITest, APIFeatures) { 151 struct { 152 std::string api_full_name; 153 bool expect_is_available; 154 Feature::Context context; 155 GURL url; 156 } test_data[] = { 157 { "test1", false, Feature::WEB_PAGE_CONTEXT, GURL() }, 158 { "test1", true, Feature::BLESSED_EXTENSION_CONTEXT, GURL() }, 159 { "test1", true, Feature::UNBLESSED_EXTENSION_CONTEXT, GURL() }, 160 { "test1", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, 161 { "test2", true, Feature::WEB_PAGE_CONTEXT, GURL("http://google.com") }, 162 { "test2", false, Feature::BLESSED_EXTENSION_CONTEXT, 163 GURL("http://google.com") }, 164 { "test2.foo", false, Feature::WEB_PAGE_CONTEXT, 165 GURL("http://google.com") }, 166 { "test2.foo", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, 167 { "test3", false, Feature::WEB_PAGE_CONTEXT, GURL("http://google.com") }, 168 { "test3.foo", true, Feature::WEB_PAGE_CONTEXT, GURL("http://google.com") }, 169 { "test3.foo", true, Feature::BLESSED_EXTENSION_CONTEXT, 170 GURL("http://bad.com") }, 171 { "test4", true, Feature::BLESSED_EXTENSION_CONTEXT, 172 GURL("http://bad.com") }, 173 { "test4.foo", false, Feature::BLESSED_EXTENSION_CONTEXT, 174 GURL("http://bad.com") }, 175 { "test4.foo", false, Feature::UNBLESSED_EXTENSION_CONTEXT, 176 GURL("http://bad.com") }, 177 { "test4.foo.foo", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, 178 { "test5", true, Feature::WEB_PAGE_CONTEXT, GURL("http://foo.com") }, 179 { "test5", false, Feature::WEB_PAGE_CONTEXT, GURL("http://bar.com") }, 180 { "test5.blah", true, Feature::WEB_PAGE_CONTEXT, GURL("http://foo.com") }, 181 { "test5.blah", false, Feature::WEB_PAGE_CONTEXT, GURL("http://bar.com") }, 182 { "test6", false, Feature::BLESSED_EXTENSION_CONTEXT, GURL() }, 183 { "test6.foo", true, Feature::BLESSED_EXTENSION_CONTEXT, GURL() }, 184 { "test7", true, Feature::WEB_PAGE_CONTEXT, GURL("http://foo.com") }, 185 { "test7.foo", false, Feature::WEB_PAGE_CONTEXT, GURL("http://bar.com") }, 186 { "test7.foo", true, Feature::WEB_PAGE_CONTEXT, GURL("http://foo.com") }, 187 { "test7.bar", false, Feature::WEB_PAGE_CONTEXT, GURL("http://bar.com") }, 188 { "test7.bar", false, Feature::WEB_PAGE_CONTEXT, GURL("http://foo.com") }, 189 190 // Test parent/child. 191 { "parent1", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, 192 { "parent1", false, Feature::WEB_PAGE_CONTEXT, GURL("http://foo.com") }, 193 { "parent1.child1", false, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, 194 { "parent1.child1", true, Feature::WEB_PAGE_CONTEXT, 195 GURL("http://foo.com") }, 196 { "parent1.child2", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, 197 { "parent1.child2", false, Feature::WEB_PAGE_CONTEXT, 198 GURL("http://foo.com") }, 199 { "parent2", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, 200 { "parent2", true, Feature::BLESSED_EXTENSION_CONTEXT, GURL() }, 201 { "parent2", true, Feature::UNBLESSED_EXTENSION_CONTEXT, GURL() }, 202 { "parent2.child3", false, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, 203 { "parent2.child3", true, Feature::BLESSED_EXTENSION_CONTEXT, GURL() }, 204 { "parent2.child3", false, Feature::UNBLESSED_EXTENSION_CONTEXT, GURL() }, 205 { "parent2.child3.child.child", true, Feature::CONTENT_SCRIPT_CONTEXT, 206 GURL() }, 207 { "parent2.child3.child.child", false, Feature::BLESSED_EXTENSION_CONTEXT, 208 GURL() }, 209 { "parent2.child3.child.child", true, Feature::UNBLESSED_EXTENSION_CONTEXT, 210 GURL() }, 211 { "parent3", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, 212 { "parent3", false, Feature::BLESSED_EXTENSION_CONTEXT, GURL() }, 213 { "parent3", false, Feature::UNBLESSED_EXTENSION_CONTEXT, GURL() }, 214 { "parent3.noparent", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, 215 { "parent3.noparent", true, Feature::BLESSED_EXTENSION_CONTEXT, GURL() }, 216 { "parent3.noparent", true, Feature::UNBLESSED_EXTENSION_CONTEXT, GURL() }, 217 { "parent3.noparent.child", true, Feature::CONTENT_SCRIPT_CONTEXT, GURL() }, 218 { "parent3.noparent.child", true, Feature::BLESSED_EXTENSION_CONTEXT, 219 GURL() }, 220 { "parent3.noparent.child", true, Feature::UNBLESSED_EXTENSION_CONTEXT, 221 GURL() } 222 }; 223 224 base::FilePath api_features_path; 225 PathService::Get(chrome::DIR_TEST_DATA, &api_features_path); 226 api_features_path = api_features_path.AppendASCII("extensions") 227 .AppendASCII("extension_api_unittest") 228 .AppendASCII("api_features.json"); 229 230 std::string api_features_str; 231 ASSERT_TRUE(base::ReadFileToString( 232 api_features_path, &api_features_str)) << "api_features.json"; 233 234 scoped_ptr<base::DictionaryValue> value(static_cast<base::DictionaryValue*>( 235 base::JSONReader::Read(api_features_str))); 236 BaseFeatureProvider api_feature_provider(*value, CreateAPIFeature); 237 238 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { 239 ExtensionAPI api; 240 api.RegisterDependencyProvider("api", &api_feature_provider); 241 for (base::DictionaryValue::Iterator iter(*value); !iter.IsAtEnd(); 242 iter.Advance()) { 243 if (iter.key().find(".") == std::string::npos) 244 api.RegisterSchemaResource(iter.key(), 0); 245 } 246 247 EXPECT_EQ(test_data[i].expect_is_available, 248 api.IsAvailable(test_data[i].api_full_name, 249 NULL, 250 test_data[i].context, 251 test_data[i].url).is_available()) << i; 252 } 253 } 254 255 TEST(ExtensionAPITest, IsAnyFeatureAvailableToContext) { 256 scoped_refptr<const Extension> app = ExtensionBuilder() 257 .SetManifest(DictionaryBuilder() 258 .Set("name", "app") 259 .Set("app", DictionaryBuilder() 260 .Set("background", DictionaryBuilder() 261 .Set("scripts", ListBuilder().Append("background.js")))) 262 .Set("version", "1") 263 .Set("manifest_version", 2)).Build(); 264 scoped_refptr<const Extension> extension = ExtensionBuilder() 265 .SetManifest(DictionaryBuilder() 266 .Set("name", "extension") 267 .Set("version", "1") 268 .Set("manifest_version", 2)).Build(); 269 270 struct { 271 std::string api_full_name; 272 bool expect_is_available; 273 Feature::Context context; 274 const Extension* extension; 275 GURL url; 276 } test_data[] = { 277 { "test1", false, Feature::WEB_PAGE_CONTEXT, NULL, GURL() }, 278 { "test1", true, Feature::UNBLESSED_EXTENSION_CONTEXT, NULL, GURL() }, 279 { "test1", false, Feature::UNBLESSED_EXTENSION_CONTEXT, app.get(), GURL() }, 280 { "test1", true, Feature::UNBLESSED_EXTENSION_CONTEXT, extension.get(), 281 GURL() }, 282 { "test2", true, Feature::CONTENT_SCRIPT_CONTEXT, NULL, GURL() }, 283 { "test2", true, Feature::WEB_PAGE_CONTEXT, NULL, 284 GURL("http://google.com") }, 285 { "test2.foo", false, Feature::WEB_PAGE_CONTEXT, NULL, 286 GURL("http://google.com") }, 287 { "test3", true, Feature::CONTENT_SCRIPT_CONTEXT, NULL, GURL() }, 288 { "test3", true, Feature::WEB_PAGE_CONTEXT, NULL, GURL("http://foo.com") }, 289 { "test4.foo", true, Feature::CONTENT_SCRIPT_CONTEXT, NULL, GURL() }, 290 { "test7", false, Feature::WEB_PAGE_CONTEXT, NULL, 291 GURL("http://google.com") }, 292 { "test7", true, Feature::WEB_PAGE_CONTEXT, NULL, GURL("http://foo.com") }, 293 { "test7", false, Feature::WEB_PAGE_CONTEXT, NULL, GURL("http://bar.com") } 294 }; 295 296 base::FilePath api_features_path; 297 PathService::Get(chrome::DIR_TEST_DATA, &api_features_path); 298 api_features_path = api_features_path.AppendASCII("extensions") 299 .AppendASCII("extension_api_unittest") 300 .AppendASCII("api_features.json"); 301 302 std::string api_features_str; 303 ASSERT_TRUE(base::ReadFileToString( 304 api_features_path, &api_features_str)) << "api_features.json"; 305 306 scoped_ptr<base::DictionaryValue> value(static_cast<base::DictionaryValue*>( 307 base::JSONReader::Read(api_features_str))); 308 BaseFeatureProvider api_feature_provider(*value, CreateAPIFeature); 309 310 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { 311 ExtensionAPI api; 312 api.RegisterDependencyProvider("api", &api_feature_provider); 313 for (base::DictionaryValue::Iterator iter(*value); !iter.IsAtEnd(); 314 iter.Advance()) { 315 if (iter.key().find(".") == std::string::npos) 316 api.RegisterSchemaResource(iter.key(), 0); 317 } 318 319 EXPECT_EQ(test_data[i].expect_is_available, 320 api.IsAnyFeatureAvailableToContext(test_data[i].api_full_name, 321 test_data[i].extension, 322 test_data[i].context, 323 test_data[i].url)) << i; 324 } 325 } 326 327 TEST(ExtensionAPITest, LazyGetSchema) { 328 scoped_ptr<ExtensionAPI> apis(ExtensionAPI::CreateWithDefaultConfiguration()); 329 330 EXPECT_EQ(NULL, apis->GetSchema(std::string())); 331 EXPECT_EQ(NULL, apis->GetSchema(std::string())); 332 EXPECT_EQ(NULL, apis->GetSchema("experimental")); 333 EXPECT_EQ(NULL, apis->GetSchema("experimental")); 334 EXPECT_EQ(NULL, apis->GetSchema("foo")); 335 EXPECT_EQ(NULL, apis->GetSchema("foo")); 336 337 EXPECT_TRUE(apis->GetSchema("dns")); 338 EXPECT_TRUE(apis->GetSchema("dns")); 339 EXPECT_TRUE(apis->GetSchema("extension")); 340 EXPECT_TRUE(apis->GetSchema("extension")); 341 EXPECT_TRUE(apis->GetSchema("infobars")); 342 EXPECT_TRUE(apis->GetSchema("infobars")); 343 EXPECT_TRUE(apis->GetSchema("omnibox")); 344 EXPECT_TRUE(apis->GetSchema("omnibox")); 345 EXPECT_TRUE(apis->GetSchema("storage")); 346 EXPECT_TRUE(apis->GetSchema("storage")); 347 } 348 349 scoped_refptr<Extension> CreateExtensionWithPermissions( 350 const std::set<std::string>& permissions) { 351 base::DictionaryValue manifest; 352 manifest.SetString("name", "extension"); 353 manifest.SetString("version", "1.0"); 354 manifest.SetInteger("manifest_version", 2); 355 { 356 scoped_ptr<base::ListValue> permissions_list(new base::ListValue()); 357 for (std::set<std::string>::const_iterator i = permissions.begin(); 358 i != permissions.end(); ++i) { 359 permissions_list->Append(new base::StringValue(*i)); 360 } 361 manifest.Set("permissions", permissions_list.release()); 362 } 363 364 std::string error; 365 scoped_refptr<Extension> extension(Extension::Create( 366 base::FilePath(), Manifest::UNPACKED, 367 manifest, Extension::NO_FLAGS, &error)); 368 CHECK(extension.get()); 369 CHECK(error.empty()); 370 371 return extension; 372 } 373 374 scoped_refptr<Extension> CreateExtensionWithPermission( 375 const std::string& permission) { 376 std::set<std::string> permissions; 377 permissions.insert(permission); 378 return CreateExtensionWithPermissions(permissions); 379 } 380 381 TEST(ExtensionAPITest, ExtensionWithUnprivilegedAPIs) { 382 scoped_refptr<Extension> extension; 383 { 384 std::set<std::string> permissions; 385 permissions.insert("storage"); 386 permissions.insert("history"); 387 extension = CreateExtensionWithPermissions(permissions); 388 } 389 390 scoped_ptr<ExtensionAPI> extension_api( 391 ExtensionAPI::CreateWithDefaultConfiguration()); 392 393 // "runtime" has privileged parts that should not be accessed by content 394 // scripts. 395 EXPECT_FALSE(extension_api->IsAnyFeatureAvailableToContext( 396 "runtime.getBackgroundPage", 397 NULL, 398 Feature::CONTENT_SCRIPT_CONTEXT, 399 GURL())); 400 EXPECT_FALSE(extension_api->IsAnyFeatureAvailableToContext( 401 "runtime.sendNativeMessage", 402 NULL, 403 Feature::CONTENT_SCRIPT_CONTEXT, 404 GURL())); 405 // "runtime" also has unprivileged parts. 406 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( 407 "runtime.sendMessage", 408 NULL, 409 Feature::CONTENT_SCRIPT_CONTEXT, 410 GURL())); 411 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( 412 "runtime.id", 413 NULL, 414 Feature::CONTENT_SCRIPT_CONTEXT, 415 GURL())); 416 417 // "storage" is completely unprivileged. 418 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( 419 "storage", 420 NULL, 421 Feature::BLESSED_EXTENSION_CONTEXT, 422 GURL())); 423 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( 424 "storage", 425 NULL, 426 Feature::UNBLESSED_EXTENSION_CONTEXT, 427 GURL())); 428 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( 429 "storage", 430 NULL, 431 Feature::CONTENT_SCRIPT_CONTEXT, 432 GURL())); 433 434 // "extension" is partially unprivileged. 435 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( 436 "extension", 437 NULL, 438 Feature::BLESSED_EXTENSION_CONTEXT, 439 GURL())); 440 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( 441 "extension", 442 NULL, 443 Feature::UNBLESSED_EXTENSION_CONTEXT, 444 GURL())); 445 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( 446 "extension", 447 NULL, 448 Feature::CONTENT_SCRIPT_CONTEXT, 449 GURL())); 450 451 // "history" is entirely privileged. 452 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( 453 "history", 454 NULL, 455 Feature::BLESSED_EXTENSION_CONTEXT, 456 GURL())); 457 EXPECT_FALSE(extension_api->IsAnyFeatureAvailableToContext( 458 "history", 459 NULL, 460 Feature::UNBLESSED_EXTENSION_CONTEXT, 461 GURL())); 462 EXPECT_FALSE(extension_api->IsAnyFeatureAvailableToContext( 463 "history", 464 NULL, 465 Feature::CONTENT_SCRIPT_CONTEXT, 466 GURL())); 467 } 468 469 scoped_refptr<Extension> CreateHostedApp() { 470 base::DictionaryValue values; 471 values.SetString(manifest_keys::kName, "test"); 472 values.SetString(manifest_keys::kVersion, "0.1"); 473 values.Set(manifest_keys::kWebURLs, new base::ListValue()); 474 values.SetString(manifest_keys::kLaunchWebURL, 475 "http://www.example.com"); 476 std::string error; 477 scoped_refptr<Extension> extension(Extension::Create( 478 base::FilePath(), Manifest::INTERNAL, values, Extension::NO_FLAGS, 479 &error)); 480 CHECK(extension.get()); 481 return extension; 482 } 483 484 scoped_refptr<Extension> CreatePackagedAppWithPermissions( 485 const std::set<std::string>& permissions) { 486 base::DictionaryValue values; 487 values.SetString(manifest_keys::kName, "test"); 488 values.SetString(manifest_keys::kVersion, "0.1"); 489 values.SetString(manifest_keys::kPlatformAppBackground, 490 "http://www.example.com"); 491 492 base::DictionaryValue* app = new base::DictionaryValue(); 493 base::DictionaryValue* background = new base::DictionaryValue(); 494 base::ListValue* scripts = new base::ListValue(); 495 scripts->Append(new base::StringValue("test.js")); 496 background->Set("scripts", scripts); 497 app->Set("background", background); 498 values.Set(manifest_keys::kApp, app); 499 { 500 scoped_ptr<base::ListValue> permissions_list(new base::ListValue()); 501 for (std::set<std::string>::const_iterator i = permissions.begin(); 502 i != permissions.end(); ++i) { 503 permissions_list->Append(new base::StringValue(*i)); 504 } 505 values.Set("permissions", permissions_list.release()); 506 } 507 508 std::string error; 509 scoped_refptr<Extension> extension(Extension::Create( 510 base::FilePath(), Manifest::INTERNAL, values, Extension::NO_FLAGS, 511 &error)); 512 CHECK(extension.get()) << error; 513 return extension; 514 } 515 516 TEST(ExtensionAPITest, HostedAppPermissions) { 517 scoped_refptr<Extension> extension = CreateHostedApp(); 518 519 scoped_ptr<ExtensionAPI> extension_api( 520 ExtensionAPI::CreateWithDefaultConfiguration()); 521 522 // "runtime" and "tabs" should not be available in hosted apps. 523 EXPECT_FALSE(extension_api->IsAvailable("runtime", 524 extension.get(), 525 Feature::BLESSED_EXTENSION_CONTEXT, 526 GURL()).is_available()); 527 EXPECT_FALSE(extension_api->IsAvailable("runtime.id", 528 extension.get(), 529 Feature::BLESSED_EXTENSION_CONTEXT, 530 GURL()).is_available()); 531 EXPECT_FALSE(extension_api->IsAvailable("runtime.sendMessage", 532 extension.get(), 533 Feature::BLESSED_EXTENSION_CONTEXT, 534 GURL()).is_available()); 535 EXPECT_FALSE(extension_api->IsAvailable("runtime.sendNativeMessage", 536 extension.get(), 537 Feature::BLESSED_EXTENSION_CONTEXT, 538 GURL()).is_available()); 539 EXPECT_FALSE(extension_api->IsAvailable("tabs.create", 540 extension.get(), 541 Feature::BLESSED_EXTENSION_CONTEXT, 542 GURL()).is_available()); 543 } 544 545 TEST(ExtensionAPITest, AppAndFriendsAvailability) { 546 547 scoped_ptr<ExtensionAPI> extension_api( 548 ExtensionAPI::CreateWithDefaultConfiguration()); 549 550 // Make sure chrome.app.runtime and chrome.app.window are available to apps, 551 // and chrome.app is not. 552 { 553 std::set<std::string> permissions; 554 permissions.insert("app.runtime"); 555 permissions.insert("app.window"); 556 scoped_refptr<Extension> extension = 557 CreatePackagedAppWithPermissions(permissions); 558 EXPECT_FALSE(extension_api->IsAvailable( 559 "app", 560 extension.get(), 561 Feature::BLESSED_EXTENSION_CONTEXT, 562 GURL("http://foo.com")).is_available()); 563 EXPECT_TRUE(extension_api->IsAvailable( 564 "app.runtime", 565 extension.get(), 566 Feature::BLESSED_EXTENSION_CONTEXT, 567 GURL("http://foo.com")).is_available()); 568 EXPECT_TRUE(extension_api->IsAvailable( 569 "app.window", 570 extension.get(), 571 Feature::BLESSED_EXTENSION_CONTEXT, 572 GURL("http://foo.com")).is_available()); 573 } 574 // Make sure chrome.app.runtime and chrome.app.window are not available to 575 // extensions, and chrome.app is. 576 { 577 std::set<std::string> permissions; 578 scoped_refptr<Extension> extension = 579 CreateExtensionWithPermissions(permissions); 580 EXPECT_TRUE(extension_api->IsAvailable( 581 "app", 582 extension.get(), 583 Feature::BLESSED_EXTENSION_CONTEXT, 584 GURL("http://foo.com")).is_available()); 585 EXPECT_FALSE(extension_api->IsAvailable( 586 "app.runtime", 587 extension.get(), 588 Feature::BLESSED_EXTENSION_CONTEXT, 589 GURL("http://foo.com")).is_available()); 590 EXPECT_FALSE(extension_api->IsAvailable( 591 "app.window", 592 extension.get(), 593 Feature::BLESSED_EXTENSION_CONTEXT, 594 GURL("http://foo.com")).is_available()); 595 } 596 } 597 598 TEST(ExtensionAPITest, ExtensionWithDependencies) { 599 // Extension with the "ttsEngine" permission but not the "tts" permission; it 600 // should not automatically get "tts" permission. 601 { 602 scoped_refptr<Extension> extension = 603 CreateExtensionWithPermission("ttsEngine"); 604 scoped_ptr<ExtensionAPI> api( 605 ExtensionAPI::CreateWithDefaultConfiguration()); 606 EXPECT_TRUE(api->IsAvailable("ttsEngine", 607 extension.get(), 608 Feature::BLESSED_EXTENSION_CONTEXT, 609 GURL()).is_available()); 610 EXPECT_FALSE(api->IsAvailable("tts", 611 extension.get(), 612 Feature::BLESSED_EXTENSION_CONTEXT, 613 GURL()).is_available()); 614 } 615 616 // Conversely, extension with the "tts" permission but not the "ttsEngine" 617 // permission shouldn't get the "ttsEngine" permission. 618 { 619 scoped_refptr<Extension> extension = 620 CreateExtensionWithPermission("tts"); 621 scoped_ptr<ExtensionAPI> api( 622 ExtensionAPI::CreateWithDefaultConfiguration()); 623 EXPECT_FALSE(api->IsAvailable("ttsEngine", 624 extension.get(), 625 Feature::BLESSED_EXTENSION_CONTEXT, 626 GURL()).is_available()); 627 EXPECT_TRUE(api->IsAvailable("tts", 628 extension.get(), 629 Feature::BLESSED_EXTENSION_CONTEXT, 630 GURL()).is_available()); 631 } 632 } 633 634 bool MatchesURL( 635 ExtensionAPI* api, const std::string& api_name, const std::string& url) { 636 return api->IsAvailable( 637 api_name, NULL, Feature::WEB_PAGE_CONTEXT, GURL(url)).is_available(); 638 } 639 640 TEST(ExtensionAPITest, URLMatching) { 641 scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration()); 642 643 // "app" API is available to all URLs that content scripts can be injected. 644 EXPECT_TRUE(MatchesURL(api.get(), "app", "http://example.com/example.html")); 645 EXPECT_TRUE(MatchesURL(api.get(), "app", "https://blah.net")); 646 EXPECT_TRUE(MatchesURL(api.get(), "app", "file://somefile.html")); 647 648 // But not internal URLs. 649 EXPECT_FALSE(MatchesURL(api.get(), "app", "about:flags")); 650 EXPECT_FALSE(MatchesURL(api.get(), "app", "chrome://flags")); 651 652 // "app" should be available to chrome-extension URLs. 653 EXPECT_TRUE(MatchesURL(api.get(), "app", 654 "chrome-extension://fakeextension")); 655 656 // "storage" API (for example) isn't available to any URLs. 657 EXPECT_FALSE(MatchesURL(api.get(), "storage", 658 "http://example.com/example.html")); 659 EXPECT_FALSE(MatchesURL(api.get(), "storage", "https://blah.net")); 660 EXPECT_FALSE(MatchesURL(api.get(), "storage", "file://somefile.html")); 661 EXPECT_FALSE(MatchesURL(api.get(), "storage", "about:flags")); 662 EXPECT_FALSE(MatchesURL(api.get(), "storage", "chrome://flags")); 663 EXPECT_FALSE(MatchesURL(api.get(), "storage", 664 "chrome-extension://fakeextension")); 665 } 666 667 TEST(ExtensionAPITest, GetAPINameFromFullName) { 668 struct { 669 std::string input; 670 std::string api_name; 671 std::string child_name; 672 } test_data[] = { 673 { "", "", "" }, 674 { "unknown", "", "" }, 675 { "bookmarks", "bookmarks", "" }, 676 { "bookmarks.", "bookmarks", "" }, 677 { ".bookmarks", "", "" }, 678 { "bookmarks.create", "bookmarks", "create" }, 679 { "bookmarks.create.", "bookmarks", "create." }, 680 { "bookmarks.create.monkey", "bookmarks", "create.monkey" }, 681 { "bookmarkManagerPrivate", "bookmarkManagerPrivate", "" }, 682 { "bookmarkManagerPrivate.copy", "bookmarkManagerPrivate", "copy" } 683 }; 684 685 scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration()); 686 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { 687 std::string child_name; 688 std::string api_name = api->GetAPINameFromFullName(test_data[i].input, 689 &child_name); 690 EXPECT_EQ(test_data[i].api_name, api_name) << test_data[i].input; 691 EXPECT_EQ(test_data[i].child_name, child_name) << test_data[i].input; 692 } 693 } 694 695 TEST(ExtensionAPITest, DefaultConfigurationFeatures) { 696 scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration()); 697 698 SimpleFeature* bookmarks = static_cast<SimpleFeature*>( 699 api->GetFeatureDependency("api:bookmarks")); 700 SimpleFeature* bookmarks_create = static_cast<SimpleFeature*>( 701 api->GetFeatureDependency("api:bookmarks.create")); 702 703 struct { 704 SimpleFeature* feature; 705 // TODO(aa): More stuff to test over time. 706 } test_data[] = { 707 { bookmarks }, 708 { bookmarks_create } 709 }; 710 711 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { 712 SimpleFeature* feature = test_data[i].feature; 713 ASSERT_TRUE(feature) << i; 714 715 EXPECT_TRUE(feature->whitelist()->empty()); 716 EXPECT_TRUE(feature->extension_types()->empty()); 717 718 EXPECT_EQ(1u, feature->GetContexts()->size()); 719 EXPECT_TRUE(feature->GetContexts()->count( 720 Feature::BLESSED_EXTENSION_CONTEXT)); 721 722 EXPECT_EQ(Feature::UNSPECIFIED_LOCATION, feature->location()); 723 EXPECT_TRUE(feature->platforms()->empty()); 724 EXPECT_EQ(0, feature->min_manifest_version()); 725 EXPECT_EQ(0, feature->max_manifest_version()); 726 } 727 } 728 729 TEST(ExtensionAPITest, FeaturesRequireContexts) { 730 // TODO(cduvall): Make this check API featues. 731 scoped_ptr<base::DictionaryValue> api_features1(new base::DictionaryValue()); 732 scoped_ptr<base::DictionaryValue> api_features2(new base::DictionaryValue()); 733 base::DictionaryValue* test1 = new base::DictionaryValue(); 734 base::DictionaryValue* test2 = new base::DictionaryValue(); 735 base::ListValue* contexts = new base::ListValue(); 736 contexts->Append(new base::StringValue("content_script")); 737 test1->Set("contexts", contexts); 738 test1->SetString("channel", "stable"); 739 test2->SetString("channel", "stable"); 740 api_features1->Set("test", test1); 741 api_features2->Set("test", test2); 742 743 struct { 744 base::DictionaryValue* api_features; 745 bool expect_success; 746 } test_data[] = { 747 { api_features1.get(), true }, 748 { api_features2.get(), false } 749 }; 750 751 752 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { 753 BaseFeatureProvider api_feature_provider(*test_data[i].api_features, 754 CreateAPIFeature); 755 Feature* feature = api_feature_provider.GetFeature("test"); 756 EXPECT_EQ(test_data[i].expect_success, feature != NULL) << i; 757 } 758 } 759 760 static void GetDictionaryFromList(const base::DictionaryValue* schema, 761 const std::string& list_name, 762 const int list_index, 763 const base::DictionaryValue** out) { 764 const base::ListValue* list; 765 EXPECT_TRUE(schema->GetList(list_name, &list)); 766 EXPECT_TRUE(list->GetDictionary(list_index, out)); 767 } 768 769 TEST(ExtensionAPITest, TypesHaveNamespace) { 770 base::FilePath manifest_path; 771 PathService::Get(chrome::DIR_TEST_DATA, &manifest_path); 772 manifest_path = manifest_path.AppendASCII("extensions") 773 .AppendASCII("extension_api_unittest") 774 .AppendASCII("types_have_namespace.json"); 775 776 std::string manifest_str; 777 ASSERT_TRUE(base::ReadFileToString(manifest_path, &manifest_str)) 778 << "Failed to load: " << manifest_path.value(); 779 780 ExtensionAPI api; 781 api.RegisterSchemaResource("test.foo", 0); 782 api.LoadSchema("test.foo", manifest_str); 783 784 const base::DictionaryValue* schema = api.GetSchema("test.foo"); 785 786 const base::DictionaryValue* dict; 787 const base::DictionaryValue* sub_dict; 788 std::string type; 789 790 GetDictionaryFromList(schema, "types", 0, &dict); 791 EXPECT_TRUE(dict->GetString("id", &type)); 792 EXPECT_EQ("test.foo.TestType", type); 793 EXPECT_TRUE(dict->GetString("customBindings", &type)); 794 EXPECT_EQ("test.foo.TestType", type); 795 EXPECT_TRUE(dict->GetDictionary("properties", &sub_dict)); 796 const base::DictionaryValue* property; 797 EXPECT_TRUE(sub_dict->GetDictionary("foo", &property)); 798 EXPECT_TRUE(property->GetString("$ref", &type)); 799 EXPECT_EQ("test.foo.OtherType", type); 800 EXPECT_TRUE(sub_dict->GetDictionary("bar", &property)); 801 EXPECT_TRUE(property->GetString("$ref", &type)); 802 EXPECT_EQ("fully.qualified.Type", type); 803 804 GetDictionaryFromList(schema, "functions", 0, &dict); 805 GetDictionaryFromList(dict, "parameters", 0, &sub_dict); 806 EXPECT_TRUE(sub_dict->GetString("$ref", &type)); 807 EXPECT_EQ("test.foo.TestType", type); 808 EXPECT_TRUE(dict->GetDictionary("returns", &sub_dict)); 809 EXPECT_TRUE(sub_dict->GetString("$ref", &type)); 810 EXPECT_EQ("fully.qualified.Type", type); 811 812 GetDictionaryFromList(schema, "functions", 1, &dict); 813 GetDictionaryFromList(dict, "parameters", 0, &sub_dict); 814 EXPECT_TRUE(sub_dict->GetString("$ref", &type)); 815 EXPECT_EQ("fully.qualified.Type", type); 816 EXPECT_TRUE(dict->GetDictionary("returns", &sub_dict)); 817 EXPECT_TRUE(sub_dict->GetString("$ref", &type)); 818 EXPECT_EQ("test.foo.TestType", type); 819 820 GetDictionaryFromList(schema, "events", 0, &dict); 821 GetDictionaryFromList(dict, "parameters", 0, &sub_dict); 822 EXPECT_TRUE(sub_dict->GetString("$ref", &type)); 823 EXPECT_EQ("test.foo.TestType", type); 824 GetDictionaryFromList(dict, "parameters", 1, &sub_dict); 825 EXPECT_TRUE(sub_dict->GetString("$ref", &type)); 826 EXPECT_EQ("fully.qualified.Type", type); 827 } 828 829 // Tests API availability with an empty manifest. 830 TEST(ExtensionAPITest, NoPermissions) { 831 const struct { 832 const char* permission_name; 833 bool expect_success; 834 } kTests[] = { 835 // Test default module/package permission. 836 { "extension", true }, 837 { "i18n", true }, 838 { "permissions", true }, 839 { "runtime", true }, 840 { "test", true }, 841 // These require manifest keys. 842 { "browserAction", false }, 843 { "pageAction", false }, 844 { "pageActions", false }, 845 // Some negative tests. 846 { "bookmarks", false }, 847 { "cookies", false }, 848 { "history", false }, 849 // Make sure we find the module name after stripping '.' 850 { "runtime.abcd.onStartup", true }, 851 // Test Tabs/Windows functions. 852 { "tabs.create", true }, 853 { "tabs.duplicate", true }, 854 { "tabs.onRemoved", true }, 855 { "tabs.remove", true }, 856 { "tabs.update", true }, 857 { "tabs.getSelected", true }, 858 { "tabs.onUpdated", true }, 859 { "windows.get", true }, 860 { "windows.create", true }, 861 { "windows.remove", true }, 862 { "windows.update", true }, 863 // Test some whitelisted functions. These require no permissions. 864 { "app.getDetails", true }, 865 { "app.getDetailsForFrame", true }, 866 { "app.getIsInstalled", true }, 867 { "app.installState", true }, 868 { "app.runningState", true }, 869 { "management.getPermissionWarningsByManifest", true }, 870 { "management.uninstallSelf", true }, 871 // But other functions in those modules do. 872 { "management.getPermissionWarningsById", false }, 873 }; 874 875 scoped_ptr<ExtensionAPI> extension_api( 876 ExtensionAPI::CreateWithDefaultConfiguration()); 877 scoped_refptr<Extension> extension = 878 BuildExtension(ExtensionBuilder().Pass()).Build(); 879 880 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) { 881 EXPECT_EQ(kTests[i].expect_success, 882 extension_api->IsAvailable(kTests[i].permission_name, 883 extension.get(), 884 Feature::BLESSED_EXTENSION_CONTEXT, 885 GURL()).is_available()) 886 << "Permission being tested: " << kTests[i].permission_name; 887 } 888 } 889 890 // Tests that permissions that require manifest keys are available when those 891 // keys are present. 892 TEST(ExtensionAPITest, ManifestKeys) { 893 scoped_ptr<ExtensionAPI> extension_api( 894 ExtensionAPI::CreateWithDefaultConfiguration()); 895 896 scoped_refptr<Extension> extension = 897 BuildExtension(ExtensionBuilder().Pass()) 898 .MergeManifest(DictionaryBuilder().Set("browser_action", 899 DictionaryBuilder().Pass())) 900 .Build(); 901 902 EXPECT_TRUE(extension_api->IsAvailable("browserAction", 903 extension.get(), 904 Feature::BLESSED_EXTENSION_CONTEXT, 905 GURL()).is_available()); 906 EXPECT_FALSE(extension_api->IsAvailable("pageAction", 907 extension.get(), 908 Feature::BLESSED_EXTENSION_CONTEXT, 909 GURL()).is_available()); 910 } 911 912 } // namespace extensions 913