blob: bcc5f85bc7e94d5fb2ef735e5a2f556b3c687aa6 [file] [log] [blame]
karandeepb53c8920d2017-04-06 02:13:071// Copyright 2017 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 "extensions/browser/renderer_startup_helper.h"
6
karandeepb53c8920d2017-04-06 02:13:077#include "base/stl_util.h"
karandeepb18ab4ab82017-04-07 00:27:318#include "components/crx_file/id_util.h"
karandeepb53c8920d2017-04-06 02:13:079#include "content/public/browser/notification_service.h"
10#include "content/public/browser/notification_types.h"
11#include "content/public/test/mock_render_process_host.h"
karandeepb18ab4ab82017-04-07 00:27:3112#include "extensions/browser/extension_prefs.h"
karandeepb53c8920d2017-04-06 02:13:0713#include "extensions/browser/extension_registry.h"
14#include "extensions/browser/extension_registry_factory.h"
karandeepb18ab4ab82017-04-07 00:27:3115#include "extensions/browser/extension_util.h"
karandeepb53c8920d2017-04-06 02:13:0716#include "extensions/browser/extensions_test.h"
karandeepb18ab4ab82017-04-07 00:27:3117#include "extensions/browser/test_extensions_browser_client.h"
karandeepb53c8920d2017-04-06 02:13:0718#include "extensions/common/extension_builder.h"
19#include "extensions/common/extension_messages.h"
20
21namespace extensions {
22
23class RendererStartupHelperTest : public ExtensionsTest {
24 public:
25 RendererStartupHelperTest() {}
26 ~RendererStartupHelperTest() override {}
27
28 void SetUp() override {
29 ExtensionsTest::SetUp();
Jeremy Roman16529d0e2017-08-24 18:13:4730 helper_ = std::make_unique<RendererStartupHelper>(browser_context());
karandeepb53c8920d2017-04-06 02:13:0731 registry_ =
32 ExtensionRegistryFactory::GetForBrowserContext(browser_context());
33 render_process_host_ =
Jeremy Roman16529d0e2017-08-24 18:13:4734 std::make_unique<content::MockRenderProcessHost>(browser_context());
karandeepb18ab4ab82017-04-07 00:27:3135 incognito_render_process_host_ =
Jeremy Roman16529d0e2017-08-24 18:13:4736 std::make_unique<content::MockRenderProcessHost>(incognito_context());
karandeepb53c8920d2017-04-06 02:13:0737 extension_ = CreateExtension("ext_1");
38 }
39
40 void TearDown() override {
41 render_process_host_.reset();
karandeepb18ab4ab82017-04-07 00:27:3142 incognito_render_process_host_.reset();
karandeepb53c8920d2017-04-06 02:13:0743 helper_.reset();
44 ExtensionsTest::TearDown();
45 }
46
47 protected:
48 void SimulateRenderProcessCreated(content::RenderProcessHost* rph) {
49 content::NotificationService::current()->Notify(
50 content::NOTIFICATION_RENDERER_PROCESS_CREATED,
51 content::Source<content::RenderProcessHost>(rph),
52 content::NotificationService::NoDetails());
53 }
54
55 void SimulateRenderProcessTerminated(content::RenderProcessHost* rph) {
56 content::NotificationService::current()->Notify(
57 content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
58 content::Source<content::RenderProcessHost>(rph),
59 content::NotificationService::NoDetails());
60 }
61
Devlin Cronin8e5892f2018-10-04 00:13:4362 scoped_refptr<const Extension> CreateExtension(const std::string& id_input) {
karandeepb53c8920d2017-04-06 02:13:0763 std::unique_ptr<base::DictionaryValue> manifest =
64 DictionaryBuilder()
65 .Set("name", "extension")
66 .Set("description", "an extension")
67 .Set("manifest_version", 2)
68 .Set("version", "0.1")
69 .Build();
karandeepb18ab4ab82017-04-07 00:27:3170 return CreateExtension(id_input, std::move(manifest));
karandeepb53c8920d2017-04-06 02:13:0771 }
72
Devlin Cronin8e5892f2018-10-04 00:13:4373 scoped_refptr<const Extension> CreateTheme(const std::string& id_input) {
karandeepb53c8920d2017-04-06 02:13:0774 std::unique_ptr<base::DictionaryValue> manifest =
75 DictionaryBuilder()
76 .Set("name", "theme")
77 .Set("description", "a theme")
78 .Set("theme", DictionaryBuilder().Build())
79 .Set("manifest_version", 2)
80 .Set("version", "0.1")
81 .Build();
karandeepb18ab4ab82017-04-07 00:27:3182 return CreateExtension(id_input, std::move(manifest));
83 }
84
Devlin Cronin8e5892f2018-10-04 00:13:4385 scoped_refptr<const Extension> CreatePlatformApp(
86 const std::string& id_input) {
karandeepb18ab4ab82017-04-07 00:27:3187 std::unique_ptr<base::Value> background =
88 DictionaryBuilder()
89 .Set("scripts", ListBuilder().Append("background.js").Build())
90 .Build();
91 std::unique_ptr<base::DictionaryValue> manifest =
92 DictionaryBuilder()
93 .Set("name", "platform_app")
94 .Set("description", "a platform app")
95 .Set("app", DictionaryBuilder()
96 .Set("background", std::move(background))
97 .Build())
98 .Set("manifest_version", 2)
99 .Set("version", "0.1")
100 .Build();
101 return CreateExtension(id_input, std::move(manifest));
karandeepb53c8920d2017-04-06 02:13:07102 }
103
Devlin Cronin8e5892f2018-10-04 00:13:43104 void AddExtensionToRegistry(scoped_refptr<const Extension> extension) {
karandeepb53c8920d2017-04-06 02:13:07105 registry_->AddEnabled(extension);
106 }
107
Devlin Cronin8e5892f2018-10-04 00:13:43108 void RemoveExtensionFromRegistry(scoped_refptr<const Extension> extension) {
karandeepb53c8920d2017-04-06 02:13:07109 registry_->RemoveEnabled(extension->id());
110 }
111
112 bool IsProcessInitialized(content::RenderProcessHost* rph) {
Jan Wilken Dörrie0fd53a22019-06-07 09:55:46113 return base::Contains(helper_->initialized_processes_, rph);
karandeepb53c8920d2017-04-06 02:13:07114 }
115
116 bool IsExtensionLoaded(const Extension& extension) {
Jan Wilken Dörrie0fd53a22019-06-07 09:55:46117 return base::Contains(helper_->extension_process_map_, extension.id());
karandeepb53c8920d2017-04-06 02:13:07118 }
119
120 bool IsExtensionLoadedInProcess(const Extension& extension,
121 content::RenderProcessHost* rph) {
122 return IsExtensionLoaded(extension) &&
Jan Wilken Dörrie0fd53a22019-06-07 09:55:46123 base::Contains(helper_->extension_process_map_[extension.id()], rph);
karandeepb53c8920d2017-04-06 02:13:07124 }
125
126 bool IsExtensionPendingActivationInProcess(const Extension& extension,
127 content::RenderProcessHost* rph) {
Jan Wilken Dörrie0fd53a22019-06-07 09:55:46128 return base::Contains(helper_->pending_active_extensions_, rph) &&
129 base::Contains(helper_->pending_active_extensions_[rph],
130 extension.id());
karandeepb53c8920d2017-04-06 02:13:07131 }
132
133 std::unique_ptr<RendererStartupHelper> helper_;
134 ExtensionRegistry* registry_; // Weak.
135 std::unique_ptr<content::MockRenderProcessHost> render_process_host_;
karandeepb18ab4ab82017-04-07 00:27:31136 std::unique_ptr<content::MockRenderProcessHost>
137 incognito_render_process_host_;
Devlin Cronin8e5892f2018-10-04 00:13:43138 scoped_refptr<const Extension> extension_;
karandeepb53c8920d2017-04-06 02:13:07139
140 private:
Devlin Cronin8e5892f2018-10-04 00:13:43141 scoped_refptr<const Extension> CreateExtension(
karandeepb18ab4ab82017-04-07 00:27:31142 const std::string& id_input,
143 std::unique_ptr<base::DictionaryValue> manifest) {
144 return ExtensionBuilder()
145 .SetManifest(std::move(manifest))
146 .SetID(crx_file::id_util::GenerateId(id_input))
147 .Build();
148 }
149
karandeepb53c8920d2017-04-06 02:13:07150 DISALLOW_COPY_AND_ASSIGN(RendererStartupHelperTest);
151};
152
153// Tests extension loading, unloading and activation and render process creation
154// and termination.
155TEST_F(RendererStartupHelperTest, NormalExtensionLifecycle) {
156 // Initialize render process.
157 EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
158 SimulateRenderProcessCreated(render_process_host_.get());
159 EXPECT_TRUE(IsProcessInitialized(render_process_host_.get()));
160
161 IPC::TestSink& sink = render_process_host_->sink();
162
163 // Enable extension.
164 sink.ClearMessages();
165 EXPECT_FALSE(IsExtensionLoaded(*extension_));
166 AddExtensionToRegistry(extension_);
167 helper_->OnExtensionLoaded(*extension_);
168 EXPECT_TRUE(
169 IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
170 EXPECT_FALSE(IsExtensionPendingActivationInProcess(
171 *extension_, render_process_host_.get()));
172 ASSERT_EQ(1u, sink.message_count());
Hans Wennborg4b99b0f2017-12-07 17:01:28173 EXPECT_EQ(static_cast<uint32_t>(ExtensionMsg_Loaded::ID),
174 sink.GetMessageAt(0)->type());
karandeepb53c8920d2017-04-06 02:13:07175
176 // Activate extension.
177 sink.ClearMessages();
178 helper_->ActivateExtensionInProcess(*extension_, render_process_host_.get());
179 EXPECT_FALSE(IsExtensionPendingActivationInProcess(
180 *extension_, render_process_host_.get()));
181 ASSERT_EQ(1u, sink.message_count());
Hans Wennborg4b99b0f2017-12-07 17:01:28182 EXPECT_EQ(static_cast<uint32_t>(ExtensionMsg_ActivateExtension::ID),
183 sink.GetMessageAt(0)->type());
karandeepb53c8920d2017-04-06 02:13:07184
185 // Disable extension.
186 sink.ClearMessages();
187 RemoveExtensionFromRegistry(extension_);
188 helper_->OnExtensionUnloaded(*extension_);
189 EXPECT_FALSE(IsExtensionLoaded(*extension_));
190 ASSERT_EQ(1u, sink.message_count());
Hans Wennborg4b99b0f2017-12-07 17:01:28191 EXPECT_EQ(static_cast<uint32_t>(ExtensionMsg_Unloaded::ID),
192 sink.GetMessageAt(0)->type());
karandeepb53c8920d2017-04-06 02:13:07193
194 // Extension enabled again.
195 sink.ClearMessages();
196 AddExtensionToRegistry(extension_);
197 helper_->OnExtensionLoaded(*extension_);
198 EXPECT_TRUE(
199 IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
200 EXPECT_FALSE(IsExtensionPendingActivationInProcess(
201 *extension_, render_process_host_.get()));
202 ASSERT_EQ(1u, sink.message_count());
Hans Wennborg4b99b0f2017-12-07 17:01:28203 EXPECT_EQ(static_cast<uint32_t>(ExtensionMsg_Loaded::ID),
204 sink.GetMessageAt(0)->type());
karandeepb53c8920d2017-04-06 02:13:07205
206 // Render Process terminated.
207 SimulateRenderProcessTerminated(render_process_host_.get());
208 EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
209 EXPECT_TRUE(IsExtensionLoaded(*extension_));
210 EXPECT_FALSE(
211 IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
212 EXPECT_FALSE(IsExtensionPendingActivationInProcess(
213 *extension_, render_process_host_.get()));
214}
215
216// Tests that activating an extension in an uninitialized render process works
217// fine.
218TEST_F(RendererStartupHelperTest, EnabledBeforeProcessInitialized) {
219 EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
220 IPC::TestSink& sink = render_process_host_->sink();
221
222 // Enable extension. The render process isn't initialized yet, so the
223 // extension should be added to the list of extensions awaiting activation.
224 sink.ClearMessages();
225 AddExtensionToRegistry(extension_);
226 helper_->OnExtensionLoaded(*extension_);
227 helper_->ActivateExtensionInProcess(*extension_, render_process_host_.get());
228 EXPECT_EQ(0u, sink.message_count());
229 EXPECT_TRUE(IsExtensionLoaded(*extension_));
230 EXPECT_FALSE(
231 IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
232 EXPECT_TRUE(IsExtensionPendingActivationInProcess(
233 *extension_, render_process_host_.get()));
234
235 // Initialize the render process.
236 SimulateRenderProcessCreated(render_process_host_.get());
237 // The renderer would have been sent multiple initialization messages
238 // including the loading and activation messages for the extension.
239 EXPECT_LE(2u, sink.message_count());
240 EXPECT_TRUE(IsProcessInitialized(render_process_host_.get()));
241 EXPECT_TRUE(
242 IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
243 EXPECT_FALSE(IsExtensionPendingActivationInProcess(
244 *extension_, render_process_host_.get()));
245}
246
247// Tests that themes aren't loaded in a renderer process.
248TEST_F(RendererStartupHelperTest, LoadTheme) {
249 // Initialize render process.
250 EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
251 SimulateRenderProcessCreated(render_process_host_.get());
252 EXPECT_TRUE(IsProcessInitialized(render_process_host_.get()));
253
Devlin Cronin8e5892f2018-10-04 00:13:43254 scoped_refptr<const Extension> extension(CreateTheme("theme"));
karandeepb53c8920d2017-04-06 02:13:07255 ASSERT_TRUE(extension->is_theme());
256
257 IPC::TestSink& sink = render_process_host_->sink();
258
259 // Enable the theme. Verify it isn't loaded and activated in the renderer.
260 sink.ClearMessages();
261 EXPECT_FALSE(IsExtensionLoaded(*extension));
262 AddExtensionToRegistry(extension_);
263 helper_->OnExtensionLoaded(*extension);
264 EXPECT_EQ(0u, sink.message_count());
265 EXPECT_TRUE(IsExtensionLoaded(*extension));
266 EXPECT_FALSE(
267 IsExtensionLoadedInProcess(*extension, render_process_host_.get()));
268
269 helper_->ActivateExtensionInProcess(*extension, render_process_host_.get());
270 EXPECT_EQ(0u, sink.message_count());
271 EXPECT_FALSE(IsExtensionPendingActivationInProcess(
272 *extension, render_process_host_.get()));
273
274 helper_->OnExtensionUnloaded(*extension);
275 EXPECT_EQ(0u, sink.message_count());
276 EXPECT_FALSE(IsExtensionLoaded(*extension));
277}
278
karandeepb18ab4ab82017-04-07 00:27:31279// Tests that only incognito-enabled extensions are loaded in an incognito
280// context.
281TEST_F(RendererStartupHelperTest, ExtensionInIncognitoRenderer) {
282 // Initialize the incognito renderer.
283 EXPECT_FALSE(IsProcessInitialized(incognito_render_process_host_.get()));
284 SimulateRenderProcessCreated(incognito_render_process_host_.get());
285 EXPECT_TRUE(IsProcessInitialized(incognito_render_process_host_.get()));
286
287 IPC::TestSink& sink = render_process_host_->sink();
288 IPC::TestSink& incognito_sink = incognito_render_process_host_->sink();
289
290 // Enable the extension. It should not be loaded in the initialized incognito
291 // renderer.
292 sink.ClearMessages();
293 incognito_sink.ClearMessages();
294 EXPECT_FALSE(util::IsIncognitoEnabled(extension_->id(), browser_context()));
295 EXPECT_FALSE(IsExtensionLoaded(*extension_));
296 AddExtensionToRegistry(extension_);
297 helper_->OnExtensionLoaded(*extension_);
298 EXPECT_EQ(0u, sink.message_count());
299 EXPECT_EQ(0u, incognito_sink.message_count());
300 EXPECT_TRUE(IsExtensionLoaded(*extension_));
301 EXPECT_FALSE(IsExtensionLoadedInProcess(
302 *extension_, incognito_render_process_host_.get()));
303 EXPECT_FALSE(
304 IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
305
306 // Initialize the normal renderer. The extension should get loaded in it.
307 sink.ClearMessages();
308 incognito_sink.ClearMessages();
309 EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
310 SimulateRenderProcessCreated(render_process_host_.get());
311 EXPECT_TRUE(IsProcessInitialized(render_process_host_.get()));
312 EXPECT_TRUE(
313 IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
314 EXPECT_FALSE(IsExtensionLoadedInProcess(
315 *extension_, incognito_render_process_host_.get()));
316 // Multiple initialization messages including the extension load message
317 // should be dispatched to the non-incognito renderer.
318 EXPECT_LE(1u, sink.message_count());
319 EXPECT_EQ(0u, incognito_sink.message_count());
320
321 // Enable the extension in incognito mode. This will reload the extension.
322 sink.ClearMessages();
323 incognito_sink.ClearMessages();
324 ExtensionPrefs::Get(browser_context())
325 ->SetIsIncognitoEnabled(extension_->id(), true);
326 helper_->OnExtensionUnloaded(*extension_);
327 helper_->OnExtensionLoaded(*extension_);
328 EXPECT_TRUE(IsExtensionLoadedInProcess(*extension_,
329 incognito_render_process_host_.get()));
330 EXPECT_TRUE(
331 IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
332 // The extension would not have been unloaded from the incognito renderer
333 // since it wasn't loaded.
334 ASSERT_EQ(1u, incognito_sink.message_count());
Hans Wennborg4b99b0f2017-12-07 17:01:28335 EXPECT_EQ(static_cast<uint32_t>(ExtensionMsg_Loaded::ID),
336 incognito_sink.GetMessageAt(0)->type());
karandeepb18ab4ab82017-04-07 00:27:31337 // The extension would be first unloaded and then loaded from the normal
338 // renderer.
339 ASSERT_EQ(2u, sink.message_count());
Hans Wennborg4b99b0f2017-12-07 17:01:28340 EXPECT_EQ(static_cast<uint32_t>(ExtensionMsg_Unloaded::ID),
341 sink.GetMessageAt(0)->type());
342 EXPECT_EQ(static_cast<uint32_t>(ExtensionMsg_Loaded::ID),
343 sink.GetMessageAt(1)->type());
karandeepb18ab4ab82017-04-07 00:27:31344}
345
346// Tests that platform apps are always loaded in an incognito renderer.
347TEST_F(RendererStartupHelperTest, PlatformAppInIncognitoRenderer) {
348 // Initialize the incognito renderer.
349 EXPECT_FALSE(IsProcessInitialized(incognito_render_process_host_.get()));
350 SimulateRenderProcessCreated(incognito_render_process_host_.get());
351 EXPECT_TRUE(IsProcessInitialized(incognito_render_process_host_.get()));
352
353 IPC::TestSink& incognito_sink = incognito_render_process_host_->sink();
354
Devlin Cronin8e5892f2018-10-04 00:13:43355 scoped_refptr<const Extension> platform_app(
356 CreatePlatformApp("platform_app"));
karandeepb18ab4ab82017-04-07 00:27:31357 ASSERT_TRUE(platform_app->is_platform_app());
358 EXPECT_FALSE(util::IsIncognitoEnabled(platform_app->id(), browser_context()));
359 EXPECT_FALSE(util::CanBeIncognitoEnabled(platform_app.get()));
360
361 // Enable the app. It should get loaded in the incognito renderer even though
362 // IsIncognitoEnabled returns false for it, since it can't be enabled for
363 // incognito.
364 incognito_sink.ClearMessages();
365 AddExtensionToRegistry(platform_app);
366 helper_->OnExtensionLoaded(*platform_app);
367 EXPECT_TRUE(IsExtensionLoadedInProcess(*platform_app,
368 incognito_render_process_host_.get()));
369 ASSERT_EQ(1u, incognito_sink.message_count());
Hans Wennborg4b99b0f2017-12-07 17:01:28370 EXPECT_EQ(static_cast<uint32_t>(ExtensionMsg_Loaded::ID),
371 incognito_sink.GetMessageAt(0)->type());
karandeepb18ab4ab82017-04-07 00:27:31372}
373
karandeepb53c8920d2017-04-06 02:13:07374} // namespace extensions