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/file_util.h"
     11 #include "base/files/file_path.h"
     12 #include "base/json/json_reader.h"
     13 #include "base/json/json_writer.h"
     14 #include "base/memory/ref_counted.h"
     15 #include "base/memory/scoped_ptr.h"
     16 #include "base/path_service.h"
     17 #include "base/strings/stringprintf.h"
     18 #include "base/values.h"
     19 #include "chrome/common/chrome_paths.h"
     20 #include "extensions/common/extension.h"
     21 #include "extensions/common/extension_builder.h"
     22 #include "extensions/common/features/api_feature.h"
     23 #include "extensions/common/features/base_feature_provider.h"
     24 #include "extensions/common/features/simple_feature.h"
     25 #include "extensions/common/manifest.h"
     26 #include "extensions/common/manifest_constants.h"
     27 #include "extensions/common/test_util.h"
     28 #include "extensions/common/value_builder.h"
     29 #include "testing/gtest/include/gtest/gtest.h"
     30 
     31 namespace extensions {
     32 
     33 using test_util::BuildExtension;
     34 
     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[] = {
     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 
     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 
    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 
    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 
    255 TEST(ExtensionAPITest, IsAnyFeatureAvailableToContext) {
    256   scoped_refptr<const Extension> app = ExtensionBuilder()
    257     .SetManifest(DictionaryBuilder()
    258       .Set("name", "app")
    259       .Set("app", DictionaryBuilder()
    260         .Set("background", DictionaryBuilder()
    261           .Set("scripts", ListBuilder().Append("background.js"))))
    262       .Set("version", "1")
    263       .Set("manifest_version", 2)).Build();
    264   scoped_refptr<const Extension> extension = ExtensionBuilder()
    265     .SetManifest(DictionaryBuilder()
    266       .Set("name", "extension")
    267       .Set("version", "1")
    268       .Set("manifest_version", 2)).Build();
    269 
    270   struct {
    271     std::string api_full_name;
    272     bool expect_is_available;
    273     Feature::Context context;
    274     const Extension* extension;
    275     GURL url;
    276   } test_data[] = {
    277     { "test1", false, Feature::WEB_PAGE_CONTEXT, NULL, GURL() },
    278     { "test1", true, Feature::UNBLESSED_EXTENSION_CONTEXT, NULL, GURL() },
    279     { "test1", false, Feature::UNBLESSED_EXTENSION_CONTEXT, app.get(), GURL() },
    280     { "test1", true, Feature::UNBLESSED_EXTENSION_CONTEXT, extension.get(),
    281         GURL() },
    282     { "test2", true, Feature::CONTENT_SCRIPT_CONTEXT, NULL, GURL() },
    283     { "test2", true, Feature::WEB_PAGE_CONTEXT, NULL,
    284         GURL("http://google.com") },
    285     { "test2.foo", false, Feature::WEB_PAGE_CONTEXT, NULL,
    286         GURL("http://google.com") },
    287     { "test3", true, Feature::CONTENT_SCRIPT_CONTEXT, NULL, GURL() },
    288     { "test3", true, Feature::WEB_PAGE_CONTEXT, NULL, GURL("http://foo.com") },
    289     { "test4.foo", true, Feature::CONTENT_SCRIPT_CONTEXT, NULL, GURL() },
    290     { "test7", false, Feature::WEB_PAGE_CONTEXT, NULL,
    291         GURL("http://google.com") },
    292     { "test7", true, Feature::WEB_PAGE_CONTEXT, NULL, GURL("http://foo.com") },
    293     { "test7", false, Feature::WEB_PAGE_CONTEXT, NULL, GURL("http://bar.com") }
    294   };
    295 
    296   base::FilePath api_features_path;
    297   PathService::Get(chrome::DIR_TEST_DATA, &api_features_path);
    298   api_features_path = api_features_path.AppendASCII("extensions")
    299       .AppendASCII("extension_api_unittest")
    300       .AppendASCII("api_features.json");
    301 
    302   std::string api_features_str;
    303   ASSERT_TRUE(base::ReadFileToString(
    304       api_features_path, &api_features_str)) << "api_features.json";
    305 
    306   scoped_ptr<base::DictionaryValue> value(static_cast<base::DictionaryValue*>(
    307       base::JSONReader::Read(api_features_str)));
    308   BaseFeatureProvider api_feature_provider(*value, CreateAPIFeature);
    309 
    310   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
    311     ExtensionAPI api;
    312     api.RegisterDependencyProvider("api", &api_feature_provider);
    313     for (base::DictionaryValue::Iterator iter(*value); !iter.IsAtEnd();
    314          iter.Advance()) {
    315       if (iter.key().find(".") == std::string::npos)
    316         api.RegisterSchemaResource(iter.key(), 0);
    317     }
    318 
    319     Feature* test_feature =
    320         api_feature_provider.GetFeature(test_data[i].api_full_name);
    321     ASSERT_TRUE(test_feature);
    322     EXPECT_EQ(test_data[i].expect_is_available,
    323               api.IsAnyFeatureAvailableToContext(*test_feature,
    324                                                  test_data[i].extension,
    325                                                  test_data[i].context,
    326                                                  test_data[i].url))
    327         << i;
    328   }
    329 }
    330 
    331 TEST(ExtensionAPITest, LazyGetSchema) {
    332   scoped_ptr<ExtensionAPI> apis(ExtensionAPI::CreateWithDefaultConfiguration());
    333 
    334   EXPECT_EQ(NULL, apis->GetSchema(std::string()));
    335   EXPECT_EQ(NULL, apis->GetSchema(std::string()));
    336   EXPECT_EQ(NULL, apis->GetSchema("experimental"));
    337   EXPECT_EQ(NULL, apis->GetSchema("experimental"));
    338   EXPECT_EQ(NULL, apis->GetSchema("foo"));
    339   EXPECT_EQ(NULL, apis->GetSchema("foo"));
    340 
    341   EXPECT_TRUE(apis->GetSchema("dns"));
    342   EXPECT_TRUE(apis->GetSchema("dns"));
    343   EXPECT_TRUE(apis->GetSchema("extension"));
    344   EXPECT_TRUE(apis->GetSchema("extension"));
    345   EXPECT_TRUE(apis->GetSchema("infobars"));
    346   EXPECT_TRUE(apis->GetSchema("infobars"));
    347   EXPECT_TRUE(apis->GetSchema("omnibox"));
    348   EXPECT_TRUE(apis->GetSchema("omnibox"));
    349   EXPECT_TRUE(apis->GetSchema("storage"));
    350   EXPECT_TRUE(apis->GetSchema("storage"));
    351 }
    352 
    353 scoped_refptr<Extension> CreateExtensionWithPermissions(
    354     const std::set<std::string>& permissions) {
    355   base::DictionaryValue manifest;
    356   manifest.SetString("name", "extension");
    357   manifest.SetString("version", "1.0");
    358   manifest.SetInteger("manifest_version", 2);
    359   {
    360     scoped_ptr<base::ListValue> permissions_list(new base::ListValue());
    361     for (std::set<std::string>::const_iterator i = permissions.begin();
    362         i != permissions.end(); ++i) {
    363       permissions_list->Append(new base::StringValue(*i));
    364     }
    365     manifest.Set("permissions", permissions_list.release());
    366   }
    367 
    368   std::string error;
    369   scoped_refptr<Extension> extension(Extension::Create(
    370       base::FilePath(), Manifest::UNPACKED,
    371       manifest, Extension::NO_FLAGS, &error));
    372   CHECK(extension.get());
    373   CHECK(error.empty());
    374 
    375   return extension;
    376 }
    377 
    378 scoped_refptr<Extension> CreateExtensionWithPermission(
    379     const std::string& permission) {
    380   std::set<std::string> permissions;
    381   permissions.insert(permission);
    382   return CreateExtensionWithPermissions(permissions);
    383 }
    384 
    385 TEST(ExtensionAPITest, ExtensionWithUnprivilegedAPIs) {
    386   scoped_refptr<Extension> extension;
    387   {
    388     std::set<std::string> permissions;
    389     permissions.insert("storage");
    390     permissions.insert("history");
    391     extension = CreateExtensionWithPermissions(permissions);
    392   }
    393 
    394   scoped_ptr<ExtensionAPI> extension_api(
    395       ExtensionAPI::CreateWithDefaultConfiguration());
    396 
    397   const FeatureProvider& api_features = *FeatureProvider::GetAPIFeatures();
    398 
    399   // "storage" is completely unprivileged.
    400   EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
    401       *api_features.GetFeature("storage"),
    402       NULL,
    403       Feature::BLESSED_EXTENSION_CONTEXT,
    404       GURL()));
    405   EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
    406       *api_features.GetFeature("storage"),
    407       NULL,
    408       Feature::UNBLESSED_EXTENSION_CONTEXT,
    409       GURL()));
    410   EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
    411       *api_features.GetFeature("storage"),
    412       NULL,
    413       Feature::CONTENT_SCRIPT_CONTEXT,
    414       GURL()));
    415 
    416   // "extension" is partially unprivileged.
    417   EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
    418       *api_features.GetFeature("extension"),
    419       NULL,
    420       Feature::BLESSED_EXTENSION_CONTEXT,
    421       GURL()));
    422   EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
    423       *api_features.GetFeature("extension"),
    424       NULL,
    425       Feature::UNBLESSED_EXTENSION_CONTEXT,
    426       GURL()));
    427   EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
    428       *api_features.GetFeature("extension"),
    429       NULL,
    430       Feature::CONTENT_SCRIPT_CONTEXT,
    431       GURL()));
    432   EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
    433       *api_features.GetFeature("extension.getURL"),
    434       NULL,
    435       Feature::CONTENT_SCRIPT_CONTEXT,
    436       GURL()));
    437 
    438   // "history" is entirely privileged.
    439   EXPECT_TRUE(extension_api->IsAnyFeatureAvailableToContext(
    440       *api_features.GetFeature("history"),
    441       NULL,
    442       Feature::BLESSED_EXTENSION_CONTEXT,
    443       GURL()));
    444   EXPECT_FALSE(extension_api->IsAnyFeatureAvailableToContext(
    445       *api_features.GetFeature("history"),
    446       NULL,
    447       Feature::UNBLESSED_EXTENSION_CONTEXT,
    448       GURL()));
    449   EXPECT_FALSE(extension_api->IsAnyFeatureAvailableToContext(
    450       *api_features.GetFeature("history"),
    451       NULL,
    452       Feature::CONTENT_SCRIPT_CONTEXT,
    453       GURL()));
    454 }
    455 
    456 scoped_refptr<Extension> CreateHostedApp() {
    457   base::DictionaryValue values;
    458   values.SetString(manifest_keys::kName, "test");
    459   values.SetString(manifest_keys::kVersion, "0.1");
    460   values.Set(manifest_keys::kWebURLs, new base::ListValue());
    461   values.SetString(manifest_keys::kLaunchWebURL,
    462                    "http://www.example.com");
    463   std::string error;
    464   scoped_refptr<Extension> extension(Extension::Create(
    465       base::FilePath(), Manifest::INTERNAL, values, Extension::NO_FLAGS,
    466       &error));
    467   CHECK(extension.get());
    468   return extension;
    469 }
    470 
    471 scoped_refptr<Extension> CreatePackagedAppWithPermissions(
    472     const std::set<std::string>& permissions) {
    473   base::DictionaryValue values;
    474   values.SetString(manifest_keys::kName, "test");
    475   values.SetString(manifest_keys::kVersion, "0.1");
    476   values.SetString(manifest_keys::kPlatformAppBackground,
    477       "http://www.example.com");
    478 
    479   base::DictionaryValue* app = new base::DictionaryValue();
    480   base::DictionaryValue* background = new base::DictionaryValue();
    481   base::ListValue* scripts = new base::ListValue();
    482   scripts->Append(new base::StringValue("test.js"));
    483   background->Set("scripts", scripts);
    484   app->Set("background", background);
    485   values.Set(manifest_keys::kApp, app);
    486   {
    487     scoped_ptr<base::ListValue> permissions_list(new base::ListValue());
    488     for (std::set<std::string>::const_iterator i = permissions.begin();
    489         i != permissions.end(); ++i) {
    490       permissions_list->Append(new base::StringValue(*i));
    491     }
    492     values.Set("permissions", permissions_list.release());
    493   }
    494 
    495   std::string error;
    496   scoped_refptr<Extension> extension(Extension::Create(
    497       base::FilePath(), Manifest::INTERNAL, values, Extension::NO_FLAGS,
    498       &error));
    499   CHECK(extension.get()) << error;
    500   return extension;
    501 }
    502 
    503 TEST(ExtensionAPITest, HostedAppPermissions) {
    504   scoped_refptr<Extension> extension = CreateHostedApp();
    505 
    506   scoped_ptr<ExtensionAPI> extension_api(
    507       ExtensionAPI::CreateWithDefaultConfiguration());
    508 
    509   // "runtime" and "tabs" should not be available in hosted apps.
    510   EXPECT_FALSE(extension_api->IsAvailable("runtime",
    511                                           extension.get(),
    512                                           Feature::BLESSED_EXTENSION_CONTEXT,
    513                                           GURL()).is_available());
    514   EXPECT_FALSE(extension_api->IsAvailable("runtime.id",
    515                                           extension.get(),
    516                                           Feature::BLESSED_EXTENSION_CONTEXT,
    517                                           GURL()).is_available());
    518   EXPECT_FALSE(extension_api->IsAvailable("runtime.sendMessage",
    519                                           extension.get(),
    520                                           Feature::BLESSED_EXTENSION_CONTEXT,
    521                                           GURL()).is_available());
    522   EXPECT_FALSE(extension_api->IsAvailable("runtime.sendNativeMessage",
    523                                           extension.get(),
    524                                           Feature::BLESSED_EXTENSION_CONTEXT,
    525                                           GURL()).is_available());
    526   EXPECT_FALSE(extension_api->IsAvailable("tabs.create",
    527                                           extension.get(),
    528                                           Feature::BLESSED_EXTENSION_CONTEXT,
    529                                           GURL()).is_available());
    530 }
    531 
    532 TEST(ExtensionAPITest, AppAndFriendsAvailability) {
    533 
    534   scoped_ptr<ExtensionAPI> extension_api(
    535       ExtensionAPI::CreateWithDefaultConfiguration());
    536 
    537   // Make sure chrome.app.runtime and chrome.app.window are available to apps,
    538   // and chrome.app is not.
    539   {
    540     std::set<std::string> permissions;
    541     permissions.insert("app.runtime");
    542     permissions.insert("app.window");
    543     scoped_refptr<Extension> extension =
    544         CreatePackagedAppWithPermissions(permissions);
    545     EXPECT_FALSE(extension_api->IsAvailable(
    546         "app",
    547         extension.get(),
    548         Feature::BLESSED_EXTENSION_CONTEXT,
    549         GURL("http://foo.com")).is_available());
    550     EXPECT_TRUE(extension_api->IsAvailable(
    551         "app.runtime",
    552         extension.get(),
    553         Feature::BLESSED_EXTENSION_CONTEXT,
    554         GURL("http://foo.com")).is_available());
    555     EXPECT_TRUE(extension_api->IsAvailable(
    556         "app.window",
    557         extension.get(),
    558         Feature::BLESSED_EXTENSION_CONTEXT,
    559         GURL("http://foo.com")).is_available());
    560   }
    561   // Make sure chrome.app.runtime and chrome.app.window are not available to
    562   // extensions, and chrome.app is.
    563   {
    564     std::set<std::string> permissions;
    565     scoped_refptr<Extension> extension =
    566         CreateExtensionWithPermissions(permissions);
    567     EXPECT_TRUE(extension_api->IsAvailable(
    568         "app",
    569         extension.get(),
    570         Feature::BLESSED_EXTENSION_CONTEXT,
    571         GURL("http://foo.com")).is_available());
    572     EXPECT_FALSE(extension_api->IsAvailable(
    573         "app.runtime",
    574         extension.get(),
    575         Feature::BLESSED_EXTENSION_CONTEXT,
    576         GURL("http://foo.com")).is_available());
    577     EXPECT_FALSE(extension_api->IsAvailable(
    578         "app.window",
    579         extension.get(),
    580         Feature::BLESSED_EXTENSION_CONTEXT,
    581         GURL("http://foo.com")).is_available());
    582   }
    583 }
    584 
    585 TEST(ExtensionAPITest, ExtensionWithDependencies) {
    586   // Extension with the "ttsEngine" permission but not the "tts" permission; it
    587   // should not automatically get "tts" permission.
    588   {
    589     scoped_refptr<Extension> extension =
    590         CreateExtensionWithPermission("ttsEngine");
    591     scoped_ptr<ExtensionAPI> api(
    592         ExtensionAPI::CreateWithDefaultConfiguration());
    593     EXPECT_TRUE(api->IsAvailable("ttsEngine",
    594                                  extension.get(),
    595                                  Feature::BLESSED_EXTENSION_CONTEXT,
    596                                  GURL()).is_available());
    597     EXPECT_FALSE(api->IsAvailable("tts",
    598                                   extension.get(),
    599                                   Feature::BLESSED_EXTENSION_CONTEXT,
    600                                   GURL()).is_available());
    601   }
    602 
    603   // Conversely, extension with the "tts" permission but not the "ttsEngine"
    604   // permission shouldn't get the "ttsEngine" permission.
    605   {
    606     scoped_refptr<Extension> extension =
    607         CreateExtensionWithPermission("tts");
    608     scoped_ptr<ExtensionAPI> api(
    609         ExtensionAPI::CreateWithDefaultConfiguration());
    610     EXPECT_FALSE(api->IsAvailable("ttsEngine",
    611                                   extension.get(),
    612                                   Feature::BLESSED_EXTENSION_CONTEXT,
    613                                   GURL()).is_available());
    614     EXPECT_TRUE(api->IsAvailable("tts",
    615                                  extension.get(),
    616                                  Feature::BLESSED_EXTENSION_CONTEXT,
    617                                  GURL()).is_available());
    618   }
    619 }
    620 
    621 bool MatchesURL(
    622     ExtensionAPI* api, const std::string& api_name, const std::string& url) {
    623   return api->IsAvailable(
    624       api_name, NULL, Feature::WEB_PAGE_CONTEXT, GURL(url)).is_available();
    625 }
    626 
    627 TEST(ExtensionAPITest, URLMatching) {
    628   scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration());
    629 
    630   // "app" API is available to all URLs that content scripts can be injected.
    631   EXPECT_TRUE(MatchesURL(api.get(), "app", "http://example.com/example.html"));
    632   EXPECT_TRUE(MatchesURL(api.get(), "app", "https://blah.net"));
    633   EXPECT_TRUE(MatchesURL(api.get(), "app", "file://somefile.html"));
    634 
    635   // But not internal URLs.
    636   EXPECT_FALSE(MatchesURL(api.get(), "app", "about:flags"));
    637   EXPECT_FALSE(MatchesURL(api.get(), "app", "chrome://flags"));
    638 
    639   // "app" should be available to chrome-extension URLs.
    640   EXPECT_TRUE(MatchesURL(api.get(), "app",
    641                           "chrome-extension://fakeextension"));
    642 
    643   // "storage" API (for example) isn't available to any URLs.
    644   EXPECT_FALSE(MatchesURL(api.get(), "storage",
    645                           "http://example.com/example.html"));
    646   EXPECT_FALSE(MatchesURL(api.get(), "storage", "https://blah.net"));
    647   EXPECT_FALSE(MatchesURL(api.get(), "storage", "file://somefile.html"));
    648   EXPECT_FALSE(MatchesURL(api.get(), "storage", "about:flags"));
    649   EXPECT_FALSE(MatchesURL(api.get(), "storage", "chrome://flags"));
    650   EXPECT_FALSE(MatchesURL(api.get(), "storage",
    651                           "chrome-extension://fakeextension"));
    652 }
    653 
    654 TEST(ExtensionAPITest, GetAPINameFromFullName) {
    655   struct {
    656     std::string input;
    657     std::string api_name;
    658     std::string child_name;
    659   } test_data[] = {
    660     { "", "", "" },
    661     { "unknown", "", "" },
    662     { "bookmarks", "bookmarks", "" },
    663     { "bookmarks.", "bookmarks", "" },
    664     { ".bookmarks", "", "" },
    665     { "bookmarks.create", "bookmarks", "create" },
    666     { "bookmarks.create.", "bookmarks", "create." },
    667     { "bookmarks.create.monkey", "bookmarks", "create.monkey" },
    668     { "bookmarkManagerPrivate", "bookmarkManagerPrivate", "" },
    669     { "bookmarkManagerPrivate.copy", "bookmarkManagerPrivate", "copy" }
    670   };
    671 
    672   scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration());
    673   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
    674     std::string child_name;
    675     std::string api_name = api->GetAPINameFromFullName(test_data[i].input,
    676                                                        &child_name);
    677     EXPECT_EQ(test_data[i].api_name, api_name) << test_data[i].input;
    678     EXPECT_EQ(test_data[i].child_name, child_name) << test_data[i].input;
    679   }
    680 }
    681 
    682 TEST(ExtensionAPITest, DefaultConfigurationFeatures) {
    683   scoped_ptr<ExtensionAPI> api(ExtensionAPI::CreateWithDefaultConfiguration());
    684 
    685   SimpleFeature* bookmarks = static_cast<SimpleFeature*>(
    686       api->GetFeatureDependency("api:bookmarks"));
    687   SimpleFeature* bookmarks_create = static_cast<SimpleFeature*>(
    688       api->GetFeatureDependency("api:bookmarks.create"));
    689 
    690   struct {
    691     SimpleFeature* feature;
    692     // TODO(aa): More stuff to test over time.
    693   } test_data[] = {
    694     { bookmarks },
    695     { bookmarks_create }
    696   };
    697 
    698   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
    699     SimpleFeature* feature = test_data[i].feature;
    700     ASSERT_TRUE(feature) << i;
    701 
    702     EXPECT_TRUE(feature->whitelist()->empty());
    703     EXPECT_TRUE(feature->extension_types()->empty());
    704 
    705     EXPECT_EQ(1u, feature->GetContexts()->size());
    706     EXPECT_TRUE(feature->GetContexts()->count(
    707         Feature::BLESSED_EXTENSION_CONTEXT));
    708 
    709     EXPECT_EQ(SimpleFeature::UNSPECIFIED_LOCATION, feature->location());
    710     EXPECT_TRUE(feature->platforms()->empty());
    711     EXPECT_EQ(0, feature->min_manifest_version());
    712     EXPECT_EQ(0, feature->max_manifest_version());
    713   }
    714 }
    715 
    716 TEST(ExtensionAPITest, FeaturesRequireContexts) {
    717   // TODO(cduvall): Make this check API featues.
    718   scoped_ptr<base::DictionaryValue> api_features1(new base::DictionaryValue());
    719   scoped_ptr<base::DictionaryValue> api_features2(new base::DictionaryValue());
    720   base::DictionaryValue* test1 = new base::DictionaryValue();
    721   base::DictionaryValue* test2 = new base::DictionaryValue();
    722   base::ListValue* contexts = new base::ListValue();
    723   contexts->Append(new base::StringValue("content_script"));
    724   test1->Set("contexts", contexts);
    725   test1->SetString("channel", "stable");
    726   test2->SetString("channel", "stable");
    727   api_features1->Set("test", test1);
    728   api_features2->Set("test", test2);
    729 
    730   struct {
    731     base::DictionaryValue* api_features;
    732     bool expect_success;
    733   } test_data[] = {
    734     { api_features1.get(), true },
    735     { api_features2.get(), false }
    736   };
    737 
    738 
    739   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
    740     BaseFeatureProvider api_feature_provider(*test_data[i].api_features,
    741                                              CreateAPIFeature);
    742     Feature* feature = api_feature_provider.GetFeature("test");
    743     EXPECT_EQ(test_data[i].expect_success, feature != NULL) << i;
    744   }
    745 }
    746 
    747 static void GetDictionaryFromList(const base::DictionaryValue* schema,
    748                                   const std::string& list_name,
    749                                   const int list_index,
    750                                   const base::DictionaryValue** out) {
    751   const base::ListValue* list;
    752   EXPECT_TRUE(schema->GetList(list_name, &list));
    753   EXPECT_TRUE(list->GetDictionary(list_index, out));
    754 }
    755 
    756 TEST(ExtensionAPITest, TypesHaveNamespace) {
    757   base::FilePath manifest_path;
    758   PathService::Get(chrome::DIR_TEST_DATA, &manifest_path);
    759   manifest_path = manifest_path.AppendASCII("extensions")
    760       .AppendASCII("extension_api_unittest")
    761       .AppendASCII("types_have_namespace.json");
    762 
    763   std::string manifest_str;
    764   ASSERT_TRUE(base::ReadFileToString(manifest_path, &manifest_str))
    765       << "Failed to load: " << manifest_path.value();
    766 
    767   ExtensionAPI api;
    768   api.RegisterSchemaResource("test.foo", 0);
    769   api.LoadSchema("test.foo", manifest_str);
    770 
    771   const base::DictionaryValue* schema = api.GetSchema("test.foo");
    772 
    773   const base::DictionaryValue* dict;
    774   const base::DictionaryValue* sub_dict;
    775   std::string type;
    776 
    777   GetDictionaryFromList(schema, "types", 0, &dict);
    778   EXPECT_TRUE(dict->GetString("id", &type));
    779   EXPECT_EQ("test.foo.TestType", type);
    780   EXPECT_TRUE(dict->GetString("customBindings", &type));
    781   EXPECT_EQ("test.foo.TestType", type);
    782   EXPECT_TRUE(dict->GetDictionary("properties", &sub_dict));
    783   const base::DictionaryValue* property;
    784   EXPECT_TRUE(sub_dict->GetDictionary("foo", &property));
    785   EXPECT_TRUE(property->GetString("$ref", &type));
    786   EXPECT_EQ("test.foo.OtherType", type);
    787   EXPECT_TRUE(sub_dict->GetDictionary("bar", &property));
    788   EXPECT_TRUE(property->GetString("$ref", &type));
    789   EXPECT_EQ("fully.qualified.Type", type);
    790 
    791   GetDictionaryFromList(schema, "functions", 0, &dict);
    792   GetDictionaryFromList(dict, "parameters", 0, &sub_dict);
    793   EXPECT_TRUE(sub_dict->GetString("$ref", &type));
    794   EXPECT_EQ("test.foo.TestType", type);
    795   EXPECT_TRUE(dict->GetDictionary("returns", &sub_dict));
    796   EXPECT_TRUE(sub_dict->GetString("$ref", &type));
    797   EXPECT_EQ("fully.qualified.Type", type);
    798 
    799   GetDictionaryFromList(schema, "functions", 1, &dict);
    800   GetDictionaryFromList(dict, "parameters", 0, &sub_dict);
    801   EXPECT_TRUE(sub_dict->GetString("$ref", &type));
    802   EXPECT_EQ("fully.qualified.Type", type);
    803   EXPECT_TRUE(dict->GetDictionary("returns", &sub_dict));
    804   EXPECT_TRUE(sub_dict->GetString("$ref", &type));
    805   EXPECT_EQ("test.foo.TestType", type);
    806 
    807   GetDictionaryFromList(schema, "events", 0, &dict);
    808   GetDictionaryFromList(dict, "parameters", 0, &sub_dict);
    809   EXPECT_TRUE(sub_dict->GetString("$ref", &type));
    810   EXPECT_EQ("test.foo.TestType", type);
    811   GetDictionaryFromList(dict, "parameters", 1, &sub_dict);
    812   EXPECT_TRUE(sub_dict->GetString("$ref", &type));
    813   EXPECT_EQ("fully.qualified.Type", type);
    814 }
    815 
    816 // Tests API availability with an empty manifest.
    817 TEST(ExtensionAPITest, NoPermissions) {
    818   const struct {
    819     const char* permission_name;
    820     bool expect_success;
    821   } kTests[] = {
    822     // Test default module/package permission.
    823     { "extension",      true },
    824     { "i18n",           true },
    825     { "permissions",    true },
    826     { "runtime",        true },
    827     { "test",           true },
    828     // These require manifest keys.
    829     { "browserAction",  false },
    830     { "pageAction",     false },
    831     { "pageActions",    false },
    832     // Some negative tests.
    833     { "bookmarks",      false },
    834     { "cookies",        false },
    835     { "history",        false },
    836     // Make sure we find the module name after stripping '.'
    837     { "runtime.abcd.onStartup",  true },
    838     // Test Tabs/Windows functions.
    839     { "tabs.create",      true },
    840     { "tabs.duplicate",   true },
    841     { "tabs.onRemoved",   true },
    842     { "tabs.remove",      true },
    843     { "tabs.update",      true },
    844     { "tabs.getSelected", true },
    845     { "tabs.onUpdated",   true },
    846     { "windows.get",      true },
    847     { "windows.create",   true },
    848     { "windows.remove",   true },
    849     { "windows.update",   true },
    850     // Test some whitelisted functions. These require no permissions.
    851     { "app.getDetails",           true },
    852     { "app.getDetailsForFrame",   true },
    853     { "app.getIsInstalled",       true },
    854     { "app.installState",         true },
    855     { "app.runningState",         true },
    856     { "management.getPermissionWarningsByManifest", true },
    857     { "management.uninstallSelf", true },
    858     // But other functions in those modules do.
    859     { "management.getPermissionWarningsById", false },
    860   };
    861 
    862   scoped_ptr<ExtensionAPI> extension_api(
    863       ExtensionAPI::CreateWithDefaultConfiguration());
    864   scoped_refptr<Extension> extension =
    865       BuildExtension(ExtensionBuilder().Pass()).Build();
    866 
    867   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) {
    868     EXPECT_EQ(kTests[i].expect_success,
    869               extension_api->IsAvailable(kTests[i].permission_name,
    870                                          extension.get(),
    871                                          Feature::BLESSED_EXTENSION_CONTEXT,
    872                                          GURL()).is_available())
    873         << "Permission being tested: " << kTests[i].permission_name;
    874   }
    875 }
    876 
    877 // Tests that permissions that require manifest keys are available when those
    878 // keys are present.
    879 TEST(ExtensionAPITest, ManifestKeys) {
    880   scoped_ptr<ExtensionAPI> extension_api(
    881       ExtensionAPI::CreateWithDefaultConfiguration());
    882 
    883   scoped_refptr<Extension> extension =
    884       BuildExtension(ExtensionBuilder().Pass())
    885       .MergeManifest(DictionaryBuilder().Set("browser_action",
    886                                              DictionaryBuilder().Pass()))
    887       .Build();
    888 
    889   EXPECT_TRUE(extension_api->IsAvailable("browserAction",
    890                                          extension.get(),
    891                                          Feature::BLESSED_EXTENSION_CONTEXT,
    892                                          GURL()).is_available());
    893   EXPECT_FALSE(extension_api->IsAvailable("pageAction",
    894                                           extension.get(),
    895                                           Feature::BLESSED_EXTENSION_CONTEXT,
    896                                           GURL()).is_available());
    897 }
    898 
    899 }  // namespace extensions
    900