[ntp-footer] Update footer visibility based on user pref

Only surfaces the footer on extension NTPs if "prefs::kNtpFooterVisible" is set to true. As `kNtpFooterVisible` is changed through the customize chrome side panel the footer visibility gets updated accordingly.

Bug: 399184119

Change-Id: I0bc1d8e37b51eb06d455ce7f7bee40b5a399137a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6512356
Reviewed-by: Paul Adedeji <pauladedeji@google.com>
Commit-Queue: Jennifer Serrano <jennserrano@google.com>
Cr-Commit-Position: refs/heads/main@{#1462951}
diff --git a/chrome/browser/ui/views/new_tab_footer/BUILD.gn b/chrome/browser/ui/views/new_tab_footer/BUILD.gn
index d40c739..7cbb750 100644
--- a/chrome/browser/ui/views/new_tab_footer/BUILD.gn
+++ b/chrome/browser/ui/views/new_tab_footer/BUILD.gn
@@ -28,3 +28,14 @@
     "//ui/views/controls/webview",
   ]
 }
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [ "footer_controller_unittest.cc" ]
+  deps = [
+    ":new_tab_footer",
+    "//base/test:test_support",
+    "//chrome/browser/ui:ui_features",
+    "//chrome/test:test_support",
+  ]
+}
diff --git a/chrome/browser/ui/views/new_tab_footer/footer_controller.cc b/chrome/browser/ui/views/new_tab_footer/footer_controller.cc
index 8ae1c724..111c192 100644
--- a/chrome/browser/ui/views/new_tab_footer/footer_controller.cc
+++ b/chrome/browser/ui/views/new_tab_footer/footer_controller.cc
@@ -8,6 +8,8 @@
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/new_tab_footer/footer_web_view.h"
 #include "chrome/browser/ui/webui/new_tab_footer/new_tab_footer_helper.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
 #include "content/public/browser/navigation_entry.h"
 
 namespace new_tab_footer {
@@ -19,6 +21,14 @@
   if (features::IsNtpFooterEnabledWithoutSideBySide()) {
     footer_web_view_ = tab_->GetBrowserWindowInterface()->NewTabFooterWebView();
   }
+
+  profile_ = tab_->GetBrowserWindowInterface()->GetProfile();
+  pref_change_registrar_.Init(profile_->GetPrefs());
+  pref_change_registrar_.Add(
+      prefs::kNtpFooterVisible,
+      base::BindRepeating(&NewTabFooterController::UpdateFooterVisibility,
+                          weak_factory_.GetWeakPtr()));
+
   content::WebContentsObserver::Observe(tab_->GetContents());
   tab_did_activate_callback_subscription_ = tab_->RegisterDidActivate(
       base::BindRepeating(&NewTabFooterController::TabForegrounded,
@@ -45,8 +55,11 @@
     url = tab_->GetContents()->GetController().GetVisibleEntry()->GetURL();
   }
 
-  if (ntp_footer::IsExtensionNtp(
-          url, tab_->GetBrowserWindowInterface()->GetProfile())) {
+  bool is_footer_visible_pref =
+      profile_->GetPrefs()->GetBoolean(prefs::kNtpFooterVisible);
+  bool can_show_footer =
+      is_footer_visible_pref && ntp_footer::IsExtensionNtp(url, profile_);
+  if (can_show_footer) {
     ShowUI();
   } else {
     CloseUI();
diff --git a/chrome/browser/ui/views/new_tab_footer/footer_controller.h b/chrome/browser/ui/views/new_tab_footer/footer_controller.h
index 755a3df7..ce32123 100644
--- a/chrome/browser/ui/views/new_tab_footer/footer_controller.h
+++ b/chrome/browser/ui/views/new_tab_footer/footer_controller.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_VIEWS_NEW_TAB_FOOTER_FOOTER_CONTROLLER_H_
 
 #include "chrome/browser/ui/views/new_tab_footer/footer_web_view.h"
+#include "components/prefs/pref_change_registrar.h"
 #include "components/tabs/public/tab_interface.h"
 #include "content/public/browser/web_contents_observer.h"
 
@@ -33,6 +34,8 @@
   const raw_ptr<tabs::TabInterface> tab_;
   raw_ptr<new_tab_footer::NewTabFooterWebView> footer_web_view_;
   base::CallbackListSubscription tab_did_activate_callback_subscription_;
+  PrefChangeRegistrar pref_change_registrar_;
+  raw_ptr<Profile> profile_;
 
   base::WeakPtrFactory<NewTabFooterController> weak_factory_{this};
 };
diff --git a/chrome/browser/ui/views/new_tab_footer/footer_controller_unittest.cc b/chrome/browser/ui/views/new_tab_footer/footer_controller_unittest.cc
new file mode 100644
index 0000000..8e90ca5
--- /dev/null
+++ b/chrome/browser/ui/views/new_tab_footer/footer_controller_unittest.cc
@@ -0,0 +1,235 @@
+// 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/ui/views/new_tab_footer/footer_controller.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/extensions/chrome_test_extension_loader.h"
+#include "chrome/browser/extensions/extension_service_test_base.h"
+#include "chrome/browser/extensions/extension_web_ui.h"
+#include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h"
+#include "chrome/browser/ui/tabs/test/mock_tab_interface.h"
+#include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/views/new_tab_footer/footer_web_view.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/search/ntp_features.h"
+#include "content/public/test/web_contents_tester.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension_builder.h"
+#include "extensions/test/test_extension_dir.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+const char kNonExtensionNtpUrl[] = "https://www.google.com";
+}
+
+class FakeNewTabFooterWebView : public new_tab_footer::NewTabFooterWebView {
+ public:
+  explicit FakeNewTabFooterWebView(BrowserWindowInterface* browser_window)
+      : NewTabFooterWebView(browser_window) {}
+  ~FakeNewTabFooterWebView() override {}
+
+  bool did_show_ui() { return did_show_ui_; }
+  bool did_close_ui() { return did_close_ui_; }
+
+  void Clear() {
+    did_show_ui_ = false;
+    did_close_ui_ = false;
+  }
+
+ protected:
+  // WebUIContentsWrapper::Host:
+  void ShowUI() override { did_show_ui_ = true; }
+  void CloseUI() override { did_close_ui_ = true; }
+
+  bool did_show_ui_ = false;
+  bool did_close_ui_ = false;
+};
+
+class FakeBrowserWindowInterface : public MockBrowserWindowInterface {
+ public:
+  explicit FakeBrowserWindowInterface(Profile* profile) { profile_ = profile; }
+  ~FakeBrowserWindowInterface() override = default;
+
+  // MockBrowserWindowInterface:
+  FakeNewTabFooterWebView* NewTabFooterWebView() override {
+    return footer_web_view_;
+  }
+  Profile* GetProfile() override { return profile_.get(); }
+
+  void SetFooterWebView(FakeNewTabFooterWebView* footer_web_view) {
+    footer_web_view_ = footer_web_view;
+  }
+
+ protected:
+  raw_ptr<Profile> profile_;
+  raw_ptr<FakeNewTabFooterWebView> footer_web_view_;
+};
+
+class FakeTabInterface : public tabs::MockTabInterface {
+ public:
+  FakeTabInterface(FakeBrowserWindowInterface* browser_window,
+                   content::BrowserContext* browser_context) {
+    browser_window_ = browser_window;
+    web_contents_ = content::WebContentsTester::CreateTestWebContents(
+        browser_context, nullptr);
+    footer_web_view_ =
+        std::make_unique<FakeNewTabFooterWebView>(browser_window);
+    browser_window_->SetFooterWebView(footer_web_view_.get());
+  }
+  ~FakeTabInterface() override {
+    browser_window_->SetFooterWebView(nullptr);
+    footer_web_view_.reset();
+  }
+
+  // tabs::MockTabInterface:
+  FakeBrowserWindowInterface* GetBrowserWindowInterface() override {
+    return browser_window_;
+  }
+  content::WebContents* GetContents() const override {
+    return web_contents_.get();
+  }
+  bool IsActivated() const override { return true; }
+
+  void NavigateTo(const GURL url) {
+    EXPECT_TRUE(web_contents_.get());
+    content::WebContentsTester* web_contents_tester =
+        content::WebContentsTester::For(web_contents_.get());
+    web_contents_tester->NavigateAndCommit(url);
+  }
+
+ protected:
+  std::unique_ptr<content::WebContents> web_contents_;
+  std::unique_ptr<FakeNewTabFooterWebView> footer_web_view_;
+  raw_ptr<FakeBrowserWindowInterface> browser_window_;
+};
+
+class FooterControllerExtensionTest
+    : public extensions::ExtensionServiceTestBase {
+ public:
+  void SetUp() override {
+    feature_list_.InitWithFeatures(
+        /*enabled_features=*/{ntp_features::kNtpFooter},
+        /*disabled_features=*/{features::kSideBySide});
+    ExtensionServiceTestBase::SetUp();
+    InitializeEmptyExtensionService();
+
+    browser_window_ = std::make_unique<FakeBrowserWindowInterface>(profile());
+    tab_ = std::make_unique<FakeTabInterface>(browser_window_.get(),
+                                              browser_context());
+    controller_ =
+        std::make_unique<new_tab_footer::NewTabFooterController>(tab_.get());
+  }
+
+  void TearDown() override {
+    controller_.reset();
+    tab_.reset();
+    browser_window_.reset();
+    ExtensionServiceTestBase::TearDown();
+  }
+
+  scoped_refptr<const extensions::Extension> LoadNtpExtension() {
+    extensions::TestExtensionDir extension_dir;
+    const std::string kManifest = R"(
+      {
+        "chrome_url_overrides": {
+            "newtab": "ext.html"
+        },
+        "name": "Extension-overridden NTP",
+          "manifest_version": 3,
+          "version": "0.1"
+      })";
+    extension_dir.WriteManifest(kManifest);
+    extension_dir.WriteFile(FILE_PATH_LITERAL("ext.html"),
+                            "<body>Extension-overridden NTP</body>");
+    extensions::ChromeTestExtensionLoader extension_loader(profile());
+    scoped_refptr<const extensions::Extension> extension =
+        extension_loader.LoadExtension(extension_dir.Pack());
+    return extension;
+  }
+
+  FakeBrowserWindowInterface* browser_window() { return browser_window_.get(); }
+
+  FakeTabInterface* tab_interface() { return tab_.get(); }
+
+ protected:
+  base::test::ScopedFeatureList feature_list_;
+  std::unique_ptr<FakeBrowserWindowInterface> browser_window_;
+  std::unique_ptr<FakeTabInterface> tab_;
+  std::unique_ptr<new_tab_footer::NewTabFooterController> controller_;
+};
+
+TEST_F(FooterControllerExtensionTest, FooterShown_ExtensionNTP) {
+  profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, true);
+  auto extension = LoadNtpExtension();
+  ASSERT_TRUE(extension);
+  // Force activation of the URL override. The usual observer for
+  // extension load isn't created in the unit test.
+  ExtensionWebUI::RegisterOrActivateChromeURLOverrides(
+      profile_.get(),
+      extensions::URLOverrides::GetChromeURLOverrides(extension.get()));
+  EXPECT_FALSE(browser_window()->NewTabFooterWebView()->did_show_ui());
+  tab_interface()->NavigateTo(extension->url());
+
+  EXPECT_TRUE(browser_window()->NewTabFooterWebView()->did_show_ui());
+}
+
+TEST_F(FooterControllerExtensionTest, FooterHidden_NonExtensionNTP) {
+  profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, true);
+  // After a pref change, there's an attempt to show the footer.
+  EXPECT_TRUE(browser_window()->NewTabFooterWebView()->did_close_ui());
+  // Clear the value of `FakeNewTabFooterWebView::did_close_ui()` to
+  // check that the navigation also results in a hidden footer.
+  browser_window()->NewTabFooterWebView()->Clear();
+
+  EXPECT_FALSE(browser_window()->NewTabFooterWebView()->did_close_ui());
+  tab_interface()->NavigateTo(GURL(kNonExtensionNtpUrl));
+
+  EXPECT_TRUE(browser_window()->NewTabFooterWebView()->did_close_ui());
+}
+
+// Ensures footer is shown on extension NTPs when
+// `prefs::kNtpFooterVisible` is set to true.
+TEST_F(FooterControllerExtensionTest, FooterShown_UserPref) {
+  profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, false);
+  auto extension = LoadNtpExtension();
+  ASSERT_TRUE(extension);
+  // Force activation of the URL override. The usual observer for
+  // extension load isn't created in the unit test.
+  ExtensionWebUI::RegisterOrActivateChromeURLOverrides(
+      profile_.get(),
+      extensions::URLOverrides::GetChromeURLOverrides(extension.get()));
+
+  tab_interface()->NavigateTo(extension->url());
+  EXPECT_FALSE(browser_window()->NewTabFooterWebView()->did_show_ui());
+
+  profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, true);
+  EXPECT_TRUE(browser_window()->NewTabFooterWebView()->did_show_ui());
+}
+
+// Ensures footer is hidden on extension NTPs when
+// `prefs::kNtpFooterVisible` is set to false.
+TEST_F(FooterControllerExtensionTest, FooterHidden_UserPref) {
+  profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, true);
+  // After a pref change, there's an attempt to show the footer.
+  EXPECT_TRUE(browser_window()->NewTabFooterWebView()->did_close_ui());
+  // Clear the value of `FakeNewTabFooterWebView::did_close_ui()` to check
+  // the effect of pref changes on an extension NTP.
+  browser_window()->NewTabFooterWebView()->Clear();
+  EXPECT_FALSE(browser_window()->NewTabFooterWebView()->did_close_ui());
+
+  auto extension = LoadNtpExtension();
+  ASSERT_TRUE(extension);
+  // Force activation of the URL override. The usual observer for
+  // extension load isn't created in the unit test.
+  ExtensionWebUI::RegisterOrActivateChromeURLOverrides(
+      profile_.get(),
+      extensions::URLOverrides::GetChromeURLOverrides(extension.get()));
+
+  tab_interface()->NavigateTo(extension->url());
+  EXPECT_FALSE(browser_window()->NewTabFooterWebView()->did_close_ui());
+
+  profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, false);
+  EXPECT_TRUE(browser_window()->NewTabFooterWebView()->did_close_ui());
+}
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index bf07e87..f9156775 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -8088,6 +8088,7 @@
       "//chrome/browser/ui/tabs:tab_strip",
       "//chrome/browser/ui/tabs:test_support",
       "//chrome/browser/ui/tabs:unit_tests",
+      "//chrome/browser/ui/views/new_tab_footer:unit_tests",
       "//chrome/browser/ui/views/page_action:unit_tests",
       "//chrome/browser/ui/views/webid:test_support",
       "//chrome/browser/ui/webui:webui_util",