extension_browsertest.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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_host.h"
24#include "chrome/browser/extensions/extension_install_prompt.h"
25#include "chrome/browser/extensions/extension_service.h"
26#include "chrome/browser/extensions/extension_system.h"
27#include "chrome/browser/extensions/unpacked_installer.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/omnibox/location_bar.h"
33#include "chrome/browser/ui/tabs/tab_strip_model.h"
34#include "chrome/common/chrome_paths.h"
35#include "chrome/common/chrome_switches.h"
36#include "chrome/common/chrome_version_info.h"
37#include "chrome/common/extensions/extension_manifest_constants.h"
38#include "chrome/common/extensions/extension_set.h"
39#include "chrome/test/base/ui_test_utils.h"
40#include "content/public/browser/navigation_controller.h"
41#include "content/public/browser/navigation_entry.h"
42#include "content/public/browser/notification_registrar.h"
43#include "content/public/browser/notification_service.h"
44#include "content/public/browser/render_view_host.h"
45#include "content/public/test/browser_test_utils.h"
46#include "extensions/common/constants.h"
47#include "sync/api/string_ordinal.h"
48
49#if defined(OS_CHROMEOS)
50#include "chromeos/chromeos_switches.h"
51#endif
52
53using extensions::Extension;
54using extensions::ExtensionCreator;
55using extensions::FeatureSwitch;
56using extensions::Manifest;
57
58ExtensionBrowserTest::ExtensionBrowserTest()
59    : loaded_(false),
60      installed_(false),
61      extension_installs_observed_(0),
62      extension_load_errors_observed_(0),
63      target_page_action_count_(-1),
64      target_visible_page_action_count_(-1),
65      current_channel_(chrome::VersionInfo::CHANNEL_DEV),
66      override_prompt_for_external_extensions_(
67          FeatureSwitch::prompt_for_external_extensions(), false),
68      profile_(NULL) {
69  EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
70}
71
72ExtensionBrowserTest::~ExtensionBrowserTest() {}
73
74Profile* ExtensionBrowserTest::profile() {
75  if (!profile_) {
76    if (browser())
77      profile_ = browser()->profile();
78    else
79      profile_ = ProfileManager::GetDefaultProfile();
80  }
81  return profile_;
82}
83
84// static
85const Extension* ExtensionBrowserTest::GetExtensionByPath(
86    const ExtensionSet* extensions, const base::FilePath& path) {
87  base::FilePath extension_path = base::MakeAbsoluteFilePath(path);
88  EXPECT_TRUE(!extension_path.empty());
89  for (ExtensionSet::const_iterator iter = extensions->begin();
90       iter != extensions->end(); ++iter) {
91    if ((*iter)->path() == extension_path) {
92      return iter->get();
93    }
94  }
95  return NULL;
96}
97
98void ExtensionBrowserTest::SetUpCommandLine(CommandLine* command_line) {
99  PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_);
100  test_data_dir_ = test_data_dir_.AppendASCII("extensions");
101
102#if defined(OS_CHROMEOS)
103  // This makes sure that we create the Default profile first, with no
104  // ExtensionService and then the real profile with one, as we do when
105  // running on chromeos.
106  command_line->AppendSwitchASCII(chromeos::switches::kLoginUser,
107                                  "TestUser@gmail.com");
108  command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
109#endif
110}
111
112void ExtensionBrowserTest::SetUpOnMainThread() {
113  InProcessBrowserTest::SetUpOnMainThread();
114}
115
116const Extension* ExtensionBrowserTest::LoadExtensionWithFlags(
117    const base::FilePath& path, int flags) {
118  ExtensionService* service = extensions::ExtensionSystem::Get(
119      profile())->extension_service();
120  {
121    content::NotificationRegistrar registrar;
122    registrar.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
123                  content::NotificationService::AllSources());
124    scoped_refptr<extensions::UnpackedInstaller> installer(
125        extensions::UnpackedInstaller::Create(service));
126    installer->set_prompt_for_plugins(false);
127    installer->set_require_modern_manifest_version(
128        (flags & kFlagAllowOldManifestVersions) == 0);
129    installer->Load(path);
130    content::RunMessageLoop();
131  }
132
133  // Find the loaded extension by its path. See crbug.com/59531 for why
134  // we cannot just use last_loaded_extension_id_.
135  const Extension* extension = GetExtensionByPath(service->extensions(), path);
136  if (!extension)
137    return NULL;
138
139  if (!(flags & kFlagIgnoreManifestWarnings)) {
140    const std::vector<extensions::InstallWarning>& install_warnings =
141        extension->install_warnings();
142    if (!install_warnings.empty()) {
143      std::string install_warnings_message = base::StringPrintf(
144          "Unexpected warnings when loading test extension %s:\n",
145          path.AsUTF8Unsafe().c_str());
146
147      for (std::vector<extensions::InstallWarning>::const_iterator it =
148          install_warnings.begin(); it != install_warnings.end(); ++it) {
149        install_warnings_message += "  " + it->message + "\n";
150      }
151
152      EXPECT_TRUE(extension->install_warnings().empty()) <<
153          install_warnings_message;
154      return NULL;
155    }
156  }
157
158  const std::string extension_id = extension->id();
159
160  // The call to OnExtensionInstalled ensures the other extension prefs
161  // are set up with the defaults.
162  service->extension_prefs()->OnExtensionInstalled(
163      extension,
164      Extension::ENABLED,
165      extensions::Blacklist::NOT_BLACKLISTED,
166      syncer::StringOrdinal::CreateInitialOrdinal());
167
168  // Toggling incognito or file access will reload the extension, so wait for
169  // the reload and grab the new extension instance. The default state is
170  // incognito disabled and file access enabled, so we don't wait in those
171  // cases.
172  {
173    content::WindowedNotificationObserver load_signal(
174        chrome::NOTIFICATION_EXTENSION_LOADED,
175        content::Source<Profile>(profile()));
176    CHECK(!service->IsIncognitoEnabled(extension_id) ||
177          extension->force_incognito_enabled());
178
179    if (flags & kFlagEnableIncognito) {
180      service->SetIsIncognitoEnabled(extension_id, true);
181      load_signal.Wait();
182      extension = service->GetExtensionById(extension_id, false);
183      CHECK(extension) << extension_id << " not found after reloading.";
184    }
185  }
186
187  {
188    content::WindowedNotificationObserver load_signal(
189        chrome::NOTIFICATION_EXTENSION_LOADED,
190        content::Source<Profile>(profile()));
191    CHECK(service->AllowFileAccess(extension));
192    if (!(flags & kFlagEnableFileAccess)) {
193      service->SetAllowFileAccess(extension, false);
194      load_signal.Wait();
195      extension = service->GetExtensionById(extension_id, false);
196      CHECK(extension) << extension_id << " not found after reloading.";
197    }
198  }
199
200  if (!WaitForExtensionViewsToLoad())
201    return NULL;
202
203  return extension;
204}
205
206const Extension* ExtensionBrowserTest::LoadExtension(
207    const base::FilePath& path) {
208  return LoadExtensionWithFlags(path, kFlagEnableFileAccess);
209}
210
211const Extension* ExtensionBrowserTest::LoadExtensionIncognito(
212    const base::FilePath& path) {
213  return LoadExtensionWithFlags(path,
214                                kFlagEnableFileAccess | kFlagEnableIncognito);
215}
216
217const Extension* ExtensionBrowserTest::LoadExtensionAsComponentWithManifest(
218    const base::FilePath& path,
219    const base::FilePath::CharType* manifest_relative_path) {
220  ExtensionService* service = extensions::ExtensionSystem::Get(
221      profile())->extension_service();
222
223  std::string manifest;
224  if (!base::ReadFileToString(path.Append(manifest_relative_path), &manifest)) {
225    return NULL;
226  }
227
228  std::string extension_id = service->component_loader()->Add(manifest, path);
229  const Extension* extension = service->extensions()->GetByID(extension_id);
230  if (!extension)
231    return NULL;
232  last_loaded_extension_id_ = extension->id();
233  return extension;
234}
235
236const Extension* ExtensionBrowserTest::LoadExtensionAsComponent(
237    const base::FilePath& path) {
238  return LoadExtensionAsComponentWithManifest(path,
239                                              extensions::kManifestFilename);
240}
241
242base::FilePath ExtensionBrowserTest::PackExtension(
243    const base::FilePath& dir_path) {
244  base::FilePath crx_path = temp_dir_.path().AppendASCII("temp.crx");
245  if (!base::DeleteFile(crx_path, false)) {
246    ADD_FAILURE() << "Failed to delete crx: " << crx_path.value();
247    return base::FilePath();
248  }
249
250  // Look for PEM files with the same name as the directory.
251  base::FilePath pem_path =
252      dir_path.ReplaceExtension(FILE_PATH_LITERAL(".pem"));
253  base::FilePath pem_path_out;
254
255  if (!base::PathExists(pem_path)) {
256    pem_path = base::FilePath();
257    pem_path_out = crx_path.DirName().AppendASCII("temp.pem");
258    if (!base::DeleteFile(pem_path_out, false)) {
259      ADD_FAILURE() << "Failed to delete pem: " << pem_path_out.value();
260      return base::FilePath();
261    }
262  }
263
264  return PackExtensionWithOptions(dir_path, crx_path, pem_path, pem_path_out);
265}
266
267base::FilePath ExtensionBrowserTest::PackExtensionWithOptions(
268    const base::FilePath& dir_path,
269    const base::FilePath& crx_path,
270    const base::FilePath& pem_path,
271    const base::FilePath& pem_out_path) {
272  if (!base::PathExists(dir_path)) {
273    ADD_FAILURE() << "Extension dir not found: " << dir_path.value();
274    return base::FilePath();
275  }
276
277  if (!base::PathExists(pem_path) && pem_out_path.empty()) {
278    ADD_FAILURE() << "Must specify a PEM file or PEM output path";
279    return base::FilePath();
280  }
281
282  scoped_ptr<ExtensionCreator> creator(new ExtensionCreator());
283  if (!creator->Run(dir_path,
284                    crx_path,
285                    pem_path,
286                    pem_out_path,
287                    ExtensionCreator::kOverwriteCRX)) {
288    ADD_FAILURE() << "ExtensionCreator::Run() failed: "
289                  << creator->error_message();
290    return base::FilePath();
291  }
292
293  if (!base::PathExists(crx_path)) {
294    ADD_FAILURE() << crx_path.value() << " was not created.";
295    return base::FilePath();
296  }
297  return crx_path;
298}
299
300// This class is used to simulate an installation abort by the user.
301class MockAbortExtensionInstallPrompt : public ExtensionInstallPrompt {
302 public:
303  MockAbortExtensionInstallPrompt() : ExtensionInstallPrompt(NULL) {
304  }
305
306  // Simulate a user abort on an extension installation.
307  virtual void ConfirmInstall(
308      Delegate* delegate,
309      const Extension* extension,
310      const ShowDialogCallback& show_dialog_callback) OVERRIDE {
311    delegate->InstallUIAbort(true);
312    base::MessageLoopForUI::current()->Quit();
313  }
314
315  virtual void OnInstallSuccess(const Extension* extension,
316                                SkBitmap* icon) OVERRIDE {}
317
318  virtual void OnInstallFailure(
319      const extensions::CrxInstallerError& error) OVERRIDE {}
320};
321
322class MockAutoConfirmExtensionInstallPrompt : public ExtensionInstallPrompt {
323 public:
324  explicit MockAutoConfirmExtensionInstallPrompt(
325      content::WebContents* web_contents)
326    : ExtensionInstallPrompt(web_contents) {}
327
328  // Proceed without confirmation prompt.
329  virtual void ConfirmInstall(
330      Delegate* delegate,
331      const Extension* extension,
332      const ShowDialogCallback& show_dialog_callback) OVERRIDE {
333    delegate->InstallUIProceed();
334  }
335};
336
337const Extension* ExtensionBrowserTest::UpdateExtensionWaitForIdle(
338    const std::string& id,
339    const base::FilePath& path,
340    int expected_change) {
341  return InstallOrUpdateExtension(id,
342                                  path,
343                                  INSTALL_UI_TYPE_NONE,
344                                  expected_change,
345                                  Manifest::INTERNAL,
346                                  browser(),
347                                  false,
348                                  true);
349}
350
351const Extension* ExtensionBrowserTest::InstallExtensionFromWebstore(
352    const base::FilePath& path,
353    int expected_change) {
354  return InstallOrUpdateExtension(std::string(),
355                                  path,
356                                  INSTALL_UI_TYPE_NONE,
357                                  expected_change,
358                                  Manifest::INTERNAL,
359                                  browser(),
360                                  true,
361                                  false);
362}
363
364const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
365    const std::string& id,
366    const base::FilePath& path,
367    InstallUIType ui_type,
368    int expected_change) {
369  return InstallOrUpdateExtension(id, path, ui_type, expected_change,
370                                  Manifest::INTERNAL, browser(), false, false);
371}
372
373const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
374    const std::string& id,
375    const base::FilePath& path,
376    InstallUIType ui_type,
377    int expected_change,
378    Browser* browser,
379    bool from_webstore) {
380  return InstallOrUpdateExtension(id, path, ui_type, expected_change,
381                                  Manifest::INTERNAL, browser, from_webstore,
382                                  false);
383}
384
385const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
386    const std::string& id,
387    const base::FilePath& path,
388    InstallUIType ui_type,
389    int expected_change,
390    Manifest::Location install_source) {
391  return InstallOrUpdateExtension(id, path, ui_type, expected_change,
392                                  install_source, browser(), false, false);
393}
394
395const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
396    const std::string& id,
397    const base::FilePath& path,
398    InstallUIType ui_type,
399    int expected_change,
400    Manifest::Location install_source,
401    Browser* browser,
402    bool from_webstore,
403    bool wait_for_idle) {
404  ExtensionService* service = profile()->GetExtensionService();
405  service->set_show_extensions_prompts(false);
406  size_t num_before = service->extensions()->size();
407
408  {
409    scoped_ptr<ExtensionInstallPrompt> install_ui;
410    if (ui_type == INSTALL_UI_TYPE_CANCEL) {
411      install_ui.reset(new MockAbortExtensionInstallPrompt());
412    } else if (ui_type == INSTALL_UI_TYPE_NORMAL) {
413      install_ui.reset(new ExtensionInstallPrompt(
414          browser->tab_strip_model()->GetActiveWebContents()));
415    } else if (ui_type == INSTALL_UI_TYPE_AUTO_CONFIRM) {
416      install_ui.reset(new MockAutoConfirmExtensionInstallPrompt(
417          browser->tab_strip_model()->GetActiveWebContents()));
418    }
419
420    // TODO(tessamac): Update callers to always pass an unpacked extension
421    //                 and then always pack the extension here.
422    base::FilePath crx_path = path;
423    if (crx_path.Extension() != FILE_PATH_LITERAL(".crx")) {
424      crx_path = PackExtension(path);
425    }
426    if (crx_path.empty())
427      return NULL;
428
429    scoped_refptr<extensions::CrxInstaller> installer(
430        extensions::CrxInstaller::Create(service, install_ui.Pass()));
431    installer->set_expected_id(id);
432    installer->set_is_gallery_install(from_webstore);
433    installer->set_install_source(install_source);
434    installer->set_install_wait_for_idle(wait_for_idle);
435    if (!from_webstore) {
436      installer->set_off_store_install_allow_reason(
437          extensions::CrxInstaller::OffStoreInstallAllowedInTest);
438    }
439
440    content::NotificationRegistrar registrar;
441    registrar.Add(this, chrome::NOTIFICATION_CRX_INSTALLER_DONE,
442                  content::Source<extensions::CrxInstaller>(installer.get()));
443
444    installer->InstallCrx(crx_path);
445
446    content::RunMessageLoop();
447  }
448
449  size_t num_after = service->extensions()->size();
450  EXPECT_EQ(num_before + expected_change, num_after);
451  if (num_before + expected_change != num_after) {
452    VLOG(1) << "Num extensions before: " << base::IntToString(num_before)
453            << " num after: " << base::IntToString(num_after)
454            << " Installed extensions follow:";
455
456    for (ExtensionSet::const_iterator it = service->extensions()->begin();
457         it != service->extensions()->end(); ++it)
458      VLOG(1) << "  " << (*it)->id();
459
460    VLOG(1) << "Errors follow:";
461    const std::vector<string16>* errors =
462        ExtensionErrorReporter::GetInstance()->GetErrors();
463    for (std::vector<string16>::const_iterator iter = errors->begin();
464         iter != errors->end(); ++iter)
465      VLOG(1) << *iter;
466
467    return NULL;
468  }
469
470  if (!WaitForExtensionViewsToLoad())
471    return NULL;
472  return service->GetExtensionById(last_loaded_extension_id_, false);
473}
474
475void ExtensionBrowserTest::ReloadExtension(const std::string extension_id) {
476  ExtensionService* service = extensions::ExtensionSystem::Get(
477      profile())->extension_service();
478  service->ReloadExtension(extension_id);
479  ui_test_utils::RegisterAndWait(this,
480                                 chrome::NOTIFICATION_EXTENSION_LOADED,
481                                 content::NotificationService::AllSources());
482}
483
484void ExtensionBrowserTest::UnloadExtension(const std::string& extension_id) {
485  ExtensionService* service = extensions::ExtensionSystem::Get(
486      profile())->extension_service();
487  service->UnloadExtension(extension_id, extension_misc::UNLOAD_REASON_DISABLE);
488}
489
490void ExtensionBrowserTest::UninstallExtension(const std::string& extension_id) {
491  ExtensionService* service = extensions::ExtensionSystem::Get(
492      profile())->extension_service();
493  service->UninstallExtension(extension_id, false, NULL);
494}
495
496void ExtensionBrowserTest::DisableExtension(const std::string& extension_id) {
497  ExtensionService* service = extensions::ExtensionSystem::Get(
498      profile())->extension_service();
499  service->DisableExtension(extension_id, Extension::DISABLE_USER_ACTION);
500}
501
502void ExtensionBrowserTest::EnableExtension(const std::string& extension_id) {
503  ExtensionService* service = extensions::ExtensionSystem::Get(
504      profile())->extension_service();
505  service->EnableExtension(extension_id);
506}
507
508bool ExtensionBrowserTest::WaitForPageActionCountChangeTo(int count) {
509  LocationBarTesting* location_bar =
510      browser()->window()->GetLocationBar()->GetLocationBarForTesting();
511  if (location_bar->PageActionCount() != count) {
512    target_page_action_count_ = count;
513    ui_test_utils::RegisterAndWait(this,
514        chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED,
515        content::NotificationService::AllSources());
516  }
517  return location_bar->PageActionCount() == count;
518}
519
520bool ExtensionBrowserTest::WaitForPageActionVisibilityChangeTo(int count) {
521  LocationBarTesting* location_bar =
522      browser()->window()->GetLocationBar()->GetLocationBarForTesting();
523  if (location_bar->PageActionVisibleCount() != count) {
524    target_visible_page_action_count_ = count;
525    ui_test_utils::RegisterAndWait(this,
526        chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED,
527        content::NotificationService::AllSources());
528  }
529  return location_bar->PageActionVisibleCount() == count;
530}
531
532bool ExtensionBrowserTest::WaitForExtensionViewsToLoad() {
533  // Wait for all the extension render view hosts that exist to finish loading.
534  content::NotificationRegistrar registrar;
535  registrar.Add(this, content::NOTIFICATION_LOAD_STOP,
536                content::NotificationService::AllSources());
537  registrar.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
538                content::NotificationService::AllSources());
539
540  ExtensionProcessManager* manager =
541      extensions::ExtensionSystem::Get(profile())->process_manager();
542  ExtensionProcessManager::ViewSet all_views = manager->GetAllViews();
543  for (ExtensionProcessManager::ViewSet::const_iterator iter =
544           all_views.begin();
545       iter != all_views.end();) {
546    if (!(*iter)->IsLoading()) {
547      ++iter;
548    } else {
549      content::RunMessageLoop();
550
551      // Test activity may have modified the set of extension processes during
552      // message processing, so re-start the iteration to catch added/removed
553      // processes.
554      all_views = manager->GetAllViews();
555      iter = all_views.begin();
556    }
557  }
558  return true;
559}
560
561bool ExtensionBrowserTest::WaitForExtensionInstall() {
562  int before = extension_installs_observed_;
563  ui_test_utils::RegisterAndWait(this,
564                                 chrome::NOTIFICATION_EXTENSION_INSTALLED,
565                                 content::NotificationService::AllSources());
566  return extension_installs_observed_ == (before + 1);
567}
568
569bool ExtensionBrowserTest::WaitForExtensionInstallError() {
570  int before = extension_installs_observed_;
571  ui_test_utils::RegisterAndWait(this,
572                                 chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
573                                 content::NotificationService::AllSources());
574  return extension_installs_observed_ == before;
575}
576
577void ExtensionBrowserTest::WaitForExtensionLoad() {
578  ui_test_utils::RegisterAndWait(this, chrome::NOTIFICATION_EXTENSION_LOADED,
579                                 content::NotificationService::AllSources());
580  WaitForExtensionViewsToLoad();
581}
582
583bool ExtensionBrowserTest::WaitForExtensionLoadError() {
584  int before = extension_load_errors_observed_;
585  ui_test_utils::RegisterAndWait(this,
586                                 chrome::NOTIFICATION_EXTENSION_LOAD_ERROR,
587                                 content::NotificationService::AllSources());
588  return extension_load_errors_observed_ != before;
589}
590
591bool ExtensionBrowserTest::WaitForExtensionCrash(
592    const std::string& extension_id) {
593  ExtensionService* service = extensions::ExtensionSystem::Get(
594      profile())->extension_service();
595
596  if (!service->GetExtensionById(extension_id, true)) {
597    // The extension is already unloaded, presumably due to a crash.
598    return true;
599  }
600  ui_test_utils::RegisterAndWait(
601      this, chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED,
602      content::NotificationService::AllSources());
603  return (service->GetExtensionById(extension_id, true) == NULL);
604}
605
606bool ExtensionBrowserTest::WaitForCrxInstallerDone() {
607  int before = crx_installers_done_observed_;
608  ui_test_utils::RegisterAndWait(this,
609                                 chrome::NOTIFICATION_CRX_INSTALLER_DONE,
610                                 content::NotificationService::AllSources());
611  return crx_installers_done_observed_ == (before + 1);
612}
613
614void ExtensionBrowserTest::OpenWindow(content::WebContents* contents,
615                                      const GURL& url,
616                                      bool newtab_process_should_equal_opener,
617                                      content::WebContents** newtab_result) {
618  content::WindowedNotificationObserver observer(
619      content::NOTIFICATION_LOAD_STOP,
620      content::NotificationService::AllSources());
621  ASSERT_TRUE(content::ExecuteScript(contents,
622                                     "window.open('" + url.spec() + "');"));
623
624  // The above window.open call is not user-initiated, so it will create
625  // a popup window instead of a new tab in current window.
626  // The stop notification will come from the new tab.
627  observer.Wait();
628  content::NavigationController* controller =
629      content::Source<content::NavigationController>(observer.source()).ptr();
630  content::WebContents* newtab = controller->GetWebContents();
631  ASSERT_TRUE(newtab);
632  EXPECT_EQ(url, controller->GetLastCommittedEntry()->GetURL());
633  if (newtab_process_should_equal_opener)
634    EXPECT_EQ(contents->GetRenderProcessHost(), newtab->GetRenderProcessHost());
635  else
636    EXPECT_NE(contents->GetRenderProcessHost(), newtab->GetRenderProcessHost());
637
638  if (newtab_result)
639    *newtab_result = newtab;
640}
641
642void ExtensionBrowserTest::NavigateInRenderer(content::WebContents* contents,
643                                              const GURL& url) {
644  bool result = false;
645  content::WindowedNotificationObserver observer(
646      content::NOTIFICATION_LOAD_STOP,
647      content::NotificationService::AllSources());
648  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
649      contents,
650      "window.addEventListener('unload', function() {"
651      "    window.domAutomationController.send(true);"
652      "}, false);"
653      "window.location = '" + url.spec() + "';",
654      &result));
655  ASSERT_TRUE(result);
656  observer.Wait();
657  EXPECT_EQ(url, contents->GetController().GetLastCommittedEntry()->GetURL());
658}
659
660extensions::ExtensionHost* ExtensionBrowserTest::FindHostWithPath(
661    ExtensionProcessManager* manager,
662    const std::string& path,
663    int expected_hosts) {
664  extensions::ExtensionHost* host = NULL;
665  int num_hosts = 0;
666  ExtensionProcessManager::ExtensionHostSet background_hosts =
667      manager->background_hosts();
668  for (ExtensionProcessManager::const_iterator iter = background_hosts.begin();
669       iter != background_hosts.end(); ++iter) {
670    if ((*iter)->GetURL().path() == path) {
671      EXPECT_FALSE(host);
672      host = *iter;
673    }
674    num_hosts++;
675  }
676  EXPECT_EQ(expected_hosts, num_hosts);
677  return host;
678}
679
680std::string ExtensionBrowserTest::ExecuteScriptInBackgroundPage(
681    const std::string& extension_id,
682    const std::string& script) {
683  return extensions::browsertest_util::ExecuteScriptInBackgroundPage(
684      profile(), extension_id, script);
685}
686
687void ExtensionBrowserTest::Observe(
688    int type,
689    const content::NotificationSource& source,
690    const content::NotificationDetails& details) {
691  switch (type) {
692    case chrome::NOTIFICATION_EXTENSION_LOADED:
693      last_loaded_extension_id_ =
694          content::Details<const Extension>(details).ptr()->id();
695      VLOG(1) << "Got EXTENSION_LOADED notification.";
696      base::MessageLoopForUI::current()->Quit();
697      break;
698
699    case chrome::NOTIFICATION_CRX_INSTALLER_DONE:
700      VLOG(1) << "Got CRX_INSTALLER_DONE notification.";
701      {
702        const Extension* extension =
703            content::Details<const Extension>(details).ptr();
704        if (extension)
705          last_loaded_extension_id_ = extension->id();
706        else
707          last_loaded_extension_id_ = "";
708      }
709      ++crx_installers_done_observed_;
710      base::MessageLoopForUI::current()->Quit();
711      break;
712
713    case chrome::NOTIFICATION_EXTENSION_INSTALLED:
714      VLOG(1) << "Got EXTENSION_INSTALLED notification.";
715      ++extension_installs_observed_;
716      base::MessageLoopForUI::current()->Quit();
717      break;
718
719    case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR:
720      VLOG(1) << "Got EXTENSION_INSTALL_ERROR notification.";
721      base::MessageLoopForUI::current()->Quit();
722      break;
723
724    case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED:
725      VLOG(1) << "Got EXTENSION_PROCESS_TERMINATED notification.";
726      base::MessageLoopForUI::current()->Quit();
727      break;
728
729    case chrome::NOTIFICATION_EXTENSION_LOAD_ERROR:
730      VLOG(1) << "Got EXTENSION_LOAD_ERROR notification.";
731      ++extension_load_errors_observed_;
732      base::MessageLoopForUI::current()->Quit();
733      break;
734
735    case chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED: {
736      LocationBarTesting* location_bar =
737          browser()->window()->GetLocationBar()->GetLocationBarForTesting();
738      VLOG(1) << "Got EXTENSION_PAGE_ACTION_COUNT_CHANGED notification. Number "
739                 "of page actions: " << location_bar->PageActionCount();
740      if (location_bar->PageActionCount() ==
741          target_page_action_count_) {
742        target_page_action_count_ = -1;
743        base::MessageLoopForUI::current()->Quit();
744      }
745      break;
746    }
747
748    case chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED: {
749      LocationBarTesting* location_bar =
750          browser()->window()->GetLocationBar()->GetLocationBarForTesting();
751      VLOG(1) << "Got EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED notification. "
752                 "Number of visible page actions: "
753              << location_bar->PageActionVisibleCount();
754      if (location_bar->PageActionVisibleCount() ==
755          target_visible_page_action_count_) {
756        target_visible_page_action_count_ = -1;
757        base::MessageLoopForUI::current()->Quit();
758      }
759      break;
760    }
761
762    case content::NOTIFICATION_LOAD_STOP:
763    case content::NOTIFICATION_WEB_CONTENTS_DESTROYED:
764      VLOG(1) << "Got LOAD_STOP or WEB_CONTENTS_DESTROYED notification.";
765      base::MessageLoopForUI::current()->Quit();
766      break;
767
768    default:
769      NOTREACHED();
770      break;
771  }
772}
773