Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / prerender / prerender_contents.cc
blobd444ff31b1efd6dae7f6672d2e405d18957ff176
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"
7 #include <algorithm>
8 #include <functional>
9 #include <utility>
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/favicon/favicon_tab_helper.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_tracker.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/ui/browser.h"
24 #include "chrome/browser/ui/browser_tab_contents.h"
25 #include "chrome/common/prerender_messages.h"
26 #include "chrome/common/render_messages.h"
27 #include "chrome/common/url_constants.h"
28 #include "content/public/browser/browser_child_process_host.h"
29 #include "content/public/browser/notification_service.h"
30 #include "content/public/browser/render_frame_host.h"
31 #include "content/public/browser/render_process_host.h"
32 #include "content/public/browser/render_view_host.h"
33 #include "content/public/browser/resource_request_details.h"
34 #include "content/public/browser/session_storage_namespace.h"
35 #include "content/public/browser/web_contents.h"
36 #include "content/public/browser/web_contents_delegate.h"
37 #include "content/public/browser/web_contents_view.h"
38 #include "content/public/common/favicon_url.h"
39 #include "content/public/common/frame_navigate_params.h"
40 #include "ui/gfx/rect.h"
42 using content::DownloadItem;
43 using content::OpenURLParams;
44 using content::RenderViewHost;
45 using content::ResourceRedirectDetails;
46 using content::SessionStorageNamespace;
47 using content::WebContents;
49 namespace prerender {
51 namespace {
53 // Internal cookie event.
54 // Whenever a prerender interacts with the cookie store, either sending
55 // existing cookies that existed before the prerender started, or when a cookie
56 // is changed, we record these events for histogramming purposes.
57 enum InternalCookieEvent {
58 INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND = 0,
59 INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE = 1,
60 INTERNAL_COOKIE_EVENT_OTHER_SEND = 2,
61 INTERNAL_COOKIE_EVENT_OTHER_CHANGE = 3,
62 INTERNAL_COOKIE_EVENT_MAX
65 } // namespace
67 // static
68 const int PrerenderContents::kNumCookieStatuses =
69 (1 << INTERNAL_COOKIE_EVENT_MAX);
71 class PrerenderContentsFactoryImpl : public PrerenderContents::Factory {
72 public:
73 virtual PrerenderContents* CreatePrerenderContents(
74 PrerenderManager* prerender_manager, Profile* profile,
75 const GURL& url, const content::Referrer& referrer,
76 Origin origin, uint8 experiment_id) OVERRIDE {
77 return new PrerenderContents(prerender_manager, profile,
78 url, referrer, origin, experiment_id);
82 // WebContentsDelegateImpl -----------------------------------------------------
84 class PrerenderContents::WebContentsDelegateImpl
85 : public content::WebContentsDelegate {
86 public:
87 explicit WebContentsDelegateImpl(PrerenderContents* prerender_contents)
88 : prerender_contents_(prerender_contents) {
91 // content::WebContentsDelegate implementation:
92 virtual WebContents* OpenURLFromTab(WebContents* source,
93 const OpenURLParams& params) OVERRIDE {
94 // |OpenURLFromTab| is typically called when a frame performs a navigation
95 // that requires the browser to perform the transition instead of WebKit.
96 // Examples include prerendering a site that redirects to an app URL,
97 // or if --enable-strict-site-isolation is specified and the prerendered
98 // frame redirects to a different origin.
99 // TODO(cbentzel): Consider supporting this if it is a common case during
100 // prerenders.
101 prerender_contents_->Destroy(FINAL_STATUS_OPEN_URL);
102 return NULL;
105 virtual void CanDownload(
106 RenderViewHost* render_view_host,
107 int request_id,
108 const std::string& request_method,
109 const base::Callback<void(bool)>& callback) OVERRIDE {
110 prerender_contents_->Destroy(FINAL_STATUS_DOWNLOAD);
111 // Cancel the download.
112 callback.Run(false);
115 virtual bool ShouldCreateWebContents(
116 WebContents* web_contents,
117 int route_id,
118 WindowContainerType window_container_type,
119 const base::string16& frame_name,
120 const GURL& target_url,
121 const std::string& partition_id,
122 SessionStorageNamespace* session_storage_namespace) OVERRIDE {
123 // Since we don't want to permit child windows that would have a
124 // window.opener property, terminate prerendering.
125 prerender_contents_->Destroy(FINAL_STATUS_CREATE_NEW_WINDOW);
126 // Cancel the popup.
127 return false;
130 virtual bool OnGoToEntryOffset(int offset) OVERRIDE {
131 // This isn't allowed because the history merge operation
132 // does not work if there are renderer issued challenges.
133 // TODO(cbentzel): Cancel in this case? May not need to do
134 // since render-issued offset navigations are not guaranteed,
135 // but indicates that the page cares about the history.
136 return false;
139 virtual void JSOutOfMemory(WebContents* tab) OVERRIDE {
140 prerender_contents_->Destroy(FINAL_STATUS_JS_OUT_OF_MEMORY);
143 virtual bool ShouldSuppressDialogs() OVERRIDE {
144 // We still want to show the user the message when they navigate to this
145 // page, so cancel this prerender.
146 prerender_contents_->Destroy(FINAL_STATUS_JAVASCRIPT_ALERT);
147 // Always suppress JavaScript messages if they're triggered by a page being
148 // prerendered.
149 return true;
152 virtual void RegisterProtocolHandler(WebContents* web_contents,
153 const std::string& protocol,
154 const GURL& url,
155 const base::string16& title,
156 bool user_gesture) OVERRIDE {
157 // TODO(mmenke): Consider supporting this if it is a common case during
158 // prerenders.
159 prerender_contents_->Destroy(FINAL_STATUS_REGISTER_PROTOCOL_HANDLER);
162 private:
163 PrerenderContents* prerender_contents_;
166 void PrerenderContents::Observer::OnPrerenderStopLoading(
167 PrerenderContents* contents) {
170 void PrerenderContents::Observer::OnPrerenderCreatedMatchCompleteReplacement(
171 PrerenderContents* contents, PrerenderContents* replacement) {
174 PrerenderContents::Observer::Observer() {
177 PrerenderContents::Observer::~Observer() {
180 PrerenderContents::PendingPrerenderInfo::PendingPrerenderInfo(
181 base::WeakPtr<PrerenderHandle> weak_prerender_handle,
182 Origin origin,
183 const GURL& url,
184 const content::Referrer& referrer,
185 const gfx::Size& size)
186 : weak_prerender_handle(weak_prerender_handle),
187 origin(origin),
188 url(url),
189 referrer(referrer),
190 size(size) {
193 PrerenderContents::PendingPrerenderInfo::~PendingPrerenderInfo() {
196 void PrerenderContents::AddPendingPrerender(
197 scoped_ptr<PendingPrerenderInfo> pending_prerender_info) {
198 pending_prerenders_.push_back(pending_prerender_info.release());
201 void PrerenderContents::PrepareForUse() {
202 if (prerender_contents_.get()) {
203 prerender_contents_->SendToAllFrames(
204 new PrerenderMsg_SetIsPrerendering(MSG_ROUTING_NONE, false));
207 NotifyPrerenderStop();
209 SessionStorageNamespace* session_storage_namespace = NULL;
210 if (prerender_contents_) {
211 // TODO(ajwong): This does not correctly handle storage for isolated apps.
212 session_storage_namespace = prerender_contents_->
213 GetController().GetDefaultSessionStorageNamespace();
215 prerender_manager_->StartPendingPrerenders(
216 child_id_, &pending_prerenders_, session_storage_namespace);
217 pending_prerenders_.clear();
220 PrerenderContents::PrerenderContents(
221 PrerenderManager* prerender_manager,
222 Profile* profile,
223 const GURL& url,
224 const content::Referrer& referrer,
225 Origin origin,
226 uint8 experiment_id)
227 : prerendering_has_started_(false),
228 session_storage_namespace_id_(-1),
229 prerender_manager_(prerender_manager),
230 prerender_url_(url),
231 referrer_(referrer),
232 profile_(profile),
233 page_id_(0),
234 has_stopped_loading_(false),
235 has_finished_loading_(false),
236 final_status_(FINAL_STATUS_MAX),
237 match_complete_status_(MATCH_COMPLETE_DEFAULT),
238 prerendering_has_been_cancelled_(false),
239 child_id_(-1),
240 route_id_(-1),
241 origin_(origin),
242 experiment_id_(experiment_id),
243 creator_child_id_(-1),
244 cookie_status_(0) {
245 DCHECK(prerender_manager != NULL);
248 PrerenderContents* PrerenderContents::CreateMatchCompleteReplacement() {
249 PrerenderContents* new_contents = prerender_manager_->CreatePrerenderContents(
250 prerender_url(), referrer(), origin(), experiment_id());
252 new_contents->load_start_time_ = load_start_time_;
253 new_contents->session_storage_namespace_id_ = session_storage_namespace_id_;
254 new_contents->set_match_complete_status(
255 PrerenderContents::MATCH_COMPLETE_REPLACEMENT_PENDING);
257 const bool did_init = new_contents->Init();
258 DCHECK(did_init);
259 DCHECK_EQ(alias_urls_.front(), new_contents->alias_urls_.front());
260 DCHECK_EQ(1u, new_contents->alias_urls_.size());
261 new_contents->alias_urls_ = alias_urls_;
262 new_contents->set_match_complete_status(
263 PrerenderContents::MATCH_COMPLETE_REPLACEMENT);
264 NotifyPrerenderCreatedMatchCompleteReplacement(new_contents);
265 return new_contents;
268 bool PrerenderContents::Init() {
269 return AddAliasURL(prerender_url_);
272 // static
273 PrerenderContents::Factory* PrerenderContents::CreateFactory() {
274 return new PrerenderContentsFactoryImpl();
277 // static
278 PrerenderContents* PrerenderContents::FromWebContents(
279 content::WebContents* web_contents) {
280 if (!web_contents)
281 return NULL;
282 PrerenderManager* prerender_manager = PrerenderManagerFactory::GetForProfile(
283 Profile::FromBrowserContext(web_contents->GetBrowserContext()));
284 if (!prerender_manager)
285 return NULL;
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();
303 size_ = size;
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
311 // control group.
312 if (prerender_manager_->IsControlGroup(experiment_id()))
313 return;
315 if (origin_ == ORIGIN_LOCAL_PREDICTOR &&
316 IsLocalPredictorPrerenderAlwaysControlEnabled()) {
317 return;
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 BrowserTabContents::AttachTabHelpers(prerender_contents_.get());
326 #if defined(OS_ANDROID)
327 // Delay icon fetching until the contents are getting swapped in
328 // to conserve network usage in mobile devices.
329 FaviconTabHelper::FromWebContents(
330 prerender_contents_.get())->set_should_fetch_icons(false);
331 #endif // defined(OS_ANDROID)
332 content::WebContentsObserver::Observe(prerender_contents_.get());
334 web_contents_delegate_.reset(new WebContentsDelegateImpl(this));
335 prerender_contents_.get()->SetDelegate(web_contents_delegate_.get());
336 // Set the size of the prerender WebContents.
337 prerender_contents_->GetView()->SizeContents(size_);
339 child_id_ = GetRenderViewHost()->GetProcess()->GetID();
340 route_id_ = GetRenderViewHost()->GetRoutingID();
342 // Log transactions to see if we could merge session storage namespaces in
343 // the event of a mismatch.
344 alias_session_storage_namespace->AddTransactionLogProcessId(child_id_);
346 // Register this with the ResourceDispatcherHost as a prerender
347 // RenderViewHost. This must be done before the Navigate message to catch all
348 // resource requests, but as it is on the same thread as the Navigate message
349 // (IO) there is no race condition.
350 AddObserver(prerender_manager()->prerender_tracker());
351 NotifyPrerenderStart();
353 // Close ourselves when the application is shutting down.
354 notification_registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
355 content::NotificationService::AllSources());
357 // Register for our parent profile to shutdown, so we can shut ourselves down
358 // as well (should only be called for OTR profiles, as we should receive
359 // APP_TERMINATING before non-OTR profiles are destroyed).
360 // TODO(tburkard): figure out if this is needed.
361 notification_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
362 content::Source<Profile>(profile_));
364 // Register to inform new RenderViews that we're prerendering.
365 notification_registrar_.Add(
366 this, content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
367 content::Source<WebContents>(prerender_contents_.get()));
369 // Transfer over the user agent override.
370 prerender_contents_.get()->SetUserAgentOverride(
371 prerender_manager_->config().user_agent_override);
373 content::NavigationController::LoadURLParams load_url_params(
374 prerender_url_);
375 load_url_params.referrer = referrer_;
376 load_url_params.transition_type =
377 (origin_ == ORIGIN_OMNIBOX || origin_ == ORIGIN_INSTANT)
378 ? content::PageTransitionFromInt(
379 content::PAGE_TRANSITION_TYPED |
380 content::PAGE_TRANSITION_FROM_ADDRESS_BAR)
381 : content::PAGE_TRANSITION_LINK;
382 load_url_params.override_user_agent =
383 prerender_manager_->config().is_overriding_user_agent ?
384 content::NavigationController::UA_OVERRIDE_TRUE :
385 content::NavigationController::UA_OVERRIDE_FALSE;
386 prerender_contents_.get()->GetController().LoadURLWithParams(load_url_params);
389 bool PrerenderContents::GetChildId(int* child_id) const {
390 CHECK(child_id);
391 DCHECK_GE(child_id_, -1);
392 *child_id = child_id_;
393 return child_id_ != -1;
396 bool PrerenderContents::GetRouteId(int* route_id) const {
397 CHECK(route_id);
398 DCHECK_GE(route_id_, -1);
399 *route_id = route_id_;
400 return route_id_ != -1;
403 void PrerenderContents::SetFinalStatus(FinalStatus final_status) {
404 DCHECK(final_status >= FINAL_STATUS_USED && final_status < FINAL_STATUS_MAX);
405 DCHECK(final_status_ == FINAL_STATUS_MAX);
407 final_status_ = final_status;
410 PrerenderContents::~PrerenderContents() {
411 DCHECK_NE(FINAL_STATUS_MAX, final_status());
412 DCHECK(
413 prerendering_has_been_cancelled() || final_status() == FINAL_STATUS_USED);
414 DCHECK_NE(ORIGIN_MAX, origin());
415 prerender_manager_->RecordCookieStatus(origin(), experiment_id(),
416 cookie_status_);
417 prerender_manager_->RecordFinalStatusWithMatchCompleteStatus(
418 origin(), experiment_id(), match_complete_status(), final_status());
420 // Broadcast the removal of aliases.
421 for (content::RenderProcessHost::iterator host_iterator =
422 content::RenderProcessHost::AllHostsIterator();
423 !host_iterator.IsAtEnd();
424 host_iterator.Advance()) {
425 content::RenderProcessHost* host = host_iterator.GetCurrentValue();
426 host->Send(new PrerenderMsg_OnPrerenderRemoveAliases(alias_urls_));
429 // If we still have a WebContents, clean up anything we need to and then
430 // destroy it.
431 if (prerender_contents_.get())
432 delete ReleasePrerenderContents();
435 void PrerenderContents::AddObserver(Observer* observer) {
436 DCHECK_EQ(FINAL_STATUS_MAX, final_status_);
437 observer_list_.AddObserver(observer);
440 void PrerenderContents::RemoveObserver(Observer* observer) {
441 observer_list_.RemoveObserver(observer);
444 void PrerenderContents::Observe(int type,
445 const content::NotificationSource& source,
446 const content::NotificationDetails& details) {
447 switch (type) {
448 case chrome::NOTIFICATION_PROFILE_DESTROYED:
449 Destroy(FINAL_STATUS_PROFILE_DESTROYED);
450 return;
452 case chrome::NOTIFICATION_APP_TERMINATING:
453 Destroy(FINAL_STATUS_APP_TERMINATING);
454 return;
456 case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: {
457 if (prerender_contents_.get()) {
458 DCHECK_EQ(content::Source<WebContents>(source).ptr(),
459 prerender_contents_.get());
461 content::Details<RenderViewHost> new_render_view_host(details);
462 OnRenderViewHostCreated(new_render_view_host.ptr());
464 // Make sure the size of the RenderViewHost has been passed to the new
465 // RenderView. Otherwise, the size may not be sent until the
466 // RenderViewReady event makes it from the render process to the UI
467 // thread of the browser process. When the RenderView receives its
468 // size, is also sets itself to be visible, which would then break the
469 // visibility API.
470 new_render_view_host->WasResized();
471 prerender_contents_->WasHidden();
473 break;
476 default:
477 NOTREACHED() << "Unexpected notification sent.";
478 break;
482 void PrerenderContents::OnRenderViewHostCreated(
483 RenderViewHost* new_render_view_host) {
486 size_t PrerenderContents::pending_prerender_count() const {
487 return pending_prerenders_.size();
490 WebContents* PrerenderContents::CreateWebContents(
491 SessionStorageNamespace* session_storage_namespace) {
492 // TODO(ajwong): Remove the temporary map once prerendering is aware of
493 // multiple session storage namespaces per tab.
494 content::SessionStorageNamespaceMap session_storage_namespace_map;
495 session_storage_namespace_map[std::string()] = session_storage_namespace;
496 return WebContents::CreateWithSessionStorage(
497 WebContents::CreateParams(profile_), session_storage_namespace_map);
500 void PrerenderContents::NotifyPrerenderStart() {
501 DCHECK_EQ(FINAL_STATUS_MAX, final_status_);
502 FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStart(this));
505 void PrerenderContents::NotifyPrerenderStopLoading() {
506 FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStopLoading(this));
509 void PrerenderContents::NotifyPrerenderStop() {
510 DCHECK_NE(FINAL_STATUS_MAX, final_status_);
511 FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStop(this));
512 observer_list_.Clear();
515 void PrerenderContents::NotifyPrerenderCreatedMatchCompleteReplacement(
516 PrerenderContents* replacement) {
517 FOR_EACH_OBSERVER(Observer, observer_list_,
518 OnPrerenderCreatedMatchCompleteReplacement(this,
519 replacement));
522 void PrerenderContents::DidUpdateFaviconURL(
523 int32 page_id,
524 const std::vector<content::FaviconURL>& urls) {
525 VLOG(1) << "PrerenderContents::OnUpdateFaviconURL" << icon_url_;
526 for (std::vector<content::FaviconURL>::const_iterator it = urls.begin();
527 it != urls.end(); ++it) {
528 if (it->icon_type == content::FaviconURL::FAVICON) {
529 icon_url_ = it->icon_url;
530 VLOG(1) << icon_url_;
531 return;
536 bool PrerenderContents::OnMessageReceived(const IPC::Message& message) {
537 bool handled = true;
538 // The following messages we do want to consume.
539 IPC_BEGIN_MESSAGE_MAP(PrerenderContents, message)
540 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CancelPrerenderForPrinting,
541 OnCancelPrerenderForPrinting)
542 IPC_MESSAGE_UNHANDLED(handled = false)
543 IPC_END_MESSAGE_MAP()
545 return handled;
548 bool PrerenderContents::CheckURL(const GURL& url) {
549 if (!url.SchemeIsHTTPOrHTTPS()) {
550 DCHECK_NE(MATCH_COMPLETE_REPLACEMENT_PENDING, match_complete_status_);
551 Destroy(FINAL_STATUS_UNSUPPORTED_SCHEME);
552 return false;
554 if (match_complete_status_ != MATCH_COMPLETE_REPLACEMENT_PENDING &&
555 prerender_manager_->HasRecentlyBeenNavigatedTo(origin(), url)) {
556 Destroy(FINAL_STATUS_RECENTLY_VISITED);
557 return false;
559 return true;
562 bool PrerenderContents::AddAliasURL(const GURL& url) {
563 if (!CheckURL(url))
564 return false;
566 alias_urls_.push_back(url);
568 for (content::RenderProcessHost::iterator host_iterator =
569 content::RenderProcessHost::AllHostsIterator();
570 !host_iterator.IsAtEnd();
571 host_iterator.Advance()) {
572 content::RenderProcessHost* host = host_iterator.GetCurrentValue();
573 host->Send(new PrerenderMsg_OnPrerenderAddAlias(url));
576 return true;
579 bool PrerenderContents::Matches(
580 const GURL& url,
581 const SessionStorageNamespace* session_storage_namespace) const {
582 if (session_storage_namespace &&
583 session_storage_namespace_id_ != session_storage_namespace->id()) {
584 return false;
586 return std::count_if(alias_urls_.begin(), alias_urls_.end(),
587 std::bind2nd(std::equal_to<GURL>(), url)) != 0;
590 void PrerenderContents::RenderProcessGone(base::TerminationStatus status) {
591 Destroy(FINAL_STATUS_RENDERER_CRASHED);
594 void PrerenderContents::RenderFrameCreated(
595 content::RenderFrameHost* render_frame_host) {
596 // When a new RenderFrame is created for a prerendering WebContents, tell the
597 // new RenderFrame it's being used for prerendering before any navigations
598 // occur. Note that this is always triggered before the first navigation, so
599 // there's no need to send the message just after the WebContents is created.
600 render_frame_host->Send(new PrerenderMsg_SetIsPrerendering(
601 render_frame_host->GetRoutingID(), true));
604 void PrerenderContents::DidStopLoading(
605 content::RenderViewHost* render_view_host) {
606 has_stopped_loading_ = true;
607 NotifyPrerenderStopLoading();
610 void PrerenderContents::DidStartProvisionalLoadForFrame(
611 int64 frame_id,
612 int64 parent_frame_id,
613 bool is_main_frame,
614 const GURL& validated_url,
615 bool is_error_page,
616 bool is_iframe_srcdoc,
617 RenderViewHost* render_view_host) {
618 if (is_main_frame) {
619 if (!CheckURL(validated_url))
620 return;
622 // Usually, this event fires if the user clicks or enters a new URL.
623 // Neither of these can happen in the case of an invisible prerender.
624 // So the cause is: Some JavaScript caused a new URL to be loaded. In that
625 // case, the spinner would start again in the browser, so we must reset
626 // has_stopped_loading_ so that the spinner won't be stopped.
627 has_stopped_loading_ = false;
628 has_finished_loading_ = false;
632 void PrerenderContents::DidFinishLoad(int64 frame_id,
633 const GURL& validated_url,
634 bool is_main_frame,
635 RenderViewHost* render_view_host) {
636 if (is_main_frame)
637 has_finished_loading_ = true;
640 void PrerenderContents::DidNavigateMainFrame(
641 const content::LoadCommittedDetails& details,
642 const content::FrameNavigateParams& params) {
643 // If the prerender made a second navigation entry, abort the prerender. This
644 // avoids having to correctly implement a complex history merging case (this
645 // interacts with location.replace) and correctly synchronize with the
646 // renderer. The final status may be monitored to see we need to revisit this
647 // decision. This does not affect client redirects as those do not push new
648 // history entries. (Calls to location.replace, navigations before onload, and
649 // <meta http-equiv=refresh> with timeouts under 1 second do not create
650 // entries in Blink.)
651 if (prerender_contents_->GetController().GetEntryCount() > 1) {
652 Destroy(FINAL_STATUS_NEW_NAVIGATION_ENTRY);
653 return;
656 // Add each redirect as an alias. |params.url| is included in
657 // |params.redirects|.
659 // TODO(davidben): We do not correctly patch up history for renderer-initated
660 // navigations which add history entries. http://crbug.com/305660.
661 for (size_t i = 0; i < params.redirects.size(); i++) {
662 if (!AddAliasURL(params.redirects[i]))
663 return;
667 void PrerenderContents::DidGetRedirectForResourceRequest(
668 RenderViewHost* render_view_host,
669 const content::ResourceRedirectDetails& details) {
670 // DidGetRedirectForResourceRequest can come for any resource on a page. If
671 // it's a redirect on the top-level resource, the name needs to be remembered
672 // for future matching, and if it redirects to an https resource, it needs to
673 // be canceled. If a subresource is redirected, nothing changes.
674 if (details.resource_type != ResourceType::MAIN_FRAME)
675 return;
676 CheckURL(details.new_url);
679 void PrerenderContents::Destroy(FinalStatus final_status) {
680 DCHECK_NE(final_status, FINAL_STATUS_USED);
682 if (prerendering_has_been_cancelled_)
683 return;
685 SetFinalStatus(final_status);
687 prerendering_has_been_cancelled_ = true;
688 prerender_manager_->AddToHistory(this);
689 prerender_manager_->MoveEntryToPendingDelete(this, final_status);
691 // Note that if this PrerenderContents was made into a MatchComplete
692 // replacement by MoveEntryToPendingDelete, NotifyPrerenderStop will
693 // not reach the PrerenderHandle. Rather
694 // OnPrerenderCreatedMatchCompleteReplacement will propogate that
695 // information to the referer.
696 if (!prerender_manager_->IsControlGroup(experiment_id()) &&
697 (prerendering_has_started() ||
698 match_complete_status() == MATCH_COMPLETE_REPLACEMENT)) {
699 NotifyPrerenderStop();
703 base::ProcessMetrics* PrerenderContents::MaybeGetProcessMetrics() {
704 if (process_metrics_.get() == NULL) {
705 // If a PrenderContents hasn't started prerending, don't be fully formed.
706 if (!GetRenderViewHost() || !GetRenderViewHost()->GetProcess())
707 return NULL;
708 base::ProcessHandle handle = GetRenderViewHost()->GetProcess()->GetHandle();
709 if (handle == base::kNullProcessHandle)
710 return NULL;
711 #if !defined(OS_MACOSX)
712 process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(handle));
713 #else
714 process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(
715 handle,
716 content::BrowserChildProcessHost::GetPortProvider()));
717 #endif
720 return process_metrics_.get();
723 void PrerenderContents::DestroyWhenUsingTooManyResources() {
724 base::ProcessMetrics* metrics = MaybeGetProcessMetrics();
725 if (metrics == NULL)
726 return;
728 size_t private_bytes, shared_bytes;
729 if (metrics->GetMemoryBytes(&private_bytes, &shared_bytes) &&
730 private_bytes > prerender_manager_->config().max_bytes) {
731 Destroy(FINAL_STATUS_MEMORY_LIMIT_EXCEEDED);
735 WebContents* PrerenderContents::ReleasePrerenderContents() {
736 prerender_contents_->SetDelegate(NULL);
737 content::WebContentsObserver::Observe(NULL);
738 if (alias_session_storage_namespace)
739 alias_session_storage_namespace->RemoveTransactionLogProcessId(child_id_);
740 return prerender_contents_.release();
743 RenderViewHost* PrerenderContents::GetRenderViewHostMutable() {
744 return const_cast<RenderViewHost*>(GetRenderViewHost());
747 const RenderViewHost* PrerenderContents::GetRenderViewHost() const {
748 if (!prerender_contents_.get())
749 return NULL;
750 return prerender_contents_->GetRenderViewHost();
753 void PrerenderContents::DidNavigate(
754 const history::HistoryAddPageArgs& add_page_args) {
755 add_page_vector_.push_back(add_page_args);
758 void PrerenderContents::CommitHistory(WebContents* tab) {
759 HistoryTabHelper* history_tab_helper = HistoryTabHelper::FromWebContents(tab);
760 for (size_t i = 0; i < add_page_vector_.size(); ++i)
761 history_tab_helper->UpdateHistoryForNavigation(add_page_vector_[i]);
764 base::Value* PrerenderContents::GetAsValue() const {
765 if (!prerender_contents_.get())
766 return NULL;
767 base::DictionaryValue* dict_value = new base::DictionaryValue();
768 dict_value->SetString("url", prerender_url_.spec());
769 base::TimeTicks current_time = base::TimeTicks::Now();
770 base::TimeDelta duration = current_time - load_start_time_;
771 dict_value->SetInteger("duration", duration.InSeconds());
772 dict_value->SetBoolean("is_loaded", prerender_contents_ &&
773 !prerender_contents_->IsLoading());
774 return dict_value;
777 bool PrerenderContents::IsCrossSiteNavigationPending() const {
778 if (!prerender_contents_)
779 return false;
780 return (prerender_contents_->GetSiteInstance() !=
781 prerender_contents_->GetPendingSiteInstance());
784 SessionStorageNamespace* PrerenderContents::GetSessionStorageNamespace() const {
785 if (!prerender_contents())
786 return NULL;
787 return prerender_contents()->GetController().
788 GetDefaultSessionStorageNamespace();
791 void PrerenderContents::OnCancelPrerenderForPrinting() {
792 Destroy(FINAL_STATUS_WINDOW_PRINT);
795 void PrerenderContents::RecordCookieEvent(CookieEvent event,
796 bool is_main_frame_http_request,
797 base::Time earliest_create_date) {
798 // We don't care about sent cookies that were created after this prerender
799 // started.
800 // The reason is that for the purpose of the histograms emitted, we only care
801 // about cookies that existed before the prerender was started, but not
802 // about cookies that were created as part of the prerender. Using the
803 // earliest creation timestamp of all cookies provided by the cookie monster
804 // is a heuristic that yields the desired result pretty closely.
805 // In particular, we pretend no other WebContents make changes to the cookies
806 // relevant to the prerender, which may not actually always be the case, but
807 // hopefully most of the times.
808 if (event == COOKIE_EVENT_SEND && earliest_create_date > start_time_)
809 return;
811 InternalCookieEvent internal_event = INTERNAL_COOKIE_EVENT_MAX;
813 if (is_main_frame_http_request) {
814 if (event == COOKIE_EVENT_SEND) {
815 internal_event = INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND;
816 } else {
817 internal_event = INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE;
819 } else {
820 if (event == COOKIE_EVENT_SEND) {
821 internal_event = INTERNAL_COOKIE_EVENT_OTHER_SEND;
822 } else {
823 internal_event = INTERNAL_COOKIE_EVENT_OTHER_CHANGE;
827 DCHECK_GE(internal_event, 0);
828 DCHECK_LT(internal_event, INTERNAL_COOKIE_EVENT_MAX);
830 cookie_status_ |= (1 << internal_event);
832 DCHECK_GE(cookie_status_, 0);
833 DCHECK_LT(cookie_status_, kNumCookieStatuses);
836 } // namespace prerender