blob: 2d29d50a97d089d53d42c4b80db78f8165a2ce5f [file] [log] [blame]
[email protected]44366da12014-01-28 01:38:411// Copyright 2014 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
Takashi Toyoshima69579072018-11-19 07:10:507#include <utility>
8#include <vector>
9
10#include "base/bind_helpers.h"
karandeepb53c8920d2017-04-06 02:13:0711#include "base/debug/dump_without_crashing.h"
Takashi Toyoshima69579072018-11-19 07:10:5012#include "base/feature_list.h"
karandeepb53c8920d2017-04-06 02:13:0713#include "base/stl_util.h"
14#include "base/strings/string_util.h"
[email protected]44366da12014-01-28 01:38:4115#include "base/values.h"
[email protected]b33f0b112014-03-13 17:05:3016#include "components/keyed_service/content/browser_context_dependency_manager.h"
karandeepb18ab4ab82017-04-07 00:27:3117#include "content/public/browser/browser_context.h"
[email protected]44366da12014-01-28 01:38:4118#include "content/public/browser/notification_service.h"
19#include "content/public/browser/notification_types.h"
20#include "content/public/browser/render_process_host.h"
[email protected]0b9de032014-03-15 05:47:0121#include "extensions/browser/extension_function_dispatcher.h"
[email protected]44366da12014-01-28 01:38:4122#include "extensions/browser/extension_registry.h"
karandeepb18ab4ab82017-04-07 00:27:3123#include "extensions/browser/extension_util.h"
[email protected]44366da12014-01-28 01:38:4124#include "extensions/browser/extensions_browser_client.h"
paulmeyerad727fc62015-09-09 15:29:5925#include "extensions/browser/guest_view/web_view/web_view_guest.h"
Takashi Toyoshima69579072018-11-19 07:10:5026#include "extensions/common/cors_util.h"
[email protected]fb820c02014-03-13 15:07:0827#include "extensions/common/extension_messages.h"
[email protected]44366da12014-01-28 01:38:4128#include "extensions/common/extension_set.h"
29#include "extensions/common/extensions_client.h"
rdevlin.croninea63fff2016-07-18 15:49:3530#include "extensions/common/features/feature_channel.h"
tbarzicc34cf3c2016-09-09 20:15:0931#include "extensions/common/features/feature_session_type.h"
nrpetere33d2a5b2017-04-25 00:12:3132#include "extensions/common/permissions/permissions_data.h"
[email protected]44366da12014-01-28 01:38:4133#include "ui/base/webui/web_ui_util.h"
Takashi Toyoshima69579072018-11-19 07:10:5034#include "url/origin.h"
[email protected]44366da12014-01-28 01:38:4135
36using content::BrowserContext;
37
38namespace extensions {
39
karandeepb18ab4ab82017-04-07 00:27:3140namespace {
41
42// Returns whether the |extension| should be loaded in the given
43// |browser_context|.
44bool IsExtensionVisibleToContext(const Extension& extension,
45 content::BrowserContext* browser_context) {
46 // Renderers don't need to know about themes.
47 if (extension.is_theme())
48 return false;
49
50 // Only extensions enabled in incognito mode should be loaded in an incognito
51 // renderer. However extensions which can't be enabled in the incognito mode
52 // (e.g. platform apps) should also be loaded in an incognito renderer to
53 // ensure connections from incognito tabs to such extensions work.
54 return !browser_context->IsOffTheRecord() ||
55 !util::CanBeIncognitoEnabled(&extension) ||
56 util::IsIncognitoEnabled(extension.id(), browser_context);
57}
58
59} // namespace
60
[email protected]44366da12014-01-28 01:38:4161RendererStartupHelper::RendererStartupHelper(BrowserContext* browser_context)
62 : browser_context_(browser_context) {
63 DCHECK(browser_context);
64 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED,
65 content::NotificationService::AllBrowserContextsAndSources());
rdevlin.cronin5e510e802016-07-26 15:09:2066 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
67 content::NotificationService::AllBrowserContextsAndSources());
karandeepb53c8920d2017-04-06 02:13:0768 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
69 content::NotificationService::AllBrowserContextsAndSources());
[email protected]44366da12014-01-28 01:38:4170}
71
72RendererStartupHelper::~RendererStartupHelper() {}
73
74void RendererStartupHelper::Observe(
75 int type,
76 const content::NotificationSource& source,
77 const content::NotificationDetails& details) {
rdevlin.cronin5e510e802016-07-26 15:09:2078 switch (type) {
79 case content::NOTIFICATION_RENDERER_PROCESS_CREATED:
80 InitializeProcess(
81 content::Source<content::RenderProcessHost>(source).ptr());
82 break;
83 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED:
karandeepb53c8920d2017-04-06 02:13:0784 // Fall through.
85 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED:
86 // This is needed to take care of the case when a RenderProcessHost is
87 // reused for a different renderer process.
rdevlin.cronin5e510e802016-07-26 15:09:2088 UntrackProcess(content::Source<content::RenderProcessHost>(source).ptr());
89 break;
90 default:
91 NOTREACHED() << "Unexpected notification: " << type;
92 }
93}
94
95void RendererStartupHelper::InitializeProcess(
96 content::RenderProcessHost* process) {
rdevlin.cronin6fba7ec2016-06-24 16:15:0597 ExtensionsBrowserClient* client = ExtensionsBrowserClient::Get();
98 if (!client->IsSameContext(browser_context_, process->GetBrowserContext()))
kalman8bcbc7592015-06-03 23:12:2799 return;
[email protected]44366da12014-01-28 01:38:41100
rdevlin.cronin6fba7ec2016-06-24 16:15:05101 bool activity_logging_enabled =
102 client->IsActivityLoggingEnabled(process->GetBrowserContext());
103 // We only send the ActivityLoggingEnabled message if it is enabled; otherwise
104 // the default (not enabled) is correct.
105 if (activity_logging_enabled) {
106 process->Send(
107 new ExtensionMsg_SetActivityLoggingEnabled(activity_logging_enabled));
108 }
109
tbarzicc34cf3c2016-09-09 20:15:09110 // Extensions need to know the channel and the session type for API
111 // restrictions. The values are sent to all renderers, as the non-extension
112 // renderers may have content scripts.
tbarzic8e89b0b12017-06-10 03:25:51113 bool is_lock_screen_context =
114 client->IsLockScreenContext(process->GetBrowserContext());
115 process->Send(new ExtensionMsg_SetSessionInfo(GetCurrentChannel(),
116 GetCurrentFeatureSessionType(),
117 is_lock_screen_context));
rdevlin.croninea63fff2016-07-18 15:49:35118
kalman8bcbc7592015-06-03 23:12:27119 // Platform apps need to know the system font.
120 // TODO(dbeam): this is not the system font in all cases.
121 process->Send(new ExtensionMsg_SetSystemFont(webui::GetFontFamily(),
122 webui::GetFontSize()));
[email protected]44366da12014-01-28 01:38:41123
kalman8bcbc7592015-06-03 23:12:27124 // Scripting whitelist. This is modified by tests and must be communicated
125 // to renderers.
126 process->Send(new ExtensionMsg_SetScriptingWhitelist(
127 extensions::ExtensionsClient::Get()->GetScriptingWhitelist()));
[email protected]44366da12014-01-28 01:38:41128
paulmeyerad727fc62015-09-09 15:29:59129 // If the new render process is a WebView guest process, propagate the WebView
130 // partition ID to it.
131 std::string webview_partition_id = WebViewGuest::GetPartitionID(process);
132 if (!webview_partition_id.empty()) {
133 process->Send(new ExtensionMsg_SetWebViewPartitionID(
134 WebViewGuest::GetPartitionID(process)));
135 }
136
nrpetere33d2a5b2017-04-25 00:12:31137 // Load default policy_blocked_hosts and policy_allowed_hosts settings, part
138 // of the ExtensionSettings policy.
139 ExtensionMsg_UpdateDefaultPolicyHostRestrictions_Params params;
140 params.default_policy_blocked_hosts =
Karan Bhatiab9199bcc2019-07-12 23:35:53141 PermissionsData::default_policy_blocked_hosts();
nrpetere33d2a5b2017-04-25 00:12:31142 params.default_policy_allowed_hosts =
Karan Bhatiab9199bcc2019-07-12 23:35:53143 PermissionsData::default_policy_allowed_hosts();
nrpetere33d2a5b2017-04-25 00:12:31144 process->Send(new ExtensionMsg_UpdateDefaultPolicyHostRestrictions(params));
145
kalman8bcbc7592015-06-03 23:12:27146 // Loaded extensions.
147 std::vector<ExtensionMsg_Loaded_Params> loaded_extensions;
karandeepb18ab4ab82017-04-07 00:27:31148 BrowserContext* renderer_context = process->GetBrowserContext();
kalman8bcbc7592015-06-03 23:12:27149 const ExtensionSet& extensions =
150 ExtensionRegistry::Get(browser_context_)->enabled_extensions();
151 for (const auto& ext : extensions) {
karandeepb53c8920d2017-04-06 02:13:07152 // OnLoadedExtension should have already been called for the extension.
Jan Wilken Dörrie0fd53a22019-06-07 09:55:46153 DCHECK(base::Contains(extension_process_map_, ext->id()));
154 DCHECK(!base::Contains(extension_process_map_[ext->id()], process));
karandeepb53c8920d2017-04-06 02:13:07155
karandeepb18ab4ab82017-04-07 00:27:31156 if (!IsExtensionVisibleToContext(*ext, renderer_context))
157 continue;
158
159 // TODO(kalman): Only include tab specific permissions for extension
160 // processes, no other process needs it, so it's mildly wasteful.
161 // I am not sure this is possible to know this here, at such a low
162 // level of the stack. Perhaps site isolation can help.
163 bool include_tab_permissions = true;
164 loaded_extensions.push_back(
165 ExtensionMsg_Loaded_Params(ext.get(), include_tab_permissions));
166 extension_process_map_[ext->id()].insert(process);
[email protected]44366da12014-01-28 01:38:41167 }
karandeepb18ab4ab82017-04-07 00:27:31168
169 // Activate pending extensions.
kalman8bcbc7592015-06-03 23:12:27170 process->Send(new ExtensionMsg_Loaded(loaded_extensions));
rdevlin.cronin5e510e802016-07-26 15:09:20171 auto iter = pending_active_extensions_.find(process);
172 if (iter != pending_active_extensions_.end()) {
173 for (const ExtensionId& id : iter->second) {
karandeepb53c8920d2017-04-06 02:13:07174 // The extension should be loaded in the process.
rdevlin.cronin5e510e802016-07-26 15:09:20175 DCHECK(extensions.Contains(id));
Jan Wilken Dörrie0fd53a22019-06-07 09:55:46176 DCHECK(base::Contains(extension_process_map_, id));
177 DCHECK(base::Contains(extension_process_map_[id], process));
rdevlin.cronin5e510e802016-07-26 15:09:20178 process->Send(new ExtensionMsg_ActivateExtension(id));
179 }
180 }
181
182 initialized_processes_.insert(process);
karandeepb53c8920d2017-04-06 02:13:07183 pending_active_extensions_.erase(process);
rdevlin.cronin5e510e802016-07-26 15:09:20184}
185
186void RendererStartupHelper::UntrackProcess(
187 content::RenderProcessHost* process) {
188 if (!ExtensionsBrowserClient::Get()->IsSameContext(
189 browser_context_, process->GetBrowserContext())) {
190 return;
191 }
192
193 initialized_processes_.erase(process);
194 pending_active_extensions_.erase(process);
karandeepb53c8920d2017-04-06 02:13:07195 for (auto& extension_process_pair : extension_process_map_)
196 extension_process_pair.second.erase(process);
rdevlin.cronin5e510e802016-07-26 15:09:20197}
198
199void RendererStartupHelper::ActivateExtensionInProcess(
rdevlin.croninc40d39f2016-08-04 23:42:13200 const Extension& extension,
rdevlin.cronin5e510e802016-07-26 15:09:20201 content::RenderProcessHost* process) {
karandeepb53c8920d2017-04-06 02:13:07202 // The extension should have been loaded already. Dump without crashing to
203 // debug crbug.com/528026.
Jan Wilken Dörrie0fd53a22019-06-07 09:55:46204 if (!base::Contains(extension_process_map_, extension.id())) {
karandeepb53c8920d2017-04-06 02:13:07205#if DCHECK_IS_ON()
206 NOTREACHED() << "Extension " << extension.id()
207 << "activated before loading";
208#else
209 base::debug::DumpWithoutCrashing();
210 return;
211#endif
212 }
213
karandeepb18ab4ab82017-04-07 00:27:31214 if (!IsExtensionVisibleToContext(extension, process->GetBrowserContext()))
rdevlin.croninc40d39f2016-08-04 23:42:13215 return;
216
Jan Wilken Dörrie0fd53a22019-06-07 09:55:46217 if (base::Contains(initialized_processes_, process)) {
218 DCHECK(base::Contains(extension_process_map_[extension.id()], process));
rdevlin.croninc40d39f2016-08-04 23:42:13219 process->Send(new ExtensionMsg_ActivateExtension(extension.id()));
karandeepb53c8920d2017-04-06 02:13:07220 } else {
rdevlin.croninc40d39f2016-08-04 23:42:13221 pending_active_extensions_[process].insert(extension.id());
karandeepb53c8920d2017-04-06 02:13:07222 }
rdevlin.cronin5e510e802016-07-26 15:09:20223}
224
225void RendererStartupHelper::OnExtensionLoaded(const Extension& extension) {
karandeepb53c8920d2017-04-06 02:13:07226 // Extension was already loaded.
227 // TODO(crbug.com/708230): Ensure that clients don't call this for an
228 // already loaded extension and change this to a DCHECK.
Jan Wilken Dörrie0fd53a22019-06-07 09:55:46229 if (base::Contains(extension_process_map_, extension.id()))
karandeepb53c8920d2017-04-06 02:13:07230 return;
231
232 // Mark the extension as loaded.
233 std::set<content::RenderProcessHost*>& loaded_process_set =
234 extension_process_map_[extension.id()];
235
karandeepb18ab4ab82017-04-07 00:27:31236 // IsExtensionVisibleToContext() would filter out themes, but we choose to
237 // return early for performance reasons.
rdevlin.cronin5e510e802016-07-26 15:09:20238 if (extension.is_theme())
239 return;
240
Takashi Toyoshima69579072018-11-19 07:10:50241 // Registers the initial origin access lists to the BrowserContext
242 // asynchronously.
243 url::Origin extension_origin = url::Origin::Create(extension.url());
244 std::vector<network::mojom::CorsOriginPatternPtr> allow_list =
Takashi Toyoshima20c20cf2019-02-27 07:39:23245 CreateCorsOriginAccessAllowList(
246 extension,
247 PermissionsData::EffectiveHostPermissionsMode::kOmitTabSpecific);
Takashi Toyoshima0190a3b2019-01-21 07:39:06248 browser_context_->SetCorsOriginAccessListForOrigin(
249 extension_origin, std::move(allow_list),
Takashi Toyoshima69579072018-11-19 07:10:50250 CreateCorsOriginAccessBlockList(extension), base::DoNothing::Once());
251
rdevlin.cronin5e510e802016-07-26 15:09:20252 // We don't need to include tab permisisons here, since the extension
253 // was just loaded.
254 // Uninitialized renderers will be informed of the extension load during the
255 // first batch of messages.
Istiaque Ahmedabb887f2018-07-12 16:59:51256 std::vector<ExtensionMsg_Loaded_Params> params;
257 params.emplace_back(&extension, false /* no tab permissions */);
258
karandeepb53c8920d2017-04-06 02:13:07259 for (content::RenderProcessHost* process : initialized_processes_) {
karandeepb18ab4ab82017-04-07 00:27:31260 if (!IsExtensionVisibleToContext(extension, process->GetBrowserContext()))
261 continue;
rdevlin.cronin5e510e802016-07-26 15:09:20262 process->Send(new ExtensionMsg_Loaded(params));
karandeepb53c8920d2017-04-06 02:13:07263 loaded_process_set.insert(process);
264 }
rdevlin.cronin5e510e802016-07-26 15:09:20265}
266
rdevlin.croninc40d39f2016-08-04 23:42:13267void RendererStartupHelper::OnExtensionUnloaded(const Extension& extension) {
karandeepb53c8920d2017-04-06 02:13:07268 // Extension is not loaded.
269 // TODO(crbug.com/708230): Ensure that clients call this for a loaded
270 // extension only and change this to a DCHECK.
Jan Wilken Dörrie0fd53a22019-06-07 09:55:46271 if (!base::Contains(extension_process_map_, extension.id()))
rdevlin.croninc40d39f2016-08-04 23:42:13272 return;
273
karandeepb53c8920d2017-04-06 02:13:07274 const std::set<content::RenderProcessHost*>& loaded_process_set =
275 extension_process_map_[extension.id()];
276 for (content::RenderProcessHost* process : loaded_process_set) {
Jan Wilken Dörrie0fd53a22019-06-07 09:55:46277 DCHECK(base::Contains(initialized_processes_, process));
rdevlin.croninc40d39f2016-08-04 23:42:13278 process->Send(new ExtensionMsg_Unloaded(extension.id()));
karandeepb53c8920d2017-04-06 02:13:07279 }
280
Takashi Toyoshima69579072018-11-19 07:10:50281 // Resets registered origin access lists in the BrowserContext asynchronously.
282 url::Origin extension_origin = url::Origin::Create(extension.url());
Takashi Toyoshima0190a3b2019-01-21 07:39:06283 browser_context_->SetCorsOriginAccessListForOrigin(
284 extension_origin, std::vector<network::mojom::CorsOriginPatternPtr>(),
Takashi Toyoshima69579072018-11-19 07:10:50285 std::vector<network::mojom::CorsOriginPatternPtr>(),
286 base::DoNothing::Once());
287
rdevlin.cronin5e510e802016-07-26 15:09:20288 for (auto& process_extensions_pair : pending_active_extensions_)
rdevlin.croninc40d39f2016-08-04 23:42:13289 process_extensions_pair.second.erase(extension.id());
karandeepb53c8920d2017-04-06 02:13:07290
291 // Mark the extension as unloaded.
292 extension_process_map_.erase(extension.id());
[email protected]44366da12014-01-28 01:38:41293}
294
295//////////////////////////////////////////////////////////////////////////////
296
297// static
298RendererStartupHelper* RendererStartupHelperFactory::GetForBrowserContext(
299 BrowserContext* context) {
300 return static_cast<RendererStartupHelper*>(
301 GetInstance()->GetServiceForBrowserContext(context, true));
302}
303
304// static
305RendererStartupHelperFactory* RendererStartupHelperFactory::GetInstance() {
olli.raula36aa8be2015-09-10 11:14:22306 return base::Singleton<RendererStartupHelperFactory>::get();
[email protected]44366da12014-01-28 01:38:41307}
308
309RendererStartupHelperFactory::RendererStartupHelperFactory()
310 : BrowserContextKeyedServiceFactory(
311 "RendererStartupHelper",
312 BrowserContextDependencyManager::GetInstance()) {
313 // No dependencies on other services.
314}
315
316RendererStartupHelperFactory::~RendererStartupHelperFactory() {}
317
[email protected]b33f0b112014-03-13 17:05:30318KeyedService* RendererStartupHelperFactory::BuildServiceInstanceFor(
[email protected]44366da12014-01-28 01:38:41319 content::BrowserContext* context) const {
320 return new RendererStartupHelper(context);
321}
322
323BrowserContext* RendererStartupHelperFactory::GetBrowserContextToUse(
324 BrowserContext* context) const {
325 // Redirected in incognito.
326 return ExtensionsBrowserClient::Get()->GetOriginalContext(context);
327}
328
329bool RendererStartupHelperFactory::ServiceIsCreatedWithBrowserContext() const {
330 return true;
331}
332
333} // namespace extensions