summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortburkard@chromium.org <tburkard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-13 08:19:59 +0000
committertburkard@chromium.org <tburkard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-13 08:19:59 +0000
commit28c5d0b7a83b11685698c5cd2ffb04c91d75a0c6 (patch)
tree949ca1fb18b48a01675ce6f62ecb0943a15bb292
parentf73e1ca075924a6ca1125d5d7f96e374532852c3 (diff)
downloadchromium_src-28c5d0b7a83b11685698c5cd2ffb04c91d75a0c6.zip
chromium_src-28c5d0b7a83b11685698c5cd2ffb04c91d75a0c6.tar.gz
chromium_src-28c5d0b7a83b11685698c5cd2ffb04c91d75a0c6.tar.bz2
Only commit cookie changes in prerenders after a prerender is shown
Will create a PrerenderCookieStore for each prerender, retaining all cookie operations of a prerender until the prerender is shown to the user. Forces prerenders to be in a new render process by themselves for this to work. This is a resubmission of https://codereview.chromium.org/233353003, which had to be reverted due to build breaks. See LGTM's there. BUG=371003 TBR=jam@chromium.org Review URL: https://codereview.chromium.org/280403002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@270049 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/android/java/src/org/chromium/chrome/browser/prerender/ExternalPrerenderHandler.java6
-rw-r--r--chrome/android/javatests/src/org/chromium/chrome/browser/prerender/ExternalPrerenderRequestTest.java8
-rw-r--r--chrome/browser/chrome_content_browser_client.cc40
-rw-r--r--chrome/browser/chrome_content_browser_client.h16
-rw-r--r--chrome/browser/extensions/activity_log/activity_log_browsertest.cc10
-rw-r--r--chrome/browser/extensions/activity_log/activity_log_unittest.cc2
-rw-r--r--chrome/browser/net/chrome_network_delegate.cc12
-rw-r--r--chrome/browser/net/chrome_network_delegate.h10
-rw-r--r--chrome/browser/net/cookie_store_util.cc19
-rw-r--r--chrome/browser/net/evicted_domain_cookie_counter.cc5
-rw-r--r--chrome/browser/net/evicted_domain_cookie_counter.h1
-rw-r--r--chrome/browser/net/evicted_domain_cookie_counter_unittest.cc2
-rw-r--r--chrome/browser/prerender/external_prerender_handler_android.cc11
-rw-r--r--chrome/browser/prerender/external_prerender_handler_android.h3
-rw-r--r--chrome/browser/prerender/prerender_browsertest.cc135
-rw-r--r--chrome/browser/prerender/prerender_contents.cc27
-rw-r--r--chrome/browser/prerender/prerender_contents.h10
-rw-r--r--chrome/browser/prerender/prerender_cookie_store.cc241
-rw-r--r--chrome/browser/prerender/prerender_cookie_store.h164
-rw-r--r--chrome/browser/prerender/prerender_final_status.cc2
-rw-r--r--chrome/browser/prerender/prerender_final_status.h2
-rw-r--r--chrome/browser/prerender/prerender_manager.cc76
-rw-r--r--chrome/browser/prerender/prerender_manager.h37
-rw-r--r--chrome/browser/prerender/prerender_tracker.cc87
-rw-r--r--chrome/browser/prerender/prerender_tracker.h37
-rw-r--r--chrome/browser/prerender/prerender_unittest.cc12
-rw-r--r--chrome/browser/profiles/profile_io_data.cc2
-rw-r--r--chrome/browser/profiles/profile_io_data.h6
-rw-r--r--chrome/browser/ui/search/instant_search_prerenderer_unittest.cc9
-rw-r--r--chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc6
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/test/data/android/prerender/homepage.html12
-rw-r--r--chrome/test/data/prerender/prerender_cookie.html41
-rw-r--r--content/browser/renderer_host/render_process_host_impl.cc10
-rw-r--r--content/public/browser/content_browser_client.cc4
-rw-r--r--content/public/browser/content_browser_client.h6
-rw-r--r--net/cookies/canonical_cookie.cc16
-rw-r--r--net/cookies/canonical_cookie.h9
-rw-r--r--net/cookies/cookie_monster.cc63
-rw-r--r--net/cookies/cookie_monster.h24
-rw-r--r--net/cookies/cookie_monster_store_test.cc2
-rw-r--r--net/cookies/cookie_monster_store_test.h2
-rw-r--r--tools/metrics/histograms/histograms.xml2
43 files changed, 1162 insertions, 29 deletions
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/prerender/ExternalPrerenderHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/prerender/ExternalPrerenderHandler.java
index 3783554..b96062e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/prerender/ExternalPrerenderHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/prerender/ExternalPrerenderHandler.java
@@ -38,12 +38,18 @@ public class ExternalPrerenderHandler {
return nativeHasPrerenderedUrl(profile, url, webContentsPtr);
}
+ public static boolean hasCookieStoreLoaded(Profile profile) {
+ return nativeHasCookieStoreLoaded(profile);
+ }
+
private static native long nativeInit();
private static native boolean nativeAddPrerender(
long nativeExternalPrerenderHandlerAndroid, Profile profile,
long webContentsPtr, String url, String referrer, int width, int height);
private static native boolean nativeHasPrerenderedUrl(
Profile profile, String url, long webContentsPtr);
+ private static native boolean nativeHasCookieStoreLoaded(
+ Profile profile);
private static native void nativeCancelCurrentPrerender(
long nativeExternalPrerenderHandlerAndroid);
}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/prerender/ExternalPrerenderRequestTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/prerender/ExternalPrerenderRequestTest.java
index dcbb9fc..4ef0054 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/prerender/ExternalPrerenderRequestTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/prerender/ExternalPrerenderRequestTest.java
@@ -21,11 +21,14 @@ import java.util.concurrent.Callable;
* Tests for adding and removing prerenders using the {@link ExternalPrerenderHandler}
*/
public class ExternalPrerenderRequestTest extends ChromeShellTestBase {
+ private static final String HOMEPAGE_URL =
+ TestHttpServerClient.getUrl("chrome/test/data/android/prerender/homepage.html");
private static final String GOOGLE_URL =
TestHttpServerClient.getUrl("chrome/test/data/android/prerender/google.html");
private static final String YOUTUBE_URL =
TestHttpServerClient.getUrl("chrome/test/data/android/prerender/youtube.html");
private static final int PRERENDER_DELAY_MS = 500;
+ private static final int CHECK_COOKIE_STORE_FREQUENCY_MS = 200;
private ExternalPrerenderHandler mHandler;
private Profile mProfile;
@@ -34,7 +37,8 @@ public class ExternalPrerenderRequestTest extends ChromeShellTestBase {
public void setUp() throws Exception {
super.setUp();
clearAppData();
- launchChromeShellWithBlankPage();
+ // Launch with a non-blank homepage, to trigger cookie store loading.
+ launchChromeShellWithUrl(HOMEPAGE_URL);
assertTrue(waitForActiveShellToBeDoneLoading());
mHandler = new ExternalPrerenderHandler();
final Callable<Profile> profileCallable = new Callable<Profile>() {
@@ -44,6 +48,8 @@ public class ExternalPrerenderRequestTest extends ChromeShellTestBase {
}
};
mProfile = ThreadUtils.runOnUiThreadBlocking(profileCallable);
+ while (!ExternalPrerenderHandler.hasCookieStoreLoaded(mProfile))
+ Thread.sleep(CHECK_COOKIE_STORE_FREQUENCY_MS);
}
@MediumTest
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 432405c..74504b5 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -631,7 +631,8 @@ float GetDeviceScaleAdjustment() {
namespace chrome {
-ChromeContentBrowserClient::ChromeContentBrowserClient() {
+ChromeContentBrowserClient::ChromeContentBrowserClient()
+ : prerender_tracker_(NULL) {
#if defined(ENABLE_PLUGINS)
for (size_t i = 0; i < arraysize(kPredefinedAllowedFileHandleOrigins); ++i)
allowed_file_handle_origins_.insert(kPredefinedAllowedFileHandleOrigins[i]);
@@ -1215,6 +1216,24 @@ bool ChromeContentBrowserClient::IsSuitableHost(
privilege_required;
}
+bool ChromeContentBrowserClient::MayReuseHost(
+ content::RenderProcessHost* process_host) {
+ // If there is currently a prerender in progress for the host provided,
+ // it may not be shared. We require prerenders to be by themselves in a
+ // separate process, so that we can monitor their resource usage, and so that
+ // we can track the cookies that they change.
+ Profile* profile = Profile::FromBrowserContext(
+ process_host->GetBrowserContext());
+ prerender::PrerenderManager* prerender_manager =
+ prerender::PrerenderManagerFactory::GetForProfile(profile);
+ if (prerender_manager &&
+ prerender_manager->IsProcessPrerendering(process_host)) {
+ return false;
+ }
+
+ return true;
+}
+
// This function is trying to limit the amount of processes used by extensions
// with background pages. It uses a globally set percentage of processes to
// run such extensions and if the limit is exceeded, it returns true, to
@@ -1766,6 +1785,13 @@ bool ChromeContentBrowserClient::AllowSetCookie(
CookieSettings* cookie_settings = io_data->GetCookieSettings();
bool allow = cookie_settings->IsSettingCookieAllowed(url, first_party);
+ if (prerender_tracker_) {
+ prerender_tracker_->OnCookieChangedForURL(
+ render_process_id,
+ context->GetRequestContext()->cookie_store()->GetCookieMonster(),
+ url);
+ }
+
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&TabSpecificContentSettings::CookieChanged, render_process_id,
@@ -2212,6 +2238,8 @@ std::string ChromeContentBrowserClient::GetWorkerProcessTitle(
}
void ChromeContentBrowserClient::ResourceDispatcherHostCreated() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ prerender_tracker_ = g_browser_process->prerender_tracker();
return g_browser_process->ResourceDispatcherHostCreated();
}
@@ -2704,6 +2732,16 @@ ChromeContentBrowserClient::GetDevToolsManagerDelegate() {
return new ChromeDevToolsManagerDelegate();
}
+net::CookieStore*
+ChromeContentBrowserClient::OverrideCookieStoreForRenderProcess(
+ int render_process_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (!prerender_tracker_)
+ return NULL;
+ return prerender_tracker_->
+ GetPrerenderCookieStoreForRenderProcess(render_process_id);
+}
+
#if defined(ENABLE_WEBRTC)
void ChromeContentBrowserClient::MaybeCopyDisableWebRtcEncryptionSwitch(
CommandLine* to_command_line,
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index a586432..3775992 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -31,6 +31,10 @@ namespace extensions {
class BrowserPermissionsPolicyDelegate;
}
+namespace prerender {
+class PrerenderTracker;
+}
+
namespace user_prefs {
class PrefRegistrySyncable;
}
@@ -102,6 +106,7 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient {
const GURL& url) OVERRIDE;
virtual bool IsSuitableHost(content::RenderProcessHost* process_host,
const GURL& site_url) OVERRIDE;
+ virtual bool MayReuseHost(content::RenderProcessHost* process_host) OVERRIDE;
virtual bool ShouldTryToUseExistingProcessHost(
content::BrowserContext* browser_context, const GURL& url) OVERRIDE;
virtual void SiteInstanceGotProcess(
@@ -280,6 +285,9 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient {
virtual bool IsPluginAllowedToUseDevChannelAPIs() OVERRIDE;
+ virtual net::CookieStore* OverrideCookieStoreForRenderProcess(
+ int render_process_id) OVERRIDE;
+
private:
#if defined(ENABLE_WEBRTC)
// Copies disable WebRTC encryption switch depending on the channel.
@@ -298,6 +306,14 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient {
scoped_ptr<extensions::BrowserPermissionsPolicyDelegate>
permissions_policy_delegate_;
+ // The prerender tracker used to determine whether a render process is used
+ // for prerendering and an override cookie store must be provided.
+ // This needs to be kept as a member rather than just looked up from
+ // the profile due to initialization ordering, as well as due to threading.
+ // It is initialized on the UI thread when the ResoureDispatcherHost is
+ // created. It is used only the IO thread.
+ prerender::PrerenderTracker* prerender_tracker_;
+
friend class DisableWebRtcEncryptionFlagTest;
DISALLOW_COPY_AND_ASSIGN(ChromeContentBrowserClient);
diff --git a/chrome/browser/extensions/activity_log/activity_log_browsertest.cc b/chrome/browser/extensions/activity_log/activity_log_browsertest.cc
index a78d30a5..32a175e 100644
--- a/chrome/browser/extensions/activity_log/activity_log_browsertest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_browsertest.cc
@@ -12,7 +12,10 @@
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
+#include "content/public/browser/storage_partition.h"
#include "content/public/test/browser_test_utils.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
@@ -99,6 +102,13 @@ IN_PROC_BROWSER_TEST_F(ActivityLogPrerenderTest, TestScriptInjected) {
"http://www.google.com.bo:%d/test.html",
port));
+ if (!prerender_manager->cookie_store_loaded()) {
+ base::RunLoop loop;
+ prerender_manager->set_on_cookie_store_loaded_cb_for_testing(
+ loop.QuitClosure());
+ loop.Run();
+ }
+
const gfx::Size kSize(640, 480);
scoped_ptr<prerender::PrerenderHandle> prerender_handle(
prerender_manager->AddPrerenderFromLocalPredictor(
diff --git a/chrome/browser/extensions/activity_log/activity_log_unittest.cc b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
index 7f1e530..5c8fda0 100644
--- a/chrome/browser/extensions/activity_log/activity_log_unittest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
@@ -244,6 +244,8 @@ TEST_F(ActivityLogTest, LogPrerender) {
prerender::PrerenderManagerFactory::GetForProfile(
Profile::FromBrowserContext(profile()));
+ prerender_manager->OnCookieStoreLoaded();
+
const gfx::Size kSize(640, 480);
scoped_ptr<prerender::PrerenderHandle> prerender_handle(
prerender_manager->AddPrerenderFromLocalPredictor(
diff --git a/chrome/browser/net/chrome_network_delegate.cc b/chrome/browser/net/chrome_network_delegate.cc
index 1cab93d..bafd11f 100644
--- a/chrome/browser/net/chrome_network_delegate.cc
+++ b/chrome/browser/net/chrome_network_delegate.cc
@@ -29,6 +29,7 @@
#include "chrome/browser/net/client_hints.h"
#include "chrome/browser/net/connect_interceptor.h"
#include "chrome/browser/performance_monitor/performance_monitor.h"
+#include "chrome/browser/prerender/prerender_tracker.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/task_manager/task_manager.h"
#include "chrome/common/pref_names.h"
@@ -52,6 +53,7 @@
#include "net/http/http_response_headers.h"
#include "net/socket_stream/socket_stream.h"
#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
#if defined(OS_CHROMEOS)
#include "base/command_line.h"
@@ -358,7 +360,8 @@ ChromeNetworkDelegate::ChromeNetworkDelegate(
domain_reliability_monitor_(NULL),
received_content_length_(0),
original_content_length_(0),
- first_request_(true) {
+ first_request_(true),
+ prerender_tracker_(NULL) {
DCHECK(event_router);
DCHECK(enable_referrers);
}
@@ -742,6 +745,13 @@ bool ChromeNetworkDelegate::OnCanSetCookie(const net::URLRequest& request,
cookie_line, *options, !allow));
}
+ if (prerender_tracker_) {
+ prerender_tracker_->OnCookieChangedForURL(
+ render_process_id,
+ request.context()->cookie_store()->GetCookieMonster(),
+ request.url());
+ }
+
return allow;
}
diff --git a/chrome/browser/net/chrome_network_delegate.h b/chrome/browser/net/chrome_network_delegate.h
index 2053180..608ba05 100644
--- a/chrome/browser/net/chrome_network_delegate.h
+++ b/chrome/browser/net/chrome_network_delegate.h
@@ -49,6 +49,10 @@ namespace policy {
class URLBlacklistManager;
}
+namespace prerender {
+class PrerenderTracker;
+}
+
// ChromeNetworkDelegate is the central point from within the chrome code to
// add hooks into the network stack.
class ChromeNetworkDelegate : public net::NetworkDelegate {
@@ -106,6 +110,10 @@ class ChromeNetworkDelegate : public net::NetworkDelegate {
domain_reliability_monitor_ = domain_reliability_monitor;
}
+ void set_prerender_tracker(prerender::PrerenderTracker* prerender_tracker) {
+ prerender_tracker_ = prerender_tracker;
+ }
+
// Adds the Client Hints header to HTTP requests.
void SetEnableClientHints();
@@ -228,6 +236,8 @@ class ChromeNetworkDelegate : public net::NetworkDelegate {
bool first_request_;
+ prerender::PrerenderTracker* prerender_tracker_;
+
DISALLOW_COPY_AND_ASSIGN(ChromeNetworkDelegate);
};
diff --git a/chrome/browser/net/cookie_store_util.cc b/chrome/browser/net/cookie_store_util.cc
index 924c9b0..5e5d780 100644
--- a/chrome/browser/net/cookie_store_util.cc
+++ b/chrome/browser/net/cookie_store_util.cc
@@ -12,6 +12,8 @@
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/net/chrome_cookie_notification_details.h"
#include "chrome/browser/net/evicted_domain_cookie_counter.h"
+#include "chrome/browser/prerender/prerender_manager.h"
+#include "chrome/browser/prerender/prerender_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_constants.h"
@@ -49,6 +51,13 @@ class ChromeCookieMonsterDelegate : public net::CookieMonsterDelegate {
this, cookie, removed, cause));
}
+ virtual void OnLoaded() OVERRIDE {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&ChromeCookieMonsterDelegate::OnLoadedAsyncHelper,
+ this));
+ }
+
private:
virtual ~ChromeCookieMonsterDelegate() {}
@@ -73,6 +82,16 @@ class ChromeCookieMonsterDelegate : public net::CookieMonsterDelegate {
}
}
+ void OnLoadedAsyncHelper() {
+ Profile* profile = profile_getter_.Run();
+ if (profile) {
+ prerender::PrerenderManager* prerender_manager =
+ prerender::PrerenderManagerFactory::GetForProfile(profile);
+ if (prerender_manager)
+ prerender_manager->OnCookieStoreLoaded();
+ }
+ }
+
const base::Callback<Profile*(void)> profile_getter_;
};
diff --git a/chrome/browser/net/evicted_domain_cookie_counter.cc b/chrome/browser/net/evicted_domain_cookie_counter.cc
index 2d9590a..71139a5 100644
--- a/chrome/browser/net/evicted_domain_cookie_counter.cc
+++ b/chrome/browser/net/evicted_domain_cookie_counter.cc
@@ -111,6 +111,11 @@ void EvictedDomainCookieCounter::OnCookieChanged(
next_cookie_monster_delegate_->OnCookieChanged(cookie, removed, cause);
}
+void EvictedDomainCookieCounter::OnLoaded() {
+ if (next_cookie_monster_delegate_.get())
+ next_cookie_monster_delegate_->OnLoaded();
+}
+
// static
EvictedDomainCookieCounter::EvictedCookieKey
EvictedDomainCookieCounter::GetKey(const net::CanonicalCookie& cookie) {
diff --git a/chrome/browser/net/evicted_domain_cookie_counter.h b/chrome/browser/net/evicted_domain_cookie_counter.h
index 5cf8797..5731fa9 100644
--- a/chrome/browser/net/evicted_domain_cookie_counter.h
+++ b/chrome/browser/net/evicted_domain_cookie_counter.h
@@ -90,6 +90,7 @@ class EvictedDomainCookieCounter : public net::CookieMonster::Delegate {
virtual void OnCookieChanged(const net::CanonicalCookie& cookie,
bool removed,
ChangeCause cause) OVERRIDE;
+ virtual void OnLoaded() OVERRIDE;
private:
// Identifier of an evicted cookie.
diff --git a/chrome/browser/net/evicted_domain_cookie_counter_unittest.cc b/chrome/browser/net/evicted_domain_cookie_counter_unittest.cc
index ca2597d..1a73589 100644
--- a/chrome/browser/net/evicted_domain_cookie_counter_unittest.cc
+++ b/chrome/browser/net/evicted_domain_cookie_counter_unittest.cc
@@ -193,6 +193,8 @@ TEST_F(EvictedDomainCookieCounterTest, TestChain) {
++(*result_);
}
+ virtual void OnLoaded() OVERRIDE {}
+
private:
virtual ~ChangedDelegateDummy() {}
diff --git a/chrome/browser/prerender/external_prerender_handler_android.cc b/chrome/browser/prerender/external_prerender_handler_android.cc
index 013873b..0be6463 100644
--- a/chrome/browser/prerender/external_prerender_handler_android.cc
+++ b/chrome/browser/prerender/external_prerender_handler_android.cc
@@ -89,6 +89,17 @@ static jboolean HasPrerenderedUrl(JNIEnv* env,
return prerender_manager->HasPrerenderedUrl(url, web_contents);
}
+static jboolean HasCookieStoreLoaded(JNIEnv* env,
+ jclass clazz,
+ jobject jprofile) {
+ Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile);
+ prerender::PrerenderManager* prerender_manager =
+ prerender::PrerenderManagerFactory::GetForProfile(profile);
+ if (!prerender_manager)
+ return false;
+ return prerender_manager->cookie_store_loaded();
+}
+
ExternalPrerenderHandlerAndroid::ExternalPrerenderHandlerAndroid() {}
ExternalPrerenderHandlerAndroid::~ExternalPrerenderHandlerAndroid() {}
diff --git a/chrome/browser/prerender/external_prerender_handler_android.h b/chrome/browser/prerender/external_prerender_handler_android.h
index 111181c..6009ddf 100644
--- a/chrome/browser/prerender/external_prerender_handler_android.h
+++ b/chrome/browser/prerender/external_prerender_handler_android.h
@@ -45,6 +45,9 @@ class ExternalPrerenderHandlerAndroid {
GURL url,
content::WebContents* web_contents);
+ // Whether the cookie store associated with this profile has been loaded.
+ static bool HasCookieStoreLoaded(Profile* profile);
+
static bool RegisterExternalPrerenderHandlerAndroid(JNIEnv* env);
private:
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index bbfba0d..20b9df5 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -1124,6 +1124,12 @@ class PrerenderBrowserTest : virtual public InProcessBrowserTest {
return scoped_ptr<TestPrerender>(prerenders[0]);
}
+ // Navigates to a URL, unrelated to prerendering
+ void NavigateStraightToURL(const std::string dest_html_file) {
+ ui_test_utils::NavigateToURL(current_browser(),
+ test_server()->GetURL(dest_html_file));
+ }
+
void NavigateToDestURL() const {
NavigateToDestURLWithDisposition(CURRENT_TAB, true);
}
@@ -1484,6 +1490,22 @@ class PrerenderBrowserTest : virtual public InProcessBrowserTest {
base::ASCIIToUTF16(page_title));
}
+ void RunJSReturningString(const char* js, std::string* result) {
+ ASSERT_TRUE(
+ content::ExecuteScriptAndExtractString(
+ GetActiveWebContents(),
+ base::StringPrintf("window.domAutomationController.send(%s)",
+ js).c_str(),
+ result));
+ }
+
+ void RunJS(const char* js) {
+ ASSERT_TRUE(content::ExecuteScript(
+ GetActiveWebContents(),
+ base::StringPrintf("window.domAutomationController.send(%s)",
+ js).c_str()));
+ }
+
protected:
bool autostart_test_server_;
@@ -4153,6 +4175,103 @@ IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderPPLTNormalNavigation) {
histograms.ExpectTotalCount("Prerender.none_PerceivedPLTMatchedComplete", 0);
}
+IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
+ PrerenderCookieChangeConflictTest) {
+ NavigateStraightToURL(
+ "files/prerender/prerender_cookie.html?set=1&key=c&value=1");
+
+ GURL url = test_server()->GetURL(
+ "files/prerender/prerender_cookie.html?set=1&key=c&value=2");
+
+ scoped_ptr<TestPrerender> prerender =
+ ExpectPrerender(FINAL_STATUS_COOKIE_CONFLICT);
+ AddPrerender(url, 1);
+ prerender->WaitForStart();
+ prerender->WaitForLoads(1);
+ // Ensure that in the prerendered page, querying the cookie again
+ // via javascript yields the same value that was set during load.
+ EXPECT_TRUE(DidPrerenderPass(prerender->contents()->prerender_contents()));
+
+ // The prerender has loaded. Ensure that the change is not visible
+ // to visible tabs.
+ std::string value;
+ RunJSReturningString("GetCookie('c')", &value);
+ ASSERT_EQ(value, "1");
+
+ // Make a conflicting cookie change, which should cancel the prerender.
+ RunJS("SetCookie('c', '3')");
+ prerender->WaitForStop();
+}
+
+IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderCookieChangeUseTest) {
+ // Permit 2 concurrent prerenders.
+ GetPrerenderManager()->mutable_config().max_link_concurrency = 2;
+ GetPrerenderManager()->mutable_config().max_link_concurrency_per_launcher = 2;
+
+ // Go to a first URL setting the cookie to value "1".
+ NavigateStraightToURL(
+ "files/prerender/prerender_cookie.html?set=1&key=c&value=1");
+
+ // Prerender a URL setting the cookie to value "2".
+ GURL url = test_server()->GetURL(
+ "files/prerender/prerender_cookie.html?set=1&key=c&value=2");
+
+ scoped_ptr<TestPrerender> prerender1 = ExpectPrerender(FINAL_STATUS_USED);
+ AddPrerender(url, 1);
+ prerender1->WaitForStart();
+ prerender1->WaitForLoads(1);
+
+ // Launch a second prerender, setting the cookie to value "3".
+ scoped_ptr<TestPrerender> prerender2 =
+ ExpectPrerender(FINAL_STATUS_COOKIE_CONFLICT);
+ AddPrerender(test_server()->GetURL(
+ "files/prerender/prerender_cookie.html?set=1&key=c&value=3"), 1);
+ prerender2->WaitForStart();
+ prerender2->WaitForLoads(1);
+
+ // Both prerenders have loaded. Ensure that the visible tab is still
+ // unchanged and cannot see their changes.
+ // to visible tabs.
+ std::string value;
+ RunJSReturningString("GetCookie('c')", &value);
+ ASSERT_EQ(value, "1");
+
+ // Navigate to the prerendered URL. The first prerender should be swapped in,
+ // and the changes should now be visible. The second prerender should
+ // be cancelled due to the conflict.
+ ui_test_utils::NavigateToURLWithDisposition(
+ current_browser(),
+ url,
+ CURRENT_TAB,
+ ui_test_utils::BROWSER_TEST_NONE);
+ RunJSReturningString("GetCookie('c')", &value);
+ ASSERT_EQ(value, "2");
+ prerender2->WaitForStop();
+}
+
+IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
+ PrerenderCookieChangeConflictHTTPHeaderTest) {
+ NavigateStraightToURL(
+ "files/prerender/prerender_cookie.html?set=1&key=c&value=1");
+
+ GURL url = test_server()->GetURL("set-cookie?c=2");
+ scoped_ptr<TestPrerender> prerender =
+ ExpectPrerender(FINAL_STATUS_COOKIE_CONFLICT);
+ AddPrerender(url, 1);
+ prerender->WaitForStart();
+ prerender->WaitForLoads(1);
+
+ // The prerender has loaded. Ensure that the change is not visible
+ // to visible tabs.
+ std::string value;
+ RunJSReturningString("GetCookie('c')", &value);
+ ASSERT_EQ(value, "1");
+
+ // Make a conflicting cookie change, which should cancel the prerender.
+ RunJS("SetCookie('c', '3')");
+ prerender->WaitForStop();
+}
+
// Checks that a prerender which calls window.close() on itself is aborted.
IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderWindowClose) {
DisableLoadEventCheck();
@@ -4230,6 +4349,14 @@ class PrerenderOmniboxBrowserTest : public PrerenderBrowserTest {
// Checks that closing the omnibox popup cancels an omnibox prerender.
IN_PROC_BROWSER_TEST_F(PrerenderOmniboxBrowserTest, PrerenderOmniboxCancel) {
+ // Ensure the cookie store has been loaded.
+ if (!GetPrerenderManager()->cookie_store_loaded()) {
+ base::RunLoop loop;
+ GetPrerenderManager()->set_on_cookie_store_loaded_cb_for_testing(
+ loop.QuitClosure());
+ loop.Run();
+ }
+
// Fake an omnibox prerender.
scoped_ptr<TestPrerender> prerender = StartOmniboxPrerender(
test_server()->GetURL("files/empty.html"),
@@ -4247,6 +4374,14 @@ IN_PROC_BROWSER_TEST_F(PrerenderOmniboxBrowserTest, PrerenderOmniboxAbandon) {
GetPrerenderManager()->mutable_config().abandon_time_to_live =
base::TimeDelta::FromDays(999);
+ // Ensure the cookie store has been loaded.
+ if (!GetPrerenderManager()->cookie_store_loaded()) {
+ base::RunLoop loop;
+ GetPrerenderManager()->set_on_cookie_store_loaded_cb_for_testing(
+ loop.QuitClosure());
+ loop.Run();
+ }
+
// Enter a URL into the Omnibox.
OmniboxView* omnibox_view = GetOmniboxView();
omnibox_view->OnBeforePossibleChange();
diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc
index 35f45a4..8e209e1 100644
--- a/chrome/browser/prerender/prerender_contents.cc
+++ b/chrome/browser/prerender/prerender_contents.cc
@@ -39,8 +39,10 @@
#include "content/public/browser/web_contents_delegate.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;
@@ -291,7 +293,8 @@ PrerenderContents* PrerenderContents::FromWebContents(
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_);
@@ -339,6 +342,24 @@ void PrerenderContents::StartPrerendering(
// 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.
+ 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.
@@ -796,8 +817,8 @@ void PrerenderContents::PrepareForUse() {
NotifyPrerenderStop();
- content::BrowserThread::PostTask(
- content::BrowserThread::IO,
+ BrowserThread::PostTask(
+ BrowserThread::IO,
FROM_HERE,
base::Bind(&ResumeThrottles, resource_throttles_));
resource_throttles_.clear();
diff --git a/chrome/browser/prerender/prerender_contents.h b/chrome/browser/prerender/prerender_contents.h
index d60602f..3b8ba1d 100644
--- a/chrome/browser/prerender/prerender_contents.h
+++ b/chrome/browser/prerender/prerender_contents.h
@@ -39,6 +39,10 @@ namespace history {
struct HistoryAddPageArgs;
}
+namespace net {
+class URLRequestContextGetter;
+}
+
namespace prerender {
class PrerenderHandle;
@@ -46,7 +50,8 @@ class PrerenderManager;
class PrerenderResourceThrottle;
class PrerenderContents : public content::NotificationObserver,
- public content::WebContentsObserver {
+ public content::WebContentsObserver,
+ public base::SupportsWeakPtr<PrerenderContents> {
public:
// PrerenderContents::Create uses the currently registered Factory to create
// the PrerenderContents. Factory is intended for testing.
@@ -151,7 +156,8 @@ class PrerenderContents : public content::NotificationObserver,
virtual void StartPrerendering(
int creator_child_id,
const gfx::Size& size,
- content::SessionStorageNamespace* session_storage_namespace);
+ content::SessionStorageNamespace* session_storage_namespace,
+ net::URLRequestContextGetter* request_context);
// Verifies that the prerendering is not using too many resources, and kills
// it if not.
diff --git a/chrome/browser/prerender/prerender_cookie_store.cc b/chrome/browser/prerender/prerender_cookie_store.cc
new file mode 100644
index 0000000..078c941
--- /dev/null
+++ b/chrome/browser/prerender/prerender_cookie_store.cc
@@ -0,0 +1,241 @@
+// Copyright 2014 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_cookie_store.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "content/public/browser/browser_thread.h"
+
+using content::BrowserThread;
+
+namespace prerender {
+
+PrerenderCookieStore::PrerenderCookieStore(
+ scoped_refptr<net::CookieMonster> default_cookie_monster,
+ const base::Closure& cookie_conflict_cb)
+ : in_forwarding_mode_(false),
+ default_cookie_monster_(default_cookie_monster),
+ changes_cookie_monster_(new net::CookieMonster(NULL, NULL)),
+ cookie_conflict_cb_(cookie_conflict_cb),
+ cookie_conflict_(false) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(default_cookie_monster_ != NULL);
+ DCHECK(default_cookie_monster_->loaded());
+}
+
+PrerenderCookieStore::~PrerenderCookieStore() {
+}
+
+void PrerenderCookieStore::SetCookieWithOptionsAsync(
+ const GURL& url,
+ const std::string& cookie_line,
+ const net::CookieOptions& options,
+ const SetCookiesCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ CookieOperation op;
+ op.op = COOKIE_OP_SET_COOKIE_WITH_OPTIONS_ASYNC;
+ op.url = url;
+ op.cookie_line = cookie_line;
+ op.options = options;
+
+ GetCookieStoreForCookieOpAndLog(op)->
+ SetCookieWithOptionsAsync(url, cookie_line, options, callback);
+}
+
+void PrerenderCookieStore::GetCookiesWithOptionsAsync(
+ const GURL& url,
+ const net::CookieOptions& options,
+ const GetCookiesCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ CookieOperation op;
+ op.op = COOKIE_OP_GET_COOKIES_WITH_OPTIONS_ASYNC;
+ op.url = url;
+ op.options = options;
+
+ GetCookieStoreForCookieOpAndLog(op)->
+ GetCookiesWithOptionsAsync(url, options, callback);
+}
+
+void PrerenderCookieStore::GetAllCookiesForURLAsync(
+ const GURL& url,
+ const GetCookieListCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ CookieOperation op;
+ op.op = COOKIE_OP_GET_ALL_COOKIES_FOR_URL_ASYNC;
+ op.url = url;
+
+ GetCookieStoreForCookieOpAndLog(op)->GetAllCookiesForURLAsync(url, callback);
+}
+
+
+void PrerenderCookieStore::DeleteCookieAsync(const GURL& url,
+ const std::string& cookie_name,
+ const base::Closure& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ CookieOperation op;
+ op.op = COOKIE_OP_DELETE_COOKIE_ASYNC;
+ op.url = url;
+ op.cookie_name = cookie_name;
+
+ GetCookieStoreForCookieOpAndLog(op)->DeleteCookieAsync(url, cookie_name,
+ callback);
+}
+
+void PrerenderCookieStore::DeleteAllCreatedBetweenAsync(
+ const base::Time& delete_begin,
+ const base::Time& delete_end,
+ const DeleteCallback& callback) {
+ NOTREACHED();
+}
+
+void PrerenderCookieStore::DeleteAllCreatedBetweenForHostAsync(
+ const base::Time delete_begin,
+ const base::Time delete_end,
+ const GURL& url,
+ const DeleteCallback& callback) {
+ NOTREACHED();
+}
+
+void PrerenderCookieStore::DeleteSessionCookiesAsync(const DeleteCallback&) {
+ NOTREACHED();
+}
+
+net::CookieMonster* PrerenderCookieStore::GetCookieMonster() {
+ NOTREACHED();
+ return NULL;
+}
+
+net::CookieStore* PrerenderCookieStore::GetCookieStoreForCookieOpAndLog(
+ const CookieOperation& op) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ std::string key = default_cookie_monster_->GetKey(op.url.host());
+ bool is_read_only = (op.op == COOKIE_OP_GET_COOKIES_WITH_OPTIONS_ASYNC ||
+ op.op == COOKIE_OP_GET_ALL_COOKIES_FOR_URL_ASYNC);
+
+ if (in_forwarding_mode_)
+ return default_cookie_monster_;
+
+ DCHECK(changes_cookie_monster_ != NULL);
+
+ cookie_ops_.push_back(op);
+
+ bool key_copied = ContainsKey(copied_keys_, key);
+
+ if (key_copied)
+ return changes_cookie_monster_;
+
+ if (is_read_only) {
+ // Insert this key into the set of read keys, if it doesn't exist yet.
+ if (!ContainsKey(read_keys_, key))
+ read_keys_.insert(key);
+ return default_cookie_monster_;
+ }
+
+ // If this method hasn't returned yet, the key has not been copied yet,
+ // and we must copy it due to the requested write operation.
+
+ bool copy_success = default_cookie_monster_->
+ CopyCookiesForKeyToOtherCookieMonster(key, changes_cookie_monster_);
+
+ // The copy must succeed.
+ DCHECK(copy_success);
+
+ copied_keys_.insert(key);
+
+ return changes_cookie_monster_;
+}
+
+void PrerenderCookieStore::ApplyChanges(std::vector<GURL>* cookie_change_urls) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (in_forwarding_mode_)
+ return;
+
+ // Apply all changes to the underlying cookie store.
+ for (std::vector<CookieOperation>::const_iterator it = cookie_ops_.begin();
+ it != cookie_ops_.end();
+ ++it) {
+ switch (it->op) {
+ case COOKIE_OP_SET_COOKIE_WITH_OPTIONS_ASYNC:
+ cookie_change_urls->push_back(it->url);
+ default_cookie_monster_->SetCookieWithOptionsAsync(
+ it->url, it->cookie_line, it->options, SetCookiesCallback());
+ break;
+ case COOKIE_OP_GET_COOKIES_WITH_OPTIONS_ASYNC:
+ default_cookie_monster_->GetCookiesWithOptionsAsync(
+ it->url, it->options, GetCookiesCallback());
+ break;
+ case COOKIE_OP_GET_ALL_COOKIES_FOR_URL_ASYNC:
+ default_cookie_monster_->GetAllCookiesForURLAsync(
+ it->url, GetCookieListCallback());
+ break;
+ case COOKIE_OP_DELETE_COOKIE_ASYNC:
+ cookie_change_urls->push_back(it->url);
+ default_cookie_monster_->DeleteCookieAsync(
+ it->url, it->cookie_name, base::Closure());
+ break;
+ case COOKIE_OP_MAX:
+ NOTREACHED();
+ }
+ }
+
+ in_forwarding_mode_ = true;
+ copied_keys_.clear();
+ cookie_ops_.clear();
+ changes_cookie_monster_ = NULL;
+}
+
+void PrerenderCookieStore::OnCookieChangedForURL(
+ net::CookieMonster* cookie_monster,
+ const GURL& url) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ // If the cookie was changed in a different cookie monster than the one
+ // being decorated, there is nothing to do).
+ if (cookie_monster != default_cookie_monster_)
+ return;
+
+ if (in_forwarding_mode_)
+ return;
+
+ // If we have encountered a conflict before, it has already been recorded
+ // and the cb has been issued, so nothing to do.
+ if (cookie_conflict_)
+ return;
+
+ std::string key = default_cookie_monster_->GetKey(url.host());
+
+ // If the key for the cookie which was modified was neither read nor written,
+ // nothing to do.
+ if ((!ContainsKey(read_keys_, key)) && (!ContainsKey(copied_keys_, key)))
+ return;
+
+ // There was a conflict in cookies. Call the conflict callback, which should
+ // cancel the prerender if necessary (i.e. if it hasn't already been
+ // cancelled for some other reason).
+ // Notice that there is a race here with swapping in the prerender, but this
+ // is the same issue that occurs when two tabs modify cookies for the
+ // same domain concurrently. Therefore, there is no need to do anything
+ // special to prevent this race condition.
+ cookie_conflict_ = true;
+ if (!cookie_conflict_cb_.is_null()) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ cookie_conflict_cb_);
+ }
+}
+
+PrerenderCookieStore::CookieOperation::CookieOperation() {
+}
+
+PrerenderCookieStore::CookieOperation::~CookieOperation() {
+}
+
+} // namespace prerender
diff --git a/chrome/browser/prerender/prerender_cookie_store.h b/chrome/browser/prerender/prerender_cookie_store.h
new file mode 100644
index 0000000..b42ef45
--- /dev/null
+++ b/chrome/browser/prerender/prerender_cookie_store.h
@@ -0,0 +1,164 @@
+// Copyright 2014 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.
+
+#ifndef CHROME_BROWSER_PRERENDER_PRERENDER_COOKIE_STORE_H_
+#define CHROME_BROWSER_PRERENDER_PRERENDER_COOKIE_STORE_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "net/cookies/cookie_monster.h"
+#include "net/cookies/cookie_store.h"
+#include "url/gurl.h"
+
+namespace prerender {
+
+// A cookie store which keeps track of provisional changes to the cookie monster
+// of an underlying request context (called the default cookie monster).
+// Initially, it will proxy read requests to the default cookie monster, and
+// copy on write keys that are being modified into a private cookie monster.
+// Reads for these will then happen from the private cookie monster.
+// Should keys be modified in the default cookie store, the corresponding
+// prerender should be aborted.
+// This class also keeps a log of all cookie transactions. Once ApplyChanges
+// is called, the changes will be applied to the default cookie monster,
+// and any future requests to this object will simply be forwarded to the
+// default cookie monster. After ApplyChanges is called, the prerender tracker,
+// which "owns" the PrerenderCookieStore reference, will remove its entry for
+// the PrerenderCookieStore. Therefore, after ApplyChanges is called, the
+// object will only stick around (and exhibit forwarding mode) as long as
+// eg pending requests hold on to its reference.
+class PrerenderCookieStore : public net::CookieStore {
+ public:
+ // Creates a PrerenderCookieStore using the default cookie monster provided
+ // by the URLRequestContext. The underlying cookie store must be loaded,
+ // ie it's call to loaded() must return true.
+ // Otherwise, copying cookie data between the prerender cookie store
+ // (used to only commit cookie changes once a prerender is shown) would
+ // not work synchronously, which would complicate the code.
+ // |cookie_conflict_cb| will be called when a cookie conflict is detected.
+ // The callback will be run on the UI thread.
+ PrerenderCookieStore(scoped_refptr<net::CookieMonster> default_cookie_store_,
+ const base::Closure& cookie_conflict_cb);
+
+ // CookieStore implementation
+ virtual void SetCookieWithOptionsAsync(
+ const GURL& url,
+ const std::string& cookie_line,
+ const net::CookieOptions& options,
+ const SetCookiesCallback& callback) OVERRIDE;
+
+ virtual void GetCookiesWithOptionsAsync(
+ const GURL& url,
+ const net::CookieOptions& options,
+ const GetCookiesCallback& callback) OVERRIDE;
+
+ virtual void GetAllCookiesForURLAsync(
+ const GURL& url,
+ const GetCookieListCallback& callback) OVERRIDE;
+
+ virtual void DeleteCookieAsync(const GURL& url,
+ const std::string& cookie_name,
+ const base::Closure& callback) OVERRIDE;
+
+ // All the following methods should not be used in the scenarios where
+ // a PrerenderCookieStore is used. This will be checked via NOTREACHED().
+ // Should PrerenderCookieStore used in contexts requiring these, they will
+ // need to be implemented first. They are only intended to be called on the
+ // IO thread.
+
+ virtual void DeleteAllCreatedBetweenAsync(
+ const base::Time& delete_begin,
+ const base::Time& delete_end,
+ const DeleteCallback& callback) OVERRIDE;
+
+ virtual void DeleteAllCreatedBetweenForHostAsync(
+ const base::Time delete_begin,
+ const base::Time delete_end,
+ const GURL& url,
+ const DeleteCallback& callback) OVERRIDE;
+
+ virtual void DeleteSessionCookiesAsync(const DeleteCallback&) OVERRIDE;
+
+ virtual net::CookieMonster* GetCookieMonster() OVERRIDE;
+
+ // Commits the changes made to the underlying cookie store, and switches
+ // into forwarding mode. To be called on the IO thread.
+ // |cookie_change_urls| will be populated with all URLs for which cookies
+ // were updated.
+ void ApplyChanges(std::vector<GURL>* cookie_change_urls);
+
+ // Called when a cookie for a URL is changed in the underlying default cookie
+ // store. To be called on the IO thread. If the key corresponding to the URL
+ // was copied or read, the prerender will be cancelled.
+ void OnCookieChangedForURL(net::CookieMonster* cookie_monster,
+ const GURL& url);
+
+ net::CookieMonster* default_cookie_monster() {
+ return default_cookie_monster_;
+ }
+
+ private:
+ enum CookieOperationType {
+ COOKIE_OP_SET_COOKIE_WITH_OPTIONS_ASYNC,
+ COOKIE_OP_GET_COOKIES_WITH_OPTIONS_ASYNC,
+ COOKIE_OP_GET_ALL_COOKIES_FOR_URL_ASYNC,
+ COOKIE_OP_DELETE_COOKIE_ASYNC,
+ COOKIE_OP_MAX
+ };
+
+ struct CookieOperation {
+ CookieOperationType op;
+ GURL url;
+ net::CookieOptions options;
+ std::string cookie_line;
+ std::string cookie_name;
+ CookieOperation();
+ ~CookieOperation();
+ };
+
+ virtual ~PrerenderCookieStore();
+
+ // Gets the appropriate cookie store for the operation provided, and pushes
+ // it back on the log of cookie operations performed.
+ net::CookieStore* GetCookieStoreForCookieOpAndLog(const CookieOperation& op);
+
+ // Indicates whether the changes have already been applied (ie the prerender
+ // has been shown), and we are merely in forwarding mode;
+ bool in_forwarding_mode_;
+
+ // The default cookie monster.
+ scoped_refptr<net::CookieMonster> default_cookie_monster_;
+
+ // A cookie monster storing changes made by the prerender.
+ // Entire keys are copied from default_cookie_monster_ on change, and then
+ // modified.
+ scoped_refptr<net::CookieMonster> changes_cookie_monster_;
+
+ // Log of cookie operations performed
+ std::vector<CookieOperation> cookie_ops_;
+
+ // The keys which have been copied on write to |changes_cookie_monster_|.
+ std::set<std::string> copied_keys_;
+
+ // Keys which have been read (but not necessarily been modified).
+ std::set<std::string> read_keys_;
+
+ // Callback when a cookie conflict was detected
+ base::Closure cookie_conflict_cb_;
+
+ // Indicates whether a cookie conflict has been detected yet.
+ bool cookie_conflict_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrerenderCookieStore);
+};
+
+} // namespace prerender
+
+#endif // CHROME_BROWSER_PRERENDER_PRERENDER_COOKIE_STORE_H_
diff --git a/chrome/browser/prerender/prerender_final_status.cc b/chrome/browser/prerender/prerender_final_status.cc
index ac0526d..adeaf49 100644
--- a/chrome/browser/prerender/prerender_final_status.cc
+++ b/chrome/browser/prerender/prerender_final_status.cc
@@ -59,6 +59,8 @@ const char* kFinalStatusNames[] = {
"Bad Deferred Redirect",
"Navigation Uncommitted",
"New Navigation Entry",
+ "Cookie Store Not Loaded",
+ "Cookie Conflict",
"Max",
};
COMPILE_ASSERT(arraysize(kFinalStatusNames) == FINAL_STATUS_MAX + 1,
diff --git a/chrome/browser/prerender/prerender_final_status.h b/chrome/browser/prerender/prerender_final_status.h
index 8551c10..412be21 100644
--- a/chrome/browser/prerender/prerender_final_status.h
+++ b/chrome/browser/prerender/prerender_final_status.h
@@ -60,6 +60,8 @@ enum FinalStatus {
FINAL_STATUS_BAD_DEFERRED_REDIRECT = 45,
FINAL_STATUS_NAVIGATION_UNCOMMITTED = 46,
FINAL_STATUS_NEW_NAVIGATION_ENTRY = 47,
+ FINAL_STATUS_COOKIE_STORE_NOT_LOADED = 48,
+ FINAL_STATUS_COOKIE_CONFLICT = 49,
FINAL_STATUS_MAX,
};
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc
index 4f0c083..141afe7 100644
--- a/chrome/browser/prerender/prerender_manager.cc
+++ b/chrome/browser/prerender/prerender_manager.cc
@@ -58,6 +58,7 @@
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/session_storage_namespace.h"
+#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/common/url_constants.h"
@@ -245,7 +246,8 @@ PrerenderManager::PrerenderManager(Profile* profile,
prerender_history_(new PrerenderHistory(kHistoryLength)),
histograms_(new PrerenderHistograms()),
profile_network_bytes_(0),
- last_recorded_profile_network_bytes_(0) {
+ last_recorded_profile_network_bytes_(0),
+ cookie_store_loaded_(false) {
// There are some assumptions that the PrerenderManager is on the UI thread.
// Any other checks simply make sure that the PrerenderManager is accessed on
// the same thread that it was created on.
@@ -303,6 +305,13 @@ PrerenderManager::~PrerenderManager() {
// emptied these vectors already.
DCHECK(active_prerenders_.empty());
DCHECK(to_delete_prerenders_.empty());
+
+ for (PrerenderProcessSet::const_iterator it =
+ prerender_process_hosts_.begin();
+ it != prerender_process_hosts_.end();
+ ++it) {
+ (*it)->RemoveObserver(this);
+ }
}
void PrerenderManager::Shutdown() {
@@ -577,6 +586,14 @@ WebContents* PrerenderManager::SwapInternal(
}
// At this point, we've determined that we will use the prerender.
+ content::RenderProcessHost* process_host =
+ prerender_data->contents()->GetRenderViewHost()->GetProcess();
+ prerender_process_hosts_.erase(process_host);
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&PrerenderTracker::RemovePrerenderCookieStoreOnIOThread,
+ base::Unretained(prerender_tracker()), process_host->GetID(),
+ true));
if (!prerender_data->contents()->load_start_time().is_null()) {
histograms_->RecordTimeUntilUsed(
prerender_data->contents()->origin(),
@@ -748,7 +765,7 @@ const char* PrerenderManager::GetModeString() {
default:
NOTREACHED() << "Invalid PrerenderManager mode.";
break;
- };
+ }
return "";
}
@@ -1216,6 +1233,12 @@ void PrerenderManager::SourceNavigatedAway(PrerenderData* prerender_data) {
SortActivePrerenders();
}
+net::URLRequestContextGetter* PrerenderManager::GetURLRequestContext() {
+ return content::BrowserContext::GetDefaultStoragePartition(profile_)->
+ GetURLRequestContext();
+}
+
+
// private
PrerenderHandle* PrerenderManager::AddPrerender(
Origin origin,
@@ -1283,6 +1306,14 @@ PrerenderHandle* PrerenderManager::AddPrerender(
return NULL;
}
+ if (!cookie_store_loaded()) {
+ // Only prerender if the cookie store for this profile has been loaded.
+ // This is required by PrerenderCookieMonster.
+ RecordFinalStatusWithoutCreatingPrerenderContents(
+ url, origin, experiment, FINAL_STATUS_COOKIE_STORE_NOT_LOADED);
+ return NULL;
+ }
+
PrerenderContents* prerender_contents = CreatePrerenderContents(
url, referrer, origin, experiment);
DCHECK(prerender_contents);
@@ -1307,11 +1338,16 @@ PrerenderHandle* PrerenderManager::AddPrerender(
gfx::Size contents_size =
size.IsEmpty() ? config_.default_tab_bounds.size() : size;
+ net::URLRequestContextGetter* request_context = GetURLRequestContext();
+
prerender_contents->StartPrerendering(process_id, contents_size,
- session_storage_namespace);
+ session_storage_namespace,
+ request_context);
DCHECK(IsControlGroup(experiment) ||
- prerender_contents->prerendering_has_started());
+ prerender_contents->prerendering_has_started() ||
+ (origin == ORIGIN_LOCAL_PREDICTOR &&
+ IsLocalPredictorPrerenderAlwaysControlEnabled()));
if (GetMode() == PRERENDER_MODE_EXPERIMENT_MULTI_PRERENDER_GROUP)
histograms_->RecordConcurrency(active_prerenders_.size());
@@ -1831,4 +1867,36 @@ void PrerenderManager::AddProfileNetworkBytesIfEnabled(int64 bytes) {
profile_network_bytes_ += bytes;
}
+void PrerenderManager::OnCookieStoreLoaded() {
+ cookie_store_loaded_ = true;
+ if (!on_cookie_store_loaded_cb_for_testing_.is_null())
+ on_cookie_store_loaded_cb_for_testing_.Run();
+}
+
+void PrerenderManager::AddPrerenderProcessHost(
+ content::RenderProcessHost* process_host) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(prerender_process_hosts_.find(process_host) ==
+ prerender_process_hosts_.end());
+ prerender_process_hosts_.insert(process_host);
+ process_host->AddObserver(this);
+}
+
+bool PrerenderManager::IsProcessPrerendering(
+ content::RenderProcessHost* process_host) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return (prerender_process_hosts_.find(process_host) !=
+ prerender_process_hosts_.end());
+}
+
+void PrerenderManager::RenderProcessHostDestroyed(
+ content::RenderProcessHost* host) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ prerender_process_hosts_.erase(host);
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&PrerenderTracker::RemovePrerenderCookieStoreOnIOThread,
+ base::Unretained(prerender_tracker()), host->GetID(), false));
+}
+
} // namespace prerender
diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h
index aad510e..95d78d5 100644
--- a/chrome/browser/prerender/prerender_manager.h
+++ b/chrome/browser/prerender/prerender_manager.h
@@ -31,6 +31,7 @@
#include "components/keyed_service/core/keyed_service.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/render_process_host_observer.h"
#include "content/public/browser/session_storage_namespace.h"
#include "content/public/browser/web_contents_observer.h"
#include "net/cookies/canonical_cookie.h"
@@ -74,6 +75,7 @@ class PrerenderLocalPredictor;
class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>,
public base::NonThreadSafe,
public content::NotificationObserver,
+ public content::RenderProcessHostObserver,
public KeyedService,
public MediaCaptureDevicesDispatcher::Observer {
public:
@@ -295,6 +297,8 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>,
PrerenderTracker* prerender_tracker() { return prerender_tracker_; }
+ bool cookie_store_loaded() { return cookie_store_loaded_; }
+
// Adds a condition. This is owned by the PrerenderManager.
void AddCondition(const PrerenderCondition* condition);
@@ -360,6 +364,25 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>,
// profile if prerendering is currently enabled.
void AddProfileNetworkBytesIfEnabled(int64 bytes);
+ // Registers a new ProcessHost performing a prerender. Called by
+ // PrerenderContents.
+ void AddPrerenderProcessHost(content::RenderProcessHost* process_host);
+
+ bool IsProcessPrerendering(content::RenderProcessHost* process_host);
+
+ // content::RenderProcessHostObserver implementation.
+ virtual void RenderProcessHostDestroyed(
+ content::RenderProcessHost* host) OVERRIDE;
+
+ // To be called once the cookie store for this profile has been loaded.
+ void OnCookieStoreLoaded();
+
+ // For testing purposes. Issues a callback once the cookie store has been
+ // loaded.
+ void set_on_cookie_store_loaded_cb_for_testing(base::Closure cb) {
+ on_cookie_store_loaded_cb_for_testing_ = cb;
+ }
+
protected:
class PendingSwap;
class PrerenderData : public base::SupportsWeakPtr<PrerenderData> {
@@ -510,6 +533,11 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>,
// shorten the TTL of the prerendered page.
void SourceNavigatedAway(PrerenderData* prerender_data);
+ // Gets the request context for the profile.
+ // For unit tests, this will be overriden to return NULL, since it is not
+ // needed.
+ virtual net::URLRequestContextGetter* GetURLRequestContext();
+
private:
friend class ::InstantSearchPrerendererTest;
friend class PrerenderBrowserTest;
@@ -720,6 +748,15 @@ class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>,
// The value of profile_network_bytes_ that was last recorded.
int64 last_recorded_profile_network_bytes_;
+ // Set of process hosts being prerendered.
+ typedef std::set<content::RenderProcessHost*> PrerenderProcessSet;
+ PrerenderProcessSet prerender_process_hosts_;
+
+ // Indicates whether the cookie store for this profile has fully loaded yet.
+ bool cookie_store_loaded_;
+
+ base::Closure on_cookie_store_loaded_cb_for_testing_;
+
DISALLOW_COPY_AND_ASSIGN(PrerenderManager);
};
diff --git a/chrome/browser/prerender/prerender_tracker.cc b/chrome/browser/prerender/prerender_tracker.cc
index b44c8d3..cabb35b 100644
--- a/chrome/browser/prerender/prerender_tracker.cc
+++ b/chrome/browser/prerender/prerender_tracker.cc
@@ -8,6 +8,9 @@
#include "base/logging.h"
#include "chrome/browser/prerender/prerender_pending_swap_throttle.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
using content::BrowserThread;
@@ -103,4 +106,88 @@ PrerenderTracker::PendingSwapThrottleData::PendingSwapThrottleData(
PrerenderTracker::PendingSwapThrottleData::~PendingSwapThrottleData() {
}
+scoped_refptr<PrerenderCookieStore>
+PrerenderTracker::GetPrerenderCookieStoreForRenderProcess(
+ int process_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ PrerenderCookieStoreMap::const_iterator it =
+ prerender_cookie_store_map_.find(process_id);
+
+ if (it == prerender_cookie_store_map_.end())
+ return NULL;
+
+ return it->second;
+}
+
+void PrerenderTracker::OnCookieChangedForURL(
+ int process_id,
+ net::CookieMonster* cookie_monster,
+ const GURL& url) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ // We only care about cookie changes by non-prerender tabs, since only those
+ // get applied to the underlying cookie store. Therefore, if a cookie change
+ // originated from a prerender, there is nothing to do.
+ if (ContainsKey(prerender_cookie_store_map_, process_id))
+ return;
+
+ // Since the cookie change did not come from a prerender, broadcast it too
+ // all prerenders so that they can be cancelled if there is a conflict.
+ for (PrerenderCookieStoreMap::iterator it =
+ prerender_cookie_store_map_.begin();
+ it != prerender_cookie_store_map_.end();
+ ++it) {
+ it->second->OnCookieChangedForURL(cookie_monster, url);
+ }
+}
+
+void PrerenderTracker::RemovePrerenderCookieStoreOnIOThread(int process_id,
+ bool was_swapped) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ PrerenderCookieStoreMap::iterator it =
+ prerender_cookie_store_map_.find(process_id);
+
+ if (it == prerender_cookie_store_map_.end())
+ return;
+
+ std::vector<GURL> cookie_change_urls;
+ if (was_swapped)
+ it->second->ApplyChanges(&cookie_change_urls);
+
+ scoped_refptr<net::CookieMonster> cookie_monster(
+ it->second->default_cookie_monster());
+
+ prerender_cookie_store_map_.erase(it);
+
+ // For each cookie updated by ApplyChanges, we need to call
+ // OnCookieChangedForURL so that any potentially conflicting prerenders
+ // will be aborted.
+ for (std::vector<GURL>::const_iterator url_it = cookie_change_urls.begin();
+ url_it != cookie_change_urls.end();
+ ++url_it) {
+ OnCookieChangedForURL(process_id, cookie_monster, *url_it);
+ }
+}
+
+void PrerenderTracker::AddPrerenderCookieStoreOnIOThread(
+ int process_id,
+ scoped_refptr<net::URLRequestContextGetter> request_context,
+ const base::Closure& cookie_conflict_cb) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(request_context != NULL);
+ net::CookieMonster* cookie_monster =
+ request_context->GetURLRequestContext()->cookie_store()->
+ GetCookieMonster();
+ DCHECK(cookie_monster != NULL);
+ bool exists = (prerender_cookie_store_map_.find(process_id) !=
+ prerender_cookie_store_map_.end());
+ DCHECK(!exists);
+ if (exists)
+ return;
+ prerender_cookie_store_map_[process_id] =
+ new PrerenderCookieStore(make_scoped_refptr(cookie_monster),
+ cookie_conflict_cb);
+}
+
} // namespace prerender
diff --git a/chrome/browser/prerender/prerender_tracker.h b/chrome/browser/prerender/prerender_tracker.h
index f9dd17b..6020658 100644
--- a/chrome/browser/prerender/prerender_tracker.h
+++ b/chrome/browser/prerender/prerender_tracker.h
@@ -6,16 +6,26 @@
#define CHROME_BROWSER_PRERENDER_PRERENDER_TRACKER_H_
#include <map>
+#include <set>
#include <utility>
+#include "base/containers/hash_tables.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
+#include "base/synchronization/lock.h"
+#include "chrome/browser/prerender/prerender_cookie_store.h"
+#include "content/public/browser/render_process_host_observer.h"
#include "url/gurl.h"
+namespace net {
+class URLRequestContextGetter;
+}
+
namespace prerender {
class PrerenderPendingSwapThrottle;
-// Global object for maintaining prerender state on the IO thread.
+// Global object for maintaining various prerender state on the IO thread.
class PrerenderTracker {
public:
typedef std::pair<int, int> ChildRouteIdPair;
@@ -46,6 +56,25 @@ class PrerenderTracker {
const ChildRouteIdPair& render_frame_route_id_pair,
bool swap_successful);
+ // Gets the Prerender Cookie Store for a specific render process, if it
+ // is a prerender. Only to be called from the IO thread.
+ scoped_refptr<PrerenderCookieStore> GetPrerenderCookieStoreForRenderProcess(
+ int process_id);
+
+ // Called when a given render process has changed a cookie for |url|,
+ // in |cookie_monster|.
+ // Only to be called from the IO thread.
+ void OnCookieChangedForURL(int process_id,
+ net::CookieMonster* cookie_monster,
+ const GURL& url);
+
+ void AddPrerenderCookieStoreOnIOThread(
+ int process_id,
+ scoped_refptr<net::URLRequestContextGetter> request_context,
+ const base::Closure& cookie_conflict_cb);
+
+ void RemovePrerenderCookieStoreOnIOThread(int process_id, bool was_swapped);
+
private:
// Add/remove prerenders pending swap on the IO Thread.
void AddPrerenderPendingSwapOnIOThread(
@@ -68,6 +97,12 @@ class PrerenderTracker {
PendingSwapThrottleMap;
PendingSwapThrottleMap pending_swap_throttle_map_;
+ // Map of prerendering render process ids to PrerenderCookieStore used for
+ // the prerender. Only to be used on the IO thread.
+ typedef base::hash_map<int, scoped_refptr<PrerenderCookieStore> >
+ PrerenderCookieStoreMap;
+ PrerenderCookieStoreMap prerender_cookie_store_map_;
+
DISALLOW_COPY_AND_ASSIGN(PrerenderTracker);
};
diff --git a/chrome/browser/prerender/prerender_unittest.cc b/chrome/browser/prerender/prerender_unittest.cc
index 1af24ea..6a8a16d 100644
--- a/chrome/browser/prerender/prerender_unittest.cc
+++ b/chrome/browser/prerender/prerender_unittest.cc
@@ -52,7 +52,8 @@ class DummyPrerenderContents : public PrerenderContents {
virtual void StartPrerendering(
int ALLOW_UNUSED creator_child_id,
const gfx::Size& ALLOW_UNUSED size,
- content::SessionStorageNamespace* ALLOW_UNUSED session_storage_namespace)
+ content::SessionStorageNamespace* ALLOW_UNUSED session_storage_namespace,
+ net::URLRequestContextGetter* ALLOW_UNUSED request_context)
OVERRIDE;
virtual bool GetChildId(int* child_id) const OVERRIDE {
@@ -101,6 +102,7 @@ class UnitTestPrerenderManager : public PrerenderManager {
time_ticks_(TimeTicks::Now()),
prerender_tracker_(prerender_tracker) {
set_rate_limit_enabled(false);
+ OnCookieStoreLoaded();
}
virtual ~UnitTestPrerenderManager() {
@@ -229,6 +231,11 @@ class UnitTestPrerenderManager : public PrerenderManager {
prerender_contents_map_.erase(std::make_pair(child_id, route_id));
}
+ protected:
+ virtual net::URLRequestContextGetter* GetURLRequestContext() OVERRIDE {
+ return NULL;
+ }
+
private:
void SetNextPrerenderContents(DummyPrerenderContents* prerender_contents) {
CHECK(!next_prerender_contents_.get());
@@ -296,7 +303,8 @@ DummyPrerenderContents::~DummyPrerenderContents() {
void DummyPrerenderContents::StartPrerendering(
int ALLOW_UNUSED creator_child_id,
const gfx::Size& ALLOW_UNUSED size,
- content::SessionStorageNamespace* ALLOW_UNUSED session_storage_namespace) {
+ content::SessionStorageNamespace* ALLOW_UNUSED session_storage_namespace,
+ net::URLRequestContextGetter* ALLOW_UNUSED request_context) {
// In the base PrerenderContents implementation, StartPrerendering will
// be called even when the PrerenderManager is part of the control group,
// but it will early exit before actually creating a new RenderView if
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index ba8b91e..9a4234f 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -366,6 +366,7 @@ void ProfileIOData::InitializeOnUIThread(Profile* profile) {
#endif
params->profile = profile;
+ params->prerender_tracker = g_browser_process->prerender_tracker();
profile_params_.reset(params.release());
ChromeNetworkDelegate::InitializePrefsOnUIThread(
@@ -948,6 +949,7 @@ void ProfileIOData::Init(
network_delegate->set_cookie_settings(profile_params_->cookie_settings.get());
network_delegate->set_enable_do_not_track(&enable_do_not_track_);
network_delegate->set_force_google_safe_search(&force_safesearch_);
+ network_delegate->set_prerender_tracker(profile_params_->prerender_tracker);
network_delegate_.reset(network_delegate);
fraudulent_certificate_reporter_.reset(
diff --git a/chrome/browser/profiles/profile_io_data.h b/chrome/browser/profiles/profile_io_data.h
index 43b46d5..aee3ea0 100644
--- a/chrome/browser/profiles/profile_io_data.h
+++ b/chrome/browser/profiles/profile_io_data.h
@@ -61,6 +61,10 @@ class PolicyHeaderIOHelper;
class URLBlacklistManager;
} // namespace policy
+namespace prerender {
+class PrerenderTracker;
+}
+
// Conceptually speaking, the ProfileIOData represents data that lives on the IO
// thread that is owned by a Profile, such as, but not limited to, network
// objects like CookieMonster, HttpTransactionFactory, etc. Profile owns
@@ -298,6 +302,8 @@ class ProfileIOData {
// ensure it's not accidently used on the IO thread. Before using it on the
// UI thread, call ProfileManager::IsValidProfile to ensure it's alive.
void* profile;
+
+ prerender::PrerenderTracker* prerender_tracker;
};
explicit ProfileIOData(Profile::ProfileType profile_type);
diff --git a/chrome/browser/ui/search/instant_search_prerenderer_unittest.cc b/chrome/browser/ui/search/instant_search_prerenderer_unittest.cc
index f4ba748..cc8ca49 100644
--- a/chrome/browser/ui/search/instant_search_prerenderer_unittest.cc
+++ b/chrome/browser/ui/search/instant_search_prerenderer_unittest.cc
@@ -59,7 +59,8 @@ class DummyPrerenderContents : public PrerenderContents {
virtual void StartPrerendering(
int ALLOW_UNUSED creator_child_id,
const gfx::Size& ALLOW_UNUSED size,
- content::SessionStorageNamespace* session_storage_namespace) OVERRIDE;
+ content::SessionStorageNamespace* session_storage_namespace,
+ net::URLRequestContextGetter* request_context) OVERRIDE;
virtual bool GetChildId(int* child_id) const OVERRIDE;
virtual bool GetRouteId(int* route_id) const OVERRIDE;
@@ -115,7 +116,8 @@ DummyPrerenderContents::DummyPrerenderContents(
void DummyPrerenderContents::StartPrerendering(
int ALLOW_UNUSED creator_child_id,
const gfx::Size& ALLOW_UNUSED size,
- content::SessionStorageNamespace* session_storage_namespace) {
+ content::SessionStorageNamespace* session_storage_namespace,
+ net::URLRequestContextGetter* request_context) {
prerender_contents_.reset(content::WebContents::CreateWithSessionStorage(
content::WebContents::CreateParams(profile_),
session_storage_namespace_map_));
@@ -181,7 +183,8 @@ class InstantSearchPrerendererTest : public InstantUnitTestBase {
SetPrerenderContentsFactory(
new DummyPrerenderContentsFactory(call_did_finish_load,
session_storage_namespace_map));
-
+ PrerenderManagerFactory::GetForProfile(browser()->profile())->
+ OnCookieStoreLoaded();
if (prerender_search_results_base_page) {
InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
prerenderer->Init(session_storage_namespace_map, gfx::Size(640, 480));
diff --git a/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc b/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc
index 6f62e8d..3d60cc0 100644
--- a/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc
@@ -398,6 +398,12 @@ void NetInternalsTest::SetUpOnMainThread() {
prerender::PrerenderManager* prerender_manager =
prerender::PrerenderManagerFactory::GetForProfile(profile);
prerender_manager->mutable_config().max_bytes = 1000 * 1024 * 1024;
+ if (!prerender_manager->cookie_store_loaded()) {
+ base::RunLoop loop;
+ prerender_manager->set_on_cookie_store_loaded_cb_for_testing(
+ loop.QuitClosure());
+ loop.Run();
+ }
}
content::WebUIMessageHandler* NetInternalsTest::GetMockMessageHandler() {
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 8751ae5..680f46b 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1593,6 +1593,8 @@
'browser/prefs/tracked/tracked_split_preference.h',
'browser/prerender/external_prerender_handler_android.cc',
'browser/prerender/external_prerender_handler_android.h',
+ 'browser/prerender/prerender_cookie_store.cc',
+ 'browser/prerender/prerender_cookie_store.h',
'browser/prerender/prerender_condition.h',
'browser/prerender/prerender_config.cc',
'browser/prerender/prerender_config.h',
diff --git a/chrome/test/data/android/prerender/homepage.html b/chrome/test/data/android/prerender/homepage.html
new file mode 100644
index 0000000..5f9c68b
--- /dev/null
+++ b/chrome/test/data/android/prerender/homepage.html
@@ -0,0 +1,12 @@
+<html>
+
+<head>
+ <title>Homepage</title>
+</head>
+
+<body>
+<h1>Dummy Homepage</h1>
+
+</body>
+
+</html>
diff --git a/chrome/test/data/prerender/prerender_cookie.html b/chrome/test/data/prerender/prerender_cookie.html
new file mode 100644
index 0000000..d57433b
--- /dev/null
+++ b/chrome/test/data/prerender/prerender_cookie.html
@@ -0,0 +1,41 @@
+<html>
+<meta http-equiv="expires" content="0">
+<script src="prerender_events_common.js"></script>
+<script>
+ function GetCookie(cookieName) {
+ var re = RegExp(cookieName + '=([^;]*)');
+ var result = re.exec(document.cookie);
+ if (result)
+ return result[1];
+ return "";
+ }
+
+ function SetCookie(cookieName, cookieValue) {
+ var exp = "Tue, 31 Dec 2030 23:59:59 GMT";
+ if (cookieValue == "")
+ exp = "Thu, 01 Jan 1970 00:00:00 GMT";
+ document.cookie = cookieName + "=" + cookieValue + "; expires=" + exp;
+ }
+
+ function DidPrerenderPass() {
+ // If the prerender did not set a value, everything's fine.
+ if (ExtractGetParameterBadlyAndInsecurely('set', 0) == 0)
+ return true;
+
+ // If the prerender set a cookie, querying it again should yield the same
+ // value that was set during the request.
+ var cookieName = ExtractGetParameterBadlyAndInsecurely('key', "");
+ var cookieValue = ExtractGetParameterBadlyAndInsecurely('value', "");
+ return GetCookie(cookieName) == cookieValue;
+ }
+
+ if (ExtractGetParameterBadlyAndInsecurely('set', 0) != 0) {
+ var cookieName = ExtractGetParameterBadlyAndInsecurely('key', "");
+ var cookieValue = ExtractGetParameterBadlyAndInsecurely('value', "");
+ SetCookie(cookieName, cookieValue);
+ }
+
+ document.write(document.cookie);
+
+</script>
+<body></body></html>
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index d5870eb..26039a4 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1711,11 +1711,12 @@ RenderProcessHost* RenderProcessHost::GetExistingProcessHost(
iterator iter(AllHostsIterator());
while (!iter.IsAtEnd()) {
- if (RenderProcessHostImpl::IsSuitableHost(
+ if (GetContentClient()->browser()->MayReuseHost(iter.GetCurrentValue()) &&
+ RenderProcessHostImpl::IsSuitableHost(
iter.GetCurrentValue(),
- browser_context, site_url))
+ browser_context, site_url)) {
suitable_renderers.push_back(iter.GetCurrentValue());
-
+ }
iter.Advance();
}
@@ -1768,7 +1769,8 @@ RenderProcessHost* RenderProcessHostImpl::GetProcessHostForSite(
std::string site = SiteInstance::GetSiteForURL(browser_context, url)
.possibly_invalid_spec();
RenderProcessHost* host = map->FindProcess(site);
- if (host && !IsSuitableHost(host, browser_context, url)) {
+ if (host && (!GetContentClient()->browser()->MayReuseHost(host) ||
+ !IsSuitableHost(host, browser_context, url))) {
// The registered process does not have an appropriate set of bindings for
// the url. Remove it from the map so we can register a better one.
RecordAction(
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index f0cbaad..4c9b4bf 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -66,6 +66,10 @@ bool ContentBrowserClient::IsSuitableHost(RenderProcessHost* process_host,
return true;
}
+bool ContentBrowserClient::MayReuseHost(RenderProcessHost* process_host) {
+ return true;
+}
+
bool ContentBrowserClient::ShouldTryToUseExistingProcessHost(
BrowserContext* browser_context, const GURL& url) {
return false;
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 8bef6ca..cc95b74 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -235,6 +235,10 @@ class CONTENT_EXPORT ContentBrowserClient {
virtual bool IsSuitableHost(RenderProcessHost* process_host,
const GURL& site_url);
+ // Returns whether a new view for a new site instance can be added to a
+ // given |process_host|.
+ virtual bool MayReuseHost(RenderProcessHost* process_host);
+
// Returns whether a new process should be created or an existing one should
// be reused based on the URL we want to load. This should return false,
// unless there is a good reason otherwise.
@@ -631,7 +635,7 @@ class CONTENT_EXPORT ContentBrowserClient {
// if the default cookie store should be used
// This is called on the IO thread.
virtual net::CookieStore* OverrideCookieStoreForRenderProcess(
- int render_process_id_);
+ int render_process_id);
#if defined(VIDEO_HOLE)
// Allows an embedder to provide its own ExternalVideoSurfaceContainer
diff --git a/net/cookies/canonical_cookie.cc b/net/cookies/canonical_cookie.cc
index 5d67ebc..54a9565 100644
--- a/net/cookies/canonical_cookie.cc
+++ b/net/cookies/canonical_cookie.cc
@@ -394,4 +394,20 @@ std::string CanonicalCookie::DebugString() const {
static_cast<int64>(creation_date_.ToTimeT()));
}
+CanonicalCookie* CanonicalCookie::Duplicate() {
+ CanonicalCookie* cc = new CanonicalCookie();
+ cc->source_ = source_;
+ cc->name_ = name_;
+ cc->value_ = value_;
+ cc->domain_ = domain_;
+ cc->path_ = path_;
+ cc->creation_date_ = creation_date_;
+ cc->expiry_date_ = expiry_date_;
+ cc->last_access_date_ = last_access_date_;
+ cc->secure_ = secure_;
+ cc->httponly_ = httponly_;
+ cc->priority_ = priority_;
+ return cc;
+}
+
} // namespace net
diff --git a/net/cookies/canonical_cookie.h b/net/cookies/canonical_cookie.h
index a78eece..a556740 100644
--- a/net/cookies/canonical_cookie.h
+++ b/net/cookies/canonical_cookie.h
@@ -125,6 +125,9 @@ class NET_EXPORT CanonicalCookie {
std::string DebugString() const;
+ // Returns a duplicate of this cookie.
+ CanonicalCookie* Duplicate();
+
// Returns the cookie source when cookies are set for |url|. This function
// is public for unit test purposes only.
static std::string GetCookieSourceFromURL(const GURL& url);
@@ -134,6 +137,9 @@ class NET_EXPORT CanonicalCookie {
const base::Time& server_time);
private:
+ // NOTE: When any new members are added below, the implementation of
+ // Duplicate() must be updated to copy the new member accordingly.
+
// The source member of a canonical cookie is the origin of the URL that tried
// to set this cookie, minus the port number if any. This field is not
// persistent though; its only used in the in-tab cookies dialog to show the
@@ -153,6 +159,9 @@ class NET_EXPORT CanonicalCookie {
bool secure_;
bool httponly_;
CookiePriority priority_;
+ // NOTE: When any new members are added above this comment, the
+ // implementation of Duplicate() must be updated to copy the new member
+ // accordingly.
};
typedef std::vector<CanonicalCookie> CookieList;
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index f2023b6..7aed8f8 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -53,6 +53,7 @@
#include "base/callback.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/metrics/histogram.h"
@@ -311,7 +312,7 @@ std::string BuildCookieLine(const CanonicalCookieVector& cookies) {
CookieMonster::CookieMonster(PersistentCookieStore* store,
CookieMonsterDelegate* delegate)
: initialized_(false),
- loaded_(false),
+ loaded_(store == NULL),
store_(store),
last_access_threshold_(
TimeDelta::FromSeconds(kDefaultAccessUpdateThresholdSeconds)),
@@ -327,7 +328,7 @@ CookieMonster::CookieMonster(PersistentCookieStore* store,
CookieMonsterDelegate* delegate,
int last_access_threshold_milliseconds)
: initialized_(false),
- loaded_(false),
+ loaded_(store == NULL),
store_(store),
last_access_threshold_(base::TimeDelta::FromMilliseconds(
last_access_threshold_milliseconds)),
@@ -1433,6 +1434,11 @@ void CookieMonster::InitStore() {
store_->Load(base::Bind(&CookieMonster::OnLoaded, this, TimeTicks::Now()));
}
+void CookieMonster::ReportLoaded() {
+ if (delegate_.get())
+ delegate_->OnLoaded();
+}
+
void CookieMonster::OnLoaded(TimeTicks beginning_time,
const std::vector<CanonicalCookie*>& cookies) {
StoreLoadedCookies(cookies);
@@ -1440,6 +1446,8 @@ void CookieMonster::OnLoaded(TimeTicks beginning_time,
// Invoke the task queue of cookie request.
InvokeQueue();
+
+ ReportLoaded();
}
void CookieMonster::OnKeyLoaded(const std::string& key,
@@ -2245,4 +2253,55 @@ Time CookieMonster::CurrentTime() {
Time::FromInternalValue(last_time_seen_.ToInternalValue() + 1));
}
+bool CookieMonster::CopyCookiesForKeyToOtherCookieMonster(
+ std::string key,
+ CookieMonster* other) {
+ ScopedVector<CanonicalCookie> duplicated_cookies;
+
+ {
+ base::AutoLock autolock(lock_);
+ DCHECK(other);
+ if (!loaded_)
+ return false;
+
+ for (CookieMapItPair its = cookies_.equal_range(key);
+ its.first != its.second;
+ ++its.first) {
+ CookieMap::iterator curit = its.first;
+ CanonicalCookie* cc = curit->second;
+
+ duplicated_cookies.push_back(cc->Duplicate());
+ }
+ }
+
+ {
+ base::AutoLock autolock(other->lock_);
+ if (!other->loaded_)
+ return false;
+
+ // There must not exist any entries for the key to be copied in |other|.
+ CookieMapItPair its = other->cookies_.equal_range(key);
+ if (its.first != its.second)
+ return false;
+
+ // Store the copied cookies in |other|.
+ for (ScopedVector<CanonicalCookie>::const_iterator it =
+ duplicated_cookies.begin();
+ it != duplicated_cookies.end();
+ ++it) {
+ other->InternalInsertCookie(key, *it, true);
+ }
+
+ // Since the cookies are owned by |other| now, weak clear must be used.
+ duplicated_cookies.weak_clear();
+ }
+
+ return true;
+}
+
+bool CookieMonster::loaded() {
+ base::AutoLock autolock(lock_);
+ return loaded_;
+}
+
} // namespace net
diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h
index 5a06922..b719c32 100644
--- a/net/cookies/cookie_monster.h
+++ b/net/cookies/cookie_monster.h
@@ -307,6 +307,19 @@ class NET_EXPORT CookieMonster : public CookieStore {
static const char* kDefaultCookieableSchemes[];
static const int kDefaultCookieableSchemesCount;
+ // Copies all keys for the given |key| to another cookie monster |other|.
+ // Both |other| and |this| must be loaded for this operation to succeed.
+ // Furthermore, there may not be any cookies stored in |other| for |key|.
+ // Returns false if any of these conditions is not met.
+ bool CopyCookiesForKeyToOtherCookieMonster(std::string key,
+ CookieMonster* other);
+
+ // Find the key (for lookup in cookies_) based on the given domain.
+ // See comment on keys before the CookieMap typedef.
+ std::string GetKey(const std::string& domain) const;
+
+ bool loaded();
+
private:
// For queueing the cookie monster calls.
class CookieMonsterTask;
@@ -455,6 +468,7 @@ class NET_EXPORT CookieMonster : public CookieStore {
InitStore();
} else {
loaded_ = true;
+ ReportLoaded();
}
initialized_ = true;
}
@@ -464,6 +478,9 @@ class NET_EXPORT CookieMonster : public CookieStore {
// Should only be called by InitIfNecessary().
void InitStore();
+ // Reports to the delegate that the cookie monster was loaded.
+ void ReportLoaded();
+
// Stores cookies loaded from the backing store and invokes any deferred
// calls. |beginning_time| should be the moment PersistentCookieStore::Load
// was invoked and is used for reporting histogram_time_blocked_on_load_.
@@ -576,10 +593,6 @@ class NET_EXPORT CookieMonster : public CookieStore {
CookieItVector::iterator cookie_its_begin,
CookieItVector::iterator cookie_its_end);
- // Find the key (for lookup in cookies_) based on the given domain.
- // See comment on keys before the CookieMap typedef.
- std::string GetKey(const std::string& domain) const;
-
bool HasCookieableScheme(const GURL& url);
// Statistics support
@@ -720,6 +733,9 @@ class NET_EXPORT CookieMonsterDelegate
virtual void OnCookieChanged(const CanonicalCookie& cookie,
bool removed,
ChangeCause cause) = 0;
+ // Indicates that the cookie store has fully loaded.
+ virtual void OnLoaded() = 0;
+
protected:
friend class base::RefCountedThreadSafe<CookieMonsterDelegate>;
virtual ~CookieMonsterDelegate() {}
diff --git a/net/cookies/cookie_monster_store_test.cc b/net/cookies/cookie_monster_store_test.cc
index 350a015..226242a 100644
--- a/net/cookies/cookie_monster_store_test.cc
+++ b/net/cookies/cookie_monster_store_test.cc
@@ -97,6 +97,8 @@ void MockCookieMonsterDelegate::OnCookieChanged(
changes_.push_back(notification);
}
+void MockCookieMonsterDelegate::OnLoaded() {}
+
MockCookieMonsterDelegate::~MockCookieMonsterDelegate() {}
CanonicalCookie BuildCanonicalCookie(const std::string& key,
diff --git a/net/cookies/cookie_monster_store_test.h b/net/cookies/cookie_monster_store_test.h
index ec40de6..efbcbe5 100644
--- a/net/cookies/cookie_monster_store_test.h
+++ b/net/cookies/cookie_monster_store_test.h
@@ -132,6 +132,8 @@ class MockCookieMonsterDelegate : public CookieMonsterDelegate {
bool removed,
CookieMonsterDelegate::ChangeCause cause) OVERRIDE;
+ virtual void OnLoaded() OVERRIDE;
+
private:
virtual ~MockCookieMonsterDelegate();
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 562a280..b5f8241 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -39845,6 +39845,8 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<int value="45" label="BAD_DEFERRED_REDIRECT"/>
<int value="46" label="NAVIGATION_UNCOMMITTED"/>
<int value="47" label="NEW_NAVIGATION_ENTRY"/>
+ <int value="48" label="COOKIE_STORE_NOT_LOADED"/>
+ <int value="49" label="COOKIE_CONFLICT"/>
</enum>
<enum name="PrerenderHoverEvent" type="int">