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/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"
30
31 namespace extensions {
32
33 using 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[] = {{"", "api", ""}, // assumes "api" when no type is present
72 {"foo", "api", "foo"},
73 {"foo:", "foo", ""},
74 {":foo", "", "foo"},
75 {"foo:bar", "foo", "bar"},
76 {"foo:bar.baz", "foo", "bar.baz"}};
77
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;
85 }
86 }
87
TEST(ExtensionAPITest,IsAvailableInUntrustedContext)88 TEST(ExtensionAPITest, IsAvailableInUntrustedContext) {
89 scoped_ptr<ExtensionAPI> extension_api(
90 ExtensionAPI::CreateWithDefaultConfiguration());
91 scoped_refptr<const Extension> extension =
92 ExtensionBuilder()
93 .SetManifest(DictionaryBuilder()
94 .Set("name", "extension")
95 .Set("version", "1")
96 .Set("permissions", ListBuilder().Append("storage"))
97 .Set("manifest_version", 2))
98 .Build();
99
100 EXPECT_TRUE(extension_api->IsAvailableInUntrustedContext("runtime.connect",
101 extension.get()));
102 EXPECT_TRUE(extension_api->IsAvailableInUntrustedContext("runtime.onConnect",
103 extension.get()));
104 EXPECT_TRUE(extension_api->IsAvailableInUntrustedContext("runtime.lastError",
105 extension.get()));
106
107 // Exists, but privileged.
108 EXPECT_FALSE(extension_api->IsAvailableInUntrustedContext(
109 "extension.getViews", extension.get()));
110 EXPECT_FALSE(extension_api->IsAvailableInUntrustedContext("history.search",
111 extension.get()));
112
113 // Whole APIs that are unprivileged.
114 EXPECT_TRUE(
115 extension_api->IsAvailableInUntrustedContext("app", extension.get()));
116 EXPECT_TRUE(extension_api->IsAvailableInUntrustedContext("app.getDetails",
117 extension.get()));
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",
121 extension.get()));
122 EXPECT_TRUE(extension_api->IsAvailableInUntrustedContext("storage.local",
123 extension.get()));
124 EXPECT_TRUE(extension_api->IsAvailableInUntrustedContext(
125 "storage.local.onChanged", extension.get()));
126 EXPECT_TRUE(extension_api->IsAvailableInUntrustedContext("storage.local.set",
127 extension.get()));
128 EXPECT_TRUE(extension_api->IsAvailableInUntrustedContext(
129 "storage.local.MAX_ITEMS", extension.get()));
130 EXPECT_TRUE(extension_api->IsAvailableInUntrustedContext("storage.set",
131 extension.get()));
132
133 // APIs which override unprivileged APIs.
134 EXPECT_FALSE(extension_api->IsAvailableInUntrustedContext("app.runtime",
135 extension.get()));
136 EXPECT_FALSE(extension_api->IsAvailableInUntrustedContext("app.window",
137 extension.get()));
138 }
139
TEST(ExtensionAPITest,IsAvailableInUntrustedContextFeatures)140 TEST(ExtensionAPITest, IsAvailableInUntrustedContextFeatures) {
141 struct {
142 std::string api_full_name;
143 bool expect_is_available;
144 } test_data[] = {{"test1", true},
145 {"test1.foo", false},
146 {"test2", false},
147 {"test2.foo", true},
148 {"test2.bar", true},
149 {"test2.baz", false},
150 {"test2.qux", false},
151 {"test3", true},
152 {"test3.foo", false},
153 {"test4", true},
154 {"test5", true}};
155
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");
161
162 std::string api_features_str;
163 ASSERT_TRUE(base::ReadFileToString(
164 api_features_path, &api_features_str)) << "privileged_api_features.json";
165
166 scoped_ptr<base::DictionaryValue> value(static_cast<base::DictionaryValue*>(
167 base::JSONReader::Read(api_features_str)));
168 BaseFeatureProvider api_feature_provider(*value, CreateAPIFeature);
169
170 scoped_refptr<Extension> extension =
171 BuildExtension(ExtensionBuilder().Pass()).Build();
172
173 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
174 ExtensionAPI api;
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,
178 extension.get()))
179 << i;
180 }
181 }
182
TEST(ExtensionAPITest,APIFeatures)183 TEST(ExtensionAPITest, APIFeatures) {
184 struct {
185 std::string api_full_name;
186 bool expect_is_available;
187 Feature::Context context;
188 GURL url;
189 } test_data[] = {
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") },
222
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,
239 GURL() },
240 { "parent2.child3.child.child", false, Feature::BLESSED_EXTENSION_CONTEXT,
241 GURL() },
242 { "parent2.child3.child.child", true, Feature::UNBLESSED_EXTENSION_CONTEXT,
243 GURL() },
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,
252 GURL() },
253 { "parent3.noparent.child", true, Feature::UNBLESSED_EXTENSION_CONTEXT,
254 GURL() }
255 };
256
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");
262
263 std::string api_features_str;
264 ASSERT_TRUE(base::ReadFileToString(
265 api_features_path, &api_features_str)) << "api_features.json";
266
267 scoped_ptr<base::DictionaryValue> value(static_cast<base::DictionaryValue*>(
268 base::JSONReader::Read(api_features_str)));
269 BaseFeatureProvider api_feature_provider(*value, CreateAPIFeature);
270
271 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
272 ExtensionAPI api;
273 api.RegisterDependencyProvider("api", &api_feature_provider);
274 for (base::DictionaryValue::Iterator iter(*value); !iter.IsAtEnd();
275 iter.Advance()) {
276 if (iter.key().find(".") == std::string::npos)
277 api.RegisterSchemaResource(iter.key(), 0);
278 }
279
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,
284 NULL,
285 test_data[i].context,
286 test_data[i].url);
287 EXPECT_EQ(expected, availability.is_available())
288 << base::StringPrintf("Test %d: Feature '%s' was %s: %s",
289 static_cast<int>(i),
290 test_data[i].api_full_name.c_str(),
291 expected ? "not available" : "available",
292 availability.message().c_str());
293 }
294 }
295
TEST(ExtensionAPITest,IsAnyFeatureAvailableToContext)296 TEST(ExtensionAPITest, IsAnyFeatureAvailableToContext) {
297 scoped_refptr<const Extension> app = ExtensionBuilder()
298 .SetManifest(DictionaryBuilder()
299 .Set("name", "app")
300 .Set("app", DictionaryBuilder()
301 .Set("background", DictionaryBuilder()
302 .Set("scripts", ListBuilder().Append("background.js"))))
303 .Set("version", "1")
304 .Set("manifest_version", 2)).Build();
305 scoped_refptr<const Extension> extension = ExtensionBuilder()
306 .SetManifest(DictionaryBuilder()
307 .Set("name", "extension")
308 .Set("version", "1")
309 .Set("manifest_version", 2)).Build();
310
311 struct {
312 std::string api_full_name;
313 bool expect_is_available;
314 Feature::Context context;
315 const Extension* extension;
316 GURL url;
317 } test_data[] = {
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(),
322 GURL() },
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") }
335 };
336
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");
342
343 std::string api_features_str;
344 ASSERT_TRUE(base::ReadFileToString(
345 api_features_path, &api_features_str)) << "api_features.json";
346
347 scoped_ptr<base::DictionaryValue> value(static_cast<base::DictionaryValue*>(
348 base::JSONReader::Read(api_features_str)));
349 BaseFeatureProvider api_feature_provider(*value, CreateAPIFeature);
350
351 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
352 ExtensionAPI api;
353 api.RegisterDependencyProvider("api", &api_feature_provider);
354 for (base::DictionaryValue::Iterator iter(*value); !iter.IsAtEnd();
355 iter.Advance()) {
356 if (iter.key().find(".") == std::string::npos)
357 api.RegisterSchemaResource(iter.key(), 0);
358 }
359
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,
367 test_data[i].url))
368 << i;
369 }
370 }
371
TEST(ExtensionAPITest,LazyGetSchema)372 TEST(ExtensionAPITest, LazyGetSchema) {
373 scoped_ptr<ExtensionAPI> apis(ExtensionAPI::CreateWithDefaultConfiguration());
374
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"));
381
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"));
392 }
393
CreateExtensionWithPermissions(const std::set<std::string> & permissions)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);
400 {
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));
405 }
406 manifest.Set("permissions", permissions_list.release());
407 }
408
409 std::string error;
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());
415
416 return extension;
417 }
418
CreateExtensionWithPermission(const std::string & permission)419 scoped_refptr<Extension> CreateExtensionWithPermission(
420 const std::string& permission) {
421 std::set<std::string> permissions;
422 permissions.insert(permission);
423 return CreateExtensionWithPermissions(permissions);
424 }
425
TEST(ExtensionAPITest,ExtensionWithUnprivilegedAPIs)426 TEST(ExtensionAPITest, ExtensionWithUnprivilegedAPIs) {
427 scoped_refptr<Extension> extension;
428 {
429 std::set<std::string> permissions;
430 permissions.insert("storage");
431 permissions.insert("history");
432 extension = CreateExtensionWithPermissions(permissions);
433 }
434
435 scoped_ptr<ExtensionAPI> extension_api(
436 ExtensionAPI::CreateWithDefaultConfiguration());
437
438 const FeatureProvider& api_features = *FeatureProvider::GetAPIFeatures();
439
440 // "storage" is completely unprivileged.
441 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
442 *api_features.GetFeature("storage"),
443 NULL,
444 Feature::BLESSED_EXTENSION_CONTEXT,
445 GURL()));
446 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
447 *api_features.GetFeature("storage"),
448 NULL,
449 Feature::UNBLESSED_EXTENSION_CONTEXT,
450 GURL()));
451 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
452 *api_features.GetFeature("storage"),
453 NULL,
454 Feature::CONTENT_SCRIPT_CONTEXT,
455 GURL()));
456
457 // "extension" is partially unprivileged.
458 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
459 *api_features.GetFeature("extension"),
460 NULL,
461 Feature::BLESSED_EXTENSION_CONTEXT,
462 GURL()));
463 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
464 *api_features.GetFeature("extension"),
465 NULL,
466 Feature::UNBLESSED_EXTENSION_CONTEXT,
467 GURL()));
468 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
469 *api_features.GetFeature("extension"),
470 NULL,
471 Feature::CONTENT_SCRIPT_CONTEXT,
472 GURL()));
473 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
474 *api_features.GetFeature("extension.getURL"),
475 NULL,
476 Feature::CONTENT_SCRIPT_CONTEXT,
477 GURL()));
478
479 // "history" is entirely privileged.
480 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
481 *api_features.GetFeature("history"),
482 NULL,
483 Feature::BLESSED_EXTENSION_CONTEXT,
484 GURL()));
485 EXPECT_FALSE(extension_api->IsAnyFeatureAvailableToContext(
486 *api_features.GetFeature("history"),
487 NULL,
488 Feature::UNBLESSED_EXTENSION_CONTEXT,
489 GURL()));
490 EXPECT_FALSE(extension_api->IsAnyFeatureAvailableToContext(
491 *api_features.GetFeature("history"),
492 NULL,
493 Feature::CONTENT_SCRIPT_CONTEXT,
494 GURL()));
495 }
496
CreateHostedApp()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");
504 std::string error;
505 scoped_refptr<Extension> extension(Extension::Create(
506 base::FilePath(), Manifest::INTERNAL, values, Extension::NO_FLAGS,
507 &error));
508 CHECK(extension.get());
509 return extension;
510 }
511
CreatePackagedAppWithPermissions(const std::set<std::string> & permissions)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");
519
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);
527 {
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));
532 }
533 values.Set("permissions", permissions_list.release());
534 }
535
536 std::string error;
537 scoped_refptr<Extension> extension(Extension::Create(
538 base::FilePath(), Manifest::INTERNAL, values, Extension::NO_FLAGS,
539 &error));
540 CHECK(extension.get()) << error;
541 return extension;
542 }
543
TEST(ExtensionAPITest,HostedAppPermissions)544 TEST(ExtensionAPITest, HostedAppPermissions) {
545 scoped_refptr<Extension> extension = CreateHostedApp();
546
547 scoped_ptr<ExtensionAPI> extension_api(
548 ExtensionAPI::CreateWithDefaultConfiguration());
549
550 // "runtime" and "tabs" should not be available in hosted apps.
551 EXPECT_FALSE(extension_api->IsAvailable("runtime",
552 extension.get(),
553 Feature::BLESSED_EXTENSION_CONTEXT,
554 GURL()).is_available());
555 EXPECT_FALSE(extension_api->IsAvailable("runtime.id",
556 extension.get(),
557 Feature::BLESSED_EXTENSION_CONTEXT,
558 GURL()).is_available());
559 EXPECT_FALSE(extension_api->IsAvailable("runtime.sendMessage",
560 extension.get(),
561 Feature::BLESSED_EXTENSION_CONTEXT,
562 GURL()).is_available());
563 EXPECT_FALSE(extension_api->IsAvailable("runtime.sendNativeMessage",
564 extension.get(),
565 Feature::BLESSED_EXTENSION_CONTEXT,
566 GURL()).is_available());
567 EXPECT_FALSE(extension_api->IsAvailable("tabs.create",
568 extension.get(),
569 Feature::BLESSED_EXTENSION_CONTEXT,
570 GURL()).is_available());
571 }
572
TEST(ExtensionAPITest,AppAndFriendsAvailability)573 TEST(ExtensionAPITest, AppAndFriendsAvailability) {
574
575 scoped_ptr<ExtensionAPI> extension_api(
576 ExtensionAPI::CreateWithDefaultConfiguration());
577
578 // Make sure chrome.app.runtime and chrome.app.window are available to apps,
579 // and chrome.app is not.
580 {
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(
587 "app",
588 extension.get(),
589 Feature::BLESSED_EXTENSION_CONTEXT,
590 GURL("http://foo.com")).is_available());
591 EXPECT_TRUE(extension_api->IsAvailable(
592 "app.runtime",
593 extension.get(),
594 Feature::BLESSED_EXTENSION_CONTEXT,
595 GURL("http://foo.com")).is_available());
596 EXPECT_TRUE(extension_api->IsAvailable(
597 "app.window",
598 extension.get(),
599 Feature::BLESSED_EXTENSION_CONTEXT,
600 GURL("http://foo.com")).is_available());
601 }
602 // Make sure chrome.app.runtime and chrome.app.window are not available to
603 // extensions, and chrome.app is.
604 {
605 std::set<std::string> permissions;
606 scoped_refptr<Extension> extension =
607 CreateExtensionWithPermissions(permissions);
608 EXPECT_TRUE(extension_api->IsAvailable(
609 "app",
610 extension.get(),
611 Feature::BLESSED_EXTENSION_CONTEXT,
612 GURL("http://foo.com")).is_available());
613 EXPECT_FALSE(extension_api->IsAvailable(
614 "app.runtime",
615 extension.get(),
616 Feature::BLESSED_EXTENSION_CONTEXT,
617 GURL("http://foo.com")).is_available());
618 EXPECT_FALSE(extension_api->IsAvailable(
619 "app.window",
620 extension.get(),
621 Feature::BLESSED_EXTENSION_CONTEXT,
622 GURL("http://foo.com")).is_available());
623 }
624 }
625
TEST(ExtensionAPITest,ExtensionWithDependencies)626 TEST(ExtensionAPITest, ExtensionWithDependencies) {
627 // Extension with the "ttsEngine" permission but not the "tts" permission; it
628 // should not automatically get "tts" permission.
629 {
630 scoped_refptr<Extension> extension =
631 CreateExtensionWithPermission("ttsEngine");
632 scoped_ptr<ExtensionAPI> api(
633 ExtensionAPI::CreateWithDefaultConfiguration());
634 EXPECT_TRUE(api->IsAvailable("ttsEngine",
635 extension.get(),
636 Feature::BLESSED_EXTENSION_CONTEXT,
637 GURL()).is_available());
638 EXPECT_FALSE(api->IsAvailable("tts",
639 extension.get(),
640 Feature::BLESSED_EXTENSION_CONTEXT,
641 GURL()).is_available());
642 }
643
644 // Conversely, extension with the "tts" permission but not the "ttsEngine"
645 // permission shouldn't get the "ttsEngine" permission.
646 {
647 scoped_refptr<Extension> extension =
648 CreateExtensionWithPermission("tts");
649 scoped_ptr<ExtensionAPI> api(
650 ExtensionAPI::CreateWithDefaultConfiguration());
651 EXPECT_FALSE(api->IsAvailable("ttsEngine",
652 extension.get(),
653 Feature::BLESSED_EXTENSION_CONTEXT,
654 GURL()).is_available());
655 EXPECT_TRUE(api->IsAvailable("tts",
656 extension.get(),
657 Feature::BLESSED_EXTENSION_CONTEXT,
658 GURL()).is_available());
659 }
660 }
661
MatchesURL(ExtensionAPI * api,const std::string & api_name,const std::string & url)662 bool MatchesURL(
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();
666 }
667
TEST(ExtensionAPITest,URLMatching)668 TEST(ExtensionAPITest, URLMatching) {
669 scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration());
670
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"));
675
676 // Also to internal URLs.
677 EXPECT_TRUE(MatchesURL(api.get(), "app", "about:flags"));
678 EXPECT_TRUE(MatchesURL(api.get(), "app", "chrome://flags"));
679
680 // "app" should be available to chrome-extension URLs.
681 EXPECT_TRUE(MatchesURL(api.get(), "app",
682 "chrome-extension://fakeextension"));
683
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"));
693 }
694
TEST(ExtensionAPITest,GetAPINameFromFullName)695 TEST(ExtensionAPITest, GetAPINameFromFullName) {
696 struct {
697 std::string input;
698 std::string api_name;
699 std::string child_name;
700 } test_data[] = {
701 { "", "", "" },
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" }
711 };
712
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,
717 &child_name);
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;
720 }
721 }
722
TEST(ExtensionAPITest,DefaultConfigurationFeatures)723 TEST(ExtensionAPITest, DefaultConfigurationFeatures) {
724 scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration());
725
726 SimpleFeature* bookmarks = static_cast<SimpleFeature*>(
727 api->GetFeatureDependency("api:bookmarks"));
728 SimpleFeature* bookmarks_create = static_cast<SimpleFeature*>(
729 api->GetFeatureDependency("api:bookmarks.create"));
730
731 struct {
732 SimpleFeature* feature;
733 // TODO(aa): More stuff to test over time.
734 } test_data[] = {
735 { bookmarks },
736 { bookmarks_create }
737 };
738
739 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
740 SimpleFeature* feature = test_data[i].feature;
741 ASSERT_TRUE(feature) << i;
742
743 EXPECT_TRUE(feature->whitelist()->empty());
744 EXPECT_TRUE(feature->extension_types()->empty());
745
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());
750 }
751 }
752
TEST(ExtensionAPITest,FeaturesRequireContexts)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);
766
767 struct {
768 base::DictionaryValue* api_features;
769 bool expect_success;
770 } test_data[] = {
771 { api_features1.get(), true },
772 { api_features2.get(), false }
773 };
774
775
776 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
777 BaseFeatureProvider api_feature_provider(*test_data[i].api_features,
778 CreateAPIFeature);
779 Feature* feature = api_feature_provider.GetFeature("test");
780 EXPECT_EQ(test_data[i].expect_success, feature != NULL) << i;
781 }
782 }
783
GetDictionaryFromList(const base::DictionaryValue * schema,const std::string & list_name,const int list_index,const base::DictionaryValue ** out)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));
791 }
792
TEST(ExtensionAPITest,TypesHaveNamespace)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");
799
800 std::string manifest_str;
801 ASSERT_TRUE(base::ReadFileToString(manifest_path, &manifest_str))
802 << "Failed to load: " << manifest_path.value();
803
804 ExtensionAPI api;
805 api.RegisterSchemaResource("test.foo", 0);
806 api.LoadSchema("test.foo", manifest_str);
807
808 const base::DictionaryValue* schema = api.GetSchema("test.foo");
809
810 const base::DictionaryValue* dict;
811 const base::DictionaryValue* sub_dict;
812 std::string type;
813
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);
827
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);
835
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);
843
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);
851 }
852
853 // Tests API availability with an empty manifest.
TEST(ExtensionAPITest,NoPermissions)854 TEST(ExtensionAPITest, NoPermissions) {
855 const struct {
856 const char* permission_name;
857 bool expect_success;
858 } kTests[] = {
859 // Test default module/package permission.
860 { "extension", true },
861 { "i18n", true },
862 { "permissions", true },
863 { "runtime", true },
864 { "test", 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 },
897 };
898
899 scoped_ptr<ExtensionAPI> extension_api(
900 ExtensionAPI::CreateWithDefaultConfiguration());
901 scoped_refptr<Extension> extension =
902 BuildExtension(ExtensionBuilder().Pass()).Build();
903
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,
907 extension.get(),
908 Feature::BLESSED_EXTENSION_CONTEXT,
909 GURL()).is_available())
910 << "Permission being tested: " << kTests[i].permission_name;
911 }
912 }
913
914 // Tests that permissions that require manifest keys are available when those
915 // keys are present.
TEST(ExtensionAPITest,ManifestKeys)916 TEST(ExtensionAPITest, ManifestKeys) {
917 scoped_ptr<ExtensionAPI> extension_api(
918 ExtensionAPI::CreateWithDefaultConfiguration());
919
920 scoped_refptr<Extension> extension =
921 BuildExtension(ExtensionBuilder().Pass())
922 .MergeManifest(DictionaryBuilder().Set("browser_action",
923 DictionaryBuilder().Pass()))
924 .Build();
925
926 EXPECT_TRUE(extension_api->IsAvailable("browserAction",
927 extension.get(),
928 Feature::BLESSED_EXTENSION_CONTEXT,
929 GURL()).is_available());
930 EXPECT_FALSE(extension_api->IsAvailable("pageAction",
931 extension.get(),
932 Feature::BLESSED_EXTENSION_CONTEXT,
933 GURL()).is_available());
934 }
935
936 } // namespace extensions
937