1 // 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.
5 #include "chrome/browser/extensions/chrome_content_browser_client_extensions_part.h"
9 #include "base/command_line.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/extensions/browser_permissions_policy_delegate.h"
12 #include "chrome/browser/extensions/extension_service.h"
13 #include "chrome/browser/extensions/extension_web_ui.h"
14 #include "chrome/browser/extensions/extension_webkit_preferences.h"
15 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/profiles/profile_io_data.h"
18 #include "chrome/browser/profiles/profile_manager.h"
19 #include "chrome/browser/renderer_host/chrome_extension_message_filter.h"
20 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
21 #include "chrome/common/chrome_constants.h"
22 #include "chrome/common/extensions/extension_process_policy.h"
23 #include "components/guest_view/browser/guest_view_message_filter.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/browser_url_handler.h"
26 #include "content/public/browser/render_process_host.h"
27 #include "content/public/browser/render_view_host.h"
28 #include "content/public/browser/site_instance.h"
29 #include "content/public/browser/web_contents.h"
30 #include "content/public/common/content_switches.h"
31 #include "extensions/browser/api/web_request/web_request_api.h"
32 #include "extensions/browser/api/web_request/web_request_api_helpers.h"
33 #include "extensions/browser/extension_host.h"
34 #include "extensions/browser/extension_message_filter.h"
35 #include "extensions/browser/extension_registry.h"
36 #include "extensions/browser/extension_system.h"
37 #include "extensions/browser/guest_view/extensions_guest_view_message_filter.h"
38 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
39 #include "extensions/browser/info_map.h"
40 #include "extensions/browser/io_thread_extension_message_filter.h"
41 #include "extensions/browser/view_type_utils.h"
42 #include "extensions/common/constants.h"
43 #include "extensions/common/manifest_constants.h"
44 #include "extensions/common/manifest_handlers/app_isolation_info.h"
45 #include "extensions/common/manifest_handlers/background_info.h"
46 #include "extensions/common/manifest_handlers/web_accessible_resources_info.h"
47 #include "extensions/common/switches.h"
49 using content::BrowserContext
;
50 using content::BrowserThread
;
51 using content::BrowserURLHandler
;
52 using content::RenderViewHost
;
53 using content::SiteInstance
;
54 using content::WebContents
;
55 using content::WebPreferences
;
57 namespace extensions
{
61 // Used by the GetPrivilegeRequiredByUrl() and GetProcessPrivilege() functions
62 // below. Extension, and isolated apps require different privileges to be
63 // granted to their RenderProcessHosts. This classification allows us to make
64 // sure URLs are served by hosts with the right set of privileges.
65 enum RenderProcessHostPrivilege
{
72 RenderProcessHostPrivilege
GetPrivilegeRequiredByUrl(
74 ExtensionRegistry
* registry
) {
75 // Default to a normal renderer cause it is lower privileged. This should only
76 // occur if the URL on a site instance is either malformed, or uninitialized.
77 // If it is malformed, then there is no need for better privileges anyways.
78 // If it is uninitialized, but eventually settles on being an a scheme other
79 // than normal webrenderer, the navigation logic will correct us out of band
84 if (!url
.SchemeIs(kExtensionScheme
))
87 const Extension
* extension
=
88 registry
->enabled_extensions().GetByID(url
.host());
89 if (extension
&& AppIsolationInfo::HasIsolatedStorage(extension
))
91 if (extension
&& extension
->is_hosted_app())
93 return PRIV_EXTENSION
;
96 RenderProcessHostPrivilege
GetProcessPrivilege(
97 content::RenderProcessHost
* process_host
,
98 ProcessMap
* process_map
,
99 ExtensionRegistry
* registry
) {
100 std::set
<std::string
> extension_ids
=
101 process_map
->GetExtensionsInProcess(process_host
->GetID());
102 if (extension_ids
.empty())
105 for (const std::string
& extension_id
: extension_ids
) {
106 const Extension
* extension
=
107 registry
->enabled_extensions().GetByID(extension_id
);
108 if (extension
&& AppIsolationInfo::HasIsolatedStorage(extension
))
109 return PRIV_ISOLATED
;
110 if (extension
&& extension
->is_hosted_app())
114 return PRIV_EXTENSION
;
119 ChromeContentBrowserClientExtensionsPart::
120 ChromeContentBrowserClientExtensionsPart() {
121 permissions_policy_delegate_
.reset(new BrowserPermissionsPolicyDelegate());
124 ChromeContentBrowserClientExtensionsPart::
125 ~ChromeContentBrowserClientExtensionsPart() {
129 GURL
ChromeContentBrowserClientExtensionsPart::GetEffectiveURL(
130 Profile
* profile
, const GURL
& url
) {
131 // If the input |url| is part of an installed app, the effective URL is an
132 // extension URL with the ID of that extension as the host. This has the
133 // effect of grouping apps together in a common SiteInstance.
134 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile
);
138 const Extension
* extension
=
139 registry
->enabled_extensions().GetHostedAppByURL(url
);
143 // Bookmark apps do not use the hosted app process model, and should be
144 // treated as normal URLs.
145 if (extension
->from_bookmark())
148 // If the URL is part of an extension's web extent, convert it to an
150 return extension
->GetResourceURL(url
.path());
154 bool ChromeContentBrowserClientExtensionsPart::ShouldUseProcessPerSite(
155 Profile
* profile
, const GURL
& effective_url
) {
156 if (!effective_url
.SchemeIs(kExtensionScheme
))
159 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile
);
163 const Extension
* extension
=
164 registry
->enabled_extensions().GetByID(effective_url
.host());
168 // If the URL is part of a hosted app that does not have the background
169 // permission, or that does not allow JavaScript access to the background
170 // page, we want to give each instance its own process to improve
172 if (extension
->GetType() == Manifest::TYPE_HOSTED_APP
) {
173 if (!extension
->permissions_data()->HasAPIPermission(
174 APIPermission::kBackground
) ||
175 !BackgroundInfo::AllowJSAccess(extension
)) {
180 // Hosted apps that have script access to their background page must use
181 // process per site, since all instances can make synchronous calls to the
182 // background window. Other extensions should use process per site as well.
187 bool ChromeContentBrowserClientExtensionsPart::ShouldLockToOrigin(
188 content::BrowserContext
* browser_context
,
189 const GURL
& effective_site_url
) {
190 // https://crbug.com/160576 workaround: Origin lock to the chrome-extension://
191 // scheme for a hosted app would kill processes on legitimate requests for the
193 if (effective_site_url
.SchemeIs(extensions::kExtensionScheme
)) {
194 const Extension
* extension
=
195 ExtensionRegistry::Get(browser_context
)
196 ->enabled_extensions()
197 .GetExtensionOrAppByURL(effective_site_url
);
198 if (extension
&& extension
->is_hosted_app())
205 bool ChromeContentBrowserClientExtensionsPart::CanCommitURL(
206 content::RenderProcessHost
* process_host
, const GURL
& url
) {
207 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
209 // We need to let most extension URLs commit in any process, since this can
210 // be allowed due to web_accessible_resources. Most hosted app URLs may also
211 // load in any process (e.g., in an iframe). However, the Chrome Web Store
212 // cannot be loaded in iframes and should never be requested outside its
214 ExtensionRegistry
* registry
=
215 ExtensionRegistry::Get(process_host
->GetBrowserContext());
219 const Extension
* new_extension
=
220 registry
->enabled_extensions().GetExtensionOrAppByURL(url
);
221 if (new_extension
&& new_extension
->is_hosted_app() &&
222 new_extension
->id() == kWebStoreAppId
&&
223 !ProcessMap::Get(process_host
->GetBrowserContext())
224 ->Contains(new_extension
->id(), process_host
->GetID())) {
230 bool ChromeContentBrowserClientExtensionsPart::IsIllegalOrigin(
231 content::ResourceContext
* resource_context
,
232 int child_process_id
,
233 const GURL
& origin
) {
234 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
236 // Consider non-extension URLs safe; they will be checked elsewhere.
237 if (!origin
.SchemeIs(kExtensionScheme
))
240 // If there is no extension installed for the URL, it couldn't have committed.
241 // (If the extension was recently uninstalled, the tab would have closed.)
242 ProfileIOData
* io_data
= ProfileIOData::FromResourceContext(resource_context
);
243 InfoMap
* extension_info_map
= io_data
->GetExtensionInfoMap();
244 const Extension
* extension
=
245 extension_info_map
->extensions().GetExtensionOrAppByURL(origin
);
249 // Check for platform app origins. These can only be committed by the app
250 // itself, or by one if its guests if there are accessible_resources.
251 const ProcessMap
& process_map
= extension_info_map
->process_map();
252 if (extension
->is_platform_app() &&
253 !process_map
.Contains(extension
->id(), child_process_id
)) {
254 // This is a platform app origin not in the app's own process. If there are
255 // no accessible resources, this is illegal.
256 if (!extension
->GetManifestData(manifest_keys::kWebviewAccessibleResources
))
259 // If there are accessible resources, the origin is only legal if the given
260 // process is a guest of the app.
261 std::string owner_extension_id
;
262 int owner_process_id
;
263 WebViewRendererState::GetInstance()->GetOwnerInfo(
264 child_process_id
, &owner_process_id
, &owner_extension_id
);
265 const Extension
* owner_extension
=
266 extension_info_map
->extensions().GetByID(owner_extension_id
);
267 return !owner_extension
|| owner_extension
!= extension
;
270 // With only the origin and not the full URL, we don't have enough information
271 // to validate hosted apps or web_accessible_resources in normal extensions.
272 // Assume they're legal.
277 bool ChromeContentBrowserClientExtensionsPart::IsSuitableHost(
279 content::RenderProcessHost
* process_host
,
280 const GURL
& site_url
) {
283 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile
);
284 ProcessMap
* process_map
= ProcessMap::Get(profile
);
286 // These may be NULL during tests. In that case, just assume any site can
288 if (!registry
|| !process_map
)
291 // Otherwise, just make sure the process privilege matches the privilege
292 // required by the site.
293 RenderProcessHostPrivilege privilege_required
=
294 GetPrivilegeRequiredByUrl(site_url
, registry
);
295 return GetProcessPrivilege(process_host
, process_map
, registry
) ==
301 ChromeContentBrowserClientExtensionsPart::ShouldTryToUseExistingProcessHost(
302 Profile
* profile
, const GURL
& url
) {
303 // This function is trying to limit the amount of processes used by extensions
304 // with background pages. It uses a globally set percentage of processes to
305 // run such extensions and if the limit is exceeded, it returns true, to
306 // indicate to the content module to group extensions together.
307 ExtensionRegistry
* registry
=
308 profile
? ExtensionRegistry::Get(profile
) : NULL
;
312 // We have to have a valid extension with background page to proceed.
313 const Extension
* extension
=
314 registry
->enabled_extensions().GetExtensionOrAppByURL(url
);
317 if (!BackgroundInfo::HasBackgroundPage(extension
))
320 std::set
<int> process_ids
;
321 size_t max_process_count
=
322 content::RenderProcessHost::GetMaxRendererProcessCount();
324 // Go through all profiles to ensure we have total count of extension
325 // processes containing background pages, otherwise one profile can
327 std::vector
<Profile
*> profiles
= g_browser_process
->profile_manager()->
329 for (size_t i
= 0; i
< profiles
.size(); ++i
) {
330 ProcessManager
* epm
= ProcessManager::Get(profiles
[i
]);
331 for (ExtensionHost
* host
: epm
->background_hosts())
332 process_ids
.insert(host
->render_process_host()->GetID());
335 return (process_ids
.size() >
336 (max_process_count
* chrome::kMaxShareOfExtensionProcesses
));
340 bool ChromeContentBrowserClientExtensionsPart::
341 ShouldSwapBrowsingInstancesForNavigation(SiteInstance
* site_instance
,
342 const GURL
& current_url
,
343 const GURL
& new_url
) {
344 // If we don't have an ExtensionRegistry, then rely on the SiteInstance logic
345 // in RenderFrameHostManager to decide when to swap.
346 ExtensionRegistry
* registry
=
347 ExtensionRegistry::Get(site_instance
->GetBrowserContext());
351 // We must use a new BrowsingInstance (forcing a process swap and disabling
352 // scripting by existing tabs) if one of the URLs is an extension and the
353 // other is not the exact same extension.
355 // We ignore hosted apps here so that other tabs in their BrowsingInstance can
356 // use postMessage with them. (The exception is the Chrome Web Store, which
357 // is a hosted app that requires its own BrowsingInstance.) Navigations
358 // to/from a hosted app will still trigger a SiteInstance swap in
359 // RenderFrameHostManager.
360 const Extension
* current_extension
=
361 registry
->enabled_extensions().GetExtensionOrAppByURL(current_url
);
362 if (current_extension
&& current_extension
->is_hosted_app() &&
363 current_extension
->id() != kWebStoreAppId
)
364 current_extension
= NULL
;
366 const Extension
* new_extension
=
367 registry
->enabled_extensions().GetExtensionOrAppByURL(new_url
);
368 if (new_extension
&& new_extension
->is_hosted_app() &&
369 new_extension
->id() != kWebStoreAppId
)
370 new_extension
= NULL
;
372 // First do a process check. We should force a BrowsingInstance swap if the
373 // current process doesn't know about new_extension, even if current_extension
374 // is somehow the same as new_extension.
375 ProcessMap
* process_map
= ProcessMap::Get(site_instance
->GetBrowserContext());
377 site_instance
->HasProcess() &&
378 !process_map
->Contains(
379 new_extension
->id(), site_instance
->GetProcess()->GetID()))
382 // Otherwise, swap BrowsingInstances if current_extension and new_extension
384 return current_extension
!= new_extension
;
388 bool ChromeContentBrowserClientExtensionsPart::ShouldSwapProcessesForRedirect(
389 content::ResourceContext
* resource_context
,
390 const GURL
& current_url
,
391 const GURL
& new_url
) {
392 ProfileIOData
* io_data
= ProfileIOData::FromResourceContext(resource_context
);
393 return CrossesExtensionProcessBoundary(
394 io_data
->GetExtensionInfoMap()->extensions(),
395 current_url
, new_url
, false);
399 bool ChromeContentBrowserClientExtensionsPart::AllowServiceWorker(
401 const GURL
& first_party_url
,
402 content::ResourceContext
* context
,
403 int render_process_id
,
404 int render_frame_id
) {
405 // We only care about extension urls.
406 // TODO(devlin): Also address chrome-extension-resource.
407 if (!first_party_url
.SchemeIs(kExtensionScheme
))
410 ProfileIOData
* io_data
= ProfileIOData::FromResourceContext(context
);
411 InfoMap
* extension_info_map
= io_data
->GetExtensionInfoMap();
412 const Extension
* extension
=
413 extension_info_map
->extensions().GetExtensionOrAppByURL(first_party_url
);
414 // Don't allow a service worker for an extension url with no extension (this
415 // could happen in the case of, e.g., an unloaded extension).
416 return extension
!= nullptr;
420 bool ChromeContentBrowserClientExtensionsPart::ShouldAllowOpenURL(
421 content::SiteInstance
* site_instance
,
422 const GURL
& from_url
,
427 // Do not allow pages from the web or other extensions navigate to
428 // non-web-accessible extension resources.
429 if (to_url
.SchemeIs(kExtensionScheme
) &&
430 (from_url
.SchemeIsHTTPOrHTTPS() || from_url
.SchemeIs(kExtensionScheme
))) {
431 Profile
* profile
= Profile::FromBrowserContext(
432 site_instance
->GetProcess()->GetBrowserContext());
433 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile
);
438 const Extension
* extension
=
439 registry
->enabled_extensions().GetExtensionOrAppByURL(to_url
);
444 const Extension
* from_extension
=
445 registry
->enabled_extensions().GetExtensionOrAppByURL(
446 site_instance
->GetSiteURL());
447 if (from_extension
&& from_extension
->id() == extension
->id()) {
452 if (!WebAccessibleResourcesInfo::IsResourceWebAccessible(
453 extension
, to_url
.path())) {
461 void ChromeContentBrowserClientExtensionsPart::RenderProcessWillLaunch(
462 content::RenderProcessHost
* host
) {
463 int id
= host
->GetID();
464 Profile
* profile
= Profile::FromBrowserContext(host
->GetBrowserContext());
466 host
->AddFilter(new ChromeExtensionMessageFilter(id
, profile
));
467 host
->AddFilter(new ExtensionMessageFilter(id
, profile
));
468 host
->AddFilter(new IOThreadExtensionMessageFilter(id
, profile
));
469 host
->AddFilter(new ExtensionsGuestViewMessageFilter(id
, profile
));
470 extension_web_request_api_helpers::SendExtensionWebRequestStatusToHost(host
);
473 void ChromeContentBrowserClientExtensionsPart::SiteInstanceGotProcess(
474 SiteInstance
* site_instance
) {
475 BrowserContext
* context
= site_instance
->GetProcess()->GetBrowserContext();
476 ExtensionRegistry
* registry
= ExtensionRegistry::Get(context
);
480 const Extension
* extension
=
481 registry
->enabled_extensions().GetExtensionOrAppByURL(
482 site_instance
->GetSiteURL());
486 ProcessMap::Get(context
)->Insert(extension
->id(),
487 site_instance
->GetProcess()->GetID(),
488 site_instance
->GetId());
490 BrowserThread::PostTask(
491 BrowserThread::IO
, FROM_HERE
,
492 base::Bind(&InfoMap::RegisterExtensionProcess
,
493 ExtensionSystem::Get(context
)->info_map(), extension
->id(),
494 site_instance
->GetProcess()->GetID(), site_instance
->GetId()));
497 void ChromeContentBrowserClientExtensionsPart::SiteInstanceDeleting(
498 SiteInstance
* site_instance
) {
499 BrowserContext
* context
= site_instance
->GetBrowserContext();
500 ExtensionRegistry
* registry
= ExtensionRegistry::Get(context
);
504 const Extension
* extension
=
505 registry
->enabled_extensions().GetExtensionOrAppByURL(
506 site_instance
->GetSiteURL());
510 ProcessMap::Get(context
)->Remove(extension
->id(),
511 site_instance
->GetProcess()->GetID(),
512 site_instance
->GetId());
514 BrowserThread::PostTask(
515 BrowserThread::IO
, FROM_HERE
,
516 base::Bind(&InfoMap::UnregisterExtensionProcess
,
517 ExtensionSystem::Get(context
)->info_map(), extension
->id(),
518 site_instance
->GetProcess()->GetID(), site_instance
->GetId()));
521 void ChromeContentBrowserClientExtensionsPart::OverrideWebkitPrefs(
523 WebPreferences
* web_prefs
) {
524 const ExtensionRegistry
* registry
=
525 ExtensionRegistry::Get(rvh
->GetProcess()->GetBrowserContext());
529 // Note: it's not possible for kExtensionsScheme to change during the lifetime
532 // Ensure that we are only granting extension preferences to URLs with
533 // the correct scheme. Without this check, chrome-guest:// schemes used by
534 // webview tags as well as hosts that happen to match the id of an
535 // installed extension would get the wrong preferences.
536 const GURL
& site_url
= rvh
->GetSiteInstance()->GetSiteURL();
537 if (!site_url
.SchemeIs(kExtensionScheme
))
540 WebContents
* web_contents
= WebContents::FromRenderViewHost(rvh
);
541 ViewType view_type
= GetViewType(web_contents
);
542 const Extension
* extension
=
543 registry
->enabled_extensions().GetByID(site_url
.host());
544 extension_webkit_preferences::SetPreferences(extension
, view_type
, web_prefs
);
547 void ChromeContentBrowserClientExtensionsPart::BrowserURLHandlerCreated(
548 BrowserURLHandler
* handler
) {
549 handler
->AddHandlerPair(&ExtensionWebUI::HandleChromeURLOverride
,
550 BrowserURLHandler::null_handler());
551 handler
->AddHandlerPair(BrowserURLHandler::null_handler(),
552 &ExtensionWebUI::HandleChromeURLOverrideReverse
);
555 void ChromeContentBrowserClientExtensionsPart::
556 GetAdditionalAllowedSchemesForFileSystem(
557 std::vector
<std::string
>* additional_allowed_schemes
) {
558 additional_allowed_schemes
->push_back(kExtensionScheme
);
561 void ChromeContentBrowserClientExtensionsPart::GetURLRequestAutoMountHandlers(
562 std::vector
<storage::URLRequestAutoMountHandler
>* handlers
) {
564 base::Bind(MediaFileSystemBackend::AttemptAutoMountForURLRequest
));
567 void ChromeContentBrowserClientExtensionsPart::GetAdditionalFileSystemBackends(
568 content::BrowserContext
* browser_context
,
569 const base::FilePath
& storage_partition_path
,
570 ScopedVector
<storage::FileSystemBackend
>* additional_backends
) {
571 base::SequencedWorkerPool
* pool
= content::BrowserThread::GetBlockingPool();
572 additional_backends
->push_back(new MediaFileSystemBackend(
573 storage_partition_path
,
574 pool
->GetSequencedTaskRunner(
575 pool
->GetNamedSequenceToken(
576 MediaFileSystemBackend::kMediaTaskRunnerName
)).get()));
578 additional_backends
->push_back(new sync_file_system::SyncFileSystemBackend(
579 Profile::FromBrowserContext(browser_context
)));
582 void ChromeContentBrowserClientExtensionsPart::
583 AppendExtraRendererCommandLineSwitches(base::CommandLine
* command_line
,
584 content::RenderProcessHost
* process
,
589 if (ProcessMap::Get(profile
)->Contains(process
->GetID())) {
590 command_line
->AppendSwitch(switches::kExtensionProcess
);
591 #if defined(ENABLE_WEBRTC)
592 command_line
->AppendSwitch(::switches::kEnableWebRtcHWH264Encoding
);
594 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
595 switches::kEnableMojoSerialService
)) {
596 command_line
->AppendSwitch(switches::kEnableMojoSerialService
);
601 } // namespace extensions