chromium/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc

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

#include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"

#include <map>
#include <memory>
#include <tuple>
#include <utility>

#include "base/check.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_simple_task_runner.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/content_settings/browser/page_specific_content_settings.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/infobars/content/content_infobar_manager.h"
#include "components/infobars/core/confirm_infobar_delegate.h"
#include "components/infobars/core/infobar.h"
#include "components/subresource_filter/content/browser/content_subresource_filter_web_contents_helper.h"
#include "components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h"
#include "components/subresource_filter/content/browser/profile_interaction_manager.h"
#include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
#include "components/subresource_filter/content/browser/throttle_manager_test_support.h"
#include "components/subresource_filter/content/mojom/subresource_filter.mojom.h"
#include "components/subresource_filter/content/shared/browser/child_frame_navigation_test_utils.h"
#include "components/subresource_filter/core/browser/async_document_subresource_filter.h"
#include "components/subresource_filter/core/browser/subresource_filter_features.h"
#include "components/subresource_filter/core/common/common_features.h"
#include "components/subresource_filter/core/common/constants.h"
#include "components/subresource_filter/core/common/test_ruleset_creator.h"
#include "components/subresource_filter/core/common/test_ruleset_utils.h"
#include "components/subresource_filter/core/mojom/subresource_filter.mojom.h"
#include "components/url_pattern_index/proto/rules.pb.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/navigation_throttle.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/back_forward_cache_util.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/test_utils.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "net/base/net_errors.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/features.h"
#include "url/url_constants.h"

#if BUILDFLAG(IS_ANDROID)
#include "components/messages/android/mock_message_dispatcher_bridge.h"
#include "components/subresource_filter/content/browser/ads_blocked_message_delegate.h"
#endif

namespace subresource_filter {

proto;

const char kTestURLWithActivation[] =;
const char kTestURLWithActivation2[] =;
const char kTestURLWithDryRun[] =;
const char kTestURLWithNoActivation[] =;

const char kReadyToCommitResultsInCommitHistogram[] =;
const char kReadyToCommitResultsInCommitRestrictedAdFrameNavigationHistogram[] =;

// Enum determining when the mock page state throttle notifies the throttle
// manager of page level activation state.
enum PageActivationNotificationTiming {};

class FakeSubresourceFilterAgent : public mojom::SubresourceFilterAgent {};

// Simple throttle that sends page-level activation to the manager for a
// specific set of URLs.
class MockPageStateActivationThrottle : public content::NavigationThrottle {};

class ContentSubresourceFilterThrottleManagerTest
    : public content::RenderViewHostTestHarness,
      public content::WebContentsObserver,
      public ::testing::WithParamInterface<PageActivationNotificationTiming> {};

INSTANTIATE_TEST_SUITE_P();

TEST_P(ContentSubresourceFilterThrottleManagerTest,
       ActivateMainFrameAndFilterSubframeNavigation) {}

#if BUILDFLAG(IS_ANDROID)
TEST_P(ContentSubresourceFilterThrottleManagerTest,
       NoCrashWhenMessageDelegateIsNotPresent) {
  auto* web_contents = RenderViewHostTestHarness::web_contents();
  web_contents->RemoveUserData(
      subresource_filter::AdsBlockedMessageDelegate::UserDataKey());

  // Commit a navigation that triggers page level activation.
  NavigateAndCommitMainFrame(GURL(kTestURLWithActivation));
  ExpectActivationSignalForFrame(main_rfh(), true /* expect_activation */);

  // A disallowed subframe navigation should be successfully filtered, and the
  // lack of infobar manager should not cause a crash.
  CreateSubframeWithTestNavigation(
      GURL("https://www.example.com/disallowed.html"), main_rfh());
  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
            SimulateStartAndGetResult(navigation_simulator()));

  EXPECT_TRUE(ads_blocked_in_content_settings());
}
#endif

TEST_P(ContentSubresourceFilterThrottleManagerTest, NoPageActivation) {}

TEST_P(ContentSubresourceFilterThrottleManagerTest,
       ActivateMainFrameAndDoNotFilterDryRun) {}

TEST_P(ContentSubresourceFilterThrottleManagerTest,
       ActivateMainFrameAndFilterSubframeNavigationOnRedirect) {}

TEST_P(ContentSubresourceFilterThrottleManagerTest,
       ActivateMainFrameAndDoNotFilterSubframeNavigation) {}

// This should fail if the throttle manager notifies the delegate twice of a
// disallowed load for the same page load.
TEST_P(ContentSubresourceFilterThrottleManagerTest,
       ActivateMainFrameAndFilterTwoSubframeNavigations) {}

TEST_P(ContentSubresourceFilterThrottleManagerTest,
       ActivateTwoMainFramesAndFilterTwoSubframeNavigations) {}

TEST_P(ContentSubresourceFilterThrottleManagerTest,
       DoNotFilterForInactiveFrame) {}

// Once there are no activated frames, the manager drops its ruleset handle. If
// another frame is activated, make sure the handle is regenerated.
TEST_P(ContentSubresourceFilterThrottleManagerTest, RulesetHandleRegeneration) {}

TEST_P(ContentSubresourceFilterThrottleManagerTest,
       SameSiteNavigation_RulesetGoesAway) {}

TEST_P(ContentSubresourceFilterThrottleManagerTest,
       SameSiteFailedNavigation_MaintainActivation) {}

TEST_P(ContentSubresourceFilterThrottleManagerTest,
       FailedNavigationToErrorPage_NoActivation) {}

// Ensure activation propagates into great-grandchild frames, including cross
// process ones.
TEST_P(ContentSubresourceFilterThrottleManagerTest, ActivationPropagation) {}

// Ensure activation propagates through allowlisted documents.
// crbug.com/1010000: crashes on win
#if BUILDFLAG(IS_WIN)
#define MAYBE_ActivationPropagation2
#else
#define MAYBE_ActivationPropagation2
#endif
TEST_P(ContentSubresourceFilterThrottleManagerTest,
       MAYBE_ActivationPropagation2) {}

// Same-site navigations within a single RFH do not persist activation.
TEST_P(ContentSubresourceFilterThrottleManagerTest,
       SameSiteNavigationStopsActivation) {}

TEST_P(ContentSubresourceFilterThrottleManagerTest,
       CreateHelperForWebContents) {}

// Check to make sure we don't send an IPC with the ad tag bit for ad frames
// that are successfully filtered.
TEST_P(ContentSubresourceFilterThrottleManagerTest,
       ActivateMainFrameAndFilterSubframeNavigationTaggedAsAd) {}

// If the RenderFrame determines that the frame is an ad due to creation by ad
// script, then any navigation for that frame should be considered an ad.
TEST_P(ContentSubresourceFilterThrottleManagerTest,
       SubframeNavigationTaggedAsAdByRenderer) {}

// Helper class to make sure strict site isolation is on for tests that need
// it. This is already the default on desktop platforms, so doing this is
// mainly to provide coverage on Android. Note that these tests can't just call
// IsolateAllSitesForTesting() in the test body, as the SetUp() method in the
// test harness also performs a navigation, so site isolation must be turned on
// early enough so that it can be in effect for that navigation.
class SitePerProcessContentSubresourceFilterThrottleManagerTest
    : public ContentSubresourceFilterThrottleManagerTest {};

INSTANTIATE_TEST_SUITE_P();

// If the RenderFrame determines that the frame is an ad due to creation by ad
// script, and the frame changes processes, then the frame should still be
// considered an ad.
TEST_P(SitePerProcessContentSubresourceFilterThrottleManagerTest,
       AdTagCarriesAcrossProcesses) {}

// If the RenderFrame determines that the frame was created by ad script, it
// should be tagged and then its child frames should also be tagged as ads.
TEST_P(ContentSubresourceFilterThrottleManagerTest,
       GrandchildNavigationTaggedAsAdByRenderer) {}

TEST_P(ContentSubresourceFilterThrottleManagerTest,
       DryRun_FrameTaggingDeleted) {}

TEST_P(ContentSubresourceFilterThrottleManagerTest,
       DryRun_FrameTaggingAsAdPropagatesToChildFrame) {}

TEST_P(ContentSubresourceFilterThrottleManagerTest,
       DryRun_AllowedFrameNotTaggedAsAd) {}

TEST_P(ContentSubresourceFilterThrottleManagerTest,
       FirstDisallowedLoadCalledOutOfOrder) {}

TEST_P(ContentSubresourceFilterThrottleManagerTest,
       NavigationIsReadyToCommitThenFinishes_HistogramIssued) {}

TEST_P(
    ContentSubresourceFilterThrottleManagerTest,
    RestrictedAdFrameNavigationIsReadyToCommitThenFinishes_HistogramsIssued) {}

TEST_P(ContentSubresourceFilterThrottleManagerTest,
       ReadyToCommitNavigationThenRenderFrameDeletes_MetricsNotRecorded) {}

// Basic test of throttle manager lifetime and getter methods. Ensure a new
// page creating navigation creates a new throttle manager and it's reachable
// using FromNavigationHandle until commit time. Once committed that same
// throttle manager should now be associated with the new page.
TEST_P(ContentSubresourceFilterThrottleManagerTest,
       ThrottleManagerLifetime_Basic) {}

// Ensure subframe navigations do not create a new throttle manager and
// FromNavigation gets the correct one.
TEST_P(ContentSubresourceFilterThrottleManagerTest,
       ThrottleManagerLifetime_Subframe) {}

// Same document navigations are similar to subframes: do not create a new
// throttle manager and FromNavigation gets the existing one.
// TODO(bokan): Would be good to test lifetime from some WebContentsObserver
// methods that can see the navigation handle for a same-document navigation.
// Some additional tests that would be good is to verify the behavior of the
// FromNavigationHandle/FromPage methods around the DidFinishNavigation time
// when it's transferred.
TEST_P(ContentSubresourceFilterThrottleManagerTest,
       ThrottleManagerLifetime_SameDocument) {}

class ContentSubresourceFilterThrottleManagerFencedFrameTest
    : public ContentSubresourceFilterThrottleManagerTest {};

INSTANTIATE_TEST_SUITE_P();

TEST_P(ContentSubresourceFilterThrottleManagerFencedFrameTest,
       ActivateMainFrameAndFilterFencedFrameNavigation) {}

TEST_P(ContentSubresourceFilterThrottleManagerFencedFrameTest,
       ActivateMainFrameAndFilterFencedFrameNavigationOnRedirect) {}

// Ensure activation propagates into great-grandchild fenced frames, including
// cross process ones.
TEST_P(ContentSubresourceFilterThrottleManagerFencedFrameTest,
       ActivationPropagation) {}

TEST_P(ContentSubresourceFilterThrottleManagerFencedFrameTest,
       SafeBrowsingThrottleCreation) {}

TEST_P(ContentSubresourceFilterThrottleManagerFencedFrameTest, LogActivation) {}

// Ensure fenced frame navigations do not create a new throttle manager and
// FromNavigation gets the correct one.
TEST_P(ContentSubresourceFilterThrottleManagerFencedFrameTest,
       ThrottleManagerLifetime_FencedFrame) {}

class ContentSubresourceFilterThrottleManagerInfoBarUiTest
    : public ContentSubresourceFilterThrottleManagerTest {};

INSTANTIATE_TEST_SUITE_P();

#if BUILDFLAG(IS_ANDROID)
TEST_P(ContentSubresourceFilterThrottleManagerInfoBarUiTest,
       NoCrashWhenInfoBarManagerIsNotPresent) {
  auto* web_contents = RenderViewHostTestHarness::web_contents();
  web_contents->RemoveUserData(infobars::ContentInfoBarManager::UserDataKey());

  // Commit a navigation that triggers page level activation.
  NavigateAndCommitMainFrame(GURL(kTestURLWithActivation));
  ExpectActivationSignalForFrame(main_rfh(), true /* expect_activation */);

  // A disallowed subframe navigation should be successfully filtered, and the
  // lack of infobar manager should not cause a crash.
  CreateSubframeWithTestNavigation(
      GURL("https://www.example.com/disallowed.html"), main_rfh());
  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
            SimulateStartAndGetResult(navigation_simulator()));

  EXPECT_TRUE(ads_blocked_in_content_settings());
}
#endif

// Test that once presented, the ads blocked infobar will remain present after a
// same-document navigation.
TEST_P(ContentSubresourceFilterThrottleManagerInfoBarUiTest,
       InfoBarStaysPresentAfterSameDocumentNav) {}

// This should fail if the throttle manager notifies the delegate twice of a
// disallowed load for the same page load.
TEST_P(ContentSubresourceFilterThrottleManagerInfoBarUiTest,
       ActivateMainFrameAndFilterTwoSubframeNavigations) {}

TEST_P(ContentSubresourceFilterThrottleManagerInfoBarUiTest,
       ActivateTwoMainFramesAndFilterTwoSubframeNavigations) {}

// Once there are no activated frames, the manager drops its ruleset handle. If
// another frame is activated, make sure the handle is regenerated.
TEST_P(ContentSubresourceFilterThrottleManagerInfoBarUiTest,
       RulesetHandleRegeneration) {}

// TODO(csharrison): Make sure the following conditions are exercised in tests:
//
// - Synchronous navigations to about:blank. These hit issues with the
//   NavigationSimulator currently.

}  // namespace subresource_filter