blob: a9877dd9ea5ed83cd60082bfc2a402472494f78d [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"
kalmanf91cb892015-04-15 19:20:488#include "content/public/common/url_constants.h"
rdevlin.croninc5d9a0ea2015-06-23 21:29:109#include "content/public/renderer/render_frame.h"
[email protected]e4452d32013-11-15 23:07:4110#include "extensions/common/extension.h"
kalmanf91cb892015-04-15 19:20:4811#include "extensions/renderer/extension_groups.h"
[email protected]bcd9580f2014-04-17 19:17:5912#include "extensions/renderer/script_context.h"
kalmanf91cb892015-04-15 19:20:4813#include "extensions/renderer/script_injection.h"
14#include "third_party/WebKit/public/web/WebDocument.h"
15#include "third_party/WebKit/public/web/WebLocalFrame.h"
[email protected]885c0e92012-11-13 20:27:4216#include "v8/include/v8.h"
[email protected]2ee1e3a2011-10-04 15:04:0417
[email protected]8fe74bf2012-08-07 21:08:4218namespace extensions {
19
rdevlin.croninb2cec912015-06-24 20:36:0120namespace {
21// There is only ever one instance of the ScriptContextSet.
22ScriptContextSet* g_context_set = nullptr;
23}
24
annekao6572d5c2015-08-19 16:13:3625ScriptContextSet::ScriptContextSet(ExtensionIdSet* active_extension_ids)
26 : active_extension_ids_(active_extension_ids) {
rdevlin.croninb2cec912015-06-24 20:36:0127 DCHECK(!g_context_set);
28 g_context_set = this;
[email protected]2ee1e3a2011-10-04 15:04:0429}
kalmanf91cb892015-04-15 19:20:4830
[email protected]bcd9580f2014-04-17 19:17:5931ScriptContextSet::~ScriptContextSet() {
rdevlin.croninb2cec912015-06-24 20:36:0132 g_context_set = nullptr;
[email protected]2ee1e3a2011-10-04 15:04:0433}
34
kalmanf91cb892015-04-15 19:20:4835ScriptContext* ScriptContextSet::Register(
36 blink::WebLocalFrame* frame,
tfarinaf85316f2015-04-29 17:03:4037 const v8::Local<v8::Context>& v8_context,
kalmanf91cb892015-04-15 19:20:4838 int extension_group,
39 int world_id) {
40 const Extension* extension =
41 GetExtensionFromFrameAndWorld(frame, world_id, false);
42 const Extension* effective_extension =
43 GetExtensionFromFrameAndWorld(frame, world_id, true);
[email protected]2ee1e3a2011-10-04 15:04:0444
kalmanf91cb892015-04-15 19:20:4845 GURL frame_url = ScriptContext::GetDataSourceURLForFrame(frame);
46 Feature::Context context_type =
47 ClassifyJavaScriptContext(extension, extension_group, frame_url,
48 frame->document().securityOrigin());
49 Feature::Context effective_context_type = ClassifyJavaScriptContext(
50 effective_extension, extension_group,
51 ScriptContext::GetEffectiveDocumentURL(frame, frame_url, true),
52 frame->document().securityOrigin());
53
54 ScriptContext* context =
55 new ScriptContext(v8_context, frame, extension, context_type,
56 effective_extension, effective_context_type);
57 contexts_.insert(context); // takes ownership
58 return context;
[email protected]2ee1e3a2011-10-04 15:04:0459}
60
[email protected]bcd9580f2014-04-17 19:17:5961void ScriptContextSet::Remove(ScriptContext* context) {
[email protected]2ee1e3a2011-10-04 15:04:0462 if (contexts_.erase(context)) {
[email protected]4f1633f2013-03-09 14:26:2463 context->Invalidate();
kalman62b5911d2015-04-04 02:33:0964 base::MessageLoop::current()->DeleteSoon(FROM_HERE, context);
[email protected]2ee1e3a2011-10-04 15:04:0465 }
66}
67
[email protected]bcd9580f2014-04-17 19:17:5968ScriptContext* ScriptContextSet::GetCurrent() const {
[email protected]95c6b3012013-12-02 14:30:3169 v8::Isolate* isolate = v8::Isolate::GetCurrent();
70 return isolate->InContext() ? GetByV8Context(isolate->GetCurrentContext())
kalmanf91cb892015-04-15 19:20:4871 : nullptr;
[email protected]ad6aa8f92013-06-22 15:34:1672}
73
[email protected]bcd9580f2014-04-17 19:17:5974ScriptContext* ScriptContextSet::GetCalling() const {
[email protected]95c6b3012013-12-02 14:30:3175 v8::Isolate* isolate = v8::Isolate::GetCurrent();
76 v8::Local<v8::Context> calling = isolate->GetCallingContext();
kalmanf91cb892015-04-15 19:20:4877 return calling.IsEmpty() ? nullptr : GetByV8Context(calling);
[email protected]2ee1e3a2011-10-04 15:04:0478}
79
[email protected]bcd9580f2014-04-17 19:17:5980ScriptContext* ScriptContextSet::GetByV8Context(
tfarinaf85316f2015-04-29 17:03:4081 const v8::Local<v8::Context>& v8_context) const {
kalmanf91cb892015-04-15 19:20:4882 for (ScriptContext* script_context : contexts_) {
83 if (script_context->v8_context() == v8_context)
84 return script_context;
[email protected]2ee1e3a2011-10-04 15:04:0485 }
kalmanf91cb892015-04-15 19:20:4886 return nullptr;
[email protected]2ee1e3a2011-10-04 15:04:0487}
88
rdevlin.croninb2cec912015-06-24 20:36:0189ScriptContext* ScriptContextSet::GetContextByV8Context(
90 const v8::Local<v8::Context>& v8_context) {
91 // g_context_set can be null in unittests.
92 return g_context_set ? g_context_set->GetByV8Context(v8_context) : nullptr;
93}
94
[email protected]bcd9580f2014-04-17 19:17:5995void ScriptContextSet::ForEach(
[email protected]2ee1e3a2011-10-04 15:04:0496 const std::string& extension_id,
rdevlin.croninc5d9a0ea2015-06-23 21:29:1097 content::RenderFrame* render_frame,
[email protected]bcd9580f2014-04-17 19:17:5998 const base::Callback<void(ScriptContext*)>& callback) const {
kozyatinskiy441ece52015-04-01 23:46:3299 // We copy the context list, because calling into javascript may modify it
100 // out from under us.
kalmanf91cb892015-04-15 19:20:48101 std::set<ScriptContext*> contexts_copy = contexts_;
kalman62b5911d2015-04-04 02:33:09102
kalmanf91cb892015-04-15 19:20:48103 for (ScriptContext* context : contexts_copy) {
kozyatinskiy441ece52015-04-01 23:46:32104 // For the same reason as above, contexts may become invalid while we run.
105 if (!context->is_valid())
106 continue;
107
108 if (!extension_id.empty()) {
109 const Extension* extension = context->extension();
110 if (!extension || (extension_id != extension->id()))
111 continue;
112 }
113
rdevlin.croninc5d9a0ea2015-06-23 21:29:10114 content::RenderFrame* context_render_frame = context->GetRenderFrame();
115 if (!context_render_frame)
kozyatinskiy441ece52015-04-01 23:46:32116 continue;
117
rdevlin.croninc5d9a0ea2015-06-23 21:29:10118 if (render_frame && render_frame != context_render_frame)
kozyatinskiy441ece52015-04-01 23:46:32119 continue;
120
kalman62b5911d2015-04-04 02:33:09121 callback.Run(context);
122 }
123}
kozyatinskiy441ece52015-04-01 23:46:32124
kalmanf91cb892015-04-15 19:20:48125std::set<ScriptContext*> ScriptContextSet::OnExtensionUnloaded(
kalman62b5911d2015-04-04 02:33:09126 const std::string& extension_id) {
kalmanf91cb892015-04-15 19:20:48127 std::set<ScriptContext*> removed;
128 ForEach(extension_id,
129 base::Bind(&ScriptContextSet::DispatchOnUnloadEventAndRemove,
130 base::Unretained(this), &removed));
131 return removed;
132}
kalman62b5911d2015-04-04 02:33:09133
kalmanf91cb892015-04-15 19:20:48134const Extension* ScriptContextSet::GetExtensionFromFrameAndWorld(
135 const blink::WebLocalFrame* frame,
136 int world_id,
137 bool use_effective_url) {
138 std::string extension_id;
139 if (world_id != 0) {
140 // Isolated worlds (content script).
141 extension_id = ScriptInjection::GetHostIdForIsolatedWorld(world_id);
142 } else if (!frame->document().securityOrigin().isUnique()) {
143 // TODO(kalman): Delete the above check.
144 // Extension pages (chrome-extension:// URLs).
145 GURL frame_url = ScriptContext::GetDataSourceURLForFrame(frame);
146 frame_url = ScriptContext::GetEffectiveDocumentURL(frame, frame_url,
147 use_effective_url);
annekao6572d5c2015-08-19 16:13:36148 extension_id =
149 RendererExtensionRegistry::Get()->GetExtensionOrAppIDByURL(frame_url);
kozyatinskiy441ece52015-04-01 23:46:32150 }
kalman62b5911d2015-04-04 02:33:09151
kalmanf91cb892015-04-15 19:20:48152 // There are conditions where despite a context being associated with an
153 // extension, no extension actually gets found. Ignore "invalid" because CSP
154 // blocks extension page loading by switching the extension ID to "invalid".
annekao6572d5c2015-08-19 16:13:36155 const Extension* extension =
156 RendererExtensionRegistry::Get()->GetByID(extension_id);
kalmanf91cb892015-04-15 19:20:48157 if (!extension && !extension_id.empty() && extension_id != "invalid") {
158 // TODO(kalman): Do something here?
159 }
160 return extension;
161}
162
163Feature::Context ScriptContextSet::ClassifyJavaScriptContext(
164 const Extension* extension,
165 int extension_group,
166 const GURL& url,
167 const blink::WebSecurityOrigin& origin) {
168 // WARNING: This logic must match ProcessMap::GetContextType, as much as
169 // possible.
170
171 DCHECK_GE(extension_group, 0);
172 if (extension_group == EXTENSION_GROUP_CONTENT_SCRIPTS) {
173 return extension ? // TODO(kalman): when does this happen?
174 Feature::CONTENT_SCRIPT_CONTEXT
175 : Feature::UNSPECIFIED_CONTEXT;
176 }
177
178 // We have an explicit check for sandboxed pages before checking whether the
179 // extension is active in this process because:
180 // 1. Sandboxed pages run in the same process as regular extension pages, so
181 // the extension is considered active.
182 // 2. ScriptContext creation (which triggers bindings injection) happens
183 // before the SecurityContext is updated with the sandbox flags (after
184 // reading the CSP header), so the caller can't check if the context's
185 // security origin is unique yet.
annekao6572d5c2015-08-19 16:13:36186 if (ScriptContext::IsSandboxedPage(url))
kalmanf91cb892015-04-15 19:20:48187 return Feature::WEB_PAGE_CONTEXT;
188
189 if (extension && active_extension_ids_->count(extension->id()) > 0) {
190 // |extension| is active in this process, but it could be either a true
191 // extension process or within the extent of a hosted app. In the latter
192 // case this would usually be considered a (blessed) web page context,
193 // unless the extension in question is a component extension, in which case
194 // we cheat and call it blessed.
195 return (extension->is_hosted_app() &&
196 extension->location() != Manifest::COMPONENT)
197 ? Feature::BLESSED_WEB_PAGE_CONTEXT
198 : Feature::BLESSED_EXTENSION_CONTEXT;
199 }
200
201 // TODO(kalman): This isUnique() check is wrong, it should be performed as
202 // part of ScriptContext::IsSandboxedPage().
annekao6572d5c2015-08-19 16:13:36203 if (!origin.isUnique() &&
204 RendererExtensionRegistry::Get()->ExtensionBindingsAllowed(url)) {
kalmanf91cb892015-04-15 19:20:48205 if (!extension) // TODO(kalman): when does this happen?
206 return Feature::UNSPECIFIED_CONTEXT;
207 return extension->is_hosted_app() ? Feature::BLESSED_WEB_PAGE_CONTEXT
208 : Feature::UNBLESSED_EXTENSION_CONTEXT;
209 }
210
211 if (!url.is_valid())
212 return Feature::UNSPECIFIED_CONTEXT;
213
214 if (url.SchemeIs(content::kChromeUIScheme))
215 return Feature::WEBUI_CONTEXT;
216
217 return Feature::WEB_PAGE_CONTEXT;
218}
219
220void ScriptContextSet::DispatchOnUnloadEventAndRemove(
221 std::set<ScriptContext*>* out,
222 ScriptContext* context) {
223 context->DispatchOnUnloadEvent();
224 Remove(context); // deleted asynchronously
225 out->insert(context);
kozyatinskiy441ece52015-04-01 23:46:32226}
227
[email protected]8fe74bf2012-08-07 21:08:42228} // namespace extensions