extension_browsertest.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
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/extensions/component_loader.h"
18#include "chrome/browser/extensions/crx_installer.h"
19#include "chrome/browser/extensions/extension_creator.h"
20#include "chrome/browser/extensions/extension_error_reporter.h"
21#include "chrome/browser/extensions/extension_host.h"
22#include "chrome/browser/extensions/extension_install_prompt.h"
23#include "chrome/browser/extensions/extension_service.h"
24#include "chrome/browser/extensions/extension_system.h"
25#include "chrome/browser/extensions/unpacked_installer.h"
26#include "chrome/browser/profiles/profile.h"
27#include "chrome/browser/profiles/profile_manager.h"
28#include "chrome/browser/ui/browser.h"
29#include "chrome/browser/ui/browser_window.h"
30#include "chrome/browser/ui/omnibox/location_bar.h"
31#include "chrome/browser/ui/tabs/tab_strip_model.h"
32#include "chrome/common/chrome_notification_types.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
175    if (flags & kFlagEnableIncognito) {
176      service->SetIsIncognitoEnabled(extension_id, true);
177      load_signal.Wait();
178      extension = service->GetExtensionById(extension_id, false);
179      CHECK(extension) << extension_id << " not found after reloading.";
180    }
181  }
182
183  {
184    content::WindowedNotificationObserver load_signal(
185        chrome::NOTIFICATION_EXTENSION_LOADED,
186        content::Source<Profile>(profile()));
187    CHECK(service->AllowFileAccess(extension));
188    if (!(flags & kFlagEnableFileAccess)) {
189      service->SetAllowFileAccess(extension, false);
190      load_signal.Wait();
191      extension = service->GetExtensionById(extension_id, false);
192      CHECK(extension) << extension_id << " not found after reloading.";
193    }
194  }
195
196  if (!WaitForExtensionViewsToLoad())
197    return NULL;
198
199  return extension;
200}
201
202const Extension* ExtensionBrowserTest::LoadExtension(
203    const base::FilePath& path) {
204  return LoadExtensionWithFlags(path, kFlagEnableFileAccess);
205}
206
207const Extension* ExtensionBrowserTest::LoadExtensionIncognito(
208    const base::FilePath& path) {
209  return LoadExtensionWithFlags(path,
210                                kFlagEnableFileAccess | kFlagEnableIncognito);
211}
212
213const Extension* ExtensionBrowserTest::LoadExtensionAsComponentWithManifest(
214    const base::FilePath& path,
215    const base::FilePath::CharType* manifest_relative_path) {
216  ExtensionService* service = extensions::ExtensionSystem::Get(
217      profile())->extension_service();
218
219  std::string manifest;
220  if (!file_util::ReadFileToString(path.Append(manifest_relative_path),
221                                   &manifest)) {
222    return NULL;
223  }
224
225  std::string extension_id = service->component_loader()->Add(manifest, path);
226  const Extension* extension = service->extensions()->GetByID(extension_id);
227  if (!extension)
228    return NULL;
229  last_loaded_extension_id_ = extension->id();
230  return extension;
231}
232
233const Extension* ExtensionBrowserTest::LoadExtensionAsComponent(
234    const base::FilePath& path) {
235  return LoadExtensionAsComponentWithManifest(path,
236                                              extensions::kManifestFilename);
237}
238
239base::FilePath ExtensionBrowserTest::PackExtension(
240    const base::FilePath& dir_path) {
241  base::FilePath crx_path = temp_dir_.path().AppendASCII("temp.crx");
242  if (!file_util::Delete(crx_path, false)) {
243    ADD_FAILURE() << "Failed to delete crx: " << crx_path.value();
244    return base::FilePath();
245  }
246
247  // Look for PEM files with the same name as the directory.
248  base::FilePath pem_path =
249      dir_path.ReplaceExtension(FILE_PATH_LITERAL(".pem"));
250  base::FilePath pem_path_out;
251
252  if (!file_util::PathExists(pem_path)) {
253    pem_path = base::FilePath();
254    pem_path_out = crx_path.DirName().AppendASCII("temp.pem");
255    if (!file_util::Delete(pem_path_out, false)) {
256      ADD_FAILURE() << "Failed to delete pem: " << pem_path_out.value();
257      return base::FilePath();
258    }
259  }
260
261  return PackExtensionWithOptions(dir_path, crx_path, pem_path, pem_path_out);
262}
263
264base::FilePath ExtensionBrowserTest::PackExtensionWithOptions(
265    const base::FilePath& dir_path,
266    const base::FilePath& crx_path,
267    const base::FilePath& pem_path,
268    const base::FilePath& pem_out_path) {
269  if (!file_util::PathExists(dir_path)) {
270    ADD_FAILURE() << "Extension dir not found: " << dir_path.value();
271    return base::FilePath();
272  }
273
274  if (!file_util::PathExists(pem_path) && pem_out_path.empty()) {
275    ADD_FAILURE() << "Must specify a PEM file or PEM output path";
276    return base::FilePath();
277  }
278
279  scoped_ptr<ExtensionCreator> creator(new ExtensionCreator());
280  if (!creator->Run(dir_path,
281                    crx_path,
282                    pem_path,
283                    pem_out_path,
284                    ExtensionCreator::kOverwriteCRX)) {
285    ADD_FAILURE() << "ExtensionCreator::Run() failed: "
286                  << creator->error_message();
287    return base::FilePath();
288  }
289
290  if (!file_util::PathExists(crx_path)) {
291    ADD_FAILURE() << crx_path.value() << " was not created.";
292    return base::FilePath();
293  }
294  return crx_path;
295}
296
297// This class is used to simulate an installation abort by the user.
298class MockAbortExtensionInstallPrompt : public ExtensionInstallPrompt {
299 public:
300  MockAbortExtensionInstallPrompt() : ExtensionInstallPrompt(NULL) {
301  }
302
303  // Simulate a user abort on an extension installation.
304  virtual void ConfirmInstall(
305      Delegate* delegate,
306      const Extension* extension,
307      const ShowDialogCallback& show_dialog_callback) OVERRIDE {
308    delegate->InstallUIAbort(true);
309    base::MessageLoopForUI::current()->Quit();
310  }
311
312  virtual void OnInstallSuccess(const Extension* extension,
313                                SkBitmap* icon) OVERRIDE {}
314
315  virtual void OnInstallFailure(
316      const extensions::CrxInstallerError& error) OVERRIDE {}
317};
318
319class MockAutoConfirmExtensionInstallPrompt : public ExtensionInstallPrompt {
320 public:
321  explicit MockAutoConfirmExtensionInstallPrompt(
322      content::WebContents* web_contents)
323    : ExtensionInstallPrompt(web_contents) {}
324
325  // Proceed without confirmation prompt.
326  virtual void ConfirmInstall(
327      Delegate* delegate,
328      const Extension* extension,
329      const ShowDialogCallback& show_dialog_callback) OVERRIDE {
330    delegate->InstallUIProceed();
331  }
332};
333
334const Extension* ExtensionBrowserTest::InstallExtensionFromWebstore(
335    const base::FilePath& path,
336    int expected_change) {
337  return InstallOrUpdateExtension(std::string(),
338                                  path,
339                                  INSTALL_UI_TYPE_NONE,
340                                  expected_change,
341                                  Manifest::INTERNAL,
342                                  browser(),
343                                  true);
344}
345
346const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
347    const std::string& id,
348    const base::FilePath& path,
349    InstallUIType ui_type,
350    int expected_change) {
351  return InstallOrUpdateExtension(id, path, ui_type, expected_change,
352                                  Manifest::INTERNAL, browser(), false);
353}
354
355const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
356    const std::string& id,
357    const base::FilePath& path,
358    InstallUIType ui_type,
359    int expected_change,
360    Browser* browser,
361    bool from_webstore) {
362  return InstallOrUpdateExtension(id, path, ui_type, expected_change,
363                                  Manifest::INTERNAL, browser, from_webstore);
364}
365
366const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
367    const std::string& id,
368    const base::FilePath& path,
369    InstallUIType ui_type,
370    int expected_change,
371    Manifest::Location install_source) {
372  return InstallOrUpdateExtension(id, path, ui_type, expected_change,
373                                  install_source, browser(), false);
374}
375
376const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
377    const std::string& id,
378    const base::FilePath& path,
379    InstallUIType ui_type,
380    int expected_change,
381    Manifest::Location install_source,
382    Browser* browser,
383    bool from_webstore) {
384  ExtensionService* service = profile()->GetExtensionService();
385  service->set_show_extensions_prompts(false);
386  size_t num_before = service->extensions()->size();
387
388  {
389    ExtensionInstallPrompt* install_ui = NULL;
390    if (ui_type == INSTALL_UI_TYPE_CANCEL) {
391      install_ui = new MockAbortExtensionInstallPrompt();
392    } else if (ui_type == INSTALL_UI_TYPE_NORMAL) {
393      install_ui = new ExtensionInstallPrompt(
394          browser->tab_strip_model()->GetActiveWebContents());
395    } else if (ui_type == INSTALL_UI_TYPE_AUTO_CONFIRM) {
396      install_ui = new MockAutoConfirmExtensionInstallPrompt(
397          browser->tab_strip_model()->GetActiveWebContents());
398    }
399
400    // TODO(tessamac): Update callers to always pass an unpacked extension
401    //                 and then always pack the extension here.
402    base::FilePath crx_path = path;
403    if (crx_path.Extension() != FILE_PATH_LITERAL(".crx")) {
404      crx_path = PackExtension(path);
405    }
406    if (crx_path.empty())
407      return NULL;
408
409    scoped_refptr<extensions::CrxInstaller> installer(
410        extensions::CrxInstaller::Create(service, install_ui));
411    installer->set_expected_id(id);
412    installer->set_is_gallery_install(from_webstore);
413    installer->set_install_source(install_source);
414    installer->set_install_wait_for_idle(false);
415    if (!from_webstore) {
416      installer->set_off_store_install_allow_reason(
417          extensions::CrxInstaller::OffStoreInstallAllowedInTest);
418    }
419
420    content::NotificationRegistrar registrar;
421    registrar.Add(this, chrome::NOTIFICATION_CRX_INSTALLER_DONE,
422                  content::Source<extensions::CrxInstaller>(installer.get()));
423
424    installer->InstallCrx(crx_path);
425
426    content::RunMessageLoop();
427  }
428
429  size_t num_after = service->extensions()->size();
430  EXPECT_EQ(num_before + expected_change, num_after);
431  if (num_before + expected_change != num_after) {
432    VLOG(1) << "Num extensions before: " << base::IntToString(num_before)
433            << " num after: " << base::IntToString(num_after)
434            << " Installed extensions follow:";
435
436    for (ExtensionSet::const_iterator it = service->extensions()->begin();
437         it != service->extensions()->end(); ++it)
438      VLOG(1) << "  " << (*it)->id();
439
440    VLOG(1) << "Errors follow:";
441    const std::vector<string16>* errors =
442        ExtensionErrorReporter::GetInstance()->GetErrors();
443    for (std::vector<string16>::const_iterator iter = errors->begin();
444         iter != errors->end(); ++iter)
445      VLOG(1) << *iter;
446
447    return NULL;
448  }
449
450  if (!WaitForExtensionViewsToLoad())
451    return NULL;
452  return service->GetExtensionById(last_loaded_extension_id_, false);
453}
454
455void ExtensionBrowserTest::ReloadExtension(const std::string& extension_id) {
456  ExtensionService* service = extensions::ExtensionSystem::Get(
457      profile())->extension_service();
458  service->ReloadExtension(extension_id);
459  ui_test_utils::RegisterAndWait(this,
460                                 chrome::NOTIFICATION_EXTENSION_LOADED,
461                                 content::NotificationService::AllSources());
462}
463
464void ExtensionBrowserTest::UnloadExtension(const std::string& extension_id) {
465  ExtensionService* service = extensions::ExtensionSystem::Get(
466      profile())->extension_service();
467  service->UnloadExtension(extension_id, extension_misc::UNLOAD_REASON_DISABLE);
468}
469
470void ExtensionBrowserTest::UninstallExtension(const std::string& extension_id) {
471  ExtensionService* service = extensions::ExtensionSystem::Get(
472      profile())->extension_service();
473  service->UninstallExtension(extension_id, false, NULL);
474}
475
476void ExtensionBrowserTest::DisableExtension(const std::string& extension_id) {
477  ExtensionService* service = extensions::ExtensionSystem::Get(
478      profile())->extension_service();
479  service->DisableExtension(extension_id, Extension::DISABLE_USER_ACTION);
480}
481
482void ExtensionBrowserTest::EnableExtension(const std::string& extension_id) {
483  ExtensionService* service = extensions::ExtensionSystem::Get(
484      profile())->extension_service();
485  service->EnableExtension(extension_id);
486}
487
488bool ExtensionBrowserTest::WaitForPageActionCountChangeTo(int count) {
489  LocationBarTesting* location_bar =
490      browser()->window()->GetLocationBar()->GetLocationBarForTesting();
491  if (location_bar->PageActionCount() != count) {
492    target_page_action_count_ = count;
493    ui_test_utils::RegisterAndWait(this,
494        chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED,
495        content::NotificationService::AllSources());
496  }
497  return location_bar->PageActionCount() == count;
498}
499
500bool ExtensionBrowserTest::WaitForPageActionVisibilityChangeTo(int count) {
501  LocationBarTesting* location_bar =
502      browser()->window()->GetLocationBar()->GetLocationBarForTesting();
503  if (location_bar->PageActionVisibleCount() != count) {
504    target_visible_page_action_count_ = count;
505    ui_test_utils::RegisterAndWait(this,
506        chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED,
507        content::NotificationService::AllSources());
508  }
509  return location_bar->PageActionVisibleCount() == count;
510}
511
512bool ExtensionBrowserTest::WaitForExtensionViewsToLoad() {
513  // Wait for all the extension render view hosts that exist to finish loading.
514  content::NotificationRegistrar registrar;
515  registrar.Add(this, content::NOTIFICATION_LOAD_STOP,
516                content::NotificationService::AllSources());
517
518  ExtensionProcessManager* manager =
519      extensions::ExtensionSystem::Get(profile())->process_manager();
520  ExtensionProcessManager::ViewSet all_views = manager->GetAllViews();
521  for (ExtensionProcessManager::ViewSet::const_iterator iter =
522           all_views.begin();
523       iter != all_views.end();) {
524    if (!(*iter)->IsLoading()) {
525      ++iter;
526    } else {
527      content::RunMessageLoop();
528
529      // Test activity may have modified the set of extension processes during
530      // message processing, so re-start the iteration to catch added/removed
531      // processes.
532      all_views = manager->GetAllViews();
533      iter = all_views.begin();
534    }
535  }
536  return true;
537}
538
539bool ExtensionBrowserTest::WaitForExtensionInstall() {
540  int before = extension_installs_observed_;
541  ui_test_utils::RegisterAndWait(this,
542                                 chrome::NOTIFICATION_EXTENSION_INSTALLED,
543                                 content::NotificationService::AllSources());
544  return extension_installs_observed_ == (before + 1);
545}
546
547bool ExtensionBrowserTest::WaitForExtensionInstallError() {
548  int before = extension_installs_observed_;
549  ui_test_utils::RegisterAndWait(this,
550                                 chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
551                                 content::NotificationService::AllSources());
552  return extension_installs_observed_ == before;
553}
554
555void ExtensionBrowserTest::WaitForExtensionLoad() {
556  ui_test_utils::RegisterAndWait(this, chrome::NOTIFICATION_EXTENSION_LOADED,
557                                 content::NotificationService::AllSources());
558  WaitForExtensionViewsToLoad();
559}
560
561bool ExtensionBrowserTest::WaitForExtensionLoadError() {
562  int before = extension_load_errors_observed_;
563  ui_test_utils::RegisterAndWait(this,
564                                 chrome::NOTIFICATION_EXTENSION_LOAD_ERROR,
565                                 content::NotificationService::AllSources());
566  return extension_load_errors_observed_ != before;
567}
568
569bool ExtensionBrowserTest::WaitForExtensionCrash(
570    const std::string& extension_id) {
571  ExtensionService* service = extensions::ExtensionSystem::Get(
572      profile())->extension_service();
573
574  if (!service->GetExtensionById(extension_id, true)) {
575    // The extension is already unloaded, presumably due to a crash.
576    return true;
577  }
578  ui_test_utils::RegisterAndWait(
579      this, chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED,
580      content::NotificationService::AllSources());
581  return (service->GetExtensionById(extension_id, true) == NULL);
582}
583
584bool ExtensionBrowserTest::WaitForCrxInstallerDone() {
585  int before = crx_installers_done_observed_;
586  ui_test_utils::RegisterAndWait(this,
587                                 chrome::NOTIFICATION_CRX_INSTALLER_DONE,
588                                 content::NotificationService::AllSources());
589  return crx_installers_done_observed_ == (before + 1);
590}
591
592void ExtensionBrowserTest::OpenWindow(content::WebContents* contents,
593                                      const GURL& url,
594                                      bool newtab_process_should_equal_opener,
595                                      content::WebContents** newtab_result) {
596  content::WindowedNotificationObserver observer(
597      content::NOTIFICATION_LOAD_STOP,
598      content::NotificationService::AllSources());
599  ASSERT_TRUE(content::ExecuteScript(contents,
600                                     "window.open('" + url.spec() + "');"));
601
602  // The above window.open call is not user-initiated, so it will create
603  // a popup window instead of a new tab in current window.
604  // The stop notification will come from the new tab.
605  observer.Wait();
606  content::NavigationController* controller =
607      content::Source<content::NavigationController>(observer.source()).ptr();
608  content::WebContents* newtab = controller->GetWebContents();
609  ASSERT_TRUE(newtab);
610  EXPECT_EQ(url, controller->GetLastCommittedEntry()->GetURL());
611  if (newtab_process_should_equal_opener)
612    EXPECT_EQ(contents->GetRenderProcessHost(), newtab->GetRenderProcessHost());
613  else
614    EXPECT_NE(contents->GetRenderProcessHost(), newtab->GetRenderProcessHost());
615
616  if (newtab_result)
617    *newtab_result = newtab;
618}
619
620void ExtensionBrowserTest::NavigateInRenderer(content::WebContents* contents,
621                                              const GURL& url) {
622  bool result = false;
623  content::WindowedNotificationObserver observer(
624      content::NOTIFICATION_LOAD_STOP,
625      content::NotificationService::AllSources());
626  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
627      contents,
628      "window.addEventListener('unload', function() {"
629      "    window.domAutomationController.send(true);"
630      "}, false);"
631      "window.location = '" + url.spec() + "';",
632      &result));
633  ASSERT_TRUE(result);
634  observer.Wait();
635  EXPECT_EQ(url, contents->GetController().GetLastCommittedEntry()->GetURL());
636}
637
638extensions::ExtensionHost* ExtensionBrowserTest::FindHostWithPath(
639    ExtensionProcessManager* manager,
640    const std::string& path,
641    int expected_hosts) {
642  extensions::ExtensionHost* host = NULL;
643  int num_hosts = 0;
644  ExtensionProcessManager::ExtensionHostSet background_hosts =
645      manager->background_hosts();
646  for (ExtensionProcessManager::const_iterator iter = background_hosts.begin();
647       iter != background_hosts.end(); ++iter) {
648    if ((*iter)->GetURL().path() == path) {
649      EXPECT_FALSE(host);
650      host = *iter;
651    }
652    num_hosts++;
653  }
654  EXPECT_EQ(expected_hosts, num_hosts);
655  return host;
656}
657
658void ExtensionBrowserTest::Observe(
659    int type,
660    const content::NotificationSource& source,
661    const content::NotificationDetails& details) {
662  switch (type) {
663    case chrome::NOTIFICATION_EXTENSION_LOADED:
664      last_loaded_extension_id_ =
665          content::Details<const Extension>(details).ptr()->id();
666      VLOG(1) << "Got EXTENSION_LOADED notification.";
667      base::MessageLoopForUI::current()->Quit();
668      break;
669
670    case chrome::NOTIFICATION_CRX_INSTALLER_DONE:
671      VLOG(1) << "Got CRX_INSTALLER_DONE notification.";
672      {
673        const Extension* extension =
674            content::Details<const Extension>(details).ptr();
675        if (extension)
676          last_loaded_extension_id_ = extension->id();
677        else
678          last_loaded_extension_id_ = "";
679      }
680      ++crx_installers_done_observed_;
681      base::MessageLoopForUI::current()->Quit();
682      break;
683
684    case chrome::NOTIFICATION_EXTENSION_INSTALLED:
685      VLOG(1) << "Got EXTENSION_INSTALLED notification.";
686      ++extension_installs_observed_;
687      base::MessageLoopForUI::current()->Quit();
688      break;
689
690    case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR:
691      VLOG(1) << "Got EXTENSION_INSTALL_ERROR notification.";
692      base::MessageLoopForUI::current()->Quit();
693      break;
694
695    case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED:
696      VLOG(1) << "Got EXTENSION_PROCESS_TERMINATED notification.";
697      base::MessageLoopForUI::current()->Quit();
698      break;
699
700    case chrome::NOTIFICATION_EXTENSION_LOAD_ERROR:
701      VLOG(1) << "Got EXTENSION_LOAD_ERROR notification.";
702      ++extension_load_errors_observed_;
703      base::MessageLoopForUI::current()->Quit();
704      break;
705
706    case chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED: {
707      LocationBarTesting* location_bar =
708          browser()->window()->GetLocationBar()->GetLocationBarForTesting();
709      VLOG(1) << "Got EXTENSION_PAGE_ACTION_COUNT_CHANGED notification. Number "
710                 "of page actions: " << location_bar->PageActionCount();
711      if (location_bar->PageActionCount() ==
712          target_page_action_count_) {
713        target_page_action_count_ = -1;
714        base::MessageLoopForUI::current()->Quit();
715      }
716      break;
717    }
718
719    case chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED: {
720      LocationBarTesting* location_bar =
721          browser()->window()->GetLocationBar()->GetLocationBarForTesting();
722      VLOG(1) << "Got EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED notification. "
723                 "Number of visible page actions: "
724              << location_bar->PageActionVisibleCount();
725      if (location_bar->PageActionVisibleCount() ==
726          target_visible_page_action_count_) {
727        target_visible_page_action_count_ = -1;
728        base::MessageLoopForUI::current()->Quit();
729      }
730      break;
731    }
732
733    case content::NOTIFICATION_LOAD_STOP:
734      VLOG(1) << "Got LOAD_STOP notification.";
735      base::MessageLoopForUI::current()->Quit();
736      break;
737
738    default:
739      NOTREACHED();
740      break;
741  }
742}
743