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.
4
5 #include "extensions/common/extension_api.h"
6
7 #include <string>
8 #include <vector>
9
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 "chrome/common/extensions/extension_test_util.h"
21 #include "chrome/common/extensions/features/api_feature.h"
22 #include "chrome/common/extensions/features/base_feature_provider.h"
23 #include "chrome/common/extensions/features/simple_feature.h"
24 #include "extensions/common/extension.h"
25 #include "extensions/common/extension_builder.h"
26 #include "extensions/common/manifest.h"
27 #include "extensions/common/manifest_constants.h"
28 #include "extensions/common/value_builder.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30
31 namespace extensions {
32
33 using extension_test_util::BuildExtension;
34
CreateAPIFeature()35 SimpleFeature* CreateAPIFeature() {
36 return new APIFeature();
37 }
38
TEST(ExtensionAPITest,Creation)39 TEST(ExtensionAPITest, Creation) {
40 ExtensionAPI* shared_instance = ExtensionAPI::GetSharedInstance();
41 EXPECT_EQ(shared_instance, ExtensionAPI::GetSharedInstance());
42
43 scoped_ptr<ExtensionAPI> new_instance(
44 ExtensionAPI::CreateWithDefaultConfiguration());
45 EXPECT_NE(new_instance.get(),
46 scoped_ptr<ExtensionAPI>(
47 ExtensionAPI::CreateWithDefaultConfiguration()).get());
48
49 ExtensionAPI empty_instance;
50
51 struct {
52 ExtensionAPI* api;
53 bool expect_populated;
54 } test_data[] = {
55 { shared_instance, true },
56 { new_instance.get(), true },
57 { &empty_instance, false }
58 };
59
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);
63 }
64 }
65
TEST(ExtensionAPITest,SplitDependencyName)66 TEST(ExtensionAPITest, SplitDependencyName) {
67 struct {
68 std::string input;
69 std::string expected_feature_type;
70 std::string expected_feature_name;
71 } test_data[] = {
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" }
78 };
79
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,
84 &feature_name);
85 EXPECT_EQ(test_data[i].expected_feature_type, feature_type) << i;
86 EXPECT_EQ(test_data[i].expected_feature_name, feature_name) << i;
87 }
88 }
89
TEST(ExtensionAPITest,IsPrivileged)90 TEST(ExtensionAPITest, IsPrivileged) {
91 scoped_ptr<ExtensionAPI> extension_api(
92 ExtensionAPI::CreateWithDefaultConfiguration());
93
94 EXPECT_FALSE(extension_api->IsPrivileged("runtime.connect"));
95 EXPECT_FALSE(extension_api->IsPrivileged("runtime.onConnect"));
96 EXPECT_FALSE(extension_api->IsPrivileged("runtime.lastError"));
97
98 // Exists, but privileged.
99 EXPECT_TRUE(extension_api->IsPrivileged("extension.getViews"));
100 EXPECT_TRUE(extension_api->IsPrivileged("history.search"));
101
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"));
110 }
111
TEST(ExtensionAPITest,IsPrivilegedFeatures)112 TEST(ExtensionAPITest, IsPrivilegedFeatures) {
113 struct {
114 std::string api_full_name;
115 bool expect_is_privilged;
116 } test_data[] = {
117 { "test1", false },
118 { "test1.foo", true },
119 { "test2", true },
120 { "test2.foo", false },
121 { "test2.bar", false },
122 { "test2.baz", true },
123 { "test3", false },
124 { "test3.foo", true },
125 { "test4", false }
126 };
127
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");
133
134 std::string api_features_str;
135 ASSERT_TRUE(base::ReadFileToString(
136 api_features_path, &api_features_str)) << "privileged_api_features.json";
137
138 scoped_ptr<base::DictionaryValue> value(static_cast<base::DictionaryValue*>(
139 base::JSONReader::Read(api_features_str)));
140 BaseFeatureProvider api_feature_provider(*value, CreateAPIFeature);
141
142 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
143 ExtensionAPI api;
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;
147 }
148 }
149
TEST(ExtensionAPITest,APIFeatures)150 TEST(ExtensionAPITest, APIFeatures) {
151 struct {
152 std::string api_full_name;
153 bool expect_is_available;
154 Feature::Context context;
155 GURL url;
156 } test_data[] = {
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") },
189
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,
206 GURL() },
207 { "parent2.child3.child.child", false, Feature::BLESSED_EXTENSION_CONTEXT,
208 GURL() },
209 { "parent2.child3.child.child", true, Feature::UNBLESSED_EXTENSION_CONTEXT,
210 GURL() },
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,
219 GURL() },
220 { "parent3.noparent.child", true, Feature::UNBLESSED_EXTENSION_CONTEXT,
221 GURL() }
222 };
223
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");
229
230 std::string api_features_str;
231 ASSERT_TRUE(base::ReadFileToString(
232 api_features_path, &api_features_str)) << "api_features.json";
233
234 scoped_ptr<base::DictionaryValue> value(static_cast<base::DictionaryValue*>(
235 base::JSONReader::Read(api_features_str)));
236 BaseFeatureProvider api_feature_provider(*value, CreateAPIFeature);
237
238 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
239 ExtensionAPI api;
240 api.RegisterDependencyProvider("api", &api_feature_provider);
241 for (base::DictionaryValue::Iterator iter(*value); !iter.IsAtEnd();
242 iter.Advance()) {
243 if (iter.key().find(".") == std::string::npos)
244 api.RegisterSchemaResource(iter.key(), 0);
245 }
246
247 EXPECT_EQ(test_data[i].expect_is_available,
248 api.IsAvailable(test_data[i].api_full_name,
249 NULL,
250 test_data[i].context,
251 test_data[i].url).is_available()) << i;
252 }
253 }
254
TEST(ExtensionAPITest,IsAnyFeatureAvailableToContext)255 TEST(ExtensionAPITest, IsAnyFeatureAvailableToContext) {
256 scoped_refptr<const Extension> app = ExtensionBuilder()
257 .SetManifest(DictionaryBuilder()
258 .Set("name", "app")
259 .Set("app", DictionaryBuilder()
260 .Set("background", DictionaryBuilder()
261 .Set("scripts", ListBuilder().Append("background.js"))))
262 .Set("version", "1")
263 .Set("manifest_version", 2)).Build();
264 scoped_refptr<const Extension> extension = ExtensionBuilder()
265 .SetManifest(DictionaryBuilder()
266 .Set("name", "extension")
267 .Set("version", "1")
268 .Set("manifest_version", 2)).Build();
269
270 struct {
271 std::string api_full_name;
272 bool expect_is_available;
273 Feature::Context context;
274 const Extension* extension;
275 GURL url;
276 } test_data[] = {
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(),
281 GURL() },
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") }
294 };
295
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");
301
302 std::string api_features_str;
303 ASSERT_TRUE(base::ReadFileToString(
304 api_features_path, &api_features_str)) << "api_features.json";
305
306 scoped_ptr<base::DictionaryValue> value(static_cast<base::DictionaryValue*>(
307 base::JSONReader::Read(api_features_str)));
308 BaseFeatureProvider api_feature_provider(*value, CreateAPIFeature);
309
310 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
311 ExtensionAPI api;
312 api.RegisterDependencyProvider("api", &api_feature_provider);
313 for (base::DictionaryValue::Iterator iter(*value); !iter.IsAtEnd();
314 iter.Advance()) {
315 if (iter.key().find(".") == std::string::npos)
316 api.RegisterSchemaResource(iter.key(), 0);
317 }
318
319 EXPECT_EQ(test_data[i].expect_is_available,
320 api.IsAnyFeatureAvailableToContext(test_data[i].api_full_name,
321 test_data[i].extension,
322 test_data[i].context,
323 test_data[i].url)) << i;
324 }
325 }
326
TEST(ExtensionAPITest,LazyGetSchema)327 TEST(ExtensionAPITest, LazyGetSchema) {
328 scoped_ptr<ExtensionAPI> apis(ExtensionAPI::CreateWithDefaultConfiguration());
329
330 EXPECT_EQ(NULL, apis->GetSchema(std::string()));
331 EXPECT_EQ(NULL, apis->GetSchema(std::string()));
332 EXPECT_EQ(NULL, apis->GetSchema("experimental"));
333 EXPECT_EQ(NULL, apis->GetSchema("experimental"));
334 EXPECT_EQ(NULL, apis->GetSchema("foo"));
335 EXPECT_EQ(NULL, apis->GetSchema("foo"));
336
337 EXPECT_TRUE(apis->GetSchema("dns"));
338 EXPECT_TRUE(apis->GetSchema("dns"));
339 EXPECT_TRUE(apis->GetSchema("extension"));
340 EXPECT_TRUE(apis->GetSchema("extension"));
341 EXPECT_TRUE(apis->GetSchema("infobars"));
342 EXPECT_TRUE(apis->GetSchema("infobars"));
343 EXPECT_TRUE(apis->GetSchema("omnibox"));
344 EXPECT_TRUE(apis->GetSchema("omnibox"));
345 EXPECT_TRUE(apis->GetSchema("storage"));
346 EXPECT_TRUE(apis->GetSchema("storage"));
347 }
348
CreateExtensionWithPermissions(const std::set<std::string> & permissions)349 scoped_refptr<Extension> CreateExtensionWithPermissions(
350 const std::set<std::string>& permissions) {
351 base::DictionaryValue manifest;
352 manifest.SetString("name", "extension");
353 manifest.SetString("version", "1.0");
354 manifest.SetInteger("manifest_version", 2);
355 {
356 scoped_ptr<base::ListValue> permissions_list(new base::ListValue());
357 for (std::set<std::string>::const_iterator i = permissions.begin();
358 i != permissions.end(); ++i) {
359 permissions_list->Append(new base::StringValue(*i));
360 }
361 manifest.Set("permissions", permissions_list.release());
362 }
363
364 std::string error;
365 scoped_refptr<Extension> extension(Extension::Create(
366 base::FilePath(), Manifest::UNPACKED,
367 manifest, Extension::NO_FLAGS, &error));
368 CHECK(extension.get());
369 CHECK(error.empty());
370
371 return extension;
372 }
373
CreateExtensionWithPermission(const std::string & permission)374 scoped_refptr<Extension> CreateExtensionWithPermission(
375 const std::string& permission) {
376 std::set<std::string> permissions;
377 permissions.insert(permission);
378 return CreateExtensionWithPermissions(permissions);
379 }
380
TEST(ExtensionAPITest,ExtensionWithUnprivilegedAPIs)381 TEST(ExtensionAPITest, ExtensionWithUnprivilegedAPIs) {
382 scoped_refptr<Extension> extension;
383 {
384 std::set<std::string> permissions;
385 permissions.insert("storage");
386 permissions.insert("history");
387 extension = CreateExtensionWithPermissions(permissions);
388 }
389
390 scoped_ptr<ExtensionAPI> extension_api(
391 ExtensionAPI::CreateWithDefaultConfiguration());
392
393 // "runtime" has privileged parts that should not be accessed by content
394 // scripts.
395 EXPECT_FALSE(extension_api->IsAnyFeatureAvailableToContext(
396 "runtime.getBackgroundPage",
397 NULL,
398 Feature::CONTENT_SCRIPT_CONTEXT,
399 GURL()));
400 EXPECT_FALSE(extension_api->IsAnyFeatureAvailableToContext(
401 "runtime.sendNativeMessage",
402 NULL,
403 Feature::CONTENT_SCRIPT_CONTEXT,
404 GURL()));
405 // "runtime" also has unprivileged parts.
406 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
407 "runtime.sendMessage",
408 NULL,
409 Feature::CONTENT_SCRIPT_CONTEXT,
410 GURL()));
411 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
412 "runtime.id",
413 NULL,
414 Feature::CONTENT_SCRIPT_CONTEXT,
415 GURL()));
416
417 // "storage" is completely unprivileged.
418 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
419 "storage",
420 NULL,
421 Feature::BLESSED_EXTENSION_CONTEXT,
422 GURL()));
423 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
424 "storage",
425 NULL,
426 Feature::UNBLESSED_EXTENSION_CONTEXT,
427 GURL()));
428 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
429 "storage",
430 NULL,
431 Feature::CONTENT_SCRIPT_CONTEXT,
432 GURL()));
433
434 // "extension" is partially unprivileged.
435 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
436 "extension",
437 NULL,
438 Feature::BLESSED_EXTENSION_CONTEXT,
439 GURL()));
440 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
441 "extension",
442 NULL,
443 Feature::UNBLESSED_EXTENSION_CONTEXT,
444 GURL()));
445 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
446 "extension",
447 NULL,
448 Feature::CONTENT_SCRIPT_CONTEXT,
449 GURL()));
450
451 // "history" is entirely privileged.
452 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
453 "history",
454 NULL,
455 Feature::BLESSED_EXTENSION_CONTEXT,
456 GURL()));
457 EXPECT_FALSE(extension_api->IsAnyFeatureAvailableToContext(
458 "history",
459 NULL,
460 Feature::UNBLESSED_EXTENSION_CONTEXT,
461 GURL()));
462 EXPECT_FALSE(extension_api->IsAnyFeatureAvailableToContext(
463 "history",
464 NULL,
465 Feature::CONTENT_SCRIPT_CONTEXT,
466 GURL()));
467 }
468
CreateHostedApp()469 scoped_refptr<Extension> CreateHostedApp() {
470 base::DictionaryValue values;
471 values.SetString(manifest_keys::kName, "test");
472 values.SetString(manifest_keys::kVersion, "0.1");
473 values.Set(manifest_keys::kWebURLs, new base::ListValue());
474 values.SetString(manifest_keys::kLaunchWebURL,
475 "http://www.example.com");
476 std::string error;
477 scoped_refptr<Extension> extension(Extension::Create(
478 base::FilePath(), Manifest::INTERNAL, values, Extension::NO_FLAGS,
479 &error));
480 CHECK(extension.get());
481 return extension;
482 }
483
CreatePackagedAppWithPermissions(const std::set<std::string> & permissions)484 scoped_refptr<Extension> CreatePackagedAppWithPermissions(
485 const std::set<std::string>& permissions) {
486 base::DictionaryValue values;
487 values.SetString(manifest_keys::kName, "test");
488 values.SetString(manifest_keys::kVersion, "0.1");
489 values.SetString(manifest_keys::kPlatformAppBackground,
490 "http://www.example.com");
491
492 base::DictionaryValue* app = new base::DictionaryValue();
493 base::DictionaryValue* background = new base::DictionaryValue();
494 base::ListValue* scripts = new base::ListValue();
495 scripts->Append(new base::StringValue("test.js"));
496 background->Set("scripts", scripts);
497 app->Set("background", background);
498 values.Set(manifest_keys::kApp, app);
499 {
500 scoped_ptr<base::ListValue> permissions_list(new base::ListValue());
501 for (std::set<std::string>::const_iterator i = permissions.begin();
502 i != permissions.end(); ++i) {
503 permissions_list->Append(new base::StringValue(*i));
504 }
505 values.Set("permissions", permissions_list.release());
506 }
507
508 std::string error;
509 scoped_refptr<Extension> extension(Extension::Create(
510 base::FilePath(), Manifest::INTERNAL, values, Extension::NO_FLAGS,
511 &error));
512 CHECK(extension.get()) << error;
513 return extension;
514 }
515
TEST(ExtensionAPITest,HostedAppPermissions)516 TEST(ExtensionAPITest, HostedAppPermissions) {
517 scoped_refptr<Extension> extension = CreateHostedApp();
518
519 scoped_ptr<ExtensionAPI> extension_api(
520 ExtensionAPI::CreateWithDefaultConfiguration());
521
522 // "runtime" and "tabs" should not be available in hosted apps.
523 EXPECT_FALSE(extension_api->IsAvailable("runtime",
524 extension.get(),
525 Feature::BLESSED_EXTENSION_CONTEXT,
526 GURL()).is_available());
527 EXPECT_FALSE(extension_api->IsAvailable("runtime.id",
528 extension.get(),
529 Feature::BLESSED_EXTENSION_CONTEXT,
530 GURL()).is_available());
531 EXPECT_FALSE(extension_api->IsAvailable("runtime.sendMessage",
532 extension.get(),
533 Feature::BLESSED_EXTENSION_CONTEXT,
534 GURL()).is_available());
535 EXPECT_FALSE(extension_api->IsAvailable("runtime.sendNativeMessage",
536 extension.get(),
537 Feature::BLESSED_EXTENSION_CONTEXT,
538 GURL()).is_available());
539 EXPECT_FALSE(extension_api->IsAvailable("tabs.create",
540 extension.get(),
541 Feature::BLESSED_EXTENSION_CONTEXT,
542 GURL()).is_available());
543 }
544
TEST(ExtensionAPITest,AppAndFriendsAvailability)545 TEST(ExtensionAPITest, AppAndFriendsAvailability) {
546
547 scoped_ptr<ExtensionAPI> extension_api(
548 ExtensionAPI::CreateWithDefaultConfiguration());
549
550 // Make sure chrome.app.runtime and chrome.app.window are available to apps,
551 // and chrome.app is not.
552 {
553 std::set<std::string> permissions;
554 permissions.insert("app.runtime");
555 permissions.insert("app.window");
556 scoped_refptr<Extension> extension =
557 CreatePackagedAppWithPermissions(permissions);
558 EXPECT_FALSE(extension_api->IsAvailable(
559 "app",
560 extension.get(),
561 Feature::BLESSED_EXTENSION_CONTEXT,
562 GURL("http://foo.com")).is_available());
563 EXPECT_TRUE(extension_api->IsAvailable(
564 "app.runtime",
565 extension.get(),
566 Feature::BLESSED_EXTENSION_CONTEXT,
567 GURL("http://foo.com")).is_available());
568 EXPECT_TRUE(extension_api->IsAvailable(
569 "app.window",
570 extension.get(),
571 Feature::BLESSED_EXTENSION_CONTEXT,
572 GURL("http://foo.com")).is_available());
573 }
574 // Make sure chrome.app.runtime and chrome.app.window are not available to
575 // extensions, and chrome.app is.
576 {
577 std::set<std::string> permissions;
578 scoped_refptr<Extension> extension =
579 CreateExtensionWithPermissions(permissions);
580 EXPECT_TRUE(extension_api->IsAvailable(
581 "app",
582 extension.get(),
583 Feature::BLESSED_EXTENSION_CONTEXT,
584 GURL("http://foo.com")).is_available());
585 EXPECT_FALSE(extension_api->IsAvailable(
586 "app.runtime",
587 extension.get(),
588 Feature::BLESSED_EXTENSION_CONTEXT,
589 GURL("http://foo.com")).is_available());
590 EXPECT_FALSE(extension_api->IsAvailable(
591 "app.window",
592 extension.get(),
593 Feature::BLESSED_EXTENSION_CONTEXT,
594 GURL("http://foo.com")).is_available());
595 }
596 }
597
TEST(ExtensionAPITest,ExtensionWithDependencies)598 TEST(ExtensionAPITest, ExtensionWithDependencies) {
599 // Extension with the "ttsEngine" permission but not the "tts" permission; it
600 // should not automatically get "tts" permission.
601 {
602 scoped_refptr<Extension> extension =
603 CreateExtensionWithPermission("ttsEngine");
604 scoped_ptr<ExtensionAPI> api(
605 ExtensionAPI::CreateWithDefaultConfiguration());
606 EXPECT_TRUE(api->IsAvailable("ttsEngine",
607 extension.get(),
608 Feature::BLESSED_EXTENSION_CONTEXT,
609 GURL()).is_available());
610 EXPECT_FALSE(api->IsAvailable("tts",
611 extension.get(),
612 Feature::BLESSED_EXTENSION_CONTEXT,
613 GURL()).is_available());
614 }
615
616 // Conversely, extension with the "tts" permission but not the "ttsEngine"
617 // permission shouldn't get the "ttsEngine" permission.
618 {
619 scoped_refptr<Extension> extension =
620 CreateExtensionWithPermission("tts");
621 scoped_ptr<ExtensionAPI> api(
622 ExtensionAPI::CreateWithDefaultConfiguration());
623 EXPECT_FALSE(api->IsAvailable("ttsEngine",
624 extension.get(),
625 Feature::BLESSED_EXTENSION_CONTEXT,
626 GURL()).is_available());
627 EXPECT_TRUE(api->IsAvailable("tts",
628 extension.get(),
629 Feature::BLESSED_EXTENSION_CONTEXT,
630 GURL()).is_available());
631 }
632 }
633
MatchesURL(ExtensionAPI * api,const std::string & api_name,const std::string & url)634 bool MatchesURL(
635 ExtensionAPI* api, const std::string& api_name, const std::string& url) {
636 return api->IsAvailable(
637 api_name, NULL, Feature::WEB_PAGE_CONTEXT, GURL(url)).is_available();
638 }
639
TEST(ExtensionAPITest,URLMatching)640 TEST(ExtensionAPITest, URLMatching) {
641 scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration());
642
643 // "app" API is available to all URLs that content scripts can be injected.
644 EXPECT_TRUE(MatchesURL(api.get(), "app", "http://example.com/example.html"));
645 EXPECT_TRUE(MatchesURL(api.get(), "app", "https://blah.net"));
646 EXPECT_TRUE(MatchesURL(api.get(), "app", "file://somefile.html"));
647
648 // But not internal URLs.
649 EXPECT_FALSE(MatchesURL(api.get(), "app", "about:flags"));
650 EXPECT_FALSE(MatchesURL(api.get(), "app", "chrome://flags"));
651
652 // "app" should be available to chrome-extension URLs.
653 EXPECT_TRUE(MatchesURL(api.get(), "app",
654 "chrome-extension://fakeextension"));
655
656 // "storage" API (for example) isn't available to any URLs.
657 EXPECT_FALSE(MatchesURL(api.get(), "storage",
658 "http://example.com/example.html"));
659 EXPECT_FALSE(MatchesURL(api.get(), "storage", "https://blah.net"));
660 EXPECT_FALSE(MatchesURL(api.get(), "storage", "file://somefile.html"));
661 EXPECT_FALSE(MatchesURL(api.get(), "storage", "about:flags"));
662 EXPECT_FALSE(MatchesURL(api.get(), "storage", "chrome://flags"));
663 EXPECT_FALSE(MatchesURL(api.get(), "storage",
664 "chrome-extension://fakeextension"));
665 }
666
TEST(ExtensionAPITest,GetAPINameFromFullName)667 TEST(ExtensionAPITest, GetAPINameFromFullName) {
668 struct {
669 std::string input;
670 std::string api_name;
671 std::string child_name;
672 } test_data[] = {
673 { "", "", "" },
674 { "unknown", "", "" },
675 { "bookmarks", "bookmarks", "" },
676 { "bookmarks.", "bookmarks", "" },
677 { ".bookmarks", "", "" },
678 { "bookmarks.create", "bookmarks", "create" },
679 { "bookmarks.create.", "bookmarks", "create." },
680 { "bookmarks.create.monkey", "bookmarks", "create.monkey" },
681 { "bookmarkManagerPrivate", "bookmarkManagerPrivate", "" },
682 { "bookmarkManagerPrivate.copy", "bookmarkManagerPrivate", "copy" }
683 };
684
685 scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration());
686 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
687 std::string child_name;
688 std::string api_name = api->GetAPINameFromFullName(test_data[i].input,
689 &child_name);
690 EXPECT_EQ(test_data[i].api_name, api_name) << test_data[i].input;
691 EXPECT_EQ(test_data[i].child_name, child_name) << test_data[i].input;
692 }
693 }
694
TEST(ExtensionAPITest,DefaultConfigurationFeatures)695 TEST(ExtensionAPITest, DefaultConfigurationFeatures) {
696 scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration());
697
698 SimpleFeature* bookmarks = static_cast<SimpleFeature*>(
699 api->GetFeatureDependency("api:bookmarks"));
700 SimpleFeature* bookmarks_create = static_cast<SimpleFeature*>(
701 api->GetFeatureDependency("api:bookmarks.create"));
702
703 struct {
704 SimpleFeature* feature;
705 // TODO(aa): More stuff to test over time.
706 } test_data[] = {
707 { bookmarks },
708 { bookmarks_create }
709 };
710
711 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
712 SimpleFeature* feature = test_data[i].feature;
713 ASSERT_TRUE(feature) << i;
714
715 EXPECT_TRUE(feature->whitelist()->empty());
716 EXPECT_TRUE(feature->extension_types()->empty());
717
718 EXPECT_EQ(1u, feature->GetContexts()->size());
719 EXPECT_TRUE(feature->GetContexts()->count(
720 Feature::BLESSED_EXTENSION_CONTEXT));
721
722 EXPECT_EQ(Feature::UNSPECIFIED_LOCATION, feature->location());
723 EXPECT_TRUE(feature->platforms()->empty());
724 EXPECT_EQ(0, feature->min_manifest_version());
725 EXPECT_EQ(0, feature->max_manifest_version());
726 }
727 }
728
TEST(ExtensionAPITest,FeaturesRequireContexts)729 TEST(ExtensionAPITest, FeaturesRequireContexts) {
730 // TODO(cduvall): Make this check API featues.
731 scoped_ptr<base::DictionaryValue> api_features1(new base::DictionaryValue());
732 scoped_ptr<base::DictionaryValue> api_features2(new base::DictionaryValue());
733 base::DictionaryValue* test1 = new base::DictionaryValue();
734 base::DictionaryValue* test2 = new base::DictionaryValue();
735 base::ListValue* contexts = new base::ListValue();
736 contexts->Append(new base::StringValue("content_script"));
737 test1->Set("contexts", contexts);
738 test1->SetString("channel", "stable");
739 test2->SetString("channel", "stable");
740 api_features1->Set("test", test1);
741 api_features2->Set("test", test2);
742
743 struct {
744 base::DictionaryValue* api_features;
745 bool expect_success;
746 } test_data[] = {
747 { api_features1.get(), true },
748 { api_features2.get(), false }
749 };
750
751
752 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
753 BaseFeatureProvider api_feature_provider(*test_data[i].api_features,
754 CreateAPIFeature);
755 Feature* feature = api_feature_provider.GetFeature("test");
756 EXPECT_EQ(test_data[i].expect_success, feature != NULL) << i;
757 }
758 }
759
GetDictionaryFromList(const base::DictionaryValue * schema,const std::string & list_name,const int list_index,const base::DictionaryValue ** out)760 static void GetDictionaryFromList(const base::DictionaryValue* schema,
761 const std::string& list_name,
762 const int list_index,
763 const base::DictionaryValue** out) {
764 const base::ListValue* list;
765 EXPECT_TRUE(schema->GetList(list_name, &list));
766 EXPECT_TRUE(list->GetDictionary(list_index, out));
767 }
768
TEST(ExtensionAPITest,TypesHaveNamespace)769 TEST(ExtensionAPITest, TypesHaveNamespace) {
770 base::FilePath manifest_path;
771 PathService::Get(chrome::DIR_TEST_DATA, &manifest_path);
772 manifest_path = manifest_path.AppendASCII("extensions")
773 .AppendASCII("extension_api_unittest")
774 .AppendASCII("types_have_namespace.json");
775
776 std::string manifest_str;
777 ASSERT_TRUE(base::ReadFileToString(manifest_path, &manifest_str))
778 << "Failed to load: " << manifest_path.value();
779
780 ExtensionAPI api;
781 api.RegisterSchemaResource("test.foo", 0);
782 api.LoadSchema("test.foo", manifest_str);
783
784 const base::DictionaryValue* schema = api.GetSchema("test.foo");
785
786 const base::DictionaryValue* dict;
787 const base::DictionaryValue* sub_dict;
788 std::string type;
789
790 GetDictionaryFromList(schema, "types", 0, &dict);
791 EXPECT_TRUE(dict->GetString("id", &type));
792 EXPECT_EQ("test.foo.TestType", type);
793 EXPECT_TRUE(dict->GetString("customBindings", &type));
794 EXPECT_EQ("test.foo.TestType", type);
795 EXPECT_TRUE(dict->GetDictionary("properties", &sub_dict));
796 const base::DictionaryValue* property;
797 EXPECT_TRUE(sub_dict->GetDictionary("foo", &property));
798 EXPECT_TRUE(property->GetString("$ref", &type));
799 EXPECT_EQ("test.foo.OtherType", type);
800 EXPECT_TRUE(sub_dict->GetDictionary("bar", &property));
801 EXPECT_TRUE(property->GetString("$ref", &type));
802 EXPECT_EQ("fully.qualified.Type", type);
803
804 GetDictionaryFromList(schema, "functions", 0, &dict);
805 GetDictionaryFromList(dict, "parameters", 0, &sub_dict);
806 EXPECT_TRUE(sub_dict->GetString("$ref", &type));
807 EXPECT_EQ("test.foo.TestType", type);
808 EXPECT_TRUE(dict->GetDictionary("returns", &sub_dict));
809 EXPECT_TRUE(sub_dict->GetString("$ref", &type));
810 EXPECT_EQ("fully.qualified.Type", type);
811
812 GetDictionaryFromList(schema, "functions", 1, &dict);
813 GetDictionaryFromList(dict, "parameters", 0, &sub_dict);
814 EXPECT_TRUE(sub_dict->GetString("$ref", &type));
815 EXPECT_EQ("fully.qualified.Type", type);
816 EXPECT_TRUE(dict->GetDictionary("returns", &sub_dict));
817 EXPECT_TRUE(sub_dict->GetString("$ref", &type));
818 EXPECT_EQ("test.foo.TestType", type);
819
820 GetDictionaryFromList(schema, "events", 0, &dict);
821 GetDictionaryFromList(dict, "parameters", 0, &sub_dict);
822 EXPECT_TRUE(sub_dict->GetString("$ref", &type));
823 EXPECT_EQ("test.foo.TestType", type);
824 GetDictionaryFromList(dict, "parameters", 1, &sub_dict);
825 EXPECT_TRUE(sub_dict->GetString("$ref", &type));
826 EXPECT_EQ("fully.qualified.Type", type);
827 }
828
829 // Tests API availability with an empty manifest.
TEST(ExtensionAPITest,NoPermissions)830 TEST(ExtensionAPITest, NoPermissions) {
831 const struct {
832 const char* permission_name;
833 bool expect_success;
834 } kTests[] = {
835 // Test default module/package permission.
836 { "extension", true },
837 { "i18n", true },
838 { "permissions", true },
839 { "runtime", true },
840 { "test", true },
841 // These require manifest keys.
842 { "browserAction", false },
843 { "pageAction", false },
844 { "pageActions", false },
845 // Some negative tests.
846 { "bookmarks", false },
847 { "cookies", false },
848 { "history", false },
849 // Make sure we find the module name after stripping '.'
850 { "runtime.abcd.onStartup", true },
851 // Test Tabs/Windows functions.
852 { "tabs.create", true },
853 { "tabs.duplicate", true },
854 { "tabs.onRemoved", true },
855 { "tabs.remove", true },
856 { "tabs.update", true },
857 { "tabs.getSelected", true },
858 { "tabs.onUpdated", true },
859 { "windows.get", true },
860 { "windows.create", true },
861 { "windows.remove", true },
862 { "windows.update", true },
863 // Test some whitelisted functions. These require no permissions.
864 { "app.getDetails", true },
865 { "app.getDetailsForFrame", true },
866 { "app.getIsInstalled", true },
867 { "app.installState", true },
868 { "app.runningState", true },
869 { "management.getPermissionWarningsByManifest", true },
870 { "management.uninstallSelf", true },
871 // But other functions in those modules do.
872 { "management.getPermissionWarningsById", false },
873 };
874
875 scoped_ptr<ExtensionAPI> extension_api(
876 ExtensionAPI::CreateWithDefaultConfiguration());
877 scoped_refptr<Extension> extension =
878 BuildExtension(ExtensionBuilder().Pass()).Build();
879
880 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) {
881 EXPECT_EQ(kTests[i].expect_success,
882 extension_api->IsAvailable(kTests[i].permission_name,
883 extension.get(),
884 Feature::BLESSED_EXTENSION_CONTEXT,
885 GURL()).is_available())
886 << "Permission being tested: " << kTests[i].permission_name;
887 }
888 }
889
890 // Tests that permissions that require manifest keys are available when those
891 // keys are present.
TEST(ExtensionAPITest,ManifestKeys)892 TEST(ExtensionAPITest, ManifestKeys) {
893 scoped_ptr<ExtensionAPI> extension_api(
894 ExtensionAPI::CreateWithDefaultConfiguration());
895
896 scoped_refptr<Extension> extension =
897 BuildExtension(ExtensionBuilder().Pass())
898 .MergeManifest(DictionaryBuilder().Set("browser_action",
899 DictionaryBuilder().Pass()))
900 .Build();
901
902 EXPECT_TRUE(extension_api->IsAvailable("browserAction",
903 extension.get(),
904 Feature::BLESSED_EXTENSION_CONTEXT,
905 GURL()).is_available());
906 EXPECT_FALSE(extension_api->IsAvailable("pageAction",
907 extension.get(),
908 Feature::BLESSED_EXTENSION_CONTEXT,
909 GURL()).is_available());
910 }
911
912 } // namespace extensions
913