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