|  | // Copyright 2012 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_commands.h" | 
|  |  | 
|  | #include <memory> | 
|  | #include <numeric> | 
|  | #include <optional> | 
|  | #include <utility> | 
|  | #include <variant> | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/check.h" | 
|  | #include "base/check_deref.h" | 
|  | #include "base/command_line.h" | 
|  | #include "base/feature_list.h" | 
|  | #include "base/i18n/rtl.h" | 
|  | #include "base/memory/scoped_refptr.h" | 
|  | #include "base/metrics/field_trial_params.h" | 
|  | #include "base/metrics/histogram_functions.h" | 
|  | #include "base/metrics/histogram_macros.h" | 
|  | #include "base/metrics/user_metrics.h" | 
|  | #include "base/strings/escape.h" | 
|  | #include "base/strings/utf_string_conversions.h" | 
|  | #include "base/trace_event/trace_event.h" | 
|  | #include "build/build_config.h" | 
|  | #include "chrome/app/chrome_command_ids.h" | 
|  | #include "chrome/browser/apps/app_service/app_launch_params.h" | 
|  | #include "chrome/browser/bookmarks/bookmark_model_factory.h" | 
|  | #include "chrome/browser/browser_process.h" | 
|  | #include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h" | 
|  | #include "chrome/browser/chained_back_navigation_tracker.h" | 
|  | #include "chrome/browser/commerce/browser_utils.h" | 
|  | #include "chrome/browser/content_settings/cookie_settings_factory.h" | 
|  | #include "chrome/browser/contextual_tasks/contextual_tasks_side_panel_coordinator.h" | 
|  | #include "chrome/browser/devtools/devtools_window.h" | 
|  | #include "chrome/browser/download/download_prefs.h" | 
|  | #include "chrome/browser/favicon/favicon_utils.h" | 
|  | #include "chrome/browser/feedback/show_feedback_page.h" | 
|  | #include "chrome/browser/lifetime/application_lifetime.h" | 
|  | #include "chrome/browser/media/router/media_router_feature.h" | 
|  | #include "chrome/browser/prefs/incognito_mode_prefs.h" | 
|  | #include "chrome/browser/profiles/profile.h" | 
|  | #include "chrome/browser/reading_list/reading_list_model_factory.h" | 
|  | #include "chrome/browser/search/search.h" | 
|  | #include "chrome/browser/search_engines/template_url_service_factory.h" | 
|  | #include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h" | 
|  | #include "chrome/browser/sessions/session_service.h" | 
|  | #include "chrome/browser/sessions/session_service_base.h" | 
|  | #include "chrome/browser/sessions/session_service_factory.h" | 
|  | #include "chrome/browser/sessions/session_service_lookup.h" | 
|  | #include "chrome/browser/sessions/tab_restore_service_factory.h" | 
|  | #include "chrome/browser/sharing_hub/sharing_hub_features.h" | 
|  | #include "chrome/browser/tab_group_sync/tab_group_sync_service_factory.h" | 
|  | #include "chrome/browser/translate/chrome_translate_client.h" | 
|  | #include "chrome/browser/ui/accelerator_utils.h" | 
|  | #include "chrome/browser/ui/autofill/address_bubbles_controller.h" | 
|  | #include "chrome/browser/ui/autofill/payments/filled_card_information_bubble_controller_impl.h" | 
|  | #include "chrome/browser/ui/autofill/payments/iban_bubble_controller_impl.h" | 
|  | #include "chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.h" | 
|  | #include "chrome/browser/ui/autofill/payments/offer_notification_bubble_controller_impl.h" | 
|  | #include "chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.h" | 
|  | #include "chrome/browser/ui/autofill/payments/virtual_card_enroll_bubble_controller_impl.h" | 
|  | #include "chrome/browser/ui/bookmarks/bookmark_bar_controller.h" | 
|  | #include "chrome/browser/ui/bookmarks/bookmark_stats.h" | 
|  | #include "chrome/browser/ui/bookmarks/bookmark_utils.h" | 
|  | #include "chrome/browser/ui/bookmarks/bookmark_utils_desktop.h" | 
|  | #include "chrome/browser/ui/browser.h" | 
|  | #include "chrome/browser/ui/browser_command_controller.h" | 
|  | #include "chrome/browser/ui/browser_dialogs.h" | 
|  | #include "chrome/browser/ui/browser_live_tab_context.h" | 
|  | #include "chrome/browser/ui/browser_navigator.h" | 
|  | #include "chrome/browser/ui/browser_navigator_params.h" | 
|  | #include "chrome/browser/ui/browser_tabstrip.h" | 
|  | #include "chrome/browser/ui/browser_window.h" | 
|  | #include "chrome/browser/ui/browser_window/public/browser_window_features.h" | 
|  | #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" | 
|  | #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h" | 
|  | #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h" | 
|  | #include "chrome/browser/ui/find_bar/find_bar.h" | 
|  | #include "chrome/browser/ui/find_bar/find_bar_controller.h" | 
|  | #include "chrome/browser/ui/intent_picker_tab_helper.h" | 
|  | #include "chrome/browser/ui/lens/lens_search_controller.h" | 
|  | #include "chrome/browser/ui/location_bar/location_bar.h" | 
|  | #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h" | 
|  | #include "chrome/browser/ui/qrcode_generator/qrcode_generator_bubble_controller.h" | 
|  | #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" | 
|  | #include "chrome/browser/ui/send_tab_to_self/send_tab_to_self_bubble.h" | 
|  | #include "chrome/browser/ui/sharing_hub/screenshot/screenshot_captured_bubble_controller.h" | 
|  | #include "chrome/browser/ui/sharing_hub/sharing_hub_bubble_controller.h" | 
|  | #include "chrome/browser/ui/startup/startup_tab.h" | 
|  | #include "chrome/browser/ui/status_bubble.h" | 
|  | #include "chrome/browser/ui/tab_contents/core_tab_helper.h" | 
|  | #include "chrome/browser/ui/tab_dialogs.h" | 
|  | #include "chrome/browser/ui/tabs/features.h" | 
|  | #include "chrome/browser/ui/tabs/new_tab_grouping_user_data.h" | 
|  | #include "chrome/browser/ui/tabs/organization/tab_organization_service.h" | 
|  | #include "chrome/browser/ui/tabs/organization/tab_organization_service_factory.h" | 
|  | #include "chrome/browser/ui/tabs/organization/tab_organization_session.h" | 
|  | #include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h" | 
|  | #include "chrome/browser/ui/tabs/split_tab_metrics.h" | 
|  | #include "chrome/browser/ui/tabs/split_tab_util.h" | 
|  | #include "chrome/browser/ui/tabs/tab_enums.h" | 
|  | #include "chrome/browser/ui/tabs/tab_group_model.h" | 
|  | #include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" | 
|  | #include "chrome/browser/ui/tabs/vertical_tab_strip_state_controller.h" | 
|  | #include "chrome/browser/ui/ui_features.h" | 
|  | #include "chrome/browser/ui/user_education/browser_user_education_interface.h" | 
|  | #include "chrome/browser/ui/views/frame/browser_view.h" | 
|  | #include "chrome/browser/ui/views/side_panel/side_panel_entry.h" | 
|  | #include "chrome/browser/ui/views/side_panel/side_panel_ui.h" | 
|  | #include "chrome/browser/ui/web_applications/app_browser_controller.h" | 
|  | #include "chrome/browser/ui/web_applications/web_app_launch_utils.h" | 
|  | #include "chrome/browser/ui/web_applications/web_app_tabbed_utils.h" | 
|  | #include "chrome/browser/ui/webui/commerce/product_specifications_disclosure_dialog.h" | 
|  | #include "chrome/browser/ui/webui/tab_search/tab_search.mojom.h" | 
|  | #include "chrome/browser/upgrade_detector/upgrade_detector.h" | 
|  | #include "chrome/browser/web_applications/web_app_constants.h" | 
|  | #include "chrome/browser/web_applications/web_app_helpers.h" | 
|  | #include "chrome/browser/web_applications/web_app_provider.h" | 
|  | #include "chrome/browser/web_applications/web_app_registrar.h" | 
|  | #include "chrome/common/chrome_features.h" | 
|  | #include "chrome/common/content_restriction.h" | 
|  | #include "chrome/common/pref_names.h" | 
|  | #include "chrome/common/url_constants.h" | 
|  | #include "chrome/grit/generated_resources.h" | 
|  | #include "components/bookmarks/browser/bookmark_model.h" | 
|  | #include "components/bookmarks/browser/bookmark_node.h" | 
|  | #include "components/bookmarks/browser/bookmark_utils.h" | 
|  | #include "components/bookmarks/common/bookmark_pref_names.h" | 
|  | #include "components/browsing_data/content/browsing_data_helper.h" | 
|  | #include "components/commerce/core/commerce_utils.h" | 
|  | #include "components/commerce/core/mojom/product_specifications.mojom.h" | 
|  | #include "components/commerce/core/pref_names.h" | 
|  | #include "components/content_settings/browser/page_specific_content_settings.h" | 
|  | #include "components/content_settings/core/browser/cookie_settings.h" | 
|  | #include "components/content_settings/core/common/content_settings.h" | 
|  | #include "components/content_settings/core/common/cookie_settings_base.h" | 
|  | #include "components/embedder_support/user_agent_utils.h" | 
|  | #include "components/favicon/content/content_favicon_driver.h" | 
|  | #include "components/feature_engagement/public/feature_constants.h" | 
|  | #include "components/find_in_page/find_tab_helper.h" | 
|  | #include "components/find_in_page/find_types.h" | 
|  | #include "components/google/core/common/google_util.h" | 
|  | #include "components/language_detection/core/constants.h" | 
|  | #include "components/lens/buildflags.h" | 
|  | #include "components/lens/lens_features.h" | 
|  | #include "components/lens/lens_overlay_invocation_source.h" | 
|  | #include "components/media_router/browser/media_router_dialog_controller.h"  // nogncheck | 
|  | #include "components/media_router/browser/media_router_metrics.h" | 
|  | #include "components/omnibox/browser/omnibox_prefs.h" | 
|  | #include "components/policy/core/common/policy_pref_names.h" | 
|  | #include "components/prefs/pref_service.h" | 
|  | #include "components/reading_list/core/reading_list_entry.h" | 
|  | #include "components/reading_list/core/reading_list_model.h" | 
|  | #include "components/reading_list/core/reading_list_pref_names.h" | 
|  | #include "components/saved_tab_groups/public/tab_group_sync_service.h" | 
|  | #include "components/services/app_service/public/cpp/app_launch_util.h" | 
|  | #include "components/sessions/core/live_tab_context.h" | 
|  | #include "components/sessions/core/tab_restore_service.h" | 
|  | #include "components/tab_groups/tab_group_id.h" | 
|  | #include "components/tab_groups/tab_group_visual_data.h" | 
|  | #include "components/tabs/public/split_tab_data.h" | 
|  | #include "components/tabs/public/split_tab_visual_data.h" | 
|  | #include "components/tabs/public/tab_collection.h" | 
|  | #include "components/tabs/public/tab_group.h" | 
|  | #include "components/tabs/public/tab_group_tab_collection.h" | 
|  | #include "components/tabs/public/tab_interface.h" | 
|  | #include "components/translate/core/browser/language_state.h" | 
|  | #include "components/translate/core/browser/translate_manager.h" | 
|  | #include "components/user_education/common/feature_promo/feature_promo_controller.h" | 
|  | #include "components/web_modal/web_contents_modal_dialog_manager.h" | 
|  | #include "components/webapps/common/web_app_id.h" | 
|  | #include "components/zoom/page_zoom.h" | 
|  | #include "components/zoom/zoom_controller.h" | 
|  | #include "content/public/browser/browsing_data_remover.h" | 
|  | #include "content/public/browser/devtools_agent_host.h" | 
|  | #include "content/public/browser/navigation_controller.h" | 
|  | #include "content/public/browser/navigation_entry.h" | 
|  | #include "content/public/browser/page_navigator.h" | 
|  | #include "content/public/browser/render_view_host.h" | 
|  | #include "content/public/browser/render_widget_host_view.h" | 
|  | #include "content/public/browser/web_contents.h" | 
|  | #include "content/public/browser/web_contents_observer.h" | 
|  | #include "content/public/common/content_switches.h" | 
|  | #include "content/public/common/url_constants.h" | 
|  | #include "content/public/common/url_utils.h" | 
|  | #include "extensions/buildflags/buildflags.h" | 
|  | #include "pdf/buildflags.h" | 
|  | #include "printing/buildflags/buildflags.h" | 
|  | #include "rlz/buildflags/buildflags.h" | 
|  | #include "services/metrics/public/cpp/ukm_builders.h" | 
|  | #include "services/metrics/public/cpp/ukm_recorder.h" | 
|  | #include "services/metrics/public/cpp/ukm_source_id.h" | 
|  | #include "ui/base/clipboard/clipboard_buffer.h" | 
|  | #include "ui/base/clipboard/scoped_clipboard_writer.h" | 
|  | #include "ui/base/models/list_selection_model.h" | 
|  | #include "ui/base/window_open_disposition.h" | 
|  | #include "ui/events/keycodes/keyboard_codes.h" | 
|  | #include "url/gurl.h" | 
|  | #include "url/url_constants.h" | 
|  |  | 
|  | #if BUILDFLAG(ENABLE_EXTENSIONS) | 
|  | #include "chrome/browser/ui/extensions/app_launch_params.h" | 
|  | #include "chrome/browser/ui/extensions/application_launch.h" | 
|  | #include "chrome/browser/ui/extensions/settings_api_bubble_helpers.h" | 
|  | #include "chrome/common/extensions/extension_metrics.h" | 
|  | #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" | 
|  | #include "extensions/browser/extension_registry.h" | 
|  | #include "extensions/browser/extension_system.h" | 
|  | #include "extensions/common/extension.h" | 
|  | #include "extensions/common/extension_set.h" | 
|  | #endif | 
|  |  | 
|  | #if BUILDFLAG(ENABLE_PDF) | 
|  | #include "chrome/browser/pdf/pdf_extension_util.h" | 
|  | #include "pdf/pdf_features.h" | 
|  | #endif | 
|  |  | 
|  | #if BUILDFLAG(ENABLE_PRINTING) | 
|  | #include "chrome/browser/printing/print_view_manager_common.h" | 
|  | #include "mojo/public/cpp/bindings/associated_remote.h" | 
|  | #if BUILDFLAG(ENABLE_PRINT_PREVIEW) | 
|  | #include "chrome/browser/printing/print_preview_dialog_controller.h" | 
|  | #endif  // BUILDFLAG(ENABLE_PRINT_PREVIEW) | 
|  | #endif  // BUILDFLAG(ENABLE_PRINTING) | 
|  |  | 
|  | #if BUILDFLAG(ENABLE_RLZ) | 
|  | #include "components/rlz/rlz_tracker.h"  // nogncheck | 
|  | #endif | 
|  |  | 
|  | #if BUILDFLAG(IS_CHROMEOS) | 
|  | #include "chrome/browser/chromeos/printing/print_preview/print_view_manager_common.h" | 
|  | #endif | 
|  |  | 
|  | #if !BUILDFLAG(IS_CHROMEOS) | 
|  | #include "chrome/browser/apps/link_capturing/enable_link_capturing_infobar_delegate.h" | 
|  | #endif | 
|  |  | 
|  | #if BUILDFLAG(IS_MAC) | 
|  | #include "chrome/browser/web_applications/extensions/launch.h" | 
|  | #endif | 
|  |  | 
|  | #if BUILDFLAG(ENABLE_LENS_DESKTOP_GOOGLE_BRANDED_FEATURES) | 
|  | #include "chrome/browser/lens/region_search/lens_region_search_controller.h" | 
|  | #include "chrome/browser/lens/region_search/lens_region_search_helper.h" | 
|  | #include "components/lens/lens_features.h" | 
|  | #endif | 
|  |  | 
|  | #if !BUILDFLAG(IS_ANDROID) | 
|  | #include "chrome/browser/ui/toasts/api/toast_id.h" | 
|  | #include "chrome/browser/ui/toasts/toast_controller.h" | 
|  | #include "chrome/browser/ui/toasts/toast_features.h" | 
|  | #endif | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const char kOsOverrideForTabletSite[] = "Linux; Android 9; Chrome tablet"; | 
|  | const char kChPlatformOverrideForTabletSite[] = "Android"; | 
|  |  | 
|  | // Creates a new tabbed browser window, with the same size, type and profile as | 
|  | // |original_browser|'s window, inserts |contents| into it, and shows it. | 
|  | void CreateAndShowNewWindowWithContents( | 
|  | std::unique_ptr<content::WebContents> contents, | 
|  | const Browser* original_browser) { | 
|  | Browser* new_browser = nullptr; | 
|  | DCHECK(!original_browser->is_type_app_popup()); | 
|  | if (original_browser->is_type_app()) { | 
|  | new_browser = Browser::Create(Browser::CreateParams::CreateForApp( | 
|  | original_browser->app_name(), original_browser->is_trusted_source(), | 
|  | gfx::Rect(), original_browser->profile(), true)); | 
|  | } else { | 
|  | new_browser = Browser::Create(Browser::CreateParams( | 
|  | original_browser->type(), original_browser->profile(), true)); | 
|  | } | 
|  | // Preserve the size of the original window. The new window has already | 
|  | // been given an offset by the OS, so we shouldn't copy the old bounds. | 
|  | BrowserWindow* new_window = new_browser->window(); | 
|  | new_window->SetBounds( | 
|  | gfx::Rect(new_window->GetRestoredBounds().origin(), | 
|  | original_browser->window()->GetRestoredBounds().size())); | 
|  |  | 
|  | // We need to show the browser now.  Otherwise ContainerWin assumes the | 
|  | // WebContents is invisible and won't size it. | 
|  | new_browser->window()->Show(); | 
|  |  | 
|  | // The page transition below is only for the purpose of inserting the tab. | 
|  | new_browser->tab_strip_model()->AddWebContents(std::move(contents), -1, | 
|  | ui::PAGE_TRANSITION_LINK, | 
|  | AddTabTypes::ADD_ACTIVE); | 
|  | } | 
|  |  | 
|  | bool GetTabURLAndTitleToSave(content::WebContents* web_contents, | 
|  | GURL* url, | 
|  | std::u16string* title) { | 
|  | // |web_contents| can be nullptr if the last tab in the browser was closed | 
|  | // but the browser wasn't closed yet. https://crbug.com/799668 | 
|  | if (!web_contents) { | 
|  | return false; | 
|  | } | 
|  | return chrome::GetURLAndTitleToBookmark(web_contents, url, title); | 
|  | } | 
|  |  | 
|  | ReadingListModel* GetReadingListModel(Browser* browser) { | 
|  | ReadingListModel* model = | 
|  | ReadingListModelFactory::GetForBrowserContext(browser->profile()); | 
|  | if (!model || !model->loaded()) { | 
|  | return nullptr;  // Ignore requests until model has loaded. | 
|  | } | 
|  | return model; | 
|  | } | 
|  |  | 
|  | bool CanMoveWebContentsToReadLater(Browser* browser, | 
|  | content::WebContents* web_contents, | 
|  | ReadingListModel* model, | 
|  | GURL* url, | 
|  | std::u16string* title) { | 
|  | return model && GetTabURLAndTitleToSave(web_contents, url, title) && | 
|  | model->IsUrlSupported(*url) && !browser->profile()->IsGuestSession(); | 
|  | } | 
|  |  | 
|  | bool BookmarkCurrentTabHelper(Browser* browser, | 
|  | bookmarks::BookmarkModel* model, | 
|  | GURL* url, | 
|  | std::u16string* title) { | 
|  | if (!model || !model->loaded()) { | 
|  | return false;  // Ignore requests until bookmarks are loaded. | 
|  | } | 
|  |  | 
|  | content::WebContents* const web_contents = | 
|  | browser->tab_strip_model()->GetActiveWebContents(); | 
|  | // |web_contents| can be nullptr if the last tab in the browser was closed | 
|  | // but the browser wasn't closed yet. https://crbug.com/799668 | 
|  | if (!web_contents) { | 
|  | return false; | 
|  | } | 
|  | if (!chrome::GetURLAndTitleToBookmark(web_contents, url, title)) { | 
|  | return false; | 
|  | } | 
|  | bool is_bookmarked_by_any = model->IsBookmarked(*url); | 
|  | if (!is_bookmarked_by_any && | 
|  | web_contents->GetBrowserContext()->IsOffTheRecord()) { | 
|  | // If we're incognito the favicon may not have been saved. Save it now | 
|  | // so that bookmarks have an icon for the page. | 
|  | favicon::SaveFaviconEvenIfInIncognito(web_contents); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | content::WebContents* DuplicateTabAt(Browser* browser, | 
|  | int index, | 
|  | int dst_index) { | 
|  | content::WebContents* contents = | 
|  | browser->tab_strip_model()->GetWebContentsAt(index); | 
|  | CHECK(contents); | 
|  | std::unique_ptr<content::WebContents> contents_dupe = contents->Clone(); | 
|  | content::WebContents* raw_contents_dupe = contents_dupe.get(); | 
|  |  | 
|  | bool pinned = false; | 
|  | if (browser->CanSupportWindowFeature(Browser::FEATURE_TABSTRIP)) { | 
|  | // If this is a tabbed browser, just create a duplicate tab inside the same | 
|  | // window next to the tab being duplicated. | 
|  | TabStripModel* tab_strip_model = browser->tab_strip_model(); | 
|  | pinned = tab_strip_model->IsTabPinned(index); | 
|  | int add_types = AddTabTypes::ADD_ACTIVE | AddTabTypes::ADD_INHERIT_OPENER | | 
|  | (pinned ? AddTabTypes::ADD_PINNED : 0); | 
|  | const auto old_group = tab_strip_model->GetTabGroupForTab(index); | 
|  | tab_strip_model->InsertWebContentsAt(dst_index, std::move(contents_dupe), | 
|  | add_types, old_group); | 
|  | } else { | 
|  | CreateAndShowNewWindowWithContents(std::move(contents_dupe), browser); | 
|  | } | 
|  |  | 
|  | SessionServiceBase* session_service = | 
|  | GetAppropriateSessionServiceIfExisting(browser); | 
|  | if (session_service) { | 
|  | session_service->TabRestored(raw_contents_dupe, pinned); | 
|  | } | 
|  | return raw_contents_dupe; | 
|  | } | 
|  |  | 
|  | void RecordTabCloseCount(int count) { | 
|  | base::UmaHistogramCounts100("TabStrip.Tab.HotkeyClosedCount", count); | 
|  | } | 
|  |  | 
|  | void CloseSelectedTabAndRecordTabCountMetric(BrowserWindowInterface* browser) { | 
|  | const int selected_tabs_count = | 
|  | browser->GetTabStripModel()->selection_model().size(); | 
|  | RecordTabCloseCount(selected_tabs_count); | 
|  |  | 
|  | browser->GetTabStripModel()->CloseSelectedTabs(); | 
|  | } | 
|  |  | 
|  | void MoveGroupToWindowImpl(Browser* source, | 
|  | Browser* target, | 
|  | tab_groups::TabGroupId group) { | 
|  | CHECK(source->tab_strip_model()->group_model()->ContainsTabGroup(group)); | 
|  |  | 
|  | tab_groups::TabGroupSyncService* tab_group_service = | 
|  | tab_groups::TabGroupSyncServiceFactory::GetForProfile(source->profile()); | 
|  |  | 
|  | std::unique_ptr<tab_groups::ScopedLocalObservationPauser> observation_pauser; | 
|  | if (tab_group_service && tab_group_service->GetGroup(group)) { | 
|  | observation_pauser = tab_group_service->CreateScopedLocalObserverPauser(); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<DetachedTabCollection> detached_group = | 
|  | source->tab_strip_model()->DetachTabGroupForInsertion(group); | 
|  | target->tab_strip_model()->InsertDetachedTabGroupAt(std::move(detached_group), | 
|  | 0); | 
|  |  | 
|  | target->window()->Show(); | 
|  | } | 
|  |  | 
|  | void MoveTabsToWindowImpl(Browser* source, | 
|  | Browser* target, | 
|  | const std::vector<int>& tab_indices) { | 
|  | if (tab_indices.empty()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | TabStripModel* source_model = source->tab_strip_model(); | 
|  | TabStripModel* target_model = target->tab_strip_model(); | 
|  |  | 
|  | // Store the active tab from the source tab strip since this will change as | 
|  | // tabs are detached. If the active tab from `source_model` isn't moving, | 
|  | // default to activating the first tab being moved. | 
|  | const tabs::TabInterface* active_tab = | 
|  | std::find(tab_indices.begin(), tab_indices.end(), | 
|  | source_model->active_index()) != tab_indices.end() | 
|  | ? source_model->GetActiveTab() | 
|  | : source_model->GetTabAtIndex(tab_indices[0]); | 
|  |  | 
|  | for (auto& tab_or_collection : | 
|  | source_model->DetachTabsAndCollectionsForInsertion(tab_indices)) { | 
|  | if (auto tab = | 
|  | std::get_if<std::unique_ptr<DetachedTab>>(&tab_or_collection)) { | 
|  | bool active = active_tab == tab->get()->tab.get(); | 
|  | bool pinned = tab->get()->was_pinned_at_time_of_removal; | 
|  | int add_types = (active ? AddTabTypes::ADD_ACTIVE : 0) | | 
|  | (pinned ? AddTabTypes::ADD_PINNED : 0); | 
|  | target_model->InsertDetachedTabAt(target_model->count(), | 
|  | std::move(tab->get()->tab), add_types); | 
|  | } else if (auto collection = | 
|  | std::get_if<std::unique_ptr<DetachedTabCollection>>( | 
|  | &tab_or_collection)) { | 
|  | if (std::holds_alternative<std::unique_ptr<tabs::TabGroupTabCollection>>( | 
|  | collection->get()->collection_)) { | 
|  | target_model->InsertDetachedTabGroupAt(std::move(*collection), | 
|  | target_model->count()); | 
|  | } else { | 
|  | bool pinned = collection->get()->pinned_; | 
|  | target_model->InsertDetachedSplitTabAt( | 
|  | std::move(*collection), | 
|  | pinned ? target_model->IndexOfFirstNonPinnedTab() | 
|  | : target_model->count(), | 
|  | pinned); | 
|  | } | 
|  | } | 
|  | } | 
|  | target->window()->Show(); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | using base::UserMetricsAction; | 
|  | using bookmarks::BookmarkModel; | 
|  | using content::NavigationController; | 
|  | using content::NavigationEntry; | 
|  | using content::OpenURLParams; | 
|  | using content::Referrer; | 
|  | using content::WebContents; | 
|  |  | 
|  | namespace chrome { | 
|  | namespace { | 
|  |  | 
|  | #if BUILDFLAG(ENABLE_EXTENSIONS) | 
|  | const extensions::Extension* GetExtensionForBrowser( | 
|  | BrowserWindowInterface* browser) { | 
|  | return extensions::ExtensionRegistry::Get(browser->GetProfile()) | 
|  | ->GetExtensionById(web_app::GetAppIdFromApplicationName( | 
|  | browser->GetBrowserForMigrationOnly()->app_name()), | 
|  | extensions::ExtensionRegistry::EVERYTHING); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | // Based on |disposition|, creates a new tab as necessary, and returns the | 
|  | // appropriate tab to navigate.  If that tab is the |current_tab|, reverts the | 
|  | // location bar contents, since all browser-UI-triggered navigations should | 
|  | // revert any omnibox edits in the |current_tab|. | 
|  | WebContents* GetTabAndRevertIfNecessaryHelper(Browser* browser, | 
|  | WindowOpenDisposition disposition, | 
|  | WebContents* current_tab) { | 
|  | switch (disposition) { | 
|  | case WindowOpenDisposition::NEW_FOREGROUND_TAB: | 
|  | case WindowOpenDisposition::NEW_BACKGROUND_TAB: { | 
|  | std::unique_ptr<WebContents> new_tab = current_tab->Clone(); | 
|  | WebContents* raw_new_tab = new_tab.get(); | 
|  | if (disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB) { | 
|  | new_tab->WasHidden(); | 
|  | } | 
|  | const int index = | 
|  | browser->tab_strip_model()->GetIndexOfWebContents(current_tab); | 
|  | const auto group = browser->tab_strip_model()->GetTabGroupForTab(index); | 
|  | browser->tab_strip_model()->AddWebContents( | 
|  | std::move(new_tab), -1, ui::PAGE_TRANSITION_LINK, | 
|  | (disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB) | 
|  | ? AddTabTypes::ADD_ACTIVE | 
|  | : AddTabTypes::ADD_NONE, | 
|  | group); | 
|  | return raw_new_tab; | 
|  | } | 
|  | case WindowOpenDisposition::NEW_WINDOW: { | 
|  | std::unique_ptr<WebContents> new_tab = current_tab->Clone(); | 
|  | WebContents* raw_new_tab = new_tab.get(); | 
|  | Browser* new_browser = | 
|  | Browser::Create(Browser::CreateParams(browser->profile(), true)); | 
|  | new_browser->tab_strip_model()->AddWebContents(std::move(new_tab), -1, | 
|  | ui::PAGE_TRANSITION_LINK, | 
|  | AddTabTypes::ADD_ACTIVE); | 
|  | new_browser->window()->Show(); | 
|  | return raw_new_tab; | 
|  | } | 
|  | default: | 
|  | browser->window()->GetLocationBar()->Revert(); | 
|  | return current_tab; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Like the above, but auto-computes the current tab | 
|  | WebContents* GetTabAndRevertIfNecessary(Browser* browser, | 
|  | WindowOpenDisposition disposition) { | 
|  | WebContents* activate_tab = | 
|  | browser->tab_strip_model()->GetActiveWebContents(); | 
|  | return GetTabAndRevertIfNecessaryHelper(browser, disposition, activate_tab); | 
|  | } | 
|  |  | 
|  | void RecordReloadWithCookieBlocking(BrowserWindowInterface* browser, | 
|  | WebContents* web_contents) { | 
|  | // Figure out if 3P cookies are blocked for this page. | 
|  | scoped_refptr<const content_settings::CookieSettings> cookie_settings = | 
|  | CookieSettingsFactory::GetForProfile(browser->GetProfile()); | 
|  |  | 
|  | // For this metric, we define "cookies blocked in settings" based on the | 
|  | // global opt-in to third-party cookie blocking as well as no overriding | 
|  | // content setting on the top-level site. | 
|  | bool cookies_blocked_in_settings = | 
|  | cookie_settings->ShouldBlockThirdPartyCookies() && | 
|  | !cookie_settings->IsThirdPartyAccessAllowed( | 
|  | web_contents->GetLastCommittedURL(), nullptr); | 
|  |  | 
|  | // Also measure if 3P cookies were actually blocked on the site. | 
|  | content_settings::PageSpecificContentSettings* pscs = | 
|  | content_settings::PageSpecificContentSettings::GetForFrame( | 
|  | web_contents->GetPrimaryMainFrame()); | 
|  | bool cookies_blocked = | 
|  | pscs && pscs->blocked_browsing_data_model()->size() > 0U; | 
|  |  | 
|  | ukm::SourceId source_id = | 
|  | web_contents->GetPrimaryMainFrame()->GetPageUkmSourceId(); | 
|  |  | 
|  | ukm::builders::ThirdPartyCookies_BreakageIndicator_UserReload(source_id) | 
|  | .SetTPCBlocked(cookies_blocked) | 
|  | .SetTPCBlockedInSettings(cookies_blocked_in_settings) | 
|  | .Record(ukm::UkmRecorder::Get()); | 
|  | } | 
|  |  | 
|  | void ReloadInternal(BrowserWindowInterface* browser, | 
|  | WindowOpenDisposition disposition, | 
|  | bool bypass_cache) { | 
|  | TabStripModel* const tab_strip_model = browser->GetTabStripModel(); | 
|  | WebContents* const active_contents = tab_strip_model->GetActiveWebContents(); | 
|  |  | 
|  | std::vector<WebContents*> tabs_to_reload; | 
|  |  | 
|  | if (base::FeatureList::IsEnabled(features::kReloadSelectionModel)) { | 
|  | tabs_to_reload.push_back(active_contents); | 
|  | } else { | 
|  | // Reloading a tab may change the selection (see crbug.com/339061099), so | 
|  | // take | 
|  | // a defensive copy into a more stable form before we begin. We take | 
|  | // WebContents* so we can follow the tabs as they shift within the same | 
|  | // tabstrip (e.g. if `disposition` is NEW_BACKGROUND_TAB). | 
|  | for (const int selected_index : | 
|  | tab_strip_model->selection_model().selected_indices()) { | 
|  | tabs_to_reload.push_back( | 
|  | tab_strip_model->GetWebContentsAt(selected_index)); | 
|  | } | 
|  | } | 
|  |  | 
|  | base::UmaHistogramCounts100("TabStrip.Tab.ReloadCount", | 
|  | tabs_to_reload.size()); | 
|  |  | 
|  | for (WebContents* const tab : tabs_to_reload) { | 
|  | // Skip this tab if it is no longer part of this tabstrip. N.B. we do this | 
|  | // instead of using WeakPtr<WebContents> because we do not want to reload | 
|  | // tabs that move to another browser. | 
|  | if (tab_strip_model->GetIndexOfWebContents(tab) == TabStripModel::kNoTab) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | WebContents* const new_tab = GetTabAndRevertIfNecessaryHelper( | 
|  | browser->GetBrowserForMigrationOnly(), disposition, tab); | 
|  |  | 
|  | // If the `tab` is the activated page, give the focus to it, as this is | 
|  | // caused by a user action | 
|  | if (tab == active_contents && !new_tab->FocusLocationBarByDefault()) { | 
|  | new_tab->Focus(); | 
|  | } | 
|  |  | 
|  | // User reloads is a possible breakage indicator from blocking 3P cookies. | 
|  | RecordReloadWithCookieBlocking(browser, tab); | 
|  |  | 
|  | DevToolsWindow* const devtools = | 
|  | DevToolsWindow::GetInstanceForInspectedWebContents(new_tab); | 
|  | constexpr content::ReloadType kBypassingType = | 
|  | content::ReloadType::BYPASSING_CACHE; | 
|  | constexpr content::ReloadType kNormalType = content::ReloadType::NORMAL; | 
|  | if (!devtools || !devtools->ReloadInspectedWebContents(bypass_cache)) { | 
|  | new_tab->GetController().Reload( | 
|  | bypass_cache ? kBypassingType : kNormalType, true); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool IsShowingWebContentsModalDialog(BrowserWindowInterface* bwi) { | 
|  | WebContents* const web_contents = | 
|  | bwi->GetTabStripModel()->GetActiveWebContents(); | 
|  | if (!web_contents) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // TODO(gbillock): This is currently called in production by the CanPrint | 
|  | // method, and may be too restrictive if we allow print preview to overlap. | 
|  | // Re-assess how to queue print preview after we know more about popup | 
|  | // management policy. | 
|  | const web_modal::WebContentsModalDialogManager* manager = | 
|  | web_modal::WebContentsModalDialogManager::FromWebContents(web_contents); | 
|  | return manager && manager->IsDialogActive(); | 
|  | } | 
|  |  | 
|  | #if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG) | 
|  | bool PrintPreviewShowing(const Browser* browser) { | 
|  | #if BUILDFLAG(ENABLE_PRINT_PREVIEW) | 
|  | WebContents* contents = browser->tab_strip_model()->GetActiveWebContents(); | 
|  | auto* controller = printing::PrintPreviewDialogController::GetInstance(); | 
|  | CHECK(controller); | 
|  | return controller->GetPrintPreviewForContents(contents) || | 
|  | controller->is_creating_print_preview_dialog(); | 
|  | #else | 
|  | return false; | 
|  | #endif | 
|  | } | 
|  | #endif  // BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG) | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | bool IsCommandEnabled(Browser* browser, int command) { | 
|  | return browser->command_controller()->IsCommandEnabled(command); | 
|  | } | 
|  |  | 
|  | bool SupportsCommand(Browser* browser, int command) { | 
|  | return browser->command_controller()->SupportsCommand(command); | 
|  | } | 
|  |  | 
|  | bool ExecuteCommand(BrowserWindowInterface* bwi, | 
|  | int command, | 
|  | base::TimeTicks time_stamp) { | 
|  | return bwi->GetFeatures().browser_command_controller()->ExecuteCommand( | 
|  | command, time_stamp); | 
|  | } | 
|  |  | 
|  | bool ExecuteCommandWithDisposition(Browser* browser, | 
|  | int command, | 
|  | WindowOpenDisposition disposition) { | 
|  | return browser->command_controller()->ExecuteCommandWithDisposition( | 
|  | command, disposition); | 
|  | } | 
|  |  | 
|  | void UpdateCommandEnabled(Browser* browser, int command, bool enabled) { | 
|  | browser->command_controller()->UpdateCommandEnabled(command, enabled); | 
|  | } | 
|  |  | 
|  | void AddCommandObserver(Browser* browser, | 
|  | int command, | 
|  | CommandObserver* observer) { | 
|  | browser->command_controller()->AddCommandObserver(command, observer); | 
|  | } | 
|  |  | 
|  | void RemoveCommandObserver(Browser* browser, | 
|  | int command, | 
|  | CommandObserver* observer) { | 
|  | browser->command_controller()->RemoveCommandObserver(command, observer); | 
|  | } | 
|  |  | 
|  | int GetContentRestrictions(const BrowserWindowInterface* bwi) { | 
|  | int content_restrictions = 0; | 
|  | WebContents* const current_tab = | 
|  | bwi->GetTabStripModel()->GetActiveWebContents(); | 
|  | if (current_tab) { | 
|  | CoreTabHelper* core_tab_helper = | 
|  | CoreTabHelper::FromWebContents(current_tab); | 
|  | content_restrictions = core_tab_helper->content_restrictions(); | 
|  | NavigationEntry* last_committed_entry = | 
|  | current_tab->GetController().GetLastCommittedEntry(); | 
|  | if (!content::IsSavableURL(last_committed_entry->GetURL())) { | 
|  | content_restrictions |= CONTENT_RESTRICTION_SAVE; | 
|  | } | 
|  | } | 
|  | return content_restrictions; | 
|  | } | 
|  |  | 
|  | void NewEmptyWindow(Profile* profile, bool should_trigger_session_restore) { | 
|  | bool off_the_record = profile->IsOffTheRecord(); | 
|  | PrefService* prefs = profile->GetPrefs(); | 
|  | if (off_the_record) { | 
|  | if (IncognitoModePrefs::GetAvailability(prefs) == | 
|  | policy::IncognitoModeAvailability::kDisabled) { | 
|  | off_the_record = false; | 
|  | } | 
|  | } else if (profile->IsGuestSession() || | 
|  | IncognitoModePrefs::ShouldOpenSubsequentBrowsersInIncognito( | 
|  | *base::CommandLine::ForCurrentProcess(), prefs)) { | 
|  | off_the_record = true; | 
|  | } | 
|  |  | 
|  | if (off_the_record) { | 
|  | // This metric counts the Incognito and Off-The-Record Guest profiles | 
|  | // together. | 
|  | base::RecordAction(UserMetricsAction("NewIncognitoWindow")); | 
|  | if (profile->IsGuestSession()) { | 
|  | base::RecordAction(UserMetricsAction("NewGuestWindow")); | 
|  | } else { | 
|  | base::RecordAction(UserMetricsAction("NewIncognitoWindow2")); | 
|  | } | 
|  | OpenEmptyWindow(profile->GetPrimaryOTRProfile(/*create_if_needed=*/true), | 
|  | should_trigger_session_restore); | 
|  | } else if (!should_trigger_session_restore) { | 
|  | base::RecordAction(UserMetricsAction("NewWindow")); | 
|  | OpenEmptyWindow(profile->GetOriginalProfile(), | 
|  | /*should_trigger_session_restore=*/false); | 
|  | } else { | 
|  | base::RecordAction(UserMetricsAction("NewWindow")); | 
|  | SessionService* session_service = | 
|  | SessionServiceFactory::GetForProfileForSessionRestore( | 
|  | profile->GetOriginalProfile()); | 
|  | if (!session_service || | 
|  | !session_service->RestoreIfNecessary(StartupTabs(), | 
|  | /* restore_apps */ false)) { | 
|  | OpenEmptyWindow(profile->GetOriginalProfile()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | Browser* OpenEmptyWindow(Profile* profile, | 
|  | bool should_trigger_session_restore) { | 
|  | if (Browser::GetCreationStatusForProfile(profile) != | 
|  | Browser::CreationStatus::kOk) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | // Don't create a new window when the profile is shutting down. | 
|  | if (profile->ShutdownStarted()) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | Browser::CreateParams params = | 
|  | Browser::CreateParams(Browser::TYPE_NORMAL, profile, true); | 
|  | params.should_trigger_session_restore = should_trigger_session_restore; | 
|  | Browser* browser = Browser::Create(params); | 
|  |  | 
|  | // Startup tabs could be created during browser creation. Add an empty tab | 
|  | // only if no tabs are created. | 
|  | if (browser->tab_strip_model()->empty()) { | 
|  | AddTabAt(browser, GURL(), -1, true); | 
|  | } | 
|  |  | 
|  | browser->window()->Show(); | 
|  | return browser; | 
|  | } | 
|  |  | 
|  | void OpenWindowWithRestoredTabs(Profile* profile) { | 
|  | sessions::TabRestoreService* service = | 
|  | TabRestoreServiceFactory::GetForProfile(profile); | 
|  | if (service) { | 
|  | service->RestoreMostRecentEntry(nullptr); | 
|  | } | 
|  | } | 
|  |  | 
|  | void OpenURLOffTheRecord(Profile* profile, const GURL& url) { | 
|  | ScopedTabbedBrowserDisplayer displayer( | 
|  | profile->GetPrimaryOTRProfile(/*create_if_needed=*/true)); | 
|  | AddSelectedTabWithURL(displayer.browser(), url, ui::PAGE_TRANSITION_LINK); | 
|  | } | 
|  |  | 
|  | bool CanGoBack(const Browser* browser) { | 
|  | return browser->tab_strip_model() | 
|  | ->GetActiveWebContents() | 
|  | ->GetController() | 
|  | .CanGoBack(); | 
|  | } | 
|  |  | 
|  | bool CanGoBack(content::WebContents* web_contents) { | 
|  | return web_contents->GetController().CanGoBack(); | 
|  | } | 
|  |  | 
|  | bool ShouldEnableBackButton(const Browser* browser) { | 
|  | return browser->tab_strip_model() | 
|  | ->GetActiveWebContents() | 
|  | ->GetController() | 
|  | .ShouldEnableBackButton(); | 
|  | } | 
|  |  | 
|  | enum class BackNavigationMenuIPHTrigger : int { | 
|  | kUserPerformsManyBackNavigation = 0, | 
|  | kUserPerformsChainedBackNavigation, | 
|  | kUserPerformsChainedBackNavigationWithBackButton | 
|  | }; | 
|  |  | 
|  | const char kBackNavigationMenuIPHExperimentParamName[] = "x_experiment"; | 
|  |  | 
|  | void MaybeShowFeatureBackNavigationMenuPromo(Browser* browser, | 
|  | WebContents* web_contents) { | 
|  | if (!base::FeatureList::IsEnabled( | 
|  | feature_engagement::kIPHBackNavigationMenuFeature)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | bool should_show_feature_promo; | 
|  | const ChainedBackNavigationTracker* tracker = | 
|  | ChainedBackNavigationTracker::FromWebContents(web_contents); | 
|  | CHECK(tracker); | 
|  | switch (static_cast<BackNavigationMenuIPHTrigger>( | 
|  | base::GetFieldTrialParamByFeatureAsInt( | 
|  | feature_engagement::kIPHBackNavigationMenuFeature, | 
|  | kBackNavigationMenuIPHExperimentParamName, 0))) { | 
|  | case BackNavigationMenuIPHTrigger::kUserPerformsChainedBackNavigation: | 
|  | should_show_feature_promo = | 
|  | tracker->IsChainedBackNavigationRecentlyPerformed(); | 
|  | break; | 
|  |  | 
|  | case BackNavigationMenuIPHTrigger:: | 
|  | kUserPerformsChainedBackNavigationWithBackButton: | 
|  | should_show_feature_promo = | 
|  | tracker->IsBackButtonChainedBackNavigationRecentlyPerformed(); | 
|  | break; | 
|  | default: | 
|  | should_show_feature_promo = true; | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (should_show_feature_promo) { | 
|  | BrowserUserEducationInterface::From(browser)->MaybeShowFeaturePromo( | 
|  | feature_engagement::kIPHBackNavigationMenuFeature); | 
|  | } | 
|  | } | 
|  |  | 
|  | void GoBack(Browser* browser, WindowOpenDisposition disposition) { | 
|  | base::RecordAction(UserMetricsAction("Back")); | 
|  |  | 
|  | if (CanGoBack(browser)) { | 
|  | WebContents* new_tab = GetTabAndRevertIfNecessary(browser, disposition); | 
|  | new_tab->GetController().GoBack(); | 
|  | MaybeShowFeatureBackNavigationMenuPromo(browser, new_tab); | 
|  | } | 
|  | } | 
|  |  | 
|  | void GoBack(content::WebContents* web_contents) { | 
|  | base::RecordAction(UserMetricsAction("Back")); | 
|  |  | 
|  | if (CanGoBack(web_contents)) { | 
|  | web_contents->GetController().GoBack(); | 
|  | Browser* browser = chrome::FindBrowserWithTab(web_contents); | 
|  | if (browser) { | 
|  | MaybeShowFeatureBackNavigationMenuPromo(browser, web_contents); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool CanGoForward(const Browser* browser) { | 
|  | return browser->tab_strip_model() | 
|  | ->GetActiveWebContents() | 
|  | ->GetController() | 
|  | .CanGoForward(); | 
|  | } | 
|  |  | 
|  | bool CanGoForward(content::WebContents* web_contents) { | 
|  | return web_contents->GetController().CanGoForward(); | 
|  | } | 
|  |  | 
|  | bool ShouldEnableForwardButton(const Browser* browser) { | 
|  | return browser->tab_strip_model() | 
|  | ->GetActiveWebContents() | 
|  | ->GetController() | 
|  | .ShouldEnableForwardButton(); | 
|  | } | 
|  |  | 
|  | void GoForward(Browser* browser, WindowOpenDisposition disposition) { | 
|  | base::RecordAction(UserMetricsAction("Forward")); | 
|  | if (CanGoForward(browser)) { | 
|  | GetTabAndRevertIfNecessary(browser, disposition) | 
|  | ->GetController() | 
|  | .GoForward(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void GoForward(content::WebContents* web_contents) { | 
|  | base::RecordAction(UserMetricsAction("Forward")); | 
|  | if (CanGoForward(web_contents)) { | 
|  | web_contents->GetController().GoForward(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void NavigateToIndexWithDisposition(Browser* browser, | 
|  | int index, | 
|  | WindowOpenDisposition disposition) { | 
|  | NavigationController* controller = | 
|  | &GetTabAndRevertIfNecessary(browser, disposition)->GetController(); | 
|  | DCHECK_GE(index, 0); | 
|  | DCHECK_LT(index, controller->GetEntryCount()); | 
|  | controller->GoToIndex(index); | 
|  | } | 
|  |  | 
|  | void Reload(BrowserWindowInterface* browser, | 
|  | WindowOpenDisposition disposition) { | 
|  | base::RecordAction(UserMetricsAction("Reload")); | 
|  | ReloadInternal(browser, disposition, false); | 
|  | } | 
|  |  | 
|  | void ReloadBypassingCache(Browser* browser, WindowOpenDisposition disposition) { | 
|  | base::RecordAction(UserMetricsAction("ReloadBypassingCache")); | 
|  | ReloadInternal(browser, disposition, true); | 
|  | } | 
|  |  | 
|  | bool CanReload(const Browser* browser) { | 
|  | return browser && !browser->is_type_devtools() && | 
|  | !browser->is_type_picture_in_picture(); | 
|  | } | 
|  |  | 
|  | void Home(Browser* browser, WindowOpenDisposition disposition) { | 
|  | base::RecordAction(UserMetricsAction("Home")); | 
|  |  | 
|  | std::string extra_headers; | 
|  | #if BUILDFLAG(ENABLE_RLZ) | 
|  | // If the home page is a Google home page, add the RLZ header to the request. | 
|  | PrefService* pref_service = browser->profile()->GetPrefs(); | 
|  | if (pref_service) { | 
|  | if (google_util::IsGoogleHomePageUrl( | 
|  | GURL(pref_service->GetString(prefs::kHomePage)))) { | 
|  | extra_headers = rlz::RLZTracker::GetAccessPointHttpHeader( | 
|  | rlz::RLZTracker::ChromeHomePage()); | 
|  | } | 
|  | } | 
|  | #endif  // BUILDFLAG(ENABLE_RLZ) | 
|  |  | 
|  | GURL url = browser->profile()->GetHomePage(); | 
|  |  | 
|  | #if BUILDFLAG(ENABLE_EXTENSIONS) | 
|  | // With bookmark apps enabled, hosted apps should return to their launch page | 
|  | // when the home button is pressed. | 
|  | if (browser->is_type_app() || browser->is_type_app_popup()) { | 
|  | const extensions::Extension* extension = GetExtensionForBrowser(browser); | 
|  | if (!extension) { | 
|  | return; | 
|  | } | 
|  | url = extensions::AppLaunchInfo::GetLaunchWebURL(extension); | 
|  | } | 
|  |  | 
|  | if (disposition == WindowOpenDisposition::CURRENT_TAB || | 
|  | disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB) { | 
|  | extensions::MaybeShowExtensionControlledHomeNotification( | 
|  | browser, browser->tab_strip_model()->GetActiveWebContents()); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | bool is_chrome_internal = url.SchemeIs(url::kAboutScheme) || | 
|  | url.SchemeIs(content::kChromeUIScheme) || | 
|  | url.SchemeIs(chrome::kChromeNativeScheme); | 
|  | base::UmaHistogramBoolean("Navigation.Home.IsChromeInternal", | 
|  | is_chrome_internal); | 
|  | // Log a user action for the !is_chrome_internal case. This value is used as | 
|  | // part of a high-level guiding metric, which is being migrated to user | 
|  | // actions. | 
|  | if (!is_chrome_internal) { | 
|  | base::RecordAction( | 
|  | base::UserMetricsAction("Navigation.Home.NotChromeInternal")); | 
|  | } | 
|  | OpenURLParams params( | 
|  | url, Referrer(), disposition, | 
|  | ui::PageTransitionFromInt(ui::PAGE_TRANSITION_AUTO_BOOKMARK | | 
|  | ui::PAGE_TRANSITION_HOME_PAGE), | 
|  | false); | 
|  | params.extra_headers = extra_headers; | 
|  | browser->OpenURL(params, /*navigation_handle_callback=*/{}); | 
|  | } | 
|  |  | 
|  | base::WeakPtr<content::NavigationHandle> OpenCurrentURL(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("LoadURL")); | 
|  | // TODO(crbug.com/40820294): Eliminate extra checks once source of | 
|  | //  bad pointer dereference is identified. See also TODO comment below. | 
|  | CHECK(browser); | 
|  | BrowserWindow* window = browser->window(); | 
|  | CHECK(window); | 
|  | LocationBar* location_bar = window->GetLocationBar(); | 
|  | if (!location_bar) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | GURL url(location_bar->navigation_params().destination_url); | 
|  | TRACE_EVENT1("navigation", "chrome::OpenCurrentURL", "url", url); | 
|  |  | 
|  | if (ShouldInterceptChromeURLNavigationInIncognito(browser, url)) { | 
|  | ProcessInterceptedChromeURLNavigationInIncognito(browser, url); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | NavigateParams params(browser, url, | 
|  | location_bar->navigation_params().transition); | 
|  | params.disposition = location_bar->navigation_params().disposition; | 
|  | // Use ADD_INHERIT_OPENER so that all pages opened by the omnibox at least | 
|  | // inherit the opener. In some cases the tabstrip will determine the group | 
|  | // should be inherited, in which case the group is inherited instead of the | 
|  | // opener. | 
|  | params.tabstrip_add_types = | 
|  | AddTabTypes::ADD_FORCE_INDEX | AddTabTypes::ADD_INHERIT_OPENER; | 
|  | params.input_start = | 
|  | location_bar->navigation_params().match_selection_timestamp; | 
|  | params.is_using_https_as_default_scheme = | 
|  | location_bar->navigation_params().url_typed_without_scheme; | 
|  | params.url_typed_with_http_scheme = | 
|  | location_bar->navigation_params().url_typed_with_http_scheme; | 
|  | params.extra_headers = location_bar->navigation_params().extra_headers; | 
|  | auto result = Navigate(¶ms); | 
|  |  | 
|  | #if BUILDFLAG(ENABLE_EXTENSIONS) | 
|  | DCHECK(extensions::ExtensionSystem::Get(browser->profile()) | 
|  | ->extension_service()); | 
|  | // TODO(crbug.com/40820294): Eliminate extra checks once source of | 
|  | //  bad pointer dereference is identified. See also TODO comment above. | 
|  | extensions::ExtensionRegistry* extension_registry = | 
|  | extensions::ExtensionRegistry::Get(browser->profile()); | 
|  | CHECK(extension_registry); | 
|  | const extensions::Extension* extension = | 
|  | extension_registry->enabled_extensions().GetAppByURL(url); | 
|  | if (extension) { | 
|  | extensions::RecordAppLaunchType(extension_misc::APP_LAUNCH_OMNIBOX_LOCATION, | 
|  | extension->GetType()); | 
|  | } | 
|  | #endif | 
|  | return result; | 
|  | } | 
|  |  | 
|  | void Stop(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("Stop")); | 
|  | browser->tab_strip_model()->GetActiveWebContents()->Stop(); | 
|  | } | 
|  |  | 
|  | void NewWindow(BrowserWindowInterface* browser) { | 
|  | Profile* const profile = browser->GetProfile(); | 
|  | #if BUILDFLAG(IS_MAC) | 
|  | // Web apps should open a window to their launch page. | 
|  | if (web_app::AppBrowserController* const app_browser_controller = | 
|  | browser->GetFeatures().app_browser_controller()) { | 
|  | const webapps::AppId app_id = app_browser_controller->app_id(); | 
|  |  | 
|  | auto launch_container = apps::LaunchContainer::kLaunchContainerWindow; | 
|  |  | 
|  | auto* provider = web_app::WebAppProvider::GetForWebApps(profile); | 
|  | if (provider && provider->registrar_unsafe().GetAppEffectiveDisplayMode( | 
|  | app_id) == blink::mojom::DisplayMode::kBrowser) { | 
|  | launch_container = apps::LaunchContainer::kLaunchContainerTab; | 
|  | } | 
|  | apps::AppLaunchParams params = apps::AppLaunchParams( | 
|  | app_id, launch_container, WindowOpenDisposition::NEW_WINDOW, | 
|  | apps::LaunchSource::kFromKeyboard); | 
|  | web_app::LaunchExtensionOrWebApp(profile, std::move(params), | 
|  | base::DoNothing()); | 
|  | return; | 
|  | } | 
|  |  | 
|  | #if BUILDFLAG(ENABLE_EXTENSIONS) | 
|  | // Hosted apps should open a window to their launch page. | 
|  | const extensions::Extension* extension = GetExtensionForBrowser(browser); | 
|  | if (extension && extension->is_hosted_app()) { | 
|  | const auto app_launch_params = CreateAppLaunchParamsUserContainer( | 
|  | profile, extension, WindowOpenDisposition::NEW_WINDOW, | 
|  | apps::LaunchSource::kFromKeyboard); | 
|  | OpenApplicationWindow( | 
|  | profile, app_launch_params, | 
|  | extensions::AppLaunchInfo::GetLaunchWebURL(extension)); | 
|  | return; | 
|  | } | 
|  | #endif  // BUILDFLAG(ENABLE_EXTENSIONS) | 
|  | #endif  // BUILDFLAG(IS_MAC) | 
|  | NewEmptyWindow(profile->GetOriginalProfile()); | 
|  | } | 
|  |  | 
|  | void NewIncognitoWindow(Profile* profile) { | 
|  | NewEmptyWindow(profile->GetPrimaryOTRProfile(/*create_if_needed=*/true)); | 
|  | } | 
|  |  | 
|  | void CloseWindow(BrowserWindowInterface* browser) { | 
|  | base::RecordAction(UserMetricsAction("CloseWindow")); | 
|  | browser->GetWindow()->Close(); | 
|  | } | 
|  |  | 
|  | content::WebContents& NewTab(Browser* browser, NewTabTypes context) { | 
|  | if (context != NewTabTypes::NO_USER_ACTION) { | 
|  | base::RecordAction(base::UserMetricsAction("NewTab")); | 
|  | } | 
|  |  | 
|  | UMA_HISTOGRAM_ENUMERATION("Tab.NewTab", context, | 
|  | NewTabTypes::NEW_TAB_ENUM_COUNT); | 
|  |  | 
|  | browser->profile()->SetUserData( | 
|  | NewTabGroupingUserData::kNewTabGroupingUserDataKey, | 
|  | std::make_unique<NewTabGroupingUserData>( | 
|  | browser->tab_strip_model()->GetActiveTabGroupId())); | 
|  |  | 
|  | if (browser->SupportsWindowFeature(Browser::FEATURE_TABSTRIP)) { | 
|  | std::optional<tab_groups::TabGroupId> group_id = std::nullopt; | 
|  |  | 
|  | if (features::IsNewTabAddsToActiveGroupEnabled()) { | 
|  | const int index = browser->tab_strip_model()->active_index(); | 
|  | group_id = browser->tab_strip_model()->GetTabGroupForTab(index); | 
|  | } | 
|  |  | 
|  | return *AddAndReturnTabAt(browser, GURL(), -1, true, group_id); | 
|  | } | 
|  |  | 
|  | ScopedTabbedBrowserDisplayer displayer(browser->profile()); | 
|  | Browser* b = displayer.browser(); | 
|  | auto* contents = AddAndReturnTabAt(b, GURL(), -1, true); | 
|  | b->window()->Show(); | 
|  | // The call to AddBlankTabAt above did not set the focus to the tab as its | 
|  | // window was not active, so we have to do it explicitly. | 
|  | // See http://crbug.com/6380. | 
|  | b->tab_strip_model()->GetActiveWebContents()->RestoreFocus(); | 
|  |  | 
|  | return *contents; | 
|  | } | 
|  |  | 
|  | void NewTabToRight(Browser* browser) { | 
|  | browser->tab_strip_model()->ExecuteContextMenuCommand( | 
|  | browser->tab_strip_model()->active_index(), | 
|  | TabStripModel::CommandNewTabToRight); | 
|  | } | 
|  |  | 
|  | void CloseTab(BrowserWindowInterface* browser) { | 
|  | base::RecordAction(UserMetricsAction("CloseTab_Accelerator")); | 
|  |  | 
|  | // If the selection model consists of only the indices of a single split tab, | 
|  | // decide if just the active tab in the split is closed instead of all tabs in | 
|  | // the split. | 
|  | const bool only_active_split_tab_selected = | 
|  | browser->GetTabStripModel()->IsActiveTabSplit() && | 
|  | browser->GetTabStripModel()->selection_model().size() == 2; | 
|  | if (only_active_split_tab_selected && | 
|  | base::FeatureList::IsEnabled( | 
|  | features::kCloseActiveTabInSplitViewViaHotkey)) { | 
|  | RecordTabCloseCount(1); | 
|  |  | 
|  | content::WebContents* active_web_contents = | 
|  | browser->GetTabStripModel()->GetActiveWebContents(); | 
|  | active_web_contents->Close(); | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | ToastController* toast_controller = browser->GetFeatures().toast_controller(); | 
|  | if (!toast_controller) { | 
|  | CloseSelectedTabAndRecordTabCountMetric(browser); | 
|  | return; | 
|  | } | 
|  |  | 
|  | tabs::TabInterface* tab = browser->GetTabStripModel()->GetActiveTab(); | 
|  | const bool single_pinned_tab_selected = | 
|  | tab->IsPinned() && | 
|  | browser->GetTabStripModel()->selection_model().size() == 1; | 
|  | if (single_pinned_tab_selected && | 
|  | toast_controller->GetCurrentToastId() != ToastId::kClosePinnedTab) { | 
|  | BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser); | 
|  | CHECK(browser_view); | 
|  | ui::Accelerator accelerator; | 
|  | CHECK( | 
|  | browser_view->GetAcceleratorForCommandId(IDC_CLOSE_TAB, &accelerator)); | 
|  |  | 
|  | ToastParams params(ToastId::kClosePinnedTab); | 
|  | params.body_string_replacement_params.emplace_back( | 
|  | accelerator.GetShortcutText()); | 
|  | toast_controller->MaybeShowToast(std::move(params)); | 
|  | } else { | 
|  | CloseSelectedTabAndRecordTabCountMetric(browser); | 
|  | if (single_pinned_tab_selected) { | 
|  | base::RecordAction( | 
|  | UserMetricsAction("Tab.PinnedTabToastClosedAfterConfirmation")); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool CanZoomIn(content::WebContents* contents) { | 
|  | return contents && !contents->IsCrashed() && | 
|  | zoom::ZoomController::FromWebContents(contents)->GetZoomPercent() != | 
|  | contents->GetMaximumZoomPercent(); | 
|  | } | 
|  |  | 
|  | bool CanZoomOut(content::WebContents* contents) { | 
|  | return contents && !contents->IsCrashed() && | 
|  | zoom::ZoomController::FromWebContents(contents)->GetZoomPercent() != | 
|  | contents->GetMinimumZoomPercent(); | 
|  | } | 
|  |  | 
|  | bool CanResetZoom(content::WebContents* contents) { | 
|  | zoom::ZoomController* zoom_controller = | 
|  | zoom::ZoomController::FromWebContents(contents); | 
|  | return !zoom_controller->IsAtDefaultZoom() || | 
|  | !zoom_controller->PageScaleFactorIsOne(); | 
|  | } | 
|  |  | 
|  | void SelectNextTab(Browser* browser, | 
|  | TabStripUserGestureDetails gesture_detail) { | 
|  | base::RecordAction(UserMetricsAction("SelectNextTab")); | 
|  | browser->tab_strip_model()->SelectNextTab(gesture_detail); | 
|  | } | 
|  |  | 
|  | void SelectPreviousTab(Browser* browser, | 
|  | TabStripUserGestureDetails gesture_detail) { | 
|  | base::RecordAction(UserMetricsAction("SelectPrevTab")); | 
|  | browser->tab_strip_model()->SelectPreviousTab(gesture_detail); | 
|  | } | 
|  |  | 
|  | void MoveTabNext(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("MoveTabNext")); | 
|  | browser->tab_strip_model()->MoveTabNext(); | 
|  | } | 
|  |  | 
|  | void MoveTabPrevious(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("MoveTabPrevious")); | 
|  | browser->tab_strip_model()->MoveTabPrevious(); | 
|  | } | 
|  |  | 
|  | void SelectNumberedTab(Browser* browser, | 
|  | int index, | 
|  | TabStripUserGestureDetails gesture_detail) { | 
|  | int visible_count = 0; | 
|  | for (int i = 0; i < browser->tab_strip_model()->count(); i++) { | 
|  | if (browser->tab_strip_model()->IsTabCollapsed(i)) { | 
|  | continue; | 
|  | } | 
|  | if (visible_count == index) { | 
|  | base::RecordAction(UserMetricsAction("SelectNumberedTab")); | 
|  | browser->tab_strip_model()->ActivateTabAt(i, gesture_detail); | 
|  | break; | 
|  | } | 
|  | visible_count += 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SelectLastTab(Browser* browser, | 
|  | TabStripUserGestureDetails gesture_detail) { | 
|  | for (int i = browser->tab_strip_model()->count() - 1; i >= 0; i--) { | 
|  | if (!browser->tab_strip_model()->IsTabCollapsed(i)) { | 
|  | base::RecordAction(UserMetricsAction("SelectLastTab")); | 
|  | browser->tab_strip_model()->ActivateTabAt(i, gesture_detail); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void DuplicateTab(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("Duplicate")); | 
|  | DuplicateTabAt(browser, browser->tab_strip_model()->active_index()); | 
|  | } | 
|  |  | 
|  | bool CanDuplicateTab(const Browser* browser) { | 
|  | return CanDuplicateTabAt(browser, browser->tab_strip_model()->active_index()); | 
|  | } | 
|  |  | 
|  | bool CanDuplicateKeyboardFocusedTab(const Browser* browser) { | 
|  | if (!HasKeyboardFocusedTab(browser)) { | 
|  | return false; | 
|  | } | 
|  | return CanDuplicateTabAt(browser, *GetKeyboardFocusedTabIndex(browser)); | 
|  | } | 
|  |  | 
|  | bool CanMoveActiveTabToNewWindow(Browser* browser) { | 
|  | const ui::ListSelectionModel::SelectedIndices& selection = | 
|  | browser->tab_strip_model()->selection_model().selected_indices(); | 
|  | return CanMoveTabsToNewWindow( | 
|  | browser, std::vector<int>(selection.begin(), selection.end())); | 
|  | } | 
|  |  | 
|  | void MoveActiveTabToNewWindow(Browser* browser) { | 
|  | const ui::ListSelectionModel::SelectedIndices& selection = | 
|  | browser->tab_strip_model()->selection_model().selected_indices(); | 
|  | MoveTabsToNewWindow(browser, | 
|  | std::vector<int>(selection.begin(), selection.end())); | 
|  | } | 
|  |  | 
|  | bool CanMoveTabsToNewWindow(Browser* browser, | 
|  | const std::vector<int>& tab_indices) { | 
|  | if (browser->is_type_app()) { | 
|  | for (int index : tab_indices) { | 
|  | if (web_app::IsPinnedHomeTab(browser->tab_strip_model(), index)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | } | 
|  | return browser->tab_strip_model()->count() > | 
|  | static_cast<int>(tab_indices.size()); | 
|  | } | 
|  |  | 
|  | void MoveGroupToNewWindow(Browser* browser, tab_groups::TabGroupId group) { | 
|  | Browser* new_browser; | 
|  | if (browser->is_type_app() && browser->app_controller()->has_tab_strip()) { | 
|  | new_browser = Browser::Create(Browser::CreateParams::CreateForApp( | 
|  | browser->app_name(), browser->is_trusted_source(), gfx::Rect(), | 
|  | browser->profile(), true)); | 
|  | web_app::MaybeAddPinnedHomeTab(new_browser, | 
|  | new_browser->app_controller()->app_id()); | 
|  | } else { | 
|  | new_browser = | 
|  | Browser::Create(Browser::CreateParams(browser->profile(), true)); | 
|  | } | 
|  |  | 
|  | MoveGroupToWindowImpl(browser, new_browser, group); | 
|  | } | 
|  |  | 
|  | void MoveTabsToNewWindow(Browser* browser, | 
|  | const std::vector<int>& tab_indices) { | 
|  | if (tab_indices.empty()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | Browser* new_browser; | 
|  | if (browser->is_type_app() && browser->app_controller()->has_tab_strip()) { | 
|  | new_browser = Browser::Create(Browser::CreateParams::CreateForApp( | 
|  | browser->app_name(), browser->is_trusted_source(), gfx::Rect(), | 
|  | browser->profile(), true)); | 
|  | web_app::MaybeAddPinnedHomeTab(new_browser, | 
|  | new_browser->app_controller()->app_id()); | 
|  | } else { | 
|  | new_browser = | 
|  | Browser::Create(Browser::CreateParams(browser->profile(), true)); | 
|  | } | 
|  |  | 
|  | MoveTabsToWindowImpl(browser, new_browser, tab_indices); | 
|  | } | 
|  |  | 
|  | bool CanCloseTabsToRight(const Browser* browser) { | 
|  | return browser->tab_strip_model()->IsContextMenuCommandEnabled( | 
|  | browser->tab_strip_model()->active_index(), | 
|  | TabStripModel::CommandCloseTabsToRight); | 
|  | } | 
|  |  | 
|  | bool CanCloseOtherTabs(const Browser* browser) { | 
|  | return browser->tab_strip_model()->IsContextMenuCommandEnabled( | 
|  | browser->tab_strip_model()->active_index(), | 
|  | TabStripModel::CommandCloseOtherTabs); | 
|  | } | 
|  |  | 
|  | WebContents* DuplicateTabAt(Browser* browser, int index) { | 
|  | return ::DuplicateTabAt(browser, index, index + 1); | 
|  | } | 
|  |  | 
|  | void DuplicateSplit(Browser* browser, split_tabs::SplitTabId split) { | 
|  | CHECK(browser->CanSupportWindowFeature(Browser::FEATURE_TABSTRIP)); | 
|  |  | 
|  | TabStripModel* model = browser->tab_strip_model(); | 
|  | split_tabs::SplitTabData* split_data = model->GetSplitData(split); | 
|  | gfx::Range split_indices_range = split_data->GetIndexRange(); | 
|  |  | 
|  | std::vector<int> duplicated_tab_indices; | 
|  | for (size_t split_index = split_indices_range.GetMin(); | 
|  | split_index < split_indices_range.GetMax(); split_index++) { | 
|  | size_t dst_index = split_index + split_indices_range.length(); | 
|  | ::DuplicateTabAt(browser, split_index, dst_index); | 
|  | duplicated_tab_indices.push_back(dst_index); | 
|  | } | 
|  |  | 
|  | // Activate the tab that was last active in the old split, and then | 
|  | // create the new split with the same visual data. | 
|  | // TODO(418015278): Revisit if we should store last active tab in the visual | 
|  | // data, to make copying it easier. | 
|  | int active_index = split_tabs::GetIndexOfLastActiveTab(model, split) + | 
|  | split_indices_range.length(); | 
|  | model->ActivateTabAt(active_index); | 
|  | // AddToNewSplit always creates a split with the active index so remove it | 
|  | // from the passed in indices. | 
|  | duplicated_tab_indices.erase(std::find(duplicated_tab_indices.begin(), | 
|  | duplicated_tab_indices.end(), | 
|  | active_index)); | 
|  | model->AddToNewSplit(duplicated_tab_indices, | 
|  | split_tabs::SplitTabVisualData( | 
|  | *(model->GetSplitData(split)->visual_data())), | 
|  | split_tabs::SplitTabCreatedSource::kDuplicateSplit); | 
|  | } | 
|  |  | 
|  | bool CanDuplicateTabAt(const Browser* browser, int index) { | 
|  | if (browser->is_type_picture_in_picture()) { | 
|  | return false; | 
|  | } | 
|  | WebContents* contents = browser->tab_strip_model()->GetWebContentsAt(index); | 
|  | return contents; | 
|  | } | 
|  |  | 
|  | void MoveTabsToExistingWindow(Browser* source, | 
|  | Browser* target, | 
|  | const std::vector<int>& tab_indices) { | 
|  | MoveTabsToWindowImpl(source, target, tab_indices); | 
|  | } | 
|  |  | 
|  | void MoveGroupToExistingWindow(Browser* source, | 
|  | Browser* target, | 
|  | tab_groups::TabGroupId group) { | 
|  | MoveGroupToWindowImpl(source, target, group); | 
|  | } | 
|  |  | 
|  | void PinTab(Browser* browser) { | 
|  | browser->tab_strip_model()->ExecuteContextMenuCommand( | 
|  | browser->tab_strip_model()->active_index(), | 
|  | TabStripModel::ContextMenuCommand::CommandTogglePinned); | 
|  | } | 
|  |  | 
|  | void GroupTab(Browser* browser) { | 
|  | browser->tab_strip_model()->ExecuteContextMenuCommand( | 
|  | browser->tab_strip_model()->active_index(), | 
|  | TabStripModel::ContextMenuCommand::CommandToggleGrouped); | 
|  | } | 
|  |  | 
|  | void NewSplitTab(BrowserWindowInterface* browser, | 
|  | split_tabs::SplitTabCreatedSource source) { | 
|  | TabStripModel* const tab_strip_model = browser->GetTabStripModel(); | 
|  | const int active_index = tab_strip_model->active_index(); | 
|  | tab_strip_model->delegate()->AddTabAt( | 
|  | GURL(chrome::kChromeUISplitViewNewTabPageURL), active_index + 1, true, | 
|  | tab_strip_model->GetTabGroupForTab(active_index), | 
|  | tab_strip_model->IsTabPinned(active_index)); | 
|  | tab_strip_model->AddToNewSplit({active_index}, | 
|  | split_tabs::SplitTabVisualData(), source); | 
|  | } | 
|  |  | 
|  | void AddNewTabToGroup(Browser* browser) { | 
|  | if (!browser->tab_strip_model()->SupportsTabGroups()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | int index = browser->tab_strip_model()->active_index(); | 
|  | std::optional<tab_groups::TabGroupId> group_id = | 
|  | browser->tab_strip_model()->GetTabGroupForTab(index); | 
|  | if (!group_id) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | AddTabAt(browser, GURL(), -1, true, group_id); | 
|  | } | 
|  |  | 
|  | void CreateNewTabGroup(Browser* browser) { | 
|  | NewTab(browser); | 
|  | browser->tab_strip_model()->ExecuteContextMenuCommand( | 
|  | browser->tab_strip_model()->active_index(), | 
|  | TabStripModel::ContextMenuCommand::CommandAddToNewGroupFromMenuItem); | 
|  | } | 
|  |  | 
|  | void CloseTabGroup(Browser* browser) { | 
|  | const int index = browser->tab_strip_model()->active_index(); | 
|  | std::optional<tab_groups::TabGroupId> group_id = | 
|  | browser->tab_strip_model()->GetTabGroupForTab(index); | 
|  | if (!group_id) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | const int num_tabs_in_group = browser->tab_strip_model() | 
|  | ->group_model() | 
|  | ->GetTabGroup(group_id.value()) | 
|  | ->tab_count(); | 
|  | if (num_tabs_in_group == browser->tab_strip_model()->count()) { | 
|  | // If the group about to be closed has all of the tabs in the browser, add a | 
|  | // new tab outside the group to prevent the browser from closing. | 
|  | browser->tab_strip_model()->delegate()->AddTabAt(GURL(), -1, true); | 
|  | } | 
|  |  | 
|  | browser->tab_strip_model()->CloseAllTabsInGroup(group_id.value()); | 
|  | } | 
|  |  | 
|  | void FocusNextTabGroup(Browser* browser) { | 
|  | TabStripModel* tab_strip_model = browser->tab_strip_model(); | 
|  | if (!tab_strip_model->SupportsTabGroups()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | int current_index = tab_strip_model->active_index(); | 
|  | std::optional<tab_groups::TabGroupId> current_group_id = | 
|  | tab_strip_model->GetTabGroupForTab(current_index); | 
|  |  | 
|  | // Find the next tab group and focus its first tab. | 
|  | int count = tab_strip_model->GetTabCount(); | 
|  | for (int i = 1; i < count; ++i) { | 
|  | int new_index = (current_index + i) % count; | 
|  | std::optional<tab_groups::TabGroupId> new_group_id = | 
|  | tab_strip_model->GetTabGroupForTab(new_index); | 
|  | if (new_group_id && new_group_id != current_group_id) { | 
|  | tab_strip_model->ActivateTabAt( | 
|  | new_index, TabStripUserGestureDetails( | 
|  | TabStripUserGestureDetails::GestureType::kKeyboard)); | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void FocusPreviousTabGroup(Browser* browser) { | 
|  | TabStripModel* tab_strip_model = browser->tab_strip_model(); | 
|  | if (!tab_strip_model->SupportsTabGroups()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | int current_index = tab_strip_model->active_index(); | 
|  | std::optional<tab_groups::TabGroupId> current_group_id = | 
|  | tab_strip_model->GetTabGroupForTab(current_index); | 
|  |  | 
|  | // Find the next tab group and focus its first tab. | 
|  | int count = tab_strip_model->GetTabCount(); | 
|  | for (int i = 1; i < count; ++i) { | 
|  | int offset = count - i; | 
|  | int new_index = (current_index + offset) % count; | 
|  | std::optional<tab_groups::TabGroupId> new_group_id = | 
|  | tab_strip_model->GetTabGroupForTab(new_index); | 
|  | if (new_group_id && new_group_id != current_group_id) { | 
|  | tabs::TabInterface* first_tab_of_group = | 
|  | tab_strip_model->group_model() | 
|  | ->GetTabGroup(new_group_id.value()) | 
|  | ->GetFirstTab(); | 
|  | CHECK(first_tab_of_group); | 
|  | tab_strip_model->ActivateTabAt( | 
|  | tab_strip_model->GetIndexOfTab(first_tab_of_group), | 
|  | TabStripUserGestureDetails( | 
|  | TabStripUserGestureDetails::GestureType::kKeyboard)); | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool GroupAllUngroupedTabs(Browser* browser) { | 
|  | TabStripModel* tab_strip_model = browser->tab_strip_model(); | 
|  | if (!tab_strip_model->SupportsTabGroups()) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | int i = 0; | 
|  | std::vector<int> indices; | 
|  | for (const tabs::TabInterface* t : *tab_strip_model) { | 
|  | if (!t->GetGroup() && !t->IsPinned()) { | 
|  | indices.push_back(i); | 
|  | } | 
|  | ++i; | 
|  | } | 
|  | if (indices.size() == 0) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | tab_groups::TabGroupId group = tab_strip_model->AddToNewGroup(indices); | 
|  | tab_strip_model->OpenTabGroupEditor(group); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void AddNewTabToRecentGroup(Browser* browser) { | 
|  | if (!features::IsTabGroupMenuMoreEntryPointsEnabled()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | TabStripModel* tab_strip_model = browser->tab_strip_model(); | 
|  |  | 
|  | if (!tab_strip_model->SupportsTabGroups()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | std::optional<tab_groups::TabGroupId> group_id = std::nullopt; | 
|  |  | 
|  | // Add the new tab to the most recently active group. | 
|  | TabGroupModel* tab_group_model = tab_strip_model->group_model(); | 
|  | CHECK(tab_group_model); | 
|  | group_id = tab_group_model->GetMostRecentTabGroupId(); | 
|  |  | 
|  | if (!group_id) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | AddTabAt(browser, GURL(), -1, true, group_id); | 
|  | } | 
|  |  | 
|  | void MuteSite(Browser* browser) { | 
|  | browser->tab_strip_model()->ExecuteContextMenuCommand( | 
|  | browser->tab_strip_model()->active_index(), | 
|  | TabStripModel::ContextMenuCommand::CommandToggleSiteMuted); | 
|  | } | 
|  |  | 
|  | void MuteSiteForKeyboardFocusedTab(Browser* browser) { | 
|  | if (!HasKeyboardFocusedTab(browser)) { | 
|  | return; | 
|  | } | 
|  | browser->tab_strip_model()->ExecuteContextMenuCommand( | 
|  | *GetKeyboardFocusedTabIndex(browser), | 
|  | TabStripModel::ContextMenuCommand::CommandToggleSiteMuted); | 
|  | } | 
|  |  | 
|  | void PinKeyboardFocusedTab(Browser* browser) { | 
|  | if (!HasKeyboardFocusedTab(browser)) { | 
|  | return; | 
|  | } | 
|  | browser->tab_strip_model()->ExecuteContextMenuCommand( | 
|  | *GetKeyboardFocusedTabIndex(browser), | 
|  | TabStripModel::ContextMenuCommand::CommandTogglePinned); | 
|  | } | 
|  |  | 
|  | void GroupKeyboardFocusedTab(Browser* browser) { | 
|  | if (!HasKeyboardFocusedTab(browser)) { | 
|  | return; | 
|  | } | 
|  | browser->tab_strip_model()->ExecuteContextMenuCommand( | 
|  | *GetKeyboardFocusedTabIndex(browser), | 
|  | TabStripModel::ContextMenuCommand::CommandToggleGrouped); | 
|  | } | 
|  |  | 
|  | void DuplicateKeyboardFocusedTab(Browser* browser) { | 
|  | if (HasKeyboardFocusedTab(browser)) { | 
|  | DuplicateTabAt(browser, *GetKeyboardFocusedTabIndex(browser)); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool HasKeyboardFocusedTab(const Browser* browser) { | 
|  | return GetKeyboardFocusedTabIndex(browser).has_value(); | 
|  | } | 
|  |  | 
|  | void ConvertPopupToTabbedBrowser(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("ShowAsTab")); | 
|  | TabStripModel* tab_strip = browser->tab_strip_model(); | 
|  | // If this popup is the last browser object, removing it from the browser-list | 
|  | // will trigger OnShutdownStarting for Window close. Create the new browser | 
|  | // object first, before removing the existing object from the browser-list in | 
|  | // order to avoid incorrectly triggering a shutdown. | 
|  | Browser* b = Browser::Create(Browser::CreateParams(browser->profile(), true)); | 
|  | // This method moves a WebContents from a non-normal browser window to a | 
|  | // normal browser window. We cannot move the Tab over directly since TabModel | 
|  | // enforces the requirement that it cannot move between window types. | 
|  | // https://crbug.com/334281979): Non-normal browser windows should not have a | 
|  | // tab to begin with. | 
|  | std::unique_ptr<content::WebContents> contents_move = | 
|  | tab_strip->DetachWebContentsAtForInsertion(tab_strip->active_index()); | 
|  |  | 
|  | // This method moves a WebContents from a non-normal browser window to a | 
|  | // normal browser window. We cannot move the Tab over directly since TabModel | 
|  | // enforces the requirement that it cannot move between window types. | 
|  | // https://crbug.com/334281979): Non-normal browser windows should not have a | 
|  | // tab to begin with. | 
|  | b->tab_strip_model()->AppendWebContents(std::move(contents_move), true); | 
|  | b->window()->Show(); | 
|  | } | 
|  |  | 
|  | void CloseTabsToRight(Browser* browser) { | 
|  | browser->tab_strip_model()->ExecuteContextMenuCommand( | 
|  | browser->tab_strip_model()->active_index(), | 
|  | TabStripModel::CommandCloseTabsToRight); | 
|  | } | 
|  |  | 
|  | void CloseOtherTabs(Browser* browser) { | 
|  | browser->tab_strip_model()->ExecuteContextMenuCommand( | 
|  | browser->tab_strip_model()->active_index(), | 
|  | TabStripModel::CommandCloseOtherTabs); | 
|  | } | 
|  |  | 
|  | void Exit() { | 
|  | base::RecordAction(UserMetricsAction("Exit")); | 
|  | chrome::AttemptUserExit(); | 
|  | } | 
|  |  | 
|  | void BookmarkCurrentTab(Browser* browser) { | 
|  | base::RecordAction(base::UserMetricsAction("Star")); | 
|  | BookmarkModel* model = | 
|  | BookmarkModelFactory::GetForBrowserContext(browser->profile()); | 
|  | GURL url; | 
|  | std::u16string title; | 
|  | if (!BookmarkCurrentTabHelper(browser, model, &url, &title)) { | 
|  | return; | 
|  | } | 
|  | bool was_bookmarked_by_user = bookmarks::IsBookmarkedByUser(model, url); | 
|  | bookmarks::AddIfNotBookmarked(model, url, title); | 
|  | bool is_bookmarked_by_user = bookmarks::IsBookmarkedByUser(model, url); | 
|  | // Make sure the model actually added a bookmark before showing the star. A | 
|  | // bookmark isn't created if the url is invalid. | 
|  | if (browser->window()->IsActive() && is_bookmarked_by_user) { | 
|  | // Only show the bubble if the window is active, otherwise we may get into | 
|  | // weird situations where the bubble is deleted as soon as it is shown. | 
|  | browser->window()->ShowBookmarkBubble(url, was_bookmarked_by_user); | 
|  | } | 
|  |  | 
|  | if (!was_bookmarked_by_user && is_bookmarked_by_user) { | 
|  | RecordBookmarksAdded(browser->profile()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void BookmarkCurrentTabInFolder(Browser* browser, | 
|  | BookmarkModel* model, | 
|  | int64_t folder_id) { | 
|  | GURL url; | 
|  | std::u16string title; | 
|  | if (!BookmarkCurrentTabHelper(browser, model, &url, &title)) { | 
|  | return; | 
|  | } | 
|  | const bookmarks::BookmarkNode* parent = | 
|  | bookmarks::GetBookmarkNodeByID(model, folder_id); | 
|  | if (parent) { | 
|  | bool was_bookmarked_by_user = bookmarks::IsBookmarkedByUser(model, url); | 
|  | model->AddNewURL(parent, 0, title, url); | 
|  | bool is_bookmarked_by_user = bookmarks::IsBookmarkedByUser(model, url); | 
|  | if (!was_bookmarked_by_user && is_bookmarked_by_user) { | 
|  | RecordBookmarksAdded(browser->profile()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool CanBookmarkCurrentTab(const Browser* browser) { | 
|  | BookmarkModel* model = | 
|  | BookmarkModelFactory::GetForBrowserContext(browser->profile()); | 
|  | return browser_defaults::bookmarks_enabled && | 
|  | browser->profile()->GetPrefs()->GetBoolean( | 
|  | bookmarks::prefs::kEditBookmarksEnabled) && | 
|  | model && model->loaded() && browser->is_type_normal(); | 
|  | } | 
|  |  | 
|  | void BookmarkAllTabs(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("BookmarkAllTabs")); | 
|  | RecordBookmarkAllTabsWithTabsCount(browser->profile(), | 
|  | browser->tab_strip_model()->count()); | 
|  |  | 
|  | bookmarks::ShowBookmarkAllTabsDialog(browser); | 
|  | } | 
|  |  | 
|  | bool CanBookmarkAllTabs(const Browser* browser) { | 
|  | return browser->tab_strip_model()->count() > 1 && | 
|  | CanBookmarkCurrentTab(browser); | 
|  | } | 
|  |  | 
|  | bool CanMoveActiveTabToReadLater(Browser* browser) { | 
|  | GURL url; | 
|  | std::u16string title; | 
|  | WebContents* web_contents = | 
|  | browser->tab_strip_model()->GetActiveWebContents(); | 
|  | ReadingListModel* model = GetReadingListModel(browser); | 
|  | return CanMoveWebContentsToReadLater(browser, web_contents, model, &url, | 
|  | &title); | 
|  | } | 
|  |  | 
|  | void MoveCurrentTabToReadLater(Browser* browser) { | 
|  | MoveTabsToReadLater(browser, | 
|  | {browser->tab_strip_model()->GetActiveWebContents()}); | 
|  | } | 
|  |  | 
|  | void MoveTabsToReadLater(Browser* browser, | 
|  | std::vector<content::WebContents*> web_contentses) { | 
|  | int added_to_read_later = 0; | 
|  | for (WebContents* const web_contents : web_contentses) { | 
|  | GURL url; | 
|  | std::u16string title; | 
|  | ReadingListModel* model = GetReadingListModel(browser); | 
|  | if (!CanMoveWebContentsToReadLater(browser, web_contents, model, &url, | 
|  | &title)) { | 
|  | continue; | 
|  | } | 
|  | model->AddOrReplaceEntry(url, base::UTF16ToUTF8(title), | 
|  | reading_list::EntrySource::ADDED_VIA_CURRENT_APP, | 
|  | /*estimated_read_time=*/std::nullopt, | 
|  | /*creation_time=*/std::nullopt); | 
|  | BrowserUserEducationInterface::From(browser)->MaybeShowFeaturePromo( | 
|  | feature_engagement::kIPHReadingListDiscoveryFeature); | 
|  | base::UmaHistogramEnumeration( | 
|  | "ReadingList.BookmarkBarState.OnEveryAddToReadingList", | 
|  | BookmarkBarController::From(browser)->bookmark_bar_state()); | 
|  | added_to_read_later += 1; | 
|  | } | 
|  |  | 
|  | if (added_to_read_later == 0) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | #if !BUILDFLAG(IS_ANDROID) | 
|  | if (toast_features::IsEnabled(toast_features::kReadingListToast)) { | 
|  | // Don't show the reading list toast if the side panel is visible. | 
|  | std::optional<SidePanelEntry::Id> id = | 
|  | browser->GetFeatures().side_panel_ui()->GetCurrentEntryId(); | 
|  | if (id.has_value() && id.value() == SidePanelEntryId::kReadingList) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | ToastController* const toast_controller = | 
|  | browser->GetFeatures().toast_controller(); | 
|  | if (toast_controller) { | 
|  | ToastParams params = ToastParams(ToastId::kAddedToReadingList); | 
|  | params.body_string_cardinality_param = added_to_read_later; | 
|  | toast_controller->MaybeShowToast(std::move(params)); | 
|  | } | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | bool MarkCurrentTabAsReadInReadLater(Browser* browser) { | 
|  | GURL url; | 
|  | std::u16string title; | 
|  | ReadingListModel* model = GetReadingListModel(browser); | 
|  | WebContents* web_contents = | 
|  | browser->tab_strip_model()->GetActiveWebContents(); | 
|  | if (!model || !GetTabURLAndTitleToSave(web_contents, &url, &title)) { | 
|  | return false; | 
|  | } | 
|  | scoped_refptr<const ReadingListEntry> entry = model->GetEntryByURL(url); | 
|  | // Mark current tab as read. | 
|  | if (entry && !entry->IsRead()) { | 
|  | model->SetReadStatusIfExists(url, true); | 
|  | } | 
|  | return entry != nullptr; | 
|  | } | 
|  |  | 
|  | bool IsCurrentTabUnreadInReadLater(Browser* browser) { | 
|  | GURL url; | 
|  | std::u16string title; | 
|  | ReadingListModel* model = GetReadingListModel(browser); | 
|  | WebContents* web_contents = | 
|  | browser->tab_strip_model()->GetActiveWebContents(); | 
|  | if (!model || !GetTabURLAndTitleToSave(web_contents, &url, &title)) { | 
|  | return false; | 
|  | } | 
|  | scoped_refptr<const ReadingListEntry> entry = model->GetEntryByURL(url); | 
|  | return entry && !entry->IsRead(); | 
|  | } | 
|  |  | 
|  | void ShowOffersAndRewardsForPage(BrowserWindowInterface* bwi) { | 
|  | WebContents* const web_contents = | 
|  | bwi->GetTabStripModel()->GetActiveWebContents(); | 
|  | autofill::OfferNotificationBubbleControllerImpl* controller = | 
|  | autofill::OfferNotificationBubbleControllerImpl::FromWebContents( | 
|  | web_contents); | 
|  | DCHECK(controller); | 
|  | controller->ReshowBubble(); | 
|  | } | 
|  |  | 
|  | void SaveCreditCard(Browser* browser) { | 
|  | WebContents* web_contents = | 
|  | browser->tab_strip_model()->GetActiveWebContents(); | 
|  | autofill::SaveCardBubbleControllerImpl* controller = | 
|  | autofill::SaveCardBubbleControllerImpl::FromWebContents(web_contents); | 
|  | controller->ReshowBubble(/*is_user_gesture=*/true); | 
|  | } | 
|  |  | 
|  | void SaveIban(Browser* browser) { | 
|  | WebContents* web_contents = | 
|  | browser->tab_strip_model()->GetActiveWebContents(); | 
|  | autofill::IbanBubbleControllerImpl* controller = | 
|  | autofill::IbanBubbleControllerImpl::FromWebContents(web_contents); | 
|  | controller->ReshowBubble(); | 
|  | } | 
|  |  | 
|  | void ShowMandatoryReauthOptInPrompt(Browser* browser) { | 
|  | WebContents* web_contents = | 
|  | browser->tab_strip_model()->GetActiveWebContents(); | 
|  | autofill::MandatoryReauthBubbleControllerImpl* controller = | 
|  | autofill::MandatoryReauthBubbleControllerImpl::FromWebContents( | 
|  | web_contents); | 
|  | controller->ReshowBubble(); | 
|  | } | 
|  |  | 
|  | void SaveAutofillAddress(Browser* browser) { | 
|  | WebContents* web_contents = | 
|  | browser->tab_strip_model()->GetActiveWebContents(); | 
|  | autofill::AddressBubblesController* controller = | 
|  | autofill::AddressBubblesController::FromWebContents(web_contents); | 
|  | controller->OnIconClicked(); | 
|  | } | 
|  |  | 
|  | void ShowFilledCardInformationBubble(Browser* browser) { | 
|  | WebContents* web_contents = | 
|  | browser->tab_strip_model()->GetActiveWebContents(); | 
|  | auto* controller = | 
|  | autofill::FilledCardInformationBubbleControllerImpl::FromWebContents( | 
|  | web_contents); | 
|  | if (controller) { | 
|  | controller->ReshowBubble(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ShowVirtualCardEnrollBubble(Browser* browser) { | 
|  | WebContents* web_contents = | 
|  | browser->tab_strip_model()->GetActiveWebContents(); | 
|  | autofill::VirtualCardEnrollBubbleControllerImpl* controller = | 
|  | autofill::VirtualCardEnrollBubbleControllerImpl::FromWebContents( | 
|  | web_contents); | 
|  | if (controller) { | 
|  | controller->ReshowBubble(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void StartTabOrganizationRequest(Browser* browser) { | 
|  | TabOrganizationService* service = | 
|  | TabOrganizationServiceFactory::GetForProfile(browser->profile()); | 
|  | UMA_HISTOGRAM_BOOLEAN("Tab.Organization.AllEntrypoints.Clicked", true); | 
|  | UMA_HISTOGRAM_BOOLEAN("Tab.Organization.ThreeDotMenu.Clicked", true); | 
|  |  | 
|  | service->RestartSessionAndShowUI(browser, | 
|  | TabOrganizationEntryPoint::kThreeDotMenu); | 
|  | } | 
|  |  | 
|  | void ShowTranslateBubble(BrowserWindowInterface* bwi) { | 
|  | if (!bwi->GetWindow()->IsActive()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | WebContents* const web_contents = | 
|  | bwi->GetTabStripModel()->GetActiveWebContents(); | 
|  | ChromeTranslateClient* chrome_translate_client = | 
|  | ChromeTranslateClient::FromWebContents(web_contents); | 
|  |  | 
|  | if (!chrome_translate_client) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // The Translate bubble will not show if a text field is focused, so we clear | 
|  | // focus here as the user has intentionally opened the bubble. | 
|  | web_contents->ClearFocusedElement(); | 
|  |  | 
|  | std::string source_language; | 
|  | std::string target_language; | 
|  | chrome_translate_client->GetTranslateLanguages(web_contents, &source_language, | 
|  | &target_language); | 
|  |  | 
|  | // If the source language matches the target language, we change the source | 
|  | // language to unknown, so that we display "Detected Language". | 
|  | if (source_language == target_language) { | 
|  | source_language = language_detection::kUnknownLanguageCode; | 
|  | } | 
|  |  | 
|  | translate::TranslateStep step = translate::TRANSLATE_STEP_BEFORE_TRANSLATE; | 
|  | auto* language_state = | 
|  | chrome_translate_client->GetTranslateManager()->GetLanguageState(); | 
|  |  | 
|  | if (language_state->translation_pending()) { | 
|  | step = translate::TRANSLATE_STEP_TRANSLATING; | 
|  | } else if (language_state->translation_error()) { | 
|  | step = translate::TRANSLATE_STEP_TRANSLATE_ERROR; | 
|  | } else if (language_state->IsPageTranslated()) { | 
|  | step = translate::TRANSLATE_STEP_AFTER_TRANSLATE; | 
|  | } | 
|  | bwi->GetBrowserForMigrationOnly()->window()->ShowTranslateBubble( | 
|  | web_contents, step, source_language, target_language, | 
|  | translate::TranslateErrors::NONE, true); | 
|  | } | 
|  |  | 
|  | void ManagePasswordsForPage(BrowserWindowInterface* bwi) { | 
|  | auto* const user_education = BrowserUserEducationInterface::From(bwi); | 
|  | user_education->NotifyFeaturePromoFeatureUsed( | 
|  | feature_engagement::kIPHPasswordsManagementBubbleAfterSaveFeature, | 
|  | FeaturePromoFeatureUsedAction::kClosePromoIfPresent); | 
|  | user_education->NotifyFeaturePromoFeatureUsed( | 
|  | feature_engagement::kIPHPasswordsManagementBubbleDuringSigninFeature, | 
|  | FeaturePromoFeatureUsedAction::kClosePromoIfPresent); | 
|  | user_education->NotifyFeaturePromoFeatureUsed( | 
|  | feature_engagement::kIPHPasswordManagerShortcutFeature, | 
|  | FeaturePromoFeatureUsedAction::kClosePromoIfPresent); | 
|  | WebContents* const web_contents = | 
|  | bwi->GetTabStripModel()->GetActiveWebContents(); | 
|  | ManagePasswordsUIController* controller = | 
|  | ManagePasswordsUIController::FromWebContents(web_contents); | 
|  | controller->QueueOrShowBubble( | 
|  | /*user_action=*/!controller->IsAutomaticallyOpeningBubble()); | 
|  | } | 
|  |  | 
|  | bool CanSendTabToSelf(BrowserWindowInterface* bwi) { | 
|  | return send_tab_to_self::ShouldDisplayEntryPoint( | 
|  | bwi->GetTabStripModel()->GetActiveWebContents()); | 
|  | } | 
|  |  | 
|  | void SendTabToSelf(Browser* browser) { | 
|  | WebContents* web_contents = | 
|  | browser->tab_strip_model()->GetActiveWebContents(); | 
|  | send_tab_to_self::ShowBubble(web_contents); | 
|  | } | 
|  |  | 
|  | bool CanGenerateQrCode(const Browser* browser) { | 
|  | return !sharing_hub::SharingIsDisabledByPolicy(browser->profile()) && | 
|  | qrcode_generator::QRCodeGeneratorBubbleController:: | 
|  | IsGeneratorAvailable(browser->tab_strip_model() | 
|  | ->GetActiveWebContents() | 
|  | ->GetController() | 
|  | .GetLastCommittedEntry() | 
|  | ->GetURL()); | 
|  | } | 
|  |  | 
|  | void GenerateQRCode(BrowserWindowInterface* bwi) { | 
|  | WebContents* web_contents = bwi->GetTabStripModel()->GetActiveWebContents(); | 
|  | qrcode_generator::QRCodeGeneratorBubbleController* controller = | 
|  | qrcode_generator::QRCodeGeneratorBubbleController::Get(web_contents); | 
|  | content::NavigationEntry* entry = | 
|  | web_contents->GetController().GetLastCommittedEntry(); | 
|  | controller->ShowBubble(entry->GetURL()); | 
|  | } | 
|  |  | 
|  | void SharingHub(Browser* browser) { | 
|  | WebContents* web_contents = | 
|  | browser->tab_strip_model()->GetActiveWebContents(); | 
|  | sharing_hub::SharingHubBubbleController* controller = | 
|  | sharing_hub::SharingHubBubbleController::CreateOrGetFromWebContents( | 
|  | web_contents); | 
|  | controller->ShowBubble(share::ShareAttempt(web_contents)); | 
|  | } | 
|  |  | 
|  | void ScreenshotCapture(Browser* browser) { | 
|  | WebContents* web_contents = | 
|  | browser->tab_strip_model()->GetActiveWebContents(); | 
|  | sharing_hub::ScreenshotCapturedBubbleController* controller = | 
|  | sharing_hub::ScreenshotCapturedBubbleController::Get(web_contents); | 
|  | controller->Capture(browser); | 
|  | } | 
|  |  | 
|  | void SavePage(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("SavePage")); | 
|  | WebContents* current_tab = browser->tab_strip_model()->GetActiveWebContents(); | 
|  | DCHECK(current_tab); | 
|  | if (current_tab->GetContentsMimeType() == "application/pdf") { | 
|  | base::RecordAction(UserMetricsAction("PDF.SavePage")); | 
|  | #if BUILDFLAG(ENABLE_PDF) | 
|  | // The PDF viewer may handle the event by itself. | 
|  | if (chrome_pdf::features::IsOopifPdfEnabled() && | 
|  | pdf_extension_util::MaybeDispatchSaveEvent( | 
|  | current_tab->GetPrimaryMainFrame())) { | 
|  | return; | 
|  | } | 
|  | #endif  // BUILDFLAG(ENABLE_PDF) | 
|  | } | 
|  | current_tab->OnSavePage(); | 
|  | } | 
|  |  | 
|  | bool CanSavePage(const Browser* browser) { | 
|  | // LocalState can be NULL in tests. | 
|  | if (g_browser_process->local_state() && | 
|  | !g_browser_process->local_state()->GetBoolean( | 
|  | prefs::kAllowFileSelectionDialogs)) { | 
|  | return false; | 
|  | } | 
|  | if (static_cast<policy::DownloadRestriction>( | 
|  | browser->profile()->GetPrefs()->GetInteger( | 
|  | policy::policy_prefs::kDownloadRestrictions)) == | 
|  | policy::DownloadRestriction::ALL_FILES) { | 
|  | return false; | 
|  | } | 
|  | return !browser->is_type_devtools() && | 
|  | !(GetContentRestrictions(browser) & CONTENT_RESTRICTION_SAVE); | 
|  | } | 
|  |  | 
|  | void Print(BrowserWindowInterface* bwi) { | 
|  | #if BUILDFLAG(ENABLE_PRINTING) | 
|  | auto* const web_contents = bwi->GetTabStripModel()->GetActiveWebContents(); | 
|  |  | 
|  | // Launch ChromeOS print preview only if in a ChromeOS build and | 
|  | // `kPrintPreviewCrosPrimary` enabled. Otherwise use browser print preview. | 
|  | #if BUILDFLAG(IS_CHROMEOS) | 
|  | if (base::FeatureList::IsEnabled(::features::kPrintPreviewCrosPrimary)) { | 
|  | chromeos::printing::StartPrint( | 
|  | web_contents, | 
|  | /*print_renderer=*/mojo::NullAssociatedRemote(), | 
|  | bwi->GetProfile()->GetPrefs()->GetBoolean(prefs::kPrintPreviewDisabled), | 
|  | /*has_selection=*/false); | 
|  | return; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | printing::StartPrint( | 
|  | web_contents, | 
|  | #if BUILDFLAG(IS_CHROMEOS) | 
|  | /*print_renderer=*/mojo::NullAssociatedRemote(), | 
|  | #endif | 
|  | bwi->GetProfile()->GetPrefs()->GetBoolean(prefs::kPrintPreviewDisabled), | 
|  | /*has_selection=*/false); | 
|  | #endif  // BUILDFLAG(ENABLE_PRINTING) | 
|  | } | 
|  |  | 
|  | bool CanPrint(BrowserWindowInterface* bwi) { | 
|  | #if BUILDFLAG(ENABLE_PRINTING) | 
|  | // Do not print when printing is disabled via pref or policy. | 
|  | // Do not print when a page has crashed. | 
|  | // Do not print when a constrained window is showing. It's confusing. | 
|  | // TODO(gbillock): Need to re-assess the call to | 
|  | // IsShowingWebContentsModalDialog after a popup management policy is | 
|  | // refined -- we will probably want to just queue the print request, not | 
|  | // block it. | 
|  | WebContents* const current_tab = | 
|  | bwi->GetTabStripModel()->GetActiveWebContents(); | 
|  | return bwi->GetProfile()->GetPrefs()->GetBoolean(prefs::kPrintingEnabled) && | 
|  | (current_tab && !current_tab->IsCrashed()) && | 
|  | !(IsShowingWebContentsModalDialog(bwi) || | 
|  | GetContentRestrictions(bwi) & CONTENT_RESTRICTION_PRINT); | 
|  | #else   // BUILDFLAG(ENABLE_PRINTING) | 
|  | return false; | 
|  | #endif  // BUILDFLAG(ENABLE_PRINTING) | 
|  | } | 
|  |  | 
|  | #if BUILDFLAG(ENABLE_PRINTING) | 
|  | void BasicPrint(Browser* browser) { | 
|  | printing::StartBasicPrint(browser->tab_strip_model()->GetActiveWebContents()); | 
|  | } | 
|  |  | 
|  | bool CanBasicPrint(Browser* browser) { | 
|  | #if BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG) | 
|  | // If printing is not disabled via pref or policy, it is always possible to | 
|  | // advanced print when the print preview is visible. | 
|  | return browser->profile()->GetPrefs()->GetBoolean(prefs::kPrintingEnabled) && | 
|  | (PrintPreviewShowing(browser) || CanPrint(browser)); | 
|  | #else | 
|  | return false;  // The print dialog is disabled. | 
|  | #endif  // BUILDFLAG(ENABLE_BASIC_PRINT_DIALOG) | 
|  | } | 
|  | #endif  // BUILDFLAG(ENABLE_PRINTING) | 
|  |  | 
|  | bool CanRouteMedia(BrowserWindowInterface* bwi) { | 
|  | // Do not allow user to open Media Router dialog when there is already an | 
|  | // active modal dialog. This avoids overlapping dialogs. | 
|  | return media_router::MediaRouterEnabled(bwi->GetProfile()) && | 
|  | !IsShowingWebContentsModalDialog(bwi); | 
|  | } | 
|  |  | 
|  | void RouteMediaInvokedFromAppMenu(Browser* browser) { | 
|  | DCHECK(CanRouteMedia(browser)); | 
|  |  | 
|  | media_router::MediaRouterDialogController* dialog_controller = | 
|  | media_router::MediaRouterDialogController::GetOrCreateForWebContents( | 
|  | browser->tab_strip_model()->GetActiveWebContents()); | 
|  | if (!dialog_controller) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | dialog_controller->ShowMediaRouterDialog( | 
|  | media_router::MediaRouterDialogActivationLocation::APP_MENU); | 
|  | } | 
|  |  | 
|  | void Find(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("Find")); | 
|  | FindInPage(browser, false, true); | 
|  | } | 
|  |  | 
|  | void FindNext(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("FindNext")); | 
|  | FindInPage(browser, true, true); | 
|  | } | 
|  |  | 
|  | void FindPrevious(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("FindPrevious")); | 
|  | FindInPage(browser, true, false); | 
|  | } | 
|  |  | 
|  | void FindInPage(Browser* browser, bool find_next, bool forward_direction) { | 
|  | browser->GetFeatures().GetFindBarController()->Show(find_next, | 
|  | forward_direction); | 
|  | } | 
|  |  | 
|  | void ShowTabSearch(BrowserWindowInterface* bwi) { | 
|  | bwi->GetBrowserForMigrationOnly()->window()->CreateTabSearchBubble( | 
|  | tab_search::mojom::TabSearchSection::kSearch, | 
|  | tab_search::mojom::TabOrganizationFeature::kNone); | 
|  | } | 
|  |  | 
|  | void CloseTabSearch(Browser* browser) { | 
|  | browser->window()->CloseTabSearchBubble(); | 
|  | } | 
|  |  | 
|  | void ShowContextualTasksSidePanel(BrowserWindowInterface* browser) { | 
|  | CHECK_DEREF( | 
|  | contextual_tasks::ContextualTasksSidePanelCoordinator::From(browser)) | 
|  | .Show(); | 
|  | } | 
|  |  | 
|  | void ToggleVerticalTabs(Browser* browser) { | 
|  | tabs::VerticalTabStripStateController* controller = | 
|  | browser->GetFeatures().vertical_tab_strip_state_controller(); | 
|  | if (!controller) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | bool initial_tab_orientation = controller->ShouldDisplayVerticalTabs(); | 
|  |  | 
|  | controller->SetVerticalTabsEnabled(!initial_tab_orientation); | 
|  | } | 
|  |  | 
|  | void ShowTabDeclutter(Browser* browser) { | 
|  | browser->window()->CreateTabSearchBubble( | 
|  | tab_search::mojom::TabSearchSection::kOrganize, | 
|  | tab_search::mojom::TabOrganizationFeature::kDeclutter); | 
|  | } | 
|  |  | 
|  | bool CanCloseFind(Browser* browser) { | 
|  | WebContents* current_tab = browser->tab_strip_model()->GetActiveWebContents(); | 
|  | if (!current_tab) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | find_in_page::FindTabHelper* find_helper = | 
|  | find_in_page::FindTabHelper::FromWebContents(current_tab); | 
|  | return find_helper ? find_helper->find_ui_active() : false; | 
|  | } | 
|  |  | 
|  | void CloseFind(Browser* browser) { | 
|  | browser->GetFeatures().GetFindBarController()->EndFindSession( | 
|  | find_in_page::SelectionAction::kKeep, find_in_page::ResultAction::kKeep); | 
|  | } | 
|  |  | 
|  | void Zoom(Browser* browser, content::PageZoom zoom) { | 
|  | zoom::PageZoom::Zoom(browser->tab_strip_model()->GetActiveWebContents(), | 
|  | zoom); | 
|  | } | 
|  |  | 
|  | void FocusToolbar(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("FocusToolbar")); | 
|  | browser->window()->FocusToolbar(); | 
|  | } | 
|  |  | 
|  | void FocusLocationBar(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("FocusLocation")); | 
|  | browser->window()->SetFocusToLocationBar(true); | 
|  | } | 
|  |  | 
|  | void FocusSearch(Browser* browser) { | 
|  | // TODO(beng): replace this with FocusLocationBar | 
|  | base::RecordAction(UserMetricsAction("FocusSearch")); | 
|  | browser->window()->GetLocationBar()->FocusSearch(); | 
|  | } | 
|  |  | 
|  | void FocusAppMenu(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("FocusAppMenu")); | 
|  | browser->window()->FocusAppMenu(); | 
|  | } | 
|  |  | 
|  | void FocusBookmarksToolbar(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("FocusBookmarksToolbar")); | 
|  | browser->window()->FocusBookmarksToolbar(); | 
|  | } | 
|  |  | 
|  | void FocusInactivePopupForAccessibility(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("FocusInactivePopupForAccessibility")); | 
|  | browser->window()->FocusInactivePopupForAccessibility(); | 
|  | } | 
|  |  | 
|  | void FocusNextPane(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("FocusNextPane")); | 
|  | browser->window()->RotatePaneFocus(true); | 
|  | } | 
|  |  | 
|  | void FocusPreviousPane(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("FocusPreviousPane")); | 
|  | browser->window()->RotatePaneFocus(false); | 
|  | } | 
|  |  | 
|  | void FocusWebContentsPane(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("FocusWebContentsPane")); | 
|  | browser->window()->FocusWebContentsPane(); | 
|  | } | 
|  |  | 
|  | void ToggleDevToolsWindow(BrowserWindowInterface* bwi, | 
|  | DevToolsToggleAction action, | 
|  | DevToolsOpenedByAction opened_by) { | 
|  | if (action.type() == DevToolsToggleAction::kShowConsolePanel) { | 
|  | base::RecordAction(UserMetricsAction("DevTools_ToggleConsole")); | 
|  | } else { | 
|  | base::RecordAction(UserMetricsAction("DevTools_ToggleWindow")); | 
|  | } | 
|  | DevToolsWindow::ToggleDevToolsWindow(bwi->GetBrowserForMigrationOnly(), | 
|  | action, opened_by); | 
|  | } | 
|  |  | 
|  | bool CanOpenTaskManager() { | 
|  | #if !BUILDFLAG(IS_ANDROID) | 
|  | return true; | 
|  | #else | 
|  | return false; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void OpenTaskManager(BrowserWindowInterface* bwi, | 
|  | task_manager::StartAction start_action) { | 
|  | #if !BUILDFLAG(IS_ANDROID) | 
|  | base::RecordAction(UserMetricsAction("TaskManager")); | 
|  | chrome::ShowTaskManager(bwi ? bwi->GetBrowserForMigrationOnly() : nullptr, | 
|  | start_action); | 
|  | #else | 
|  | NOTREACHED(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void OpenFeedbackDialog(BrowserWindowInterface* bwi, | 
|  | feedback::FeedbackSource source, | 
|  | const std::string& description_template, | 
|  | const std::string& category_tag) { | 
|  | base::RecordAction(UserMetricsAction("Feedback")); | 
|  | chrome::ShowFeedbackPage(bwi, source, description_template, | 
|  | std::string() /* description_placeholder_text */, | 
|  | category_tag, std::string() /* extra_diagnostics */); | 
|  | } | 
|  |  | 
|  | void ToggleBookmarkBar(Browser* browser) { | 
|  | base::RecordAction(UserMetricsAction("ShowBookmarksBar")); | 
|  | ToggleBookmarkBarWhenVisible(browser->profile()); | 
|  | } | 
|  |  | 
|  | void ToggleShowFullURLs(Browser* browser) { | 
|  | bool pref_enabled = browser->profile()->GetPrefs()->GetBoolean( | 
|  | omnibox::kPreventUrlElisionsInOmnibox); | 
|  | browser->profile()->GetPrefs()->SetBoolean( | 
|  | omnibox::kPreventUrlElisionsInOmnibox, !pref_enabled); | 
|  | } | 
|  |  | 
|  | void ToggleShowGoogleLensShortcut(Browser* browser) { | 
|  | bool pref_enabled = browser->profile()->GetPrefs()->GetBoolean( | 
|  | omnibox::kShowGoogleLensShortcut); | 
|  | browser->profile()->GetPrefs()->SetBoolean(omnibox::kShowGoogleLensShortcut, | 
|  | !pref_enabled); | 
|  | } | 
|  |  | 
|  | void ToggleShowAiModeOmniboxButton(Browser* browser) { | 
|  | bool pref_enabled = browser->profile()->GetPrefs()->GetBoolean( | 
|  | omnibox::kShowAiModeOmniboxButton); | 
|  | browser->profile()->GetPrefs()->SetBoolean(omnibox::kShowAiModeOmniboxButton, | 
|  | !pref_enabled); | 
|  | } | 
|  |  | 
|  | void ToggleShowSearchTools(Browser* browser) { | 
|  | bool pref_enabled = | 
|  | browser->profile()->GetPrefs()->GetBoolean(omnibox::kShowSearchTools); | 
|  | browser->profile()->GetPrefs()->SetBoolean(omnibox::kShowSearchTools, | 
|  | !pref_enabled); | 
|  | } | 
|  |  | 
|  | void ShowAppMenu(Browser* browser) { | 
|  | // We record the user metric for this event in AppMenu::RunMenu. | 
|  | browser->window()->ShowAppMenu(); | 
|  | } | 
|  |  | 
|  | void ShowAvatarMenu(Browser* browser) { | 
|  | browser->window()->ShowAvatarBubbleFromAvatarButton( | 
|  | /*is_source_accelerator=*/true); | 
|  | } | 
|  |  | 
|  | // TODO(crbug.com/345770406): Rename the function name. | 
|  | // We removed the extra confirmation step in the Chrome update flow. After the | 
|  | // full rollout of the code, this name will be misleading. We will clean up the | 
|  | // code and its related source enums. | 
|  | void OpenUpdateChromeDialog(Browser* browser) { | 
|  | if (UpgradeDetector::GetInstance()->is_outdated_install()) { | 
|  | UpgradeDetector::GetInstance()->NotifyOutdatedInstall(); | 
|  | } else if (UpgradeDetector::GetInstance()->is_outdated_install_no_au()) { | 
|  | UpgradeDetector::GetInstance()->NotifyOutdatedInstallNoAutoUpdate(); | 
|  | } else { | 
|  | #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) | 
|  | if (base::FeatureList::IsEnabled(features::kFewerUpdateConfirmations)) { | 
|  | chrome::AttemptRelaunch(); | 
|  | return; | 
|  | } | 
|  | #endif | 
|  | base::RecordAction(UserMetricsAction("UpdateChrome")); | 
|  | browser->window()->ShowUpdateChromeDialog(); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool CanRequestTabletSite(WebContents* current_tab) { | 
|  | return current_tab && | 
|  | current_tab->GetController().GetLastCommittedEntry() != nullptr; | 
|  | } | 
|  |  | 
|  | bool IsRequestingTabletSite(Browser* browser) { | 
|  | WebContents* current_tab = browser->tab_strip_model()->GetActiveWebContents(); | 
|  | if (!current_tab) { | 
|  | return false; | 
|  | } | 
|  | content::NavigationEntry* entry = | 
|  | current_tab->GetController().GetLastCommittedEntry(); | 
|  | if (!entry) { | 
|  | return false; | 
|  | } | 
|  | return entry->GetIsOverridingUserAgent(); | 
|  | } | 
|  |  | 
|  | void ToggleRequestTabletSite(Browser* browser) { | 
|  | WebContents* current_tab = browser->tab_strip_model()->GetActiveWebContents(); | 
|  | if (!current_tab) { | 
|  | return; | 
|  | } | 
|  | NavigationController& controller = current_tab->GetController(); | 
|  | NavigationEntry* entry = controller.GetLastCommittedEntry(); | 
|  | if (!entry) { | 
|  | return; | 
|  | } | 
|  | if (entry->GetIsOverridingUserAgent()) { | 
|  | entry->SetIsOverridingUserAgent(false); | 
|  | } else { | 
|  | SetAndroidOsForTabletSite(current_tab); | 
|  | } | 
|  | controller.LoadOriginalRequestURL(); | 
|  | } | 
|  |  | 
|  | void SetAndroidOsForTabletSite(content::WebContents* current_tab) { | 
|  | DCHECK(current_tab); | 
|  | NavigationEntry* entry = current_tab->GetController().GetLastCommittedEntry(); | 
|  | if (entry) { | 
|  | entry->SetIsOverridingUserAgent(true); | 
|  | std::string product = embedder_support::GetProductAndVersion() + " Mobile"; | 
|  | blink::UserAgentOverride ua_override; | 
|  | ua_override.ua_string_override = | 
|  | embedder_support::BuildUserAgentFromOSAndProduct( | 
|  | kOsOverrideForTabletSite, product); | 
|  | ua_override.ua_metadata_override = embedder_support::GetUserAgentMetadata(); | 
|  | ua_override.ua_metadata_override->mobile = true; | 
|  | ua_override.ua_metadata_override->form_factors = {blink::kTabletFormFactor}; | 
|  | ua_override.ua_metadata_override->platform = | 
|  | kChPlatformOverrideForTabletSite; | 
|  | ua_override.ua_metadata_override->platform_version = std::string(); | 
|  | current_tab->SetUserAgentOverride(ua_override, false); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ToggleFullscreenMode(BrowserWindowInterface* browser, | 
|  | bool user_initiated) { | 
|  | DCHECK(browser); | 
|  | browser->GetFeatures() | 
|  | .exclusive_access_manager() | 
|  | ->fullscreen_controller() | 
|  | ->ToggleBrowserFullscreenMode(user_initiated); | 
|  | } | 
|  |  | 
|  | void ClearCache(Browser* browser) { | 
|  | content::BrowsingDataRemover* remover = | 
|  | browser->profile()->GetBrowsingDataRemover(); | 
|  | remover->Remove(base::Time(), base::Time::Max(), | 
|  | content::BrowsingDataRemover::DATA_TYPE_CACHE, | 
|  | content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB); | 
|  | // BrowsingDataRemover takes care of deleting itself when done. | 
|  | } | 
|  |  | 
|  | bool IsDebuggerAttachedToCurrentTab(Browser* browser) { | 
|  | WebContents* contents = browser->tab_strip_model()->GetActiveWebContents(); | 
|  | return contents ? content::DevToolsAgentHost::IsDebuggerAttached(contents) | 
|  | : false; | 
|  | } | 
|  |  | 
|  | void CopyURL(BrowserWindowInterface* bwi, content::WebContents* web_contents) { | 
|  | ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste); | 
|  | scw.WriteText(base::UTF8ToUTF16(web_contents->GetVisibleURL().spec())); | 
|  |  | 
|  | #if !BUILDFLAG(IS_ANDROID) | 
|  | if (toast_features::IsEnabled(toast_features::kLinkCopiedToast)) { | 
|  | ToastController* const toast_controller = | 
|  | bwi->GetFeatures().toast_controller(); | 
|  | if (toast_controller) { | 
|  | toast_controller->MaybeShowToast(ToastParams(ToastId::kLinkCopied)); | 
|  | } | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | bool CanCopyUrl(BrowserWindowInterface* bwi) { | 
|  | return IsWebAppOrCustomTab(bwi) || | 
|  | !sharing_hub::SharingIsDisabledByPolicy(bwi->GetProfile()); | 
|  | } | 
|  |  | 
|  | bool IsWebAppOrCustomTab(const BrowserWindowInterface* bwi) { | 
|  | return | 
|  | #if BUILDFLAG(IS_CHROMEOS) | 
|  | bwi->GetType() == BrowserWindowInterface::TYPE_CUSTOM_TAB || | 
|  | #endif | 
|  | !!bwi->GetAppBrowserController(); | 
|  | } | 
|  |  | 
|  | Browser* OpenInChrome(Browser* hosted_app_browser) { | 
|  | // Find a non-incognito browser. | 
|  | Browser* target_browser = | 
|  | chrome::FindTabbedBrowser(hosted_app_browser->profile(), false); | 
|  |  | 
|  | if (!target_browser) { | 
|  | target_browser = Browser::Create( | 
|  | Browser::CreateParams(hosted_app_browser->profile(), true)); | 
|  | } | 
|  |  | 
|  | web_app::ReparentWebContentsIntoBrowserImpl( | 
|  | hosted_app_browser, | 
|  | hosted_app_browser->tab_strip_model()->GetActiveWebContents(), | 
|  | target_browser); | 
|  | return target_browser; | 
|  | } | 
|  |  | 
|  | bool CanViewSource(const Browser* browser) { | 
|  | if (browser->is_type_devtools()) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | WebContents* web_contents = | 
|  | browser->tab_strip_model()->GetActiveWebContents(); | 
|  |  | 
|  | // Disallow ViewSource if DevTools are disabled. | 
|  | if (!DevToolsWindow::AllowDevToolsFor(browser->profile(), web_contents)) { | 
|  | return false; | 
|  | } | 
|  | return web_contents->GetController().CanViewSource(); | 
|  | } | 
|  |  | 
|  | bool CanToggleCaretBrowsing(Browser* browser) { | 
|  | #if BUILDFLAG(IS_MAC) | 
|  | // On Mac, ignore the keyboard shortcut unless web contents is focused, | 
|  | // because the keyboard shortcut interferes with a Japenese IME when the | 
|  | // omnibox is focused.  See https://crbug.com/1138475 | 
|  | WebContents* web_contents = | 
|  | browser->tab_strip_model()->GetActiveWebContents(); | 
|  | if (!web_contents) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | content::RenderWidgetHostView* rwhv = web_contents->GetRenderWidgetHostView(); | 
|  | return rwhv && rwhv->HasFocus(); | 
|  | #else | 
|  | return true; | 
|  | #endif  // BUILDFLAG(IS_MAC) | 
|  | } | 
|  |  | 
|  | void ToggleCaretBrowsing(Browser* browser) { | 
|  | if (!CanToggleCaretBrowsing(browser)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | PrefService* prefService = browser->profile()->GetPrefs(); | 
|  | bool enabled = prefService->GetBoolean(prefs::kCaretBrowsingEnabled); | 
|  |  | 
|  | if (enabled) { | 
|  | base::RecordAction(base::UserMetricsAction( | 
|  | "Accessibility.CaretBrowsing.DisableWithKeyboard")); | 
|  | prefService->SetBoolean(prefs::kCaretBrowsingEnabled, false); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Show a confirmation dialog, unless either (1) the command-line | 
|  | // flag was used, or (2) the user previously checked the box | 
|  | // indicating not to ask them next time. | 
|  | if (prefService->GetBoolean(prefs::kShowCaretBrowsingDialog) && | 
|  | !base::CommandLine::ForCurrentProcess()->HasSwitch( | 
|  | switches::kEnableCaretBrowsing)) { | 
|  | browser->window()->ShowCaretBrowsingDialog(); | 
|  | } else { | 
|  | base::RecordAction(base::UserMetricsAction( | 
|  | "Accessibility.CaretBrowsing.EnableWithKeyboard")); | 
|  | prefService->SetBoolean(prefs::kCaretBrowsingEnabled, true); | 
|  | } | 
|  | } | 
|  |  | 
|  | void PromptToNameWindow(Browser* browser) { | 
|  | chrome::ShowWindowNamePrompt(browser); | 
|  | } | 
|  |  | 
|  | #if BUILDFLAG(IS_CHROMEOS) | 
|  | void ToggleMultitaskMenu(Browser* browser) { | 
|  | browser->window()->ToggleMultitaskMenu(); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #if !defined(TOOLKIT_VIEWS) | 
|  | std::optional<int> GetKeyboardFocusedTabIndex(const Browser* browser) { | 
|  | return std::nullopt; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | void ShowIncognitoClearBrowsingDataDialog(BrowserWindowInterface* bwi) { | 
|  | bwi->GetBrowserForMigrationOnly() | 
|  | ->window() | 
|  | ->ShowIncognitoClearBrowsingDataDialog(); | 
|  | } | 
|  |  | 
|  | void ShowIncognitoHistoryDisclaimerDialog(Browser* browser) { | 
|  | browser->window()->ShowIncognitoHistoryDisclaimerDialog(); | 
|  | } | 
|  |  | 
|  | bool ShouldInterceptChromeURLNavigationInIncognito(Browser* browser, | 
|  | const GURL& url) { | 
|  | if (!browser || !browser->profile()->IsIncognitoProfile()) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool show_clear_browsing_data_dialog = | 
|  | url == GURL(chrome::kChromeUISettingsURL) | 
|  | .Resolve(chrome::kClearBrowserDataSubPage); | 
|  |  | 
|  | bool show_history_disclaimer_dialog = | 
|  | url == GURL(chrome::kChromeUIHistoryURL); | 
|  |  | 
|  | return show_clear_browsing_data_dialog || show_history_disclaimer_dialog; | 
|  | } | 
|  |  | 
|  | void ProcessInterceptedChromeURLNavigationInIncognito(Browser* browser, | 
|  | const GURL& url) { | 
|  | if (url == GURL(chrome::kChromeUISettingsURL) | 
|  | .Resolve(chrome::kClearBrowserDataSubPage)) { | 
|  | ShowIncognitoClearBrowsingDataDialog(browser); | 
|  | } else if (url == GURL(chrome::kChromeUIHistoryURL)) { | 
|  | ShowIncognitoHistoryDisclaimerDialog(browser); | 
|  | } else { | 
|  | NOTREACHED(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ExecLensOverlay(Browser* browser) { | 
|  | content::WebContents* web_contents = | 
|  | browser->tab_strip_model()->GetActiveWebContents(); | 
|  | CHECK(web_contents); | 
|  |  | 
|  | LensSearchController* const controller = | 
|  | LensSearchController::FromTabWebContents(web_contents); | 
|  | CHECK(controller); | 
|  | controller->OpenLensOverlay(lens::LensOverlayInvocationSource::kAppMenu); | 
|  | BrowserUserEducationInterface::From(browser)->NotifyNewBadgeFeatureUsed( | 
|  | lens::features::kLensOverlay); | 
|  | } | 
|  |  | 
|  | void ExecLensRegionSearch(Browser* browser) { | 
|  | #if BUILDFLAG(ENABLE_LENS_DESKTOP_GOOGLE_BRANDED_FEATURES) | 
|  | Profile* profile = browser->profile(); | 
|  | TemplateURLService* service = | 
|  | TemplateURLServiceFactory::GetForProfile(profile); | 
|  | WebContents* contents = browser->tab_strip_model()->GetActiveWebContents(); | 
|  | GURL url = contents->GetController().GetLastCommittedEntry()->GetURL(); | 
|  |  | 
|  | if (lens::IsRegionSearchEnabled(browser, profile, service, url)) { | 
|  | const bool is_google_dsp = search::DefaultSearchProviderIsGoogle(profile); | 
|  | const lens::AmbientSearchEntryPoint entry_point = | 
|  | is_google_dsp ? lens::AmbientSearchEntryPoint:: | 
|  | CONTEXT_MENU_SEARCH_REGION_WITH_GOOGLE_LENS | 
|  | : lens::AmbientSearchEntryPoint:: | 
|  | CONTEXT_MENU_SEARCH_REGION_WITH_WEB; | 
|  | browser->GetFeatures().lens_region_search_controller()->Start( | 
|  | contents, | 
|  | /*use_fullscreen_capture=*/false, is_google_dsp, entry_point); | 
|  | } | 
|  | #endif  // BUILDFLAG(ENABLE_LENS_DESKTOP_GOOGLE_BRANDED_FEATURES) | 
|  | } | 
|  |  | 
|  | void OpenCommerceProductSpecificationsTab(Browser* browser, | 
|  | const std::vector<GURL>& urls, | 
|  | const int position) { | 
|  | auto* prefs = browser->profile()->GetPrefs(); | 
|  | // If user has not accepted the latest disclosure, show the disclosure dialog | 
|  | // first. | 
|  | if (prefs && prefs->GetInteger( | 
|  | commerce::kProductSpecificationsAcceptedDisclosureVersion) != | 
|  | static_cast<int>(commerce::product_specifications::mojom:: | 
|  | DisclosureVersion::kV1)) { | 
|  | commerce::DialogArgs dialog_args(urls, std::string(), /*set_id=*/"", | 
|  | /*in_new_tab=*/true); | 
|  | commerce::ProductSpecificationsDisclosureDialog::ShowDialog( | 
|  | browser->profile(), browser->tab_strip_model()->GetActiveWebContents(), | 
|  | std::move(dialog_args)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | chrome::AddTabAt(browser, commerce::GetProductSpecsTabUrl(urls), position + 1, | 
|  | true, std::nullopt); | 
|  | } | 
|  |  | 
|  | }  // namespace chrome |