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 "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[] = { 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 Feature* test_feature = 320 api_feature_provider.GetFeature(test_data[i].api_full_name); 321 ASSERT_TRUE(test_feature); 322 EXPECT_EQ(test_data[i].expect_is_available, 323 api.IsAnyFeatureAvailableToContext(*test_feature, 324 test_data[i].extension, 325 test_data[i].context, 326 test_data[i].url)) 327 << i; 328 } 329 } 330 331 TEST(ExtensionAPITest, LazyGetSchema) { 332 scoped_ptr<ExtensionAPI> apis(ExtensionAPI::CreateWithDefaultConfiguration()); 333 334 EXPECT_EQ(NULL, apis->GetSchema(std::string())); 335 EXPECT_EQ(NULL, apis->GetSchema(std::string())); 336 EXPECT_EQ(NULL, apis->GetSchema("experimental")); 337 EXPECT_EQ(NULL, apis->GetSchema("experimental")); 338 EXPECT_EQ(NULL, apis->GetSchema("foo")); 339 EXPECT_EQ(NULL, apis->GetSchema("foo")); 340 341 EXPECT_TRUE(apis->GetSchema("dns")); 342 EXPECT_TRUE(apis->GetSchema("dns")); 343 EXPECT_TRUE(apis->GetSchema("extension")); 344 EXPECT_TRUE(apis->GetSchema("extension")); 345 EXPECT_TRUE(apis->GetSchema("infobars")); 346 EXPECT_TRUE(apis->GetSchema("infobars")); 347 EXPECT_TRUE(apis->GetSchema("omnibox")); 348 EXPECT_TRUE(apis->GetSchema("omnibox")); 349 EXPECT_TRUE(apis->GetSchema("storage")); 350 EXPECT_TRUE(apis->GetSchema("storage")); 351 } 352 353 scoped_refptr<Extension> CreateExtensionWithPermissions( 354 const std::set<std::string>& permissions) { 355 base::DictionaryValue manifest; 356 manifest.SetString("name", "extension"); 357 manifest.SetString("version", "1.0"); 358 manifest.SetInteger("manifest_version", 2); 359 { 360 scoped_ptr<base::ListValue> permissions_list(new base::ListValue()); 361 for (std::set<std::string>::const_iterator i = permissions.begin(); 362 i != permissions.end(); ++i) { 363 permissions_list->Append(new base::StringValue(*i)); 364 } 365 manifest.Set("permissions", permissions_list.release()); 366 } 367 368 std::string error; 369 scoped_refptr<Extension> extension(Extension::Create( 370 base::FilePath(), Manifest::UNPACKED, 371 manifest, Extension::NO_FLAGS, &error)); 372 CHECK(extension.get()); 373 CHECK(error.empty()); 374 375 return extension; 376 } 377 378 scoped_refptr<Extension> CreateExtensionWithPermission( 379 const std::string& permission) { 380 std::set<std::string> permissions; 381 permissions.insert(permission); 382 return CreateExtensionWithPermissions(permissions); 383 } 384 385 TEST(ExtensionAPITest, ExtensionWithUnprivilegedAPIs) { 386 scoped_refptr<Extension> extension; 387 { 388 std::set<std::string> permissions; 389 permissions.insert("storage"); 390 permissions.insert("history"); 391 extension = CreateExtensionWithPermissions(permissions); 392 } 393 394 scoped_ptr<ExtensionAPI> extension_api( 395 ExtensionAPI::CreateWithDefaultConfiguration()); 396 397 const FeatureProvider& api_features = *FeatureProvider::GetAPIFeatures(); 398 399 // "storage" is completely unprivileged. 400 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( 401 *api_features.GetFeature("storage"), 402 NULL, 403 Feature::BLESSED_EXTENSION_CONTEXT, 404 GURL())); 405 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( 406 *api_features.GetFeature("storage"), 407 NULL, 408 Feature::UNBLESSED_EXTENSION_CONTEXT, 409 GURL())); 410 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( 411 *api_features.GetFeature("storage"), 412 NULL, 413 Feature::CONTENT_SCRIPT_CONTEXT, 414 GURL())); 415 416 // "extension" is partially unprivileged. 417 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( 418 *api_features.GetFeature("extension"), 419 NULL, 420 Feature::BLESSED_EXTENSION_CONTEXT, 421 GURL())); 422 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( 423 *api_features.GetFeature("extension"), 424 NULL, 425 Feature::UNBLESSED_EXTENSION_CONTEXT, 426 GURL())); 427 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( 428 *api_features.GetFeature("extension"), 429 NULL, 430 Feature::CONTENT_SCRIPT_CONTEXT, 431 GURL())); 432 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( 433 *api_features.GetFeature("extension.getURL"), 434 NULL, 435 Feature::CONTENT_SCRIPT_CONTEXT, 436 GURL())); 437 438 // "history" is entirely privileged. 439 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext( 440 *api_features.GetFeature("history"), 441 NULL, 442 Feature::BLESSED_EXTENSION_CONTEXT, 443 GURL())); 444 EXPECT_FALSE(extension_api->IsAnyFeatureAvailableToContext( 445 *api_features.GetFeature("history"), 446 NULL, 447 Feature::UNBLESSED_EXTENSION_CONTEXT, 448 GURL())); 449 EXPECT_FALSE(extension_api->IsAnyFeatureAvailableToContext( 450 *api_features.GetFeature("history"), 451 NULL, 452 Feature::CONTENT_SCRIPT_CONTEXT, 453 GURL())); 454 } 455 456 scoped_refptr<Extension> CreateHostedApp() { 457 base::DictionaryValue values; 458 values.SetString(manifest_keys::kName, "test"); 459 values.SetString(manifest_keys::kVersion, "0.1"); 460 values.Set(manifest_keys::kWebURLs, new base::ListValue()); 461 values.SetString(manifest_keys::kLaunchWebURL, 462 "http://www.example.com"); 463 std::string error; 464 scoped_refptr<Extension> extension(Extension::Create( 465 base::FilePath(), Manifest::INTERNAL, values, Extension::NO_FLAGS, 466 &error)); 467 CHECK(extension.get()); 468 return extension; 469 } 470 471 scoped_refptr<Extension> CreatePackagedAppWithPermissions( 472 const std::set<std::string>& permissions) { 473 base::DictionaryValue values; 474 values.SetString(manifest_keys::kName, "test"); 475 values.SetString(manifest_keys::kVersion, "0.1"); 476 values.SetString(manifest_keys::kPlatformAppBackground, 477 "http://www.example.com"); 478 479 base::DictionaryValue* app = new base::DictionaryValue(); 480 base::DictionaryValue* background = new base::DictionaryValue(); 481 base::ListValue* scripts = new base::ListValue(); 482 scripts->Append(new base::StringValue("test.js")); 483 background->Set("scripts", scripts); 484 app->Set("background", background); 485 values.Set(manifest_keys::kApp, app); 486 { 487 scoped_ptr<base::ListValue> permissions_list(new base::ListValue()); 488 for (std::set<std::string>::const_iterator i = permissions.begin(); 489 i != permissions.end(); ++i) { 490 permissions_list->Append(new base::StringValue(*i)); 491 } 492 values.Set("permissions", permissions_list.release()); 493 } 494 495 std::string error; 496 scoped_refptr<Extension> extension(Extension::Create( 497 base::FilePath(), Manifest::INTERNAL, values, Extension::NO_FLAGS, 498 &error)); 499 CHECK(extension.get()) << error; 500 return extension; 501 } 502 503 TEST(ExtensionAPITest, HostedAppPermissions) { 504 scoped_refptr<Extension> extension = CreateHostedApp(); 505 506 scoped_ptr<ExtensionAPI> extension_api( 507 ExtensionAPI::CreateWithDefaultConfiguration()); 508 509 // "runtime" and "tabs" should not be available in hosted apps. 510 EXPECT_FALSE(extension_api->IsAvailable("runtime", 511 extension.get(), 512 Feature::BLESSED_EXTENSION_CONTEXT, 513 GURL()).is_available()); 514 EXPECT_FALSE(extension_api->IsAvailable("runtime.id", 515 extension.get(), 516 Feature::BLESSED_EXTENSION_CONTEXT, 517 GURL()).is_available()); 518 EXPECT_FALSE(extension_api->IsAvailable("runtime.sendMessage", 519 extension.get(), 520 Feature::BLESSED_EXTENSION_CONTEXT, 521 GURL()).is_available()); 522 EXPECT_FALSE(extension_api->IsAvailable("runtime.sendNativeMessage", 523 extension.get(), 524 Feature::BLESSED_EXTENSION_CONTEXT, 525 GURL()).is_available()); 526 EXPECT_FALSE(extension_api->IsAvailable("tabs.create", 527 extension.get(), 528 Feature::BLESSED_EXTENSION_CONTEXT, 529 GURL()).is_available()); 530 } 531 532 TEST(ExtensionAPITest, AppAndFriendsAvailability) { 533 534 scoped_ptr<ExtensionAPI> extension_api( 535 ExtensionAPI::CreateWithDefaultConfiguration()); 536 537 // Make sure chrome.app.runtime and chrome.app.window are available to apps, 538 // and chrome.app is not. 539 { 540 std::set<std::string> permissions; 541 permissions.insert("app.runtime"); 542 permissions.insert("app.window"); 543 scoped_refptr<Extension> extension = 544 CreatePackagedAppWithPermissions(permissions); 545 EXPECT_FALSE(extension_api->IsAvailable( 546 "app", 547 extension.get(), 548 Feature::BLESSED_EXTENSION_CONTEXT, 549 GURL("http://foo.com")).is_available()); 550 EXPECT_TRUE(extension_api->IsAvailable( 551 "app.runtime", 552 extension.get(), 553 Feature::BLESSED_EXTENSION_CONTEXT, 554 GURL("http://foo.com")).is_available()); 555 EXPECT_TRUE(extension_api->IsAvailable( 556 "app.window", 557 extension.get(), 558 Feature::BLESSED_EXTENSION_CONTEXT, 559 GURL("http://foo.com")).is_available()); 560 } 561 // Make sure chrome.app.runtime and chrome.app.window are not available to 562 // extensions, and chrome.app is. 563 { 564 std::set<std::string> permissions; 565 scoped_refptr<Extension> extension = 566 CreateExtensionWithPermissions(permissions); 567 EXPECT_TRUE(extension_api->IsAvailable( 568 "app", 569 extension.get(), 570 Feature::BLESSED_EXTENSION_CONTEXT, 571 GURL("http://foo.com")).is_available()); 572 EXPECT_FALSE(extension_api->IsAvailable( 573 "app.runtime", 574 extension.get(), 575 Feature::BLESSED_EXTENSION_CONTEXT, 576 GURL("http://foo.com")).is_available()); 577 EXPECT_FALSE(extension_api->IsAvailable( 578 "app.window", 579 extension.get(), 580 Feature::BLESSED_EXTENSION_CONTEXT, 581 GURL("http://foo.com")).is_available()); 582 } 583 } 584 585 TEST(ExtensionAPITest, ExtensionWithDependencies) { 586 // Extension with the "ttsEngine" permission but not the "tts" permission; it 587 // should not automatically get "tts" permission. 588 { 589 scoped_refptr<Extension> extension = 590 CreateExtensionWithPermission("ttsEngine"); 591 scoped_ptr<ExtensionAPI> api( 592 ExtensionAPI::CreateWithDefaultConfiguration()); 593 EXPECT_TRUE(api->IsAvailable("ttsEngine", 594 extension.get(), 595 Feature::BLESSED_EXTENSION_CONTEXT, 596 GURL()).is_available()); 597 EXPECT_FALSE(api->IsAvailable("tts", 598 extension.get(), 599 Feature::BLESSED_EXTENSION_CONTEXT, 600 GURL()).is_available()); 601 } 602 603 // Conversely, extension with the "tts" permission but not the "ttsEngine" 604 // permission shouldn't get the "ttsEngine" permission. 605 { 606 scoped_refptr<Extension> extension = 607 CreateExtensionWithPermission("tts"); 608 scoped_ptr<ExtensionAPI> api( 609 ExtensionAPI::CreateWithDefaultConfiguration()); 610 EXPECT_FALSE(api->IsAvailable("ttsEngine", 611 extension.get(), 612 Feature::BLESSED_EXTENSION_CONTEXT, 613 GURL()).is_available()); 614 EXPECT_TRUE(api->IsAvailable("tts", 615 extension.get(), 616 Feature::BLESSED_EXTENSION_CONTEXT, 617 GURL()).is_available()); 618 } 619 } 620 621 bool MatchesURL( 622 ExtensionAPI* api, const std::string& api_name, const std::string& url) { 623 return api->IsAvailable( 624 api_name, NULL, Feature::WEB_PAGE_CONTEXT, GURL(url)).is_available(); 625 } 626 627 TEST(ExtensionAPITest, URLMatching) { 628 scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration()); 629 630 // "app" API is available to all URLs that content scripts can be injected. 631 EXPECT_TRUE(MatchesURL(api.get(), "app", "http://example.com/example.html")); 632 EXPECT_TRUE(MatchesURL(api.get(), "app", "https://blah.net")); 633 EXPECT_TRUE(MatchesURL(api.get(), "app", "file://somefile.html")); 634 635 // But not internal URLs. 636 EXPECT_FALSE(MatchesURL(api.get(), "app", "about:flags")); 637 EXPECT_FALSE(MatchesURL(api.get(), "app", "chrome://flags")); 638 639 // "app" should be available to chrome-extension URLs. 640 EXPECT_TRUE(MatchesURL(api.get(), "app", 641 "chrome-extension://fakeextension")); 642 643 // "storage" API (for example) isn't available to any URLs. 644 EXPECT_FALSE(MatchesURL(api.get(), "storage", 645 "http://example.com/example.html")); 646 EXPECT_FALSE(MatchesURL(api.get(), "storage", "https://blah.net")); 647 EXPECT_FALSE(MatchesURL(api.get(), "storage", "file://somefile.html")); 648 EXPECT_FALSE(MatchesURL(api.get(), "storage", "about:flags")); 649 EXPECT_FALSE(MatchesURL(api.get(), "storage", "chrome://flags")); 650 EXPECT_FALSE(MatchesURL(api.get(), "storage", 651 "chrome-extension://fakeextension")); 652 } 653 654 TEST(ExtensionAPITest, GetAPINameFromFullName) { 655 struct { 656 std::string input; 657 std::string api_name; 658 std::string child_name; 659 } test_data[] = { 660 { "", "", "" }, 661 { "unknown", "", "" }, 662 { "bookmarks", "bookmarks", "" }, 663 { "bookmarks.", "bookmarks", "" }, 664 { ".bookmarks", "", "" }, 665 { "bookmarks.create", "bookmarks", "create" }, 666 { "bookmarks.create.", "bookmarks", "create." }, 667 { "bookmarks.create.monkey", "bookmarks", "create.monkey" }, 668 { "bookmarkManagerPrivate", "bookmarkManagerPrivate", "" }, 669 { "bookmarkManagerPrivate.copy", "bookmarkManagerPrivate", "copy" } 670 }; 671 672 scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration()); 673 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { 674 std::string child_name; 675 std::string api_name = api->GetAPINameFromFullName(test_data[i].input, 676 &child_name); 677 EXPECT_EQ(test_data[i].api_name, api_name) << test_data[i].input; 678 EXPECT_EQ(test_data[i].child_name, child_name) << test_data[i].input; 679 } 680 } 681 682 TEST(ExtensionAPITest, DefaultConfigurationFeatures) { 683 scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration()); 684 685 SimpleFeature* bookmarks = static_cast<SimpleFeature*>( 686 api->GetFeatureDependency("api:bookmarks")); 687 SimpleFeature* bookmarks_create = static_cast<SimpleFeature*>( 688 api->GetFeatureDependency("api:bookmarks.create")); 689 690 struct { 691 SimpleFeature* feature; 692 // TODO(aa): More stuff to test over time. 693 } test_data[] = { 694 { bookmarks }, 695 { bookmarks_create } 696 }; 697 698 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { 699 SimpleFeature* feature = test_data[i].feature; 700 ASSERT_TRUE(feature) << i; 701 702 EXPECT_TRUE(feature->whitelist()->empty()); 703 EXPECT_TRUE(feature->extension_types()->empty()); 704 705 EXPECT_EQ(1u, feature->GetContexts()->size()); 706 EXPECT_TRUE(feature->GetContexts()->count( 707 Feature::BLESSED_EXTENSION_CONTEXT)); 708 709 EXPECT_EQ(SimpleFeature::UNSPECIFIED_LOCATION, feature->location()); 710 EXPECT_TRUE(feature->platforms()->empty()); 711 EXPECT_EQ(0, feature->min_manifest_version()); 712 EXPECT_EQ(0, feature->max_manifest_version()); 713 } 714 } 715 716 TEST(ExtensionAPITest, FeaturesRequireContexts) { 717 // TODO(cduvall): Make this check API featues. 718 scoped_ptr<base::DictionaryValue> api_features1(new base::DictionaryValue()); 719 scoped_ptr<base::DictionaryValue> api_features2(new base::DictionaryValue()); 720 base::DictionaryValue* test1 = new base::DictionaryValue(); 721 base::DictionaryValue* test2 = new base::DictionaryValue(); 722 base::ListValue* contexts = new base::ListValue(); 723 contexts->Append(new base::StringValue("content_script")); 724 test1->Set("contexts", contexts); 725 test1->SetString("channel", "stable"); 726 test2->SetString("channel", "stable"); 727 api_features1->Set("test", test1); 728 api_features2->Set("test", test2); 729 730 struct { 731 base::DictionaryValue* api_features; 732 bool expect_success; 733 } test_data[] = { 734 { api_features1.get(), true }, 735 { api_features2.get(), false } 736 }; 737 738 739 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { 740 BaseFeatureProvider api_feature_provider(*test_data[i].api_features, 741 CreateAPIFeature); 742 Feature* feature = api_feature_provider.GetFeature("test"); 743 EXPECT_EQ(test_data[i].expect_success, feature != NULL) << i; 744 } 745 } 746 747 static void GetDictionaryFromList(const base::DictionaryValue* schema, 748 const std::string& list_name, 749 const int list_index, 750 const base::DictionaryValue** out) { 751 const base::ListValue* list; 752 EXPECT_TRUE(schema->GetList(list_name, &list)); 753 EXPECT_TRUE(list->GetDictionary(list_index, out)); 754 } 755 756 TEST(ExtensionAPITest, TypesHaveNamespace) { 757 base::FilePath manifest_path; 758 PathService::Get(chrome::DIR_TEST_DATA, &manifest_path); 759 manifest_path = manifest_path.AppendASCII("extensions") 760 .AppendASCII("extension_api_unittest") 761 .AppendASCII("types_have_namespace.json"); 762 763 std::string manifest_str; 764 ASSERT_TRUE(base::ReadFileToString(manifest_path, &manifest_str)) 765 << "Failed to load: " << manifest_path.value(); 766 767 ExtensionAPI api; 768 api.RegisterSchemaResource("test.foo", 0); 769 api.LoadSchema("test.foo", manifest_str); 770 771 const base::DictionaryValue* schema = api.GetSchema("test.foo"); 772 773 const base::DictionaryValue* dict; 774 const base::DictionaryValue* sub_dict; 775 std::string type; 776 777 GetDictionaryFromList(schema, "types", 0, &dict); 778 EXPECT_TRUE(dict->GetString("id", &type)); 779 EXPECT_EQ("test.foo.TestType", type); 780 EXPECT_TRUE(dict->GetString("customBindings", &type)); 781 EXPECT_EQ("test.foo.TestType", type); 782 EXPECT_TRUE(dict->GetDictionary("properties", &sub_dict)); 783 const base::DictionaryValue* property; 784 EXPECT_TRUE(sub_dict->GetDictionary("foo", &property)); 785 EXPECT_TRUE(property->GetString("$ref", &type)); 786 EXPECT_EQ("test.foo.OtherType", type); 787 EXPECT_TRUE(sub_dict->GetDictionary("bar", &property)); 788 EXPECT_TRUE(property->GetString("$ref", &type)); 789 EXPECT_EQ("fully.qualified.Type", type); 790 791 GetDictionaryFromList(schema, "functions", 0, &dict); 792 GetDictionaryFromList(dict, "parameters", 0, &sub_dict); 793 EXPECT_TRUE(sub_dict->GetString("$ref", &type)); 794 EXPECT_EQ("test.foo.TestType", type); 795 EXPECT_TRUE(dict->GetDictionary("returns", &sub_dict)); 796 EXPECT_TRUE(sub_dict->GetString("$ref", &type)); 797 EXPECT_EQ("fully.qualified.Type", type); 798 799 GetDictionaryFromList(schema, "functions", 1, &dict); 800 GetDictionaryFromList(dict, "parameters", 0, &sub_dict); 801 EXPECT_TRUE(sub_dict->GetString("$ref", &type)); 802 EXPECT_EQ("fully.qualified.Type", type); 803 EXPECT_TRUE(dict->GetDictionary("returns", &sub_dict)); 804 EXPECT_TRUE(sub_dict->GetString("$ref", &type)); 805 EXPECT_EQ("test.foo.TestType", type); 806 807 GetDictionaryFromList(schema, "events", 0, &dict); 808 GetDictionaryFromList(dict, "parameters", 0, &sub_dict); 809 EXPECT_TRUE(sub_dict->GetString("$ref", &type)); 810 EXPECT_EQ("test.foo.TestType", type); 811 GetDictionaryFromList(dict, "parameters", 1, &sub_dict); 812 EXPECT_TRUE(sub_dict->GetString("$ref", &type)); 813 EXPECT_EQ("fully.qualified.Type", type); 814 } 815 816 // Tests API availability with an empty manifest. 817 TEST(ExtensionAPITest, NoPermissions) { 818 const struct { 819 const char* permission_name; 820 bool expect_success; 821 } kTests[] = { 822 // Test default module/package permission. 823 { "extension", true }, 824 { "i18n", true }, 825 { "permissions", true }, 826 { "runtime", true }, 827 { "test", true }, 828 // These require manifest keys. 829 { "browserAction", false }, 830 { "pageAction", false }, 831 { "pageActions", false }, 832 // Some negative tests. 833 { "bookmarks", false }, 834 { "cookies", false }, 835 { "history", false }, 836 // Make sure we find the module name after stripping '.' 837 { "runtime.abcd.onStartup", true }, 838 // Test Tabs/Windows functions. 839 { "tabs.create", true }, 840 { "tabs.duplicate", true }, 841 { "tabs.onRemoved", true }, 842 { "tabs.remove", true }, 843 { "tabs.update", true }, 844 { "tabs.getSelected", true }, 845 { "tabs.onUpdated", true }, 846 { "windows.get", true }, 847 { "windows.create", true }, 848 { "windows.remove", true }, 849 { "windows.update", true }, 850 // Test some whitelisted functions. These require no permissions. 851 { "app.getDetails", true }, 852 { "app.getDetailsForFrame", true }, 853 { "app.getIsInstalled", true }, 854 { "app.installState", true }, 855 { "app.runningState", true }, 856 { "management.getPermissionWarningsByManifest", true }, 857 { "management.uninstallSelf", true }, 858 // But other functions in those modules do. 859 { "management.getPermissionWarningsById", false }, 860 }; 861 862 scoped_ptr<ExtensionAPI> extension_api( 863 ExtensionAPI::CreateWithDefaultConfiguration()); 864 scoped_refptr<Extension> extension = 865 BuildExtension(ExtensionBuilder().Pass()).Build(); 866 867 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) { 868 EXPECT_EQ(kTests[i].expect_success, 869 extension_api->IsAvailable(kTests[i].permission_name, 870 extension.get(), 871 Feature::BLESSED_EXTENSION_CONTEXT, 872 GURL()).is_available()) 873 << "Permission being tested: " << kTests[i].permission_name; 874 } 875 } 876 877 // Tests that permissions that require manifest keys are available when those 878 // keys are present. 879 TEST(ExtensionAPITest, ManifestKeys) { 880 scoped_ptr<ExtensionAPI> extension_api( 881 ExtensionAPI::CreateWithDefaultConfiguration()); 882 883 scoped_refptr<Extension> extension = 884 BuildExtension(ExtensionBuilder().Pass()) 885 .MergeManifest(DictionaryBuilder().Set("browser_action", 886 DictionaryBuilder().Pass())) 887 .Build(); 888 889 EXPECT_TRUE(extension_api->IsAvailable("browserAction", 890 extension.get(), 891 Feature::BLESSED_EXTENSION_CONTEXT, 892 GURL()).is_available()); 893 EXPECT_FALSE(extension_api->IsAvailable("pageAction", 894 extension.get(), 895 Feature::BLESSED_EXTENSION_CONTEXT, 896 GURL()).is_available()); 897 } 898 899 } // namespace extensions 900