blob: 9a44002da73f6e6dd3e89fc020a1d3c8532a8545 [file] [log] [blame]
rdevlin.cronin11f01682016-11-28 18:15:521// Copyright 2016 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/chrome_test_extension_loader.h"
6
7#include <memory>
8
9#include "base/files/file_util.h"
10#include "base/run_loop.h"
11#include "chrome/browser/extensions/chrome_extension_test_notification_observer.h"
12#include "chrome/browser/extensions/crx_installer.h"
13#include "chrome/browser/extensions/extension_creator.h"
14#include "chrome/browser/extensions/extension_service.h"
15#include "chrome/browser/extensions/extension_util.h"
16#include "chrome/browser/extensions/unpacked_installer.h"
17#include "content/public/browser/notification_details.h"
18#include "content/public/browser/notification_source.h"
19#include "content/public/test/test_utils.h"
20#include "extensions/browser/extension_registry.h"
21#include "extensions/browser/extension_system.h"
22#include "extensions/browser/notification_types.h"
23#include "extensions/browser/test_extension_registry_observer.h"
24#include "testing/gtest/include/gtest/gtest.h"
25
26namespace extensions {
27
28ChromeTestExtensionLoader::ChromeTestExtensionLoader(
29 content::BrowserContext* browser_context)
30 : browser_context_(browser_context),
31 extension_system_(ExtensionSystem::Get(browser_context)),
32 extension_service_(extension_system_->extension_service()),
33 extension_registry_(ExtensionRegistry::Get(browser_context)) {}
34
35ChromeTestExtensionLoader::~ChromeTestExtensionLoader() {}
36
37scoped_refptr<const Extension> ChromeTestExtensionLoader::LoadExtension(
38 const base::FilePath& path) {
39 scoped_refptr<const Extension> extension;
40 if (path.MatchesExtension(FILE_PATH_LITERAL(".crx"))) {
41 extension = LoadCrx(path);
42 } else if (pack_extension_) {
43 base::FilePath crx_path = PackExtension(path);
44 if (crx_path.empty())
45 return nullptr;
46 extension = LoadCrx(crx_path);
47 } else {
48 extension = LoadUnpacked(path);
49 }
50
51 if (should_fail_ && extension)
52 ADD_FAILURE() << "Expected extension installation failure, but succeeded";
53 else if (!should_fail_ && !extension)
54 ADD_FAILURE() << "Failed to install extension";
55
56 if (!extension)
57 return nullptr;
58
59 extension_id_ = extension->id();
60 extension = nullptr;
61 CheckPermissions(extension_id_);
62
63 if (!install_param_.empty()) {
64 ExtensionPrefs::Get(browser_context_)
65 ->SetInstallParam(extension_id_, install_param_);
66 // Reload the extension so listeners of the loaded notification have access
67 // to the install param.
68 TestExtensionRegistryObserver registry_observer(extension_registry_,
69 extension_id_);
70 extension_service_->ReloadExtension(extension_id_);
71 registry_observer.WaitForExtensionLoaded();
72 }
73
74 extension = extension_registry_->enabled_extensions().GetByID(extension_id_);
75 if (!extension)
76 return nullptr;
77 if (!CheckErrors(*extension))
78 return nullptr;
79
80 base::RunLoop().RunUntilIdle();
81 if (!WaitForExtensionReady()) {
82 ADD_FAILURE() << "Failed to wait for extension ready";
83 return nullptr;
84 }
85 return extension;
86}
87
88bool ChromeTestExtensionLoader::WaitForExtensionReady() {
89 return ChromeExtensionTestNotificationObserver(browser_context_)
90 .WaitForExtensionViewsToLoad();
91}
92
93base::FilePath ChromeTestExtensionLoader::PackExtension(
94 const base::FilePath& unpacked_path) {
95 if (!base::PathExists(unpacked_path)) {
96 ADD_FAILURE() << "Unpacked path does not exist: " << unpacked_path.value();
97 return base::FilePath();
98 }
99
100 base::FilePath crx_path = temp_dir_.GetPath().AppendASCII("temp.crx");
101 if (base::PathExists(crx_path)) {
102 ADD_FAILURE() << "Crx path exists: " << crx_path.value()
103 << ", are you trying to reuse the same ChromeTestExtensionLoader?";
104 return base::FilePath();
105 }
106 base::FilePath fallback_pem_path =
107 temp_dir_.GetPath().AppendASCII("temp.pem");
108 if (base::PathExists(fallback_pem_path)) {
109 ADD_FAILURE() << "PEM path exists: " << fallback_pem_path.value()
110 << ", are you trying to reuse the same ChromeTestExtensionLoader?";
111 return base::FilePath();
112 }
113
114 base::FilePath* pem_path_to_use = &fallback_pem_path;
115 if (!pem_path_.empty()) {
116 pem_path_to_use = &pem_path_;
117 if (!base::PathExists(pem_path_)) {
118 ADD_FAILURE() << "Provided PEM path does not exist: "
119 << pem_path_.value();
120 return base::FilePath();
121 }
122 }
123
124 ExtensionCreator creator;
125 if (!creator.Run(unpacked_path, crx_path, *pem_path_to_use, fallback_pem_path,
126 ExtensionCreator::kOverwriteCRX)) {
127 ADD_FAILURE() << "ExtensionCreator::Run() failed: "
128 << creator.error_message();
129 return base::FilePath();
130 }
131
132 CHECK(base::PathExists(crx_path));
133
134 return crx_path;
135}
136
137scoped_refptr<const Extension> ChromeTestExtensionLoader::LoadCrx(
138 const base::FilePath& file_path) {
139 if (!file_path.MatchesExtension(FILE_PATH_LITERAL(".crx"))) {
140 ADD_FAILURE() << "Must pass a crx path to LoadCrx()";
141 return nullptr;
142 }
143
144 scoped_refptr<const Extension> extension;
145 {
146 // TODO(devlin): Allow consumers to specify the install ui type.
147 std::unique_ptr<ExtensionInstallPrompt> install_ui;
148 scoped_refptr<CrxInstaller> installer =
149 CrxInstaller::Create(extension_service_, std::move(install_ui));
150 installer->set_expected_id(expected_id_);
151 installer->set_creation_flags(creation_flags_);
152 installer->set_install_source(location_);
153 installer->set_install_immediately(install_immediately_);
154 installer->set_allow_silent_install(grant_permissions_);
155 if (!installer->is_gallery_install()) {
156 installer->set_off_store_install_allow_reason(
157 CrxInstaller::OffStoreInstallAllowedInTest);
158 }
159
160 content::WindowedNotificationObserver install_observer(
161 NOTIFICATION_CRX_INSTALLER_DONE,
162 content::Source<CrxInstaller>(installer.get()));
163 installer->InstallCrx(file_path);
164 install_observer.Wait();
165
166 extension =
167 content::Details<const Extension>(install_observer.details()).ptr();
168 }
169
170 return extension;
171}
172
173void ChromeTestExtensionLoader::CheckPermissions(
174 const std::string& extension_id) {
175 std::string id = extension_id;
176
177 // Toggling incognito or file access will reload the extension, so wait for
178 // the reload.
179 if (allow_file_access_ != util::AllowFileAccess(id, browser_context_)) {
180 TestExtensionRegistryObserver registry_observer(extension_registry_, id);
181 util::SetAllowFileAccess(id, browser_context_, allow_file_access_);
182 registry_observer.WaitForExtensionLoaded();
183 }
184
185 if (allow_incognito_access_ !=
186 util::IsIncognitoEnabled(id, browser_context_)) {
187 TestExtensionRegistryObserver registry_observer(extension_registry_, id);
188 util::SetIsIncognitoEnabled(id, browser_context_, true);
189 registry_observer.WaitForExtensionLoaded();
190 }
191}
192
193scoped_refptr<const Extension> ChromeTestExtensionLoader::LoadUnpacked(
194 const base::FilePath& file_path) {
195 const Extension* extension = nullptr;
196 TestExtensionRegistryObserver registry_observer(extension_registry_);
197 scoped_refptr<UnpackedInstaller> installer =
198 UnpackedInstaller::Create(extension_service_);
199 installer->set_prompt_for_plugins(false);
200 installer->set_require_modern_manifest_version(
201 require_modern_manifest_version_);
202 installer->Load(file_path);
203 extension = registry_observer.WaitForExtensionLoaded();
204
205 return extension;
206}
207
208bool ChromeTestExtensionLoader::CheckErrors(const Extension& extension) {
209 if (ignore_manifest_warnings_)
210 return true;
211 const std::vector<InstallWarning>& install_warnings =
212 extension.install_warnings();
213 if (install_warnings.empty())
214 return true;
215
216 std::string install_warnings_message = "Unexpected warnings for extension:\n";
217 for (const InstallWarning& warning : install_warnings)
218 install_warnings_message += " " + warning.message + "\n";
219
220 ADD_FAILURE() << install_warnings_message;
221 return false;
222}
223
224} // namespace extensions