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/memory/ref_counted.h"
      6 #include "chrome/browser/download/download_crx_util.h"
      7 #include "chrome/browser/extensions/crx_installer.h"
      8 #include "chrome/browser/extensions/extension_browsertest.h"
      9 #include "chrome/browser/extensions/extension_install_prompt.h"
     10 #include "chrome/browser/extensions/extension_service.h"
     11 #include "chrome/browser/extensions/extension_system.h"
     12 #include "chrome/browser/profiles/profile.h"
     13 #include "chrome/browser/ui/browser.h"
     14 #include "chrome/browser/ui/browser_window.h"
     15 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     16 #include "chrome/common/extensions/extension.h"
     17 #include "chrome/common/extensions/extension_file_util.h"
     18 #include "chrome/common/extensions/feature_switch.h"
     19 #include "chrome/common/extensions/permissions/permission_set.h"
     20 #include "chrome/test/base/ui_test_utils.h"
     21 #include "content/public/browser/download_manager.h"
     22 #include "content/public/test/download_test_observer.h"
     23 #include "extensions/common/switches.h"
     24 #include "grit/generated_resources.h"
     25 #include "ui/base/l10n/l10n_util.h"
     26 
     27 class SkBitmap;
     28 
     29 namespace extensions {
     30 
     31 namespace {
     32 
     33 class MockInstallPrompt;
     34 
     35 // This class holds information about things that happen with a
     36 // MockInstallPrompt. We create the MockInstallPrompt but need to pass
     37 // ownership of it to CrxInstaller, so it isn't safe to hang this data on
     38 // MockInstallPrompt itself becuase we can't guarantee it's lifetime.
     39 class MockPromptProxy :
     40       public base::RefCountedThreadSafe<MockPromptProxy> {
     41  public:
     42   explicit MockPromptProxy(content::WebContents* web_contents);
     43 
     44   bool did_succeed() const { return !extension_id_.empty(); }
     45   const std::string& extension_id() { return extension_id_; }
     46   bool confirmation_requested() const { return confirmation_requested_; }
     47   const string16& error() const { return error_; }
     48 
     49   // To have any effect, this should be called before CreatePrompt.
     50   void set_record_oauth2_grant(bool record_oauth2_grant) {
     51     record_oauth2_grant_.reset(new bool(record_oauth2_grant));
     52   }
     53 
     54   void set_extension_id(const std::string& id) { extension_id_ = id; }
     55   void set_confirmation_requested() { confirmation_requested_ = true; }
     56   void set_error(const string16& error) { error_ = error; }
     57 
     58   scoped_ptr<ExtensionInstallPrompt> CreatePrompt();
     59 
     60  private:
     61   friend class base::RefCountedThreadSafe<MockPromptProxy>;
     62   virtual ~MockPromptProxy();
     63 
     64   // Data used to create a prompt.
     65   content::WebContents* web_contents_;
     66   scoped_ptr<bool> record_oauth2_grant_;
     67 
     68   // Data reported back to us by the prompt we created.
     69   bool confirmation_requested_;
     70   std::string extension_id_;
     71   string16 error_;
     72 };
     73 
     74 class MockInstallPrompt : public ExtensionInstallPrompt {
     75  public:
     76   MockInstallPrompt(content::WebContents* web_contents,
     77                     MockPromptProxy* proxy) :
     78       ExtensionInstallPrompt(web_contents),
     79       proxy_(proxy) {}
     80 
     81   void set_record_oauth2_grant(bool record) { record_oauth2_grant_ = record; }
     82 
     83   // Overriding some of the ExtensionInstallUI API.
     84   virtual void ConfirmInstall(
     85       Delegate* delegate,
     86       const Extension* extension,
     87       const ShowDialogCallback& show_dialog_callback) OVERRIDE {
     88     proxy_->set_confirmation_requested();
     89     delegate->InstallUIProceed();
     90   }
     91   virtual void OnInstallSuccess(const Extension* extension,
     92                                 SkBitmap* icon) OVERRIDE {
     93     proxy_->set_extension_id(extension->id());
     94     base::MessageLoopForUI::current()->Quit();
     95   }
     96   virtual void OnInstallFailure(const CrxInstallerError& error) OVERRIDE {
     97     proxy_->set_error(error.message());
     98     base::MessageLoopForUI::current()->Quit();
     99   }
    100 
    101  private:
    102   scoped_refptr<MockPromptProxy> proxy_;
    103 };
    104 
    105 
    106 MockPromptProxy::MockPromptProxy(content::WebContents* web_contents) :
    107     web_contents_(web_contents),
    108     confirmation_requested_(false) {
    109 }
    110 
    111 MockPromptProxy::~MockPromptProxy() {}
    112 
    113 scoped_ptr<ExtensionInstallPrompt> MockPromptProxy::CreatePrompt() {
    114   scoped_ptr<MockInstallPrompt> prompt(
    115       new MockInstallPrompt(web_contents_, this));
    116   if (record_oauth2_grant_.get())
    117     prompt->set_record_oauth2_grant(*record_oauth2_grant_.get());
    118   return prompt.PassAs<ExtensionInstallPrompt>();
    119 }
    120 
    121 
    122 scoped_refptr<MockPromptProxy> CreateMockPromptProxyForBrowser(
    123     Browser* browser) {
    124   return new MockPromptProxy(
    125       browser->tab_strip_model()->GetActiveWebContents());
    126 }
    127 
    128 }  // namespace
    129 
    130 class ExtensionCrxInstallerTest : public ExtensionBrowserTest {
    131  public:
    132   // Installs a crx from |crx_relpath| (a path relative to the extension test
    133   // data dir) with expected id |id|. Returns the installer.
    134   scoped_refptr<CrxInstaller> InstallWithPrompt(
    135       const std::string& ext_relpath,
    136       const std::string& id,
    137       scoped_refptr<MockPromptProxy> mock_install_prompt) {
    138     ExtensionService* service = extensions::ExtensionSystem::Get(
    139         browser()->profile())->extension_service();
    140     base::FilePath ext_path = test_data_dir_.AppendASCII(ext_relpath);
    141 
    142     std::string error;
    143     base::DictionaryValue* parsed_manifest =
    144         extension_file_util::LoadManifest(ext_path, &error);
    145     if (!parsed_manifest)
    146       return scoped_refptr<CrxInstaller>();
    147 
    148     scoped_ptr<WebstoreInstaller::Approval> approval;
    149     if (!id.empty()) {
    150       approval = WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
    151           browser()->profile(),
    152           id,
    153           scoped_ptr<base::DictionaryValue>(parsed_manifest));
    154     }
    155 
    156     scoped_refptr<CrxInstaller> installer(
    157         CrxInstaller::Create(service,
    158                              mock_install_prompt->CreatePrompt(),
    159                              approval.get()       /* keep ownership */));
    160     installer->set_allow_silent_install(true);
    161     installer->set_is_gallery_install(true);
    162     installer->InstallCrx(PackExtension(ext_path));
    163     content::RunMessageLoop();
    164 
    165     EXPECT_TRUE(mock_install_prompt->did_succeed());
    166     return installer;
    167   }
    168 
    169   // Installs an extension and checks that it has scopes granted IFF
    170   // |record_oauth2_grant| is true.
    171   void CheckHasEmptyScopesAfterInstall(const std::string& ext_relpath,
    172                                        bool record_oauth2_grant) {
    173     CommandLine::ForCurrentProcess()->AppendSwitch(
    174         switches::kEnableExperimentalExtensionApis);
    175 
    176     ExtensionService* service = extensions::ExtensionSystem::Get(
    177         browser()->profile())->extension_service();
    178 
    179     scoped_refptr<MockPromptProxy> mock_prompt =
    180         CreateMockPromptProxyForBrowser(browser());
    181 
    182     mock_prompt->set_record_oauth2_grant(record_oauth2_grant);
    183     scoped_refptr<CrxInstaller> installer =
    184         InstallWithPrompt("browsertest/scopes", std::string(), mock_prompt);
    185 
    186     scoped_refptr<PermissionSet> permissions =
    187         service->extension_prefs()->GetGrantedPermissions(
    188             mock_prompt->extension_id());
    189     ASSERT_TRUE(permissions.get());
    190   }
    191 
    192   // Creates and returns a popup ExtensionHost for an extension and waits
    193   // for a url to load in the host's web contents.
    194   // The caller is responsible for cleaning up the returned ExtensionHost.
    195   ExtensionHost* OpenUrlInExtensionPopupHost(const Extension* extension,
    196                                              const GURL& url) {
    197     ExtensionSystem* extension_system = extensions::ExtensionSystem::Get(
    198         browser()->profile());
    199     ExtensionProcessManager* epm = extension_system->process_manager();
    200     ExtensionHost* extension_host =
    201         epm->CreatePopupHost(extension, url, browser());
    202 
    203     extension_host->CreateRenderViewSoon();
    204     if (!extension_host->IsRenderViewLive()) {
    205       content::WindowedNotificationObserver observer(
    206           content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
    207           content::Source<content::WebContents>(
    208               extension_host->host_contents()));
    209       observer.Wait();
    210     }
    211 
    212     return extension_host;
    213   }
    214 };
    215 
    216 #if defined(OS_CHROMEOS)
    217 #define MAYBE_Whitelisting DISABLED_Whitelisting
    218 #else
    219 #define MAYBE_Whitelisting Whitelisting
    220 #endif
    221 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, MAYBE_Whitelisting) {
    222   std::string id = "hdgllgikmikobbofgnabhfimcfoopgnd";
    223   ExtensionService* service = extensions::ExtensionSystem::Get(
    224       browser()->profile())->extension_service();
    225 
    226   // Even whitelisted extensions with NPAPI should not prompt.
    227   scoped_refptr<MockPromptProxy> mock_prompt =
    228       CreateMockPromptProxyForBrowser(browser());
    229   scoped_refptr<CrxInstaller> installer =
    230       InstallWithPrompt("uitest/plugins", id, mock_prompt);
    231   EXPECT_FALSE(mock_prompt->confirmation_requested());
    232   EXPECT_TRUE(service->GetExtensionById(id, false));
    233 }
    234 
    235 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest,
    236                        GalleryInstallGetsExperimental) {
    237   // We must modify the command line temporarily in order to pack an extension
    238   // that requests the experimental permission.
    239   CommandLine* command_line = CommandLine::ForCurrentProcess();
    240   CommandLine old_command_line = *command_line;
    241   command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
    242   base::FilePath crx_path = PackExtension(
    243       test_data_dir_.AppendASCII("experimental"));
    244   ASSERT_FALSE(crx_path.empty());
    245 
    246   // Now reset the command line so that we are testing specifically whether
    247   // installing from webstore enables experimental permissions.
    248   *(CommandLine::ForCurrentProcess()) = old_command_line;
    249 
    250   EXPECT_FALSE(InstallExtension(crx_path, 0));
    251   EXPECT_TRUE(InstallExtensionFromWebstore(crx_path, 1));
    252 }
    253 
    254 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, PlatformAppCrx) {
    255   CommandLine::ForCurrentProcess()->AppendSwitch(
    256       switches::kEnableExperimentalExtensionApis);
    257   EXPECT_TRUE(InstallExtension(
    258       test_data_dir_.AppendASCII("minimal_platform_app.crx"), 1));
    259 }
    260 
    261 // http://crbug.com/136397
    262 #if defined(OS_CHROMEOS)
    263 #define MAYBE_PackAndInstallExtension DISABLED_PackAndInstallExtension
    264 #else
    265 #define MAYBE_PackAndInstallExtension PackAndInstallExtension
    266 #endif
    267 IN_PROC_BROWSER_TEST_F(
    268     ExtensionCrxInstallerTest, MAYBE_PackAndInstallExtension) {
    269   if (!FeatureSwitch::easy_off_store_install()->IsEnabled())
    270     return;
    271 
    272   const int kNumDownloadsExpected = 1;
    273 
    274   LOG(ERROR) << "PackAndInstallExtension: Packing extension";
    275   base::FilePath crx_path = PackExtension(
    276       test_data_dir_.AppendASCII("common/background_page"));
    277   ASSERT_FALSE(crx_path.empty());
    278   std::string crx_path_string(crx_path.value().begin(), crx_path.value().end());
    279   GURL url = GURL(std::string("file:///").append(crx_path_string));
    280 
    281   scoped_refptr<MockPromptProxy> mock_prompt =
    282       CreateMockPromptProxyForBrowser(browser());
    283   download_crx_util::SetMockInstallPromptForTesting(
    284       mock_prompt->CreatePrompt());
    285 
    286   LOG(ERROR) << "PackAndInstallExtension: Getting download manager";
    287   content::DownloadManager* download_manager =
    288       content::BrowserContext::GetDownloadManager(browser()->profile());
    289 
    290   LOG(ERROR) << "PackAndInstallExtension: Setting observer";
    291   scoped_ptr<content::DownloadTestObserver> observer(
    292       new content::DownloadTestObserverTerminal(
    293           download_manager, kNumDownloadsExpected,
    294           content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT));
    295   LOG(ERROR) << "PackAndInstallExtension: Navigating to URL";
    296   ui_test_utils::NavigateToURLWithDisposition(browser(), url, CURRENT_TAB,
    297       ui_test_utils::BROWSER_TEST_NONE);
    298 
    299   EXPECT_TRUE(WaitForCrxInstallerDone());
    300   LOG(ERROR) << "PackAndInstallExtension: Extension install";
    301   EXPECT_TRUE(mock_prompt->confirmation_requested());
    302   LOG(ERROR) << "PackAndInstallExtension: Extension install confirmed";
    303 }
    304 
    305 // Tests that scopes are only granted if |record_oauth2_grant_| on the prompt is
    306 // true.
    307 #if defined(OS_WIN)
    308 #define MAYBE_GrantScopes DISABLED_GrantScopes
    309 #else
    310 #define MAYBE_GrantScopes GrantScopes
    311 #endif
    312 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, MAYBE_GrantScopes) {
    313   EXPECT_NO_FATAL_FAILURE(CheckHasEmptyScopesAfterInstall("browsertest/scopes",
    314                                                           true));
    315 }
    316 
    317 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, DoNotGrantScopes) {
    318   EXPECT_NO_FATAL_FAILURE(CheckHasEmptyScopesAfterInstall("browsertest/scopes",
    319                                                           false));
    320 }
    321 
    322 // Off-store install cannot yet be disabled on Aura.
    323 #if defined(USE_AURA)
    324 #define MAYBE_AllowOffStore DISABLED_AllowOffStore
    325 #else
    326 #define MAYBE_AllowOffStore AllowOffStore
    327 #endif
    328 // Crashy: http://crbug.com/140893
    329 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, DISABLED_AllowOffStore) {
    330   ExtensionService* service = extensions::ExtensionSystem::Get(
    331       browser()->profile())->extension_service();
    332   const bool kTestData[] = {false, true};
    333 
    334   for (size_t i = 0; i < arraysize(kTestData); ++i) {
    335     scoped_refptr<MockPromptProxy> mock_prompt =
    336         CreateMockPromptProxyForBrowser(browser());
    337 
    338     scoped_refptr<CrxInstaller> crx_installer(
    339         CrxInstaller::Create(service, mock_prompt->CreatePrompt()));
    340     crx_installer->set_install_cause(
    341         extension_misc::INSTALL_CAUSE_USER_DOWNLOAD);
    342 
    343     if (kTestData[i]) {
    344       crx_installer->set_off_store_install_allow_reason(
    345           CrxInstaller::OffStoreInstallAllowedInTest);
    346     }
    347 
    348     crx_installer->InstallCrx(test_data_dir_.AppendASCII("good.crx"));
    349     EXPECT_EQ(kTestData[i], WaitForExtensionInstall()) << kTestData[i];
    350     EXPECT_EQ(kTestData[i], mock_prompt->did_succeed());
    351     EXPECT_EQ(kTestData[i], mock_prompt->confirmation_requested()) <<
    352         kTestData[i];
    353     if (kTestData[i]) {
    354       EXPECT_EQ(string16(), mock_prompt->error()) << kTestData[i];
    355     } else {
    356       EXPECT_EQ(l10n_util::GetStringUTF16(
    357           IDS_EXTENSION_INSTALL_DISALLOWED_ON_SITE),
    358           mock_prompt->error()) << kTestData[i];
    359     }
    360   }
    361 }
    362 
    363 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, HiDpiThemeTest) {
    364   base::FilePath crx_path = test_data_dir_.AppendASCII("theme_hidpi_crx");
    365   crx_path = crx_path.AppendASCII("theme_hidpi.crx");
    366 
    367   ASSERT_TRUE(InstallExtension(crx_path,1));
    368 
    369   const std::string extension_id("gllekhaobjnhgeagipipnkpmmmpchacm");
    370   ExtensionService* service = extensions::ExtensionSystem::Get(
    371       browser()->profile())->extension_service();
    372   ASSERT_TRUE(service);
    373   const extensions::Extension* extension =
    374      service->GetExtensionById(extension_id, false);
    375   ASSERT_TRUE(extension);
    376   EXPECT_EQ(extension_id, extension->id());
    377 
    378   UninstallExtension(extension_id);
    379   EXPECT_FALSE(service->GetExtensionById(extension_id, false));
    380 }
    381 
    382 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest,
    383                        InstallDelayedUntilNextUpdate) {
    384   const std::string extension_id("ldnnhddmnhbkjipkidpdiheffobcpfmf");
    385   base::FilePath crx_path = test_data_dir_.AppendASCII("delayed_install");
    386   ExtensionSystem* extension_system = extensions::ExtensionSystem::Get(
    387       browser()->profile());
    388   ExtensionService* service = extension_system->extension_service();
    389   ASSERT_TRUE(service);
    390 
    391   // Install version 1 of the test extension. This extension does not have
    392   // a background page but does have a browser action.
    393   ASSERT_TRUE(InstallExtension(crx_path.AppendASCII("v1.crx"), 1));
    394   const extensions::Extension* extension =
    395      service->GetExtensionById(extension_id, false);
    396   ASSERT_TRUE(extension);
    397   ASSERT_EQ(extension_id, extension->id());
    398   ASSERT_EQ("1.0", extension->version()->GetString());
    399 
    400   // Make test extension non-idle by opening the extension's browser action
    401   // popup. This should cause the installation to be delayed.
    402   std::string popup_url = std::string("chrome-extension://")
    403       + extension_id + std::string("/popup.html");
    404   scoped_ptr<ExtensionHost> extension_host = scoped_ptr<ExtensionHost>(
    405       OpenUrlInExtensionPopupHost(extension, GURL(popup_url)));
    406 
    407   // Install version 2 of the extension and check that it is indeed delayed.
    408   ASSERT_TRUE(UpdateExtensionWaitForIdle(
    409       extension_id, crx_path.AppendASCII("v2.crx"), 0));
    410 
    411   ASSERT_EQ(1u, service->delayed_installs()->size());
    412   extension = service->GetExtensionById(extension_id, false);
    413   ASSERT_EQ("1.0", extension->version()->GetString());
    414 
    415   // Make the extension idle again by navigating away from the extension's
    416   // browser action page. This should not trigger the delayed install.
    417   extension_system->process_manager()->UnregisterRenderViewHost(
    418       extension_host->render_view_host());
    419   ASSERT_EQ(1u, service->delayed_installs()->size());
    420 
    421   // Install version 3 of the extension. Because the extension is idle,
    422   // this install should succeed.
    423   ASSERT_TRUE(UpdateExtensionWaitForIdle(
    424       extension_id, crx_path.AppendASCII("v3.crx"), 0));
    425   extension = service->GetExtensionById(extension_id, false);
    426   ASSERT_EQ("3.0", extension->version()->GetString());
    427 
    428   // The version 2 delayed install should be cleaned up, and finishing
    429   // delayed extension installation shouldn't break anything.
    430   ASSERT_EQ(0u, service->delayed_installs()->size());
    431   service->MaybeFinishDelayedInstallations();
    432   extension = service->GetExtensionById(extension_id, false);
    433   ASSERT_EQ("3.0", extension->version()->GetString());
    434 }
    435 
    436 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, Blacklist) {
    437   extensions::Blacklist* blacklist =
    438       ExtensionSystem::Get(profile())->blacklist();
    439 
    440   // Fake the blacklisting of the extension we're about to install by
    441   // pretending that we get a blacklist update which includes it.
    442   const std::string kId = "gllekhaobjnhgeagipipnkpmmmpchacm";
    443   blacklist->SetFromUpdater(std::vector<std::string>(1, kId), "some-version");
    444 
    445   base::FilePath crx_path = test_data_dir_.AppendASCII("theme_hidpi_crx")
    446                                           .AppendASCII("theme_hidpi.crx");
    447   EXPECT_FALSE(InstallExtension(crx_path, 0));
    448 }
    449 
    450 }  // namespace extensions
    451