Add a webstorePrivate API to show a permission prompt for delegated bundle installs
[chromium-blink-merge.git] / chrome / browser / extensions / chrome_content_browser_client_extensions_part.cc
blobe90d98b725ac2a817ff62643821f57601787700c
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 "components/guest_view/browser/guest_view_message_filter.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/browser_url_handler.h"
27 #include "content/public/browser/render_process_host.h"
28 #include "content/public/browser/render_view_host.h"
29 #include "content/public/browser/site_instance.h"
30 #include "content/public/browser/web_contents.h"
31 #include "content/public/common/content_switches.h"
32 #include "extensions/browser/api/web_request/web_request_api.h"
33 #include "extensions/browser/api/web_request/web_request_api_helpers.h"
34 #include "extensions/browser/extension_host.h"
35 #include "extensions/browser/extension_message_filter.h"
36 #include "extensions/browser/extension_registry.h"
37 #include "extensions/browser/extension_system.h"
38 #include "extensions/browser/guest_view/extensions_guest_view_message_filter.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_handlers/background_info.h"
44 #include "extensions/common/manifest_handlers/web_accessible_resources_info.h"
45 #include "extensions/common/switches.h"
47 using content::BrowserContext;
48 using content::BrowserThread;
49 using content::BrowserURLHandler;
50 using content::RenderViewHost;
51 using content::SiteInstance;
52 using content::WebContents;
53 using content::WebPreferences;
55 namespace extensions {
57 namespace {
59 // Used by the GetPrivilegeRequiredByUrl() and GetProcessPrivilege() functions
60 // below. Extension, and isolated apps require different privileges to be
61 // granted to their RenderProcessHosts. This classification allows us to make
62 // sure URLs are served by hosts with the right set of privileges.
63 enum RenderProcessHostPrivilege {
64 PRIV_NORMAL,
65 PRIV_HOSTED,
66 PRIV_ISOLATED,
67 PRIV_EXTENSION,
70 RenderProcessHostPrivilege GetPrivilegeRequiredByUrl(
71 const GURL& url,
72 ExtensionRegistry* registry) {
73 // Default to a normal renderer cause it is lower privileged. This should only
74 // occur if the URL on a site instance is either malformed, or uninitialized.
75 // If it is malformed, then there is no need for better privileges anyways.
76 // If it is uninitialized, but eventually settles on being an a scheme other
77 // than normal webrenderer, the navigation logic will correct us out of band
78 // anyways.
79 if (!url.is_valid())
80 return PRIV_NORMAL;
82 if (!url.SchemeIs(kExtensionScheme))
83 return PRIV_NORMAL;
85 const Extension* extension =
86 registry->enabled_extensions().GetByID(url.host());
87 if (extension && AppIsolationInfo::HasIsolatedStorage(extension))
88 return PRIV_ISOLATED;
89 if (extension && extension->is_hosted_app())
90 return PRIV_HOSTED;
91 return PRIV_EXTENSION;
94 RenderProcessHostPrivilege GetProcessPrivilege(
95 content::RenderProcessHost* process_host,
96 ProcessMap* process_map,
97 ExtensionRegistry* registry) {
98 std::set<std::string> extension_ids =
99 process_map->GetExtensionsInProcess(process_host->GetID());
100 if (extension_ids.empty())
101 return PRIV_NORMAL;
103 for (const std::string& extension_id : extension_ids) {
104 const Extension* extension =
105 registry->enabled_extensions().GetByID(extension_id);
106 if (extension && AppIsolationInfo::HasIsolatedStorage(extension))
107 return PRIV_ISOLATED;
108 if (extension && extension->is_hosted_app())
109 return PRIV_HOSTED;
112 return PRIV_EXTENSION;
115 } // namespace
117 ChromeContentBrowserClientExtensionsPart::
118 ChromeContentBrowserClientExtensionsPart() {
119 permissions_policy_delegate_.reset(new BrowserPermissionsPolicyDelegate());
122 ChromeContentBrowserClientExtensionsPart::
123 ~ChromeContentBrowserClientExtensionsPart() {
126 // static
127 GURL ChromeContentBrowserClientExtensionsPart::GetEffectiveURL(
128 Profile* profile, const GURL& url) {
129 // If the input |url| is part of an installed app, the effective URL is an
130 // extension URL with the ID of that extension as the host. This has the
131 // effect of grouping apps together in a common SiteInstance.
132 ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
133 if (!registry)
134 return url;
136 const Extension* extension =
137 registry->enabled_extensions().GetHostedAppByURL(url);
138 if (!extension)
139 return url;
141 // Bookmark apps do not use the hosted app process model, and should be
142 // treated as normal URLs.
143 if (extension->from_bookmark())
144 return url;
146 // If the URL is part of an extension's web extent, convert it to an
147 // extension URL.
148 return extension->GetResourceURL(url.path());
151 // static
152 bool ChromeContentBrowserClientExtensionsPart::ShouldUseProcessPerSite(
153 Profile* profile, const GURL& effective_url) {
154 if (!effective_url.SchemeIs(kExtensionScheme))
155 return false;
157 ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
158 if (!registry)
159 return false;
161 const Extension* extension =
162 registry->enabled_extensions().GetByID(effective_url.host());
163 if (!extension)
164 return false;
166 // If the URL is part of a hosted app that does not have the background
167 // permission, or that does not allow JavaScript access to the background
168 // page, we want to give each instance its own process to improve
169 // responsiveness.
170 if (extension->GetType() == Manifest::TYPE_HOSTED_APP) {
171 if (!extension->permissions_data()->HasAPIPermission(
172 APIPermission::kBackground) ||
173 !BackgroundInfo::AllowJSAccess(extension)) {
174 return false;
178 // Hosted apps that have script access to their background page must use
179 // process per site, since all instances can make synchronous calls to the
180 // background window. Other extensions should use process per site as well.
181 return true;
184 // static
185 bool ChromeContentBrowserClientExtensionsPart::CanCommitURL(
186 content::RenderProcessHost* process_host, const GURL& url) {
187 // We need to let most extension URLs commit in any process, since this can
188 // be allowed due to web_accessible_resources. Most hosted app URLs may also
189 // load in any process (e.g., in an iframe). However, the Chrome Web Store
190 // cannot be loaded in iframes and should never be requested outside its
191 // process.
192 ExtensionRegistry* registry =
193 ExtensionRegistry::Get(process_host->GetBrowserContext());
194 if (!registry)
195 return true;
197 const Extension* new_extension =
198 registry->enabled_extensions().GetExtensionOrAppByURL(url);
199 if (new_extension && new_extension->is_hosted_app() &&
200 new_extension->id() == extensions::kWebStoreAppId &&
201 !ProcessMap::Get(process_host->GetBrowserContext())
202 ->Contains(new_extension->id(), process_host->GetID())) {
203 return false;
205 return true;
208 // static
209 bool ChromeContentBrowserClientExtensionsPart::IsSuitableHost(
210 Profile* profile,
211 content::RenderProcessHost* process_host,
212 const GURL& site_url) {
213 DCHECK(profile);
215 ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
216 ProcessMap* process_map = ProcessMap::Get(profile);
218 // These may be NULL during tests. In that case, just assume any site can
219 // share any host.
220 if (!registry || !process_map)
221 return true;
223 // Otherwise, just make sure the process privilege matches the privilege
224 // required by the site.
225 RenderProcessHostPrivilege privilege_required =
226 GetPrivilegeRequiredByUrl(site_url, registry);
227 return GetProcessPrivilege(process_host, process_map, registry) ==
228 privilege_required;
231 // static
232 bool
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 ExtensionRegistry* registry =
240 profile ? ExtensionRegistry::Get(profile) : NULL;
241 if (!registry)
242 return false;
244 // We have to have a valid extension with background page to proceed.
245 const Extension* extension =
246 registry->enabled_extensions().GetExtensionOrAppByURL(url);
247 if (!extension)
248 return false;
249 if (!BackgroundInfo::HasBackgroundPage(extension))
250 return false;
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
258 // starve the other.
259 std::vector<Profile*> profiles = g_browser_process->profile_manager()->
260 GetLoadedProfiles();
261 for (size_t i = 0; i < profiles.size(); ++i) {
262 ProcessManager* epm = ProcessManager::Get(profiles[i]);
263 for (extensions::ExtensionHost* host : epm->background_hosts())
264 process_ids.insert(host->render_process_host()->GetID());
267 return (process_ids.size() >
268 (max_process_count * chrome::kMaxShareOfExtensionProcesses));
271 // static
272 bool ChromeContentBrowserClientExtensionsPart::
273 ShouldSwapBrowsingInstancesForNavigation(SiteInstance* site_instance,
274 const GURL& current_url,
275 const GURL& new_url) {
276 // If we don't have an ExtensionRegistry, then rely on the SiteInstance logic
277 // in RenderFrameHostManager to decide when to swap.
278 ExtensionRegistry* registry =
279 ExtensionRegistry::Get(site_instance->GetBrowserContext());
280 if (!registry)
281 return false;
283 // We must use a new BrowsingInstance (forcing a process swap and disabling
284 // scripting by existing tabs) if one of the URLs is an extension and the
285 // other is not the exact same extension.
287 // We ignore hosted apps here so that other tabs in their BrowsingInstance can
288 // use postMessage with them. (The exception is the Chrome Web Store, which
289 // is a hosted app that requires its own BrowsingInstance.) Navigations
290 // to/from a hosted app will still trigger a SiteInstance swap in
291 // RenderFrameHostManager.
292 const Extension* current_extension =
293 registry->enabled_extensions().GetExtensionOrAppByURL(current_url);
294 if (current_extension &&
295 current_extension->is_hosted_app() &&
296 current_extension->id() != extensions::kWebStoreAppId)
297 current_extension = NULL;
299 const Extension* new_extension =
300 registry->enabled_extensions().GetExtensionOrAppByURL(new_url);
301 if (new_extension &&
302 new_extension->is_hosted_app() &&
303 new_extension->id() != extensions::kWebStoreAppId)
304 new_extension = NULL;
306 // First do a process check. We should force a BrowsingInstance swap if the
307 // current process doesn't know about new_extension, even if current_extension
308 // is somehow the same as new_extension.
309 ProcessMap* process_map = ProcessMap::Get(site_instance->GetBrowserContext());
310 if (new_extension &&
311 site_instance->HasProcess() &&
312 !process_map->Contains(
313 new_extension->id(), site_instance->GetProcess()->GetID()))
314 return true;
316 // Otherwise, swap BrowsingInstances if current_extension and new_extension
317 // differ.
318 return current_extension != new_extension;
321 // static
322 bool ChromeContentBrowserClientExtensionsPart::ShouldSwapProcessesForRedirect(
323 content::ResourceContext* resource_context,
324 const GURL& current_url,
325 const GURL& new_url) {
326 ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context);
327 return CrossesExtensionProcessBoundary(
328 io_data->GetExtensionInfoMap()->extensions(),
329 current_url, new_url, false);
332 // static
333 bool ChromeContentBrowserClientExtensionsPart::ShouldAllowOpenURL(
334 content::SiteInstance* site_instance,
335 const GURL& from_url,
336 const GURL& to_url,
337 bool* result) {
338 DCHECK(result);
340 // Do not allow pages from the web or other extensions navigate to
341 // non-web-accessible extension resources.
342 if (to_url.SchemeIs(kExtensionScheme) &&
343 (from_url.SchemeIsHTTPOrHTTPS() || from_url.SchemeIs(kExtensionScheme))) {
344 Profile* profile = Profile::FromBrowserContext(
345 site_instance->GetProcess()->GetBrowserContext());
346 ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
347 if (!registry) {
348 *result = true;
349 return true;
351 const Extension* extension =
352 registry->enabled_extensions().GetExtensionOrAppByURL(to_url);
353 if (!extension) {
354 *result = true;
355 return true;
357 const Extension* from_extension =
358 registry->enabled_extensions().GetExtensionOrAppByURL(
359 site_instance->GetSiteURL());
360 if (from_extension && from_extension->id() == extension->id()) {
361 *result = true;
362 return true;
365 if (!WebAccessibleResourcesInfo::IsResourceWebAccessible(
366 extension, to_url.path())) {
367 *result = false;
368 return true;
371 return false;
374 void ChromeContentBrowserClientExtensionsPart::RenderProcessWillLaunch(
375 content::RenderProcessHost* host) {
376 int id = host->GetID();
377 Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext());
379 host->AddFilter(new ChromeExtensionMessageFilter(id, profile));
380 host->AddFilter(new ExtensionMessageFilter(id, profile));
381 host->AddFilter(new IOThreadExtensionMessageFilter(id, profile));
382 host->AddFilter(new ExtensionsGuestViewMessageFilter(id, profile));
383 extension_web_request_api_helpers::SendExtensionWebRequestStatusToHost(host);
386 void ChromeContentBrowserClientExtensionsPart::SiteInstanceGotProcess(
387 SiteInstance* site_instance) {
388 BrowserContext* context = site_instance->GetProcess()->GetBrowserContext();
389 ExtensionRegistry* registry = ExtensionRegistry::Get(context);
390 if (!registry)
391 return;
393 const Extension* extension =
394 registry->enabled_extensions().GetExtensionOrAppByURL(
395 site_instance->GetSiteURL());
396 if (!extension)
397 return;
399 ProcessMap::Get(context)->Insert(extension->id(),
400 site_instance->GetProcess()->GetID(),
401 site_instance->GetId());
403 BrowserThread::PostTask(
404 BrowserThread::IO, FROM_HERE,
405 base::Bind(&InfoMap::RegisterExtensionProcess,
406 ExtensionSystem::Get(context)->info_map(), extension->id(),
407 site_instance->GetProcess()->GetID(), site_instance->GetId()));
410 void ChromeContentBrowserClientExtensionsPart::SiteInstanceDeleting(
411 SiteInstance* site_instance) {
412 BrowserContext* context = site_instance->GetBrowserContext();
413 ExtensionRegistry* registry = ExtensionRegistry::Get(context);
414 if (!registry)
415 return;
417 const Extension* extension =
418 registry->enabled_extensions().GetExtensionOrAppByURL(
419 site_instance->GetSiteURL());
420 if (!extension)
421 return;
423 ProcessMap::Get(context)->Remove(extension->id(),
424 site_instance->GetProcess()->GetID(),
425 site_instance->GetId());
427 BrowserThread::PostTask(
428 BrowserThread::IO, FROM_HERE,
429 base::Bind(&InfoMap::UnregisterExtensionProcess,
430 ExtensionSystem::Get(context)->info_map(), extension->id(),
431 site_instance->GetProcess()->GetID(), site_instance->GetId()));
434 void ChromeContentBrowserClientExtensionsPart::OverrideWebkitPrefs(
435 RenderViewHost* rvh,
436 WebPreferences* web_prefs) {
437 const ExtensionRegistry* registry =
438 ExtensionRegistry::Get(rvh->GetProcess()->GetBrowserContext());
439 if (!registry)
440 return;
442 // Note: it's not possible for kExtensionsScheme to change during the lifetime
443 // of the process.
445 // Ensure that we are only granting extension preferences to URLs with
446 // the correct scheme. Without this check, chrome-guest:// schemes used by
447 // webview tags as well as hosts that happen to match the id of an
448 // installed extension would get the wrong preferences.
449 const GURL& site_url = rvh->GetSiteInstance()->GetSiteURL();
450 if (!site_url.SchemeIs(kExtensionScheme))
451 return;
453 WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
454 ViewType view_type = GetViewType(web_contents);
455 const Extension* extension =
456 registry->enabled_extensions().GetByID(site_url.host());
457 extension_webkit_preferences::SetPreferences(extension, view_type, web_prefs);
460 void ChromeContentBrowserClientExtensionsPart::BrowserURLHandlerCreated(
461 BrowserURLHandler* handler) {
462 handler->AddHandlerPair(&ExtensionWebUI::HandleChromeURLOverride,
463 BrowserURLHandler::null_handler());
464 handler->AddHandlerPair(BrowserURLHandler::null_handler(),
465 &ExtensionWebUI::HandleChromeURLOverrideReverse);
468 void ChromeContentBrowserClientExtensionsPart::
469 GetAdditionalAllowedSchemesForFileSystem(
470 std::vector<std::string>* additional_allowed_schemes) {
471 additional_allowed_schemes->push_back(kExtensionScheme);
474 void ChromeContentBrowserClientExtensionsPart::GetURLRequestAutoMountHandlers(
475 std::vector<storage::URLRequestAutoMountHandler>* handlers) {
476 handlers->push_back(
477 base::Bind(MediaFileSystemBackend::AttemptAutoMountForURLRequest));
480 void ChromeContentBrowserClientExtensionsPart::GetAdditionalFileSystemBackends(
481 content::BrowserContext* browser_context,
482 const base::FilePath& storage_partition_path,
483 ScopedVector<storage::FileSystemBackend>* additional_backends) {
484 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
485 additional_backends->push_back(new MediaFileSystemBackend(
486 storage_partition_path,
487 pool->GetSequencedTaskRunner(
488 pool->GetNamedSequenceToken(
489 MediaFileSystemBackend::kMediaTaskRunnerName)).get()));
491 additional_backends->push_back(new sync_file_system::SyncFileSystemBackend(
492 Profile::FromBrowserContext(browser_context)));
495 void ChromeContentBrowserClientExtensionsPart::
496 AppendExtraRendererCommandLineSwitches(base::CommandLine* command_line,
497 content::RenderProcessHost* process,
498 Profile* profile) {
499 if (!process)
500 return;
501 DCHECK(profile);
502 if (ProcessMap::Get(profile)->Contains(process->GetID())) {
503 command_line->AppendSwitch(switches::kExtensionProcess);
504 #if defined(ENABLE_WEBRTC)
505 command_line->AppendSwitch(::switches::kEnableWebRtcHWH264Encoding);
506 #endif
507 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
508 switches::kEnableMojoSerialService)) {
509 command_line->AppendSwitch(switches::kEnableMojoSerialService);
514 } // namespace extensions