| Index: chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
|
| diff --git a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
|
| index de7be636ee5c4b2a750d8329912fce9397b3cf89..70648025a7565d473a6cb4b746e4d67b8c73e2c5 100644
|
| --- a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
|
| +++ b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
|
| @@ -4,22 +4,35 @@
|
|
|
| #include "chrome/browser/extensions/chrome_content_browser_client_extensions_part.h"
|
|
|
| +#include <set>
|
| +
|
| #include "base/command_line.h"
|
| +#include "chrome/browser/browser_process.h"
|
| +#include "chrome/browser/extensions/browser_permissions_policy_delegate.h"
|
| #include "chrome/browser/extensions/extension_service.h"
|
| #include "chrome/browser/extensions/extension_web_ui.h"
|
| #include "chrome/browser/extensions/extension_webkit_preferences.h"
|
| #include "chrome/browser/profiles/profile.h"
|
| +#include "chrome/browser/profiles/profile_io_data.h"
|
| +#include "chrome/browser/profiles/profile_manager.h"
|
| +#include "chrome/common/chrome_constants.h"
|
| +#include "chrome/common/extensions/extension_constants.h"
|
| +#include "chrome/common/extensions/extension_process_policy.h"
|
| +#include "chrome/common/extensions/manifest_handlers/app_isolation_info.h"
|
| #include "content/public/browser/browser_thread.h"
|
| #include "content/public/browser/browser_url_handler.h"
|
| #include "content/public/browser/render_process_host.h"
|
| #include "content/public/browser/render_view_host.h"
|
| #include "content/public/browser/site_instance.h"
|
| #include "content/public/browser/web_contents.h"
|
| +#include "extensions/browser/extension_host.h"
|
| #include "extensions/browser/extension_registry.h"
|
| #include "extensions/browser/extension_system.h"
|
| #include "extensions/browser/info_map.h"
|
| #include "extensions/browser/view_type_utils.h"
|
| #include "extensions/common/constants.h"
|
| +#include "extensions/common/manifest_handlers/background_info.h"
|
| +#include "extensions/common/manifest_handlers/web_accessible_resources_info.h"
|
| #include "extensions/common/switches.h"
|
|
|
| // TODO(thestig): Remove ifdefs when extensions no longer build on mobile.
|
| @@ -40,14 +53,359 @@ using content::WebPreferences;
|
|
|
| namespace extensions {
|
|
|
| +namespace {
|
| +
|
| +// Used by the GetPrivilegeRequiredByUrl() and GetProcessPrivilege() functions
|
| +// below. Extension, and isolated apps require different privileges to be
|
| +// granted to their RenderProcessHosts. This classification allows us to make
|
| +// sure URLs are served by hosts with the right set of privileges.
|
| +enum RenderProcessHostPrivilege {
|
| + PRIV_NORMAL,
|
| + PRIV_HOSTED,
|
| + PRIV_ISOLATED,
|
| + PRIV_EXTENSION,
|
| +};
|
| +
|
| +RenderProcessHostPrivilege GetPrivilegeRequiredByUrl(
|
| + const GURL& url,
|
| + ExtensionService* service) {
|
| + // Default to a normal renderer cause it is lower privileged. This should only
|
| + // occur if the URL on a site instance is either malformed, or uninitialized.
|
| + // If it is malformed, then there is no need for better privileges anyways.
|
| + // If it is uninitialized, but eventually settles on being an a scheme other
|
| + // than normal webrenderer, the navigation logic will correct us out of band
|
| + // anyways.
|
| + if (!url.is_valid())
|
| + return PRIV_NORMAL;
|
| +
|
| + if (!url.SchemeIs(kExtensionScheme))
|
| + return PRIV_NORMAL;
|
| +
|
| + const Extension* extension = service->extensions()->GetByID(url.host());
|
| + if (extension && AppIsolationInfo::HasIsolatedStorage(extension))
|
| + return PRIV_ISOLATED;
|
| + if (extension && extension->is_hosted_app())
|
| + return PRIV_HOSTED;
|
| + return PRIV_EXTENSION;
|
| +}
|
| +
|
| +RenderProcessHostPrivilege GetProcessPrivilege(
|
| + content::RenderProcessHost* process_host,
|
| + ProcessMap* process_map,
|
| + ExtensionService* service) {
|
| + std::set<std::string> extension_ids =
|
| + process_map->GetExtensionsInProcess(process_host->GetID());
|
| + if (extension_ids.empty())
|
| + return PRIV_NORMAL;
|
| +
|
| + for (std::set<std::string>::iterator iter = extension_ids.begin();
|
| + iter != extension_ids.end(); ++iter) {
|
| + const Extension* extension = service->GetExtensionById(*iter, false);
|
| + if (extension && AppIsolationInfo::HasIsolatedStorage(extension))
|
| + return PRIV_ISOLATED;
|
| + if (extension && extension->is_hosted_app())
|
| + return PRIV_HOSTED;
|
| + }
|
| +
|
| + return PRIV_EXTENSION;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| ChromeContentBrowserClientExtensionsPart::
|
| ChromeContentBrowserClientExtensionsPart() {
|
| + permissions_policy_delegate_.reset(new BrowserPermissionsPolicyDelegate());
|
| }
|
|
|
| ChromeContentBrowserClientExtensionsPart::
|
| ~ChromeContentBrowserClientExtensionsPart() {
|
| }
|
|
|
| +// static
|
| +GURL ChromeContentBrowserClientExtensionsPart::GetEffectiveURL(
|
| + Profile* profile, const GURL& url) {
|
| + // If the input |url| is part of an installed app, the effective URL is an
|
| + // extension URL with the ID of that extension as the host. This has the
|
| + // effect of grouping apps together in a common SiteInstance.
|
| + ExtensionService* extension_service =
|
| + ExtensionSystem::Get(profile)->extension_service();
|
| + if (!extension_service)
|
| + return url;
|
| +
|
| + const Extension* extension =
|
| + extension_service->extensions()->GetHostedAppByURL(url);
|
| + if (!extension)
|
| + return url;
|
| +
|
| + // Bookmark apps do not use the hosted app process model, and should be
|
| + // treated as normal URLs.
|
| + if (extension->from_bookmark())
|
| + return url;
|
| +
|
| + // If the URL is part of an extension's web extent, convert it to an
|
| + // extension URL.
|
| + return extension->GetResourceURL(url.path());
|
| +}
|
| +
|
| +// static
|
| +bool ChromeContentBrowserClientExtensionsPart::ShouldUseProcessPerSite(
|
| + Profile* profile, const GURL& effective_url) {
|
| + if (!effective_url.SchemeIs(kExtensionScheme))
|
| + return false;
|
| +
|
| + ExtensionService* extension_service =
|
| + ExtensionSystem::Get(profile)->extension_service();
|
| + if (!extension_service)
|
| + return false;
|
| +
|
| + const Extension* extension =
|
| + extension_service->extensions()->GetExtensionOrAppByURL(effective_url);
|
| + if (!extension)
|
| + return false;
|
| +
|
| + // If the URL is part of a hosted app that does not have the background
|
| + // permission, or that does not allow JavaScript access to the background
|
| + // page, we want to give each instance its own process to improve
|
| + // responsiveness.
|
| + if (extension->GetType() == Manifest::TYPE_HOSTED_APP) {
|
| + if (!extension->permissions_data()->HasAPIPermission(
|
| + APIPermission::kBackground) ||
|
| + !BackgroundInfo::AllowJSAccess(extension)) {
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + // Hosted apps that have script access to their background page must use
|
| + // process per site, since all instances can make synchronous calls to the
|
| + // background window. Other extensions should use process per site as well.
|
| + return true;
|
| +}
|
| +
|
| +// static
|
| +bool ChromeContentBrowserClientExtensionsPart::CanCommitURL(
|
| + content::RenderProcessHost* process_host, const GURL& url) {
|
| + // We need to let most extension URLs commit in any process, since this can
|
| + // be allowed due to web_accessible_resources. Most hosted app URLs may also
|
| + // load in any process (e.g., in an iframe). However, the Chrome Web Store
|
| + // cannot be loaded in iframes and should never be requested outside its
|
| + // process.
|
| + Profile* profile =
|
| + Profile::FromBrowserContext(process_host->GetBrowserContext());
|
| + ExtensionService* service =
|
| + ExtensionSystem::Get(profile)->extension_service();
|
| + if (!service)
|
| + return true;
|
| +
|
| + const Extension* new_extension =
|
| + service->extensions()->GetExtensionOrAppByURL(url);
|
| + if (new_extension &&
|
| + new_extension->is_hosted_app() &&
|
| + new_extension->id() == extension_misc::kWebStoreAppId &&
|
| + !ProcessMap::Get(profile)->Contains(
|
| + new_extension->id(), process_host->GetID())) {
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +// static
|
| +bool ChromeContentBrowserClientExtensionsPart::IsSuitableHost(
|
| + Profile* profile,
|
| + content::RenderProcessHost* process_host,
|
| + const GURL& site_url) {
|
| + DCHECK(profile);
|
| +
|
| + ExtensionService* service =
|
| + ExtensionSystem::Get(profile)->extension_service();
|
| + ProcessMap* process_map = ProcessMap::Get(profile);
|
| +
|
| + // These may be NULL during tests. In that case, just assume any site can
|
| + // share any host.
|
| + if (!service || !process_map)
|
| + return true;
|
| +
|
| + // Otherwise, just make sure the process privilege matches the privilege
|
| + // required by the site.
|
| + RenderProcessHostPrivilege privilege_required =
|
| + GetPrivilegeRequiredByUrl(site_url, service);
|
| + return GetProcessPrivilege(process_host, process_map, service) ==
|
| + privilege_required;
|
| +}
|
| +
|
| +// static
|
| +bool
|
| +ChromeContentBrowserClientExtensionsPart::ShouldTryToUseExistingProcessHost(
|
| + Profile* profile, const GURL& url) {
|
| + // This function is trying to limit the amount of processes used by extensions
|
| + // with background pages. It uses a globally set percentage of processes to
|
| + // run such extensions and if the limit is exceeded, it returns true, to
|
| + // indicate to the content module to group extensions together.
|
| + ExtensionService* service = profile ?
|
| + ExtensionSystem::Get(profile)->extension_service() : NULL;
|
| + if (!service)
|
| + return false;
|
| +
|
| + // We have to have a valid extension with background page to proceed.
|
| + const Extension* extension =
|
| + service->extensions()->GetExtensionOrAppByURL(url);
|
| + if (!extension)
|
| + return false;
|
| + if (!BackgroundInfo::HasBackgroundPage(extension))
|
| + return false;
|
| +
|
| + std::set<int> process_ids;
|
| + size_t max_process_count =
|
| + content::RenderProcessHost::GetMaxRendererProcessCount();
|
| +
|
| + // Go through all profiles to ensure we have total count of extension
|
| + // processes containing background pages, otherwise one profile can
|
| + // starve the other.
|
| + std::vector<Profile*> profiles = g_browser_process->profile_manager()->
|
| + GetLoadedProfiles();
|
| + for (size_t i = 0; i < profiles.size(); ++i) {
|
| + ProcessManager* epm = ExtensionSystem::Get(profiles[i])->process_manager();
|
| + for (ProcessManager::const_iterator iter = epm->background_hosts().begin();
|
| + iter != epm->background_hosts().end(); ++iter) {
|
| + const ExtensionHost* host = *iter;
|
| + process_ids.insert(host->render_process_host()->GetID());
|
| + }
|
| + }
|
| +
|
| + return (process_ids.size() >
|
| + (max_process_count * chrome::kMaxShareOfExtensionProcesses));
|
| +}
|
| +
|
| +// static
|
| +bool ChromeContentBrowserClientExtensionsPart::
|
| + ShouldSwapBrowsingInstancesForNavigation(SiteInstance* site_instance,
|
| + const GURL& current_url,
|
| + const GURL& new_url) {
|
| + // If we don't have an ExtensionService, then rely on the SiteInstance logic
|
| + // in RenderFrameHostManager to decide when to swap.
|
| + Profile* profile =
|
| + Profile::FromBrowserContext(site_instance->GetBrowserContext());
|
| + ExtensionService* service =
|
| + ExtensionSystem::Get(profile)->extension_service();
|
| + if (!service)
|
| + return false;
|
| +
|
| + // We must use a new BrowsingInstance (forcing a process swap and disabling
|
| + // scripting by existing tabs) if one of the URLs is an extension and the
|
| + // other is not the exact same extension.
|
| + //
|
| + // We ignore hosted apps here so that other tabs in their BrowsingInstance can
|
| + // use postMessage with them. (The exception is the Chrome Web Store, which
|
| + // is a hosted app that requires its own BrowsingInstance.) Navigations
|
| + // to/from a hosted app will still trigger a SiteInstance swap in
|
| + // RenderFrameHostManager.
|
| + const Extension* current_extension =
|
| + service->extensions()->GetExtensionOrAppByURL(current_url);
|
| + if (current_extension &&
|
| + current_extension->is_hosted_app() &&
|
| + current_extension->id() != extension_misc::kWebStoreAppId)
|
| + current_extension = NULL;
|
| +
|
| + const Extension* new_extension =
|
| + service->extensions()->GetExtensionOrAppByURL(new_url);
|
| + if (new_extension &&
|
| + new_extension->is_hosted_app() &&
|
| + new_extension->id() != extension_misc::kWebStoreAppId)
|
| + new_extension = NULL;
|
| +
|
| + // First do a process check. We should force a BrowsingInstance swap if the
|
| + // current process doesn't know about new_extension, even if current_extension
|
| + // is somehow the same as new_extension.
|
| + ProcessMap* process_map = ProcessMap::Get(profile);
|
| + if (new_extension &&
|
| + site_instance->HasProcess() &&
|
| + !process_map->Contains(
|
| + new_extension->id(), site_instance->GetProcess()->GetID()))
|
| + return true;
|
| +
|
| + // Otherwise, swap BrowsingInstances if current_extension and new_extension
|
| + // differ.
|
| + return current_extension != new_extension;
|
| +}
|
| +
|
| +// static
|
| +bool ChromeContentBrowserClientExtensionsPart::ShouldSwapProcessesForRedirect(
|
| + content::ResourceContext* resource_context,
|
| + const GURL& current_url,
|
| + const GURL& new_url) {
|
| + ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context);
|
| + return CrossesExtensionProcessBoundary(
|
| + io_data->GetExtensionInfoMap()->extensions(),
|
| + current_url, new_url, false);
|
| +}
|
| +
|
| +// static
|
| +std::string ChromeContentBrowserClientExtensionsPart::GetWorkerProcessTitle(
|
| + const GURL& url, content::ResourceContext* context) {
|
| + // Check if it's an extension-created worker, in which case we want to use
|
| + // the name of the extension.
|
| + ProfileIOData* io_data = ProfileIOData::FromResourceContext(context);
|
| + const Extension* extension =
|
| + io_data->GetExtensionInfoMap()->extensions().GetByID(url.host());
|
| + return extension ? extension->name() : std::string();
|
| +}
|
| +
|
| +// static
|
| +bool ChromeContentBrowserClientExtensionsPart::ShouldAllowOpenURL(
|
| + content::SiteInstance* site_instance,
|
| + const GURL& from_url,
|
| + const GURL& to_url,
|
| + bool* result) {
|
| + DCHECK(result);
|
| +
|
| + // Do not allow pages from the web or other extensions navigate to
|
| + // non-web-accessible extension resources.
|
| + if (to_url.SchemeIs(kExtensionScheme) &&
|
| + (from_url.SchemeIsHTTPOrHTTPS() || from_url.SchemeIs(kExtensionScheme))) {
|
| + Profile* profile = Profile::FromBrowserContext(
|
| + site_instance->GetProcess()->GetBrowserContext());
|
| + ExtensionService* service =
|
| + ExtensionSystem::Get(profile)->extension_service();
|
| + if (!service) {
|
| + *result = true;
|
| + return true;
|
| + }
|
| + const Extension* extension =
|
| + service->extensions()->GetExtensionOrAppByURL(to_url);
|
| + if (!extension) {
|
| + *result = true;
|
| + return true;
|
| + }
|
| + const Extension* from_extension =
|
| + service->extensions()->GetExtensionOrAppByURL(
|
| + site_instance->GetSiteURL());
|
| + if (from_extension && from_extension->id() == extension->id()) {
|
| + *result = true;
|
| + return true;
|
| + }
|
| +
|
| + if (!WebAccessibleResourcesInfo::IsResourceWebAccessible(
|
| + extension, to_url.path())) {
|
| + *result = false;
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +// static
|
| +void ChromeContentBrowserClientExtensionsPart::SetSigninProcess(
|
| + content::SiteInstance* site_instance) {
|
| + Profile* profile =
|
| + Profile::FromBrowserContext(site_instance->GetBrowserContext());
|
| + DCHECK(profile);
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO,
|
| + FROM_HERE,
|
| + base::Bind(&InfoMap::SetSigninProcess,
|
| + ExtensionSystem::Get(profile)->info_map(),
|
| + site_instance->GetProcess()->GetID()));
|
| +}
|
| +
|
| void ChromeContentBrowserClientExtensionsPart::RenderProcessWillLaunch(
|
| content::RenderProcessHost* host) {
|
| #if defined(ENABLE_EXTENSIONS)
|
|
|