| 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 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 220 void ScriptInjectionManager::RVOHelper::InvalidateFrame( | 220 void ScriptInjectionManager::RVOHelper::InvalidateFrame( |
| 221 blink::WebFrame* frame) { | 221 blink::WebFrame* frame) { |
| 222 pending_idle_frames_.erase(frame); | 222 pending_idle_frames_.erase(frame); |
| 223 manager_->InvalidateForFrame(frame); | 223 manager_->InvalidateForFrame(frame); |
| 224 } | 224 } |
| 225 | 225 |
| 226 ScriptInjectionManager::ScriptInjectionManager( | 226 ScriptInjectionManager::ScriptInjectionManager( |
| 227 const ExtensionSet* extensions, | 227 const ExtensionSet* extensions, |
| 228 UserScriptSetManager* user_script_set_manager) | 228 UserScriptSetManager* user_script_set_manager) |
| 229 : extensions_(extensions), | 229 : extensions_(extensions), |
| 230 injecting_scripts_(false), | |
| 231 user_script_set_manager_(user_script_set_manager), | 230 user_script_set_manager_(user_script_set_manager), |
| 232 user_script_set_manager_observer_(this) { | 231 user_script_set_manager_observer_(this) { |
| 233 user_script_set_manager_observer_.Add(user_script_set_manager_); | 232 user_script_set_manager_observer_.Add(user_script_set_manager_); |
| 234 } | 233 } |
| 235 | 234 |
| 236 ScriptInjectionManager::~ScriptInjectionManager() { | 235 ScriptInjectionManager::~ScriptInjectionManager() { |
| 237 } | 236 } |
| 238 | 237 |
| 239 void ScriptInjectionManager::OnRenderViewCreated( | 238 void ScriptInjectionManager::OnRenderViewCreated( |
| 240 content::RenderView* render_view) { | 239 content::RenderView* render_view) { |
| 241 rvo_helpers_.push_back(new RVOHelper(render_view, this)); | 240 rvo_helpers_.push_back(new RVOHelper(render_view, this)); |
| 242 } | 241 } |
| 243 | 242 |
| 243 void ScriptInjectionManager::OnInjectionFinished( |
| 244 ScriptInjection* injection, |
| 245 ScriptsRunInfo* scripts_run_info) { |
| 246 if (IsFrameValid(injection->web_frame())) |
| 247 scripts_run_info->LogRun(injection->web_frame(), injection->run_location()); |
| 248 |
| 249 ScopedVector<ScriptInjection>::iterator it; |
| 250 for (it = running_injections_.begin(); it != running_injections_.end(); ++it){ |
| 251 running_injections_.erase(it); |
| 252 break; |
| 253 } |
| 254 } |
| 255 |
| 244 void ScriptInjectionManager::OnUserScriptsUpdated( | 256 void ScriptInjectionManager::OnUserScriptsUpdated( |
| 245 const std::set<std::string>& changed_extensions, | 257 const std::set<std::string>& changed_extensions, |
| 246 const std::vector<UserScript*>& scripts) { | 258 const std::vector<UserScript*>& scripts) { |
| 247 for (ScopedVector<ScriptInjection>::iterator iter = | 259 for (ScopedVector<ScriptInjection>::iterator iter = |
| 248 pending_injections_.begin(); | 260 pending_injections_.begin(); |
| 249 iter != pending_injections_.end();) { | 261 iter != pending_injections_.end();) { |
| 250 if (changed_extensions.count((*iter)->extension_id()) > 0) | 262 if (changed_extensions.count((*iter)->extension_id()) > 0) |
| 251 iter = pending_injections_.erase(iter); | 263 iter = pending_injections_.erase(iter); |
| 252 else | 264 else |
| 253 ++iter; | 265 ++iter; |
| 254 } | 266 } |
| 255 | |
| 256 // If we are currently injecting scripts, we need to make a note that these | |
| 257 // extensions were updated. | |
| 258 if (injecting_scripts_) { | |
| 259 invalidated_while_injecting_.insert(changed_extensions.begin(), | |
| 260 changed_extensions.end()); | |
| 261 } | |
| 262 } | 267 } |
| 263 | 268 |
| 264 void ScriptInjectionManager::RemoveObserver(RVOHelper* helper) { | 269 void ScriptInjectionManager::RemoveObserver(RVOHelper* helper) { |
| 265 for (ScopedVector<RVOHelper>::iterator iter = rvo_helpers_.begin(); | 270 for (ScopedVector<RVOHelper>::iterator iter = rvo_helpers_.begin(); |
| 266 iter != rvo_helpers_.end(); | 271 iter != rvo_helpers_.end(); |
| 267 ++iter) { | 272 ++iter) { |
| 268 if (*iter == helper) { | 273 if (*iter == helper) { |
| 269 rvo_helpers_.erase(iter); | 274 rvo_helpers_.erase(iter); |
| 270 break; | 275 break; |
| 271 } | 276 } |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 315 // Certain run location signals (like DidCreateDocumentElement) can happen | 320 // Certain run location signals (like DidCreateDocumentElement) can happen |
| 316 // multiple times. Ignore the subsequent signals. | 321 // multiple times. Ignore the subsequent signals. |
| 317 return; | 322 return; |
| 318 } | 323 } |
| 319 | 324 |
| 320 // Otherwise, all is right in the world, and we can get on with the | 325 // Otherwise, all is right in the world, and we can get on with the |
| 321 // injections! | 326 // injections! |
| 322 | 327 |
| 323 frame_statuses_[frame] = run_location; | 328 frame_statuses_[frame] = run_location; |
| 324 | 329 |
| 325 // If a content script injects blocking code (such as a javascript alert()), | 330 InjectScripts(frame, run_location); |
| 326 // then there is a chance that we are running in a nested message loop, and | 331 // As above, we might have been blocked, but that means that, in the mean |
| 327 // shouldn't inject scripts right now (to avoid conflicts). | 332 // time, it's possible the frame advanced. Inject any scripts for run |
| 328 if (!injecting_scripts_) { | 333 // locations that were registered, but never ran. |
| 334 while ((iter = frame_statuses_.find(frame)) != frame_statuses_.end() && |
| 335 iter->second > run_location) { |
| 336 run_location = NextRunLocation(run_location); |
| 337 DCHECK_LE(run_location, UserScript::DOCUMENT_IDLE); |
| 329 InjectScripts(frame, run_location); | 338 InjectScripts(frame, run_location); |
| 330 // As above, we might have been blocked, but that means that, in the mean | |
| 331 // time, it's possible the frame advanced. Inject any scripts for run | |
| 332 // locations that were registered, but never ran. | |
| 333 while ((iter = frame_statuses_.find(frame)) != frame_statuses_.end() && | |
| 334 iter->second > run_location) { | |
| 335 run_location = NextRunLocation(run_location); | |
| 336 DCHECK_LE(run_location, UserScript::DOCUMENT_IDLE); | |
| 337 InjectScripts(frame, run_location); | |
| 338 } | |
| 339 } | 339 } |
| 340 } | 340 } |
| 341 | 341 |
| 342 void ScriptInjectionManager::InjectScripts( | 342 void ScriptInjectionManager::InjectScripts( |
| 343 blink::WebFrame* frame, | 343 blink::WebFrame* frame, |
| 344 UserScript::RunLocation run_location) { | 344 UserScript::RunLocation run_location) { |
| 345 DCHECK(!injecting_scripts_); | |
| 346 DCHECK(invalidated_while_injecting_.empty()); | |
| 347 base::AutoReset<bool>(&injecting_scripts_, true); | |
| 348 | |
| 349 // Find any injections that want to run on the given frame. | 345 // Find any injections that want to run on the given frame. |
| 350 // We create a separate vector for these because there is a chance that | 346 // We create a separate vector for these because there is a chance that |
| 351 // injected scripts can block, which can create a nested message loop. When | 347 // injected scripts can block, which can create a nested message loop. When |
| 352 // this happens, other signals (like IPCs) can cause |pending_injections_| to | 348 // this happens, other signals (like IPCs) can cause |pending_injections_| to |
| 353 // be changed, so we don't want to risk that. | 349 // be changed, so we don't want to risk that. |
| 354 ScopedVector<ScriptInjection> frame_injections; | 350 ScopedVector<ScriptInjection> frame_injections; |
| 355 for (ScopedVector<ScriptInjection>::iterator iter = | 351 for (ScopedVector<ScriptInjection>::iterator iter = |
| 356 pending_injections_.begin(); | 352 pending_injections_.begin(); |
| 357 iter != pending_injections_.end();) { | 353 iter != pending_injections_.end();) { |
| 358 if ((*iter)->web_frame() == frame) { | 354 if ((*iter)->web_frame() == frame) { |
| 359 frame_injections.push_back(*iter); | 355 frame_injections.push_back(*iter); |
| 360 iter = pending_injections_.weak_erase(iter); | 356 iter = pending_injections_.weak_erase(iter); |
| 361 } else { | 357 } else { |
| 362 ++iter; | 358 ++iter; |
| 363 } | 359 } |
| 364 } | 360 } |
| 365 | 361 |
| 366 // Add any injections for user scripts. | 362 // Add any injections for user scripts. |
| 367 int tab_id = ExtensionHelper::Get(content::RenderView::FromWebView( | 363 int tab_id = ExtensionHelper::Get(content::RenderView::FromWebView( |
| 368 frame->top()->view()))->tab_id(); | 364 frame->top()->view()))->tab_id(); |
| 369 user_script_set_manager_->GetAllInjections( | 365 user_script_set_manager_->GetAllInjections( |
| 370 &frame_injections, frame, tab_id, run_location); | 366 &frame_injections, frame, tab_id, run_location); |
| 371 | 367 |
| 372 ScriptsRunInfo scripts_run_info; | |
| 373 for (ScopedVector<ScriptInjection>::iterator iter = frame_injections.begin(); | 368 for (ScopedVector<ScriptInjection>::iterator iter = frame_injections.begin(); |
| 374 iter != frame_injections.end();) { | 369 iter != frame_injections.end();) { |
| 375 // If a blocking script was injected, there is potentially a possibility | 370 // If a blocking script was injected, there is potentially a possibility |
| 376 // that the frame has been invalidated in the time since. Check. | 371 // that the frame has been invalidated in the time since. Check. |
| 377 if (!IsFrameValid(frame)) | 372 if (!IsFrameValid(frame)) |
| 378 break; | 373 break; |
| 379 | 374 |
| 380 // Try to inject the script if the extension is not "dirty" (invalidated by | 375 // Try to inject the script. If the injection does not started |
| 381 // an update). If the injection does not finish (i.e., it is waiting for | 376 // (i.e., it is waiting for permission), add it to the list of pending |
| 382 // permission), add it to the list of pending injections. | 377 // injections. If the injection does not finished, add it to the list of |
| 383 if (invalidated_while_injecting_.count((*iter)->extension_id()) == 0 && | 378 // running injections. |
| 384 !(*iter)->TryToInject(run_location, | 379 ScriptInjection* injection = *iter; |
| 385 extensions_->GetByID((*iter)->extension_id()), | 380 injection->SetScriptInjectionManager(this); |
| 386 &scripts_run_info)) { | 381 if (!injection->TryToInject(run_location, |
| 382 extensions_->GetByID((*iter)->extension_id()))) { |
| 387 pending_injections_.insert(pending_injections_.begin(), *iter); | 383 pending_injections_.insert(pending_injections_.begin(), *iter); |
| 388 iter = frame_injections.weak_erase(iter); | 384 iter = frame_injections.weak_erase(iter); |
| 385 } else if (!injection->is_complete()) { |
| 386 running_injections_.insert(running_injections_.begin(), *iter); |
| 387 iter = frame_injections.weak_erase(iter); |
| 389 } else { | 388 } else { |
| 390 ++iter; | 389 ++iter; |
| 391 } | 390 } |
| 392 } | 391 } |
| 393 | |
| 394 if (IsFrameValid(frame)) | |
| 395 scripts_run_info.LogRun(frame, run_location); | |
| 396 | |
| 397 invalidated_while_injecting_.clear(); | |
| 398 } | 392 } |
| 399 | 393 |
| 400 void ScriptInjectionManager::HandleExecuteCode( | 394 void ScriptInjectionManager::HandleExecuteCode( |
| 401 const ExtensionMsg_ExecuteCode_Params& params, | 395 const ExtensionMsg_ExecuteCode_Params& params, |
| 402 content::RenderView* render_view) { | 396 content::RenderView* render_view) { |
| 403 // 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 |
| 404 // 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 |
| 405 // right renderer process to begin with. | 399 // right renderer process to begin with. |
| 406 blink::WebLocalFrame* main_frame = | 400 blink::WebLocalFrame* main_frame = |
| 407 render_view->GetWebView()->mainFrame()->toWebLocalFrame(); | 401 render_view->GetWebView()->mainFrame()->toWebLocalFrame(); |
| 408 if (!main_frame) { | 402 if (!main_frame) { |
| 409 render_view->Send( | 403 render_view->Send( |
| 410 new ExtensionHostMsg_ExecuteCodeFinished(render_view->GetRoutingID(), | 404 new ExtensionHostMsg_ExecuteCodeFinished(render_view->GetRoutingID(), |
| 411 params.request_id, | 405 params.request_id, |
| 412 "No main frame", | 406 "No main frame", |
| 413 GURL(std::string()), | 407 GURL(std::string()), |
| 414 base::ListValue())); | 408 base::ListValue())); |
| 415 return; | 409 return; |
| 416 } | 410 } |
| 417 | 411 |
| 418 scoped_ptr<ScriptInjection> injection(new ScriptInjection( | 412 scoped_ptr<ScriptInjection> injection(new ScriptInjection( |
| 419 scoped_ptr<ScriptInjector>( | 413 scoped_ptr<ScriptInjector>( |
| 420 new ProgrammaticScriptInjector(params, main_frame)), | 414 new ProgrammaticScriptInjector(params, main_frame)), |
| 421 main_frame, | 415 main_frame, |
| 422 params.extension_id, | 416 params.extension_id, |
| 423 static_cast<UserScript::RunLocation>(params.run_at), | 417 static_cast<UserScript::RunLocation>(params.run_at), |
| 424 ExtensionHelper::Get(render_view)->tab_id())); | 418 ExtensionHelper::Get(render_view)->tab_id())); |
| 425 | 419 |
| 426 ScriptsRunInfo scripts_run_info; | |
| 427 FrameStatusMap::const_iterator iter = frame_statuses_.find(main_frame); | 420 FrameStatusMap::const_iterator iter = frame_statuses_.find(main_frame); |
| 421 injection->SetScriptInjectionManager(this); |
| 428 if (!injection->TryToInject( | 422 if (!injection->TryToInject( |
| 429 iter == frame_statuses_.end() ? UserScript::UNDEFINED : iter->second, | 423 iter == frame_statuses_.end() ? UserScript::UNDEFINED : iter->second, |
| 430 extensions_->GetByID(injection->extension_id()), | 424 extensions_->GetByID(injection->extension_id()))) { |
| 431 &scripts_run_info)) { | |
| 432 pending_injections_.push_back(injection.release()); | 425 pending_injections_.push_back(injection.release()); |
| 426 } else if (!injection->is_complete()) { |
| 427 running_injections_.push_back(injection.release()); |
| 433 } | 428 } |
| 434 } | 429 } |
| 435 | 430 |
| 436 void ScriptInjectionManager::HandleExecuteDeclarativeScript( | 431 void ScriptInjectionManager::HandleExecuteDeclarativeScript( |
| 437 blink::WebFrame* web_frame, | 432 blink::WebFrame* web_frame, |
| 438 int tab_id, | 433 int tab_id, |
| 439 const ExtensionId& extension_id, | 434 const ExtensionId& extension_id, |
| 440 int script_id, | 435 int script_id, |
| 441 const GURL& url) { | 436 const GURL& url) { |
| 442 const Extension* extension = extensions_->GetByID(extension_id); | 437 const Extension* extension = extensions_->GetByID(extension_id); |
| 443 // TODO(dcheng): This function signature should really be a WebLocalFrame, | 438 // TODO(dcheng): This function signature should really be a WebLocalFrame, |
| 444 // rather than trying to coerce it here. | 439 // rather than trying to coerce it here. |
| 445 scoped_ptr<ScriptInjection> injection = | 440 scoped_ptr<ScriptInjection> injection = |
| 446 user_script_set_manager_->GetInjectionForDeclarativeScript( | 441 user_script_set_manager_->GetInjectionForDeclarativeScript( |
| 447 script_id, | 442 script_id, |
| 448 web_frame->toWebLocalFrame(), | 443 web_frame->toWebLocalFrame(), |
| 449 tab_id, | 444 tab_id, |
| 450 url, | 445 url, |
| 451 extension); | 446 extension); |
| 452 if (injection.get()) { | 447 if (injection.get()) { |
| 453 ScriptsRunInfo scripts_run_info; | 448 injection->SetScriptInjectionManager(this); |
| 454 // TODO(markdittmer): Use return value of TryToInject for error handling. | 449 // TODO(markdittmer): Use return value of TryToInject for error handling. |
| 455 injection->TryToInject(UserScript::BROWSER_DRIVEN, | 450 if (injection->TryToInject(UserScript::BROWSER_DRIVEN, |
| 456 extension, | 451 extension)) { |
| 457 &scripts_run_info); | 452 if (!injection->is_complete()) |
| 458 scripts_run_info.LogRun(web_frame, UserScript::BROWSER_DRIVEN); | 453 running_injections_.push_back(injection.release()); |
| 454 } |
| 459 } | 455 } |
| 460 } | 456 } |
| 461 | 457 |
| 462 void ScriptInjectionManager::HandlePermitScriptInjection(int64 request_id) { | 458 void ScriptInjectionManager::HandlePermitScriptInjection(int64 request_id) { |
| 463 ScopedVector<ScriptInjection>::iterator iter = | 459 ScopedVector<ScriptInjection>::iterator iter = |
| 464 pending_injections_.begin(); | 460 pending_injections_.begin(); |
| 465 for (; iter != pending_injections_.end(); ++iter) { | 461 for (; iter != pending_injections_.end(); ++iter) { |
| 466 if ((*iter)->request_id() == request_id) | 462 if ((*iter)->request_id() == request_id) |
| 467 break; | 463 break; |
| 468 } | 464 } |
| 469 if (iter == pending_injections_.end()) | 465 if (iter == pending_injections_.end()) |
| 470 return; | 466 return; |
| 471 | 467 |
| 472 // At this point, because the request is present in pending_injections_, we | 468 // At this point, because the request is present in pending_injections_, we |
| 473 // know that this is the same page that issued the request (otherwise, | 469 // know that this is the same page that issued the request (otherwise, |
| 474 // RVOHelper's DidStartProvisionalLoad callback would have caused it to be | 470 // RVOHelper's DidStartProvisionalLoad callback would have caused it to be |
| 475 // cleared out). | 471 // cleared out). |
| 476 | 472 |
| 477 scoped_ptr<ScriptInjection> injection(*iter); | 473 scoped_ptr<ScriptInjection> injection(*iter); |
| 478 pending_injections_.weak_erase(iter); | 474 pending_injections_.weak_erase(iter); |
| 479 | 475 |
| 480 ScriptsRunInfo scripts_run_info; | |
| 481 if (injection->OnPermissionGranted(extensions_->GetByID( | 476 if (injection->OnPermissionGranted(extensions_->GetByID( |
| 482 injection->extension_id()), | 477 injection->extension_id()))) { |
| 483 &scripts_run_info)) { | 478 if (!injection->is_complete()) |
| 484 scripts_run_info.LogRun(injection->web_frame(), UserScript::RUN_DEFERRED); | 479 running_injections_.push_back(injection.Pass()); |
| 485 } | 480 } |
| 486 } | 481 } |
| 487 | 482 |
| 488 } // namespace extensions | 483 } // namespace extensions |
| OLD | NEW |