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 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
78 int script_id, | 78 int script_id, |
79 const GURL& url); | 79 const GURL& url); |
80 virtual void OnPermitScriptInjection(int64 request_id); | 80 virtual void OnPermitScriptInjection(int64 request_id); |
81 | 81 |
82 // Tells the ScriptInjectionManager to run tasks associated with | 82 // Tells the ScriptInjectionManager to run tasks associated with |
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 InvalidateAndResetFrame(); |
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) |
116 IPC_MESSAGE_HANDLER(ExtensionMsg_ExecuteCode, OnExecuteCode) | 113 IPC_MESSAGE_HANDLER(ExtensionMsg_ExecuteCode, OnExecuteCode) |
117 IPC_MESSAGE_HANDLER(ExtensionMsg_PermitScriptInjection, | 114 IPC_MESSAGE_HANDLER(ExtensionMsg_PermitScriptInjection, |
118 OnPermitScriptInjection) | 115 OnPermitScriptInjection) |
119 IPC_MESSAGE_HANDLER(ExtensionMsg_ExecuteDeclarativeScript, | 116 IPC_MESSAGE_HANDLER(ExtensionMsg_ExecuteDeclarativeScript, |
120 OnExecuteDeclarativeScript) | 117 OnExecuteDeclarativeScript) |
121 IPC_MESSAGE_UNHANDLED(handled = false) | 118 IPC_MESSAGE_UNHANDLED(handled = false) |
122 IPC_END_MESSAGE_MAP() | 119 IPC_END_MESSAGE_MAP() |
123 return handled; | 120 return handled; |
124 } | 121 } |
125 | 122 |
126 void ScriptInjectionManager::RFOHelper::DidCreateNewDocument() { | 123 void ScriptInjectionManager::RFOHelper::DidCreateNewDocument() { |
127 // A new document is going to be shown, so invalidate the old document state. | 124 // A new document is going to be shown, so invalidate the old document state. |
128 // Check that the frame's state is known before invalidating the frame, | 125 // Check that the frame's state is known before invalidating the frame, |
129 // because it is possible that a script injection was scheduled before the | 126 // because it is possible that a script injection was scheduled before the |
130 // page was loaded, e.g. by navigating to a javascript: URL before the page | 127 // page was loaded, e.g. by navigating to a javascript: URL before the page |
131 // has loaded. | 128 // has loaded. |
132 if (manager_->frame_statuses_.count(render_frame()) != 0) | 129 if (manager_->frame_statuses_.count(render_frame()) != 0) |
133 InvalidateFrame(); | 130 InvalidateAndResetFrame(); |
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 InvalidateAndResetFrame(); |
172 } | 164 } |
173 | 165 |
174 void ScriptInjectionManager::RFOHelper::OnDestruct() { | 166 void ScriptInjectionManager::RFOHelper::OnDestruct() { |
175 manager_->RemoveObserver(this); | 167 manager_->RemoveObserver(this); |
176 } | 168 } |
177 | 169 |
178 void ScriptInjectionManager::RFOHelper::OnExecuteCode( | 170 void ScriptInjectionManager::RFOHelper::OnExecuteCode( |
179 const ExtensionMsg_ExecuteCode_Params& params) { | 171 const ExtensionMsg_ExecuteCode_Params& params) { |
180 manager_->HandleExecuteCode(params, render_frame()); | 172 manager_->HandleExecuteCode(params, render_frame()); |
181 } | 173 } |
(...skipping 16 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::InvalidateAndResetFrame() { |
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), |
223 user_script_set_manager_(user_script_set_manager), | 219 user_script_set_manager_(user_script_set_manager), |
224 user_script_set_manager_observer_(this) { | 220 user_script_set_manager_observer_(this) { |
225 user_script_set_manager_observer_.Add(user_script_set_manager_); | 221 user_script_set_manager_observer_.Add(user_script_set_manager_); |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
274 iter != rfo_helpers_.end(); | 270 iter != rfo_helpers_.end(); |
275 ++iter) { | 271 ++iter) { |
276 if (*iter == helper) { | 272 if (*iter == helper) { |
277 rfo_helpers_.erase(iter); | 273 rfo_helpers_.erase(iter); |
278 break; | 274 break; |
279 } | 275 } |
280 } | 276 } |
281 } | 277 } |
282 | 278 |
283 void ScriptInjectionManager::InvalidateForFrame(content::RenderFrame* frame) { | 279 void ScriptInjectionManager::InvalidateForFrame(content::RenderFrame* frame) { |
| 280 // If the frame invalidated is the frame being injected into, we need to |
| 281 // note it. |
| 282 active_injection_frames_.erase(frame); |
| 283 |
284 for (ScopedVector<ScriptInjection>::iterator iter = | 284 for (ScopedVector<ScriptInjection>::iterator iter = |
285 pending_injections_.begin(); | 285 pending_injections_.begin(); |
286 iter != pending_injections_.end();) { | 286 iter != pending_injections_.end();) { |
287 if ((*iter)->render_frame() == frame) | 287 if ((*iter)->render_frame() == frame) |
288 iter = pending_injections_.erase(iter); | 288 iter = pending_injections_.erase(iter); |
289 else | 289 else |
290 ++iter; | 290 ++iter; |
291 } | 291 } |
292 | 292 |
293 frame_statuses_.erase(frame); | 293 frame_statuses_.erase(frame); |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
342 } else { | 342 } else { |
343 ++iter; | 343 ++iter; |
344 } | 344 } |
345 } | 345 } |
346 | 346 |
347 // Add any injections for user scripts. | 347 // Add any injections for user scripts. |
348 int tab_id = ExtensionFrameHelper::Get(frame)->tab_id(); | 348 int tab_id = ExtensionFrameHelper::Get(frame)->tab_id(); |
349 user_script_set_manager_->GetAllInjections( | 349 user_script_set_manager_->GetAllInjections( |
350 &frame_injections, frame, tab_id, run_location); | 350 &frame_injections, frame, tab_id, run_location); |
351 | 351 |
352 ScriptsRunInfo scripts_run_info; | 352 // Note that we are running in |frame|. |
| 353 active_injection_frames_.insert(frame); |
| 354 |
| 355 ScriptsRunInfo scripts_run_info(frame, run_location); |
353 std::vector<ScriptInjection*> released_injections; | 356 std::vector<ScriptInjection*> released_injections; |
354 frame_injections.release(&released_injections); | 357 frame_injections.release(&released_injections); |
355 for (ScriptInjection* injection : released_injections) | 358 for (ScriptInjection* injection : released_injections) { |
| 359 // It's possible for the frame to be invalidated in the course of injection |
| 360 // (if a script removes its own frame, for example). If this happens, abort. |
| 361 if (!active_injection_frames_.count(frame)) |
| 362 break; |
356 TryToInject(make_scoped_ptr(injection), run_location, &scripts_run_info); | 363 TryToInject(make_scoped_ptr(injection), run_location, &scripts_run_info); |
| 364 } |
357 | 365 |
358 scripts_run_info.LogRun(frame->GetWebFrame(), run_location); | 366 // We are done running in the frame. |
| 367 active_injection_frames_.erase(frame); |
| 368 |
| 369 scripts_run_info.LogRun(); |
359 } | 370 } |
360 | 371 |
361 void ScriptInjectionManager::TryToInject( | 372 void ScriptInjectionManager::TryToInject( |
362 scoped_ptr<ScriptInjection> injection, | 373 scoped_ptr<ScriptInjection> injection, |
363 UserScript::RunLocation run_location, | 374 UserScript::RunLocation run_location, |
364 ScriptsRunInfo* scripts_run_info) { | 375 ScriptsRunInfo* scripts_run_info) { |
365 // Try to inject the script. If the injection is waiting (i.e., for | 376 // 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 | 377 // permission), add it to the list of pending injections. If the injection |
367 // has blocked, add it to the list of running injections. | 378 // has blocked, add it to the list of running injections. |
368 // The Unretained below is safe because this object owns all the | 379 // The Unretained below is safe because this object owns all the |
(...skipping 29 matching lines...) Expand all Loading... |
398 } | 409 } |
399 | 410 |
400 scoped_ptr<ScriptInjection> injection(new ScriptInjection( | 411 scoped_ptr<ScriptInjection> injection(new ScriptInjection( |
401 scoped_ptr<ScriptInjector>( | 412 scoped_ptr<ScriptInjector>( |
402 new ProgrammaticScriptInjector(params, render_frame)), | 413 new ProgrammaticScriptInjector(params, render_frame)), |
403 render_frame, | 414 render_frame, |
404 injection_host.Pass(), | 415 injection_host.Pass(), |
405 static_cast<UserScript::RunLocation>(params.run_at), | 416 static_cast<UserScript::RunLocation>(params.run_at), |
406 ExtensionFrameHelper::Get(render_frame)->tab_id())); | 417 ExtensionFrameHelper::Get(render_frame)->tab_id())); |
407 | 418 |
408 ScriptsRunInfo scripts_run_info; | |
409 FrameStatusMap::const_iterator iter = frame_statuses_.find(render_frame); | 419 FrameStatusMap::const_iterator iter = frame_statuses_.find(render_frame); |
| 420 UserScript::RunLocation run_location = |
| 421 iter == frame_statuses_.end() ? UserScript::UNDEFINED : iter->second; |
410 | 422 |
411 TryToInject( | 423 ScriptsRunInfo scripts_run_info(render_frame, run_location); |
412 injection.Pass(), | 424 TryToInject(injection.Pass(), run_location, &scripts_run_info); |
413 iter == frame_statuses_.end() ? UserScript::UNDEFINED : iter->second, | |
414 &scripts_run_info); | |
415 } | 425 } |
416 | 426 |
417 void ScriptInjectionManager::HandleExecuteDeclarativeScript( | 427 void ScriptInjectionManager::HandleExecuteDeclarativeScript( |
418 content::RenderFrame* render_frame, | 428 content::RenderFrame* render_frame, |
419 int tab_id, | 429 int tab_id, |
420 const ExtensionId& extension_id, | 430 const ExtensionId& extension_id, |
421 int script_id, | 431 int script_id, |
422 const GURL& url) { | 432 const GURL& url) { |
423 scoped_ptr<ScriptInjection> injection = | 433 scoped_ptr<ScriptInjection> injection = |
424 user_script_set_manager_->GetInjectionForDeclarativeScript( | 434 user_script_set_manager_->GetInjectionForDeclarativeScript( |
425 script_id, | 435 script_id, |
426 render_frame, | 436 render_frame, |
427 tab_id, | 437 tab_id, |
428 url, | 438 url, |
429 extension_id); | 439 extension_id); |
430 if (injection.get()) { | 440 if (injection.get()) { |
431 ScriptsRunInfo scripts_run_info; | 441 ScriptsRunInfo scripts_run_info(render_frame, UserScript::BROWSER_DRIVEN); |
432 // TODO(markdittmer): Use return value of TryToInject for error handling. | 442 // TODO(markdittmer): Use return value of TryToInject for error handling. |
433 TryToInject(injection.Pass(), | 443 TryToInject(injection.Pass(), |
434 UserScript::BROWSER_DRIVEN, | 444 UserScript::BROWSER_DRIVEN, |
435 &scripts_run_info); | 445 &scripts_run_info); |
436 | 446 |
437 scripts_run_info.LogRun(render_frame->GetWebFrame(), | 447 scripts_run_info.LogRun(); |
438 UserScript::BROWSER_DRIVEN); | |
439 } | 448 } |
440 } | 449 } |
441 | 450 |
442 void ScriptInjectionManager::HandlePermitScriptInjection(int64 request_id) { | 451 void ScriptInjectionManager::HandlePermitScriptInjection(int64 request_id) { |
443 ScopedVector<ScriptInjection>::iterator iter = | 452 ScopedVector<ScriptInjection>::iterator iter = |
444 pending_injections_.begin(); | 453 pending_injections_.begin(); |
445 for (; iter != pending_injections_.end(); ++iter) { | 454 for (; iter != pending_injections_.end(); ++iter) { |
446 if ((*iter)->request_id() == request_id) { | 455 if ((*iter)->request_id() == request_id) { |
447 DCHECK((*iter)->host_id().type() == HostID::EXTENSIONS); | 456 DCHECK((*iter)->host_id().type() == HostID::EXTENSIONS); |
448 break; | 457 break; |
449 } | 458 } |
450 } | 459 } |
451 if (iter == pending_injections_.end()) | 460 if (iter == pending_injections_.end()) |
452 return; | 461 return; |
453 | 462 |
454 // At this point, because the request is present in pending_injections_, we | 463 // 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, | 464 // know that this is the same page that issued the request (otherwise, |
456 // RFOHelper's DidStartProvisionalLoad callback would have caused it to be | 465 // RFOHelper's DidStartProvisionalLoad callback would have caused it to be |
457 // cleared out). | 466 // cleared out). |
458 | 467 |
459 scoped_ptr<ScriptInjection> injection(*iter); | 468 scoped_ptr<ScriptInjection> injection(*iter); |
460 pending_injections_.weak_erase(iter); | 469 pending_injections_.weak_erase(iter); |
461 | 470 |
462 ScriptsRunInfo scripts_run_info; | 471 ScriptsRunInfo scripts_run_info(injection->render_frame(), |
| 472 UserScript::RUN_DEFERRED); |
463 ScriptInjection::InjectionResult res = injection->OnPermissionGranted( | 473 ScriptInjection::InjectionResult res = injection->OnPermissionGranted( |
464 &scripts_run_info); | 474 &scripts_run_info); |
465 if (res == ScriptInjection::INJECTION_BLOCKED) | 475 if (res == ScriptInjection::INJECTION_BLOCKED) |
466 running_injections_.push_back(injection.Pass()); | 476 running_injections_.push_back(injection.Pass()); |
467 scripts_run_info.LogRun(injection->render_frame()->GetWebFrame(), | 477 scripts_run_info.LogRun(); |
468 UserScript::RUN_DEFERRED); | |
469 } | 478 } |
470 | 479 |
471 } // namespace extensions | 480 } // namespace extensions |
OLD | NEW |