blob: f4c04bbc91b8d880be4931277528d650d543f5b3 [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();
rdevlin.cronin55f2b6232016-12-05 21:25:5360 // Trying to reload a shared module (as we do when adjusting extension
61 // permissions) causes ExtensionService to crash. Only adjust permissions for
62 // non-shared modules.
63 // TODO(devlin): That's not good; we shouldn't be crashing.
64 if (!SharedModuleInfo::IsSharedModule(extension.get())) {
65 extension = nullptr;
66 CheckPermissions(extension_id_);
67 }
rdevlin.cronin11f01682016-11-28 18:15:5268
69 if (!install_param_.empty()) {
70 ExtensionPrefs::Get(browser_context_)
71 ->SetInstallParam(extension_id_, install_param_);
72 // Reload the extension so listeners of the loaded notification have access
73 // to the install param.
74 TestExtensionRegistryObserver registry_observer(extension_registry_,
75 extension_id_);
76 extension_service_->ReloadExtension(extension_id_);
77 registry_observer.WaitForExtensionLoaded();
78 }
79
80 extension = extension_registry_->enabled_extensions().GetByID(extension_id_);
81 if (!extension)
82 return nullptr;
83 if (!CheckErrors(*extension))
84 return nullptr;
85
86 base::RunLoop().RunUntilIdle();
87 if (!WaitForExtensionReady()) {
88 ADD_FAILURE() << "Failed to wait for extension ready";
89 return nullptr;
90 }
91 return extension;
92}
93
94bool ChromeTestExtensionLoader::WaitForExtensionReady() {
95 return ChromeExtensionTestNotificationObserver(browser_context_)
96 .WaitForExtensionViewsToLoad();
97}
98
99base::FilePath ChromeTestExtensionLoader::PackExtension(
100 const base::FilePath& unpacked_path) {
101 if (!base::PathExists(unpacked_path)) {
102 ADD_FAILURE() << "Unpacked path does not exist: " << unpacked_path.value();
103 return base::FilePath();
104 }
105
106 base::FilePath crx_path = temp_dir_.GetPath().AppendASCII("temp.crx");
107 if (base::PathExists(crx_path)) {
108 ADD_FAILURE() << "Crx path exists: " << crx_path.value()
109 << ", are you trying to reuse the same ChromeTestExtensionLoader?";
110 return base::FilePath();
111 }
112 base::FilePath fallback_pem_path =
113 temp_dir_.GetPath().AppendASCII("temp.pem");
114 if (base::PathExists(fallback_pem_path)) {
115 ADD_FAILURE() << "PEM path exists: " << fallback_pem_path.value()
116 << ", are you trying to reuse the same ChromeTestExtensionLoader?";
117 return base::FilePath();
118 }
119
120 base::FilePath* pem_path_to_use = &fallback_pem_path;
121 if (!pem_path_.empty()) {
122 pem_path_to_use = &pem_path_;
123 if (!base::PathExists(pem_path_)) {
124 ADD_FAILURE() << "Provided PEM path does not exist: "
125 << pem_path_.value();
126 return base::FilePath();
127 }
128 }
129
130 ExtensionCreator creator;
131 if (!creator.Run(unpacked_path, crx_path, *pem_path_to_use, fallback_pem_path,
132 ExtensionCreator::kOverwriteCRX)) {
133 ADD_FAILURE() << "ExtensionCreator::Run() failed: "
134 << creator.error_message();
135 return base::FilePath();
136 }
137
138 CHECK(base::PathExists(crx_path));
139
140 return crx_path;
141}
142
143scoped_refptr<const Extension> ChromeTestExtensionLoader::LoadCrx(
144 const base::FilePath& file_path) {
145 if (!file_path.MatchesExtension(FILE_PATH_LITERAL(".crx"))) {
146 ADD_FAILURE() << "Must pass a crx path to LoadCrx()";
147 return nullptr;
148 }
149
150 scoped_refptr<const Extension> extension;
151 {
152 // TODO(devlin): Allow consumers to specify the install ui type.
153 std::unique_ptr<ExtensionInstallPrompt> install_ui;
154 scoped_refptr<CrxInstaller> installer =
155 CrxInstaller::Create(extension_service_, std::move(install_ui));
156 installer->set_expected_id(expected_id_);
157 installer->set_creation_flags(creation_flags_);
158 installer->set_install_source(location_);
159 installer->set_install_immediately(install_immediately_);
160 installer->set_allow_silent_install(grant_permissions_);
161 if (!installer->is_gallery_install()) {
162 installer->set_off_store_install_allow_reason(
163 CrxInstaller::OffStoreInstallAllowedInTest);
164 }
165
166 content::WindowedNotificationObserver install_observer(
167 NOTIFICATION_CRX_INSTALLER_DONE,
168 content::Source<CrxInstaller>(installer.get()));
169 installer->InstallCrx(file_path);
170 install_observer.Wait();
171
172 extension =
173 content::Details<const Extension>(install_observer.details()).ptr();
174 }
175
176 return extension;
177}
178
179void ChromeTestExtensionLoader::CheckPermissions(
180 const std::string& extension_id) {
181 std::string id = extension_id;
182
183 // Toggling incognito or file access will reload the extension, so wait for
184 // the reload.
185 if (allow_file_access_ != util::AllowFileAccess(id, browser_context_)) {
186 TestExtensionRegistryObserver registry_observer(extension_registry_, id);
187 util::SetAllowFileAccess(id, browser_context_, allow_file_access_);
188 registry_observer.WaitForExtensionLoaded();
189 }
190
191 if (allow_incognito_access_ !=
192 util::IsIncognitoEnabled(id, browser_context_)) {
193 TestExtensionRegistryObserver registry_observer(extension_registry_, id);
194 util::SetIsIncognitoEnabled(id, browser_context_, true);
195 registry_observer.WaitForExtensionLoaded();
196 }
197}
198
199scoped_refptr<const Extension> ChromeTestExtensionLoader::LoadUnpacked(
200 const base::FilePath& file_path) {
201 const Extension* extension = nullptr;
202 TestExtensionRegistryObserver registry_observer(extension_registry_);
203 scoped_refptr<UnpackedInstaller> installer =
204 UnpackedInstaller::Create(extension_service_);
205 installer->set_prompt_for_plugins(false);
206 installer->set_require_modern_manifest_version(
207 require_modern_manifest_version_);
208 installer->Load(file_path);
209 extension = registry_observer.WaitForExtensionLoaded();
210
211 return extension;
212}
213
214bool ChromeTestExtensionLoader::CheckErrors(const Extension& extension) {
215 if (ignore_manifest_warnings_)
216 return true;
217 const std::vector<InstallWarning>& install_warnings =
218 extension.install_warnings();
219 if (install_warnings.empty())
220 return true;
221
222 std::string install_warnings_message = "Unexpected warnings for extension:\n";
223 for (const InstallWarning& warning : install_warnings)
224 install_warnings_message += " " + warning.message + "\n";
225
226 ADD_FAILURE() << install_warnings_message;
227 return false;
228}
229
230} // namespace extensions