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