From 52ac08c006bcbb946b336c668d2a1dc00ad2cdc8 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 18 Nov 2025 12:49:33 -0500 Subject: [PATCH] [Shields UI] Open page info when Shields detects repeated reloads (#32363) --- browser/ui/views/location_bar/BUILD.gn | 5 + .../location_bar/brave_location_bar_view.cc | 8 ++ .../location_bar/brave_location_bar_view.h | 2 + .../brave_shields_page_info_controller.cc | 97 +++++++++++++++++++ .../brave_shields_page_info_controller.h | 49 ++++++++++ browser/ui/views/page_info/BUILD.gn | 1 + ...brave_page_info_bubble_view_browsertest.cc | 51 ++++++++++ 7 files changed, 213 insertions(+) create mode 100644 browser/ui/views/location_bar/brave_shields_page_info_controller.cc create mode 100644 browser/ui/views/location_bar/brave_shields_page_info_controller.h diff --git a/browser/ui/views/location_bar/BUILD.gn b/browser/ui/views/location_bar/BUILD.gn index dd24ecec346..059717d676b 100644 --- a/browser/ui/views/location_bar/BUILD.gn +++ b/browser/ui/views/location_bar/BUILD.gn @@ -18,6 +18,8 @@ source_set("location_bar") { "brave_search_conversion/promotion_button_view.h", "brave_selected_keyword_view_util.cc", "brave_selected_keyword_view_util.h", + "brave_shields_page_info_controller.cc", + "brave_shields_page_info_controller.h", "brave_star_view.cc", "brave_star_view.h", ] @@ -27,6 +29,8 @@ source_set("location_bar") { "//brave/app/theme:brave_theme_resources", "//brave/app/vector_icons", "//brave/browser/ui:brave_tab_prefs", + "//brave/browser/ui/page_info", + "//brave/browser/ui/views/page_info", "//brave/components/brave_search_conversion", "//brave/components/commander/browser", "//brave/components/commander/common", @@ -43,6 +47,7 @@ source_set("location_bar") { "//chrome/browser/ui/tabs:tab_strip", "//chrome/browser/ui/views/location_bar", "//chrome/browser/ui/views/page_action", + "//chrome/browser/ui/views/page_info", "//components/image_fetcher/core", "//components/search_engines", "//components/version_info:channel", diff --git a/browser/ui/views/location_bar/brave_location_bar_view.cc b/browser/ui/views/location_bar/brave_location_bar_view.cc index 01c7d3477e3..aac3e0f4259 100644 --- a/browser/ui/views/location_bar/brave_location_bar_view.cc +++ b/browser/ui/views/location_bar/brave_location_bar_view.cc @@ -14,11 +14,13 @@ #include "brave/app/vector_icons/vector_icons.h" #include "brave/browser/themes/brave_theme_service.h" #include "brave/browser/ui/color/brave_color_id.h" +#include "brave/browser/ui/page_info/features.h" #include "brave/browser/ui/tabs/brave_tab_prefs.h" #include "brave/browser/ui/views/brave_actions/brave_actions_container.h" #include "brave/browser/ui/views/brave_news/brave_news_action_icon_view.h" #include "brave/browser/ui/views/location_bar/brave_search_conversion/promotion_button_controller.h" #include "brave/browser/ui/views/location_bar/brave_search_conversion/promotion_button_view.h" +#include "brave/browser/ui/views/location_bar/brave_shields_page_info_controller.h" #include "brave/browser/ui/views/playlist/playlist_action_icon_view.h" #include "brave/browser/ui/views/toolbar/brave_toolbar_view.h" #include "brave/components/commander/common/buildflags/buildflags.h" @@ -134,6 +136,12 @@ void BraveLocationBarView::Init() { promotion_button_, omnibox_view_, browser()); } + if (page_info::features::IsShowBraveShieldsInPageInfoEnabled()) { + shields_page_info_controller_ = + std::make_unique( + browser_->GetTabStripModel(), location_icon_view()); + } + // brave action buttons brave_actions_ = AddChildView( std::make_unique(browser_, profile())); diff --git a/browser/ui/views/location_bar/brave_location_bar_view.h b/browser/ui/views/location_bar/brave_location_bar_view.h index de0dc4dc24a..36d20b67618 100644 --- a/browser/ui/views/location_bar/brave_location_bar_view.h +++ b/browser/ui/views/location_bar/brave_location_bar_view.h @@ -21,6 +21,7 @@ class BraveActionsContainer; class BraveActionsContainerTest; +class BraveShieldsPageInfoController; class PromotionButtonController; class PromotionButtonView; class PlaylistActionIconView; @@ -135,6 +136,7 @@ class BraveLocationBarView : public LocationBarView { raw_ptr brave_news_action_icon_view_ = nullptr; std::unique_ptr promotion_controller_; raw_ptr promotion_button_ = nullptr; + std::unique_ptr shields_page_info_controller_; #if BUILDFLAG(ENABLE_TOR) raw_ptr onion_location_view_ = nullptr; #endif diff --git a/browser/ui/views/location_bar/brave_shields_page_info_controller.cc b/browser/ui/views/location_bar/brave_shields_page_info_controller.cc new file mode 100644 index 00000000000..bd51a71033d --- /dev/null +++ b/browser/ui/views/location_bar/brave_shields_page_info_controller.cc @@ -0,0 +1,97 @@ +/* Copyright (c) 2025 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/browser/ui/views/location_bar/brave_shields_page_info_controller.h" + +#include "base/check_deref.h" +#include "brave/browser/ui/views/page_info/brave_page_info_bubble_view.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/views/location_bar/location_icon_view.h" +#include "chrome/browser/ui/views/page_info/page_info_bubble_specification.h" +#include "chrome/browser/ui/views/page_info/page_info_bubble_view.h" +#include "content/public/browser/navigation_controller.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/web_contents.h" +#include "ui/views/view_utils.h" + +namespace { + +brave_shields::BraveShieldsTabHelper* GetShieldsHelper( + content::WebContents* web_contents) { + if (!web_contents) { + return nullptr; + } + return brave_shields::BraveShieldsTabHelper::FromWebContents(web_contents); +} + +} // namespace + +BraveShieldsPageInfoController::BraveShieldsPageInfoController( + TabStripModel* tab_strip_model, + LocationIconView* location_icon_view) + : tab_strip_model_(CHECK_DEREF(tab_strip_model)), + location_icon_view_(CHECK_DEREF(location_icon_view)) { + tab_strip_model_->AddObserver(this); + auto* web_contents = tab_strip_model_->GetActiveWebContents(); + if (auto* shields_helper = GetShieldsHelper(web_contents)) { + shields_helper->AddObserver(this); + } +} + +BraveShieldsPageInfoController::~BraveShieldsPageInfoController() { + auto* web_contents = tab_strip_model_->GetActiveWebContents(); + if (auto* shields_helper = GetShieldsHelper(web_contents)) { + shields_helper->RemoveObserver(this); + } +} + +void BraveShieldsPageInfoController::OnTabStripModelChanged( + TabStripModel* tab_strip_model, + const TabStripModelChange& change, + const TabStripSelectionChange& selection) { + if (selection.active_tab_changed()) { + if (auto* shields_helper = GetShieldsHelper(selection.old_contents)) { + shields_helper->RemoveObserver(this); + } + if (auto* shields_helper = GetShieldsHelper(selection.new_contents)) { + shields_helper->AddObserver(this); + } + } +} + +void BraveShieldsPageInfoController::OnResourcesChanged() {} + +void BraveShieldsPageInfoController::OnRepeatedReloadsDetected() { + ShowBubbleForRepeatedReloads(); +} + +void BraveShieldsPageInfoController::ShowBubbleForRepeatedReloads() { + auto* web_contents = tab_strip_model_->GetActiveWebContents(); + if (!web_contents) { + return; + } + + content::NavigationEntry* entry = + web_contents->GetController().GetVisibleEntry(); + if (!entry || entry->IsInitialEntry()) { + return; + } + + std::unique_ptr specification = + PageInfoBubbleSpecification::Builder( + &location_icon_view_.get(), + location_icon_view_->GetWidget()->GetNativeWindow(), web_contents, + entry->GetVirtualURL()) + .Build(); + + views::BubbleDialogDelegateView* bubble = + PageInfoBubbleView::CreatePageInfoBubble(std::move(specification)); + + auto* page_info_bubble = views::AsViewClass(bubble); + CHECK(page_info_bubble); + page_info_bubble->SetHighlightedButton(&location_icon_view_.get()); + page_info_bubble->GetWidget()->Show(); + page_info_bubble->OpenShieldsPageAfterRepeatedReloads(); +} diff --git a/browser/ui/views/location_bar/brave_shields_page_info_controller.h b/browser/ui/views/location_bar/brave_shields_page_info_controller.h new file mode 100644 index 00000000000..db1724f7b74 --- /dev/null +++ b/browser/ui/views/location_bar/brave_shields_page_info_controller.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2025 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_BROWSER_UI_VIEWS_LOCATION_BAR_BRAVE_SHIELDS_PAGE_INFO_CONTROLLER_H_ +#define BRAVE_BROWSER_UI_VIEWS_LOCATION_BAR_BRAVE_SHIELDS_PAGE_INFO_CONTROLLER_H_ + +#include "base/memory/raw_ref.h" +#include "brave/browser/brave_shields/brave_shields_tab_helper.h" +#include "chrome/browser/ui/tabs/tab_strip_model_observer.h" + +class LocationIconView; +class TabStripModel; + +// Controller that listens for Brave Shields events for the current active tab +// and opens the Page Info bubble when appropriate. +class BraveShieldsPageInfoController + : public brave_shields::BraveShieldsTabHelper::Observer, + public TabStripModelObserver { + public: + BraveShieldsPageInfoController(TabStripModel* tab_strip_model, + LocationIconView* location_icon_view); + + BraveShieldsPageInfoController(const BraveShieldsPageInfoController&) = + delete; + BraveShieldsPageInfoController& operator=( + const BraveShieldsPageInfoController&) = delete; + + ~BraveShieldsPageInfoController() override; + + // TabStripModelObserver: + void OnTabStripModelChanged( + TabStripModel* tab_strip_model, + const TabStripModelChange& change, + const TabStripSelectionChange& selection) override; + + // brave_shields::BraveShieldsTabHelper::Observer: + void OnResourcesChanged() override; + void OnRepeatedReloadsDetected() override; + + private: + void ShowBubbleForRepeatedReloads(); + + raw_ref tab_strip_model_; + raw_ref location_icon_view_; +}; + +#endif // BRAVE_BROWSER_UI_VIEWS_LOCATION_BAR_BRAVE_SHIELDS_PAGE_INFO_CONTROLLER_H_ diff --git a/browser/ui/views/page_info/BUILD.gn b/browser/ui/views/page_info/BUILD.gn index c6fe39be3e4..3cb928ec5d6 100644 --- a/browser/ui/views/page_info/BUILD.gn +++ b/browser/ui/views/page_info/BUILD.gn @@ -43,6 +43,7 @@ source_set("browser_tests") { deps = [ ":page_info", "//brave/browser/ui/page_info", + "//brave/components/brave_shields/core/common", "//chrome/browser/ui/views/frame", "//chrome/browser/ui/views/location_bar", "//chrome/browser/ui/views/page_info", diff --git a/browser/ui/views/page_info/brave_page_info_bubble_view_browsertest.cc b/browser/ui/views/page_info/brave_page_info_bubble_view_browsertest.cc index 403cb6436ff..acf7360ae2e 100644 --- a/browser/ui/views/page_info/brave_page_info_bubble_view_browsertest.cc +++ b/browser/ui/views/page_info/brave_page_info_bubble_view_browsertest.cc @@ -8,8 +8,10 @@ #include "base/test/scoped_feature_list.h" #include "brave/browser/ui/page_info/features.h" #include "brave/browser/ui/views/page_info/brave_page_info_view_ids.h" +#include "brave/components/brave_shields/core/common/features.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/location_bar/location_bar_view.h" #include "chrome/browser/ui/views/location_bar/location_icon_view.h" @@ -21,6 +23,7 @@ #include "components/content_settings/core/common/content_settings_types.h" #include "components/page_info/page_info.h" #include "content/public/test/browser_test.h" +#include "content/public/test/test_navigation_observer.h" #include "ui/events/test/test_event.h" #include "ui/views/controls/button/button.h" #include "ui/views/test/button_test_api.h" @@ -292,3 +295,51 @@ IN_PROC_BROWSER_TEST_F(BravePageInfoBubbleViewFlagDisabledBrowserTest, // Verify that the shields web UI is not visible. EXPECT_FALSE(IsShieldsViewDrawn(bubble_view)); } + +class BravePageInfoBubbleViewRepeatedReloadsBrowserTest + : public BravePageInfoBubbleViewBrowserTestBase { + public: + BravePageInfoBubbleViewRepeatedReloadsBrowserTest() { + using brave_shields::features::kAdblockOnlyMode; + using brave_shields::features::kAdblockOnlyModePromptAfterPageReloadsMax; + using brave_shields::features::kAdblockOnlyModePromptAfterPageReloadsMin; + + feature_list_.InitWithFeaturesAndParameters( + {{page_info::features::kShowBraveShieldsInPageInfo, {}}, + {kAdblockOnlyMode, + {{kAdblockOnlyModePromptAfterPageReloadsMin.name, "2"}, + {kAdblockOnlyModePromptAfterPageReloadsMax.name, "3"}}}}, + {}); + } + + ~BravePageInfoBubbleViewRepeatedReloadsBrowserTest() override = default; + + private: + base::test::ScopedFeatureList feature_list_; +}; + +// Test that the page info bubble is automatically opened and shows the shields +// tab after repeated reloads. +IN_PROC_BROWSER_TEST_F(BravePageInfoBubbleViewRepeatedReloadsBrowserTest, + AutoOpenAfterRepeatedReloads) { + GURL test_url = embedded_https_test_server().GetURL("/test.html"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url)); + + auto reload = [&]() { + content::TestNavigationObserver reload_observer( + browser()->tab_strip_model()->GetActiveWebContents()); + chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB); + reload_observer.Wait(); + }; + + // The first reload should not open the bubble. + reload(); + EXPECT_FALSE(GetBubbleView()); + + // The second reload should open the bubble with the Shield tab visible. + reload(); + auto* bubble_view = GetBubbleView(); + ASSERT_TRUE(bubble_view); + EXPECT_TRUE(IsShieldsViewDrawn(bubble_view)); + EXPECT_FALSE(IsSiteSettingsViewDrawn(bubble_view)); +}