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"
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
;
53 bool expect_populated
;
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
) {
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"},
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
) {
90 std::string api_full_name
;
91 bool expect_is_available
;
92 Feature::Context context
;
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
,
145 { "parent2.child3.child.child", false, Feature::BLESSED_EXTENSION_CONTEXT
,
147 { "parent2.child3.child.child", true, Feature::UNBLESSED_EXTENSION_CONTEXT
,
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
,
158 { "parent3.noparent.child", true, Feature::UNBLESSED_EXTENSION_CONTEXT
,
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
) {
178 api
.RegisterDependencyProvider("api", &api_feature_provider
);
179 for (base::DictionaryValue::Iterator
iter(*value
); !iter
.IsAtEnd();
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
,
190 test_data
[i
].context
,
192 EXPECT_EQ(expected
, availability
.is_available())
193 << base::StringPrintf("Test %d: Feature '%s' was %s: %s",
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()
205 .Set("app", DictionaryBuilder()
206 .Set("background", DictionaryBuilder()
207 .Set("scripts", ListBuilder().Append("background.js"))))
209 .Set("manifest_version", 2)).Build();
210 scoped_refptr
<const Extension
> extension
= ExtensionBuilder()
211 .SetManifest(DictionaryBuilder()
212 .Set("name", "extension")
214 .Set("manifest_version", 2)).Build();
217 std::string api_full_name
;
218 bool expect_is_available
;
219 Feature::Context context
;
220 const Extension
* extension
;
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(),
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
) {
258 api
.RegisterDependencyProvider("api", &api_feature_provider
);
259 for (base::DictionaryValue::Iterator
iter(*value
); !iter
.IsAtEnd();
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
,
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());
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());
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"),
347 Feature::BLESSED_EXTENSION_CONTEXT
,
349 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
350 *api_features
.GetFeature("storage"),
352 Feature::UNBLESSED_EXTENSION_CONTEXT
,
354 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
355 *api_features
.GetFeature("storage"),
357 Feature::CONTENT_SCRIPT_CONTEXT
,
360 // "extension" is partially unprivileged.
361 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
362 *api_features
.GetFeature("extension"),
364 Feature::BLESSED_EXTENSION_CONTEXT
,
366 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
367 *api_features
.GetFeature("extension"),
369 Feature::UNBLESSED_EXTENSION_CONTEXT
,
371 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
372 *api_features
.GetFeature("extension"),
374 Feature::CONTENT_SCRIPT_CONTEXT
,
376 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
377 *api_features
.GetFeature("extension.getURL"),
379 Feature::CONTENT_SCRIPT_CONTEXT
,
382 // "history" is entirely privileged.
383 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
384 *api_features
.GetFeature("history"),
386 Feature::BLESSED_EXTENSION_CONTEXT
,
388 EXPECT_FALSE(extension_api
->IsAnyFeatureAvailableToContext(
389 *api_features
.GetFeature("history"),
391 Feature::UNBLESSED_EXTENSION_CONTEXT
,
393 EXPECT_FALSE(extension_api
->IsAnyFeatureAvailableToContext(
394 *api_features
.GetFeature("history"),
396 Feature::CONTENT_SCRIPT_CONTEXT
,
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");
408 scoped_refptr
<Extension
> extension(Extension::Create(
409 base::FilePath(), Manifest::INTERNAL
, values
, Extension::NO_FLAGS
,
411 CHECK(extension
.get());
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());
440 scoped_refptr
<Extension
> extension(Extension::Create(
441 base::FilePath(), Manifest::INTERNAL
, values
, Extension::NO_FLAGS
,
443 CHECK(extension
.get()) << error
;
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",
456 Feature::BLESSED_EXTENSION_CONTEXT
,
457 GURL()).is_available());
458 EXPECT_FALSE(extension_api
->IsAvailable("runtime.id",
460 Feature::BLESSED_EXTENSION_CONTEXT
,
461 GURL()).is_available());
462 EXPECT_FALSE(extension_api
->IsAvailable("runtime.sendMessage",
464 Feature::BLESSED_EXTENSION_CONTEXT
,
465 GURL()).is_available());
466 EXPECT_FALSE(extension_api
->IsAvailable("runtime.sendNativeMessage",
468 Feature::BLESSED_EXTENSION_CONTEXT
,
469 GURL()).is_available());
470 EXPECT_FALSE(extension_api
->IsAvailable("tabs.create",
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(
492 Feature::BLESSED_EXTENSION_CONTEXT
,
493 GURL("http://foo.com")).is_available());
494 EXPECT_TRUE(extension_api
->IsAvailable(
497 Feature::BLESSED_EXTENSION_CONTEXT
,
498 GURL("http://foo.com")).is_available());
499 EXPECT_TRUE(extension_api
->IsAvailable(
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(
514 Feature::BLESSED_EXTENSION_CONTEXT
,
515 GURL("http://foo.com")).is_available());
516 EXPECT_FALSE(extension_api
->IsAvailable(
519 Feature::BLESSED_EXTENSION_CONTEXT
,
520 GURL("http://foo.com")).is_available());
521 EXPECT_FALSE(extension_api
->IsAvailable(
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",
539 Feature::BLESSED_EXTENSION_CONTEXT
,
540 GURL()).is_available());
541 EXPECT_FALSE(api
->IsAvailable("tts",
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",
556 Feature::BLESSED_EXTENSION_CONTEXT
,
557 GURL()).is_available());
558 EXPECT_TRUE(api
->IsAvailable("tts",
560 Feature::BLESSED_EXTENSION_CONTEXT
,
561 GURL()).is_available());
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
) {
601 std::string api_name
;
602 std::string child_name
;
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
,
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"));
635 SimpleFeature
* feature
;
636 // TODO(aa): More stuff to test over time.
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
);
671 base::DictionaryValue
* api_features
;
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
,
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();
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
;
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
) {
758 const char* permission_name
;
761 // Test default module/package permission.
762 { "extension", true },
764 { "permissions", 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
,
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
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()))
828 EXPECT_TRUE(extension_api
->IsAvailable("browserAction",
830 Feature::BLESSED_EXTENSION_CONTEXT
,
831 GURL()).is_available());
832 EXPECT_FALSE(extension_api
->IsAvailable("pageAction",
834 Feature::BLESSED_EXTENSION_CONTEXT
,
835 GURL()).is_available());
838 } // namespace extensions