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_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
;
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_UNSAFE(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
, IsAvailableInUntrustedContext
) {
89 scoped_ptr
<ExtensionAPI
> extension_api(
90 ExtensionAPI::CreateWithDefaultConfiguration());
91 scoped_refptr
<const Extension
> extension
=
93 .SetManifest(DictionaryBuilder()
94 .Set("name", "extension")
96 .Set("permissions", ListBuilder().Append("storage"))
97 .Set("manifest_version", 2))
100 EXPECT_TRUE(extension_api
->IsAvailableInUntrustedContext("runtime.connect",
102 EXPECT_TRUE(extension_api
->IsAvailableInUntrustedContext("runtime.onConnect",
104 EXPECT_TRUE(extension_api
->IsAvailableInUntrustedContext("runtime.lastError",
107 // Exists, but privileged.
108 EXPECT_FALSE(extension_api
->IsAvailableInUntrustedContext(
109 "extension.getViews", extension
.get()));
110 EXPECT_FALSE(extension_api
->IsAvailableInUntrustedContext("history.search",
113 // Whole APIs that are unprivileged.
115 extension_api
->IsAvailableInUntrustedContext("app", extension
.get()));
116 EXPECT_TRUE(extension_api
->IsAvailableInUntrustedContext("app.getDetails",
118 // There is no feature "app.isInstalled" (it's "app.getIsInstalled") but
119 // this should be available nonetheless.
120 EXPECT_TRUE(extension_api
->IsAvailableInUntrustedContext("app.isInstalled",
122 EXPECT_TRUE(extension_api
->IsAvailableInUntrustedContext("storage.local",
124 EXPECT_TRUE(extension_api
->IsAvailableInUntrustedContext(
125 "storage.local.onChanged", extension
.get()));
126 EXPECT_TRUE(extension_api
->IsAvailableInUntrustedContext("storage.local.set",
128 EXPECT_TRUE(extension_api
->IsAvailableInUntrustedContext(
129 "storage.local.MAX_ITEMS", extension
.get()));
130 EXPECT_TRUE(extension_api
->IsAvailableInUntrustedContext("storage.set",
133 // APIs which override unprivileged APIs.
134 EXPECT_FALSE(extension_api
->IsAvailableInUntrustedContext("app.runtime",
136 EXPECT_FALSE(extension_api
->IsAvailableInUntrustedContext("app.window",
140 TEST(ExtensionAPITest
, IsAvailableInUntrustedContextFeatures
) {
142 std::string api_full_name
;
143 bool expect_is_available
;
144 } test_data
[] = {{"test1", true},
145 {"test1.foo", false},
149 {"test2.baz", false},
150 {"test2.qux", false},
152 {"test3.foo", false},
156 base::FilePath api_features_path
;
157 PathService::Get(chrome::DIR_TEST_DATA
, &api_features_path
);
158 api_features_path
= api_features_path
.AppendASCII("extensions")
159 .AppendASCII("extension_api_unittest")
160 .AppendASCII("privileged_api_features.json");
162 std::string api_features_str
;
163 ASSERT_TRUE(base::ReadFileToString(
164 api_features_path
, &api_features_str
)) << "privileged_api_features.json";
166 scoped_ptr
<base::DictionaryValue
> value(static_cast<base::DictionaryValue
*>(
167 base::JSONReader::Read(api_features_str
)));
168 BaseFeatureProvider
api_feature_provider(*value
, CreateAPIFeature
);
170 scoped_refptr
<Extension
> extension
=
171 BuildExtension(ExtensionBuilder().Pass()).Build();
173 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(test_data
); ++i
) {
175 api
.RegisterDependencyProvider("api", &api_feature_provider
);
176 EXPECT_EQ(test_data
[i
].expect_is_available
,
177 api
.IsAvailableInUntrustedContext(test_data
[i
].api_full_name
,
183 TEST(ExtensionAPITest
, APIFeatures
) {
185 std::string api_full_name
;
186 bool expect_is_available
;
187 Feature::Context context
;
190 { "test1", false, Feature::WEB_PAGE_CONTEXT
, GURL() },
191 { "test1", true, Feature::BLESSED_EXTENSION_CONTEXT
, GURL() },
192 { "test1", true, Feature::UNBLESSED_EXTENSION_CONTEXT
, GURL() },
193 { "test1", true, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
194 { "test2", true, Feature::WEB_PAGE_CONTEXT
, GURL("http://google.com") },
195 { "test2", false, Feature::BLESSED_EXTENSION_CONTEXT
,
196 GURL("http://google.com") },
197 { "test2.foo", false, Feature::WEB_PAGE_CONTEXT
,
198 GURL("http://google.com") },
199 { "test2.foo", true, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
200 { "test3", false, Feature::WEB_PAGE_CONTEXT
, GURL("http://google.com") },
201 { "test3.foo", true, Feature::WEB_PAGE_CONTEXT
, GURL("http://google.com") },
202 { "test3.foo", true, Feature::BLESSED_EXTENSION_CONTEXT
,
203 GURL("http://bad.com") },
204 { "test4", true, Feature::BLESSED_EXTENSION_CONTEXT
,
205 GURL("http://bad.com") },
206 { "test4.foo", false, Feature::BLESSED_EXTENSION_CONTEXT
,
207 GURL("http://bad.com") },
208 { "test4.foo", false, Feature::UNBLESSED_EXTENSION_CONTEXT
,
209 GURL("http://bad.com") },
210 { "test4.foo.foo", true, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
211 { "test5", true, Feature::WEB_PAGE_CONTEXT
, GURL("http://foo.com") },
212 { "test5", false, Feature::WEB_PAGE_CONTEXT
, GURL("http://bar.com") },
213 { "test5.blah", true, Feature::WEB_PAGE_CONTEXT
, GURL("http://foo.com") },
214 { "test5.blah", false, Feature::WEB_PAGE_CONTEXT
, GURL("http://bar.com") },
215 { "test6", false, Feature::BLESSED_EXTENSION_CONTEXT
, GURL() },
216 { "test6.foo", true, Feature::BLESSED_EXTENSION_CONTEXT
, GURL() },
217 { "test7", true, Feature::WEB_PAGE_CONTEXT
, GURL("http://foo.com") },
218 { "test7.foo", false, Feature::WEB_PAGE_CONTEXT
, GURL("http://bar.com") },
219 { "test7.foo", true, Feature::WEB_PAGE_CONTEXT
, GURL("http://foo.com") },
220 { "test7.bar", false, Feature::WEB_PAGE_CONTEXT
, GURL("http://bar.com") },
221 { "test7.bar", false, Feature::WEB_PAGE_CONTEXT
, GURL("http://foo.com") },
223 // Test parent/child.
224 { "parent1", true, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
225 { "parent1", false, Feature::WEB_PAGE_CONTEXT
, GURL("http://foo.com") },
226 { "parent1.child1", false, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
227 { "parent1.child1", true, Feature::WEB_PAGE_CONTEXT
,
228 GURL("http://foo.com") },
229 { "parent1.child2", true, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
230 { "parent1.child2", false, Feature::WEB_PAGE_CONTEXT
,
231 GURL("http://foo.com") },
232 { "parent2", true, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
233 { "parent2", true, Feature::BLESSED_EXTENSION_CONTEXT
, GURL() },
234 { "parent2", true, Feature::UNBLESSED_EXTENSION_CONTEXT
, GURL() },
235 { "parent2.child3", false, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
236 { "parent2.child3", true, Feature::BLESSED_EXTENSION_CONTEXT
, GURL() },
237 { "parent2.child3", false, Feature::UNBLESSED_EXTENSION_CONTEXT
, GURL() },
238 { "parent2.child3.child.child", true, Feature::CONTENT_SCRIPT_CONTEXT
,
240 { "parent2.child3.child.child", false, Feature::BLESSED_EXTENSION_CONTEXT
,
242 { "parent2.child3.child.child", true, Feature::UNBLESSED_EXTENSION_CONTEXT
,
244 { "parent3", true, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
245 { "parent3", false, Feature::BLESSED_EXTENSION_CONTEXT
, GURL() },
246 { "parent3", false, Feature::UNBLESSED_EXTENSION_CONTEXT
, GURL() },
247 { "parent3.noparent", true, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
248 { "parent3.noparent", true, Feature::BLESSED_EXTENSION_CONTEXT
, GURL() },
249 { "parent3.noparent", true, Feature::UNBLESSED_EXTENSION_CONTEXT
, GURL() },
250 { "parent3.noparent.child", true, Feature::CONTENT_SCRIPT_CONTEXT
, GURL() },
251 { "parent3.noparent.child", true, Feature::BLESSED_EXTENSION_CONTEXT
,
253 { "parent3.noparent.child", true, Feature::UNBLESSED_EXTENSION_CONTEXT
,
257 base::FilePath api_features_path
;
258 PathService::Get(chrome::DIR_TEST_DATA
, &api_features_path
);
259 api_features_path
= api_features_path
.AppendASCII("extensions")
260 .AppendASCII("extension_api_unittest")
261 .AppendASCII("api_features.json");
263 std::string api_features_str
;
264 ASSERT_TRUE(base::ReadFileToString(
265 api_features_path
, &api_features_str
)) << "api_features.json";
267 scoped_ptr
<base::DictionaryValue
> value(static_cast<base::DictionaryValue
*>(
268 base::JSONReader::Read(api_features_str
)));
269 BaseFeatureProvider
api_feature_provider(*value
, CreateAPIFeature
);
271 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(test_data
); ++i
) {
273 api
.RegisterDependencyProvider("api", &api_feature_provider
);
274 for (base::DictionaryValue::Iterator
iter(*value
); !iter
.IsAtEnd();
276 if (iter
.key().find(".") == std::string::npos
)
277 api
.RegisterSchemaResource(iter
.key(), 0);
280 ExtensionAPI::OverrideSharedInstanceForTest
scope(&api
);
281 bool expected
= test_data
[i
].expect_is_available
;
282 Feature::Availability availability
=
283 api
.IsAvailable(test_data
[i
].api_full_name
,
285 test_data
[i
].context
,
287 EXPECT_EQ(expected
, availability
.is_available())
288 << base::StringPrintf("Test %d: Feature '%s' was %s: %s",
290 test_data
[i
].api_full_name
.c_str(),
291 expected
? "not available" : "available",
292 availability
.message().c_str());
296 TEST(ExtensionAPITest
, IsAnyFeatureAvailableToContext
) {
297 scoped_refptr
<const Extension
> app
= ExtensionBuilder()
298 .SetManifest(DictionaryBuilder()
300 .Set("app", DictionaryBuilder()
301 .Set("background", DictionaryBuilder()
302 .Set("scripts", ListBuilder().Append("background.js"))))
304 .Set("manifest_version", 2)).Build();
305 scoped_refptr
<const Extension
> extension
= ExtensionBuilder()
306 .SetManifest(DictionaryBuilder()
307 .Set("name", "extension")
309 .Set("manifest_version", 2)).Build();
312 std::string api_full_name
;
313 bool expect_is_available
;
314 Feature::Context context
;
315 const Extension
* extension
;
318 { "test1", false, Feature::WEB_PAGE_CONTEXT
, NULL
, GURL() },
319 { "test1", true, Feature::UNBLESSED_EXTENSION_CONTEXT
, NULL
, GURL() },
320 { "test1", false, Feature::UNBLESSED_EXTENSION_CONTEXT
, app
.get(), GURL() },
321 { "test1", true, Feature::UNBLESSED_EXTENSION_CONTEXT
, extension
.get(),
323 { "test2", true, Feature::CONTENT_SCRIPT_CONTEXT
, NULL
, GURL() },
324 { "test2", true, Feature::WEB_PAGE_CONTEXT
, NULL
,
325 GURL("http://google.com") },
326 { "test2.foo", false, Feature::WEB_PAGE_CONTEXT
, NULL
,
327 GURL("http://google.com") },
328 { "test3", true, Feature::CONTENT_SCRIPT_CONTEXT
, NULL
, GURL() },
329 { "test3", true, Feature::WEB_PAGE_CONTEXT
, NULL
, GURL("http://foo.com") },
330 { "test4.foo", true, Feature::CONTENT_SCRIPT_CONTEXT
, NULL
, GURL() },
331 { "test7", false, Feature::WEB_PAGE_CONTEXT
, NULL
,
332 GURL("http://google.com") },
333 { "test7", true, Feature::WEB_PAGE_CONTEXT
, NULL
, GURL("http://foo.com") },
334 { "test7", false, Feature::WEB_PAGE_CONTEXT
, NULL
, GURL("http://bar.com") }
337 base::FilePath api_features_path
;
338 PathService::Get(chrome::DIR_TEST_DATA
, &api_features_path
);
339 api_features_path
= api_features_path
.AppendASCII("extensions")
340 .AppendASCII("extension_api_unittest")
341 .AppendASCII("api_features.json");
343 std::string api_features_str
;
344 ASSERT_TRUE(base::ReadFileToString(
345 api_features_path
, &api_features_str
)) << "api_features.json";
347 scoped_ptr
<base::DictionaryValue
> value(static_cast<base::DictionaryValue
*>(
348 base::JSONReader::Read(api_features_str
)));
349 BaseFeatureProvider
api_feature_provider(*value
, CreateAPIFeature
);
351 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(test_data
); ++i
) {
353 api
.RegisterDependencyProvider("api", &api_feature_provider
);
354 for (base::DictionaryValue::Iterator
iter(*value
); !iter
.IsAtEnd();
356 if (iter
.key().find(".") == std::string::npos
)
357 api
.RegisterSchemaResource(iter
.key(), 0);
360 Feature
* test_feature
=
361 api_feature_provider
.GetFeature(test_data
[i
].api_full_name
);
362 ASSERT_TRUE(test_feature
);
363 EXPECT_EQ(test_data
[i
].expect_is_available
,
364 api
.IsAnyFeatureAvailableToContext(*test_feature
,
365 test_data
[i
].extension
,
366 test_data
[i
].context
,
372 TEST(ExtensionAPITest
, LazyGetSchema
) {
373 scoped_ptr
<ExtensionAPI
> apis(ExtensionAPI::CreateWithDefaultConfiguration());
375 EXPECT_EQ(NULL
, apis
->GetSchema(std::string()));
376 EXPECT_EQ(NULL
, apis
->GetSchema(std::string()));
377 EXPECT_EQ(NULL
, apis
->GetSchema("experimental"));
378 EXPECT_EQ(NULL
, apis
->GetSchema("experimental"));
379 EXPECT_EQ(NULL
, apis
->GetSchema("foo"));
380 EXPECT_EQ(NULL
, apis
->GetSchema("foo"));
382 EXPECT_TRUE(apis
->GetSchema("dns"));
383 EXPECT_TRUE(apis
->GetSchema("dns"));
384 EXPECT_TRUE(apis
->GetSchema("extension"));
385 EXPECT_TRUE(apis
->GetSchema("extension"));
386 EXPECT_TRUE(apis
->GetSchema("infobars"));
387 EXPECT_TRUE(apis
->GetSchema("infobars"));
388 EXPECT_TRUE(apis
->GetSchema("omnibox"));
389 EXPECT_TRUE(apis
->GetSchema("omnibox"));
390 EXPECT_TRUE(apis
->GetSchema("storage"));
391 EXPECT_TRUE(apis
->GetSchema("storage"));
394 scoped_refptr
<Extension
> CreateExtensionWithPermissions(
395 const std::set
<std::string
>& permissions
) {
396 base::DictionaryValue manifest
;
397 manifest
.SetString("name", "extension");
398 manifest
.SetString("version", "1.0");
399 manifest
.SetInteger("manifest_version", 2);
401 scoped_ptr
<base::ListValue
> permissions_list(new base::ListValue());
402 for (std::set
<std::string
>::const_iterator i
= permissions
.begin();
403 i
!= permissions
.end(); ++i
) {
404 permissions_list
->Append(new base::StringValue(*i
));
406 manifest
.Set("permissions", permissions_list
.release());
410 scoped_refptr
<Extension
> extension(Extension::Create(
411 base::FilePath(), Manifest::UNPACKED
,
412 manifest
, Extension::NO_FLAGS
, &error
));
413 CHECK(extension
.get());
414 CHECK(error
.empty());
419 scoped_refptr
<Extension
> CreateExtensionWithPermission(
420 const std::string
& permission
) {
421 std::set
<std::string
> permissions
;
422 permissions
.insert(permission
);
423 return CreateExtensionWithPermissions(permissions
);
426 TEST(ExtensionAPITest
, ExtensionWithUnprivilegedAPIs
) {
427 scoped_refptr
<Extension
> extension
;
429 std::set
<std::string
> permissions
;
430 permissions
.insert("storage");
431 permissions
.insert("history");
432 extension
= CreateExtensionWithPermissions(permissions
);
435 scoped_ptr
<ExtensionAPI
> extension_api(
436 ExtensionAPI::CreateWithDefaultConfiguration());
438 const FeatureProvider
& api_features
= *FeatureProvider::GetAPIFeatures();
440 // "storage" is completely unprivileged.
441 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
442 *api_features
.GetFeature("storage"),
444 Feature::BLESSED_EXTENSION_CONTEXT
,
446 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
447 *api_features
.GetFeature("storage"),
449 Feature::UNBLESSED_EXTENSION_CONTEXT
,
451 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
452 *api_features
.GetFeature("storage"),
454 Feature::CONTENT_SCRIPT_CONTEXT
,
457 // "extension" is partially unprivileged.
458 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
459 *api_features
.GetFeature("extension"),
461 Feature::BLESSED_EXTENSION_CONTEXT
,
463 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
464 *api_features
.GetFeature("extension"),
466 Feature::UNBLESSED_EXTENSION_CONTEXT
,
468 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
469 *api_features
.GetFeature("extension"),
471 Feature::CONTENT_SCRIPT_CONTEXT
,
473 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
474 *api_features
.GetFeature("extension.getURL"),
476 Feature::CONTENT_SCRIPT_CONTEXT
,
479 // "history" is entirely privileged.
480 EXPECT_TRUE(extension_api
->IsAnyFeatureAvailableToContext(
481 *api_features
.GetFeature("history"),
483 Feature::BLESSED_EXTENSION_CONTEXT
,
485 EXPECT_FALSE(extension_api
->IsAnyFeatureAvailableToContext(
486 *api_features
.GetFeature("history"),
488 Feature::UNBLESSED_EXTENSION_CONTEXT
,
490 EXPECT_FALSE(extension_api
->IsAnyFeatureAvailableToContext(
491 *api_features
.GetFeature("history"),
493 Feature::CONTENT_SCRIPT_CONTEXT
,
497 scoped_refptr
<Extension
> CreateHostedApp() {
498 base::DictionaryValue values
;
499 values
.SetString(manifest_keys::kName
, "test");
500 values
.SetString(manifest_keys::kVersion
, "0.1");
501 values
.Set(manifest_keys::kWebURLs
, new base::ListValue());
502 values
.SetString(manifest_keys::kLaunchWebURL
,
503 "http://www.example.com");
505 scoped_refptr
<Extension
> extension(Extension::Create(
506 base::FilePath(), Manifest::INTERNAL
, values
, Extension::NO_FLAGS
,
508 CHECK(extension
.get());
512 scoped_refptr
<Extension
> CreatePackagedAppWithPermissions(
513 const std::set
<std::string
>& permissions
) {
514 base::DictionaryValue values
;
515 values
.SetString(manifest_keys::kName
, "test");
516 values
.SetString(manifest_keys::kVersion
, "0.1");
517 values
.SetString(manifest_keys::kPlatformAppBackground
,
518 "http://www.example.com");
520 base::DictionaryValue
* app
= new base::DictionaryValue();
521 base::DictionaryValue
* background
= new base::DictionaryValue();
522 base::ListValue
* scripts
= new base::ListValue();
523 scripts
->Append(new base::StringValue("test.js"));
524 background
->Set("scripts", scripts
);
525 app
->Set("background", background
);
526 values
.Set(manifest_keys::kApp
, app
);
528 scoped_ptr
<base::ListValue
> permissions_list(new base::ListValue());
529 for (std::set
<std::string
>::const_iterator i
= permissions
.begin();
530 i
!= permissions
.end(); ++i
) {
531 permissions_list
->Append(new base::StringValue(*i
));
533 values
.Set("permissions", permissions_list
.release());
537 scoped_refptr
<Extension
> extension(Extension::Create(
538 base::FilePath(), Manifest::INTERNAL
, values
, Extension::NO_FLAGS
,
540 CHECK(extension
.get()) << error
;
544 TEST(ExtensionAPITest
, HostedAppPermissions
) {
545 scoped_refptr
<Extension
> extension
= CreateHostedApp();
547 scoped_ptr
<ExtensionAPI
> extension_api(
548 ExtensionAPI::CreateWithDefaultConfiguration());
550 // "runtime" and "tabs" should not be available in hosted apps.
551 EXPECT_FALSE(extension_api
->IsAvailable("runtime",
553 Feature::BLESSED_EXTENSION_CONTEXT
,
554 GURL()).is_available());
555 EXPECT_FALSE(extension_api
->IsAvailable("runtime.id",
557 Feature::BLESSED_EXTENSION_CONTEXT
,
558 GURL()).is_available());
559 EXPECT_FALSE(extension_api
->IsAvailable("runtime.sendMessage",
561 Feature::BLESSED_EXTENSION_CONTEXT
,
562 GURL()).is_available());
563 EXPECT_FALSE(extension_api
->IsAvailable("runtime.sendNativeMessage",
565 Feature::BLESSED_EXTENSION_CONTEXT
,
566 GURL()).is_available());
567 EXPECT_FALSE(extension_api
->IsAvailable("tabs.create",
569 Feature::BLESSED_EXTENSION_CONTEXT
,
570 GURL()).is_available());
573 TEST(ExtensionAPITest
, AppAndFriendsAvailability
) {
575 scoped_ptr
<ExtensionAPI
> extension_api(
576 ExtensionAPI::CreateWithDefaultConfiguration());
578 // Make sure chrome.app.runtime and chrome.app.window are available to apps,
579 // and chrome.app is not.
581 std::set
<std::string
> permissions
;
582 permissions
.insert("app.runtime");
583 permissions
.insert("app.window");
584 scoped_refptr
<Extension
> extension
=
585 CreatePackagedAppWithPermissions(permissions
);
586 EXPECT_FALSE(extension_api
->IsAvailable(
589 Feature::BLESSED_EXTENSION_CONTEXT
,
590 GURL("http://foo.com")).is_available());
591 EXPECT_TRUE(extension_api
->IsAvailable(
594 Feature::BLESSED_EXTENSION_CONTEXT
,
595 GURL("http://foo.com")).is_available());
596 EXPECT_TRUE(extension_api
->IsAvailable(
599 Feature::BLESSED_EXTENSION_CONTEXT
,
600 GURL("http://foo.com")).is_available());
602 // Make sure chrome.app.runtime and chrome.app.window are not available to
603 // extensions, and chrome.app is.
605 std::set
<std::string
> permissions
;
606 scoped_refptr
<Extension
> extension
=
607 CreateExtensionWithPermissions(permissions
);
608 EXPECT_TRUE(extension_api
->IsAvailable(
611 Feature::BLESSED_EXTENSION_CONTEXT
,
612 GURL("http://foo.com")).is_available());
613 EXPECT_FALSE(extension_api
->IsAvailable(
616 Feature::BLESSED_EXTENSION_CONTEXT
,
617 GURL("http://foo.com")).is_available());
618 EXPECT_FALSE(extension_api
->IsAvailable(
621 Feature::BLESSED_EXTENSION_CONTEXT
,
622 GURL("http://foo.com")).is_available());
626 TEST(ExtensionAPITest
, ExtensionWithDependencies
) {
627 // Extension with the "ttsEngine" permission but not the "tts" permission; it
628 // should not automatically get "tts" permission.
630 scoped_refptr
<Extension
> extension
=
631 CreateExtensionWithPermission("ttsEngine");
632 scoped_ptr
<ExtensionAPI
> api(
633 ExtensionAPI::CreateWithDefaultConfiguration());
634 EXPECT_TRUE(api
->IsAvailable("ttsEngine",
636 Feature::BLESSED_EXTENSION_CONTEXT
,
637 GURL()).is_available());
638 EXPECT_FALSE(api
->IsAvailable("tts",
640 Feature::BLESSED_EXTENSION_CONTEXT
,
641 GURL()).is_available());
644 // Conversely, extension with the "tts" permission but not the "ttsEngine"
645 // permission shouldn't get the "ttsEngine" permission.
647 scoped_refptr
<Extension
> extension
=
648 CreateExtensionWithPermission("tts");
649 scoped_ptr
<ExtensionAPI
> api(
650 ExtensionAPI::CreateWithDefaultConfiguration());
651 EXPECT_FALSE(api
->IsAvailable("ttsEngine",
653 Feature::BLESSED_EXTENSION_CONTEXT
,
654 GURL()).is_available());
655 EXPECT_TRUE(api
->IsAvailable("tts",
657 Feature::BLESSED_EXTENSION_CONTEXT
,
658 GURL()).is_available());
663 ExtensionAPI
* api
, const std::string
& api_name
, const std::string
& url
) {
664 return api
->IsAvailable(
665 api_name
, NULL
, Feature::WEB_PAGE_CONTEXT
, GURL(url
)).is_available();
668 TEST(ExtensionAPITest
, URLMatching
) {
669 scoped_ptr
<ExtensionAPI
> api(ExtensionAPI::CreateWithDefaultConfiguration());
671 // "app" API is available to all URLs that content scripts can be injected.
672 EXPECT_TRUE(MatchesURL(api
.get(), "app", "http://example.com/example.html"));
673 EXPECT_TRUE(MatchesURL(api
.get(), "app", "https://blah.net"));
674 EXPECT_TRUE(MatchesURL(api
.get(), "app", "file://somefile.html"));
676 // Also to internal URLs.
677 EXPECT_TRUE(MatchesURL(api
.get(), "app", "about:flags"));
678 EXPECT_TRUE(MatchesURL(api
.get(), "app", "chrome://flags"));
680 // "app" should be available to chrome-extension URLs.
681 EXPECT_TRUE(MatchesURL(api
.get(), "app",
682 "chrome-extension://fakeextension"));
684 // "storage" API (for example) isn't available to any URLs.
685 EXPECT_FALSE(MatchesURL(api
.get(), "storage",
686 "http://example.com/example.html"));
687 EXPECT_FALSE(MatchesURL(api
.get(), "storage", "https://blah.net"));
688 EXPECT_FALSE(MatchesURL(api
.get(), "storage", "file://somefile.html"));
689 EXPECT_FALSE(MatchesURL(api
.get(), "storage", "about:flags"));
690 EXPECT_FALSE(MatchesURL(api
.get(), "storage", "chrome://flags"));
691 EXPECT_FALSE(MatchesURL(api
.get(), "storage",
692 "chrome-extension://fakeextension"));
695 TEST(ExtensionAPITest
, GetAPINameFromFullName
) {
698 std::string api_name
;
699 std::string child_name
;
702 { "unknown", "", "" },
703 { "bookmarks", "bookmarks", "" },
704 { "bookmarks.", "bookmarks", "" },
705 { ".bookmarks", "", "" },
706 { "bookmarks.create", "bookmarks", "create" },
707 { "bookmarks.create.", "bookmarks", "create." },
708 { "bookmarks.create.monkey", "bookmarks", "create.monkey" },
709 { "bookmarkManagerPrivate", "bookmarkManagerPrivate", "" },
710 { "bookmarkManagerPrivate.copy", "bookmarkManagerPrivate", "copy" }
713 scoped_ptr
<ExtensionAPI
> api(ExtensionAPI::CreateWithDefaultConfiguration());
714 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(test_data
); ++i
) {
715 std::string child_name
;
716 std::string api_name
= api
->GetAPINameFromFullName(test_data
[i
].input
,
718 EXPECT_EQ(test_data
[i
].api_name
, api_name
) << test_data
[i
].input
;
719 EXPECT_EQ(test_data
[i
].child_name
, child_name
) << test_data
[i
].input
;
723 TEST(ExtensionAPITest
, DefaultConfigurationFeatures
) {
724 scoped_ptr
<ExtensionAPI
> api(ExtensionAPI::CreateWithDefaultConfiguration());
726 SimpleFeature
* bookmarks
= static_cast<SimpleFeature
*>(
727 api
->GetFeatureDependency("api:bookmarks"));
728 SimpleFeature
* bookmarks_create
= static_cast<SimpleFeature
*>(
729 api
->GetFeatureDependency("api:bookmarks.create"));
732 SimpleFeature
* feature
;
733 // TODO(aa): More stuff to test over time.
739 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(test_data
); ++i
) {
740 SimpleFeature
* feature
= test_data
[i
].feature
;
741 ASSERT_TRUE(feature
) << i
;
743 EXPECT_TRUE(feature
->whitelist()->empty());
744 EXPECT_TRUE(feature
->extension_types()->empty());
746 EXPECT_EQ(SimpleFeature::UNSPECIFIED_LOCATION
, feature
->location());
747 EXPECT_TRUE(feature
->platforms()->empty());
748 EXPECT_EQ(0, feature
->min_manifest_version());
749 EXPECT_EQ(0, feature
->max_manifest_version());
753 TEST(ExtensionAPITest
, FeaturesRequireContexts
) {
754 // TODO(cduvall): Make this check API featues.
755 scoped_ptr
<base::DictionaryValue
> api_features1(new base::DictionaryValue());
756 scoped_ptr
<base::DictionaryValue
> api_features2(new base::DictionaryValue());
757 base::DictionaryValue
* test1
= new base::DictionaryValue();
758 base::DictionaryValue
* test2
= new base::DictionaryValue();
759 base::ListValue
* contexts
= new base::ListValue();
760 contexts
->Append(new base::StringValue("content_script"));
761 test1
->Set("contexts", contexts
);
762 test1
->SetString("channel", "stable");
763 test2
->SetString("channel", "stable");
764 api_features1
->Set("test", test1
);
765 api_features2
->Set("test", test2
);
768 base::DictionaryValue
* api_features
;
771 { api_features1
.get(), true },
772 { api_features2
.get(), false }
776 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(test_data
); ++i
) {
777 BaseFeatureProvider
api_feature_provider(*test_data
[i
].api_features
,
779 Feature
* feature
= api_feature_provider
.GetFeature("test");
780 EXPECT_EQ(test_data
[i
].expect_success
, feature
!= NULL
) << i
;
784 static void GetDictionaryFromList(const base::DictionaryValue
* schema
,
785 const std::string
& list_name
,
786 const int list_index
,
787 const base::DictionaryValue
** out
) {
788 const base::ListValue
* list
;
789 EXPECT_TRUE(schema
->GetList(list_name
, &list
));
790 EXPECT_TRUE(list
->GetDictionary(list_index
, out
));
793 TEST(ExtensionAPITest
, TypesHaveNamespace
) {
794 base::FilePath manifest_path
;
795 PathService::Get(chrome::DIR_TEST_DATA
, &manifest_path
);
796 manifest_path
= manifest_path
.AppendASCII("extensions")
797 .AppendASCII("extension_api_unittest")
798 .AppendASCII("types_have_namespace.json");
800 std::string manifest_str
;
801 ASSERT_TRUE(base::ReadFileToString(manifest_path
, &manifest_str
))
802 << "Failed to load: " << manifest_path
.value();
805 api
.RegisterSchemaResource("test.foo", 0);
806 api
.LoadSchema("test.foo", manifest_str
);
808 const base::DictionaryValue
* schema
= api
.GetSchema("test.foo");
810 const base::DictionaryValue
* dict
;
811 const base::DictionaryValue
* sub_dict
;
814 GetDictionaryFromList(schema
, "types", 0, &dict
);
815 EXPECT_TRUE(dict
->GetString("id", &type
));
816 EXPECT_EQ("test.foo.TestType", type
);
817 EXPECT_TRUE(dict
->GetString("customBindings", &type
));
818 EXPECT_EQ("test.foo.TestType", type
);
819 EXPECT_TRUE(dict
->GetDictionary("properties", &sub_dict
));
820 const base::DictionaryValue
* property
;
821 EXPECT_TRUE(sub_dict
->GetDictionary("foo", &property
));
822 EXPECT_TRUE(property
->GetString("$ref", &type
));
823 EXPECT_EQ("test.foo.OtherType", type
);
824 EXPECT_TRUE(sub_dict
->GetDictionary("bar", &property
));
825 EXPECT_TRUE(property
->GetString("$ref", &type
));
826 EXPECT_EQ("fully.qualified.Type", type
);
828 GetDictionaryFromList(schema
, "functions", 0, &dict
);
829 GetDictionaryFromList(dict
, "parameters", 0, &sub_dict
);
830 EXPECT_TRUE(sub_dict
->GetString("$ref", &type
));
831 EXPECT_EQ("test.foo.TestType", type
);
832 EXPECT_TRUE(dict
->GetDictionary("returns", &sub_dict
));
833 EXPECT_TRUE(sub_dict
->GetString("$ref", &type
));
834 EXPECT_EQ("fully.qualified.Type", type
);
836 GetDictionaryFromList(schema
, "functions", 1, &dict
);
837 GetDictionaryFromList(dict
, "parameters", 0, &sub_dict
);
838 EXPECT_TRUE(sub_dict
->GetString("$ref", &type
));
839 EXPECT_EQ("fully.qualified.Type", type
);
840 EXPECT_TRUE(dict
->GetDictionary("returns", &sub_dict
));
841 EXPECT_TRUE(sub_dict
->GetString("$ref", &type
));
842 EXPECT_EQ("test.foo.TestType", type
);
844 GetDictionaryFromList(schema
, "events", 0, &dict
);
845 GetDictionaryFromList(dict
, "parameters", 0, &sub_dict
);
846 EXPECT_TRUE(sub_dict
->GetString("$ref", &type
));
847 EXPECT_EQ("test.foo.TestType", type
);
848 GetDictionaryFromList(dict
, "parameters", 1, &sub_dict
);
849 EXPECT_TRUE(sub_dict
->GetString("$ref", &type
));
850 EXPECT_EQ("fully.qualified.Type", type
);
853 // Tests API availability with an empty manifest.
854 TEST(ExtensionAPITest
, NoPermissions
) {
856 const char* permission_name
;
859 // Test default module/package permission.
860 { "extension", true },
862 { "permissions", true },
865 // These require manifest keys.
866 { "browserAction", false },
867 { "pageAction", false },
868 { "pageActions", false },
869 // Some negative tests.
870 { "bookmarks", false },
871 { "cookies", false },
872 { "history", false },
873 // Make sure we find the module name after stripping '.'
874 { "runtime.abcd.onStartup", true },
875 // Test Tabs/Windows functions.
876 { "tabs.create", true },
877 { "tabs.duplicate", true },
878 { "tabs.onRemoved", true },
879 { "tabs.remove", true },
880 { "tabs.update", true },
881 { "tabs.getSelected", true },
882 { "tabs.onUpdated", true },
883 { "windows.get", true },
884 { "windows.create", true },
885 { "windows.remove", true },
886 { "windows.update", true },
887 // Test some whitelisted functions. These require no permissions.
888 { "app.getDetails", true },
889 { "app.getDetailsForFrame", true },
890 { "app.getIsInstalled", true },
891 { "app.installState", true },
892 { "app.runningState", true },
893 { "management.getPermissionWarningsByManifest", true },
894 { "management.uninstallSelf", true },
895 // But other functions in those modules do.
896 { "management.getPermissionWarningsById", false },
899 scoped_ptr
<ExtensionAPI
> extension_api(
900 ExtensionAPI::CreateWithDefaultConfiguration());
901 scoped_refptr
<Extension
> extension
=
902 BuildExtension(ExtensionBuilder().Pass()).Build();
904 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(kTests
); ++i
) {
905 EXPECT_EQ(kTests
[i
].expect_success
,
906 extension_api
->IsAvailable(kTests
[i
].permission_name
,
908 Feature::BLESSED_EXTENSION_CONTEXT
,
909 GURL()).is_available())
910 << "Permission being tested: " << kTests
[i
].permission_name
;
914 // Tests that permissions that require manifest keys are available when those
916 TEST(ExtensionAPITest
, ManifestKeys
) {
917 scoped_ptr
<ExtensionAPI
> extension_api(
918 ExtensionAPI::CreateWithDefaultConfiguration());
920 scoped_refptr
<Extension
> extension
=
921 BuildExtension(ExtensionBuilder().Pass())
922 .MergeManifest(DictionaryBuilder().Set("browser_action",
923 DictionaryBuilder().Pass()))
926 EXPECT_TRUE(extension_api
->IsAvailable("browserAction",
928 Feature::BLESSED_EXTENSION_CONTEXT
,
929 GURL()).is_available());
930 EXPECT_FALSE(extension_api
->IsAvailable("pageAction",
932 Feature::BLESSED_EXTENSION_CONTEXT
,
933 GURL()).is_available());
936 } // namespace extensions