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 "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[] = {
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 Feature* test_feature =
320 api_feature_provider.GetFeature(test_data[i].api_full_name);
321 ASSERT_TRUE(test_feature);
322 EXPECT_EQ(test_data[i].expect_is_available,
323 api.IsAnyFeatureAvailableToContext(*test_feature,
324 test_data[i].extension,
325 test_data[i].context,
326 test_data[i].url))
327 << i;
328 }
329 }
330
TEST(ExtensionAPITest,LazyGetSchema)331 TEST(ExtensionAPITest, LazyGetSchema) {
332 scoped_ptr<ExtensionAPI> apis(ExtensionAPI::CreateWithDefaultConfiguration());
333
334 EXPECT_EQ(NULL, apis->GetSchema(std::string()));
335 EXPECT_EQ(NULL, apis->GetSchema(std::string()));
336 EXPECT_EQ(NULL, apis->GetSchema("experimental"));
337 EXPECT_EQ(NULL, apis->GetSchema("experimental"));
338 EXPECT_EQ(NULL, apis->GetSchema("foo"));
339 EXPECT_EQ(NULL, apis->GetSchema("foo"));
340
341 EXPECT_TRUE(apis->GetSchema("dns"));
342 EXPECT_TRUE(apis->GetSchema("dns"));
343 EXPECT_TRUE(apis->GetSchema("extension"));
344 EXPECT_TRUE(apis->GetSchema("extension"));
345 EXPECT_TRUE(apis->GetSchema("infobars"));
346 EXPECT_TRUE(apis->GetSchema("infobars"));
347 EXPECT_TRUE(apis->GetSchema("omnibox"));
348 EXPECT_TRUE(apis->GetSchema("omnibox"));
349 EXPECT_TRUE(apis->GetSchema("storage"));
350 EXPECT_TRUE(apis->GetSchema("storage"));
351 }
352
CreateExtensionWithPermissions(const std::set<std::string> & permissions)353 scoped_refptr<Extension> CreateExtensionWithPermissions(
354 const std::set<std::string>& permissions) {
355 base::DictionaryValue manifest;
356 manifest.SetString("name", "extension");
357 manifest.SetString("version", "1.0");
358 manifest.SetInteger("manifest_version", 2);
359 {
360 scoped_ptr<base::ListValue> permissions_list(new base::ListValue());
361 for (std::set<std::string>::const_iterator i = permissions.begin();
362 i != permissions.end(); ++i) {
363 permissions_list->Append(new base::StringValue(*i));
364 }
365 manifest.Set("permissions", permissions_list.release());
366 }
367
368 std::string error;
369 scoped_refptr<Extension> extension(Extension::Create(
370 base::FilePath(), Manifest::UNPACKED,
371 manifest, Extension::NO_FLAGS, &error));
372 CHECK(extension.get());
373 CHECK(error.empty());
374
375 return extension;
376 }
377
CreateExtensionWithPermission(const std::string & permission)378 scoped_refptr<Extension> CreateExtensionWithPermission(
379 const std::string& permission) {
380 std::set<std::string> permissions;
381 permissions.insert(permission);
382 return CreateExtensionWithPermissions(permissions);
383 }
384
TEST(ExtensionAPITest,ExtensionWithUnprivilegedAPIs)385 TEST(ExtensionAPITest, ExtensionWithUnprivilegedAPIs) {
386 scoped_refptr<Extension> extension;
387 {
388 std::set<std::string> permissions;
389 permissions.insert("storage");
390 permissions.insert("history");
391 extension = CreateExtensionWithPermissions(permissions);
392 }
393
394 scoped_ptr<ExtensionAPI> extension_api(
395 ExtensionAPI::CreateWithDefaultConfiguration());
396
397 const FeatureProvider& api_features = *FeatureProvider::GetAPIFeatures();
398
399 // "storage" is completely unprivileged.
400 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
401 *api_features.GetFeature("storage"),
402 NULL,
403 Feature::BLESSED_EXTENSION_CONTEXT,
404 GURL()));
405 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
406 *api_features.GetFeature("storage"),
407 NULL,
408 Feature::UNBLESSED_EXTENSION_CONTEXT,
409 GURL()));
410 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
411 *api_features.GetFeature("storage"),
412 NULL,
413 Feature::CONTENT_SCRIPT_CONTEXT,
414 GURL()));
415
416 // "extension" is partially unprivileged.
417 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
418 *api_features.GetFeature("extension"),
419 NULL,
420 Feature::BLESSED_EXTENSION_CONTEXT,
421 GURL()));
422 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
423 *api_features.GetFeature("extension"),
424 NULL,
425 Feature::UNBLESSED_EXTENSION_CONTEXT,
426 GURL()));
427 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
428 *api_features.GetFeature("extension"),
429 NULL,
430 Feature::CONTENT_SCRIPT_CONTEXT,
431 GURL()));
432 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
433 *api_features.GetFeature("extension.getURL"),
434 NULL,
435 Feature::CONTENT_SCRIPT_CONTEXT,
436 GURL()));
437
438 // "history" is entirely privileged.
439 EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
440 *api_features.GetFeature("history"),
441 NULL,
442 Feature::BLESSED_EXTENSION_CONTEXT,
443 GURL()));
444 EXPECT_FALSE(extension_api->IsAnyFeatureAvailableToContext(
445 *api_features.GetFeature("history"),
446 NULL,
447 Feature::UNBLESSED_EXTENSION_CONTEXT,
448 GURL()));
449 EXPECT_FALSE(extension_api->IsAnyFeatureAvailableToContext(
450 *api_features.GetFeature("history"),
451 NULL,
452 Feature::CONTENT_SCRIPT_CONTEXT,
453 GURL()));
454 }
455
CreateHostedApp()456 scoped_refptr<Extension> CreateHostedApp() {
457 base::DictionaryValue values;
458 values.SetString(manifest_keys::kName, "test");
459 values.SetString(manifest_keys::kVersion, "0.1");
460 values.Set(manifest_keys::kWebURLs, new base::ListValue());
461 values.SetString(manifest_keys::kLaunchWebURL,
462 "http://www.example.com");
463 std::string error;
464 scoped_refptr<Extension> extension(Extension::Create(
465 base::FilePath(), Manifest::INTERNAL, values, Extension::NO_FLAGS,
466 &error));
467 CHECK(extension.get());
468 return extension;
469 }
470
CreatePackagedAppWithPermissions(const std::set<std::string> & permissions)471 scoped_refptr<Extension> CreatePackagedAppWithPermissions(
472 const std::set<std::string>& permissions) {
473 base::DictionaryValue values;
474 values.SetString(manifest_keys::kName, "test");
475 values.SetString(manifest_keys::kVersion, "0.1");
476 values.SetString(manifest_keys::kPlatformAppBackground,
477 "http://www.example.com");
478
479 base::DictionaryValue* app = new base::DictionaryValue();
480 base::DictionaryValue* background = new base::DictionaryValue();
481 base::ListValue* scripts = new base::ListValue();
482 scripts->Append(new base::StringValue("test.js"));
483 background->Set("scripts", scripts);
484 app->Set("background", background);
485 values.Set(manifest_keys::kApp, app);
486 {
487 scoped_ptr<base::ListValue> permissions_list(new base::ListValue());
488 for (std::set<std::string>::const_iterator i = permissions.begin();
489 i != permissions.end(); ++i) {
490 permissions_list->Append(new base::StringValue(*i));
491 }
492 values.Set("permissions", permissions_list.release());
493 }
494
495 std::string error;
496 scoped_refptr<Extension> extension(Extension::Create(
497 base::FilePath(), Manifest::INTERNAL, values, Extension::NO_FLAGS,
498 &error));
499 CHECK(extension.get()) << error;
500 return extension;
501 }
502
TEST(ExtensionAPITest,HostedAppPermissions)503 TEST(ExtensionAPITest, HostedAppPermissions) {
504 scoped_refptr<Extension> extension = CreateHostedApp();
505
506 scoped_ptr<ExtensionAPI> extension_api(
507 ExtensionAPI::CreateWithDefaultConfiguration());
508
509 // "runtime" and "tabs" should not be available in hosted apps.
510 EXPECT_FALSE(extension_api->IsAvailable("runtime",
511 extension.get(),
512 Feature::BLESSED_EXTENSION_CONTEXT,
513 GURL()).is_available());
514 EXPECT_FALSE(extension_api->IsAvailable("runtime.id",
515 extension.get(),
516 Feature::BLESSED_EXTENSION_CONTEXT,
517 GURL()).is_available());
518 EXPECT_FALSE(extension_api->IsAvailable("runtime.sendMessage",
519 extension.get(),
520 Feature::BLESSED_EXTENSION_CONTEXT,
521 GURL()).is_available());
522 EXPECT_FALSE(extension_api->IsAvailable("runtime.sendNativeMessage",
523 extension.get(),
524 Feature::BLESSED_EXTENSION_CONTEXT,
525 GURL()).is_available());
526 EXPECT_FALSE(extension_api->IsAvailable("tabs.create",
527 extension.get(),
528 Feature::BLESSED_EXTENSION_CONTEXT,
529 GURL()).is_available());
530 }
531
TEST(ExtensionAPITest,AppAndFriendsAvailability)532 TEST(ExtensionAPITest, AppAndFriendsAvailability) {
533
534 scoped_ptr<ExtensionAPI> extension_api(
535 ExtensionAPI::CreateWithDefaultConfiguration());
536
537 // Make sure chrome.app.runtime and chrome.app.window are available to apps,
538 // and chrome.app is not.
539 {
540 std::set<std::string> permissions;
541 permissions.insert("app.runtime");
542 permissions.insert("app.window");
543 scoped_refptr<Extension> extension =
544 CreatePackagedAppWithPermissions(permissions);
545 EXPECT_FALSE(extension_api->IsAvailable(
546 "app",
547 extension.get(),
548 Feature::BLESSED_EXTENSION_CONTEXT,
549 GURL("http://foo.com")).is_available());
550 EXPECT_TRUE(extension_api->IsAvailable(
551 "app.runtime",
552 extension.get(),
553 Feature::BLESSED_EXTENSION_CONTEXT,
554 GURL("http://foo.com")).is_available());
555 EXPECT_TRUE(extension_api->IsAvailable(
556 "app.window",
557 extension.get(),
558 Feature::BLESSED_EXTENSION_CONTEXT,
559 GURL("http://foo.com")).is_available());
560 }
561 // Make sure chrome.app.runtime and chrome.app.window are not available to
562 // extensions, and chrome.app is.
563 {
564 std::set<std::string> permissions;
565 scoped_refptr<Extension> extension =
566 CreateExtensionWithPermissions(permissions);
567 EXPECT_TRUE(extension_api->IsAvailable(
568 "app",
569 extension.get(),
570 Feature::BLESSED_EXTENSION_CONTEXT,
571 GURL("http://foo.com")).is_available());
572 EXPECT_FALSE(extension_api->IsAvailable(
573 "app.runtime",
574 extension.get(),
575 Feature::BLESSED_EXTENSION_CONTEXT,
576 GURL("http://foo.com")).is_available());
577 EXPECT_FALSE(extension_api->IsAvailable(
578 "app.window",
579 extension.get(),
580 Feature::BLESSED_EXTENSION_CONTEXT,
581 GURL("http://foo.com")).is_available());
582 }
583 }
584
TEST(ExtensionAPITest,ExtensionWithDependencies)585 TEST(ExtensionAPITest, ExtensionWithDependencies) {
586 // Extension with the "ttsEngine" permission but not the "tts" permission; it
587 // should not automatically get "tts" permission.
588 {
589 scoped_refptr<Extension> extension =
590 CreateExtensionWithPermission("ttsEngine");
591 scoped_ptr<ExtensionAPI> api(
592 ExtensionAPI::CreateWithDefaultConfiguration());
593 EXPECT_TRUE(api->IsAvailable("ttsEngine",
594 extension.get(),
595 Feature::BLESSED_EXTENSION_CONTEXT,
596 GURL()).is_available());
597 EXPECT_FALSE(api->IsAvailable("tts",
598 extension.get(),
599 Feature::BLESSED_EXTENSION_CONTEXT,
600 GURL()).is_available());
601 }
602
603 // Conversely, extension with the "tts" permission but not the "ttsEngine"
604 // permission shouldn't get the "ttsEngine" permission.
605 {
606 scoped_refptr<Extension> extension =
607 CreateExtensionWithPermission("tts");
608 scoped_ptr<ExtensionAPI> api(
609 ExtensionAPI::CreateWithDefaultConfiguration());
610 EXPECT_FALSE(api->IsAvailable("ttsEngine",
611 extension.get(),
612 Feature::BLESSED_EXTENSION_CONTEXT,
613 GURL()).is_available());
614 EXPECT_TRUE(api->IsAvailable("tts",
615 extension.get(),
616 Feature::BLESSED_EXTENSION_CONTEXT,
617 GURL()).is_available());
618 }
619 }
620
MatchesURL(ExtensionAPI * api,const std::string & api_name,const std::string & url)621 bool MatchesURL(
622 ExtensionAPI* api, const std::string& api_name, const std::string& url) {
623 return api->IsAvailable(
624 api_name, NULL, Feature::WEB_PAGE_CONTEXT, GURL(url)).is_available();
625 }
626
TEST(ExtensionAPITest,URLMatching)627 TEST(ExtensionAPITest, URLMatching) {
628 scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration());
629
630 // "app" API is available to all URLs that content scripts can be injected.
631 EXPECT_TRUE(MatchesURL(api.get(), "app", "http://example.com/example.html"));
632 EXPECT_TRUE(MatchesURL(api.get(), "app", "https://blah.net"));
633 EXPECT_TRUE(MatchesURL(api.get(), "app", "file://somefile.html"));
634
635 // But not internal URLs.
636 EXPECT_FALSE(MatchesURL(api.get(), "app", "about:flags"));
637 EXPECT_FALSE(MatchesURL(api.get(), "app", "chrome://flags"));
638
639 // "app" should be available to chrome-extension URLs.
640 EXPECT_TRUE(MatchesURL(api.get(), "app",
641 "chrome-extension://fakeextension"));
642
643 // "storage" API (for example) isn't available to any URLs.
644 EXPECT_FALSE(MatchesURL(api.get(), "storage",
645 "http://example.com/example.html"));
646 EXPECT_FALSE(MatchesURL(api.get(), "storage", "https://blah.net"));
647 EXPECT_FALSE(MatchesURL(api.get(), "storage", "file://somefile.html"));
648 EXPECT_FALSE(MatchesURL(api.get(), "storage", "about:flags"));
649 EXPECT_FALSE(MatchesURL(api.get(), "storage", "chrome://flags"));
650 EXPECT_FALSE(MatchesURL(api.get(), "storage",
651 "chrome-extension://fakeextension"));
652 }
653
TEST(ExtensionAPITest,GetAPINameFromFullName)654 TEST(ExtensionAPITest, GetAPINameFromFullName) {
655 struct {
656 std::string input;
657 std::string api_name;
658 std::string child_name;
659 } test_data[] = {
660 { "", "", "" },
661 { "unknown", "", "" },
662 { "bookmarks", "bookmarks", "" },
663 { "bookmarks.", "bookmarks", "" },
664 { ".bookmarks", "", "" },
665 { "bookmarks.create", "bookmarks", "create" },
666 { "bookmarks.create.", "bookmarks", "create." },
667 { "bookmarks.create.monkey", "bookmarks", "create.monkey" },
668 { "bookmarkManagerPrivate", "bookmarkManagerPrivate", "" },
669 { "bookmarkManagerPrivate.copy", "bookmarkManagerPrivate", "copy" }
670 };
671
672 scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration());
673 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
674 std::string child_name;
675 std::string api_name = api->GetAPINameFromFullName(test_data[i].input,
676 &child_name);
677 EXPECT_EQ(test_data[i].api_name, api_name) << test_data[i].input;
678 EXPECT_EQ(test_data[i].child_name, child_name) << test_data[i].input;
679 }
680 }
681
TEST(ExtensionAPITest,DefaultConfigurationFeatures)682 TEST(ExtensionAPITest, DefaultConfigurationFeatures) {
683 scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration());
684
685 SimpleFeature* bookmarks = static_cast<SimpleFeature*>(
686 api->GetFeatureDependency("api:bookmarks"));
687 SimpleFeature* bookmarks_create = static_cast<SimpleFeature*>(
688 api->GetFeatureDependency("api:bookmarks.create"));
689
690 struct {
691 SimpleFeature* feature;
692 // TODO(aa): More stuff to test over time.
693 } test_data[] = {
694 { bookmarks },
695 { bookmarks_create }
696 };
697
698 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
699 SimpleFeature* feature = test_data[i].feature;
700 ASSERT_TRUE(feature) << i;
701
702 EXPECT_TRUE(feature->whitelist()->empty());
703 EXPECT_TRUE(feature->extension_types()->empty());
704
705 EXPECT_EQ(1u, feature->GetContexts()->size());
706 EXPECT_TRUE(feature->GetContexts()->count(
707 Feature::BLESSED_EXTENSION_CONTEXT));
708
709 EXPECT_EQ(SimpleFeature::UNSPECIFIED_LOCATION, feature->location());
710 EXPECT_TRUE(feature->platforms()->empty());
711 EXPECT_EQ(0, feature->min_manifest_version());
712 EXPECT_EQ(0, feature->max_manifest_version());
713 }
714 }
715
TEST(ExtensionAPITest,FeaturesRequireContexts)716 TEST(ExtensionAPITest, FeaturesRequireContexts) {
717 // TODO(cduvall): Make this check API featues.
718 scoped_ptr<base::DictionaryValue> api_features1(new base::DictionaryValue());
719 scoped_ptr<base::DictionaryValue> api_features2(new base::DictionaryValue());
720 base::DictionaryValue* test1 = new base::DictionaryValue();
721 base::DictionaryValue* test2 = new base::DictionaryValue();
722 base::ListValue* contexts = new base::ListValue();
723 contexts->Append(new base::StringValue("content_script"));
724 test1->Set("contexts", contexts);
725 test1->SetString("channel", "stable");
726 test2->SetString("channel", "stable");
727 api_features1->Set("test", test1);
728 api_features2->Set("test", test2);
729
730 struct {
731 base::DictionaryValue* api_features;
732 bool expect_success;
733 } test_data[] = {
734 { api_features1.get(), true },
735 { api_features2.get(), false }
736 };
737
738
739 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
740 BaseFeatureProvider api_feature_provider(*test_data[i].api_features,
741 CreateAPIFeature);
742 Feature* feature = api_feature_provider.GetFeature("test");
743 EXPECT_EQ(test_data[i].expect_success, feature != NULL) << i;
744 }
745 }
746
GetDictionaryFromList(const base::DictionaryValue * schema,const std::string & list_name,const int list_index,const base::DictionaryValue ** out)747 static void GetDictionaryFromList(const base::DictionaryValue* schema,
748 const std::string& list_name,
749 const int list_index,
750 const base::DictionaryValue** out) {
751 const base::ListValue* list;
752 EXPECT_TRUE(schema->GetList(list_name, &list));
753 EXPECT_TRUE(list->GetDictionary(list_index, out));
754 }
755
TEST(ExtensionAPITest,TypesHaveNamespace)756 TEST(ExtensionAPITest, TypesHaveNamespace) {
757 base::FilePath manifest_path;
758 PathService::Get(chrome::DIR_TEST_DATA, &manifest_path);
759 manifest_path = manifest_path.AppendASCII("extensions")
760 .AppendASCII("extension_api_unittest")
761 .AppendASCII("types_have_namespace.json");
762
763 std::string manifest_str;
764 ASSERT_TRUE(base::ReadFileToString(manifest_path, &manifest_str))
765 << "Failed to load: " << manifest_path.value();
766
767 ExtensionAPI api;
768 api.RegisterSchemaResource("test.foo", 0);
769 api.LoadSchema("test.foo", manifest_str);
770
771 const base::DictionaryValue* schema = api.GetSchema("test.foo");
772
773 const base::DictionaryValue* dict;
774 const base::DictionaryValue* sub_dict;
775 std::string type;
776
777 GetDictionaryFromList(schema, "types", 0, &dict);
778 EXPECT_TRUE(dict->GetString("id", &type));
779 EXPECT_EQ("test.foo.TestType", type);
780 EXPECT_TRUE(dict->GetString("customBindings", &type));
781 EXPECT_EQ("test.foo.TestType", type);
782 EXPECT_TRUE(dict->GetDictionary("properties", &sub_dict));
783 const base::DictionaryValue* property;
784 EXPECT_TRUE(sub_dict->GetDictionary("foo", &property));
785 EXPECT_TRUE(property->GetString("$ref", &type));
786 EXPECT_EQ("test.foo.OtherType", type);
787 EXPECT_TRUE(sub_dict->GetDictionary("bar", &property));
788 EXPECT_TRUE(property->GetString("$ref", &type));
789 EXPECT_EQ("fully.qualified.Type", type);
790
791 GetDictionaryFromList(schema, "functions", 0, &dict);
792 GetDictionaryFromList(dict, "parameters", 0, &sub_dict);
793 EXPECT_TRUE(sub_dict->GetString("$ref", &type));
794 EXPECT_EQ("test.foo.TestType", type);
795 EXPECT_TRUE(dict->GetDictionary("returns", &sub_dict));
796 EXPECT_TRUE(sub_dict->GetString("$ref", &type));
797 EXPECT_EQ("fully.qualified.Type", type);
798
799 GetDictionaryFromList(schema, "functions", 1, &dict);
800 GetDictionaryFromList(dict, "parameters", 0, &sub_dict);
801 EXPECT_TRUE(sub_dict->GetString("$ref", &type));
802 EXPECT_EQ("fully.qualified.Type", type);
803 EXPECT_TRUE(dict->GetDictionary("returns", &sub_dict));
804 EXPECT_TRUE(sub_dict->GetString("$ref", &type));
805 EXPECT_EQ("test.foo.TestType", type);
806
807 GetDictionaryFromList(schema, "events", 0, &dict);
808 GetDictionaryFromList(dict, "parameters", 0, &sub_dict);
809 EXPECT_TRUE(sub_dict->GetString("$ref", &type));
810 EXPECT_EQ("test.foo.TestType", type);
811 GetDictionaryFromList(dict, "parameters", 1, &sub_dict);
812 EXPECT_TRUE(sub_dict->GetString("$ref", &type));
813 EXPECT_EQ("fully.qualified.Type", type);
814 }
815
816 // Tests API availability with an empty manifest.
TEST(ExtensionAPITest,NoPermissions)817 TEST(ExtensionAPITest, NoPermissions) {
818 const struct {
819 const char* permission_name;
820 bool expect_success;
821 } kTests[] = {
822 // Test default module/package permission.
823 { "extension", true },
824 { "i18n", true },
825 { "permissions", true },
826 { "runtime", true },
827 { "test", true },
828 // These require manifest keys.
829 { "browserAction", false },
830 { "pageAction", false },
831 { "pageActions", false },
832 // Some negative tests.
833 { "bookmarks", false },
834 { "cookies", false },
835 { "history", false },
836 // Make sure we find the module name after stripping '.'
837 { "runtime.abcd.onStartup", true },
838 // Test Tabs/Windows functions.
839 { "tabs.create", true },
840 { "tabs.duplicate", true },
841 { "tabs.onRemoved", true },
842 { "tabs.remove", true },
843 { "tabs.update", true },
844 { "tabs.getSelected", true },
845 { "tabs.onUpdated", true },
846 { "windows.get", true },
847 { "windows.create", true },
848 { "windows.remove", true },
849 { "windows.update", true },
850 // Test some whitelisted functions. These require no permissions.
851 { "app.getDetails", true },
852 { "app.getDetailsForFrame", true },
853 { "app.getIsInstalled", true },
854 { "app.installState", true },
855 { "app.runningState", true },
856 { "management.getPermissionWarningsByManifest", true },
857 { "management.uninstallSelf", true },
858 // But other functions in those modules do.
859 { "management.getPermissionWarningsById", false },
860 };
861
862 scoped_ptr<ExtensionAPI> extension_api(
863 ExtensionAPI::CreateWithDefaultConfiguration());
864 scoped_refptr<Extension> extension =
865 BuildExtension(ExtensionBuilder().Pass()).Build();
866
867 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) {
868 EXPECT_EQ(kTests[i].expect_success,
869 extension_api->IsAvailable(kTests[i].permission_name,
870 extension.get(),
871 Feature::BLESSED_EXTENSION_CONTEXT,
872 GURL()).is_available())
873 << "Permission being tested: " << kTests[i].permission_name;
874 }
875 }
876
877 // Tests that permissions that require manifest keys are available when those
878 // keys are present.
TEST(ExtensionAPITest,ManifestKeys)879 TEST(ExtensionAPITest, ManifestKeys) {
880 scoped_ptr<ExtensionAPI> extension_api(
881 ExtensionAPI::CreateWithDefaultConfiguration());
882
883 scoped_refptr<Extension> extension =
884 BuildExtension(ExtensionBuilder().Pass())
885 .MergeManifest(DictionaryBuilder().Set("browser_action",
886 DictionaryBuilder().Pass()))
887 .Build();
888
889 EXPECT_TRUE(extension_api->IsAvailable("browserAction",
890 extension.get(),
891 Feature::BLESSED_EXTENSION_CONTEXT,
892 GURL()).is_available());
893 EXPECT_FALSE(extension_api->IsAvailable("pageAction",
894 extension.get(),
895 Feature::BLESSED_EXTENSION_CONTEXT,
896 GURL()).is_available());
897 }
898
899 } // namespace extensions
900