910e62b5创建于 1月15日历史提交
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/content_settings/generated_javascript_optimizer_pref.h"

#include "base/run_loop.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/download/download_item_warning_data.h"
#include "chrome/browser/extensions/api/settings_private/generated_pref.h"
#include "chrome/common/extensions/api/settings_private.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/content_settings_registry.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/content_settings/core/common/features.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace settings_private_api = extensions::api::settings_private;
using extensions::settings_private::GeneratedPref;
using extensions::settings_private::SetPrefResult;
using settings_private_api::PrefObject;

namespace content_settings {
namespace {

int GetPrefValue(const PrefObject& pref_object) {
  if (pref_object.type != settings_private_api::PrefType::kNumber ||
      !pref_object.value->is_int()) {
    return -1;
  }
  return pref_object.value->GetInt();
}

int GetGeneratedPrefValue(Profile* profile) {
  PrefObject pref_object =
      GeneratedJavascriptOptimizerPref(profile).GetPrefObject();
  return GetPrefValue(pref_object);
}

extensions::settings_private::SetPrefResult SetPref(
    GeneratedJavascriptOptimizerPref& pref,
    const base::Value& value) {
  return pref.SetPref(&value);
}

// GeneratedPref::Observer which exposes method for waiting till generated pref
// has changed.
class TestObserver : public GeneratedPref::Observer {
 public:
  TestObserver() {
    base::RunLoop run_loop;
    quit_closure_ = run_loop.QuitClosure();
  }

  ~TestObserver() override = default;

  void WaitForGeneratedPrefChange() {
    if (was_pref_changed_) {
      return;
    }

    base::RunLoop run_loop;
    quit_closure_ = run_loop.QuitClosure();
    run_loop.Run();
  }

  void OnGeneratedPrefChanged(const std::string&) override {
    was_pref_changed_ = true;
    if (quit_closure_) {
      std::move(quit_closure_).Run();
    }
  }

 private:
  bool was_pref_changed_ = false;
  base::OnceClosure quit_closure_;
};

}  // anonymous namespace

class GeneratedJavascriptOptimizerPrefTest : public testing::Test {
 public:
  GeneratedJavascriptOptimizerPrefTest() = default;

  void SetUp() override {
    testing::Test::SetUp();
    profile_ = std::make_unique<TestingProfile>();
    host_content_settings_map_ =
        HostContentSettingsMapFactory::GetForProfile(profile_.get());
  }

  TestingProfile* profile() { return profile_.get(); }
  PrefService* prefs() { return profile()->GetPrefs(); }

  HostContentSettingsMap* host_content_settings_map() {
    return host_content_settings_map_.get();
  }

 private:
  content::BrowserTaskEnvironment task_environment_;
  std::unique_ptr<TestingProfile> profile_;
  scoped_refptr<HostContentSettingsMap> host_content_settings_map_;
};

void EnableFeature(base::test::ScopedFeatureList* feature_list) {
  feature_list
      ->InitWithFeatures(/*enabled_features=*/
                         {content_settings::features::
                              kBlockV8OptimizerOnUnfamiliarSitesSetting,
                          ::features::kProcessSelectionDeferringConditions},
                         /*disabled_features=*/{});
}

TEST_F(GeneratedJavascriptOptimizerPrefTest, GetPrefObject_FeatureEnabled) {
  base::test::ScopedFeatureList scoped_feature_list;
  EnableFeature(&scoped_feature_list);

  const struct TestCase {
    ContentSetting content_setting;
    bool pref_blocked_for_unfamiliar_sites;
    JavascriptOptimizerSetting expected_setting;
  } kTestCases[] = {
      {ContentSetting::CONTENT_SETTING_ALLOW, false,
       JavascriptOptimizerSetting::kAllowed},
      {ContentSetting::CONTENT_SETTING_ALLOW, true,
       JavascriptOptimizerSetting::kBlockedForUnfamiliarSites},
      {ContentSetting::CONTENT_SETTING_BLOCK, false,
       JavascriptOptimizerSetting::kBlocked},
      {ContentSetting::CONTENT_SETTING_BLOCK, true,
       JavascriptOptimizerSetting::kBlocked},
  };

  for (const auto& test_case : kTestCases) {
    host_content_settings_map()->SetDefaultContentSetting(
        ContentSettingsType::JAVASCRIPT_OPTIMIZER, test_case.content_setting);
    prefs()->SetBoolean(prefs::kJavascriptOptimizerBlockedForUnfamiliarSites,
                        test_case.pref_blocked_for_unfamiliar_sites);
    EXPECT_EQ(static_cast<int>(test_case.expected_setting),
              GetGeneratedPrefValue(profile()));
  }
}

TEST_F(GeneratedJavascriptOptimizerPrefTest, GetPrefObject_FeatureDisabled) {
  host_content_settings_map()->SetDefaultContentSetting(
      ContentSettingsType::JAVASCRIPT_OPTIMIZER,
      ContentSetting::CONTENT_SETTING_ALLOW);
  prefs()->SetBoolean(prefs::kJavascriptOptimizerBlockedForUnfamiliarSites,
                      true);

  std::vector<base::test::FeatureRef> test_cases = {
      content_settings::features::kBlockV8OptimizerOnUnfamiliarSitesSetting,
      ::features::kProcessSelectionDeferringConditions};
  for (const base::test::FeatureRef& feature : test_cases) {
    base::test::ScopedFeatureList scoped_feature_list;
    scoped_feature_list.InitAndDisableFeature(*feature);
    EXPECT_EQ(static_cast<int>(JavascriptOptimizerSetting::kAllowed),
              GetGeneratedPrefValue(profile()));
  }
}

// Test potential future scenario where
// kJavascriptOptimizerBlockedForUnfamiliarSites is updated by
// non generated-pref code.
TEST_F(GeneratedJavascriptOptimizerPrefTest,
       GetPrefObject_PrefChangedExternally) {
  base::test::ScopedFeatureList scoped_feature_list;
  EnableFeature(&scoped_feature_list);

  prefs()->SetBoolean(prefs::kJavascriptOptimizerBlockedForUnfamiliarSites,
                      false);
  EXPECT_EQ(static_cast<int>(JavascriptOptimizerSetting::kAllowed),
            GetGeneratedPrefValue(profile()));

  GeneratedJavascriptOptimizerPref pref(profile());
  TestObserver observer;
  pref.AddObserver(&observer);

  prefs()->SetBoolean(prefs::kJavascriptOptimizerBlockedForUnfamiliarSites,
                      true);
  observer.WaitForGeneratedPrefChange();
  EXPECT_EQ(
      static_cast<int>(JavascriptOptimizerSetting::kBlockedForUnfamiliarSites),
      GetGeneratedPrefValue(profile()));
}

TEST_F(GeneratedJavascriptOptimizerPrefTest, GetPrefObject_SafeBrowsingOff) {
  profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, false);

  PrefObject pref_object =
      GeneratedJavascriptOptimizerPref(profile()).GetPrefObject();
  EXPECT_EQ(static_cast<int>(JavascriptOptimizerSetting::kAllowed),
            GetPrefValue(pref_object));
  EXPECT_EQ(settings_private_api::Enforcement::kEnforced,
            pref_object.enforcement);
  EXPECT_EQ(settings_private_api::ControlledBy::kSafeBrowsingOff,
            pref_object.controlled_by);
}

TEST_F(GeneratedJavascriptOptimizerPrefTest,
       GetPrefObject_DisableForUnfamiliar_ThenSafeBrowsingOff) {
  base::test::ScopedFeatureList scoped_feature_list;
  EnableFeature(&scoped_feature_list);
  prefs()->SetBoolean(prefs::kJavascriptOptimizerBlockedForUnfamiliarSites,
                      true);
  EXPECT_EQ(
      static_cast<int>(JavascriptOptimizerSetting::kBlockedForUnfamiliarSites),
      GetGeneratedPrefValue(profile()));

  // Disable safe browsing after user has disabled v8-optimizers for unfamiliar
  // sites.
  profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, false);

  PrefObject pref_object =
      GeneratedJavascriptOptimizerPref(profile()).GetPrefObject();
  EXPECT_EQ(static_cast<int>(JavascriptOptimizerSetting::kAllowed),
            GetPrefValue(pref_object));
  EXPECT_EQ(settings_private_api::Enforcement::kEnforced,
            pref_object.enforcement);
  EXPECT_EQ(settings_private_api::ControlledBy::kSafeBrowsingOff,
            pref_object.controlled_by);
}

TEST_F(GeneratedJavascriptOptimizerPrefTest, GetPrefObject_SafeBrowsingOn) {
  profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, true);
  PrefObject pref_object =
      GeneratedJavascriptOptimizerPref(profile()).GetPrefObject();
  EXPECT_EQ(settings_private_api::Enforcement::kNone, pref_object.enforcement);
}

TEST_F(GeneratedJavascriptOptimizerPrefTest, GetPrefObject_Policy) {
  ContentSettingsRegistry::GetInstance();
  profile()->GetTestingPrefService()->SetManagedPref(
      prefs::kManagedDefaultJavaScriptOptimizerSetting,
      base::Value(ContentSetting::CONTENT_SETTING_BLOCK));

  PrefObject pref_object =
      GeneratedJavascriptOptimizerPref(profile()).GetPrefObject();
  EXPECT_EQ(settings_private_api::Enforcement::kEnforced,
            pref_object.enforcement);
  EXPECT_EQ(settings_private_api::ControlledBy::kDevicePolicy,
            pref_object.controlled_by);
}

TEST_F(GeneratedJavascriptOptimizerPrefTest,
       GetPrefObject_PolicyDisablesFeature) {
  base::test::ScopedFeatureList scoped_feature_list;
  EnableFeature(&scoped_feature_list);
  // Could be set by the user via chrome://settings prior to policy being set by
  // administrator.
  prefs()->SetBoolean(prefs::kJavascriptOptimizerBlockedForUnfamiliarSites,
                      true);

  ContentSettingsRegistry::GetInstance();
  profile()->GetTestingPrefService()->SetManagedPref(
      prefs::kManagedDefaultJavaScriptOptimizerSetting,
      base::Value(ContentSetting::CONTENT_SETTING_ALLOW));

  EXPECT_EQ(static_cast<int>(JavascriptOptimizerSetting::kAllowed),
            GetGeneratedPrefValue(profile()));
}

TEST_F(GeneratedJavascriptOptimizerPrefTest, SetPrefResult) {
  const struct TestCase {
    JavascriptOptimizerSetting setting;
    ContentSetting expected_content_setting;
    bool expected_pref_blocked_for_unfamiliar_sites;
  } kTestCases[] = {
      {JavascriptOptimizerSetting::kAllowed,
       ContentSetting::CONTENT_SETTING_ALLOW, false},
      {JavascriptOptimizerSetting::kBlockedForUnfamiliarSites,
       ContentSetting::CONTENT_SETTING_ALLOW, true},
      {JavascriptOptimizerSetting::kBlocked,
       ContentSetting::CONTENT_SETTING_BLOCK, false},
  };

  for (const auto& test_case : kTestCases) {
    GeneratedJavascriptOptimizerPref pref(profile());
    SetPrefResult result =
        SetPref(pref, base::Value(static_cast<int>(test_case.setting)));
    EXPECT_EQ(result, SetPrefResult::SUCCESS);
    EXPECT_EQ(host_content_settings_map()->GetDefaultContentSetting(
                  ContentSettingsType::JAVASCRIPT_OPTIMIZER),
              test_case.expected_content_setting);
    EXPECT_EQ(prefs()->GetBoolean(
                  prefs::kJavascriptOptimizerBlockedForUnfamiliarSites),
              test_case.expected_pref_blocked_for_unfamiliar_sites);
  }
}

TEST_F(GeneratedJavascriptOptimizerPrefTest, SetPref_NotInt) {
  GeneratedJavascriptOptimizerPref pref(profile());
  SetPrefResult result = SetPref(pref, base::Value("not an int"));
  EXPECT_EQ(result, SetPrefResult::PREF_TYPE_MISMATCH);
}

TEST_F(GeneratedJavascriptOptimizerPrefTest, SetPref_OutOfRange) {
  GeneratedJavascriptOptimizerPref pref(profile());
  SetPrefResult result = SetPref(pref, base::Value(100));
  EXPECT_EQ(result, SetPrefResult::PREF_TYPE_MISMATCH);
}

}  // namespace content_settings