prerender_contents.cc revision 010d83a9304c5a91596085d917d248abff47903a
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 "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/common/frame_navigate_params.h"
41#include "content/public/common/page_transition_types.h"
42#include "ui/gfx/rect.h"
43
44using content::DownloadItem;
45using content::OpenURLParams;
46using content::RenderViewHost;
47using content::ResourceRedirectDetails;
48using content::SessionStorageNamespace;
49using content::WebContents;
50
51namespace prerender {
52
53namespace {
54
55// Internal cookie event.
56// Whenever a prerender interacts with the cookie store, either sending
57// existing cookies that existed before the prerender started, or when a cookie
58// is changed, we record these events for histogramming purposes.
59enum InternalCookieEvent {
60  INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND = 0,
61  INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE = 1,
62  INTERNAL_COOKIE_EVENT_OTHER_SEND = 2,
63  INTERNAL_COOKIE_EVENT_OTHER_CHANGE = 3,
64  INTERNAL_COOKIE_EVENT_MAX
65};
66
67// Indicates whether existing cookies were sent, and if they were third party
68// cookies, and whether they were for blocking resources.
69// Each value may be inclusive of previous values. We only care about the
70// value with the highest index that has ever occurred in the course of a
71// prerender.
72enum CookieSendType {
73  COOKIE_SEND_TYPE_NONE = 0,
74  COOKIE_SEND_TYPE_FIRST_PARTY = 1,
75  COOKIE_SEND_TYPE_THIRD_PARTY = 2,
76  COOKIE_SEND_TYPE_THIRD_PARTY_BLOCKING_RESOURCE = 3,
77  COOKIE_SEND_TYPE_MAX
78};
79
80void ResumeThrottles(
81    std::vector<base::WeakPtr<PrerenderResourceThrottle> > throttles) {
82  for (size_t i = 0; i < throttles.size(); i++) {
83    if (throttles[i])
84      throttles[i]->Resume();
85  }
86}
87
88}  // namespace
89
90// static
91const int PrerenderContents::kNumCookieStatuses =
92    (1 << INTERNAL_COOKIE_EVENT_MAX);
93
94// static
95const int PrerenderContents::kNumCookieSendTypes = COOKIE_SEND_TYPE_MAX;
96
97class PrerenderContentsFactoryImpl : public PrerenderContents::Factory {
98 public:
99  virtual PrerenderContents* CreatePrerenderContents(
100      PrerenderManager* prerender_manager, Profile* profile,
101      const GURL& url, const content::Referrer& referrer,
102      Origin origin, uint8 experiment_id) OVERRIDE {
103    return new PrerenderContents(prerender_manager, profile,
104                                 url, referrer, origin, experiment_id);
105  }
106};
107
108// WebContentsDelegateImpl -----------------------------------------------------
109
110class PrerenderContents::WebContentsDelegateImpl
111    : public content::WebContentsDelegate {
112 public:
113  explicit WebContentsDelegateImpl(PrerenderContents* prerender_contents)
114      : prerender_contents_(prerender_contents) {
115  }
116
117  // content::WebContentsDelegate implementation:
118  virtual WebContents* OpenURLFromTab(WebContents* source,
119                                      const OpenURLParams& params) OVERRIDE {
120    // |OpenURLFromTab| is typically called when a frame performs a navigation
121    // that requires the browser to perform the transition instead of WebKit.
122    // Examples include prerendering a site that redirects to an app URL,
123    // or if --enable-strict-site-isolation is specified and the prerendered
124    // frame redirects to a different origin.
125    // TODO(cbentzel): Consider supporting this if it is a common case during
126    // prerenders.
127    prerender_contents_->Destroy(FINAL_STATUS_OPEN_URL);
128    return NULL;
129  }
130
131  virtual void CloseContents(content::WebContents* contents) OVERRIDE {
132    prerender_contents_->Destroy(FINAL_STATUS_CLOSED);
133  }
134
135  virtual void CanDownload(
136      RenderViewHost* render_view_host,
137      int request_id,
138      const std::string& request_method,
139      const base::Callback<void(bool)>& callback) OVERRIDE {
140    prerender_contents_->Destroy(FINAL_STATUS_DOWNLOAD);
141    // Cancel the download.
142    callback.Run(false);
143  }
144
145   virtual bool ShouldCreateWebContents(
146       WebContents* web_contents,
147       int route_id,
148       WindowContainerType window_container_type,
149       const base::string16& frame_name,
150       const GURL& target_url,
151       const std::string& partition_id,
152       SessionStorageNamespace* session_storage_namespace) OVERRIDE {
153    // Since we don't want to permit child windows that would have a
154    // window.opener property, terminate prerendering.
155    prerender_contents_->Destroy(FINAL_STATUS_CREATE_NEW_WINDOW);
156    // Cancel the popup.
157    return false;
158  }
159
160  virtual bool OnGoToEntryOffset(int offset) OVERRIDE {
161    // This isn't allowed because the history merge operation
162    // does not work if there are renderer issued challenges.
163    // TODO(cbentzel): Cancel in this case? May not need to do
164    // since render-issued offset navigations are not guaranteed,
165    // but indicates that the page cares about the history.
166    return false;
167  }
168
169  virtual bool ShouldSuppressDialogs() OVERRIDE {
170    // We still want to show the user the message when they navigate to this
171    // page, so cancel this prerender.
172    prerender_contents_->Destroy(FINAL_STATUS_JAVASCRIPT_ALERT);
173    // Always suppress JavaScript messages if they're triggered by a page being
174    // prerendered.
175    return true;
176  }
177
178  virtual void RegisterProtocolHandler(WebContents* web_contents,
179                                       const std::string& protocol,
180                                       const GURL& url,
181                                       const base::string16& title,
182                                       bool user_gesture) OVERRIDE {
183    // TODO(mmenke): Consider supporting this if it is a common case during
184    // prerenders.
185    prerender_contents_->Destroy(FINAL_STATUS_REGISTER_PROTOCOL_HANDLER);
186  }
187
188  virtual gfx::Size GetSizeForNewRenderView(
189      WebContents* web_contents) const OVERRIDE {
190    // Have to set the size of the RenderView on initialization to be sure it is
191    // set before the RenderView is hidden on all platforms (esp. Android).
192    return prerender_contents_->size_;
193  }
194
195 private:
196  PrerenderContents* prerender_contents_;
197};
198
199void PrerenderContents::Observer::OnPrerenderStopLoading(
200    PrerenderContents* contents) {
201}
202
203void PrerenderContents::Observer::OnPrerenderDomContentLoaded(
204    PrerenderContents* contents) {
205}
206
207void PrerenderContents::Observer::OnPrerenderCreatedMatchCompleteReplacement(
208    PrerenderContents* contents, PrerenderContents* replacement) {
209}
210
211PrerenderContents::Observer::Observer() {
212}
213
214PrerenderContents::Observer::~Observer() {
215}
216
217PrerenderContents::PrerenderContents(
218    PrerenderManager* prerender_manager,
219    Profile* profile,
220    const GURL& url,
221    const content::Referrer& referrer,
222    Origin origin,
223    uint8 experiment_id)
224    : prerendering_has_started_(false),
225      session_storage_namespace_id_(-1),
226      prerender_manager_(prerender_manager),
227      prerender_url_(url),
228      referrer_(referrer),
229      profile_(profile),
230      page_id_(0),
231      has_stopped_loading_(false),
232      has_finished_loading_(false),
233      final_status_(FINAL_STATUS_MAX),
234      match_complete_status_(MATCH_COMPLETE_DEFAULT),
235      prerendering_has_been_cancelled_(false),
236      child_id_(-1),
237      route_id_(-1),
238      origin_(origin),
239      experiment_id_(experiment_id),
240      creator_child_id_(-1),
241      main_frame_id_(0),
242      cookie_status_(0),
243      cookie_send_type_(COOKIE_SEND_TYPE_NONE),
244      network_bytes_(0) {
245  DCHECK(prerender_manager != NULL);
246}
247
248PrerenderContents* PrerenderContents::CreateMatchCompleteReplacement() {
249  PrerenderContents* new_contents = prerender_manager_->CreatePrerenderContents(
250      prerender_url(), referrer(), origin(), experiment_id());
251
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);
256
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  // Erase all but the first alias URL; the replacement has adopted the
263  // remainder without increasing the renderer-side reference count.
264  alias_urls_.resize(1);
265  new_contents->set_match_complete_status(
266      PrerenderContents::MATCH_COMPLETE_REPLACEMENT);
267  NotifyPrerenderCreatedMatchCompleteReplacement(new_contents);
268  return new_contents;
269}
270
271bool PrerenderContents::Init() {
272  return AddAliasURL(prerender_url_);
273}
274
275// static
276PrerenderContents::Factory* PrerenderContents::CreateFactory() {
277  return new PrerenderContentsFactoryImpl();
278}
279
280// static
281PrerenderContents* PrerenderContents::FromWebContents(
282    content::WebContents* web_contents) {
283  if (!web_contents)
284    return NULL;
285  PrerenderManager* prerender_manager = PrerenderManagerFactory::GetForProfile(
286      Profile::FromBrowserContext(web_contents->GetBrowserContext()));
287  if (!prerender_manager)
288    return NULL;
289  return prerender_manager->GetPrerenderContents(web_contents);
290}
291
292void PrerenderContents::StartPrerendering(
293    int creator_child_id,
294    const gfx::Size& size,
295    SessionStorageNamespace* session_storage_namespace) {
296  DCHECK(profile_ != NULL);
297  DCHECK(!size.IsEmpty());
298  DCHECK(!prerendering_has_started_);
299  DCHECK(prerender_contents_.get() == NULL);
300  DCHECK_EQ(-1, creator_child_id_);
301  DCHECK(size_.IsEmpty());
302  DCHECK_EQ(1U, alias_urls_.size());
303
304  creator_child_id_ = creator_child_id;
305  session_storage_namespace_id_ = session_storage_namespace->id();
306  size_ = size;
307
308  DCHECK(load_start_time_.is_null());
309  load_start_time_ = base::TimeTicks::Now();
310  start_time_ = base::Time::Now();
311
312  // Everything after this point sets up the WebContents object and associated
313  // RenderView for the prerender page. Don't do this for members of the
314  // control group.
315  if (prerender_manager_->IsControlGroup(experiment_id()))
316    return;
317
318  if (origin_ == ORIGIN_LOCAL_PREDICTOR &&
319      IsLocalPredictorPrerenderAlwaysControlEnabled()) {
320    return;
321  }
322
323  prerendering_has_started_ = true;
324
325  alias_session_storage_namespace = session_storage_namespace->CreateAlias();
326  prerender_contents_.reset(
327      CreateWebContents(alias_session_storage_namespace.get()));
328  TabHelpers::AttachTabHelpers(prerender_contents_.get());
329  content::WebContentsObserver::Observe(prerender_contents_.get());
330
331  web_contents_delegate_.reset(new WebContentsDelegateImpl(this));
332  prerender_contents_.get()->SetDelegate(web_contents_delegate_.get());
333  // Set the size of the prerender WebContents.
334  apps::ResizeWebContents(prerender_contents_.get(), size_);
335
336  child_id_ = GetRenderViewHost()->GetProcess()->GetID();
337  route_id_ = GetRenderViewHost()->GetRoutingID();
338
339  // Log transactions to see if we could merge session storage namespaces in
340  // the event of a mismatch.
341  alias_session_storage_namespace->AddTransactionLogProcessId(child_id_);
342
343  NotifyPrerenderStart();
344
345  // Close ourselves when the application is shutting down.
346  notification_registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
347                              content::NotificationService::AllSources());
348
349  // Register to inform new RenderViews that we're prerendering.
350  notification_registrar_.Add(
351      this, content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
352      content::Source<WebContents>(prerender_contents_.get()));
353
354  // Transfer over the user agent override.
355  prerender_contents_.get()->SetUserAgentOverride(
356      prerender_manager_->config().user_agent_override);
357
358  content::NavigationController::LoadURLParams load_url_params(
359      prerender_url_);
360  load_url_params.referrer = referrer_;
361  load_url_params.transition_type = content::PAGE_TRANSITION_LINK;
362  if (origin_ == ORIGIN_OMNIBOX) {
363    load_url_params.transition_type = content::PageTransitionFromInt(
364        content::PAGE_TRANSITION_TYPED |
365        content::PAGE_TRANSITION_FROM_ADDRESS_BAR);
366  } else if (origin_ == ORIGIN_INSTANT) {
367    load_url_params.transition_type = content::PageTransitionFromInt(
368        content::PAGE_TRANSITION_GENERATED |
369        content::PAGE_TRANSITION_FROM_ADDRESS_BAR);
370  }
371  load_url_params.override_user_agent =
372      prerender_manager_->config().is_overriding_user_agent ?
373      content::NavigationController::UA_OVERRIDE_TRUE :
374      content::NavigationController::UA_OVERRIDE_FALSE;
375  prerender_contents_.get()->GetController().LoadURLWithParams(load_url_params);
376}
377
378bool PrerenderContents::GetChildId(int* child_id) const {
379  CHECK(child_id);
380  DCHECK_GE(child_id_, -1);
381  *child_id = child_id_;
382  return child_id_ != -1;
383}
384
385bool PrerenderContents::GetRouteId(int* route_id) const {
386  CHECK(route_id);
387  DCHECK_GE(route_id_, -1);
388  *route_id = route_id_;
389  return route_id_ != -1;
390}
391
392void PrerenderContents::SetFinalStatus(FinalStatus final_status) {
393  DCHECK_GE(final_status, FINAL_STATUS_USED);
394  DCHECK_LT(final_status, FINAL_STATUS_MAX);
395
396  DCHECK_EQ(FINAL_STATUS_MAX, final_status_);
397
398  final_status_ = final_status;
399}
400
401PrerenderContents::~PrerenderContents() {
402  DCHECK_NE(FINAL_STATUS_MAX, final_status());
403  DCHECK(
404      prerendering_has_been_cancelled() || final_status() == FINAL_STATUS_USED);
405  DCHECK_NE(ORIGIN_MAX, origin());
406  // Since a lot of prerenders terminate before any meaningful cookie action
407  // would have happened, only record the cookie status for prerenders who
408  // were used, cancelled, or timed out.
409  if (prerendering_has_started_ && final_status() == FINAL_STATUS_USED) {
410    prerender_manager_->RecordCookieStatus(origin(), experiment_id(),
411                                           cookie_status_);
412    prerender_manager_->RecordCookieSendType(origin(), experiment_id(),
413                                             cookie_send_type_);
414  }
415  prerender_manager_->RecordFinalStatusWithMatchCompleteStatus(
416      origin(), experiment_id(), match_complete_status(), final_status());
417
418  bool used = final_status() == FINAL_STATUS_USED ||
419              final_status() == FINAL_STATUS_WOULD_HAVE_BEEN_USED;
420  prerender_manager_->RecordNetworkBytes(used, network_bytes_);
421
422  // Broadcast the removal of aliases.
423  for (content::RenderProcessHost::iterator host_iterator =
424           content::RenderProcessHost::AllHostsIterator();
425       !host_iterator.IsAtEnd();
426       host_iterator.Advance()) {
427    content::RenderProcessHost* host = host_iterator.GetCurrentValue();
428    host->Send(new PrerenderMsg_OnPrerenderRemoveAliases(alias_urls_));
429  }
430
431  // If we still have a WebContents, clean up anything we need to and then
432  // destroy it.
433  if (prerender_contents_.get())
434    delete ReleasePrerenderContents();
435}
436
437void PrerenderContents::AddObserver(Observer* observer) {
438  DCHECK_EQ(FINAL_STATUS_MAX, final_status_);
439  observer_list_.AddObserver(observer);
440}
441
442void PrerenderContents::RemoveObserver(Observer* observer) {
443  observer_list_.RemoveObserver(observer);
444}
445
446void PrerenderContents::Observe(int type,
447                                const content::NotificationSource& source,
448                                const content::NotificationDetails& details) {
449  switch (type) {
450    // TODO(davidben): Try to remove this in favor of relying on
451    // FINAL_STATUS_PROFILE_DESTROYED.
452    case chrome::NOTIFICATION_APP_TERMINATING:
453      Destroy(FINAL_STATUS_APP_TERMINATING);
454      return;
455
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());
460
461        content::Details<RenderViewHost> new_render_view_host(details);
462        OnRenderViewHostCreated(new_render_view_host.ptr());
463
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();
472      }
473      break;
474    }
475
476    default:
477      NOTREACHED() << "Unexpected notification sent.";
478      break;
479  }
480}
481
482void PrerenderContents::OnRenderViewHostCreated(
483    RenderViewHost* new_render_view_host) {
484}
485
486WebContents* PrerenderContents::CreateWebContents(
487    SessionStorageNamespace* session_storage_namespace) {
488  // TODO(ajwong): Remove the temporary map once prerendering is aware of
489  // multiple session storage namespaces per tab.
490  content::SessionStorageNamespaceMap session_storage_namespace_map;
491  session_storage_namespace_map[std::string()] = session_storage_namespace;
492  return WebContents::CreateWithSessionStorage(
493      WebContents::CreateParams(profile_), session_storage_namespace_map);
494}
495
496void PrerenderContents::NotifyPrerenderStart() {
497  DCHECK_EQ(FINAL_STATUS_MAX, final_status_);
498  FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStart(this));
499}
500
501void PrerenderContents::NotifyPrerenderStopLoading() {
502  FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStopLoading(this));
503}
504
505void PrerenderContents::NotifyPrerenderDomContentLoaded() {
506  FOR_EACH_OBSERVER(Observer, observer_list_,
507                    OnPrerenderDomContentLoaded(this));
508}
509
510void PrerenderContents::NotifyPrerenderStop() {
511  DCHECK_NE(FINAL_STATUS_MAX, final_status_);
512  FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStop(this));
513  observer_list_.Clear();
514}
515
516void PrerenderContents::NotifyPrerenderCreatedMatchCompleteReplacement(
517    PrerenderContents* replacement) {
518  FOR_EACH_OBSERVER(Observer, observer_list_,
519                    OnPrerenderCreatedMatchCompleteReplacement(this,
520                                                               replacement));
521}
522
523bool PrerenderContents::OnMessageReceived(const IPC::Message& message) {
524  bool handled = true;
525  // The following messages we do want to consume.
526  IPC_BEGIN_MESSAGE_MAP(PrerenderContents, message)
527    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CancelPrerenderForPrinting,
528                        OnCancelPrerenderForPrinting)
529    IPC_MESSAGE_UNHANDLED(handled = false)
530  IPC_END_MESSAGE_MAP()
531
532  return handled;
533}
534
535bool PrerenderContents::CheckURL(const GURL& url) {
536  if (!url.SchemeIsHTTPOrHTTPS()) {
537    DCHECK_NE(MATCH_COMPLETE_REPLACEMENT_PENDING, match_complete_status_);
538    Destroy(FINAL_STATUS_UNSUPPORTED_SCHEME);
539    return false;
540  }
541  if (match_complete_status_ != MATCH_COMPLETE_REPLACEMENT_PENDING &&
542      prerender_manager_->HasRecentlyBeenNavigatedTo(origin(), url)) {
543    Destroy(FINAL_STATUS_RECENTLY_VISITED);
544    return false;
545  }
546  return true;
547}
548
549bool PrerenderContents::AddAliasURL(const GURL& url) {
550  if (!CheckURL(url))
551    return false;
552
553  alias_urls_.push_back(url);
554
555  for (content::RenderProcessHost::iterator host_iterator =
556           content::RenderProcessHost::AllHostsIterator();
557       !host_iterator.IsAtEnd();
558       host_iterator.Advance()) {
559    content::RenderProcessHost* host = host_iterator.GetCurrentValue();
560    host->Send(new PrerenderMsg_OnPrerenderAddAlias(url));
561  }
562
563  return true;
564}
565
566bool PrerenderContents::Matches(
567    const GURL& url,
568    const SessionStorageNamespace* session_storage_namespace) const {
569  if (session_storage_namespace &&
570      session_storage_namespace_id_ != session_storage_namespace->id()) {
571    return false;
572  }
573  return std::count_if(alias_urls_.begin(), alias_urls_.end(),
574                       std::bind2nd(std::equal_to<GURL>(), url)) != 0;
575}
576
577void PrerenderContents::RenderProcessGone(base::TerminationStatus status) {
578  Destroy(FINAL_STATUS_RENDERER_CRASHED);
579}
580
581void PrerenderContents::RenderFrameCreated(
582    content::RenderFrameHost* render_frame_host) {
583  // When a new RenderFrame is created for a prerendering WebContents, tell the
584  // new RenderFrame it's being used for prerendering before any navigations
585  // occur.  Note that this is always triggered before the first navigation, so
586  // there's no need to send the message just after the WebContents is created.
587  render_frame_host->Send(new PrerenderMsg_SetIsPrerendering(
588      render_frame_host->GetRoutingID(), true));
589}
590
591void PrerenderContents::DidStopLoading(
592    content::RenderViewHost* render_view_host) {
593  has_stopped_loading_ = true;
594  NotifyPrerenderStopLoading();
595}
596
597void PrerenderContents::DocumentLoadedInFrame(
598    int64 frame_id,
599    RenderViewHost* render_view_host) {
600  if (frame_id == main_frame_id_)
601    NotifyPrerenderDomContentLoaded();
602}
603
604void PrerenderContents::DidStartProvisionalLoadForFrame(
605    int64 frame_id,
606    int64 parent_frame_id,
607    bool is_main_frame,
608    const GURL& validated_url,
609    bool is_error_page,
610    bool is_iframe_srcdoc,
611    RenderViewHost* render_view_host) {
612  if (is_main_frame) {
613    if (!CheckURL(validated_url))
614      return;
615
616    // Usually, this event fires if the user clicks or enters a new URL.
617    // Neither of these can happen in the case of an invisible prerender.
618    // So the cause is: Some JavaScript caused a new URL to be loaded.  In that
619    // case, the spinner would start again in the browser, so we must reset
620    // has_stopped_loading_ so that the spinner won't be stopped.
621    has_stopped_loading_ = false;
622    has_finished_loading_ = false;
623  }
624}
625
626void PrerenderContents::DidCommitProvisionalLoadForFrame(
627      int64 frame_id,
628      const base::string16& frame_unique_name,
629      bool is_main_frame,
630      const GURL& url,
631      content::PageTransition transition_type,
632      RenderViewHost* render_view_host) {
633  if (is_main_frame) {
634    main_frame_id_ = frame_id;
635  }
636}
637
638void PrerenderContents::DidFinishLoad(int64 frame_id,
639                                      const GURL& validated_url,
640                                      bool is_main_frame,
641                                      RenderViewHost* render_view_host) {
642  if (is_main_frame)
643    has_finished_loading_ = true;
644}
645
646void PrerenderContents::DidNavigateMainFrame(
647    const content::LoadCommittedDetails& details,
648    const content::FrameNavigateParams& params) {
649  // If the prerender made a second navigation entry, abort the prerender. This
650  // avoids having to correctly implement a complex history merging case (this
651  // interacts with location.replace) and correctly synchronize with the
652  // renderer. The final status may be monitored to see we need to revisit this
653  // decision. This does not affect client redirects as those do not push new
654  // history entries. (Calls to location.replace, navigations before onload, and
655  // <meta http-equiv=refresh> with timeouts under 1 second do not create
656  // entries in Blink.)
657  if (prerender_contents_->GetController().GetEntryCount() > 1) {
658    Destroy(FINAL_STATUS_NEW_NAVIGATION_ENTRY);
659    return;
660  }
661
662  // Add each redirect as an alias. |params.url| is included in
663  // |params.redirects|.
664  //
665  // TODO(davidben): We do not correctly patch up history for renderer-initated
666  // navigations which add history entries. http://crbug.com/305660.
667  for (size_t i = 0; i < params.redirects.size(); i++) {
668    if (!AddAliasURL(params.redirects[i]))
669      return;
670  }
671}
672
673void PrerenderContents::DidGetRedirectForResourceRequest(
674    RenderViewHost* render_view_host,
675    const content::ResourceRedirectDetails& details) {
676  // DidGetRedirectForResourceRequest can come for any resource on a page.  If
677  // it's a redirect on the top-level resource, the name needs to be remembered
678  // for future matching, and if it redirects to an https resource, it needs to
679  // be canceled. If a subresource is redirected, nothing changes.
680  if (details.resource_type != ResourceType::MAIN_FRAME)
681    return;
682  CheckURL(details.new_url);
683}
684
685void PrerenderContents::Destroy(FinalStatus final_status) {
686  DCHECK_NE(final_status, FINAL_STATUS_USED);
687
688  if (prerendering_has_been_cancelled_)
689    return;
690
691  SetFinalStatus(final_status);
692
693  prerendering_has_been_cancelled_ = true;
694  prerender_manager_->AddToHistory(this);
695  prerender_manager_->MoveEntryToPendingDelete(this, final_status);
696
697  // Note that if this PrerenderContents was made into a MatchComplete
698  // replacement by MoveEntryToPendingDelete, NotifyPrerenderStop will
699  // not reach the PrerenderHandle. Rather
700  // OnPrerenderCreatedMatchCompleteReplacement will propogate that
701  // information to the referer.
702  if (!prerender_manager_->IsControlGroup(experiment_id()) &&
703      (prerendering_has_started() ||
704       match_complete_status() == MATCH_COMPLETE_REPLACEMENT)) {
705    NotifyPrerenderStop();
706  }
707}
708
709base::ProcessMetrics* PrerenderContents::MaybeGetProcessMetrics() {
710  if (process_metrics_.get() == NULL) {
711    // If a PrenderContents hasn't started prerending, don't be fully formed.
712    if (!GetRenderViewHost() || !GetRenderViewHost()->GetProcess())
713      return NULL;
714    base::ProcessHandle handle = GetRenderViewHost()->GetProcess()->GetHandle();
715    if (handle == base::kNullProcessHandle)
716      return NULL;
717#if !defined(OS_MACOSX)
718    process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(handle));
719#else
720    process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(
721        handle,
722        content::BrowserChildProcessHost::GetPortProvider()));
723#endif
724  }
725
726  return process_metrics_.get();
727}
728
729void PrerenderContents::DestroyWhenUsingTooManyResources() {
730  base::ProcessMetrics* metrics = MaybeGetProcessMetrics();
731  if (metrics == NULL)
732    return;
733
734  size_t private_bytes, shared_bytes;
735  if (metrics->GetMemoryBytes(&private_bytes, &shared_bytes) &&
736      private_bytes > prerender_manager_->config().max_bytes) {
737    Destroy(FINAL_STATUS_MEMORY_LIMIT_EXCEEDED);
738  }
739}
740
741WebContents* PrerenderContents::ReleasePrerenderContents() {
742  prerender_contents_->SetDelegate(NULL);
743  content::WebContentsObserver::Observe(NULL);
744  if (alias_session_storage_namespace)
745    alias_session_storage_namespace->RemoveTransactionLogProcessId(child_id_);
746  return prerender_contents_.release();
747}
748
749RenderViewHost* PrerenderContents::GetRenderViewHostMutable() {
750  return const_cast<RenderViewHost*>(GetRenderViewHost());
751}
752
753const RenderViewHost* PrerenderContents::GetRenderViewHost() const {
754  if (!prerender_contents_.get())
755    return NULL;
756  return prerender_contents_->GetRenderViewHost();
757}
758
759void PrerenderContents::DidNavigate(
760    const history::HistoryAddPageArgs& add_page_args) {
761  add_page_vector_.push_back(add_page_args);
762}
763
764void PrerenderContents::CommitHistory(WebContents* tab) {
765  HistoryTabHelper* history_tab_helper = HistoryTabHelper::FromWebContents(tab);
766  for (size_t i = 0; i < add_page_vector_.size(); ++i)
767    history_tab_helper->UpdateHistoryForNavigation(add_page_vector_[i]);
768}
769
770base::Value* PrerenderContents::GetAsValue() const {
771  if (!prerender_contents_.get())
772    return NULL;
773  base::DictionaryValue* dict_value = new base::DictionaryValue();
774  dict_value->SetString("url", prerender_url_.spec());
775  base::TimeTicks current_time = base::TimeTicks::Now();
776  base::TimeDelta duration = current_time - load_start_time_;
777  dict_value->SetInteger("duration", duration.InSeconds());
778  dict_value->SetBoolean("is_loaded", prerender_contents_ &&
779                                      !prerender_contents_->IsLoading());
780  return dict_value;
781}
782
783bool PrerenderContents::IsCrossSiteNavigationPending() const {
784  if (!prerender_contents_)
785    return false;
786  return (prerender_contents_->GetSiteInstance() !=
787          prerender_contents_->GetPendingSiteInstance());
788}
789
790void PrerenderContents::PrepareForUse() {
791  SetFinalStatus(FINAL_STATUS_USED);
792
793  if (prerender_contents_.get()) {
794    prerender_contents_->SendToAllFrames(
795        new PrerenderMsg_SetIsPrerendering(MSG_ROUTING_NONE, false));
796  }
797
798  NotifyPrerenderStop();
799
800  content::BrowserThread::PostTask(
801      content::BrowserThread::IO,
802      FROM_HERE,
803      base::Bind(&ResumeThrottles, resource_throttles_));
804  resource_throttles_.clear();
805}
806
807SessionStorageNamespace* PrerenderContents::GetSessionStorageNamespace() const {
808  if (!prerender_contents())
809    return NULL;
810  return prerender_contents()->GetController().
811      GetDefaultSessionStorageNamespace();
812}
813
814void PrerenderContents::OnCancelPrerenderForPrinting() {
815  Destroy(FINAL_STATUS_WINDOW_PRINT);
816}
817
818void PrerenderContents::RecordCookieEvent(CookieEvent event,
819                                          bool is_main_frame_http_request,
820                                          bool is_third_party_cookie,
821                                          bool is_for_blocking_resource,
822                                          base::Time earliest_create_date) {
823  // We don't care about sent cookies that were created after this prerender
824  // started.
825  // The reason is that for the purpose of the histograms emitted, we only care
826  // about cookies that existed before the prerender was started, but not
827  // about cookies that were created as part of the prerender. Using the
828  // earliest creation timestamp of all cookies provided by the cookie monster
829  // is a heuristic that yields the desired result pretty closely.
830  // In particular, we pretend no other WebContents make changes to the cookies
831  // relevant to the prerender, which may not actually always be the case, but
832  // hopefully most of the times.
833  if (event == COOKIE_EVENT_SEND && earliest_create_date > start_time_)
834    return;
835
836  InternalCookieEvent internal_event = INTERNAL_COOKIE_EVENT_MAX;
837
838  if (is_main_frame_http_request) {
839    if (event == COOKIE_EVENT_SEND) {
840      internal_event = INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND;
841    } else {
842      internal_event = INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE;
843    }
844  } else {
845    if (event == COOKIE_EVENT_SEND) {
846      internal_event = INTERNAL_COOKIE_EVENT_OTHER_SEND;
847    } else {
848      internal_event = INTERNAL_COOKIE_EVENT_OTHER_CHANGE;
849    }
850  }
851
852  DCHECK_GE(internal_event, 0);
853  DCHECK_LT(internal_event, INTERNAL_COOKIE_EVENT_MAX);
854
855  cookie_status_ |= (1 << internal_event);
856
857  DCHECK_GE(cookie_status_, 0);
858  DCHECK_LT(cookie_status_, kNumCookieStatuses);
859
860  CookieSendType send_type = COOKIE_SEND_TYPE_NONE;
861  if (event == COOKIE_EVENT_SEND) {
862    if (!is_third_party_cookie) {
863      send_type = COOKIE_SEND_TYPE_FIRST_PARTY;
864    } else {
865      if (is_for_blocking_resource) {
866        send_type = COOKIE_SEND_TYPE_THIRD_PARTY_BLOCKING_RESOURCE;
867      } else {
868        send_type = COOKIE_SEND_TYPE_THIRD_PARTY;
869      }
870    }
871  }
872  DCHECK_GE(send_type, 0);
873  DCHECK_LT(send_type, COOKIE_SEND_TYPE_MAX);
874
875  if (cookie_send_type_ < send_type)
876    cookie_send_type_ = send_type;
877}
878
879 void PrerenderContents::AddResourceThrottle(
880     const base::WeakPtr<PrerenderResourceThrottle>& throttle) {
881   resource_throttles_.push_back(throttle);
882 }
883
884 void PrerenderContents::AddNetworkBytes(int64 bytes) {
885   network_bytes_ += bytes;
886 }
887
888}  // namespace prerender
889