blob: 5f2ab9a280f772ef6d25ab5d9a852e069246cf60 [file] [log] [blame] [edit]
// Copyright 2013 The Chromium Authors
// 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/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/strings/stringprintf.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.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.h"
#include "content/public/test/browser_test_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "services/network/public/cpp/features.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 domain_lookup_start;
int domain_lookup_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() = default;
~LoadTimingBrowserTest() override = default;
// 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->domain_lookup_start =
GetResultDelta("domainLookupStart");
navigation_deltas->domain_lookup_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 =
content::EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
"performance.timing.secureConnectionStart == 0;")
.ExtractBool();
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->domain_lookup_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) {
std::string command(base::StringPrintf(
"performance.timing.%s - performance.timing.fetchStart;",
name.c_str()));
int time_ms =
content::EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
command.c_str())
.ExtractInt();
// Basic sanity check.
EXPECT_GE(time_ms, 0);
return time_ms;
}
};
IN_PROC_BROWSER_TEST_F(LoadTimingBrowserTest, HTTP) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL("/chunked?waitBeforeHeaders=100");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
TimingDeltas navigation_deltas;
GetResultDeltas(&navigation_deltas);
EXPECT_LE(navigation_deltas.domain_lookup_start,
navigation_deltas.domain_lookup_end);
EXPECT_LE(navigation_deltas.domain_lookup_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);
}
// TODO(crbug.com/40719387): Flaky on all platforms
IN_PROC_BROWSER_TEST_F(LoadTimingBrowserTest, DISABLED_HTTPS) {
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
https_server.AddDefaultHandlers();
ASSERT_TRUE(https_server.Start());
GURL url = https_server.GetURL("/chunked?waitBeforeHeaders=100");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
TimingDeltas navigation_deltas;
GetResultDeltas(&navigation_deltas);
EXPECT_LE(navigation_deltas.domain_lookup_start,
navigation_deltas.domain_lookup_end);
EXPECT_LE(navigation_deltas.domain_lookup_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(embedded_test_server()->Start());
browser()->profile()->GetPrefs()->SetDict(
proxy_config::prefs::kProxy,
ProxyConfigDictionary::CreateFixedServers(
embedded_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");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
TimingDeltas navigation_deltas;
GetResultDeltas(&navigation_deltas);
EXPECT_LE(navigation_deltas.domain_lookup_start,
navigation_deltas.domain_lookup_end);
EXPECT_LE(navigation_deltas.domain_lookup_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);
}
} // namespace