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