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