- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / prerender / prerender_contents.cc
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.
4
5 #include "chrome/browser/prerender/prerender_contents.h"
6
7 #include <algorithm>
8 #include <functional>
9 #include <utility>
10
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_tracker.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/ui/browser.h"
23 #include "chrome/browser/ui/browser_tab_contents.h"
24 #include "chrome/common/prerender_messages.h"
25 #include "chrome/common/render_messages.h"
26 #include "chrome/common/url_constants.h"
27 #include "content/public/browser/browser_child_process_host.h"
28 #include "content/public/browser/notification_service.h"
29 #include "content/public/browser/render_process_host.h"
30 #include "content/public/browser/render_view_host.h"
31 #include "content/public/browser/resource_request_details.h"
32 #include "content/public/browser/session_storage_namespace.h"
33 #include "content/public/browser/web_contents.h"
34 #include "content/public/browser/web_contents_delegate.h"
35 #include "content/public/browser/web_contents_view.h"
36 #include "content/public/common/favicon_url.h"
37 #include "content/public/common/frame_navigate_params.h"
38 #include "ui/gfx/rect.h"
39
40 using content::DownloadItem;
41 using content::OpenURLParams;
42 using content::RenderViewHost;
43 using content::ResourceRedirectDetails;
44 using content::SessionStorageNamespace;
45 using content::WebContents;
46
47 namespace prerender {
48
49 class PrerenderContentsFactoryImpl : public PrerenderContents::Factory {
50  public:
51   virtual PrerenderContents* CreatePrerenderContents(
52       PrerenderManager* prerender_manager, Profile* profile,
53       const GURL& url, const content::Referrer& referrer,
54       Origin origin, uint8 experiment_id) OVERRIDE {
55     return new PrerenderContents(prerender_manager, profile,
56                                  url, referrer, origin, experiment_id);
57   }
58 };
59
60 // WebContentsDelegateImpl -----------------------------------------------------
61
62 class PrerenderContents::WebContentsDelegateImpl
63     : public content::WebContentsDelegate {
64  public:
65   explicit WebContentsDelegateImpl(PrerenderContents* prerender_contents)
66       : prerender_contents_(prerender_contents) {
67   }
68
69   // content::WebContentsDelegate implementation:
70   virtual WebContents* OpenURLFromTab(WebContents* source,
71                                       const OpenURLParams& params) OVERRIDE {
72     // |OpenURLFromTab| is typically called when a frame performs a navigation
73     // that requires the browser to perform the transition instead of WebKit.
74     // Examples include prerendering a site that redirects to an app URL,
75     // or if --enable-strict-site-isolation is specified and the prerendered
76     // frame redirects to a different origin.
77     // TODO(cbentzel): Consider supporting this if it is a common case during
78     // prerenders.
79     prerender_contents_->Destroy(FINAL_STATUS_OPEN_URL);
80     return NULL;
81   }
82
83   virtual void CanDownload(
84       RenderViewHost* render_view_host,
85       int request_id,
86       const std::string& request_method,
87       const base::Callback<void(bool)>& callback) OVERRIDE {
88     prerender_contents_->Destroy(FINAL_STATUS_DOWNLOAD);
89     // Cancel the download.
90     callback.Run(false);
91   }
92
93   virtual bool OnGoToEntryOffset(int offset) OVERRIDE {
94     // This isn't allowed because the history merge operation
95     // does not work if there are renderer issued challenges.
96     // TODO(cbentzel): Cancel in this case? May not need to do
97     // since render-issued offset navigations are not guaranteed,
98     // but indicates that the page cares about the history.
99     return false;
100   }
101
102   virtual void JSOutOfMemory(WebContents* tab) OVERRIDE {
103     prerender_contents_->Destroy(FINAL_STATUS_JS_OUT_OF_MEMORY);
104   }
105
106   virtual bool ShouldSuppressDialogs() OVERRIDE {
107     // We still want to show the user the message when they navigate to this
108     // page, so cancel this prerender.
109     prerender_contents_->Destroy(FINAL_STATUS_JAVASCRIPT_ALERT);
110     // Always suppress JavaScript messages if they're triggered by a page being
111     // prerendered.
112     return true;
113   }
114
115   virtual void RegisterProtocolHandler(WebContents* web_contents,
116                                        const std::string& protocol,
117                                        const GURL& url,
118                                        const string16& title,
119                                        bool user_gesture) OVERRIDE {
120     // TODO(mmenke): Consider supporting this if it is a common case during
121     // prerenders.
122     prerender_contents_->Destroy(FINAL_STATUS_REGISTER_PROTOCOL_HANDLER);
123   }
124
125  private:
126   PrerenderContents* prerender_contents_;
127 };
128
129 void PrerenderContents::Observer::OnPrerenderStopLoading(
130     PrerenderContents* contents) {
131 }
132
133 void PrerenderContents::Observer::OnPrerenderCreatedMatchCompleteReplacement(
134     PrerenderContents* contents, PrerenderContents* replacement) {
135 }
136
137 PrerenderContents::Observer::Observer() {
138 }
139
140 PrerenderContents::Observer::~Observer() {
141 }
142
143 PrerenderContents::PendingPrerenderInfo::PendingPrerenderInfo(
144     base::WeakPtr<PrerenderHandle> weak_prerender_handle,
145     Origin origin,
146     const GURL& url,
147     const content::Referrer& referrer,
148     const gfx::Size& size)
149     : weak_prerender_handle(weak_prerender_handle),
150       origin(origin),
151       url(url),
152       referrer(referrer),
153       size(size) {
154 }
155
156 PrerenderContents::PendingPrerenderInfo::~PendingPrerenderInfo() {
157 }
158
159 void PrerenderContents::AddPendingPrerender(
160     scoped_ptr<PendingPrerenderInfo> pending_prerender_info) {
161   pending_prerenders_.push_back(pending_prerender_info.release());
162 }
163
164 void PrerenderContents::PrepareForUse() {
165   NotifyPrerenderStop();
166
167   SessionStorageNamespace* session_storage_namespace = NULL;
168   if (prerender_contents_) {
169     // TODO(ajwong): This does not correctly handle storage for isolated apps.
170     session_storage_namespace = prerender_contents_->
171         GetController().GetDefaultSessionStorageNamespace();
172   }
173   prerender_manager_->StartPendingPrerenders(
174       child_id_, &pending_prerenders_, session_storage_namespace);
175   pending_prerenders_.clear();
176 }
177
178 PrerenderContents::PrerenderContents(
179     PrerenderManager* prerender_manager,
180     Profile* profile,
181     const GURL& url,
182     const content::Referrer& referrer,
183     Origin origin,
184     uint8 experiment_id)
185     : prerendering_has_started_(false),
186       prerender_manager_(prerender_manager),
187       prerender_url_(url),
188       referrer_(referrer),
189       profile_(profile),
190       page_id_(0),
191       session_storage_namespace_id_(-1),
192       has_stopped_loading_(false),
193       has_finished_loading_(false),
194       final_status_(FINAL_STATUS_MAX),
195       match_complete_status_(MATCH_COMPLETE_DEFAULT),
196       prerendering_has_been_cancelled_(false),
197       child_id_(-1),
198       route_id_(-1),
199       origin_(origin),
200       experiment_id_(experiment_id),
201       creator_child_id_(-1) {
202   DCHECK(prerender_manager != NULL);
203 }
204
205 PrerenderContents* PrerenderContents::CreateMatchCompleteReplacement() {
206   PrerenderContents* new_contents = prerender_manager_->CreatePrerenderContents(
207       prerender_url(), referrer(), origin(), experiment_id());
208
209   new_contents->load_start_time_ = load_start_time_;
210   new_contents->session_storage_namespace_id_ = session_storage_namespace_id_;
211   new_contents->set_match_complete_status(
212       PrerenderContents::MATCH_COMPLETE_REPLACEMENT_PENDING);
213
214   const bool did_init = new_contents->Init();
215   DCHECK(did_init);
216   DCHECK_EQ(alias_urls_.front(), new_contents->alias_urls_.front());
217   DCHECK_EQ(1u, new_contents->alias_urls_.size());
218   new_contents->alias_urls_ = alias_urls_;
219   new_contents->set_match_complete_status(
220       PrerenderContents::MATCH_COMPLETE_REPLACEMENT);
221   NotifyPrerenderCreatedMatchCompleteReplacement(new_contents);
222   return new_contents;
223 }
224
225 bool PrerenderContents::Init() {
226   return AddAliasURL(prerender_url_);
227 }
228
229 // static
230 PrerenderContents::Factory* PrerenderContents::CreateFactory() {
231   return new PrerenderContentsFactoryImpl();
232 }
233
234 void PrerenderContents::StartPrerendering(
235     int creator_child_id,
236     const gfx::Size& size,
237     SessionStorageNamespace* session_storage_namespace) {
238   DCHECK(profile_ != NULL);
239   DCHECK(!size.IsEmpty());
240   DCHECK(!prerendering_has_started_);
241   DCHECK(prerender_contents_.get() == NULL);
242   DCHECK_EQ(-1, creator_child_id_);
243   DCHECK(size_.IsEmpty());
244   DCHECK_EQ(1U, alias_urls_.size());
245
246   creator_child_id_ = creator_child_id;
247   session_storage_namespace_id_ = session_storage_namespace->id();
248   size_ = size;
249
250   DCHECK(load_start_time_.is_null());
251   load_start_time_ = base::TimeTicks::Now();
252
253   // Everything after this point sets up the WebContents object and associated
254   // RenderView for the prerender page. Don't do this for members of the
255   // control group.
256   if (prerender_manager_->IsControlGroup(experiment_id()))
257     return;
258
259   if (origin_ == ORIGIN_LOCAL_PREDICTOR &&
260       IsLocalPredictorPrerenderAlwaysControlEnabled()) {
261     return;
262   }
263
264   prerendering_has_started_ = true;
265
266   prerender_contents_.reset(CreateWebContents(session_storage_namespace));
267   BrowserTabContents::AttachTabHelpers(prerender_contents_.get());
268 #if defined(OS_ANDROID)
269   // Delay icon fetching until the contents are getting swapped in
270   // to conserve network usage in mobile devices.
271   FaviconTabHelper::FromWebContents(
272       prerender_contents_.get())->set_should_fetch_icons(false);
273 #endif  // defined(OS_ANDROID)
274   content::WebContentsObserver::Observe(prerender_contents_.get());
275
276   web_contents_delegate_.reset(new WebContentsDelegateImpl(this));
277   prerender_contents_.get()->SetDelegate(web_contents_delegate_.get());
278   // Set the size of the prerender WebContents.
279   prerender_contents_->GetView()->SizeContents(size_);
280
281   child_id_ = GetRenderViewHost()->GetProcess()->GetID();
282   route_id_ = GetRenderViewHost()->GetRoutingID();
283
284   // For Local Predictor based prerendering, log transactions to see if we could
285   // merge session storage namespaces in the event of a mismatch.
286   if (origin_ == ORIGIN_LOCAL_PREDICTOR)
287     session_storage_namespace->AddTransactionLogProcessId(child_id_);
288
289   // Register this with the ResourceDispatcherHost as a prerender
290   // RenderViewHost. This must be done before the Navigate message to catch all
291   // resource requests, but as it is on the same thread as the Navigate message
292   // (IO) there is no race condition.
293   AddObserver(prerender_manager()->prerender_tracker());
294   NotifyPrerenderStart();
295
296   // Close ourselves when the application is shutting down.
297   notification_registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
298                               content::NotificationService::AllSources());
299
300   // Register for our parent profile to shutdown, so we can shut ourselves down
301   // as well (should only be called for OTR profiles, as we should receive
302   // APP_TERMINATING before non-OTR profiles are destroyed).
303   // TODO(tburkard): figure out if this is needed.
304   notification_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
305                               content::Source<Profile>(profile_));
306
307   // Register to inform new RenderViews that we're prerendering.
308   notification_registrar_.Add(
309       this, content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
310       content::Source<WebContents>(prerender_contents_.get()));
311
312   // Transfer over the user agent override.
313   prerender_contents_.get()->SetUserAgentOverride(
314       prerender_manager_->config().user_agent_override);
315
316   content::NavigationController::LoadURLParams load_url_params(
317       prerender_url_);
318   load_url_params.referrer = referrer_;
319   load_url_params.transition_type = (origin_ == ORIGIN_OMNIBOX ?
320       content::PAGE_TRANSITION_TYPED : content::PAGE_TRANSITION_LINK);
321   load_url_params.override_user_agent =
322       prerender_manager_->config().is_overriding_user_agent ?
323       content::NavigationController::UA_OVERRIDE_TRUE :
324       content::NavigationController::UA_OVERRIDE_FALSE;
325   prerender_contents_.get()->GetController().LoadURLWithParams(load_url_params);
326 }
327
328 bool PrerenderContents::GetChildId(int* child_id) const {
329   CHECK(child_id);
330   DCHECK_GE(child_id_, -1);
331   *child_id = child_id_;
332   return child_id_ != -1;
333 }
334
335 bool PrerenderContents::GetRouteId(int* route_id) const {
336   CHECK(route_id);
337   DCHECK_GE(route_id_, -1);
338   *route_id = route_id_;
339   return route_id_ != -1;
340 }
341
342 void PrerenderContents::SetFinalStatus(FinalStatus final_status) {
343   DCHECK(final_status >= FINAL_STATUS_USED && final_status < FINAL_STATUS_MAX);
344   DCHECK(final_status_ == FINAL_STATUS_MAX);
345
346   final_status_ = final_status;
347 }
348
349 PrerenderContents::~PrerenderContents() {
350   DCHECK_NE(FINAL_STATUS_MAX, final_status());
351   DCHECK(
352       prerendering_has_been_cancelled() || final_status() == FINAL_STATUS_USED);
353   DCHECK_NE(ORIGIN_MAX, origin());
354
355   prerender_manager_->RecordFinalStatusWithMatchCompleteStatus(
356       origin(), experiment_id(), match_complete_status(), final_status());
357
358   // Broadcast the removal of aliases.
359   for (content::RenderProcessHost::iterator host_iterator =
360            content::RenderProcessHost::AllHostsIterator();
361        !host_iterator.IsAtEnd();
362        host_iterator.Advance()) {
363     content::RenderProcessHost* host = host_iterator.GetCurrentValue();
364     host->Send(new PrerenderMsg_OnPrerenderRemoveAliases(alias_urls_));
365   }
366
367   // If we still have a WebContents, clean up anything we need to and then
368   // destroy it.
369   if (prerender_contents_.get())
370     delete ReleasePrerenderContents();
371 }
372
373 void PrerenderContents::AddObserver(Observer* observer) {
374   DCHECK_EQ(FINAL_STATUS_MAX, final_status_);
375   observer_list_.AddObserver(observer);
376 }
377
378 void PrerenderContents::RemoveObserver(Observer* observer) {
379   observer_list_.RemoveObserver(observer);
380 }
381
382 void PrerenderContents::Observe(int type,
383                                 const content::NotificationSource& source,
384                                 const content::NotificationDetails& details) {
385   switch (type) {
386     case chrome::NOTIFICATION_PROFILE_DESTROYED:
387       Destroy(FINAL_STATUS_PROFILE_DESTROYED);
388       return;
389
390     case chrome::NOTIFICATION_APP_TERMINATING:
391       Destroy(FINAL_STATUS_APP_TERMINATING);
392       return;
393
394     case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: {
395       if (prerender_contents_.get()) {
396         DCHECK_EQ(content::Source<WebContents>(source).ptr(),
397                   prerender_contents_.get());
398
399         content::Details<RenderViewHost> new_render_view_host(details);
400         OnRenderViewHostCreated(new_render_view_host.ptr());
401
402         // When a new RenderView is created for a prerendering WebContents,
403         // tell the new RenderView it's being used for prerendering before any
404         // navigations occur.  Note that this is always triggered before the
405         // first navigation, so there's no need to send the message just after
406         // the WebContents is created.
407         new_render_view_host->Send(
408             new PrerenderMsg_SetIsPrerendering(
409                 new_render_view_host->GetRoutingID(),
410                 true));
411
412         // Make sure the size of the RenderViewHost has been passed to the new
413         // RenderView.  Otherwise, the size may not be sent until the
414         // RenderViewReady event makes it from the render process to the UI
415         // thread of the browser process.  When the RenderView receives its
416         // size, is also sets itself to be visible, which would then break the
417         // visibility API.
418         new_render_view_host->WasResized();
419         prerender_contents_->WasHidden();
420       }
421       break;
422     }
423
424     default:
425       NOTREACHED() << "Unexpected notification sent.";
426       break;
427   }
428 }
429
430 void PrerenderContents::OnRenderViewHostCreated(
431     RenderViewHost* new_render_view_host) {
432 }
433
434 size_t PrerenderContents::pending_prerender_count() const {
435   return pending_prerenders_.size();
436 }
437
438 WebContents* PrerenderContents::CreateWebContents(
439     SessionStorageNamespace* session_storage_namespace) {
440   // TODO(ajwong): Remove the temporary map once prerendering is aware of
441   // multiple session storage namespaces per tab.
442   content::SessionStorageNamespaceMap session_storage_namespace_map;
443   session_storage_namespace_map[std::string()] = session_storage_namespace;
444   return WebContents::CreateWithSessionStorage(
445       WebContents::CreateParams(profile_), session_storage_namespace_map);
446 }
447
448 void PrerenderContents::NotifyPrerenderStart() {
449   DCHECK_EQ(FINAL_STATUS_MAX, final_status_);
450   FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStart(this));
451 }
452
453 void PrerenderContents::NotifyPrerenderStopLoading() {
454   FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStopLoading(this));
455 }
456
457 void PrerenderContents::NotifyPrerenderStop() {
458   DCHECK_NE(FINAL_STATUS_MAX, final_status_);
459   FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStop(this));
460   observer_list_.Clear();
461 }
462
463 void PrerenderContents::NotifyPrerenderCreatedMatchCompleteReplacement(
464     PrerenderContents* replacement) {
465   FOR_EACH_OBSERVER(Observer, observer_list_,
466                     OnPrerenderCreatedMatchCompleteReplacement(this,
467                                                                replacement));
468 }
469
470 void PrerenderContents::DidUpdateFaviconURL(
471     int32 page_id,
472     const std::vector<content::FaviconURL>& urls) {
473   VLOG(1) << "PrerenderContents::OnUpdateFaviconURL" << icon_url_;
474   for (std::vector<content::FaviconURL>::const_iterator it = urls.begin();
475        it != urls.end(); ++it) {
476     if (it->icon_type == content::FaviconURL::FAVICON) {
477       icon_url_ = it->icon_url;
478       VLOG(1) << icon_url_;
479       return;
480     }
481   }
482 }
483
484 bool PrerenderContents::OnMessageReceived(const IPC::Message& message) {
485   bool handled = true;
486   // The following messages we do want to consume.
487   IPC_BEGIN_MESSAGE_MAP(PrerenderContents, message)
488     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CancelPrerenderForPrinting,
489                         OnCancelPrerenderForPrinting)
490     IPC_MESSAGE_UNHANDLED(handled = false)
491   IPC_END_MESSAGE_MAP()
492
493   return handled;
494 }
495
496 bool PrerenderContents::CheckURL(const GURL& url) {
497   const bool http = url.SchemeIs(content::kHttpScheme);
498   const bool https = url.SchemeIs(content::kHttpsScheme);
499   if (!http && !https) {
500     DCHECK_NE(MATCH_COMPLETE_REPLACEMENT_PENDING, match_complete_status_);
501     Destroy(FINAL_STATUS_UNSUPPORTED_SCHEME);
502     return false;
503   }
504   if (https && !prerender_manager_->config().https_allowed) {
505     DCHECK_NE(MATCH_COMPLETE_REPLACEMENT_PENDING, match_complete_status_);
506     Destroy(FINAL_STATUS_HTTPS);
507     return false;
508   }
509   if (match_complete_status_ != MATCH_COMPLETE_REPLACEMENT_PENDING &&
510       prerender_manager_->HasRecentlyBeenNavigatedTo(origin(), url)) {
511     Destroy(FINAL_STATUS_RECENTLY_VISITED);
512     return false;
513   }
514   return true;
515 }
516
517 bool PrerenderContents::AddAliasURL(const GURL& url) {
518   if (!CheckURL(url))
519     return false;
520
521   alias_urls_.push_back(url);
522
523   for (content::RenderProcessHost::iterator host_iterator =
524            content::RenderProcessHost::AllHostsIterator();
525        !host_iterator.IsAtEnd();
526        host_iterator.Advance()) {
527     content::RenderProcessHost* host = host_iterator.GetCurrentValue();
528     host->Send(new PrerenderMsg_OnPrerenderAddAlias(url));
529   }
530
531   return true;
532 }
533
534 bool PrerenderContents::Matches(
535     const GURL& url,
536     const SessionStorageNamespace* session_storage_namespace) const {
537   if (session_storage_namespace &&
538       session_storage_namespace_id_ != session_storage_namespace->id()) {
539     return false;
540   }
541   return std::count_if(alias_urls_.begin(), alias_urls_.end(),
542                        std::bind2nd(std::equal_to<GURL>(), url)) != 0;
543 }
544
545 void PrerenderContents::RenderProcessGone(base::TerminationStatus status) {
546   Destroy(FINAL_STATUS_RENDERER_CRASHED);
547 }
548
549 void PrerenderContents::DidStopLoading(
550     content::RenderViewHost* render_view_host) {
551   has_stopped_loading_ = true;
552   NotifyPrerenderStopLoading();
553 }
554
555 void PrerenderContents::DidStartProvisionalLoadForFrame(
556     int64 frame_id,
557     int64 parent_frame_id,
558     bool is_main_frame,
559     const GURL& validated_url,
560     bool is_error_page,
561     bool is_iframe_srcdoc,
562     RenderViewHost* render_view_host) {
563   if (is_main_frame) {
564     if (!CheckURL(validated_url))
565       return;
566
567     // Usually, this event fires if the user clicks or enters a new URL.
568     // Neither of these can happen in the case of an invisible prerender.
569     // So the cause is: Some JavaScript caused a new URL to be loaded.  In that
570     // case, the spinner would start again in the browser, so we must reset
571     // has_stopped_loading_ so that the spinner won't be stopped.
572     has_stopped_loading_ = false;
573     has_finished_loading_ = false;
574   }
575 }
576
577 void PrerenderContents::DidFinishLoad(int64 frame_id,
578                                       const GURL& validated_url,
579                                       bool is_main_frame,
580                                       RenderViewHost* render_view_host) {
581   if (is_main_frame)
582     has_finished_loading_ = true;
583 }
584
585 void PrerenderContents::DidNavigateMainFrame(
586     const content::LoadCommittedDetails& details,
587     const content::FrameNavigateParams& params) {
588   // Add each redirect as an alias. |params.url| is included in
589   // |params.redirects|.
590   //
591   // TODO(davidben): We do not correctly patch up history for renderer-initated
592   // navigations which add history entries. http://crbug.com/305660.
593   for (size_t i = 0; i < params.redirects.size(); i++) {
594     if (!AddAliasURL(params.redirects[i]))
595       return;
596   }
597 }
598
599 void PrerenderContents::DidGetRedirectForResourceRequest(
600     const content::ResourceRedirectDetails& details) {
601   // DidGetRedirectForResourceRequest can come for any resource on a page.  If
602   // it's a redirect on the top-level resource, the name needs to be remembered
603   // for future matching, and if it redirects to an https resource, it needs to
604   // be canceled. If a subresource is redirected, nothing changes.
605   if (details.resource_type != ResourceType::MAIN_FRAME)
606     return;
607   CheckURL(details.new_url);
608 }
609
610 void PrerenderContents::Destroy(FinalStatus final_status) {
611   DCHECK_NE(final_status, FINAL_STATUS_USED);
612
613   if (prerendering_has_been_cancelled_)
614     return;
615
616   if (child_id_ != -1 && route_id_ != -1) {
617     // Cancel the prerender in the PrerenderTracker.  This is needed
618     // because destroy may be called directly from the UI thread without calling
619     // TryCancel().  This is difficult to completely avoid, since prerendering
620     // can be cancelled before a RenderView is created.
621     bool is_cancelled = prerender_manager()->prerender_tracker()->TryCancel(
622         child_id_, route_id_, final_status);
623     CHECK(is_cancelled);
624
625     // A different final status may have been set already from another thread.
626     // If so, use it instead.
627     if (!prerender_manager()->prerender_tracker()->
628             GetFinalStatus(child_id_, route_id_, &final_status)) {
629       NOTREACHED();
630     }
631   }
632   SetFinalStatus(final_status);
633
634   prerendering_has_been_cancelled_ = true;
635   prerender_manager_->AddToHistory(this);
636   prerender_manager_->MoveEntryToPendingDelete(this, final_status);
637
638   // Note that if this PrerenderContents was made into a MatchComplete
639   // replacement by MoveEntryToPendingDelete, NotifyPrerenderStop will
640   // not reach the PrerenderHandle. Rather
641   // OnPrerenderCreatedMatchCompleteReplacement will propogate that
642   // information to the referer.
643   if (!prerender_manager_->IsControlGroup(experiment_id()) &&
644       (prerendering_has_started() ||
645        match_complete_status() == MATCH_COMPLETE_REPLACEMENT)) {
646     NotifyPrerenderStop();
647   }
648 }
649
650 base::ProcessMetrics* PrerenderContents::MaybeGetProcessMetrics() {
651   if (process_metrics_.get() == NULL) {
652     // If a PrenderContents hasn't started prerending, don't be fully formed.
653     if (!GetRenderViewHost() || !GetRenderViewHost()->GetProcess())
654       return NULL;
655     base::ProcessHandle handle = GetRenderViewHost()->GetProcess()->GetHandle();
656     if (handle == base::kNullProcessHandle)
657       return NULL;
658 #if !defined(OS_MACOSX)
659     process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(handle));
660 #else
661     process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(
662         handle,
663         content::BrowserChildProcessHost::GetPortProvider()));
664 #endif
665   }
666
667   return process_metrics_.get();
668 }
669
670 void PrerenderContents::DestroyWhenUsingTooManyResources() {
671   base::ProcessMetrics* metrics = MaybeGetProcessMetrics();
672   if (metrics == NULL)
673     return;
674
675   size_t private_bytes, shared_bytes;
676   if (metrics->GetMemoryBytes(&private_bytes, &shared_bytes) &&
677       private_bytes > prerender_manager_->config().max_bytes) {
678     Destroy(FINAL_STATUS_MEMORY_LIMIT_EXCEEDED);
679   }
680 }
681
682 WebContents* PrerenderContents::ReleasePrerenderContents() {
683   prerender_contents_->SetDelegate(NULL);
684   content::WebContentsObserver::Observe(NULL);
685   SessionStorageNamespace* session_storage_namespace =
686       GetSessionStorageNamespace();
687   if (session_storage_namespace && origin_ == ORIGIN_LOCAL_PREDICTOR)
688     session_storage_namespace->RemoveTransactionLogProcessId(child_id_);
689   return prerender_contents_.release();
690 }
691
692 RenderViewHost* PrerenderContents::GetRenderViewHostMutable() {
693   return const_cast<RenderViewHost*>(GetRenderViewHost());
694 }
695
696 const RenderViewHost* PrerenderContents::GetRenderViewHost() const {
697   if (!prerender_contents_.get())
698     return NULL;
699   return prerender_contents_->GetRenderViewHost();
700 }
701
702 void PrerenderContents::DidNavigate(
703     const history::HistoryAddPageArgs& add_page_args) {
704   add_page_vector_.push_back(add_page_args);
705 }
706
707 void PrerenderContents::CommitHistory(WebContents* tab) {
708   HistoryTabHelper* history_tab_helper = HistoryTabHelper::FromWebContents(tab);
709   for (size_t i = 0; i < add_page_vector_.size(); ++i)
710     history_tab_helper->UpdateHistoryForNavigation(add_page_vector_[i]);
711 }
712
713 Value* PrerenderContents::GetAsValue() const {
714   if (!prerender_contents_.get())
715     return NULL;
716   DictionaryValue* dict_value = new DictionaryValue();
717   dict_value->SetString("url", prerender_url_.spec());
718   base::TimeTicks current_time = base::TimeTicks::Now();
719   base::TimeDelta duration = current_time - load_start_time_;
720   dict_value->SetInteger("duration", duration.InSeconds());
721   dict_value->SetBoolean("is_loaded", prerender_contents_ &&
722                                       !prerender_contents_->IsLoading());
723   return dict_value;
724 }
725
726 bool PrerenderContents::IsCrossSiteNavigationPending() const {
727   if (!prerender_contents_)
728     return false;
729   return (prerender_contents_->GetSiteInstance() !=
730           prerender_contents_->GetPendingSiteInstance());
731 }
732
733 SessionStorageNamespace* PrerenderContents::GetSessionStorageNamespace() const {
734   if (!prerender_contents())
735     return NULL;
736   return prerender_contents()->GetController().
737       GetDefaultSessionStorageNamespace();
738 }
739
740 void PrerenderContents::OnCancelPrerenderForPrinting() {
741   Destroy(FINAL_STATUS_WINDOW_PRINT);
742 }
743
744 }  // namespace prerender