Prevent chrome://net-internals/#export from flickering
[chromium-blink-merge.git] / chrome / browser / extensions / chrome_content_browser_client_extensions_part.cc
blob33ff440929844b920b24021b94728c2116e51287
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"
7 #include <set>
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 "chrome/common/extensions/manifest_handlers/app_isolation_info.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/guest_view_message_filter.h"
38 #include "extensions/browser/info_map.h"
39 #include "extensions/browser/io_thread_extension_message_filter.h"
40 #include "extensions/browser/view_type_utils.h"
41 #include "extensions/common/constants.h"
42 #include "extensions/common/manifest_handlers/background_info.h"
43 #include "extensions/common/manifest_handlers/web_accessible_resources_info.h"
44 #include "extensions/common/switches.h"
46 using content::BrowserContext;
47 using content::BrowserThread;
48 using content::BrowserURLHandler;
49 using content::RenderViewHost;
50 using content::SiteInstance;
51 using content::WebContents;
52 using content::WebPreferences;
54 namespace extensions {
56 namespace {
58 // Used by the GetPrivilegeRequiredByUrl() and GetProcessPrivilege() functions
59 // below. Extension, and isolated apps require different privileges to be
60 // granted to their RenderProcessHosts. This classification allows us to make
61 // sure URLs are served by hosts with the right set of privileges.
62 enum RenderProcessHostPrivilege {
63 PRIV_NORMAL,
64 PRIV_HOSTED,
65 PRIV_ISOLATED,
66 PRIV_EXTENSION,
69 RenderProcessHostPrivilege GetPrivilegeRequiredByUrl(
70 const GURL& url,
71 ExtensionRegistry* registry) {
72 // Default to a normal renderer cause it is lower privileged. This should only
73 // occur if the URL on a site instance is either malformed, or uninitialized.
74 // If it is malformed, then there is no need for better privileges anyways.
75 // If it is uninitialized, but eventually settles on being an a scheme other
76 // than normal webrenderer, the navigation logic will correct us out of band
77 // anyways.
78 if (!url.is_valid())
79 return PRIV_NORMAL;
81 if (!url.SchemeIs(kExtensionScheme))
82 return PRIV_NORMAL;
84 const Extension* extension =
85 registry->enabled_extensions().GetByID(url.host());
86 if (extension && AppIsolationInfo::HasIsolatedStorage(extension))
87 return PRIV_ISOLATED;
88 if (extension && extension->is_hosted_app())
89 return PRIV_HOSTED;
90 return PRIV_EXTENSION;
93 RenderProcessHostPrivilege GetProcessPrivilege(
94 content::RenderProcessHost* process_host,
95 ProcessMap* process_map,
96 ExtensionRegistry* registry) {
97 std::set<std::string> extension_ids =
98 process_map->GetExtensionsInProcess(process_host->GetID());
99 if (extension_ids.empty())
100 return PRIV_NORMAL;
102 for (const std::string& extension_id : extension_ids) {
103 const Extension* extension =
104 registry->enabled_extensions().GetByID(extension_id);
105 if (extension && AppIsolationInfo::HasIsolatedStorage(extension))
106 return PRIV_ISOLATED;
107 if (extension && extension->is_hosted_app())
108 return PRIV_HOSTED;
111 return PRIV_EXTENSION;
114 } // namespace
116 ChromeContentBrowserClientExtensionsPart::
117 ChromeContentBrowserClientExtensionsPart() {
118 permissions_policy_delegate_.reset(new BrowserPermissionsPolicyDelegate());
121 ChromeContentBrowserClientExtensionsPart::
122 ~ChromeContentBrowserClientExtensionsPart() {
125 // static
126 GURL ChromeContentBrowserClientExtensionsPart::GetEffectiveURL(
127 Profile* profile, const GURL& url) {
128 // If the input |url| is part of an installed app, the effective URL is an
129 // extension URL with the ID of that extension as the host. This has the
130 // effect of grouping apps together in a common SiteInstance.
131 ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
132 if (!registry)
133 return url;
135 const Extension* extension =
136 registry->enabled_extensions().GetHostedAppByURL(url);
137 if (!extension)
138 return url;
140 // Bookmark apps do not use the hosted app process model, and should be
141 // treated as normal URLs.
142 if (extension->from_bookmark())
143 return url;
145 // If the URL is part of an extension's web extent, convert it to an
146 // extension URL.
147 return extension->GetResourceURL(url.path());
150 // static
151 bool ChromeContentBrowserClientExtensionsPart::ShouldUseProcessPerSite(
152 Profile* profile, const GURL& effective_url) {
153 if (!effective_url.SchemeIs(kExtensionScheme))
154 return false;
156 ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
157 if (!registry)
158 return false;
160 const Extension* extension =
161 registry->enabled_extensions().GetByID(effective_url.host());
162 if (!extension)
163 return false;
165 // If the URL is part of a hosted app that does not have the background
166 // permission, or that does not allow JavaScript access to the background
167 // page, we want to give each instance its own process to improve
168 // responsiveness.
169 if (extension->GetType() == Manifest::TYPE_HOSTED_APP) {
170 if (!extension->permissions_data()->HasAPIPermission(
171 APIPermission::kBackground) ||
172 !BackgroundInfo::AllowJSAccess(extension)) {
173 return false;
177 // Hosted apps that have script access to their background page must use
178 // process per site, since all instances can make synchronous calls to the
179 // background window. Other extensions should use process per site as well.
180 return true;
183 // static
184 bool ChromeContentBrowserClientExtensionsPart::CanCommitURL(
185 content::RenderProcessHost* process_host, const GURL& url) {
186 // We need to let most extension URLs commit in any process, since this can
187 // be allowed due to web_accessible_resources. Most hosted app URLs may also
188 // load in any process (e.g., in an iframe). However, the Chrome Web Store
189 // cannot be loaded in iframes and should never be requested outside its
190 // process.
191 ExtensionRegistry* registry =
192 ExtensionRegistry::Get(process_host->GetBrowserContext());
193 if (!registry)
194 return true;
196 const Extension* new_extension =
197 registry->enabled_extensions().GetExtensionOrAppByURL(url);
198 if (new_extension && new_extension->is_hosted_app() &&
199 new_extension->id() == extensions::kWebStoreAppId &&
200 !ProcessMap::Get(process_host->GetBrowserContext())
201 ->Contains(new_extension->id(), process_host->GetID())) {
202 return false;
204 return true;
207 // static
208 bool ChromeContentBrowserClientExtensionsPart::IsSuitableHost(
209 Profile* profile,
210 content::RenderProcessHost* process_host,
211 const GURL& site_url) {
212 DCHECK(profile);
214 ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
215 ProcessMap* process_map = ProcessMap::Get(profile);
217 // These may be NULL during tests. In that case, just assume any site can
218 // share any host.
219 if (!registry || !process_map)
220 return true;
222 // Otherwise, just make sure the process privilege matches the privilege
223 // required by the site.
224 RenderProcessHostPrivilege privilege_required =
225 GetPrivilegeRequiredByUrl(site_url, registry);
226 return GetProcessPrivilege(process_host, process_map, registry) ==
227 privilege_required;
230 // static
231 bool
232 ChromeContentBrowserClientExtensionsPart::ShouldTryToUseExistingProcessHost(
233 Profile* profile, const GURL& url) {
234 // This function is trying to limit the amount of processes used by extensions
235 // with background pages. It uses a globally set percentage of processes to
236 // run such extensions and if the limit is exceeded, it returns true, to
237 // indicate to the content module to group extensions together.
238 ExtensionRegistry* registry =
239 profile ? ExtensionRegistry::Get(profile) : NULL;
240 if (!registry)
241 return false;
243 // We have to have a valid extension with background page to proceed.
244 const Extension* extension =
245 registry->enabled_extensions().GetExtensionOrAppByURL(url);
246 if (!extension)
247 return false;
248 if (!BackgroundInfo::HasBackgroundPage(extension))
249 return false;
251 std::set<int> process_ids;
252 size_t max_process_count =
253 content::RenderProcessHost::GetMaxRendererProcessCount();
255 // Go through all profiles to ensure we have total count of extension
256 // processes containing background pages, otherwise one profile can
257 // starve the other.
258 std::vector<Profile*> profiles = g_browser_process->profile_manager()->
259 GetLoadedProfiles();
260 for (size_t i = 0; i < profiles.size(); ++i) {
261 ProcessManager* epm = ProcessManager::Get(profiles[i]);
262 for (ProcessManager::const_iterator iter = epm->background_hosts().begin();
263 iter != epm->background_hosts().end(); ++iter) {
264 const ExtensionHost* host = *iter;
265 process_ids.insert(host->render_process_host()->GetID());
269 return (process_ids.size() >
270 (max_process_count * chrome::kMaxShareOfExtensionProcesses));
273 // static
274 bool ChromeContentBrowserClientExtensionsPart::
275 ShouldSwapBrowsingInstancesForNavigation(SiteInstance* site_instance,
276 const GURL& current_url,
277 const GURL& new_url) {
278 // If we don't have an ExtensionRegistry, then rely on the SiteInstance logic
279 // in RenderFrameHostManager to decide when to swap.
280 ExtensionRegistry* registry =
281 ExtensionRegistry::Get(site_instance->GetBrowserContext());
282 if (!registry)
283 return false;
285 // We must use a new BrowsingInstance (forcing a process swap and disabling
286 // scripting by existing tabs) if one of the URLs is an extension and the
287 // other is not the exact same extension.
289 // We ignore hosted apps here so that other tabs in their BrowsingInstance can
290 // use postMessage with them. (The exception is the Chrome Web Store, which
291 // is a hosted app that requires its own BrowsingInstance.) Navigations
292 // to/from a hosted app will still trigger a SiteInstance swap in
293 // RenderFrameHostManager.
294 const Extension* current_extension =
295 registry->enabled_extensions().GetExtensionOrAppByURL(current_url);
296 if (current_extension &&
297 current_extension->is_hosted_app() &&
298 current_extension->id() != extensions::kWebStoreAppId)
299 current_extension = NULL;
301 const Extension* new_extension =
302 registry->enabled_extensions().GetExtensionOrAppByURL(new_url);
303 if (new_extension &&
304 new_extension->is_hosted_app() &&
305 new_extension->id() != extensions::kWebStoreAppId)
306 new_extension = NULL;
308 // First do a process check. We should force a BrowsingInstance swap if the
309 // current process doesn't know about new_extension, even if current_extension
310 // is somehow the same as new_extension.
311 ProcessMap* process_map = ProcessMap::Get(site_instance->GetBrowserContext());
312 if (new_extension &&
313 site_instance->HasProcess() &&
314 !process_map->Contains(
315 new_extension->id(), site_instance->GetProcess()->GetID()))
316 return true;
318 // Otherwise, swap BrowsingInstances if current_extension and new_extension
319 // differ.
320 return current_extension != new_extension;
323 // static
324 bool ChromeContentBrowserClientExtensionsPart::ShouldSwapProcessesForRedirect(
325 content::ResourceContext* resource_context,
326 const GURL& current_url,
327 const GURL& new_url) {
328 ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context);
329 return CrossesExtensionProcessBoundary(
330 io_data->GetExtensionInfoMap()->extensions(),
331 current_url, new_url, false);
334 // static
335 bool ChromeContentBrowserClientExtensionsPart::ShouldAllowOpenURL(
336 content::SiteInstance* site_instance,
337 const GURL& from_url,
338 const GURL& to_url,
339 bool* result) {
340 DCHECK(result);
342 // Do not allow pages from the web or other extensions navigate to
343 // non-web-accessible extension resources.
344 if (to_url.SchemeIs(kExtensionScheme) &&
345 (from_url.SchemeIsHTTPOrHTTPS() || from_url.SchemeIs(kExtensionScheme))) {
346 Profile* profile = Profile::FromBrowserContext(
347 site_instance->GetProcess()->GetBrowserContext());
348 ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
349 if (!registry) {
350 *result = true;
351 return true;
353 const Extension* extension =
354 registry->enabled_extensions().GetExtensionOrAppByURL(to_url);
355 if (!extension) {
356 *result = true;
357 return true;
359 const Extension* from_extension =
360 registry->enabled_extensions().GetExtensionOrAppByURL(
361 site_instance->GetSiteURL());
362 if (from_extension && from_extension->id() == extension->id()) {
363 *result = true;
364 return true;
367 if (!WebAccessibleResourcesInfo::IsResourceWebAccessible(
368 extension, to_url.path())) {
369 *result = false;
370 return true;
373 return false;
376 // static
377 void ChromeContentBrowserClientExtensionsPart::SetSigninProcess(
378 content::SiteInstance* site_instance) {
379 Profile* profile =
380 Profile::FromBrowserContext(site_instance->GetBrowserContext());
381 DCHECK(profile);
382 BrowserThread::PostTask(
383 BrowserThread::IO,
384 FROM_HERE,
385 base::Bind(&InfoMap::SetSigninProcess,
386 ExtensionSystem::Get(profile)->info_map(),
387 site_instance->GetProcess()->GetID()));
390 void ChromeContentBrowserClientExtensionsPart::RenderProcessWillLaunch(
391 content::RenderProcessHost* host) {
392 int id = host->GetID();
393 Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext());
395 host->AddFilter(new ChromeExtensionMessageFilter(id, profile));
396 host->AddFilter(new ExtensionMessageFilter(id, profile));
397 host->AddFilter(new IOThreadExtensionMessageFilter(id, profile));
398 host->AddFilter(new GuestViewMessageFilter(id, profile));
399 extension_web_request_api_helpers::SendExtensionWebRequestStatusToHost(host);
402 void ChromeContentBrowserClientExtensionsPart::SiteInstanceGotProcess(
403 SiteInstance* site_instance) {
404 BrowserContext* context = site_instance->GetProcess()->GetBrowserContext();
405 ExtensionRegistry* registry = ExtensionRegistry::Get(context);
406 if (!registry)
407 return;
409 const Extension* extension =
410 registry->enabled_extensions().GetExtensionOrAppByURL(
411 site_instance->GetSiteURL());
412 if (!extension)
413 return;
415 ProcessMap::Get(context)->Insert(extension->id(),
416 site_instance->GetProcess()->GetID(),
417 site_instance->GetId());
419 BrowserThread::PostTask(
420 BrowserThread::IO, FROM_HERE,
421 base::Bind(&InfoMap::RegisterExtensionProcess,
422 ExtensionSystem::Get(context)->info_map(), extension->id(),
423 site_instance->GetProcess()->GetID(), site_instance->GetId()));
426 void ChromeContentBrowserClientExtensionsPart::SiteInstanceDeleting(
427 SiteInstance* site_instance) {
428 BrowserContext* context = site_instance->GetBrowserContext();
429 ExtensionRegistry* registry = ExtensionRegistry::Get(context);
430 if (!registry)
431 return;
433 const Extension* extension =
434 registry->enabled_extensions().GetExtensionOrAppByURL(
435 site_instance->GetSiteURL());
436 if (!extension)
437 return;
439 ProcessMap::Get(context)->Remove(extension->id(),
440 site_instance->GetProcess()->GetID(),
441 site_instance->GetId());
443 BrowserThread::PostTask(
444 BrowserThread::IO, FROM_HERE,
445 base::Bind(&InfoMap::UnregisterExtensionProcess,
446 ExtensionSystem::Get(context)->info_map(), extension->id(),
447 site_instance->GetProcess()->GetID(), site_instance->GetId()));
450 void ChromeContentBrowserClientExtensionsPart::OverrideWebkitPrefs(
451 RenderViewHost* rvh,
452 WebPreferences* web_prefs) {
453 const ExtensionRegistry* registry =
454 ExtensionRegistry::Get(rvh->GetProcess()->GetBrowserContext());
455 if (!registry)
456 return;
458 // Note: it's not possible for kExtensionsScheme to change during the lifetime
459 // of the process.
461 // Ensure that we are only granting extension preferences to URLs with
462 // the correct scheme. Without this check, chrome-guest:// schemes used by
463 // webview tags as well as hosts that happen to match the id of an
464 // installed extension would get the wrong preferences.
465 const GURL& site_url = rvh->GetSiteInstance()->GetSiteURL();
466 if (!site_url.SchemeIs(kExtensionScheme))
467 return;
469 WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
470 ViewType view_type = GetViewType(web_contents);
471 const Extension* extension =
472 registry->enabled_extensions().GetByID(site_url.host());
473 extension_webkit_preferences::SetPreferences(extension, view_type, web_prefs);
476 void ChromeContentBrowserClientExtensionsPart::BrowserURLHandlerCreated(
477 BrowserURLHandler* handler) {
478 handler->AddHandlerPair(&ExtensionWebUI::HandleChromeURLOverride,
479 BrowserURLHandler::null_handler());
480 handler->AddHandlerPair(BrowserURLHandler::null_handler(),
481 &ExtensionWebUI::HandleChromeURLOverrideReverse);
484 void ChromeContentBrowserClientExtensionsPart::
485 GetAdditionalAllowedSchemesForFileSystem(
486 std::vector<std::string>* additional_allowed_schemes) {
487 additional_allowed_schemes->push_back(kExtensionScheme);
490 void ChromeContentBrowserClientExtensionsPart::GetURLRequestAutoMountHandlers(
491 std::vector<storage::URLRequestAutoMountHandler>* handlers) {
492 handlers->push_back(
493 base::Bind(MediaFileSystemBackend::AttemptAutoMountForURLRequest));
496 void ChromeContentBrowserClientExtensionsPart::GetAdditionalFileSystemBackends(
497 content::BrowserContext* browser_context,
498 const base::FilePath& storage_partition_path,
499 ScopedVector<storage::FileSystemBackend>* additional_backends) {
500 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
501 additional_backends->push_back(new MediaFileSystemBackend(
502 storage_partition_path,
503 pool->GetSequencedTaskRunner(
504 pool->GetNamedSequenceToken(
505 MediaFileSystemBackend::kMediaTaskRunnerName)).get()));
507 additional_backends->push_back(new sync_file_system::SyncFileSystemBackend(
508 Profile::FromBrowserContext(browser_context)));
511 void ChromeContentBrowserClientExtensionsPart::
512 AppendExtraRendererCommandLineSwitches(base::CommandLine* command_line,
513 content::RenderProcessHost* process,
514 Profile* profile) {
515 if (!process)
516 return;
517 DCHECK(profile);
518 if (ProcessMap::Get(profile)->Contains(process->GetID())) {
519 command_line->AppendSwitch(switches::kExtensionProcess);
520 #if defined(ENABLE_WEBRTC)
521 command_line->AppendSwitch(::switches::kEnableWebRtcHWH264Encoding);
522 #endif
523 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
524 switches::kEnableMojoSerialService)) {
525 command_line->AppendSwitch(switches::kEnableMojoSerialService);
530 } // namespace extensions