NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome / browser / extensions / extension_browsertest.cc
blobca731b3450318f15f5cde6bc7c4ea7cc5945ffa6
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.
5 #include "chrome/browser/extensions/extension_browsertest.h"
7 #include <vector>
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_util.h"
27 #include "chrome/browser/extensions/unpacked_installer.h"
28 #include "chrome/browser/extensions/updater/extension_cache_fake.h"
29 #include "chrome/browser/profiles/profile.h"
30 #include "chrome/browser/profiles/profile_manager.h"
31 #include "chrome/browser/ui/browser.h"
32 #include "chrome/browser/ui/browser_window.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/test/base/ui_test_utils.h"
38 #include "content/public/browser/navigation_controller.h"
39 #include "content/public/browser/navigation_entry.h"
40 #include "content/public/browser/notification_registrar.h"
41 #include "content/public/browser/notification_service.h"
42 #include "content/public/browser/render_view_host.h"
43 #include "content/public/test/browser_test_utils.h"
44 #include "extensions/browser/extension_system.h"
45 #include "extensions/common/constants.h"
46 #include "extensions/common/extension_set.h"
47 #include "sync/api/string_ordinal.h"
49 #if defined(OS_CHROMEOS)
50 #include "chromeos/chromeos_switches.h"
51 #endif
53 using extensions::Extension;
54 using extensions::ExtensionCreator;
55 using extensions::FeatureSwitch;
56 using extensions::Manifest;
58 ExtensionBrowserTest::ExtensionBrowserTest()
59 : loaded_(false),
60 installed_(false),
61 #if defined(OS_CHROMEOS)
62 set_chromeos_user_(true),
63 #endif
64 current_channel_(chrome::VersionInfo::CHANNEL_DEV),
65 override_prompt_for_external_extensions_(
66 FeatureSwitch::prompt_for_external_extensions(),
67 false),
68 profile_(NULL) {
69 EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
72 ExtensionBrowserTest::~ExtensionBrowserTest() {
75 Profile* ExtensionBrowserTest::profile() {
76 if (!profile_) {
77 if (browser())
78 profile_ = browser()->profile();
79 else
80 profile_ = ProfileManager::GetActiveUserProfile();
82 return profile_;
85 // static
86 const Extension* ExtensionBrowserTest::GetExtensionByPath(
87 const extensions::ExtensionSet* extensions, const base::FilePath& path) {
88 base::FilePath extension_path = base::MakeAbsoluteFilePath(path);
89 EXPECT_TRUE(!extension_path.empty());
90 for (extensions::ExtensionSet::const_iterator iter = extensions->begin();
91 iter != extensions->end(); ++iter) {
92 if ((*iter)->path() == extension_path) {
93 return iter->get();
96 return NULL;
99 void ExtensionBrowserTest::SetUp() {
100 test_extension_cache_.reset(new extensions::ExtensionCacheFake());
101 InProcessBrowserTest::SetUp();
104 void ExtensionBrowserTest::SetUpCommandLine(CommandLine* command_line) {
105 PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_);
106 test_data_dir_ = test_data_dir_.AppendASCII("extensions");
107 observer_.reset(new ExtensionTestNotificationObserver(browser()));
109 #if defined(OS_CHROMEOS)
110 if (set_chromeos_user_) {
111 // This makes sure that we create the Default profile first, with no
112 // ExtensionService and then the real profile with one, as we do when
113 // running on chromeos.
114 command_line->AppendSwitchASCII(chromeos::switches::kLoginUser,
115 "TestUser@gmail.com");
116 command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
118 #endif
121 void ExtensionBrowserTest::SetUpOnMainThread() {
122 InProcessBrowserTest::SetUpOnMainThread();
123 observer_.reset(new ExtensionTestNotificationObserver(browser()));
126 const Extension* ExtensionBrowserTest::LoadExtensionWithFlags(
127 const base::FilePath& path, int flags) {
128 ExtensionService* service = extensions::ExtensionSystem::Get(
129 profile())->extension_service();
131 observer_->Watch(chrome::NOTIFICATION_EXTENSION_LOADED,
132 content::NotificationService::AllSources());
134 scoped_refptr<extensions::UnpackedInstaller> installer(
135 extensions::UnpackedInstaller::Create(service));
136 installer->set_prompt_for_plugins(false);
137 installer->set_require_modern_manifest_version(
138 (flags & kFlagAllowOldManifestVersions) == 0);
139 installer->Load(path);
141 observer_->Wait();
144 // Find the loaded extension by its path. See crbug.com/59531 for why
145 // we cannot just use last_loaded_extension_id().
146 const Extension* extension = GetExtensionByPath(service->extensions(), path);
147 if (!extension)
148 return NULL;
150 if (!(flags & kFlagIgnoreManifestWarnings)) {
151 const std::vector<extensions::InstallWarning>& install_warnings =
152 extension->install_warnings();
153 if (!install_warnings.empty()) {
154 std::string install_warnings_message = base::StringPrintf(
155 "Unexpected warnings when loading test extension %s:\n",
156 path.AsUTF8Unsafe().c_str());
158 for (std::vector<extensions::InstallWarning>::const_iterator it =
159 install_warnings.begin(); it != install_warnings.end(); ++it) {
160 install_warnings_message += " " + it->message + "\n";
163 EXPECT_TRUE(extension->install_warnings().empty()) <<
164 install_warnings_message;
165 return NULL;
169 const std::string extension_id = extension->id();
171 // The call to OnExtensionInstalled ensures the other extension prefs
172 // are set up with the defaults.
173 service->extension_prefs()->OnExtensionInstalled(
174 extension,
175 Extension::ENABLED,
176 false,
177 syncer::StringOrdinal::CreateInitialOrdinal());
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.
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());
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.";
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.";
211 if (!observer_->WaitForExtensionViewsToLoad())
212 return NULL;
214 return extension;
217 const Extension* ExtensionBrowserTest::LoadExtension(
218 const base::FilePath& path) {
219 return LoadExtensionWithFlags(path, kFlagEnableFileAccess);
222 const Extension* ExtensionBrowserTest::LoadExtensionIncognito(
223 const base::FilePath& path) {
224 return LoadExtensionWithFlags(path,
225 kFlagEnableFileAccess | kFlagEnableIncognito);
228 const 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();
234 std::string manifest;
235 if (!base::ReadFileToString(path.Append(manifest_relative_path), &manifest)) {
236 return NULL;
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;
247 const Extension* ExtensionBrowserTest::LoadExtensionAsComponent(
248 const base::FilePath& path) {
249 return LoadExtensionAsComponentWithManifest(path,
250 extensions::kManifestFilename);
253 base::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();
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;
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();
275 return PackExtensionWithOptions(dir_path, crx_path, pem_path, pem_path_out);
278 base::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();
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();
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();
304 if (!base::PathExists(crx_path)) {
305 ADD_FAILURE() << crx_path.value() << " was not created.";
306 return base::FilePath();
308 return crx_path;
311 // This class is used to simulate an installation abort by the user.
312 class MockAbortExtensionInstallPrompt : public ExtensionInstallPrompt {
313 public:
314 MockAbortExtensionInstallPrompt() : ExtensionInstallPrompt(NULL) {
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();
326 virtual void OnInstallSuccess(const Extension* extension,
327 SkBitmap* icon) OVERRIDE {}
329 virtual void OnInstallFailure(
330 const extensions::CrxInstallerError& error) OVERRIDE {}
333 class MockAutoConfirmExtensionInstallPrompt : public ExtensionInstallPrompt {
334 public:
335 explicit MockAutoConfirmExtensionInstallPrompt(
336 content::WebContents* web_contents)
337 : ExtensionInstallPrompt(web_contents) {}
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();
348 const 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);
362 const 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);
375 const 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);
384 const 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);
396 const 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);
406 const 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();
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()));
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);
437 if (crx_path.empty())
438 return NULL;
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);
451 observer_->Watch(
452 chrome::NOTIFICATION_CRX_INSTALLER_DONE,
453 content::Source<extensions::CrxInstaller>(installer.get()));
455 installer->InstallCrx(crx_path);
457 observer_->Wait();
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:";
467 for (extensions::ExtensionSet::const_iterator it =
468 service->extensions()->begin();
469 it != service->extensions()->end(); ++it)
470 VLOG(1) << " " << (*it)->id();
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;
479 return NULL;
482 if (!observer_->WaitForExtensionViewsToLoad())
483 return NULL;
484 return service->GetExtensionById(last_loaded_extension_id(), false);
487 void ExtensionBrowserTest::ReloadExtension(const std::string extension_id) {
488 observer_->Watch(chrome::NOTIFICATION_EXTENSION_LOADED,
489 content::NotificationService::AllSources());
491 ExtensionService* service =
492 extensions::ExtensionSystem::Get(profile())->extension_service();
493 service->ReloadExtension(extension_id);
495 observer_->Wait();
496 observer_->WaitForExtensionViewsToLoad();
499 void 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);
506 void ExtensionBrowserTest::UninstallExtension(const std::string& extension_id) {
507 ExtensionService* service = extensions::ExtensionSystem::Get(
508 profile())->extension_service();
509 service->UninstallExtension(extension_id, false, NULL);
512 void 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);
518 void ExtensionBrowserTest::EnableExtension(const std::string& extension_id) {
519 ExtensionService* service = extensions::ExtensionSystem::Get(
520 profile())->extension_service();
521 service->EnableExtension(extension_id);
524 void 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() + "');"));
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());
549 if (newtab_result)
550 *newtab_result = newtab;
553 void 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());
571 extensions::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;
587 num_hosts++;
589 EXPECT_EQ(expected_hosts, num_hosts);
590 return host;
593 std::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);