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/api/web_request/web_request_api.h"
12 #include "chrome/browser/extensions/browser_permissions_policy_delegate.h"
13 #include "chrome/browser/extensions/extension_service.h"
14 #include "chrome/browser/extensions/extension_web_ui.h"
15 #include "chrome/browser/extensions/extension_webkit_preferences.h"
16 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/profiles/profile_io_data.h"
19 #include "chrome/browser/profiles/profile_manager.h"
20 #include "chrome/browser/renderer_host/chrome_extension_message_filter.h"
21 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
22 #include "chrome/common/chrome_constants.h"
23 #include "chrome/common/extensions/extension_constants.h"
24 #include "chrome/common/extensions/extension_process_policy.h"
25 #include "chrome/common/extensions/manifest_handlers/app_isolation_info.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/browser_url_handler.h"
28 #include "content/public/browser/render_process_host.h"
29 #include "content/public/browser/render_view_host.h"
30 #include "content/public/browser/site_instance.h"
31 #include "content/public/browser/web_contents.h"
32 #include "extensions/browser/extension_host.h"
33 #include "extensions/browser/extension_message_filter.h"
34 #include "extensions/browser/extension_registry.h"
35 #include "extensions/browser/extension_system.h"
36 #include "extensions/browser/info_map.h"
37 #include "extensions/browser/view_type_utils.h"
38 #include "extensions/common/constants.h"
39 #include "extensions/common/manifest_handlers/background_info.h"
40 #include "extensions/common/manifest_handlers/web_accessible_resources_info.h"
41 #include "extensions/common/switches.h"
43 using content::BrowserThread;
44 using content::BrowserURLHandler;
45 using content::RenderViewHost;
46 using content::SiteInstance;
47 using content::WebContents;
48 using content::WebPreferences;
50 namespace extensions {
54 // Used by the GetPrivilegeRequiredByUrl() and GetProcessPrivilege() functions
55 // below. Extension, and isolated apps require different privileges to be
56 // granted to their RenderProcessHosts. This classification allows us to make
57 // sure URLs are served by hosts with the right set of privileges.
58 enum RenderProcessHostPrivilege {
65 RenderProcessHostPrivilege GetPrivilegeRequiredByUrl(
67 ExtensionService* service) {
68 // Default to a normal renderer cause it is lower privileged. This should only
69 // occur if the URL on a site instance is either malformed, or uninitialized.
70 // If it is malformed, then there is no need for better privileges anyways.
71 // If it is uninitialized, but eventually settles on being an a scheme other
72 // than normal webrenderer, the navigation logic will correct us out of band
77 if (!url.SchemeIs(kExtensionScheme))
80 const Extension* extension = service->extensions()->GetByID(url.host());
81 if (extension && AppIsolationInfo::HasIsolatedStorage(extension))
83 if (extension && extension->is_hosted_app())
85 return PRIV_EXTENSION;
88 RenderProcessHostPrivilege GetProcessPrivilege(
89 content::RenderProcessHost* process_host,
90 ProcessMap* process_map,
91 ExtensionService* service) {
92 std::set<std::string> extension_ids =
93 process_map->GetExtensionsInProcess(process_host->GetID());
94 if (extension_ids.empty())
97 for (std::set<std::string>::iterator iter = extension_ids.begin();
98 iter != extension_ids.end(); ++iter) {
99 const Extension* extension = service->GetExtensionById(*iter, false);
100 if (extension && AppIsolationInfo::HasIsolatedStorage(extension))
101 return PRIV_ISOLATED;
102 if (extension && extension->is_hosted_app())
106 return PRIV_EXTENSION;
111 ChromeContentBrowserClientExtensionsPart::
112 ChromeContentBrowserClientExtensionsPart() {
113 permissions_policy_delegate_.reset(new BrowserPermissionsPolicyDelegate());
116 ChromeContentBrowserClientExtensionsPart::
117 ~ChromeContentBrowserClientExtensionsPart() {
121 GURL ChromeContentBrowserClientExtensionsPart::GetEffectiveURL(
122 Profile* profile, const GURL& url) {
123 // If the input |url| is part of an installed app, the effective URL is an
124 // extension URL with the ID of that extension as the host. This has the
125 // effect of grouping apps together in a common SiteInstance.
126 ExtensionService* extension_service =
127 ExtensionSystem::Get(profile)->extension_service();
128 if (!extension_service)
131 const Extension* extension =
132 extension_service->extensions()->GetHostedAppByURL(url);
136 // Bookmark apps do not use the hosted app process model, and should be
137 // treated as normal URLs.
138 if (extension->from_bookmark())
141 // If the URL is part of an extension's web extent, convert it to an
143 return extension->GetResourceURL(url.path());
147 bool ChromeContentBrowserClientExtensionsPart::ShouldUseProcessPerSite(
148 Profile* profile, const GURL& effective_url) {
149 if (!effective_url.SchemeIs(kExtensionScheme))
152 ExtensionService* extension_service =
153 ExtensionSystem::Get(profile)->extension_service();
154 if (!extension_service)
157 const Extension* extension =
158 extension_service->extensions()->GetExtensionOrAppByURL(effective_url);
162 // If the URL is part of a hosted app that does not have the background
163 // permission, or that does not allow JavaScript access to the background
164 // page, we want to give each instance its own process to improve
166 if (extension->GetType() == Manifest::TYPE_HOSTED_APP) {
167 if (!extension->permissions_data()->HasAPIPermission(
168 APIPermission::kBackground) ||
169 !BackgroundInfo::AllowJSAccess(extension)) {
174 // Hosted apps that have script access to their background page must use
175 // process per site, since all instances can make synchronous calls to the
176 // background window. Other extensions should use process per site as well.
181 bool ChromeContentBrowserClientExtensionsPart::CanCommitURL(
182 content::RenderProcessHost* process_host, const GURL& url) {
183 // We need to let most extension URLs commit in any process, since this can
184 // be allowed due to web_accessible_resources. Most hosted app URLs may also
185 // load in any process (e.g., in an iframe). However, the Chrome Web Store
186 // cannot be loaded in iframes and should never be requested outside its
189 Profile::FromBrowserContext(process_host->GetBrowserContext());
190 ExtensionService* service =
191 ExtensionSystem::Get(profile)->extension_service();
195 const Extension* new_extension =
196 service->extensions()->GetExtensionOrAppByURL(url);
198 new_extension->is_hosted_app() &&
199 new_extension->id() == extension_misc::kWebStoreAppId &&
200 !ProcessMap::Get(profile)->Contains(
201 new_extension->id(), process_host->GetID())) {
208 bool ChromeContentBrowserClientExtensionsPart::IsSuitableHost(
210 content::RenderProcessHost* process_host,
211 const GURL& site_url) {
214 ExtensionService* service =
215 ExtensionSystem::Get(profile)->extension_service();
216 ProcessMap* process_map = ProcessMap::Get(profile);
218 // These may be NULL during tests. In that case, just assume any site can
220 if (!service || !process_map)
223 // Otherwise, just make sure the process privilege matches the privilege
224 // required by the site.
225 RenderProcessHostPrivilege privilege_required =
226 GetPrivilegeRequiredByUrl(site_url, service);
227 return GetProcessPrivilege(process_host, process_map, service) ==
233 ChromeContentBrowserClientExtensionsPart::ShouldTryToUseExistingProcessHost(
234 Profile* profile, const GURL& url) {
235 // This function is trying to limit the amount of processes used by extensions
236 // with background pages. It uses a globally set percentage of processes to
237 // run such extensions and if the limit is exceeded, it returns true, to
238 // indicate to the content module to group extensions together.
239 ExtensionService* service = profile ?
240 ExtensionSystem::Get(profile)->extension_service() : NULL;
244 // We have to have a valid extension with background page to proceed.
245 const Extension* extension =
246 service->extensions()->GetExtensionOrAppByURL(url);
249 if (!BackgroundInfo::HasBackgroundPage(extension))
252 std::set<int> process_ids;
253 size_t max_process_count =
254 content::RenderProcessHost::GetMaxRendererProcessCount();
256 // Go through all profiles to ensure we have total count of extension
257 // processes containing background pages, otherwise one profile can
259 std::vector<Profile*> profiles = g_browser_process->profile_manager()->
261 for (size_t i = 0; i < profiles.size(); ++i) {
262 ProcessManager* epm = ExtensionSystem::Get(profiles[i])->process_manager();
263 for (ProcessManager::const_iterator iter = epm->background_hosts().begin();
264 iter != epm->background_hosts().end(); ++iter) {
265 const ExtensionHost* host = *iter;
266 process_ids.insert(host->render_process_host()->GetID());
270 return (process_ids.size() >
271 (max_process_count * chrome::kMaxShareOfExtensionProcesses));
275 bool ChromeContentBrowserClientExtensionsPart::
276 ShouldSwapBrowsingInstancesForNavigation(SiteInstance* site_instance,
277 const GURL& current_url,
278 const GURL& new_url) {
279 // If we don't have an ExtensionService, then rely on the SiteInstance logic
280 // in RenderFrameHostManager to decide when to swap.
282 Profile::FromBrowserContext(site_instance->GetBrowserContext());
283 ExtensionService* service =
284 ExtensionSystem::Get(profile)->extension_service();
288 // We must use a new BrowsingInstance (forcing a process swap and disabling
289 // scripting by existing tabs) if one of the URLs is an extension and the
290 // other is not the exact same extension.
292 // We ignore hosted apps here so that other tabs in their BrowsingInstance can
293 // use postMessage with them. (The exception is the Chrome Web Store, which
294 // is a hosted app that requires its own BrowsingInstance.) Navigations
295 // to/from a hosted app will still trigger a SiteInstance swap in
296 // RenderFrameHostManager.
297 const Extension* current_extension =
298 service->extensions()->GetExtensionOrAppByURL(current_url);
299 if (current_extension &&
300 current_extension->is_hosted_app() &&
301 current_extension->id() != extension_misc::kWebStoreAppId)
302 current_extension = NULL;
304 const Extension* new_extension =
305 service->extensions()->GetExtensionOrAppByURL(new_url);
307 new_extension->is_hosted_app() &&
308 new_extension->id() != extension_misc::kWebStoreAppId)
309 new_extension = NULL;
311 // First do a process check. We should force a BrowsingInstance swap if the
312 // current process doesn't know about new_extension, even if current_extension
313 // is somehow the same as new_extension.
314 ProcessMap* process_map = ProcessMap::Get(profile);
316 site_instance->HasProcess() &&
317 !process_map->Contains(
318 new_extension->id(), site_instance->GetProcess()->GetID()))
321 // Otherwise, swap BrowsingInstances if current_extension and new_extension
323 return current_extension != new_extension;
327 bool ChromeContentBrowserClientExtensionsPart::ShouldSwapProcessesForRedirect(
328 content::ResourceContext* resource_context,
329 const GURL& current_url,
330 const GURL& new_url) {
331 ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context);
332 return CrossesExtensionProcessBoundary(
333 io_data->GetExtensionInfoMap()->extensions(),
334 current_url, new_url, false);
338 std::string ChromeContentBrowserClientExtensionsPart::GetWorkerProcessTitle(
339 const GURL& url, content::ResourceContext* context) {
340 // Check if it's an extension-created worker, in which case we want to use
341 // the name of the extension.
342 ProfileIOData* io_data = ProfileIOData::FromResourceContext(context);
343 const Extension* extension =
344 io_data->GetExtensionInfoMap()->extensions().GetByID(url.host());
345 return extension ? extension->name() : std::string();
349 bool ChromeContentBrowserClientExtensionsPart::ShouldAllowOpenURL(
350 content::SiteInstance* site_instance,
351 const GURL& from_url,
356 // Do not allow pages from the web or other extensions navigate to
357 // non-web-accessible extension resources.
358 if (to_url.SchemeIs(kExtensionScheme) &&
359 (from_url.SchemeIsHTTPOrHTTPS() || from_url.SchemeIs(kExtensionScheme))) {
360 Profile* profile = Profile::FromBrowserContext(
361 site_instance->GetProcess()->GetBrowserContext());
362 ExtensionService* service =
363 ExtensionSystem::Get(profile)->extension_service();
368 const Extension* extension =
369 service->extensions()->GetExtensionOrAppByURL(to_url);
374 const Extension* from_extension =
375 service->extensions()->GetExtensionOrAppByURL(
376 site_instance->GetSiteURL());
377 if (from_extension && from_extension->id() == extension->id()) {
382 if (!WebAccessibleResourcesInfo::IsResourceWebAccessible(
383 extension, to_url.path())) {
392 void ChromeContentBrowserClientExtensionsPart::SetSigninProcess(
393 content::SiteInstance* site_instance) {
395 Profile::FromBrowserContext(site_instance->GetBrowserContext());
397 BrowserThread::PostTask(
400 base::Bind(&InfoMap::SetSigninProcess,
401 ExtensionSystem::Get(profile)->info_map(),
402 site_instance->GetProcess()->GetID()));
405 void ChromeContentBrowserClientExtensionsPart::RenderProcessWillLaunch(
406 content::RenderProcessHost* host) {
407 int id = host->GetID();
408 Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext());
410 host->AddFilter(new ChromeExtensionMessageFilter(id, profile));
411 host->AddFilter(new ExtensionMessageFilter(id, profile));
412 SendExtensionWebRequestStatusToHost(host);
415 void ChromeContentBrowserClientExtensionsPart::SiteInstanceGotProcess(
416 SiteInstance* site_instance) {
417 Profile* profile = Profile::FromBrowserContext(
418 site_instance->GetProcess()->GetBrowserContext());
419 ExtensionService* service =
420 ExtensionSystem::Get(profile)->extension_service();
424 const Extension* extension = service->extensions()->GetExtensionOrAppByURL(
425 site_instance->GetSiteURL());
429 ProcessMap::Get(profile)->Insert(extension->id(),
430 site_instance->GetProcess()->GetID(),
431 site_instance->GetId());
433 BrowserThread::PostTask(BrowserThread::IO,
435 base::Bind(&InfoMap::RegisterExtensionProcess,
436 ExtensionSystem::Get(profile)->info_map(),
438 site_instance->GetProcess()->GetID(),
439 site_instance->GetId()));
442 void ChromeContentBrowserClientExtensionsPart::SiteInstanceDeleting(
443 SiteInstance* site_instance) {
445 Profile::FromBrowserContext(site_instance->GetBrowserContext());
446 ExtensionService* service =
447 ExtensionSystem::Get(profile)->extension_service();
451 const Extension* extension = service->extensions()->GetExtensionOrAppByURL(
452 site_instance->GetSiteURL());
456 ProcessMap::Get(profile)->Remove(extension->id(),
457 site_instance->GetProcess()->GetID(),
458 site_instance->GetId());
460 BrowserThread::PostTask(BrowserThread::IO,
462 base::Bind(&InfoMap::UnregisterExtensionProcess,
463 ExtensionSystem::Get(profile)->info_map(),
465 site_instance->GetProcess()->GetID(),
466 site_instance->GetId()));
469 void ChromeContentBrowserClientExtensionsPart::WorkerProcessCreated(
470 SiteInstance* site_instance,
471 int worker_process_id) {
472 ExtensionRegistry* extension_registry =
473 ExtensionRegistry::Get(site_instance->GetBrowserContext());
474 if (!extension_registry)
476 const Extension* extension =
477 extension_registry->enabled_extensions().GetExtensionOrAppByURL(
478 site_instance->GetSiteURL());
481 ExtensionSystem* extension_system =
482 ExtensionSystem::Get(site_instance->GetBrowserContext());
483 extension_system->info_map()->RegisterExtensionWorkerProcess(
484 extension->id(), worker_process_id, site_instance->GetId());
487 void ChromeContentBrowserClientExtensionsPart::WorkerProcessTerminated(
488 SiteInstance* site_instance,
489 int worker_process_id) {
490 ExtensionSystem* extension_system =
491 ExtensionSystem::Get(site_instance->GetBrowserContext());
492 extension_system->info_map()->UnregisterExtensionWorkerProcess(
496 void ChromeContentBrowserClientExtensionsPart::OverrideWebkitPrefs(
499 WebPreferences* web_prefs) {
501 Profile::FromBrowserContext(rvh->GetProcess()->GetBrowserContext());
503 ExtensionService* service =
504 ExtensionSystem::Get(profile)->extension_service();
508 // Note: it's not possible for kExtensionsScheme to change during the lifetime
511 // Ensure that we are only granting extension preferences to URLs with
512 // the correct scheme. Without this check, chrome-guest:// schemes used by
513 // webview tags as well as hosts that happen to match the id of an
514 // installed extension would get the wrong preferences.
515 const GURL& site_url = rvh->GetSiteInstance()->GetSiteURL();
516 if (!site_url.SchemeIs(kExtensionScheme))
519 WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
520 ViewType view_type = GetViewType(web_contents);
521 const Extension* extension = service->extensions()->GetByID(site_url.host());
522 extension_webkit_preferences::SetPreferences(extension, view_type, web_prefs);
525 void ChromeContentBrowserClientExtensionsPart::BrowserURLHandlerCreated(
526 BrowserURLHandler* handler) {
527 handler->AddHandlerPair(&ExtensionWebUI::HandleChromeURLOverride,
528 BrowserURLHandler::null_handler());
529 handler->AddHandlerPair(BrowserURLHandler::null_handler(),
530 &ExtensionWebUI::HandleChromeURLOverrideReverse);
533 void ChromeContentBrowserClientExtensionsPart::
534 GetAdditionalAllowedSchemesForFileSystem(
535 std::vector<std::string>* additional_allowed_schemes) {
536 additional_allowed_schemes->push_back(kExtensionScheme);
539 void ChromeContentBrowserClientExtensionsPart::GetURLRequestAutoMountHandlers(
540 std::vector<fileapi::URLRequestAutoMountHandler>* handlers) {
542 base::Bind(MediaFileSystemBackend::AttemptAutoMountForURLRequest));
545 void ChromeContentBrowserClientExtensionsPart::GetAdditionalFileSystemBackends(
546 content::BrowserContext* browser_context,
547 const base::FilePath& storage_partition_path,
548 ScopedVector<fileapi::FileSystemBackend>* additional_backends) {
549 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
550 additional_backends->push_back(new MediaFileSystemBackend(
551 storage_partition_path,
552 pool->GetSequencedTaskRunner(
553 pool->GetNamedSequenceToken(
554 MediaFileSystemBackend::kMediaTaskRunnerName)).get()));
556 additional_backends->push_back(new sync_file_system::SyncFileSystemBackend(
557 Profile::FromBrowserContext(browser_context)));
560 void ChromeContentBrowserClientExtensionsPart::
561 AppendExtraRendererCommandLineSwitches(base::CommandLine* command_line,
562 content::RenderProcessHost* process,
567 if (ProcessMap::Get(profile)->Contains(process->GetID()))
568 command_line->AppendSwitch(switches::kExtensionProcess);
571 } // namespace extensions