Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / chrome / browser / extensions / chrome_content_browser_client_extensions_part.cc
blob4ddff47ec068c01a0e600bdcc338f5c4d10cceff
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 "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 {
59 namespace {
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 {
66 PRIV_NORMAL,
67 PRIV_HOSTED,
68 PRIV_ISOLATED,
69 PRIV_EXTENSION,
72 RenderProcessHostPrivilege GetPrivilegeRequiredByUrl(
73 const GURL& url,
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
80 // anyways.
81 if (!url.is_valid())
82 return PRIV_NORMAL;
84 if (!url.SchemeIs(kExtensionScheme))
85 return PRIV_NORMAL;
87 const Extension* extension =
88 registry->enabled_extensions().GetByID(url.host());
89 if (extension && AppIsolationInfo::HasIsolatedStorage(extension))
90 return PRIV_ISOLATED;
91 if (extension && extension->is_hosted_app())
92 return PRIV_HOSTED;
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())
103 return PRIV_NORMAL;
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())
111 return PRIV_HOSTED;
114 return PRIV_EXTENSION;
117 } // namespace
119 ChromeContentBrowserClientExtensionsPart::
120 ChromeContentBrowserClientExtensionsPart() {
121 permissions_policy_delegate_.reset(new BrowserPermissionsPolicyDelegate());
124 ChromeContentBrowserClientExtensionsPart::
125 ~ChromeContentBrowserClientExtensionsPart() {
128 // static
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);
135 if (!registry)
136 return url;
138 const Extension* extension =
139 registry->enabled_extensions().GetHostedAppByURL(url);
140 if (!extension)
141 return 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())
146 return url;
148 // If the URL is part of an extension's web extent, convert it to an
149 // extension URL.
150 return extension->GetResourceURL(url.path());
153 // static
154 bool ChromeContentBrowserClientExtensionsPart::ShouldUseProcessPerSite(
155 Profile* profile, const GURL& effective_url) {
156 if (!effective_url.SchemeIs(kExtensionScheme))
157 return false;
159 ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
160 if (!registry)
161 return false;
163 const Extension* extension =
164 registry->enabled_extensions().GetByID(effective_url.host());
165 if (!extension)
166 return false;
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
171 // responsiveness.
172 if (extension->GetType() == Manifest::TYPE_HOSTED_APP) {
173 if (!extension->permissions_data()->HasAPIPermission(
174 APIPermission::kBackground) ||
175 !BackgroundInfo::AllowJSAccess(extension)) {
176 return false;
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.
183 return true;
186 // static
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
192 // app's cookies.
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())
199 return false;
201 return true;
204 // static
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
213 // process.
214 ExtensionRegistry* registry =
215 ExtensionRegistry::Get(process_host->GetBrowserContext());
216 if (!registry)
217 return true;
219 const Extension* new_extension =
220 registry->enabled_extensions().GetExtensionOrAppByURL(url);
221 if (new_extension && new_extension->is_hosted_app() &&
222 new_extension->id() == extensions::kWebStoreAppId &&
223 !ProcessMap::Get(process_host->GetBrowserContext())
224 ->Contains(new_extension->id(), process_host->GetID())) {
225 return false;
227 return true;
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(extensions::kExtensionScheme))
238 return false;
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 extensions::InfoMap* extension_info_map = io_data->GetExtensionInfoMap();
244 const extensions::Extension* extension =
245 extension_info_map->extensions().GetExtensionOrAppByURL(origin);
246 if (!extension)
247 return true;
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 extensions::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))
257 return true;
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.
273 return false;
276 // static
277 bool ChromeContentBrowserClientExtensionsPart::IsSuitableHost(
278 Profile* profile,
279 content::RenderProcessHost* process_host,
280 const GURL& site_url) {
281 DCHECK(profile);
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
287 // share any host.
288 if (!registry || !process_map)
289 return true;
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) ==
296 privilege_required;
299 // static
300 bool
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;
309 if (!registry)
310 return false;
312 // We have to have a valid extension with background page to proceed.
313 const Extension* extension =
314 registry->enabled_extensions().GetExtensionOrAppByURL(url);
315 if (!extension)
316 return false;
317 if (!BackgroundInfo::HasBackgroundPage(extension))
318 return false;
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
326 // starve the other.
327 std::vector<Profile*> profiles = g_browser_process->profile_manager()->
328 GetLoadedProfiles();
329 for (size_t i = 0; i < profiles.size(); ++i) {
330 ProcessManager* epm = ProcessManager::Get(profiles[i]);
331 for (extensions::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));
339 // static
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());
348 if (!registry)
349 return false;
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 &&
363 current_extension->is_hosted_app() &&
364 current_extension->id() != extensions::kWebStoreAppId)
365 current_extension = NULL;
367 const Extension* new_extension =
368 registry->enabled_extensions().GetExtensionOrAppByURL(new_url);
369 if (new_extension &&
370 new_extension->is_hosted_app() &&
371 new_extension->id() != extensions::kWebStoreAppId)
372 new_extension = NULL;
374 // First do a process check. We should force a BrowsingInstance swap if the
375 // current process doesn't know about new_extension, even if current_extension
376 // is somehow the same as new_extension.
377 ProcessMap* process_map = ProcessMap::Get(site_instance->GetBrowserContext());
378 if (new_extension &&
379 site_instance->HasProcess() &&
380 !process_map->Contains(
381 new_extension->id(), site_instance->GetProcess()->GetID()))
382 return true;
384 // Otherwise, swap BrowsingInstances if current_extension and new_extension
385 // differ.
386 return current_extension != new_extension;
389 // static
390 bool ChromeContentBrowserClientExtensionsPart::ShouldSwapProcessesForRedirect(
391 content::ResourceContext* resource_context,
392 const GURL& current_url,
393 const GURL& new_url) {
394 ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context);
395 return CrossesExtensionProcessBoundary(
396 io_data->GetExtensionInfoMap()->extensions(),
397 current_url, new_url, false);
400 // static
401 bool ChromeContentBrowserClientExtensionsPart::ShouldAllowOpenURL(
402 content::SiteInstance* site_instance,
403 const GURL& from_url,
404 const GURL& to_url,
405 bool* result) {
406 DCHECK(result);
408 // Do not allow pages from the web or other extensions navigate to
409 // non-web-accessible extension resources.
410 if (to_url.SchemeIs(kExtensionScheme) &&
411 (from_url.SchemeIsHTTPOrHTTPS() || from_url.SchemeIs(kExtensionScheme))) {
412 Profile* profile = Profile::FromBrowserContext(
413 site_instance->GetProcess()->GetBrowserContext());
414 ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
415 if (!registry) {
416 *result = true;
417 return true;
419 const Extension* extension =
420 registry->enabled_extensions().GetExtensionOrAppByURL(to_url);
421 if (!extension) {
422 *result = true;
423 return true;
425 const Extension* from_extension =
426 registry->enabled_extensions().GetExtensionOrAppByURL(
427 site_instance->GetSiteURL());
428 if (from_extension && from_extension->id() == extension->id()) {
429 *result = true;
430 return true;
433 if (!WebAccessibleResourcesInfo::IsResourceWebAccessible(
434 extension, to_url.path())) {
435 *result = false;
436 return true;
439 return false;
442 void ChromeContentBrowserClientExtensionsPart::RenderProcessWillLaunch(
443 content::RenderProcessHost* host) {
444 int id = host->GetID();
445 Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext());
447 host->AddFilter(new ChromeExtensionMessageFilter(id, profile));
448 host->AddFilter(new ExtensionMessageFilter(id, profile));
449 host->AddFilter(new IOThreadExtensionMessageFilter(id, profile));
450 host->AddFilter(new ExtensionsGuestViewMessageFilter(id, profile));
451 extension_web_request_api_helpers::SendExtensionWebRequestStatusToHost(host);
454 void ChromeContentBrowserClientExtensionsPart::SiteInstanceGotProcess(
455 SiteInstance* site_instance) {
456 BrowserContext* context = site_instance->GetProcess()->GetBrowserContext();
457 ExtensionRegistry* registry = ExtensionRegistry::Get(context);
458 if (!registry)
459 return;
461 const Extension* extension =
462 registry->enabled_extensions().GetExtensionOrAppByURL(
463 site_instance->GetSiteURL());
464 if (!extension)
465 return;
467 ProcessMap::Get(context)->Insert(extension->id(),
468 site_instance->GetProcess()->GetID(),
469 site_instance->GetId());
471 BrowserThread::PostTask(
472 BrowserThread::IO, FROM_HERE,
473 base::Bind(&InfoMap::RegisterExtensionProcess,
474 ExtensionSystem::Get(context)->info_map(), extension->id(),
475 site_instance->GetProcess()->GetID(), site_instance->GetId()));
478 void ChromeContentBrowserClientExtensionsPart::SiteInstanceDeleting(
479 SiteInstance* site_instance) {
480 BrowserContext* context = site_instance->GetBrowserContext();
481 ExtensionRegistry* registry = ExtensionRegistry::Get(context);
482 if (!registry)
483 return;
485 const Extension* extension =
486 registry->enabled_extensions().GetExtensionOrAppByURL(
487 site_instance->GetSiteURL());
488 if (!extension)
489 return;
491 ProcessMap::Get(context)->Remove(extension->id(),
492 site_instance->GetProcess()->GetID(),
493 site_instance->GetId());
495 BrowserThread::PostTask(
496 BrowserThread::IO, FROM_HERE,
497 base::Bind(&InfoMap::UnregisterExtensionProcess,
498 ExtensionSystem::Get(context)->info_map(), extension->id(),
499 site_instance->GetProcess()->GetID(), site_instance->GetId()));
502 void ChromeContentBrowserClientExtensionsPart::OverrideWebkitPrefs(
503 RenderViewHost* rvh,
504 WebPreferences* web_prefs) {
505 const ExtensionRegistry* registry =
506 ExtensionRegistry::Get(rvh->GetProcess()->GetBrowserContext());
507 if (!registry)
508 return;
510 // Note: it's not possible for kExtensionsScheme to change during the lifetime
511 // of the process.
513 // Ensure that we are only granting extension preferences to URLs with
514 // the correct scheme. Without this check, chrome-guest:// schemes used by
515 // webview tags as well as hosts that happen to match the id of an
516 // installed extension would get the wrong preferences.
517 const GURL& site_url = rvh->GetSiteInstance()->GetSiteURL();
518 if (!site_url.SchemeIs(kExtensionScheme))
519 return;
521 WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
522 ViewType view_type = GetViewType(web_contents);
523 const Extension* extension =
524 registry->enabled_extensions().GetByID(site_url.host());
525 extension_webkit_preferences::SetPreferences(extension, view_type, web_prefs);
528 void ChromeContentBrowserClientExtensionsPart::BrowserURLHandlerCreated(
529 BrowserURLHandler* handler) {
530 handler->AddHandlerPair(&ExtensionWebUI::HandleChromeURLOverride,
531 BrowserURLHandler::null_handler());
532 handler->AddHandlerPair(BrowserURLHandler::null_handler(),
533 &ExtensionWebUI::HandleChromeURLOverrideReverse);
536 void ChromeContentBrowserClientExtensionsPart::
537 GetAdditionalAllowedSchemesForFileSystem(
538 std::vector<std::string>* additional_allowed_schemes) {
539 additional_allowed_schemes->push_back(kExtensionScheme);
542 void ChromeContentBrowserClientExtensionsPart::GetURLRequestAutoMountHandlers(
543 std::vector<storage::URLRequestAutoMountHandler>* handlers) {
544 handlers->push_back(
545 base::Bind(MediaFileSystemBackend::AttemptAutoMountForURLRequest));
548 void ChromeContentBrowserClientExtensionsPart::GetAdditionalFileSystemBackends(
549 content::BrowserContext* browser_context,
550 const base::FilePath& storage_partition_path,
551 ScopedVector<storage::FileSystemBackend>* additional_backends) {
552 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
553 additional_backends->push_back(new MediaFileSystemBackend(
554 storage_partition_path,
555 pool->GetSequencedTaskRunner(
556 pool->GetNamedSequenceToken(
557 MediaFileSystemBackend::kMediaTaskRunnerName)).get()));
559 additional_backends->push_back(new sync_file_system::SyncFileSystemBackend(
560 Profile::FromBrowserContext(browser_context)));
563 void ChromeContentBrowserClientExtensionsPart::
564 AppendExtraRendererCommandLineSwitches(base::CommandLine* command_line,
565 content::RenderProcessHost* process,
566 Profile* profile) {
567 if (!process)
568 return;
569 DCHECK(profile);
570 if (ProcessMap::Get(profile)->Contains(process->GetID())) {
571 command_line->AppendSwitch(switches::kExtensionProcess);
572 #if defined(ENABLE_WEBRTC)
573 command_line->AppendSwitch(::switches::kEnableWebRtcHWH264Encoding);
574 #endif
575 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
576 switches::kEnableMojoSerialService)) {
577 command_line->AppendSwitch(switches::kEnableMojoSerialService);
582 } // namespace extensions