OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/chrome_content_browser_client_extensions_par
t.h" | 5 #include "chrome/browser/extensions/chrome_content_browser_client_extensions_par
t.h" |
6 | 6 |
| 7 #include <set> |
| 8 |
7 #include "base/command_line.h" | 9 #include "base/command_line.h" |
| 10 #include "chrome/browser/browser_process.h" |
| 11 #include "chrome/browser/extensions/browser_permissions_policy_delegate.h" |
8 #include "chrome/browser/extensions/extension_service.h" | 12 #include "chrome/browser/extensions/extension_service.h" |
9 #include "chrome/browser/extensions/extension_web_ui.h" | 13 #include "chrome/browser/extensions/extension_web_ui.h" |
10 #include "chrome/browser/extensions/extension_webkit_preferences.h" | 14 #include "chrome/browser/extensions/extension_webkit_preferences.h" |
11 #include "chrome/browser/profiles/profile.h" | 15 #include "chrome/browser/profiles/profile.h" |
| 16 #include "chrome/browser/profiles/profile_io_data.h" |
| 17 #include "chrome/browser/profiles/profile_manager.h" |
| 18 #include "chrome/common/chrome_constants.h" |
| 19 #include "chrome/common/extensions/extension_constants.h" |
| 20 #include "chrome/common/extensions/extension_process_policy.h" |
| 21 #include "chrome/common/extensions/manifest_handlers/app_isolation_info.h" |
12 #include "content/public/browser/browser_thread.h" | 22 #include "content/public/browser/browser_thread.h" |
13 #include "content/public/browser/browser_url_handler.h" | 23 #include "content/public/browser/browser_url_handler.h" |
14 #include "content/public/browser/render_process_host.h" | 24 #include "content/public/browser/render_process_host.h" |
15 #include "content/public/browser/render_view_host.h" | 25 #include "content/public/browser/render_view_host.h" |
16 #include "content/public/browser/site_instance.h" | 26 #include "content/public/browser/site_instance.h" |
17 #include "content/public/browser/web_contents.h" | 27 #include "content/public/browser/web_contents.h" |
| 28 #include "extensions/browser/extension_host.h" |
18 #include "extensions/browser/extension_registry.h" | 29 #include "extensions/browser/extension_registry.h" |
19 #include "extensions/browser/extension_system.h" | 30 #include "extensions/browser/extension_system.h" |
20 #include "extensions/browser/info_map.h" | 31 #include "extensions/browser/info_map.h" |
21 #include "extensions/browser/view_type_utils.h" | 32 #include "extensions/browser/view_type_utils.h" |
22 #include "extensions/common/constants.h" | 33 #include "extensions/common/constants.h" |
| 34 #include "extensions/common/manifest_handlers/background_info.h" |
| 35 #include "extensions/common/manifest_handlers/web_accessible_resources_info.h" |
23 #include "extensions/common/switches.h" | 36 #include "extensions/common/switches.h" |
24 | 37 |
25 // TODO(thestig): Remove ifdefs when extensions no longer build on mobile. | 38 // TODO(thestig): Remove ifdefs when extensions no longer build on mobile. |
26 #if defined(ENABLE_EXTENSIONS) | 39 #if defined(ENABLE_EXTENSIONS) |
27 #include "chrome/browser/extensions/api/web_request/web_request_api.h" | 40 #include "chrome/browser/extensions/api/web_request/web_request_api.h" |
28 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h" | 41 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h" |
29 #include "chrome/browser/renderer_host/chrome_extension_message_filter.h" | 42 #include "chrome/browser/renderer_host/chrome_extension_message_filter.h" |
30 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h" | 43 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h" |
31 #include "extensions/browser/extension_message_filter.h" | 44 #include "extensions/browser/extension_message_filter.h" |
32 #endif | 45 #endif |
33 | 46 |
34 using content::BrowserThread; | 47 using content::BrowserThread; |
35 using content::BrowserURLHandler; | 48 using content::BrowserURLHandler; |
36 using content::RenderViewHost; | 49 using content::RenderViewHost; |
37 using content::SiteInstance; | 50 using content::SiteInstance; |
38 using content::WebContents; | 51 using content::WebContents; |
39 using content::WebPreferences; | 52 using content::WebPreferences; |
40 | 53 |
41 namespace extensions { | 54 namespace extensions { |
42 | 55 |
| 56 namespace { |
| 57 |
| 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, |
| 67 }; |
| 68 |
| 69 RenderProcessHostPrivilege GetPrivilegeRequiredByUrl( |
| 70 const GURL& url, |
| 71 ExtensionService* service) { |
| 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; |
| 80 |
| 81 if (!url.SchemeIs(kExtensionScheme)) |
| 82 return PRIV_NORMAL; |
| 83 |
| 84 const Extension* extension = service->extensions()->GetByID(url.host()); |
| 85 if (extension && AppIsolationInfo::HasIsolatedStorage(extension)) |
| 86 return PRIV_ISOLATED; |
| 87 if (extension && extension->is_hosted_app()) |
| 88 return PRIV_HOSTED; |
| 89 return PRIV_EXTENSION; |
| 90 } |
| 91 |
| 92 RenderProcessHostPrivilege GetProcessPrivilege( |
| 93 content::RenderProcessHost* process_host, |
| 94 ProcessMap* process_map, |
| 95 ExtensionService* service) { |
| 96 std::set<std::string> extension_ids = |
| 97 process_map->GetExtensionsInProcess(process_host->GetID()); |
| 98 if (extension_ids.empty()) |
| 99 return PRIV_NORMAL; |
| 100 |
| 101 for (std::set<std::string>::iterator iter = extension_ids.begin(); |
| 102 iter != extension_ids.end(); ++iter) { |
| 103 const Extension* extension = service->GetExtensionById(*iter, false); |
| 104 if (extension && AppIsolationInfo::HasIsolatedStorage(extension)) |
| 105 return PRIV_ISOLATED; |
| 106 if (extension && extension->is_hosted_app()) |
| 107 return PRIV_HOSTED; |
| 108 } |
| 109 |
| 110 return PRIV_EXTENSION; |
| 111 } |
| 112 |
| 113 } // namespace |
| 114 |
43 ChromeContentBrowserClientExtensionsPart:: | 115 ChromeContentBrowserClientExtensionsPart:: |
44 ChromeContentBrowserClientExtensionsPart() { | 116 ChromeContentBrowserClientExtensionsPart() { |
| 117 permissions_policy_delegate_.reset(new BrowserPermissionsPolicyDelegate()); |
45 } | 118 } |
46 | 119 |
47 ChromeContentBrowserClientExtensionsPart:: | 120 ChromeContentBrowserClientExtensionsPart:: |
48 ~ChromeContentBrowserClientExtensionsPart() { | 121 ~ChromeContentBrowserClientExtensionsPart() { |
49 } | 122 } |
50 | 123 |
| 124 // static |
| 125 GURL ChromeContentBrowserClientExtensionsPart::GetEffectiveURL( |
| 126 Profile* profile, const GURL& url) { |
| 127 // If the input |url| is part of an installed app, the effective URL is an |
| 128 // extension URL with the ID of that extension as the host. This has the |
| 129 // effect of grouping apps together in a common SiteInstance. |
| 130 ExtensionService* extension_service = |
| 131 ExtensionSystem::Get(profile)->extension_service(); |
| 132 if (!extension_service) |
| 133 return url; |
| 134 |
| 135 const Extension* extension = |
| 136 extension_service->extensions()->GetHostedAppByURL(url); |
| 137 if (!extension) |
| 138 return url; |
| 139 |
| 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; |
| 144 |
| 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()); |
| 148 } |
| 149 |
| 150 // static |
| 151 bool ChromeContentBrowserClientExtensionsPart::ShouldUseProcessPerSite( |
| 152 Profile* profile, const GURL& effective_url) { |
| 153 if (!effective_url.SchemeIs(kExtensionScheme)) |
| 154 return false; |
| 155 |
| 156 ExtensionService* extension_service = |
| 157 ExtensionSystem::Get(profile)->extension_service(); |
| 158 if (!extension_service) |
| 159 return false; |
| 160 |
| 161 const Extension* extension = |
| 162 extension_service->extensions()->GetExtensionOrAppByURL(effective_url); |
| 163 if (!extension) |
| 164 return false; |
| 165 |
| 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; |
| 175 } |
| 176 } |
| 177 |
| 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; |
| 182 } |
| 183 |
| 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 Profile* profile = |
| 193 Profile::FromBrowserContext(process_host->GetBrowserContext()); |
| 194 ExtensionService* service = |
| 195 ExtensionSystem::Get(profile)->extension_service(); |
| 196 if (!service) |
| 197 return true; |
| 198 |
| 199 const Extension* new_extension = |
| 200 service->extensions()->GetExtensionOrAppByURL(url); |
| 201 if (new_extension && |
| 202 new_extension->is_hosted_app() && |
| 203 new_extension->id() == extension_misc::kWebStoreAppId && |
| 204 !ProcessMap::Get(profile)->Contains( |
| 205 new_extension->id(), process_host->GetID())) { |
| 206 return false; |
| 207 } |
| 208 return true; |
| 209 } |
| 210 |
| 211 // static |
| 212 bool ChromeContentBrowserClientExtensionsPart::IsSuitableHost( |
| 213 Profile* profile, |
| 214 content::RenderProcessHost* process_host, |
| 215 const GURL& site_url) { |
| 216 DCHECK(profile); |
| 217 |
| 218 ExtensionService* service = |
| 219 ExtensionSystem::Get(profile)->extension_service(); |
| 220 ProcessMap* process_map = ProcessMap::Get(profile); |
| 221 |
| 222 // These may be NULL during tests. In that case, just assume any site can |
| 223 // share any host. |
| 224 if (!service || !process_map) |
| 225 return true; |
| 226 |
| 227 // Otherwise, just make sure the process privilege matches the privilege |
| 228 // required by the site. |
| 229 RenderProcessHostPrivilege privilege_required = |
| 230 GetPrivilegeRequiredByUrl(site_url, service); |
| 231 return GetProcessPrivilege(process_host, process_map, service) == |
| 232 privilege_required; |
| 233 } |
| 234 |
| 235 // static |
| 236 bool |
| 237 ChromeContentBrowserClientExtensionsPart::ShouldTryToUseExistingProcessHost( |
| 238 Profile* profile, const GURL& url) { |
| 239 // This function is trying to limit the amount of processes used by extensions |
| 240 // with background pages. It uses a globally set percentage of processes to |
| 241 // run such extensions and if the limit is exceeded, it returns true, to |
| 242 // indicate to the content module to group extensions together. |
| 243 ExtensionService* service = profile ? |
| 244 ExtensionSystem::Get(profile)->extension_service() : NULL; |
| 245 if (!service) |
| 246 return false; |
| 247 |
| 248 // We have to have a valid extension with background page to proceed. |
| 249 const Extension* extension = |
| 250 service->extensions()->GetExtensionOrAppByURL(url); |
| 251 if (!extension) |
| 252 return false; |
| 253 if (!BackgroundInfo::HasBackgroundPage(extension)) |
| 254 return false; |
| 255 |
| 256 std::set<int> process_ids; |
| 257 size_t max_process_count = |
| 258 content::RenderProcessHost::GetMaxRendererProcessCount(); |
| 259 |
| 260 // Go through all profiles to ensure we have total count of extension |
| 261 // processes containing background pages, otherwise one profile can |
| 262 // starve the other. |
| 263 std::vector<Profile*> profiles = g_browser_process->profile_manager()-> |
| 264 GetLoadedProfiles(); |
| 265 for (size_t i = 0; i < profiles.size(); ++i) { |
| 266 ProcessManager* epm = ExtensionSystem::Get(profiles[i])->process_manager(); |
| 267 for (ProcessManager::const_iterator iter = epm->background_hosts().begin(); |
| 268 iter != epm->background_hosts().end(); ++iter) { |
| 269 const ExtensionHost* host = *iter; |
| 270 process_ids.insert(host->render_process_host()->GetID()); |
| 271 } |
| 272 } |
| 273 |
| 274 return (process_ids.size() > |
| 275 (max_process_count * chrome::kMaxShareOfExtensionProcesses)); |
| 276 } |
| 277 |
| 278 // static |
| 279 bool ChromeContentBrowserClientExtensionsPart:: |
| 280 ShouldSwapBrowsingInstancesForNavigation(SiteInstance* site_instance, |
| 281 const GURL& current_url, |
| 282 const GURL& new_url) { |
| 283 // If we don't have an ExtensionService, then rely on the SiteInstance logic |
| 284 // in RenderFrameHostManager to decide when to swap. |
| 285 Profile* profile = |
| 286 Profile::FromBrowserContext(site_instance->GetBrowserContext()); |
| 287 ExtensionService* service = |
| 288 ExtensionSystem::Get(profile)->extension_service(); |
| 289 if (!service) |
| 290 return false; |
| 291 |
| 292 // We must use a new BrowsingInstance (forcing a process swap and disabling |
| 293 // scripting by existing tabs) if one of the URLs is an extension and the |
| 294 // other is not the exact same extension. |
| 295 // |
| 296 // We ignore hosted apps here so that other tabs in their BrowsingInstance can |
| 297 // use postMessage with them. (The exception is the Chrome Web Store, which |
| 298 // is a hosted app that requires its own BrowsingInstance.) Navigations |
| 299 // to/from a hosted app will still trigger a SiteInstance swap in |
| 300 // RenderFrameHostManager. |
| 301 const Extension* current_extension = |
| 302 service->extensions()->GetExtensionOrAppByURL(current_url); |
| 303 if (current_extension && |
| 304 current_extension->is_hosted_app() && |
| 305 current_extension->id() != extension_misc::kWebStoreAppId) |
| 306 current_extension = NULL; |
| 307 |
| 308 const Extension* new_extension = |
| 309 service->extensions()->GetExtensionOrAppByURL(new_url); |
| 310 if (new_extension && |
| 311 new_extension->is_hosted_app() && |
| 312 new_extension->id() != extension_misc::kWebStoreAppId) |
| 313 new_extension = NULL; |
| 314 |
| 315 // First do a process check. We should force a BrowsingInstance swap if the |
| 316 // current process doesn't know about new_extension, even if current_extension |
| 317 // is somehow the same as new_extension. |
| 318 ProcessMap* process_map = ProcessMap::Get(profile); |
| 319 if (new_extension && |
| 320 site_instance->HasProcess() && |
| 321 !process_map->Contains( |
| 322 new_extension->id(), site_instance->GetProcess()->GetID())) |
| 323 return true; |
| 324 |
| 325 // Otherwise, swap BrowsingInstances if current_extension and new_extension |
| 326 // differ. |
| 327 return current_extension != new_extension; |
| 328 } |
| 329 |
| 330 // static |
| 331 bool ChromeContentBrowserClientExtensionsPart::ShouldSwapProcessesForRedirect( |
| 332 content::ResourceContext* resource_context, |
| 333 const GURL& current_url, |
| 334 const GURL& new_url) { |
| 335 ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context); |
| 336 return CrossesExtensionProcessBoundary( |
| 337 io_data->GetExtensionInfoMap()->extensions(), |
| 338 current_url, new_url, false); |
| 339 } |
| 340 |
| 341 // static |
| 342 std::string ChromeContentBrowserClientExtensionsPart::GetWorkerProcessTitle( |
| 343 const GURL& url, content::ResourceContext* context) { |
| 344 // Check if it's an extension-created worker, in which case we want to use |
| 345 // the name of the extension. |
| 346 ProfileIOData* io_data = ProfileIOData::FromResourceContext(context); |
| 347 const Extension* extension = |
| 348 io_data->GetExtensionInfoMap()->extensions().GetByID(url.host()); |
| 349 return extension ? extension->name() : std::string(); |
| 350 } |
| 351 |
| 352 // static |
| 353 bool ChromeContentBrowserClientExtensionsPart::ShouldAllowOpenURL( |
| 354 content::SiteInstance* site_instance, |
| 355 const GURL& from_url, |
| 356 const GURL& to_url, |
| 357 bool* result) { |
| 358 DCHECK(result); |
| 359 |
| 360 // Do not allow pages from the web or other extensions navigate to |
| 361 // non-web-accessible extension resources. |
| 362 if (to_url.SchemeIs(kExtensionScheme) && |
| 363 (from_url.SchemeIsHTTPOrHTTPS() || from_url.SchemeIs(kExtensionScheme))) { |
| 364 Profile* profile = Profile::FromBrowserContext( |
| 365 site_instance->GetProcess()->GetBrowserContext()); |
| 366 ExtensionService* service = |
| 367 ExtensionSystem::Get(profile)->extension_service(); |
| 368 if (!service) { |
| 369 *result = true; |
| 370 return true; |
| 371 } |
| 372 const Extension* extension = |
| 373 service->extensions()->GetExtensionOrAppByURL(to_url); |
| 374 if (!extension) { |
| 375 *result = true; |
| 376 return true; |
| 377 } |
| 378 const Extension* from_extension = |
| 379 service->extensions()->GetExtensionOrAppByURL( |
| 380 site_instance->GetSiteURL()); |
| 381 if (from_extension && from_extension->id() == extension->id()) { |
| 382 *result = true; |
| 383 return true; |
| 384 } |
| 385 |
| 386 if (!WebAccessibleResourcesInfo::IsResourceWebAccessible( |
| 387 extension, to_url.path())) { |
| 388 *result = false; |
| 389 return true; |
| 390 } |
| 391 } |
| 392 return false; |
| 393 } |
| 394 |
| 395 // static |
| 396 void ChromeContentBrowserClientExtensionsPart::SetSigninProcess( |
| 397 content::SiteInstance* site_instance) { |
| 398 Profile* profile = |
| 399 Profile::FromBrowserContext(site_instance->GetBrowserContext()); |
| 400 DCHECK(profile); |
| 401 BrowserThread::PostTask( |
| 402 BrowserThread::IO, |
| 403 FROM_HERE, |
| 404 base::Bind(&InfoMap::SetSigninProcess, |
| 405 ExtensionSystem::Get(profile)->info_map(), |
| 406 site_instance->GetProcess()->GetID())); |
| 407 } |
| 408 |
51 void ChromeContentBrowserClientExtensionsPart::RenderProcessWillLaunch( | 409 void ChromeContentBrowserClientExtensionsPart::RenderProcessWillLaunch( |
52 content::RenderProcessHost* host) { | 410 content::RenderProcessHost* host) { |
53 #if defined(ENABLE_EXTENSIONS) | 411 #if defined(ENABLE_EXTENSIONS) |
54 int id = host->GetID(); | 412 int id = host->GetID(); |
55 Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext()); | 413 Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext()); |
56 | 414 |
57 host->AddFilter(new ChromeExtensionMessageFilter(id, profile)); | 415 host->AddFilter(new ChromeExtensionMessageFilter(id, profile)); |
58 host->AddFilter(new ExtensionMessageFilter(id, profile)); | 416 host->AddFilter(new ExtensionMessageFilter(id, profile)); |
59 SendExtensionWebRequestStatusToHost(host); | 417 SendExtensionWebRequestStatusToHost(host); |
60 #endif | 418 #endif |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
214 content::RenderProcessHost* process, | 572 content::RenderProcessHost* process, |
215 Profile* profile) { | 573 Profile* profile) { |
216 if (!process) | 574 if (!process) |
217 return; | 575 return; |
218 DCHECK(profile); | 576 DCHECK(profile); |
219 if (ProcessMap::Get(profile)->Contains(process->GetID())) | 577 if (ProcessMap::Get(profile)->Contains(process->GetID())) |
220 command_line->AppendSwitch(switches::kExtensionProcess); | 578 command_line->AppendSwitch(switches::kExtensionProcess); |
221 } | 579 } |
222 | 580 |
223 } // namespace extensions | 581 } // namespace extensions |
OLD | NEW |