1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/prerender/prerender_contents.h"
11 #include "apps/ui/web_contents_sizer.h"
12 #include "base/bind.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/history/history_tab_helper.h"
16 #include "chrome/browser/history/history_types.h"
17 #include "chrome/browser/prerender/prerender_field_trial.h"
18 #include "chrome/browser/prerender/prerender_final_status.h"
19 #include "chrome/browser/prerender/prerender_handle.h"
20 #include "chrome/browser/prerender/prerender_manager.h"
21 #include "chrome/browser/prerender/prerender_manager_factory.h"
22 #include "chrome/browser/prerender/prerender_resource_throttle.h"
23 #include "chrome/browser/prerender/prerender_tracker.h"
24 #include "chrome/browser/profiles/profile.h"
25 #include "chrome/browser/ui/browser.h"
26 #include "chrome/browser/ui/tab_helpers.h"
27 #include "chrome/common/prerender_messages.h"
28 #include "chrome/common/render_messages.h"
29 #include "chrome/common/url_constants.h"
30 #include "content/public/browser/browser_child_process_host.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "content/public/browser/notification_service.h"
33 #include "content/public/browser/render_frame_host.h"
34 #include "content/public/browser/render_process_host.h"
35 #include "content/public/browser/render_view_host.h"
36 #include "content/public/browser/resource_request_details.h"
37 #include "content/public/browser/session_storage_namespace.h"
38 #include "content/public/browser/web_contents.h"
39 #include "content/public/browser/web_contents_delegate.h"
40 #include "content/public/common/frame_navigate_params.h"
41 #include "content/public/common/page_transition_types.h"
42 #include "net/url_request/url_request_context_getter.h"
43 #include "ui/gfx/rect.h"
45 using content::BrowserThread
;
46 using content::DownloadItem
;
47 using content::OpenURLParams
;
48 using content::RenderViewHost
;
49 using content::ResourceRedirectDetails
;
50 using content::ResourceType
;
51 using content::SessionStorageNamespace
;
52 using content::WebContents
;
58 // Internal cookie event.
59 // Whenever a prerender interacts with the cookie store, either sending
60 // existing cookies that existed before the prerender started, or when a cookie
61 // is changed, we record these events for histogramming purposes.
62 enum InternalCookieEvent
{
63 INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND
= 0,
64 INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE
= 1,
65 INTERNAL_COOKIE_EVENT_OTHER_SEND
= 2,
66 INTERNAL_COOKIE_EVENT_OTHER_CHANGE
= 3,
67 INTERNAL_COOKIE_EVENT_MAX
70 // Indicates whether existing cookies were sent, and if they were third party
71 // cookies, and whether they were for blocking resources.
72 // Each value may be inclusive of previous values. We only care about the
73 // value with the highest index that has ever occurred in the course of a
76 COOKIE_SEND_TYPE_NONE
= 0,
77 COOKIE_SEND_TYPE_FIRST_PARTY
= 1,
78 COOKIE_SEND_TYPE_THIRD_PARTY
= 2,
79 COOKIE_SEND_TYPE_THIRD_PARTY_BLOCKING_RESOURCE
= 3,
84 std::vector
<base::WeakPtr
<PrerenderResourceThrottle
> > throttles
) {
85 for (size_t i
= 0; i
< throttles
.size(); i
++) {
87 throttles
[i
]->Resume();
94 const int PrerenderContents::kNumCookieStatuses
=
95 (1 << INTERNAL_COOKIE_EVENT_MAX
);
98 const int PrerenderContents::kNumCookieSendTypes
= COOKIE_SEND_TYPE_MAX
;
100 class PrerenderContentsFactoryImpl
: public PrerenderContents::Factory
{
102 virtual PrerenderContents
* CreatePrerenderContents(
103 PrerenderManager
* prerender_manager
, Profile
* profile
,
104 const GURL
& url
, const content::Referrer
& referrer
,
105 Origin origin
, uint8 experiment_id
) OVERRIDE
{
106 return new PrerenderContents(prerender_manager
, profile
,
107 url
, referrer
, origin
, experiment_id
);
111 // WebContentsDelegateImpl -----------------------------------------------------
113 class PrerenderContents::WebContentsDelegateImpl
114 : public content::WebContentsDelegate
{
116 explicit WebContentsDelegateImpl(PrerenderContents
* prerender_contents
)
117 : prerender_contents_(prerender_contents
) {
120 // content::WebContentsDelegate implementation:
121 virtual WebContents
* OpenURLFromTab(WebContents
* source
,
122 const OpenURLParams
& params
) OVERRIDE
{
123 // |OpenURLFromTab| is typically called when a frame performs a navigation
124 // that requires the browser to perform the transition instead of WebKit.
125 // Examples include prerendering a site that redirects to an app URL,
126 // or if --enable-strict-site-isolation is specified and the prerendered
127 // frame redirects to a different origin.
128 // TODO(cbentzel): Consider supporting this if it is a common case during
130 prerender_contents_
->Destroy(FINAL_STATUS_OPEN_URL
);
134 virtual void CloseContents(content::WebContents
* contents
) OVERRIDE
{
135 prerender_contents_
->Destroy(FINAL_STATUS_CLOSED
);
138 virtual void CanDownload(
139 RenderViewHost
* render_view_host
,
141 const std::string
& request_method
,
142 const base::Callback
<void(bool)>& callback
) OVERRIDE
{
143 prerender_contents_
->Destroy(FINAL_STATUS_DOWNLOAD
);
144 // Cancel the download.
148 virtual bool ShouldCreateWebContents(
149 WebContents
* web_contents
,
151 WindowContainerType window_container_type
,
152 const base::string16
& frame_name
,
153 const GURL
& target_url
,
154 const std::string
& partition_id
,
155 SessionStorageNamespace
* session_storage_namespace
) OVERRIDE
{
156 // Since we don't want to permit child windows that would have a
157 // window.opener property, terminate prerendering.
158 prerender_contents_
->Destroy(FINAL_STATUS_CREATE_NEW_WINDOW
);
163 virtual bool OnGoToEntryOffset(int offset
) OVERRIDE
{
164 // This isn't allowed because the history merge operation
165 // does not work if there are renderer issued challenges.
166 // TODO(cbentzel): Cancel in this case? May not need to do
167 // since render-issued offset navigations are not guaranteed,
168 // but indicates that the page cares about the history.
172 virtual bool ShouldSuppressDialogs() OVERRIDE
{
173 // We still want to show the user the message when they navigate to this
174 // page, so cancel this prerender.
175 prerender_contents_
->Destroy(FINAL_STATUS_JAVASCRIPT_ALERT
);
176 // Always suppress JavaScript messages if they're triggered by a page being
181 virtual void RegisterProtocolHandler(WebContents
* web_contents
,
182 const std::string
& protocol
,
184 bool user_gesture
) OVERRIDE
{
185 // TODO(mmenke): Consider supporting this if it is a common case during
187 prerender_contents_
->Destroy(FINAL_STATUS_REGISTER_PROTOCOL_HANDLER
);
190 virtual gfx::Size
GetSizeForNewRenderView(
191 WebContents
* web_contents
) const OVERRIDE
{
192 // Have to set the size of the RenderView on initialization to be sure it is
193 // set before the RenderView is hidden on all platforms (esp. Android).
194 return prerender_contents_
->size_
;
198 PrerenderContents
* prerender_contents_
;
201 void PrerenderContents::Observer::OnPrerenderStopLoading(
202 PrerenderContents
* contents
) {
205 void PrerenderContents::Observer::OnPrerenderDomContentLoaded(
206 PrerenderContents
* contents
) {
209 void PrerenderContents::Observer::OnPrerenderCreatedMatchCompleteReplacement(
210 PrerenderContents
* contents
, PrerenderContents
* replacement
) {
213 PrerenderContents::Observer::Observer() {
216 PrerenderContents::Observer::~Observer() {
219 PrerenderContents::PrerenderContents(
220 PrerenderManager
* prerender_manager
,
223 const content::Referrer
& referrer
,
226 : prerendering_has_started_(false),
227 session_storage_namespace_id_(-1),
228 prerender_manager_(prerender_manager
),
233 has_stopped_loading_(false),
234 has_finished_loading_(false),
235 final_status_(FINAL_STATUS_MAX
),
236 match_complete_status_(MATCH_COMPLETE_DEFAULT
),
237 prerendering_has_been_cancelled_(false),
241 experiment_id_(experiment_id
),
242 creator_child_id_(-1),
244 cookie_send_type_(COOKIE_SEND_TYPE_NONE
),
246 DCHECK(prerender_manager
!= NULL
);
249 PrerenderContents
* PrerenderContents::CreateMatchCompleteReplacement() {
250 PrerenderContents
* new_contents
= prerender_manager_
->CreatePrerenderContents(
251 prerender_url(), referrer(), origin(), experiment_id());
253 new_contents
->load_start_time_
= load_start_time_
;
254 new_contents
->session_storage_namespace_id_
= session_storage_namespace_id_
;
255 new_contents
->set_match_complete_status(
256 PrerenderContents::MATCH_COMPLETE_REPLACEMENT_PENDING
);
258 const bool did_init
= new_contents
->Init();
260 DCHECK_EQ(alias_urls_
.front(), new_contents
->alias_urls_
.front());
261 DCHECK_EQ(1u, new_contents
->alias_urls_
.size());
262 new_contents
->alias_urls_
= alias_urls_
;
263 // Erase all but the first alias URL; the replacement has adopted the
264 // remainder without increasing the renderer-side reference count.
265 alias_urls_
.resize(1);
266 new_contents
->set_match_complete_status(
267 PrerenderContents::MATCH_COMPLETE_REPLACEMENT
);
268 NotifyPrerenderCreatedMatchCompleteReplacement(new_contents
);
272 bool PrerenderContents::Init() {
273 return AddAliasURL(prerender_url_
);
277 PrerenderContents::Factory
* PrerenderContents::CreateFactory() {
278 return new PrerenderContentsFactoryImpl();
282 PrerenderContents
* PrerenderContents::FromWebContents(
283 content::WebContents
* web_contents
) {
286 PrerenderManager
* prerender_manager
= PrerenderManagerFactory::GetForProfile(
287 Profile::FromBrowserContext(web_contents
->GetBrowserContext()));
288 if (!prerender_manager
)
290 return prerender_manager
->GetPrerenderContents(web_contents
);
293 void PrerenderContents::StartPrerendering(
294 int creator_child_id
,
295 const gfx::Size
& size
,
296 SessionStorageNamespace
* session_storage_namespace
,
297 net::URLRequestContextGetter
* request_context
) {
298 DCHECK(profile_
!= NULL
);
299 DCHECK(!size
.IsEmpty());
300 DCHECK(!prerendering_has_started_
);
301 DCHECK(prerender_contents_
.get() == NULL
);
302 DCHECK_EQ(-1, creator_child_id_
);
303 DCHECK(size_
.IsEmpty());
304 DCHECK_EQ(1U, alias_urls_
.size());
306 creator_child_id_
= creator_child_id
;
307 session_storage_namespace_id_
= session_storage_namespace
->id();
310 DCHECK(load_start_time_
.is_null());
311 load_start_time_
= base::TimeTicks::Now();
312 start_time_
= base::Time::Now();
314 // Everything after this point sets up the WebContents object and associated
315 // RenderView for the prerender page. Don't do this for members of the
317 if (prerender_manager_
->IsControlGroup(experiment_id()))
320 if (origin_
== ORIGIN_LOCAL_PREDICTOR
&&
321 IsLocalPredictorPrerenderAlwaysControlEnabled()) {
325 prerendering_has_started_
= true;
327 alias_session_storage_namespace
= session_storage_namespace
->CreateAlias();
328 prerender_contents_
.reset(
329 CreateWebContents(alias_session_storage_namespace
.get()));
330 TabHelpers::AttachTabHelpers(prerender_contents_
.get());
331 content::WebContentsObserver::Observe(prerender_contents_
.get());
333 web_contents_delegate_
.reset(new WebContentsDelegateImpl(this));
334 prerender_contents_
.get()->SetDelegate(web_contents_delegate_
.get());
335 // Set the size of the prerender WebContents.
336 apps::ResizeWebContents(prerender_contents_
.get(), size_
);
338 child_id_
= GetRenderViewHost()->GetProcess()->GetID();
339 route_id_
= GetRenderViewHost()->GetRoutingID();
341 // Log transactions to see if we could merge session storage namespaces in
342 // the event of a mismatch.
343 alias_session_storage_namespace
->AddTransactionLogProcessId(child_id_
);
345 // Add the RenderProcessHost to the Prerender Manager.
346 prerender_manager()->AddPrerenderProcessHost(
347 GetRenderViewHost()->GetProcess());
349 // In the prerender tracker, create a Prerender Cookie Store to keep track of
350 // cookie changes performed by the prerender. Once the prerender is shown,
351 // the cookie changes will be committed to the actual cookie store,
352 // otherwise, they will be discarded.
353 // If |request_context| is NULL, the feature must be disabled, so the
354 // operation will not be performed.
355 if (request_context
) {
356 BrowserThread::PostTask(
357 BrowserThread::IO
, FROM_HERE
,
358 base::Bind(&PrerenderTracker::AddPrerenderCookieStoreOnIOThread
,
359 base::Unretained(prerender_manager()->prerender_tracker()),
360 GetRenderViewHost()->GetProcess()->GetID(),
361 make_scoped_refptr(request_context
),
362 base::Bind(&PrerenderContents::Destroy
,
364 FINAL_STATUS_COOKIE_CONFLICT
)));
367 NotifyPrerenderStart();
369 // Close ourselves when the application is shutting down.
370 notification_registrar_
.Add(this, chrome::NOTIFICATION_APP_TERMINATING
,
371 content::NotificationService::AllSources());
373 // Register to inform new RenderViews that we're prerendering.
374 notification_registrar_
.Add(
375 this, content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED
,
376 content::Source
<WebContents
>(prerender_contents_
.get()));
378 // Transfer over the user agent override.
379 prerender_contents_
.get()->SetUserAgentOverride(
380 prerender_manager_
->config().user_agent_override
);
382 content::NavigationController::LoadURLParams
load_url_params(
384 load_url_params
.referrer
= referrer_
;
385 load_url_params
.transition_type
= content::PAGE_TRANSITION_LINK
;
386 if (origin_
== ORIGIN_OMNIBOX
) {
387 load_url_params
.transition_type
= content::PageTransitionFromInt(
388 content::PAGE_TRANSITION_TYPED
|
389 content::PAGE_TRANSITION_FROM_ADDRESS_BAR
);
390 } else if (origin_
== ORIGIN_INSTANT
) {
391 load_url_params
.transition_type
= content::PageTransitionFromInt(
392 content::PAGE_TRANSITION_GENERATED
|
393 content::PAGE_TRANSITION_FROM_ADDRESS_BAR
);
395 load_url_params
.override_user_agent
=
396 prerender_manager_
->config().is_overriding_user_agent
?
397 content::NavigationController::UA_OVERRIDE_TRUE
:
398 content::NavigationController::UA_OVERRIDE_FALSE
;
399 prerender_contents_
.get()->GetController().LoadURLWithParams(load_url_params
);
402 bool PrerenderContents::GetChildId(int* child_id
) const {
404 DCHECK_GE(child_id_
, -1);
405 *child_id
= child_id_
;
406 return child_id_
!= -1;
409 bool PrerenderContents::GetRouteId(int* route_id
) const {
411 DCHECK_GE(route_id_
, -1);
412 *route_id
= route_id_
;
413 return route_id_
!= -1;
416 void PrerenderContents::SetFinalStatus(FinalStatus final_status
) {
417 DCHECK_GE(final_status
, FINAL_STATUS_USED
);
418 DCHECK_LT(final_status
, FINAL_STATUS_MAX
);
420 DCHECK_EQ(FINAL_STATUS_MAX
, final_status_
);
422 final_status_
= final_status
;
425 PrerenderContents::~PrerenderContents() {
426 DCHECK_NE(FINAL_STATUS_MAX
, final_status());
428 prerendering_has_been_cancelled() || final_status() == FINAL_STATUS_USED
);
429 DCHECK_NE(ORIGIN_MAX
, origin());
430 // Since a lot of prerenders terminate before any meaningful cookie action
431 // would have happened, only record the cookie status for prerenders who
432 // were used, cancelled, or timed out.
433 if (prerendering_has_started_
&& final_status() == FINAL_STATUS_USED
) {
434 prerender_manager_
->RecordCookieStatus(origin(), experiment_id(),
436 prerender_manager_
->RecordCookieSendType(origin(), experiment_id(),
439 prerender_manager_
->RecordFinalStatusWithMatchCompleteStatus(
440 origin(), experiment_id(), match_complete_status(), final_status());
442 bool used
= final_status() == FINAL_STATUS_USED
||
443 final_status() == FINAL_STATUS_WOULD_HAVE_BEEN_USED
;
444 prerender_manager_
->RecordNetworkBytes(origin(), used
, network_bytes_
);
446 // Broadcast the removal of aliases.
447 for (content::RenderProcessHost::iterator host_iterator
=
448 content::RenderProcessHost::AllHostsIterator();
449 !host_iterator
.IsAtEnd();
450 host_iterator
.Advance()) {
451 content::RenderProcessHost
* host
= host_iterator
.GetCurrentValue();
452 host
->Send(new PrerenderMsg_OnPrerenderRemoveAliases(alias_urls_
));
455 // If we still have a WebContents, clean up anything we need to and then
457 if (prerender_contents_
.get())
458 delete ReleasePrerenderContents();
461 void PrerenderContents::AddObserver(Observer
* observer
) {
462 DCHECK_EQ(FINAL_STATUS_MAX
, final_status_
);
463 observer_list_
.AddObserver(observer
);
466 void PrerenderContents::RemoveObserver(Observer
* observer
) {
467 observer_list_
.RemoveObserver(observer
);
470 void PrerenderContents::Observe(int type
,
471 const content::NotificationSource
& source
,
472 const content::NotificationDetails
& details
) {
474 // TODO(davidben): Try to remove this in favor of relying on
475 // FINAL_STATUS_PROFILE_DESTROYED.
476 case chrome::NOTIFICATION_APP_TERMINATING
:
477 Destroy(FINAL_STATUS_APP_TERMINATING
);
480 case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED
: {
481 if (prerender_contents_
.get()) {
482 DCHECK_EQ(content::Source
<WebContents
>(source
).ptr(),
483 prerender_contents_
.get());
485 content::Details
<RenderViewHost
> new_render_view_host(details
);
486 OnRenderViewHostCreated(new_render_view_host
.ptr());
488 // Make sure the size of the RenderViewHost has been passed to the new
489 // RenderView. Otherwise, the size may not be sent until the
490 // RenderViewReady event makes it from the render process to the UI
491 // thread of the browser process. When the RenderView receives its
492 // size, is also sets itself to be visible, which would then break the
494 new_render_view_host
->WasResized();
495 prerender_contents_
->WasHidden();
501 NOTREACHED() << "Unexpected notification sent.";
506 void PrerenderContents::OnRenderViewHostCreated(
507 RenderViewHost
* new_render_view_host
) {
510 WebContents
* PrerenderContents::CreateWebContents(
511 SessionStorageNamespace
* session_storage_namespace
) {
512 // TODO(ajwong): Remove the temporary map once prerendering is aware of
513 // multiple session storage namespaces per tab.
514 content::SessionStorageNamespaceMap session_storage_namespace_map
;
515 session_storage_namespace_map
[std::string()] = session_storage_namespace
;
516 return WebContents::CreateWithSessionStorage(
517 WebContents::CreateParams(profile_
), session_storage_namespace_map
);
520 void PrerenderContents::NotifyPrerenderStart() {
521 DCHECK_EQ(FINAL_STATUS_MAX
, final_status_
);
522 FOR_EACH_OBSERVER(Observer
, observer_list_
, OnPrerenderStart(this));
525 void PrerenderContents::NotifyPrerenderStopLoading() {
526 FOR_EACH_OBSERVER(Observer
, observer_list_
, OnPrerenderStopLoading(this));
529 void PrerenderContents::NotifyPrerenderDomContentLoaded() {
530 FOR_EACH_OBSERVER(Observer
, observer_list_
,
531 OnPrerenderDomContentLoaded(this));
534 void PrerenderContents::NotifyPrerenderStop() {
535 DCHECK_NE(FINAL_STATUS_MAX
, final_status_
);
536 FOR_EACH_OBSERVER(Observer
, observer_list_
, OnPrerenderStop(this));
537 observer_list_
.Clear();
540 void PrerenderContents::NotifyPrerenderCreatedMatchCompleteReplacement(
541 PrerenderContents
* replacement
) {
542 FOR_EACH_OBSERVER(Observer
, observer_list_
,
543 OnPrerenderCreatedMatchCompleteReplacement(this,
547 bool PrerenderContents::OnMessageReceived(const IPC::Message
& message
) {
549 // The following messages we do want to consume.
550 IPC_BEGIN_MESSAGE_MAP(PrerenderContents
, message
)
551 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CancelPrerenderForPrinting
,
552 OnCancelPrerenderForPrinting
)
553 IPC_MESSAGE_UNHANDLED(handled
= false)
554 IPC_END_MESSAGE_MAP()
559 bool PrerenderContents::CheckURL(const GURL
& url
) {
560 if (!url
.SchemeIsHTTPOrHTTPS()) {
561 DCHECK_NE(MATCH_COMPLETE_REPLACEMENT_PENDING
, match_complete_status_
);
562 Destroy(FINAL_STATUS_UNSUPPORTED_SCHEME
);
565 if (match_complete_status_
!= MATCH_COMPLETE_REPLACEMENT_PENDING
&&
566 prerender_manager_
->HasRecentlyBeenNavigatedTo(origin(), url
)) {
567 Destroy(FINAL_STATUS_RECENTLY_VISITED
);
573 bool PrerenderContents::AddAliasURL(const GURL
& url
) {
577 alias_urls_
.push_back(url
);
579 for (content::RenderProcessHost::iterator host_iterator
=
580 content::RenderProcessHost::AllHostsIterator();
581 !host_iterator
.IsAtEnd();
582 host_iterator
.Advance()) {
583 content::RenderProcessHost
* host
= host_iterator
.GetCurrentValue();
584 host
->Send(new PrerenderMsg_OnPrerenderAddAlias(url
));
590 bool PrerenderContents::Matches(
592 const SessionStorageNamespace
* session_storage_namespace
) const {
593 if (session_storage_namespace
&&
594 session_storage_namespace_id_
!= session_storage_namespace
->id()) {
597 return std::count_if(alias_urls_
.begin(), alias_urls_
.end(),
598 std::bind2nd(std::equal_to
<GURL
>(), url
)) != 0;
601 void PrerenderContents::RenderProcessGone(base::TerminationStatus status
) {
602 Destroy(FINAL_STATUS_RENDERER_CRASHED
);
605 void PrerenderContents::RenderFrameCreated(
606 content::RenderFrameHost
* render_frame_host
) {
607 // When a new RenderFrame is created for a prerendering WebContents, tell the
608 // new RenderFrame it's being used for prerendering before any navigations
609 // occur. Note that this is always triggered before the first navigation, so
610 // there's no need to send the message just after the WebContents is created.
611 render_frame_host
->Send(new PrerenderMsg_SetIsPrerendering(
612 render_frame_host
->GetRoutingID(), true));
615 void PrerenderContents::DidStopLoading(
616 content::RenderViewHost
* render_view_host
) {
617 has_stopped_loading_
= true;
618 NotifyPrerenderStopLoading();
621 void PrerenderContents::DocumentLoadedInFrame(
622 content::RenderFrameHost
* render_frame_host
) {
623 if (!render_frame_host
->GetParent())
624 NotifyPrerenderDomContentLoaded();
627 void PrerenderContents::DidStartProvisionalLoadForFrame(
628 content::RenderFrameHost
* render_frame_host
,
629 const GURL
& validated_url
,
631 bool is_iframe_srcdoc
) {
632 if (!render_frame_host
->GetParent()) {
633 if (!CheckURL(validated_url
))
636 // Usually, this event fires if the user clicks or enters a new URL.
637 // Neither of these can happen in the case of an invisible prerender.
638 // So the cause is: Some JavaScript caused a new URL to be loaded. In that
639 // case, the spinner would start again in the browser, so we must reset
640 // has_stopped_loading_ so that the spinner won't be stopped.
641 has_stopped_loading_
= false;
642 has_finished_loading_
= false;
646 void PrerenderContents::DidFinishLoad(
647 content::RenderFrameHost
* render_frame_host
,
648 const GURL
& validated_url
) {
649 if (!render_frame_host
->GetParent())
650 has_finished_loading_
= true;
653 void PrerenderContents::DidNavigateMainFrame(
654 const content::LoadCommittedDetails
& details
,
655 const content::FrameNavigateParams
& params
) {
656 // If the prerender made a second navigation entry, abort the prerender. This
657 // avoids having to correctly implement a complex history merging case (this
658 // interacts with location.replace) and correctly synchronize with the
659 // renderer. The final status may be monitored to see we need to revisit this
660 // decision. This does not affect client redirects as those do not push new
661 // history entries. (Calls to location.replace, navigations before onload, and
662 // <meta http-equiv=refresh> with timeouts under 1 second do not create
663 // entries in Blink.)
664 if (prerender_contents_
->GetController().GetEntryCount() > 1) {
665 Destroy(FINAL_STATUS_NEW_NAVIGATION_ENTRY
);
669 // Add each redirect as an alias. |params.url| is included in
670 // |params.redirects|.
672 // TODO(davidben): We do not correctly patch up history for renderer-initated
673 // navigations which add history entries. http://crbug.com/305660.
674 for (size_t i
= 0; i
< params
.redirects
.size(); i
++) {
675 if (!AddAliasURL(params
.redirects
[i
]))
680 void PrerenderContents::DidGetRedirectForResourceRequest(
681 RenderViewHost
* render_view_host
,
682 const content::ResourceRedirectDetails
& details
) {
683 // DidGetRedirectForResourceRequest can come for any resource on a page. If
684 // it's a redirect on the top-level resource, the name needs to be remembered
685 // for future matching, and if it redirects to an https resource, it needs to
686 // be canceled. If a subresource is redirected, nothing changes.
687 if (details
.resource_type
!= content::RESOURCE_TYPE_MAIN_FRAME
)
689 CheckURL(details
.new_url
);
692 void PrerenderContents::Destroy(FinalStatus final_status
) {
693 DCHECK_NE(final_status
, FINAL_STATUS_USED
);
695 if (prerendering_has_been_cancelled_
)
698 SetFinalStatus(final_status
);
700 prerendering_has_been_cancelled_
= true;
701 prerender_manager_
->AddToHistory(this);
702 prerender_manager_
->MoveEntryToPendingDelete(this, final_status
);
704 // Note that if this PrerenderContents was made into a MatchComplete
705 // replacement by MoveEntryToPendingDelete, NotifyPrerenderStop will
706 // not reach the PrerenderHandle. Rather
707 // OnPrerenderCreatedMatchCompleteReplacement will propogate that
708 // information to the referer.
709 if (!prerender_manager_
->IsControlGroup(experiment_id()) &&
710 (prerendering_has_started() ||
711 match_complete_status() == MATCH_COMPLETE_REPLACEMENT
)) {
712 NotifyPrerenderStop();
716 base::ProcessMetrics
* PrerenderContents::MaybeGetProcessMetrics() {
717 if (process_metrics_
.get() == NULL
) {
718 // If a PrenderContents hasn't started prerending, don't be fully formed.
719 if (!GetRenderViewHost() || !GetRenderViewHost()->GetProcess())
721 base::ProcessHandle handle
= GetRenderViewHost()->GetProcess()->GetHandle();
722 if (handle
== base::kNullProcessHandle
)
724 #if !defined(OS_MACOSX)
725 process_metrics_
.reset(base::ProcessMetrics::CreateProcessMetrics(handle
));
727 process_metrics_
.reset(base::ProcessMetrics::CreateProcessMetrics(
729 content::BrowserChildProcessHost::GetPortProvider()));
733 return process_metrics_
.get();
736 void PrerenderContents::DestroyWhenUsingTooManyResources() {
737 base::ProcessMetrics
* metrics
= MaybeGetProcessMetrics();
741 size_t private_bytes
, shared_bytes
;
742 if (metrics
->GetMemoryBytes(&private_bytes
, &shared_bytes
) &&
743 private_bytes
> prerender_manager_
->config().max_bytes
) {
744 Destroy(FINAL_STATUS_MEMORY_LIMIT_EXCEEDED
);
748 WebContents
* PrerenderContents::ReleasePrerenderContents() {
749 prerender_contents_
->SetDelegate(NULL
);
750 content::WebContentsObserver::Observe(NULL
);
751 if (alias_session_storage_namespace
)
752 alias_session_storage_namespace
->RemoveTransactionLogProcessId(child_id_
);
753 return prerender_contents_
.release();
756 RenderViewHost
* PrerenderContents::GetRenderViewHostMutable() {
757 return const_cast<RenderViewHost
*>(GetRenderViewHost());
760 const RenderViewHost
* PrerenderContents::GetRenderViewHost() const {
761 if (!prerender_contents_
.get())
763 return prerender_contents_
->GetRenderViewHost();
766 void PrerenderContents::DidNavigate(
767 const history::HistoryAddPageArgs
& add_page_args
) {
768 add_page_vector_
.push_back(add_page_args
);
771 void PrerenderContents::CommitHistory(WebContents
* tab
) {
772 HistoryTabHelper
* history_tab_helper
= HistoryTabHelper::FromWebContents(tab
);
773 for (size_t i
= 0; i
< add_page_vector_
.size(); ++i
)
774 history_tab_helper
->UpdateHistoryForNavigation(add_page_vector_
[i
]);
777 base::Value
* PrerenderContents::GetAsValue() const {
778 if (!prerender_contents_
.get())
780 base::DictionaryValue
* dict_value
= new base::DictionaryValue();
781 dict_value
->SetString("url", prerender_url_
.spec());
782 base::TimeTicks current_time
= base::TimeTicks::Now();
783 base::TimeDelta duration
= current_time
- load_start_time_
;
784 dict_value
->SetInteger("duration", duration
.InSeconds());
785 dict_value
->SetBoolean("is_loaded", prerender_contents_
&&
786 !prerender_contents_
->IsLoading());
790 bool PrerenderContents::IsCrossSiteNavigationPending() const {
791 if (!prerender_contents_
)
793 return (prerender_contents_
->GetSiteInstance() !=
794 prerender_contents_
->GetPendingSiteInstance());
797 void PrerenderContents::PrepareForUse() {
798 SetFinalStatus(FINAL_STATUS_USED
);
800 if (prerender_contents_
.get()) {
801 prerender_contents_
->SendToAllFrames(
802 new PrerenderMsg_SetIsPrerendering(MSG_ROUTING_NONE
, false));
805 NotifyPrerenderStop();
807 BrowserThread::PostTask(
810 base::Bind(&ResumeThrottles
, resource_throttles_
));
811 resource_throttles_
.clear();
814 SessionStorageNamespace
* PrerenderContents::GetSessionStorageNamespace() const {
815 if (!prerender_contents())
817 return prerender_contents()->GetController().
818 GetDefaultSessionStorageNamespace();
821 void PrerenderContents::OnCancelPrerenderForPrinting() {
822 Destroy(FINAL_STATUS_WINDOW_PRINT
);
825 void PrerenderContents::RecordCookieEvent(CookieEvent event
,
826 bool is_main_frame_http_request
,
827 bool is_third_party_cookie
,
828 bool is_for_blocking_resource
,
829 base::Time earliest_create_date
) {
830 // We don't care about sent cookies that were created after this prerender
832 // The reason is that for the purpose of the histograms emitted, we only care
833 // about cookies that existed before the prerender was started, but not
834 // about cookies that were created as part of the prerender. Using the
835 // earliest creation timestamp of all cookies provided by the cookie monster
836 // is a heuristic that yields the desired result pretty closely.
837 // In particular, we pretend no other WebContents make changes to the cookies
838 // relevant to the prerender, which may not actually always be the case, but
839 // hopefully most of the times.
840 if (event
== COOKIE_EVENT_SEND
&& earliest_create_date
> start_time_
)
843 InternalCookieEvent internal_event
= INTERNAL_COOKIE_EVENT_MAX
;
845 if (is_main_frame_http_request
) {
846 if (event
== COOKIE_EVENT_SEND
) {
847 internal_event
= INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND
;
849 internal_event
= INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE
;
852 if (event
== COOKIE_EVENT_SEND
) {
853 internal_event
= INTERNAL_COOKIE_EVENT_OTHER_SEND
;
855 internal_event
= INTERNAL_COOKIE_EVENT_OTHER_CHANGE
;
859 DCHECK_GE(internal_event
, 0);
860 DCHECK_LT(internal_event
, INTERNAL_COOKIE_EVENT_MAX
);
862 cookie_status_
|= (1 << internal_event
);
864 DCHECK_GE(cookie_status_
, 0);
865 DCHECK_LT(cookie_status_
, kNumCookieStatuses
);
867 CookieSendType send_type
= COOKIE_SEND_TYPE_NONE
;
868 if (event
== COOKIE_EVENT_SEND
) {
869 if (!is_third_party_cookie
) {
870 send_type
= COOKIE_SEND_TYPE_FIRST_PARTY
;
872 if (is_for_blocking_resource
) {
873 send_type
= COOKIE_SEND_TYPE_THIRD_PARTY_BLOCKING_RESOURCE
;
875 send_type
= COOKIE_SEND_TYPE_THIRD_PARTY
;
879 DCHECK_GE(send_type
, 0);
880 DCHECK_LT(send_type
, COOKIE_SEND_TYPE_MAX
);
882 if (cookie_send_type_
< send_type
)
883 cookie_send_type_
= send_type
;
886 void PrerenderContents::AddResourceThrottle(
887 const base::WeakPtr
<PrerenderResourceThrottle
>& throttle
) {
888 resource_throttles_
.push_back(throttle
);
891 void PrerenderContents::AddNetworkBytes(int64 bytes
) {
892 network_bytes_
+= bytes
;
895 } // namespace prerender