From fea0b189a81fcc7e090a41b546fcbe1abde144e6 Mon Sep 17 00:00:00 2001 From: Aleksey Seren Date: Tue, 20 Sep 2022 16:43:51 -0500 Subject: [PATCH] Add Brave Ads status header to search.brave.com calls. fix https://github.com/brave/brave-browser/issues/25430 --- browser/brave_content_browser_client.cc | 13 ++- .../brave_search/brave_search_browsertest.cc | 37 +++++++ components/brave_ads/browser/BUILD.gn | 3 + components/brave_ads/browser/DEPS | 1 + .../browser/ads_status_header_throttle.cc | 51 ++++++++++ .../browser/ads_status_header_throttle.h | 42 ++++++++ .../ads_status_header_throttle_unittest.cc | 97 +++++++++++++++++++ test/BUILD.gn | 1 + 8 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 components/brave_ads/browser/ads_status_header_throttle.cc create mode 100644 components/brave_ads/browser/ads_status_header_throttle.h create mode 100644 components/brave_ads/browser/ads_status_header_throttle_unittest.cc diff --git a/browser/brave_content_browser_client.cc b/browser/brave_content_browser_client.cc index d6e5d6fcd747..a3aae3597554 100644 --- a/browser/brave_content_browser_client.cc +++ b/browser/brave_content_browser_client.cc @@ -14,6 +14,7 @@ #include "base/json/json_reader.h" #include "base/strings/strcat.h" #include "base/system/sys_info.h" +#include "brave/browser/brave_ads/ads_service_factory.h" #include "brave/browser/brave_ads/brave_ads_host.h" #include "brave/browser/brave_browser_main_extra_parts.h" #include "brave/browser/brave_browser_process.h" @@ -34,6 +35,7 @@ #include "brave/browser/profiles/profile_util.h" #include "brave/browser/skus/skus_service_factory.h" #include "brave/components/binance/browser/buildflags/buildflags.h" +#include "brave/components/brave_ads/browser/ads_status_header_throttle.h" #include "brave/components/brave_ads/common/features.h" #include "brave/components/brave_federated/features.h" #include "brave/components/brave_rewards/browser/rewards_protocol_handler.h" @@ -770,12 +772,21 @@ BraveContentBrowserClient::CreateURLLoaderThrottles( } #endif // ENABLE_SPEEDREADER - // De-AMP if (isMainFrame) { + // De-AMP if (auto de_amp_throttle = de_amp::DeAmpThrottle::MaybeCreateThrottleFor( base::ThreadTaskRunnerHandle::Get(), request, wc_getter)) { result.push_back(std::move(de_amp_throttle)); } + + brave_ads::AdsService* ads_service = + brave_ads::AdsServiceFactory::GetForProfile( + Profile::FromBrowserContext(browser_context)); + if (auto ads_status_header_throttle = + brave_ads::AdsStatusHeaderThrottle::MaybeCreateThrottle( + ads_service, request)) { + result.push_back(std::move(ads_status_header_throttle)); + } } } diff --git a/browser/brave_search/brave_search_browsertest.cc b/browser/brave_search/brave_search_browsertest.cc index 0602522817fa..f87bae3ea415 100644 --- a/browser/brave_search/brave_search_browsertest.cc +++ b/browser/brave_search/brave_search_browsertest.cc @@ -5,10 +5,13 @@ #include +#include "base/callback.h" +#include "base/containers/contains.h" #include "base/path_service.h" #include "base/strings/stringprintf.h" #include "base/test/scoped_feature_list.h" #include "base/test/thread_test_helper.h" +#include "bat/ads/pref_names.h" #include "brave/components/brave_search/browser/brave_search_fallback_host.h" #include "brave/components/brave_search/common/features.h" #include "brave/components/constants/brave_paths.h" @@ -20,6 +23,7 @@ #include "chrome/test/base/search_test_utils.h" #include "chrome/test/base/ui_test_utils.h" #include "components/network_session_configurator/common/network_switches.h" +#include "components/prefs/pref_service.h" #include "components/search_engines/template_url_service.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/test/browser_test.h" @@ -30,6 +34,8 @@ #include "net/test/embedded_test_server/http_response.h" using extensions::ExtensionBrowserTest; +using RequestExpectationsCallback = + base::RepeatingCallback; namespace { @@ -37,6 +43,8 @@ const char kEmbeddedTestServerDirectory[] = "brave-search"; const char kAllowedDomain[] = "search.brave.com"; const char kAllowedDomainDev[] = "search-dev.brave.com"; const char kNotAllowedDomain[] = "brave.com"; +const char kAdsStatusHeaderName[] = "X-Brave-Ads-Enabled"; +const char kAdsStatusHeaderValue[] = "1"; const char kBackupSearchContent[] = "results"; const char kScriptDefaultAPIExists[] = "window.domAutomationController.send(" @@ -119,6 +127,10 @@ class BraveSearchTest : public InProcessBrowserTest { std::unique_ptr HandleRequest( const net::test_server::HttpRequest& request) { + if (request_expectations_callback_) { + request_expectations_callback_.Run(request); + } + GURL url = request.GetURL(); auto path = url.path_piece(); @@ -141,10 +153,15 @@ class BraveSearchTest : public InProcessBrowserTest { net::EmbeddedTestServer* https_server() { return https_server_.get(); } + void SetRequestExpectationsCallback(RequestExpectationsCallback callback) { + request_expectations_callback_ = std::move(callback); + } + protected: base::test::ScopedFeatureList feature_list_; private: + RequestExpectationsCallback request_expectations_callback_; content::ContentMockCertVerifier mock_cert_verifier_; std::unique_ptr https_server_; }; @@ -301,3 +318,23 @@ IN_PROC_BROWSER_TEST_F(BraveSearchTestDisabled, DefaultAPIInvisibleKnownHost) { ExecuteScriptAndExtractBool(contents, kScriptDefaultAPIExists, &has_api)); EXPECT_FALSE(has_api); } + +IN_PROC_BROWSER_TEST_F(BraveSearchTest, AdsStatusHeader) { + SetRequestExpectationsCallback( + base::BindRepeating([](const net::test_server::HttpRequest& request) { + const GURL url = request.GetURL(); + if (url.path_piece() == "/bravesearch.html") { + EXPECT_TRUE(base::Contains(request.headers, kAdsStatusHeaderName)); + EXPECT_EQ(kAdsStatusHeaderValue, + request.headers.at(kAdsStatusHeaderName)); + } else { + EXPECT_FALSE(base::Contains(request.headers, kAdsStatusHeaderName)); + } + })); + + PrefService* prefs = browser()->profile()->GetPrefs(); + prefs->SetBoolean(ads::prefs::kEnabled, true); + + GURL url = https_server()->GetURL(kAllowedDomain, "/bravesearch.html"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); +} diff --git a/components/brave_ads/browser/BUILD.gn b/components/brave_ads/browser/BUILD.gn index 375dade9f3da..76c21a86bdae 100644 --- a/components/brave_ads/browser/BUILD.gn +++ b/components/brave_ads/browser/BUILD.gn @@ -8,6 +8,8 @@ source_set("browser") { "ads_service.cc", "ads_service.h", "ads_service_observer.h", + "ads_status_header_throttle.cc", + "ads_status_header_throttle.h", "ads_storage_cleanup.cc", "ads_storage_cleanup.h", "component_updater/component_info.cc", @@ -36,6 +38,7 @@ source_set("browser") { "//components/prefs", "//components/sessions", "//sql", + "//third_party/blink/public/common", "//url", ] diff --git a/components/brave_ads/browser/DEPS b/components/brave_ads/browser/DEPS index 5b9a492359d0..d519a0e75803 100644 --- a/components/brave_ads/browser/DEPS +++ b/components/brave_ads/browser/DEPS @@ -6,6 +6,7 @@ include_rules = [ "+brave/components/constants", "+content/public/browser", "+services/network/public", + "+third_party/blink/public", "+ui/base", "+ui/message_center/public", ] diff --git a/components/brave_ads/browser/ads_status_header_throttle.cc b/components/brave_ads/browser/ads_status_header_throttle.cc new file mode 100644 index 000000000000..29a3e7d2be7a --- /dev/null +++ b/components/brave_ads/browser/ads_status_header_throttle.cc @@ -0,0 +1,51 @@ +/* Copyright 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "brave/components/brave_ads/browser/ads_status_header_throttle.h" + +#include "brave/components/brave_ads/browser/ads_service.h" +#include "brave/components/brave_search/common/brave_search_utils.h" +#include "services/network/public/cpp/resource_request.h" +#include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h" + +namespace { + +constexpr char kAdsStatusHeader[] = "X-Brave-Ads-Enabled"; +constexpr char kAdsEnabledStatusValue[] = "1"; + +} // namespace + +namespace brave_ads { + +// static +std::unique_ptr +AdsStatusHeaderThrottle::MaybeCreateThrottle( + const AdsService* ads_service, + const network::ResourceRequest& request) { + DCHECK_EQ(request.resource_type, + static_cast(blink::mojom::ResourceType::kMainFrame)); + if (!ads_service || !ads_service->IsEnabled() || + !request.is_outermost_main_frame || + !brave_search::IsAllowedHost(request.url)) { + return nullptr; + } + + return std::make_unique(); +} + +AdsStatusHeaderThrottle::AdsStatusHeaderThrottle() = default; + +AdsStatusHeaderThrottle::~AdsStatusHeaderThrottle() = default; + +void AdsStatusHeaderThrottle::WillStartRequest( + network::ResourceRequest* request, + bool* /* defer */) { + DCHECK(request); + DCHECK(brave_search::IsAllowedHost(request->url)); + + request->headers.SetHeader(kAdsStatusHeader, kAdsEnabledStatusValue); +} + +} // namespace brave_ads diff --git a/components/brave_ads/browser/ads_status_header_throttle.h b/components/brave_ads/browser/ads_status_header_throttle.h new file mode 100644 index 000000000000..e033923ee349 --- /dev/null +++ b/components/brave_ads/browser/ads_status_header_throttle.h @@ -0,0 +1,42 @@ +/* Copyright 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_BRAVE_ADS_BROWSER_ADS_STATUS_HEADER_THROTTLE_H_ +#define BRAVE_COMPONENTS_BRAVE_ADS_BROWSER_ADS_STATUS_HEADER_THROTTLE_H_ + +#include + +#include "third_party/blink/public/common/loader/url_loader_throttle.h" + +namespace network { +struct ResourceRequest; +} // namespace network + +namespace brave_ads { + +class AdsService; + +class AdsStatusHeaderThrottle : public blink::URLLoaderThrottle { + public: + static std::unique_ptr MaybeCreateThrottle( + const AdsService* ads_service, + const network::ResourceRequest& request); + + AdsStatusHeaderThrottle(); + ~AdsStatusHeaderThrottle() override; + + AdsStatusHeaderThrottle(const AdsStatusHeaderThrottle&) = delete; + AdsStatusHeaderThrottle& operator=(const AdsStatusHeaderThrottle&) = delete; + AdsStatusHeaderThrottle(AdsStatusHeaderThrottle&&) = delete; + AdsStatusHeaderThrottle& operator=(AdsStatusHeaderThrottle&&) = delete; + + // Implements blink::URLLoaderThrottle: + void WillStartRequest(network::ResourceRequest* request, + bool* defer) override; +}; + +} // namespace brave_ads + +#endif // BRAVE_COMPONENTS_BRAVE_ADS_BROWSER_ADS_STATUS_HEADER_THROTTLE_H_ diff --git a/components/brave_ads/browser/ads_status_header_throttle_unittest.cc b/components/brave_ads/browser/ads_status_header_throttle_unittest.cc new file mode 100644 index 000000000000..287d88bbc1f9 --- /dev/null +++ b/components/brave_ads/browser/ads_status_header_throttle_unittest.cc @@ -0,0 +1,97 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +#include "brave/components/brave_ads/browser/ads_service.h" +#include "brave/components/brave_ads/browser/ads_status_header_throttle.h" +#include "brave/components/brave_ads/browser/mock_ads_service.h" +#include "services/network/public/cpp/resource_request.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h" + +using ::testing::Return; + +namespace { + +constexpr char kAdsStatusHeader[] = "X-Brave-Ads-Enabled"; +constexpr char kAdsEnabledStatusValue[] = "1"; +constexpr char kAllowedURL[] = "https://search.brave.com/search"; +constexpr char kNotAllowedURL[] = "https://brave.com/search"; +constexpr char kTestingHeaderName[] = "TestingHeaderName"; +constexpr char kTestingHeaderValue[] = "TestingHeaderValue"; + +} // namespace + +namespace brave_ads { + +class AdsStatusHeaderThrottleTest : public ::testing::Test { + public: + void SetUp() override { + ON_CALL(mock_ads_service_, IsEnabled()).WillByDefault(Return(true)); + } + + const AdsService* GetAdsService() const { return &mock_ads_service_; } + + network::ResourceRequest BuildRequest() { + network::ResourceRequest request; + request.url = GURL(kAllowedURL); + request.is_outermost_main_frame = true; + request.headers.SetHeader("TestingHeaderName", "TestingHeaderValue"); + return request; + } + + private: + MockAdsService mock_ads_service_; +}; + +TEST_F(AdsStatusHeaderThrottleTest, AdsEnabledForAllowedHost) { + network::ResourceRequest request = BuildRequest(); + auto throttle = + AdsStatusHeaderThrottle::MaybeCreateThrottle(GetAdsService(), request); + ASSERT_TRUE(throttle); + bool defer = false; + throttle->WillStartRequest(&request, &defer); + EXPECT_FALSE(defer); + std::string value; + EXPECT_TRUE(request.headers.GetHeader(kAdsStatusHeader, &value)); + EXPECT_EQ(kAdsEnabledStatusValue, value); + EXPECT_TRUE(request.headers.GetHeader(kTestingHeaderName, &value)); + EXPECT_EQ(kTestingHeaderValue, value); +} + +TEST_F(AdsStatusHeaderThrottleTest, AdsDisabledForAllowedHost) { + network::ResourceRequest request = BuildRequest(); + MockAdsService ads_service; + EXPECT_CALL(ads_service, IsEnabled()).WillOnce(Return(false)); + auto throttle = + AdsStatusHeaderThrottle::MaybeCreateThrottle(&ads_service, request); + EXPECT_FALSE(throttle); +} + +TEST_F(AdsStatusHeaderThrottleTest, IncognitoModeForAllowedHost) { + network::ResourceRequest request = BuildRequest(); + auto throttle = + AdsStatusHeaderThrottle::MaybeCreateThrottle(nullptr, request); + EXPECT_FALSE(throttle); +} + +TEST_F(AdsStatusHeaderThrottleTest, AdsEnabledForNotAllowedHost) { + network::ResourceRequest request = BuildRequest(); + request.url = GURL(kNotAllowedURL); + auto throttle = + AdsStatusHeaderThrottle::MaybeCreateThrottle(GetAdsService(), request); + EXPECT_FALSE(throttle); +} + +TEST_F(AdsStatusHeaderThrottleTest, NonOutermostMainFrameNavigation) { + network::ResourceRequest request = BuildRequest(); + request.is_outermost_main_frame = false; + auto throttle = + AdsStatusHeaderThrottle::MaybeCreateThrottle(GetAdsService(), request); + EXPECT_FALSE(throttle); +} + +} // namespace brave_ads diff --git a/test/BUILD.gn b/test/BUILD.gn index d5183bfc3223..82a70e73d62f 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -110,6 +110,7 @@ test("brave_unit_tests") { "//brave/chromium_src/services/network/public/cpp/cors/cors_unittest.cc", "//brave/common/brave_content_client_unittest.cc", "//brave/components/assist_ranker/ranker_model_loader_impl_unittest.cc", + "//brave/components/brave_ads/browser/ads_status_header_throttle_unittest.cc", "//brave/components/brave_ads/common/search_result_ad_util_unittest.cc", "//brave/components/brave_ads/content/browser/search_result_ad/search_result_ad_parsing_unittest.cc", "//brave/components/brave_perf_predictor/browser/bandwidth_linreg_unittest.cc",