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