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