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

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: Fix linux 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 *child_id = child_id_;
303 if (prerender_render_view_host) { 285 return child_id_ >= 0;
dominich 2011/05/19 15:49:33 This should be a != -1 as you never expect a value
mmenke 2011/05/19 16:40:42 Done (for all the checks in this file, also update
304 *child_id = prerender_render_view_host->process()->id();
305 return true;
306 }
307 return false;
308 } 286 }
309 287
310 bool PrerenderContents::GetRouteId(int* route_id) const { 288 bool PrerenderContents::GetRouteId(int* route_id) const {
311 CHECK(route_id); 289 CHECK(route_id);
312 const RenderViewHost* prerender_render_view_host = render_view_host(); 290 *route_id = route_id_;
313 if (prerender_render_view_host) { 291 return route_id_ >= 0;
dominich 2011/05/19 15:49:33 See above comment re != -1.
314 *route_id = prerender_render_view_host->routing_id();
315 return true;
316 }
317 return false;
318 } 292 }
319 293
320 void PrerenderContents::set_final_status(FinalStatus final_status) { 294 void PrerenderContents::set_final_status(FinalStatus final_status) {
321 DCHECK(final_status >= FINAL_STATUS_USED && final_status < FINAL_STATUS_MAX); 295 DCHECK(final_status >= FINAL_STATUS_USED && final_status < FINAL_STATUS_MAX);
322 DCHECK(final_status_ == FINAL_STATUS_MAX || 296 DCHECK(final_status_ == FINAL_STATUS_MAX ||
323 final_status_ == FINAL_STATUS_CONTROL_GROUP); 297 final_status_ == FINAL_STATUS_CONTROL_GROUP);
324 298
325 // Don't override final_status_ if it's FINAL_STATUS_CONTROL_GROUP, 299 // Don't override final_status_ if it's FINAL_STATUS_CONTROL_GROUP,
326 // otherwise data will be collected in the Prerender.FinalStatus histogram. 300 // otherwise data will be collected in the Prerender.FinalStatus histogram.
327 if (final_status_ == FINAL_STATUS_CONTROL_GROUP) 301 if (final_status_ == FINAL_STATUS_CONTROL_GROUP)
328 return; 302 return;
329 303
330 final_status_ = final_status; 304 final_status_ = final_status;
331 } 305 }
332 306
333 FinalStatus PrerenderContents::final_status() const { 307 FinalStatus PrerenderContents::final_status() const {
334 return final_status_; 308 return final_status_;
335 } 309 }
336 310
337 PrerenderContents::~PrerenderContents() { 311 PrerenderContents::~PrerenderContents() {
338 DCHECK(final_status_ != FINAL_STATUS_MAX); 312 DCHECK(final_status_ != FINAL_STATUS_MAX);
339 313
340 // If we haven't even started prerendering, we were just in the control 314 // 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. 315 // group, which means we do not want to record the status.
342 if (prerendering_has_started()) 316 if (prerendering_has_started())
343 RecordFinalStatus(final_status_); 317 RecordFinalStatus(final_status_);
344 318
345 if (render_view_host_ || prerender_contents_.get()) { 319 if (render_view_host_ || prerender_contents_.get()) {
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. 320 // Only delete the RenderViewHost if we own it.
358 if (render_view_host_) 321 if (render_view_host_)
359 render_view_host_->Shutdown(); 322 render_view_host_->Shutdown();
360 } 323 }
324
325 if (child_id_ >= 0 && route_id_ >= 0) {
326 PrerenderTracker::GetInstance()->OnPrerenderingFinished(
327 child_id_, route_id_);
328 }
361 } 329 }
362 330
363 RenderViewHostDelegate::View* PrerenderContents::GetViewDelegate() { 331 RenderViewHostDelegate::View* PrerenderContents::GetViewDelegate() {
364 return this; 332 return this;
365 } 333 }
366 334
367 const GURL& PrerenderContents::GetURL() const { 335 const GURL& PrerenderContents::GetURL() const {
368 return url_; 336 return url_;
369 } 337 }
370 338
(...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after
674 } 642 }
675 643
676 void PrerenderContents::DidStopLoading() { 644 void PrerenderContents::DidStopLoading() {
677 has_stopped_loading_ = true; 645 has_stopped_loading_ = true;
678 } 646 }
679 647
680 void PrerenderContents::Destroy(FinalStatus final_status) { 648 void PrerenderContents::Destroy(FinalStatus final_status) {
681 if (prerender_manager_->IsPendingDelete(this)) 649 if (prerender_manager_->IsPendingDelete(this))
682 return; 650 return;
683 651
652 if (child_id_ >= 0 && route_id_ >= 0) {
653 // Cancel the prerender in the PrerenderTracker. This is needed
654 // because destroy may be called directly from the UI thread without calling
655 // TryCancel(). This is difficult to completely avoid, since prerendering
656 // can be cancelled before a RenderView is created.
657 bool is_cancelled =
658 PrerenderTracker::GetInstance()->TryCancel(child_id_, route_id_,
659 final_status);
660 DCHECK(is_cancelled);
661
662 // A different final status may have been set already from another thread.
663 // If so, use it instead.
664 if (!PrerenderTracker::GetInstance()->GetFinalStatus(child_id_, route_id_,
665 &final_status)) {
666 NOTREACHED();
dominich 2011/05/19 15:49:33 Can this not be CHECK(PrerenderTracker::GetInstanc
mmenke 2011/05/19 16:40:42 A CHECK results in a crash in release builds, whic
dominich 2011/05/19 16:55:47 I thought NOTREACHED was a CHECK rather than a DCH
667 }
668 }
669
684 prerender_manager_->MoveEntryToPendingDelete(this); 670 prerender_manager_->MoveEntryToPendingDelete(this);
685 set_final_status(final_status); 671 set_final_status(final_status);
686 // We may destroy the PrerenderContents before we have initialized the 672 // We may destroy the PrerenderContents before we have initialized the
687 // RenderViewHost. Otherwise set the Observer's PrerenderContents to NULL to 673 // RenderViewHost. Otherwise set the Observer's PrerenderContents to NULL to
688 // avoid any more messages being sent. 674 // avoid any more messages being sent.
689 if (render_view_host_observer_.get()) 675 if (render_view_host_observer_.get())
690 render_view_host_observer_->set_prerender_contents(NULL); 676 render_view_host_observer_->set_prerender_contents(NULL);
691 } 677 }
692 678
693 void PrerenderContents::RendererUnresponsive(RenderViewHost* render_view_host, 679 void PrerenderContents::RendererUnresponsive(RenderViewHost* render_view_host,
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
754 // used. 740 // used.
755 if (UseTabContents()) { 741 if (UseTabContents()) {
756 if (!prerender_contents_.get()) 742 if (!prerender_contents_.get())
757 return NULL; 743 return NULL;
758 return prerender_contents_->render_view_host(); 744 return prerender_contents_->render_view_host();
759 } 745 }
760 return render_view_host_; 746 return render_view_host_;
761 } 747 }
762 748
763 } // namespace prerender 749 } // namespace prerender
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698