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