extension_browsertest.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_browsertest.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/file_util.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/files/file_path.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/files/scoped_temp_dir.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/path_service.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/strings/string_number_conversions.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/strings/stringprintf.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/chrome_notification_types.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/browsertest_util.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/component_loader.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/crx_installer.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_creator.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_error_reporter.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_host.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_install_prompt.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_service.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_util.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/unpacked_installer.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/updater/extension_cache_fake.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile_manager.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser_window.h"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h"
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_paths.h"
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_switches.h"
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_version_info.h"
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/test/base/ui_test_utils.h"
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/navigation_controller.h"
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/navigation_entry.h"
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_registrar.h"
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_service.h"
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_view_host.h"
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/test/browser_test_utils.h"
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "extensions/browser/extension_system.h"
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "extensions/common/constants.h"
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "extensions/common/extension_set.h"
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sync/api/string_ordinal.h"
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_CHROMEOS)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chromeos/chromeos_switches.h"
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::Extension;
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::ExtensionCreator;
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::FeatureSwitch;
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::Manifest;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionBrowserTest::ExtensionBrowserTest()
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : loaded_(false),
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      installed_(false),
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_CHROMEOS)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      set_chromeos_user_(true),
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      current_channel_(chrome::VersionInfo::CHANNEL_DEV),
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      override_prompt_for_external_extensions_(
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          FeatureSwitch::prompt_for_external_extensions(),
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          false),
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile_(NULL) {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionBrowserTest::~ExtensionBrowserTest() {
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Profile* ExtensionBrowserTest::profile() {
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!profile_) {
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (browser())
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile_ = browser()->profile();
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile_ = ProfileManager::GetActiveUserProfile();
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return profile_;
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const Extension* ExtensionBrowserTest::GetExtensionByPath(
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const extensions::ExtensionSet* extensions, const base::FilePath& path) {
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::FilePath extension_path = base::MakeAbsoluteFilePath(path);
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(!extension_path.empty());
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (extensions::ExtensionSet::const_iterator iter = extensions->begin();
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       iter != extensions->end(); ++iter) {
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ((*iter)->path() == extension_path) {
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return iter->get();
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return NULL;
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionBrowserTest::SetUp() {
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_extension_cache_.reset(new extensions::ExtensionCacheFake());
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InProcessBrowserTest::SetUp();
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionBrowserTest::SetUpCommandLine(CommandLine* command_line) {
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_);
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_data_dir_ = test_data_dir_.AppendASCII("extensions");
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  observer_.reset(new ExtensionTestNotificationObserver(browser()));
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_CHROMEOS)
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (set_chromeos_user_) {
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // This makes sure that we create the Default profile first, with no
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // ExtensionService and then the real profile with one, as we do when
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // running on chromeos.
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    command_line->AppendSwitchASCII(chromeos::switches::kLoginUser,
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    "TestUser@gmail.com");
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionBrowserTest::SetUpOnMainThread() {
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InProcessBrowserTest::SetUpOnMainThread();
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  observer_.reset(new ExtensionTestNotificationObserver(browser()));
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const Extension* ExtensionBrowserTest::LoadExtensionWithFlags(
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const base::FilePath& path, int flags) {
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionService* service = extensions::ExtensionSystem::Get(
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile())->extension_service();
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  {
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    observer_->Watch(chrome::NOTIFICATION_EXTENSION_LOADED,
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     content::NotificationService::AllSources());
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scoped_refptr<extensions::UnpackedInstaller> installer(
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        extensions::UnpackedInstaller::Create(service));
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    installer->set_prompt_for_plugins(false);
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    installer->set_require_modern_manifest_version(
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (flags & kFlagAllowOldManifestVersions) == 0);
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    installer->Load(path);
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    observer_->Wait();
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Find the loaded extension by its path. See crbug.com/59531 for why
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // we cannot just use last_loaded_extension_id().
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const Extension* extension = GetExtensionByPath(service->extensions(), path);
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!extension)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!(flags & kFlagIgnoreManifestWarnings)) {
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::vector<extensions::InstallWarning>& install_warnings =
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        extension->install_warnings();
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!install_warnings.empty()) {
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string install_warnings_message = base::StringPrintf(
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "Unexpected warnings when loading test extension %s:\n",
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          path.AsUTF8Unsafe().c_str());
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (std::vector<extensions::InstallWarning>::const_iterator it =
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          install_warnings.begin(); it != install_warnings.end(); ++it) {
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        install_warnings_message += "  " + it->message + "\n";
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      EXPECT_TRUE(extension->install_warnings().empty()) <<
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          install_warnings_message;
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return NULL;
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const std::string extension_id = extension->id();
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The call to OnExtensionInstalled ensures the other extension prefs
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // are set up with the defaults.
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service->extension_prefs()->OnExtensionInstalled(
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      extension,
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Extension::ENABLED,
176      false,
177      syncer::StringOrdinal::CreateInitialOrdinal());
178
179  // Toggling incognito or file access will reload the extension, so wait for
180  // the reload and grab the new extension instance. The default state is
181  // incognito disabled and file access enabled, so we don't wait in those
182  // cases.
183  {
184    content::WindowedNotificationObserver load_signal(
185        chrome::NOTIFICATION_EXTENSION_LOADED,
186        content::Source<Profile>(profile()));
187    CHECK(!extensions::util::IsIncognitoEnabled(extension_id, profile()) ||
188          extension->force_incognito_enabled());
189
190    if (flags & kFlagEnableIncognito) {
191      extensions::util::SetIsIncognitoEnabled(extension_id, profile(), true);
192      load_signal.Wait();
193      extension = service->GetExtensionById(extension_id, false);
194      CHECK(extension) << extension_id << " not found after reloading.";
195    }
196  }
197
198  {
199    content::WindowedNotificationObserver load_signal(
200        chrome::NOTIFICATION_EXTENSION_LOADED,
201        content::Source<Profile>(profile()));
202    CHECK(extensions::util::AllowFileAccess(extension_id, profile()));
203    if (!(flags & kFlagEnableFileAccess)) {
204      extensions::util::SetAllowFileAccess(extension_id, profile(), false);
205      load_signal.Wait();
206      extension = service->GetExtensionById(extension_id, false);
207      CHECK(extension) << extension_id << " not found after reloading.";
208    }
209  }
210
211  if (!observer_->WaitForExtensionViewsToLoad())
212    return NULL;
213
214  return extension;
215}
216
217const Extension* ExtensionBrowserTest::LoadExtension(
218    const base::FilePath& path) {
219  return LoadExtensionWithFlags(path, kFlagEnableFileAccess);
220}
221
222const Extension* ExtensionBrowserTest::LoadExtensionIncognito(
223    const base::FilePath& path) {
224  return LoadExtensionWithFlags(path,
225                                kFlagEnableFileAccess | kFlagEnableIncognito);
226}
227
228const Extension* ExtensionBrowserTest::LoadExtensionAsComponentWithManifest(
229    const base::FilePath& path,
230    const base::FilePath::CharType* manifest_relative_path) {
231  ExtensionService* service = extensions::ExtensionSystem::Get(
232      profile())->extension_service();
233
234  std::string manifest;
235  if (!base::ReadFileToString(path.Append(manifest_relative_path), &manifest)) {
236    return NULL;
237  }
238
239  std::string extension_id = service->component_loader()->Add(manifest, path);
240  const Extension* extension = service->extensions()->GetByID(extension_id);
241  if (!extension)
242    return NULL;
243  observer_->set_last_loaded_extension_id(extension->id());
244  return extension;
245}
246
247const Extension* ExtensionBrowserTest::LoadExtensionAsComponent(
248    const base::FilePath& path) {
249  return LoadExtensionAsComponentWithManifest(path,
250                                              extensions::kManifestFilename);
251}
252
253base::FilePath ExtensionBrowserTest::PackExtension(
254    const base::FilePath& dir_path) {
255  base::FilePath crx_path = temp_dir_.path().AppendASCII("temp.crx");
256  if (!base::DeleteFile(crx_path, false)) {
257    ADD_FAILURE() << "Failed to delete crx: " << crx_path.value();
258    return base::FilePath();
259  }
260
261  // Look for PEM files with the same name as the directory.
262  base::FilePath pem_path =
263      dir_path.ReplaceExtension(FILE_PATH_LITERAL(".pem"));
264  base::FilePath pem_path_out;
265
266  if (!base::PathExists(pem_path)) {
267    pem_path = base::FilePath();
268    pem_path_out = crx_path.DirName().AppendASCII("temp.pem");
269    if (!base::DeleteFile(pem_path_out, false)) {
270      ADD_FAILURE() << "Failed to delete pem: " << pem_path_out.value();
271      return base::FilePath();
272    }
273  }
274
275  return PackExtensionWithOptions(dir_path, crx_path, pem_path, pem_path_out);
276}
277
278base::FilePath ExtensionBrowserTest::PackExtensionWithOptions(
279    const base::FilePath& dir_path,
280    const base::FilePath& crx_path,
281    const base::FilePath& pem_path,
282    const base::FilePath& pem_out_path) {
283  if (!base::PathExists(dir_path)) {
284    ADD_FAILURE() << "Extension dir not found: " << dir_path.value();
285    return base::FilePath();
286  }
287
288  if (!base::PathExists(pem_path) && pem_out_path.empty()) {
289    ADD_FAILURE() << "Must specify a PEM file or PEM output path";
290    return base::FilePath();
291  }
292
293  scoped_ptr<ExtensionCreator> creator(new ExtensionCreator());
294  if (!creator->Run(dir_path,
295                    crx_path,
296                    pem_path,
297                    pem_out_path,
298                    ExtensionCreator::kOverwriteCRX)) {
299    ADD_FAILURE() << "ExtensionCreator::Run() failed: "
300                  << creator->error_message();
301    return base::FilePath();
302  }
303
304  if (!base::PathExists(crx_path)) {
305    ADD_FAILURE() << crx_path.value() << " was not created.";
306    return base::FilePath();
307  }
308  return crx_path;
309}
310
311// This class is used to simulate an installation abort by the user.
312class MockAbortExtensionInstallPrompt : public ExtensionInstallPrompt {
313 public:
314  MockAbortExtensionInstallPrompt() : ExtensionInstallPrompt(NULL) {
315  }
316
317  // Simulate a user abort on an extension installation.
318  virtual void ConfirmInstall(
319      Delegate* delegate,
320      const Extension* extension,
321      const ShowDialogCallback& show_dialog_callback) OVERRIDE {
322    delegate->InstallUIAbort(true);
323    base::MessageLoopForUI::current()->Quit();
324  }
325
326  virtual void OnInstallSuccess(const Extension* extension,
327                                SkBitmap* icon) OVERRIDE {}
328
329  virtual void OnInstallFailure(
330      const extensions::CrxInstallerError& error) OVERRIDE {}
331};
332
333class MockAutoConfirmExtensionInstallPrompt : public ExtensionInstallPrompt {
334 public:
335  explicit MockAutoConfirmExtensionInstallPrompt(
336      content::WebContents* web_contents)
337    : ExtensionInstallPrompt(web_contents) {}
338
339  // Proceed without confirmation prompt.
340  virtual void ConfirmInstall(
341      Delegate* delegate,
342      const Extension* extension,
343      const ShowDialogCallback& show_dialog_callback) OVERRIDE {
344    delegate->InstallUIProceed();
345  }
346};
347
348const Extension* ExtensionBrowserTest::UpdateExtensionWaitForIdle(
349    const std::string& id,
350    const base::FilePath& path,
351    int expected_change) {
352  return InstallOrUpdateExtension(id,
353                                  path,
354                                  INSTALL_UI_TYPE_NONE,
355                                  expected_change,
356                                  Manifest::INTERNAL,
357                                  browser(),
358                                  Extension::NO_FLAGS,
359                                  true);
360}
361
362const Extension* ExtensionBrowserTest::InstallExtensionFromWebstore(
363    const base::FilePath& path,
364    int expected_change) {
365  return InstallOrUpdateExtension(std::string(),
366                                  path,
367                                  INSTALL_UI_TYPE_NONE,
368                                  expected_change,
369                                  Manifest::INTERNAL,
370                                  browser(),
371                                  Extension::FROM_WEBSTORE,
372                                  false);
373}
374
375const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
376    const std::string& id,
377    const base::FilePath& path,
378    InstallUIType ui_type,
379    int expected_change) {
380  return InstallOrUpdateExtension(id, path, ui_type, expected_change,
381      Manifest::INTERNAL, browser(), Extension::NO_FLAGS, false);
382}
383
384const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
385    const std::string& id,
386    const base::FilePath& path,
387    InstallUIType ui_type,
388    int expected_change,
389    Browser* browser,
390    Extension::InitFromValueFlags creation_flags) {
391  return InstallOrUpdateExtension(id, path, ui_type, expected_change,
392                                  Manifest::INTERNAL, browser, creation_flags,
393                                  false);
394}
395
396const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
397    const std::string& id,
398    const base::FilePath& path,
399    InstallUIType ui_type,
400    int expected_change,
401    Manifest::Location install_source) {
402  return InstallOrUpdateExtension(id, path, ui_type, expected_change,
403      install_source, browser(), Extension::NO_FLAGS, false);
404}
405
406const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
407    const std::string& id,
408    const base::FilePath& path,
409    InstallUIType ui_type,
410    int expected_change,
411    Manifest::Location install_source,
412    Browser* browser,
413    Extension::InitFromValueFlags creation_flags,
414    bool wait_for_idle) {
415  ExtensionService* service = profile()->GetExtensionService();
416  service->set_show_extensions_prompts(false);
417  size_t num_before = service->extensions()->size();
418
419  {
420    scoped_ptr<ExtensionInstallPrompt> install_ui;
421    if (ui_type == INSTALL_UI_TYPE_CANCEL) {
422      install_ui.reset(new MockAbortExtensionInstallPrompt());
423    } else if (ui_type == INSTALL_UI_TYPE_NORMAL) {
424      install_ui.reset(new ExtensionInstallPrompt(
425          browser->tab_strip_model()->GetActiveWebContents()));
426    } else if (ui_type == INSTALL_UI_TYPE_AUTO_CONFIRM) {
427      install_ui.reset(new MockAutoConfirmExtensionInstallPrompt(
428          browser->tab_strip_model()->GetActiveWebContents()));
429    }
430
431    // TODO(tessamac): Update callers to always pass an unpacked extension
432    //                 and then always pack the extension here.
433    base::FilePath crx_path = path;
434    if (crx_path.Extension() != FILE_PATH_LITERAL(".crx")) {
435      crx_path = PackExtension(path);
436    }
437    if (crx_path.empty())
438      return NULL;
439
440    scoped_refptr<extensions::CrxInstaller> installer(
441        extensions::CrxInstaller::Create(service, install_ui.Pass()));
442    installer->set_expected_id(id);
443    installer->set_creation_flags(creation_flags);
444    installer->set_install_source(install_source);
445    installer->set_install_wait_for_idle(wait_for_idle);
446    if (!installer->is_gallery_install()) {
447      installer->set_off_store_install_allow_reason(
448          extensions::CrxInstaller::OffStoreInstallAllowedInTest);
449    }
450
451    observer_->Watch(
452        chrome::NOTIFICATION_CRX_INSTALLER_DONE,
453        content::Source<extensions::CrxInstaller>(installer.get()));
454
455    installer->InstallCrx(crx_path);
456
457    observer_->Wait();
458  }
459
460  size_t num_after = service->extensions()->size();
461  EXPECT_EQ(num_before + expected_change, num_after);
462  if (num_before + expected_change != num_after) {
463    VLOG(1) << "Num extensions before: " << base::IntToString(num_before)
464            << " num after: " << base::IntToString(num_after)
465            << " Installed extensions follow:";
466
467    for (extensions::ExtensionSet::const_iterator it =
468             service->extensions()->begin();
469         it != service->extensions()->end(); ++it)
470      VLOG(1) << "  " << (*it)->id();
471
472    VLOG(1) << "Errors follow:";
473    const std::vector<base::string16>* errors =
474        ExtensionErrorReporter::GetInstance()->GetErrors();
475    for (std::vector<base::string16>::const_iterator iter = errors->begin();
476         iter != errors->end(); ++iter)
477      VLOG(1) << *iter;
478
479    return NULL;
480  }
481
482  if (!observer_->WaitForExtensionViewsToLoad())
483    return NULL;
484  return service->GetExtensionById(last_loaded_extension_id(), false);
485}
486
487void ExtensionBrowserTest::ReloadExtension(const std::string extension_id) {
488  observer_->Watch(chrome::NOTIFICATION_EXTENSION_LOADED,
489                content::NotificationService::AllSources());
490
491  ExtensionService* service =
492      extensions::ExtensionSystem::Get(profile())->extension_service();
493  service->ReloadExtension(extension_id);
494
495  observer_->Wait();
496  observer_->WaitForExtensionViewsToLoad();
497}
498
499void ExtensionBrowserTest::UnloadExtension(const std::string& extension_id) {
500  ExtensionService* service = extensions::ExtensionSystem::Get(
501      profile())->extension_service();
502  service->UnloadExtension(extension_id,
503                           extensions::UnloadedExtensionInfo::REASON_DISABLE);
504}
505
506void ExtensionBrowserTest::UninstallExtension(const std::string& extension_id) {
507  ExtensionService* service = extensions::ExtensionSystem::Get(
508      profile())->extension_service();
509  service->UninstallExtension(extension_id, false, NULL);
510}
511
512void ExtensionBrowserTest::DisableExtension(const std::string& extension_id) {
513  ExtensionService* service = extensions::ExtensionSystem::Get(
514      profile())->extension_service();
515  service->DisableExtension(extension_id, Extension::DISABLE_USER_ACTION);
516}
517
518void ExtensionBrowserTest::EnableExtension(const std::string& extension_id) {
519  ExtensionService* service = extensions::ExtensionSystem::Get(
520      profile())->extension_service();
521  service->EnableExtension(extension_id);
522}
523
524void ExtensionBrowserTest::OpenWindow(content::WebContents* contents,
525                                      const GURL& url,
526                                      bool newtab_process_should_equal_opener,
527                                      content::WebContents** newtab_result) {
528  content::WindowedNotificationObserver windowed_observer(
529      content::NOTIFICATION_LOAD_STOP,
530      content::NotificationService::AllSources());
531  ASSERT_TRUE(content::ExecuteScript(contents,
532                                     "window.open('" + url.spec() + "');"));
533
534  // The above window.open call is not user-initiated, so it will create
535  // a popup window instead of a new tab in current window.
536  // The stop notification will come from the new tab.
537  windowed_observer.Wait();
538  content::NavigationController* controller =
539      content::Source<content::NavigationController>(
540          windowed_observer.source()).ptr();
541  content::WebContents* newtab = controller->GetWebContents();
542  ASSERT_TRUE(newtab);
543  EXPECT_EQ(url, controller->GetLastCommittedEntry()->GetURL());
544  if (newtab_process_should_equal_opener)
545    EXPECT_EQ(contents->GetRenderProcessHost(), newtab->GetRenderProcessHost());
546  else
547    EXPECT_NE(contents->GetRenderProcessHost(), newtab->GetRenderProcessHost());
548
549  if (newtab_result)
550    *newtab_result = newtab;
551}
552
553void ExtensionBrowserTest::NavigateInRenderer(content::WebContents* contents,
554                                              const GURL& url) {
555  bool result = false;
556  content::WindowedNotificationObserver windowed_observer(
557      content::NOTIFICATION_LOAD_STOP,
558      content::NotificationService::AllSources());
559  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
560      contents,
561      "window.addEventListener('unload', function() {"
562      "    window.domAutomationController.send(true);"
563      "}, false);"
564      "window.location = '" + url.spec() + "';",
565      &result));
566  ASSERT_TRUE(result);
567  windowed_observer.Wait();
568  EXPECT_EQ(url, contents->GetController().GetLastCommittedEntry()->GetURL());
569}
570
571extensions::ExtensionHost* ExtensionBrowserTest::FindHostWithPath(
572    extensions::ProcessManager* manager,
573    const std::string& path,
574    int expected_hosts) {
575  extensions::ExtensionHost* host = NULL;
576  int num_hosts = 0;
577  extensions::ProcessManager::ExtensionHostSet background_hosts =
578      manager->background_hosts();
579  for (extensions::ProcessManager::const_iterator iter =
580           background_hosts.begin();
581       iter != background_hosts.end();
582       ++iter) {
583    if ((*iter)->GetURL().path() == path) {
584      EXPECT_FALSE(host);
585      host = *iter;
586    }
587    num_hosts++;
588  }
589  EXPECT_EQ(expected_hosts, num_hosts);
590  return host;
591}
592
593std::string ExtensionBrowserTest::ExecuteScriptInBackgroundPage(
594    const std::string& extension_id,
595    const std::string& script) {
596  return extensions::browsertest_util::ExecuteScriptInBackgroundPage(
597      profile(), extension_id, script);
598}
599