blob: 97856e6f6139413412159341bb875e02ee4f9d86 [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"
Takashi Toyoshima69579072018-11-19 07:10:5033#include "services/network/public/cpp/features.h"
[email protected]44366da12014-01-28 01:38:4134#include "ui/base/webui/web_ui_util.h"
Takashi Toyoshima69579072018-11-19 07:10:5035#include "url/origin.h"
[email protected]44366da12014-01-28 01:38:4136
37using content::BrowserContext;
38
39namespace extensions {
40
karandeepb18ab4ab82017-04-07 00:27:3141namespace {
42
43// Returns whether the |extension| should be loaded in the given
44// |browser_context|.
45bool IsExtensionVisibleToContext(const Extension& extension,
46 content::BrowserContext* browser_context) {
47 // Renderers don't need to know about themes.
48 if (extension.is_theme())
49 return false;
50
51 // Only extensions enabled in incognito mode should be loaded in an incognito
52 // renderer. However extensions which can't be enabled in the incognito mode
53 // (e.g. platform apps) should also be loaded in an incognito renderer to
54 // ensure connections from incognito tabs to such extensions work.
55 return !browser_context->IsOffTheRecord() ||
56 !util::CanBeIncognitoEnabled(&extension) ||
57 util::IsIncognitoEnabled(extension.id(), browser_context);
58}
59
60} // namespace
61
[email protected]44366da12014-01-28 01:38:4162RendererStartupHelper::RendererStartupHelper(BrowserContext* browser_context)
63 : browser_context_(browser_context) {
64 DCHECK(browser_context);
65 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED,
66 content::NotificationService::AllBrowserContextsAndSources());
rdevlin.cronin5e510e802016-07-26 15:09:2067 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
68 content::NotificationService::AllBrowserContextsAndSources());
karandeepb53c8920d2017-04-06 02:13:0769 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
70 content::NotificationService::AllBrowserContextsAndSources());
[email protected]44366da12014-01-28 01:38:4171}
72
73RendererStartupHelper::~RendererStartupHelper() {}
74
75void RendererStartupHelper::Observe(
76 int type,
77 const content::NotificationSource& source,
78 const content::NotificationDetails& details) {
rdevlin.cronin5e510e802016-07-26 15:09:2079 switch (type) {
80 case content::NOTIFICATION_RENDERER_PROCESS_CREATED:
81 InitializeProcess(
82 content::Source<content::RenderProcessHost>(source).ptr());
83 break;
84 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED:
karandeepb53c8920d2017-04-06 02:13:0785 // Fall through.
86 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED:
87 // This is needed to take care of the case when a RenderProcessHost is
88 // reused for a different renderer process.
rdevlin.cronin5e510e802016-07-26 15:09:2089 UntrackProcess(content::Source<content::RenderProcessHost>(source).ptr());
90 break;
91 default:
92 NOTREACHED() << "Unexpected notification: " << type;
93 }
94}
95
96void RendererStartupHelper::InitializeProcess(
97 content::RenderProcessHost* process) {
rdevlin.cronin6fba7ec2016-06-24 16:15:0598 ExtensionsBrowserClient* client = ExtensionsBrowserClient::Get();
99 if (!client->IsSameContext(browser_context_, process->GetBrowserContext()))
kalman8bcbc7592015-06-03 23:12:27100 return;
[email protected]44366da12014-01-28 01:38:41101
rdevlin.cronin6fba7ec2016-06-24 16:15:05102 bool activity_logging_enabled =
103 client->IsActivityLoggingEnabled(process->GetBrowserContext());
104 // We only send the ActivityLoggingEnabled message if it is enabled; otherwise
105 // the default (not enabled) is correct.
106 if (activity_logging_enabled) {
107 process->Send(
108 new ExtensionMsg_SetActivityLoggingEnabled(activity_logging_enabled));
109 }
110
tbarzicc34cf3c2016-09-09 20:15:09111 // Extensions need to know the channel and the session type for API
112 // restrictions. The values are sent to all renderers, as the non-extension
113 // renderers may have content scripts.
tbarzic8e89b0b12017-06-10 03:25:51114 bool is_lock_screen_context =
115 client->IsLockScreenContext(process->GetBrowserContext());
116 process->Send(new ExtensionMsg_SetSessionInfo(GetCurrentChannel(),
117 GetCurrentFeatureSessionType(),
118 is_lock_screen_context));
rdevlin.croninea63fff2016-07-18 15:49:35119
kalman8bcbc7592015-06-03 23:12:27120 // Platform apps need to know the system font.
121 // TODO(dbeam): this is not the system font in all cases.
122 process->Send(new ExtensionMsg_SetSystemFont(webui::GetFontFamily(),
123 webui::GetFontSize()));
[email protected]44366da12014-01-28 01:38:41124
kalman8bcbc7592015-06-03 23:12:27125 // Scripting whitelist. This is modified by tests and must be communicated
126 // to renderers.
127 process->Send(new ExtensionMsg_SetScriptingWhitelist(
128 extensions::ExtensionsClient::Get()->GetScriptingWhitelist()));
[email protected]44366da12014-01-28 01:38:41129
paulmeyerad727fc62015-09-09 15:29:59130 // If the new render process is a WebView guest process, propagate the WebView
131 // partition ID to it.
132 std::string webview_partition_id = WebViewGuest::GetPartitionID(process);
133 if (!webview_partition_id.empty()) {
134 process->Send(new ExtensionMsg_SetWebViewPartitionID(
135 WebViewGuest::GetPartitionID(process)));
136 }
137
nrpetere33d2a5b2017-04-25 00:12:31138 // Load default policy_blocked_hosts and policy_allowed_hosts settings, part
139 // of the ExtensionSettings policy.
140 ExtensionMsg_UpdateDefaultPolicyHostRestrictions_Params params;
141 params.default_policy_blocked_hosts =
142 PermissionsData::default_policy_blocked_hosts();
143 params.default_policy_allowed_hosts =
144 PermissionsData::default_policy_allowed_hosts();
145 process->Send(new ExtensionMsg_UpdateDefaultPolicyHostRestrictions(params));
146
kalman8bcbc7592015-06-03 23:12:27147 // Loaded extensions.
148 std::vector<ExtensionMsg_Loaded_Params> loaded_extensions;
karandeepb18ab4ab82017-04-07 00:27:31149 BrowserContext* renderer_context = process->GetBrowserContext();
kalman8bcbc7592015-06-03 23:12:27150 const ExtensionSet& extensions =
151 ExtensionRegistry::Get(browser_context_)->enabled_extensions();
152 for (const auto& ext : extensions) {
karandeepb53c8920d2017-04-06 02:13:07153 // OnLoadedExtension should have already been called for the extension.
154 DCHECK(base::ContainsKey(extension_process_map_, ext->id()));
155 DCHECK(!base::ContainsKey(extension_process_map_[ext->id()], process));
156
karandeepb18ab4ab82017-04-07 00:27:31157 if (!IsExtensionVisibleToContext(*ext, renderer_context))
158 continue;
159
160 // TODO(kalman): Only include tab specific permissions for extension
161 // processes, no other process needs it, so it's mildly wasteful.
162 // I am not sure this is possible to know this here, at such a low
163 // level of the stack. Perhaps site isolation can help.
164 bool include_tab_permissions = true;
165 loaded_extensions.push_back(
166 ExtensionMsg_Loaded_Params(ext.get(), include_tab_permissions));
167 extension_process_map_[ext->id()].insert(process);
[email protected]44366da12014-01-28 01:38:41168 }
karandeepb18ab4ab82017-04-07 00:27:31169
170 // Activate pending extensions.
kalman8bcbc7592015-06-03 23:12:27171 process->Send(new ExtensionMsg_Loaded(loaded_extensions));
rdevlin.cronin5e510e802016-07-26 15:09:20172 auto iter = pending_active_extensions_.find(process);
173 if (iter != pending_active_extensions_.end()) {
174 for (const ExtensionId& id : iter->second) {
karandeepb53c8920d2017-04-06 02:13:07175 // The extension should be loaded in the process.
rdevlin.cronin5e510e802016-07-26 15:09:20176 DCHECK(extensions.Contains(id));
karandeepb53c8920d2017-04-06 02:13:07177 DCHECK(base::ContainsKey(extension_process_map_, id));
178 DCHECK(base::ContainsKey(extension_process_map_[id], process));
rdevlin.cronin5e510e802016-07-26 15:09:20179 process->Send(new ExtensionMsg_ActivateExtension(id));
180 }
181 }
182
183 initialized_processes_.insert(process);
karandeepb53c8920d2017-04-06 02:13:07184 pending_active_extensions_.erase(process);
rdevlin.cronin5e510e802016-07-26 15:09:20185}
186
187void RendererStartupHelper::UntrackProcess(
188 content::RenderProcessHost* process) {
189 if (!ExtensionsBrowserClient::Get()->IsSameContext(
190 browser_context_, process->GetBrowserContext())) {
191 return;
192 }
193
194 initialized_processes_.erase(process);
195 pending_active_extensions_.erase(process);
karandeepb53c8920d2017-04-06 02:13:07196 for (auto& extension_process_pair : extension_process_map_)
197 extension_process_pair.second.erase(process);
rdevlin.cronin5e510e802016-07-26 15:09:20198}
199
200void RendererStartupHelper::ActivateExtensionInProcess(
rdevlin.croninc40d39f2016-08-04 23:42:13201 const Extension& extension,
rdevlin.cronin5e510e802016-07-26 15:09:20202 content::RenderProcessHost* process) {
karandeepb53c8920d2017-04-06 02:13:07203 // The extension should have been loaded already. Dump without crashing to
204 // debug crbug.com/528026.
205 if (!base::ContainsKey(extension_process_map_, extension.id())) {
206#if DCHECK_IS_ON()
207 NOTREACHED() << "Extension " << extension.id()
208 << "activated before loading";
209#else
210 base::debug::DumpWithoutCrashing();
211 return;
212#endif
213 }
214
karandeepb18ab4ab82017-04-07 00:27:31215 if (!IsExtensionVisibleToContext(extension, process->GetBrowserContext()))
rdevlin.croninc40d39f2016-08-04 23:42:13216 return;
217
karandeepb53c8920d2017-04-06 02:13:07218 if (base::ContainsKey(initialized_processes_, process)) {
219 DCHECK(base::ContainsKey(extension_process_map_[extension.id()], process));
rdevlin.croninc40d39f2016-08-04 23:42:13220 process->Send(new ExtensionMsg_ActivateExtension(extension.id()));
karandeepb53c8920d2017-04-06 02:13:07221 } else {
rdevlin.croninc40d39f2016-08-04 23:42:13222 pending_active_extensions_[process].insert(extension.id());
karandeepb53c8920d2017-04-06 02:13:07223 }
rdevlin.cronin5e510e802016-07-26 15:09:20224}
225
226void RendererStartupHelper::OnExtensionLoaded(const Extension& extension) {
karandeepb53c8920d2017-04-06 02:13:07227 // Extension was already loaded.
228 // TODO(crbug.com/708230): Ensure that clients don't call this for an
229 // already loaded extension and change this to a DCHECK.
230 if (base::ContainsKey(extension_process_map_, extension.id()))
231 return;
232
233 // Mark the extension as loaded.
234 std::set<content::RenderProcessHost*>& loaded_process_set =
235 extension_process_map_[extension.id()];
236
karandeepb18ab4ab82017-04-07 00:27:31237 // IsExtensionVisibleToContext() would filter out themes, but we choose to
238 // return early for performance reasons.
rdevlin.cronin5e510e802016-07-26 15:09:20239 if (extension.is_theme())
240 return;
241
Takashi Toyoshima69579072018-11-19 07:10:50242 // Registers the initial origin access lists to the BrowserContext
243 // asynchronously.
244 url::Origin extension_origin = url::Origin::Create(extension.url());
245 std::vector<network::mojom::CorsOriginPatternPtr> allow_list =
246 CreateCorsOriginAccessAllowList(extension);
247 if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
248 ExtensionsClient::Get()->AddOriginAccessPermissions(extension, true,
249 &allow_list);
250 }
251
252 content::BrowserContext::SetCorsOriginAccessListsForOrigin(
253 browser_context_, extension_origin, std::move(allow_list),
254 CreateCorsOriginAccessBlockList(extension), base::DoNothing::Once());
255
rdevlin.cronin5e510e802016-07-26 15:09:20256 // We don't need to include tab permisisons here, since the extension
257 // was just loaded.
258 // Uninitialized renderers will be informed of the extension load during the
259 // first batch of messages.
Istiaque Ahmedabb887f2018-07-12 16:59:51260 std::vector<ExtensionMsg_Loaded_Params> params;
261 params.emplace_back(&extension, false /* no tab permissions */);
262
karandeepb53c8920d2017-04-06 02:13:07263 for (content::RenderProcessHost* process : initialized_processes_) {
karandeepb18ab4ab82017-04-07 00:27:31264 if (!IsExtensionVisibleToContext(extension, process->GetBrowserContext()))
265 continue;
rdevlin.cronin5e510e802016-07-26 15:09:20266 process->Send(new ExtensionMsg_Loaded(params));
karandeepb53c8920d2017-04-06 02:13:07267 loaded_process_set.insert(process);
268 }
rdevlin.cronin5e510e802016-07-26 15:09:20269}
270
rdevlin.croninc40d39f2016-08-04 23:42:13271void RendererStartupHelper::OnExtensionUnloaded(const Extension& extension) {
karandeepb53c8920d2017-04-06 02:13:07272 // Extension is not loaded.
273 // TODO(crbug.com/708230): Ensure that clients call this for a loaded
274 // extension only and change this to a DCHECK.
275 if (!base::ContainsKey(extension_process_map_, extension.id()))
rdevlin.croninc40d39f2016-08-04 23:42:13276 return;
277
karandeepb53c8920d2017-04-06 02:13:07278 const std::set<content::RenderProcessHost*>& loaded_process_set =
279 extension_process_map_[extension.id()];
280 for (content::RenderProcessHost* process : loaded_process_set) {
281 DCHECK(base::ContainsKey(initialized_processes_, process));
rdevlin.croninc40d39f2016-08-04 23:42:13282 process->Send(new ExtensionMsg_Unloaded(extension.id()));
karandeepb53c8920d2017-04-06 02:13:07283 }
284
Takashi Toyoshima69579072018-11-19 07:10:50285 // Resets registered origin access lists in the BrowserContext asynchronously.
286 url::Origin extension_origin = url::Origin::Create(extension.url());
287 content::BrowserContext::SetCorsOriginAccessListsForOrigin(
288 browser_context_, extension_origin,
289 std::vector<network::mojom::CorsOriginPatternPtr>(),
290 std::vector<network::mojom::CorsOriginPatternPtr>(),
291 base::DoNothing::Once());
292
rdevlin.cronin5e510e802016-07-26 15:09:20293 for (auto& process_extensions_pair : pending_active_extensions_)
rdevlin.croninc40d39f2016-08-04 23:42:13294 process_extensions_pair.second.erase(extension.id());
karandeepb53c8920d2017-04-06 02:13:07295
296 // Mark the extension as unloaded.
297 extension_process_map_.erase(extension.id());
[email protected]44366da12014-01-28 01:38:41298}
299
300//////////////////////////////////////////////////////////////////////////////
301
302// static
303RendererStartupHelper* RendererStartupHelperFactory::GetForBrowserContext(
304 BrowserContext* context) {
305 return static_cast<RendererStartupHelper*>(
306 GetInstance()->GetServiceForBrowserContext(context, true));
307}
308
309// static
310RendererStartupHelperFactory* RendererStartupHelperFactory::GetInstance() {
olli.raula36aa8be2015-09-10 11:14:22311 return base::Singleton<RendererStartupHelperFactory>::get();
[email protected]44366da12014-01-28 01:38:41312}
313
314RendererStartupHelperFactory::RendererStartupHelperFactory()
315 : BrowserContextKeyedServiceFactory(
316 "RendererStartupHelper",
317 BrowserContextDependencyManager::GetInstance()) {
318 // No dependencies on other services.
319}
320
321RendererStartupHelperFactory::~RendererStartupHelperFactory() {}
322
[email protected]b33f0b112014-03-13 17:05:30323KeyedService* RendererStartupHelperFactory::BuildServiceInstanceFor(
[email protected]44366da12014-01-28 01:38:41324 content::BrowserContext* context) const {
325 return new RendererStartupHelper(context);
326}
327
328BrowserContext* RendererStartupHelperFactory::GetBrowserContextToUse(
329 BrowserContext* context) const {
330 // Redirected in incognito.
331 return ExtensionsBrowserClient::Get()->GetOriginalContext(context);
332}
333
334bool RendererStartupHelperFactory::ServiceIsCreatedWithBrowserContext() const {
335 return true;
336}
337
338} // namespace extensions