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/file_util.h"
11 #include "base/files/file_path.h"
12 #include "base/json/json_reader.h"
13 #include "base/json/json_writer.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/path_service.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/values.h"
19 #include "chrome/common/chrome_paths.h"
20 #include "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_UNSAFE(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
;
72 { "", "api", "" }, // assumes "api" when no type is present
73 { "foo", "api", "foo" },
74 { "foo:", "foo", "" },
75 { ":foo", "", "foo" },
76 { "foo:bar", "foo", "bar" },
77 { "foo:bar.baz", "foo", "bar.baz" }
80 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(test_data
); ++i
) {
81 std::string feature_type
;
82 std::string feature_name
;
83 ExtensionAPI::SplitDependencyName(test_data
[i
].input
, &feature_type
,
85 EXPECT_EQ(test_data
[i
].expected_feature_type
, feature_type
) << i
;
86 EXPECT_EQ(test_data
[i
].expected_feature_name
, feature_name
) << i
;
90 TEST(ExtensionAPITest
, IsPrivileged
) {
91 scoped_ptr
<ExtensionAPI
> extension_api(
92 ExtensionAPI::CreateWithDefaultConfiguration());
94 EXPECT_FALSE(extension_api
->IsPrivileged("runtime.connect"));
95 EXPECT_FALSE(extension_api
->IsPrivileged("runtime.onConnect"));
96 EXPECT_FALSE(extension_api
->IsPrivileged("runtime.lastError"));
98 // Exists, but privileged.
99 EXPECT_TRUE(extension_api
->IsPrivileged("extension.getViews"));
100 EXPECT_TRUE(extension_api
->IsPrivileged("history.search"));
102 // Whole APIs that are unprivileged.
103 EXPECT_FALSE(extension_api
->IsPrivileged("app.getDetails"));
104 EXPECT_FALSE(extension_api
->IsPrivileged("app.isInstalled"));
105 EXPECT_FALSE(extension_api
->IsPrivileged("storage.local"));
106 EXPECT_FALSE(extension_api
->IsPrivileged("storage.local.onChanged"));
107 EXPECT_FALSE(extension_api
->IsPrivileged("storage.local.set"));
108 EXPECT_FALSE(extension_api
->IsPrivileged("storage.local.MAX_ITEMS"));
109 EXPECT_FALSE(extension_api
->IsPrivileged("storage.set"));
112 TEST(ExtensionAPITest
, IsPrivilegedFeatures
) {
114 std::string api_full_name
;
115 bool expect_is_privilged
;
118 { "test1.foo", true },
120 { "test2.foo", false },
121 { "test2.bar", false },
122 { "test2.baz", true },
124 { "test3.foo", true },
128 base::FilePath api_features_path
;
129 PathService::Get(chrome::DIR_TEST_DATA
, &api_features_path
);
130 api_features_path
= api_features_path
.AppendASCII("extensions")
131 .AppendASCII("extension_api_unittest")
132 .AppendASCII("privileged_api_features.json");
134 std::string api_features_str
;
135 ASSERT_TRUE(base::ReadFileToString(
136 api_features_path
, &api_features_str
)) << "privileged_api_features.json";
138 scoped_ptr
<base::DictionaryValue
> value(static_cast<base::DictionaryValue
*>(
139 base::JSONReader::Read(api_features_str
)));
140 BaseFeatureProvider
api_feature_provider(*value
, CreateAPIFeature
);
142 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(test_data
); ++i
) {
144 api
.RegisterDependencyProvider("api", &api_feature_provider
);
145 EXPECT_EQ(test_data
[i
].expect_is_privilged
,
146 api
.IsPrivileged(test_data
[i
].api_full_name
)) << i
;
150 TEST(ExtensionAPITest
, APIFeatures
) {
152 std::string api_full_name
;
153 bool expect_is_available
;
154 Feature::Context context
;
157 { "test1", false, Feature::WEB_PAGE_CONTEXT
, GURL() },
158 { "test1", true, Feature::BLESSED_EXTENSION_CONTEXT
, GURL() },
159 { "test1", true, Feature::UNBLESSED_EXTENSION_CONTEXT
, GURL() },
160 { "test1", true, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
161 { "test2", true, Feature::WEB_PAGE_CONTEXT
, GURL("http://google.com") },
162 { "test2", false, Feature::BLESSED_EXTENSION_CONTEXT
,
163 GURL("http://google.com") },
164 { "test2.foo", false, Feature::WEB_PAGE_CONTEXT
,
165 GURL("http://google.com") },
166 { "test2.foo", true, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
167 { "test3", false, Feature::WEB_PAGE_CONTEXT
, GURL("http://google.com") },
168 { "test3.foo", true, Feature::WEB_PAGE_CONTEXT
, GURL("http://google.com") },
169 { "test3.foo", true, Feature::BLESSED_EXTENSION_CONTEXT
,
170 GURL("http://bad.com") },
171 { "test4", true, Feature::BLESSED_EXTENSION_CONTEXT
,
172 GURL("http://bad.com") },
173 { "test4.foo", false, Feature::BLESSED_EXTENSION_CONTEXT
,
174 GURL("http://bad.com") },
175 { "test4.foo", false, Feature::UNBLESSED_EXTENSION_CONTEXT
,
176 GURL("http://bad.com") },
177 { "test4.foo.foo", true, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
178 { "test5", true, Feature::WEB_PAGE_CONTEXT
, GURL("http://foo.com") },
179 { "test5", false, Feature::WEB_PAGE_CONTEXT
, GURL("http://bar.com") },
180 { "test5.blah", true, Feature::WEB_PAGE_CONTEXT
, GURL("http://foo.com") },
181 { "test5.blah", false, Feature::WEB_PAGE_CONTEXT
, GURL("http://bar.com") },
182 { "test6", false, Feature::BLESSED_EXTENSION_CONTEXT
, GURL() },
183 { "test6.foo", true, Feature::BLESSED_EXTENSION_CONTEXT
, GURL() },
184 { "test7", true, Feature::WEB_PAGE_CONTEXT
, GURL("http://foo.com") },
185 { "test7.foo", false, Feature::WEB_PAGE_CONTEXT
, GURL("http://bar.com") },
186 { "test7.foo", true, Feature::WEB_PAGE_CONTEXT
, GURL("http://foo.com") },
187 { "test7.bar", false, Feature::WEB_PAGE_CONTEXT
, GURL("http://bar.com") },
188 { "test7.bar", false, Feature::WEB_PAGE_CONTEXT
, GURL("http://foo.com") },
190 // Test parent/child.
191 { "parent1", true, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
192 { "parent1", false, Feature::WEB_PAGE_CONTEXT
, GURL("http://foo.com") },
193 { "parent1.child1", false, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
194 { "parent1.child1", true, Feature::WEB_PAGE_CONTEXT
,
195 GURL("http://foo.com") },
196 { "parent1.child2", true, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
197 { "parent1.child2", false, Feature::WEB_PAGE_CONTEXT
,
198 GURL("http://foo.com") },
199 { "parent2", true, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
200 { "parent2", true, Feature::BLESSED_EXTENSION_CONTEXT
, GURL() },
201 { "parent2", true, Feature::UNBLESSED_EXTENSION_CONTEXT
, GURL() },
202 { "parent2.child3", false, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
203 { "parent2.child3", true, Feature::BLESSED_EXTENSION_CONTEXT
, GURL() },
204 { "parent2.child3", false, Feature::UNBLESSED_EXTENSION_CONTEXT
, GURL() },
205 { "parent2.child3.child.child", true, Feature::CONTENT_SCRIPT_CONTEXT
,
207 { "parent2.child3.child.child", false, Feature::BLESSED_EXTENSION_CONTEXT
,
209 { "parent2.child3.child.child", true, Feature::UNBLESSED_EXTENSION_CONTEXT
,
211 { "parent3", true, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
212 { "parent3", false, Feature::BLESSED_EXTENSION_CONTEXT
, GURL() },
213 { "parent3", false, Feature::UNBLESSED_EXTENSION_CONTEXT
, GURL() },
214 { "parent3.noparent", true, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
215 { "parent3.noparent", true, Feature::BLESSED_EXTENSION_CONTEXT
, GURL() },
216 { "parent3.noparent", true, Feature::UNBLESSED_EXTENSION_CONTEXT
, GURL() },
217 { "parent3.noparent.child", true, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
218 { "parent3.noparent.child", true, Feature::BLESSED_EXTENSION_CONTEXT
,
220 { "parent3.noparent.child", true, Feature::UNBLESSED_EXTENSION_CONTEXT
,
224 base::FilePath api_features_path
;
225 PathService::Get(chrome::DIR_TEST_DATA
, &api_features_path
);
226 api_features_path
= api_features_path
.AppendASCII("extensions")
227 .AppendASCII("extension_api_unittest")
228 .AppendASCII("api_features.json");
230 std::string api_features_str
;
231 ASSERT_TRUE(base::ReadFileToString(
232 api_features_path
, &api_features_str
)) << "api_features.json";
234 scoped_ptr
<base::DictionaryValue
> value(static_cast<base::DictionaryValue
*>(
235 base::JSONReader::Read(api_features_str
)));
236 BaseFeatureProvider
api_feature_provider(*value
, CreateAPIFeature
);
238 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(test_data
); ++i
) {
240 api
.RegisterDependencyProvider("api", &api_feature_provider
);
241 for (base::DictionaryValue::Iterator
iter(*value
); !iter
.IsAtEnd();
243 if (iter
.key().find(".") == std::string::npos
)
244 api
.RegisterSchemaResource(iter
.key(), 0);
247 EXPECT_EQ(test_data
[i
].expect_is_available
,
248 api
.IsAvailable(test_data
[i
].api_full_name
,
250 test_data
[i
].context
,
251 test_data
[i
].url
).is_available()) << i
;
255 TEST(ExtensionAPITest
, IsAnyFeatureAvailableToContext
) {
256 scoped_refptr
<const Extension
> app
= ExtensionBuilder()
257 .SetManifest(DictionaryBuilder()
259 .Set("app", DictionaryBuilder()
260 .Set("background", DictionaryBuilder()
261 .Set("scripts", ListBuilder().Append("background.js"))))
263 .Set("manifest_version", 2)).Build();
264 scoped_refptr
<const Extension
> extension
= ExtensionBuilder()
265 .SetManifest(DictionaryBuilder()
266 .Set("name", "extension")
268 .Set("manifest_version", 2)).Build();
271 std::string api_full_name
;
272 bool expect_is_available
;
273 Feature::Context context
;
274 const Extension
* extension
;
277 { "test1", false, Feature::WEB_PAGE_CONTEXT
, NULL
, GURL() },
278 { "test1", true, Feature::UNBLESSED_EXTENSION_CONTEXT
, NULL
, GURL() },
279 { "test1", false, Feature::UNBLESSED_EXTENSION_CONTEXT
, app
.get(), GURL() },
280 { "test1", true, Feature::UNBLESSED_EXTENSION_CONTEXT
, extension
.get(),
282 { "test2", true, Feature::CONTENT_SCRIPT_CONTEXT
, NULL
, GURL() },
283 { "test2", true, Feature::WEB_PAGE_CONTEXT
, NULL
,
284 GURL("http://google.com") },
285 { "test2.foo", false, Feature::WEB_PAGE_CONTEXT
, NULL
,
286 GURL("http://google.com") },
287 { "test3", true, Feature::CONTENT_SCRIPT_CONTEXT
, NULL
, GURL() },
288 { "test3", true, Feature::WEB_PAGE_CONTEXT
, NULL
, GURL("http://foo.com") },
289 { "test4.foo", true, Feature::CONTENT_SCRIPT_CONTEXT
, NULL
, GURL() },
290 { "test7", false, Feature::WEB_PAGE_CONTEXT
, NULL
,
291 GURL("http://google.com") },
292 { "test7", true, Feature::WEB_PAGE_CONTEXT
, NULL
, GURL("http://foo.com") },
293 { "test7", false, Feature::WEB_PAGE_CONTEXT
, NULL
, GURL("http://bar.com") }
296 base::FilePath api_features_path
;
297 PathService::Get(chrome::DIR_TEST_DATA
, &api_features_path
);
298 api_features_path
= api_features_path
.AppendASCII("extensions")
299 .AppendASCII("extension_api_unittest")
300 .AppendASCII("api_features.json");
302 std::string api_features_str
;
303 ASSERT_TRUE(base::ReadFileToString(
304 api_features_path
, &api_features_str
)) << "api_features.json";
306 scoped_ptr
<base::DictionaryValue
> value(static_cast<base::DictionaryValue
*>(
307 base::JSONReader::Read(api_features_str
)));
308 BaseFeatureProvider
api_feature_provider(*value
, CreateAPIFeature
);
310 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(test_data
); ++i
) {
312 api
.RegisterDependencyProvider("api", &api_feature_provider
);
313 for (base::DictionaryValue::Iterator
iter(*value
); !iter
.IsAtEnd();
315 if (iter
.key().find(".") == std::string::npos
)
316 api
.RegisterSchemaResource(iter
.key(), 0);
319 Feature
* test_feature
=
320 api_feature_provider
.GetFeature(test_data
[i
].api_full_name
);
321 ASSERT_TRUE(test_feature
);
322 EXPECT_EQ(test_data
[i
].expect_is_available
,
323 api
.IsAnyFeatureAvailableToContext(*test_feature
,
324 test_data
[i
].extension
,
325 test_data
[i
].context
,
331 TEST(ExtensionAPITest
, LazyGetSchema
) {
332 scoped_ptr
<ExtensionAPI
> apis(ExtensionAPI::CreateWithDefaultConfiguration());
334 EXPECT_EQ(NULL
, apis
->GetSchema(std::string()));
335 EXPECT_EQ(NULL
, apis
->GetSchema(std::string()));
336 EXPECT_EQ(NULL
, apis
->GetSchema("experimental"));
337 EXPECT_EQ(NULL
, apis
->GetSchema("experimental"));
338 EXPECT_EQ(NULL
, apis
->GetSchema("foo"));
339 EXPECT_EQ(NULL
, apis
->GetSchema("foo"));
341 EXPECT_TRUE(apis
->GetSchema("dns"));
342 EXPECT_TRUE(apis
->GetSchema("dns"));
343 EXPECT_TRUE(apis
->GetSchema("extension"));
344 EXPECT_TRUE(apis
->GetSchema("extension"));
345 EXPECT_TRUE(apis
->GetSchema("infobars"));
346 EXPECT_TRUE(apis
->GetSchema("infobars"));
347 EXPECT_TRUE(apis
->GetSchema("omnibox"));
348 EXPECT_TRUE(apis
->GetSchema("omnibox"));
349 EXPECT_TRUE(apis
->GetSchema("storage"));
350 EXPECT_TRUE(apis
->GetSchema("storage"));
353 scoped_refptr
<Extension
> CreateExtensionWithPermissions(
354 const std::set
<std::string
>& permissions
) {
355 base::DictionaryValue manifest
;
356 manifest
.SetString("name", "extension");
357 manifest
.SetString("version", "1.0");
358 manifest
.SetInteger("manifest_version", 2);
360 scoped_ptr
<base::ListValue
> permissions_list(new base::ListValue());
361 for (std::set
<std::string
>::const_iterator i
= permissions
.begin();
362 i
!= permissions
.end(); ++i
) {
363 permissions_list
->Append(new base::StringValue(*i
));
365 manifest
.Set("permissions", permissions_list
.release());
369 scoped_refptr
<Extension
> extension(Extension::Create(
370 base::FilePath(), Manifest::UNPACKED
,
371 manifest
, Extension::NO_FLAGS
, &error
));
372 CHECK(extension
.get());
373 CHECK(error
.empty());
378 scoped_refptr
<Extension
> CreateExtensionWithPermission(
379 const std::string
& permission
) {
380 std::set
<std::string
> permissions
;
381 permissions
.insert(permission
);
382 return CreateExtensionWithPermissions(permissions
);
385 TEST(ExtensionAPITest
, ExtensionWithUnprivilegedAPIs
) {
386 scoped_refptr
<Extension
> extension
;
388 std::set
<std::string
> permissions
;
389 permissions
.insert("storage");
390 permissions
.insert("history");
391 extension
= CreateExtensionWithPermissions(permissions
);
394 scoped_ptr
<ExtensionAPI
> extension_api(
395 ExtensionAPI::CreateWithDefaultConfiguration());
397 const FeatureProvider
& api_features
= *FeatureProvider::GetAPIFeatures();
399 // "storage" is completely unprivileged.
400 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
401 *api_features
.GetFeature("storage"),
403 Feature::BLESSED_EXTENSION_CONTEXT
,
405 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
406 *api_features
.GetFeature("storage"),
408 Feature::UNBLESSED_EXTENSION_CONTEXT
,
410 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
411 *api_features
.GetFeature("storage"),
413 Feature::CONTENT_SCRIPT_CONTEXT
,
416 // "extension" is partially unprivileged.
417 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
418 *api_features
.GetFeature("extension"),
420 Feature::BLESSED_EXTENSION_CONTEXT
,
422 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
423 *api_features
.GetFeature("extension"),
425 Feature::UNBLESSED_EXTENSION_CONTEXT
,
427 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
428 *api_features
.GetFeature("extension"),
430 Feature::CONTENT_SCRIPT_CONTEXT
,
432 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
433 *api_features
.GetFeature("extension.getURL"),
435 Feature::CONTENT_SCRIPT_CONTEXT
,
438 // "history" is entirely privileged.
439 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
440 *api_features
.GetFeature("history"),
442 Feature::BLESSED_EXTENSION_CONTEXT
,
444 EXPECT_FALSE(extension_api
->IsAnyFeatureAvailableToContext(
445 *api_features
.GetFeature("history"),
447 Feature::UNBLESSED_EXTENSION_CONTEXT
,
449 EXPECT_FALSE(extension_api
->IsAnyFeatureAvailableToContext(
450 *api_features
.GetFeature("history"),
452 Feature::CONTENT_SCRIPT_CONTEXT
,
456 scoped_refptr
<Extension
> CreateHostedApp() {
457 base::DictionaryValue values
;
458 values
.SetString(manifest_keys::kName
, "test");
459 values
.SetString(manifest_keys::kVersion
, "0.1");
460 values
.Set(manifest_keys::kWebURLs
, new base::ListValue());
461 values
.SetString(manifest_keys::kLaunchWebURL
,
462 "http://www.example.com");
464 scoped_refptr
<Extension
> extension(Extension::Create(
465 base::FilePath(), Manifest::INTERNAL
, values
, Extension::NO_FLAGS
,
467 CHECK(extension
.get());
471 scoped_refptr
<Extension
> CreatePackagedAppWithPermissions(
472 const std::set
<std::string
>& permissions
) {
473 base::DictionaryValue values
;
474 values
.SetString(manifest_keys::kName
, "test");
475 values
.SetString(manifest_keys::kVersion
, "0.1");
476 values
.SetString(manifest_keys::kPlatformAppBackground
,
477 "http://www.example.com");
479 base::DictionaryValue
* app
= new base::DictionaryValue();
480 base::DictionaryValue
* background
= new base::DictionaryValue();
481 base::ListValue
* scripts
= new base::ListValue();
482 scripts
->Append(new base::StringValue("test.js"));
483 background
->Set("scripts", scripts
);
484 app
->Set("background", background
);
485 values
.Set(manifest_keys::kApp
, app
);
487 scoped_ptr
<base::ListValue
> permissions_list(new base::ListValue());
488 for (std::set
<std::string
>::const_iterator i
= permissions
.begin();
489 i
!= permissions
.end(); ++i
) {
490 permissions_list
->Append(new base::StringValue(*i
));
492 values
.Set("permissions", permissions_list
.release());
496 scoped_refptr
<Extension
> extension(Extension::Create(
497 base::FilePath(), Manifest::INTERNAL
, values
, Extension::NO_FLAGS
,
499 CHECK(extension
.get()) << error
;
503 TEST(ExtensionAPITest
, HostedAppPermissions
) {
504 scoped_refptr
<Extension
> extension
= CreateHostedApp();
506 scoped_ptr
<ExtensionAPI
> extension_api(
507 ExtensionAPI::CreateWithDefaultConfiguration());
509 // "runtime" and "tabs" should not be available in hosted apps.
510 EXPECT_FALSE(extension_api
->IsAvailable("runtime",
512 Feature::BLESSED_EXTENSION_CONTEXT
,
513 GURL()).is_available());
514 EXPECT_FALSE(extension_api
->IsAvailable("runtime.id",
516 Feature::BLESSED_EXTENSION_CONTEXT
,
517 GURL()).is_available());
518 EXPECT_FALSE(extension_api
->IsAvailable("runtime.sendMessage",
520 Feature::BLESSED_EXTENSION_CONTEXT
,
521 GURL()).is_available());
522 EXPECT_FALSE(extension_api
->IsAvailable("runtime.sendNativeMessage",
524 Feature::BLESSED_EXTENSION_CONTEXT
,
525 GURL()).is_available());
526 EXPECT_FALSE(extension_api
->IsAvailable("tabs.create",
528 Feature::BLESSED_EXTENSION_CONTEXT
,
529 GURL()).is_available());
532 TEST(ExtensionAPITest
, AppAndFriendsAvailability
) {
534 scoped_ptr
<ExtensionAPI
> extension_api(
535 ExtensionAPI::CreateWithDefaultConfiguration());
537 // Make sure chrome.app.runtime and chrome.app.window are available to apps,
538 // and chrome.app is not.
540 std::set
<std::string
> permissions
;
541 permissions
.insert("app.runtime");
542 permissions
.insert("app.window");
543 scoped_refptr
<Extension
> extension
=
544 CreatePackagedAppWithPermissions(permissions
);
545 EXPECT_FALSE(extension_api
->IsAvailable(
548 Feature::BLESSED_EXTENSION_CONTEXT
,
549 GURL("http://foo.com")).is_available());
550 EXPECT_TRUE(extension_api
->IsAvailable(
553 Feature::BLESSED_EXTENSION_CONTEXT
,
554 GURL("http://foo.com")).is_available());
555 EXPECT_TRUE(extension_api
->IsAvailable(
558 Feature::BLESSED_EXTENSION_CONTEXT
,
559 GURL("http://foo.com")).is_available());
561 // Make sure chrome.app.runtime and chrome.app.window are not available to
562 // extensions, and chrome.app is.
564 std::set
<std::string
> permissions
;
565 scoped_refptr
<Extension
> extension
=
566 CreateExtensionWithPermissions(permissions
);
567 EXPECT_TRUE(extension_api
->IsAvailable(
570 Feature::BLESSED_EXTENSION_CONTEXT
,
571 GURL("http://foo.com")).is_available());
572 EXPECT_FALSE(extension_api
->IsAvailable(
575 Feature::BLESSED_EXTENSION_CONTEXT
,
576 GURL("http://foo.com")).is_available());
577 EXPECT_FALSE(extension_api
->IsAvailable(
580 Feature::BLESSED_EXTENSION_CONTEXT
,
581 GURL("http://foo.com")).is_available());
585 TEST(ExtensionAPITest
, ExtensionWithDependencies
) {
586 // Extension with the "ttsEngine" permission but not the "tts" permission; it
587 // should not automatically get "tts" permission.
589 scoped_refptr
<Extension
> extension
=
590 CreateExtensionWithPermission("ttsEngine");
591 scoped_ptr
<ExtensionAPI
> api(
592 ExtensionAPI::CreateWithDefaultConfiguration());
593 EXPECT_TRUE(api
->IsAvailable("ttsEngine",
595 Feature::BLESSED_EXTENSION_CONTEXT
,
596 GURL()).is_available());
597 EXPECT_FALSE(api
->IsAvailable("tts",
599 Feature::BLESSED_EXTENSION_CONTEXT
,
600 GURL()).is_available());
603 // Conversely, extension with the "tts" permission but not the "ttsEngine"
604 // permission shouldn't get the "ttsEngine" permission.
606 scoped_refptr
<Extension
> extension
=
607 CreateExtensionWithPermission("tts");
608 scoped_ptr
<ExtensionAPI
> api(
609 ExtensionAPI::CreateWithDefaultConfiguration());
610 EXPECT_FALSE(api
->IsAvailable("ttsEngine",
612 Feature::BLESSED_EXTENSION_CONTEXT
,
613 GURL()).is_available());
614 EXPECT_TRUE(api
->IsAvailable("tts",
616 Feature::BLESSED_EXTENSION_CONTEXT
,
617 GURL()).is_available());
622 ExtensionAPI
* api
, const std::string
& api_name
, const std::string
& url
) {
623 return api
->IsAvailable(
624 api_name
, NULL
, Feature::WEB_PAGE_CONTEXT
, GURL(url
)).is_available();
627 TEST(ExtensionAPITest
, URLMatching
) {
628 scoped_ptr
<ExtensionAPI
> api(ExtensionAPI::CreateWithDefaultConfiguration());
630 // "app" API is available to all URLs that content scripts can be injected.
631 EXPECT_TRUE(MatchesURL(api
.get(), "app", "http://example.com/example.html"));
632 EXPECT_TRUE(MatchesURL(api
.get(), "app", "https://blah.net"));
633 EXPECT_TRUE(MatchesURL(api
.get(), "app", "file://somefile.html"));
635 // But not internal URLs.
636 EXPECT_FALSE(MatchesURL(api
.get(), "app", "about:flags"));
637 EXPECT_FALSE(MatchesURL(api
.get(), "app", "chrome://flags"));
639 // "app" should be available to chrome-extension URLs.
640 EXPECT_TRUE(MatchesURL(api
.get(), "app",
641 "chrome-extension://fakeextension"));
643 // "storage" API (for example) isn't available to any URLs.
644 EXPECT_FALSE(MatchesURL(api
.get(), "storage",
645 "http://example.com/example.html"));
646 EXPECT_FALSE(MatchesURL(api
.get(), "storage", "https://blah.net"));
647 EXPECT_FALSE(MatchesURL(api
.get(), "storage", "file://somefile.html"));
648 EXPECT_FALSE(MatchesURL(api
.get(), "storage", "about:flags"));
649 EXPECT_FALSE(MatchesURL(api
.get(), "storage", "chrome://flags"));
650 EXPECT_FALSE(MatchesURL(api
.get(), "storage",
651 "chrome-extension://fakeextension"));
654 TEST(ExtensionAPITest
, GetAPINameFromFullName
) {
657 std::string api_name
;
658 std::string child_name
;
661 { "unknown", "", "" },
662 { "bookmarks", "bookmarks", "" },
663 { "bookmarks.", "bookmarks", "" },
664 { ".bookmarks", "", "" },
665 { "bookmarks.create", "bookmarks", "create" },
666 { "bookmarks.create.", "bookmarks", "create." },
667 { "bookmarks.create.monkey", "bookmarks", "create.monkey" },
668 { "bookmarkManagerPrivate", "bookmarkManagerPrivate", "" },
669 { "bookmarkManagerPrivate.copy", "bookmarkManagerPrivate", "copy" }
672 scoped_ptr
<ExtensionAPI
> api(ExtensionAPI::CreateWithDefaultConfiguration());
673 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(test_data
); ++i
) {
674 std::string child_name
;
675 std::string api_name
= api
->GetAPINameFromFullName(test_data
[i
].input
,
677 EXPECT_EQ(test_data
[i
].api_name
, api_name
) << test_data
[i
].input
;
678 EXPECT_EQ(test_data
[i
].child_name
, child_name
) << test_data
[i
].input
;
682 TEST(ExtensionAPITest
, DefaultConfigurationFeatures
) {
683 scoped_ptr
<ExtensionAPI
> api(ExtensionAPI::CreateWithDefaultConfiguration());
685 SimpleFeature
* bookmarks
= static_cast<SimpleFeature
*>(
686 api
->GetFeatureDependency("api:bookmarks"));
687 SimpleFeature
* bookmarks_create
= static_cast<SimpleFeature
*>(
688 api
->GetFeatureDependency("api:bookmarks.create"));
691 SimpleFeature
* feature
;
692 // TODO(aa): More stuff to test over time.
698 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(test_data
); ++i
) {
699 SimpleFeature
* feature
= test_data
[i
].feature
;
700 ASSERT_TRUE(feature
) << i
;
702 EXPECT_TRUE(feature
->whitelist()->empty());
703 EXPECT_TRUE(feature
->extension_types()->empty());
705 EXPECT_EQ(1u, feature
->GetContexts()->size());
706 EXPECT_TRUE(feature
->GetContexts()->count(
707 Feature::BLESSED_EXTENSION_CONTEXT
));
709 EXPECT_EQ(SimpleFeature::UNSPECIFIED_LOCATION
, feature
->location());
710 EXPECT_TRUE(feature
->platforms()->empty());
711 EXPECT_EQ(0, feature
->min_manifest_version());
712 EXPECT_EQ(0, feature
->max_manifest_version());
716 TEST(ExtensionAPITest
, FeaturesRequireContexts
) {
717 // TODO(cduvall): Make this check API featues.
718 scoped_ptr
<base::DictionaryValue
> api_features1(new base::DictionaryValue());
719 scoped_ptr
<base::DictionaryValue
> api_features2(new base::DictionaryValue());
720 base::DictionaryValue
* test1
= new base::DictionaryValue();
721 base::DictionaryValue
* test2
= new base::DictionaryValue();
722 base::ListValue
* contexts
= new base::ListValue();
723 contexts
->Append(new base::StringValue("content_script"));
724 test1
->Set("contexts", contexts
);
725 test1
->SetString("channel", "stable");
726 test2
->SetString("channel", "stable");
727 api_features1
->Set("test", test1
);
728 api_features2
->Set("test", test2
);
731 base::DictionaryValue
* api_features
;
734 { api_features1
.get(), true },
735 { api_features2
.get(), false }
739 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(test_data
); ++i
) {
740 BaseFeatureProvider
api_feature_provider(*test_data
[i
].api_features
,
742 Feature
* feature
= api_feature_provider
.GetFeature("test");
743 EXPECT_EQ(test_data
[i
].expect_success
, feature
!= NULL
) << i
;
747 static void GetDictionaryFromList(const base::DictionaryValue
* schema
,
748 const std::string
& list_name
,
749 const int list_index
,
750 const base::DictionaryValue
** out
) {
751 const base::ListValue
* list
;
752 EXPECT_TRUE(schema
->GetList(list_name
, &list
));
753 EXPECT_TRUE(list
->GetDictionary(list_index
, out
));
756 TEST(ExtensionAPITest
, TypesHaveNamespace
) {
757 base::FilePath manifest_path
;
758 PathService::Get(chrome::DIR_TEST_DATA
, &manifest_path
);
759 manifest_path
= manifest_path
.AppendASCII("extensions")
760 .AppendASCII("extension_api_unittest")
761 .AppendASCII("types_have_namespace.json");
763 std::string manifest_str
;
764 ASSERT_TRUE(base::ReadFileToString(manifest_path
, &manifest_str
))
765 << "Failed to load: " << manifest_path
.value();
768 api
.RegisterSchemaResource("test.foo", 0);
769 api
.LoadSchema("test.foo", manifest_str
);
771 const base::DictionaryValue
* schema
= api
.GetSchema("test.foo");
773 const base::DictionaryValue
* dict
;
774 const base::DictionaryValue
* sub_dict
;
777 GetDictionaryFromList(schema
, "types", 0, &dict
);
778 EXPECT_TRUE(dict
->GetString("id", &type
));
779 EXPECT_EQ("test.foo.TestType", type
);
780 EXPECT_TRUE(dict
->GetString("customBindings", &type
));
781 EXPECT_EQ("test.foo.TestType", type
);
782 EXPECT_TRUE(dict
->GetDictionary("properties", &sub_dict
));
783 const base::DictionaryValue
* property
;
784 EXPECT_TRUE(sub_dict
->GetDictionary("foo", &property
));
785 EXPECT_TRUE(property
->GetString("$ref", &type
));
786 EXPECT_EQ("test.foo.OtherType", type
);
787 EXPECT_TRUE(sub_dict
->GetDictionary("bar", &property
));
788 EXPECT_TRUE(property
->GetString("$ref", &type
));
789 EXPECT_EQ("fully.qualified.Type", type
);
791 GetDictionaryFromList(schema
, "functions", 0, &dict
);
792 GetDictionaryFromList(dict
, "parameters", 0, &sub_dict
);
793 EXPECT_TRUE(sub_dict
->GetString("$ref", &type
));
794 EXPECT_EQ("test.foo.TestType", type
);
795 EXPECT_TRUE(dict
->GetDictionary("returns", &sub_dict
));
796 EXPECT_TRUE(sub_dict
->GetString("$ref", &type
));
797 EXPECT_EQ("fully.qualified.Type", type
);
799 GetDictionaryFromList(schema
, "functions", 1, &dict
);
800 GetDictionaryFromList(dict
, "parameters", 0, &sub_dict
);
801 EXPECT_TRUE(sub_dict
->GetString("$ref", &type
));
802 EXPECT_EQ("fully.qualified.Type", type
);
803 EXPECT_TRUE(dict
->GetDictionary("returns", &sub_dict
));
804 EXPECT_TRUE(sub_dict
->GetString("$ref", &type
));
805 EXPECT_EQ("test.foo.TestType", type
);
807 GetDictionaryFromList(schema
, "events", 0, &dict
);
808 GetDictionaryFromList(dict
, "parameters", 0, &sub_dict
);
809 EXPECT_TRUE(sub_dict
->GetString("$ref", &type
));
810 EXPECT_EQ("test.foo.TestType", type
);
811 GetDictionaryFromList(dict
, "parameters", 1, &sub_dict
);
812 EXPECT_TRUE(sub_dict
->GetString("$ref", &type
));
813 EXPECT_EQ("fully.qualified.Type", type
);
816 // Tests API availability with an empty manifest.
817 TEST(ExtensionAPITest
, NoPermissions
) {
819 const char* permission_name
;
822 // Test default module/package permission.
823 { "extension", true },
825 { "permissions", true },
828 // These require manifest keys.
829 { "browserAction", false },
830 { "pageAction", false },
831 { "pageActions", false },
832 // Some negative tests.
833 { "bookmarks", false },
834 { "cookies", false },
835 { "history", false },
836 // Make sure we find the module name after stripping '.'
837 { "runtime.abcd.onStartup", true },
838 // Test Tabs/Windows functions.
839 { "tabs.create", true },
840 { "tabs.duplicate", true },
841 { "tabs.onRemoved", true },
842 { "tabs.remove", true },
843 { "tabs.update", true },
844 { "tabs.getSelected", true },
845 { "tabs.onUpdated", true },
846 { "windows.get", true },
847 { "windows.create", true },
848 { "windows.remove", true },
849 { "windows.update", true },
850 // Test some whitelisted functions. These require no permissions.
851 { "app.getDetails", true },
852 { "app.getDetailsForFrame", true },
853 { "app.getIsInstalled", true },
854 { "app.installState", true },
855 { "app.runningState", true },
856 { "management.getPermissionWarningsByManifest", true },
857 { "management.uninstallSelf", true },
858 // But other functions in those modules do.
859 { "management.getPermissionWarningsById", false },
862 scoped_ptr
<ExtensionAPI
> extension_api(
863 ExtensionAPI::CreateWithDefaultConfiguration());
864 scoped_refptr
<Extension
> extension
=
865 BuildExtension(ExtensionBuilder().Pass()).Build();
867 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(kTests
); ++i
) {
868 EXPECT_EQ(kTests
[i
].expect_success
,
869 extension_api
->IsAvailable(kTests
[i
].permission_name
,
871 Feature::BLESSED_EXTENSION_CONTEXT
,
872 GURL()).is_available())
873 << "Permission being tested: " << kTests
[i
].permission_name
;
877 // Tests that permissions that require manifest keys are available when those
879 TEST(ExtensionAPITest
, ManifestKeys
) {
880 scoped_ptr
<ExtensionAPI
> extension_api(
881 ExtensionAPI::CreateWithDefaultConfiguration());
883 scoped_refptr
<Extension
> extension
=
884 BuildExtension(ExtensionBuilder().Pass())
885 .MergeManifest(DictionaryBuilder().Set("browser_action",
886 DictionaryBuilder().Pass()))
889 EXPECT_TRUE(extension_api
->IsAvailable("browserAction",
891 Feature::BLESSED_EXTENSION_CONTEXT
,
892 GURL()).is_available());
893 EXPECT_FALSE(extension_api
->IsAvailable("pageAction",
895 Feature::BLESSED_EXTENSION_CONTEXT
,
896 GURL()).is_available());
899 } // namespace extensions