Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2011 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/browser/extensions/extension_browsertest.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/command_line.h"
     10 #include "base/file_path.h"
     11 #include "base/file_util.h"
     12 #include "base/path_service.h"
     13 #include "base/string_number_conversions.h"
     14 #include "base/memory/scoped_temp_dir.h"
     15 #include "chrome/browser/extensions/crx_installer.h"
     16 #include "chrome/browser/extensions/extension_creator.h"
     17 #include "chrome/browser/extensions/extension_error_reporter.h"
     18 #include "chrome/browser/extensions/extension_host.h"
     19 #include "chrome/browser/extensions/extension_install_ui.h"
     20 #include "chrome/browser/extensions/extension_service.h"
     21 #include "chrome/browser/profiles/profile.h"
     22 #include "chrome/browser/ui/browser.h"
     23 #include "chrome/browser/ui/browser_window.h"
     24 #include "chrome/browser/ui/omnibox/location_bar.h"
     25 #include "chrome/common/chrome_paths.h"
     26 #include "chrome/common/chrome_switches.h"
     27 #include "chrome/test/ui_test_utils.h"
     28 #include "content/common/notification_registrar.h"
     29 #include "content/common/notification_service.h"
     30 #include "content/common/notification_type.h"
     31 
     32 ExtensionBrowserTest::ExtensionBrowserTest()
     33     : loaded_(false),
     34       installed_(false),
     35       extension_installs_observed_(0),
     36       target_page_action_count_(-1),
     37       target_visible_page_action_count_(-1) {
     38   EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
     39 }
     40 
     41 void ExtensionBrowserTest::SetUpCommandLine(CommandLine* command_line) {
     42   // This enables DOM automation for tab contentses.
     43   EnableDOMAutomation();
     44 
     45   // This enables it for extension hosts.
     46   ExtensionHost::EnableDOMAutomation();
     47 
     48   PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_);
     49   test_data_dir_ = test_data_dir_.AppendASCII("extensions");
     50 
     51 #if defined(OS_CHROMEOS)
     52   // This makes sure that we create the Default profile first, with no
     53   // ExtensionService and then the real profile with one, as we do when
     54   // running on chromeos.
     55   command_line->AppendSwitchASCII(switches::kLoginUser,
     56                                   "TestUser (at) gmail.com");
     57   command_line->AppendSwitchASCII(switches::kLoginProfile, "user");
     58   command_line->AppendSwitch(switches::kNoFirstRun);
     59 #endif
     60 }
     61 
     62 const Extension* ExtensionBrowserTest::LoadExtensionImpl(
     63     const FilePath& path, bool incognito_enabled, bool fileaccess_enabled) {
     64   ExtensionService* service = browser()->profile()->GetExtensionService();
     65   {
     66     NotificationRegistrar registrar;
     67     registrar.Add(this, NotificationType::EXTENSION_LOADED,
     68                   NotificationService::AllSources());
     69     service->LoadExtension(path);
     70     ui_test_utils::RunMessageLoop();
     71   }
     72 
     73   // Find the extension by iterating backwards since it is likely last.
     74   FilePath extension_path = path;
     75   file_util::AbsolutePath(&extension_path);
     76   const Extension* extension = NULL;
     77   for (ExtensionList::const_reverse_iterator iter =
     78            service->extensions()->rbegin();
     79        iter != service->extensions()->rend(); ++iter) {
     80     if ((*iter)->path() == extension_path) {
     81       extension = *iter;
     82       break;
     83     }
     84   }
     85   if (!extension)
     86     return NULL;
     87 
     88   // The call to OnExtensionInstalled ensures the other extension prefs
     89   // are set up with the defaults.
     90   service->extension_prefs()->OnExtensionInstalled(
     91       extension, Extension::ENABLED, false);
     92   service->SetIsIncognitoEnabled(extension->id(), incognito_enabled);
     93   service->SetAllowFileAccess(extension, fileaccess_enabled);
     94 
     95   if (!WaitForExtensionHostsToLoad())
     96     return NULL;
     97 
     98   return extension;
     99 }
    100 
    101 const Extension* ExtensionBrowserTest::LoadExtension(const FilePath& path) {
    102   return LoadExtensionImpl(path, false, true);
    103 }
    104 
    105 const Extension* ExtensionBrowserTest::LoadExtensionIncognito(
    106     const FilePath& path) {
    107   return LoadExtensionImpl(path, true, true);
    108 }
    109 
    110 const Extension* ExtensionBrowserTest::LoadExtensionNoFileAccess(
    111     const FilePath& path) {
    112   return LoadExtensionImpl(path, false, false);
    113 }
    114 
    115 const Extension* ExtensionBrowserTest::LoadExtensionIncognitoNoFileAccess(
    116     const FilePath& path) {
    117   return LoadExtensionImpl(path, true, false);
    118 }
    119 
    120 bool ExtensionBrowserTest::LoadExtensionAsComponent(const FilePath& path) {
    121   ExtensionService* service = browser()->profile()->GetExtensionService();
    122 
    123   std::string manifest;
    124   if (!file_util::ReadFileToString(path.Append(Extension::kManifestFilename),
    125                                    &manifest))
    126     return false;
    127 
    128   service->LoadComponentExtension(
    129       ExtensionService::ComponentExtensionInfo(manifest, path));
    130 
    131   return true;
    132 }
    133 
    134 FilePath ExtensionBrowserTest::PackExtension(const FilePath& dir_path) {
    135   FilePath crx_path = temp_dir_.path().AppendASCII("temp.crx");
    136   if (!file_util::Delete(crx_path, false)) {
    137     ADD_FAILURE() << "Failed to delete crx: " << crx_path.value();
    138     return FilePath();
    139   }
    140 
    141   FilePath pem_path = crx_path.DirName().AppendASCII("temp.pem");
    142   if (!file_util::Delete(pem_path, false)) {
    143     ADD_FAILURE() << "Failed to delete pem: " << pem_path.value();
    144     return FilePath();
    145   }
    146 
    147   if (!file_util::PathExists(dir_path)) {
    148     ADD_FAILURE() << "Extension dir not found: " << dir_path.value();
    149     return FilePath();
    150   }
    151 
    152   scoped_ptr<ExtensionCreator> creator(new ExtensionCreator());
    153   if (!creator->Run(dir_path,
    154                     crx_path,
    155                     FilePath(),  // no existing pem, use empty path
    156                     pem_path)) {
    157     ADD_FAILURE() << "ExtensionCreator::Run() failed.";
    158     return FilePath();
    159   }
    160 
    161   if (!file_util::PathExists(crx_path)) {
    162     ADD_FAILURE() << crx_path.value() << " was not created.";
    163     return FilePath();
    164   }
    165   return crx_path;
    166 }
    167 
    168 // This class is used to simulate an installation abort by the user.
    169 class MockAbortExtensionInstallUI : public ExtensionInstallUI {
    170  public:
    171   MockAbortExtensionInstallUI() : ExtensionInstallUI(NULL) {}
    172 
    173   // Simulate a user abort on an extension installation.
    174   virtual void ConfirmInstall(Delegate* delegate, const Extension* extension) {
    175     delegate->InstallUIAbort();
    176     MessageLoopForUI::current()->Quit();
    177   }
    178 
    179   virtual void OnInstallSuccess(const Extension* extension, SkBitmap* icon) {}
    180 
    181   virtual void OnInstallFailure(const std::string& error) {}
    182 };
    183 
    184 class MockAutoConfirmExtensionInstallUI : public ExtensionInstallUI {
    185  public:
    186   explicit MockAutoConfirmExtensionInstallUI(Profile* profile) :
    187       ExtensionInstallUI(profile) {}
    188 
    189   // Proceed without confirmation prompt.
    190   virtual void ConfirmInstall(Delegate* delegate, const Extension* extension) {
    191     delegate->InstallUIProceed();
    192   }
    193 };
    194 
    195 bool ExtensionBrowserTest::InstallOrUpdateExtension(const std::string& id,
    196                                                     const FilePath& path,
    197                                                     InstallUIType ui_type,
    198                                                     int expected_change) {
    199   return InstallOrUpdateExtension(id, path, ui_type, expected_change,
    200                                   browser()->profile());
    201 }
    202 
    203 bool ExtensionBrowserTest::InstallOrUpdateExtension(const std::string& id,
    204                                                     const FilePath& path,
    205                                                     InstallUIType ui_type,
    206                                                     int expected_change,
    207                                                     Profile* profile) {
    208   ExtensionService* service = profile->GetExtensionService();
    209   service->set_show_extensions_prompts(false);
    210   size_t num_before = service->extensions()->size();
    211 
    212   {
    213     NotificationRegistrar registrar;
    214     registrar.Add(this, NotificationType::EXTENSION_LOADED,
    215                   NotificationService::AllSources());
    216     registrar.Add(this, NotificationType::EXTENSION_UPDATE_DISABLED,
    217                   NotificationService::AllSources());
    218     registrar.Add(this, NotificationType::EXTENSION_INSTALL_ERROR,
    219                   NotificationService::AllSources());
    220 
    221     ExtensionInstallUI* install_ui = NULL;
    222     if (ui_type == INSTALL_UI_TYPE_CANCEL)
    223       install_ui = new MockAbortExtensionInstallUI();
    224     else if (ui_type == INSTALL_UI_TYPE_NORMAL)
    225       install_ui = new ExtensionInstallUI(profile);
    226     else if (ui_type == INSTALL_UI_TYPE_AUTO_CONFIRM)
    227       install_ui = new MockAutoConfirmExtensionInstallUI(profile);
    228 
    229     // TODO(tessamac): Update callers to always pass an unpacked extension
    230     //                 and then always pack the extension here.
    231     FilePath crx_path = path;
    232     if (crx_path.Extension() != FILE_PATH_LITERAL(".crx")) {
    233       crx_path = PackExtension(path);
    234     }
    235     if (crx_path.empty())
    236       return false;
    237 
    238     scoped_refptr<CrxInstaller> installer(
    239         new CrxInstaller(service, install_ui));
    240     installer->set_expected_id(id);
    241     installer->InstallCrx(crx_path);
    242 
    243     ui_test_utils::RunMessageLoop();
    244   }
    245 
    246   size_t num_after = service->extensions()->size();
    247   if (num_after != (num_before + expected_change)) {
    248     VLOG(1) << "Num extensions before: " << base::IntToString(num_before)
    249             << " num after: " << base::IntToString(num_after)
    250             << " Installed extensions follow:";
    251 
    252     for (size_t i = 0; i < service->extensions()->size(); ++i)
    253       VLOG(1) << "  " << (*service->extensions())[i]->id();
    254 
    255     VLOG(1) << "Errors follow:";
    256     const std::vector<std::string>* errors =
    257         ExtensionErrorReporter::GetInstance()->GetErrors();
    258     for (std::vector<std::string>::const_iterator iter = errors->begin();
    259          iter != errors->end(); ++iter)
    260       VLOG(1) << *iter;
    261 
    262     return false;
    263   }
    264 
    265   return WaitForExtensionHostsToLoad();
    266 }
    267 
    268 void ExtensionBrowserTest::ReloadExtension(const std::string& extension_id) {
    269   ExtensionService* service = browser()->profile()->GetExtensionService();
    270   service->ReloadExtension(extension_id);
    271   ui_test_utils::RegisterAndWait(this,
    272                                  NotificationType::EXTENSION_LOADED,
    273                                  NotificationService::AllSources());
    274 }
    275 
    276 void ExtensionBrowserTest::UnloadExtension(const std::string& extension_id) {
    277   ExtensionService* service = browser()->profile()->GetExtensionService();
    278   service->UnloadExtension(extension_id, UnloadedExtensionInfo::DISABLE);
    279 }
    280 
    281 void ExtensionBrowserTest::UninstallExtension(const std::string& extension_id) {
    282   ExtensionService* service = browser()->profile()->GetExtensionService();
    283   service->UninstallExtension(extension_id, false, NULL);
    284 }
    285 
    286 void ExtensionBrowserTest::DisableExtension(const std::string& extension_id) {
    287   ExtensionService* service = browser()->profile()->GetExtensionService();
    288   service->DisableExtension(extension_id);
    289 }
    290 
    291 void ExtensionBrowserTest::EnableExtension(const std::string& extension_id) {
    292   ExtensionService* service = browser()->profile()->GetExtensionService();
    293   service->EnableExtension(extension_id);
    294 }
    295 
    296 bool ExtensionBrowserTest::WaitForPageActionCountChangeTo(int count) {
    297   LocationBarTesting* location_bar =
    298       browser()->window()->GetLocationBar()->GetLocationBarForTesting();
    299   if (location_bar->PageActionCount() != count) {
    300     target_page_action_count_ = count;
    301     ui_test_utils::RegisterAndWait(this,
    302         NotificationType::EXTENSION_PAGE_ACTION_COUNT_CHANGED,
    303         NotificationService::AllSources());
    304   }
    305   return location_bar->PageActionCount() == count;
    306 }
    307 
    308 bool ExtensionBrowserTest::WaitForPageActionVisibilityChangeTo(int count) {
    309   LocationBarTesting* location_bar =
    310       browser()->window()->GetLocationBar()->GetLocationBarForTesting();
    311   if (location_bar->PageActionVisibleCount() != count) {
    312     target_visible_page_action_count_ = count;
    313     ui_test_utils::RegisterAndWait(this,
    314         NotificationType::EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED,
    315         NotificationService::AllSources());
    316   }
    317   return location_bar->PageActionVisibleCount() == count;
    318 }
    319 
    320 bool ExtensionBrowserTest::WaitForExtensionHostsToLoad() {
    321   // Wait for all the extension hosts that exist to finish loading.
    322   NotificationRegistrar registrar;
    323   registrar.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
    324                 NotificationService::AllSources());
    325 
    326   ExtensionProcessManager* manager =
    327         browser()->profile()->GetExtensionProcessManager();
    328   for (ExtensionProcessManager::const_iterator iter = manager->begin();
    329        iter != manager->end();) {
    330     if ((*iter)->did_stop_loading()) {
    331       ++iter;
    332     } else {
    333       ui_test_utils::RunMessageLoop();
    334 
    335       // Test activity may have modified the set of extension processes during
    336       // message processing, so re-start the iteration to catch added/removed
    337       // processes.
    338       iter = manager->begin();
    339     }
    340   }
    341   return true;
    342 }
    343 
    344 bool ExtensionBrowserTest::WaitForExtensionInstall() {
    345   int before = extension_installs_observed_;
    346   ui_test_utils::RegisterAndWait(this, NotificationType::EXTENSION_INSTALLED,
    347                                  NotificationService::AllSources());
    348   return extension_installs_observed_ == (before + 1);
    349 }
    350 
    351 bool ExtensionBrowserTest::WaitForExtensionInstallError() {
    352   int before = extension_installs_observed_;
    353   ui_test_utils::RegisterAndWait(this,
    354                                  NotificationType::EXTENSION_INSTALL_ERROR,
    355                                  NotificationService::AllSources());
    356   return extension_installs_observed_ == before;
    357 }
    358 
    359 void ExtensionBrowserTest::WaitForExtensionLoad() {
    360   ui_test_utils::RegisterAndWait(this, NotificationType::EXTENSION_LOADED,
    361                                  NotificationService::AllSources());
    362   WaitForExtensionHostsToLoad();
    363 }
    364 
    365 bool ExtensionBrowserTest::WaitForExtensionCrash(
    366     const std::string& extension_id) {
    367   ExtensionService* service = browser()->profile()->GetExtensionService();
    368 
    369   if (!service->GetExtensionById(extension_id, true)) {
    370     // The extension is already unloaded, presumably due to a crash.
    371     return true;
    372   }
    373   ui_test_utils::RegisterAndWait(this,
    374                                  NotificationType::EXTENSION_PROCESS_TERMINATED,
    375                                  NotificationService::AllSources());
    376   return (service->GetExtensionById(extension_id, true) == NULL);
    377 }
    378 
    379 void ExtensionBrowserTest::Observe(NotificationType type,
    380                                    const NotificationSource& source,
    381                                    const NotificationDetails& details) {
    382   switch (type.value) {
    383     case NotificationType::EXTENSION_LOADED:
    384       last_loaded_extension_id_ = Details<const Extension>(details).ptr()->id();
    385       VLOG(1) << "Got EXTENSION_LOADED notification.";
    386       MessageLoopForUI::current()->Quit();
    387       break;
    388 
    389     case NotificationType::EXTENSION_UPDATE_DISABLED:
    390       VLOG(1) << "Got EXTENSION_UPDATE_DISABLED notification.";
    391       MessageLoopForUI::current()->Quit();
    392       break;
    393 
    394     case NotificationType::EXTENSION_HOST_DID_STOP_LOADING:
    395       VLOG(1) << "Got EXTENSION_HOST_DID_STOP_LOADING notification.";
    396       MessageLoopForUI::current()->Quit();
    397       break;
    398 
    399     case NotificationType::EXTENSION_INSTALLED:
    400       VLOG(1) << "Got EXTENSION_INSTALLED notification.";
    401       ++extension_installs_observed_;
    402       MessageLoopForUI::current()->Quit();
    403       break;
    404 
    405     case NotificationType::EXTENSION_INSTALL_ERROR:
    406       VLOG(1) << "Got EXTENSION_INSTALL_ERROR notification.";
    407       MessageLoopForUI::current()->Quit();
    408       break;
    409 
    410     case NotificationType::EXTENSION_PROCESS_CREATED:
    411       VLOG(1) << "Got EXTENSION_PROCESS_CREATED notification.";
    412       MessageLoopForUI::current()->Quit();
    413       break;
    414 
    415     case NotificationType::EXTENSION_PROCESS_TERMINATED:
    416       VLOG(1) << "Got EXTENSION_PROCESS_TERMINATED notification.";
    417       MessageLoopForUI::current()->Quit();
    418       break;
    419 
    420     case NotificationType::EXTENSION_PAGE_ACTION_COUNT_CHANGED: {
    421       LocationBarTesting* location_bar =
    422           browser()->window()->GetLocationBar()->GetLocationBarForTesting();
    423       VLOG(1) << "Got EXTENSION_PAGE_ACTION_COUNT_CHANGED notification. Number "
    424                  "of page actions: " << location_bar->PageActionCount();
    425       if (location_bar->PageActionCount() ==
    426           target_page_action_count_) {
    427         target_page_action_count_ = -1;
    428         MessageLoopForUI::current()->Quit();
    429       }
    430       break;
    431     }
    432 
    433     case NotificationType::EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED: {
    434       LocationBarTesting* location_bar =
    435           browser()->window()->GetLocationBar()->GetLocationBarForTesting();
    436       VLOG(1) << "Got EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED notification. "
    437                  "Number of visible page actions: "
    438               << location_bar->PageActionVisibleCount();
    439       if (location_bar->PageActionVisibleCount() ==
    440           target_visible_page_action_count_) {
    441         target_visible_page_action_count_ = -1;
    442         MessageLoopForUI::current()->Quit();
    443       }
    444       break;
    445     }
    446 
    447     default:
    448       NOTREACHED();
    449       break;
    450   }
    451 }
    452