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