| // Copyright 2024 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/browser_window/public/browser_window_features.h" |
| |
| #include <memory> |
| |
| #include "base/check_is_test.h" |
| #include "base/feature_list.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/no_destructor.h" |
| #include "chrome/browser/collaboration/collaboration_service_factory.h" |
| #include "chrome/browser/commerce/shopping_service_factory.h" |
| #include "chrome/browser/download/bubble/download_bubble_prefs.h" |
| #include "chrome/browser/extensions/manifest_v2_experiment_manager.h" |
| #include "chrome/browser/extensions/mv2_experiment_stage.h" |
| #include "chrome/browser/media/router/media_router_feature.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_command_controller.h" |
| #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" |
| #include "chrome/browser/ui/commerce/product_specifications_entry_point_controller.h" |
| #include "chrome/browser/ui/extensions/mv2_disabled_dialog_controller.h" |
| #include "chrome/browser/ui/lens/lens_overlay_entry_point_controller.h" |
| #include "chrome/browser/ui/performance_controls/memory_saver_opt_in_iph_controller.h" |
| #include "chrome/browser/ui/tabs/glic_nudge_controller.h" |
| #include "chrome/browser/ui/tabs/organization/tab_declutter_controller.h" |
| #include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h" |
| #include "chrome/browser/ui/tabs/saved_tab_groups/session_service_tab_group_sync_observer.h" |
| #include "chrome/browser/ui/toasts/toast_controller.h" |
| #include "chrome/browser/ui/toasts/toast_features.h" |
| #include "chrome/browser/ui/toasts/toast_service.h" |
| #include "chrome/browser/ui/toolbar/chrome_labs/chrome_labs_utils.h" |
| #include "chrome/browser/ui/ui_features.h" |
| #include "chrome/browser/ui/views/data_sharing/data_sharing_open_group_helper.h" |
| #include "chrome/browser/ui/views/download/bubble/download_toolbar_ui_controller.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/media_router/cast_browser_controller.h" |
| #include "chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_toolbar_bubble_controller.h" |
| #include "chrome/browser/ui/views/side_panel/extensions/extension_side_panel_manager.h" |
| #include "chrome/browser/ui/views/side_panel/side_panel_coordinator.h" |
| #include "chrome/browser/ui/views/toolbar/chrome_labs/chrome_labs_coordinator.h" |
| #include "chrome/common/chrome_features.h" |
| #include "components/collaboration/public/collaboration_service.h" |
| #include "components/commerce/core/commerce_feature_list.h" |
| #include "components/commerce/core/feature_utils.h" |
| #include "components/commerce/core/shopping_service.h" |
| #include "components/lens/lens_features.h" |
| #include "components/profile_metrics/browser_profile_type.h" |
| #include "components/saved_tab_groups/public/features.h" |
| |
| #if BUILDFLAG(ENABLE_GLIC) |
| #include "chrome/browser/glic/glic_border_view_manager.h" |
| #include "chrome/browser/glic/glic_enabling.h" |
| #endif |
| namespace { |
| |
| // This is the generic entry point for test code to stub out browser window |
| // functionality. It is called by production code, but only used by tests. |
| BrowserWindowFeatures::BrowserWindowFeaturesFactory& GetFactory() { |
| static base::NoDestructor<BrowserWindowFeatures::BrowserWindowFeaturesFactory> |
| factory; |
| return *factory; |
| } |
| |
| } // namespace |
| |
| // static |
| std::unique_ptr<BrowserWindowFeatures> |
| BrowserWindowFeatures::CreateBrowserWindowFeatures() { |
| if (GetFactory()) { |
| CHECK_IS_TEST(); |
| return GetFactory().Run(); |
| } |
| // Constructor is protected. |
| return base::WrapUnique(new BrowserWindowFeatures()); |
| } |
| |
| BrowserWindowFeatures::~BrowserWindowFeatures() = default; |
| |
| // static |
| void BrowserWindowFeatures::ReplaceBrowserWindowFeaturesForTesting( |
| BrowserWindowFeaturesFactory factory) { |
| BrowserWindowFeatures::BrowserWindowFeaturesFactory& f = GetFactory(); |
| f = std::move(factory); |
| } |
| |
| void BrowserWindowFeatures::Init(BrowserWindowInterface* browser) { |
| // Avoid passing `browser` directly to features. Instead, pass the minimum |
| // necessary state or controllers necessary. |
| // Ping erikchen for assistance. This comment will be deleted after there are |
| // 10+ features. |
| // |
| // Features that are only enabled for normal browser windows (e.g. a window |
| // with an omnibox and a tab strip). By default most features should be |
| // instantiated in this block. |
| if (browser->GetType() == BrowserWindowInterface::Type::TYPE_NORMAL) { |
| if (browser->GetProfile()->IsRegularProfile()) { |
| auto* shopping_service = |
| commerce::ShoppingServiceFactory::GetForBrowserContext( |
| browser->GetProfile()); |
| if (shopping_service && commerce::CanLoadProductSpecificationsFullPageUi( |
| shopping_service->GetAccountChecker())) { |
| product_specifications_entry_point_controller_ = std::make_unique< |
| commerce::ProductSpecificationsEntryPointController>(browser); |
| } |
| } |
| |
| if (browser->GetProfile()->IsRegularProfile() && |
| tab_groups::IsTabGroupsSaveV2Enabled() && |
| browser->GetTabStripModel()->SupportsTabGroups() && |
| tab_groups::SavedTabGroupUtils::GetServiceForProfile( |
| browser->GetProfile())) { |
| session_service_tab_group_sync_observer_ = |
| std::make_unique<tab_groups::SessionServiceTabGroupSyncObserver>( |
| browser->GetProfile(), browser->GetTabStripModel(), |
| browser->GetSessionID()); |
| } |
| |
| if (features::IsTabstripDeclutterEnabled() && |
| (browser->GetProfile()->IsRegularProfile() || |
| browser->GetProfile()->IsGuestSession())) { |
| tab_declutter_controller_ = |
| std::make_unique<tabs::TabDeclutterController>(browser); |
| } |
| |
| #if BUILDFLAG(ENABLE_GLIC) |
| if (GlicEnabling::IsProfileEligible(browser->GetProfile())) { |
| glic_border_view_manager_ = |
| std::make_unique<glic::GlicBorderViewManager>(browser); |
| if (features::IsTabstripComboButtonEnabled()) { |
| glic_nudge_controller_ = std::make_unique<tabs::GlicNudgeController>(); |
| } |
| } |
| #endif // BUILDFLAG(ENABLE_GLIC) |
| } |
| |
| // The LensOverlayEntryPointController is constructed for all browser types |
| // but is only initialized for normal browser windows. This simplifies the |
| // logic for code shared by both normal and non-normal windows. |
| lens_overlay_entry_point_controller_ = |
| std::make_unique<lens::LensOverlayEntryPointController>(); |
| |
| tab_strip_model_ = browser->GetTabStripModel(); |
| } |
| |
| void BrowserWindowFeatures::InitPostWindowConstruction(Browser* browser) { |
| // Features that are only enabled for normal browser windows (e.g. a window |
| // with an omnibox and a tab strip). By default most features should be |
| // instantiated in this block. |
| if (browser->is_type_normal()) { |
| if (IsChromeLabsEnabled()) { |
| chrome_labs_coordinator_ = |
| std::make_unique<ChromeLabsCoordinator>(browser); |
| } |
| |
| send_tab_to_self_toolbar_bubble_controller_ = std::make_unique< |
| send_tab_to_self::SendTabToSelfToolbarBubbleController>(browser); |
| |
| // TODO(crbug.com/350508658): Ideally, we don't pass in a reference to |
| // browser as per the guidance in the comment above. However, currently, |
| // we need browser to properly determine if the lens overlay is enabled. |
| // Cannot be in Init since needs to listen to the fullscreen controller |
| // and location bar view which are initialized after Init. |
| if (lens::features::IsLensOverlayEnabled()) { |
| views::View* location_bar = nullptr; |
| // TODO(crbug.com/360163254): We should really be using |
| // Browser::GetBrowserView, which always returns a non-null BrowserView |
| // in production, but this crashes during unittests using |
| // BrowserWithTestWindowTest; these should eventually be refactored. |
| if (BrowserView* browser_view = |
| BrowserView::GetBrowserViewForBrowser(browser)) { |
| location_bar = browser_view->GetLocationBarView(); |
| } |
| lens_overlay_entry_point_controller_->Initialize( |
| browser, browser->command_controller(), location_bar); |
| } |
| |
| auto* experiment_manager = |
| extensions::ManifestV2ExperimentManager::Get(browser->profile()); |
| if (experiment_manager) { |
| extensions::MV2ExperimentStage experiment_stage = |
| experiment_manager->GetCurrentExperimentStage(); |
| if (experiment_stage == |
| extensions::MV2ExperimentStage::kDisableWithReEnable || |
| experiment_stage == extensions::MV2ExperimentStage::kUnsupported) { |
| mv2_disabled_dialog_controller_ = |
| std::make_unique<extensions::Mv2DisabledDialogController>(browser); |
| } |
| } |
| } |
| |
| if ((browser->is_type_normal() || browser->is_type_app()) && |
| base::FeatureList::IsEnabled(toast_features::kToastFramework)) { |
| toast_service_ = std::make_unique<ToastService>(browser); |
| } |
| |
| collaboration::CollaborationService* service = |
| collaboration::CollaborationServiceFactory::GetForProfile( |
| browser->profile()); |
| if (service && service->GetServiceStatus().IsAllowedToJoin() && |
| tab_groups::IsTabGroupSyncServiceDesktopMigrationEnabled()) { |
| data_sharing_open_group_helper_ = |
| std::make_unique<DataSharingOpenGroupHelper>(browser); |
| } |
| } |
| |
| void BrowserWindowFeatures::InitPostBrowserViewConstruction( |
| BrowserView* browser_view) { |
| // TODO(crbug.com/346148093): Move SidePanelCoordinator construction to Init. |
| // TODO(crbug.com/346148554): Do not create a SidePanelCoordinator for most |
| // browser.h types |
| // Conceptually, SidePanelCoordinator handles the "model" whereas |
| // BrowserView::unified_side_panel_ handles the "ui". When we stop making this |
| // for most browser.h types, we should also stop making the |
| // unified_side_panel_. |
| side_panel_coordinator_ = |
| std::make_unique<SidePanelCoordinator>(browser_view); |
| side_panel_coordinator_->Init(browser_view->browser()); |
| |
| extension_side_panel_manager_ = |
| std::make_unique<extensions::ExtensionSidePanelManager>( |
| browser_view->browser(), |
| side_panel_coordinator_->GetWindowRegistry()); |
| |
| // Memory Saver mode is default off but is available to turn on. |
| // The controller relies on performance manager which isn't initialized in |
| // some unit tests without browser view. |
| if (browser_view->GetIsNormalType()) { |
| memory_saver_opt_in_iph_controller_ = |
| std::make_unique<MemorySaverOptInIPHController>( |
| browser_view->browser()); |
| |
| if (media_router::MediaRouterEnabled(browser_view->browser()->profile())) { |
| cast_browser_controller_ = |
| std::make_unique<media_router::CastBrowserController>( |
| browser_view->browser()); |
| } |
| } |
| |
| if (download::IsDownloadBubbleEnabled() && |
| features::IsToolbarPinningEnabled() && |
| base::FeatureList::IsEnabled(features::kPinnableDownloadsButton)) { |
| download_toolbar_ui_controller_ = |
| std::make_unique<DownloadToolbarUIController>(browser_view); |
| } |
| } |
| |
| void BrowserWindowFeatures::TearDownPreBrowserViewDestruction() { |
| memory_saver_opt_in_iph_controller_.reset(); |
| lens_overlay_entry_point_controller_.reset(); |
| |
| // TODO(crbug.com/346148093): This logic should not be gated behind a |
| // conditional. |
| if (side_panel_coordinator_) { |
| side_panel_coordinator_->TearDownPreBrowserViewDestruction(); |
| } |
| |
| if (mv2_disabled_dialog_controller_) { |
| mv2_disabled_dialog_controller_->TearDown(); |
| } |
| } |
| |
| SidePanelUI* BrowserWindowFeatures::side_panel_ui() { |
| return side_panel_coordinator_.get(); |
| } |
| |
| ToastController* BrowserWindowFeatures::toast_controller() { |
| return toast_service_ ? toast_service_->toast_controller() : nullptr; |
| } |
| |
| BrowserWindowFeatures::BrowserWindowFeatures() = default; |