Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / common / extensions / api / common_extension_api_unittest.cc
blobf8580623dc26f4d4d2ad79d0f421f446e10ff6e7
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.
5 #include "extensions/common/extension_api.h"
7 #include <string>
8 #include <vector>
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"
31 namespace extensions {
33 using test_util::BuildExtension;
35 SimpleFeature* CreateAPIFeature() {
36 return new APIFeature();
39 TEST(ExtensionAPITest, Creation) {
40 ExtensionAPI* shared_instance = ExtensionAPI::GetSharedInstance();
41 EXPECT_EQ(shared_instance, ExtensionAPI::GetSharedInstance());
43 scoped_ptr<ExtensionAPI> new_instance(
44 ExtensionAPI::CreateWithDefaultConfiguration());
45 EXPECT_NE(new_instance.get(),
46 scoped_ptr<ExtensionAPI>(
47 ExtensionAPI::CreateWithDefaultConfiguration()).get());
49 ExtensionAPI empty_instance;
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 }
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);
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"}};
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;
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") },
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() }
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");
168 std::string api_features_str;
169 ASSERT_TRUE(base::ReadFileToString(
170 api_features_path, &api_features_str)) << "api_features.json";
172 scoped_ptr<base::DictionaryValue> value(static_cast<base::DictionaryValue*>(
173 base::JSONReader::Read(api_features_str).release()));
174 BaseFeatureProvider api_feature_provider(*value, CreateAPIFeature);
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);
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());
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();
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") }
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");
248 std::string api_features_str;
249 ASSERT_TRUE(base::ReadFileToString(
250 api_features_path, &api_features_str)) << "api_features.json";
252 scoped_ptr<base::DictionaryValue> value(static_cast<base::DictionaryValue*>(
253 base::JSONReader::Read(api_features_str).release()));
254 BaseFeatureProvider api_feature_provider(*value, CreateAPIFeature);
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);
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;
277 TEST(ExtensionAPITest, LazyGetSchema) {
278 scoped_ptr<ExtensionAPI> apis(ExtensionAPI::CreateWithDefaultConfiguration());
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"));
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"));
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);
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));
309 manifest.Set("permissions", permissions_list.release());
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());
319 return extension;
322 scoped_refptr<Extension> CreateExtensionWithPermission(
323 const std::string& permission) {
324 std::set<std::string> permissions;
325 permissions.insert(permission);
326 return CreateExtensionWithPermissions(permissions);
329 TEST(ExtensionAPITest, ExtensionWithUnprivilegedAPIs) {
330 scoped_refptr<Extension> extension;
332 std::set<std::string> permissions;
333 permissions.insert("storage");
334 permissions.insert("history");
335 extension = CreateExtensionWithPermissions(permissions);
338 scoped_ptr<ExtensionAPI> extension_api(
339 ExtensionAPI::CreateWithDefaultConfiguration());
341 const FeatureProvider& api_features = *FeatureProvider::GetAPIFeatures();
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()));
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()));
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()));
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;
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");
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);
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));
436 values.Set("permissions", permissions_list.release());
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;
447 TEST(ExtensionAPITest, HostedAppPermissions) {
448 scoped_refptr<Extension> extension = CreateHostedApp();
450 scoped_ptr<ExtensionAPI> extension_api(
451 ExtensionAPI::CreateWithDefaultConfiguration());
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());
476 TEST(ExtensionAPITest, AppAndFriendsAvailability) {
478 scoped_ptr<ExtensionAPI> extension_api(
479 ExtensionAPI::CreateWithDefaultConfiguration());
481 // Make sure chrome.app.runtime and chrome.app.window are available to apps,
482 // and chrome.app is not.
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());
505 // Make sure chrome.app.runtime and chrome.app.window are not available to
506 // extensions, and chrome.app is.
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());
529 TEST(ExtensionAPITest, ExtensionWithDependencies) {
530 // Extension with the "ttsEngine" permission but not the "tts" permission; it
531 // should not automatically get "tts" permission.
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());
547 // Conversely, extension with the "tts" permission but not the "ttsEngine"
548 // permission shouldn't get the "ttsEngine" permission.
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());
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();
571 TEST(ExtensionAPITest, URLMatching) {
572 scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration());
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"));
579 // Also to internal URLs.
580 EXPECT_TRUE(MatchesURL(api.get(), "app", "about:flags"));
581 EXPECT_TRUE(MatchesURL(api.get(), "app", "chrome://flags"));
583 // "app" should be available to chrome-extension URLs.
584 EXPECT_TRUE(MatchesURL(api.get(), "app",
585 "chrome-extension://fakeextension"));
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"));
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" }
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;
626 TEST(ExtensionAPITest, DefaultConfigurationFeatures) {
627 scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration());
629 SimpleFeature* bookmarks = static_cast<SimpleFeature*>(
630 api->GetFeatureDependency("api:bookmarks"));
631 SimpleFeature* bookmarks_create = static_cast<SimpleFeature*>(
632 api->GetFeatureDependency("api:bookmarks.create"));
634 struct {
635 SimpleFeature* feature;
636 // TODO(aa): More stuff to test over time.
637 } test_data[] = {
638 { bookmarks },
639 { bookmarks_create }
642 for (size_t i = 0; i < arraysize(test_data); ++i) {
643 SimpleFeature* feature = test_data[i].feature;
644 ASSERT_TRUE(feature) << i;
646 EXPECT_TRUE(feature->whitelist()->empty());
647 EXPECT_TRUE(feature->extension_types()->empty());
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());
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);
670 struct {
671 base::DictionaryValue* api_features;
672 bool expect_success;
673 } test_data[] = {
674 { api_features1.get(), true },
675 { api_features2.get(), false }
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;
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));
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");
702 std::string manifest_str;
703 ASSERT_TRUE(base::ReadFileToString(manifest_path, &manifest_str))
704 << "Failed to load: " << manifest_path.value();
706 ExtensionAPI api;
707 api.RegisterSchemaResource("test.foo", 0);
708 api.LoadSchema("test.foo", manifest_str);
710 const base::DictionaryValue* schema = api.GetSchema("test.foo");
712 const base::DictionaryValue* dict;
713 const base::DictionaryValue* sub_dict;
714 std::string type;
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);
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);
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);
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);
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.getIsInstalled", true },
792 { "app.installState", true },
793 { "app.runningState", true },
794 { "management.getPermissionWarningsByManifest", true },
795 { "management.uninstallSelf", true },
796 // But other functions in those modules do.
797 { "management.getPermissionWarningsById", false },
798 { "runtime.connectNative", false },
801 scoped_ptr<ExtensionAPI> extension_api(
802 ExtensionAPI::CreateWithDefaultConfiguration());
803 scoped_refptr<Extension> extension =
804 BuildExtension(ExtensionBuilder().Pass()).Build();
806 for (size_t i = 0; i < arraysize(kTests); ++i) {
807 EXPECT_EQ(kTests[i].expect_success,
808 extension_api->IsAvailable(kTests[i].permission_name,
809 extension.get(),
810 Feature::BLESSED_EXTENSION_CONTEXT,
811 GURL()).is_available())
812 << "Permission being tested: " << kTests[i].permission_name;
816 // Tests that permissions that require manifest keys are available when those
817 // keys are present.
818 TEST(ExtensionAPITest, ManifestKeys) {
819 scoped_ptr<ExtensionAPI> extension_api(
820 ExtensionAPI::CreateWithDefaultConfiguration());
822 scoped_refptr<Extension> extension =
823 BuildExtension(ExtensionBuilder().Pass())
824 .MergeManifest(DictionaryBuilder().Set("browser_action",
825 DictionaryBuilder().Pass()))
826 .Build();
828 EXPECT_TRUE(extension_api->IsAvailable("browserAction",
829 extension.get(),
830 Feature::BLESSED_EXTENSION_CONTEXT,
831 GURL()).is_available());
832 EXPECT_FALSE(extension_api->IsAvailable("pageAction",
833 extension.get(),
834 Feature::BLESSED_EXTENSION_CONTEXT,
835 GURL()).is_available());
838 } // namespace extensions