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 "base/bind.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/history/history_tab_helper.h"
15 #include "chrome/browser/history/history_types.h"
16 #include "chrome/browser/prerender/prerender_field_trial.h"
17 #include "chrome/browser/prerender/prerender_final_status.h"
18 #include "chrome/browser/prerender/prerender_handle.h"
19 #include "chrome/browser/prerender/prerender_manager.h"
20 #include "chrome/browser/prerender/prerender_manager_factory.h"
21 #include "chrome/browser/prerender/prerender_resource_throttle.h"
22 #include "chrome/browser/prerender/prerender_tracker.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/browser/ui/tab_helpers.h"
26 #include "chrome/common/prerender_messages.h"
27 #include "chrome/common/render_messages.h"
28 #include "chrome/common/url_constants.h"
29 #include "content/public/browser/browser_child_process_host.h"
30 #include "content/public/browser/browser_thread.h"
31 #include "content/public/browser/notification_service.h"
32 #include "content/public/browser/render_frame_host.h"
33 #include "content/public/browser/render_process_host.h"
34 #include "content/public/browser/render_view_host.h"
35 #include "content/public/browser/resource_request_details.h"
36 #include "content/public/browser/session_storage_namespace.h"
37 #include "content/public/browser/web_contents.h"
38 #include "content/public/browser/web_contents_delegate.h"
39 #include "content/public/browser/web_contents_view.h"
40 #include "content/public/common/frame_navigate_params.h"
41 #include "ui/gfx/rect.h"
43 using content::DownloadItem
;
44 using content::OpenURLParams
;
45 using content::RenderViewHost
;
46 using content::ResourceRedirectDetails
;
47 using content::SessionStorageNamespace
;
48 using content::WebContents
;
54 // Internal cookie event.
55 // Whenever a prerender interacts with the cookie store, either sending
56 // existing cookies that existed before the prerender started, or when a cookie
57 // is changed, we record these events for histogramming purposes.
58 enum InternalCookieEvent
{
59 INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND
= 0,
60 INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE
= 1,
61 INTERNAL_COOKIE_EVENT_OTHER_SEND
= 2,
62 INTERNAL_COOKIE_EVENT_OTHER_CHANGE
= 3,
63 INTERNAL_COOKIE_EVENT_MAX
67 std::vector
<base::WeakPtr
<PrerenderResourceThrottle
> > throttles
) {
68 for (size_t i
= 0; i
< throttles
.size(); i
++) {
70 throttles
[i
]->Resume();
77 const int PrerenderContents::kNumCookieStatuses
=
78 (1 << INTERNAL_COOKIE_EVENT_MAX
);
80 class PrerenderContentsFactoryImpl
: public PrerenderContents::Factory
{
82 virtual PrerenderContents
* CreatePrerenderContents(
83 PrerenderManager
* prerender_manager
, Profile
* profile
,
84 const GURL
& url
, const content::Referrer
& referrer
,
85 Origin origin
, uint8 experiment_id
) OVERRIDE
{
86 return new PrerenderContents(prerender_manager
, profile
,
87 url
, referrer
, origin
, experiment_id
);
91 // WebContentsDelegateImpl -----------------------------------------------------
93 class PrerenderContents::WebContentsDelegateImpl
94 : public content::WebContentsDelegate
{
96 explicit WebContentsDelegateImpl(PrerenderContents
* prerender_contents
)
97 : prerender_contents_(prerender_contents
) {
100 // content::WebContentsDelegate implementation:
101 virtual WebContents
* OpenURLFromTab(WebContents
* source
,
102 const OpenURLParams
& params
) OVERRIDE
{
103 // |OpenURLFromTab| is typically called when a frame performs a navigation
104 // that requires the browser to perform the transition instead of WebKit.
105 // Examples include prerendering a site that redirects to an app URL,
106 // or if --enable-strict-site-isolation is specified and the prerendered
107 // frame redirects to a different origin.
108 // TODO(cbentzel): Consider supporting this if it is a common case during
110 prerender_contents_
->Destroy(FINAL_STATUS_OPEN_URL
);
114 virtual void CanDownload(
115 RenderViewHost
* render_view_host
,
117 const std::string
& request_method
,
118 const base::Callback
<void(bool)>& callback
) OVERRIDE
{
119 prerender_contents_
->Destroy(FINAL_STATUS_DOWNLOAD
);
120 // Cancel the download.
124 virtual bool ShouldCreateWebContents(
125 WebContents
* web_contents
,
127 WindowContainerType window_container_type
,
128 const base::string16
& frame_name
,
129 const GURL
& target_url
,
130 const std::string
& partition_id
,
131 SessionStorageNamespace
* session_storage_namespace
) OVERRIDE
{
132 // Since we don't want to permit child windows that would have a
133 // window.opener property, terminate prerendering.
134 prerender_contents_
->Destroy(FINAL_STATUS_CREATE_NEW_WINDOW
);
139 virtual bool OnGoToEntryOffset(int offset
) OVERRIDE
{
140 // This isn't allowed because the history merge operation
141 // does not work if there are renderer issued challenges.
142 // TODO(cbentzel): Cancel in this case? May not need to do
143 // since render-issued offset navigations are not guaranteed,
144 // but indicates that the page cares about the history.
148 virtual void JSOutOfMemory(WebContents
* tab
) OVERRIDE
{
149 prerender_contents_
->Destroy(FINAL_STATUS_JS_OUT_OF_MEMORY
);
152 virtual bool ShouldSuppressDialogs() OVERRIDE
{
153 // We still want to show the user the message when they navigate to this
154 // page, so cancel this prerender.
155 prerender_contents_
->Destroy(FINAL_STATUS_JAVASCRIPT_ALERT
);
156 // Always suppress JavaScript messages if they're triggered by a page being
161 virtual void RegisterProtocolHandler(WebContents
* web_contents
,
162 const std::string
& protocol
,
164 const base::string16
& title
,
165 bool user_gesture
) OVERRIDE
{
166 // TODO(mmenke): Consider supporting this if it is a common case during
168 prerender_contents_
->Destroy(FINAL_STATUS_REGISTER_PROTOCOL_HANDLER
);
171 virtual gfx::Size
GetSizeForNewRenderView(
172 const WebContents
* web_contents
) const OVERRIDE
{
173 // Have to set the size of the RenderView on initialization to be sure it is
174 // set before the RenderView is hidden on all platforms (esp. Android).
175 return prerender_contents_
->size_
;
179 PrerenderContents
* prerender_contents_
;
182 void PrerenderContents::Observer::OnPrerenderStopLoading(
183 PrerenderContents
* contents
) {
186 void PrerenderContents::Observer::OnPrerenderDomContentLoaded(
187 PrerenderContents
* contents
) {
190 void PrerenderContents::Observer::OnPrerenderCreatedMatchCompleteReplacement(
191 PrerenderContents
* contents
, PrerenderContents
* replacement
) {
194 PrerenderContents::Observer::Observer() {
197 PrerenderContents::Observer::~Observer() {
200 PrerenderContents::PrerenderContents(
201 PrerenderManager
* prerender_manager
,
204 const content::Referrer
& referrer
,
207 : prerendering_has_started_(false),
208 session_storage_namespace_id_(-1),
209 prerender_manager_(prerender_manager
),
214 has_stopped_loading_(false),
215 has_finished_loading_(false),
216 final_status_(FINAL_STATUS_MAX
),
217 match_complete_status_(MATCH_COMPLETE_DEFAULT
),
218 prerendering_has_been_cancelled_(false),
222 experiment_id_(experiment_id
),
223 creator_child_id_(-1),
227 DCHECK(prerender_manager
!= NULL
);
230 PrerenderContents
* PrerenderContents::CreateMatchCompleteReplacement() {
231 PrerenderContents
* new_contents
= prerender_manager_
->CreatePrerenderContents(
232 prerender_url(), referrer(), origin(), experiment_id());
234 new_contents
->load_start_time_
= load_start_time_
;
235 new_contents
->session_storage_namespace_id_
= session_storage_namespace_id_
;
236 new_contents
->set_match_complete_status(
237 PrerenderContents::MATCH_COMPLETE_REPLACEMENT_PENDING
);
239 const bool did_init
= new_contents
->Init();
241 DCHECK_EQ(alias_urls_
.front(), new_contents
->alias_urls_
.front());
242 DCHECK_EQ(1u, new_contents
->alias_urls_
.size());
243 new_contents
->alias_urls_
= alias_urls_
;
244 new_contents
->set_match_complete_status(
245 PrerenderContents::MATCH_COMPLETE_REPLACEMENT
);
246 NotifyPrerenderCreatedMatchCompleteReplacement(new_contents
);
250 bool PrerenderContents::Init() {
251 return AddAliasURL(prerender_url_
);
255 PrerenderContents::Factory
* PrerenderContents::CreateFactory() {
256 return new PrerenderContentsFactoryImpl();
260 PrerenderContents
* PrerenderContents::FromWebContents(
261 content::WebContents
* web_contents
) {
264 PrerenderManager
* prerender_manager
= PrerenderManagerFactory::GetForProfile(
265 Profile::FromBrowserContext(web_contents
->GetBrowserContext()));
266 if (!prerender_manager
)
268 return prerender_manager
->GetPrerenderContents(web_contents
);
271 void PrerenderContents::StartPrerendering(
272 int creator_child_id
,
273 const gfx::Size
& size
,
274 SessionStorageNamespace
* session_storage_namespace
) {
275 DCHECK(profile_
!= NULL
);
276 DCHECK(!size
.IsEmpty());
277 DCHECK(!prerendering_has_started_
);
278 DCHECK(prerender_contents_
.get() == NULL
);
279 DCHECK_EQ(-1, creator_child_id_
);
280 DCHECK(size_
.IsEmpty());
281 DCHECK_EQ(1U, alias_urls_
.size());
283 creator_child_id_
= creator_child_id
;
284 session_storage_namespace_id_
= session_storage_namespace
->id();
287 DCHECK(load_start_time_
.is_null());
288 load_start_time_
= base::TimeTicks::Now();
289 start_time_
= base::Time::Now();
291 // Everything after this point sets up the WebContents object and associated
292 // RenderView for the prerender page. Don't do this for members of the
294 if (prerender_manager_
->IsControlGroup(experiment_id()))
297 if (origin_
== ORIGIN_LOCAL_PREDICTOR
&&
298 IsLocalPredictorPrerenderAlwaysControlEnabled()) {
302 prerendering_has_started_
= true;
304 alias_session_storage_namespace
= session_storage_namespace
->CreateAlias();
305 prerender_contents_
.reset(
306 CreateWebContents(alias_session_storage_namespace
.get()));
307 TabHelpers::AttachTabHelpers(prerender_contents_
.get());
308 content::WebContentsObserver::Observe(prerender_contents_
.get());
310 web_contents_delegate_
.reset(new WebContentsDelegateImpl(this));
311 prerender_contents_
.get()->SetDelegate(web_contents_delegate_
.get());
312 // Set the size of the prerender WebContents.
313 prerender_contents_
->GetView()->SizeContents(size_
);
315 child_id_
= GetRenderViewHost()->GetProcess()->GetID();
316 route_id_
= GetRenderViewHost()->GetRoutingID();
318 // Log transactions to see if we could merge session storage namespaces in
319 // the event of a mismatch.
320 alias_session_storage_namespace
->AddTransactionLogProcessId(child_id_
);
322 NotifyPrerenderStart();
324 // Close ourselves when the application is shutting down.
325 notification_registrar_
.Add(this, chrome::NOTIFICATION_APP_TERMINATING
,
326 content::NotificationService::AllSources());
328 // Register for our parent profile to shutdown, so we can shut ourselves down
329 // as well (should only be called for OTR profiles, as we should receive
330 // APP_TERMINATING before non-OTR profiles are destroyed).
331 // TODO(tburkard): figure out if this is needed.
332 notification_registrar_
.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED
,
333 content::Source
<Profile
>(profile_
));
335 // Register to inform new RenderViews that we're prerendering.
336 notification_registrar_
.Add(
337 this, content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED
,
338 content::Source
<WebContents
>(prerender_contents_
.get()));
340 // Transfer over the user agent override.
341 prerender_contents_
.get()->SetUserAgentOverride(
342 prerender_manager_
->config().user_agent_override
);
344 content::NavigationController::LoadURLParams
load_url_params(
346 load_url_params
.referrer
= referrer_
;
347 load_url_params
.transition_type
=
348 (origin_
== ORIGIN_OMNIBOX
|| origin_
== ORIGIN_INSTANT
)
349 ? content::PageTransitionFromInt(
350 content::PAGE_TRANSITION_TYPED
|
351 content::PAGE_TRANSITION_FROM_ADDRESS_BAR
)
352 : content::PAGE_TRANSITION_LINK
;
353 load_url_params
.override_user_agent
=
354 prerender_manager_
->config().is_overriding_user_agent
?
355 content::NavigationController::UA_OVERRIDE_TRUE
:
356 content::NavigationController::UA_OVERRIDE_FALSE
;
357 prerender_contents_
.get()->GetController().LoadURLWithParams(load_url_params
);
360 bool PrerenderContents::GetChildId(int* child_id
) const {
362 DCHECK_GE(child_id_
, -1);
363 *child_id
= child_id_
;
364 return child_id_
!= -1;
367 bool PrerenderContents::GetRouteId(int* route_id
) const {
369 DCHECK_GE(route_id_
, -1);
370 *route_id
= route_id_
;
371 return route_id_
!= -1;
374 void PrerenderContents::SetFinalStatus(FinalStatus final_status
) {
375 DCHECK_GE(final_status
, FINAL_STATUS_USED
);
376 DCHECK_LT(final_status
, FINAL_STATUS_MAX
);
378 DCHECK_EQ(FINAL_STATUS_MAX
, final_status_
);
380 final_status_
= final_status
;
383 PrerenderContents::~PrerenderContents() {
384 DCHECK_NE(FINAL_STATUS_MAX
, final_status());
386 prerendering_has_been_cancelled() || final_status() == FINAL_STATUS_USED
);
387 DCHECK_NE(ORIGIN_MAX
, origin());
388 // Since a lot of prerenders terminate before any meaningful cookie action
389 // would have happened, only record the cookie status for prerenders who
390 // were used, cancelled, or timed out.
391 if (prerendering_has_started_
&&
392 (final_status() == FINAL_STATUS_USED
||
393 final_status() == FINAL_STATUS_TIMED_OUT
||
394 final_status() == FINAL_STATUS_CANCELLED
)) {
395 prerender_manager_
->RecordCookieStatus(origin(), experiment_id(),
398 prerender_manager_
->RecordFinalStatusWithMatchCompleteStatus(
399 origin(), experiment_id(), match_complete_status(), final_status());
401 bool used
= final_status() == FINAL_STATUS_USED
||
402 final_status() == FINAL_STATUS_WOULD_HAVE_BEEN_USED
;
403 prerender_manager_
->RecordNetworkBytes(used
, network_bytes_
);
405 // Broadcast the removal of aliases.
406 for (content::RenderProcessHost::iterator host_iterator
=
407 content::RenderProcessHost::AllHostsIterator();
408 !host_iterator
.IsAtEnd();
409 host_iterator
.Advance()) {
410 content::RenderProcessHost
* host
= host_iterator
.GetCurrentValue();
411 host
->Send(new PrerenderMsg_OnPrerenderRemoveAliases(alias_urls_
));
414 // If we still have a WebContents, clean up anything we need to and then
416 if (prerender_contents_
.get())
417 delete ReleasePrerenderContents();
420 void PrerenderContents::AddObserver(Observer
* observer
) {
421 DCHECK_EQ(FINAL_STATUS_MAX
, final_status_
);
422 observer_list_
.AddObserver(observer
);
425 void PrerenderContents::RemoveObserver(Observer
* observer
) {
426 observer_list_
.RemoveObserver(observer
);
429 void PrerenderContents::Observe(int type
,
430 const content::NotificationSource
& source
,
431 const content::NotificationDetails
& details
) {
433 case chrome::NOTIFICATION_PROFILE_DESTROYED
:
434 Destroy(FINAL_STATUS_PROFILE_DESTROYED
);
437 case chrome::NOTIFICATION_APP_TERMINATING
:
438 Destroy(FINAL_STATUS_APP_TERMINATING
);
441 case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED
: {
442 if (prerender_contents_
.get()) {
443 DCHECK_EQ(content::Source
<WebContents
>(source
).ptr(),
444 prerender_contents_
.get());
446 content::Details
<RenderViewHost
> new_render_view_host(details
);
447 OnRenderViewHostCreated(new_render_view_host
.ptr());
449 // Make sure the size of the RenderViewHost has been passed to the new
450 // RenderView. Otherwise, the size may not be sent until the
451 // RenderViewReady event makes it from the render process to the UI
452 // thread of the browser process. When the RenderView receives its
453 // size, is also sets itself to be visible, which would then break the
455 new_render_view_host
->WasResized();
456 prerender_contents_
->WasHidden();
462 NOTREACHED() << "Unexpected notification sent.";
467 void PrerenderContents::OnRenderViewHostCreated(
468 RenderViewHost
* new_render_view_host
) {
471 WebContents
* PrerenderContents::CreateWebContents(
472 SessionStorageNamespace
* session_storage_namespace
) {
473 // TODO(ajwong): Remove the temporary map once prerendering is aware of
474 // multiple session storage namespaces per tab.
475 content::SessionStorageNamespaceMap session_storage_namespace_map
;
476 session_storage_namespace_map
[std::string()] = session_storage_namespace
;
477 return WebContents::CreateWithSessionStorage(
478 WebContents::CreateParams(profile_
), session_storage_namespace_map
);
481 void PrerenderContents::NotifyPrerenderStart() {
482 DCHECK_EQ(FINAL_STATUS_MAX
, final_status_
);
483 FOR_EACH_OBSERVER(Observer
, observer_list_
, OnPrerenderStart(this));
486 void PrerenderContents::NotifyPrerenderStopLoading() {
487 FOR_EACH_OBSERVER(Observer
, observer_list_
, OnPrerenderStopLoading(this));
490 void PrerenderContents::NotifyPrerenderDomContentLoaded() {
491 FOR_EACH_OBSERVER(Observer
, observer_list_
,
492 OnPrerenderDomContentLoaded(this));
495 void PrerenderContents::NotifyPrerenderStop() {
496 DCHECK_NE(FINAL_STATUS_MAX
, final_status_
);
497 FOR_EACH_OBSERVER(Observer
, observer_list_
, OnPrerenderStop(this));
498 observer_list_
.Clear();
501 void PrerenderContents::NotifyPrerenderCreatedMatchCompleteReplacement(
502 PrerenderContents
* replacement
) {
503 FOR_EACH_OBSERVER(Observer
, observer_list_
,
504 OnPrerenderCreatedMatchCompleteReplacement(this,
508 bool PrerenderContents::OnMessageReceived(const IPC::Message
& message
) {
510 // The following messages we do want to consume.
511 IPC_BEGIN_MESSAGE_MAP(PrerenderContents
, message
)
512 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CancelPrerenderForPrinting
,
513 OnCancelPrerenderForPrinting
)
514 IPC_MESSAGE_UNHANDLED(handled
= false)
515 IPC_END_MESSAGE_MAP()
520 bool PrerenderContents::CheckURL(const GURL
& url
) {
521 if (!url
.SchemeIsHTTPOrHTTPS()) {
522 DCHECK_NE(MATCH_COMPLETE_REPLACEMENT_PENDING
, match_complete_status_
);
523 Destroy(FINAL_STATUS_UNSUPPORTED_SCHEME
);
526 if (match_complete_status_
!= MATCH_COMPLETE_REPLACEMENT_PENDING
&&
527 prerender_manager_
->HasRecentlyBeenNavigatedTo(origin(), url
)) {
528 Destroy(FINAL_STATUS_RECENTLY_VISITED
);
534 bool PrerenderContents::AddAliasURL(const GURL
& url
) {
538 alias_urls_
.push_back(url
);
540 for (content::RenderProcessHost::iterator host_iterator
=
541 content::RenderProcessHost::AllHostsIterator();
542 !host_iterator
.IsAtEnd();
543 host_iterator
.Advance()) {
544 content::RenderProcessHost
* host
= host_iterator
.GetCurrentValue();
545 host
->Send(new PrerenderMsg_OnPrerenderAddAlias(url
));
551 bool PrerenderContents::Matches(
553 const SessionStorageNamespace
* session_storage_namespace
) const {
554 if (session_storage_namespace
&&
555 session_storage_namespace_id_
!= session_storage_namespace
->id()) {
558 return std::count_if(alias_urls_
.begin(), alias_urls_
.end(),
559 std::bind2nd(std::equal_to
<GURL
>(), url
)) != 0;
562 void PrerenderContents::RenderProcessGone(base::TerminationStatus status
) {
563 Destroy(FINAL_STATUS_RENDERER_CRASHED
);
566 void PrerenderContents::RenderFrameCreated(
567 content::RenderFrameHost
* render_frame_host
) {
568 // When a new RenderFrame is created for a prerendering WebContents, tell the
569 // new RenderFrame it's being used for prerendering before any navigations
570 // occur. Note that this is always triggered before the first navigation, so
571 // there's no need to send the message just after the WebContents is created.
572 render_frame_host
->Send(new PrerenderMsg_SetIsPrerendering(
573 render_frame_host
->GetRoutingID(), true));
576 void PrerenderContents::DidStopLoading(
577 content::RenderViewHost
* render_view_host
) {
578 has_stopped_loading_
= true;
579 NotifyPrerenderStopLoading();
582 void PrerenderContents::DocumentLoadedInFrame(
584 RenderViewHost
* render_view_host
) {
585 if (frame_id
== main_frame_id_
)
586 NotifyPrerenderDomContentLoaded();
589 void PrerenderContents::DidStartProvisionalLoadForFrame(
591 int64 parent_frame_id
,
593 const GURL
& validated_url
,
595 bool is_iframe_srcdoc
,
596 RenderViewHost
* render_view_host
) {
598 if (!CheckURL(validated_url
))
601 // Usually, this event fires if the user clicks or enters a new URL.
602 // Neither of these can happen in the case of an invisible prerender.
603 // So the cause is: Some JavaScript caused a new URL to be loaded. In that
604 // case, the spinner would start again in the browser, so we must reset
605 // has_stopped_loading_ so that the spinner won't be stopped.
606 has_stopped_loading_
= false;
607 has_finished_loading_
= false;
611 void PrerenderContents::DidCommitProvisionalLoadForFrame(
613 const base::string16
& frame_unique_name
,
616 content::PageTransition transition_type
,
617 RenderViewHost
* render_view_host
) {
619 main_frame_id_
= frame_id
;
623 void PrerenderContents::DidFinishLoad(int64 frame_id
,
624 const GURL
& validated_url
,
626 RenderViewHost
* render_view_host
) {
628 has_finished_loading_
= true;
631 void PrerenderContents::DidNavigateMainFrame(
632 const content::LoadCommittedDetails
& details
,
633 const content::FrameNavigateParams
& params
) {
634 // If the prerender made a second navigation entry, abort the prerender. This
635 // avoids having to correctly implement a complex history merging case (this
636 // interacts with location.replace) and correctly synchronize with the
637 // renderer. The final status may be monitored to see we need to revisit this
638 // decision. This does not affect client redirects as those do not push new
639 // history entries. (Calls to location.replace, navigations before onload, and
640 // <meta http-equiv=refresh> with timeouts under 1 second do not create
641 // entries in Blink.)
642 if (prerender_contents_
->GetController().GetEntryCount() > 1) {
643 Destroy(FINAL_STATUS_NEW_NAVIGATION_ENTRY
);
647 // Add each redirect as an alias. |params.url| is included in
648 // |params.redirects|.
650 // TODO(davidben): We do not correctly patch up history for renderer-initated
651 // navigations which add history entries. http://crbug.com/305660.
652 for (size_t i
= 0; i
< params
.redirects
.size(); i
++) {
653 if (!AddAliasURL(params
.redirects
[i
]))
658 void PrerenderContents::DidGetRedirectForResourceRequest(
659 RenderViewHost
* render_view_host
,
660 const content::ResourceRedirectDetails
& details
) {
661 // DidGetRedirectForResourceRequest can come for any resource on a page. If
662 // it's a redirect on the top-level resource, the name needs to be remembered
663 // for future matching, and if it redirects to an https resource, it needs to
664 // be canceled. If a subresource is redirected, nothing changes.
665 if (details
.resource_type
!= ResourceType::MAIN_FRAME
)
667 CheckURL(details
.new_url
);
670 void PrerenderContents::Destroy(FinalStatus final_status
) {
671 DCHECK_NE(final_status
, FINAL_STATUS_USED
);
673 if (prerendering_has_been_cancelled_
)
676 SetFinalStatus(final_status
);
678 prerendering_has_been_cancelled_
= true;
679 prerender_manager_
->AddToHistory(this);
680 prerender_manager_
->MoveEntryToPendingDelete(this, final_status
);
682 // Note that if this PrerenderContents was made into a MatchComplete
683 // replacement by MoveEntryToPendingDelete, NotifyPrerenderStop will
684 // not reach the PrerenderHandle. Rather
685 // OnPrerenderCreatedMatchCompleteReplacement will propogate that
686 // information to the referer.
687 if (!prerender_manager_
->IsControlGroup(experiment_id()) &&
688 (prerendering_has_started() ||
689 match_complete_status() == MATCH_COMPLETE_REPLACEMENT
)) {
690 NotifyPrerenderStop();
694 base::ProcessMetrics
* PrerenderContents::MaybeGetProcessMetrics() {
695 if (process_metrics_
.get() == NULL
) {
696 // If a PrenderContents hasn't started prerending, don't be fully formed.
697 if (!GetRenderViewHost() || !GetRenderViewHost()->GetProcess())
699 base::ProcessHandle handle
= GetRenderViewHost()->GetProcess()->GetHandle();
700 if (handle
== base::kNullProcessHandle
)
702 #if !defined(OS_MACOSX)
703 process_metrics_
.reset(base::ProcessMetrics::CreateProcessMetrics(handle
));
705 process_metrics_
.reset(base::ProcessMetrics::CreateProcessMetrics(
707 content::BrowserChildProcessHost::GetPortProvider()));
711 return process_metrics_
.get();
714 void PrerenderContents::DestroyWhenUsingTooManyResources() {
715 base::ProcessMetrics
* metrics
= MaybeGetProcessMetrics();
719 size_t private_bytes
, shared_bytes
;
720 if (metrics
->GetMemoryBytes(&private_bytes
, &shared_bytes
) &&
721 private_bytes
> prerender_manager_
->config().max_bytes
) {
722 Destroy(FINAL_STATUS_MEMORY_LIMIT_EXCEEDED
);
726 WebContents
* PrerenderContents::ReleasePrerenderContents() {
727 prerender_contents_
->SetDelegate(NULL
);
728 content::WebContentsObserver::Observe(NULL
);
729 if (alias_session_storage_namespace
)
730 alias_session_storage_namespace
->RemoveTransactionLogProcessId(child_id_
);
731 return prerender_contents_
.release();
734 RenderViewHost
* PrerenderContents::GetRenderViewHostMutable() {
735 return const_cast<RenderViewHost
*>(GetRenderViewHost());
738 const RenderViewHost
* PrerenderContents::GetRenderViewHost() const {
739 if (!prerender_contents_
.get())
741 return prerender_contents_
->GetRenderViewHost();
744 void PrerenderContents::DidNavigate(
745 const history::HistoryAddPageArgs
& add_page_args
) {
746 add_page_vector_
.push_back(add_page_args
);
749 void PrerenderContents::CommitHistory(WebContents
* tab
) {
750 HistoryTabHelper
* history_tab_helper
= HistoryTabHelper::FromWebContents(tab
);
751 for (size_t i
= 0; i
< add_page_vector_
.size(); ++i
)
752 history_tab_helper
->UpdateHistoryForNavigation(add_page_vector_
[i
]);
755 base::Value
* PrerenderContents::GetAsValue() const {
756 if (!prerender_contents_
.get())
758 base::DictionaryValue
* dict_value
= new base::DictionaryValue();
759 dict_value
->SetString("url", prerender_url_
.spec());
760 base::TimeTicks current_time
= base::TimeTicks::Now();
761 base::TimeDelta duration
= current_time
- load_start_time_
;
762 dict_value
->SetInteger("duration", duration
.InSeconds());
763 dict_value
->SetBoolean("is_loaded", prerender_contents_
&&
764 !prerender_contents_
->IsLoading());
768 bool PrerenderContents::IsCrossSiteNavigationPending() const {
769 if (!prerender_contents_
)
771 return (prerender_contents_
->GetSiteInstance() !=
772 prerender_contents_
->GetPendingSiteInstance());
775 void PrerenderContents::PrepareForUse() {
776 SetFinalStatus(FINAL_STATUS_USED
);
778 if (prerender_contents_
.get()) {
779 prerender_contents_
->SendToAllFrames(
780 new PrerenderMsg_SetIsPrerendering(MSG_ROUTING_NONE
, false));
783 NotifyPrerenderStop();
785 content::BrowserThread::PostTask(
786 content::BrowserThread::IO
,
788 base::Bind(&ResumeThrottles
, resource_throttles_
));
789 resource_throttles_
.clear();
792 SessionStorageNamespace
* PrerenderContents::GetSessionStorageNamespace() const {
793 if (!prerender_contents())
795 return prerender_contents()->GetController().
796 GetDefaultSessionStorageNamespace();
799 void PrerenderContents::OnCancelPrerenderForPrinting() {
800 Destroy(FINAL_STATUS_WINDOW_PRINT
);
803 void PrerenderContents::RecordCookieEvent(CookieEvent event
,
804 bool is_main_frame_http_request
,
805 base::Time earliest_create_date
) {
806 // We don't care about sent cookies that were created after this prerender
808 // The reason is that for the purpose of the histograms emitted, we only care
809 // about cookies that existed before the prerender was started, but not
810 // about cookies that were created as part of the prerender. Using the
811 // earliest creation timestamp of all cookies provided by the cookie monster
812 // is a heuristic that yields the desired result pretty closely.
813 // In particular, we pretend no other WebContents make changes to the cookies
814 // relevant to the prerender, which may not actually always be the case, but
815 // hopefully most of the times.
816 if (event
== COOKIE_EVENT_SEND
&& earliest_create_date
> start_time_
)
819 InternalCookieEvent internal_event
= INTERNAL_COOKIE_EVENT_MAX
;
821 if (is_main_frame_http_request
) {
822 if (event
== COOKIE_EVENT_SEND
) {
823 internal_event
= INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND
;
825 internal_event
= INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE
;
828 if (event
== COOKIE_EVENT_SEND
) {
829 internal_event
= INTERNAL_COOKIE_EVENT_OTHER_SEND
;
831 internal_event
= INTERNAL_COOKIE_EVENT_OTHER_CHANGE
;
835 DCHECK_GE(internal_event
, 0);
836 DCHECK_LT(internal_event
, INTERNAL_COOKIE_EVENT_MAX
);
838 cookie_status_
|= (1 << internal_event
);
840 DCHECK_GE(cookie_status_
, 0);
841 DCHECK_LT(cookie_status_
, kNumCookieStatuses
);
844 void PrerenderContents::AddResourceThrottle(
845 const base::WeakPtr
<PrerenderResourceThrottle
>& throttle
) {
846 resource_throttles_
.push_back(throttle
);
849 void PrerenderContents::AddNetworkBytes(int64 bytes
) {
850 network_bytes_
+= bytes
;
853 } // namespace prerender