blob: 6a94bdf7dcd6f6d7695edfd926134bf9f7dd8aa3 [file] [log] [blame]
[email protected]1ce15972014-03-20 19:25:481// 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.
4
5#include "extensions/browser/extension_web_contents_observer.h"
6
7#include "content/public/browser/child_process_security_policy.h"
jam78581ca2017-01-27 19:52:428#include "content/public/browser/navigation_handle.h"
rdevlin.cronin6ae04a012015-04-03 20:19:409#include "content/public/browser/render_frame_host.h"
[email protected]1ce15972014-03-20 19:25:4810#include "content/public/browser/render_process_host.h"
11#include "content/public/browser/render_view_host.h"
12#include "content/public/browser/site_instance.h"
13#include "content/public/browser/web_contents.h"
14#include "content/public/common/url_constants.h"
rob3e2a0732016-01-06 21:22:0915#include "extensions/browser/extension_api_frame_id_map.h"
[email protected]1ce15972014-03-20 19:25:4816#include "extensions/browser/extension_prefs.h"
17#include "extensions/browser/extension_registry.h"
kmarshall166e5b42015-04-03 22:29:4318#include "extensions/browser/extensions_browser_client.h"
sammc143f3c52015-02-13 09:42:3819#include "extensions/browser/mojo/service_registration.h"
rdevlin.cronin6ae04a012015-04-03 20:19:4020#include "extensions/browser/process_manager.h"
rdevlin.cronin5e510e802016-07-26 15:09:2021#include "extensions/browser/renderer_startup_helper.h"
[email protected]1ce15972014-03-20 19:25:4822#include "extensions/browser/view_type_utils.h"
23#include "extensions/common/constants.h"
emaxxe70f5e12015-05-29 11:26:0024#include "extensions/common/extension.h"
[email protected]1ce15972014-03-20 19:25:4825#include "extensions/common/extension_messages.h"
emaxxe3baba12015-10-19 22:45:0026#include "extensions/common/view_type.h"
csharrisonaec2c542016-10-12 19:40:3627#include "url/origin.h"
[email protected]1ce15972014-03-20 19:25:4828
29namespace extensions {
30
rdevlin.cronincb2ec659a2015-06-10 23:32:4131// static
32ExtensionWebContentsObserver* ExtensionWebContentsObserver::GetForWebContents(
33 content::WebContents* web_contents) {
34 return ExtensionsBrowserClient::Get()->GetExtensionWebContentsObserver(
35 web_contents);
36}
37
[email protected]1ce15972014-03-20 19:25:4838ExtensionWebContentsObserver::ExtensionWebContentsObserver(
39 content::WebContents* web_contents)
40 : content::WebContentsObserver(web_contents),
rdevlin.cronincb2ec659a2015-06-10 23:32:4141 browser_context_(web_contents->GetBrowserContext()),
42 dispatcher_(browser_context_) {
rdevlin.cronin6f42c2522015-06-19 18:58:5143 web_contents->ForEachFrame(
44 base::Bind(&ExtensionWebContentsObserver::InitializeFrameHelper,
45 base::Unretained(this)));
rdevlin.cronincb2ec659a2015-06-10 23:32:4146 dispatcher_.set_delegate(this);
[email protected]0b365072014-03-22 06:14:1847}
[email protected]1ce15972014-03-20 19:25:4848
rdevlin.cronin6ae04a012015-04-03 20:19:4049ExtensionWebContentsObserver::~ExtensionWebContentsObserver() {
50}
[email protected]1ce15972014-03-20 19:25:4851
rdevlin.cronin6f42c2522015-06-19 18:58:5152void ExtensionWebContentsObserver::InitializeRenderFrame(
53 content::RenderFrameHost* render_frame_host) {
54 DCHECK(render_frame_host);
55 DCHECK(render_frame_host->IsRenderFrameLive());
56
robcdcc4b82015-12-06 12:39:4557 // At the initialization of the render frame, the last committed URL is not
58 // reliable, so do not take it into account in determining whether it is an
59 // extension frame.
60 const Extension* frame_extension =
61 GetExtensionFromFrame(render_frame_host, false);
62 // This observer is attached to every WebContents, so we are also notified of
63 // frames that are not in an extension process.
64 if (!frame_extension)
65 return;
66
nick2a8ba8c2016-10-03 18:51:3967 // |render_frame_host->GetProcess()| is an extension process. Grant permission
68 // to commit pages from chrome-extension:// origins.
69 content::ChildProcessSecurityPolicy* security_policy =
70 content::ChildProcessSecurityPolicy::GetInstance();
71 int process_id = render_frame_host->GetProcess()->GetID();
72 security_policy->GrantScheme(process_id, extensions::kExtensionScheme);
nick2a8ba8c2016-10-03 18:51:3973
rdevlin.cronin6f42c2522015-06-19 18:58:5174 // Notify the render frame of the view type.
75 render_frame_host->Send(new ExtensionMsg_NotifyRenderViewType(
76 render_frame_host->GetRoutingID(), GetViewType(web_contents())));
77
robcdcc4b82015-12-06 12:39:4578 ExtensionsBrowserClient::Get()->RegisterMojoServices(render_frame_host,
79 frame_extension);
80 ProcessManager::Get(browser_context_)
81 ->RegisterRenderFrameHost(web_contents(), render_frame_host,
82 frame_extension);
rdevlin.cronin6f42c2522015-06-19 18:58:5183}
84
rdevlin.cronincb2ec659a2015-06-10 23:32:4185content::WebContents* ExtensionWebContentsObserver::GetAssociatedWebContents()
86 const {
87 return web_contents();
88}
89
[email protected]1ce15972014-03-20 19:25:4890void ExtensionWebContentsObserver::RenderViewCreated(
91 content::RenderViewHost* render_view_host) {
rdevlin.cronin6f42c2522015-06-19 18:58:5192 // TODO(devlin): Most/all of this should move to RenderFrameCreated.
[email protected]1ce15972014-03-20 19:25:4893 const Extension* extension = GetExtension(render_view_host);
94 if (!extension)
95 return;
96
[email protected]1ce15972014-03-20 19:25:4897 Manifest::Type type = extension->GetType();
[email protected]1ce15972014-03-20 19:25:4898
99 // Some extensions use file:// URLs.
100 if (type == Manifest::TYPE_EXTENSION ||
101 type == Manifest::TYPE_LEGACY_PACKAGED_APP) {
102 ExtensionPrefs* prefs = ExtensionPrefs::Get(browser_context_);
103 if (prefs->AllowFileAccess(extension->id())) {
104 content::ChildProcessSecurityPolicy::GetInstance()->GrantScheme(
paulmeyer1eefa26e2015-10-01 02:11:13105 render_view_host->GetProcess()->GetID(), url::kFileScheme);
[email protected]1ce15972014-03-20 19:25:48106 }
107 }
108
kalman8bcbc7592015-06-03 23:12:27109 // Tells the new view that it's hosted in an extension process.
110 //
111 // This will often be a rendant IPC, because activating extensions happens at
112 // the process level, not at the view level. However, without some mild
113 // refactoring this isn't trivial to do, and this way is simpler.
114 //
115 // Plus, we can delete the concept of activating an extension once site
116 // isolation is turned on.
rdevlin.cronin5e510e802016-07-26 15:09:20117 RendererStartupHelperFactory::GetForBrowserContext(browser_context_)
rdevlin.croninc40d39f2016-08-04 23:42:13118 ->ActivateExtensionInProcess(*extension, render_view_host->GetProcess());
[email protected]1ce15972014-03-20 19:25:48119}
120
sammc143f3c52015-02-13 09:42:38121void ExtensionWebContentsObserver::RenderFrameCreated(
122 content::RenderFrameHost* render_frame_host) {
rdevlin.cronin6f42c2522015-06-19 18:58:51123 InitializeRenderFrame(render_frame_host);
rob3e2a0732016-01-06 21:22:09124
125 // Optimization: Look up the extension API frame ID to force the mapping to be
126 // cached. This minimizes the number of IO->UI->IO thread hops when the ID is
127 // looked up again on the IO thread for the webRequest API.
rdevlin.cronin9a62870f2016-02-11 23:25:58128 ExtensionApiFrameIdMap::Get()->CacheFrameData(render_frame_host);
rdevlin.cronin6f42c2522015-06-19 18:58:51129}
130
131void ExtensionWebContentsObserver::RenderFrameDeleted(
132 content::RenderFrameHost* render_frame_host) {
133 ProcessManager::Get(browser_context_)
134 ->UnregisterRenderFrameHost(render_frame_host);
rdevlin.cronin9a62870f2016-02-11 23:25:58135 ExtensionApiFrameIdMap::Get()->RemoveFrameData(render_frame_host);
sammc143f3c52015-02-13 09:42:38136}
137
jam78581ca2017-01-27 19:52:42138void ExtensionWebContentsObserver::DidFinishNavigation(
139 content::NavigationHandle* navigation_handle) {
140 if (!navigation_handle->HasCommitted())
141 return;
142
robcdcc4b82015-12-06 12:39:45143 ProcessManager* pm = ProcessManager::Get(browser_context_);
144
jam78581ca2017-01-27 19:52:42145 content::RenderFrameHost* render_frame_host =
146 navigation_handle->GetRenderFrameHost();
robcdcc4b82015-12-06 12:39:45147 const Extension* frame_extension =
148 GetExtensionFromFrame(render_frame_host, true);
robcdcc4b82015-12-06 12:39:45149 if (pm->IsRenderFrameHostRegistered(render_frame_host)) {
Nasko Oskov7cdb0ae2017-06-06 15:17:41150 if (!frame_extension)
jam6987a2d2017-02-06 19:10:43151 pm->UnregisterRenderFrameHost(render_frame_host);
jam6987a2d2017-02-06 19:10:43152 } else if (frame_extension) {
robcdcc4b82015-12-06 12:39:45153 pm->RegisterRenderFrameHost(web_contents(), render_frame_host,
154 frame_extension);
155 }
156}
157
rdevlin.cronincb2ec659a2015-06-10 23:32:41158bool ExtensionWebContentsObserver::OnMessageReceived(
rdevlin.cronin92503ba2015-06-12 17:00:56159 const IPC::Message& message,
160 content::RenderFrameHost* render_frame_host) {
rdevlin.cronincb2ec659a2015-06-10 23:32:41161 bool handled = true;
rdevlin.cronin92503ba2015-06-12 17:00:56162 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(
163 ExtensionWebContentsObserver, message, render_frame_host)
rdevlin.cronincb2ec659a2015-06-10 23:32:41164 IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest)
165 IPC_MESSAGE_UNHANDLED(handled = false)
166 IPC_END_MESSAGE_MAP()
167 return handled;
168}
169
emaxxe70f5e12015-05-29 11:26:00170void ExtensionWebContentsObserver::PepperInstanceCreated() {
emaxxe3baba12015-10-19 22:45:00171 if (GetViewType(web_contents()) == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
172 ProcessManager* const process_manager =
173 ProcessManager::Get(browser_context_);
174 const Extension* const extension =
175 process_manager->GetExtensionForWebContents(web_contents());
176 if (extension)
177 process_manager->IncrementLazyKeepaliveCount(extension);
178 }
emaxxe70f5e12015-05-29 11:26:00179}
180
181void ExtensionWebContentsObserver::PepperInstanceDeleted() {
emaxxe3baba12015-10-19 22:45:00182 if (GetViewType(web_contents()) == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
183 ProcessManager* const process_manager =
184 ProcessManager::Get(browser_context_);
185 const Extension* const extension =
186 process_manager->GetExtensionForWebContents(web_contents());
187 if (extension)
188 process_manager->DecrementLazyKeepaliveCount(extension);
189 }
emaxxe70f5e12015-05-29 11:26:00190}
191
rdevlin.cronin86f5b702015-06-24 18:49:17192std::string ExtensionWebContentsObserver::GetExtensionIdFromFrame(
193 content::RenderFrameHost* render_frame_host) const {
rob90e0bcd72015-12-08 09:29:42194 // The second argument is false because |render_frame_host| need not be an
195 // active RenderFrameHost (crbug.com/567277).
196 // TODO(robwu): If there is a method to check whether |render_frame_host| is
197 // an active host, use it.
198 const Extension* extension = GetExtensionFromFrame(render_frame_host, false);
robcdcc4b82015-12-06 12:39:45199 return extension ? extension->id() : std::string();
rdevlin.cronin86f5b702015-06-24 18:49:17200}
201
202const Extension* ExtensionWebContentsObserver::GetExtensionFromFrame(
robcdcc4b82015-12-06 12:39:45203 content::RenderFrameHost* render_frame_host,
204 bool verify_url) const {
205 const GURL site_url(render_frame_host->GetSiteInstance()->GetSiteURL());
206 if (!site_url.SchemeIs(kExtensionScheme))
207 return nullptr;
208
209 const std::string& extension_id = site_url.host();
210 content::BrowserContext* browser_context =
211 render_frame_host->GetProcess()->GetBrowserContext();
212 const Extension* extension = ExtensionRegistry::Get(browser_context)
213 ->enabled_extensions()
214 .GetByID(extension_id);
215 if (!extension)
216 return nullptr;
217
218 if (verify_url) {
219 const url::Origin& origin(render_frame_host->GetLastCommittedOrigin());
220 // Without site isolation, this check is needed to eliminate non-extension
221 // schemes. With site isolation, this is still needed to exclude sandboxed
222 // extension frames with a unique origin.
223 if (origin.unique() ||
csharrisonaec2c542016-10-12 19:40:36224 site_url != content::SiteInstance::GetSiteForURL(browser_context,
225 origin.GetURL()))
robcdcc4b82015-12-06 12:39:45226 return nullptr;
227 }
228
229 return extension;
rdevlin.cronin86f5b702015-06-24 18:49:17230}
231
[email protected]1ce15972014-03-20 19:25:48232const Extension* ExtensionWebContentsObserver::GetExtension(
233 content::RenderViewHost* render_view_host) {
234 std::string extension_id = GetExtensionId(render_view_host);
235 if (extension_id.empty())
236 return NULL;
237
238 // May be null if the extension doesn't exist, for example if somebody typos
239 // a chrome-extension:// URL.
240 return ExtensionRegistry::Get(browser_context_)
241 ->GetExtensionById(extension_id, ExtensionRegistry::ENABLED);
242}
243
244// static
245std::string ExtensionWebContentsObserver::GetExtensionId(
246 content::RenderViewHost* render_view_host) {
247 // Note that due to ChromeContentBrowserClient::GetEffectiveURL(), hosted apps
248 // (excluding bookmark apps) will have a chrome-extension:// URL for their
249 // site, so we can ignore that wrinkle here.
250 const GURL& site = render_view_host->GetSiteInstance()->GetSiteURL();
251
252 if (!site.SchemeIs(kExtensionScheme))
253 return std::string();
254
255 return site.host();
256}
257
rdevlin.cronincb2ec659a2015-06-10 23:32:41258void ExtensionWebContentsObserver::OnRequest(
rdevlin.cronin92503ba2015-06-12 17:00:56259 content::RenderFrameHost* render_frame_host,
rdevlin.cronincb2ec659a2015-06-10 23:32:41260 const ExtensionHostMsg_Request_Params& params) {
lazyboyee4adef2016-05-24 00:55:16261 dispatcher_.Dispatch(params, render_frame_host,
262 render_frame_host->GetProcess()->GetID());
rdevlin.cronincb2ec659a2015-06-10 23:32:41263}
264
rdevlin.cronin6f42c2522015-06-19 18:58:51265void ExtensionWebContentsObserver::InitializeFrameHelper(
266 content::RenderFrameHost* render_frame_host) {
267 // Since this is called for all existing RenderFrameHosts during the
268 // ExtensionWebContentsObserver's creation, it's possible that not all hosts
269 // are ready.
270 // We only initialize the frame if the renderer counterpart is live; otherwise
271 // we wait for the RenderFrameCreated notification.
272 if (render_frame_host->IsRenderFrameLive())
273 InitializeRenderFrame(render_frame_host);
274}
275
[email protected]1ce15972014-03-20 19:25:48276} // namespace extensions