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