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 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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 Loading... |
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()); |
396 break; | 382 break; |
397 | 383 case ScriptInjection::INJECTION_BLOCKED: |
398 // Try to inject the script if the injection host is not "dirty" | 384 running_injections_.push_back(injection.release()); |
399 // (invalidated by an update). If the injection does not finish | 385 break; |
400 // (i.e., it is waiting for permission), add it to the list of pending | 386 case ScriptInjection::INJECTION_FINISHED: |
401 // injections. | 387 break; |
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 } | 388 } |
411 | |
412 if (IsFrameValid(frame)) | |
413 scripts_run_info.LogRun(frame, run_location); | |
414 | |
415 invalidated_while_injecting_.clear(); | |
416 } | 389 } |
417 | 390 |
418 void ScriptInjectionManager::HandleExecuteCode( | 391 void ScriptInjectionManager::HandleExecuteCode( |
419 const ExtensionMsg_ExecuteCode_Params& params, | 392 const ExtensionMsg_ExecuteCode_Params& params, |
420 content::RenderView* render_view) { | 393 content::RenderView* render_view) { |
421 // TODO(dcheng): Not sure how this can happen today. In an OOPI world, it | 394 // 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 | 395 // would indicate a logic error--the browser must direct this request to the |
423 // right renderer process to begin with. | 396 // right renderer process to begin with. |
424 blink::WebLocalFrame* main_frame = | 397 blink::WebLocalFrame* main_frame = |
425 render_view->GetWebView()->mainFrame()->toWebLocalFrame(); | 398 render_view->GetWebView()->mainFrame()->toWebLocalFrame(); |
(...skipping 17 matching lines...) Expand all Loading... |
443 scoped_ptr<ScriptInjector>( | 416 scoped_ptr<ScriptInjector>( |
444 new ProgrammaticScriptInjector(params, main_frame)), | 417 new ProgrammaticScriptInjector(params, main_frame)), |
445 main_frame, | 418 main_frame, |
446 extension_injection_host.Pass(), | 419 extension_injection_host.Pass(), |
447 static_cast<UserScript::RunLocation>(params.run_at), | 420 static_cast<UserScript::RunLocation>(params.run_at), |
448 ExtensionHelper::Get(render_view)->tab_id())); | 421 ExtensionHelper::Get(render_view)->tab_id())); |
449 | 422 |
450 ScriptsRunInfo scripts_run_info; | 423 ScriptsRunInfo scripts_run_info; |
451 FrameStatusMap::const_iterator iter = frame_statuses_.find(main_frame); | 424 FrameStatusMap::const_iterator iter = frame_statuses_.find(main_frame); |
452 | 425 |
453 if (!injection->TryToInject( | 426 TryToInject( |
454 iter == frame_statuses_.end() ? UserScript::UNDEFINED : iter->second, | 427 injection.Pass(), |
455 &scripts_run_info)) { | 428 iter == frame_statuses_.end() ? UserScript::UNDEFINED : iter->second, |
456 pending_injections_.push_back(injection.release()); | 429 &scripts_run_info); |
457 } | |
458 } | 430 } |
459 | 431 |
460 void ScriptInjectionManager::HandleExecuteDeclarativeScript( | 432 void ScriptInjectionManager::HandleExecuteDeclarativeScript( |
461 blink::WebFrame* web_frame, | 433 blink::WebFrame* web_frame, |
462 int tab_id, | 434 int tab_id, |
463 const ExtensionId& extension_id, | 435 const ExtensionId& extension_id, |
464 int script_id, | 436 int script_id, |
465 const GURL& url) { | 437 const GURL& url) { |
466 // TODO(dcheng): This function signature should really be a WebLocalFrame, | 438 // TODO(dcheng): This function signature should really be a WebLocalFrame, |
467 // rather than trying to coerce it here. | 439 // rather than trying to coerce it here. |
468 scoped_ptr<ScriptInjection> injection = | 440 scoped_ptr<ScriptInjection> injection = |
469 user_script_set_manager_->GetInjectionForDeclarativeScript( | 441 user_script_set_manager_->GetInjectionForDeclarativeScript( |
470 script_id, | 442 script_id, |
471 web_frame->toWebLocalFrame(), | 443 web_frame->toWebLocalFrame(), |
472 tab_id, | 444 tab_id, |
473 url, | 445 url, |
474 extension_id); | 446 extension_id); |
475 if (injection) { | 447 if (injection.get()) { |
476 ScriptsRunInfo scripts_run_info; | 448 ScriptsRunInfo scripts_run_info; |
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 |
OLD | NEW |