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