extension_api_unittest.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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
31namespace extensions {
32
33using test_util::BuildExtension;
34
35SimpleFeature* CreateAPIFeature() {
36  return new APIFeature();
37}
38
39TEST(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
66TEST(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
90TEST(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
112TEST(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
150TEST(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
255TEST(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
331TEST(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
353scoped_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
378scoped_refptr<Extension> CreateExtensionWithPermission(
379    const std::string& permission) {
380  std::set<std::string> permissions;
381  permissions.insert(permission);
382  return CreateExtensionWithPermissions(permissions);
383}
384
385TEST(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
456scoped_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
471scoped_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
503TEST(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
532TEST(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
585TEST(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
621bool 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
627TEST(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
654TEST(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
682TEST(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(Feature::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
716TEST(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
747static 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
756TEST(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.
817TEST(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.
879TEST(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