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