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 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
83 // document_idle. | 83 // document_idle. |
84 void RunIdle(); | 84 void RunIdle(); |
85 | 85 |
86 // Indicate that the frame is no longer valid because it is starting | 86 // Indicate that the frame is no longer valid because it is starting |
87 // a new load or closing. | 87 // a new load or closing. |
88 void InvalidateFrame(); | 88 void InvalidateFrame(); |
89 | 89 |
90 // The owning ScriptInjectionManager. | 90 // The owning ScriptInjectionManager. |
91 ScriptInjectionManager* manager_; | 91 ScriptInjectionManager* manager_; |
92 | 92 |
93 // The set of frames that we are about to notify for DOCUMENT_IDLE. We keep | 93 bool should_run_idle_; |
94 // a set of those that are valid, so we don't notify that an invalid frame | |
95 // became idle. | |
96 std::set<content::RenderFrame*> pending_idle_frames_; | |
97 | 94 |
98 base::WeakPtrFactory<RFOHelper> weak_factory_; | 95 base::WeakPtrFactory<RFOHelper> weak_factory_; |
99 }; | 96 }; |
100 | 97 |
101 ScriptInjectionManager::RFOHelper::RFOHelper( | 98 ScriptInjectionManager::RFOHelper::RFOHelper(content::RenderFrame* render_frame, |
102 content::RenderFrame* render_frame, | 99 ScriptInjectionManager* manager) |
103 ScriptInjectionManager* manager) | |
104 : content::RenderFrameObserver(render_frame), | 100 : content::RenderFrameObserver(render_frame), |
105 manager_(manager), | 101 manager_(manager), |
102 should_run_idle_(true), | |
106 weak_factory_(this) { | 103 weak_factory_(this) { |
107 } | 104 } |
108 | 105 |
109 ScriptInjectionManager::RFOHelper::~RFOHelper() { | 106 ScriptInjectionManager::RFOHelper::~RFOHelper() { |
110 } | 107 } |
111 | 108 |
112 bool ScriptInjectionManager::RFOHelper::OnMessageReceived( | 109 bool ScriptInjectionManager::RFOHelper::OnMessageReceived( |
113 const IPC::Message& message) { | 110 const IPC::Message& message) { |
114 bool handled = true; | 111 bool handled = true; |
115 IPC_BEGIN_MESSAGE_MAP(ScriptInjectionManager::RFOHelper, message) | 112 IPC_BEGIN_MESSAGE_MAP(ScriptInjectionManager::RFOHelper, message) |
(...skipping 16 matching lines...) Expand all Loading... | |
132 if (manager_->frame_statuses_.count(render_frame()) != 0) | 129 if (manager_->frame_statuses_.count(render_frame()) != 0) |
133 InvalidateFrame(); | 130 InvalidateFrame(); |
134 } | 131 } |
135 | 132 |
136 void ScriptInjectionManager::RFOHelper::DidCreateDocumentElement() { | 133 void ScriptInjectionManager::RFOHelper::DidCreateDocumentElement() { |
137 manager_->StartInjectScripts(render_frame(), UserScript::DOCUMENT_START); | 134 manager_->StartInjectScripts(render_frame(), UserScript::DOCUMENT_START); |
138 } | 135 } |
139 | 136 |
140 void ScriptInjectionManager::RFOHelper::DidFinishDocumentLoad() { | 137 void ScriptInjectionManager::RFOHelper::DidFinishDocumentLoad() { |
141 manager_->StartInjectScripts(render_frame(), UserScript::DOCUMENT_END); | 138 manager_->StartInjectScripts(render_frame(), UserScript::DOCUMENT_END); |
142 pending_idle_frames_.insert(render_frame()); | |
143 // We try to run idle in two places: here and DidFinishLoad. | 139 // We try to run idle in two places: here and DidFinishLoad. |
144 // DidFinishDocumentLoad() corresponds to completing the document's load, | 140 // DidFinishDocumentLoad() corresponds to completing the document's load, |
145 // whereas DidFinishLoad corresponds to completing the document and all | 141 // whereas DidFinishLoad corresponds to completing the document and all |
146 // subresources' load. We don't want to hold up script injection for a | 142 // subresources' load. We don't want to hold up script injection for a |
147 // particularly slow subresource, so we set a delayed task from here - but if | 143 // particularly slow subresource, so we set a delayed task from here - but if |
148 // we finish everything before that point (i.e., DidFinishLoad() is | 144 // we finish everything before that point (i.e., DidFinishLoad() is |
149 // triggered), then there's no reason to keep waiting. | 145 // triggered), then there's no reason to keep waiting. |
150 content::RenderThread::Get()->GetTaskRunner()->PostDelayedTask( | 146 content::RenderThread::Get()->GetTaskRunner()->PostDelayedTask( |
151 FROM_HERE, | 147 FROM_HERE, |
152 base::Bind(&ScriptInjectionManager::RFOHelper::RunIdle, | 148 base::Bind(&ScriptInjectionManager::RFOHelper::RunIdle, |
153 weak_factory_.GetWeakPtr()), | 149 weak_factory_.GetWeakPtr()), |
154 base::TimeDelta::FromMilliseconds(kScriptIdleTimeoutInMs)); | 150 base::TimeDelta::FromMilliseconds(kScriptIdleTimeoutInMs)); |
155 } | 151 } |
156 | 152 |
157 void ScriptInjectionManager::RFOHelper::DidFinishLoad() { | 153 void ScriptInjectionManager::RFOHelper::DidFinishLoad() { |
158 // Ensure that we don't block any UI progress by running scripts. | 154 // Ensure that we don't block any UI progress by running scripts. |
159 // We *don't* add the frame to |pending_idle_frames_| here because | |
160 // DidFinishDocumentLoad should strictly come before DidFinishLoad, so the | |
161 // first posted task to RunIdle() pops it out of the set. This ensures we | |
162 // don't try to run idle twice. | |
163 content::RenderThread::Get()->GetTaskRunner()->PostTask( | 155 content::RenderThread::Get()->GetTaskRunner()->PostTask( |
164 FROM_HERE, | 156 FROM_HERE, |
165 base::Bind(&ScriptInjectionManager::RFOHelper::RunIdle, | 157 base::Bind(&ScriptInjectionManager::RFOHelper::RunIdle, |
166 weak_factory_.GetWeakPtr())); | 158 weak_factory_.GetWeakPtr())); |
167 } | 159 } |
168 | 160 |
169 void ScriptInjectionManager::RFOHelper::FrameDetached() { | 161 void ScriptInjectionManager::RFOHelper::FrameDetached() { |
170 // The frame is closing - invalidate. | 162 // The frame is closing - invalidate. |
171 InvalidateFrame(); | 163 InvalidateFrame(); |
172 } | 164 } |
(...skipping 25 matching lines...) Expand all Loading... | |
198 } | 190 } |
199 | 191 |
200 void ScriptInjectionManager::RFOHelper::OnPermitScriptInjection( | 192 void ScriptInjectionManager::RFOHelper::OnPermitScriptInjection( |
201 int64 request_id) { | 193 int64 request_id) { |
202 manager_->HandlePermitScriptInjection(request_id); | 194 manager_->HandlePermitScriptInjection(request_id); |
203 } | 195 } |
204 | 196 |
205 void ScriptInjectionManager::RFOHelper::RunIdle() { | 197 void ScriptInjectionManager::RFOHelper::RunIdle() { |
206 // Only notify the manager if the frame hasn't either been removed or already | 198 // Only notify the manager if the frame hasn't either been removed or already |
207 // had idle run since the task to RunIdle() was posted. | 199 // had idle run since the task to RunIdle() was posted. |
208 if (pending_idle_frames_.count(render_frame()) > 0) { | 200 if (should_run_idle_) { |
201 should_run_idle_ = false; | |
209 manager_->StartInjectScripts(render_frame(), UserScript::DOCUMENT_IDLE); | 202 manager_->StartInjectScripts(render_frame(), UserScript::DOCUMENT_IDLE); |
210 pending_idle_frames_.erase(render_frame()); | |
211 } | 203 } |
212 } | 204 } |
213 | 205 |
214 void ScriptInjectionManager::RFOHelper::InvalidateFrame() { | 206 void ScriptInjectionManager::RFOHelper::InvalidateFrame() { |
not at google - send to devlin
2015/06/29 20:09:07
Could you rename this "ResetFrame"? Then you would
Devlin
2015/06/29 20:43:13
Made it InvalidateAndResetFrame() (I like the inva
| |
215 pending_idle_frames_.erase(render_frame()); | 207 // Invalidate any pending idle injections, and reset the frame inject on idle. |
208 weak_factory_.InvalidateWeakPtrs(); | |
209 // We reset to inject on idle, because the frame can be reused (in the case of | |
210 // navigation). | |
211 should_run_idle_ = true; | |
216 manager_->InvalidateForFrame(render_frame()); | 212 manager_->InvalidateForFrame(render_frame()); |
217 } | 213 } |
218 | 214 |
219 ScriptInjectionManager::ScriptInjectionManager( | 215 ScriptInjectionManager::ScriptInjectionManager( |
220 const ExtensionSet* extensions, | 216 const ExtensionSet* extensions, |
221 UserScriptSetManager* user_script_set_manager) | 217 UserScriptSetManager* user_script_set_manager) |
222 : extensions_(extensions), | 218 : extensions_(extensions), |
219 valid_running_injection_frame_(nullptr), | |
223 user_script_set_manager_(user_script_set_manager), | 220 user_script_set_manager_(user_script_set_manager), |
224 user_script_set_manager_observer_(this) { | 221 user_script_set_manager_observer_(this) { |
225 user_script_set_manager_observer_.Add(user_script_set_manager_); | 222 user_script_set_manager_observer_.Add(user_script_set_manager_); |
226 } | 223 } |
227 | 224 |
228 ScriptInjectionManager::~ScriptInjectionManager() { | 225 ScriptInjectionManager::~ScriptInjectionManager() { |
229 } | 226 } |
230 | 227 |
231 void ScriptInjectionManager::OnRenderFrameCreated( | 228 void ScriptInjectionManager::OnRenderFrameCreated( |
232 content::RenderFrame* render_frame) { | 229 content::RenderFrame* render_frame) { |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
274 iter != rfo_helpers_.end(); | 271 iter != rfo_helpers_.end(); |
275 ++iter) { | 272 ++iter) { |
276 if (*iter == helper) { | 273 if (*iter == helper) { |
277 rfo_helpers_.erase(iter); | 274 rfo_helpers_.erase(iter); |
278 break; | 275 break; |
279 } | 276 } |
280 } | 277 } |
281 } | 278 } |
282 | 279 |
283 void ScriptInjectionManager::InvalidateForFrame(content::RenderFrame* frame) { | 280 void ScriptInjectionManager::InvalidateForFrame(content::RenderFrame* frame) { |
281 // If the frame invalidated is the frame being injected into, we need to | |
282 // note it. | |
283 if (frame == valid_running_injection_frame_) | |
284 valid_running_injection_frame_ = nullptr; | |
285 | |
284 for (ScopedVector<ScriptInjection>::iterator iter = | 286 for (ScopedVector<ScriptInjection>::iterator iter = |
285 pending_injections_.begin(); | 287 pending_injections_.begin(); |
286 iter != pending_injections_.end();) { | 288 iter != pending_injections_.end();) { |
287 if ((*iter)->render_frame() == frame) | 289 if ((*iter)->render_frame() == frame) |
288 iter = pending_injections_.erase(iter); | 290 iter = pending_injections_.erase(iter); |
289 else | 291 else |
290 ++iter; | 292 ++iter; |
291 } | 293 } |
292 | 294 |
293 frame_statuses_.erase(frame); | 295 frame_statuses_.erase(frame); |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
342 } else { | 344 } else { |
343 ++iter; | 345 ++iter; |
344 } | 346 } |
345 } | 347 } |
346 | 348 |
347 // Add any injections for user scripts. | 349 // Add any injections for user scripts. |
348 int tab_id = ExtensionFrameHelper::Get(frame)->tab_id(); | 350 int tab_id = ExtensionFrameHelper::Get(frame)->tab_id(); |
349 user_script_set_manager_->GetAllInjections( | 351 user_script_set_manager_->GetAllInjections( |
350 &frame_injections, frame, tab_id, run_location); | 352 &frame_injections, frame, tab_id, run_location); |
351 | 353 |
352 ScriptsRunInfo scripts_run_info; | 354 // Note that we are running in |frame|. We should only ever be running in a |
355 // single frame at a time. | |
356 DCHECK(!valid_running_injection_frame_); | |
357 valid_running_injection_frame_ = frame; | |
358 | |
359 ScriptsRunInfo scripts_run_info(frame, run_location); | |
353 std::vector<ScriptInjection*> released_injections; | 360 std::vector<ScriptInjection*> released_injections; |
354 frame_injections.release(&released_injections); | 361 frame_injections.release(&released_injections); |
355 for (ScriptInjection* injection : released_injections) | 362 for (ScriptInjection* injection : released_injections) { |
363 // It's possible for the frame to be invalidated in the course of injection | |
364 // (if a script removes its own frame, for example). If this happens, abort. | |
365 if (!valid_running_injection_frame_) | |
366 break; | |
356 TryToInject(make_scoped_ptr(injection), run_location, &scripts_run_info); | 367 TryToInject(make_scoped_ptr(injection), run_location, &scripts_run_info); |
368 } | |
357 | 369 |
358 scripts_run_info.LogRun(frame->GetWebFrame(), run_location); | 370 // We are done running in the frame. |
371 valid_running_injection_frame_ = nullptr; | |
372 | |
373 scripts_run_info.LogRun(); | |
359 } | 374 } |
360 | 375 |
361 void ScriptInjectionManager::TryToInject( | 376 void ScriptInjectionManager::TryToInject( |
362 scoped_ptr<ScriptInjection> injection, | 377 scoped_ptr<ScriptInjection> injection, |
363 UserScript::RunLocation run_location, | 378 UserScript::RunLocation run_location, |
364 ScriptsRunInfo* scripts_run_info) { | 379 ScriptsRunInfo* scripts_run_info) { |
365 // Try to inject the script. If the injection is waiting (i.e., for | 380 // Try to inject the script. If the injection is waiting (i.e., for |
366 // permission), add it to the list of pending injections. If the injection | 381 // permission), add it to the list of pending injections. If the injection |
367 // has blocked, add it to the list of running injections. | 382 // has blocked, add it to the list of running injections. |
368 // The Unretained below is safe because this object owns all the | 383 // The Unretained below is safe because this object owns all the |
(...skipping 29 matching lines...) Expand all Loading... | |
398 } | 413 } |
399 | 414 |
400 scoped_ptr<ScriptInjection> injection(new ScriptInjection( | 415 scoped_ptr<ScriptInjection> injection(new ScriptInjection( |
401 scoped_ptr<ScriptInjector>( | 416 scoped_ptr<ScriptInjector>( |
402 new ProgrammaticScriptInjector(params, render_frame)), | 417 new ProgrammaticScriptInjector(params, render_frame)), |
403 render_frame, | 418 render_frame, |
404 injection_host.Pass(), | 419 injection_host.Pass(), |
405 static_cast<UserScript::RunLocation>(params.run_at), | 420 static_cast<UserScript::RunLocation>(params.run_at), |
406 ExtensionFrameHelper::Get(render_frame)->tab_id())); | 421 ExtensionFrameHelper::Get(render_frame)->tab_id())); |
407 | 422 |
408 ScriptsRunInfo scripts_run_info; | |
409 FrameStatusMap::const_iterator iter = frame_statuses_.find(render_frame); | 423 FrameStatusMap::const_iterator iter = frame_statuses_.find(render_frame); |
424 UserScript::RunLocation run_location = | |
425 iter == frame_statuses_.end() ? UserScript::UNDEFINED : iter->second; | |
410 | 426 |
411 TryToInject( | 427 ScriptsRunInfo scripts_run_info(render_frame, run_location); |
412 injection.Pass(), | 428 TryToInject(injection.Pass(), run_location, &scripts_run_info); |
413 iter == frame_statuses_.end() ? UserScript::UNDEFINED : iter->second, | |
414 &scripts_run_info); | |
415 } | 429 } |
416 | 430 |
417 void ScriptInjectionManager::HandleExecuteDeclarativeScript( | 431 void ScriptInjectionManager::HandleExecuteDeclarativeScript( |
418 content::RenderFrame* render_frame, | 432 content::RenderFrame* render_frame, |
419 int tab_id, | 433 int tab_id, |
420 const ExtensionId& extension_id, | 434 const ExtensionId& extension_id, |
421 int script_id, | 435 int script_id, |
422 const GURL& url) { | 436 const GURL& url) { |
423 scoped_ptr<ScriptInjection> injection = | 437 scoped_ptr<ScriptInjection> injection = |
424 user_script_set_manager_->GetInjectionForDeclarativeScript( | 438 user_script_set_manager_->GetInjectionForDeclarativeScript( |
425 script_id, | 439 script_id, |
426 render_frame, | 440 render_frame, |
427 tab_id, | 441 tab_id, |
428 url, | 442 url, |
429 extension_id); | 443 extension_id); |
430 if (injection.get()) { | 444 if (injection.get()) { |
431 ScriptsRunInfo scripts_run_info; | 445 ScriptsRunInfo scripts_run_info(render_frame, UserScript::BROWSER_DRIVEN); |
432 // TODO(markdittmer): Use return value of TryToInject for error handling. | 446 // TODO(markdittmer): Use return value of TryToInject for error handling. |
433 TryToInject(injection.Pass(), | 447 TryToInject(injection.Pass(), |
434 UserScript::BROWSER_DRIVEN, | 448 UserScript::BROWSER_DRIVEN, |
435 &scripts_run_info); | 449 &scripts_run_info); |
436 | 450 |
437 scripts_run_info.LogRun(render_frame->GetWebFrame(), | 451 scripts_run_info.LogRun(); |
438 UserScript::BROWSER_DRIVEN); | |
439 } | 452 } |
440 } | 453 } |
441 | 454 |
442 void ScriptInjectionManager::HandlePermitScriptInjection(int64 request_id) { | 455 void ScriptInjectionManager::HandlePermitScriptInjection(int64 request_id) { |
443 ScopedVector<ScriptInjection>::iterator iter = | 456 ScopedVector<ScriptInjection>::iterator iter = |
444 pending_injections_.begin(); | 457 pending_injections_.begin(); |
445 for (; iter != pending_injections_.end(); ++iter) { | 458 for (; iter != pending_injections_.end(); ++iter) { |
446 if ((*iter)->request_id() == request_id) { | 459 if ((*iter)->request_id() == request_id) { |
447 DCHECK((*iter)->host_id().type() == HostID::EXTENSIONS); | 460 DCHECK((*iter)->host_id().type() == HostID::EXTENSIONS); |
448 break; | 461 break; |
449 } | 462 } |
450 } | 463 } |
451 if (iter == pending_injections_.end()) | 464 if (iter == pending_injections_.end()) |
452 return; | 465 return; |
453 | 466 |
454 // 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 |
455 // 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, |
456 // RFOHelper's DidStartProvisionalLoad callback would have caused it to be | 469 // RFOHelper's DidStartProvisionalLoad callback would have caused it to be |
457 // cleared out). | 470 // cleared out). |
458 | 471 |
459 scoped_ptr<ScriptInjection> injection(*iter); | 472 scoped_ptr<ScriptInjection> injection(*iter); |
460 pending_injections_.weak_erase(iter); | 473 pending_injections_.weak_erase(iter); |
461 | 474 |
462 ScriptsRunInfo scripts_run_info; | 475 ScriptsRunInfo scripts_run_info(injection->render_frame(), |
476 UserScript::RUN_DEFERRED); | |
463 ScriptInjection::InjectionResult res = injection->OnPermissionGranted( | 477 ScriptInjection::InjectionResult res = injection->OnPermissionGranted( |
464 &scripts_run_info); | 478 &scripts_run_info); |
465 if (res == ScriptInjection::INJECTION_BLOCKED) | 479 if (res == ScriptInjection::INJECTION_BLOCKED) |
466 running_injections_.push_back(injection.Pass()); | 480 running_injections_.push_back(injection.Pass()); |
467 scripts_run_info.LogRun(injection->render_frame()->GetWebFrame(), | 481 scripts_run_info.LogRun(); |
468 UserScript::RUN_DEFERRED); | |
469 } | 482 } |
470 | 483 |
471 } // namespace extensions | 484 } // namespace extensions |
OLD | NEW |