blob: a1f22de245d04d3b21cbed6f7f7b8ca06028f23a [file] [log] [blame]
// Copyright (c) 2013 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 <memory>
#include <string>
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/stringprintf.h"
#include "base/task/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/net/profile_network_context_service.h"
#include "chrome/browser/net/profile_network_context_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.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 "components/proxy_config/proxy_config_dictionary.h"
#include "components/proxy_config/proxy_config_pref_names.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_test_utils.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "url/gurl.h"
// This file tests that net::LoadTimingInfo is correctly hooked up to the
// NavigationTiming API. It depends on behavior in a large number of files
// spread across multiple projects, so is somewhat arbitrarily put in
// chrome/browser/net.
namespace {
// Structure used for retrieved from the renderer process.
// These are milliseconds after fetch start, or -1 if not
// present
struct TimingDeltas {
int dns_start;
int dns_end;
int connect_start;
int ssl_start;
int connect_end;
int send_start;
// Must be non-negative and greater than all other times.
int receive_headers_end;
};
class LoadTimingBrowserTest : public InProcessBrowserTest {
public:
LoadTimingBrowserTest() {
}
~LoadTimingBrowserTest() override {}
// Reads applicable times from performance.timing and writes them to
// |navigation_deltas|. Proxy times and send end cannot be read from the
// Navigation Timing API, so those are all left as null.
void GetResultDeltas(TimingDeltas* navigation_deltas) {
*navigation_deltas = TimingDeltas();
navigation_deltas->dns_start = GetResultDelta("domainLookupStart");
navigation_deltas->dns_end = GetResultDelta("domainLookupEnd");
navigation_deltas->connect_start = GetResultDelta("connectStart");
navigation_deltas->connect_end = GetResultDelta("connectEnd");
navigation_deltas->send_start = GetResultDelta("requestStart");
navigation_deltas->receive_headers_end = GetResultDelta("responseStart");
// Unlike the above times, secureConnectionStart will be zero when not
// applicable. In that case, leave ssl_start as null.
bool ssl_start_zero = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
browser()->tab_strip_model()->GetActiveWebContents(),
"window.domAutomationController.send("
"performance.timing.secureConnectionStart == 0);",
&ssl_start_zero));
if (!ssl_start_zero)
navigation_deltas->ssl_start = GetResultDelta("secureConnectionStart");
else
navigation_deltas->ssl_start = -1;
// Simple sanity checks. Make sure times that correspond to LoadTimingInfo
// occur between fetchStart and loadEventEnd. Relationships between
// intervening times are handled by the test bodies.
int fetch_start = GetResultDelta("fetchStart");
// While the input dns_start is sometimes null, when read from the
// NavigationTiming API, it's always non-null.
EXPECT_LE(fetch_start, navigation_deltas->dns_start);
int load_event_end = GetResultDelta("loadEventEnd");
EXPECT_LE(navigation_deltas->receive_headers_end, load_event_end);
}
// Returns the time between performance.timing.fetchStart and the time with
// the specified name. This time must be non-negative.
int GetResultDelta(const std::string& name) {
int time_ms = 0;
std::string command(base::StringPrintf(
"window.domAutomationController.send("
"performance.timing.%s - performance.timing.fetchStart);",
name.c_str()));
EXPECT_TRUE(content::ExecuteScriptAndExtractInt(
browser()->tab_strip_model()->GetActiveWebContents(),
command.c_str(),
&time_ms));
// Basic sanity check.
EXPECT_GE(time_ms, 0);
return time_ms;
}
};
IN_PROC_BROWSER_TEST_F(LoadTimingBrowserTest, HTTP) {
ASSERT_TRUE(spawned_test_server()->Start());
GURL url = spawned_test_server()->GetURL("chunked?waitBeforeHeaders=100");
ui_test_utils::NavigateToURL(browser(), url);
TimingDeltas navigation_deltas;
GetResultDeltas(&navigation_deltas);
EXPECT_LE(navigation_deltas.dns_start, navigation_deltas.dns_end);
EXPECT_LE(navigation_deltas.dns_end, navigation_deltas.connect_start);
EXPECT_LE(navigation_deltas.connect_start, navigation_deltas.connect_end);
EXPECT_LE(navigation_deltas.connect_end, navigation_deltas.send_start);
EXPECT_LT(navigation_deltas.send_start,
navigation_deltas.receive_headers_end);
EXPECT_EQ(navigation_deltas.ssl_start, -1);
}
IN_PROC_BROWSER_TEST_F(LoadTimingBrowserTest, HTTPS) {
net::SpawnedTestServer https_server(net::SpawnedTestServer::TYPE_HTTPS,
net::BaseTestServer::SSLOptions(),
base::FilePath());
ASSERT_TRUE(https_server.Start());
GURL url = https_server.GetURL("chunked?waitBeforeHeaders=100");
ui_test_utils::NavigateToURL(browser(), url);
TimingDeltas navigation_deltas;
GetResultDeltas(&navigation_deltas);
EXPECT_LE(navigation_deltas.dns_start, navigation_deltas.dns_end);
EXPECT_LE(navigation_deltas.dns_end, navigation_deltas.connect_start);
EXPECT_LE(navigation_deltas.connect_start, navigation_deltas.ssl_start);
EXPECT_LE(navigation_deltas.ssl_start, navigation_deltas.connect_end);
EXPECT_LE(navigation_deltas.connect_end, navigation_deltas.send_start);
EXPECT_LT(navigation_deltas.send_start,
navigation_deltas.receive_headers_end);
}
IN_PROC_BROWSER_TEST_F(LoadTimingBrowserTest, Proxy) {
ASSERT_TRUE(spawned_test_server()->Start());
browser()->profile()->GetPrefs()->Set(
proxy_config::prefs::kProxy,
ProxyConfigDictionary::CreateFixedServers(
spawned_test_server()->host_port_pair().ToString(), std::string()));
ProfileNetworkContextServiceFactory::GetForContext(browser()->profile())
->FlushProxyConfigMonitorForTesting();
// This request will fail if it doesn't go through proxy.
GURL url("http://does.not.resolve.test/chunked?waitBeforeHeaders=100");
ui_test_utils::NavigateToURL(browser(), url);
TimingDeltas navigation_deltas;
GetResultDeltas(&navigation_deltas);
EXPECT_LE(navigation_deltas.dns_start, navigation_deltas.dns_end);
EXPECT_LE(navigation_deltas.dns_end, navigation_deltas.connect_start);
EXPECT_LE(navigation_deltas.connect_start, navigation_deltas.connect_end);
EXPECT_LE(navigation_deltas.connect_end, navigation_deltas.send_start);
EXPECT_LT(navigation_deltas.send_start,
navigation_deltas.receive_headers_end);
EXPECT_EQ(navigation_deltas.ssl_start, -1);
}
IN_PROC_BROWSER_TEST_F(LoadTimingBrowserTest, FTP) {
net::SpawnedTestServer ftp_server(net::SpawnedTestServer::TYPE_FTP,
base::FilePath());
ASSERT_TRUE(ftp_server.Start());
GURL url = ftp_server.GetURL("/");
ui_test_utils::NavigateToURL(browser(), url);
TimingDeltas navigation_deltas;
GetResultDeltas(&navigation_deltas);
EXPECT_EQ(navigation_deltas.dns_start, 0);
EXPECT_EQ(navigation_deltas.dns_end, 0);
EXPECT_EQ(navigation_deltas.connect_start, 0);
EXPECT_EQ(navigation_deltas.connect_end, 0);
EXPECT_EQ(navigation_deltas.receive_headers_end, 0);
EXPECT_EQ(navigation_deltas.ssl_start, -1);
}
} // namespace