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