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

Side by Side Diff: chrome/browser/prerender/prerender_contents.cc

Issue 7038012: Safely cancel prerenders on threads other than the UI thread (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Response to Dominic's comments, part 2 Created 9 years, 7 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 "chrome/browser/prerender/prerender_contents.h" 5 #include "chrome/browser/prerender/prerender_contents.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/process_util.h" 10 #include "base/process_util.h"
11 #include "base/task.h" 11 #include "base/task.h"
12 #include "base/utf_string_conversions.h" 12 #include "base/utf_string_conversions.h"
13 #include "chrome/browser/background_contents_service.h" 13 #include "chrome/browser/background_contents_service.h"
14 #include "chrome/browser/browser_process.h" 14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/prerender/prerender_final_status.h" 15 #include "chrome/browser/prerender/prerender_final_status.h"
16 #include "chrome/browser/prerender/prerender_manager.h" 16 #include "chrome/browser/prerender/prerender_manager.h"
17 #include "chrome/browser/prerender/prerender_render_widget_host_view.h" 17 #include "chrome/browser/prerender/prerender_render_widget_host_view.h"
18 #include "chrome/browser/prerender/prerender_tracker.h"
18 #include "chrome/browser/profiles/profile.h" 19 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/renderer_preferences_util.h" 20 #include "chrome/browser/renderer_preferences_util.h"
20 #include "chrome/browser/ui/login/login_prompt.h" 21 #include "chrome/browser/ui/login/login_prompt.h"
21 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" 22 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
22 #include "chrome/common/extensions/extension_constants.h" 23 #include "chrome/common/extensions/extension_constants.h"
23 #include "chrome/common/extensions/extension_messages.h" 24 #include "chrome/common/extensions/extension_messages.h"
24 #include "chrome/common/icon_messages.h" 25 #include "chrome/common/icon_messages.h"
25 #include "chrome/common/render_messages.h" 26 #include "chrome/common/render_messages.h"
26 #include "chrome/common/url_constants.h" 27 #include "chrome/common/url_constants.h"
27 #include "content/browser/browsing_instance.h" 28 #include "content/browser/browsing_instance.h"
(...skipping 24 matching lines...) Expand all
52 bool operator()(const GURL& url) const { 53 bool operator()(const GURL& url) const {
53 return url.scheme() == url_.scheme() && 54 return url.scheme() == url_.scheme() &&
54 url.host() == url_.host() && 55 url.host() == url_.host() &&
55 url.port() == url_.port() && 56 url.port() == url_.port() &&
56 url.path() == url_.path() && 57 url.path() == url_.path() &&
57 url.query() == url_.query(); 58 url.query() == url_.query();
58 } 59 }
59 GURL url_; 60 GURL url_;
60 }; 61 };
61 62
62 void AddChildRoutePair(ResourceDispatcherHost* resource_dispatcher_host,
63 int child_id, int route_id) {
64 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
65 resource_dispatcher_host->AddPrerenderChildRoutePair(child_id, route_id);
66 }
67
68 void RemoveChildRoutePair(ResourceDispatcherHost* resource_dispatcher_host,
69 int child_id, int route_id) {
70 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
71 resource_dispatcher_host->RemovePrerenderChildRoutePair(child_id, route_id);
72 }
73
74 } // end namespace 63 } // end namespace
75 64
76 class PrerenderContentsFactoryImpl : public PrerenderContents::Factory { 65 class PrerenderContentsFactoryImpl : public PrerenderContents::Factory {
77 public: 66 public:
78 virtual PrerenderContents* CreatePrerenderContents( 67 virtual PrerenderContents* CreatePrerenderContents(
79 PrerenderManager* prerender_manager, Profile* profile, const GURL& url, 68 PrerenderManager* prerender_manager, Profile* profile, const GURL& url,
80 const GURL& referrer) OVERRIDE { 69 const GURL& referrer) OVERRIDE {
81 return new PrerenderContents(prerender_manager, profile, url, referrer); 70 return new PrerenderContents(prerender_manager, profile, url, referrer);
82 } 71 }
83 }; 72 };
84 73
85 PrerenderContents::PrerenderContents(PrerenderManager* prerender_manager, 74 PrerenderContents::PrerenderContents(PrerenderManager* prerender_manager,
86 Profile* profile, 75 Profile* profile,
87 const GURL& url, 76 const GURL& url,
88 const GURL& referrer) 77 const GURL& referrer)
89 : prerender_manager_(prerender_manager), 78 : prerender_manager_(prerender_manager),
90 render_view_host_(NULL), 79 render_view_host_(NULL),
91 prerender_url_(url), 80 prerender_url_(url),
92 referrer_(referrer), 81 referrer_(referrer),
93 profile_(profile), 82 profile_(profile),
94 page_id_(0), 83 page_id_(0),
95 ALLOW_THIS_IN_INITIALIZER_LIST(tab_contents_observer_registrar_(this)), 84 ALLOW_THIS_IN_INITIALIZER_LIST(tab_contents_observer_registrar_(this)),
96 has_stopped_loading_(false), 85 has_stopped_loading_(false),
97 final_status_(FINAL_STATUS_MAX), 86 final_status_(FINAL_STATUS_MAX),
98 prerendering_has_started_(false) { 87 prerendering_has_started_(false),
88 child_id_(-1),
89 route_id_(-1) {
99 DCHECK(prerender_manager != NULL); 90 DCHECK(prerender_manager != NULL);
100 } 91 }
101 92
102 bool PrerenderContents::Init() { 93 bool PrerenderContents::Init() {
103 if (!AddAliasURL(prerender_url_)) 94 if (!AddAliasURL(prerender_url_))
104 return false; 95 return false;
105 return true; 96 return true;
106 } 97 }
107 98
108 // static 99 // static
(...skipping 20 matching lines...) Expand all
129 render_view_host_->CreateRenderView(string16()); 120 render_view_host_->CreateRenderView(string16());
130 121
131 OnRenderViewHostCreated(render_view_host_); 122 OnRenderViewHostCreated(render_view_host_);
132 123
133 // Give the RVH a PrerenderRenderWidgetHostView, both so its size can be set 124 // Give the RVH a PrerenderRenderWidgetHostView, both so its size can be set
134 // and so that the prerender can be cancelled under certain circumstances. 125 // and so that the prerender can be cancelled under certain circumstances.
135 PrerenderRenderWidgetHostView* view = 126 PrerenderRenderWidgetHostView* view =
136 new PrerenderRenderWidgetHostView(render_view_host_, this); 127 new PrerenderRenderWidgetHostView(render_view_host_, this);
137 view->Init(source_render_view_host->view()); 128 view->Init(source_render_view_host->view());
138 129
139 // Register this with the ResourceDispatcherHost as a prerender 130 child_id_ = render_view_host_->process()->id();
140 // RenderViewHost. This must be done before the Navigate message to catch all 131 route_id_ = render_view_host_->routing_id();
141 // resource requests, but as it is on the same thread as the Navigate message
142 // (IO) there is no race condition.
143 int process_id = render_view_host_->process()->id();
144 int view_id = render_view_host_->routing_id();
145 132
146 BrowserThread::PostTask( 133 // Register this with the PrerenderTracker as a prerendering RenderViewHost.
147 BrowserThread::IO, FROM_HERE, 134 // This must be done before the Navigate message to catch all resource
148 NewRunnableFunction(&AddChildRoutePair, 135 // requests.
149 g_browser_process->resource_dispatcher_host(), 136 PrerenderTracker::GetInstance()->OnPrerenderingStarted(child_id_, route_id_,
150 process_id, view_id)); 137 prerender_manager_);
151 138
152 // Close ourselves when the application is shutting down. 139 // Close ourselves when the application is shutting down.
153 notification_registrar_.Add(this, NotificationType::APP_TERMINATING, 140 notification_registrar_.Add(this, NotificationType::APP_TERMINATING,
154 NotificationService::AllSources()); 141 NotificationService::AllSources());
155 142
156 // Register for our parent profile to shutdown, so we can shut ourselves down 143 // Register for our parent profile to shutdown, so we can shut ourselves down
157 // as well (should only be called for OTR profiles, as we should receive 144 // as well (should only be called for OTR profiles, as we should receive
158 // APP_TERMINATING before non-OTR profiles are destroyed). 145 // APP_TERMINATING before non-OTR profiles are destroyed).
159 // TODO(tburkard): figure out if this is needed. 146 // TODO(tburkard): figure out if this is needed.
160 notification_registrar_.Add(this, NotificationType::PROFILE_DESTROYED, 147 notification_registrar_.Add(this, NotificationType::PROFILE_DESTROYED,
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
231 // Set the size of the new TC to that of the old TC. 218 // Set the size of the new TC to that of the old TC.
232 gfx::Rect tab_bounds; 219 gfx::Rect tab_bounds;
233 source_tc->view()->GetContainerBounds(&tab_bounds); 220 source_tc->view()->GetContainerBounds(&tab_bounds);
234 prerender_contents_->view()->SizeContents(tab_bounds.size()); 221 prerender_contents_->view()->SizeContents(tab_bounds.size());
235 } 222 }
236 223
237 // Register as an observer of the RenderViewHost so we get messages. 224 // Register as an observer of the RenderViewHost so we get messages.
238 render_view_host_observer_.reset( 225 render_view_host_observer_.reset(
239 new PrerenderRenderViewHostObserver(this, render_view_host_mutable())); 226 new PrerenderRenderViewHostObserver(this, render_view_host_mutable()));
240 227
241 int process_id; 228 child_id_ = render_view_host()->process()->id();
242 int view_id; 229 route_id_ = render_view_host()->routing_id();
243 CHECK(GetChildId(&process_id));
244 CHECK(GetRouteId(&view_id));
245 230
246 // Register this with the ResourceDispatcherHost as a prerender 231 // Register this with the ResourceDispatcherHost as a prerender
247 // RenderViewHost. This must be done before the Navigate message to catch all 232 // RenderViewHost. This must be done before the Navigate message to catch all
248 // resource requests, but as it is on the same thread as the Navigate message 233 // resource requests, but as it is on the same thread as the Navigate message
249 // (IO) there is no race condition. 234 // (IO) there is no race condition.
250 BrowserThread::PostTask( 235 PrerenderTracker::GetInstance()->OnPrerenderingStarted(child_id_, route_id_,
251 BrowserThread::IO, FROM_HERE, 236 prerender_manager_);
252 NewRunnableFunction(&AddChildRoutePair,
253 g_browser_process->resource_dispatcher_host(),
254 process_id, view_id));
255 237
256 // Close ourselves when the application is shutting down. 238 // Close ourselves when the application is shutting down.
257 notification_registrar_.Add(this, NotificationType::APP_TERMINATING, 239 notification_registrar_.Add(this, NotificationType::APP_TERMINATING,
258 NotificationService::AllSources()); 240 NotificationService::AllSources());
259 241
260 // Register for our parent profile to shutdown, so we can shut ourselves down 242 // Register for our parent profile to shutdown, so we can shut ourselves down
261 // as well (should only be called for OTR profiles, as we should receive 243 // as well (should only be called for OTR profiles, as we should receive
262 // APP_TERMINATING before non-OTR profiles are destroyed). 244 // APP_TERMINATING before non-OTR profiles are destroyed).
263 // TODO(tburkard): figure out if this is needed. 245 // TODO(tburkard): figure out if this is needed.
264 notification_registrar_.Add(this, NotificationType::PROFILE_DESTROYED, 246 notification_registrar_.Add(this, NotificationType::PROFILE_DESTROYED,
(...skipping 27 matching lines...) Expand all
292 274
293 DCHECK(load_start_time_.is_null()); 275 DCHECK(load_start_time_.is_null());
294 load_start_time_ = base::TimeTicks::Now(); 276 load_start_time_ = base::TimeTicks::Now();
295 277
296 new_contents->controller().LoadURL(prerender_url_, 278 new_contents->controller().LoadURL(prerender_url_,
297 referrer_, PageTransition::LINK); 279 referrer_, PageTransition::LINK);
298 } 280 }
299 281
300 bool PrerenderContents::GetChildId(int* child_id) const { 282 bool PrerenderContents::GetChildId(int* child_id) const {
301 CHECK(child_id); 283 CHECK(child_id);
302 const RenderViewHost* prerender_render_view_host = render_view_host(); 284 DCHECK_GE(child_id_, -1);
303 if (prerender_render_view_host) { 285 *child_id = child_id_;
304 *child_id = prerender_render_view_host->process()->id(); 286 return child_id_ != -1;
305 return true;
306 }
307 return false;
308 } 287 }
309 288
310 bool PrerenderContents::GetRouteId(int* route_id) const { 289 bool PrerenderContents::GetRouteId(int* route_id) const {
311 CHECK(route_id); 290 CHECK(route_id);
312 const RenderViewHost* prerender_render_view_host = render_view_host(); 291 DCHECK_GE(route_id_, -1);
313 if (prerender_render_view_host) { 292 *route_id = route_id_;
314 *route_id = prerender_render_view_host->routing_id(); 293 return route_id_ != -1;
315 return true;
316 }
317 return false;
318 } 294 }
319 295
320 void PrerenderContents::set_final_status(FinalStatus final_status) { 296 void PrerenderContents::set_final_status(FinalStatus final_status) {
321 DCHECK(final_status >= FINAL_STATUS_USED && final_status < FINAL_STATUS_MAX); 297 DCHECK(final_status >= FINAL_STATUS_USED && final_status < FINAL_STATUS_MAX);
322 DCHECK(final_status_ == FINAL_STATUS_MAX || 298 DCHECK(final_status_ == FINAL_STATUS_MAX ||
323 final_status_ == FINAL_STATUS_CONTROL_GROUP); 299 final_status_ == FINAL_STATUS_CONTROL_GROUP);
324 300
325 // Don't override final_status_ if it's FINAL_STATUS_CONTROL_GROUP, 301 // Don't override final_status_ if it's FINAL_STATUS_CONTROL_GROUP,
326 // otherwise data will be collected in the Prerender.FinalStatus histogram. 302 // otherwise data will be collected in the Prerender.FinalStatus histogram.
327 if (final_status_ == FINAL_STATUS_CONTROL_GROUP) 303 if (final_status_ == FINAL_STATUS_CONTROL_GROUP)
328 return; 304 return;
329 305
330 final_status_ = final_status; 306 final_status_ = final_status;
331 } 307 }
332 308
333 FinalStatus PrerenderContents::final_status() const { 309 FinalStatus PrerenderContents::final_status() const {
334 return final_status_; 310 return final_status_;
335 } 311 }
336 312
337 PrerenderContents::~PrerenderContents() { 313 PrerenderContents::~PrerenderContents() {
338 DCHECK(final_status_ != FINAL_STATUS_MAX); 314 DCHECK(final_status_ != FINAL_STATUS_MAX);
339 315
340 // If we haven't even started prerendering, we were just in the control 316 // If we haven't even started prerendering, we were just in the control
341 // group, which means we do not want to record the status. 317 // group, which means we do not want to record the status.
342 if (prerendering_has_started()) 318 if (prerendering_has_started())
343 RecordFinalStatus(final_status_); 319 RecordFinalStatus(final_status_);
344 320
345 if (render_view_host_ || prerender_contents_.get()) { 321 if (render_view_host_ || prerender_contents_.get()) {
dominich 2011/05/19 16:55:47 there's no need for this prerender_contents_.get()
mmenke 2011/05/19 17:09:24 Good point.
346 RenderViewHost* prerender_render_view_host = render_view_host_mutable();
347
348 int process_id = prerender_render_view_host->process()->id();
349 int view_id = prerender_render_view_host->routing_id();
350
351 BrowserThread::PostTask(
352 BrowserThread::IO, FROM_HERE,
353 NewRunnableFunction(&RemoveChildRoutePair,
354 g_browser_process->resource_dispatcher_host(),
355 process_id, view_id));
356
357 // Only delete the RenderViewHost if we own it. 322 // Only delete the RenderViewHost if we own it.
358 if (render_view_host_) 323 if (render_view_host_)
359 render_view_host_->Shutdown(); 324 render_view_host_->Shutdown();
360 } 325 }
326
327 if (child_id_ != -1 && route_id_ != -1) {
328 PrerenderTracker::GetInstance()->OnPrerenderingFinished(
329 child_id_, route_id_);
330 }
361 } 331 }
362 332
363 RenderViewHostDelegate::View* PrerenderContents::GetViewDelegate() { 333 RenderViewHostDelegate::View* PrerenderContents::GetViewDelegate() {
364 return this; 334 return this;
365 } 335 }
366 336
367 const GURL& PrerenderContents::GetURL() const { 337 const GURL& PrerenderContents::GetURL() const {
368 return url_; 338 return url_;
369 } 339 }
370 340
(...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after
674 } 644 }
675 645
676 void PrerenderContents::DidStopLoading() { 646 void PrerenderContents::DidStopLoading() {
677 has_stopped_loading_ = true; 647 has_stopped_loading_ = true;
678 } 648 }
679 649
680 void PrerenderContents::Destroy(FinalStatus final_status) { 650 void PrerenderContents::Destroy(FinalStatus final_status) {
681 if (prerender_manager_->IsPendingDelete(this)) 651 if (prerender_manager_->IsPendingDelete(this))
682 return; 652 return;
683 653
654 if (child_id_ != -1 && route_id_ != -1) {
655 // Cancel the prerender in the PrerenderTracker. This is needed
656 // because destroy may be called directly from the UI thread without calling
657 // TryCancel(). This is difficult to completely avoid, since prerendering
658 // can be cancelled before a RenderView is created.
659 bool is_cancelled =
660 PrerenderTracker::GetInstance()->TryCancel(child_id_, route_id_,
661 final_status);
662 DCHECK(is_cancelled);
dominich 2011/05/19 16:55:47 Consider making this a CHECK if it should never be
mmenke 2011/05/19 17:09:24 Done. If it ever happens, I suspect we'd crash a
663
664 // A different final status may have been set already from another thread.
665 // If so, use it instead.
666 if (!PrerenderTracker::GetInstance()->GetFinalStatus(child_id_, route_id_,
667 &final_status)) {
668 NOTREACHED();
669 }
670 }
671
684 prerender_manager_->MoveEntryToPendingDelete(this); 672 prerender_manager_->MoveEntryToPendingDelete(this);
685 set_final_status(final_status); 673 set_final_status(final_status);
686 // We may destroy the PrerenderContents before we have initialized the 674 // We may destroy the PrerenderContents before we have initialized the
687 // RenderViewHost. Otherwise set the Observer's PrerenderContents to NULL to 675 // RenderViewHost. Otherwise set the Observer's PrerenderContents to NULL to
688 // avoid any more messages being sent. 676 // avoid any more messages being sent.
689 if (render_view_host_observer_.get()) 677 if (render_view_host_observer_.get())
690 render_view_host_observer_->set_prerender_contents(NULL); 678 render_view_host_observer_->set_prerender_contents(NULL);
691 } 679 }
692 680
693 void PrerenderContents::RendererUnresponsive(RenderViewHost* render_view_host, 681 void PrerenderContents::RendererUnresponsive(RenderViewHost* render_view_host,
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
754 // used. 742 // used.
755 if (UseTabContents()) { 743 if (UseTabContents()) {
756 if (!prerender_contents_.get()) 744 if (!prerender_contents_.get())
757 return NULL; 745 return NULL;
758 return prerender_contents_->render_view_host(); 746 return prerender_contents_->render_view_host();
759 } 747 }
760 return render_view_host_; 748 return render_view_host_;
761 } 749 }
762 750
763 } // namespace prerender 751 } // namespace prerender
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698