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