Chromium Code Reviews| Index: chrome/browser/net/predictor_browsertest.cc |
| diff --git a/chrome/browser/net/predictor_browsertest.cc b/chrome/browser/net/predictor_browsertest.cc |
| index 0b1884856d51aaa37ca2f5757b48499e2ff05839..5fe2eb38e550c550efbcc8b47a3f690b59e58313 100644 |
| --- a/chrome/browser/net/predictor_browsertest.cc |
| +++ b/chrome/browser/net/predictor_browsertest.cc |
| @@ -16,11 +16,13 @@ |
| #include "chrome/browser/net/predictor.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| +#include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/common/content_switches.h" |
| +#include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/test_utils.h" |
| #include "net/base/host_port_pair.h" |
| #include "net/base/ip_endpoint.h" |
| @@ -30,6 +32,11 @@ |
| #include "net/socket/stream_socket.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/test/embedded_test_server/embedded_test_server_connection_listener.h" |
| +#include "net/test/embedded_test_server/http_request.h" |
| +#include "net/test/embedded_test_server/http_response.h" |
| +#include "net/test/url_request/url_request_failed_job.h" |
| +#include "net/url_request/url_request_filter.h" |
| +#include "net/url_request/url_request_interceptor.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| using content::BrowserThread; |
| @@ -71,7 +78,11 @@ class ConnectionListener |
| // Get called from the EmbeddedTestServer thread to be notified that |
| // a connection was read from. |
| - void ReadFromSocket(const net::StreamSocket& connection) override { |
| + void ReadFromSocket(const net::StreamSocket& connection, int rv) override { |
| + // Don't log a read if no data was transferred. This case often happens if |
| + // the sockets of the test server are being flushed and disconnected. |
| + if (rv <= 0) |
| + return; |
| base::AutoLock lock(lock_); |
| uint16_t socket = GetPort(connection); |
| EXPECT_FALSE(sockets_.find(socket) == sockets_.end()); |
| @@ -101,6 +112,11 @@ class ConnectionListener |
| void WaitUntilFirstConnectionRead() { read_loop_.Run(); } |
| + void ClearSockets() { |
|
mmenke
2016/04/22 16:29:26
ClearSocketCounts? The external API is all about
Charlie Harrison
2016/04/28 16:29:51
Done.
|
| + base::AutoLock lock(lock_); |
| + sockets_.clear(); |
| + } |
| + |
| private: |
| static uint16_t GetPort(const net::StreamSocket& connection) { |
| // Get the remote port of the peer, since the local port will always be the |
| @@ -202,6 +218,111 @@ class HostResolutionRequestRecorder : public net::HostResolverProc { |
| DISALLOW_COPY_AND_ASSIGN(HostResolutionRequestRecorder); |
| }; |
| +// This class intercepts URLRequests and responds with 404s. |
| +class NotFoundRequestInterceptor : public net::URLRequestInterceptor { |
| + public: |
| + NotFoundRequestInterceptor() {} |
| + ~NotFoundRequestInterceptor() override {} |
| + |
| + private: |
| + net::URLRequestJob* MaybeInterceptRequest( |
| + net::URLRequest* request, |
| + net::NetworkDelegate* network_delegate) const override { |
| + const char kHeaders[] = |
| + "HTTP/1.1 404 Not Found\n" |
| + "\n"; |
| + std::string headers(kHeaders, arraysize(kHeaders)); |
|
mmenke
2016/04/22 16:29:25
Above two lines aren't used.
Charlie Harrison
2016/04/28 16:29:51
Done.
|
| + |
| + return new net::URLRequestFailedJob(request, network_delegate, |
| + net::ERR_ABORTED); |
| + } |
| + DISALLOW_COPY_AND_ASSIGN(NotFoundRequestInterceptor); |
| +}; |
| + |
| +class CrossSitePredictorObserver |
| + : public chrome_browser_net::PredictorObserver { |
| + public: |
| + CrossSitePredictorObserver(const GURL source_host, const GURL cross_site_host) |
|
mmenke
2016/04/22 16:29:25
nit: const GURL& (x2)
mmenke
2016/04/22 16:29:25
include gurl.h
Charlie Harrison
2016/04/28 16:29:51
Done.
Charlie Harrison
2016/04/28 16:29:51
Done.
|
| + : source_host_(source_host), |
| + cross_site_host_(cross_site_host), |
| + cross_site_learned_(0), |
| + same_site_learned_(0), |
|
mmenke
2016/04/22 16:29:25
I don't think we ever use same_site_learned_? The
Charlie Harrison
2016/04/28 16:29:51
Removed. If we want to add tests for them we can l
|
| + cross_site_preconnected_(0), |
| + same_site_preconnected_(0) {} |
| + |
| + void OnPreconnectUrl( |
| + const GURL& original_url, |
| + const GURL& first_party_for_cookies, |
| + chrome_browser_net::UrlInfo::ResolutionMotivation motivation, |
| + int count) override { |
| + base::AutoLock lock(lock_); |
| + if (original_url == cross_site_host_) { |
| + cross_site_preconnected_ = std::max(cross_site_preconnected_, count); |
| + } else if (original_url == source_host_) { |
| + same_site_preconnected_ = std::max(same_site_preconnected_, count); |
| + } else { |
| + NOTREACHED() << "Preconnected" << original_url |
|
mmenke
2016/04/22 16:29:25
Should use ADD_FAILURE() instead (A gtest macro th
Charlie Harrison
2016/04/28 16:29:51
Done.
|
| + << " When should only be preconnecting the source host: " |
| + << source_host_ |
| + << " or the cross site host: " << cross_site_host_; |
| + } |
| + } |
| + |
| + void OnLearnFromNavigation(const GURL& referring_url, |
| + const GURL& target_url) override { |
| + base::AutoLock lock(lock_); |
| + if (referring_url == source_host_ && target_url == cross_site_host_) { |
| + cross_site_learned_++; |
| + } else if (referring_url == source_host_ && target_url == source_host_) { |
| + same_site_learned_++; |
| + } else if (referring_url != cross_site_host_) { |
|
mmenke
2016/04/22 16:29:25
What if target_url is unexpected? Can we just do:
Charlie Harrison
2016/04/28 16:29:51
The code in your comment is a bit unclear but I th
|
| + NOTREACHED() << "Learned " << referring_url << " => " << target_url |
|
mmenke
2016/04/22 16:29:25
ADD_FAILURE()
Charlie Harrison
2016/04/28 16:29:51
Done.
|
| + << " When should only be learning the source host: " |
| + << source_host_ |
| + << " or the cross site host: " << cross_site_host_; |
| + } |
| + } |
| + |
| + void ResetCounts() { |
| + base::AutoLock lock(lock_); |
| + cross_site_learned_ = 0; |
| + same_site_learned_ = 0; |
| + cross_site_preconnected_ = 0; |
| + same_site_preconnected_ = 0; |
| + } |
| + |
| + int cross_site_learned() const { |
| + base::AutoLock lock(lock_); |
| + return cross_site_learned_; |
| + } |
| + |
| + int same_site_learned() const { |
| + base::AutoLock lock(lock_); |
| + return same_site_learned_; |
| + } |
| + |
| + int cross_site_preconnected() const { |
| + base::AutoLock lock(lock_); |
| + return cross_site_preconnected_; |
| + } |
| + |
| + int same_site_preconnected() const { |
| + base::AutoLock lock(lock_); |
| + return same_site_preconnected_; |
| + } |
| + |
| + private: |
| + const GURL source_host_; |
| + const GURL cross_site_host_; |
| + |
| + int cross_site_learned_; |
| + int same_site_learned_; |
| + |
| + int cross_site_preconnected_; |
| + int same_site_preconnected_; |
| + mutable base::Lock lock_; |
|
mmenke
2016/04/22 16:29:25
DISALLOW_COPY_AND_ASSIGN
Charlie Harrison
2016/04/28 16:29:51
Done.
|
| +}; |
| + |
| } // namespace |
| namespace chrome_browser_net { |
| @@ -212,8 +333,8 @@ class PredictorBrowserTest : public InProcessBrowserTest { |
| : startup_url_("http://host1:1"), |
| referring_url_("http://host2:1"), |
| target_url_("http://host3:1"), |
| - host_resolution_request_recorder_(new HostResolutionRequestRecorder) { |
| - } |
| + host_resolution_request_recorder_(new HostResolutionRequestRecorder), |
| + cross_site_test_server_(new net::EmbeddedTestServer()) {} |
| protected: |
| void SetUpInProcessBrowserTestFixture() override { |
| @@ -229,10 +350,43 @@ class PredictorBrowserTest : public InProcessBrowserTest { |
| switches::kEnableBlinkFeatures, kBlinkPreconnectFeature); |
| } |
| + std::unique_ptr<net::test_server::HttpResponse> HandleRequest( |
|
mmenke
2016/04/22 16:29:25
This method name should be clearer, and may need a
Charlie Harrison
2016/04/28 16:29:52
Updated the name to "RedirectForPathPathHandler".
|
| + const net::test_server::HttpRequest& request) { |
| + if (request.GetURL().path() != "/") |
|
mmenke
2016/04/22 16:29:25
Suggest taking this as an argument, then can have
Charlie Harrison
2016/04/28 16:29:52
Done.
|
| + return nullptr; |
| + std::unique_ptr<net::test_server::BasicHttpResponse> response( |
| + new net::test_server::BasicHttpResponse); |
| + response->set_code(net::HTTP_MOVED_PERMANENTLY); |
| + response->AddCustomHeader( |
| + "Location", cross_site_test_server()->GetURL("/title1.html").spec()); |
|
mmenke
2016/04/22 16:29:25
Calling into the cross_site_test_server on the oth
Charlie Harrison
2016/04/28 16:29:52
Done.
|
| + return std::move(response); |
| + } |
| + |
| void SetUpOnMainThread() override { |
| + embedded_test_server()->RegisterRequestHandler(base::Bind( |
| + &PredictorBrowserTest::HandleRequest, base::Unretained(this))); |
| + cross_site_test_server_->ServeFilesFromSourceDirectory("chrome/test/data/"); |
| + |
| connection_listener_.reset(new ConnectionListener()); |
| + cross_site_connection_listener_.reset(new ConnectionListener()); |
| embedded_test_server()->SetConnectionListener(connection_listener_.get()); |
| + cross_site_test_server_->SetConnectionListener( |
| + cross_site_connection_listener_.get()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| + ASSERT_TRUE(cross_site_test_server_->Start()); |
| + |
| + predictor()->set_preconnect_enabled_for_test(true); |
| + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| + base::Bind(&PredictorBrowserTest::SetUpOnIOThread, |
| + base::Unretained(this))); |
| + } |
| + |
| + void SetUpOnIOThread() { |
| + // Ignore all favicon requests. These are tricky to deal with because they |
| + // are scheduled on the UI thread and are not accounted for in the load |
| + // event. The interceptor will respond with 404s, and won't connect any |
| + // sockets to the test server. |
| + AddUrlInterceptor(cross_site_test_server()->GetURL("/favicon.ico")); |
| } |
| void TearDownOnMainThread() override { |
| @@ -254,29 +408,22 @@ class PredictorBrowserTest : public InProcessBrowserTest { |
| } |
| void LearnAboutInitialNavigation(const GURL& url) { |
| - Predictor* predictor = browser()->profile()->GetNetworkPredictor(); |
| - BrowserThread::PostTask(BrowserThread::IO, |
| - FROM_HERE, |
| + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| base::Bind(&Predictor::LearnAboutInitialNavigation, |
| - base::Unretained(predictor), |
| - url)); |
| + base::Unretained(predictor()), url)); |
| content::RunAllPendingInMessageLoop(BrowserThread::IO); |
| } |
| void LearnFromNavigation(const GURL& referring_url, const GURL& target_url) { |
| - Predictor* predictor = browser()->profile()->GetNetworkPredictor(); |
| - BrowserThread::PostTask(BrowserThread::IO, |
| - FROM_HERE, |
| - base::Bind(&Predictor::LearnFromNavigation, |
| - base::Unretained(predictor), |
| - referring_url, |
| - target_url)); |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, FROM_HERE, |
| + base::Bind(&Predictor::LearnFromNavigation, |
| + base::Unretained(predictor()), referring_url, target_url)); |
| content::RunAllPendingInMessageLoop(BrowserThread::IO); |
| } |
| void PrepareFrameSubresources(const GURL& url) { |
| - Predictor* predictor = browser()->profile()->GetNetworkPredictor(); |
| - predictor->PredictFrameSubresources(url, GURL()); |
| + predictor()->PredictFrameSubresources(url, GURL()); |
| } |
| void GetListFromPrefsAsString(const char* list_path, |
| @@ -299,26 +446,431 @@ class PredictorBrowserTest : public InProcessBrowserTest { |
| return host_resolution_request_recorder_->RequestedHostnameCount(); |
| } |
| + net::EmbeddedTestServer* cross_site_test_server() { |
| + return cross_site_test_server_.get(); |
| + } |
| + |
| + Predictor* predictor() { return browser()->profile()->GetNetworkPredictor(); } |
| + |
| + void InstallPredictorObserver() { |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, FROM_HERE, |
| + base::Bind(&PredictorBrowserTest::InstallPredictorObserverOnIOThread, |
| + base::Unretained(this), base::Unretained(predictor()))); |
| + } |
| + |
| + void InstallPredictorObserverOnIOThread( |
| + chrome_browser_net::Predictor* predictor) { |
| + observer_.reset( |
| + new CrossSitePredictorObserver(embedded_test_server()->base_url(), |
| + cross_site_test_server()->base_url())); |
|
mmenke
2016/04/22 16:29:25
Suggest just creating the observer_ in InstallPred
Charlie Harrison
2016/04/28 16:29:51
Done.
|
| + predictor->SetObserver(observer_.get()); |
| + } |
| + |
| + CrossSitePredictorObserver* observer() { return observer_.get(); } |
| + |
| + // Navigate to an html file and request async fetches. Wait until the fetches |
|
mmenke
2016/04/22 16:29:25
Suggest "Navigate to an html file and request asyn
Charlie Harrison
2016/04/28 16:29:51
Done.
|
| + // complete before continuing. Note that "cors" here means using cors-mode in |
| + // correspondence with the fetch spec. This translates to using the privacy |
| + // mode socket pools. |
| + void NavigateToCrossSiteHTMLURL(int num_cors, const char* file_suffix) { |
| + const GURL& base_url = cross_site_test_server()->base_url(); |
| + std::string path = base::StringPrintf( |
| + "/predictor/" |
| + "predictor_cross_site%s.html?subresourceHost=%s&" |
| + "numCORSResources=%d", |
| + file_suffix, base_url.spec().c_str(), num_cors); |
| + ui_test_utils::NavigateToURL(browser(), |
| + embedded_test_server()->GetURL(path)); |
| + bool result = false; |
| + EXPECT_TRUE(content::ExecuteScriptAndExtractBool( |
| + browser()->tab_strip_model()->GetActiveWebContents(), "sendFetches()", |
|
mmenke
2016/04/22 16:29:25
Maybe sendFetches -> startFetchesAndWaitForReply?
Charlie Harrison
2016/04/28 16:29:51
SGTM. Done.
|
| + &result)); |
| + EXPECT_TRUE(result); |
| + } |
| + |
| + // Simulate the target server restarting. Delete all sockets and reset members |
| + // tracking preconnects. |
| + void ResetTarget() { |
| + cross_site_test_server()->FlushAllSocketsAndConnectionsOnUIThread(); |
| + cross_site_connection_listener_->ClearSockets(); |
| + EXPECT_EQ(0u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + } |
| + |
| + static void AddUrlInterceptor(const GURL url) { |
|
mmenke
2016/04/22 16:29:25
GURL&
Charlie Harrison
2016/04/28 16:29:51
Done.
|
| + net::URLRequestFilter::GetInstance()->AddUrlInterceptor( |
| + url, make_scoped_ptr(new NotFoundRequestInterceptor())); |
| + } |
| + |
| const GURL startup_url_; |
| const GURL referring_url_; |
| const GURL target_url_; |
| std::unique_ptr<ConnectionListener> connection_listener_; |
| + std::unique_ptr<ConnectionListener> cross_site_connection_listener_; |
| private: |
| scoped_refptr<HostResolutionRequestRecorder> |
| host_resolution_request_recorder_; |
| std::unique_ptr<net::ScopedDefaultHostResolverProc> |
| scoped_host_resolver_proc_; |
| + std::unique_ptr<net::EmbeddedTestServer> cross_site_test_server_; |
| + std::unique_ptr<CrossSitePredictorObserver> observer_; |
| }; |
| -IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PRE_ShutdownStartupCycle) { |
| +// Test the html test harness used to initiate cross site fetches. These |
| +// initiate cross site subresource requests to the cross site test server. |
| +// Inspect the predictor's internal state to make sure that they are properly |
| +// logged. |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, CrossSiteUseOneSocket) { |
| + InstallPredictorObserver(); |
| + NavigateToCrossSiteHTMLURL(1 /* num_cors */, "" /* file_suffix */); |
| + EXPECT_EQ(1u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(1, observer()->cross_site_learned()); |
| + EXPECT_EQ(2, observer()->same_site_preconnected()); |
| +} |
| + |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, CrossSiteUseThreeSockets) { |
| + InstallPredictorObserver(); |
| + NavigateToCrossSiteHTMLURL(3 /* num_cors */, "" /* file_suffix */); |
| + EXPECT_EQ(3u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(3, observer()->cross_site_learned()); |
| + EXPECT_EQ(2, observer()->same_site_preconnected()); |
| +} |
| + |
| +// The following tests confirm that Chrome accurately predicts preconnects after |
| +// learning from navigations. Note that every "learned" connection adds ~.33 to |
| +// the expected connection number, which starts at 2. Every preconnect Chrome |
| +// performs multiplies the expected connections by .66. One additional |
| +// complexity |
| +// is that if a preconnect from A to B is learned, an extra preconnect will be |
| +// issued if the host part of A and B match (ignoring port). |
| +// TODO(csharrison): This logic could probably be removed. |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, |
| + CrossSiteSimplePredictionAfterOneNavigation) { |
| + InstallPredictorObserver(); |
| + NavigateToCrossSiteHTMLURL(2 /* num_cors */, "" /* file_suffix */); |
| + EXPECT_EQ(2u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(2, observer()->cross_site_learned()); |
| + EXPECT_EQ(2, observer()->same_site_preconnected()); |
| + |
| + ResetTarget(); |
| + observer()->ResetCounts(); |
| + |
| + // Navigate again and confirm a preconnect. Note that because the two |
| + // embedded test servers have the same host_piece, an extra preconnect is |
| + // issued. This results in ceil(2.66) + 1 = 4 preconnects. |
| + ui_test_utils::NavigateToURL(browser(), |
| + embedded_test_server()->GetURL("/title1.html")); |
| + EXPECT_EQ(4u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(4, observer()->cross_site_preconnected()); |
| +} |
| + |
| +// Expect that the predictor correctly predicts subframe navigations. |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, SubframeCrossSitePrediction) { |
| + InstallPredictorObserver(); |
| + ui_test_utils::NavigateToURL( |
| + browser(), embedded_test_server()->GetURL( |
| + "/predictor/predictor_cross_site_subframe_nav.html")); |
| + bool result = false; |
| + EXPECT_TRUE(content::ExecuteScriptAndExtractBool( |
| + browser()->tab_strip_model()->GetActiveWebContents(), |
| + base::StringPrintf( |
| + "navigateSubframe('%s')", |
| + cross_site_test_server()->GetURL("/title1.html").spec().c_str()), |
| + &result)); |
| + EXPECT_TRUE(result); |
| + EXPECT_EQ(2u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(1, observer()->cross_site_learned()); |
| + EXPECT_EQ(2, observer()->same_site_preconnected()); |
| + |
| + ResetTarget(); |
| + observer()->ResetCounts(); |
| + |
| + // Navigate again and confirm a preconnect. Note that because the two |
| + // embedded test servers have the same host_piece, an extra preconnect is |
| + // issued. This results in ceil(2 + .33) + 1 = 4 preconnects. |
| + ui_test_utils::NavigateToURL(browser(), |
| + embedded_test_server()->GetURL("/title1.html")); |
| + EXPECT_EQ(4u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(4, observer()->cross_site_preconnected()); |
| +} |
| + |
| +// Expect that the predictor correctly preconnects an already learned resource |
| +// if the host shows up in a subframe. |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, SubframeInitiatesPreconnects) { |
| + InstallPredictorObserver(); |
| + // Navigate to the normal cross site URL to learn the relationship. |
| + NavigateToCrossSiteHTMLURL(1 /* num_cors */, "" /* file_suffix */); |
| + EXPECT_EQ(1u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(1, observer()->cross_site_learned()); |
| + EXPECT_EQ(2, observer()->same_site_preconnected()); |
| + |
| + ResetTarget(); |
| + observer()->ResetCounts(); |
| + |
| + // Navigate again and confirm a preconnect. Note that because the two |
| + // embedded test servers have the same host_piece, an extra preconnect is |
| + // issued. This results in ceil(2 + .33) + 1 = 4 preconnects. |
| + NavigateToDataURLWithContent( |
| + "<iframe src=\"" + embedded_test_server()->GetURL("/title1.html").spec() + |
| + "\"></iframe>"); |
| + EXPECT_EQ(4u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(4, observer()->cross_site_preconnected()); |
| +} |
| + |
| +// Expect that the predictor correctly learns the subresources a subframe needs |
| +// to preconnect to. |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, SubframeLearning) { |
| + InstallPredictorObserver(); |
| + std::string path = base::StringPrintf( |
| + "/predictor/" |
| + "predictor_cross_site.html?subresourceHost=%s&" |
| + "numCORSResources=1&sendImmediately=1", |
| + cross_site_test_server()->base_url().spec().c_str()); |
| + NavigateToDataURLWithContent( |
| + base::StringPrintf("<iframe src=\"%s\"></iframe>", |
| + embedded_test_server()->GetURL(path).spec().c_str())); |
| + cross_site_connection_listener_->WaitUntilFirstConnectionRead(); |
| + EXPECT_EQ(1u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(1, observer()->cross_site_learned()); |
| + EXPECT_EQ(2, observer()->same_site_preconnected()); |
| + |
| + ResetTarget(); |
| + observer()->ResetCounts(); |
| + |
| + // Navigate again and confirm a preconnect. Note that because the two |
| + // embedded test servers have the same host_piece, an extra preconnect is |
| + // issued. This results in ceil(2 + .33) + 1 = 4 preconnects. |
| + NavigateToDataURLWithContent( |
| + "<iframe src=\"" + embedded_test_server()->GetURL("/title1.html").spec() + |
| + "\"></iframe>"); |
| + EXPECT_EQ(4u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(4, observer()->cross_site_preconnected()); |
| +} |
| + |
| +// This tests the implementation details of the predictor. The current predictor |
| +// only learns via the referrer header. |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, CrossSiteNoReferrerNoLearning) { |
| + InstallPredictorObserver(); |
| + NavigateToCrossSiteHTMLURL(1 /* num_cors */, |
| + "_no_referrer" /* file_suffix */); |
| + EXPECT_EQ(1u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(0, observer()->cross_site_learned()); |
| + EXPECT_EQ(2, observer()->same_site_preconnected()); |
| +} |
| + |
| +// This test navigates to an html file with a tag: |
| +// <meta name="referrer" content="never"> |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, |
| + CrossSiteNoReferrerNoPredictionAfterOneNavigation) { |
| + InstallPredictorObserver(); |
| + NavigateToCrossSiteHTMLURL(2 /* num_cors */, |
| + "_no_referrer" /* file_suffix */); |
| + EXPECT_EQ(2u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(0, observer()->cross_site_learned()); |
| + EXPECT_EQ(2, observer()->same_site_preconnected()); |
| + |
| + ResetTarget(); |
| + observer()->ResetCounts(); |
| + |
| + // Navigate again and confirm that no preconnects occurred. |
| + ui_test_utils::NavigateToURL(browser(), |
| + embedded_test_server()->GetURL("/title1.html")); |
| + EXPECT_EQ(0u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(0, observer()->cross_site_preconnected()); |
| +} |
| + |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, |
| + CrossSiteSimplePredictionAfterTwoNavigations) { |
| + InstallPredictorObserver(); |
| + NavigateToCrossSiteHTMLURL(1 /* num_cors */, "" /* file_suffix */); |
| + NavigateToCrossSiteHTMLURL(1 /* num_cors */, "" /* file_suffix */); |
| + |
| + ResetTarget(); |
| + observer()->ResetCounts(); |
| + |
| + // Navigate again and confirm a preconnect. Note that because the two |
| + // embedded test servers have the same host_piece, an extra preconnect is |
| + // issued. This results in ceil((2 + .33)*.66 + .33) + 1 ~= 3 preconnects. |
| + ui_test_utils::NavigateToURL(browser(), |
| + embedded_test_server()->GetURL("/title1.html")); |
| + EXPECT_EQ(3u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(3, observer()->cross_site_preconnected()); |
| +} |
| + |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, |
| + CrossSiteSimplePredictionAfterTwoNavigations2) { |
| + InstallPredictorObserver(); |
| + NavigateToCrossSiteHTMLURL(2 /* num_cors */, "" /* file_suffix */); |
| + NavigateToCrossSiteHTMLURL(2 /* num_cors */, "" /* file_suffix */); |
| + |
| + ResetTarget(); |
| + observer()->ResetCounts(); |
| + |
| + // (((2 + .66)*.66) + .66) + 1 ~= 3.4 |
| + ui_test_utils::NavigateToURL(browser(), |
| + embedded_test_server()->GetURL("/title1.html")); |
| + EXPECT_EQ(4u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(4, observer()->cross_site_preconnected()); |
| +} |
| + |
| +// The first navigation uses a subresource. Subsequent navigations don't use |
| +// that subresource. This tests how the predictor forgets about these bad |
| +// navigations. |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, ForgetBadPrediction) { |
| + InstallPredictorObserver(); |
| + NavigateToCrossSiteHTMLURL(1 /* num_cors */, "" /* file_suffix */); |
| + |
| + ResetTarget(); |
| + observer()->ResetCounts(); |
| + |
| + ui_test_utils::NavigateToURL(browser(), |
| + embedded_test_server()->GetURL("/title1.html")); |
| + // (2 + .33) + 1 = 3.33. |
| + EXPECT_EQ(4u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(4, observer()->cross_site_preconnected()); |
| + |
| + ResetTarget(); |
| + observer()->ResetCounts(); |
| + |
| + ui_test_utils::NavigateToURL(browser(), |
| + embedded_test_server()->GetURL("/title1.html")); |
| + |
| + // ceil((2 + .33) * .66) + 1 = 3. |
| + EXPECT_EQ(3u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(3, observer()->cross_site_preconnected()); |
| + |
| + ResetTarget(); |
| + observer()->ResetCounts(); |
| + |
| + ui_test_utils::NavigateToURL(browser(), |
| + embedded_test_server()->GetURL("/title1.html")); |
| + // ceil((2 + .33) * .66 * .66) + 1 = 3. |
| + EXPECT_EQ(3u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(3, observer()->cross_site_preconnected()); |
| + |
| + ResetTarget(); |
| + observer()->ResetCounts(); |
| + |
| + ui_test_utils::NavigateToURL(browser(), |
| + embedded_test_server()->GetURL("/title1.html")); |
| + // Finally, (2 + .33) * .66^3 ~= .67. Not enough for a preconnect. |
| + EXPECT_EQ(0u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(0, observer()->cross_site_preconnected()); |
| +} |
| + |
| +// The predictor does not follow redirects if the original url had a non-empty |
| +// path (a path that was more than just "/"). |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, |
| + CrossSiteRedirectNoPredictionWithPath) { |
| + InstallPredictorObserver(); |
| + ui_test_utils::NavigateToURL( |
| + browser(), |
| + embedded_test_server()->GetURL(base::StringPrintf( |
| + "/server-redirect?%s", |
| + cross_site_test_server()->GetURL("/title1.html").spec().c_str()))); |
| + EXPECT_EQ(2u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(0, observer()->cross_site_learned()); |
| + EXPECT_EQ(2, observer()->same_site_preconnected()); |
| + |
| + ResetTarget(); |
| + observer()->ResetCounts(); |
| + |
| + ui_test_utils::NavigateToURL(browser(), |
| + embedded_test_server()->GetURL("/title1.html")); |
| + EXPECT_EQ(0u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(0, observer()->cross_site_preconnected()); |
| +} |
| + |
| +// The predictor does follow redirects if the original url had an empty path |
| +// (a path that was more than just "/"). Use the registered "/" path to redirect |
| +// to the target test server. |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, |
| + CrossSiteRedirectPredictionWithNoPath) { |
| + InstallPredictorObserver(); |
| + ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL("/")); |
| + EXPECT_EQ(2u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(1, observer()->cross_site_learned()); |
| + EXPECT_EQ(2, observer()->same_site_preconnected()); |
| + |
| + ResetTarget(); |
| + observer()->ResetCounts(); |
| + |
| + ui_test_utils::NavigateToURL(browser(), |
| + embedded_test_server()->GetURL("/title1.html")); |
| + // Preconnect 4 sockets because ceil(2 + .33) + 1 = 4. |
| + EXPECT_EQ(4u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(4, observer()->cross_site_preconnected()); |
| +} |
| + |
| +// This test uses "localhost" instead of "127.0.0.1" to avoid extra preconnects |
| +// to hosts with the same host piece (ignoring port). Note that the preconnect |
| +// observer is not used here due to its strict checks on hostname. |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, |
| + CrossSiteRedirectPredictionWithNoPathDifferentHostName) { |
| + GURL localhost_source = GURL(base::StringPrintf( |
| + "http://localhost:%s/predictor/" |
| + "predictor_cross_site.html?subresourceHost=%s&numCORSResources=1", |
| + embedded_test_server()->base_url().port().c_str(), |
| + cross_site_test_server()->base_url().spec().c_str())); |
| + ui_test_utils::NavigateToURL(browser(), localhost_source); |
| + bool result = false; |
| + EXPECT_TRUE(content::ExecuteScriptAndExtractBool( |
| + browser()->tab_strip_model()->GetActiveWebContents(), "sendFetches()", |
| + &result)); |
| + EXPECT_TRUE(result); |
| + |
| + EXPECT_EQ(1u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + |
| + ResetTarget(); |
| + |
| + ui_test_utils::NavigateToURL( |
| + browser(), GURL(base::StringPrintf( |
| + "http://localhost:%s/title1.html", |
| + embedded_test_server()->base_url().port().c_str()))); |
| + // Preconnect 3 sockets because ceil(2 + .33) = 3. Note that this time there |
| + // is no additional connection due to same host. |
| + EXPECT_EQ(3u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| +} |
| + |
| +// Perform the "/" redirect twice and make sure the predictor updates twice. |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, |
| + CrossSiteTwoRedirectsPredictionWithNoPath) { |
| + InstallPredictorObserver(); |
| + ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL("/")); |
| + EXPECT_EQ(1, observer()->cross_site_learned()); |
| + EXPECT_EQ(2, observer()->same_site_preconnected()); |
| + |
| + ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL("/")); |
| + EXPECT_EQ(2, observer()->cross_site_learned()); |
| + EXPECT_EQ(2, observer()->same_site_preconnected()); |
| + |
| + ResetTarget(); |
| + observer()->ResetCounts(); |
| + |
| + ui_test_utils::NavigateToURL(browser(), |
| + embedded_test_server()->GetURL("/title1.html")); |
| + // 3 preconnects expected because (2 + .33) * .66 + .33 + 1 ~= 2.87. |
| + EXPECT_EQ(3u, cross_site_connection_listener_->GetAcceptedSocketCount()); |
| + EXPECT_EQ(3, observer()->cross_site_preconnected()); |
| +} |
| + |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, |
| + PRE_ShutdownStartupCyclePreresolve) { |
| // Prepare state that will be serialized on this shut-down and read on next |
| - // start-up. |
| + // start-up. Ensure preresolution over preconnection. |
| LearnAboutInitialNavigation(startup_url_); |
| + // The target url will have a expected connection count of 2 after this call. |
| LearnFromNavigation(referring_url_, target_url_); |
| + |
| + // In order to reduce the expected connection count < .8, issue predictions 3 |
| + // times. 2 * .66^3 ~= .58. |
| + PrepareFrameSubresources(referring_url_); |
| + PrepareFrameSubresources(referring_url_); |
| + PrepareFrameSubresources(referring_url_); |
| } |
| -IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, ShutdownStartupCycle) { |
| +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, ShutdownStartupCyclePreresolve) { |
| // Make sure that the Preferences file is actually wiped of all DNS prefetch |
| // related data after start-up. |
| std::string cleared_startup_list; |