Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / extensions / extension_browsertest.cc
blob95ed97f2fdb887927e9222e691cb89dc0e2a115f
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_system.h"
27 #include "chrome/browser/extensions/extension_util.h"
28 #include "chrome/browser/extensions/unpacked_installer.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/common/constants.h"
45 #include "extensions/common/extension_set.h"
46 #include "sync/api/string_ordinal.h"
48 #if defined(OS_CHROMEOS)
49 #include "chromeos/chromeos_switches.h"
50 #endif
52 using extensions::Extension;
53 using extensions::ExtensionCreator;
54 using extensions::FeatureSwitch;
55 using extensions::Manifest;
57 ExtensionBrowserTest::ExtensionBrowserTest()
58 : loaded_(false),
59 installed_(false),
60 #if defined(OS_CHROMEOS)
61 set_chromeos_user_(true),
62 #endif
63 current_channel_(chrome::VersionInfo::CHANNEL_DEV),
64 override_prompt_for_external_extensions_(
65 FeatureSwitch::prompt_for_external_extensions(),
66 false),
67 profile_(NULL) {
68 EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
71 ExtensionBrowserTest::~ExtensionBrowserTest() {
74 Profile* ExtensionBrowserTest::profile() {
75 if (!profile_) {
76 if (browser())
77 profile_ = browser()->profile();
78 else
79 profile_ = ProfileManager::GetActiveUserProfile();
81 return profile_;
84 // static
85 const Extension* ExtensionBrowserTest::GetExtensionByPath(
86 const extensions::ExtensionSet* extensions, const base::FilePath& path) {
87 base::FilePath extension_path = base::MakeAbsoluteFilePath(path);
88 EXPECT_TRUE(!extension_path.empty());
89 for (extensions::ExtensionSet::const_iterator iter = extensions->begin();
90 iter != extensions->end(); ++iter) {
91 if ((*iter)->path() == extension_path) {
92 return iter->get();
95 return NULL;
98 void ExtensionBrowserTest::SetUpCommandLine(CommandLine* command_line) {
99 PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_);
100 test_data_dir_ = test_data_dir_.AppendASCII("extensions");
101 observer_.reset(new ExtensionTestNotificationObserver(browser()));
103 #if defined(OS_CHROMEOS)
104 if (set_chromeos_user_) {
105 // This makes sure that we create the Default profile first, with no
106 // ExtensionService and then the real profile with one, as we do when
107 // running on chromeos.
108 command_line->AppendSwitchASCII(chromeos::switches::kLoginUser,
109 "TestUser@gmail.com");
110 command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
112 #endif
115 void ExtensionBrowserTest::SetUpOnMainThread() {
116 InProcessBrowserTest::SetUpOnMainThread();
117 observer_.reset(new ExtensionTestNotificationObserver(browser()));
120 const Extension* ExtensionBrowserTest::LoadExtensionWithFlags(
121 const base::FilePath& path, int flags) {
122 ExtensionService* service = extensions::ExtensionSystem::Get(
123 profile())->extension_service();
125 observer_->Watch(chrome::NOTIFICATION_EXTENSION_LOADED,
126 content::NotificationService::AllSources());
128 scoped_refptr<extensions::UnpackedInstaller> installer(
129 extensions::UnpackedInstaller::Create(service));
130 installer->set_prompt_for_plugins(false);
131 installer->set_require_modern_manifest_version(
132 (flags & kFlagAllowOldManifestVersions) == 0);
133 installer->Load(path);
135 observer_->Wait();
138 // Find the loaded extension by its path. See crbug.com/59531 for why
139 // we cannot just use last_loaded_extension_id().
140 const Extension* extension = GetExtensionByPath(service->extensions(), path);
141 if (!extension)
142 return NULL;
144 if (!(flags & kFlagIgnoreManifestWarnings)) {
145 const std::vector<extensions::InstallWarning>& install_warnings =
146 extension->install_warnings();
147 if (!install_warnings.empty()) {
148 std::string install_warnings_message = base::StringPrintf(
149 "Unexpected warnings when loading test extension %s:\n",
150 path.AsUTF8Unsafe().c_str());
152 for (std::vector<extensions::InstallWarning>::const_iterator it =
153 install_warnings.begin(); it != install_warnings.end(); ++it) {
154 install_warnings_message += " " + it->message + "\n";
157 EXPECT_TRUE(extension->install_warnings().empty()) <<
158 install_warnings_message;
159 return NULL;
163 const std::string extension_id = extension->id();
165 // The call to OnExtensionInstalled ensures the other extension prefs
166 // are set up with the defaults.
167 service->extension_prefs()->OnExtensionInstalled(
168 extension,
169 Extension::ENABLED,
170 false,
171 syncer::StringOrdinal::CreateInitialOrdinal());
173 // Toggling incognito or file access will reload the extension, so wait for
174 // the reload and grab the new extension instance. The default state is
175 // incognito disabled and file access enabled, so we don't wait in those
176 // cases.
178 content::WindowedNotificationObserver load_signal(
179 chrome::NOTIFICATION_EXTENSION_LOADED,
180 content::Source<Profile>(profile()));
181 CHECK(!extension_util::IsIncognitoEnabled(extension_id, service) ||
182 extension->force_incognito_enabled());
184 if (flags & kFlagEnableIncognito) {
185 extension_util::SetIsIncognitoEnabled(extension_id, service, true);
186 load_signal.Wait();
187 extension = service->GetExtensionById(extension_id, false);
188 CHECK(extension) << extension_id << " not found after reloading.";
193 content::WindowedNotificationObserver load_signal(
194 chrome::NOTIFICATION_EXTENSION_LOADED,
195 content::Source<Profile>(profile()));
196 CHECK(extension_util::AllowFileAccess(extension, service));
197 if (!(flags & kFlagEnableFileAccess)) {
198 extension_util::SetAllowFileAccess(extension, service, false);
199 load_signal.Wait();
200 extension = service->GetExtensionById(extension_id, false);
201 CHECK(extension) << extension_id << " not found after reloading.";
205 if (!observer_->WaitForExtensionViewsToLoad())
206 return NULL;
208 return extension;
211 const Extension* ExtensionBrowserTest::LoadExtension(
212 const base::FilePath& path) {
213 return LoadExtensionWithFlags(path, kFlagEnableFileAccess);
216 const Extension* ExtensionBrowserTest::LoadExtensionIncognito(
217 const base::FilePath& path) {
218 return LoadExtensionWithFlags(path,
219 kFlagEnableFileAccess | kFlagEnableIncognito);
222 const Extension* ExtensionBrowserTest::LoadExtensionAsComponentWithManifest(
223 const base::FilePath& path,
224 const base::FilePath::CharType* manifest_relative_path) {
225 ExtensionService* service = extensions::ExtensionSystem::Get(
226 profile())->extension_service();
228 std::string manifest;
229 if (!base::ReadFileToString(path.Append(manifest_relative_path), &manifest)) {
230 return NULL;
233 std::string extension_id = service->component_loader()->Add(manifest, path);
234 const Extension* extension = service->extensions()->GetByID(extension_id);
235 if (!extension)
236 return NULL;
237 observer_->set_last_loaded_extension_id(extension->id());
238 return extension;
241 const Extension* ExtensionBrowserTest::LoadExtensionAsComponent(
242 const base::FilePath& path) {
243 return LoadExtensionAsComponentWithManifest(path,
244 extensions::kManifestFilename);
247 base::FilePath ExtensionBrowserTest::PackExtension(
248 const base::FilePath& dir_path) {
249 base::FilePath crx_path = temp_dir_.path().AppendASCII("temp.crx");
250 if (!base::DeleteFile(crx_path, false)) {
251 ADD_FAILURE() << "Failed to delete crx: " << crx_path.value();
252 return base::FilePath();
255 // Look for PEM files with the same name as the directory.
256 base::FilePath pem_path =
257 dir_path.ReplaceExtension(FILE_PATH_LITERAL(".pem"));
258 base::FilePath pem_path_out;
260 if (!base::PathExists(pem_path)) {
261 pem_path = base::FilePath();
262 pem_path_out = crx_path.DirName().AppendASCII("temp.pem");
263 if (!base::DeleteFile(pem_path_out, false)) {
264 ADD_FAILURE() << "Failed to delete pem: " << pem_path_out.value();
265 return base::FilePath();
269 return PackExtensionWithOptions(dir_path, crx_path, pem_path, pem_path_out);
272 base::FilePath ExtensionBrowserTest::PackExtensionWithOptions(
273 const base::FilePath& dir_path,
274 const base::FilePath& crx_path,
275 const base::FilePath& pem_path,
276 const base::FilePath& pem_out_path) {
277 if (!base::PathExists(dir_path)) {
278 ADD_FAILURE() << "Extension dir not found: " << dir_path.value();
279 return base::FilePath();
282 if (!base::PathExists(pem_path) && pem_out_path.empty()) {
283 ADD_FAILURE() << "Must specify a PEM file or PEM output path";
284 return base::FilePath();
287 scoped_ptr<ExtensionCreator> creator(new ExtensionCreator());
288 if (!creator->Run(dir_path,
289 crx_path,
290 pem_path,
291 pem_out_path,
292 ExtensionCreator::kOverwriteCRX)) {
293 ADD_FAILURE() << "ExtensionCreator::Run() failed: "
294 << creator->error_message();
295 return base::FilePath();
298 if (!base::PathExists(crx_path)) {
299 ADD_FAILURE() << crx_path.value() << " was not created.";
300 return base::FilePath();
302 return crx_path;
305 // This class is used to simulate an installation abort by the user.
306 class MockAbortExtensionInstallPrompt : public ExtensionInstallPrompt {
307 public:
308 MockAbortExtensionInstallPrompt() : ExtensionInstallPrompt(NULL) {
311 // Simulate a user abort on an extension installation.
312 virtual void ConfirmInstall(
313 Delegate* delegate,
314 const Extension* extension,
315 const ShowDialogCallback& show_dialog_callback) OVERRIDE {
316 delegate->InstallUIAbort(true);
317 base::MessageLoopForUI::current()->Quit();
320 virtual void OnInstallSuccess(const Extension* extension,
321 SkBitmap* icon) OVERRIDE {}
323 virtual void OnInstallFailure(
324 const extensions::CrxInstallerError& error) OVERRIDE {}
327 class MockAutoConfirmExtensionInstallPrompt : public ExtensionInstallPrompt {
328 public:
329 explicit MockAutoConfirmExtensionInstallPrompt(
330 content::WebContents* web_contents)
331 : ExtensionInstallPrompt(web_contents) {}
333 // Proceed without confirmation prompt.
334 virtual void ConfirmInstall(
335 Delegate* delegate,
336 const Extension* extension,
337 const ShowDialogCallback& show_dialog_callback) OVERRIDE {
338 delegate->InstallUIProceed();
342 const Extension* ExtensionBrowserTest::UpdateExtensionWaitForIdle(
343 const std::string& id,
344 const base::FilePath& path,
345 int expected_change) {
346 return InstallOrUpdateExtension(id,
347 path,
348 INSTALL_UI_TYPE_NONE,
349 expected_change,
350 Manifest::INTERNAL,
351 browser(),
352 Extension::NO_FLAGS,
353 true);
356 const Extension* ExtensionBrowserTest::InstallExtensionFromWebstore(
357 const base::FilePath& path,
358 int expected_change) {
359 return InstallOrUpdateExtension(std::string(),
360 path,
361 INSTALL_UI_TYPE_NONE,
362 expected_change,
363 Manifest::INTERNAL,
364 browser(),
365 Extension::FROM_WEBSTORE,
366 false);
369 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
370 const std::string& id,
371 const base::FilePath& path,
372 InstallUIType ui_type,
373 int expected_change) {
374 return InstallOrUpdateExtension(id, path, ui_type, expected_change,
375 Manifest::INTERNAL, browser(), Extension::NO_FLAGS, false);
378 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
379 const std::string& id,
380 const base::FilePath& path,
381 InstallUIType ui_type,
382 int expected_change,
383 Browser* browser,
384 Extension::InitFromValueFlags creation_flags) {
385 return InstallOrUpdateExtension(id, path, ui_type, expected_change,
386 Manifest::INTERNAL, browser, creation_flags,
387 false);
390 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
391 const std::string& id,
392 const base::FilePath& path,
393 InstallUIType ui_type,
394 int expected_change,
395 Manifest::Location install_source) {
396 return InstallOrUpdateExtension(id, path, ui_type, expected_change,
397 install_source, browser(), Extension::NO_FLAGS, false);
400 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
401 const std::string& id,
402 const base::FilePath& path,
403 InstallUIType ui_type,
404 int expected_change,
405 Manifest::Location install_source,
406 Browser* browser,
407 Extension::InitFromValueFlags creation_flags,
408 bool wait_for_idle) {
409 ExtensionService* service = profile()->GetExtensionService();
410 service->set_show_extensions_prompts(false);
411 size_t num_before = service->extensions()->size();
414 scoped_ptr<ExtensionInstallPrompt> install_ui;
415 if (ui_type == INSTALL_UI_TYPE_CANCEL) {
416 install_ui.reset(new MockAbortExtensionInstallPrompt());
417 } else if (ui_type == INSTALL_UI_TYPE_NORMAL) {
418 install_ui.reset(new ExtensionInstallPrompt(
419 browser->tab_strip_model()->GetActiveWebContents()));
420 } else if (ui_type == INSTALL_UI_TYPE_AUTO_CONFIRM) {
421 install_ui.reset(new MockAutoConfirmExtensionInstallPrompt(
422 browser->tab_strip_model()->GetActiveWebContents()));
425 // TODO(tessamac): Update callers to always pass an unpacked extension
426 // and then always pack the extension here.
427 base::FilePath crx_path = path;
428 if (crx_path.Extension() != FILE_PATH_LITERAL(".crx")) {
429 crx_path = PackExtension(path);
431 if (crx_path.empty())
432 return NULL;
434 scoped_refptr<extensions::CrxInstaller> installer(
435 extensions::CrxInstaller::Create(service, install_ui.Pass()));
436 installer->set_expected_id(id);
437 installer->set_creation_flags(creation_flags);
438 installer->set_install_source(install_source);
439 installer->set_install_wait_for_idle(wait_for_idle);
440 if (!installer->is_gallery_install()) {
441 installer->set_off_store_install_allow_reason(
442 extensions::CrxInstaller::OffStoreInstallAllowedInTest);
445 observer_->Watch(
446 chrome::NOTIFICATION_CRX_INSTALLER_DONE,
447 content::Source<extensions::CrxInstaller>(installer.get()));
449 installer->InstallCrx(crx_path);
451 observer_->Wait();
454 size_t num_after = service->extensions()->size();
455 EXPECT_EQ(num_before + expected_change, num_after);
456 if (num_before + expected_change != num_after) {
457 VLOG(1) << "Num extensions before: " << base::IntToString(num_before)
458 << " num after: " << base::IntToString(num_after)
459 << " Installed extensions follow:";
461 for (extensions::ExtensionSet::const_iterator it =
462 service->extensions()->begin();
463 it != service->extensions()->end(); ++it)
464 VLOG(1) << " " << (*it)->id();
466 VLOG(1) << "Errors follow:";
467 const std::vector<base::string16>* errors =
468 ExtensionErrorReporter::GetInstance()->GetErrors();
469 for (std::vector<base::string16>::const_iterator iter = errors->begin();
470 iter != errors->end(); ++iter)
471 VLOG(1) << *iter;
473 return NULL;
476 if (!observer_->WaitForExtensionViewsToLoad())
477 return NULL;
478 return service->GetExtensionById(last_loaded_extension_id(), false);
481 void ExtensionBrowserTest::ReloadExtension(const std::string extension_id) {
482 observer_->Watch(chrome::NOTIFICATION_EXTENSION_LOADED,
483 content::NotificationService::AllSources());
485 ExtensionService* service =
486 extensions::ExtensionSystem::Get(profile())->extension_service();
487 service->ReloadExtension(extension_id);
489 observer_->Wait();
490 observer_->WaitForExtensionViewsToLoad();
493 void ExtensionBrowserTest::UnloadExtension(const std::string& extension_id) {
494 ExtensionService* service = extensions::ExtensionSystem::Get(
495 profile())->extension_service();
496 service->UnloadExtension(extension_id,
497 extensions::UnloadedExtensionInfo::REASON_DISABLE);
500 void ExtensionBrowserTest::UninstallExtension(const std::string& extension_id) {
501 ExtensionService* service = extensions::ExtensionSystem::Get(
502 profile())->extension_service();
503 service->UninstallExtension(extension_id, false, NULL);
506 void ExtensionBrowserTest::DisableExtension(const std::string& extension_id) {
507 ExtensionService* service = extensions::ExtensionSystem::Get(
508 profile())->extension_service();
509 service->DisableExtension(extension_id, Extension::DISABLE_USER_ACTION);
512 void ExtensionBrowserTest::EnableExtension(const std::string& extension_id) {
513 ExtensionService* service = extensions::ExtensionSystem::Get(
514 profile())->extension_service();
515 service->EnableExtension(extension_id);
518 void ExtensionBrowserTest::OpenWindow(content::WebContents* contents,
519 const GURL& url,
520 bool newtab_process_should_equal_opener,
521 content::WebContents** newtab_result) {
522 content::WindowedNotificationObserver windowed_observer(
523 content::NOTIFICATION_LOAD_STOP,
524 content::NotificationService::AllSources());
525 ASSERT_TRUE(content::ExecuteScript(contents,
526 "window.open('" + url.spec() + "');"));
528 // The above window.open call is not user-initiated, so it will create
529 // a popup window instead of a new tab in current window.
530 // The stop notification will come from the new tab.
531 windowed_observer.Wait();
532 content::NavigationController* controller =
533 content::Source<content::NavigationController>(
534 windowed_observer.source()).ptr();
535 content::WebContents* newtab = controller->GetWebContents();
536 ASSERT_TRUE(newtab);
537 EXPECT_EQ(url, controller->GetLastCommittedEntry()->GetURL());
538 if (newtab_process_should_equal_opener)
539 EXPECT_EQ(contents->GetRenderProcessHost(), newtab->GetRenderProcessHost());
540 else
541 EXPECT_NE(contents->GetRenderProcessHost(), newtab->GetRenderProcessHost());
543 if (newtab_result)
544 *newtab_result = newtab;
547 void ExtensionBrowserTest::NavigateInRenderer(content::WebContents* contents,
548 const GURL& url) {
549 bool result = false;
550 content::WindowedNotificationObserver windowed_observer(
551 content::NOTIFICATION_LOAD_STOP,
552 content::NotificationService::AllSources());
553 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
554 contents,
555 "window.addEventListener('unload', function() {"
556 " window.domAutomationController.send(true);"
557 "}, false);"
558 "window.location = '" + url.spec() + "';",
559 &result));
560 ASSERT_TRUE(result);
561 windowed_observer.Wait();
562 EXPECT_EQ(url, contents->GetController().GetLastCommittedEntry()->GetURL());
565 extensions::ExtensionHost* ExtensionBrowserTest::FindHostWithPath(
566 extensions::ProcessManager* manager,
567 const std::string& path,
568 int expected_hosts) {
569 extensions::ExtensionHost* host = NULL;
570 int num_hosts = 0;
571 extensions::ProcessManager::ExtensionHostSet background_hosts =
572 manager->background_hosts();
573 for (extensions::ProcessManager::const_iterator iter =
574 background_hosts.begin();
575 iter != background_hosts.end();
576 ++iter) {
577 if ((*iter)->GetURL().path() == path) {
578 EXPECT_FALSE(host);
579 host = *iter;
581 num_hosts++;
583 EXPECT_EQ(expected_hosts, num_hosts);
584 return host;
587 std::string ExtensionBrowserTest::ExecuteScriptInBackgroundPage(
588 const std::string& extension_id,
589 const std::string& script) {
590 return extensions::browsertest_util::ExecuteScriptInBackgroundPage(
591 profile(), extension_id, script);