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