blob: 5ac461054aa11c76e6b73650fb6b000b4fadca91 [file] [log] [blame]
[email protected]bcd9580f2014-04-17 19:17:591// Copyright 2014 The Chromium Authors. All rights reserved.
[email protected]2ee1e3a2011-10-04 15:04:042// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]bcd9580f2014-04-17 19:17:595#include "extensions/renderer/script_context_set.h"
[email protected]2ee1e3a2011-10-04 15:04:046
[email protected]68275102013-07-17 23:54:207#include "base/message_loop/message_loop.h"
kalman745b6622015-03-17 21:07:208#include "content/public/common/url_constants.h"
kozyatinskiy441ece52015-04-01 23:46:329#include "content/public/renderer/render_frame.h"
10#include "content/public/renderer/render_frame_observer.h"
[email protected]a2ef54c2011-10-10 16:20:3111#include "content/public/renderer/render_view.h"
[email protected]e4452d32013-11-15 23:07:4112#include "extensions/common/extension.h"
kalman745b6622015-03-17 21:07:2013#include "extensions/renderer/extension_groups.h"
[email protected]bcd9580f2014-04-17 19:17:5914#include "extensions/renderer/script_context.h"
kalman745b6622015-03-17 21:07:2015#include "extensions/renderer/script_injection.h"
kozyatinskiy441ece52015-04-01 23:46:3216#include "third_party/WebKit/public/platform/WebSuspendableTask.h"
kalman745b6622015-03-17 21:07:2017#include "third_party/WebKit/public/web/WebDocument.h"
18#include "third_party/WebKit/public/web/WebLocalFrame.h"
[email protected]885c0e92012-11-13 20:27:4219#include "v8/include/v8.h"
[email protected]2ee1e3a2011-10-04 15:04:0420
[email protected]8fe74bf2012-08-07 21:08:4221namespace extensions {
22
kozyatinskiy441ece52015-04-01 23:46:3223namespace {
24
25// This class inherits content::RenderFrameObserver for tracking when render
26// frame goes away.
27class BlinkTaskRunner : public blink::WebSuspendableTask,
28 public content::RenderFrameObserver {
29 public:
30 BlinkTaskRunner(content::RenderFrame* render_frame,
31 ScriptContext* context,
32 const base::Callback<void(ScriptContext*)>& callback)
33 : RenderFrameObserver(render_frame),
34 context_(context),
35 callback_(callback) {}
36
37 void run() override {
38 if (render_frame() && context_->is_valid())
39 callback_.Run(context_);
40 }
41
42 private:
43 ScriptContext* context_;
44 base::Callback<void(ScriptContext*)> callback_;
45
46 // Overriden to avoid being destroyed when RenderFrame goes away.
47 // BlinkTaskRunner objects are owned by WebLocalFrame.
48 void OnDestruct() override {}
49
50 DISALLOW_COPY_AND_ASSIGN(BlinkTaskRunner);
51};
52
kozyatinskiya08a3102015-04-03 17:50:0353// This class deletes ScriptContext on run or on contextDestoyed.
54// It allows us to pass ScriptContext as raw pointer to BlinkTaskRunner.
55// In ScriptContextSet::Remove method we mark the context as invalid, but
56// don't delete the object until all scheduled tasks are finished.
57class ScriptContextDeleter : public blink::WebSuspendableTask {
58 public:
59 explicit ScriptContextDeleter(ScriptContext* context) : context_(context) {}
60
61 void run() override { delete context_; }
62 void contextDestroyed() override { delete context_; }
63
64 private:
65 ScriptContext* context_;
66
67 DISALLOW_COPY_AND_ASSIGN(ScriptContextDeleter);
68};
69
kozyatinskiy441ece52015-04-01 23:46:3270} // namespace
71
kalman745b6622015-03-17 21:07:2072ScriptContextSet::ScriptContextSet(ExtensionSet* extensions,
73 ExtensionIdSet* active_extension_ids)
74 : extensions_(extensions), active_extension_ids_(active_extension_ids) {
[email protected]2ee1e3a2011-10-04 15:04:0475}
kalman745b6622015-03-17 21:07:2076
[email protected]bcd9580f2014-04-17 19:17:5977ScriptContextSet::~ScriptContextSet() {
[email protected]2ee1e3a2011-10-04 15:04:0478}
79
kalman745b6622015-03-17 21:07:2080ScriptContext* ScriptContextSet::Register(
81 blink::WebLocalFrame* frame,
82 const v8::Handle<v8::Context>& v8_context,
83 int extension_group,
84 int world_id) {
85 const Extension* extension =
86 GetExtensionFromFrameAndWorld(frame, world_id, false);
87 const Extension* effective_extension =
88 GetExtensionFromFrameAndWorld(frame, world_id, true);
[email protected]2ee1e3a2011-10-04 15:04:0489
kalman745b6622015-03-17 21:07:2090 GURL frame_url = ScriptContext::GetDataSourceURLForFrame(frame);
91 Feature::Context context_type =
92 ClassifyJavaScriptContext(extension, extension_group, frame_url,
93 frame->document().securityOrigin());
94 Feature::Context effective_context_type = ClassifyJavaScriptContext(
95 effective_extension, extension_group,
96 ScriptContext::GetEffectiveDocumentURL(frame, frame_url, true),
97 frame->document().securityOrigin());
98
99 ScriptContext* context =
100 new ScriptContext(v8_context, frame, extension, context_type,
101 effective_extension, effective_context_type);
102 contexts_.insert(context); // takes ownership
103 return context;
[email protected]2ee1e3a2011-10-04 15:04:04104}
105
[email protected]bcd9580f2014-04-17 19:17:59106void ScriptContextSet::Remove(ScriptContext* context) {
[email protected]2ee1e3a2011-10-04 15:04:04107 if (contexts_.erase(context)) {
kozyatinskiya08a3102015-04-03 17:50:03108 content::RenderFrame* context_render_frame = context->GetRenderFrame();
109 blink::WebLocalFrame* web_local_frame =
110 context_render_frame ? context_render_frame->GetWebFrame() : nullptr;
111
[email protected]4f1633f2013-03-09 14:26:24112 context->Invalidate();
kozyatinskiya08a3102015-04-03 17:50:03113
114 if (!web_local_frame)
115 base::MessageLoop::current()->DeleteSoon(FROM_HERE, context);
116 else
117 web_local_frame->requestRunTask(new ScriptContextDeleter(context));
[email protected]2ee1e3a2011-10-04 15:04:04118 }
119}
120
[email protected]bcd9580f2014-04-17 19:17:59121ScriptContext* ScriptContextSet::GetCurrent() const {
[email protected]95c6b3012013-12-02 14:30:31122 v8::Isolate* isolate = v8::Isolate::GetCurrent();
123 return isolate->InContext() ? GetByV8Context(isolate->GetCurrentContext())
kalman745b6622015-03-17 21:07:20124 : nullptr;
[email protected]ad6aa8f92013-06-22 15:34:16125}
126
[email protected]bcd9580f2014-04-17 19:17:59127ScriptContext* ScriptContextSet::GetCalling() const {
[email protected]95c6b3012013-12-02 14:30:31128 v8::Isolate* isolate = v8::Isolate::GetCurrent();
129 v8::Local<v8::Context> calling = isolate->GetCallingContext();
kalman745b6622015-03-17 21:07:20130 return calling.IsEmpty() ? nullptr : GetByV8Context(calling);
[email protected]2ee1e3a2011-10-04 15:04:04131}
132
[email protected]bcd9580f2014-04-17 19:17:59133ScriptContext* ScriptContextSet::GetByV8Context(
kalman745b6622015-03-17 21:07:20134 const v8::Handle<v8::Context>& v8_context) const {
135 for (ScriptContext* script_context : contexts_) {
136 if (script_context->v8_context() == v8_context)
137 return script_context;
[email protected]2ee1e3a2011-10-04 15:04:04138 }
kalman745b6622015-03-17 21:07:20139 return nullptr;
[email protected]2ee1e3a2011-10-04 15:04:04140}
141
[email protected]bcd9580f2014-04-17 19:17:59142void ScriptContextSet::ForEach(
[email protected]2ee1e3a2011-10-04 15:04:04143 const std::string& extension_id,
kozyatinskiy441ece52015-04-01 23:46:32144 content::RenderView* restrict_to_render_view,
[email protected]bcd9580f2014-04-17 19:17:59145 const base::Callback<void(ScriptContext*)>& callback) const {
kozyatinskiy441ece52015-04-01 23:46:32146 ForEachImpl(extension_id, restrict_to_render_view, callback, false);
147}
[email protected]2ee1e3a2011-10-04 15:04:04148
kozyatinskiy441ece52015-04-01 23:46:32149void ScriptContextSet::RequestRunForEach(
150 const std::string& extension_id,
151 content::RenderFrame* restrict_to_render_frame,
152 const base::Callback<void(ScriptContext*)>& callback) const {
153 content::RenderView* restrict_to_render_view =
154 restrict_to_render_frame ? restrict_to_render_frame->GetRenderView()
155 : NULL;
156 ForEachImpl(extension_id, restrict_to_render_view, callback, true);
[email protected]2ee1e3a2011-10-04 15:04:04157}
[email protected]8fe74bf2012-08-07 21:08:42158
kalman745b6622015-03-17 21:07:20159std::set<ScriptContext*> ScriptContextSet::OnExtensionUnloaded(
[email protected]280055f2013-04-23 00:50:47160 const std::string& extension_id) {
kalman745b6622015-03-17 21:07:20161 std::set<ScriptContext*> removed;
162 ForEach(extension_id,
163 base::Bind(&ScriptContextSet::DispatchOnUnloadEventAndRemove,
164 base::Unretained(this), &removed));
165 return removed;
166}
kalman77b04112015-03-16 21:20:44167
kalman745b6622015-03-17 21:07:20168const Extension* ScriptContextSet::GetExtensionFromFrameAndWorld(
169 const blink::WebLocalFrame* frame,
170 int world_id,
171 bool use_effective_url) {
172 std::string extension_id;
173 if (world_id != 0) {
174 // Isolated worlds (content script).
175 extension_id = ScriptInjection::GetHostIdForIsolatedWorld(world_id);
176 } else if (!frame->document().securityOrigin().isUnique()) {
177 // TODO(kalman): Delete the above check.
178 // Extension pages (chrome-extension:// URLs).
179 GURL frame_url = ScriptContext::GetDataSourceURLForFrame(frame);
180 frame_url = ScriptContext::GetEffectiveDocumentURL(frame, frame_url,
181 use_effective_url);
182 extension_id = extensions_->GetExtensionOrAppIDByURL(frame_url);
kalman77b04112015-03-16 21:20:44183 }
184
kalman745b6622015-03-17 21:07:20185 // There are conditions where despite a context being associated with an
186 // extension, no extension actually gets found. Ignore "invalid" because CSP
187 // blocks extension page loading by switching the extension ID to "invalid".
188 const Extension* extension = extensions_->GetByID(extension_id);
189 if (!extension && !extension_id.empty() && extension_id != "invalid") {
190 // TODO(kalman): Do something here?
191 }
192 return extension;
193}
194
195Feature::Context ScriptContextSet::ClassifyJavaScriptContext(
196 const Extension* extension,
197 int extension_group,
198 const GURL& url,
199 const blink::WebSecurityOrigin& origin) {
200 // WARNING: This logic must match ProcessMap::GetContextType, as much as
201 // possible.
202
203 DCHECK_GE(extension_group, 0);
204 if (extension_group == EXTENSION_GROUP_CONTENT_SCRIPTS) {
205 return extension ? // TODO(kalman): when does this happen?
206 Feature::CONTENT_SCRIPT_CONTEXT
207 : Feature::UNSPECIFIED_CONTEXT;
208 }
209
210 // We have an explicit check for sandboxed pages before checking whether the
211 // extension is active in this process because:
212 // 1. Sandboxed pages run in the same process as regular extension pages, so
213 // the extension is considered active.
214 // 2. ScriptContext creation (which triggers bindings injection) happens
215 // before the SecurityContext is updated with the sandbox flags (after
216 // reading the CSP header), so the caller can't check if the context's
217 // security origin is unique yet.
218 if (ScriptContext::IsSandboxedPage(*extensions_, url))
219 return Feature::WEB_PAGE_CONTEXT;
220
221 if (extension && active_extension_ids_->count(extension->id()) > 0) {
222 // |extension| is active in this process, but it could be either a true
223 // extension process or within the extent of a hosted app. In the latter
224 // case this would usually be considered a (blessed) web page context,
225 // unless the extension in question is a component extension, in which case
226 // we cheat and call it blessed.
227 return (extension->is_hosted_app() &&
228 extension->location() != Manifest::COMPONENT)
229 ? Feature::BLESSED_WEB_PAGE_CONTEXT
230 : Feature::BLESSED_EXTENSION_CONTEXT;
231 }
232
233 // TODO(kalman): This isUnique() check is wrong, it should be performed as
234 // part of ScriptContext::IsSandboxedPage().
235 if (!origin.isUnique() && extensions_->ExtensionBindingsAllowed(url)) {
236 if (!extension) // TODO(kalman): when does this happen?
237 return Feature::UNSPECIFIED_CONTEXT;
238 return extension->is_hosted_app() ? Feature::BLESSED_WEB_PAGE_CONTEXT
239 : Feature::UNBLESSED_EXTENSION_CONTEXT;
240 }
241
242 if (!url.is_valid())
243 return Feature::UNSPECIFIED_CONTEXT;
244
245 if (url.SchemeIs(content::kChromeUIScheme))
246 return Feature::WEBUI_CONTEXT;
247
248 return Feature::WEB_PAGE_CONTEXT;
249}
250
251void ScriptContextSet::DispatchOnUnloadEventAndRemove(
252 std::set<ScriptContext*>* out,
253 ScriptContext* context) {
kalman0eda9712015-03-30 23:25:55254 context->DispatchOnUnloadEvent();
kalman745b6622015-03-17 21:07:20255 Remove(context); // deleted asynchronously
256 out->insert(context);
kalman52f4caf2015-03-16 19:16:26257}
kalman8ee8ee92015-03-14 00:52:26258
kozyatinskiy441ece52015-04-01 23:46:32259void ScriptContextSet::ForEachImpl(
260 const std::string& extension_id,
261 content::RenderView* restrict_to_render_view,
262 const base::Callback<void(ScriptContext*)>& callback,
263 bool run_asynchronously) const {
264 // We copy the context list, because calling into javascript may modify it
265 // out from under us.
266 std::set<ScriptContext*> contexts_copy = contexts_;
267 for (ScriptContext* context : contexts_copy) {
268 // For the same reason as above, contexts may become invalid while we run.
269 if (!context->is_valid())
270 continue;
271
272 if (!extension_id.empty()) {
273 const Extension* extension = context->extension();
274 if (!extension || (extension_id != extension->id()))
275 continue;
276 }
277
278 content::RenderView* context_render_view = context->GetRenderView();
279 if (!context_render_view)
280 continue;
281
282 if (restrict_to_render_view &&
283 restrict_to_render_view != context_render_view)
284 continue;
285
286 content::RenderFrame* context_render_frame = context->GetRenderFrame();
287 if (!context_render_frame)
288 continue;
289
290 if (run_asynchronously) {
291 blink::WebLocalFrame* web_local_frame =
292 context_render_frame->GetWebFrame();
293 if (!web_local_frame)
294 continue;
295 // WebLocalFrame takes BlinkTaskRunner ownership.
296 web_local_frame->requestRunTask(
297 new BlinkTaskRunner(context_render_frame, context, callback));
298 } else {
299 callback.Run(context);
300 }
301 }
302}
303
[email protected]8fe74bf2012-08-07 21:08:42304} // namespace extensions