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

Side by Side Diff: extensions/renderer/script_injection_manager.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: Addressed comments 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_manager.h" 5 #include "extensions/renderer/script_injection_manager.h"
6 6
7 #include "base/auto_reset.h" 7 #include "base/auto_reset.h"
8 #include "base/bind.h" 8 #include "base/bind.h"
9 #include "base/memory/weak_ptr.h" 9 #include "base/memory/weak_ptr.h"
10 #include "base/values.h" 10 #include "base/values.h"
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
46 case UserScript::UNDEFINED: 46 case UserScript::UNDEFINED:
47 case UserScript::RUN_DEFERRED: 47 case UserScript::RUN_DEFERRED:
48 case UserScript::BROWSER_DRIVEN: 48 case UserScript::BROWSER_DRIVEN:
49 case UserScript::RUN_LOCATION_LAST: 49 case UserScript::RUN_LOCATION_LAST:
50 break; 50 break;
51 } 51 }
52 NOTREACHED(); 52 NOTREACHED();
53 return UserScript::RUN_LOCATION_LAST; 53 return UserScript::RUN_LOCATION_LAST;
54 } 54 }
55 55
56
57 // TODO(hanxi): let ScriptInjection own an InjectionHost to avoid constructing 56 // TODO(hanxi): let ScriptInjection own an InjectionHost to avoid constructing
58 // an ExtensionInjectionHost many times. 57 // an ExtensionInjectionHost many times.
59 // Note: the ScriptInjection should be able to know when the backing extension 58 // Note: the ScriptInjection should be able to know when the backing extension
60 // is removed. 59 // is removed.
61 scoped_ptr<ExtensionInjectionHost> GetExtensionInjectionHost( 60 scoped_ptr<ExtensionInjectionHost> GetExtensionInjectionHost(
62 const std::string& extension_id, const ExtensionSet* extensions) { 61 const std::string& extension_id, const ExtensionSet* extensions) {
63 const Extension* extension = extensions->GetByID(extension_id); 62 const Extension* extension = extensions->GetByID(extension_id);
64 if (!extension) 63 if (!extension)
65 return scoped_ptr<ExtensionInjectionHost>(); 64 return scoped_ptr<ExtensionInjectionHost>();
66 return scoped_ptr<ExtensionInjectionHost>( 65 return scoped_ptr<ExtensionInjectionHost>(
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after
235 void ScriptInjectionManager::RVOHelper::InvalidateFrame( 234 void ScriptInjectionManager::RVOHelper::InvalidateFrame(
236 blink::WebFrame* frame) { 235 blink::WebFrame* frame) {
237 pending_idle_frames_.erase(frame); 236 pending_idle_frames_.erase(frame);
238 manager_->InvalidateForFrame(frame); 237 manager_->InvalidateForFrame(frame);
239 } 238 }
240 239
241 ScriptInjectionManager::ScriptInjectionManager( 240 ScriptInjectionManager::ScriptInjectionManager(
242 const ExtensionSet* extensions, 241 const ExtensionSet* extensions,
243 UserScriptSetManager* user_script_set_manager) 242 UserScriptSetManager* user_script_set_manager)
244 : extensions_(extensions), 243 : extensions_(extensions),
245 injecting_scripts_(false),
246 user_script_set_manager_(user_script_set_manager), 244 user_script_set_manager_(user_script_set_manager),
247 user_script_set_manager_observer_(this) { 245 user_script_set_manager_observer_(this) {
248 user_script_set_manager_observer_.Add(user_script_set_manager_); 246 user_script_set_manager_observer_.Add(user_script_set_manager_);
249 } 247 }
250 248
251 ScriptInjectionManager::~ScriptInjectionManager() { 249 ScriptInjectionManager::~ScriptInjectionManager() {
252 } 250 }
253 251
254 void ScriptInjectionManager::OnRenderViewCreated( 252 void ScriptInjectionManager::OnRenderViewCreated(
255 content::RenderView* render_view) { 253 content::RenderView* render_view) {
256 rvo_helpers_.push_back(new RVOHelper(render_view, this)); 254 rvo_helpers_.push_back(new RVOHelper(render_view, this));
257 } 255 }
258 256
257 void ScriptInjectionManager::OnInjectionFinished(
258 ScriptInjection* injection) {
259 ScopedVector<ScriptInjection>::iterator it;
260 for (it = running_injections_.begin(); it != running_injections_.end(); ++it){
Devlin 2015/02/27 18:25:33 This looks like it clears all running injections?
kozy 2015/02/28 15:23:22 Fixed. It always clears first injection without fi
261 running_injections_.erase(it);
262 break;
263 }
264 }
265
259 void ScriptInjectionManager::OnUserScriptsUpdated( 266 void ScriptInjectionManager::OnUserScriptsUpdated(
260 const std::set<std::string>& changed_extensions, 267 const std::set<std::string>& changed_extensions,
261 const std::vector<UserScript*>& scripts) { 268 const std::vector<UserScript*>& scripts) {
262 for (ScopedVector<ScriptInjection>::iterator iter = 269 for (ScopedVector<ScriptInjection>::iterator iter =
263 pending_injections_.begin(); 270 pending_injections_.begin();
264 iter != pending_injections_.end();) { 271 iter != pending_injections_.end();) {
265 if (changed_extensions.count((*iter)->host_id().id()) > 0) 272 if (changed_extensions.count((*iter)->host_id().id()) > 0)
266 iter = pending_injections_.erase(iter); 273 iter = pending_injections_.erase(iter);
267 else 274 else
268 ++iter; 275 ++iter;
269 } 276 }
270
271 // If we are currently injecting scripts, we need to make a note that these
272 // extensions were updated.
273 if (injecting_scripts_) {
274 invalidated_while_injecting_.insert(changed_extensions.begin(),
275 changed_extensions.end());
276 }
277 } 277 }
278 278
279 void ScriptInjectionManager::RemoveObserver(RVOHelper* helper) { 279 void ScriptInjectionManager::RemoveObserver(RVOHelper* helper) {
280 for (ScopedVector<RVOHelper>::iterator iter = rvo_helpers_.begin(); 280 for (ScopedVector<RVOHelper>::iterator iter = rvo_helpers_.begin();
281 iter != rvo_helpers_.end(); 281 iter != rvo_helpers_.end();
282 ++iter) { 282 ++iter) {
283 if (*iter == helper) { 283 if (*iter == helper) {
284 rvo_helpers_.erase(iter); 284 rvo_helpers_.erase(iter);
285 break; 285 break;
286 } 286 }
287 } 287 }
288 } 288 }
289 289
290 void ScriptInjectionManager::InvalidateForFrame(blink::WebFrame* frame) { 290 void ScriptInjectionManager::InvalidateForFrame(blink::WebFrame* frame) {
291 for (ScopedVector<ScriptInjection>::iterator iter = 291 for (ScopedVector<ScriptInjection>::iterator iter =
292 pending_injections_.begin(); 292 pending_injections_.begin();
293 iter != pending_injections_.end();) { 293 iter != pending_injections_.end();) {
294 if ((*iter)->web_frame() == frame) 294 if ((*iter)->web_frame() == frame)
295 iter = pending_injections_.erase(iter); 295 iter = pending_injections_.erase(iter);
296 else 296 else
297 ++iter; 297 ++iter;
298 } 298 }
299 299
300 frame_statuses_.erase(frame); 300 frame_statuses_.erase(frame);
301 } 301 }
302 302
303 bool ScriptInjectionManager::IsFrameValid(blink::WebFrame* frame) const {
304 return frame_statuses_.find(frame) != frame_statuses_.end();
305 }
306
307 void ScriptInjectionManager::StartInjectScripts( 303 void ScriptInjectionManager::StartInjectScripts(
308 blink::WebFrame* frame, UserScript::RunLocation run_location) { 304 blink::WebFrame* frame, UserScript::RunLocation run_location) {
309 FrameStatusMap::iterator iter = frame_statuses_.find(frame); 305 FrameStatusMap::iterator iter = frame_statuses_.find(frame);
310 // We also don't execute if we detect that the run location is somehow out of 306 // We also don't execute if we detect that the run location is somehow out of
311 // order. This can happen if: 307 // order. This can happen if:
312 // - The first run location reported for the frame isn't DOCUMENT_START, or 308 // - The first run location reported for the frame isn't DOCUMENT_START, or
313 // - The run location reported doesn't immediately follow the previous 309 // - The run location reported doesn't immediately follow the previous
314 // reported run location. 310 // reported run location.
315 // We don't want to run because extensions may have requirements that scripts 311 // We don't want to run because extensions may have requirements that scripts
316 // running in an earlier run location have run by the time a later script 312 // running in an earlier run location have run by the time a later script
(...skipping 10 matching lines...) Expand all
327 InvalidateForFrame(frame); 323 InvalidateForFrame(frame);
328 return; 324 return;
329 } else if (iter != frame_statuses_.end() && iter->second >= run_location) { 325 } else if (iter != frame_statuses_.end() && iter->second >= run_location) {
330 // Certain run location signals (like DidCreateDocumentElement) can happen 326 // Certain run location signals (like DidCreateDocumentElement) can happen
331 // multiple times. Ignore the subsequent signals. 327 // multiple times. Ignore the subsequent signals.
332 return; 328 return;
333 } 329 }
334 330
335 // Otherwise, all is right in the world, and we can get on with the 331 // Otherwise, all is right in the world, and we can get on with the
336 // injections! 332 // injections!
337
338 frame_statuses_[frame] = run_location; 333 frame_statuses_[frame] = run_location;
339 334 InjectScripts(frame, run_location);
340 // If a content script injects blocking code (such as a javascript alert()),
341 // then there is a chance that we are running in a nested message loop, and
342 // shouldn't inject scripts right now (to avoid conflicts).
343 if (!injecting_scripts_) {
344 InjectScripts(frame, run_location);
345 // As above, we might have been blocked, but that means that, in the mean
346 // time, it's possible the frame advanced. Inject any scripts for run
347 // locations that were registered, but never ran.
348 while ((iter = frame_statuses_.find(frame)) != frame_statuses_.end() &&
349 iter->second > run_location) {
350 run_location = NextRunLocation(run_location);
351 DCHECK_LE(run_location, UserScript::DOCUMENT_IDLE);
352 InjectScripts(frame, run_location);
353 }
354 }
355 } 335 }
356 336
357 void ScriptInjectionManager::InjectScripts( 337 void ScriptInjectionManager::InjectScripts(
358 blink::WebFrame* frame, 338 blink::WebFrame* frame,
359 UserScript::RunLocation run_location) { 339 UserScript::RunLocation run_location) {
360 DCHECK(!injecting_scripts_);
361 DCHECK(invalidated_while_injecting_.empty());
362 base::AutoReset<bool>(&injecting_scripts_, true);
363
364 // Find any injections that want to run on the given frame. 340 // Find any injections that want to run on the given frame.
365 // We create a separate vector for these because there is a chance that 341 // We create a separate vector for these because there is a chance that
Devlin 2015/02/27 18:25:33 This is also not true, right?
kozy 2015/02/28 15:23:22 I removed comment about nested message loop. But w
366 // injected scripts can block, which can create a nested message loop. When 342 // injected scripts can block, which can create a nested message loop. When
367 // this happens, other signals (like IPCs) can cause |pending_injections_| to 343 // this happens, other signals (like IPCs) can cause |pending_injections_| to
368 // be changed, so we don't want to risk that. 344 // be changed, so we don't want to risk that.
369 ScopedVector<ScriptInjection> frame_injections; 345 ScopedVector<ScriptInjection> frame_injections;
370 for (ScopedVector<ScriptInjection>::iterator iter = 346 for (ScopedVector<ScriptInjection>::iterator iter =
371 pending_injections_.begin(); 347 pending_injections_.begin();
372 iter != pending_injections_.end();) { 348 iter != pending_injections_.end();) {
373 if ((*iter)->web_frame() == frame) { 349 if ((*iter)->web_frame() == frame) {
374 frame_injections.push_back(*iter); 350 frame_injections.push_back(*iter);
375 iter = pending_injections_.weak_erase(iter); 351 iter = pending_injections_.weak_erase(iter);
376 } else { 352 } else {
377 ++iter; 353 ++iter;
378 } 354 }
379 } 355 }
380 356
381 // Add any injections for user scripts. 357 // Add any injections for user scripts.
382 int tab_id = ExtensionHelper::Get(content::RenderView::FromWebView( 358 int tab_id = ExtensionHelper::Get(content::RenderView::FromWebView(
383 frame->top()->view()))->tab_id(); 359 frame->top()->view()))->tab_id();
384 user_script_set_manager_->GetAllInjections( 360 user_script_set_manager_->GetAllInjections(
385 &frame_injections, frame, tab_id, run_location); 361 &frame_injections, frame, tab_id, run_location);
386 362
387 ScriptsRunInfo scripts_run_info; 363 ScriptsRunInfo scripts_run_info;
388 for (ScopedVector<ScriptInjection>::iterator iter = frame_injections.begin(); 364 for (ScopedVector<ScriptInjection>::iterator iter = frame_injections.begin();
Devlin 2015/02/27 18:25:33 Let's simplify this to: std::vector<ScriptInjectio
kozy 2015/02/28 15:23:22 Done.
389 iter != frame_injections.end();) { 365 iter != frame_injections.end();) {
390 // If a blocking script was injected, there is potentially a possibility
391 // that the frame has been invalidated in the time since. Check.
392 if (!IsFrameValid(frame))
393 break;
394 366
395 const std::string& extension_id = (*iter)->host_id().id(); 367 scoped_ptr<ScriptInjection> injection(*iter);
396 scoped_ptr<ExtensionInjectionHost> extension_injection_host = 368 *iter = NULL;
397 GetExtensionInjectionHost(extension_id, extensions_); 369 injection->SetScriptInjectionManager(this);
398 // Try to inject the script if the extension is not "dirty" (invalidated by 370 TryToInject(injection.Pass(), run_location, &scripts_run_info, true);
399 // an update). If the injection does not finish (i.e., it is waiting for 371
400 // permission), add it to the list of pending injections. 372 ++iter;
401 if (invalidated_while_injecting_.count(extension_id) == 0 &&
402 !(*iter)->TryToInject(run_location,
403 extension_injection_host.get(),
404 &scripts_run_info)) {
405 pending_injections_.insert(pending_injections_.begin(), *iter);
406 iter = frame_injections.weak_erase(iter);
407 } else {
408 ++iter;
409 }
410 } 373 }
411 374
412 if (IsFrameValid(frame)) 375 scripts_run_info.LogRun(frame, run_location);
413 scripts_run_info.LogRun(frame, run_location); 376 }
414 377
415 invalidated_while_injecting_.clear(); 378 void ScriptInjectionManager::TryToInject(
379 scoped_ptr<ScriptInjection> injection,
380 UserScript::RunLocation run_location,
381 ScriptsRunInfo* scripts_run_info,
382 bool store_waiting) {
383
Devlin 2015/02/27 18:25:33 remove this line.
kozy 2015/02/28 15:23:22 Done.
384 scoped_ptr<ExtensionInjectionHost> extension_injection_host =
385 GetExtensionInjectionHost(injection->host_id().id(), extensions_);
386
387 // Try to inject the script. If the injection is waiting (i.e., for
388 // permission), add it to the list of pending injections. If the injection
389 // has blocked, add it to the list of running injections.
390 ScriptInjection::InjectionResult res = injection->TryToInject(
391 run_location,
392 extension_injection_host.get(),
393 scripts_run_info);
394
395 switch (res) {
Devlin 2015/02/27 18:25:33 inline res.
kozy 2015/02/28 15:23:22 Done.
396 case ScriptInjection::INJECTION_WAITING:
397 if (store_waiting)
398 pending_injections_.push_back(injection.release());
399 break;
400 case ScriptInjection::INJECTION_BLOCKED:
401 running_injections_.push_back(injection.release());
402 break;
403 case ScriptInjection::INJECTION_FINISHED:
404 break;
405 }
416 } 406 }
417 407
418 void ScriptInjectionManager::HandleExecuteCode( 408 void ScriptInjectionManager::HandleExecuteCode(
419 const ExtensionMsg_ExecuteCode_Params& params, 409 const ExtensionMsg_ExecuteCode_Params& params,
420 content::RenderView* render_view) { 410 content::RenderView* render_view) {
421 // TODO(dcheng): Not sure how this can happen today. In an OOPI world, it 411 // TODO(dcheng): Not sure how this can happen today. In an OOPI world, it
422 // would indicate a logic error--the browser must direct this request to the 412 // would indicate a logic error--the browser must direct this request to the
423 // right renderer process to begin with. 413 // right renderer process to begin with.
424 blink::WebLocalFrame* main_frame = 414 blink::WebLocalFrame* main_frame =
425 render_view->GetWebView()->mainFrame()->toWebLocalFrame(); 415 render_view->GetWebView()->mainFrame()->toWebLocalFrame();
(...skipping 11 matching lines...) Expand all
437 scoped_ptr<ScriptInjector>( 427 scoped_ptr<ScriptInjector>(
438 new ProgrammaticScriptInjector(params, main_frame)), 428 new ProgrammaticScriptInjector(params, main_frame)),
439 main_frame, 429 main_frame,
440 HostID(HostID::EXTENSIONS, params.extension_id), 430 HostID(HostID::EXTENSIONS, params.extension_id),
441 static_cast<UserScript::RunLocation>(params.run_at), 431 static_cast<UserScript::RunLocation>(params.run_at),
442 ExtensionHelper::Get(render_view)->tab_id())); 432 ExtensionHelper::Get(render_view)->tab_id()));
443 433
444 ScriptsRunInfo scripts_run_info; 434 ScriptsRunInfo scripts_run_info;
445 FrameStatusMap::const_iterator iter = frame_statuses_.find(main_frame); 435 FrameStatusMap::const_iterator iter = frame_statuses_.find(main_frame);
446 436
447 scoped_ptr<ExtensionInjectionHost> extension_injection_host = 437 UserScript::RunLocation run_location = (iter == frame_statuses_.end() ?
Devlin 2015/02/27 18:25:33 Why not keep run_location inlined?
kozy 2015/02/28 15:23:22 Done.
448 GetExtensionInjectionHost(injection->host_id().id(), extensions_); 438 UserScript::UNDEFINED : iter->second);
449 439
450 if (!injection->TryToInject( 440 injection->SetScriptInjectionManager(this);
451 iter == frame_statuses_.end() ? UserScript::UNDEFINED : iter->second, 441 TryToInject(injection.Pass(), run_location, &scripts_run_info, true);
452 extension_injection_host.get(),
453 &scripts_run_info)) {
454 pending_injections_.push_back(injection.release());
455 }
456 } 442 }
457 443
458 void ScriptInjectionManager::HandleExecuteDeclarativeScript( 444 void ScriptInjectionManager::HandleExecuteDeclarativeScript(
459 blink::WebFrame* web_frame, 445 blink::WebFrame* web_frame,
460 int tab_id, 446 int tab_id,
461 const ExtensionId& extension_id, 447 const ExtensionId& extension_id,
462 int script_id, 448 int script_id,
463 const GURL& url) { 449 const GURL& url) {
464 scoped_ptr<ExtensionInjectionHost> extension_injection_host =
465 GetExtensionInjectionHost(extension_id, extensions_);
466 const Extension* extension = extensions_->GetByID(extension_id); 450 const Extension* extension = extensions_->GetByID(extension_id);
467 // TODO(dcheng): This function signature should really be a WebLocalFrame, 451 // TODO(dcheng): This function signature should really be a WebLocalFrame,
468 // rather than trying to coerce it here. 452 // rather than trying to coerce it here.
469 scoped_ptr<ScriptInjection> injection = 453 scoped_ptr<ScriptInjection> injection =
470 user_script_set_manager_->GetInjectionForDeclarativeScript( 454 user_script_set_manager_->GetInjectionForDeclarativeScript(
471 script_id, 455 script_id,
472 web_frame->toWebLocalFrame(), 456 web_frame->toWebLocalFrame(),
473 tab_id, 457 tab_id,
474 url, 458 url,
475 extension); 459 extension);
476 if (injection.get()) { 460 if (injection.get()) {
477 ScriptsRunInfo scripts_run_info; 461 ScriptsRunInfo scripts_run_info;
462
463 injection->SetScriptInjectionManager(this);
478 // TODO(markdittmer): Use return value of TryToInject for error handling. 464 // TODO(markdittmer): Use return value of TryToInject for error handling.
479 injection->TryToInject(UserScript::BROWSER_DRIVEN, 465 TryToInject(injection.Pass(),
480 extension_injection_host.get(), 466 UserScript::BROWSER_DRIVEN,
481 &scripts_run_info); 467 &scripts_run_info,
468 false);
Devlin 2015/02/27 18:25:33 I think it was a bug that we didn't store waiting
kozy 2015/02/28 15:23:22 Done.
469
482 scripts_run_info.LogRun(web_frame, UserScript::BROWSER_DRIVEN); 470 scripts_run_info.LogRun(web_frame, UserScript::BROWSER_DRIVEN);
483 } 471 }
484 } 472 }
485 473
486 void ScriptInjectionManager::HandlePermitScriptInjection(int64 request_id) { 474 void ScriptInjectionManager::HandlePermitScriptInjection(int64 request_id) {
487 ScopedVector<ScriptInjection>::iterator iter = 475 ScopedVector<ScriptInjection>::iterator iter =
488 pending_injections_.begin(); 476 pending_injections_.begin();
489 for (; iter != pending_injections_.end(); ++iter) { 477 for (; iter != pending_injections_.end(); ++iter) {
490 if ((*iter)->request_id() == request_id) 478 if ((*iter)->request_id() == request_id)
491 break; 479 break;
492 } 480 }
493 if (iter == pending_injections_.end()) 481 if (iter == pending_injections_.end())
494 return; 482 return;
495 483
496 // At this point, because the request is present in pending_injections_, we 484 // At this point, because the request is present in pending_injections_, we
497 // know that this is the same page that issued the request (otherwise, 485 // know that this is the same page that issued the request (otherwise,
498 // RVOHelper's DidStartProvisionalLoad callback would have caused it to be 486 // RVOHelper's DidStartProvisionalLoad callback would have caused it to be
499 // cleared out). 487 // cleared out).
500 488
501 scoped_ptr<ScriptInjection> injection(*iter); 489 scoped_ptr<ScriptInjection> injection(*iter);
502 pending_injections_.weak_erase(iter); 490 pending_injections_.weak_erase(iter);
503 491
504 ScriptsRunInfo scripts_run_info; 492 injection->SetScriptInjectionManager(this);
493
505 scoped_ptr<ExtensionInjectionHost> extension_injection_host = 494 scoped_ptr<ExtensionInjectionHost> extension_injection_host =
506 GetExtensionInjectionHost(injection->host_id().id(), extensions_); 495 GetExtensionInjectionHost(injection->host_id().id(), extensions_);
507 if (injection->OnPermissionGranted(extension_injection_host.get(), 496 ScriptsRunInfo scripts_run_info;
508 &scripts_run_info)) { 497 ScriptInjection::InjectionResult res = injection->OnPermissionGranted(
498 extension_injection_host.get(), &scripts_run_info);
499 if (res == ScriptInjection::INJECTION_BLOCKED) {
500 running_injections_.push_back(injection.Pass());
501 }
502 if (extension_injection_host.get()) {
509 scripts_run_info.LogRun(injection->web_frame(), UserScript::RUN_DEFERRED); 503 scripts_run_info.LogRun(injection->web_frame(), UserScript::RUN_DEFERRED);
510 } 504 }
511 } 505 }
512 506
513 } // namespace extensions 507 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698