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 <stddef.h>
8
9 #include <utility>
10 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/command_line.h"
14 #include "base/files/file_path.h"
15 #include "base/files/file_util.h"
16 #include "base/files/scoped_temp_dir.h"
17 #include "base/path_service.h"
18 #include "base/run_loop.h"
19 #include "base/strings/strcat.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/task/post_task.h"
24 #include "base/threading/thread_restrictions.h"
25 #include "build/build_config.h"
26 #include "chrome/browser/apps/app_service/app_launch_params.h"
27 #include "chrome/browser/apps/app_service/app_service_proxy.h"
28 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
29 #include "chrome/browser/apps/app_service/browser_app_launcher.h"
30 #include "chrome/browser/extensions/browsertest_util.h"
31 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
32 #include "chrome/browser/extensions/component_loader.h"
33 #include "chrome/browser/extensions/crx_installer.h"
34 #include "chrome/browser/extensions/extension_install_prompt.h"
35 #include "chrome/browser/extensions/extension_install_prompt_show_params.h"
36 #include "chrome/browser/extensions/extension_service.h"
37 #include "chrome/browser/extensions/extension_util.h"
38 #include "chrome/browser/extensions/load_error_reporter.h"
39 #include "chrome/browser/extensions/unpacked_installer.h"
40 #include "chrome/browser/extensions/updater/extension_updater.h"
41 #include "chrome/browser/profiles/profile.h"
42 #include "chrome/browser/profiles/profile_manager.h"
43 #include "chrome/browser/ui/browser.h"
44 #include "chrome/browser/ui/browser_list.h"
45 #include "chrome/browser/ui/browser_window.h"
46 #include "chrome/browser/ui/extensions/extension_message_bubble_factory.h"
47 #include "chrome/browser/ui/tabs/tab_strip_model.h"
48 #include "chrome/common/chrome_paths.h"
49 #include "chrome/common/chrome_switches.h"
50 #include "components/crx_file/crx_verifier.h"
51 #include "components/sync/model/string_ordinal.h"
52 #include "components/version_info/version_info.h"
53 #include "content/public/browser/navigation_controller.h"
54 #include "content/public/browser/navigation_entry.h"
55 #include "content/public/browser/notification_registrar.h"
56 #include "content/public/browser/notification_service.h"
57 #include "content/public/browser/render_frame_host.h"
58 #include "content/public/browser/render_view_host.h"
59 #include "content/public/test/browser_test_utils.h"
60 #include "content/public/test/test_utils.h"
61 #include "extensions/browser/browsertest_util.h"
62 #include "extensions/browser/disable_reason.h"
63 #include "extensions/browser/extension_dialog_auto_confirm.h"
64 #include "extensions/browser/extension_host.h"
65 #include "extensions/browser/extension_prefs.h"
66 #include "extensions/browser/extension_registry.h"
67 #include "extensions/browser/extension_system.h"
68 #include "extensions/browser/notification_types.h"
69 #include "extensions/browser/test_extension_registry_observer.h"
70 #include "extensions/browser/uninstall_reason.h"
71 #include "extensions/browser/updater/extension_cache_fake.h"
72 #include "extensions/common/constants.h"
73 #include "extensions/common/extension_set.h"
74 #include "extensions/common/file_test_util.h"
75 #include "extensions/common/file_util.h"
76 #include "extensions/common/switches.h"
77 #include "extensions/common/value_builder.h"
78
79 #if defined(OS_CHROMEOS)
80 #include "chromeos/constants/chromeos_switches.h"
81 #endif
82
83 namespace extensions {
84
85 namespace {
86
87 // Maps all chrome-extension://<id>/_test_resources/foo requests to
88 // <test_dir_root>/foo or <test_dir_gen_root>/foo, where |test_dir_gen_root| is
89 // inferred from <test_dir_root>. The latter is triggered only if the first path
90 // does not correspond to an existing file. This is what allows us to share code
91 // between tests without needing to duplicate files in each extension.
92 // Example invocation #1, where the requested file exists in |test_dir_root|
93 // Input:
94 // test_dir_root: /abs/path/src/chrome/test/data
95 // directory_path: /abs/path/src/out/<out_dir>/resources/pdf
96 // relative_path: _test_resources/webui/test_browser_proxy.js
97 // Output:
98 // directory_path: /abs/path/src/chrome/test/data
99 // relative_path: webui/test_browser_proxy.js
100 //
101 // Example invocation #2, where the requested file exists in |test_dir_gen_root|
102 // Input:
103 // test_dir_root: /abs/path/src/chrome/test/data
104 // directory_path: /abs/path/src/out/<out_dir>/resources/pdf
105 // relative_path: _test_resources/webui/test_browser_proxy.js
106 // Output:
107 // directory_path: /abs/path/src/out/<out_dir>/gen/chrome/test/data
108 // relative_path: webui/test_browser_proxy.js
ExtensionProtocolTestResourcesHandler(const base::FilePath & test_dir_root,base::FilePath * directory_path,base::FilePath * relative_path)109 void ExtensionProtocolTestResourcesHandler(const base::FilePath& test_dir_root,
110 base::FilePath* directory_path,
111 base::FilePath* relative_path) {
112 // Only map paths that begin with _test_resources.
113 if (!base::FilePath(FILE_PATH_LITERAL("_test_resources"))
114 .IsParent(*relative_path)) {
115 return;
116 }
117
118 // Strip the '_test_resources/' prefix from |relative_path|.
119 std::vector<base::FilePath::StringType> components;
120 relative_path->GetComponents(&components);
121 DCHECK_GT(components.size(), 1u);
122 base::FilePath new_relative_path;
123 for (size_t i = 1u; i < components.size(); ++i)
124 new_relative_path = new_relative_path.Append(components[i]);
125 *relative_path = new_relative_path;
126
127 // Check if the file exists in the |test_dir_root| folder first.
128 base::FilePath src_path = test_dir_root.Append(new_relative_path);
129 // Replace _test_resources/foo with <test_dir_root>/foo.
130 *directory_path = test_dir_root;
131 {
132 base::ScopedAllowBlockingForTesting scoped_allow_blocking;
133 if (base::PathExists(src_path)) {
134 return;
135 }
136 }
137
138 // Infer |test_dir_gen_root| from |test_dir_root|.
139 // E.g., if |test_dir_root| is /abs/path/src/chrome/test/data,
140 // |test_dir_gen_root| will be /abs/path/out/<out_dir>/gen/chrome/test/data.
141 base::FilePath dir_source_root;
142 base::PathService::Get(base::DIR_SOURCE_ROOT, &dir_source_root);
143 base::FilePath exe_dir;
144 base::PathService::Get(base::DIR_EXE, &exe_dir);
145 base::FilePath relative_root_path;
146 dir_source_root.AppendRelativePath(test_dir_root, &relative_root_path);
147 // TODO(dpapad): Add a new DIR_GEN key to PathService instead of manually
148 // appending "gen".
149 base::FilePath test_dir_gen_root =
150 exe_dir.AppendASCII("gen").Append(relative_root_path);
151
152 // Then check if the file exists in the |test_dir_gen_root| folder
153 // covering cases where the test file is generated at build time.
154 base::FilePath gen_path = test_dir_gen_root.Append(new_relative_path);
155 {
156 base::ScopedAllowBlockingForTesting scoped_allow_blocking;
157 if (base::PathExists(gen_path)) {
158 *directory_path = test_dir_gen_root;
159 }
160 }
161 }
162
163 } // namespace
164
ExtensionBrowserTest()165 ExtensionBrowserTest::ExtensionBrowserTest()
166 :
167 #if defined(OS_CHROMEOS)
168 set_chromeos_user_(true),
169 #endif
170 // Default channel is STABLE but override with UNKNOWN so that unlaunched
171 // or incomplete APIs can write tests.
172 current_channel_(version_info::Channel::UNKNOWN),
173 override_prompt_for_external_extensions_(
174 FeatureSwitch::prompt_for_external_extensions(),
175 false),
176 #if defined(OS_WIN)
177 user_desktop_override_(base::DIR_USER_DESKTOP),
178 common_desktop_override_(base::DIR_COMMON_DESKTOP),
179 user_quick_launch_override_(base::DIR_USER_QUICK_LAUNCH),
180 start_menu_override_(base::DIR_START_MENU),
181 common_start_menu_override_(base::DIR_COMMON_START_MENU),
182 #endif
183 profile_(nullptr),
184 verifier_format_override_(crx_file::VerifierFormat::CRX3) {
185 EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
186 }
187
~ExtensionBrowserTest()188 ExtensionBrowserTest::~ExtensionBrowserTest() {
189 }
190
profile()191 Profile* ExtensionBrowserTest::profile() {
192 if (!profile_) {
193 if (browser())
194 profile_ = browser()->profile();
195 else
196 profile_ = ProfileManager::GetActiveUserProfile();
197 }
198 return profile_;
199 }
200
ShouldEnableContentVerification()201 bool ExtensionBrowserTest::ShouldEnableContentVerification() {
202 return false;
203 }
204
ShouldEnableInstallVerification()205 bool ExtensionBrowserTest::ShouldEnableInstallVerification() {
206 return false;
207 }
208
GetTestResourcesParentDir()209 base::FilePath ExtensionBrowserTest::GetTestResourcesParentDir() {
210 // Don't use |test_data_dir_| here (even though it points to
211 // chrome/test/data/extensions by default) because subclasses have the ability
212 // to alter it by overriding the SetUpCommandLine() method.
213 base::FilePath test_root_path;
214 base::PathService::Get(chrome::DIR_TEST_DATA, &test_root_path);
215 return test_root_path.AppendASCII("extensions");
216 }
217
218 // static
GetExtensionByPath(const ExtensionSet & extensions,const base::FilePath & path)219 const Extension* ExtensionBrowserTest::GetExtensionByPath(
220 const ExtensionSet& extensions,
221 const base::FilePath& path) {
222 base::ScopedAllowBlockingForTesting allow_blocking;
223 base::FilePath extension_path = base::MakeAbsoluteFilePath(path);
224 EXPECT_TRUE(!extension_path.empty());
225 for (const scoped_refptr<const Extension>& extension : extensions) {
226 if (extension->path() == extension_path) {
227 return extension.get();
228 }
229 }
230 return NULL;
231 }
232
SetUp()233 void ExtensionBrowserTest::SetUp() {
234 test_extension_cache_.reset(new ExtensionCacheFake());
235 InProcessBrowserTest::SetUp();
236 }
237
SetUpCommandLine(base::CommandLine * command_line)238 void ExtensionBrowserTest::SetUpCommandLine(base::CommandLine* command_line) {
239 base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_);
240 test_data_dir_ = test_data_dir_.AppendASCII("extensions");
241
242 // We don't want any warning bubbles for, e.g., unpacked extensions.
243 ExtensionMessageBubbleFactory::set_override_for_tests(
244 ExtensionMessageBubbleFactory::OVERRIDE_DISABLED);
245
246 if (!ShouldEnableContentVerification()) {
247 ignore_content_verification_.reset(
248 new ScopedIgnoreContentVerifierForTest());
249 }
250
251 if (!ShouldEnableInstallVerification()) {
252 ignore_install_verification_.reset(
253 new ScopedInstallVerifierBypassForTest());
254 }
255
256 #if defined(OS_CHROMEOS)
257 if (set_chromeos_user_) {
258 // This makes sure that we create the Default profile first, with no
259 // ExtensionService and then the real profile with one, as we do when
260 // running on chromeos.
261 command_line->AppendSwitchASCII(chromeos::switches::kLoginUser,
262 "testuser@gmail.com");
263 command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user");
264 }
265 #endif
266 }
267
SetUpOnMainThread()268 void ExtensionBrowserTest::SetUpOnMainThread() {
269 observer_.reset(new ChromeExtensionTestNotificationObserver(browser()));
270 if (extension_service()->updater()) {
271 extension_service()->updater()->SetExtensionCacheForTesting(
272 test_extension_cache_.get());
273 }
274
275 test_protocol_handler_ = base::Bind(&ExtensionProtocolTestResourcesHandler,
276 GetTestResourcesParentDir());
277 SetExtensionProtocolTestHandler(&test_protocol_handler_);
278 }
279
TearDownOnMainThread()280 void ExtensionBrowserTest::TearDownOnMainThread() {
281 ExtensionMessageBubbleFactory::set_override_for_tests(
282 ExtensionMessageBubbleFactory::NO_OVERRIDE);
283 SetExtensionProtocolTestHandler(nullptr);
284 }
285
LoadExtension(const base::FilePath & path)286 const Extension* ExtensionBrowserTest::LoadExtension(
287 const base::FilePath& path) {
288 return LoadExtensionWithFlags(path, kFlagEnableFileAccess);
289 }
290
LoadExtensionIncognito(const base::FilePath & path)291 const Extension* ExtensionBrowserTest::LoadExtensionIncognito(
292 const base::FilePath& path) {
293 return LoadExtensionWithFlags(path,
294 kFlagEnableFileAccess | kFlagEnableIncognito);
295 }
296
LoadExtensionWithFlags(const base::FilePath & path,int flags)297 const Extension* ExtensionBrowserTest::LoadExtensionWithFlags(
298 const base::FilePath& path, int flags) {
299 base::FilePath extension_path = path;
300 if (flags & kFlagRunAsServiceWorkerBasedExtension) {
301 if (!CreateServiceWorkerBasedExtension(path, &extension_path))
302 return nullptr;
303 }
304 return LoadExtensionWithInstallParam(extension_path, flags, std::string());
305 }
306
LoadExtensionWithInstallParam(const base::FilePath & path,int flags,const std::string & install_param)307 const Extension* ExtensionBrowserTest::LoadExtensionWithInstallParam(
308 const base::FilePath& path,
309 int flags,
310 const std::string& install_param) {
311 // Make sure there aren't any stray bits in "flags." This could happen
312 // if someone inadvertently used any of the ExtensionApiTest flag values.
313 CHECK_LT(flags, kFlagNextValue);
314 ChromeTestExtensionLoader loader(profile());
315 loader.set_require_modern_manifest_version(
316 (flags & kFlagAllowOldManifestVersions) == 0);
317 loader.set_ignore_manifest_warnings(flags & kFlagIgnoreManifestWarnings);
318 loader.set_allow_incognito_access(flags & kFlagEnableIncognito);
319 loader.set_allow_file_access(flags & kFlagEnableFileAccess);
320 loader.set_install_param(install_param);
321
322 // Note: Rely on the default value to wait for renderers unless otherwise
323 // specified.
324 if (flags & kFlagDontWaitForExtensionRenderers)
325 loader.set_wait_for_renderers(false);
326
327 if ((flags & kFlagLoadForLoginScreen) != 0) {
328 loader.add_creation_flag(Extension::FOR_LOGIN_SCREEN);
329 loader.set_location(Manifest::EXTERNAL_POLICY);
330 }
331 scoped_refptr<const Extension> extension = loader.LoadExtension(path);
332 if (extension)
333 observer_->set_last_loaded_extension_id(extension->id());
334 return extension.get();
335 }
336
CreateServiceWorkerBasedExtension(const base::FilePath & path,base::FilePath * out_path)337 bool ExtensionBrowserTest::CreateServiceWorkerBasedExtension(
338 const base::FilePath& path,
339 base::FilePath* out_path) {
340 base::ScopedAllowBlockingForTesting allow_blocking;
341
342 // This dir will contain all files for the Service Worker based extension.
343 base::FilePath temp_extension_container;
344 if (!base::CreateTemporaryDirInDir(temp_dir_.GetPath(),
345 base::FilePath::StringType(),
346 &temp_extension_container)) {
347 ADD_FAILURE() << "Could not create temporary dir for test under "
348 << temp_dir_.GetPath();
349 return false;
350 }
351
352 // Copy all files from test dir to temp dir.
353 if (!base::CopyDirectory(path, temp_extension_container,
354 true /* recursive */)) {
355 ADD_FAILURE() << path.value() << " could not be copied to "
356 << temp_extension_container.value();
357 return false;
358 }
359
360 const base::FilePath extension_root =
361 temp_extension_container.Append(path.BaseName());
362
363 std::string error;
364 std::unique_ptr<base::DictionaryValue> manifest_dict =
365 file_util::LoadManifest(extension_root, &error);
366 if (!manifest_dict) {
367 ADD_FAILURE() << path.value() << " could not load manifest: " << error;
368 return false;
369 }
370
371 // Retrieve the value of the "background" key and verify that it is
372 // non-persistent and specifies JS files.
373 // Persistent background pages or background pages that specify HTML files
374 // are not supported.
375 base::Value* background_dict =
376 manifest_dict->FindKeyOfType("background", base::Value::Type::DICTIONARY);
377 if (!background_dict) {
378 ADD_FAILURE() << path.value()
379 << " 'background' key not found in manifest.json";
380 return false;
381 }
382 {
383 base::Value* background_persistent = background_dict->FindKeyOfType(
384 "persistent", base::Value::Type::BOOLEAN);
385 if (!background_persistent) {
386 ADD_FAILURE() << path.value()
387 << ": The \"persistent\" key must be specified to run as a "
388 "Service Worker-based extension.";
389 return false;
390 }
391 }
392 base::Value* background_scripts_list =
393 background_dict->FindKeyOfType("scripts", base::Value::Type::LIST);
394 if (!background_scripts_list) {
395 ADD_FAILURE() << path.value()
396 << ": Only event pages with JS script(s) can be loaded "
397 "as SW extension.";
398 return false;
399 }
400
401 // Number of JS scripts must be > 1.
402 base::Value::ConstListView scripts_list = background_scripts_list->GetList();
403 if (scripts_list.size() < 1) {
404 ADD_FAILURE() << path.value()
405 << ": Only event pages with JS script(s) can be loaded "
406 " as SW extension.";
407 return false;
408 }
409
410 // Generate combined script as Service Worker script using importScripts().
411 constexpr const char kGeneratedSWFileName[] = "generated_service_worker__.js";
412
413 std::vector<std::string> script_filenames;
414 for (const base::Value& script : scripts_list)
415 script_filenames.push_back(base::StrCat({"'", script.GetString(), "'"}));
416
417 base::FilePath combined_script_filepath =
418 extension_root.AppendASCII(kGeneratedSWFileName);
419 // Collision with generated script filename.
420 if (base::PathExists(combined_script_filepath)) {
421 ADD_FAILURE() << combined_script_filepath.value()
422 << " already exists, make sure " << path.value()
423 << " does not contained file named " << kGeneratedSWFileName;
424 return false;
425 }
426 std::string generated_sw_script_content = base::StringPrintf(
427 "importScripts(%s);", base::JoinString(script_filenames, ",").c_str());
428 if (!file_test_util::WriteFile(combined_script_filepath,
429 generated_sw_script_content)) {
430 ADD_FAILURE() << "Could not write combined Service Worker script to: "
431 << combined_script_filepath.value();
432 return false;
433 }
434
435 // Remove the existing background specification and replace it with a service
436 // worker.
437 background_dict->RemoveKey("persistent");
438 background_dict->RemoveKey("scripts");
439 background_dict->SetStringPath("service_worker", kGeneratedSWFileName);
440
441 // Write out manifest.json.
442 DictionaryBuilder manifest_builder(*manifest_dict);
443 std::string manifest_contents = manifest_builder.ToJSON();
444 base::FilePath manifest_path = extension_root.Append(kManifestFilename);
445 if (!file_test_util::WriteFile(manifest_path, manifest_contents)) {
446 ADD_FAILURE() << "Could not write manifest file to "
447 << manifest_path.value();
448 return false;
449 }
450
451 *out_path = extension_root;
452 return true;
453 }
454
LoadExtensionAsComponentWithManifest(const base::FilePath & path,const base::FilePath::CharType * manifest_relative_path)455 const Extension* ExtensionBrowserTest::LoadExtensionAsComponentWithManifest(
456 const base::FilePath& path,
457 const base::FilePath::CharType* manifest_relative_path) {
458 base::ScopedAllowBlockingForTesting allow_blocking;
459 std::string manifest;
460 if (!base::ReadFileToString(path.Append(manifest_relative_path), &manifest)) {
461 return NULL;
462 }
463
464 extension_service()->component_loader()->set_ignore_allowlist_for_testing(
465 true);
466 std::string extension_id =
467 extension_service()->component_loader()->Add(manifest, path);
468 const Extension* extension =
469 extension_registry()->enabled_extensions().GetByID(extension_id);
470 if (!extension)
471 return NULL;
472 observer_->set_last_loaded_extension_id(extension->id());
473 return extension;
474 }
475
LoadExtensionAsComponent(const base::FilePath & path)476 const Extension* ExtensionBrowserTest::LoadExtensionAsComponent(
477 const base::FilePath& path) {
478 return LoadExtensionAsComponentWithManifest(path, kManifestFilename);
479 }
480
LoadAndLaunchApp(const base::FilePath & path)481 const Extension* ExtensionBrowserTest::LoadAndLaunchApp(
482 const base::FilePath& path) {
483 const Extension* app = LoadExtension(path);
484 CHECK(app);
485 content::WindowedNotificationObserver app_loaded_observer(
486 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
487 content::NotificationService::AllSources());
488 apps::AppLaunchParams params(app->id(), LaunchContainer::kLaunchContainerNone,
489 WindowOpenDisposition::NEW_WINDOW,
490 AppLaunchSource::kSourceTest);
491 params.command_line = *base::CommandLine::ForCurrentProcess();
492 apps::AppServiceProxyFactory::GetForProfile(profile())
493 ->BrowserAppLauncher()
494 ->LaunchAppWithParams(std::move(params));
495 app_loaded_observer.Wait();
496
497 return app;
498 }
499
LaunchAppBrowser(const Extension * extension)500 Browser* ExtensionBrowserTest::LaunchAppBrowser(const Extension* extension) {
501 return browsertest_util::LaunchAppBrowser(profile(), extension);
502 }
503
PackExtension(const base::FilePath & dir_path,int extra_run_flags)504 base::FilePath ExtensionBrowserTest::PackExtension(
505 const base::FilePath& dir_path,
506 int extra_run_flags) {
507 base::ScopedAllowBlockingForTesting allow_blocking;
508 base::FilePath crx_path = temp_dir_.GetPath().AppendASCII("temp.crx");
509 if (!base::DeleteFile(crx_path)) {
510 ADD_FAILURE() << "Failed to delete crx: " << crx_path.value();
511 return base::FilePath();
512 }
513
514 // Look for PEM files with the same name as the directory.
515 base::FilePath pem_path =
516 dir_path.ReplaceExtension(FILE_PATH_LITERAL(".pem"));
517 base::FilePath pem_path_out;
518
519 if (!base::PathExists(pem_path)) {
520 pem_path = base::FilePath();
521 pem_path_out = crx_path.DirName().AppendASCII("temp.pem");
522 if (!base::DeleteFile(pem_path_out)) {
523 ADD_FAILURE() << "Failed to delete pem: " << pem_path_out.value();
524 return base::FilePath();
525 }
526 }
527
528 return PackExtensionWithOptions(dir_path, crx_path, pem_path, pem_path_out,
529 extra_run_flags);
530 }
531
PackExtensionWithOptions(const base::FilePath & dir_path,const base::FilePath & crx_path,const base::FilePath & pem_path,const base::FilePath & pem_out_path,int extra_run_flags)532 base::FilePath ExtensionBrowserTest::PackExtensionWithOptions(
533 const base::FilePath& dir_path,
534 const base::FilePath& crx_path,
535 const base::FilePath& pem_path,
536 const base::FilePath& pem_out_path,
537 int extra_run_flags) {
538 base::ScopedAllowBlockingForTesting allow_blocking;
539 if (!base::PathExists(dir_path)) {
540 ADD_FAILURE() << "Extension dir not found: " << dir_path.value();
541 return base::FilePath();
542 }
543
544 if (!base::PathExists(pem_path) && pem_out_path.empty()) {
545 ADD_FAILURE() << "Must specify a PEM file or PEM output path";
546 return base::FilePath();
547 }
548
549 std::unique_ptr<ExtensionCreator> creator(new ExtensionCreator());
550 if (!creator->Run(dir_path, crx_path, pem_path, pem_out_path,
551 extra_run_flags | ExtensionCreator::kOverwriteCRX)) {
552 ADD_FAILURE() << "ExtensionCreator::Run() failed: "
553 << creator->error_message();
554 return base::FilePath();
555 }
556
557 if (!base::PathExists(crx_path)) {
558 ADD_FAILURE() << crx_path.value() << " was not created.";
559 return base::FilePath();
560 }
561 return crx_path;
562 }
563
UpdateExtensionWaitForIdle(const std::string & id,const base::FilePath & path,int expected_change)564 const Extension* ExtensionBrowserTest::UpdateExtensionWaitForIdle(
565 const std::string& id,
566 const base::FilePath& path,
567 int expected_change) {
568 return InstallOrUpdateExtension(id, path, INSTALL_UI_TYPE_NONE,
569 expected_change, Manifest::INTERNAL,
570 browser(), Extension::NO_FLAGS, false, false);
571 }
572
InstallExtensionFromWebstore(const base::FilePath & path,int expected_change)573 const Extension* ExtensionBrowserTest::InstallExtensionFromWebstore(
574 const base::FilePath& path,
575 int expected_change) {
576 return InstallOrUpdateExtension(
577 std::string(), path, INSTALL_UI_TYPE_AUTO_CONFIRM, expected_change,
578 Manifest::INTERNAL, browser(), Extension::FROM_WEBSTORE, true, false);
579 }
580
InstallOrUpdateExtension(const std::string & id,const base::FilePath & path,InstallUIType ui_type,int expected_change)581 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
582 const std::string& id,
583 const base::FilePath& path,
584 InstallUIType ui_type,
585 int expected_change) {
586 return InstallOrUpdateExtension(id, path, ui_type, expected_change,
587 Manifest::INTERNAL, browser(),
588 Extension::NO_FLAGS, true, false);
589 }
590
InstallOrUpdateExtension(const std::string & id,const base::FilePath & path,InstallUIType ui_type,int expected_change,Browser * browser,Extension::InitFromValueFlags creation_flags)591 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
592 const std::string& id,
593 const base::FilePath& path,
594 InstallUIType ui_type,
595 int expected_change,
596 Browser* browser,
597 Extension::InitFromValueFlags creation_flags) {
598 return InstallOrUpdateExtension(id, path, ui_type, expected_change,
599 Manifest::INTERNAL, browser, creation_flags,
600 true, false);
601 }
602
InstallOrUpdateExtension(const std::string & id,const base::FilePath & path,InstallUIType ui_type,int expected_change,Manifest::Location install_source)603 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
604 const std::string& id,
605 const base::FilePath& path,
606 InstallUIType ui_type,
607 int expected_change,
608 Manifest::Location install_source) {
609 return InstallOrUpdateExtension(id, path, ui_type, expected_change,
610 install_source, browser(),
611 Extension::NO_FLAGS, true, false);
612 }
613
InstallOrUpdateExtension(const std::string & id,const base::FilePath & path,InstallUIType ui_type,int expected_change,Manifest::Location install_source,Browser * browser,Extension::InitFromValueFlags creation_flags,bool install_immediately,bool grant_permissions)614 const Extension* ExtensionBrowserTest::InstallOrUpdateExtension(
615 const std::string& id,
616 const base::FilePath& path,
617 InstallUIType ui_type,
618 int expected_change,
619 Manifest::Location install_source,
620 Browser* browser,
621 Extension::InitFromValueFlags creation_flags,
622 bool install_immediately,
623 bool grant_permissions) {
624 ExtensionRegistry* registry = extension_registry();
625 size_t num_before = registry->enabled_extensions().size();
626
627 {
628 std::unique_ptr<ScopedTestDialogAutoConfirm> prompt_auto_confirm;
629 if (ui_type == INSTALL_UI_TYPE_CANCEL) {
630 prompt_auto_confirm.reset(new ScopedTestDialogAutoConfirm(
631 ScopedTestDialogAutoConfirm::CANCEL));
632 } else if (ui_type == INSTALL_UI_TYPE_NORMAL) {
633 prompt_auto_confirm.reset(new ScopedTestDialogAutoConfirm(
634 ScopedTestDialogAutoConfirm::NONE));
635 } else if (ui_type == INSTALL_UI_TYPE_AUTO_CONFIRM) {
636 prompt_auto_confirm.reset(new ScopedTestDialogAutoConfirm(
637 ScopedTestDialogAutoConfirm::ACCEPT));
638 }
639
640 // TODO(tessamac): Update callers to always pass an unpacked extension
641 // and then always pack the extension here.
642 base::FilePath crx_path = path;
643 if (crx_path.Extension() != FILE_PATH_LITERAL(".crx")) {
644 int run_flags = ExtensionCreator::kNoRunFlags;
645 if (creation_flags & Extension::FROM_BOOKMARK) {
646 run_flags = ExtensionCreator::kBookmarkApp;
647 if (install_source == Manifest::EXTERNAL_COMPONENT)
648 run_flags |= ExtensionCreator::kSystemApp;
649 }
650
651 crx_path = PackExtension(path, run_flags);
652 }
653 if (crx_path.empty())
654 return NULL;
655
656 std::unique_ptr<ExtensionInstallPrompt> install_ui;
657 if (prompt_auto_confirm) {
658 install_ui.reset(new ExtensionInstallPrompt(
659 browser->tab_strip_model()->GetActiveWebContents()));
660 }
661 scoped_refptr<CrxInstaller> installer(
662 CrxInstaller::Create(extension_service(), std::move(install_ui)));
663 installer->set_expected_id(id);
664 installer->set_creation_flags(creation_flags);
665 installer->set_install_source(install_source);
666 installer->set_install_immediately(install_immediately);
667 installer->set_allow_silent_install(grant_permissions);
668 if (!installer->is_gallery_install()) {
669 installer->set_off_store_install_allow_reason(
670 CrxInstaller::OffStoreInstallAllowedInTest);
671 }
672
673 observer_->Watch(NOTIFICATION_CRX_INSTALLER_DONE,
674 content::Source<CrxInstaller>(installer.get()));
675
676 installer->InstallCrx(crx_path);
677
678 observer_->Wait();
679 }
680
681 size_t num_after = registry->enabled_extensions().size();
682 EXPECT_EQ(num_before + expected_change, num_after);
683 if (num_before + expected_change != num_after) {
684 VLOG(1) << "Num extensions before: " << base::NumberToString(num_before)
685 << " num after: " << base::NumberToString(num_after)
686 << " Installed extensions follow:";
687
688 for (const scoped_refptr<const Extension>& extension :
689 registry->enabled_extensions())
690 VLOG(1) << " " << extension->id();
691
692 VLOG(1) << "Errors follow:";
693 const std::vector<base::string16>* errors =
694 LoadErrorReporter::GetInstance()->GetErrors();
695 for (auto iter = errors->begin(); iter != errors->end(); ++iter)
696 VLOG(1) << *iter;
697
698 return NULL;
699 }
700
701 if (!observer_->WaitForExtensionViewsToLoad())
702 return NULL;
703 return registry->GetExtensionById(last_loaded_extension_id(),
704 ExtensionRegistry::ENABLED);
705 }
706
ReloadExtension(const std::string & extension_id)707 void ExtensionBrowserTest::ReloadExtension(const std::string& extension_id) {
708 const Extension* extension =
709 extension_registry()->GetInstalledExtension(extension_id);
710 ASSERT_TRUE(extension);
711 TestExtensionRegistryObserver observer(extension_registry(), extension_id);
712 extension_service()->ReloadExtension(extension_id);
713 observer.WaitForExtensionLoaded();
714
715 // We need to let other ExtensionRegistryObservers handle the extension load
716 // in order to finish initialization. This has to be done before waiting for
717 // extension views to load, since we only register views after observing
718 // extension load.
719 base::RunLoop().RunUntilIdle();
720 observer_->WaitForExtensionViewsToLoad();
721 }
722
UnloadExtension(const std::string & extension_id)723 void ExtensionBrowserTest::UnloadExtension(const std::string& extension_id) {
724 extension_service()->UnloadExtension(extension_id,
725 UnloadedExtensionReason::DISABLE);
726 }
727
UninstallExtension(const std::string & extension_id)728 void ExtensionBrowserTest::UninstallExtension(const std::string& extension_id) {
729 extension_service()->UninstallExtension(
730 extension_id, UNINSTALL_REASON_FOR_TESTING, nullptr);
731 }
732
DisableExtension(const std::string & extension_id)733 void ExtensionBrowserTest::DisableExtension(const std::string& extension_id) {
734 extension_service()->DisableExtension(extension_id,
735 disable_reason::DISABLE_USER_ACTION);
736 }
737
EnableExtension(const std::string & extension_id)738 void ExtensionBrowserTest::EnableExtension(const std::string& extension_id) {
739 extension_service()->EnableExtension(extension_id);
740 }
741
OpenWindow(content::WebContents * contents,const GURL & url,bool newtab_process_should_equal_opener,bool should_succeed,content::WebContents ** newtab_result)742 void ExtensionBrowserTest::OpenWindow(content::WebContents* contents,
743 const GURL& url,
744 bool newtab_process_should_equal_opener,
745 bool should_succeed,
746 content::WebContents** newtab_result) {
747 content::WebContentsAddedObserver tab_added_observer;
748 ASSERT_TRUE(content::ExecuteScript(contents,
749 "window.open('" + url.spec() + "');"));
750 content::WebContents* newtab = tab_added_observer.GetWebContents();
751 ASSERT_TRUE(newtab);
752 WaitForLoadStop(newtab);
753
754 if (should_succeed) {
755 EXPECT_EQ(url, newtab->GetLastCommittedURL());
756 EXPECT_EQ(content::PAGE_TYPE_NORMAL,
757 newtab->GetController().GetLastCommittedEntry()->GetPageType());
758 } else {
759 // "Failure" comes in two forms: redirecting to about:blank or showing an
760 // error page. At least one should be true.
761 EXPECT_TRUE(
762 newtab->GetLastCommittedURL() == GURL(url::kAboutBlankURL) ||
763 newtab->GetController().GetLastCommittedEntry()->GetPageType() ==
764 content::PAGE_TYPE_ERROR);
765 }
766
767 if (newtab_process_should_equal_opener) {
768 EXPECT_EQ(contents->GetMainFrame()->GetSiteInstance(),
769 newtab->GetMainFrame()->GetSiteInstance());
770 } else {
771 EXPECT_NE(contents->GetMainFrame()->GetSiteInstance(),
772 newtab->GetMainFrame()->GetSiteInstance());
773 }
774
775 if (newtab_result)
776 *newtab_result = newtab;
777 }
778
NavigateInRenderer(content::WebContents * contents,const GURL & url)779 void ExtensionBrowserTest::NavigateInRenderer(content::WebContents* contents,
780 const GURL& url) {
781 // Note: We use ExecuteScript instead of ExecJS here, because ExecuteScript
782 // works on pages with a Content Security Policy.
783 EXPECT_TRUE(content::ExecuteScript(
784 contents, "window.location = '" + url.spec() + "';"));
785 content::WaitForLoadStop(contents);
786 EXPECT_EQ(url, contents->GetController().GetLastCommittedEntry()->GetURL());
787 }
788
FindHostWithPath(ProcessManager * manager,const std::string & path,int expected_hosts)789 ExtensionHost* ExtensionBrowserTest::FindHostWithPath(ProcessManager* manager,
790 const std::string& path,
791 int expected_hosts) {
792 ExtensionHost* result_host = nullptr;
793 int num_hosts = 0;
794 for (ExtensionHost* host : manager->background_hosts()) {
795 if (host->GetLastCommittedURL().path() == path) {
796 EXPECT_FALSE(result_host);
797 result_host = host;
798 }
799 num_hosts++;
800 }
801 EXPECT_EQ(expected_hosts, num_hosts);
802 return result_host;
803 }
804
ExecuteScriptInBackgroundPage(const std::string & extension_id,const std::string & script,browsertest_util::ScriptUserActivation script_user_activation)805 std::string ExtensionBrowserTest::ExecuteScriptInBackgroundPage(
806 const std::string& extension_id,
807 const std::string& script,
808 browsertest_util::ScriptUserActivation script_user_activation) {
809 return browsertest_util::ExecuteScriptInBackgroundPage(
810 profile(), extension_id, script, script_user_activation);
811 }
812
ExecuteScriptInBackgroundPageNoWait(const std::string & extension_id,const std::string & script)813 bool ExtensionBrowserTest::ExecuteScriptInBackgroundPageNoWait(
814 const std::string& extension_id,
815 const std::string& script) {
816 return browsertest_util::ExecuteScriptInBackgroundPageNoWait(
817 profile(), extension_id, script);
818 }
819
820 } // namespace extensions
821