• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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