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

Side by Side Diff: extensions/renderer/script_injection_manager.cc

Issue 1216453002: [Extensions] Handle some funny cases in script injection (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Ben's Created 5 years, 5 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
« no previous file with comments | « extensions/renderer/script_injection_manager.h ('k') | extensions/renderer/scripts_run_info.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « extensions/renderer/script_injection_manager.h ('k') | extensions/renderer/scripts_run_info.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698