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