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