blob: 7afa40f87538175fe5c126abba073fd9e297ac0a [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) {
113 return base::ContainsKey(helper_->initialized_processes_, rph);
114 }
115
116 bool IsExtensionLoaded(const Extension& extension) {
117 return base::ContainsKey(helper_->extension_process_map_, extension.id());
118 }
119
120 bool IsExtensionLoadedInProcess(const Extension& extension,
121 content::RenderProcessHost* rph) {
122 return IsExtensionLoaded(extension) &&
123 base::ContainsKey(helper_->extension_process_map_[extension.id()],
124 rph);
125 }
126
127 bool IsExtensionPendingActivationInProcess(const Extension& extension,
128 content::RenderProcessHost* rph) {
129 return base::ContainsKey(helper_->pending_active_extensions_, rph) &&
130 base::ContainsKey(helper_->pending_active_extensions_[rph],
131 extension.id());
132 }
133
134 std::unique_ptr<RendererStartupHelper> helper_;
135 ExtensionRegistry* registry_; // Weak.
136 std::unique_ptr<content::MockRenderProcessHost> render_process_host_;
karandeepb18ab4ab82017-04-07 00:27:31137 std::unique_ptr<content::MockRenderProcessHost>
138 incognito_render_process_host_;
Devlin Cronin8e5892f2018-10-04 00:13:43139 scoped_refptr<const Extension> extension_;
karandeepb53c8920d2017-04-06 02:13:07140
141 private:
Devlin Cronin8e5892f2018-10-04 00:13:43142 scoped_refptr<const Extension> CreateExtension(
karandeepb18ab4ab82017-04-07 00:27:31143 const std::string& id_input,
144 std::unique_ptr<base::DictionaryValue> manifest) {
145 return ExtensionBuilder()
146 .SetManifest(std::move(manifest))
147 .SetID(crx_file::id_util::GenerateId(id_input))
148 .Build();
149 }
150
karandeepb53c8920d2017-04-06 02:13:07151 DISALLOW_COPY_AND_ASSIGN(RendererStartupHelperTest);
152};
153
154// Tests extension loading, unloading and activation and render process creation
155// and termination.
156TEST_F(RendererStartupHelperTest, NormalExtensionLifecycle) {
157 // Initialize render process.
158 EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
159 SimulateRenderProcessCreated(render_process_host_.get());
160 EXPECT_TRUE(IsProcessInitialized(render_process_host_.get()));
161
162 IPC::TestSink& sink = render_process_host_->sink();
163
164 // Enable extension.
165 sink.ClearMessages();
166 EXPECT_FALSE(IsExtensionLoaded(*extension_));
167 AddExtensionToRegistry(extension_);
168 helper_->OnExtensionLoaded(*extension_);
169 EXPECT_TRUE(
170 IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
171 EXPECT_FALSE(IsExtensionPendingActivationInProcess(
172 *extension_, render_process_host_.get()));
173 ASSERT_EQ(1u, sink.message_count());
Hans Wennborg4b99b0f2017-12-07 17:01:28174 EXPECT_EQ(static_cast<uint32_t>(ExtensionMsg_Loaded::ID),
175 sink.GetMessageAt(0)->type());
karandeepb53c8920d2017-04-06 02:13:07176
177 // Activate extension.
178 sink.ClearMessages();
179 helper_->ActivateExtensionInProcess(*extension_, render_process_host_.get());
180 EXPECT_FALSE(IsExtensionPendingActivationInProcess(
181 *extension_, render_process_host_.get()));
182 ASSERT_EQ(1u, sink.message_count());
Hans Wennborg4b99b0f2017-12-07 17:01:28183 EXPECT_EQ(static_cast<uint32_t>(ExtensionMsg_ActivateExtension::ID),
184 sink.GetMessageAt(0)->type());
karandeepb53c8920d2017-04-06 02:13:07185
186 // Disable extension.
187 sink.ClearMessages();
188 RemoveExtensionFromRegistry(extension_);
189 helper_->OnExtensionUnloaded(*extension_);
190 EXPECT_FALSE(IsExtensionLoaded(*extension_));
191 ASSERT_EQ(1u, sink.message_count());
Hans Wennborg4b99b0f2017-12-07 17:01:28192 EXPECT_EQ(static_cast<uint32_t>(ExtensionMsg_Unloaded::ID),
193 sink.GetMessageAt(0)->type());
karandeepb53c8920d2017-04-06 02:13:07194
195 // Extension enabled again.
196 sink.ClearMessages();
197 AddExtensionToRegistry(extension_);
198 helper_->OnExtensionLoaded(*extension_);
199 EXPECT_TRUE(
200 IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
201 EXPECT_FALSE(IsExtensionPendingActivationInProcess(
202 *extension_, render_process_host_.get()));
203 ASSERT_EQ(1u, sink.message_count());
Hans Wennborg4b99b0f2017-12-07 17:01:28204 EXPECT_EQ(static_cast<uint32_t>(ExtensionMsg_Loaded::ID),
205 sink.GetMessageAt(0)->type());
karandeepb53c8920d2017-04-06 02:13:07206
207 // Render Process terminated.
208 SimulateRenderProcessTerminated(render_process_host_.get());
209 EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
210 EXPECT_TRUE(IsExtensionLoaded(*extension_));
211 EXPECT_FALSE(
212 IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
213 EXPECT_FALSE(IsExtensionPendingActivationInProcess(
214 *extension_, render_process_host_.get()));
215}
216
217// Tests that activating an extension in an uninitialized render process works
218// fine.
219TEST_F(RendererStartupHelperTest, EnabledBeforeProcessInitialized) {
220 EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
221 IPC::TestSink& sink = render_process_host_->sink();
222
223 // Enable extension. The render process isn't initialized yet, so the
224 // extension should be added to the list of extensions awaiting activation.
225 sink.ClearMessages();
226 AddExtensionToRegistry(extension_);
227 helper_->OnExtensionLoaded(*extension_);
228 helper_->ActivateExtensionInProcess(*extension_, render_process_host_.get());
229 EXPECT_EQ(0u, sink.message_count());
230 EXPECT_TRUE(IsExtensionLoaded(*extension_));
231 EXPECT_FALSE(
232 IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
233 EXPECT_TRUE(IsExtensionPendingActivationInProcess(
234 *extension_, render_process_host_.get()));
235
236 // Initialize the render process.
237 SimulateRenderProcessCreated(render_process_host_.get());
238 // The renderer would have been sent multiple initialization messages
239 // including the loading and activation messages for the extension.
240 EXPECT_LE(2u, sink.message_count());
241 EXPECT_TRUE(IsProcessInitialized(render_process_host_.get()));
242 EXPECT_TRUE(
243 IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
244 EXPECT_FALSE(IsExtensionPendingActivationInProcess(
245 *extension_, render_process_host_.get()));
246}
247
248// Tests that themes aren't loaded in a renderer process.
249TEST_F(RendererStartupHelperTest, LoadTheme) {
250 // Initialize render process.
251 EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
252 SimulateRenderProcessCreated(render_process_host_.get());
253 EXPECT_TRUE(IsProcessInitialized(render_process_host_.get()));
254
Devlin Cronin8e5892f2018-10-04 00:13:43255 scoped_refptr<const Extension> extension(CreateTheme("theme"));
karandeepb53c8920d2017-04-06 02:13:07256 ASSERT_TRUE(extension->is_theme());
257
258 IPC::TestSink& sink = render_process_host_->sink();
259
260 // Enable the theme. Verify it isn't loaded and activated in the renderer.
261 sink.ClearMessages();
262 EXPECT_FALSE(IsExtensionLoaded(*extension));
263 AddExtensionToRegistry(extension_);
264 helper_->OnExtensionLoaded(*extension);
265 EXPECT_EQ(0u, sink.message_count());
266 EXPECT_TRUE(IsExtensionLoaded(*extension));
267 EXPECT_FALSE(
268 IsExtensionLoadedInProcess(*extension, render_process_host_.get()));
269
270 helper_->ActivateExtensionInProcess(*extension, render_process_host_.get());
271 EXPECT_EQ(0u, sink.message_count());
272 EXPECT_FALSE(IsExtensionPendingActivationInProcess(
273 *extension, render_process_host_.get()));
274
275 helper_->OnExtensionUnloaded(*extension);
276 EXPECT_EQ(0u, sink.message_count());
277 EXPECT_FALSE(IsExtensionLoaded(*extension));
278}
279
karandeepb18ab4ab82017-04-07 00:27:31280// Tests that only incognito-enabled extensions are loaded in an incognito
281// context.
282TEST_F(RendererStartupHelperTest, ExtensionInIncognitoRenderer) {
283 // Initialize the incognito renderer.
284 EXPECT_FALSE(IsProcessInitialized(incognito_render_process_host_.get()));
285 SimulateRenderProcessCreated(incognito_render_process_host_.get());
286 EXPECT_TRUE(IsProcessInitialized(incognito_render_process_host_.get()));
287
288 IPC::TestSink& sink = render_process_host_->sink();
289 IPC::TestSink& incognito_sink = incognito_render_process_host_->sink();
290
291 // Enable the extension. It should not be loaded in the initialized incognito
292 // renderer.
293 sink.ClearMessages();
294 incognito_sink.ClearMessages();
295 EXPECT_FALSE(util::IsIncognitoEnabled(extension_->id(), browser_context()));
296 EXPECT_FALSE(IsExtensionLoaded(*extension_));
297 AddExtensionToRegistry(extension_);
298 helper_->OnExtensionLoaded(*extension_);
299 EXPECT_EQ(0u, sink.message_count());
300 EXPECT_EQ(0u, incognito_sink.message_count());
301 EXPECT_TRUE(IsExtensionLoaded(*extension_));
302 EXPECT_FALSE(IsExtensionLoadedInProcess(
303 *extension_, incognito_render_process_host_.get()));
304 EXPECT_FALSE(
305 IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
306
307 // Initialize the normal renderer. The extension should get loaded in it.
308 sink.ClearMessages();
309 incognito_sink.ClearMessages();
310 EXPECT_FALSE(IsProcessInitialized(render_process_host_.get()));
311 SimulateRenderProcessCreated(render_process_host_.get());
312 EXPECT_TRUE(IsProcessInitialized(render_process_host_.get()));
313 EXPECT_TRUE(
314 IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
315 EXPECT_FALSE(IsExtensionLoadedInProcess(
316 *extension_, incognito_render_process_host_.get()));
317 // Multiple initialization messages including the extension load message
318 // should be dispatched to the non-incognito renderer.
319 EXPECT_LE(1u, sink.message_count());
320 EXPECT_EQ(0u, incognito_sink.message_count());
321
322 // Enable the extension in incognito mode. This will reload the extension.
323 sink.ClearMessages();
324 incognito_sink.ClearMessages();
325 ExtensionPrefs::Get(browser_context())
326 ->SetIsIncognitoEnabled(extension_->id(), true);
327 helper_->OnExtensionUnloaded(*extension_);
328 helper_->OnExtensionLoaded(*extension_);
329 EXPECT_TRUE(IsExtensionLoadedInProcess(*extension_,
330 incognito_render_process_host_.get()));
331 EXPECT_TRUE(
332 IsExtensionLoadedInProcess(*extension_, render_process_host_.get()));
333 // The extension would not have been unloaded from the incognito renderer
334 // since it wasn't loaded.
335 ASSERT_EQ(1u, incognito_sink.message_count());
Hans Wennborg4b99b0f2017-12-07 17:01:28336 EXPECT_EQ(static_cast<uint32_t>(ExtensionMsg_Loaded::ID),
337 incognito_sink.GetMessageAt(0)->type());
karandeepb18ab4ab82017-04-07 00:27:31338 // The extension would be first unloaded and then loaded from the normal
339 // renderer.
340 ASSERT_EQ(2u, sink.message_count());
Hans Wennborg4b99b0f2017-12-07 17:01:28341 EXPECT_EQ(static_cast<uint32_t>(ExtensionMsg_Unloaded::ID),
342 sink.GetMessageAt(0)->type());
343 EXPECT_EQ(static_cast<uint32_t>(ExtensionMsg_Loaded::ID),
344 sink.GetMessageAt(1)->type());
karandeepb18ab4ab82017-04-07 00:27:31345}
346
347// Tests that platform apps are always loaded in an incognito renderer.
348TEST_F(RendererStartupHelperTest, PlatformAppInIncognitoRenderer) {
349 // Initialize the incognito renderer.
350 EXPECT_FALSE(IsProcessInitialized(incognito_render_process_host_.get()));
351 SimulateRenderProcessCreated(incognito_render_process_host_.get());
352 EXPECT_TRUE(IsProcessInitialized(incognito_render_process_host_.get()));
353
354 IPC::TestSink& incognito_sink = incognito_render_process_host_->sink();
355
Devlin Cronin8e5892f2018-10-04 00:13:43356 scoped_refptr<const Extension> platform_app(
357 CreatePlatformApp("platform_app"));
karandeepb18ab4ab82017-04-07 00:27:31358 ASSERT_TRUE(platform_app->is_platform_app());
359 EXPECT_FALSE(util::IsIncognitoEnabled(platform_app->id(), browser_context()));
360 EXPECT_FALSE(util::CanBeIncognitoEnabled(platform_app.get()));
361
362 // Enable the app. It should get loaded in the incognito renderer even though
363 // IsIncognitoEnabled returns false for it, since it can't be enabled for
364 // incognito.
365 incognito_sink.ClearMessages();
366 AddExtensionToRegistry(platform_app);
367 helper_->OnExtensionLoaded(*platform_app);
368 EXPECT_TRUE(IsExtensionLoadedInProcess(*platform_app,
369 incognito_render_process_host_.get()));
370 ASSERT_EQ(1u, incognito_sink.message_count());
Hans Wennborg4b99b0f2017-12-07 17:01:28371 EXPECT_EQ(static_cast<uint32_t>(ExtensionMsg_Loaded::ID),
372 incognito_sink.GetMessageAt(0)->type());
karandeepb18ab4ab82017-04-07 00:27:31373}
374
karandeepb53c8920d2017-04-06 02:13:07375} // namespace extensions