// Copyright 2019 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 "weblayer/test/weblayer_browser_test.h" #include "base/callback.h" #include "base/files/file_path.h" #include "base/test/bind_test_util.h" #include "components/variations/net/variations_http_headers.h" #include "components/variations/variations_http_header_provider.h" #include "content/public/test/url_loader_interceptor.h" #include "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/controllable_http_response.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "net/test/embedded_test_server/http_response.h" #include "weblayer/public/navigation.h" #include "weblayer/public/navigation_controller.h" #include "weblayer/public/navigation_observer.h" #include "weblayer/public/tab.h" #include "weblayer/shell/browser/shell.h" #include "weblayer/test/interstitial_utils.h" #include "weblayer/test/weblayer_browser_test_utils.h" namespace weblayer { namespace { // NavigationObserver that allows registering a callback for various // NavigationObserver functions. class NavigationObserverImpl : public NavigationObserver { public: explicit NavigationObserverImpl(NavigationController* controller) : controller_(controller) { controller_->AddObserver(this); } ~NavigationObserverImpl() override { controller_->RemoveObserver(this); } using Callback = base::RepeatingCallback; void SetStartedCallback(Callback callback) { started_callback_ = std::move(callback); } void SetRedirectedCallback(Callback callback) { redirected_callback_ = std::move(callback); } void SetFailedCallback(Callback callback) { failed_callback_ = std::move(callback); } void SetCompletedClosure(Callback callback) { completed_callback_ = std::move(callback); } // NavigationObserver: void NavigationStarted(Navigation* navigation) override { if (started_callback_) started_callback_.Run(navigation); } void NavigationRedirected(Navigation* navigation) override { if (redirected_callback_) redirected_callback_.Run(navigation); } void NavigationCompleted(Navigation* navigation) override { if (completed_callback_) completed_callback_.Run(navigation); } void NavigationFailed(Navigation* navigation) override { if (failed_callback_) failed_callback_.Run(navigation); } private: NavigationController* controller_; Callback started_callback_; Callback redirected_callback_; Callback completed_callback_; Callback failed_callback_; }; class OneShotNavigationObserver : public NavigationObserver { public: explicit OneShotNavigationObserver(Shell* shell) : tab_(shell->tab()) { tab_->GetNavigationController()->AddObserver(this); } ~OneShotNavigationObserver() override { tab_->GetNavigationController()->RemoveObserver(this); } void WaitForNavigation() { run_loop_.Run(); } bool completed() { return completed_; } bool is_error_page() { return is_error_page_; } bool is_download() { return is_download_; } bool was_stop_called() { return was_stop_called_; } Navigation::LoadError load_error() { return load_error_; } int http_status_code() { return http_status_code_; } NavigationState navigation_state() { return navigation_state_; } private: // NavigationObserver implementation: void NavigationCompleted(Navigation* navigation) override { completed_ = true; Finish(navigation); } void NavigationFailed(Navigation* navigation) override { Finish(navigation); } void Finish(Navigation* navigation) { is_error_page_ = navigation->IsErrorPage(); is_download_ = navigation->IsDownload(); was_stop_called_ = navigation->WasStopCalled(); load_error_ = navigation->GetLoadError(); http_status_code_ = navigation->GetHttpStatusCode(); navigation_state_ = navigation->GetState(); run_loop_.Quit(); } base::RunLoop run_loop_; Tab* tab_; bool completed_ = false; bool is_error_page_ = false; bool is_download_ = false; bool was_stop_called_ = false; Navigation::LoadError load_error_ = Navigation::kNoError; int http_status_code_ = 0; NavigationState navigation_state_ = NavigationState::kWaitingResponse; }; } // namespace class NavigationBrowserTest : public WebLayerBrowserTest { public: NavigationController* GetNavigationController() { return shell()->tab()->GetNavigationController(); } }; IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, NoError) { EXPECT_TRUE(embedded_test_server()->Start()); OneShotNavigationObserver observer(shell()); GetNavigationController()->Navigate( embedded_test_server()->GetURL("/simple_page.html")); observer.WaitForNavigation(); EXPECT_TRUE(observer.completed()); EXPECT_FALSE(observer.is_error_page()); EXPECT_FALSE(observer.is_download()); EXPECT_FALSE(observer.was_stop_called()); EXPECT_EQ(observer.load_error(), Navigation::kNoError); EXPECT_EQ(observer.http_status_code(), 200); EXPECT_EQ(observer.navigation_state(), NavigationState::kComplete); } IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, HttpClientError) { EXPECT_TRUE(embedded_test_server()->Start()); OneShotNavigationObserver observer(shell()); GetNavigationController()->Navigate( embedded_test_server()->GetURL("/non_existent.html")); observer.WaitForNavigation(); EXPECT_TRUE(observer.completed()); EXPECT_FALSE(observer.is_error_page()); EXPECT_EQ(observer.load_error(), Navigation::kHttpClientError); EXPECT_EQ(observer.http_status_code(), 404); EXPECT_EQ(observer.navigation_state(), NavigationState::kComplete); } IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, HttpServerError) { EXPECT_TRUE(embedded_test_server()->Start()); OneShotNavigationObserver observer(shell()); GetNavigationController()->Navigate( embedded_test_server()->GetURL("/echo?status=500")); observer.WaitForNavigation(); EXPECT_TRUE(observer.completed()); EXPECT_FALSE(observer.is_error_page()); EXPECT_EQ(observer.load_error(), Navigation::kHttpServerError); EXPECT_EQ(observer.http_status_code(), 500); EXPECT_EQ(observer.navigation_state(), NavigationState::kComplete); } IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, SSLError) { net::EmbeddedTestServer https_server_mismatched( net::EmbeddedTestServer::TYPE_HTTPS); https_server_mismatched.SetSSLConfig( net::EmbeddedTestServer::CERT_MISMATCHED_NAME); https_server_mismatched.AddDefaultHandlers( base::FilePath(FILE_PATH_LITERAL("weblayer/test/data"))); ASSERT_TRUE(https_server_mismatched.Start()); OneShotNavigationObserver observer(shell()); GetNavigationController()->Navigate( https_server_mismatched.GetURL("/simple_page.html")); observer.WaitForNavigation(); EXPECT_FALSE(observer.completed()); EXPECT_TRUE(observer.is_error_page()); EXPECT_EQ(observer.load_error(), Navigation::kSSLError); EXPECT_EQ(observer.navigation_state(), NavigationState::kFailed); } IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, HttpConnectivityError) { GURL url("http://doesntexist.com/foo"); auto interceptor = content::URLLoaderInterceptor::SetupRequestFailForURL( url, net::ERR_NAME_NOT_RESOLVED); OneShotNavigationObserver observer(shell()); GetNavigationController()->Navigate(url); observer.WaitForNavigation(); EXPECT_FALSE(observer.completed()); EXPECT_TRUE(observer.is_error_page()); EXPECT_EQ(observer.load_error(), Navigation::kConnectivityError); EXPECT_EQ(observer.navigation_state(), NavigationState::kFailed); } IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, Download) { EXPECT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/content-disposition.html")); OneShotNavigationObserver observer(shell()); GetNavigationController()->Navigate(url); observer.WaitForNavigation(); EXPECT_FALSE(observer.completed()); EXPECT_FALSE(observer.is_error_page()); EXPECT_TRUE(observer.is_download()); EXPECT_FALSE(observer.was_stop_called()); EXPECT_EQ(observer.load_error(), Navigation::kOtherError); EXPECT_EQ(observer.navigation_state(), NavigationState::kFailed); } IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, StopInOnStart) { ASSERT_TRUE(embedded_test_server()->Start()); base::RunLoop run_loop; NavigationObserverImpl observer(GetNavigationController()); observer.SetStartedCallback(base::BindLambdaForTesting( [&](Navigation*) { GetNavigationController()->Stop(); })); observer.SetFailedCallback( base::BindLambdaForTesting([&](Navigation* navigation) { ASSERT_TRUE(navigation->WasStopCalled()); run_loop.Quit(); })); GetNavigationController()->Navigate( embedded_test_server()->GetURL("/simple_page.html")); run_loop.Run(); } IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, StopInOnRedirect) { ASSERT_TRUE(embedded_test_server()->Start()); base::RunLoop run_loop; NavigationObserverImpl observer(GetNavigationController()); observer.SetRedirectedCallback(base::BindLambdaForTesting( [&](Navigation*) { GetNavigationController()->Stop(); })); observer.SetFailedCallback( base::BindLambdaForTesting([&](Navigation* navigation) { ASSERT_TRUE(navigation->WasStopCalled()); run_loop.Quit(); })); const GURL original_url = embedded_test_server()->GetURL("/simple_page.html"); GetNavigationController()->Navigate(embedded_test_server()->GetURL( "/server-redirect?" + original_url.spec())); run_loop.Run(); } IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, NavigateFromRendererInitiatedNavigation) { ASSERT_TRUE(embedded_test_server()->Start()); NavigationController* controller = shell()->tab()->GetNavigationController(); const GURL final_url = embedded_test_server()->GetURL("/simple_page2.html"); int failed_count = 0; int completed_count = 0; NavigationObserverImpl observer(controller); base::RunLoop run_loop; observer.SetFailedCallback( base::BindLambdaForTesting([&](Navigation*) { failed_count++; })); observer.SetCompletedClosure( base::BindLambdaForTesting([&](Navigation* navigation) { completed_count++; if (navigation->GetURL().path() == "/simple_page2.html") run_loop.Quit(); })); observer.SetStartedCallback( base::BindLambdaForTesting([&](Navigation* navigation) { if (navigation->GetURL().path() == "/simple_page.html") controller->Navigate(final_url); })); controller->Navigate(embedded_test_server()->GetURL("/simple_page4.html")); run_loop.Run(); EXPECT_EQ(1, failed_count); EXPECT_EQ(2, completed_count); ASSERT_EQ(2, controller->GetNavigationListSize()); EXPECT_EQ(final_url, controller->GetNavigationEntryDisplayURL(1)); } // Verifies calling Navigate() from NavigationRedirected() works. IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, NavigateFromRedirect) { net::test_server::ControllableHttpResponse response_1(embedded_test_server(), "", true); net::test_server::ControllableHttpResponse response_2(embedded_test_server(), "", true); ASSERT_TRUE(embedded_test_server()->Start()); NavigationObserverImpl observer(GetNavigationController()); bool got_redirect = false; const GURL url_to_load_on_redirect = embedded_test_server()->GetURL("/url_to_load_on_redirect.html"); observer.SetRedirectedCallback( base::BindLambdaForTesting([&](Navigation* navigation) { shell()->LoadURL(url_to_load_on_redirect); got_redirect = true; })); shell()->LoadURL(embedded_test_server()->GetURL("/initial_url.html")); response_1.WaitForRequest(); response_1.Send( "HTTP/1.1 302 Moved Temporarily\r\nLocation: /redirect_dest_url\r\n\r\n"); response_1.Done(); response_2.WaitForRequest(); response_2.Done(); EXPECT_EQ(url_to_load_on_redirect, response_2.http_request()->GetURL()); EXPECT_TRUE(got_redirect); } IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, SetRequestHeader) { net::test_server::ControllableHttpResponse response_1(embedded_test_server(), "", true); net::test_server::ControllableHttpResponse response_2(embedded_test_server(), "", true); ASSERT_TRUE(embedded_test_server()->Start()); const std::string header_name = "header"; const std::string header_value = "value"; NavigationObserverImpl observer(GetNavigationController()); observer.SetStartedCallback( base::BindLambdaForTesting([&](Navigation* navigation) { navigation->SetRequestHeader(header_name, header_value); })); shell()->LoadURL(embedded_test_server()->GetURL("/simple_page.html")); response_1.WaitForRequest(); // Header should be present in initial request. EXPECT_EQ(header_value, response_1.http_request()->headers.at(header_name)); response_1.Send( "HTTP/1.1 302 Moved Temporarily\r\nLocation: /new_doc\r\n\r\n"); response_1.Done(); // Header should carry through to redirect. response_2.WaitForRequest(); EXPECT_EQ(header_value, response_2.http_request()->headers.at(header_name)); } // Verifies setting the 'referer' via SetRequestHeader() works as expected. IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, SetRequestHeaderWithReferer) { net::test_server::ControllableHttpResponse response(embedded_test_server(), "", true); ASSERT_TRUE(embedded_test_server()->Start()); const std::string header_name = "Referer"; const std::string header_value = "http://request.com"; NavigationObserverImpl observer(GetNavigationController()); observer.SetStartedCallback( base::BindLambdaForTesting([&](Navigation* navigation) { navigation->SetRequestHeader(header_name, header_value); })); shell()->LoadURL(embedded_test_server()->GetURL("/simple_page.html")); response.WaitForRequest(); // Verify 'referer' matches expected value. EXPECT_EQ(GURL(header_value), GURL(response.http_request()->headers.at(header_name))); } IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, SetRequestHeaderInRedirect) { net::test_server::ControllableHttpResponse response_1(embedded_test_server(), "", true); net::test_server::ControllableHttpResponse response_2(embedded_test_server(), "", true); ASSERT_TRUE(embedded_test_server()->Start()); const std::string header_name = "header"; const std::string header_value = "value"; NavigationObserverImpl observer(GetNavigationController()); observer.SetRedirectedCallback( base::BindLambdaForTesting([&](Navigation* navigation) { navigation->SetRequestHeader(header_name, header_value); })); shell()->LoadURL(embedded_test_server()->GetURL("/simple_page.html")); response_1.WaitForRequest(); // Header should not be present in initial request. EXPECT_FALSE(base::Contains(response_1.http_request()->headers, header_name)); response_1.Send( "HTTP/1.1 302 Moved Temporarily\r\nLocation: /new_doc\r\n\r\n"); response_1.Done(); response_2.WaitForRequest(); // Header should be in redirect. ASSERT_TRUE(base::Contains(response_2.http_request()->headers, header_name)); EXPECT_EQ(header_value, response_2.http_request()->headers.at(header_name)); } IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, PageSeesUserAgentString) { ASSERT_TRUE(embedded_test_server()->Start()); const std::string custom_ua = "custom"; NavigationObserverImpl observer(GetNavigationController()); observer.SetStartedCallback( base::BindLambdaForTesting([&](Navigation* navigation) { navigation->SetUserAgentString(custom_ua); })); OneShotNavigationObserver navigation_observer(shell()); shell()->LoadURL(embedded_test_server()->GetURL("/simple_page.html")); navigation_observer.WaitForNavigation(); base::RunLoop run_loop; shell()->tab()->ExecuteScript( base::ASCIIToUTF16("navigator.userAgent;"), false, base::BindLambdaForTesting([&](base::Value value) { ASSERT_TRUE(value.is_string()); EXPECT_EQ(custom_ua, value.GetString()); run_loop.Quit(); })); run_loop.Run(); } IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, SetUserAgentString) { net::test_server::ControllableHttpResponse response_1(embedded_test_server(), "", true); net::test_server::ControllableHttpResponse response_2(embedded_test_server(), "", true); ASSERT_TRUE(embedded_test_server()->Start()); const std::string custom_ua = "CUSTOM"; NavigationObserverImpl observer(GetNavigationController()); observer.SetStartedCallback( base::BindLambdaForTesting([&](Navigation* navigation) { navigation->SetUserAgentString(custom_ua); })); shell()->LoadURL(embedded_test_server()->GetURL("/simple_page.html")); response_1.WaitForRequest(); // |custom_ua| should be present in initial request. ASSERT_TRUE(base::Contains(response_1.http_request()->headers, net::HttpRequestHeaders::kUserAgent)); const std::string new_header = response_1.http_request()->headers.at( net::HttpRequestHeaders::kUserAgent); EXPECT_EQ(custom_ua, new_header); // Header should carry through to redirect. response_1.Send( "HTTP/1.1 302 Moved Temporarily\r\nLocation: /new_doc\r\n\r\n"); response_1.Done(); response_2.WaitForRequest(); EXPECT_EQ(custom_ua, response_2.http_request()->headers.at( net::HttpRequestHeaders::kUserAgent)); } // Verifies changing the user agent twice in a row works. IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, UserAgentDoesntCarryThrough1) { net::test_server::ControllableHttpResponse response_1(embedded_test_server(), "", true); net::test_server::ControllableHttpResponse response_2(embedded_test_server(), "", true); ASSERT_TRUE(embedded_test_server()->Start()); const std::string custom_ua1 = "my ua1"; const std::string custom_ua2 = "my ua2"; NavigationObserverImpl observer(GetNavigationController()); observer.SetStartedCallback( base::BindLambdaForTesting([&](Navigation* navigation) { navigation->SetUserAgentString(custom_ua1); })); shell()->LoadURL(embedded_test_server()->GetURL("/simple_page.html")); response_1.WaitForRequest(); EXPECT_EQ(custom_ua1, response_1.http_request()->headers.at( net::HttpRequestHeaders::kUserAgent)); // Before the request is done, start another navigation. observer.SetStartedCallback( base::BindLambdaForTesting([&](Navigation* navigation) { navigation->SetUserAgentString(custom_ua2); })); shell()->LoadURL(embedded_test_server()->GetURL("/simple_page2.html")); response_2.WaitForRequest(); EXPECT_EQ(custom_ua2, response_2.http_request()->headers.at( net::HttpRequestHeaders::kUserAgent)); } // Verifies changing the user agent doesn't bleed through to next navigation. IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, UserAgentDoesntCarryThrough2) { net::test_server::ControllableHttpResponse response_1(embedded_test_server(), "", true); net::test_server::ControllableHttpResponse response_2(embedded_test_server(), "", true); ASSERT_TRUE(embedded_test_server()->Start()); const std::string custom_ua = "my ua1"; NavigationObserverImpl observer(GetNavigationController()); observer.SetStartedCallback( base::BindLambdaForTesting([&](Navigation* navigation) { navigation->SetUserAgentString(custom_ua); })); shell()->LoadURL(embedded_test_server()->GetURL("/simple_page.html")); response_1.WaitForRequest(); EXPECT_EQ(custom_ua, response_1.http_request()->headers.at( net::HttpRequestHeaders::kUserAgent)); // Before the request is done, start another navigation. observer.SetStartedCallback(base::DoNothing()); shell()->LoadURL(embedded_test_server()->GetURL("/simple_page2.html")); response_2.WaitForRequest(); EXPECT_NE(custom_ua, response_2.http_request()->headers.at( net::HttpRequestHeaders::kUserAgent)); EXPECT_FALSE(response_2.http_request() ->headers.at(net::HttpRequestHeaders::kUserAgent) .empty()); } // Verifies changing the user-agent applies to child resources, such as an // . IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, UserAgentAppliesToChildResources) { net::test_server::ControllableHttpResponse response_1(embedded_test_server(), "", true); net::test_server::ControllableHttpResponse response_2(embedded_test_server(), "", true); ASSERT_TRUE(embedded_test_server()->Start()); const std::string custom_ua = "custom-ua"; NavigationObserverImpl observer(GetNavigationController()); observer.SetStartedCallback( base::BindLambdaForTesting([&](Navigation* navigation) { navigation->SetUserAgentString(custom_ua); })); shell()->LoadURL(embedded_test_server()->GetURL("/foo.html")); response_1.WaitForRequest(); response_1.Send(net::HTTP_OK, "text/html", ""); response_1.Done(); EXPECT_EQ(custom_ua, response_1.http_request()->headers.at( net::HttpRequestHeaders::kUserAgent)); observer.SetStartedCallback(base::DoNothing()); response_2.WaitForRequest(); EXPECT_EQ(custom_ua, response_2.http_request()->headers.at( net::HttpRequestHeaders::kUserAgent)); } IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, SetUserAgentStringRendererInitiated) { net::test_server::ControllableHttpResponse response_1(embedded_test_server(), "", true); net::test_server::ControllableHttpResponse response_2(embedded_test_server(), "", true); ASSERT_TRUE(embedded_test_server()->Start()); OneShotNavigationObserver load_observer(shell()); NavigationObserverImpl observer(GetNavigationController()); shell()->LoadURL(embedded_test_server()->GetURL("/simple_page.html")); response_1.WaitForRequest(); response_1.Send(net::HTTP_OK, "text/html", ""); response_1.Done(); load_observer.WaitForNavigation(); const std::string custom_ua = "custom-ua"; observer.SetStartedCallback( base::BindLambdaForTesting([&](Navigation* navigation) { navigation->SetUserAgentString(custom_ua); })); const GURL target_url = embedded_test_server()->GetURL("/foo.html"); shell()->tab()->ExecuteScript( base::ASCIIToUTF16("location.href='" + target_url.spec() + "';"), false, base::DoNothing()); response_2.WaitForRequest(); // |custom_ua| should be present in the renderer initiated navigation. ASSERT_TRUE(base::Contains(response_2.http_request()->headers, net::HttpRequestHeaders::kUserAgent)); const std::string new_ua = response_2.http_request()->headers.at( net::HttpRequestHeaders::kUserAgent); EXPECT_EQ(custom_ua, new_ua); } class NavigationBrowserTest2 : public NavigationBrowserTest { public: void SetUp() override { // HTTPS server only serves a valid cert for localhost, so this is needed to // load pages from "www.google.com" without an interstitial. base::CommandLine::ForCurrentProcess()->AppendSwitch( "ignore-certificate-errors"); NavigationBrowserTest::SetUp(); } void SetUpOnMainThread() override { NavigationBrowserTest::SetUpOnMainThread(); https_server_ = std::make_unique( net::EmbeddedTestServer::TYPE_HTTPS); // The test makes requests to google.com which we want to redirect to the // test server. host_resolver()->AddRule("*", "127.0.0.1"); // Forces variations code to set the header. auto* variations_provider = variations::VariationsHttpHeaderProvider::GetInstance(); variations_provider->ForceVariationIds({"12", "456", "t789"}, ""); } net::EmbeddedTestServer* https_server() { return https_server_.get(); } private: std::unique_ptr https_server_; }; // This test verifies the embedder can replace the X-Client-Data header that // is also set by //components/variations. IN_PROC_BROWSER_TEST_F(NavigationBrowserTest2, ReplaceXClientDataHeader) { std::unique_ptr run_loop = std::make_unique(); std::string last_header_value; https_server()->RegisterRequestHandler(base::BindLambdaForTesting( [&](const net::test_server::HttpRequest& request) -> std::unique_ptr { auto iter = request.headers.find(variations::kClientDataHeader); if (iter != request.headers.end()) last_header_value = iter->second; run_loop->Quit(); return std::make_unique(); })); ASSERT_TRUE(https_server()->Start()); // Verify the header is set by default. const GURL url = https_server()->GetURL("www.google.com", "/"); shell()->LoadURL(url); run_loop->Run(); EXPECT_FALSE(last_header_value.empty()); // Repeat, but clobber the header when navigating. const std::string header_value = "value"; EXPECT_NE(last_header_value, header_value); last_header_value.clear(); run_loop = std::make_unique(); NavigationObserverImpl observer(GetNavigationController()); observer.SetStartedCallback( base::BindLambdaForTesting([&](Navigation* navigation) { navigation->SetRequestHeader(variations::kClientDataHeader, header_value); })); shell()->LoadURL(https_server()->GetURL("www.google.com", "/foo")); run_loop->Run(); EXPECT_EQ(header_value, last_header_value); } IN_PROC_BROWSER_TEST_F(NavigationBrowserTest2, SetXClientDataHeaderCarriesThroughToRedirect) { std::unique_ptr run_loop = std::make_unique(); std::string last_header_value; bool should_redirect = true; https_server()->RegisterRequestHandler(base::BindLambdaForTesting( [&](const net::test_server::HttpRequest& request) -> std::unique_ptr { auto response = std::make_unique(); if (should_redirect) { should_redirect = false; response->set_code(net::HTTP_MOVED_PERMANENTLY); response->AddCustomHeader( "Location", https_server()->GetURL("www.google.com", "/redirect").spec()); } else { auto iter = request.headers.find(variations::kClientDataHeader); last_header_value = iter->second; run_loop->Quit(); } return response; })); ASSERT_TRUE(https_server()->Start()); const std::string header_value = "value"; run_loop = std::make_unique(); NavigationObserverImpl observer(GetNavigationController()); observer.SetStartedCallback( base::BindLambdaForTesting([&](Navigation* navigation) { navigation->SetRequestHeader(variations::kClientDataHeader, header_value); })); shell()->LoadURL(https_server()->GetURL("www.google.com", "/foo")); run_loop->Run(); EXPECT_EQ(header_value, last_header_value); } IN_PROC_BROWSER_TEST_F(NavigationBrowserTest2, SetXClientDataHeaderInRedirect) { std::unique_ptr run_loop = std::make_unique(); std::string last_header_value; bool should_redirect = true; https_server()->RegisterRequestHandler(base::BindLambdaForTesting( [&](const net::test_server::HttpRequest& request) -> std::unique_ptr { auto response = std::make_unique(); if (should_redirect) { should_redirect = false; response->set_code(net::HTTP_MOVED_PERMANENTLY); response->AddCustomHeader( "Location", https_server()->GetURL("www.google.com", "/redirect").spec()); } else { auto iter = request.headers.find(variations::kClientDataHeader); last_header_value = iter->second; run_loop->Quit(); } return response; })); ASSERT_TRUE(https_server()->Start()); const std::string header_value = "value"; run_loop = std::make_unique(); NavigationObserverImpl observer(GetNavigationController()); observer.SetRedirectedCallback( base::BindLambdaForTesting([&](Navigation* navigation) { navigation->SetRequestHeader(variations::kClientDataHeader, header_value); })); shell()->LoadURL(https_server()->GetURL("www.google.com", "/foo")); run_loop->Run(); EXPECT_EQ(header_value, last_header_value); } } // namespace weblayer