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/browsertest_util.h" 19 #include "chrome/browser/extensions/component_loader.h" 20 #include "chrome/browser/extensions/crx_installer.h" 21 #include "chrome/browser/extensions/extension_creator.h" 22 #include "chrome/browser/extensions/extension_error_reporter.h" 23 #include "chrome/browser/extensions/extension_install_prompt.h" 24 #include "chrome/browser/extensions/extension_service.h" 25 #include "chrome/browser/extensions/extension_util.h" 26 #include "chrome/browser/extensions/unpacked_installer.h" 27 #include "chrome/browser/extensions/updater/extension_cache_fake.h" 28 #include "chrome/browser/profiles/profile.h" 29 #include "chrome/browser/profiles/profile_manager.h" 30 #include "chrome/browser/ui/browser.h" 31 #include "chrome/browser/ui/browser_window.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/test/base/ui_test_utils.h" 37 #include "content/public/browser/navigation_controller.h" 38 #include "content/public/browser/navigation_entry.h" 39 #include "content/public/browser/notification_registrar.h" 40 #include "content/public/browser/notification_service.h" 41 #include "content/public/browser/render_view_host.h" 42 #include "content/public/test/browser_test_utils.h" 43 #include "extensions/browser/extension_host.h" 44 #include "extensions/browser/extension_prefs.h" 45 #include "extensions/browser/extension_system.h" 46 #include "extensions/common/constants.h" 47 #include "extensions/common/extension_set.h" 48 #include "sync/api/string_ordinal.h" 49 50 #if defined(OS_CHROMEOS) 51 #include "chromeos/chromeos_switches.h" 52 #endif 53 54 using extensions::Extension; 55 using extensions::ExtensionCreator; 56 using extensions::FeatureSwitch; 57 using extensions::Manifest; 58 59 ExtensionBrowserTest::ExtensionBrowserTest() 60 : loaded_(false), 61 installed_(false), 62 #if defined(OS_CHROMEOS) 63 set_chromeos_user_(true), 64 #endif 65 // Default channel is STABLE but override with UNKNOWN so that unlaunched 66 // or incomplete APIs can write tests. 67 current_channel_(chrome::VersionInfo::CHANNEL_UNKNOWN), 68 override_prompt_for_external_extensions_( 69 FeatureSwitch::prompt_for_external_extensions(), 70 false), 71 profile_(NULL) { 72 EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); 73 } 74 75 ExtensionBrowserTest::~ExtensionBrowserTest() { 76 } 77 78 Profile* ExtensionBrowserTest::profile() { 79 if (!profile_) { 80 if (browser()) 81 profile_ = browser()->profile(); 82 else 83 profile_ = ProfileManager::GetActiveUserProfile(); 84 } 85 return profile_; 86 } 87 88 // static 89 const Extension* ExtensionBrowserTest::GetExtensionByPath( 90 const extensions::ExtensionSet* extensions, const base::FilePath& path) { 91 base::FilePath extension_path = base::MakeAbsoluteFilePath(path); 92 EXPECT_TRUE(!extension_path.empty()); 93 for (extensions::ExtensionSet::const_iterator iter = extensions->begin(); 94 iter != extensions->end(); ++iter) { 95 if ((*iter)->path() == extension_path) { 96 return iter->get(); 97 } 98 } 99 return NULL; 100 } 101 102 void ExtensionBrowserTest::SetUp() { 103 test_extension_cache_.reset(new extensions::ExtensionCacheFake()); 104 InProcessBrowserTest::SetUp(); 105 } 106 107 void ExtensionBrowserTest::SetUpCommandLine(CommandLine* command_line) { 108 PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_); 109 test_data_dir_ = test_data_dir_.AppendASCII("extensions"); 110 observer_.reset(new ExtensionTestNotificationObserver(browser())); 111 112 #if defined(OS_CHROMEOS) 113 if (set_chromeos_user_) { 114 // This makes sure that we create the Default profile first, with no 115 // ExtensionService and then the real profile with one, as we do when 116 // running on chromeos. 117 command_line->AppendSwitchASCII(chromeos::switches::kLoginUser, 118 "TestUser (at) gmail.com"); 119 command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user"); 120 } 121 #endif 122 } 123 124 void ExtensionBrowserTest::SetUpOnMainThread() { 125 InProcessBrowserTest::SetUpOnMainThread(); 126 observer_.reset(new ExtensionTestNotificationObserver(browser())); 127 } 128 129 const Extension* ExtensionBrowserTest::LoadExtension( 130 const base::FilePath& path) { 131 return LoadExtensionWithFlags(path, kFlagEnableFileAccess); 132 } 133 134 const Extension* ExtensionBrowserTest::LoadExtensionIncognito( 135 const base::FilePath& path) { 136 return LoadExtensionWithFlags(path, 137 kFlagEnableFileAccess | kFlagEnableIncognito); 138 } 139 140 const Extension* ExtensionBrowserTest::LoadExtensionWithFlags( 141 const base::FilePath& path, int flags) { 142 return LoadExtensionWithInstallParam(path, flags, std::string()); 143 } 144 145 const extensions::Extension* 146 ExtensionBrowserTest::LoadExtensionWithInstallParam( 147 const base::FilePath& path, 148 int flags, 149 const std::string& install_param) { 150 ExtensionService* service = extensions::ExtensionSystem::Get( 151 profile())->extension_service(); 152 { 153 observer_->Watch(chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED, 154 content::NotificationService::AllSources()); 155 156 scoped_refptr<extensions::UnpackedInstaller> installer( 157 extensions::UnpackedInstaller::Create(service)); 158 installer->set_prompt_for_plugins(false); 159 installer->set_require_modern_manifest_version( 160 (flags & kFlagAllowOldManifestVersions) == 0); 161 installer->Load(path); 162 163 observer_->Wait(); 164 } 165 166 // Find the loaded extension by its path. See crbug.com/59531 for why 167 // we cannot just use last_loaded_extension_id(). 168 const Extension* extension = GetExtensionByPath(service->extensions(), path); 169 if (!extension) 170 return NULL; 171 172 if (!(flags & kFlagIgnoreManifestWarnings)) { 173 const std::vector<extensions::InstallWarning>& install_warnings = 174 extension->install_warnings(); 175 if (!install_warnings.empty()) { 176 std::string install_warnings_message = base::StringPrintf( 177 "Unexpected warnings when loading test extension %s:\n", 178 path.AsUTF8Unsafe().c_str()); 179 180 for (std::vector<extensions::InstallWarning>::const_iterator it = 181 install_warnings.begin(); it != install_warnings.end(); ++it) { 182 install_warnings_message += " " + it->message + "\n"; 183 } 184 185 EXPECT_TRUE(extension->install_warnings().empty()) << 186 install_warnings_message; 187 return NULL; 188 } 189 } 190 191 const std::string extension_id = extension->id(); 192 193 if (!install_param.empty()) { 194 extensions::ExtensionPrefs::Get(profile()) 195 ->SetInstallParam(extension_id, install_param); 196 // Re-enable the extension if needed. 197 if (service->extensions()->Contains(extension_id)) { 198 content::WindowedNotificationObserver load_signal( 199 chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED, 200 content::Source<Profile>(profile())); 201 // Reload the extension so that the 202 // NOTIFICATION_EXTENSION_LOADED_DEPRECATED 203 // observers may access |install_param|. 204 service->ReloadExtension(extension_id); 205 load_signal.Wait(); 206 extension = service->GetExtensionById(extension_id, false); 207 CHECK(extension) << extension_id << " not found after reloading."; 208 } 209 } 210 211 // Toggling incognito or file access will reload the extension, so wait for 212 // the reload and grab the new extension instance. The default state is 213 // incognito disabled and file access enabled, so we don't wait in those 214 // cases. 215 { 216 content::WindowedNotificationObserver load_signal( 217 chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED, 218 content::Source<Profile>(profile())); 219 CHECK(!extensions::util::IsIncognitoEnabled(extension_id, profile())); 220 221 if (flags & kFlagEnableIncognito) { 222 extensions::util::SetIsIncognitoEnabled(extension_id, profile(), true); 223 load_signal.Wait(); 224 extension = service->GetExtensionById(extension_id, false); 225 CHECK(extension) << extension_id << " not found after reloading."; 226 } 227 } 228 229 { 230 content::WindowedNotificationObserver load_signal( 231 chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED, 232 content::Source<Profile>(profile())); 233 CHECK(extensions::util::AllowFileAccess(extension_id, profile())); 234 if (!(flags & kFlagEnableFileAccess)) { 235 extensions::util::SetAllowFileAccess(extension_id, profile(), false); 236 load_signal.Wait(); 237 extension = service->GetExtensionById(extension_id, false); 238 CHECK(extension) << extension_id << " not found after reloading."; 239 } 240 } 241 242 if (!observer_->WaitForExtensionViewsToLoad()) 243 return NULL; 244 245 return extension; 246 } 247 248 const Extension* ExtensionBrowserTest::LoadExtensionAsComponentWithManifest( 249 const base::FilePath& path, 250 const base::FilePath::CharType* manifest_relative_path) { 251 ExtensionService* service = extensions::ExtensionSystem::Get( 252 profile())->extension_service(); 253 254 std::string manifest; 255 if (!base::ReadFileToString(path.Append(manifest_relative_path), &manifest)) { 256 return NULL; 257 } 258 259 std::string extension_id = service->component_loader()->Add(manifest, path); 260 const Extension* extension = service->extensions()->GetByID(extension_id); 261 if (!extension) 262 return NULL; 263 observer_->set_last_loaded_extension_id(extension->id()); 264 return extension; 265 } 266 267 const Extension* ExtensionBrowserTest::LoadExtensionAsComponent( 268 const base::FilePath& path) { 269 return LoadExtensionAsComponentWithManifest(path, 270 extensions::kManifestFilename); 271 } 272 273 base::FilePath ExtensionBrowserTest::PackExtension( 274 const base::FilePath& dir_path) { 275 base::FilePath crx_path = temp_dir_.path().AppendASCII("temp.crx"); 276 if (!base::DeleteFile(crx_path, false)) { 277 ADD_FAILURE() << "Failed to delete crx: " << crx_path.value(); 278 return base::FilePath(); 279 } 280 281 // Look for PEM files with the same name as the directory. 282 base::FilePath pem_path = 283 dir_path.ReplaceExtension(FILE_PATH_LITERAL(".pem")); 284 base::FilePath pem_path_out; 285 286 if (!base::PathExists(pem_path)) { 287 pem_path = base::FilePath(); 288 pem_path_out = crx_path.DirName().AppendASCII("temp.pem"); 289 if (!base::DeleteFile(pem_path_out, false)) { 290 ADD_FAILURE() << "Failed to delete pem: " << pem_path_out.value(); 291 return base::FilePath(); 292 } 293 } 294 295 return PackExtensionWithOptions(dir_path, crx_path, pem_path, pem_path_out); 296 } 297 298 base::FilePath ExtensionBrowserTest::PackExtensionWithOptions( 299 const base::FilePath& dir_path, 300 const base::FilePath& crx_path, 301 const base::FilePath& pem_path, 302 const base::FilePath& pem_out_path) { 303 if (!base::PathExists(dir_path)) { 304 ADD_FAILURE() << "Extension dir not found: " << dir_path.value(); 305 return base::FilePath(); 306 } 307 308 if (!base::PathExists(pem_path) && pem_out_path.empty()) { 309 ADD_FAILURE() << "Must specify a PEM file or PEM output path"; 310 return base::FilePath(); 311 } 312 313 scoped_ptr<ExtensionCreator> creator(new ExtensionCreator()); 314 if (!creator->Run(dir_path, 315 crx_path, 316 pem_path, 317 pem_out_path, 318 ExtensionCreator::kOverwriteCRX)) { 319 ADD_FAILURE() << "ExtensionCreator::Run() failed: " 320 << creator->error_message(); 321 return base::FilePath(); 322 } 323 324 if (!base::PathExists(crx_path)) { 325 ADD_FAILURE() << crx_path.value() << " was not created."; 326 return base::FilePath(); 327 } 328 return crx_path; 329 } 330 331 // This class is used to simulate an installation abort by the user. 332 class MockAbortExtensionInstallPrompt : public ExtensionInstallPrompt { 333 public: 334 MockAbortExtensionInstallPrompt() : ExtensionInstallPrompt(NULL) { 335 } 336 337 // Simulate a user abort on an extension installation. 338 virtual void ConfirmInstall( 339 Delegate* delegate, 340 const Extension* extension, 341 const ShowDialogCallback& show_dialog_callback) OVERRIDE { 342 delegate->InstallUIAbort(true); 343 base::MessageLoopForUI::current()->Quit(); 344 } 345 346 virtual void OnInstallSuccess(const Extension* extension, 347 SkBitmap* icon) OVERRIDE {} 348 349 virtual void OnInstallFailure( 350 const extensions::CrxInstallerError& error) OVERRIDE {} 351 }; 352 353 class MockAutoConfirmExtensionInstallPrompt : public ExtensionInstallPrompt { 354 public: 355 explicit MockAutoConfirmExtensionInstallPrompt( 356 content::WebContents* web_contents) 357 : ExtensionInstallPrompt(web_contents) {} 358 359 // Proceed without confirmation prompt. 360 virtual void ConfirmInstall( 361 Delegate* delegate, 362 const Extension* extension, 363 const ShowDialogCallback& show_dialog_callback) OVERRIDE { 364 delegate->InstallUIProceed(); 365 } 366 }; 367 368 const Extension* ExtensionBrowserTest::UpdateExtensionWaitForIdle( 369 const std::string& id, 370 const base::FilePath& path, 371 int expected_change) { 372 return InstallOrUpdateExtension(id, 373 path, 374 INSTALL_UI_TYPE_NONE, 375 expected_change, 376 Manifest::INTERNAL, 377 browser(), 378 Extension::NO_FLAGS, 379 false, 380 false); 381 } 382 383 const Extension* ExtensionBrowserTest::InstallExtensionFromWebstore( 384 const base::FilePath& path, 385 int expected_change) { 386 return InstallOrUpdateExtension(std::string(), 387 path, 388 INSTALL_UI_TYPE_NONE, 389 expected_change, 390 Manifest::INTERNAL, 391 browser(), 392 Extension::FROM_WEBSTORE, 393 true, 394 false); 395 } 396 397 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension( 398 const std::string& id, 399 const base::FilePath& path, 400 InstallUIType ui_type, 401 int expected_change) { 402 return InstallOrUpdateExtension(id, 403 path, 404 ui_type, 405 expected_change, 406 Manifest::INTERNAL, 407 browser(), 408 Extension::NO_FLAGS, 409 true, 410 false); 411 } 412 413 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension( 414 const std::string& id, 415 const base::FilePath& path, 416 InstallUIType ui_type, 417 int expected_change, 418 Browser* browser, 419 Extension::InitFromValueFlags creation_flags) { 420 return InstallOrUpdateExtension(id, 421 path, 422 ui_type, 423 expected_change, 424 Manifest::INTERNAL, 425 browser, 426 creation_flags, 427 true, 428 false); 429 } 430 431 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension( 432 const std::string& id, 433 const base::FilePath& path, 434 InstallUIType ui_type, 435 int expected_change, 436 Manifest::Location install_source) { 437 return InstallOrUpdateExtension(id, 438 path, 439 ui_type, 440 expected_change, 441 install_source, 442 browser(), 443 Extension::NO_FLAGS, 444 true, 445 false); 446 } 447 448 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension( 449 const std::string& id, 450 const base::FilePath& path, 451 InstallUIType ui_type, 452 int expected_change, 453 Manifest::Location install_source, 454 Browser* browser, 455 Extension::InitFromValueFlags creation_flags, 456 bool install_immediately, 457 bool is_ephemeral) { 458 ExtensionService* service = profile()->GetExtensionService(); 459 service->set_show_extensions_prompts(false); 460 size_t num_before = service->extensions()->size(); 461 462 { 463 scoped_ptr<ExtensionInstallPrompt> install_ui; 464 if (ui_type == INSTALL_UI_TYPE_CANCEL) { 465 install_ui.reset(new MockAbortExtensionInstallPrompt()); 466 } else if (ui_type == INSTALL_UI_TYPE_NORMAL) { 467 install_ui.reset(new ExtensionInstallPrompt( 468 browser->tab_strip_model()->GetActiveWebContents())); 469 } else if (ui_type == INSTALL_UI_TYPE_AUTO_CONFIRM) { 470 install_ui.reset(new MockAutoConfirmExtensionInstallPrompt( 471 browser->tab_strip_model()->GetActiveWebContents())); 472 } 473 474 // TODO(tessamac): Update callers to always pass an unpacked extension 475 // and then always pack the extension here. 476 base::FilePath crx_path = path; 477 if (crx_path.Extension() != FILE_PATH_LITERAL(".crx")) { 478 crx_path = PackExtension(path); 479 } 480 if (crx_path.empty()) 481 return NULL; 482 483 scoped_refptr<extensions::CrxInstaller> installer( 484 extensions::CrxInstaller::Create(service, install_ui.Pass())); 485 installer->set_expected_id(id); 486 installer->set_creation_flags(creation_flags); 487 installer->set_install_source(install_source); 488 installer->set_install_immediately(install_immediately); 489 installer->set_is_ephemeral(is_ephemeral); 490 if (!installer->is_gallery_install()) { 491 installer->set_off_store_install_allow_reason( 492 extensions::CrxInstaller::OffStoreInstallAllowedInTest); 493 } 494 495 observer_->Watch( 496 chrome::NOTIFICATION_CRX_INSTALLER_DONE, 497 content::Source<extensions::CrxInstaller>(installer.get())); 498 499 installer->InstallCrx(crx_path); 500 501 observer_->Wait(); 502 } 503 504 size_t num_after = service->extensions()->size(); 505 EXPECT_EQ(num_before + expected_change, num_after); 506 if (num_before + expected_change != num_after) { 507 VLOG(1) << "Num extensions before: " << base::IntToString(num_before) 508 << " num after: " << base::IntToString(num_after) 509 << " Installed extensions follow:"; 510 511 for (extensions::ExtensionSet::const_iterator it = 512 service->extensions()->begin(); 513 it != service->extensions()->end(); ++it) 514 VLOG(1) << " " << (*it)->id(); 515 516 VLOG(1) << "Errors follow:"; 517 const std::vector<base::string16>* errors = 518 ExtensionErrorReporter::GetInstance()->GetErrors(); 519 for (std::vector<base::string16>::const_iterator iter = errors->begin(); 520 iter != errors->end(); ++iter) 521 VLOG(1) << *iter; 522 523 return NULL; 524 } 525 526 if (!observer_->WaitForExtensionViewsToLoad()) 527 return NULL; 528 return service->GetExtensionById(last_loaded_extension_id(), false); 529 } 530 531 void ExtensionBrowserTest::ReloadExtension(const std::string extension_id) { 532 observer_->Watch(chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED, 533 content::NotificationService::AllSources()); 534 535 ExtensionService* service = 536 extensions::ExtensionSystem::Get(profile())->extension_service(); 537 service->ReloadExtension(extension_id); 538 539 observer_->Wait(); 540 observer_->WaitForExtensionViewsToLoad(); 541 } 542 543 void ExtensionBrowserTest::UnloadExtension(const std::string& extension_id) { 544 ExtensionService* service = extensions::ExtensionSystem::Get( 545 profile())->extension_service(); 546 service->UnloadExtension(extension_id, 547 extensions::UnloadedExtensionInfo::REASON_DISABLE); 548 } 549 550 void ExtensionBrowserTest::UninstallExtension(const std::string& extension_id) { 551 ExtensionService* service = extensions::ExtensionSystem::Get( 552 profile())->extension_service(); 553 service->UninstallExtension(extension_id, false, NULL); 554 } 555 556 void ExtensionBrowserTest::DisableExtension(const std::string& extension_id) { 557 ExtensionService* service = extensions::ExtensionSystem::Get( 558 profile())->extension_service(); 559 service->DisableExtension(extension_id, Extension::DISABLE_USER_ACTION); 560 } 561 562 void ExtensionBrowserTest::EnableExtension(const std::string& extension_id) { 563 ExtensionService* service = extensions::ExtensionSystem::Get( 564 profile())->extension_service(); 565 service->EnableExtension(extension_id); 566 } 567 568 void ExtensionBrowserTest::OpenWindow(content::WebContents* contents, 569 const GURL& url, 570 bool newtab_process_should_equal_opener, 571 content::WebContents** newtab_result) { 572 content::WindowedNotificationObserver windowed_observer( 573 content::NOTIFICATION_LOAD_STOP, 574 content::NotificationService::AllSources()); 575 ASSERT_TRUE(content::ExecuteScript(contents, 576 "window.open('" + url.spec() + "');")); 577 578 // The above window.open call is not user-initiated, so it will create 579 // a popup window instead of a new tab in current window. 580 // The stop notification will come from the new tab. 581 windowed_observer.Wait(); 582 content::NavigationController* controller = 583 content::Source<content::NavigationController>( 584 windowed_observer.source()).ptr(); 585 content::WebContents* newtab = controller->GetWebContents(); 586 ASSERT_TRUE(newtab); 587 EXPECT_EQ(url, controller->GetLastCommittedEntry()->GetURL()); 588 if (newtab_process_should_equal_opener) 589 EXPECT_EQ(contents->GetRenderProcessHost(), newtab->GetRenderProcessHost()); 590 else 591 EXPECT_NE(contents->GetRenderProcessHost(), newtab->GetRenderProcessHost()); 592 593 if (newtab_result) 594 *newtab_result = newtab; 595 } 596 597 void ExtensionBrowserTest::NavigateInRenderer(content::WebContents* contents, 598 const GURL& url) { 599 bool result = false; 600 content::WindowedNotificationObserver windowed_observer( 601 content::NOTIFICATION_LOAD_STOP, 602 content::NotificationService::AllSources()); 603 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 604 contents, 605 "window.addEventListener('unload', function() {" 606 " window.domAutomationController.send(true);" 607 "}, false);" 608 "window.location = '" + url.spec() + "';", 609 &result)); 610 ASSERT_TRUE(result); 611 windowed_observer.Wait(); 612 EXPECT_EQ(url, contents->GetController().GetLastCommittedEntry()->GetURL()); 613 } 614 615 extensions::ExtensionHost* ExtensionBrowserTest::FindHostWithPath( 616 extensions::ProcessManager* manager, 617 const std::string& path, 618 int expected_hosts) { 619 extensions::ExtensionHost* host = NULL; 620 int num_hosts = 0; 621 extensions::ProcessManager::ExtensionHostSet background_hosts = 622 manager->background_hosts(); 623 for (extensions::ProcessManager::const_iterator iter = 624 background_hosts.begin(); 625 iter != background_hosts.end(); 626 ++iter) { 627 if ((*iter)->GetURL().path() == path) { 628 EXPECT_FALSE(host); 629 host = *iter; 630 } 631 num_hosts++; 632 } 633 EXPECT_EQ(expected_hosts, num_hosts); 634 return host; 635 } 636 637 std::string ExtensionBrowserTest::ExecuteScriptInBackgroundPage( 638 const std::string& extension_id, 639 const std::string& script) { 640 return extensions::browsertest_util::ExecuteScriptInBackgroundPage( 641 profile(), extension_id, script); 642 } 643 644 bool ExtensionBrowserTest::ExecuteScriptInBackgroundPageNoWait( 645 const std::string& extension_id, 646 const std::string& script) { 647 return extensions::browsertest_util::ExecuteScriptInBackgroundPageNoWait( 648 profile(), extension_id, script); 649 } 650