[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",