Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(37)

Side by Side Diff: extensions/renderer/script_injection.cc

Issue 878513005: Extensions: suspend extension's scripts when V8 is paused (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 "extensions/renderer/script_injection.h" 5 #include "extensions/renderer/script_injection.h"
6 6
7 #include <map> 7 #include <map>
8 8
9 #include "base/lazy_instance.h" 9 #include "base/lazy_instance.h"
10 #include "base/metrics/histogram.h" 10 #include "base/metrics/histogram.h"
11 #include "base/timer/elapsed_timer.h" 11 #include "base/timer/elapsed_timer.h"
12 #include "base/values.h" 12 #include "base/values.h"
13 #include "content/public/child/v8_value_converter.h" 13 #include "content/public/child/v8_value_converter.h"
14 #include "content/public/renderer/render_view.h" 14 #include "content/public/renderer/render_view.h"
15 #include "extensions/common/extension_messages.h" 15 #include "extensions/common/extension_messages.h"
16 #include "extensions/common/host_id.h" 16 #include "extensions/common/host_id.h"
17 #include "extensions/common/manifest_handlers/csp_info.h" 17 #include "extensions/common/manifest_handlers/csp_info.h"
18 #include "extensions/renderer/dom_activity_logger.h" 18 #include "extensions/renderer/dom_activity_logger.h"
19 #include "extensions/renderer/extension_groups.h" 19 #include "extensions/renderer/extension_groups.h"
20 #include "extensions/renderer/extension_injection_host.h" 20 #include "extensions/renderer/extension_injection_host.h"
21 #include "extensions/renderer/extensions_renderer_client.h" 21 #include "extensions/renderer/extensions_renderer_client.h"
22 #include "extensions/renderer/script_injection_callback.h"
23 #include "extensions/renderer/script_injection_manager.h"
24 #include "extensions/renderer/scripts_run_info.h"
22 #include "third_party/WebKit/public/platform/WebString.h" 25 #include "third_party/WebKit/public/platform/WebString.h"
23 #include "third_party/WebKit/public/web/WebDocument.h" 26 #include "third_party/WebKit/public/web/WebDocument.h"
24 #include "third_party/WebKit/public/web/WebLocalFrame.h" 27 #include "third_party/WebKit/public/web/WebLocalFrame.h"
25 #include "third_party/WebKit/public/web/WebScopedUserGesture.h" 28 #include "third_party/WebKit/public/web/WebScopedUserGesture.h"
26 #include "third_party/WebKit/public/web/WebScriptSource.h" 29 #include "third_party/WebKit/public/web/WebScriptSource.h"
27 #include "third_party/WebKit/public/web/WebSecurityOrigin.h" 30 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
28 #include "url/gurl.h" 31 #include "url/gurl.h"
29 32
30 namespace extensions { 33 namespace extensions {
31 34
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
110 blink::WebLocalFrame* web_frame, 113 blink::WebLocalFrame* web_frame,
111 scoped_ptr<const InjectionHost> injection_host, 114 scoped_ptr<const InjectionHost> injection_host,
112 UserScript::RunLocation run_location, 115 UserScript::RunLocation run_location,
113 int tab_id) 116 int tab_id)
114 : injector_(injector.Pass()), 117 : injector_(injector.Pass()),
115 web_frame_(web_frame), 118 web_frame_(web_frame),
116 injection_host_(injection_host.Pass()), 119 injection_host_(injection_host.Pass()),
117 run_location_(run_location), 120 run_location_(run_location),
118 tab_id_(tab_id), 121 tab_id_(tab_id),
119 request_id_(kInvalidRequestId), 122 request_id_(kInvalidRequestId),
120 complete_(false) { 123 complete_(false),
124 running_frames_(0),
125 execution_results_(new base::ListValue()),
126 all_injections_started_(false),
127 script_injection_manager_(nullptr) {
121 CHECK(injection_host_.get()); 128 CHECK(injection_host_.get());
122 } 129 }
123 130
124 ScriptInjection::~ScriptInjection() { 131 ScriptInjection::~ScriptInjection() {
125 if (!complete_) 132 if (!complete_)
126 injector_->OnWillNotInject(ScriptInjector::WONT_INJECT); 133 injector_->OnWillNotInject(ScriptInjector::WONT_INJECT);
127 } 134 }
128 135
129 bool ScriptInjection::TryToInject(UserScript::RunLocation current_location, 136 ScriptInjection::InjectionResult ScriptInjection::TryToInject(
130 ScriptsRunInfo* scripts_run_info) { 137 UserScript::RunLocation current_location,
138 ScriptsRunInfo* scripts_run_info,
139 ScriptInjectionManager* manager) {
131 if (current_location < run_location_) 140 if (current_location < run_location_)
132 return false; // Wait for the right location. 141 return INJECTION_WAITING; // Wait for the right location.
133 142
134 if (request_id_ != kInvalidRequestId) 143 if (request_id_ != kInvalidRequestId) {
135 return false; // We're waiting for permission right now, try again later. 144 // We're waiting for permission right now, try again later.
145 return INJECTION_WAITING;
146 }
136 147
137 if (!injection_host_) { 148 if (!injection_host_) {
138 NotifyWillNotInject(ScriptInjector::EXTENSION_REMOVED); 149 NotifyWillNotInject(ScriptInjector::EXTENSION_REMOVED);
139 return true; // We're done. 150 return INJECTION_FINISHED; // We're done.
140 } 151 }
141 152
142 switch (injector_->CanExecuteOnFrame( 153 switch (injector_->CanExecuteOnFrame(
143 injection_host_.get(), web_frame_, tab_id_, 154 injection_host_.get(), web_frame_, tab_id_,
144 web_frame_->top()->document().url())) { 155 web_frame_->top()->document().url())) {
145 case PermissionsData::ACCESS_DENIED: 156 case PermissionsData::ACCESS_DENIED:
146 NotifyWillNotInject(ScriptInjector::NOT_ALLOWED); 157 NotifyWillNotInject(ScriptInjector::NOT_ALLOWED);
147 return true; // We're done. 158 return INJECTION_FINISHED; // We're done.
148 case PermissionsData::ACCESS_WITHHELD: 159 case PermissionsData::ACCESS_WITHHELD:
149 SendInjectionMessage(true /* request permission */); 160 SendInjectionMessage(true /* request permission */);
150 return false; // Wait around for permission. 161 return INJECTION_WAITING; // Wait around for permission.
151 case PermissionsData::ACCESS_ALLOWED: 162 case PermissionsData::ACCESS_ALLOWED:
152 Inject(scripts_run_info); 163 InjectionResult result = Inject(scripts_run_info);
153 return true; // We're done! 164 // If the injection is blocked, we need to set the manager so we can
165 // notify it upon completion.
166 if (result == INJECTION_BLOCKED)
167 script_injection_manager_ = manager;
168 return result;
154 } 169 }
155 170
156 NOTREACHED(); 171 NOTREACHED();
157 return false; 172 return INJECTION_FINISHED;
158 } 173 }
159 174
160 bool ScriptInjection::OnPermissionGranted(ScriptsRunInfo* scripts_run_info) { 175 ScriptInjection::InjectionResult ScriptInjection::OnPermissionGranted(
176 ScriptsRunInfo* scripts_run_info) {
161 if (!injection_host_) { 177 if (!injection_host_) {
162 NotifyWillNotInject(ScriptInjector::EXTENSION_REMOVED); 178 NotifyWillNotInject(ScriptInjector::EXTENSION_REMOVED);
163 return false; 179 return INJECTION_FINISHED;
164 } 180 }
165 181
166 Inject(scripts_run_info); 182 return Inject(scripts_run_info);
167 return true;
168 } 183 }
169 184
170 void ScriptInjection::OnHostRemoved() { 185 void ScriptInjection::OnHostRemoved() {
171 injection_host_.reset(nullptr); 186 injection_host_.reset(nullptr);
172 } 187 }
173 188
174 void ScriptInjection::SendInjectionMessage(bool request_permission) { 189 void ScriptInjection::SendInjectionMessage(bool request_permission) {
175 content::RenderView* render_view = 190 content::RenderView* render_view =
176 content::RenderView::FromWebView(web_frame()->top()->view()); 191 content::RenderView::FromWebView(web_frame()->top()->view());
177 192
178 // If we are just notifying the browser of the injection, then send an 193 // If we are just notifying the browser of the injection, then send an
179 // invalid request (which is treated like a notification). 194 // invalid request (which is treated like a notification).
180 request_id_ = request_permission ? g_next_pending_id++ : kInvalidRequestId; 195 request_id_ = request_permission ? g_next_pending_id++ : kInvalidRequestId;
181 render_view->Send(new ExtensionHostMsg_RequestScriptInjectionPermission( 196 render_view->Send(new ExtensionHostMsg_RequestScriptInjectionPermission(
182 render_view->GetRoutingID(), 197 render_view->GetRoutingID(),
183 host_id().id(), 198 host_id().id(),
184 injector_->script_type(), 199 injector_->script_type(),
185 request_id_)); 200 request_id_));
186 } 201 }
187 202
188 void ScriptInjection::NotifyWillNotInject( 203 void ScriptInjection::NotifyWillNotInject(
189 ScriptInjector::InjectFailureReason reason) { 204 ScriptInjector::InjectFailureReason reason) {
190 complete_ = true; 205 complete_ = true;
191 injector_->OnWillNotInject(reason); 206 injector_->OnWillNotInject(reason);
192 } 207 }
193 208
194 void ScriptInjection::Inject(ScriptsRunInfo* scripts_run_info) { 209 ScriptInjection::InjectionResult ScriptInjection::Inject(
210 ScriptsRunInfo* scripts_run_info) {
195 DCHECK(injection_host_); 211 DCHECK(injection_host_);
196 DCHECK(scripts_run_info); 212 DCHECK(scripts_run_info);
197 DCHECK(!complete_); 213 DCHECK(!complete_);
198 214
199 if (injection_host_->ShouldNotifyBrowserOfInjection()) 215 if (injection_host_->ShouldNotifyBrowserOfInjection())
200 SendInjectionMessage(false /* don't request permission */); 216 SendInjectionMessage(false /* don't request permission */);
201 217
202 std::vector<blink::WebFrame*> frame_vector; 218 std::vector<blink::WebFrame*> frame_vector;
203 frame_vector.push_back(web_frame_); 219 frame_vector.push_back(web_frame_);
204 if (injector_->ShouldExecuteInChildFrames()) 220 if (injector_->ShouldExecuteInChildFrames())
205 AppendAllChildFrames(web_frame_, &frame_vector); 221 AppendAllChildFrames(web_frame_, &frame_vector);
206 222
207 scoped_ptr<blink::WebScopedUserGesture> gesture;
208 if (injector_->IsUserGesture())
209 gesture.reset(new blink::WebScopedUserGesture());
210
211 bool inject_js = injector_->ShouldInjectJs(run_location_); 223 bool inject_js = injector_->ShouldInjectJs(run_location_);
212 bool inject_css = injector_->ShouldInjectCss(run_location_); 224 bool inject_css = injector_->ShouldInjectCss(run_location_);
213 DCHECK(inject_js || inject_css); 225 DCHECK(inject_js || inject_css);
214 226
215 scoped_ptr<base::ListValue> execution_results(new base::ListValue());
216 GURL top_url = web_frame_->top()->document().url(); 227 GURL top_url = web_frame_->top()->document().url();
217 for (std::vector<blink::WebFrame*>::iterator iter = frame_vector.begin(); 228 for (std::vector<blink::WebFrame*>::iterator iter = frame_vector.begin();
218 iter != frame_vector.end(); 229 iter != frame_vector.end();
219 ++iter) { 230 ++iter) {
220 // TODO(dcheng): Unfortunately, the code as written won't work in an OOPI 231 // TODO(dcheng): Unfortunately, the code as written won't work in an OOPI
221 // world. This is just a temporary hack to make things compile. 232 // world. This is just a temporary hack to make things compile.
222 blink::WebLocalFrame* frame = (*iter)->toWebLocalFrame(); 233 blink::WebLocalFrame* frame = (*iter)->toWebLocalFrame();
223 234
224 // We recheck access here in the renderer for extra safety against races 235 // We recheck access here in the renderer for extra safety against races
225 // with navigation, but different frames can have different URLs, and the 236 // with navigation, but different frames can have different URLs, and the
226 // injection host might only have access to a subset of them. 237 // injection host might only have access to a subset of them.
227 // For child frames, we just skip ones the injection host doesn't have 238 // For child frames, we just skip ones the injection host doesn't have
228 // access to and carry on. 239 // access to and carry on.
229 // Note: we don't consider ACCESS_WITHHELD because there is nowhere to 240 // Note: we don't consider ACCESS_WITHHELD because there is nowhere to
230 // surface a request for a child frame. 241 // surface a request for a child frame.
231 // TODO(rdevlin.cronin): We should ask for permission somehow. 242 // TODO(rdevlin.cronin): We should ask for permission somehow.
232 if (injector_->CanExecuteOnFrame( 243 if (injector_->CanExecuteOnFrame(
233 injection_host_.get(), frame, tab_id_, top_url) == 244 injection_host_.get(), frame, tab_id_, top_url) ==
234 PermissionsData::ACCESS_DENIED) { 245 PermissionsData::ACCESS_DENIED) {
235 DCHECK(frame->parent()); 246 DCHECK(frame->parent());
236 continue; 247 continue;
237 } 248 }
238 if (inject_js) 249 if (inject_js)
239 InjectJs(frame, execution_results.get()); 250 InjectJs(frame);
240 if (inject_css) 251 if (inject_css)
241 InjectCss(frame); 252 InjectCss(frame);
242 } 253 }
243 254
244 complete_ = true; 255 all_injections_started_ = true;
245 256 injector_->GetRunInfo(scripts_run_info, run_location_);
246 // TODO(hanxi): don't log these metrics for webUIs' injections. 257 scripts_run_info->num_blocking_js = running_frames_;
247 injector_->OnInjectionComplete(execution_results.Pass(), 258 TryToFinish();
248 scripts_run_info, 259 return complete_ ? INJECTION_FINISHED : INJECTION_BLOCKED;
249 run_location_);
250 } 260 }
251 261
252 void ScriptInjection::InjectJs(blink::WebLocalFrame* frame, 262 void ScriptInjection::InjectJs(blink::WebLocalFrame* frame) {
253 base::ListValue* execution_results) { 263 ++running_frames_;
254 std::vector<blink::WebScriptSource> sources = 264 std::vector<blink::WebScriptSource> sources =
255 injector_->GetJsSources(run_location_); 265 injector_->GetJsSources(run_location_);
256 bool in_main_world = injector_->ShouldExecuteInMainWorld(); 266 bool in_main_world = injector_->ShouldExecuteInMainWorld();
257 int world_id = in_main_world 267 int world_id = in_main_world
258 ? DOMActivityLogger::kMainWorldId 268 ? DOMActivityLogger::kMainWorldId
259 : GetIsolatedWorldIdForInstance(injection_host_.get(), 269 : GetIsolatedWorldIdForInstance(injection_host_.get(),
260 frame); 270 frame);
261 bool expects_results = injector_->ExpectsResults(); 271 bool is_user_gesture = injector_->IsUserGesture();
272
273 scoped_ptr<blink::WebScriptExecutionCallback> callback(
274 new ScriptInjectionCallback(this, frame));
262 275
263 base::ElapsedTimer exec_timer; 276 base::ElapsedTimer exec_timer;
264 if (injection_host_->id().type() == HostID::EXTENSIONS) 277 if (injection_host_->id().type() == HostID::EXTENSIONS)
265 DOMActivityLogger::AttachToWorld(world_id, injection_host_->id().id()); 278 DOMActivityLogger::AttachToWorld(world_id, injection_host_->id().id());
266 v8::HandleScope scope(v8::Isolate::GetCurrent());
267 v8::Local<v8::Value> script_value;
268 if (in_main_world) { 279 if (in_main_world) {
269 // We only inject in the main world for javascript: urls. 280 // We only inject in the main world for javascript: urls.
270 DCHECK_EQ(1u, sources.size()); 281 DCHECK_EQ(1u, sources.size());
271 282
272 const blink::WebScriptSource& source = sources.front(); 283 frame->requestExecuteScriptAndReturnValue(sources.front(),
273 if (expects_results) 284 is_user_gesture,
274 script_value = frame->executeScriptAndReturnValue(source); 285 callback.release());
275 else 286 } else {
276 frame->executeScript(source); 287 frame->requestExecuteScriptInIsolatedWorld(world_id,
277 } else { // in isolated world 288 &sources.front(),
278 scoped_ptr<blink::WebVector<v8::Local<v8::Value> > > results; 289 sources.size(),
279 if (expects_results) 290 EXTENSION_GROUP_CONTENT_SCRIPTS,
280 results.reset(new blink::WebVector<v8::Local<v8::Value> >()); 291 is_user_gesture,
281 frame->executeScriptInIsolatedWorld(world_id, 292 callback.release());
282 &sources.front(),
283 sources.size(),
284 EXTENSION_GROUP_CONTENT_SCRIPTS,
285 results.get());
286 if (expects_results && !results->isEmpty())
287 script_value = (*results)[0];
288 } 293 }
289 294
290 if (injection_host_->id().type() == HostID::EXTENSIONS) 295 if (injection_host_->id().type() == HostID::EXTENSIONS)
291 UMA_HISTOGRAM_TIMES("Extensions.InjectScriptTime", exec_timer.Elapsed()); 296 UMA_HISTOGRAM_TIMES("Extensions.InjectScriptTime", exec_timer.Elapsed());
297 }
292 298
299 void ScriptInjection::OnJsInjectionCompleted(
300 blink::WebLocalFrame* frame,
301 const blink::WebVector<v8::Local<v8::Value> >& results) {
302 DCHECK(running_frames_ > 0);
303 --running_frames_;
304
305 bool expects_results = injector_->ExpectsResults();
293 if (expects_results) { 306 if (expects_results) {
307 v8::Local<v8::Value> script_value;
308 if (!results.isEmpty())
309 script_value = results[0];
294 // Right now, we only support returning single results (per frame). 310 // Right now, we only support returning single results (per frame).
295 scoped_ptr<content::V8ValueConverter> v8_converter( 311 scoped_ptr<content::V8ValueConverter> v8_converter(
296 content::V8ValueConverter::create()); 312 content::V8ValueConverter::create());
297 // It's safe to always use the main world context when converting 313 // It's safe to always use the main world context when converting
298 // here. V8ValueConverterImpl shouldn't actually care about the 314 // here. V8ValueConverterImpl shouldn't actually care about the
299 // context scope, and it switches to v8::Object's creation context 315 // context scope, and it switches to v8::Object's creation context
300 // when encountered. 316 // when encountered.
301 v8::Local<v8::Context> context = frame->mainWorldScriptContext(); 317 v8::Local<v8::Context> context = frame->mainWorldScriptContext();
302 scoped_ptr<base::Value> result( 318 scoped_ptr<base::Value> result(
303 v8_converter->FromV8Value(script_value, context)); 319 v8_converter->FromV8Value(script_value, context));
304 // Always append an execution result (i.e. no result == null result) 320 if (!result.get())
305 // so that |execution_results| lines up with the frames. 321 result.reset(base::Value::CreateNullValue());
306 execution_results->Append(result.get() ? result.release() 322 // We guarantee that the main frame's result is at the first index, but
307 : base::Value::CreateNullValue()); 323 // any sub frames results do not have guaranteed order.
324 execution_results_->Insert(
325 frame == web_frame_ ? 0 : execution_results_->GetSize(),
326 result.release());
327 }
328 TryToFinish();
329 }
330
331 void ScriptInjection::TryToFinish() {
332 if (all_injections_started_ && running_frames_ == 0) {
333 complete_ = true;
334 injector_->OnInjectionComplete(execution_results_.Pass(),
335 run_location_);
336
337 // This object can be destroyed after next line.
338 if (script_injection_manager_)
339 script_injection_manager_->OnInjectionFinished(this);
308 } 340 }
309 } 341 }
310 342
311 void ScriptInjection::InjectCss(blink::WebLocalFrame* frame) { 343 void ScriptInjection::InjectCss(blink::WebLocalFrame* frame) {
312 std::vector<std::string> css_sources = 344 std::vector<std::string> css_sources =
313 injector_->GetCssSources(run_location_); 345 injector_->GetCssSources(run_location_);
314 for (std::vector<std::string>::const_iterator iter = css_sources.begin(); 346 for (std::vector<std::string>::const_iterator iter = css_sources.begin();
315 iter != css_sources.end(); 347 iter != css_sources.end();
316 ++iter) { 348 ++iter) {
317 frame->document().insertStyleSheet(blink::WebString::fromUTF8(*iter)); 349 frame->document().insertStyleSheet(blink::WebString::fromUTF8(*iter));
318 } 350 }
319 } 351 }
320 352
321 } // namespace extensions 353 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698