#include <functional>
#include <utility>
+#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/favicon/favicon_tab_helper.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_tracker.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_tab_contents.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/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/favicon_url.h"
#include "content/public/common/frame_navigate_params.h"
+#include "content/public/common/page_transition_types.h"
+#include "net/url_request/url_request_context_getter.h"
#include "ui/gfx/rect.h"
+using content::BrowserThread;
using content::DownloadItem;
using content::OpenURLParams;
using content::RenderViewHost;
using content::ResourceRedirectDetails;
+using content::ResourceType;
using content::SessionStorageNamespace;
using content::WebContents;
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<base::WeakPtr<PrerenderResourceThrottle> > throttles) {
for (size_t i = 0; i < throttles.size(); i++) {
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(
return NULL;
}
+ virtual void CloseContents(content::WebContents* contents) OVERRIDE {
+ prerender_contents_->Destroy(FINAL_STATUS_CLOSED);
+ }
+
virtual void CanDownload(
RenderViewHost* render_view_host,
- int request_id,
+ const GURL& url,
const std::string& request_method,
const base::Callback<void(bool)>& callback) OVERRIDE {
prerender_contents_->Destroy(FINAL_STATUS_DOWNLOAD);
return false;
}
- virtual void JSOutOfMemory(WebContents* tab) OVERRIDE {
- prerender_contents_->Destroy(FINAL_STATUS_JS_OUT_OF_MEMORY);
- }
-
virtual bool ShouldSuppressDialogs() OVERRIDE {
// We still want to show the user the message when they navigate to this
// page, so cancel this prerender.
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.
}
virtual gfx::Size GetSizeForNewRenderView(
- const WebContents* web_contents) const OVERRIDE {
+ 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_;
PrerenderContents* contents) {
}
+void PrerenderContents::Observer::OnPrerenderDomContentLoaded(
+ PrerenderContents* contents) {
+}
+
void PrerenderContents::Observer::OnPrerenderCreatedMatchCompleteReplacement(
PrerenderContents* contents, PrerenderContents* replacement) {
}
PrerenderContents::Observer::~Observer() {
}
-PrerenderContents::PendingPrerenderInfo::PendingPrerenderInfo(
- base::WeakPtr<PrerenderHandle> weak_prerender_handle,
- Origin origin,
- const GURL& url,
- const content::Referrer& referrer,
- const gfx::Size& size)
- : weak_prerender_handle(weak_prerender_handle),
- origin(origin),
- url(url),
- referrer(referrer),
- size(size) {
-}
-
-PrerenderContents::PendingPrerenderInfo::~PendingPrerenderInfo() {
-}
-
-void PrerenderContents::AddPendingPrerender(
- scoped_ptr<PendingPrerenderInfo> pending_prerender_info) {
- pending_prerenders_.push_back(pending_prerender_info.release());
-}
-
-void PrerenderContents::PrepareForUse() {
- if (prerender_contents_.get()) {
- prerender_contents_->SendToAllFrames(
- new PrerenderMsg_SetIsPrerendering(MSG_ROUTING_NONE, false));
- }
-
- NotifyPrerenderStop();
-
- SessionStorageNamespace* session_storage_namespace = NULL;
- if (prerender_contents_) {
- // TODO(ajwong): This does not correctly handle storage for isolated apps.
- session_storage_namespace = prerender_contents_->
- GetController().GetDefaultSessionStorageNamespace();
- }
- prerender_manager_->StartPendingPrerenders(
- child_id_, &pending_prerenders_, session_storage_namespace);
- pending_prerenders_.clear();
-
- content::BrowserThread::PostTask(
- content::BrowserThread::IO,
- FROM_HERE,
- base::Bind(&ResumeThrottles, resource_throttles_));
- resource_throttles_.clear();
-}
-
PrerenderContents::PrerenderContents(
PrerenderManager* prerender_manager,
Profile* profile,
origin_(origin),
experiment_id_(experiment_id),
creator_child_id_(-1),
- cookie_status_(0) {
+ cookie_status_(0),
+ cookie_send_type_(COOKIE_SEND_TYPE_NONE),
+ network_bytes_(0) {
DCHECK(prerender_manager != NULL);
}
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);
void PrerenderContents::StartPrerendering(
int creator_child_id,
const gfx::Size& size,
- SessionStorageNamespace* session_storage_namespace) {
+ SessionStorageNamespace* session_storage_namespace,
+ net::URLRequestContextGetter* request_context) {
DCHECK(profile_ != NULL);
DCHECK(!size.IsEmpty());
DCHECK(!prerendering_has_started_);
alias_session_storage_namespace = session_storage_namespace->CreateAlias();
prerender_contents_.reset(
CreateWebContents(alias_session_storage_namespace.get()));
- BrowserTabContents::AttachTabHelpers(prerender_contents_.get());
-#if defined(OS_ANDROID)
- // Delay icon fetching until the contents are getting swapped in
- // to conserve network usage in mobile devices.
- FaviconTabHelper::FromWebContents(
- prerender_contents_.get())->set_should_fetch_icons(false);
-#endif // defined(OS_ANDROID)
+ 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.
- prerender_contents_->GetView()->SizeContents(size_);
+ apps::ResizeWebContents(prerender_contents_.get(), size_);
child_id_ = GetRenderViewHost()->GetProcess()->GetID();
route_id_ = GetRenderViewHost()->GetRoutingID();
// the event of a mismatch.
alias_session_storage_namespace->AddTransactionLogProcessId(child_id_);
+ // Add the RenderProcessHost to the Prerender Manager.
+ prerender_manager()->AddPrerenderProcessHost(
+ GetRenderViewHost()->GetProcess());
+
+ // In the prerender tracker, create a Prerender Cookie Store to keep track of
+ // cookie changes performed by the prerender. Once the prerender is shown,
+ // the cookie changes will be committed to the actual cookie store,
+ // otherwise, they will be discarded.
+ // If |request_context| is NULL, the feature must be disabled, so the
+ // operation will not be performed.
+ if (request_context) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&PrerenderTracker::AddPrerenderCookieStoreOnIOThread,
+ base::Unretained(prerender_manager()->prerender_tracker()),
+ GetRenderViewHost()->GetProcess()->GetID(),
+ make_scoped_refptr(request_context),
+ base::Bind(&PrerenderContents::Destroy,
+ AsWeakPtr(),
+ FINAL_STATUS_COOKIE_CONFLICT)));
+ }
+
NotifyPrerenderStart();
// Close ourselves when the application is shutting down.
notification_registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
content::NotificationService::AllSources());
- // Register for our parent profile to shutdown, so we can shut ourselves down
- // as well (should only be called for OTR profiles, as we should receive
- // APP_TERMINATING before non-OTR profiles are destroyed).
- // TODO(tburkard): figure out if this is needed.
- notification_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
- content::Source<Profile>(profile_));
-
// Register to inform new RenderViews that we're prerendering.
notification_registrar_.Add(
this, content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
content::NavigationController::LoadURLParams load_url_params(
prerender_url_);
load_url_params.referrer = referrer_;
- load_url_params.transition_type =
- (origin_ == ORIGIN_OMNIBOX || origin_ == ORIGIN_INSTANT)
- ? content::PageTransitionFromInt(
- content::PAGE_TRANSITION_TYPED |
- content::PAGE_TRANSITION_FROM_ADDRESS_BAR)
- : content::PAGE_TRANSITION_LINK;
+ 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 :
}
void PrerenderContents::SetFinalStatus(FinalStatus final_status) {
- DCHECK(final_status >= FINAL_STATUS_USED && final_status < FINAL_STATUS_MAX);
- DCHECK(final_status_ == FINAL_STATUS_MAX);
+ 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;
}
// 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 ||
- final_status() == FINAL_STATUS_TIMED_OUT ||
- final_status() == FINAL_STATUS_CANCELLED)) {
+ 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(origin(), used, network_bytes_);
+
// Broadcast the removal of aliases.
for (content::RenderProcessHost::iterator host_iterator =
content::RenderProcessHost::AllHostsIterator();
const content::NotificationSource& source,
const content::NotificationDetails& details) {
switch (type) {
- case chrome::NOTIFICATION_PROFILE_DESTROYED:
- Destroy(FINAL_STATUS_PROFILE_DESTROYED);
- return;
-
+ // 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;
RenderViewHost* new_render_view_host) {
}
-size_t PrerenderContents::pending_prerender_count() const {
- return pending_prerenders_.size();
-}
-
WebContents* PrerenderContents::CreateWebContents(
SessionStorageNamespace* session_storage_namespace) {
// TODO(ajwong): Remove the temporary map once prerendering is aware of
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));
replacement));
}
-void PrerenderContents::DidUpdateFaviconURL(
- int32 page_id,
- const std::vector<content::FaviconURL>& urls) {
- VLOG(1) << "PrerenderContents::OnUpdateFaviconURL" << icon_url_;
- for (std::vector<content::FaviconURL>::const_iterator it = urls.begin();
- it != urls.end(); ++it) {
- if (it->icon_type == content::FaviconURL::FAVICON) {
- icon_url_ = it->icon_url;
- VLOG(1) << icon_url_;
- return;
- }
- }
-}
-
bool PrerenderContents::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
// The following messages we do want to consume.
NotifyPrerenderStopLoading();
}
+void PrerenderContents::DocumentLoadedInFrame(
+ content::RenderFrameHost* render_frame_host) {
+ if (!render_frame_host->GetParent())
+ NotifyPrerenderDomContentLoaded();
+}
+
void PrerenderContents::DidStartProvisionalLoadForFrame(
- int64 frame_id,
- int64 parent_frame_id,
- bool is_main_frame,
+ content::RenderFrameHost* render_frame_host,
const GURL& validated_url,
bool is_error_page,
- bool is_iframe_srcdoc,
- RenderViewHost* render_view_host) {
- if (is_main_frame) {
+ bool is_iframe_srcdoc) {
+ if (!render_frame_host->GetParent()) {
if (!CheckURL(validated_url))
return;
}
}
-void PrerenderContents::DidFinishLoad(int64 frame_id,
- const GURL& validated_url,
- bool is_main_frame,
- RenderViewHost* render_view_host) {
- if (is_main_frame)
+void PrerenderContents::DidFinishLoad(
+ content::RenderFrameHost* render_frame_host,
+ const GURL& validated_url) {
+ if (!render_frame_host->GetParent())
has_finished_loading_ = true;
}
// 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)
+ if (details.resource_type != content::RESOURCE_TYPE_MAIN_FRAME)
return;
CheckURL(details.new_url);
}
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();
+
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&ResumeThrottles, resource_throttles_));
+ resource_throttles_.clear();
+}
+
SessionStorageNamespace* PrerenderContents::GetSessionStorageNamespace() const {
if (!prerender_contents())
return NULL;
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.
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(
resource_throttles_.push_back(throttle);
}
+ void PrerenderContents::AddNetworkBytes(int64 bytes) {
+ network_bytes_ += bytes;
+ }
+
} // namespace prerender