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 32ExtensionBrowserTest::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 41void 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@gmail.com"); 57 command_line->AppendSwitchASCII(switches::kLoginProfile, "user"); 58 command_line->AppendSwitch(switches::kNoFirstRun); 59#endif 60} 61 62const 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 101const Extension* ExtensionBrowserTest::LoadExtension(const FilePath& path) { 102 return LoadExtensionImpl(path, false, true); 103} 104 105const Extension* ExtensionBrowserTest::LoadExtensionIncognito( 106 const FilePath& path) { 107 return LoadExtensionImpl(path, true, true); 108} 109 110const Extension* ExtensionBrowserTest::LoadExtensionNoFileAccess( 111 const FilePath& path) { 112 return LoadExtensionImpl(path, false, false); 113} 114 115const Extension* ExtensionBrowserTest::LoadExtensionIncognitoNoFileAccess( 116 const FilePath& path) { 117 return LoadExtensionImpl(path, true, false); 118} 119 120bool 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 134FilePath 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. 169class 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 184class 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 195bool 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 203bool 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 268void 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 276void ExtensionBrowserTest::UnloadExtension(const std::string& extension_id) { 277 ExtensionService* service = browser()->profile()->GetExtensionService(); 278 service->UnloadExtension(extension_id, UnloadedExtensionInfo::DISABLE); 279} 280 281void ExtensionBrowserTest::UninstallExtension(const std::string& extension_id) { 282 ExtensionService* service = browser()->profile()->GetExtensionService(); 283 service->UninstallExtension(extension_id, false, NULL); 284} 285 286void ExtensionBrowserTest::DisableExtension(const std::string& extension_id) { 287 ExtensionService* service = browser()->profile()->GetExtensionService(); 288 service->DisableExtension(extension_id); 289} 290 291void ExtensionBrowserTest::EnableExtension(const std::string& extension_id) { 292 ExtensionService* service = browser()->profile()->GetExtensionService(); 293 service->EnableExtension(extension_id); 294} 295 296bool 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 308bool 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 320bool 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 344bool 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 351bool 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 359void ExtensionBrowserTest::WaitForExtensionLoad() { 360 ui_test_utils::RegisterAndWait(this, NotificationType::EXTENSION_LOADED, 361 NotificationService::AllSources()); 362 WaitForExtensionHostsToLoad(); 363} 364 365bool 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 379void 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