OLD | NEW |
---|---|
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 224 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
235 void ScriptInjectionManager::RVOHelper::InvalidateFrame( | 235 void ScriptInjectionManager::RVOHelper::InvalidateFrame( |
236 blink::WebFrame* frame) { | 236 blink::WebFrame* frame) { |
237 pending_idle_frames_.erase(frame); | 237 pending_idle_frames_.erase(frame); |
238 manager_->InvalidateForFrame(frame); | 238 manager_->InvalidateForFrame(frame); |
239 } | 239 } |
240 | 240 |
241 ScriptInjectionManager::ScriptInjectionManager( | 241 ScriptInjectionManager::ScriptInjectionManager( |
242 const ExtensionSet* extensions, | 242 const ExtensionSet* extensions, |
243 UserScriptSetManager* user_script_set_manager) | 243 UserScriptSetManager* user_script_set_manager) |
244 : extensions_(extensions), | 244 : extensions_(extensions), |
245 injecting_scripts_(false), | |
246 user_script_set_manager_(user_script_set_manager), | 245 user_script_set_manager_(user_script_set_manager), |
247 user_script_set_manager_observer_(this) { | 246 user_script_set_manager_observer_(this) { |
248 user_script_set_manager_observer_.Add(user_script_set_manager_); | 247 user_script_set_manager_observer_.Add(user_script_set_manager_); |
249 } | 248 } |
250 | 249 |
251 ScriptInjectionManager::~ScriptInjectionManager() { | 250 ScriptInjectionManager::~ScriptInjectionManager() { |
252 } | 251 } |
253 | 252 |
254 void ScriptInjectionManager::OnRenderViewCreated( | 253 void ScriptInjectionManager::OnRenderViewCreated( |
255 content::RenderView* render_view) { | 254 content::RenderView* render_view) { |
256 rvo_helpers_.push_back(new RVOHelper(render_view, this)); | 255 rvo_helpers_.push_back(new RVOHelper(render_view, this)); |
257 } | 256 } |
258 | 257 |
258 void ScriptInjectionManager::OnInjectionFinished( | |
259 ScriptInjection* injection, | |
260 scoped_refptr<ScriptsRunInfo> scripts_run_info) { | |
261 blink::WebFrame* frame = injection->web_frame(); | |
262 UserScript::RunLocation run_location = injection->run_location(); | |
263 | |
264 ScopedVector<ScriptInjection>::iterator it; | |
265 for (it = running_injections_.begin(); it != running_injections_.end(); ++it){ | |
266 running_injections_.erase(it); | |
267 break; | |
268 } | |
269 | |
270 if (scripts_run_info && scripts_run_info->HasOneRef() && IsFrameValid(frame)) | |
Devlin
2015/02/25 18:02:15
ref counted lifetimes are already somewhat Evil.
kozy
2015/02/27 17:32:28
I've removed ref counted Scripts Run Info and retu
Devlin
2015/02/27 18:25:32
Fair enough. More than anything else, I'd want to
kozy
2015/02/28 15:23:21
I've added _BlockedScriptCount metrics to ScriptsR
| |
271 scripts_run_info->LogRun(frame, run_location); | |
272 } | |
273 | |
259 void ScriptInjectionManager::OnUserScriptsUpdated( | 274 void ScriptInjectionManager::OnUserScriptsUpdated( |
260 const std::set<std::string>& changed_extensions, | 275 const std::set<std::string>& changed_extensions, |
261 const std::vector<UserScript*>& scripts) { | 276 const std::vector<UserScript*>& scripts) { |
262 for (ScopedVector<ScriptInjection>::iterator iter = | 277 for (ScopedVector<ScriptInjection>::iterator iter = |
263 pending_injections_.begin(); | 278 pending_injections_.begin(); |
264 iter != pending_injections_.end();) { | 279 iter != pending_injections_.end();) { |
265 if (changed_extensions.count((*iter)->host_id().id()) > 0) | 280 if (changed_extensions.count((*iter)->host_id().id()) > 0) |
266 iter = pending_injections_.erase(iter); | 281 iter = pending_injections_.erase(iter); |
267 else | 282 else |
268 ++iter; | 283 ++iter; |
269 } | 284 } |
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 } | 285 } |
278 | 286 |
279 void ScriptInjectionManager::RemoveObserver(RVOHelper* helper) { | 287 void ScriptInjectionManager::RemoveObserver(RVOHelper* helper) { |
280 for (ScopedVector<RVOHelper>::iterator iter = rvo_helpers_.begin(); | 288 for (ScopedVector<RVOHelper>::iterator iter = rvo_helpers_.begin(); |
281 iter != rvo_helpers_.end(); | 289 iter != rvo_helpers_.end(); |
282 ++iter) { | 290 ++iter) { |
283 if (*iter == helper) { | 291 if (*iter == helper) { |
284 rvo_helpers_.erase(iter); | 292 rvo_helpers_.erase(iter); |
285 break; | 293 break; |
286 } | 294 } |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
330 // Certain run location signals (like DidCreateDocumentElement) can happen | 338 // Certain run location signals (like DidCreateDocumentElement) can happen |
331 // multiple times. Ignore the subsequent signals. | 339 // multiple times. Ignore the subsequent signals. |
332 return; | 340 return; |
333 } | 341 } |
334 | 342 |
335 // Otherwise, all is right in the world, and we can get on with the | 343 // Otherwise, all is right in the world, and we can get on with the |
336 // injections! | 344 // injections! |
337 | 345 |
338 frame_statuses_[frame] = run_location; | 346 frame_statuses_[frame] = run_location; |
339 | 347 |
340 // If a content script injects blocking code (such as a javascript alert()), | 348 InjectScripts(frame, run_location); |
341 // then there is a chance that we are running in a nested message loop, and | 349 // As above, we might have been blocked, but that means that, in the mean |
Devlin
2015/02/25 18:02:15
Isn't the point of this patch that executing scrip
kozy
2015/02/27 17:32:28
Done.
| |
342 // shouldn't inject scripts right now (to avoid conflicts). | 350 // time, it's possible the frame advanced. Inject any scripts for run |
343 if (!injecting_scripts_) { | 351 // locations that were registered, but never ran. |
352 while ((iter = frame_statuses_.find(frame)) != frame_statuses_.end() && | |
353 iter->second > run_location) { | |
354 run_location = NextRunLocation(run_location); | |
355 DCHECK_LE(run_location, UserScript::DOCUMENT_IDLE); | |
344 InjectScripts(frame, run_location); | 356 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 } | 357 } |
355 } | 358 } |
356 | 359 |
357 void ScriptInjectionManager::InjectScripts( | 360 void ScriptInjectionManager::InjectScripts( |
358 blink::WebFrame* frame, | 361 blink::WebFrame* frame, |
359 UserScript::RunLocation run_location) { | 362 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. | 363 // 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 | 364 // We create a separate vector for these because there is a chance that |
366 // injected scripts can block, which can create a nested message loop. When | 365 // injected scripts can block, which can create a nested message loop. When |
367 // this happens, other signals (like IPCs) can cause |pending_injections_| to | 366 // this happens, other signals (like IPCs) can cause |pending_injections_| to |
368 // be changed, so we don't want to risk that. | 367 // be changed, so we don't want to risk that. |
369 ScopedVector<ScriptInjection> frame_injections; | 368 ScopedVector<ScriptInjection> frame_injections; |
370 for (ScopedVector<ScriptInjection>::iterator iter = | 369 for (ScopedVector<ScriptInjection>::iterator iter = |
371 pending_injections_.begin(); | 370 pending_injections_.begin(); |
372 iter != pending_injections_.end();) { | 371 iter != pending_injections_.end();) { |
373 if ((*iter)->web_frame() == frame) { | 372 if ((*iter)->web_frame() == frame) { |
374 frame_injections.push_back(*iter); | 373 frame_injections.push_back(*iter); |
375 iter = pending_injections_.weak_erase(iter); | 374 iter = pending_injections_.weak_erase(iter); |
376 } else { | 375 } else { |
377 ++iter; | 376 ++iter; |
378 } | 377 } |
379 } | 378 } |
380 | 379 |
381 // Add any injections for user scripts. | 380 // Add any injections for user scripts. |
382 int tab_id = ExtensionHelper::Get(content::RenderView::FromWebView( | 381 int tab_id = ExtensionHelper::Get(content::RenderView::FromWebView( |
383 frame->top()->view()))->tab_id(); | 382 frame->top()->view()))->tab_id(); |
384 user_script_set_manager_->GetAllInjections( | 383 user_script_set_manager_->GetAllInjections( |
385 &frame_injections, frame, tab_id, run_location); | 384 &frame_injections, frame, tab_id, run_location); |
386 | 385 |
387 ScriptsRunInfo scripts_run_info; | 386 scoped_refptr<ScriptsRunInfo> scripts_run_info(new ScriptsRunInfo()); |
388 for (ScopedVector<ScriptInjection>::iterator iter = frame_injections.begin(); | 387 for (ScopedVector<ScriptInjection>::iterator iter = frame_injections.begin(); |
389 iter != frame_injections.end();) { | 388 iter != frame_injections.end();) { |
390 // If a blocking script was injected, there is potentially a possibility | 389 // If a blocking script was injected, there is potentially a possibility |
391 // that the frame has been invalidated in the time since. Check. | 390 // that the frame has been invalidated in the time since. Check. |
392 if (!IsFrameValid(frame)) | 391 if (!IsFrameValid(frame)) |
393 break; | 392 break; |
394 | 393 |
395 const std::string& extension_id = (*iter)->host_id().id(); | 394 const std::string& extension_id = (*iter)->host_id().id(); |
396 scoped_ptr<ExtensionInjectionHost> extension_injection_host = | 395 scoped_ptr<ExtensionInjectionHost> extension_injection_host = |
397 GetExtensionInjectionHost(extension_id, extensions_); | 396 GetExtensionInjectionHost(extension_id, extensions_); |
398 // Try to inject the script if the extension is not "dirty" (invalidated by | 397 // Try to inject the script. If the injection does not finish |
399 // an update). If the injection does not finish (i.e., it is waiting for | 398 // (i.e., it is waiting for permission), add it to the list of pending |
400 // permission), add it to the list of pending injections. | 399 // injections. If the injection does not finished, add it to the list of |
401 if (invalidated_while_injecting_.count(extension_id) == 0 && | 400 // running injections. |
402 !(*iter)->TryToInject(run_location, | 401 ScriptInjection* injection = *iter; |
403 extension_injection_host.get(), | 402 injection->SetScriptInjectionManager(this); |
404 &scripts_run_info)) { | 403 if (!injection->TryToInject(run_location, |
405 pending_injections_.insert(pending_injections_.begin(), *iter); | 404 extension_injection_host.get(), |
405 scripts_run_info)) { | |
406 pending_injections_.insert(pending_injections_.begin(), injection); | |
407 iter = frame_injections.weak_erase(iter); | |
408 } else if (!injection->is_complete()) { | |
409 running_injections_.insert(running_injections_.begin(), injection); | |
406 iter = frame_injections.weak_erase(iter); | 410 iter = frame_injections.weak_erase(iter); |
407 } else { | 411 } else { |
408 ++iter; | 412 ++iter; |
409 } | 413 } |
410 } | 414 } |
411 | 415 |
412 if (IsFrameValid(frame)) | 416 frame_injections.clear(); |
413 scripts_run_info.LogRun(frame, run_location); | 417 // If injection executes synchronously in OnInjectionFinished handler we |
414 | 418 // will have two refs to scripts run info and we need call here LogRun |
415 invalidated_while_injecting_.clear(); | 419 if (scripts_run_info && scripts_run_info->HasOneRef() && IsFrameValid(frame)) |
420 scripts_run_info->LogRun(frame, run_location); | |
416 } | 421 } |
417 | 422 |
418 void ScriptInjectionManager::HandleExecuteCode( | 423 void ScriptInjectionManager::HandleExecuteCode( |
419 const ExtensionMsg_ExecuteCode_Params& params, | 424 const ExtensionMsg_ExecuteCode_Params& params, |
420 content::RenderView* render_view) { | 425 content::RenderView* render_view) { |
421 // TODO(dcheng): Not sure how this can happen today. In an OOPI world, it | 426 // 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 | 427 // would indicate a logic error--the browser must direct this request to the |
423 // right renderer process to begin with. | 428 // right renderer process to begin with. |
424 blink::WebLocalFrame* main_frame = | 429 blink::WebLocalFrame* main_frame = |
425 render_view->GetWebView()->mainFrame()->toWebLocalFrame(); | 430 render_view->GetWebView()->mainFrame()->toWebLocalFrame(); |
426 if (!main_frame) { | 431 if (!main_frame) { |
427 render_view->Send( | 432 render_view->Send( |
428 new ExtensionHostMsg_ExecuteCodeFinished(render_view->GetRoutingID(), | 433 new ExtensionHostMsg_ExecuteCodeFinished(render_view->GetRoutingID(), |
429 params.request_id, | 434 params.request_id, |
430 "No main frame", | 435 "No main frame", |
431 GURL(std::string()), | 436 GURL(std::string()), |
432 base::ListValue())); | 437 base::ListValue())); |
433 return; | 438 return; |
434 } | 439 } |
435 | 440 |
436 scoped_ptr<ScriptInjection> injection(new ScriptInjection( | 441 scoped_ptr<ScriptInjection> injection(new ScriptInjection( |
437 scoped_ptr<ScriptInjector>( | 442 scoped_ptr<ScriptInjector>( |
438 new ProgrammaticScriptInjector(params, main_frame)), | 443 new ProgrammaticScriptInjector(params, main_frame)), |
439 main_frame, | 444 main_frame, |
440 HostID(HostID::EXTENSIONS, params.extension_id), | 445 HostID(HostID::EXTENSIONS, params.extension_id), |
441 static_cast<UserScript::RunLocation>(params.run_at), | 446 static_cast<UserScript::RunLocation>(params.run_at), |
442 ExtensionHelper::Get(render_view)->tab_id())); | 447 ExtensionHelper::Get(render_view)->tab_id())); |
443 | 448 |
444 ScriptsRunInfo scripts_run_info; | 449 scoped_refptr<ScriptsRunInfo> scripts_run_info(new ScriptsRunInfo()); |
445 FrameStatusMap::const_iterator iter = frame_statuses_.find(main_frame); | 450 FrameStatusMap::const_iterator iter = frame_statuses_.find(main_frame); |
446 | 451 |
452 blink::WebFrame* frame = injection->web_frame(); | |
453 UserScript::RunLocation run_location = injection->run_location(); | |
454 | |
447 scoped_ptr<ExtensionInjectionHost> extension_injection_host = | 455 scoped_ptr<ExtensionInjectionHost> extension_injection_host = |
448 GetExtensionInjectionHost(injection->host_id().id(), extensions_); | 456 GetExtensionInjectionHost(injection->host_id().id(), extensions_); |
449 | 457 injection->SetScriptInjectionManager(this); |
450 if (!injection->TryToInject( | 458 if (!injection->TryToInject( |
451 iter == frame_statuses_.end() ? UserScript::UNDEFINED : iter->second, | 459 iter == frame_statuses_.end() ? UserScript::UNDEFINED : iter->second, |
452 extension_injection_host.get(), | 460 extension_injection_host.get(), |
453 &scripts_run_info)) { | 461 scripts_run_info)) { |
454 pending_injections_.push_back(injection.release()); | 462 pending_injections_.push_back(injection.release()); |
463 } else if (!injection->is_complete()) { | |
464 running_injections_.push_back(injection.release()); | |
465 } else { | |
466 injection = NULL; | |
455 } | 467 } |
468 | |
469 if (scripts_run_info && scripts_run_info->HasOneRef() && IsFrameValid(frame)) | |
470 scripts_run_info->LogRun(frame, run_location); | |
456 } | 471 } |
457 | 472 |
458 void ScriptInjectionManager::HandleExecuteDeclarativeScript( | 473 void ScriptInjectionManager::HandleExecuteDeclarativeScript( |
459 blink::WebFrame* web_frame, | 474 blink::WebFrame* web_frame, |
460 int tab_id, | 475 int tab_id, |
461 const ExtensionId& extension_id, | 476 const ExtensionId& extension_id, |
462 int script_id, | 477 int script_id, |
463 const GURL& url) { | 478 const GURL& url) { |
464 scoped_ptr<ExtensionInjectionHost> extension_injection_host = | 479 scoped_ptr<ExtensionInjectionHost> extension_injection_host = |
465 GetExtensionInjectionHost(extension_id, extensions_); | 480 GetExtensionInjectionHost(extension_id, extensions_); |
466 const Extension* extension = extensions_->GetByID(extension_id); | 481 const Extension* extension = extensions_->GetByID(extension_id); |
467 // TODO(dcheng): This function signature should really be a WebLocalFrame, | 482 // TODO(dcheng): This function signature should really be a WebLocalFrame, |
468 // rather than trying to coerce it here. | 483 // rather than trying to coerce it here. |
469 scoped_ptr<ScriptInjection> injection = | 484 scoped_ptr<ScriptInjection> injection = |
470 user_script_set_manager_->GetInjectionForDeclarativeScript( | 485 user_script_set_manager_->GetInjectionForDeclarativeScript( |
471 script_id, | 486 script_id, |
472 web_frame->toWebLocalFrame(), | 487 web_frame->toWebLocalFrame(), |
473 tab_id, | 488 tab_id, |
474 url, | 489 url, |
475 extension); | 490 extension); |
476 if (injection.get()) { | 491 if (injection.get()) { |
477 ScriptsRunInfo scripts_run_info; | 492 scoped_refptr<ScriptsRunInfo> scripts_run_info(new ScriptsRunInfo()); |
493 injection->SetScriptInjectionManager(this); | |
494 | |
495 blink::WebFrame* frame = injection->web_frame(); | |
496 UserScript::RunLocation run_location = injection->run_location(); | |
497 | |
478 // TODO(markdittmer): Use return value of TryToInject for error handling. | 498 // TODO(markdittmer): Use return value of TryToInject for error handling. |
479 injection->TryToInject(UserScript::BROWSER_DRIVEN, | 499 if (injection->TryToInject(UserScript::BROWSER_DRIVEN, |
480 extension_injection_host.get(), | 500 extension_injection_host.get(), |
481 &scripts_run_info); | 501 scripts_run_info)) { |
482 scripts_run_info.LogRun(web_frame, UserScript::BROWSER_DRIVEN); | 502 if (!injection->is_complete()) |
503 running_injections_.push_back(injection.release()); | |
504 } | |
505 injection = NULL; | |
506 if (scripts_run_info && scripts_run_info->HasOneRef() | |
507 && IsFrameValid(frame)) | |
508 scripts_run_info->LogRun(frame, run_location); | |
483 } | 509 } |
484 } | 510 } |
485 | 511 |
486 void ScriptInjectionManager::HandlePermitScriptInjection(int64 request_id) { | 512 void ScriptInjectionManager::HandlePermitScriptInjection(int64 request_id) { |
487 ScopedVector<ScriptInjection>::iterator iter = | 513 ScopedVector<ScriptInjection>::iterator iter = |
488 pending_injections_.begin(); | 514 pending_injections_.begin(); |
489 for (; iter != pending_injections_.end(); ++iter) { | 515 for (; iter != pending_injections_.end(); ++iter) { |
490 if ((*iter)->request_id() == request_id) | 516 if ((*iter)->request_id() == request_id) |
491 break; | 517 break; |
492 } | 518 } |
493 if (iter == pending_injections_.end()) | 519 if (iter == pending_injections_.end()) |
494 return; | 520 return; |
495 | 521 |
496 // At this point, because the request is present in pending_injections_, we | 522 // 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, | 523 // know that this is the same page that issued the request (otherwise, |
498 // RVOHelper's DidStartProvisionalLoad callback would have caused it to be | 524 // RVOHelper's DidStartProvisionalLoad callback would have caused it to be |
499 // cleared out). | 525 // cleared out). |
500 | 526 |
501 scoped_ptr<ScriptInjection> injection(*iter); | 527 scoped_ptr<ScriptInjection> injection(*iter); |
502 pending_injections_.weak_erase(iter); | 528 pending_injections_.weak_erase(iter); |
503 | 529 |
504 ScriptsRunInfo scripts_run_info; | 530 injection->SetScriptInjectionManager(this); |
531 | |
505 scoped_ptr<ExtensionInjectionHost> extension_injection_host = | 532 scoped_ptr<ExtensionInjectionHost> extension_injection_host = |
506 GetExtensionInjectionHost(injection->host_id().id(), extensions_); | 533 GetExtensionInjectionHost(injection->host_id().id(), extensions_); |
507 if (injection->OnPermissionGranted(extension_injection_host.get(), | 534 if (injection->OnPermissionGranted(extension_injection_host.get())) { |
508 &scripts_run_info)) { | 535 if (!injection->is_complete()) |
509 scripts_run_info.LogRun(injection->web_frame(), UserScript::RUN_DEFERRED); | 536 running_injections_.push_back(injection.Pass()); |
510 } | 537 } |
511 } | 538 } |
512 | 539 |
513 } // namespace extensions | 540 } // namespace extensions |
OLD | NEW |