Improve Forced Colors Invalidation for Page Color Block List

This CL addresses an issue where changes in Forced Colors mode could
lead to duplicate invalidations and subsequent repainting.
Specifically, a forced colors invalidation can be triggered as a
result of two events:
1.) The forced colors web preference changing
2.) The color providers in Blink updating

The issue was that not only was the page being repainted more than
required for a single change, but also, if the order of repainting was
incorrect, it could result in a brief display of incorrect colors. For
instance, if repainting was triggered by a change in the forced colors
web pref before the color providers in Blink were updated, the page
could momentarily display incorrect colors.

The key steps taken are:

1.) Removing invalidation from the `inForcedColors` web pref: The
invalidation method associated with the `inForcedColors preference` in
settings.json5 was removed. This ensured that forced colors web pref
changes no longer triggered repaints.

2.) Handling Page Colors Block List Changes: The page colors block list
allows users to opt out of forced colors mode for specific sites.
Previously, this feature relied on the `inForcedColors` web pref
invalidation. With this change, a consistent way to handle changes to
the block list is established by:
a.) Introducing a new web pref, `isForcedColorsDisabled`, specifically
for the block list scenario
b.) Adding a custom invalidation method for Forced Colors, used to
invalidate when the `isForcedColorsDisabled` pref changes

3.) Updating Blink Tests: Relevant Blink tests were also updated.
Previously, these tests relied on invalidation triggered by the
forced colors web pref. Now, they directly invalidate from the
Document.

Bug: 40779801
Change-Id: I2fde82bf6d1511b1c882d00523e6faf6c9278b37
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5651705
Reviewed-by: danakj <[email protected]>
Reviewed-by: Alison Maher <[email protected]>
Commit-Queue: Sam Davis Omekara <[email protected]>
Reviewed-by: Benjamin Beaudry <[email protected]>
Reviewed-by: Dominic Battré <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1328347}
diff --git a/chrome/browser/accessibility/page_colors_factory.cc b/chrome/browser/accessibility/page_colors_factory.cc
index a41ff17..0174933 100644
--- a/chrome/browser/accessibility/page_colors_factory.cc
+++ b/chrome/browser/accessibility/page_colors_factory.cc
@@ -13,11 +13,6 @@
       GetInstance()->GetServiceForBrowserContext(profile, /*create=*/true));
 }
 
-void PageColorsFactory::RegisterProfilePrefs(
-    user_prefs::PrefRegistrySyncable* registry) {
-  PageColors::RegisterProfilePrefs(registry);
-}
-
 // static
 PageColorsFactory* PageColorsFactory::GetInstance() {
   static base::NoDestructor<PageColorsFactory> instance;
diff --git a/chrome/browser/accessibility/page_colors_factory.h b/chrome/browser/accessibility/page_colors_factory.h
index 0787889..70d3a8f 100644
--- a/chrome/browser/accessibility/page_colors_factory.h
+++ b/chrome/browser/accessibility/page_colors_factory.h
@@ -29,8 +29,6 @@
   std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
       content::BrowserContext* context) const override;
   bool ServiceIsCreatedWithBrowserContext() const override;
-  void RegisterProfilePrefs(
-      user_prefs::PrefRegistrySyncable* registry) override;
 };
 
 #endif  // CHROME_BROWSER_ACCESSIBILITY_PAGE_COLORS_FACTORY_H_
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 64c2edd..47e9dcb4 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -3811,9 +3811,8 @@
 
 namespace {
 
-bool IsForcedColorsEnabledForWebContent(content::WebContents* contents,
-                                        const ui::NativeTheme* native_theme) {
-  if (!native_theme->InForcedColorsMode() || !contents) {
+bool ShouldDisableForcedColorsForWebContent(content::WebContents* contents) {
+  if (!contents) {
     return false;
   }
 
@@ -3825,7 +3824,7 @@
       prefs->GetList(prefs::kPageColorsBlockList);
 
   if (forced_colors_blocklist.empty()) {
-    return true;
+    return false;
   }
 
   GURL url = contents->GetLastCommittedURL();
@@ -3841,11 +3840,17 @@
     }
 
     if (pattern.Matches(url)) {
-      return false;
+      return true;
     }
   }
 
-  return true;
+  return false;
+}
+
+bool IsForcedColorsEnabledForWebContent(content::WebContents* contents,
+                                        const ui::NativeTheme* native_theme) {
+  return native_theme->InForcedColorsMode() &&
+         !ShouldDisableForcedColorsForWebContent(contents);
 }
 
 #if !BUILDFLAG(IS_ANDROID)
@@ -4524,6 +4529,9 @@
   web_prefs->in_forced_colors =
       IsForcedColorsEnabledForWebContent(web_contents, GetWebTheme());
 
+  web_prefs->is_forced_colors_disabled =
+      ShouldDisableForcedColorsForWebContent(web_contents);
+
   UpdatePreferredColorScheme(
       web_prefs,
       web_contents->GetPrimaryMainFrame()->GetSiteInstance()->GetSiteURL(),
@@ -4623,6 +4631,12 @@
   prefs_changed |= (web_prefs->in_forced_colors != in_forced_colors);
   web_prefs->in_forced_colors = in_forced_colors;
 
+  const bool is_forced_colors_disabled =
+      ShouldDisableForcedColorsForWebContent(web_contents);
+  prefs_changed |=
+      (web_prefs->is_forced_colors_disabled != is_forced_colors_disabled);
+  web_prefs->is_forced_colors_disabled = is_forced_colors_disabled;
+
   prefs_changed |=
       UpdatePreferredColorScheme(web_prefs, web_contents->GetLastCommittedURL(),
                                  web_contents, GetWebTheme());
diff --git a/chrome/browser/chrome_content_browser_client_browsertest.cc b/chrome/browser/chrome_content_browser_client_browsertest.cc
index be8f9c1f..1501bd6 100644
--- a/chrome/browser/chrome_content_browser_client_browsertest.cc
+++ b/chrome/browser/chrome_content_browser_client_browsertest.cc
@@ -429,20 +429,19 @@
 IN_PROC_BROWSER_TEST_P(ForcedColorsTest, ForcedColorsWithBlockList) {
   test_theme_.set_forced_colors(GetParam());
 
-  // Add url to the page colors block list.
   const char* url = "http://foo.com";
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(url)));
+
+  // Add url to the page colors block list.
   base::Value::List list;
   list.Append(url);
   Profile* profile = browser()->profile();
   profile->GetPrefs()->SetList(prefs::kPageColorsBlockList, list.Clone());
-
   browser()
       ->tab_strip_model()
       ->GetActiveWebContents()
       ->OnWebPreferencesChanged();
 
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(url)));
-
   // Forced colors should be `none` when a site is added to the block list.
   EXPECT_EQ(true, EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
                          base::StringPrintf(
@@ -452,7 +451,6 @@
   // Remove url from the page colors block list.
   list.EraseValue(base::Value(url));
   profile->GetPrefs()->SetList(prefs::kPageColorsBlockList, list.Clone());
-
   browser()
       ->tab_strip_model()
       ->GetActiveWebContents()
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index a9e8315..f20578f 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/about_flags.h"
 #include "chrome/browser/accessibility/accessibility_labels_service.h"
 #include "chrome/browser/accessibility/invert_bubble_prefs.h"
+#include "chrome/browser/accessibility/page_colors.h"
 #include "chrome/browser/accessibility/prefers_default_scrollbar_styles_prefs.h"
 #include "chrome/browser/browser_process_impl.h"
 #include "chrome/browser/chrome_content_browser_client.h"
@@ -1901,6 +1902,7 @@
   ntp_tiles::MostVisitedSites::RegisterProfilePrefs(registry);
   optimization_guide::prefs::RegisterProfilePrefs(registry);
   optimization_guide::model_execution::prefs::RegisterProfilePrefs(registry);
+  PageColors::RegisterProfilePrefs(registry);
   password_manager::PasswordManager::RegisterProfilePrefs(registry);
   payments::RegisterProfilePrefs(registry);
   performance_manager::user_tuning::prefs::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/ui/prefs/pref_watcher.cc b/chrome/browser/ui/prefs/pref_watcher.cc
index ccbb4aa..e6565c4f 100644
--- a/chrome/browser/ui/prefs/pref_watcher.cc
+++ b/chrome/browser/ui/prefs/pref_watcher.cc
@@ -69,6 +69,7 @@
 #else
     prefs::kAccessibilityFocusHighlightEnabled,
 #endif
+    prefs::kPageColorsBlockList,
 };
 
 const int kWebPrefsToObserveLength = std::size(kWebPrefsToObserve);
diff --git a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
index 2eb41801..61daa80 100644
--- a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
+++ b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
@@ -217,6 +217,7 @@
   out->require_transient_activation_for_html_fullscreen =
       data.require_transient_activation_for_html_fullscreen();
   out->in_forced_colors = data.in_forced_colors();
+  out->is_forced_colors_disabled = data.is_forced_colors_disabled();
   out->preferred_root_scrollbar_color_scheme =
       data.preferred_root_scrollbar_color_scheme();
   out->preferred_color_scheme = data.preferred_color_scheme();
diff --git a/third_party/blink/public/common/web_preferences/web_preferences.h b/third_party/blink/public/common/web_preferences/web_preferences.h
index 5bebc1a..9b5f8ae 100644
--- a/third_party/blink/public/common/web_preferences/web_preferences.h
+++ b/third_party/blink/public/common/web_preferences/web_preferences.h
@@ -355,6 +355,11 @@
   // when to apply system color overrides to author specified styles.
   bool in_forced_colors = false;
 
+  // Indicates if Forced Colors mode should be disabled for this page.
+  // This allows users opt out of forced colors on specific sites.
+  // Forced colors are disabled for sites in the `kPageColorsBlockList` pref.
+  bool is_forced_colors_disabled = false;
+
   // The preferred color scheme set by the user's browser settings. The variable
   // follows the browser's color mode setting unless a browser theme (custom or
   // not) is defined, in which case the color scheme is set to the default
diff --git a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
index ffddf92b..d4dfa492 100644
--- a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
+++ b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
@@ -708,6 +708,11 @@
     return r.in_forced_colors;
   }
 
+  static bool is_forced_colors_disabled(
+      const blink::web_pref::WebPreferences& r) {
+    return r.is_forced_colors_disabled;
+  }
+
   static blink::mojom::PreferredColorScheme
   preferred_root_scrollbar_color_scheme(
       const blink::web_pref::WebPreferences& r) {
diff --git a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
index 2dc2c85..a599b501 100644
--- a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
+++ b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
@@ -418,6 +418,11 @@
   // when to apply system color overrides to author specified styles.
   bool in_forced_colors;
 
+  // Indicates if Forced Colors mode should be disabled for this page.
+  // This allows users opt out of forced colors on specific sites.
+  // Forced colors are disabled for sites in the `kPageColorsBlockList` pref.
+  bool is_forced_colors_disabled;
+
   // The preferred color scheme set by the user's browser settings. The variable
   // follows the browser's color mode setting unless a browser theme (custom or
   // not) is defined, in which case the color scheme is set to the default
diff --git a/third_party/blink/public/web/web_settings.h b/third_party/blink/public/web/web_settings.h
index 61a247b..31d58a4 100644
--- a/third_party/blink/public/web/web_settings.h
+++ b/third_party/blink/public/web/web_settings.h
@@ -271,6 +271,7 @@
   virtual void SetLazyLoadingImageMarginPx4G(int) = 0;
   virtual void SetForceDarkModeEnabled(bool) = 0;
   virtual void SetInForcedColors(bool) = 0;
+  virtual void SetIsForcedColorsDisabled(bool) = 0;
   virtual void SetPreferredRootScrollbarColorScheme(
       blink::mojom::PreferredColorScheme) = 0;
   virtual void SetPreferredColorScheme(blink::mojom::PreferredColorScheme) = 0;
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc
index a5bd447..1accb57 100644
--- a/third_party/blink/renderer/core/css/style_engine_test.cc
+++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -1966,7 +1966,8 @@
                 GetCSSPropertyColor()));
 
   ColorSchemeHelper color_scheme_helper(GetDocument());
-  color_scheme_helper.SetInForcedColors(/*in_forced_colors=*/true);
+  color_scheme_helper.SetInForcedColors(GetDocument(),
+                                        /*in_forced_colors=*/true);
   UpdateAllLifecyclePhases();
   EXPECT_EQ(Color::FromRGB(0, 128, 0),
             GetDocument().body()->GetComputedStyle()->VisitedDependentColor(
@@ -1998,7 +1999,8 @@
 
   // InForcedColors = false, PreferredColorScheme = kLight
   ColorSchemeHelper color_scheme_helper(GetDocument());
-  color_scheme_helper.SetInForcedColors(/*in_forced_colors=*/false);
+  color_scheme_helper.SetInForcedColors(GetDocument(),
+                                        /*in_forced_colors=*/false);
   color_scheme_helper.SetPreferredColorScheme(
       mojom::blink::PreferredColorScheme::kLight);
   UpdateAllLifecyclePhases();
@@ -2015,7 +2017,8 @@
                 GetCSSPropertyColor()));
 
   // InForcedColors = true, PreferredColorScheme = kDark
-  color_scheme_helper.SetInForcedColors(/*in_forced_colors=*/true);
+  color_scheme_helper.SetInForcedColors(GetDocument(),
+                                        /*in_forced_colors=*/true);
   UpdateAllLifecyclePhases();
   EXPECT_EQ(Color::FromRGB(255, 165, 0),
             GetDocument().body()->GetComputedStyle()->VisitedDependentColor(
@@ -2905,7 +2908,8 @@
   EXPECT_EQ(Color(0x12, 0x12, 0x12),
             GetDocument().View()->BaseBackgroundColor());
 
-  color_scheme_helper.SetInForcedColors(/*in_forced_colors=*/true);
+  color_scheme_helper.SetInForcedColors(GetDocument(),
+                                        /*in_forced_colors=*/true);
   UpdateAllLifecyclePhases();
   mojom::blink::ColorScheme color_scheme = mojom::blink::ColorScheme::kLight;
   Color system_background_color = LayoutTheme::GetTheme().SystemColor(
@@ -3518,7 +3522,8 @@
   EXPECT_EQ("rgb(255, 0, 0)", ComputedValue(elem, "color")->CssText());
 
   ColorSchemeHelper color_scheme_helper(GetDocument());
-  color_scheme_helper.SetInForcedColors(/*in_forced_colors=*/true);
+  color_scheme_helper.SetInForcedColors(GetDocument(),
+                                        /*in_forced_colors=*/true);
   UpdateAllLifecyclePhases();
   EXPECT_EQ(ComputedValue(ref, "color")->CssText(),
             ComputedValue(elem, "color")->CssText());
@@ -7337,7 +7342,8 @@
 TEST_F(StyleEngineTest, ForcedColorsLightDark) {
   ScopedForcedColorsForTest scoped_feature(true);
   ColorSchemeHelper color_scheme_helper(GetDocument());
-  color_scheme_helper.SetInForcedColors(/*in_forced_colors=*/true);
+  color_scheme_helper.SetInForcedColors(GetDocument(),
+                                        /*in_forced_colors=*/true);
   GetDocument().body()->setInnerHTML(R"HTML(
     <style>
       :root { color-scheme: light only; }
diff --git a/third_party/blink/renderer/core/exported/web_settings_impl.cc b/third_party/blink/renderer/core/exported/web_settings_impl.cc
index c6504a2..3a8c6c83 100644
--- a/third_party/blink/renderer/core/exported/web_settings_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_settings_impl.cc
@@ -757,6 +757,11 @@
   settings_->SetInForcedColors(in_forced_colors);
 }
 
+void WebSettingsImpl::SetIsForcedColorsDisabled(
+    bool is_forced_colors_disabled) {
+  settings_->SetIsForcedColorsDisabled(is_forced_colors_disabled);
+}
+
 void WebSettingsImpl::SetPreferredRootScrollbarColorScheme(
     mojom::blink::PreferredColorScheme color_scheme) {
   settings_->SetPreferredRootScrollbarColorScheme(color_scheme);
diff --git a/third_party/blink/renderer/core/exported/web_settings_impl.h b/third_party/blink/renderer/core/exported/web_settings_impl.h
index 7ad0c20..125d174e 100644
--- a/third_party/blink/renderer/core/exported/web_settings_impl.h
+++ b/third_party/blink/renderer/core/exported/web_settings_impl.h
@@ -222,6 +222,7 @@
 
   void SetForceDarkModeEnabled(bool) override;
   void SetInForcedColors(bool) override;
+  void SetIsForcedColorsDisabled(bool) override;
   void SetPreferredRootScrollbarColorScheme(
       mojom::blink::PreferredColorScheme) override;
   void SetPreferredColorScheme(mojom::blink::PreferredColorScheme) override;
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 110c0ff..ae03be1 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -1819,6 +1819,7 @@
 
   settings->SetLazyLoadEnabled(prefs.lazy_load_enabled);
   settings->SetInForcedColors(prefs.in_forced_colors);
+  settings->SetIsForcedColorsDisabled(prefs.is_forced_colors_disabled);
   settings->SetPreferredRootScrollbarColorScheme(
       prefs.preferred_root_scrollbar_color_scheme);
   settings->SetPreferredColorScheme(prefs.preferred_color_scheme);
@@ -3343,8 +3344,7 @@
   bool color_providers_did_change =
       page_->UpdateColorProviders(color_provider_colors);
   if (color_providers_did_change) {
-    Page::PlatformColorsChanged();
-    Page::ColorSchemeChanged();
+    Page::ForcedColorsChanged();
   }
 }
 
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc
index ad6e0a80..ba6c5f1 100644
--- a/third_party/blink/renderer/core/exported/web_view_test.cc
+++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -614,7 +614,10 @@
   web_view->SetPageBaseBackgroundColor(SK_ColorBLUE);
   EXPECT_EQ(Color(0x12, 0x12, 0x12), frame_view->BaseBackgroundColor());
 
-  color_scheme_helper.SetInForcedColors(/*in_forced_colors=*/true);
+  WebLocalFrameImpl* frame = web_view->MainFrameImpl();
+  Document* document = frame->GetFrame()->GetDocument();
+  CHECK(document);
+  color_scheme_helper.SetInForcedColors(*document, /*in_forced_colors=*/true);
   UpdateAllLifecyclePhases();
 
   mojom::blink::ColorScheme color_scheme = mojom::blink::ColorScheme::kLight;
@@ -624,7 +627,7 @@
           color_scheme, /*in_forced_colors=*/true));
   EXPECT_EQ(system_background_color, frame_view->BaseBackgroundColor());
 
-  color_scheme_helper.SetInForcedColors(/*in_forced_colors=*/false);
+  color_scheme_helper.SetInForcedColors(*document, /*in_forced_colors=*/false);
   UpdateAllLifecyclePhases();
   EXPECT_EQ(Color(0x12, 0x12, 0x12), frame_view->BaseBackgroundColor());
 
diff --git a/third_party/blink/renderer/core/frame/settings.json5 b/third_party/blink/renderer/core/frame/settings.json5
index fd10a6aa..af6df22 100644
--- a/third_party/blink/renderer/core/frame/settings.json5
+++ b/third_party/blink/renderer/core/frame/settings.json5
@@ -1088,7 +1088,13 @@
     {
       name: "inForcedColors",
       initial: false,
-      invalidate: ["ColorScheme"],
+      type: "bool",
+    },
+
+    {
+      name: "isForcedColorsDisabled",
+      initial: false,
+      invalidate: ["ForcedColors"],
       type: "bool",
     },
 
diff --git a/third_party/blink/renderer/core/frame/settings_delegate.h b/third_party/blink/renderer/core/frame/settings_delegate.h
index b5c6104..33a23ca 100644
--- a/third_party/blink/renderer/core/frame/settings_delegate.h
+++ b/third_party/blink/renderer/core/frame/settings_delegate.h
@@ -72,6 +72,7 @@
     kColorScheme,
     kUniversalAccess,
     kVisionDeficiency,
+    kForcedColors,
   };
 
   virtual void SettingsChanged(ChangeType) = 0;
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc
index e0227e9..0db1ee299 100644
--- a/third_party/blink/renderer/core/page/page.cc
+++ b/third_party/blink/renderer/core/page/page.cc
@@ -537,6 +537,11 @@
   }
 }
 
+void Page::ForcedColorsChanged() {
+  PlatformColorsChanged();
+  ColorSchemeChanged();
+}
+
 void Page::PlatformColorsChanged() {
   for (const Page* page : AllPages()) {
     for (Frame* frame = page->MainFrame(); frame;
@@ -1143,6 +1148,10 @@
         main_local_frame->GetDocument()->VisionDeficiencyChanged();
       break;
     }
+    case ChangeType::kForcedColors: {
+      ForcedColorsChanged();
+      break;
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/core/page/page.h b/third_party/blink/renderer/core/page/page.h
index a345ed0f..acbdb514 100644
--- a/third_party/blink/renderer/core/page/page.h
+++ b/third_party/blink/renderer/core/page/page.h
@@ -164,6 +164,7 @@
   // Should be called when |GetScrollbarTheme().UsesOverlayScrollbars()|
   // changes.
   static void UsesOverlayScrollbarsChanged();
+  static void ForcedColorsChanged();
   static void PlatformColorsChanged();
   static void ColorSchemeChanged();
 
diff --git a/third_party/blink/renderer/core/testing/color_scheme_helper.cc b/third_party/blink/renderer/core/testing/color_scheme_helper.cc
index 752ef68..342e21d 100644
--- a/third_party/blink/renderer/core/testing/color_scheme_helper.cc
+++ b/third_party/blink/renderer/core/testing/color_scheme_helper.cc
@@ -31,11 +31,11 @@
 ColorSchemeHelper::~ColorSchemeHelper() {
   // Reset preferred color scheme, preferred contrast and forced colors to their
   // original values.
+  settings_.SetInForcedColors(default_in_forced_colors_);
   settings_.SetPreferredRootScrollbarColorScheme(
       default_preferred_root_scrollbar_color_scheme_);
   settings_.SetPreferredColorScheme(default_preferred_color_scheme_);
   settings_.SetPreferredContrast(default_preferred_contrast_);
-  settings_.SetInForcedColors(default_in_forced_colors_);
 }
 
 void ColorSchemeHelper::SetPreferredRootScrollbarColorScheme(
@@ -54,6 +54,12 @@
   settings_.SetPreferredContrast(preferred_contrast);
 }
 
+void ColorSchemeHelper::SetInForcedColors(Document& document,
+                                          bool in_forced_colors) {
+  settings_.SetInForcedColors(in_forced_colors);
+  document.ColorSchemeChanged();
+}
+
 void ColorSchemeHelper::SetInForcedColors(bool in_forced_colors) {
   settings_.SetInForcedColors(in_forced_colors);
 }
diff --git a/third_party/blink/renderer/core/testing/color_scheme_helper.h b/third_party/blink/renderer/core/testing/color_scheme_helper.h
index 7e35bdb..34859e9 100644
--- a/third_party/blink/renderer/core/testing/color_scheme_helper.h
+++ b/third_party/blink/renderer/core/testing/color_scheme_helper.h
@@ -32,7 +32,7 @@
   void SetPreferredColorScheme(
       mojom::PreferredColorScheme preferred_color_scheme);
   void SetPreferredContrast(mojom::PreferredContrast preferred_contrast);
-  void SetInForcedColors(bool in_forced_colors);
+  void SetInForcedColors(Document& document, bool in_forced_colors);
   void SetEmulatedForcedColors(Document& document, bool is_dark_theme);
 
  private:
@@ -44,6 +44,9 @@
   mojom::PreferredContrast default_preferred_contrast_ =
       mojom::PreferredContrast::kNoPreference;
   bool default_in_forced_colors_ = false;
+  // Only to be used by the destructor, since we need to cleanup but don't store
+  // the Document/Page.
+  void SetInForcedColors(bool in_forced_colors);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/testing/internals.cc b/third_party/blink/renderer/core/testing/internals.cc
index aafc927..70c0f28 100644
--- a/third_party/blink/renderer/core/testing/internals.cc
+++ b/third_party/blink/renderer/core/testing/internals.cc
@@ -3402,7 +3402,7 @@
   color_scheme_helper_.emplace(*document);
   color_scheme_helper_->SetPreferredColorScheme(
       mojom::blink::PreferredColorScheme::kDark);
-  color_scheme_helper_->SetInForcedColors(/*in_forced_colors=*/true);
+  color_scheme_helper_->SetInForcedColors(*document, /*in_forced_colors=*/true);
   color_scheme_helper_->SetEmulatedForcedColors(*document,
                                                 /*is_dark_theme=*/false);
 }