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 "chrome/browser/extensions/extension_browsertest.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/command_line.h"
     10 #include "base/file_util.h"
     11 #include "base/files/file_path.h"
     12 #include "base/files/scoped_temp_dir.h"
     13 #include "base/path_service.h"
     14 #include "base/strings/string_number_conversions.h"
     15 #include "base/strings/stringprintf.h"
     16 #include "base/strings/utf_string_conversions.h"
     17 #include "chrome/browser/chrome_notification_types.h"
     18 #include "chrome/browser/extensions/component_loader.h"
     19 #include "chrome/browser/extensions/crx_installer.h"
     20 #include "chrome/browser/extensions/extension_creator.h"
     21 #include "chrome/browser/extensions/extension_error_reporter.h"
     22 #include "chrome/browser/extensions/extension_host.h"
     23 #include "chrome/browser/extensions/extension_install_prompt.h"
     24 #include "chrome/browser/extensions/extension_service.h"
     25 #include "chrome/browser/extensions/extension_system.h"
     26 #include "chrome/browser/extensions/unpacked_installer.h"
     27 #include "chrome/browser/profiles/profile.h"
     28 #include "chrome/browser/profiles/profile_manager.h"
     29 #include "chrome/browser/ui/browser.h"
     30 #include "chrome/browser/ui/browser_window.h"
     31 #include "chrome/browser/ui/omnibox/location_bar.h"
     32 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     33 #include "chrome/common/chrome_paths.h"
     34 #include "chrome/common/chrome_switches.h"
     35 #include "chrome/common/chrome_version_info.h"
     36 #include "chrome/common/extensions/extension_manifest_constants.h"
     37 #include "chrome/common/extensions/extension_set.h"
     38 #include "chrome/test/base/ui_test_utils.h"
     39 #include "content/public/browser/navigation_controller.h"
     40 #include "content/public/browser/navigation_entry.h"
     41 #include "content/public/browser/notification_registrar.h"
     42 #include "content/public/browser/notification_service.h"
     43 #include "content/public/browser/render_view_host.h"
     44 #include "content/public/test/browser_test_utils.h"
     45 #include "extensions/common/constants.h"
     46 #include "sync/api/string_ordinal.h"
     47 
     48 #if defined(OS_CHROMEOS)
     49 #include "chromeos/chromeos_switches.h"
     50 #endif
     51 
     52 using extensions::Extension;
     53 using extensions::ExtensionCreator;
     54 using extensions::FeatureSwitch;
     55 using extensions::Manifest;
     56 
     57 ExtensionBrowserTest::ExtensionBrowserTest()
     58     : loaded_(false),
     59       installed_(false),
     60       extension_installs_observed_(0),
     61       extension_load_errors_observed_(0),
     62       target_page_action_count_(-1),
     63       target_visible_page_action_count_(-1),
     64       current_channel_(chrome::VersionInfo::CHANNEL_DEV),
     65       override_prompt_for_external_extensions_(
     66           FeatureSwitch::prompt_for_external_extensions(), false),
     67       profile_(NULL) {
     68   EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
     69 }
     70 
     71 ExtensionBrowserTest::~ExtensionBrowserTest() {}
     72 
     73 Profile* ExtensionBrowserTest::profile() {
     74   if (!profile_) {
     75     if (browser())
     76       profile_ = browser()->profile();
     77     else
     78       profile_ = ProfileManager::GetDefaultProfile();
     79   }
     80   return profile_;
     81 }
     82 
     83 // static
     84 const Extension* ExtensionBrowserTest::GetExtensionByPath(
     85     const ExtensionSet* extensions, const base::FilePath& path) {
     86   base::FilePath extension_path = base::MakeAbsoluteFilePath(path);
     87   EXPECT_TRUE(!extension_path.empty());
     88   for (ExtensionSet::const_iterator iter = extensions->begin();
     89        iter != extensions->end(); ++iter) {
     90     if ((*iter)->path() == extension_path) {
     91       return iter->get();
     92     }
     93   }
     94   return NULL;
     95 }
     96 
     97 void ExtensionBrowserTest::SetUpCommandLine(CommandLine* command_line) {
     98   PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_);
     99   test_data_dir_ = test_data_dir_.AppendASCII("extensions");
    100 
    101 #if defined(OS_CHROMEOS)
    102   // This makes sure that we create the Default profile first, with no
    103   // ExtensionService and then the real profile with one, as we do when
    104   // running on chromeos.
    105   command_line->AppendSwitchASCII(chromeos::switches::kLoginUser,
    106                                   "TestUser (at) gmail.com");
    107   command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
    108 #endif
    109 }
    110 
    111 void ExtensionBrowserTest::SetUpOnMainThread() {
    112   InProcessBrowserTest::SetUpOnMainThread();
    113 }
    114 
    115 const Extension* ExtensionBrowserTest::LoadExtensionWithFlags(
    116     const base::FilePath& path, int flags) {
    117   ExtensionService* service = extensions::ExtensionSystem::Get(
    118       profile())->extension_service();
    119   {
    120     content::NotificationRegistrar registrar;
    121     registrar.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
    122                   content::NotificationService::AllSources());
    123     scoped_refptr<extensions::UnpackedInstaller> installer(
    124         extensions::UnpackedInstaller::Create(service));
    125     installer->set_prompt_for_plugins(false);
    126     installer->set_require_modern_manifest_version(
    127         (flags & kFlagAllowOldManifestVersions) == 0);
    128     installer->Load(path);
    129     content::RunMessageLoop();
    130   }
    131 
    132   // Find the loaded extension by its path. See crbug.com/59531 for why
    133   // we cannot just use last_loaded_extension_id_.
    134   const Extension* extension = GetExtensionByPath(service->extensions(), path);
    135   if (!extension)
    136     return NULL;
    137 
    138   if (!(flags & kFlagIgnoreManifestWarnings)) {
    139     const std::vector<extensions::InstallWarning>& install_warnings =
    140         extension->install_warnings();
    141     if (!install_warnings.empty()) {
    142       std::string install_warnings_message = base::StringPrintf(
    143           "Unexpected warnings when loading test extension %s:\n",
    144           path.AsUTF8Unsafe().c_str());
    145 
    146       for (std::vector<extensions::InstallWarning>::const_iterator it =
    147           install_warnings.begin(); it != install_warnings.end(); ++it) {
    148         install_warnings_message += "  " + it->message + "\n";
    149       }
    150 
    151       EXPECT_TRUE(extension->install_warnings().empty()) <<
    152           install_warnings_message;
    153       return NULL;
    154     }
    155   }
    156 
    157   const std::string extension_id = extension->id();
    158 
    159   // The call to OnExtensionInstalled ensures the other extension prefs
    160   // are set up with the defaults.
    161   service->extension_prefs()->OnExtensionInstalled(
    162       extension,
    163       Extension::ENABLED,
    164       extensions::Blacklist::NOT_BLACKLISTED,
    165       syncer::StringOrdinal::CreateInitialOrdinal());
    166 
    167   // Toggling incognito or file access will reload the extension, so wait for
    168   // the reload and grab the new extension instance. The default state is
    169   // incognito disabled and file access enabled, so we don't wait in those
    170   // cases.
    171   {
    172     content::WindowedNotificationObserver load_signal(
    173         chrome::NOTIFICATION_EXTENSION_LOADED,
    174         content::Source<Profile>(profile()));
    175     CHECK(!service->IsIncognitoEnabled(extension_id) ||
    176           extension->force_incognito_enabled());
    177 
    178     if (flags & kFlagEnableIncognito) {
    179       service->SetIsIncognitoEnabled(extension_id, true);
    180       load_signal.Wait();
    181       extension = service->GetExtensionById(extension_id, false);
    182       CHECK(extension) << extension_id << " not found after reloading.";
    183     }
    184   }
    185 
    186   {
    187     content::WindowedNotificationObserver load_signal(
    188         chrome::NOTIFICATION_EXTENSION_LOADED,
    189         content::Source<Profile>(profile()));
    190     CHECK(service->AllowFileAccess(extension));
    191     if (!(flags & kFlagEnableFileAccess)) {
    192       service->SetAllowFileAccess(extension, false);
    193       load_signal.Wait();
    194       extension = service->GetExtensionById(extension_id, false);
    195       CHECK(extension) << extension_id << " not found after reloading.";
    196     }
    197   }
    198 
    199   if (!WaitForExtensionViewsToLoad())
    200     return NULL;
    201 
    202   return extension;
    203 }
    204 
    205 const Extension* ExtensionBrowserTest::LoadExtension(
    206     const base::FilePath& path) {
    207   return LoadExtensionWithFlags(path, kFlagEnableFileAccess);
    208 }
    209 
    210 const Extension* ExtensionBrowserTest::LoadExtensionIncognito(
    211     const base::FilePath& path) {
    212   return LoadExtensionWithFlags(path,
    213                                 kFlagEnableFileAccess | kFlagEnableIncognito);
    214 }
    215 
    216 const Extension* ExtensionBrowserTest::LoadExtensionAsComponentWithManifest(
    217     const base::FilePath& path,
    218     const base::FilePath::CharType* manifest_relative_path) {
    219   ExtensionService* service = extensions::ExtensionSystem::Get(
    220       profile())->extension_service();
    221 
    222   std::string manifest;
    223   if (!file_util::ReadFileToString(path.Append(manifest_relative_path),
    224                                    &manifest)) {
    225     return NULL;
    226   }
    227 
    228   std::string extension_id = service->component_loader()->Add(manifest, path);
    229   const Extension* extension = service->extensions()->GetByID(extension_id);
    230   if (!extension)
    231     return NULL;
    232   last_loaded_extension_id_ = extension->id();
    233   return extension;
    234 }
    235 
    236 const Extension* ExtensionBrowserTest::LoadExtensionAsComponent(
    237     const base::FilePath& path) {
    238   return LoadExtensionAsComponentWithManifest(path,
    239                                               extensions::kManifestFilename);
    240 }
    241 
    242 base::FilePath ExtensionBrowserTest::PackExtension(
    243     const base::FilePath& dir_path) {
    244   base::FilePath crx_path = temp_dir_.path().AppendASCII("temp.crx");
    245   if (!base::DeleteFile(crx_path, false)) {
    246     ADD_FAILURE() << "Failed to delete crx: " << crx_path.value();
    247     return base::FilePath();
    248   }
    249 
    250   // Look for PEM files with the same name as the directory.
    251   base::FilePath pem_path =
    252       dir_path.ReplaceExtension(FILE_PATH_LITERAL(".pem"));
    253   base::FilePath pem_path_out;
    254 
    255   if (!base::PathExists(pem_path)) {
    256     pem_path = base::FilePath();
    257     pem_path_out = crx_path.DirName().AppendASCII("temp.pem");
    258     if (!base::DeleteFile(pem_path_out, false)) {
    259       ADD_FAILURE() << "Failed to delete pem: " << pem_path_out.value();
    260       return base::FilePath();
    261     }
    262   }
    263 
    264   return PackExtensionWithOptions(dir_path, crx_path, pem_path, pem_path_out);
    265 }
    266 
    267 base::FilePath ExtensionBrowserTest::PackExtensionWithOptions(
    268     const base::FilePath& dir_path,
    269     const base::FilePath& crx_path,
    270     const base::FilePath& pem_path,
    271     const base::FilePath& pem_out_path) {
    272   if (!base::PathExists(dir_path)) {
    273     ADD_FAILURE() << "Extension dir not found: " << dir_path.value();
    274     return base::FilePath();
    275   }
    276 
    277   if (!base::PathExists(pem_path) && pem_out_path.empty()) {
    278     ADD_FAILURE() << "Must specify a PEM file or PEM output path";
    279     return base::FilePath();
    280   }
    281 
    282   scoped_ptr<ExtensionCreator> creator(new ExtensionCreator());
    283   if (!creator->Run(dir_path,
    284                     crx_path,
    285                     pem_path,
    286                     pem_out_path,
    287                     ExtensionCreator::kOverwriteCRX)) {
    288     ADD_FAILURE() << "ExtensionCreator::Run() failed: "
    289                   << creator->error_message();
    290     return base::FilePath();
    291   }
    292 
    293   if (!base::PathExists(crx_path)) {
    294     ADD_FAILURE() << crx_path.value() << " was not created.";
    295     return base::FilePath();
    296   }
    297   return crx_path;
    298 }
    299 
    300 // This class is used to simulate an installation abort by the user.
    301 class MockAbortExtensionInstallPrompt : public ExtensionInstallPrompt {
    302  public:
    303   MockAbortExtensionInstallPrompt() : ExtensionInstallPrompt(NULL) {
    304   }
    305 
    306   // Simulate a user abort on an extension installation.
    307   virtual void ConfirmInstall(
    308       Delegate* delegate,
    309       const Extension* extension,
    310       const ShowDialogCallback& show_dialog_callback) OVERRIDE {
    311     delegate->InstallUIAbort(true);
    312     base::MessageLoopForUI::current()->Quit();
    313   }
    314 
    315   virtual void OnInstallSuccess(const Extension* extension,
    316                                 SkBitmap* icon) OVERRIDE {}
    317 
    318   virtual void OnInstallFailure(
    319       const extensions::CrxInstallerError& error) OVERRIDE {}
    320 };
    321 
    322 class MockAutoConfirmExtensionInstallPrompt : public ExtensionInstallPrompt {
    323  public:
    324   explicit MockAutoConfirmExtensionInstallPrompt(
    325       content::WebContents* web_contents)
    326     : ExtensionInstallPrompt(web_contents) {}
    327 
    328   // Proceed without confirmation prompt.
    329   virtual void ConfirmInstall(
    330       Delegate* delegate,
    331       const Extension* extension,
    332       const ShowDialogCallback& show_dialog_callback) OVERRIDE {
    333     delegate->InstallUIProceed();
    334   }
    335 };
    336 
    337 const Extension* ExtensionBrowserTest::UpdateExtensionWaitForIdle(
    338     const std::string& id,
    339     const base::FilePath& path,
    340     int expected_change) {
    341   return InstallOrUpdateExtension(id,
    342                                   path,
    343                                   INSTALL_UI_TYPE_NONE,
    344                                   expected_change,
    345                                   Manifest::INTERNAL,
    346                                   browser(),
    347                                   false,
    348                                   true);
    349 }
    350 
    351 const Extension* ExtensionBrowserTest::InstallExtensionFromWebstore(
    352     const base::FilePath& path,
    353     int expected_change) {
    354   return InstallOrUpdateExtension(std::string(),
    355                                   path,
    356                                   INSTALL_UI_TYPE_NONE,
    357                                   expected_change,
    358                                   Manifest::INTERNAL,
    359                                   browser(),
    360                                   true,
    361                                   false);
    362 }
    363 
    364 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
    365     const std::string& id,
    366     const base::FilePath& path,
    367     InstallUIType ui_type,
    368     int expected_change) {
    369   return InstallOrUpdateExtension(id, path, ui_type, expected_change,
    370                                   Manifest::INTERNAL, browser(), false, false);
    371 }
    372 
    373 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
    374     const std::string& id,
    375     const base::FilePath& path,
    376     InstallUIType ui_type,
    377     int expected_change,
    378     Browser* browser,
    379     bool from_webstore) {
    380   return InstallOrUpdateExtension(id, path, ui_type, expected_change,
    381                                   Manifest::INTERNAL, browser, from_webstore,
    382                                   false);
    383 }
    384 
    385 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
    386     const std::string& id,
    387     const base::FilePath& path,
    388     InstallUIType ui_type,
    389     int expected_change,
    390     Manifest::Location install_source) {
    391   return InstallOrUpdateExtension(id, path, ui_type, expected_change,
    392                                   install_source, browser(), false, false);
    393 }
    394 
    395 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
    396     const std::string& id,
    397     const base::FilePath& path,
    398     InstallUIType ui_type,
    399     int expected_change,
    400     Manifest::Location install_source,
    401     Browser* browser,
    402     bool from_webstore,
    403     bool wait_for_idle) {
    404   ExtensionService* service = profile()->GetExtensionService();
    405   service->set_show_extensions_prompts(false);
    406   size_t num_before = service->extensions()->size();
    407 
    408   {
    409     scoped_ptr<ExtensionInstallPrompt> install_ui;
    410     if (ui_type == INSTALL_UI_TYPE_CANCEL) {
    411       install_ui.reset(new MockAbortExtensionInstallPrompt());
    412     } else if (ui_type == INSTALL_UI_TYPE_NORMAL) {
    413       install_ui.reset(new ExtensionInstallPrompt(
    414           browser->tab_strip_model()->GetActiveWebContents()));
    415     } else if (ui_type == INSTALL_UI_TYPE_AUTO_CONFIRM) {
    416       install_ui.reset(new MockAutoConfirmExtensionInstallPrompt(
    417           browser->tab_strip_model()->GetActiveWebContents()));
    418     }
    419 
    420     // TODO(tessamac): Update callers to always pass an unpacked extension
    421     //                 and then always pack the extension here.
    422     base::FilePath crx_path = path;
    423     if (crx_path.Extension() != FILE_PATH_LITERAL(".crx")) {
    424       crx_path = PackExtension(path);
    425     }
    426     if (crx_path.empty())
    427       return NULL;
    428 
    429     scoped_refptr<extensions::CrxInstaller> installer(
    430         extensions::CrxInstaller::Create(service, install_ui.Pass()));
    431     installer->set_expected_id(id);
    432     installer->set_is_gallery_install(from_webstore);
    433     installer->set_install_source(install_source);
    434     installer->set_install_wait_for_idle(wait_for_idle);
    435     if (!from_webstore) {
    436       installer->set_off_store_install_allow_reason(
    437           extensions::CrxInstaller::OffStoreInstallAllowedInTest);
    438     }
    439 
    440     content::NotificationRegistrar registrar;
    441     registrar.Add(this, chrome::NOTIFICATION_CRX_INSTALLER_DONE,
    442                   content::Source<extensions::CrxInstaller>(installer.get()));
    443 
    444     installer->InstallCrx(crx_path);
    445 
    446     content::RunMessageLoop();
    447   }
    448 
    449   size_t num_after = service->extensions()->size();
    450   EXPECT_EQ(num_before + expected_change, num_after);
    451   if (num_before + expected_change != num_after) {
    452     VLOG(1) << "Num extensions before: " << base::IntToString(num_before)
    453             << " num after: " << base::IntToString(num_after)
    454             << " Installed extensions follow:";
    455 
    456     for (ExtensionSet::const_iterator it = service->extensions()->begin();
    457          it != service->extensions()->end(); ++it)
    458       VLOG(1) << "  " << (*it)->id();
    459 
    460     VLOG(1) << "Errors follow:";
    461     const std::vector<string16>* errors =
    462         ExtensionErrorReporter::GetInstance()->GetErrors();
    463     for (std::vector<string16>::const_iterator iter = errors->begin();
    464          iter != errors->end(); ++iter)
    465       VLOG(1) << *iter;
    466 
    467     return NULL;
    468   }
    469 
    470   if (!WaitForExtensionViewsToLoad())
    471     return NULL;
    472   return service->GetExtensionById(last_loaded_extension_id_, false);
    473 }
    474 
    475 void ExtensionBrowserTest::ReloadExtension(const std::string extension_id) {
    476   ExtensionService* service = extensions::ExtensionSystem::Get(
    477       profile())->extension_service();
    478   service->ReloadExtension(extension_id);
    479   ui_test_utils::RegisterAndWait(this,
    480                                  chrome::NOTIFICATION_EXTENSION_LOADED,
    481                                  content::NotificationService::AllSources());
    482 }
    483 
    484 void ExtensionBrowserTest::UnloadExtension(const std::string& extension_id) {
    485   ExtensionService* service = extensions::ExtensionSystem::Get(
    486       profile())->extension_service();
    487   service->UnloadExtension(extension_id, extension_misc::UNLOAD_REASON_DISABLE);
    488 }
    489 
    490 void ExtensionBrowserTest::UninstallExtension(const std::string& extension_id) {
    491   ExtensionService* service = extensions::ExtensionSystem::Get(
    492       profile())->extension_service();
    493   service->UninstallExtension(extension_id, false, NULL);
    494 }
    495 
    496 void ExtensionBrowserTest::DisableExtension(const std::string& extension_id) {
    497   ExtensionService* service = extensions::ExtensionSystem::Get(
    498       profile())->extension_service();
    499   service->DisableExtension(extension_id, Extension::DISABLE_USER_ACTION);
    500 }
    501 
    502 void ExtensionBrowserTest::EnableExtension(const std::string& extension_id) {
    503   ExtensionService* service = extensions::ExtensionSystem::Get(
    504       profile())->extension_service();
    505   service->EnableExtension(extension_id);
    506 }
    507 
    508 bool ExtensionBrowserTest::WaitForPageActionCountChangeTo(int count) {
    509   LocationBarTesting* location_bar =
    510       browser()->window()->GetLocationBar()->GetLocationBarForTesting();
    511   if (location_bar->PageActionCount() != count) {
    512     target_page_action_count_ = count;
    513     ui_test_utils::RegisterAndWait(this,
    514         chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED,
    515         content::NotificationService::AllSources());
    516   }
    517   return location_bar->PageActionCount() == count;
    518 }
    519 
    520 bool ExtensionBrowserTest::WaitForPageActionVisibilityChangeTo(int count) {
    521   LocationBarTesting* location_bar =
    522       browser()->window()->GetLocationBar()->GetLocationBarForTesting();
    523   if (location_bar->PageActionVisibleCount() != count) {
    524     target_visible_page_action_count_ = count;
    525     ui_test_utils::RegisterAndWait(this,
    526         chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED,
    527         content::NotificationService::AllSources());
    528   }
    529   return location_bar->PageActionVisibleCount() == count;
    530 }
    531 
    532 bool ExtensionBrowserTest::WaitForExtensionViewsToLoad() {
    533   // Wait for all the extension render view hosts that exist to finish loading.
    534   content::NotificationRegistrar registrar;
    535   registrar.Add(this, content::NOTIFICATION_LOAD_STOP,
    536                 content::NotificationService::AllSources());
    537   registrar.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
    538                 content::NotificationService::AllSources());
    539 
    540   ExtensionProcessManager* manager =
    541       extensions::ExtensionSystem::Get(profile())->process_manager();
    542   ExtensionProcessManager::ViewSet all_views = manager->GetAllViews();
    543   for (ExtensionProcessManager::ViewSet::const_iterator iter =
    544            all_views.begin();
    545        iter != all_views.end();) {
    546     if (!(*iter)->IsLoading()) {
    547       ++iter;
    548     } else {
    549       content::RunMessageLoop();
    550 
    551       // Test activity may have modified the set of extension processes during
    552       // message processing, so re-start the iteration to catch added/removed
    553       // processes.
    554       all_views = manager->GetAllViews();
    555       iter = all_views.begin();
    556     }
    557   }
    558   return true;
    559 }
    560 
    561 bool ExtensionBrowserTest::WaitForExtensionInstall() {
    562   int before = extension_installs_observed_;
    563   ui_test_utils::RegisterAndWait(this,
    564                                  chrome::NOTIFICATION_EXTENSION_INSTALLED,
    565                                  content::NotificationService::AllSources());
    566   return extension_installs_observed_ == (before + 1);
    567 }
    568 
    569 bool ExtensionBrowserTest::WaitForExtensionInstallError() {
    570   int before = extension_installs_observed_;
    571   ui_test_utils::RegisterAndWait(this,
    572                                  chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
    573                                  content::NotificationService::AllSources());
    574   return extension_installs_observed_ == before;
    575 }
    576 
    577 void ExtensionBrowserTest::WaitForExtensionLoad() {
    578   ui_test_utils::RegisterAndWait(this, chrome::NOTIFICATION_EXTENSION_LOADED,
    579                                  content::NotificationService::AllSources());
    580   WaitForExtensionViewsToLoad();
    581 }
    582 
    583 bool ExtensionBrowserTest::WaitForExtensionLoadError() {
    584   int before = extension_load_errors_observed_;
    585   ui_test_utils::RegisterAndWait(this,
    586                                  chrome::NOTIFICATION_EXTENSION_LOAD_ERROR,
    587                                  content::NotificationService::AllSources());
    588   return extension_load_errors_observed_ != before;
    589 }
    590 
    591 bool ExtensionBrowserTest::WaitForExtensionCrash(
    592     const std::string& extension_id) {
    593   ExtensionService* service = extensions::ExtensionSystem::Get(
    594       profile())->extension_service();
    595 
    596   if (!service->GetExtensionById(extension_id, true)) {
    597     // The extension is already unloaded, presumably due to a crash.
    598     return true;
    599   }
    600   ui_test_utils::RegisterAndWait(
    601       this, chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED,
    602       content::NotificationService::AllSources());
    603   return (service->GetExtensionById(extension_id, true) == NULL);
    604 }
    605 
    606 bool ExtensionBrowserTest::WaitForCrxInstallerDone() {
    607   int before = crx_installers_done_observed_;
    608   ui_test_utils::RegisterAndWait(this,
    609                                  chrome::NOTIFICATION_CRX_INSTALLER_DONE,
    610                                  content::NotificationService::AllSources());
    611   return crx_installers_done_observed_ == (before + 1);
    612 }
    613 
    614 void ExtensionBrowserTest::OpenWindow(content::WebContents* contents,
    615                                       const GURL& url,
    616                                       bool newtab_process_should_equal_opener,
    617                                       content::WebContents** newtab_result) {
    618   content::WindowedNotificationObserver observer(
    619       content::NOTIFICATION_LOAD_STOP,
    620       content::NotificationService::AllSources());
    621   ASSERT_TRUE(content::ExecuteScript(contents,
    622                                      "window.open('" + url.spec() + "');"));
    623 
    624   // The above window.open call is not user-initiated, so it will create
    625   // a popup window instead of a new tab in current window.
    626   // The stop notification will come from the new tab.
    627   observer.Wait();
    628   content::NavigationController* controller =
    629       content::Source<content::NavigationController>(observer.source()).ptr();
    630   content::WebContents* newtab = controller->GetWebContents();
    631   ASSERT_TRUE(newtab);
    632   EXPECT_EQ(url, controller->GetLastCommittedEntry()->GetURL());
    633   if (newtab_process_should_equal_opener)
    634     EXPECT_EQ(contents->GetRenderProcessHost(), newtab->GetRenderProcessHost());
    635   else
    636     EXPECT_NE(contents->GetRenderProcessHost(), newtab->GetRenderProcessHost());
    637 
    638   if (newtab_result)
    639     *newtab_result = newtab;
    640 }
    641 
    642 void ExtensionBrowserTest::NavigateInRenderer(content::WebContents* contents,
    643                                               const GURL& url) {
    644   bool result = false;
    645   content::WindowedNotificationObserver observer(
    646       content::NOTIFICATION_LOAD_STOP,
    647       content::NotificationService::AllSources());
    648   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
    649       contents,
    650       "window.addEventListener('unload', function() {"
    651       "    window.domAutomationController.send(true);"
    652       "}, false);"
    653       "window.location = '" + url.spec() + "';",
    654       &result));
    655   ASSERT_TRUE(result);
    656   observer.Wait();
    657   EXPECT_EQ(url, contents->GetController().GetLastCommittedEntry()->GetURL());
    658 }
    659 
    660 extensions::ExtensionHost* ExtensionBrowserTest::FindHostWithPath(
    661     ExtensionProcessManager* manager,
    662     const std::string& path,
    663     int expected_hosts) {
    664   extensions::ExtensionHost* host = NULL;
    665   int num_hosts = 0;
    666   ExtensionProcessManager::ExtensionHostSet background_hosts =
    667       manager->background_hosts();
    668   for (ExtensionProcessManager::const_iterator iter = background_hosts.begin();
    669        iter != background_hosts.end(); ++iter) {
    670     if ((*iter)->GetURL().path() == path) {
    671       EXPECT_FALSE(host);
    672       host = *iter;
    673     }
    674     num_hosts++;
    675   }
    676   EXPECT_EQ(expected_hosts, num_hosts);
    677   return host;
    678 }
    679 
    680 void ExtensionBrowserTest::Observe(
    681     int type,
    682     const content::NotificationSource& source,
    683     const content::NotificationDetails& details) {
    684   switch (type) {
    685     case chrome::NOTIFICATION_EXTENSION_LOADED:
    686       last_loaded_extension_id_ =
    687           content::Details<const Extension>(details).ptr()->id();
    688       VLOG(1) << "Got EXTENSION_LOADED notification.";
    689       base::MessageLoopForUI::current()->Quit();
    690       break;
    691 
    692     case chrome::NOTIFICATION_CRX_INSTALLER_DONE:
    693       VLOG(1) << "Got CRX_INSTALLER_DONE notification.";
    694       {
    695         const Extension* extension =
    696             content::Details<const Extension>(details).ptr();
    697         if (extension)
    698           last_loaded_extension_id_ = extension->id();
    699         else
    700           last_loaded_extension_id_ = "";
    701       }
    702       ++crx_installers_done_observed_;
    703       base::MessageLoopForUI::current()->Quit();
    704       break;
    705 
    706     case chrome::NOTIFICATION_EXTENSION_INSTALLED:
    707       VLOG(1) << "Got EXTENSION_INSTALLED notification.";
    708       ++extension_installs_observed_;
    709       base::MessageLoopForUI::current()->Quit();
    710       break;
    711 
    712     case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR:
    713       VLOG(1) << "Got EXTENSION_INSTALL_ERROR notification.";
    714       base::MessageLoopForUI::current()->Quit();
    715       break;
    716 
    717     case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED:
    718       VLOG(1) << "Got EXTENSION_PROCESS_TERMINATED notification.";
    719       base::MessageLoopForUI::current()->Quit();
    720       break;
    721 
    722     case chrome::NOTIFICATION_EXTENSION_LOAD_ERROR:
    723       VLOG(1) << "Got EXTENSION_LOAD_ERROR notification.";
    724       ++extension_load_errors_observed_;
    725       base::MessageLoopForUI::current()->Quit();
    726       break;
    727 
    728     case chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED: {
    729       LocationBarTesting* location_bar =
    730           browser()->window()->GetLocationBar()->GetLocationBarForTesting();
    731       VLOG(1) << "Got EXTENSION_PAGE_ACTION_COUNT_CHANGED notification. Number "
    732                  "of page actions: " << location_bar->PageActionCount();
    733       if (location_bar->PageActionCount() ==
    734           target_page_action_count_) {
    735         target_page_action_count_ = -1;
    736         base::MessageLoopForUI::current()->Quit();
    737       }
    738       break;
    739     }
    740 
    741     case chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED: {
    742       LocationBarTesting* location_bar =
    743           browser()->window()->GetLocationBar()->GetLocationBarForTesting();
    744       VLOG(1) << "Got EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED notification. "
    745                  "Number of visible page actions: "
    746               << location_bar->PageActionVisibleCount();
    747       if (location_bar->PageActionVisibleCount() ==
    748           target_visible_page_action_count_) {
    749         target_visible_page_action_count_ = -1;
    750         base::MessageLoopForUI::current()->Quit();
    751       }
    752       break;
    753     }
    754 
    755     case content::NOTIFICATION_LOAD_STOP:
    756     case content::NOTIFICATION_WEB_CONTENTS_DESTROYED:
    757       VLOG(1) << "Got LOAD_STOP or WEB_CONTENTS_DESTROYED notification.";
    758       base::MessageLoopForUI::current()->Quit();
    759       break;
    760 
    761     default:
    762       NOTREACHED();
    763       break;
    764   }
    765 }
    766