chromium/chrome/browser/browsing_topics/browsing_topics_service_browsertest.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/json/json_file_value_serializer.h"
#include "base/json/values_util.h"
#include "base/path_service.h"
#include "base/strings/strcat.h"
#include "base/task/sequenced_task_runner.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/browsing_topics/browsing_topics_service_factory.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/optimization_guide/browser_test_util.h"
#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
#include "chrome/browser/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_mixin.h"
#include "chrome/browser/privacy_sandbox/privacy_sandbox_settings_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/mixin_based_in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/browsing_topics/browsing_topics_service.h"
#include "components/browsing_topics/browsing_topics_service_impl.h"
#include "components/browsing_topics/epoch_topics.h"
#include "components/browsing_topics/test_util.h"
#include "components/content_settings/browser/page_specific_content_settings.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/optimization_guide/core/optimization_guide_features.h"
#include "components/optimization_guide/core/test_model_info_builder.h"
#include "components/optimization_guide/core/test_optimization_guide_model_provider.h"
#include "components/optimization_guide/proto/page_topics_model_metadata.pb.h"
#include "components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations.h"
#include "components/privacy_sandbox/privacy_sandbox_features.h"
#include "components/privacy_sandbox/privacy_sandbox_settings.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/browser/browsing_topics_site_data_manager.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_features.h"
#include "content/public/test/back_forward_cache_util.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browsing_topics_test_util.h"
#include "content/public/test/download_test_observer.h"
#include "content/public/test/fenced_frame_test_util.h"
#include "content/public/test/prerender_test_util.h"
#include "content/public/test/test_navigation_observer.h"
#include "net/base/schemeful_site.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/request_handler_util.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom.h"

namespace browsing_topics {

namespace {

constexpr browsing_topics::HmacKey kTestKey =;

constexpr base::Time kTime1 =;
constexpr base::Time kTime2 =;

constexpr int kConfigVersion =;
constexpr int kTaxonomyVersion =;
constexpr int64_t kModelVersion =;
constexpr size_t kPaddedTopTopicsStartIndex =;
constexpr Topic kExpectedTopic1 =;
constexpr Topic kExpectedTopic2 =;

constexpr char kExpectedApiResult[] =;

constexpr char kExpectedHeaderValueForEmptyTopics[] =;

constexpr char kExpectedHeaderValueForSiteA[] =;

constexpr char kExpectedHeaderValueForSiteB[] =;

static constexpr char kBrowsingTopicsApiActionTypeHistogramId[] =;

static constexpr char kRedirectCountHistogramId[] =;

static constexpr char kRedirectWithTopicsInvokedCountHistogramId[] =;

EpochTopics CreateTestEpochTopics(
    const std::vector<std::pair<Topic, std::set<HashedDomain>>>& topics,
    base::Time calculation_time) {}

}  // namespace

// A tester class that allows waiting for the first calculation to finish.
class TesterBrowsingTopicsService : public BrowsingTopicsServiceImpl {};

class BrowsingTopicsBrowserTestBase : public MixinBasedInProcessBrowserTest {};

class BrowsingTopicsDisabledBrowserTest : public BrowsingTopicsBrowserTestBase {};

IN_PROC_BROWSER_TEST_F(BrowsingTopicsDisabledBrowserTest,
                       NoBrowsingTopicsService) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsDisabledBrowserTest, NoTopicsAPI) {}

// Enables the feature flags for BrowsingTopics but does not override the
// Annotator to a mocked instance.
class BrowsingTopicsAnnotationGoldenDataBrowserTest
    : public BrowsingTopicsBrowserTestBase {};

// Running a TFLite model in a test is expensive so it can only be done in a
// browser test without any page loads.
IN_PROC_BROWSER_TEST_F(BrowsingTopicsAnnotationGoldenDataBrowserTest,
                       GoldenData) {}

class BrowsingTopicsBrowserTest : public BrowsingTopicsBrowserTestBase {};

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, HasBrowsingTopicsService) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, NoServiceInIncognitoMode) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, BrowsingTopicsStateOnStart) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, ApiResultUkm) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, PageLoadUkm) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, GetTopTopicsForDisplay) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       TopicsAPI_ContextDomainNotFiltered_FromMainFrame) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       TopicsAPI_ContextDomainNotFiltered_FromSubframe) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       TopicsAPI_ContextDomainFiltered) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, TopicsAPI_ObserveBehavior) {}

IN_PROC_BROWSER_TEST_F(
    BrowsingTopicsBrowserTest,
    EmptyPage_PermissionsPolicyBrowsingTopicsNone_TopicsAPI) {}

IN_PROC_BROWSER_TEST_F(
    BrowsingTopicsBrowserTest,
    EmptyPage_PermissionsPolicyInterestCohortNone_TopicsAPI) {}

IN_PROC_BROWSER_TEST_F(
    BrowsingTopicsBrowserTest,
    OneIframePage_SubframePermissionsPolicyBrowsingTopicsNone_TopicsAPI) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       PermissionsPolicyAllowCertainOrigin_TopicsAPI) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       TopicsAPINotAllowedInInsecureContext) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       TopicsAPINotAllowedInDetachedDocument) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       TopicsAPINotAllowedInOpaqueOriginDocument) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       TopicsAPINotAllowedInFencedFrame) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       TopicsAPINotAllowedInPrerenderedPage) {}

// Regression test for crbug/1339735.
IN_PROC_BROWSER_TEST_F(
    BrowsingTopicsBrowserTest,
    TopicsAPIInvokedInMainFrameUnloadHandler_NoRendererCrash) {}

IN_PROC_BROWSER_TEST_F(
    BrowsingTopicsBrowserTest,
    FetchSameOrigin_TopicsEligible_SendTopics_HasNoObserveResponse) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, FetchWithoutTopicsFlagSet) {}

IN_PROC_BROWSER_TEST_F(
    BrowsingTopicsBrowserTest,
    FetchSameOrigin_TopicsEligible_SendNoTopic_HasNoObserveResponse) {}

IN_PROC_BROWSER_TEST_F(
    BrowsingTopicsBrowserTest,
    FetchSameOrigin_TopicsEligible_SendNoTopic_HasObserveResponse) {}

IN_PROC_BROWSER_TEST_F(
    BrowsingTopicsBrowserTest,
    FetchSameOrigin_TopicsNotEligibleDueToUserSettings_HasObserveResponse) {}

IN_PROC_BROWSER_TEST_F(
    BrowsingTopicsBrowserTest,
    FetchCrossOrigin_TopicsEligible_SendTopics_HasObserveResponse) {}

// On an insecure site (i.e. URL with http scheme), test fetch request with
// the `browsingTopics` set to true. Expect it to throw an exception.
IN_PROC_BROWSER_TEST_F(
    BrowsingTopicsBrowserTest,
    FetchCrossOrigin_TopicsNotEligibleDueToInsecureInitiatorContext) {}

// Only allow topics from origin c.test, and test fetch requests to b.test and
// c.test to verify that only c.test gets them.
IN_PROC_BROWSER_TEST_F(
    BrowsingTopicsBrowserTest,
    FetchCrossOrigin_TopicsNotEligibleDueToPermissionsPolicyAgainstRequestOrigin) {}

// On site b.test, test fetch request to a.test that gets redirected to c.test.
// The topics header should be calculated for them individually (i.e. given that
// only a.test has observed the candidate topics for site b.test, the request to
// a.test should have a non-empty topics header, while the redirected request to
// c.test should have an empty topics header.)
IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       FetchCrossOriginWithRedirect) {}

// On site b.test, test fetch request to a.test that gets redirected to c.test.
// The topics header eligibility should be checked for them individually (i.e.
// given that the declared policy on the page only allows origin c.test, the
// request to a.test should not have the topics header, while the redirected
// request to c.test should have the topics header.)
IN_PROC_BROWSER_TEST_F(
    BrowsingTopicsBrowserTest,
    FetchCrossOriginWithRedirect_InitialRequestTopicsNotEligibleDueToPermissionsPolicy) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, UseCounter_DocumentApi) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, UseCounter_Fetch) {}

// For a page that contains a static <iframe> with a "browsingtopics"
// attribute, the iframe navigation request should be eligible for topics.
IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       CrossOriginStaticIframeWithTopicsAttribute) {}

// For a page that contains a static <iframe> without a "browsingtopics"
// attribute, the iframe navigation request should not be eligible for topics.
IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       CrossOriginStaticIframeWithoutTopicsAttribute) {}

// For a page with a dynamically appended iframe with iframe.browsingTopics set
// to true, the iframe navigation request should be eligible for topics.
IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       CrossOriginDynamicIframeWithTopicsAttribute) {}

// For a page with a dynamically appended iframe with iframe.browsingTopics set
// to true, the iframe navigation request should not be eligible for topics.
IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       CrossOriginDynamicIframeWithoutTopicsAttribute) {}

IN_PROC_BROWSER_TEST_F(
    BrowsingTopicsBrowserTest,
    CrossOriginDynamicIframe_TopicsNotEligibleDueToUserSettings_HasObserveResponse) {}

// Only allow topics from origin c.test, and test <iframe browsingtopics>
// requests to b.test and c.test to verify that only c.test gets the header.
IN_PROC_BROWSER_TEST_F(
    BrowsingTopicsBrowserTest,
    CrossOriginIframe_TopicsNotEligibleDueToPermissionsPolicyAgainstRequestOrigin) {}

// On site b.test, test <iframe browsingtopics> request to a.test that gets
// redirected to c.test. The topics header should be calculated for them
// individually (i.e. given that only a.test has observed the candidate topics
// for site b.test, the request to a.test should have a non-empty topics header,
// while the redirected request to c.test should have an empty topics header.)
IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       CrossOriginIframeWithRedirect) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest, RedirectMetrics_NoRedirect) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       RedirectMetrics_OneRedirectWithoutTopicsInvoked) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       RedirectMetrics_OneRedirectWithTopicsInvoked) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       RedirectMetrics_HasGesture_RedirectTrackingReset) {}

IN_PROC_BROWSER_TEST_F(
    BrowsingTopicsBrowserTest,
    RedirectMetrics_BrowserInitiatedNavigation_RedirectTrackingReset) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       RedirectMetrics_PopUp_RedirectTrackingReset) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       RedirectMetrics_PopUpAndOpenerNavigation) {}

IN_PROC_BROWSER_TEST_F(
    BrowsingTopicsBrowserTest,
    RedirectMetrics_SameDocNavigation_RedirectStateUnaffected) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       RedirectMetrics_TenRedirectsWithTopicsInvoked) {}

IN_PROC_BROWSER_TEST_F(BrowsingTopicsBrowserTest,
                       Download_RedirectStateUnaffected) {}

// Tests that the Topics API abides by the Privacy Sandbox Enrollment framework.
class AttestationBrowsingTopicsBrowserTest : public BrowsingTopicsBrowserTest {};

// Site a.test is attested for Topics, so it should receive a valid response.
IN_PROC_BROWSER_TEST_F(AttestationBrowsingTopicsBrowserTest,
                       AttestedSiteCanGetBrowsingTopicsViaDocumentAPI) {}

// Site a.test is not attested for Topics, so it should receive no topics. Note:
// Attestation failure works differently from other failure modes like operating
// in an insecure context. In this case, the API is still exposed, but handling
// will exit before any topics are filled.
IN_PROC_BROWSER_TEST_F(AttestationBrowsingTopicsBrowserTest,
                       UnattestedSiteCannotGetBrowsingTopicsViaDocumentAPI) {}

// Site a.test is attested, but not for Topics, so no topics should be returned.
IN_PROC_BROWSER_TEST_F(
    AttestationBrowsingTopicsBrowserTest,
    AttestedSiteCannotGetBrowsingTopicsViaDocumentAPIWithMismatchedMap) {}

IN_PROC_BROWSER_TEST_F(AttestationBrowsingTopicsBrowserTest,
                       FetchSameOrigin_TopicsEligible_SendTopics_SiteAttested) {}

IN_PROC_BROWSER_TEST_F(AttestationBrowsingTopicsBrowserTest,
                       FetchSameOrigin_TopicsEligible_SiteNotAttested) {}

IN_PROC_BROWSER_TEST_F(
    AttestationBrowsingTopicsBrowserTest,
    FetchSameOrigin_TopicsEligible_SiteAttested_MismatchedMap) {}

// Site a.test is attested, so when an x-origin request is made to it from
// site b.test, a.test should still include a topics header.
IN_PROC_BROWSER_TEST_F(
    AttestationBrowsingTopicsBrowserTest,
    FetchCrossOrigin_TopicsEligible_SendTopics_HasObserveResponse_SiteAttested) {}

// Site a.test is not attested, so this should not generate a Topics header in a
// x-origin fetch to site a.test.
IN_PROC_BROWSER_TEST_F(AttestationBrowsingTopicsBrowserTest,
                       FetchCrossOrigin_TopicsEligible_SiteNotAttested) {}

// Site a.test is attested, but not for Topics, so the fetch request to a.test
// should not get a header.
IN_PROC_BROWSER_TEST_F(
    AttestationBrowsingTopicsBrowserTest,
    FetchCrossOrigin_TopicsEligible_SiteNotAttested_MismatchedMap) {}

}  // namespace browsing_topics