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