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