Popular sites on the NTP: re-download popular suggestions once per Chrome run
[chromium-blink-merge.git] / chrome / browser / net / predictor_browsertest.cc
blobffd9f0b4de1fa3b9b5e045ac75d38aae85256ff5
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include <stdint.h>
7 #include "base/base64.h"
8 #include "base/command_line.h"
9 #include "base/json/json_string_value_serializer.h"
10 #include "base/macros.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/synchronization/lock.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/net/predictor.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/common/pref_names.h"
19 #include "chrome/test/base/in_process_browser_test.h"
20 #include "chrome/test/base/ui_test_utils.h"
21 #include "content/public/common/content_switches.h"
22 #include "content/public/test/test_utils.h"
23 #include "net/base/host_port_pair.h"
24 #include "net/base/ip_endpoint.h"
25 #include "net/base/net_errors.h"
26 #include "net/dns/host_resolver_proc.h"
27 #include "net/dns/mock_host_resolver.h"
28 #include "net/test/embedded_test_server/embedded_test_server.h"
29 #include "net/test/embedded_test_server/embedded_test_server_connection_listener.h"
30 #include "testing/gmock/include/gmock/gmock.h"
32 using content::BrowserThread;
33 using testing::HasSubstr;
35 namespace {
37 const char kBlinkPreconnectFeature[] = "LinkPreconnect";
38 const char kChromiumHostname[] = "chromium.org";
39 const char kInvalidLongHostname[] = "illegally-long-hostname-over-255-"
40 "characters-should-not-send-an-ipc-message-to-the-browser-"
41 "0000000000000000000000000000000000000000000000000000000000000000000000000"
42 "0000000000000000000000000000000000000000000000000000000000000000000000000"
43 "000000000000000000000000000000000000000000000000000000.org";
45 // Gets notified by the EmbeddedTestServer on incoming connections being
46 // accepted or read from, keeps track of them and exposes that info to
47 // the tests.
48 // A port being reused is currently considered an error. If a test
49 // needs to verify multiple connections are opened in sequence, that will need
50 // to be changed.
51 class ConnectionListener
52 : public net::test_server::EmbeddedTestServerConnectionListener {
53 public:
54 ConnectionListener() : task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
56 ~ConnectionListener() override {}
58 // Get called from the EmbeddedTestServer thread to be notified that
59 // a connection was accepted.
60 void AcceptedSocket(
61 const net::test_server::StreamListenSocket& connection) override {
62 base::AutoLock lock(lock_);
63 uint16_t socket = GetPort(connection);
64 EXPECT_TRUE(sockets_.find(socket) == sockets_.end());
66 sockets_[socket] = SOCKET_ACCEPTED;
67 task_runner_->PostTask(FROM_HERE, accept_loop_.QuitClosure());
70 // Get called from the EmbeddedTestServer thread to be notified that
71 // a connection was read from.
72 void ReadFromSocket(
73 const net::test_server::StreamListenSocket& connection) override {
74 base::AutoLock lock(lock_);
75 uint16_t socket = GetPort(connection);
76 EXPECT_FALSE(sockets_.find(socket) == sockets_.end());
78 sockets_[socket] = SOCKET_READ_FROM;
79 task_runner_->PostTask(FROM_HERE, read_loop_.QuitClosure());
82 // Returns the number of sockets that were accepted by the server.
83 size_t GetAcceptedSocketCount() const {
84 base::AutoLock lock(lock_);
85 return sockets_.size();
88 // Returns the number of sockets that were read from by the server.
89 size_t GetReadSocketCount() const {
90 base::AutoLock lock(lock_);
91 size_t read_sockets = 0;
92 for (const auto& socket : sockets_) {
93 if (socket.second == SOCKET_READ_FROM)
94 ++read_sockets;
96 return read_sockets;
99 void WaitUntilFirstConnectionAccepted() { accept_loop_.Run(); }
101 void WaitUntilFirstConnectionRead() { read_loop_.Run(); }
103 private:
104 static uint16_t GetPort(
105 const net::test_server::StreamListenSocket& connection) {
106 // Get the remote port of the peer, since the local port will always be the
107 // port the test server is listening on. This isn't strictly correct - it's
108 // possible for multiple peers to connect with the same remote port but
109 // different remote IPs - but the tests here assume that connections to the
110 // test server (running on localhost) will always come from localhost, and
111 // thus the peer port is all thats needed to distinguish two connections.
112 // This also would be problematic if the OS reused ports, but that's not
113 // something to worry about for these tests.
114 net::IPEndPoint address;
115 EXPECT_EQ(net::OK, connection.GetPeerAddress(&address));
116 return address.port();
119 enum SocketStatus { SOCKET_ACCEPTED, SOCKET_READ_FROM };
121 typedef base::hash_map<uint16_t, SocketStatus> SocketContainer;
122 SocketContainer sockets_;
124 base::RunLoop accept_loop_;
125 base::RunLoop read_loop_;
126 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
128 mutable base::Lock lock_;
130 DISALLOW_COPY_AND_ASSIGN(ConnectionListener);
133 // Records a history of all hostnames for which resolving has been requested,
134 // and immediately fails the resolution requests themselves.
135 class HostResolutionRequestRecorder : public net::HostResolverProc {
136 public:
137 HostResolutionRequestRecorder()
138 : HostResolverProc(NULL),
139 is_waiting_for_hostname_(false) {
142 int Resolve(const std::string& host,
143 net::AddressFamily address_family,
144 net::HostResolverFlags host_resolver_flags,
145 net::AddressList* addrlist,
146 int* os_error) override {
147 BrowserThread::PostTask(
148 BrowserThread::UI,
149 FROM_HERE,
150 base::Bind(&HostResolutionRequestRecorder::AddToHistory,
151 base::Unretained(this),
152 host));
153 return net::ERR_NAME_NOT_RESOLVED;
156 int RequestedHostnameCount() const {
157 return requested_hostnames_.size();
160 bool HasHostBeenRequested(const std::string& hostname) const {
161 DCHECK_CURRENTLY_ON(BrowserThread::UI);
162 return std::find(requested_hostnames_.begin(),
163 requested_hostnames_.end(),
164 hostname) != requested_hostnames_.end();
167 void WaitUntilHostHasBeenRequested(const std::string& hostname) {
168 DCHECK_CURRENTLY_ON(BrowserThread::UI);
169 DCHECK(!is_waiting_for_hostname_);
170 if (HasHostBeenRequested(hostname))
171 return;
172 waiting_for_hostname_ = hostname;
173 is_waiting_for_hostname_ = true;
174 content::RunMessageLoop();
177 private:
178 ~HostResolutionRequestRecorder() override {}
180 void AddToHistory(const std::string& hostname) {
181 DCHECK_CURRENTLY_ON(BrowserThread::UI);
182 requested_hostnames_.push_back(hostname);
183 if (is_waiting_for_hostname_ && waiting_for_hostname_ == hostname) {
184 is_waiting_for_hostname_ = false;
185 waiting_for_hostname_.clear();
186 base::MessageLoop::current()->Quit();
190 // The hostname which WaitUntilHostHasBeenRequested is currently waiting for
191 // to be requested.
192 std::string waiting_for_hostname_;
194 // Whether WaitUntilHostHasBeenRequested is waiting for a hostname to be
195 // requested and thus is running a nested message loop.
196 bool is_waiting_for_hostname_;
198 // A list of hostnames for which resolution has already been requested. Only
199 // to be accessed from the UI thread.
200 std::vector<std::string> requested_hostnames_;
202 DISALLOW_COPY_AND_ASSIGN(HostResolutionRequestRecorder);
205 } // namespace
207 namespace chrome_browser_net {
209 class PredictorBrowserTest : public InProcessBrowserTest {
210 public:
211 PredictorBrowserTest()
212 : startup_url_("http://host1:1"),
213 referring_url_("http://host2:1"),
214 target_url_("http://host3:1"),
215 host_resolution_request_recorder_(new HostResolutionRequestRecorder) {
218 protected:
219 void SetUpInProcessBrowserTestFixture() override {
220 scoped_host_resolver_proc_.reset(new net::ScopedDefaultHostResolverProc(
221 host_resolution_request_recorder_.get()));
222 InProcessBrowserTest::SetUpInProcessBrowserTestFixture();
225 void SetUpCommandLine(base::CommandLine* command_line) override {
226 command_line->AppendSwitch(
227 switches::kEnableExperimentalWebPlatformFeatures);
228 command_line->AppendSwitchASCII(
229 switches::kEnableBlinkFeatures, kBlinkPreconnectFeature);
232 void SetUpOnMainThread() override {
233 connection_listener_.reset(new ConnectionListener());
234 embedded_test_server()->SetConnectionListener(connection_listener_.get());
235 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
238 void TearDownOnMainThread() override {
239 ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
242 // Navigates to a data URL containing the given content, with a MIME type of
243 // text/html.
244 void NavigateToDataURLWithContent(const std::string& content) {
245 std::string encoded_content;
246 base::Base64Encode(content, &encoded_content);
247 std::string data_uri_content = "data:text/html;base64," + encoded_content;
248 ui_test_utils::NavigateToURL(browser(), GURL(data_uri_content));
251 void TearDownInProcessBrowserTestFixture() override {
252 InProcessBrowserTest::TearDownInProcessBrowserTestFixture();
253 scoped_host_resolver_proc_.reset();
256 void LearnAboutInitialNavigation(const GURL& url) {
257 Predictor* predictor = browser()->profile()->GetNetworkPredictor();
258 BrowserThread::PostTask(BrowserThread::IO,
259 FROM_HERE,
260 base::Bind(&Predictor::LearnAboutInitialNavigation,
261 base::Unretained(predictor),
262 url));
263 content::RunAllPendingInMessageLoop(BrowserThread::IO);
266 void LearnFromNavigation(const GURL& referring_url, const GURL& target_url) {
267 Predictor* predictor = browser()->profile()->GetNetworkPredictor();
268 BrowserThread::PostTask(BrowserThread::IO,
269 FROM_HERE,
270 base::Bind(&Predictor::LearnFromNavigation,
271 base::Unretained(predictor),
272 referring_url,
273 target_url));
274 content::RunAllPendingInMessageLoop(BrowserThread::IO);
277 void PrepareFrameSubresources(const GURL& url) {
278 Predictor* predictor = browser()->profile()->GetNetworkPredictor();
279 predictor->PredictFrameSubresources(url, GURL());
282 void GetListFromPrefsAsString(const char* list_path,
283 std::string* value_as_string) const {
284 PrefService* prefs = browser()->profile()->GetPrefs();
285 const base::ListValue* list_value = prefs->GetList(list_path);
286 JSONStringValueSerializer serializer(value_as_string);
287 serializer.Serialize(*list_value);
290 bool HasHostBeenRequested(const std::string& hostname) const {
291 return host_resolution_request_recorder_->HasHostBeenRequested(hostname);
294 void WaitUntilHostHasBeenRequested(const std::string& hostname) {
295 host_resolution_request_recorder_->WaitUntilHostHasBeenRequested(hostname);
298 int RequestedHostnameCount() const {
299 return host_resolution_request_recorder_->RequestedHostnameCount();
302 const GURL startup_url_;
303 const GURL referring_url_;
304 const GURL target_url_;
305 scoped_ptr<ConnectionListener> connection_listener_;
307 private:
308 scoped_refptr<HostResolutionRequestRecorder>
309 host_resolution_request_recorder_;
310 scoped_ptr<net::ScopedDefaultHostResolverProc> scoped_host_resolver_proc_;
313 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PRE_ShutdownStartupCycle) {
314 // Prepare state that will be serialized on this shut-down and read on next
315 // start-up.
316 LearnAboutInitialNavigation(startup_url_);
317 LearnFromNavigation(referring_url_, target_url_);
320 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, ShutdownStartupCycle) {
321 // Make sure that the Preferences file is actually wiped of all DNS prefetch
322 // related data after start-up.
323 std::string cleared_startup_list;
324 std::string cleared_referral_list;
325 GetListFromPrefsAsString(prefs::kDnsPrefetchingStartupList,
326 &cleared_startup_list);
327 GetListFromPrefsAsString(prefs::kDnsPrefetchingHostReferralList,
328 &cleared_referral_list);
330 EXPECT_THAT(cleared_startup_list, Not(HasSubstr(startup_url_.host())));
331 EXPECT_THAT(cleared_referral_list, Not(HasSubstr(referring_url_.host())));
332 EXPECT_THAT(cleared_referral_list, Not(HasSubstr(target_url_.host())));
334 // But also make sure this data has been first loaded into the Predictor, by
335 // inspecting that the Predictor starts making the expected hostname requests.
336 PrepareFrameSubresources(referring_url_);
337 WaitUntilHostHasBeenRequested(startup_url_.host());
338 WaitUntilHostHasBeenRequested(target_url_.host());
341 // Flaky on Windows: http://crbug.com/469120
342 #if defined(OS_WIN)
343 #define MAYBE_DnsPrefetch DISABLED_DnsPrefetch
344 #else
345 #define MAYBE_DnsPrefetch DnsPrefetch
346 #endif
347 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, MAYBE_DnsPrefetch) {
348 ASSERT_TRUE(test_server()->Start());
349 int hostnames_requested_before_load = RequestedHostnameCount();
350 ui_test_utils::NavigateToURL(
351 browser(),
352 GURL(test_server()->GetURL("files/predictor/dns_prefetch.html")));
353 WaitUntilHostHasBeenRequested(kChromiumHostname);
354 ASSERT_FALSE(HasHostBeenRequested(kInvalidLongHostname));
355 ASSERT_EQ(hostnames_requested_before_load + 1, RequestedHostnameCount());
358 // Tests that preconnect warms up a socket connection to a test server.
359 // Note: This test uses a data URI to serve the preconnect hint, to make sure
360 // that the network stack doesn't just re-use its connection to the test server.
361 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PreconnectNonCORS) {
362 GURL preconnect_url = embedded_test_server()->base_url();
363 std::string preconnect_content =
364 "<link rel=\"preconnect\" href=\"" + preconnect_url.spec() + "\">";
365 NavigateToDataURLWithContent(preconnect_content);
366 connection_listener_->WaitUntilFirstConnectionAccepted();
367 EXPECT_EQ(1u, connection_listener_->GetAcceptedSocketCount());
368 EXPECT_EQ(0u, connection_listener_->GetReadSocketCount());
371 // Tests that preconnect warms up a socket connection to a test server,
372 // and that that socket is later used when fetching a resource.
373 // Note: This test uses a data URI to serve the preconnect hint, to make sure
374 // that the network stack doesn't just re-use its connection to the test server.
375 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PreconnectAndFetchNonCORS) {
376 GURL preconnect_url = embedded_test_server()->base_url();
377 // First navigation to content with a preconnect hint.
378 std::string preconnect_content =
379 "<link rel=\"preconnect\" href=\"" + preconnect_url.spec() + "\">";
380 NavigateToDataURLWithContent(preconnect_content);
381 connection_listener_->WaitUntilFirstConnectionAccepted();
382 EXPECT_EQ(1u, connection_listener_->GetAcceptedSocketCount());
383 EXPECT_EQ(0u, connection_listener_->GetReadSocketCount());
385 // Second navigation to content with an img.
386 std::string img_content =
387 "<img src=\"" + preconnect_url.spec() + "test.gif\">";
388 NavigateToDataURLWithContent(img_content);
389 connection_listener_->WaitUntilFirstConnectionRead();
390 EXPECT_EQ(1u, connection_listener_->GetAcceptedSocketCount());
391 EXPECT_EQ(1u, connection_listener_->GetReadSocketCount());
394 // Tests that preconnect warms up a CORS connection to a test
395 // server, and that socket is later used when fetching a CORS resource.
396 // Note: This test uses a data URI to serve the preconnect hint, to make sure
397 // that the network stack doesn't just re-use its connection to the test server.
398 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PreconnectAndFetchCORS) {
399 GURL preconnect_url = embedded_test_server()->base_url();
400 // First navigation to content with a preconnect hint.
401 std::string preconnect_content = "<link rel=\"preconnect\" href=\"" +
402 preconnect_url.spec() + "\" crossorigin>";
403 NavigateToDataURLWithContent(preconnect_content);
404 connection_listener_->WaitUntilFirstConnectionAccepted();
405 EXPECT_EQ(1u, connection_listener_->GetAcceptedSocketCount());
406 EXPECT_EQ(0u, connection_listener_->GetReadSocketCount());
408 // Second navigation to content with a font.
409 std::string font_content = "<script>var font = new FontFace('FontA', 'url(" +
410 preconnect_url.spec() +
411 "test.woff2)');font.load();</script>";
412 NavigateToDataURLWithContent(font_content);
413 connection_listener_->WaitUntilFirstConnectionRead();
414 EXPECT_EQ(1u, connection_listener_->GetAcceptedSocketCount());
415 EXPECT_EQ(1u, connection_listener_->GetReadSocketCount());
418 // Tests that preconnect warms up a non-CORS connection to a test
419 // server, but that socket is not used when fetching a CORS resource.
420 // Note: This test uses a data URI to serve the preconnect hint, to make sure
421 // that the network stack doesn't just re-use its connection to the test server.
422 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PreconnectNonCORSAndFetchCORS) {
423 GURL preconnect_url = embedded_test_server()->base_url();
424 // First navigation to content with a preconnect hint.
425 std::string preconnect_content =
426 "<link rel=\"preconnect\" href=\"" + preconnect_url.spec() + "\">";
427 NavigateToDataURLWithContent(preconnect_content);
428 connection_listener_->WaitUntilFirstConnectionAccepted();
429 EXPECT_EQ(1u, connection_listener_->GetAcceptedSocketCount());
430 EXPECT_EQ(0u, connection_listener_->GetReadSocketCount());
432 // Second navigation to content with a font.
433 std::string font_content = "<script>var font = new FontFace('FontA', 'url(" +
434 preconnect_url.spec() +
435 "test.woff2)');font.load();</script>";
436 NavigateToDataURLWithContent(font_content);
437 connection_listener_->WaitUntilFirstConnectionRead();
438 EXPECT_EQ(2u, connection_listener_->GetAcceptedSocketCount());
439 EXPECT_EQ(1u, connection_listener_->GetReadSocketCount());
442 // Tests that preconnect warms up a CORS connection to a test server,
443 // but that socket is not used when fetching a non-CORS resource.
444 // Note: This test uses a data URI to serve the preconnect hint, to make sure
445 // that the network stack doesn't just re-use its connection to the test server.
446 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PreconnectCORSAndFetchNonCORS) {
447 GURL preconnect_url = embedded_test_server()->base_url();
448 // First navigation to content with a preconnect hint.
449 std::string preconnect_content = "<link rel=\"preconnect\" href=\"" +
450 preconnect_url.spec() + "\" crossorigin>";
451 NavigateToDataURLWithContent(preconnect_content);
452 connection_listener_->WaitUntilFirstConnectionAccepted();
453 EXPECT_EQ(1u, connection_listener_->GetAcceptedSocketCount());
454 EXPECT_EQ(0u, connection_listener_->GetReadSocketCount());
456 // Second navigation to content with an img.
457 std::string img_content =
458 "<img src=\"" + preconnect_url.spec() + "test.gif\">";
459 NavigateToDataURLWithContent(img_content);
460 connection_listener_->WaitUntilFirstConnectionRead();
461 EXPECT_EQ(2u, connection_listener_->GetAcceptedSocketCount());
462 EXPECT_EQ(1u, connection_listener_->GetReadSocketCount());
465 } // namespace chrome_browser_net