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/browser/web_contents_view.h"
41 #include "content/public/common/frame_navigate_params.h"
42 #include "content/public/common/page_transition_types.h"
43 #include "ui/gfx/rect.h"
45 using content::DownloadItem;
46 using content::OpenURLParams;
47 using content::RenderViewHost;
48 using content::ResourceRedirectDetails;
49 using content::SessionStorageNamespace;
50 using content::WebContents;
56 // Internal cookie event.
57 // Whenever a prerender interacts with the cookie store, either sending
58 // existing cookies that existed before the prerender started, or when a cookie
59 // is changed, we record these events for histogramming purposes.
60 enum InternalCookieEvent {
61 INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND = 0,
62 INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE = 1,
63 INTERNAL_COOKIE_EVENT_OTHER_SEND = 2,
64 INTERNAL_COOKIE_EVENT_OTHER_CHANGE = 3,
65 INTERNAL_COOKIE_EVENT_MAX
68 // Indicates whether existing cookies were sent, and if they were third party
69 // cookies, and whether they were for blocking resources.
70 // Each value may be inclusive of previous values. We only care about the
71 // value with the highest index that has ever occurred in the course of a
74 COOKIE_SEND_TYPE_NONE = 0,
75 COOKIE_SEND_TYPE_FIRST_PARTY = 1,
76 COOKIE_SEND_TYPE_THIRD_PARTY = 2,
77 COOKIE_SEND_TYPE_THIRD_PARTY_BLOCKING_RESOURCE = 3,
82 std::vector<base::WeakPtr<PrerenderResourceThrottle> > throttles) {
83 for (size_t i = 0; i < throttles.size(); i++) {
85 throttles[i]->Resume();
92 const int PrerenderContents::kNumCookieStatuses =
93 (1 << INTERNAL_COOKIE_EVENT_MAX);
96 const int PrerenderContents::kNumCookieSendTypes = COOKIE_SEND_TYPE_MAX;
98 class PrerenderContentsFactoryImpl : public PrerenderContents::Factory {
100 virtual PrerenderContents* CreatePrerenderContents(
101 PrerenderManager* prerender_manager, Profile* profile,
102 const GURL& url, const content::Referrer& referrer,
103 Origin origin, uint8 experiment_id) OVERRIDE {
104 return new PrerenderContents(prerender_manager, profile,
105 url, referrer, origin, experiment_id);
109 // WebContentsDelegateImpl -----------------------------------------------------
111 class PrerenderContents::WebContentsDelegateImpl
112 : public content::WebContentsDelegate {
114 explicit WebContentsDelegateImpl(PrerenderContents* prerender_contents)
115 : prerender_contents_(prerender_contents) {
118 // content::WebContentsDelegate implementation:
119 virtual WebContents* OpenURLFromTab(WebContents* source,
120 const OpenURLParams& params) OVERRIDE {
121 // |OpenURLFromTab| is typically called when a frame performs a navigation
122 // that requires the browser to perform the transition instead of WebKit.
123 // Examples include prerendering a site that redirects to an app URL,
124 // or if --enable-strict-site-isolation is specified and the prerendered
125 // frame redirects to a different origin.
126 // TODO(cbentzel): Consider supporting this if it is a common case during
128 prerender_contents_->Destroy(FINAL_STATUS_OPEN_URL);
132 virtual void CanDownload(
133 RenderViewHost* render_view_host,
135 const std::string& request_method,
136 const base::Callback<void(bool)>& callback) OVERRIDE {
137 prerender_contents_->Destroy(FINAL_STATUS_DOWNLOAD);
138 // Cancel the download.
142 virtual bool ShouldCreateWebContents(
143 WebContents* web_contents,
145 WindowContainerType window_container_type,
146 const base::string16& frame_name,
147 const GURL& target_url,
148 const std::string& partition_id,
149 SessionStorageNamespace* session_storage_namespace) OVERRIDE {
150 // Since we don't want to permit child windows that would have a
151 // window.opener property, terminate prerendering.
152 prerender_contents_->Destroy(FINAL_STATUS_CREATE_NEW_WINDOW);
157 virtual bool OnGoToEntryOffset(int offset) OVERRIDE {
158 // This isn't allowed because the history merge operation
159 // does not work if there are renderer issued challenges.
160 // TODO(cbentzel): Cancel in this case? May not need to do
161 // since render-issued offset navigations are not guaranteed,
162 // but indicates that the page cares about the history.
166 virtual bool ShouldSuppressDialogs() OVERRIDE {
167 // We still want to show the user the message when they navigate to this
168 // page, so cancel this prerender.
169 prerender_contents_->Destroy(FINAL_STATUS_JAVASCRIPT_ALERT);
170 // Always suppress JavaScript messages if they're triggered by a page being
175 virtual void RegisterProtocolHandler(WebContents* web_contents,
176 const std::string& protocol,
178 const base::string16& title,
179 bool user_gesture) OVERRIDE {
180 // TODO(mmenke): Consider supporting this if it is a common case during
182 prerender_contents_->Destroy(FINAL_STATUS_REGISTER_PROTOCOL_HANDLER);
185 virtual gfx::Size GetSizeForNewRenderView(
186 const WebContents* web_contents) const OVERRIDE {
187 // Have to set the size of the RenderView on initialization to be sure it is
188 // set before the RenderView is hidden on all platforms (esp. Android).
189 return prerender_contents_->size_;
193 PrerenderContents* prerender_contents_;
196 void PrerenderContents::Observer::OnPrerenderStopLoading(
197 PrerenderContents* contents) {
200 void PrerenderContents::Observer::OnPrerenderDomContentLoaded(
201 PrerenderContents* contents) {
204 void PrerenderContents::Observer::OnPrerenderCreatedMatchCompleteReplacement(
205 PrerenderContents* contents, PrerenderContents* replacement) {
208 PrerenderContents::Observer::Observer() {
211 PrerenderContents::Observer::~Observer() {
214 PrerenderContents::PrerenderContents(
215 PrerenderManager* prerender_manager,
218 const content::Referrer& referrer,
221 : prerendering_has_started_(false),
222 session_storage_namespace_id_(-1),
223 prerender_manager_(prerender_manager),
228 has_stopped_loading_(false),
229 has_finished_loading_(false),
230 final_status_(FINAL_STATUS_MAX),
231 match_complete_status_(MATCH_COMPLETE_DEFAULT),
232 prerendering_has_been_cancelled_(false),
236 experiment_id_(experiment_id),
237 creator_child_id_(-1),
240 cookie_send_type_(COOKIE_SEND_TYPE_NONE),
242 DCHECK(prerender_manager != NULL);
245 PrerenderContents* PrerenderContents::CreateMatchCompleteReplacement() {
246 PrerenderContents* new_contents = prerender_manager_->CreatePrerenderContents(
247 prerender_url(), referrer(), origin(), experiment_id());
249 new_contents->load_start_time_ = load_start_time_;
250 new_contents->session_storage_namespace_id_ = session_storage_namespace_id_;
251 new_contents->set_match_complete_status(
252 PrerenderContents::MATCH_COMPLETE_REPLACEMENT_PENDING);
254 const bool did_init = new_contents->Init();
256 DCHECK_EQ(alias_urls_.front(), new_contents->alias_urls_.front());
257 DCHECK_EQ(1u, new_contents->alias_urls_.size());
258 new_contents->alias_urls_ = alias_urls_;
259 // Erase all but the first alias URL; the replacement has adopted the
260 // remainder without increasing the renderer-side reference count.
261 alias_urls_.resize(1);
262 new_contents->set_match_complete_status(
263 PrerenderContents::MATCH_COMPLETE_REPLACEMENT);
264 NotifyPrerenderCreatedMatchCompleteReplacement(new_contents);
268 bool PrerenderContents::Init() {
269 return AddAliasURL(prerender_url_);
273 PrerenderContents::Factory* PrerenderContents::CreateFactory() {
274 return new PrerenderContentsFactoryImpl();
278 PrerenderContents* PrerenderContents::FromWebContents(
279 content::WebContents* web_contents) {
282 PrerenderManager* prerender_manager = PrerenderManagerFactory::GetForProfile(
283 Profile::FromBrowserContext(web_contents->GetBrowserContext()));
284 if (!prerender_manager)
286 return prerender_manager->GetPrerenderContents(web_contents);
289 void PrerenderContents::StartPrerendering(
290 int creator_child_id,
291 const gfx::Size& size,
292 SessionStorageNamespace* session_storage_namespace) {
293 DCHECK(profile_ != NULL);
294 DCHECK(!size.IsEmpty());
295 DCHECK(!prerendering_has_started_);
296 DCHECK(prerender_contents_.get() == NULL);
297 DCHECK_EQ(-1, creator_child_id_);
298 DCHECK(size_.IsEmpty());
299 DCHECK_EQ(1U, alias_urls_.size());
301 creator_child_id_ = creator_child_id;
302 session_storage_namespace_id_ = session_storage_namespace->id();
305 DCHECK(load_start_time_.is_null());
306 load_start_time_ = base::TimeTicks::Now();
307 start_time_ = base::Time::Now();
309 // Everything after this point sets up the WebContents object and associated
310 // RenderView for the prerender page. Don't do this for members of the
312 if (prerender_manager_->IsControlGroup(experiment_id()))
315 if (origin_ == ORIGIN_LOCAL_PREDICTOR &&
316 IsLocalPredictorPrerenderAlwaysControlEnabled()) {
320 prerendering_has_started_ = true;
322 alias_session_storage_namespace = session_storage_namespace->CreateAlias();
323 prerender_contents_.reset(
324 CreateWebContents(alias_session_storage_namespace.get()));
325 TabHelpers::AttachTabHelpers(prerender_contents_.get());
326 content::WebContentsObserver::Observe(prerender_contents_.get());
328 web_contents_delegate_.reset(new WebContentsDelegateImpl(this));
329 prerender_contents_.get()->SetDelegate(web_contents_delegate_.get());
330 // Set the size of the prerender WebContents.
331 apps::ResizeWebContents(prerender_contents_.get(), size_);
333 child_id_ = GetRenderViewHost()->GetProcess()->GetID();
334 route_id_ = GetRenderViewHost()->GetRoutingID();
336 // Log transactions to see if we could merge session storage namespaces in
337 // the event of a mismatch.
338 alias_session_storage_namespace->AddTransactionLogProcessId(child_id_);
340 NotifyPrerenderStart();
342 // Close ourselves when the application is shutting down.
343 notification_registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
344 content::NotificationService::AllSources());
346 // Register to inform new RenderViews that we're prerendering.
347 notification_registrar_.Add(
348 this, content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
349 content::Source<WebContents>(prerender_contents_.get()));
351 // Transfer over the user agent override.
352 prerender_contents_.get()->SetUserAgentOverride(
353 prerender_manager_->config().user_agent_override);
355 content::NavigationController::LoadURLParams load_url_params(
357 load_url_params.referrer = referrer_;
358 load_url_params.transition_type = content::PAGE_TRANSITION_LINK;
359 if (origin_ == ORIGIN_OMNIBOX) {
360 load_url_params.transition_type = content::PageTransitionFromInt(
361 content::PAGE_TRANSITION_TYPED |
362 content::PAGE_TRANSITION_FROM_ADDRESS_BAR);
363 } else if (origin_ == ORIGIN_INSTANT) {
364 load_url_params.transition_type = content::PageTransitionFromInt(
365 content::PAGE_TRANSITION_GENERATED |
366 content::PAGE_TRANSITION_FROM_ADDRESS_BAR);
368 load_url_params.override_user_agent =
369 prerender_manager_->config().is_overriding_user_agent ?
370 content::NavigationController::UA_OVERRIDE_TRUE :
371 content::NavigationController::UA_OVERRIDE_FALSE;
372 prerender_contents_.get()->GetController().LoadURLWithParams(load_url_params);
375 bool PrerenderContents::GetChildId(int* child_id) const {
377 DCHECK_GE(child_id_, -1);
378 *child_id = child_id_;
379 return child_id_ != -1;
382 bool PrerenderContents::GetRouteId(int* route_id) const {
384 DCHECK_GE(route_id_, -1);
385 *route_id = route_id_;
386 return route_id_ != -1;
389 void PrerenderContents::SetFinalStatus(FinalStatus final_status) {
390 DCHECK_GE(final_status, FINAL_STATUS_USED);
391 DCHECK_LT(final_status, FINAL_STATUS_MAX);
393 DCHECK_EQ(FINAL_STATUS_MAX, final_status_);
395 final_status_ = final_status;
398 PrerenderContents::~PrerenderContents() {
399 DCHECK_NE(FINAL_STATUS_MAX, final_status());
401 prerendering_has_been_cancelled() || final_status() == FINAL_STATUS_USED);
402 DCHECK_NE(ORIGIN_MAX, origin());
403 // Since a lot of prerenders terminate before any meaningful cookie action
404 // would have happened, only record the cookie status for prerenders who
405 // were used, cancelled, or timed out.
406 if (prerendering_has_started_ && final_status() == FINAL_STATUS_USED) {
407 prerender_manager_->RecordCookieStatus(origin(), experiment_id(),
409 prerender_manager_->RecordCookieSendType(origin(), experiment_id(),
412 prerender_manager_->RecordFinalStatusWithMatchCompleteStatus(
413 origin(), experiment_id(), match_complete_status(), final_status());
415 bool used = final_status() == FINAL_STATUS_USED ||
416 final_status() == FINAL_STATUS_WOULD_HAVE_BEEN_USED;
417 prerender_manager_->RecordNetworkBytes(used, network_bytes_);
419 // Broadcast the removal of aliases.
420 for (content::RenderProcessHost::iterator host_iterator =
421 content::RenderProcessHost::AllHostsIterator();
422 !host_iterator.IsAtEnd();
423 host_iterator.Advance()) {
424 content::RenderProcessHost* host = host_iterator.GetCurrentValue();
425 host->Send(new PrerenderMsg_OnPrerenderRemoveAliases(alias_urls_));
428 // If we still have a WebContents, clean up anything we need to and then
430 if (prerender_contents_.get())
431 delete ReleasePrerenderContents();
434 void PrerenderContents::AddObserver(Observer* observer) {
435 DCHECK_EQ(FINAL_STATUS_MAX, final_status_);
436 observer_list_.AddObserver(observer);
439 void PrerenderContents::RemoveObserver(Observer* observer) {
440 observer_list_.RemoveObserver(observer);
443 void PrerenderContents::Observe(int type,
444 const content::NotificationSource& source,
445 const content::NotificationDetails& details) {
447 // TODO(davidben): Try to remove this in favor of relying on
448 // FINAL_STATUS_PROFILE_DESTROYED.
449 case chrome::NOTIFICATION_APP_TERMINATING:
450 Destroy(FINAL_STATUS_APP_TERMINATING);
453 case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: {
454 if (prerender_contents_.get()) {
455 DCHECK_EQ(content::Source<WebContents>(source).ptr(),
456 prerender_contents_.get());
458 content::Details<RenderViewHost> new_render_view_host(details);
459 OnRenderViewHostCreated(new_render_view_host.ptr());
461 // Make sure the size of the RenderViewHost has been passed to the new
462 // RenderView. Otherwise, the size may not be sent until the
463 // RenderViewReady event makes it from the render process to the UI
464 // thread of the browser process. When the RenderView receives its
465 // size, is also sets itself to be visible, which would then break the
467 new_render_view_host->WasResized();
468 prerender_contents_->WasHidden();
474 NOTREACHED() << "Unexpected notification sent.";
479 void PrerenderContents::OnRenderViewHostCreated(
480 RenderViewHost* new_render_view_host) {
483 WebContents* PrerenderContents::CreateWebContents(
484 SessionStorageNamespace* session_storage_namespace) {
485 // TODO(ajwong): Remove the temporary map once prerendering is aware of
486 // multiple session storage namespaces per tab.
487 content::SessionStorageNamespaceMap session_storage_namespace_map;
488 session_storage_namespace_map[std::string()] = session_storage_namespace;
489 return WebContents::CreateWithSessionStorage(
490 WebContents::CreateParams(profile_), session_storage_namespace_map);
493 void PrerenderContents::NotifyPrerenderStart() {
494 DCHECK_EQ(FINAL_STATUS_MAX, final_status_);
495 FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStart(this));
498 void PrerenderContents::NotifyPrerenderStopLoading() {
499 FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStopLoading(this));
502 void PrerenderContents::NotifyPrerenderDomContentLoaded() {
503 FOR_EACH_OBSERVER(Observer, observer_list_,
504 OnPrerenderDomContentLoaded(this));
507 void PrerenderContents::NotifyPrerenderStop() {
508 DCHECK_NE(FINAL_STATUS_MAX, final_status_);
509 FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStop(this));
510 observer_list_.Clear();
513 void PrerenderContents::NotifyPrerenderCreatedMatchCompleteReplacement(
514 PrerenderContents* replacement) {
515 FOR_EACH_OBSERVER(Observer, observer_list_,
516 OnPrerenderCreatedMatchCompleteReplacement(this,
520 bool PrerenderContents::OnMessageReceived(const IPC::Message& message) {
522 // The following messages we do want to consume.
523 IPC_BEGIN_MESSAGE_MAP(PrerenderContents, message)
524 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CancelPrerenderForPrinting,
525 OnCancelPrerenderForPrinting)
526 IPC_MESSAGE_UNHANDLED(handled = false)
527 IPC_END_MESSAGE_MAP()
532 bool PrerenderContents::CheckURL(const GURL& url) {
533 if (!url.SchemeIsHTTPOrHTTPS()) {
534 DCHECK_NE(MATCH_COMPLETE_REPLACEMENT_PENDING, match_complete_status_);
535 Destroy(FINAL_STATUS_UNSUPPORTED_SCHEME);
538 if (match_complete_status_ != MATCH_COMPLETE_REPLACEMENT_PENDING &&
539 prerender_manager_->HasRecentlyBeenNavigatedTo(origin(), url)) {
540 Destroy(FINAL_STATUS_RECENTLY_VISITED);
546 bool PrerenderContents::AddAliasURL(const GURL& url) {
550 alias_urls_.push_back(url);
552 for (content::RenderProcessHost::iterator host_iterator =
553 content::RenderProcessHost::AllHostsIterator();
554 !host_iterator.IsAtEnd();
555 host_iterator.Advance()) {
556 content::RenderProcessHost* host = host_iterator.GetCurrentValue();
557 host->Send(new PrerenderMsg_OnPrerenderAddAlias(url));
563 bool PrerenderContents::Matches(
565 const SessionStorageNamespace* session_storage_namespace) const {
566 if (session_storage_namespace &&
567 session_storage_namespace_id_ != session_storage_namespace->id()) {
570 return std::count_if(alias_urls_.begin(), alias_urls_.end(),
571 std::bind2nd(std::equal_to<GURL>(), url)) != 0;
574 void PrerenderContents::RenderProcessGone(base::TerminationStatus status) {
575 Destroy(FINAL_STATUS_RENDERER_CRASHED);
578 void PrerenderContents::RenderFrameCreated(
579 content::RenderFrameHost* render_frame_host) {
580 // When a new RenderFrame is created for a prerendering WebContents, tell the
581 // new RenderFrame it's being used for prerendering before any navigations
582 // occur. Note that this is always triggered before the first navigation, so
583 // there's no need to send the message just after the WebContents is created.
584 render_frame_host->Send(new PrerenderMsg_SetIsPrerendering(
585 render_frame_host->GetRoutingID(), true));
588 void PrerenderContents::DidStopLoading(
589 content::RenderViewHost* render_view_host) {
590 has_stopped_loading_ = true;
591 NotifyPrerenderStopLoading();
594 void PrerenderContents::DocumentLoadedInFrame(
596 RenderViewHost* render_view_host) {
597 if (frame_id == main_frame_id_)
598 NotifyPrerenderDomContentLoaded();
601 void PrerenderContents::DidStartProvisionalLoadForFrame(
603 int64 parent_frame_id,
605 const GURL& validated_url,
607 bool is_iframe_srcdoc,
608 RenderViewHost* render_view_host) {
610 if (!CheckURL(validated_url))
613 // Usually, this event fires if the user clicks or enters a new URL.
614 // Neither of these can happen in the case of an invisible prerender.
615 // So the cause is: Some JavaScript caused a new URL to be loaded. In that
616 // case, the spinner would start again in the browser, so we must reset
617 // has_stopped_loading_ so that the spinner won't be stopped.
618 has_stopped_loading_ = false;
619 has_finished_loading_ = false;
623 void PrerenderContents::DidCommitProvisionalLoadForFrame(
625 const base::string16& frame_unique_name,
628 content::PageTransition transition_type,
629 RenderViewHost* render_view_host) {
631 main_frame_id_ = frame_id;
635 void PrerenderContents::DidFinishLoad(int64 frame_id,
636 const GURL& validated_url,
638 RenderViewHost* render_view_host) {
640 has_finished_loading_ = true;
643 void PrerenderContents::DidNavigateMainFrame(
644 const content::LoadCommittedDetails& details,
645 const content::FrameNavigateParams& params) {
646 // If the prerender made a second navigation entry, abort the prerender. This
647 // avoids having to correctly implement a complex history merging case (this
648 // interacts with location.replace) and correctly synchronize with the
649 // renderer. The final status may be monitored to see we need to revisit this
650 // decision. This does not affect client redirects as those do not push new
651 // history entries. (Calls to location.replace, navigations before onload, and
652 // <meta http-equiv=refresh> with timeouts under 1 second do not create
653 // entries in Blink.)
654 if (prerender_contents_->GetController().GetEntryCount() > 1) {
655 Destroy(FINAL_STATUS_NEW_NAVIGATION_ENTRY);
659 // Add each redirect as an alias. |params.url| is included in
660 // |params.redirects|.
662 // TODO(davidben): We do not correctly patch up history for renderer-initated
663 // navigations which add history entries. http://crbug.com/305660.
664 for (size_t i = 0; i < params.redirects.size(); i++) {
665 if (!AddAliasURL(params.redirects[i]))
670 void PrerenderContents::DidGetRedirectForResourceRequest(
671 RenderViewHost* render_view_host,
672 const content::ResourceRedirectDetails& details) {
673 // DidGetRedirectForResourceRequest can come for any resource on a page. If
674 // it's a redirect on the top-level resource, the name needs to be remembered
675 // for future matching, and if it redirects to an https resource, it needs to
676 // be canceled. If a subresource is redirected, nothing changes.
677 if (details.resource_type != ResourceType::MAIN_FRAME)
679 CheckURL(details.new_url);
682 void PrerenderContents::Destroy(FinalStatus final_status) {
683 DCHECK_NE(final_status, FINAL_STATUS_USED);
685 if (prerendering_has_been_cancelled_)
688 SetFinalStatus(final_status);
690 prerendering_has_been_cancelled_ = true;
691 prerender_manager_->AddToHistory(this);
692 prerender_manager_->MoveEntryToPendingDelete(this, final_status);
694 // Note that if this PrerenderContents was made into a MatchComplete
695 // replacement by MoveEntryToPendingDelete, NotifyPrerenderStop will
696 // not reach the PrerenderHandle. Rather
697 // OnPrerenderCreatedMatchCompleteReplacement will propogate that
698 // information to the referer.
699 if (!prerender_manager_->IsControlGroup(experiment_id()) &&
700 (prerendering_has_started() ||
701 match_complete_status() == MATCH_COMPLETE_REPLACEMENT)) {
702 NotifyPrerenderStop();
706 base::ProcessMetrics* PrerenderContents::MaybeGetProcessMetrics() {
707 if (process_metrics_.get() == NULL) {
708 // If a PrenderContents hasn't started prerending, don't be fully formed.
709 if (!GetRenderViewHost() || !GetRenderViewHost()->GetProcess())
711 base::ProcessHandle handle = GetRenderViewHost()->GetProcess()->GetHandle();
712 if (handle == base::kNullProcessHandle)
714 #if !defined(OS_MACOSX)
715 process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(handle));
717 process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(
719 content::BrowserChildProcessHost::GetPortProvider()));
723 return process_metrics_.get();
726 void PrerenderContents::DestroyWhenUsingTooManyResources() {
727 base::ProcessMetrics* metrics = MaybeGetProcessMetrics();
731 size_t private_bytes, shared_bytes;
732 if (metrics->GetMemoryBytes(&private_bytes, &shared_bytes) &&
733 private_bytes > prerender_manager_->config().max_bytes) {
734 Destroy(FINAL_STATUS_MEMORY_LIMIT_EXCEEDED);
738 WebContents* PrerenderContents::ReleasePrerenderContents() {
739 prerender_contents_->SetDelegate(NULL);
740 content::WebContentsObserver::Observe(NULL);
741 if (alias_session_storage_namespace)
742 alias_session_storage_namespace->RemoveTransactionLogProcessId(child_id_);
743 return prerender_contents_.release();
746 RenderViewHost* PrerenderContents::GetRenderViewHostMutable() {
747 return const_cast<RenderViewHost*>(GetRenderViewHost());
750 const RenderViewHost* PrerenderContents::GetRenderViewHost() const {
751 if (!prerender_contents_.get())
753 return prerender_contents_->GetRenderViewHost();
756 void PrerenderContents::DidNavigate(
757 const history::HistoryAddPageArgs& add_page_args) {
758 add_page_vector_.push_back(add_page_args);
761 void PrerenderContents::CommitHistory(WebContents* tab) {
762 HistoryTabHelper* history_tab_helper = HistoryTabHelper::FromWebContents(tab);
763 for (size_t i = 0; i < add_page_vector_.size(); ++i)
764 history_tab_helper->UpdateHistoryForNavigation(add_page_vector_[i]);
767 base::Value* PrerenderContents::GetAsValue() const {
768 if (!prerender_contents_.get())
770 base::DictionaryValue* dict_value = new base::DictionaryValue();
771 dict_value->SetString("url", prerender_url_.spec());
772 base::TimeTicks current_time = base::TimeTicks::Now();
773 base::TimeDelta duration = current_time - load_start_time_;
774 dict_value->SetInteger("duration", duration.InSeconds());
775 dict_value->SetBoolean("is_loaded", prerender_contents_ &&
776 !prerender_contents_->IsLoading());
780 bool PrerenderContents::IsCrossSiteNavigationPending() const {
781 if (!prerender_contents_)
783 return (prerender_contents_->GetSiteInstance() !=
784 prerender_contents_->GetPendingSiteInstance());
787 void PrerenderContents::PrepareForUse() {
788 SetFinalStatus(FINAL_STATUS_USED);
790 if (prerender_contents_.get()) {
791 prerender_contents_->SendToAllFrames(
792 new PrerenderMsg_SetIsPrerendering(MSG_ROUTING_NONE, false));
795 NotifyPrerenderStop();
797 content::BrowserThread::PostTask(
798 content::BrowserThread::IO,
800 base::Bind(&ResumeThrottles, resource_throttles_));
801 resource_throttles_.clear();
804 SessionStorageNamespace* PrerenderContents::GetSessionStorageNamespace() const {
805 if (!prerender_contents())
807 return prerender_contents()->GetController().
808 GetDefaultSessionStorageNamespace();
811 void PrerenderContents::OnCancelPrerenderForPrinting() {
812 Destroy(FINAL_STATUS_WINDOW_PRINT);
815 void PrerenderContents::RecordCookieEvent(CookieEvent event,
816 bool is_main_frame_http_request,
817 bool is_third_party_cookie,
818 bool is_for_blocking_resource,
819 base::Time earliest_create_date) {
820 // We don't care about sent cookies that were created after this prerender
822 // The reason is that for the purpose of the histograms emitted, we only care
823 // about cookies that existed before the prerender was started, but not
824 // about cookies that were created as part of the prerender. Using the
825 // earliest creation timestamp of all cookies provided by the cookie monster
826 // is a heuristic that yields the desired result pretty closely.
827 // In particular, we pretend no other WebContents make changes to the cookies
828 // relevant to the prerender, which may not actually always be the case, but
829 // hopefully most of the times.
830 if (event == COOKIE_EVENT_SEND && earliest_create_date > start_time_)
833 InternalCookieEvent internal_event = INTERNAL_COOKIE_EVENT_MAX;
835 if (is_main_frame_http_request) {
836 if (event == COOKIE_EVENT_SEND) {
837 internal_event = INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND;
839 internal_event = INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE;
842 if (event == COOKIE_EVENT_SEND) {
843 internal_event = INTERNAL_COOKIE_EVENT_OTHER_SEND;
845 internal_event = INTERNAL_COOKIE_EVENT_OTHER_CHANGE;
849 DCHECK_GE(internal_event, 0);
850 DCHECK_LT(internal_event, INTERNAL_COOKIE_EVENT_MAX);
852 cookie_status_ |= (1 << internal_event);
854 DCHECK_GE(cookie_status_, 0);
855 DCHECK_LT(cookie_status_, kNumCookieStatuses);
857 CookieSendType send_type = COOKIE_SEND_TYPE_NONE;
858 if (event == COOKIE_EVENT_SEND) {
859 if (!is_third_party_cookie) {
860 send_type = COOKIE_SEND_TYPE_FIRST_PARTY;
862 if (is_for_blocking_resource) {
863 send_type = COOKIE_SEND_TYPE_THIRD_PARTY_BLOCKING_RESOURCE;
865 send_type = COOKIE_SEND_TYPE_THIRD_PARTY;
869 DCHECK_GE(send_type, 0);
870 DCHECK_LT(send_type, COOKIE_SEND_TYPE_MAX);
872 if (cookie_send_type_ < send_type)
873 cookie_send_type_ = send_type;
876 void PrerenderContents::AddResourceThrottle(
877 const base::WeakPtr<PrerenderResourceThrottle>& throttle) {
878 resource_throttles_.push_back(throttle);
881 void PrerenderContents::AddNetworkBytes(int64 bytes) {
882 network_bytes_ += bytes;
885 } // namespace prerender