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

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

Powered by Google App Engine
This is Rietveld 408576698