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