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
)));
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
)));
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("infobars"));
292 EXPECT_TRUE(apis
->GetSchema("infobars"));
293 EXPECT_TRUE(apis
->GetSchema("omnibox"));
294 EXPECT_TRUE(apis
->GetSchema("omnibox"));
295 EXPECT_TRUE(apis
->GetSchema("storage"));
296 EXPECT_TRUE(apis
->GetSchema("storage"));
299 scoped_refptr
<Extension
> CreateExtensionWithPermissions(
300 const std::set
<std::string
>& permissions
) {
301 base::DictionaryValue manifest
;
302 manifest
.SetString("name", "extension");
303 manifest
.SetString("version", "1.0");
304 manifest
.SetInteger("manifest_version", 2);
306 scoped_ptr
<base::ListValue
> permissions_list(new base::ListValue());
307 for (std::set
<std::string
>::const_iterator i
= permissions
.begin();
308 i
!= permissions
.end(); ++i
) {
309 permissions_list
->Append(new base::StringValue(*i
));
311 manifest
.Set("permissions", permissions_list
.release());
315 scoped_refptr
<Extension
> extension(Extension::Create(
316 base::FilePath(), Manifest::UNPACKED
,
317 manifest
, Extension::NO_FLAGS
, &error
));
318 CHECK(extension
.get());
319 CHECK(error
.empty());
324 scoped_refptr
<Extension
> CreateExtensionWithPermission(
325 const std::string
& permission
) {
326 std::set
<std::string
> permissions
;
327 permissions
.insert(permission
);
328 return CreateExtensionWithPermissions(permissions
);
331 TEST(ExtensionAPITest
, ExtensionWithUnprivilegedAPIs
) {
332 scoped_refptr
<Extension
> extension
;
334 std::set
<std::string
> permissions
;
335 permissions
.insert("storage");
336 permissions
.insert("history");
337 extension
= CreateExtensionWithPermissions(permissions
);
340 scoped_ptr
<ExtensionAPI
> extension_api(
341 ExtensionAPI::CreateWithDefaultConfiguration());
343 const FeatureProvider
& api_features
= *FeatureProvider::GetAPIFeatures();
345 // "storage" is completely unprivileged.
346 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
347 *api_features
.GetFeature("storage"),
349 Feature::BLESSED_EXTENSION_CONTEXT
,
351 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
352 *api_features
.GetFeature("storage"),
354 Feature::UNBLESSED_EXTENSION_CONTEXT
,
356 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
357 *api_features
.GetFeature("storage"),
359 Feature::CONTENT_SCRIPT_CONTEXT
,
362 // "extension" is partially unprivileged.
363 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
364 *api_features
.GetFeature("extension"),
366 Feature::BLESSED_EXTENSION_CONTEXT
,
368 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
369 *api_features
.GetFeature("extension"),
371 Feature::UNBLESSED_EXTENSION_CONTEXT
,
373 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
374 *api_features
.GetFeature("extension"),
376 Feature::CONTENT_SCRIPT_CONTEXT
,
378 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
379 *api_features
.GetFeature("extension.getURL"),
381 Feature::CONTENT_SCRIPT_CONTEXT
,
384 // "history" is entirely privileged.
385 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
386 *api_features
.GetFeature("history"),
388 Feature::BLESSED_EXTENSION_CONTEXT
,
390 EXPECT_FALSE(extension_api
->IsAnyFeatureAvailableToContext(
391 *api_features
.GetFeature("history"),
393 Feature::UNBLESSED_EXTENSION_CONTEXT
,
395 EXPECT_FALSE(extension_api
->IsAnyFeatureAvailableToContext(
396 *api_features
.GetFeature("history"),
398 Feature::CONTENT_SCRIPT_CONTEXT
,
402 scoped_refptr
<Extension
> CreateHostedApp() {
403 base::DictionaryValue values
;
404 values
.SetString(manifest_keys::kName
, "test");
405 values
.SetString(manifest_keys::kVersion
, "0.1");
406 values
.Set(manifest_keys::kWebURLs
, new base::ListValue());
407 values
.SetString(manifest_keys::kLaunchWebURL
,
408 "http://www.example.com");
410 scoped_refptr
<Extension
> extension(Extension::Create(
411 base::FilePath(), Manifest::INTERNAL
, values
, Extension::NO_FLAGS
,
413 CHECK(extension
.get());
417 scoped_refptr
<Extension
> CreatePackagedAppWithPermissions(
418 const std::set
<std::string
>& permissions
) {
419 base::DictionaryValue values
;
420 values
.SetString(manifest_keys::kName
, "test");
421 values
.SetString(manifest_keys::kVersion
, "0.1");
422 values
.SetString(manifest_keys::kPlatformAppBackground
,
423 "http://www.example.com");
425 base::DictionaryValue
* app
= new base::DictionaryValue();
426 base::DictionaryValue
* background
= new base::DictionaryValue();
427 base::ListValue
* scripts
= new base::ListValue();
428 scripts
->Append(new base::StringValue("test.js"));
429 background
->Set("scripts", scripts
);
430 app
->Set("background", background
);
431 values
.Set(manifest_keys::kApp
, app
);
433 scoped_ptr
<base::ListValue
> permissions_list(new base::ListValue());
434 for (std::set
<std::string
>::const_iterator i
= permissions
.begin();
435 i
!= permissions
.end(); ++i
) {
436 permissions_list
->Append(new base::StringValue(*i
));
438 values
.Set("permissions", permissions_list
.release());
442 scoped_refptr
<Extension
> extension(Extension::Create(
443 base::FilePath(), Manifest::INTERNAL
, values
, Extension::NO_FLAGS
,
445 CHECK(extension
.get()) << error
;
449 TEST(ExtensionAPITest
, HostedAppPermissions
) {
450 scoped_refptr
<Extension
> extension
= CreateHostedApp();
452 scoped_ptr
<ExtensionAPI
> extension_api(
453 ExtensionAPI::CreateWithDefaultConfiguration());
455 // "runtime" and "tabs" should not be available in hosted apps.
456 EXPECT_FALSE(extension_api
->IsAvailable("runtime",
458 Feature::BLESSED_EXTENSION_CONTEXT
,
459 GURL()).is_available());
460 EXPECT_FALSE(extension_api
->IsAvailable("runtime.id",
462 Feature::BLESSED_EXTENSION_CONTEXT
,
463 GURL()).is_available());
464 EXPECT_FALSE(extension_api
->IsAvailable("runtime.sendMessage",
466 Feature::BLESSED_EXTENSION_CONTEXT
,
467 GURL()).is_available());
468 EXPECT_FALSE(extension_api
->IsAvailable("runtime.sendNativeMessage",
470 Feature::BLESSED_EXTENSION_CONTEXT
,
471 GURL()).is_available());
472 EXPECT_FALSE(extension_api
->IsAvailable("tabs.create",
474 Feature::BLESSED_EXTENSION_CONTEXT
,
475 GURL()).is_available());
478 TEST(ExtensionAPITest
, AppAndFriendsAvailability
) {
480 scoped_ptr
<ExtensionAPI
> extension_api(
481 ExtensionAPI::CreateWithDefaultConfiguration());
483 // Make sure chrome.app.runtime and chrome.app.window are available to apps,
484 // and chrome.app is not.
486 std::set
<std::string
> permissions
;
487 permissions
.insert("app.runtime");
488 permissions
.insert("app.window");
489 scoped_refptr
<Extension
> extension
=
490 CreatePackagedAppWithPermissions(permissions
);
491 EXPECT_FALSE(extension_api
->IsAvailable(
494 Feature::BLESSED_EXTENSION_CONTEXT
,
495 GURL("http://foo.com")).is_available());
496 EXPECT_TRUE(extension_api
->IsAvailable(
499 Feature::BLESSED_EXTENSION_CONTEXT
,
500 GURL("http://foo.com")).is_available());
501 EXPECT_TRUE(extension_api
->IsAvailable(
504 Feature::BLESSED_EXTENSION_CONTEXT
,
505 GURL("http://foo.com")).is_available());
507 // Make sure chrome.app.runtime and chrome.app.window are not available to
508 // extensions, and chrome.app is.
510 std::set
<std::string
> permissions
;
511 scoped_refptr
<Extension
> extension
=
512 CreateExtensionWithPermissions(permissions
);
513 EXPECT_TRUE(extension_api
->IsAvailable(
516 Feature::BLESSED_EXTENSION_CONTEXT
,
517 GURL("http://foo.com")).is_available());
518 EXPECT_FALSE(extension_api
->IsAvailable(
521 Feature::BLESSED_EXTENSION_CONTEXT
,
522 GURL("http://foo.com")).is_available());
523 EXPECT_FALSE(extension_api
->IsAvailable(
526 Feature::BLESSED_EXTENSION_CONTEXT
,
527 GURL("http://foo.com")).is_available());
531 TEST(ExtensionAPITest
, ExtensionWithDependencies
) {
532 // Extension with the "ttsEngine" permission but not the "tts" permission; it
533 // should not automatically get "tts" permission.
535 scoped_refptr
<Extension
> extension
=
536 CreateExtensionWithPermission("ttsEngine");
537 scoped_ptr
<ExtensionAPI
> api(
538 ExtensionAPI::CreateWithDefaultConfiguration());
539 EXPECT_TRUE(api
->IsAvailable("ttsEngine",
541 Feature::BLESSED_EXTENSION_CONTEXT
,
542 GURL()).is_available());
543 EXPECT_FALSE(api
->IsAvailable("tts",
545 Feature::BLESSED_EXTENSION_CONTEXT
,
546 GURL()).is_available());
549 // Conversely, extension with the "tts" permission but not the "ttsEngine"
550 // permission shouldn't get the "ttsEngine" permission.
552 scoped_refptr
<Extension
> extension
=
553 CreateExtensionWithPermission("tts");
554 scoped_ptr
<ExtensionAPI
> api(
555 ExtensionAPI::CreateWithDefaultConfiguration());
556 EXPECT_FALSE(api
->IsAvailable("ttsEngine",
558 Feature::BLESSED_EXTENSION_CONTEXT
,
559 GURL()).is_available());
560 EXPECT_TRUE(api
->IsAvailable("tts",
562 Feature::BLESSED_EXTENSION_CONTEXT
,
563 GURL()).is_available());
568 ExtensionAPI
* api
, const std::string
& api_name
, const std::string
& url
) {
569 return api
->IsAvailable(
570 api_name
, NULL
, Feature::WEB_PAGE_CONTEXT
, GURL(url
)).is_available();
573 TEST(ExtensionAPITest
, URLMatching
) {
574 scoped_ptr
<ExtensionAPI
> api(ExtensionAPI::CreateWithDefaultConfiguration());
576 // "app" API is available to all URLs that content scripts can be injected.
577 EXPECT_TRUE(MatchesURL(api
.get(), "app", "http://example.com/example.html"));
578 EXPECT_TRUE(MatchesURL(api
.get(), "app", "https://blah.net"));
579 EXPECT_TRUE(MatchesURL(api
.get(), "app", "file://somefile.html"));
581 // Also to internal URLs.
582 EXPECT_TRUE(MatchesURL(api
.get(), "app", "about:flags"));
583 EXPECT_TRUE(MatchesURL(api
.get(), "app", "chrome://flags"));
585 // "app" should be available to chrome-extension URLs.
586 EXPECT_TRUE(MatchesURL(api
.get(), "app",
587 "chrome-extension://fakeextension"));
589 // "storage" API (for example) isn't available to any URLs.
590 EXPECT_FALSE(MatchesURL(api
.get(), "storage",
591 "http://example.com/example.html"));
592 EXPECT_FALSE(MatchesURL(api
.get(), "storage", "https://blah.net"));
593 EXPECT_FALSE(MatchesURL(api
.get(), "storage", "file://somefile.html"));
594 EXPECT_FALSE(MatchesURL(api
.get(), "storage", "about:flags"));
595 EXPECT_FALSE(MatchesURL(api
.get(), "storage", "chrome://flags"));
596 EXPECT_FALSE(MatchesURL(api
.get(), "storage",
597 "chrome-extension://fakeextension"));
600 TEST(ExtensionAPITest
, GetAPINameFromFullName
) {
603 std::string api_name
;
604 std::string child_name
;
607 { "unknown", "", "" },
608 { "bookmarks", "bookmarks", "" },
609 { "bookmarks.", "bookmarks", "" },
610 { ".bookmarks", "", "" },
611 { "bookmarks.create", "bookmarks", "create" },
612 { "bookmarks.create.", "bookmarks", "create." },
613 { "bookmarks.create.monkey", "bookmarks", "create.monkey" },
614 { "bookmarkManagerPrivate", "bookmarkManagerPrivate", "" },
615 { "bookmarkManagerPrivate.copy", "bookmarkManagerPrivate", "copy" }
618 scoped_ptr
<ExtensionAPI
> api(ExtensionAPI::CreateWithDefaultConfiguration());
619 for (size_t i
= 0; i
< arraysize(test_data
); ++i
) {
620 std::string child_name
;
621 std::string api_name
= api
->GetAPINameFromFullName(test_data
[i
].input
,
623 EXPECT_EQ(test_data
[i
].api_name
, api_name
) << test_data
[i
].input
;
624 EXPECT_EQ(test_data
[i
].child_name
, child_name
) << test_data
[i
].input
;
628 TEST(ExtensionAPITest
, DefaultConfigurationFeatures
) {
629 scoped_ptr
<ExtensionAPI
> api(ExtensionAPI::CreateWithDefaultConfiguration());
631 SimpleFeature
* bookmarks
= static_cast<SimpleFeature
*>(
632 api
->GetFeatureDependency("api:bookmarks"));
633 SimpleFeature
* bookmarks_create
= static_cast<SimpleFeature
*>(
634 api
->GetFeatureDependency("api:bookmarks.create"));
637 SimpleFeature
* feature
;
638 // TODO(aa): More stuff to test over time.
644 for (size_t i
= 0; i
< arraysize(test_data
); ++i
) {
645 SimpleFeature
* feature
= test_data
[i
].feature
;
646 ASSERT_TRUE(feature
) << i
;
648 EXPECT_TRUE(feature
->whitelist()->empty());
649 EXPECT_TRUE(feature
->extension_types()->empty());
651 EXPECT_EQ(SimpleFeature::UNSPECIFIED_LOCATION
, feature
->location());
652 EXPECT_TRUE(feature
->platforms()->empty());
653 EXPECT_EQ(0, feature
->min_manifest_version());
654 EXPECT_EQ(0, feature
->max_manifest_version());
658 TEST(ExtensionAPITest
, FeaturesRequireContexts
) {
659 // TODO(cduvall): Make this check API featues.
660 scoped_ptr
<base::DictionaryValue
> api_features1(new base::DictionaryValue());
661 scoped_ptr
<base::DictionaryValue
> api_features2(new base::DictionaryValue());
662 base::DictionaryValue
* test1
= new base::DictionaryValue();
663 base::DictionaryValue
* test2
= new base::DictionaryValue();
664 base::ListValue
* contexts
= new base::ListValue();
665 contexts
->Append(new base::StringValue("content_script"));
666 test1
->Set("contexts", contexts
);
667 test1
->SetString("channel", "stable");
668 test2
->SetString("channel", "stable");
669 api_features1
->Set("test", test1
);
670 api_features2
->Set("test", test2
);
673 base::DictionaryValue
* api_features
;
676 { api_features1
.get(), true },
677 { api_features2
.get(), false }
680 for (size_t i
= 0; i
< arraysize(test_data
); ++i
) {
681 BaseFeatureProvider
api_feature_provider(*test_data
[i
].api_features
,
683 Feature
* feature
= api_feature_provider
.GetFeature("test");
684 EXPECT_EQ(test_data
[i
].expect_success
, feature
!= NULL
) << i
;
688 static void GetDictionaryFromList(const base::DictionaryValue
* schema
,
689 const std::string
& list_name
,
690 const int list_index
,
691 const base::DictionaryValue
** out
) {
692 const base::ListValue
* list
;
693 EXPECT_TRUE(schema
->GetList(list_name
, &list
));
694 EXPECT_TRUE(list
->GetDictionary(list_index
, out
));
697 TEST(ExtensionAPITest
, TypesHaveNamespace
) {
698 base::FilePath manifest_path
;
699 PathService::Get(chrome::DIR_TEST_DATA
, &manifest_path
);
700 manifest_path
= manifest_path
.AppendASCII("extensions")
701 .AppendASCII("extension_api_unittest")
702 .AppendASCII("types_have_namespace.json");
704 std::string manifest_str
;
705 ASSERT_TRUE(base::ReadFileToString(manifest_path
, &manifest_str
))
706 << "Failed to load: " << manifest_path
.value();
709 api
.RegisterSchemaResource("test.foo", 0);
710 api
.LoadSchema("test.foo", manifest_str
);
712 const base::DictionaryValue
* schema
= api
.GetSchema("test.foo");
714 const base::DictionaryValue
* dict
;
715 const base::DictionaryValue
* sub_dict
;
718 GetDictionaryFromList(schema
, "types", 0, &dict
);
719 EXPECT_TRUE(dict
->GetString("id", &type
));
720 EXPECT_EQ("test.foo.TestType", type
);
721 EXPECT_TRUE(dict
->GetString("customBindings", &type
));
722 EXPECT_EQ("test.foo.TestType", type
);
723 EXPECT_TRUE(dict
->GetDictionary("properties", &sub_dict
));
724 const base::DictionaryValue
* property
;
725 EXPECT_TRUE(sub_dict
->GetDictionary("foo", &property
));
726 EXPECT_TRUE(property
->GetString("$ref", &type
));
727 EXPECT_EQ("test.foo.OtherType", type
);
728 EXPECT_TRUE(sub_dict
->GetDictionary("bar", &property
));
729 EXPECT_TRUE(property
->GetString("$ref", &type
));
730 EXPECT_EQ("fully.qualified.Type", type
);
732 GetDictionaryFromList(schema
, "functions", 0, &dict
);
733 GetDictionaryFromList(dict
, "parameters", 0, &sub_dict
);
734 EXPECT_TRUE(sub_dict
->GetString("$ref", &type
));
735 EXPECT_EQ("test.foo.TestType", type
);
736 EXPECT_TRUE(dict
->GetDictionary("returns", &sub_dict
));
737 EXPECT_TRUE(sub_dict
->GetString("$ref", &type
));
738 EXPECT_EQ("fully.qualified.Type", type
);
740 GetDictionaryFromList(schema
, "functions", 1, &dict
);
741 GetDictionaryFromList(dict
, "parameters", 0, &sub_dict
);
742 EXPECT_TRUE(sub_dict
->GetString("$ref", &type
));
743 EXPECT_EQ("fully.qualified.Type", type
);
744 EXPECT_TRUE(dict
->GetDictionary("returns", &sub_dict
));
745 EXPECT_TRUE(sub_dict
->GetString("$ref", &type
));
746 EXPECT_EQ("test.foo.TestType", type
);
748 GetDictionaryFromList(schema
, "events", 0, &dict
);
749 GetDictionaryFromList(dict
, "parameters", 0, &sub_dict
);
750 EXPECT_TRUE(sub_dict
->GetString("$ref", &type
));
751 EXPECT_EQ("test.foo.TestType", type
);
752 GetDictionaryFromList(dict
, "parameters", 1, &sub_dict
);
753 EXPECT_TRUE(sub_dict
->GetString("$ref", &type
));
754 EXPECT_EQ("fully.qualified.Type", type
);
757 // Tests API availability with an empty manifest.
758 TEST(ExtensionAPITest
, NoPermissions
) {
760 const char* permission_name
;
763 // Test default module/package permission.
764 { "extension", true },
766 { "permissions", true },
769 // These require manifest keys.
770 { "browserAction", false },
771 { "pageAction", false },
772 { "pageActions", false },
773 // Some negative tests.
774 { "bookmarks", false },
775 { "cookies", false },
776 { "history", false },
777 // Make sure we find the module name after stripping '.'
778 { "runtime.abcd.onStartup", true },
779 // Test Tabs/Windows functions.
780 { "tabs.create", true },
781 { "tabs.duplicate", true },
782 { "tabs.onRemoved", true },
783 { "tabs.remove", true },
784 { "tabs.update", true },
785 { "tabs.getSelected", true },
786 { "tabs.onUpdated", true },
787 { "windows.get", true },
788 { "windows.create", true },
789 { "windows.remove", true },
790 { "windows.update", true },
791 // Test some whitelisted functions. These require no permissions.
792 { "app.getDetails", true },
793 { "app.getDetailsForFrame", true },
794 { "app.getIsInstalled", true },
795 { "app.installState", true },
796 { "app.runningState", true },
797 { "management.getPermissionWarningsByManifest", true },
798 { "management.uninstallSelf", true },
799 // But other functions in those modules do.
800 { "management.getPermissionWarningsById", false },
801 { "runtime.connectNative", false },
804 scoped_ptr
<ExtensionAPI
> extension_api(
805 ExtensionAPI::CreateWithDefaultConfiguration());
806 scoped_refptr
<Extension
> extension
=
807 BuildExtension(ExtensionBuilder().Pass()).Build();
809 for (size_t i
= 0; i
< arraysize(kTests
); ++i
) {
810 EXPECT_EQ(kTests
[i
].expect_success
,
811 extension_api
->IsAvailable(kTests
[i
].permission_name
,
813 Feature::BLESSED_EXTENSION_CONTEXT
,
814 GURL()).is_available())
815 << "Permission being tested: " << kTests
[i
].permission_name
;
819 // Tests that permissions that require manifest keys are available when those
821 TEST(ExtensionAPITest
, ManifestKeys
) {
822 scoped_ptr
<ExtensionAPI
> extension_api(
823 ExtensionAPI::CreateWithDefaultConfiguration());
825 scoped_refptr
<Extension
> extension
=
826 BuildExtension(ExtensionBuilder().Pass())
827 .MergeManifest(DictionaryBuilder().Set("browser_action",
828 DictionaryBuilder().Pass()))
831 EXPECT_TRUE(extension_api
->IsAvailable("browserAction",
833 Feature::BLESSED_EXTENSION_CONTEXT
,
834 GURL()).is_available());
835 EXPECT_FALSE(extension_api
->IsAvailable("pageAction",
837 Feature::BLESSED_EXTENSION_CONTEXT
,
838 GURL()).is_available());
841 } // namespace extensions