Home | History | Annotate | Download | only in extensions
      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 "base/command_line.h"
      6 #include "base/json/json_file_value_serializer.h"
      7 #include "base/message_loop/message_loop.h"
      8 #include "base/path_service.h"
      9 #include "base/strings/string_util.h"
     10 #include "chrome/browser/extensions/extension_service.h"
     11 #include "chrome/browser/extensions/extension_util.h"
     12 #include "chrome/browser/extensions/permissions_updater.h"
     13 #include "chrome/browser/extensions/test_extension_system.h"
     14 #include "chrome/browser/ui/webui/extensions/extension_settings_handler.h"
     15 #include "chrome/common/chrome_paths.h"
     16 #include "chrome/test/base/testing_profile.h"
     17 #include "components/crx_file/id_util.h"
     18 #include "content/public/test/test_browser_thread.h"
     19 #include "extensions/browser/extension_registry.h"
     20 #include "extensions/browser/management_policy.h"
     21 #include "extensions/common/constants.h"
     22 #include "extensions/common/extension.h"
     23 #include "extensions/common/extension_builder.h"
     24 #include "extensions/common/feature_switch.h"
     25 #include "extensions/common/value_builder.h"
     26 #include "testing/gtest/include/gtest/gtest.h"
     27 
     28 #if defined(OS_CHROMEOS)
     29 #include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
     30 #include "chrome/browser/chromeos/settings/cros_settings.h"
     31 #include "chrome/browser/chromeos/settings/device_settings_service.h"
     32 #endif
     33 
     34 namespace extensions {
     35 
     36 namespace {
     37 const char kAllHostsPermission[] = "*://*/*";
     38 }
     39 
     40 class ExtensionUITest : public testing::Test {
     41  public:
     42   ExtensionUITest()
     43       : ui_thread_(content::BrowserThread::UI, &message_loop_),
     44         file_thread_(content::BrowserThread::FILE, &message_loop_)  {}
     45 
     46  protected:
     47   virtual void SetUp() OVERRIDE {
     48     // Create an ExtensionService and ManagementPolicy to inject into the
     49     // ExtensionSettingsHandler.
     50     profile_.reset(new TestingProfile());
     51     TestExtensionSystem* system =
     52         static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile_.get()));
     53     extension_service_ = system->CreateExtensionService(
     54         CommandLine::ForCurrentProcess(), base::FilePath(), false);
     55     management_policy_ = system->management_policy();
     56 
     57     handler_.reset(new ExtensionSettingsHandler(extension_service_,
     58                                                 management_policy_));
     59   }
     60 
     61   virtual void TearDown() OVERRIDE {
     62     handler_.reset();
     63     profile_.reset();
     64     // Execute any pending deletion tasks.
     65     message_loop_.RunUntilIdle();
     66   }
     67 
     68   static base::DictionaryValue* DeserializeJSONTestData(
     69       const base::FilePath& path,
     70       std::string *error) {
     71     base::Value* value;
     72 
     73     JSONFileValueSerializer serializer(path);
     74     value = serializer.Deserialize(NULL, error);
     75 
     76     return static_cast<base::DictionaryValue*>(value);
     77   }
     78 
     79   const scoped_refptr<const Extension> CreateExtension(
     80       const std::string& name,
     81       ListBuilder& permissions) {
     82     const std::string kId = crx_file::id_util::GenerateId(name);
     83     scoped_refptr<const Extension> extension =
     84         ExtensionBuilder().SetManifest(
     85                                DictionaryBuilder()
     86                                    .Set("name", name)
     87                                    .Set("description", "an extension")
     88                                    .Set("manifest_version", 2)
     89                                    .Set("version", "1.0.0")
     90                                    .Set("permissions", permissions))
     91                           .SetLocation(Manifest::INTERNAL)
     92                           .SetID(kId)
     93                           .Build();
     94 
     95     ExtensionRegistry::Get(profile())->AddEnabled(extension);
     96     PermissionsUpdater(profile()).InitializePermissions(extension.get());
     97     return extension;
     98   }
     99 
    100   base::DictionaryValue* CreateExtensionDetailViewFromPath(
    101       const base::FilePath& extension_path,
    102       const std::vector<ExtensionPage>& pages,
    103       Manifest::Location location) {
    104     std::string error;
    105 
    106     base::FilePath manifest_path = extension_path.Append(kManifestFilename);
    107     scoped_ptr<base::DictionaryValue> extension_data(DeserializeJSONTestData(
    108         manifest_path, &error));
    109     EXPECT_EQ("", error);
    110 
    111     scoped_refptr<Extension> extension(Extension::Create(
    112         extension_path, location, *extension_data, Extension::REQUIRE_KEY,
    113         &error));
    114     EXPECT_TRUE(extension.get());
    115     EXPECT_EQ("", error);
    116 
    117     return handler_->CreateExtensionDetailValue(extension.get(), pages, NULL);
    118   }
    119 
    120   void CompareExpectedAndActualOutput(
    121       const base::FilePath& extension_path,
    122       const std::vector<ExtensionPage>& pages,
    123       const base::FilePath& expected_output_path) {
    124     std::string error;
    125 
    126     scoped_ptr<base::DictionaryValue> expected_output_data(
    127         DeserializeJSONTestData(expected_output_path, &error));
    128     EXPECT_EQ("", error);
    129 
    130     // Produce test output.
    131     scoped_ptr<base::DictionaryValue> actual_output_data(
    132         CreateExtensionDetailViewFromPath(
    133             extension_path, pages, Manifest::INVALID_LOCATION));
    134 
    135     // Compare the outputs.
    136     // Ignore unknown fields in the actual output data.
    137     std::string paths_details = " - expected (" +
    138         expected_output_path.MaybeAsASCII() + ") vs. actual (" +
    139         extension_path.MaybeAsASCII() + ")";
    140     for (base::DictionaryValue::Iterator field(*expected_output_data);
    141          !field.IsAtEnd(); field.Advance()) {
    142       const base::Value* expected_value = &field.value();
    143       base::Value* actual_value = NULL;
    144       EXPECT_TRUE(actual_output_data->Get(field.key(), &actual_value)) <<
    145           field.key() + " is missing" + paths_details;
    146       EXPECT_TRUE(expected_value->Equals(actual_value)) << field.key() +
    147           paths_details;
    148     }
    149   }
    150 
    151   Profile* profile() { return profile_.get(); }
    152   ExtensionSettingsHandler* handler() { return handler_.get(); }
    153 
    154   base::MessageLoop message_loop_;
    155   content::TestBrowserThread ui_thread_;
    156   content::TestBrowserThread file_thread_;
    157   scoped_ptr<TestingProfile> profile_;
    158   ExtensionService* extension_service_;
    159   ManagementPolicy* management_policy_;
    160   scoped_ptr<ExtensionSettingsHandler> handler_;
    161 
    162 #if defined OS_CHROMEOS
    163   chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
    164   chromeos::ScopedTestCrosSettings test_cros_settings_;
    165   chromeos::ScopedTestUserManager test_user_manager_;
    166 #endif
    167 };
    168 
    169 TEST_F(ExtensionUITest, GenerateExtensionsJSONData) {
    170   base::FilePath data_test_dir_path, extension_path, expected_output_path;
    171   EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_test_dir_path));
    172 
    173   // Test Extension1
    174   extension_path = data_test_dir_path.AppendASCII("extensions")
    175       .AppendASCII("good")
    176       .AppendASCII("Extensions")
    177       .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
    178       .AppendASCII("1.0.0.0");
    179 
    180   std::vector<ExtensionPage> pages;
    181   pages.push_back(ExtensionPage(
    182       GURL("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/bar.html"),
    183       42, 88, false, false));
    184   pages.push_back(ExtensionPage(
    185       GURL("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/dog.html"),
    186       0, 0, false, false));
    187 
    188   expected_output_path = data_test_dir_path.AppendASCII("extensions")
    189       .AppendASCII("ui")
    190       .AppendASCII("create_extension_detail_value_expected_output")
    191       .AppendASCII("good-extension1.json");
    192 
    193   CompareExpectedAndActualOutput(extension_path, pages, expected_output_path);
    194 
    195 #if !defined(OS_CHROMEOS)
    196   // Test Extension2
    197   extension_path = data_test_dir_path.AppendASCII("extensions")
    198       .AppendASCII("good")
    199       .AppendASCII("Extensions")
    200       .AppendASCII("hpiknbiabeeppbpihjehijgoemciehgk")
    201       .AppendASCII("2");
    202 
    203   expected_output_path = data_test_dir_path.AppendASCII("extensions")
    204       .AppendASCII("ui")
    205       .AppendASCII("create_extension_detail_value_expected_output")
    206       .AppendASCII("good-extension2.json");
    207 
    208   // It's OK to have duplicate URLs, so long as the IDs are different.
    209   pages[1].url = pages[0].url;
    210 
    211   CompareExpectedAndActualOutput(extension_path, pages, expected_output_path);
    212 #endif
    213 
    214   // Test Extension3
    215   extension_path = data_test_dir_path.AppendASCII("extensions")
    216       .AppendASCII("good")
    217       .AppendASCII("Extensions")
    218       .AppendASCII("bjafgdebaacbbbecmhlhpofkepfkgcpa")
    219       .AppendASCII("1.0");
    220 
    221   expected_output_path = data_test_dir_path.AppendASCII("extensions")
    222       .AppendASCII("ui")
    223       .AppendASCII("create_extension_detail_value_expected_output")
    224       .AppendASCII("good-extension3.json");
    225 
    226   pages.clear();
    227 
    228   CompareExpectedAndActualOutput(extension_path, pages, expected_output_path);
    229 }
    230 
    231 // Test that using Manifest::UNPACKED for the extension location triggers the
    232 // correct values in the details, including location, order, and allow_reload.
    233 TEST_F(ExtensionUITest, LocationLoadPropagation) {
    234   base::FilePath data_test_dir_path, extension_path;
    235   EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_test_dir_path));
    236 
    237   extension_path = data_test_dir_path.AppendASCII("extensions")
    238       .AppendASCII("good")
    239       .AppendASCII("Extensions")
    240       .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
    241       .AppendASCII("1.0.0.0");
    242 
    243   std::vector<ExtensionPage> pages;
    244 
    245   scoped_ptr<base::DictionaryValue> extension_details(
    246       CreateExtensionDetailViewFromPath(
    247           extension_path, pages, Manifest::UNPACKED));
    248 
    249   bool ui_allow_reload = false;
    250   bool ui_is_unpacked = false;
    251   base::FilePath::StringType ui_path;
    252 
    253   EXPECT_TRUE(extension_details->GetBoolean("allow_reload", &ui_allow_reload));
    254   EXPECT_TRUE(extension_details->GetBoolean("isUnpacked", &ui_is_unpacked));
    255   EXPECT_TRUE(extension_details->GetString("path", &ui_path));
    256   EXPECT_EQ(true, ui_allow_reload);
    257   EXPECT_EQ(true, ui_is_unpacked);
    258   EXPECT_EQ(extension_path, base::FilePath(ui_path));
    259 }
    260 
    261 // Test that using Manifest::EXTERNAL_PREF for the extension location triggers
    262 // the correct values in the details, including location, order, and
    263 // allow_reload.  Contrast to Manifest::UNPACKED, which has somewhat different
    264 // values.
    265 TEST_F(ExtensionUITest, LocationExternalPrefPropagation) {
    266   base::FilePath data_test_dir_path, extension_path;
    267   EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_test_dir_path));
    268 
    269   extension_path = data_test_dir_path.AppendASCII("extensions")
    270       .AppendASCII("good")
    271       .AppendASCII("Extensions")
    272       .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
    273       .AppendASCII("1.0.0.0");
    274 
    275   std::vector<ExtensionPage> pages;
    276 
    277   scoped_ptr<base::DictionaryValue> extension_details(
    278       CreateExtensionDetailViewFromPath(
    279           extension_path, pages, Manifest::EXTERNAL_PREF));
    280 
    281   bool ui_allow_reload = true;
    282   bool ui_is_unpacked = true;
    283   base::FilePath::StringType ui_path;
    284 
    285   EXPECT_TRUE(extension_details->GetBoolean("allow_reload", &ui_allow_reload));
    286   EXPECT_TRUE(extension_details->GetBoolean("isUnpacked", &ui_is_unpacked));
    287   EXPECT_FALSE(extension_details->GetString("path", &ui_path));
    288   EXPECT_FALSE(ui_allow_reload);
    289   EXPECT_FALSE(ui_is_unpacked);
    290 }
    291 
    292 // Test that the extension path is correctly propagated into the extension
    293 // details.
    294 TEST_F(ExtensionUITest, PathPropagation) {
    295   base::FilePath data_test_dir_path, extension_path;
    296   EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_test_dir_path));
    297 
    298   extension_path = data_test_dir_path.AppendASCII("extensions")
    299       .AppendASCII("good")
    300       .AppendASCII("Extensions")
    301       .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
    302       .AppendASCII("1.0.0.0");
    303 
    304   std::vector<ExtensionPage> pages;
    305 
    306   scoped_ptr<base::DictionaryValue> extension_details(
    307       CreateExtensionDetailViewFromPath(
    308           extension_path, pages, Manifest::UNPACKED));
    309 
    310   base::FilePath::StringType ui_path;
    311 
    312   EXPECT_TRUE(extension_details->GetString("path", &ui_path));
    313   EXPECT_EQ(extension_path, base::FilePath(ui_path));
    314 }
    315 
    316 // Test that the all_urls checkbox only shows up for extensions that want all
    317 // urls, and only when the switch is on.
    318 TEST_F(ExtensionUITest, ExtensionUIAllUrlsCheckbox) {
    319   // Start with the switch enabled.
    320   scoped_ptr<FeatureSwitch::ScopedOverride> enable_scripts_switch(
    321       new FeatureSwitch::ScopedOverride(
    322           FeatureSwitch::scripts_require_action(), true));
    323   // Two extensions - one with all urls, one without.
    324   scoped_refptr<const Extension> all_urls_extension = CreateExtension(
    325       "all_urls", ListBuilder().Append(kAllHostsPermission).Pass());
    326   scoped_refptr<const Extension> no_urls_extension =
    327       CreateExtension("no urls", ListBuilder().Pass());
    328 
    329   scoped_ptr<base::DictionaryValue> value(handler()->CreateExtensionDetailValue(
    330       all_urls_extension.get(), std::vector<ExtensionPage>(), NULL));
    331   bool result = false;
    332   const std::string kWantsAllUrls = "wantsAllUrls";
    333   const std::string kAllowAllUrls = "allowAllUrls";
    334 
    335   // The extension should want all urls, but not currently have it.
    336   EXPECT_TRUE(value->GetBoolean(kWantsAllUrls, &result));
    337   EXPECT_TRUE(result);
    338   EXPECT_TRUE(value->GetBoolean(kAllowAllUrls, &result));
    339   EXPECT_FALSE(result);
    340 
    341   // Give the extension all urls.
    342   util::SetAllowedScriptingOnAllUrls(
    343       all_urls_extension->id(), profile(), true);
    344 
    345   // Now the extension should both want and have all urls.
    346   value.reset(handler()->CreateExtensionDetailValue(
    347       all_urls_extension.get(), std::vector<ExtensionPage>(), NULL));
    348   EXPECT_TRUE(value->GetBoolean(kWantsAllUrls, &result));
    349   EXPECT_TRUE(result);
    350   EXPECT_TRUE(value->GetBoolean(kAllowAllUrls, &result));
    351   EXPECT_TRUE(result);
    352 
    353   // The other extension should neither want nor have all urls.
    354   value.reset(handler()->CreateExtensionDetailValue(
    355       no_urls_extension.get(), std::vector<ExtensionPage>(), NULL));
    356   EXPECT_TRUE(value->GetBoolean(kWantsAllUrls, &result));
    357   EXPECT_FALSE(result);
    358   EXPECT_TRUE(value->GetBoolean(kAllowAllUrls, &result));
    359   EXPECT_FALSE(result);
    360 
    361   // Turn off the switch and load another extension (so permissions are
    362   // re-initialized).
    363   enable_scripts_switch.reset();
    364 
    365   // Even though the extension has the all urls preference, the checkbox
    366   // shouldn't show up with the switch off.
    367   value.reset(handler()->CreateExtensionDetailValue(
    368       all_urls_extension.get(), std::vector<ExtensionPage>(), NULL));
    369   EXPECT_TRUE(value->GetBoolean(kWantsAllUrls, &result));
    370   EXPECT_FALSE(result);
    371   EXPECT_TRUE(value->GetBoolean(kAllowAllUrls, &result));
    372   EXPECT_TRUE(result);
    373 
    374   // Load another extension with all urls (so permissions get re-init'd).
    375   all_urls_extension = CreateExtension(
    376       "all_urls_II", ListBuilder().Append(kAllHostsPermission).Pass());
    377 
    378   // Even though the extension has all_urls permission, the checkbox shouldn't
    379   // show up without the switch.
    380   value.reset(handler()->CreateExtensionDetailValue(
    381       all_urls_extension.get(), std::vector<ExtensionPage>(), NULL));
    382   EXPECT_TRUE(value->GetBoolean(kWantsAllUrls, &result));
    383   EXPECT_FALSE(result);
    384   EXPECT_TRUE(value->GetBoolean(kAllowAllUrls, &result));
    385   EXPECT_FALSE(result);
    386 }
    387 
    388 }  // namespace extensions
    389