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

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: 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/03/03 00:34:10 Prefer: ScopedVector<ScriptInjection>::iterator it
kozy 2015/03/03 13:50:20 Done.
261 if (*it == injection) {
262 running_injections_.erase(it);
263 break;
264 }
265 }
266 }
267
259 void ScriptInjectionManager::OnUserScriptsUpdated( 268 void ScriptInjectionManager::OnUserScriptsUpdated(
260 const std::set<std::string>& changed_extensions, 269 const std::set<std::string>& changed_extensions,
261 const std::vector<UserScript*>& scripts) { 270 const std::vector<UserScript*>& scripts) {
262 for (ScopedVector<ScriptInjection>::iterator iter = 271 for (ScopedVector<ScriptInjection>::iterator iter =
263 pending_injections_.begin(); 272 pending_injections_.begin();
264 iter != pending_injections_.end();) { 273 iter != pending_injections_.end();) {
265 if (changed_extensions.count((*iter)->host_id().id()) > 0) 274 if (changed_extensions.count((*iter)->host_id().id()) > 0)
266 iter = pending_injections_.erase(iter); 275 iter = pending_injections_.erase(iter);
267 else 276 else
268 ++iter; 277 ++iter;
269 } 278 }
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 } 279 }
278 280
279 void ScriptInjectionManager::RemoveObserver(RVOHelper* helper) { 281 void ScriptInjectionManager::RemoveObserver(RVOHelper* helper) {
280 for (ScopedVector<RVOHelper>::iterator iter = rvo_helpers_.begin(); 282 for (ScopedVector<RVOHelper>::iterator iter = rvo_helpers_.begin();
281 iter != rvo_helpers_.end(); 283 iter != rvo_helpers_.end();
282 ++iter) { 284 ++iter) {
283 if (*iter == helper) { 285 if (*iter == helper) {
284 rvo_helpers_.erase(iter); 286 rvo_helpers_.erase(iter);
285 break; 287 break;
286 } 288 }
287 } 289 }
288 } 290 }
289 291
290 void ScriptInjectionManager::InvalidateForFrame(blink::WebFrame* frame) { 292 void ScriptInjectionManager::InvalidateForFrame(blink::WebFrame* frame) {
291 for (ScopedVector<ScriptInjection>::iterator iter = 293 for (ScopedVector<ScriptInjection>::iterator iter =
292 pending_injections_.begin(); 294 pending_injections_.begin();
293 iter != pending_injections_.end();) { 295 iter != pending_injections_.end();) {
294 if ((*iter)->web_frame() == frame) 296 if ((*iter)->web_frame() == frame)
295 iter = pending_injections_.erase(iter); 297 iter = pending_injections_.erase(iter);
296 else 298 else
297 ++iter; 299 ++iter;
298 } 300 }
299 301
300 frame_statuses_.erase(frame); 302 frame_statuses_.erase(frame);
301 } 303 }
302 304
303 bool ScriptInjectionManager::IsFrameValid(blink::WebFrame* frame) const {
304 return frame_statuses_.find(frame) != frame_statuses_.end();
305 }
306
307 void ScriptInjectionManager::StartInjectScripts( 305 void ScriptInjectionManager::StartInjectScripts(
308 blink::WebFrame* frame, UserScript::RunLocation run_location) { 306 blink::WebFrame* frame, UserScript::RunLocation run_location) {
309 FrameStatusMap::iterator iter = frame_statuses_.find(frame); 307 FrameStatusMap::iterator iter = frame_statuses_.find(frame);
310 // We also don't execute if we detect that the run location is somehow out of 308 // We also don't execute if we detect that the run location is somehow out of
311 // order. This can happen if: 309 // order. This can happen if:
312 // - The first run location reported for the frame isn't DOCUMENT_START, or 310 // - The first run location reported for the frame isn't DOCUMENT_START, or
313 // - The run location reported doesn't immediately follow the previous 311 // - The run location reported doesn't immediately follow the previous
314 // reported run location. 312 // reported run location.
315 // We don't want to run because extensions may have requirements that scripts 313 // 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 314 // running in an earlier run location have run by the time a later script
(...skipping 10 matching lines...) Expand all
327 InvalidateForFrame(frame); 325 InvalidateForFrame(frame);
328 return; 326 return;
329 } else if (iter != frame_statuses_.end() && iter->second >= run_location) { 327 } else if (iter != frame_statuses_.end() && iter->second >= run_location) {
330 // Certain run location signals (like DidCreateDocumentElement) can happen 328 // Certain run location signals (like DidCreateDocumentElement) can happen
331 // multiple times. Ignore the subsequent signals. 329 // multiple times. Ignore the subsequent signals.
332 return; 330 return;
333 } 331 }
334 332
335 // Otherwise, all is right in the world, and we can get on with the 333 // Otherwise, all is right in the world, and we can get on with the
336 // injections! 334 // injections!
337
338 frame_statuses_[frame] = run_location; 335 frame_statuses_[frame] = run_location;
339 336 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 } 337 }
356 338
357 void ScriptInjectionManager::InjectScripts( 339 void ScriptInjectionManager::InjectScripts(
358 blink::WebFrame* frame, 340 blink::WebFrame* frame,
359 UserScript::RunLocation run_location) { 341 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. 342 // 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
366 // injected scripts can block, which can create a nested message loop. When
367 // this happens, other signals (like IPCs) can cause |pending_injections_| to
368 // be changed, so we don't want to risk that.
369 ScopedVector<ScriptInjection> frame_injections; 343 ScopedVector<ScriptInjection> frame_injections;
370 for (ScopedVector<ScriptInjection>::iterator iter = 344 for (ScopedVector<ScriptInjection>::iterator iter =
371 pending_injections_.begin(); 345 pending_injections_.begin();
372 iter != pending_injections_.end();) { 346 iter != pending_injections_.end();) {
373 if ((*iter)->web_frame() == frame) { 347 if ((*iter)->web_frame() == frame) {
374 frame_injections.push_back(*iter); 348 frame_injections.push_back(*iter);
375 iter = pending_injections_.weak_erase(iter); 349 iter = pending_injections_.weak_erase(iter);
376 } else { 350 } else {
377 ++iter; 351 ++iter;
378 } 352 }
379 } 353 }
380 354
381 // Add any injections for user scripts. 355 // Add any injections for user scripts.
382 int tab_id = ExtensionHelper::Get(content::RenderView::FromWebView( 356 int tab_id = ExtensionHelper::Get(content::RenderView::FromWebView(
383 frame->top()->view()))->tab_id(); 357 frame->top()->view()))->tab_id();
384 user_script_set_manager_->GetAllInjections( 358 user_script_set_manager_->GetAllInjections(
385 &frame_injections, frame, tab_id, run_location); 359 &frame_injections, frame, tab_id, run_location);
386 360
387 ScriptsRunInfo scripts_run_info; 361 ScriptsRunInfo scripts_run_info;
388 for (ScopedVector<ScriptInjection>::iterator iter = frame_injections.begin(); 362 std::vector<ScriptInjection*> released_injections;
389 iter != frame_injections.end();) { 363 frame_injections.release(&released_injections);
390 // If a blocking script was injected, there is potentially a possibility 364 for (ScriptInjection* injection : released_injections)
391 // that the frame has been invalidated in the time since. Check. 365 TryToInject(make_scoped_ptr(injection), run_location, &scripts_run_info);
392 if (!IsFrameValid(frame)) 366
367 scripts_run_info.LogRun(frame, run_location);
368 }
369
370 void ScriptInjectionManager::TryToInject(
371 scoped_ptr<ScriptInjection> injection,
372 UserScript::RunLocation run_location,
373 ScriptsRunInfo* scripts_run_info) {
374 scoped_ptr<ExtensionInjectionHost> extension_injection_host =
375 GetExtensionInjectionHost(injection->host_id().id(), extensions_);
376 injection->SetScriptInjectionManager(this);
377 // Try to inject the script. If the injection is waiting (i.e., for
378 // permission), add it to the list of pending injections. If the injection
379 // has blocked, add it to the list of running injections.
380 switch (injection->TryToInject(
381 run_location,
382 extension_injection_host.get(),
383 scripts_run_info)) {
384 case ScriptInjection::INJECTION_WAITING:
385 pending_injections_.push_back(injection.release());
386 case ScriptInjection::INJECTION_BLOCKED:
387 running_injections_.push_back(injection.release());
393 break; 388 break;
394 389 case ScriptInjection::INJECTION_FINISHED:
395 const std::string& extension_id = (*iter)->host_id().id(); 390 break;
396 scoped_ptr<ExtensionInjectionHost> extension_injection_host =
397 GetExtensionInjectionHost(extension_id, extensions_);
398 // Try to inject the script if the extension is not "dirty" (invalidated by
399 // an update). If the injection does not finish (i.e., it is waiting for
400 // permission), add it to the list of pending injections.
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 } 391 }
411
412 if (IsFrameValid(frame))
413 scripts_run_info.LogRun(frame, run_location);
414
415 invalidated_while_injecting_.clear();
416 } 392 }
417 393
418 void ScriptInjectionManager::HandleExecuteCode( 394 void ScriptInjectionManager::HandleExecuteCode(
419 const ExtensionMsg_ExecuteCode_Params& params, 395 const ExtensionMsg_ExecuteCode_Params& params,
420 content::RenderView* render_view) { 396 content::RenderView* render_view) {
421 // TODO(dcheng): Not sure how this can happen today. In an OOPI world, it 397 // 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 398 // would indicate a logic error--the browser must direct this request to the
423 // right renderer process to begin with. 399 // right renderer process to begin with.
424 blink::WebLocalFrame* main_frame = 400 blink::WebLocalFrame* main_frame =
425 render_view->GetWebView()->mainFrame()->toWebLocalFrame(); 401 render_view->GetWebView()->mainFrame()->toWebLocalFrame();
(...skipping 11 matching lines...) Expand all
437 scoped_ptr<ScriptInjector>( 413 scoped_ptr<ScriptInjector>(
438 new ProgrammaticScriptInjector(params, main_frame)), 414 new ProgrammaticScriptInjector(params, main_frame)),
439 main_frame, 415 main_frame,
440 HostID(HostID::EXTENSIONS, params.extension_id), 416 HostID(HostID::EXTENSIONS, params.extension_id),
441 static_cast<UserScript::RunLocation>(params.run_at), 417 static_cast<UserScript::RunLocation>(params.run_at),
442 ExtensionHelper::Get(render_view)->tab_id())); 418 ExtensionHelper::Get(render_view)->tab_id()));
443 419
444 ScriptsRunInfo scripts_run_info; 420 ScriptsRunInfo scripts_run_info;
445 FrameStatusMap::const_iterator iter = frame_statuses_.find(main_frame); 421 FrameStatusMap::const_iterator iter = frame_statuses_.find(main_frame);
446 422
447 scoped_ptr<ExtensionInjectionHost> extension_injection_host = 423 TryToInject(
448 GetExtensionInjectionHost(injection->host_id().id(), extensions_); 424 injection.Pass(),
449 425 iter == frame_statuses_.end() ? UserScript::UNDEFINED : iter->second,
450 if (!injection->TryToInject( 426 &scripts_run_info);
451 iter == frame_statuses_.end() ? UserScript::UNDEFINED : iter->second,
452 extension_injection_host.get(),
453 &scripts_run_info)) {
454 pending_injections_.push_back(injection.release());
455 }
456 } 427 }
457 428
458 void ScriptInjectionManager::HandleExecuteDeclarativeScript( 429 void ScriptInjectionManager::HandleExecuteDeclarativeScript(
459 blink::WebFrame* web_frame, 430 blink::WebFrame* web_frame,
460 int tab_id, 431 int tab_id,
461 const ExtensionId& extension_id, 432 const ExtensionId& extension_id,
462 int script_id, 433 int script_id,
463 const GURL& url) { 434 const GURL& url) {
464 scoped_ptr<ExtensionInjectionHost> extension_injection_host =
465 GetExtensionInjectionHost(extension_id, extensions_);
466 const Extension* extension = extensions_->GetByID(extension_id); 435 const Extension* extension = extensions_->GetByID(extension_id);
467 // TODO(dcheng): This function signature should really be a WebLocalFrame, 436 // TODO(dcheng): This function signature should really be a WebLocalFrame,
468 // rather than trying to coerce it here. 437 // rather than trying to coerce it here.
469 scoped_ptr<ScriptInjection> injection = 438 scoped_ptr<ScriptInjection> injection =
470 user_script_set_manager_->GetInjectionForDeclarativeScript( 439 user_script_set_manager_->GetInjectionForDeclarativeScript(
471 script_id, 440 script_id,
472 web_frame->toWebLocalFrame(), 441 web_frame->toWebLocalFrame(),
473 tab_id, 442 tab_id,
474 url, 443 url,
475 extension); 444 extension);
476 if (injection.get()) { 445 if (injection.get()) {
477 ScriptsRunInfo scripts_run_info; 446 ScriptsRunInfo scripts_run_info;
447
478 // TODO(markdittmer): Use return value of TryToInject for error handling. 448 // TODO(markdittmer): Use return value of TryToInject for error handling.
479 injection->TryToInject(UserScript::BROWSER_DRIVEN, 449 TryToInject(injection.Pass(),
480 extension_injection_host.get(), 450 UserScript::BROWSER_DRIVEN,
481 &scripts_run_info); 451 &scripts_run_info);
452
482 scripts_run_info.LogRun(web_frame, UserScript::BROWSER_DRIVEN); 453 scripts_run_info.LogRun(web_frame, UserScript::BROWSER_DRIVEN);
483 } 454 }
484 } 455 }
485 456
486 void ScriptInjectionManager::HandlePermitScriptInjection(int64 request_id) { 457 void ScriptInjectionManager::HandlePermitScriptInjection(int64 request_id) {
487 ScopedVector<ScriptInjection>::iterator iter = 458 ScopedVector<ScriptInjection>::iterator iter =
488 pending_injections_.begin(); 459 pending_injections_.begin();
489 for (; iter != pending_injections_.end(); ++iter) { 460 for (; iter != pending_injections_.end(); ++iter) {
490 if ((*iter)->request_id() == request_id) 461 if ((*iter)->request_id() == request_id)
491 break; 462 break;
492 } 463 }
493 if (iter == pending_injections_.end()) 464 if (iter == pending_injections_.end())
494 return; 465 return;
495 466
496 // At this point, because the request is present in pending_injections_, we 467 // 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, 468 // know that this is the same page that issued the request (otherwise,
498 // RVOHelper's DidStartProvisionalLoad callback would have caused it to be 469 // RVOHelper's DidStartProvisionalLoad callback would have caused it to be
499 // cleared out). 470 // cleared out).
500 471
501 scoped_ptr<ScriptInjection> injection(*iter); 472 scoped_ptr<ScriptInjection> injection(*iter);
502 pending_injections_.weak_erase(iter); 473 pending_injections_.weak_erase(iter);
503 474
504 ScriptsRunInfo scripts_run_info; 475 injection->SetScriptInjectionManager(this);
Devlin 2015/03/03 00:34:10 Shouldn't this already be set? (We can't have an
kozy 2015/03/03 13:50:20 Removed.
476
505 scoped_ptr<ExtensionInjectionHost> extension_injection_host = 477 scoped_ptr<ExtensionInjectionHost> extension_injection_host =
506 GetExtensionInjectionHost(injection->host_id().id(), extensions_); 478 GetExtensionInjectionHost(injection->host_id().id(), extensions_);
507 if (injection->OnPermissionGranted(extension_injection_host.get(), 479 ScriptsRunInfo scripts_run_info;
508 &scripts_run_info)) { 480 ScriptInjection::InjectionResult res = injection->OnPermissionGranted(
481 extension_injection_host.get(), &scripts_run_info);
482 if (res == ScriptInjection::INJECTION_BLOCKED) {
Devlin 2015/03/03 00:34:10 nit: no brackets on single-line ifs.
kozy 2015/03/03 13:50:20 Done.
483 running_injections_.push_back(injection.Pass());
484 }
485 if (extension_injection_host.get()) {
509 scripts_run_info.LogRun(injection->web_frame(), UserScript::RUN_DEFERRED); 486 scripts_run_info.LogRun(injection->web_frame(), UserScript::RUN_DEFERRED);
510 } 487 }
511 } 488 }
512 489
513 } // namespace extensions 490 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698