// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/prerender/prerender_contents.h" #include #include #include #include "apps/ui/web_contents_sizer.h" #include "base/bind.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/history/history_tab_helper.h" #include "chrome/browser/history/history_types.h" #include "chrome/browser/prerender/prerender_field_trial.h" #include "chrome/browser/prerender/prerender_final_status.h" #include "chrome/browser/prerender/prerender_handle.h" #include "chrome/browser/prerender/prerender_manager.h" #include "chrome/browser/prerender/prerender_manager_factory.h" #include "chrome/browser/prerender/prerender_resource_throttle.h" #include "chrome/browser/prerender/prerender_tracker.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/tab_helpers.h" #include "chrome/common/prerender_messages.h" #include "chrome/common/render_messages.h" #include "chrome/common/url_constants.h" #include "content/public/browser/browser_child_process_host.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/resource_request_details.h" #include "content/public/browser/session_storage_namespace.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_delegate.h" #include "content/public/browser/web_contents_view.h" #include "content/public/common/frame_navigate_params.h" #include "content/public/common/page_transition_types.h" #include "ui/gfx/rect.h" using content::DownloadItem; using content::OpenURLParams; using content::RenderViewHost; using content::ResourceRedirectDetails; using content::SessionStorageNamespace; using content::WebContents; namespace prerender { namespace { // Internal cookie event. // Whenever a prerender interacts with the cookie store, either sending // existing cookies that existed before the prerender started, or when a cookie // is changed, we record these events for histogramming purposes. enum InternalCookieEvent { INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND = 0, INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE = 1, INTERNAL_COOKIE_EVENT_OTHER_SEND = 2, INTERNAL_COOKIE_EVENT_OTHER_CHANGE = 3, INTERNAL_COOKIE_EVENT_MAX }; // Indicates whether existing cookies were sent, and if they were third party // cookies, and whether they were for blocking resources. // Each value may be inclusive of previous values. We only care about the // value with the highest index that has ever occurred in the course of a // prerender. enum CookieSendType { COOKIE_SEND_TYPE_NONE = 0, COOKIE_SEND_TYPE_FIRST_PARTY = 1, COOKIE_SEND_TYPE_THIRD_PARTY = 2, COOKIE_SEND_TYPE_THIRD_PARTY_BLOCKING_RESOURCE = 3, COOKIE_SEND_TYPE_MAX }; void ResumeThrottles( std::vector > throttles) { for (size_t i = 0; i < throttles.size(); i++) { if (throttles[i]) throttles[i]->Resume(); } } } // namespace // static const int PrerenderContents::kNumCookieStatuses = (1 << INTERNAL_COOKIE_EVENT_MAX); // static const int PrerenderContents::kNumCookieSendTypes = COOKIE_SEND_TYPE_MAX; class PrerenderContentsFactoryImpl : public PrerenderContents::Factory { public: virtual PrerenderContents* CreatePrerenderContents( PrerenderManager* prerender_manager, Profile* profile, const GURL& url, const content::Referrer& referrer, Origin origin, uint8 experiment_id) OVERRIDE { return new PrerenderContents(prerender_manager, profile, url, referrer, origin, experiment_id); } }; // WebContentsDelegateImpl ----------------------------------------------------- class PrerenderContents::WebContentsDelegateImpl : public content::WebContentsDelegate { public: explicit WebContentsDelegateImpl(PrerenderContents* prerender_contents) : prerender_contents_(prerender_contents) { } // content::WebContentsDelegate implementation: virtual WebContents* OpenURLFromTab(WebContents* source, const OpenURLParams& params) OVERRIDE { // |OpenURLFromTab| is typically called when a frame performs a navigation // that requires the browser to perform the transition instead of WebKit. // Examples include prerendering a site that redirects to an app URL, // or if --enable-strict-site-isolation is specified and the prerendered // frame redirects to a different origin. // TODO(cbentzel): Consider supporting this if it is a common case during // prerenders. prerender_contents_->Destroy(FINAL_STATUS_OPEN_URL); return NULL; } virtual void CanDownload( RenderViewHost* render_view_host, int request_id, const std::string& request_method, const base::Callback& callback) OVERRIDE { prerender_contents_->Destroy(FINAL_STATUS_DOWNLOAD); // Cancel the download. callback.Run(false); } virtual bool ShouldCreateWebContents( WebContents* web_contents, int route_id, WindowContainerType window_container_type, const base::string16& frame_name, const GURL& target_url, const std::string& partition_id, SessionStorageNamespace* session_storage_namespace) OVERRIDE { // Since we don't want to permit child windows that would have a // window.opener property, terminate prerendering. prerender_contents_->Destroy(FINAL_STATUS_CREATE_NEW_WINDOW); // Cancel the popup. return false; } virtual bool OnGoToEntryOffset(int offset) OVERRIDE { // This isn't allowed because the history merge operation // does not work if there are renderer issued challenges. // TODO(cbentzel): Cancel in this case? May not need to do // since render-issued offset navigations are not guaranteed, // but indicates that the page cares about the history. return false; } virtual bool ShouldSuppressDialogs() OVERRIDE { // We still want to show the user the message when they navigate to this // page, so cancel this prerender. prerender_contents_->Destroy(FINAL_STATUS_JAVASCRIPT_ALERT); // Always suppress JavaScript messages if they're triggered by a page being // prerendered. return true; } virtual void RegisterProtocolHandler(WebContents* web_contents, const std::string& protocol, const GURL& url, const base::string16& title, bool user_gesture) OVERRIDE { // TODO(mmenke): Consider supporting this if it is a common case during // prerenders. prerender_contents_->Destroy(FINAL_STATUS_REGISTER_PROTOCOL_HANDLER); } virtual gfx::Size GetSizeForNewRenderView( const WebContents* web_contents) const OVERRIDE { // Have to set the size of the RenderView on initialization to be sure it is // set before the RenderView is hidden on all platforms (esp. Android). return prerender_contents_->size_; } private: PrerenderContents* prerender_contents_; }; void PrerenderContents::Observer::OnPrerenderStopLoading( PrerenderContents* contents) { } void PrerenderContents::Observer::OnPrerenderDomContentLoaded( PrerenderContents* contents) { } void PrerenderContents::Observer::OnPrerenderCreatedMatchCompleteReplacement( PrerenderContents* contents, PrerenderContents* replacement) { } PrerenderContents::Observer::Observer() { } PrerenderContents::Observer::~Observer() { } PrerenderContents::PrerenderContents( PrerenderManager* prerender_manager, Profile* profile, const GURL& url, const content::Referrer& referrer, Origin origin, uint8 experiment_id) : prerendering_has_started_(false), session_storage_namespace_id_(-1), prerender_manager_(prerender_manager), prerender_url_(url), referrer_(referrer), profile_(profile), page_id_(0), has_stopped_loading_(false), has_finished_loading_(false), final_status_(FINAL_STATUS_MAX), match_complete_status_(MATCH_COMPLETE_DEFAULT), prerendering_has_been_cancelled_(false), child_id_(-1), route_id_(-1), origin_(origin), experiment_id_(experiment_id), creator_child_id_(-1), main_frame_id_(0), cookie_status_(0), cookie_send_type_(COOKIE_SEND_TYPE_NONE), network_bytes_(0) { DCHECK(prerender_manager != NULL); } PrerenderContents* PrerenderContents::CreateMatchCompleteReplacement() { PrerenderContents* new_contents = prerender_manager_->CreatePrerenderContents( prerender_url(), referrer(), origin(), experiment_id()); new_contents->load_start_time_ = load_start_time_; new_contents->session_storage_namespace_id_ = session_storage_namespace_id_; new_contents->set_match_complete_status( PrerenderContents::MATCH_COMPLETE_REPLACEMENT_PENDING); const bool did_init = new_contents->Init(); DCHECK(did_init); DCHECK_EQ(alias_urls_.front(), new_contents->alias_urls_.front()); DCHECK_EQ(1u, new_contents->alias_urls_.size()); new_contents->alias_urls_ = alias_urls_; // Erase all but the first alias URL; the replacement has adopted the // remainder without increasing the renderer-side reference count. alias_urls_.resize(1); new_contents->set_match_complete_status( PrerenderContents::MATCH_COMPLETE_REPLACEMENT); NotifyPrerenderCreatedMatchCompleteReplacement(new_contents); return new_contents; } bool PrerenderContents::Init() { return AddAliasURL(prerender_url_); } // static PrerenderContents::Factory* PrerenderContents::CreateFactory() { return new PrerenderContentsFactoryImpl(); } // static PrerenderContents* PrerenderContents::FromWebContents( content::WebContents* web_contents) { if (!web_contents) return NULL; PrerenderManager* prerender_manager = PrerenderManagerFactory::GetForProfile( Profile::FromBrowserContext(web_contents->GetBrowserContext())); if (!prerender_manager) return NULL; return prerender_manager->GetPrerenderContents(web_contents); } void PrerenderContents::StartPrerendering( int creator_child_id, const gfx::Size& size, SessionStorageNamespace* session_storage_namespace) { DCHECK(profile_ != NULL); DCHECK(!size.IsEmpty()); DCHECK(!prerendering_has_started_); DCHECK(prerender_contents_.get() == NULL); DCHECK_EQ(-1, creator_child_id_); DCHECK(size_.IsEmpty()); DCHECK_EQ(1U, alias_urls_.size()); creator_child_id_ = creator_child_id; session_storage_namespace_id_ = session_storage_namespace->id(); size_ = size; DCHECK(load_start_time_.is_null()); load_start_time_ = base::TimeTicks::Now(); start_time_ = base::Time::Now(); // Everything after this point sets up the WebContents object and associated // RenderView for the prerender page. Don't do this for members of the // control group. if (prerender_manager_->IsControlGroup(experiment_id())) return; if (origin_ == ORIGIN_LOCAL_PREDICTOR && IsLocalPredictorPrerenderAlwaysControlEnabled()) { return; } prerendering_has_started_ = true; alias_session_storage_namespace = session_storage_namespace->CreateAlias(); prerender_contents_.reset( CreateWebContents(alias_session_storage_namespace.get())); TabHelpers::AttachTabHelpers(prerender_contents_.get()); content::WebContentsObserver::Observe(prerender_contents_.get()); web_contents_delegate_.reset(new WebContentsDelegateImpl(this)); prerender_contents_.get()->SetDelegate(web_contents_delegate_.get()); // Set the size of the prerender WebContents. apps::ResizeWebContents(prerender_contents_.get(), size_); child_id_ = GetRenderViewHost()->GetProcess()->GetID(); route_id_ = GetRenderViewHost()->GetRoutingID(); // Log transactions to see if we could merge session storage namespaces in // the event of a mismatch. alias_session_storage_namespace->AddTransactionLogProcessId(child_id_); NotifyPrerenderStart(); // Close ourselves when the application is shutting down. notification_registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, content::NotificationService::AllSources()); // Register to inform new RenderViews that we're prerendering. notification_registrar_.Add( this, content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED, content::Source(prerender_contents_.get())); // Transfer over the user agent override. prerender_contents_.get()->SetUserAgentOverride( prerender_manager_->config().user_agent_override); content::NavigationController::LoadURLParams load_url_params( prerender_url_); load_url_params.referrer = referrer_; load_url_params.transition_type = content::PAGE_TRANSITION_LINK; if (origin_ == ORIGIN_OMNIBOX) { load_url_params.transition_type = content::PageTransitionFromInt( content::PAGE_TRANSITION_TYPED | content::PAGE_TRANSITION_FROM_ADDRESS_BAR); } else if (origin_ == ORIGIN_INSTANT) { load_url_params.transition_type = content::PageTransitionFromInt( content::PAGE_TRANSITION_GENERATED | content::PAGE_TRANSITION_FROM_ADDRESS_BAR); } load_url_params.override_user_agent = prerender_manager_->config().is_overriding_user_agent ? content::NavigationController::UA_OVERRIDE_TRUE : content::NavigationController::UA_OVERRIDE_FALSE; prerender_contents_.get()->GetController().LoadURLWithParams(load_url_params); } bool PrerenderContents::GetChildId(int* child_id) const { CHECK(child_id); DCHECK_GE(child_id_, -1); *child_id = child_id_; return child_id_ != -1; } bool PrerenderContents::GetRouteId(int* route_id) const { CHECK(route_id); DCHECK_GE(route_id_, -1); *route_id = route_id_; return route_id_ != -1; } void PrerenderContents::SetFinalStatus(FinalStatus final_status) { DCHECK_GE(final_status, FINAL_STATUS_USED); DCHECK_LT(final_status, FINAL_STATUS_MAX); DCHECK_EQ(FINAL_STATUS_MAX, final_status_); final_status_ = final_status; } PrerenderContents::~PrerenderContents() { DCHECK_NE(FINAL_STATUS_MAX, final_status()); DCHECK( prerendering_has_been_cancelled() || final_status() == FINAL_STATUS_USED); DCHECK_NE(ORIGIN_MAX, origin()); // Since a lot of prerenders terminate before any meaningful cookie action // would have happened, only record the cookie status for prerenders who // were used, cancelled, or timed out. if (prerendering_has_started_ && final_status() == FINAL_STATUS_USED) { prerender_manager_->RecordCookieStatus(origin(), experiment_id(), cookie_status_); prerender_manager_->RecordCookieSendType(origin(), experiment_id(), cookie_send_type_); } prerender_manager_->RecordFinalStatusWithMatchCompleteStatus( origin(), experiment_id(), match_complete_status(), final_status()); bool used = final_status() == FINAL_STATUS_USED || final_status() == FINAL_STATUS_WOULD_HAVE_BEEN_USED; prerender_manager_->RecordNetworkBytes(used, network_bytes_); // Broadcast the removal of aliases. for (content::RenderProcessHost::iterator host_iterator = content::RenderProcessHost::AllHostsIterator(); !host_iterator.IsAtEnd(); host_iterator.Advance()) { content::RenderProcessHost* host = host_iterator.GetCurrentValue(); host->Send(new PrerenderMsg_OnPrerenderRemoveAliases(alias_urls_)); } // If we still have a WebContents, clean up anything we need to and then // destroy it. if (prerender_contents_.get()) delete ReleasePrerenderContents(); } void PrerenderContents::AddObserver(Observer* observer) { DCHECK_EQ(FINAL_STATUS_MAX, final_status_); observer_list_.AddObserver(observer); } void PrerenderContents::RemoveObserver(Observer* observer) { observer_list_.RemoveObserver(observer); } void PrerenderContents::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { switch (type) { // TODO(davidben): Try to remove this in favor of relying on // FINAL_STATUS_PROFILE_DESTROYED. case chrome::NOTIFICATION_APP_TERMINATING: Destroy(FINAL_STATUS_APP_TERMINATING); return; case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: { if (prerender_contents_.get()) { DCHECK_EQ(content::Source(source).ptr(), prerender_contents_.get()); content::Details new_render_view_host(details); OnRenderViewHostCreated(new_render_view_host.ptr()); // Make sure the size of the RenderViewHost has been passed to the new // RenderView. Otherwise, the size may not be sent until the // RenderViewReady event makes it from the render process to the UI // thread of the browser process. When the RenderView receives its // size, is also sets itself to be visible, which would then break the // visibility API. new_render_view_host->WasResized(); prerender_contents_->WasHidden(); } break; } default: NOTREACHED() << "Unexpected notification sent."; break; } } void PrerenderContents::OnRenderViewHostCreated( RenderViewHost* new_render_view_host) { } WebContents* PrerenderContents::CreateWebContents( SessionStorageNamespace* session_storage_namespace) { // TODO(ajwong): Remove the temporary map once prerendering is aware of // multiple session storage namespaces per tab. content::SessionStorageNamespaceMap session_storage_namespace_map; session_storage_namespace_map[std::string()] = session_storage_namespace; return WebContents::CreateWithSessionStorage( WebContents::CreateParams(profile_), session_storage_namespace_map); } void PrerenderContents::NotifyPrerenderStart() { DCHECK_EQ(FINAL_STATUS_MAX, final_status_); FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStart(this)); } void PrerenderContents::NotifyPrerenderStopLoading() { FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStopLoading(this)); } void PrerenderContents::NotifyPrerenderDomContentLoaded() { FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderDomContentLoaded(this)); } void PrerenderContents::NotifyPrerenderStop() { DCHECK_NE(FINAL_STATUS_MAX, final_status_); FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderStop(this)); observer_list_.Clear(); } void PrerenderContents::NotifyPrerenderCreatedMatchCompleteReplacement( PrerenderContents* replacement) { FOR_EACH_OBSERVER(Observer, observer_list_, OnPrerenderCreatedMatchCompleteReplacement(this, replacement)); } bool PrerenderContents::OnMessageReceived(const IPC::Message& message) { bool handled = true; // The following messages we do want to consume. IPC_BEGIN_MESSAGE_MAP(PrerenderContents, message) IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CancelPrerenderForPrinting, OnCancelPrerenderForPrinting) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } bool PrerenderContents::CheckURL(const GURL& url) { if (!url.SchemeIsHTTPOrHTTPS()) { DCHECK_NE(MATCH_COMPLETE_REPLACEMENT_PENDING, match_complete_status_); Destroy(FINAL_STATUS_UNSUPPORTED_SCHEME); return false; } if (match_complete_status_ != MATCH_COMPLETE_REPLACEMENT_PENDING && prerender_manager_->HasRecentlyBeenNavigatedTo(origin(), url)) { Destroy(FINAL_STATUS_RECENTLY_VISITED); return false; } return true; } bool PrerenderContents::AddAliasURL(const GURL& url) { if (!CheckURL(url)) return false; alias_urls_.push_back(url); for (content::RenderProcessHost::iterator host_iterator = content::RenderProcessHost::AllHostsIterator(); !host_iterator.IsAtEnd(); host_iterator.Advance()) { content::RenderProcessHost* host = host_iterator.GetCurrentValue(); host->Send(new PrerenderMsg_OnPrerenderAddAlias(url)); } return true; } bool PrerenderContents::Matches( const GURL& url, const SessionStorageNamespace* session_storage_namespace) const { if (session_storage_namespace && session_storage_namespace_id_ != session_storage_namespace->id()) { return false; } return std::count_if(alias_urls_.begin(), alias_urls_.end(), std::bind2nd(std::equal_to(), url)) != 0; } void PrerenderContents::RenderProcessGone(base::TerminationStatus status) { Destroy(FINAL_STATUS_RENDERER_CRASHED); } void PrerenderContents::RenderFrameCreated( content::RenderFrameHost* render_frame_host) { // When a new RenderFrame is created for a prerendering WebContents, tell the // new RenderFrame it's being used for prerendering before any navigations // occur. Note that this is always triggered before the first navigation, so // there's no need to send the message just after the WebContents is created. render_frame_host->Send(new PrerenderMsg_SetIsPrerendering( render_frame_host->GetRoutingID(), true)); } void PrerenderContents::DidStopLoading( content::RenderViewHost* render_view_host) { has_stopped_loading_ = true; NotifyPrerenderStopLoading(); } void PrerenderContents::DocumentLoadedInFrame( int64 frame_id, RenderViewHost* render_view_host) { if (frame_id == main_frame_id_) NotifyPrerenderDomContentLoaded(); } void PrerenderContents::DidStartProvisionalLoadForFrame( int64 frame_id, int64 parent_frame_id, bool is_main_frame, const GURL& validated_url, bool is_error_page, bool is_iframe_srcdoc, RenderViewHost* render_view_host) { if (is_main_frame) { if (!CheckURL(validated_url)) return; // Usually, this event fires if the user clicks or enters a new URL. // Neither of these can happen in the case of an invisible prerender. // So the cause is: Some JavaScript caused a new URL to be loaded. In that // case, the spinner would start again in the browser, so we must reset // has_stopped_loading_ so that the spinner won't be stopped. has_stopped_loading_ = false; has_finished_loading_ = false; } } void PrerenderContents::DidCommitProvisionalLoadForFrame( int64 frame_id, const base::string16& frame_unique_name, bool is_main_frame, const GURL& url, content::PageTransition transition_type, RenderViewHost* render_view_host) { if (is_main_frame) { main_frame_id_ = frame_id; } } void PrerenderContents::DidFinishLoad(int64 frame_id, const GURL& validated_url, bool is_main_frame, RenderViewHost* render_view_host) { if (is_main_frame) has_finished_loading_ = true; } void PrerenderContents::DidNavigateMainFrame( const content::LoadCommittedDetails& details, const content::FrameNavigateParams& params) { // If the prerender made a second navigation entry, abort the prerender. This // avoids having to correctly implement a complex history merging case (this // interacts with location.replace) and correctly synchronize with the // renderer. The final status may be monitored to see we need to revisit this // decision. This does not affect client redirects as those do not push new // history entries. (Calls to location.replace, navigations before onload, and // with timeouts under 1 second do not create // entries in Blink.) if (prerender_contents_->GetController().GetEntryCount() > 1) { Destroy(FINAL_STATUS_NEW_NAVIGATION_ENTRY); return; } // Add each redirect as an alias. |params.url| is included in // |params.redirects|. // // TODO(davidben): We do not correctly patch up history for renderer-initated // navigations which add history entries. http://crbug.com/305660. for (size_t i = 0; i < params.redirects.size(); i++) { if (!AddAliasURL(params.redirects[i])) return; } } void PrerenderContents::DidGetRedirectForResourceRequest( RenderViewHost* render_view_host, const content::ResourceRedirectDetails& details) { // DidGetRedirectForResourceRequest can come for any resource on a page. If // it's a redirect on the top-level resource, the name needs to be remembered // for future matching, and if it redirects to an https resource, it needs to // be canceled. If a subresource is redirected, nothing changes. if (details.resource_type != ResourceType::MAIN_FRAME) return; CheckURL(details.new_url); } void PrerenderContents::Destroy(FinalStatus final_status) { DCHECK_NE(final_status, FINAL_STATUS_USED); if (prerendering_has_been_cancelled_) return; SetFinalStatus(final_status); prerendering_has_been_cancelled_ = true; prerender_manager_->AddToHistory(this); prerender_manager_->MoveEntryToPendingDelete(this, final_status); // Note that if this PrerenderContents was made into a MatchComplete // replacement by MoveEntryToPendingDelete, NotifyPrerenderStop will // not reach the PrerenderHandle. Rather // OnPrerenderCreatedMatchCompleteReplacement will propogate that // information to the referer. if (!prerender_manager_->IsControlGroup(experiment_id()) && (prerendering_has_started() || match_complete_status() == MATCH_COMPLETE_REPLACEMENT)) { NotifyPrerenderStop(); } } base::ProcessMetrics* PrerenderContents::MaybeGetProcessMetrics() { if (process_metrics_.get() == NULL) { // If a PrenderContents hasn't started prerending, don't be fully formed. if (!GetRenderViewHost() || !GetRenderViewHost()->GetProcess()) return NULL; base::ProcessHandle handle = GetRenderViewHost()->GetProcess()->GetHandle(); if (handle == base::kNullProcessHandle) return NULL; #if !defined(OS_MACOSX) process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(handle)); #else process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics( handle, content::BrowserChildProcessHost::GetPortProvider())); #endif } return process_metrics_.get(); } void PrerenderContents::DestroyWhenUsingTooManyResources() { base::ProcessMetrics* metrics = MaybeGetProcessMetrics(); if (metrics == NULL) return; size_t private_bytes, shared_bytes; if (metrics->GetMemoryBytes(&private_bytes, &shared_bytes) && private_bytes > prerender_manager_->config().max_bytes) { Destroy(FINAL_STATUS_MEMORY_LIMIT_EXCEEDED); } } WebContents* PrerenderContents::ReleasePrerenderContents() { prerender_contents_->SetDelegate(NULL); content::WebContentsObserver::Observe(NULL); if (alias_session_storage_namespace) alias_session_storage_namespace->RemoveTransactionLogProcessId(child_id_); return prerender_contents_.release(); } RenderViewHost* PrerenderContents::GetRenderViewHostMutable() { return const_cast(GetRenderViewHost()); } const RenderViewHost* PrerenderContents::GetRenderViewHost() const { if (!prerender_contents_.get()) return NULL; return prerender_contents_->GetRenderViewHost(); } void PrerenderContents::DidNavigate( const history::HistoryAddPageArgs& add_page_args) { add_page_vector_.push_back(add_page_args); } void PrerenderContents::CommitHistory(WebContents* tab) { HistoryTabHelper* history_tab_helper = HistoryTabHelper::FromWebContents(tab); for (size_t i = 0; i < add_page_vector_.size(); ++i) history_tab_helper->UpdateHistoryForNavigation(add_page_vector_[i]); } base::Value* PrerenderContents::GetAsValue() const { if (!prerender_contents_.get()) return NULL; base::DictionaryValue* dict_value = new base::DictionaryValue(); dict_value->SetString("url", prerender_url_.spec()); base::TimeTicks current_time = base::TimeTicks::Now(); base::TimeDelta duration = current_time - load_start_time_; dict_value->SetInteger("duration", duration.InSeconds()); dict_value->SetBoolean("is_loaded", prerender_contents_ && !prerender_contents_->IsLoading()); return dict_value; } bool PrerenderContents::IsCrossSiteNavigationPending() const { if (!prerender_contents_) return false; return (prerender_contents_->GetSiteInstance() != prerender_contents_->GetPendingSiteInstance()); } void PrerenderContents::PrepareForUse() { SetFinalStatus(FINAL_STATUS_USED); if (prerender_contents_.get()) { prerender_contents_->SendToAllFrames( new PrerenderMsg_SetIsPrerendering(MSG_ROUTING_NONE, false)); } NotifyPrerenderStop(); content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, base::Bind(&ResumeThrottles, resource_throttles_)); resource_throttles_.clear(); } SessionStorageNamespace* PrerenderContents::GetSessionStorageNamespace() const { if (!prerender_contents()) return NULL; return prerender_contents()->GetController(). GetDefaultSessionStorageNamespace(); } void PrerenderContents::OnCancelPrerenderForPrinting() { Destroy(FINAL_STATUS_WINDOW_PRINT); } void PrerenderContents::RecordCookieEvent(CookieEvent event, bool is_main_frame_http_request, bool is_third_party_cookie, bool is_for_blocking_resource, base::Time earliest_create_date) { // We don't care about sent cookies that were created after this prerender // started. // The reason is that for the purpose of the histograms emitted, we only care // about cookies that existed before the prerender was started, but not // about cookies that were created as part of the prerender. Using the // earliest creation timestamp of all cookies provided by the cookie monster // is a heuristic that yields the desired result pretty closely. // In particular, we pretend no other WebContents make changes to the cookies // relevant to the prerender, which may not actually always be the case, but // hopefully most of the times. if (event == COOKIE_EVENT_SEND && earliest_create_date > start_time_) return; InternalCookieEvent internal_event = INTERNAL_COOKIE_EVENT_MAX; if (is_main_frame_http_request) { if (event == COOKIE_EVENT_SEND) { internal_event = INTERNAL_COOKIE_EVENT_MAIN_FRAME_SEND; } else { internal_event = INTERNAL_COOKIE_EVENT_MAIN_FRAME_CHANGE; } } else { if (event == COOKIE_EVENT_SEND) { internal_event = INTERNAL_COOKIE_EVENT_OTHER_SEND; } else { internal_event = INTERNAL_COOKIE_EVENT_OTHER_CHANGE; } } DCHECK_GE(internal_event, 0); DCHECK_LT(internal_event, INTERNAL_COOKIE_EVENT_MAX); cookie_status_ |= (1 << internal_event); DCHECK_GE(cookie_status_, 0); DCHECK_LT(cookie_status_, kNumCookieStatuses); CookieSendType send_type = COOKIE_SEND_TYPE_NONE; if (event == COOKIE_EVENT_SEND) { if (!is_third_party_cookie) { send_type = COOKIE_SEND_TYPE_FIRST_PARTY; } else { if (is_for_blocking_resource) { send_type = COOKIE_SEND_TYPE_THIRD_PARTY_BLOCKING_RESOURCE; } else { send_type = COOKIE_SEND_TYPE_THIRD_PARTY; } } } DCHECK_GE(send_type, 0); DCHECK_LT(send_type, COOKIE_SEND_TYPE_MAX); if (cookie_send_type_ < send_type) cookie_send_type_ = send_type; } void PrerenderContents::AddResourceThrottle( const base::WeakPtr& throttle) { resource_throttles_.push_back(throttle); } void PrerenderContents::AddNetworkBytes(int64 bytes) { network_bytes_ += bytes; } } // namespace prerender