• 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/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