#include "base/barrier_closure.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_content_browser_client.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/system/sys_info.h"
#endif
namespace content {
namespace {
class MockContentBrowserClient final
: public ContentBrowserTestContentBrowserClient {
public:
void UpdateRendererPreferencesForWorker(
BrowserContext*,
blink::RendererPreferences* prefs) override {
if (do_not_track_enabled_) {
prefs->enable_do_not_track = true;
prefs->enable_referrers = true;
}
}
void EnableDoNotTrack() { do_not_track_enabled_ = true; }
private:
bool do_not_track_enabled_ = false;
};
class DoNotTrackTest : public ContentBrowserTest {
protected:
void SetUpOnMainThread() override {
#if BUILDFLAG(IS_ANDROID)
int32_t major_version = 0, minor_version = 0, bugfix_version = 0;
base::SysInfo::OperatingSystemVersionNumbers(&major_version, &minor_version,
&bugfix_version);
if (major_version < 5)
return;
#endif
client_ = std::make_unique<MockContentBrowserClient>();
}
void TearDownOnMainThread() override { client_.reset(); }
bool EnableDoNotTrack() {
client_->EnableDoNotTrack();
blink::RendererPreferences* prefs =
shell()->web_contents()->GetMutableRendererPrefs();
EXPECT_FALSE(prefs->enable_do_not_track);
prefs->enable_do_not_track = true;
return true;
}
void ExpectPageTextEq(const std::string& expected_content) {
EXPECT_EQ(expected_content, EvalJs(shell(), "document.body.innerText;"));
}
std::string GetDOMDoNotTrackProperty() {
return EvalJs(shell(),
"navigator.doNotTrack === null ? '' : navigator.doNotTrack")
.ExtractString();
}
GURL GetURL(std::string relative_url) {
return embedded_test_server()->GetURL(relative_url);
}
private:
std::unique_ptr<MockContentBrowserClient> client_;
};
std::unique_ptr<net::test_server::HttpResponse>
CaptureHeaderHandlerAndReturnScript(
const std::string& path,
net::test_server::HttpRequest::HeaderMap* header_map,
const std::string& script,
base::OnceClosure done_callback,
const net::test_server::HttpRequest& request) {
GURL request_url = request.GetURL();
if (request_url.path() != path)
return nullptr;
*header_map = request.headers;
std::move(done_callback).Run();
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
response->set_content_type("text/javascript");
response->set_content(script);
return response;
}
IN_PROC_BROWSER_TEST_F(DoNotTrackTest, NotEnabled) {
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(NavigateToURL(shell(), GetURL("/echoheader?DNT")));
ExpectPageTextEq("None");
EXPECT_EQ("", GetDOMDoNotTrackProperty());
}
IN_PROC_BROWSER_TEST_F(DoNotTrackTest, Simple) {
ASSERT_TRUE(embedded_test_server()->Start());
if (!EnableDoNotTrack())
return;
EXPECT_TRUE(NavigateToURL(shell(), GetURL("/echoheader?DNT")));
ExpectPageTextEq("1");
}
IN_PROC_BROWSER_TEST_F(DoNotTrackTest, Redirect) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL final_url = GetURL("/echoheader?DNT");
GURL url = GetURL("/server-redirect?" + final_url.spec());
if (!EnableDoNotTrack())
return;
EXPECT_TRUE(NavigateToURL(shell(), url, final_url ));
ExpectPageTextEq("1");
}
IN_PROC_BROWSER_TEST_F(DoNotTrackTest, DOMProperty) {
ASSERT_TRUE(embedded_test_server()->Start());
if (!EnableDoNotTrack())
return;
EXPECT_TRUE(NavigateToURL(shell(), GetURL("/echo")));
EXPECT_EQ("1", GetDOMDoNotTrackProperty());
}
IN_PROC_BROWSER_TEST_F(DoNotTrackTest, Worker) {
const std::string kWorkerScript = R"(postMessage('DONE');)";
net::test_server::HttpRequest::HeaderMap header_map;
base::RunLoop loop;
embedded_test_server()->RegisterRequestHandler(
base::BindRepeating(&CaptureHeaderHandlerAndReturnScript, "/capture",
&header_map, kWorkerScript, loop.QuitClosure()));
ASSERT_TRUE(embedded_test_server()->Start());
if (!EnableDoNotTrack())
return;
EXPECT_TRUE(NavigateToURL(
shell(), GetURL("/workers/create_worker.html?worker_url=/capture")));
loop.Run();
EXPECT_TRUE(header_map.find("DNT") != header_map.end());
EXPECT_EQ("1", header_map["DNT"]);
EXPECT_EQ("DONE", EvalJs(shell(), "waitForMessage();"));
}
#if BUILDFLAG(IS_ANDROID)
#define MAYBE_SharedWorker DISABLED_SharedWorker
#else
#define MAYBE_SharedWorker SharedWorker
#endif
IN_PROC_BROWSER_TEST_F(DoNotTrackTest, MAYBE_SharedWorker) {
const std::string kWorkerScript =
R"(self.onconnect = e => { e.ports[0].postMessage('DONE'); };)";
net::test_server::HttpRequest::HeaderMap header_map;
base::RunLoop loop;
embedded_test_server()->RegisterRequestHandler(
base::BindRepeating(&CaptureHeaderHandlerAndReturnScript, "/capture",
&header_map, kWorkerScript, loop.QuitClosure()));
ASSERT_TRUE(embedded_test_server()->Start());
if (!EnableDoNotTrack())
return;
EXPECT_TRUE(NavigateToURL(
shell(),
GetURL("/workers/create_shared_worker.html?worker_url=/capture")));
loop.Run();
EXPECT_TRUE(header_map.find("DNT") != header_map.end());
EXPECT_EQ("1", header_map["DNT"]);
EXPECT_EQ("DONE", EvalJs(shell(), "waitForMessage();"));
}
IN_PROC_BROWSER_TEST_F(DoNotTrackTest, ServiceWorker_Register) {
const std::string kWorkerScript = "// empty";
net::test_server::HttpRequest::HeaderMap header_map;
base::RunLoop loop;
embedded_test_server()->RegisterRequestHandler(
base::BindRepeating(&CaptureHeaderHandlerAndReturnScript, "/capture",
&header_map, kWorkerScript, loop.QuitClosure()));
ASSERT_TRUE(embedded_test_server()->Start());
if (!EnableDoNotTrack())
return;
EXPECT_TRUE(NavigateToURL(
shell(), GetURL("/service_worker/create_service_worker.html")));
EXPECT_EQ("DONE", EvalJs(shell(), "register('/capture');"));
loop.Run();
EXPECT_TRUE(header_map.find("DNT") != header_map.end());
EXPECT_EQ("1", header_map["DNT"]);
}
IN_PROC_BROWSER_TEST_F(DoNotTrackTest, ModuleServiceWorker_Register) {
if (!EnableDoNotTrack())
return;
const std::string kWorkerScript = "// empty";
net::test_server::HttpRequest::HeaderMap header_map;
base::RunLoop loop;
embedded_test_server()->RegisterRequestHandler(
base::BindRepeating(&CaptureHeaderHandlerAndReturnScript, "/capture",
&header_map, kWorkerScript, loop.QuitClosure()));
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(NavigateToURL(
shell(), GetURL("/service_worker/create_service_worker.html")));
EXPECT_EQ("DONE", EvalJs(shell(), "register('/capture', '', 'module');"));
loop.Run();
EXPECT_TRUE(header_map.find("DNT") != header_map.end());
EXPECT_EQ("1", header_map["DNT"]);
}
IN_PROC_BROWSER_TEST_F(DoNotTrackTest,
StaticImportModuleServiceWorker_Register) {
if (!EnableDoNotTrack())
return;
const std::string kModuleScript = "// empty";
const std::string kWorkerScript = "import './captureModule';";
net::test_server::HttpRequest::HeaderMap header_map;
base::RunLoop loop;
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
&CaptureHeaderHandlerAndReturnScript, "/captureModule", &header_map,
kModuleScript, loop.QuitClosure()));
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
&CaptureHeaderHandlerAndReturnScript, "/captureWorker", &header_map,
kWorkerScript, loop.QuitClosure()));
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(NavigateToURL(
shell(), GetURL("/service_worker/create_service_worker.html")));
EXPECT_EQ("DONE",
EvalJs(shell(), "register('/captureWorker','', 'module');"));
loop.Run();
EXPECT_TRUE(header_map.find("DNT") != header_map.end());
EXPECT_EQ("1", header_map["DNT"]);
}
IN_PROC_BROWSER_TEST_F(DoNotTrackTest, ServiceWorker_Update) {
const std::string kWorkerScript = "// empty";
net::test_server::HttpRequest::HeaderMap header_map;
base::RunLoop loop;
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
&CaptureHeaderHandlerAndReturnScript, "/capture", &header_map,
kWorkerScript, base::BarrierClosure(2, loop.QuitClosure())));
ASSERT_TRUE(embedded_test_server()->Start());
if (!EnableDoNotTrack())
return;
EXPECT_TRUE(NavigateToURL(
shell(), GetURL("/service_worker/create_service_worker.html")));
EXPECT_EQ("DONE", EvalJs(shell(), "register('/capture');"));
EXPECT_EQ("DONE", EvalJs(shell(), "update();"));
loop.Run();
EXPECT_TRUE(header_map.find("DNT") != header_map.end());
EXPECT_EQ("1", header_map["DNT"]);
}
IN_PROC_BROWSER_TEST_F(DoNotTrackTest, ModuleServiceWorker_Update) {
if (!EnableDoNotTrack())
return;
const std::string kWorkerScript = "// empty";
net::test_server::HttpRequest::HeaderMap header_map;
base::RunLoop loop;
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
&CaptureHeaderHandlerAndReturnScript, "/capture", &header_map,
kWorkerScript, base::BarrierClosure(2, loop.QuitClosure())));
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(NavigateToURL(
shell(), GetURL("/service_worker/create_service_worker.html")));
EXPECT_EQ("DONE", EvalJs(shell(), "register('/capture','','module');"));
EXPECT_EQ("DONE", EvalJs(shell(), "update();"));
loop.Run();
EXPECT_TRUE(header_map.find("DNT") != header_map.end());
EXPECT_EQ("1", header_map["DNT"]);
}
IN_PROC_BROWSER_TEST_F(DoNotTrackTest, StaticImportModuleServiceWorker_Update) {
if (!EnableDoNotTrack())
return;
const std::string kModuleScript = "// empty";
const std::string kWorkerScript = "import '/captureModule';";
net::test_server::HttpRequest::HeaderMap header_map;
base::RunLoop loop;
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
&CaptureHeaderHandlerAndReturnScript, "/captureModule", &header_map,
kModuleScript, loop.QuitClosure()));
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
&CaptureHeaderHandlerAndReturnScript, "/captureWorker", &header_map,
kWorkerScript, loop.QuitClosure()));
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(NavigateToURL(
shell(), GetURL("/service_worker/create_service_worker.html")));
EXPECT_EQ("DONE", EvalJs(shell(), "register('/captureWorker','','module');"));
EXPECT_EQ("DONE", EvalJs(shell(), "update();"));
loop.Run();
EXPECT_TRUE(header_map.find("DNT") != header_map.end());
EXPECT_EQ("1", header_map["DNT"]);
}
IN_PROC_BROWSER_TEST_F(DoNotTrackTest, FetchFromWorker) {
ASSERT_TRUE(embedded_test_server()->Start());
if (!EnableDoNotTrack())
return;
EXPECT_TRUE(
NavigateToURL(shell(), GetURL("/workers/fetch_from_worker.html")));
EXPECT_EQ("1", EvalJs(shell(), "fetch_from_worker('/echoheader?DNT');"));
}
#if BUILDFLAG(IS_ANDROID)
#define MAYBE_FetchFromSharedWorker DISABLED_FetchFromSharedWorker
#else
#define MAYBE_FetchFromSharedWorker FetchFromSharedWorker
#endif
IN_PROC_BROWSER_TEST_F(DoNotTrackTest, MAYBE_FetchFromSharedWorker) {
ASSERT_TRUE(embedded_test_server()->Start());
if (!EnableDoNotTrack())
return;
EXPECT_TRUE(
NavigateToURL(shell(), GetURL("/workers/fetch_from_shared_worker.html")));
EXPECT_EQ("1",
EvalJs(shell(), "fetch_from_shared_worker('/echoheader?DNT');"));
}
IN_PROC_BROWSER_TEST_F(DoNotTrackTest, FetchFromServiceWorker) {
ASSERT_TRUE(embedded_test_server()->Start());
if (!EnableDoNotTrack())
return;
EXPECT_TRUE(NavigateToURL(
shell(), GetURL("/service_worker/fetch_from_service_worker.html")));
EXPECT_EQ("ready", EvalJs(shell(), "setup();"));
EXPECT_EQ("1",
EvalJs(shell(), "fetch_from_service_worker('/echoheader?DNT');"));
}
IN_PROC_BROWSER_TEST_F(DoNotTrackTest,
FetchFromServiceWorkerControlledPage_NoFetchHandler) {
ASSERT_TRUE(embedded_test_server()->Start());
if (!EnableDoNotTrack())
return;
EXPECT_TRUE(NavigateToURL(
shell(), GetURL("/service_worker/create_service_worker.html")));
EXPECT_EQ("DONE", EvalJs(shell(), "register('empty.js');"));
EXPECT_TRUE(
NavigateToURL(shell(), GetURL("/service_worker/fetch_from_page.html")));
EXPECT_EQ("1", EvalJs(shell(), "fetch_from_page('/echoheader?DNT');"));
}
IN_PROC_BROWSER_TEST_F(DoNotTrackTest,
FetchFromServiceWorkerControlledPage_PassThrough) {
ASSERT_TRUE(embedded_test_server()->Start());
if (!EnableDoNotTrack())
return;
EXPECT_TRUE(NavigateToURL(
shell(), GetURL("/service_worker/create_service_worker.html")));
EXPECT_EQ("DONE",
EvalJs(shell(), "register('fetch_event_pass_through.js');"));
EXPECT_TRUE(
NavigateToURL(shell(), GetURL("/service_worker/fetch_from_page.html")));
EXPECT_EQ("1", EvalJs(shell(), "fetch_from_page('/echoheader?DNT');"));
}
IN_PROC_BROWSER_TEST_F(DoNotTrackTest,
FetchFromServiceWorkerControlledPage_RespondWithFetch) {
ASSERT_TRUE(embedded_test_server()->Start());
if (!EnableDoNotTrack())
return;
EXPECT_TRUE(NavigateToURL(
shell(), GetURL("/service_worker/create_service_worker.html")));
EXPECT_EQ("DONE",
EvalJs(shell(), "register('fetch_event_respond_with_fetch.js');"));
EXPECT_TRUE(
NavigateToURL(shell(), GetURL("/service_worker/fetch_from_page.html")));
EXPECT_EQ("1", EvalJs(shell(), "fetch_from_page('/echoheader?DNT');"));
}
}
}