// 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 <algorithm>
#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/containers/adapters.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/time/time.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/autocomplete/autocomplete_classifier_factory.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/contextual_tasks/contextual_tasks_side_panel_coordinator.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_utils.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/report_unsafe_site_dialog.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/resource_coordinator/tab_lifecycle_unit_external.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_init_state.h"
#include "chrome/browser/ui/browser_live_tab_context.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/browser_window/public/create_browser_window.h"
#include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
#include "chrome/browser/ui/dialogs/browser_dialogs.h"
#include "chrome/browser/ui/dialogs/outdated_upgrade_bubble.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/focus/browser_focus_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/navigator/browser_navigator.h"
#include "chrome/browser/ui/navigator/browser_navigator_params.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/side_panel/side_panel_entry.h"
#include "chrome/browser/ui/side_panel/side_panel_entry_key.h"
#include "chrome/browser/ui/side_panel/side_panel_ui.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/back_to_opener/back_to_opener_controller.h"
#include "chrome/browser/ui/tabs/features.h"
#include "chrome/browser/ui/tabs/new_tab_grouping_user_data.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_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_selection_state.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/waap/initial_webui_window_metrics_manager.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/tab_search/tab_search.mojom.h"
#include "chrome/browser/ui/window_feature_controller/window_feature_controller.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/common/webui_url_constants.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_bar_visibility_state.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/pref_names.h"
#include "components/contextual_tasks/public/features.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/autocomplete_classifier.h"
#include "components/omnibox/browser/autocomplete_match.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_params.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/split_tabs/split_tab_visual_data.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/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/child_process_security_policy.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/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 "ui/base/clipboard/clipboard.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/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(IS_WIN)
#include <windows.h>

#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.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"
#include "chrome/browser/ui/views/contextual_tasks/contextual_tasks_close_button_controller.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,
    BrowserWindowInterface* original_browser) {
  BrowserWindowInterface* new_browser = nullptr;
  DCHECK(original_browser->GetType() != BrowserWindowInterface::TYPE_APP_POPUP);
  if (original_browser->GetType() == BrowserWindowInterface::TYPE_APP) {
    const Browser* browser = original_browser->GetBrowserForMigrationOnly();
    const bool is_trusted_source =
        WindowFeatureController::From(original_browser)->IsTrustedSource();
    new_browser = CreateBrowserWindow(BrowserWindowCreateParams::CreateForApp(
        BrowserInitState::From(browser)->create_params().app_name,
        is_trusted_source, gfx::Rect(), original_browser->GetProfile(), true));
  } else {
    new_browser = CreateBrowserWindow(BrowserWindowCreateParams(
        original_browser->GetType(), original_browser->GetProfile(), 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.
  ui::BaseWindow* new_window = new_browser->GetWindow();
  new_window->SetBounds(
      gfx::Rect(new_window->GetRestoredBounds().origin(),
                original_browser->GetWindow()->GetRestoredBounds().size()));

  // We need to show the browser now.  Otherwise ContainerWin assumes the
  // WebContents is invisible and won't size it.
  new_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/40557069
  if (!web_contents) {
    return false;
  }
  return chrome::GetURLAndTitleToBookmark(web_contents, url, title);
}

ReadingListModel* GetReadingListModel(BrowserWindowInterface* browser) {
  ReadingListModel* model =
      ReadingListModelFactory::GetForBrowserContext(browser->GetProfile());
  if (!model || !model->loaded()) {
    return nullptr;  // Ignore requests until model has loaded.
  }
  return model;
}

bool CanMoveWebContentsToReadLater(BrowserWindowInterface* browser,
                                   content::WebContents* web_contents,
                                   ReadingListModel* model,
                                   GURL* url,
                                   std::u16string* title) {
  return model && GetTabURLAndTitleToSave(web_contents, url, title) &&
         model->IsUrlSupported(*url) &&
         !browser->GetProfile()->IsGuestSession();
}

bool BookmarkCurrentTabHelper(BrowserWindowInterface* 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->GetTabStripModel()->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/40557069
  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(BrowserWindowInterface* browser,
                                     int index,
                                     int dst_index) {
  content::WebContents* contents =
      browser->GetTabStripModel()->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 (WindowFeatureController::From(browser)->CanSupportWindowFeature(
          WindowFeatureController::WindowFeature::kFeatureTabStrip)) {
    // 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->GetTabStripModel();
    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();
}

// Preserves the focus state in the target window if a focused tab group was
// moved to a new window (either as an entire group or via moving all of its
// tabs) and the target window contains only the moved group (and any pinned
// tabs).
void MaybePreserveFocusedGroupInTarget(
    TabStripModel* target_model,
    std::optional<tab_groups::TabGroupId> focused_group) {
  if (!focused_group.has_value() || !target_model->group_model() ||
      !target_model->group_model()->ContainsTabGroup(*focused_group)) {
    return;
  }

  const int non_pinned_count =
      target_model->count() - target_model->IndexOfFirstNonPinnedTab();
  const int group_tab_count =
      target_model->group_model()->GetTabGroup(*focused_group)->tab_count();
  if (non_pinned_count == group_tab_count) {
    target_model->SetFocusedGroup(*focused_group);
  }
}

void MoveGroupToWindowImpl(BrowserWindowInterface* source,
                           BrowserWindowInterface* target,
                           tab_groups::TabGroupId group) {
  CHECK(source->GetTabStripModel()->group_model()->ContainsTabGroup(group));

  const std::optional<tab_groups::TabGroupId> focused_group =
      source->GetTabStripModel()->GetFocusedGroup() == group
          ? std::make_optional(group)
          : std::nullopt;

  tab_groups::TabGroupSyncService* tab_group_service =
      tab_groups::TabGroupSyncServiceFactory::GetForProfile(
          source->GetProfile());

  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->GetTabStripModel()->DetachTabGroupForInsertion(group);
  target->GetTabStripModel()->InsertDetachedTabGroupAt(
      std::move(detached_group), 0);

  MaybePreserveFocusedGroupInTarget(target->GetTabStripModel(), focused_group);

  target->GetWindow()->Show();
}

void MoveTabsToWindowImpl(BrowserWindowInterface* source,
                          BrowserWindowInterface* target,
                          const std::vector<int>& tab_indices) {
  if (tab_indices.empty()) {
    return;
  }

  TabStripModel* source_model = source->GetTabStripModel();
  TabStripModel* target_model = target->GetTabStripModel();

  const std::optional<tab_groups::TabGroupId> source_focused_group =
      source_model->GetFocusedGroup();

  // 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);
      }
    }
  }

  MaybePreserveFocusedGroupInTarget(target_model, source_focused_group);

  target->GetWindow()->Show();
}

BrowserWindowInterface* CreateNewBrowser(BrowserWindowInterface* browser,
                                         bool user_gesture) {
  auto params = BrowserWindowCreateParams(browser->GetProfile(), user_gesture);
  return CreateBrowserWindow(std::move(params));
}

struct MruTabResult {
  raw_ptr<BrowserWindowInterface> browser;
  int index;
};

std::optional<MruTabResult> GetGlobalMruTab(
    BrowserCollection* collection,
    BrowserWindowInterface* active_browser) {
  BrowserWindowInterface* mru_browser = nullptr;
  int mru_idx = -1;
  base::Time max_time = base::Time::Min();

  collection->ForEach(
      [&](BrowserWindowInterface* b) {
        TabStripModel* model = b->GetTabStripModel();
        int i = 0;
        for (auto it = model->begin(); it != model->end(); ++it, ++i) {
          if (b == active_browser && i == model->active_index()) {
            continue;
          }
          content::WebContents* contents = (*it)->GetContents();
          auto* lifecycle_unit =
              resource_coordinator::TabLifecycleUnitExternal::FromWebContents(
                  contents);
          if (!lifecycle_unit) {
            continue;
          }
          base::Time last_active = lifecycle_unit->GetLastFocusedTime();
          if (last_active > max_time) {
            max_time = last_active;
            mru_browser = b;
            mru_idx = i;
          }
        }
        return true;
      },
      BrowserCollection::Order::kActivation);

  if (mru_browser) {
    return MruTabResult{mru_browser, mru_idx};
  }
  return std::nullopt;
}

void ActivateTab(TabStripModel* model,
                 int index,
                 TabStripUserGestureDetails gesture_detail) {
  std::optional<tab_groups::TabGroupId> group_id =
      model->GetTabGroupForTab(index);
  if (group_id.has_value() && model->IsGroupCollapsed(group_id.value())) {
    TabGroup* group = model->group_model()->GetTabGroup(group_id.value());
    tab_groups::TabGroupVisualData new_visual_data(
        group->visual_data()->title(), group->visual_data()->color(),
        /*is_collapsed=*/false);
    model->ChangeTabGroupVisuals(group_id.value(), std::move(new_visual_data));
  }
  model->ActivateTabAt(index, gesture_detail);
}

bool IsTabSelectable(
    TabStripModel* model,
    tabs::TabInterface* tab,
    const std::optional<tab_groups::TabGroupId>& focused_group) {
  // Do not select the tab if it is in a collapsed group.
  if (tab->GetGroup().has_value() &&
      model->IsGroupCollapsed(tab->GetGroup().value())) {
    return false;
  }

  // Do not select the tab if it is not part of the focused state.
  if (focused_group.has_value() &&
      !tabs::TabStripModelSelectionState::IsTabValidInFocusedGroup(
          tab, focused_group)) {
    return false;
  }

  return true;
}

bool FocusAdjacentTabGroupInFocusMode(TabStripModel* tab_strip_model,
                                      bool next) {
  std::optional<tab_groups::TabGroupId> current_focused_group =
      tab_strip_model->GetFocusedGroup();
  if (!current_focused_group.has_value()) {
    return false;
  }

  TabGroupModel* group_model = tab_strip_model->group_model();
  if (!group_model) {
    return false;
  }

  std::vector<tab_groups::TabGroupId> groups_in_order =
      group_model->ListTabGroups();
  if (groups_in_order.empty()) {
    return false;
  }

  std::ranges::sort(groups_in_order, {}, [&](const tab_groups::TabGroupId& id) {
    return group_model->GetTabGroup(id)->ListTabs().start();
  });

  auto it = std::ranges::find(groups_in_order, *current_focused_group);
  if (it == groups_in_order.end()) {
    return false;
  }

  size_t current_index = std::distance(groups_in_order.begin(), it);
  size_t target_index = next ? (current_index + 1) % groups_in_order.size()
                             : (current_index + groups_in_order.size() - 1) %
                                   groups_in_order.size();
  tab_strip_model->SetFocusedGroup(groups_in_order[target_index]);
  return true;
}

}  // 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) {
  auto* controller = web_app::AppBrowserController::From(browser);
  if (!controller) {
    return nullptr;
  }
  return extensions::ExtensionRegistry::Get(browser->GetProfile())
      ->GetExtensionById(controller->app_id(),
                         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|.
tabs::TabInterface* GetTabAndRevertIfNecessaryHelper(
    BrowserWindowInterface* browser,
    WindowOpenDisposition disposition,
    tabs::TabInterface* current_tab) {
  CHECK(current_tab);
  WebContents* current_contents = current_tab->GetContents();

  switch (disposition) {
    case WindowOpenDisposition::NEW_FOREGROUND_TAB:
    case WindowOpenDisposition::NEW_BACKGROUND_TAB: {
      std::unique_ptr<WebContents> new_tab = current_contents->Clone();
      WebContents* raw_new_tab = new_tab.get();
      if (disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB) {
        new_tab->WasHidden();
      }
      browser->GetTabStripModel()->AddWebContents(
          std::move(new_tab), -1, ui::PAGE_TRANSITION_LINK,
          (disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB)
              ? AddTabTypes::ADD_ACTIVE
              : AddTabTypes::ADD_NONE,
          current_tab->GetGroup());
      return browser->GetTabStripModel()->GetTabForWebContents(raw_new_tab);
    }
    case WindowOpenDisposition::NEW_WINDOW: {
      std::unique_ptr<WebContents> new_tab = current_contents->Clone();
      WebContents* raw_new_tab = new_tab.get();
      BrowserWindowInterface* new_browser = CreateBrowserWindow(
          BrowserWindowCreateParams(browser->GetProfile(), true));
      new_browser->GetTabStripModel()->AddWebContents(std::move(new_tab), -1,
                                                      ui::PAGE_TRANSITION_LINK,
                                                      AddTabTypes::ADD_ACTIVE);
      new_browser->GetWindow()->Show();
      return new_browser->GetTabStripModel()->GetTabForWebContents(raw_new_tab);
    }
    default:
      BrowserWindow::FromBrowser(browser)->GetLocationBar()->Revert();
      return current_tab;
  }
}

// Like the above, but auto-computes the current tab
tabs::TabInterface* GetTabAndRevertIfNecessary(
    BrowserWindowInterface* browser,
    WindowOpenDisposition disposition) {
  tabs::TabInterface* active_tab = browser->GetTabStripModel()->GetActiveTab();
  return GetTabAndRevertIfNecessaryHelper(browser, disposition, active_tab);
}

void ReloadInternal(BrowserWindowInterface* browser,
                    WindowOpenDisposition disposition,
                    bool bypass_cache) {
  TabStripModel* const tab_strip_model = browser->GetTabStripModel();
  tabs::TabInterface* const active_tab = tab_strip_model->GetActiveTab();

  std::vector<tabs::TabInterface*> tabs_to_reload;

  // When using split view, both tabs composing the split view are considered
  // selected by the `selection_model` and `selection_model().size()` returns 2;
  // even though visually, both tabs are represented by a single UI tab in the
  // tab strip. To detect whether the user has selected multiple UI tabs,
  // compare the number of model selected tabs wither either 2 or 1 depending on
  // whether the active tab is split.
  bool multiple_ui_tabs_selected =
      active_tab && tab_strip_model->selection_model().size() >
                        (active_tab->IsSplit() ? 2 : 1);

  if (multiple_ui_tabs_selected) {
    // 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 (tabs::TabInterface* t :
         tab_strip_model->selection_model().selected_tabs()) {
      tabs_to_reload.push_back(t);
    }
  } else {
    CHECK(active_tab);
    tabs_to_reload.push_back(active_tab);
  }

  base::UmaHistogramCounts100("TabStrip.Tab.ReloadCount",
                              tabs_to_reload.size());

  for (tabs::TabInterface* 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->GetBrowserWindowInterface() != browser) {
      continue;
    }

    tabs::TabInterface* const new_tab_interface =
        GetTabAndRevertIfNecessaryHelper(browser, disposition, tab);
    CHECK(new_tab_interface);
    WebContents* const new_tab = new_tab_interface->GetContents();
    CHECK(new_tab);

    // If the `tab` is the activated page, give the focus to it, as this is
    // caused by a user action
    if (tab == active_tab && !new_tab->FocusLocationBarByDefault()) {
      new_tab->Focus();
    }

    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* browser) {
  WebContents* const web_contents =
      browser->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 BrowserWindowInterface* browser) {
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
  WebContents* contents = browser->GetTabStripModel()->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)

// Helper function for tab grouping commands.
// Returns a vector of indices of ungrouped, non-pinned tabs.
std::vector<int> GetUngroupedTabIndices(BrowserWindowInterface* browser) {
  TabStripModel* tab_strip_model = browser->GetTabStripModel();
  std::vector<int> indices;
  if (!tab_strip_model->SupportsTabGroups()) {
    return indices;
  }

  int i = 0;
  for (const tabs::TabInterface* t : *tab_strip_model) {
    if (!t->GetGroup() && !t->IsPinned()) {
      indices.push_back(i);
    }
    ++i;
  }
  return indices;
}

}  // namespace

bool IsCommandEnabled(BrowserWindowInterface* browser, int command) {
  return browser->GetFeatures().browser_command_controller()->IsCommandEnabled(
      command);
}

bool SupportsCommand(BrowserWindowInterface* browser, int command) {
  return browser->GetFeatures().browser_command_controller()->SupportsCommand(
      command);
}

bool ExecuteCommand(BrowserWindowInterface* browser,
                    int command,
                    base::TimeTicks time_stamp) {
  return browser->GetFeatures().browser_command_controller()->ExecuteCommand(
      command, std::nullopt, time_stamp);
}

bool ExecuteCommandWithContext(BrowserWindowInterface* browser,
                               int command,
                               actions::ActionInvocationContext context,
                               base::TimeTicks time_stamp) {
  return browser->GetFeatures().browser_command_controller()->ExecuteCommand(
      command, std::move(context), time_stamp);
}

bool ExecuteCommandWithDisposition(BrowserWindowInterface* browser,
                                   int command,
                                   WindowOpenDisposition disposition,
                                   base::TimeTicks time_stamp) {
  return browser->GetFeatures()
      .browser_command_controller()
      ->ExecuteCommandWithDisposition(command, disposition, std::nullopt,
                                      time_stamp);
}

bool ExecuteCommandWithDispositionAndContext(
    BrowserWindowInterface* browser,
    int command,
    WindowOpenDisposition disposition,
    actions::ActionInvocationContext context,
    base::TimeTicks time_stamp) {
  return browser->GetFeatures()
      .browser_command_controller()
      ->ExecuteCommandWithDisposition(command, disposition, std::move(context),
                                      time_stamp);
}

void UpdateCommandEnabled(BrowserWindowInterface* browser,
                          int command,
                          bool enabled) {
  browser->GetFeatures().browser_command_controller()->UpdateCommandEnabled(
      command, enabled);
}

void AddCommandObserver(BrowserWindowInterface* browser,
                        int command,
                        CommandObserver* observer) {
  browser->GetFeatures().browser_command_controller()->AddCommandObserver(
      command, observer);
}

void RemoveCommandObserver(BrowserWindowInterface* browser,
                           int command,
                           CommandObserver* observer) {
  browser->GetFeatures().browser_command_controller()->RemoveCommandObserver(
      command, observer);
}

int GetContentRestrictions(const BrowserWindowInterface* browser) {
  int content_restrictions = 0;
  WebContents* const current_tab =
      browser->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());
    }
  }
}

BrowserWindowInterface* OpenEmptyWindow(Profile* profile,
                                        bool should_trigger_session_restore) {
  if (GetBrowserWindowCreationStatusForProfile(*profile) !=
      Browser::CreationStatus::kOk) {
    return nullptr;
  }

  // Don't create a new window when the profile is shutting down.
  if (profile->ShutdownStarted()) {
    return nullptr;
  }

  BrowserWindowCreateParams params(BrowserWindowInterface::TYPE_NORMAL, profile,
                                   true);
  params.should_trigger_session_restore = should_trigger_session_restore;

  base::TimeTicks now = base::TimeTicks::Now();
  BrowserWindowInterface* browser = CreateBrowserWindow(std::move(params));
  if (auto* manager = InitialWebUIWindowMetricsManager::From(browser)) {
    manager->SetWindowCreationInfo(
        waap::NewWindowCreationSource::kBrowserInitiated, now);
  }

  // Startup tabs could be created during browser creation. Add an empty tab
  // only if no tabs are created.
  if (browser->GetTabStripModel()->empty()) {
    AddTabAt(browser, GURL(), -1, true);
  }

  browser->GetWindow()->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_window_interface(), url,
                        ui::PAGE_TRANSITION_LINK);
}

bool CanGoBack(const BrowserWindowInterface* browser) {
  return CanGoBack(browser->GetTabStripModel()->GetActiveWebContents());
}

bool CanGoBack(content::WebContents* web_contents) {
  return web_contents &&
         (web_contents->GetController().CanGoBack() ||
          back_to_opener::BackToOpenerController::CanGoBackToOpener(
              web_contents));
}

bool ShouldEnableBackButton(const BrowserWindowInterface* browser) {
  content::WebContents* web_contents =
      browser->GetTabStripModel()->GetActiveWebContents();
  if (!web_contents) {
    return false;
  }

  // Check for regular back navigation first.
  if (web_contents->GetController().ShouldEnableBackButton()) {
    return true;
  }

  // If no regular back navigation, check for back-to-opener.
  return back_to_opener::BackToOpenerController::CanGoBackToOpener(
      web_contents);
}

enum class BackNavigationMenuIPHTrigger : int {
  kUserPerformsManyBackNavigation = 0,
  kUserPerformsChainedBackNavigation,
  kUserPerformsChainedBackNavigationWithBackButton
};

const char kBackNavigationMenuIPHExperimentParamName[] = "x_experiment";

void MaybeShowFeatureBackNavigationMenuPromo(BrowserWindowInterface* 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(BrowserWindowInterface* browser,
            WindowOpenDisposition disposition) {
  tabs::TabInterface* tab = GetTabAndRevertIfNecessary(browser, disposition);
  CHECK(tab);
  GoBack(tab->GetContents());
}

void GoBack(content::WebContents* web_contents) {
  base::RecordAction(UserMetricsAction("Back"));

  if (!web_contents) {
    return;
  }

  // Try regular back navigation first.
  if (web_contents->GetController().CanGoBack()) {
    web_contents->GetController().GoBack();
    BrowserWindowInterface* browser =
        GlobalBrowserCollection::GetInstance()->FindBrowserWithTab(
            web_contents);
    if (browser) {
      MaybeShowFeatureBackNavigationMenuPromo(browser, web_contents);
    }
    return;
  }

  // If no regular back navigation, try back-to-opener.
  back_to_opener::BackToOpenerController::GoBackToOpener(web_contents);
}

bool CanGoForward(const BrowserWindowInterface* browser) {
  return browser->GetTabStripModel()
      ->GetActiveWebContents()
      ->GetController()
      .CanGoForward();
}

bool CanGoForward(content::WebContents* web_contents) {
  return web_contents->GetController().CanGoForward();
}

bool ShouldEnableForwardButton(const BrowserWindowInterface* browser) {
  return browser->GetTabStripModel()
      ->GetActiveWebContents()
      ->GetController()
      .ShouldEnableForwardButton();
}

void GoForward(BrowserWindowInterface* browser,
               WindowOpenDisposition disposition) {
  base::RecordAction(UserMetricsAction("Forward"));
  if (CanGoForward(browser)) {
    tabs::TabInterface* tab = GetTabAndRevertIfNecessary(browser, disposition);
    CHECK(tab);
    tab->GetContents()->GetController().GoForward();
  }
}

void GoForward(content::WebContents* web_contents) {
  base::RecordAction(UserMetricsAction("Forward"));
  if (CanGoForward(web_contents)) {
    web_contents->GetController().GoForward();
  }
}

void NavigateToIndexWithDisposition(BrowserWindowInterface* browser,
                                    int index,
                                    WindowOpenDisposition disposition) {
  tabs::TabInterface* tab = GetTabAndRevertIfNecessary(browser, disposition);
  CHECK(tab);
  WebContents* web_contents = tab->GetContents();
  CHECK(web_contents);
  NavigationController* controller = &web_contents->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(BrowserWindowInterface* browser,
                          WindowOpenDisposition disposition) {
  base::RecordAction(UserMetricsAction("ReloadBypassingCache"));
  ReloadInternal(browser, disposition, true);
}

bool CanReload(const BrowserWindowInterface* browser) {
  return browser &&
         browser->GetType() != BrowserWindowInterface::TYPE_DEVTOOLS &&
         browser->GetType() != BrowserWindowInterface::TYPE_PICTURE_IN_PICTURE;
}

void Home(BrowserWindowInterface* 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->GetProfile()->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->GetProfile()->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->GetType() == BrowserWindowInterface::TYPE_APP ||
      browser->GetType() == BrowserWindowInterface::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->GetTabStripModel()->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(
    BrowserWindowInterface* 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 = BrowserWindow::FromBrowser(browser);
  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(&params);

#if BUILDFLAG(ENABLE_EXTENSIONS)
  Profile* profile = browser->GetProfile();
  DCHECK(extensions::ExtensionSystem::Get(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(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(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("Stop"));
  browser->GetTabStripModel()->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 (auto* const app_browser_controller =
          web_app::AppBrowserController::From(browser)) {
    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();
}

#if BUILDFLAG(IS_WIN)
void OpenMoveWindow(BrowserWindowInterface* browser) {
  HWND hwnd = BrowserView::GetBrowserViewForBrowser(
                  browser->GetBrowserForMigrationOnly())
                  ->GetWidget()
                  ->GetNativeWindow()
                  ->GetHost()
                  ->GetAcceleratedWidget();
  PostMessage(hwnd, WM_SYSCOMMAND, SC_MOVE, 0);
}

void OpenSizeWindow(BrowserWindowInterface* browser) {
  HWND hwnd = BrowserView::GetBrowserViewForBrowser(
                  browser->GetBrowserForMigrationOnly())
                  ->GetWidget()
                  ->GetNativeWindow()
                  ->GetHost()
                  ->GetAcceleratedWidget();
  PostMessage(hwnd, WM_SYSCOMMAND, SC_SIZE, 0);
}
#endif  // BUILDFLAG(IS_WIN)

content::WebContents& NewTab(BrowserWindowInterface* browser,
                             NewTabTypes context) {
  if (context != NewTabTypes::kNoUserAction) {
    base::RecordAction(base::UserMetricsAction("NewTab"));
  }

  UMA_HISTOGRAM_ENUMERATION("Tab.NewTab", context,
                            NewTabTypes::kNewTabEnumCount);

  const tabs::TabInterface* active_tab =
      browser->GetTabStripModel()->GetActiveTab();
  const std::optional<tab_groups::TabGroupId> active_tab_group_id =
      active_tab ? active_tab->GetGroup() : std::nullopt;

  browser->GetProfile()->SetUserData(
      NewTabGroupingUserData::kNewTabGroupingUserDataKey,
      std::make_unique<NewTabGroupingUserData>(active_tab_group_id));

  const NavigateParams::WindowAction window_action =
      context == NewTabTypes::kNoUserAction
          ? NavigateParams::WindowAction::kNoAction
          : NavigateParams::WindowAction::kShowWindow;

  if (WindowFeatureController::From(browser)->SupportsWindowFeature(
          WindowFeatureController::WindowFeature::kFeatureTabStrip)) {
    return *AddAndReturnTabAt(browser, GURL(), -1, /*foreground=*/true,
                              std::nullopt, /*pinned=*/false, window_action);
  }

  ScopedTabbedBrowserDisplayer displayer(browser->GetProfile());
  BrowserWindowInterface* displayer_browser =
      displayer.browser_window_interface();
  auto* contents = AddAndReturnTabAt(displayer_browser, GURL(), -1,
                                     /*foreground=*/true, std::nullopt,
                                     /*pinned=*/false, window_action);
  displayer_browser->GetWindow()->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/41270207.
  displayer_browser->GetTabStripModel()->GetActiveWebContents()->RestoreFocus();

  return *contents;
}

void NewTabToRight(BrowserWindowInterface* browser) {
  browser->GetTabStripModel()->ExecuteContextMenuCommand(
      browser->GetTabStripModel()->active_index(),
      TabStripModel::CommandNewTabToRight);
}

void NewTabFromClipboardURL(BrowserWindowInterface* browser) {
#if BUILDFLAG(IS_LINUX)
  if (ui::Clipboard::IsSupportedClipboardBuffer(
          ui::ClipboardBuffer::kSelection)) {
    ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
    CHECK(clipboard)
        << "Clipboard instance is not available, cannot proceed with "
           "middle mouse button action.";
    clipboard->ReadText(
        ui::ClipboardBuffer::kSelection, /* data_dst = */ std::nullopt,
        base::BindOnce(
            [](base::WeakPtr<BrowserWindowInterface> browser_weak,
               std::u16string text) {
              if (!browser_weak || text.empty()) {
                return;
              }
              base::RecordAction(
                  base::UserMetricsAction("NewTabButton_PasteAndNavigate"));
              AutocompleteMatch match;
              AutocompleteClassifierFactory::GetForProfile(
                  browser_weak->GetProfile())
                  ->Classify(text, false, false,
                             metrics::OmniboxEventProto::BLANK, &match,
                             nullptr);
              if (match.destination_url.is_valid() &&
                  content::ChildProcessSecurityPolicy::GetInstance()
                      ->IsWebSafeScheme(
                          std::string(match.destination_url.scheme()))) {
                browser_weak->GetTabStripModel()->delegate()->AddTabAt(
                    match.destination_url, -1, true);
              }
            },
            browser->GetWeakPtr()));
  }
#endif
}

void CloseTab(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("CloseTab_Accelerator"));

  tabs::TabInterface* active_tab = browser->GetTabStripModel()->GetActiveTab();
  if (!active_tab) {
    return;
  }

  // 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 =
      active_tab->IsSplit() &&
      browser->GetTabStripModel()->selection_model().size() == 2;

  if (only_active_split_tab_selected) {
    RecordTabCloseCount(1);
    active_tab->GetContents()->Close();
    return;
  }

#if !BUILDFLAG(IS_ANDROID)
  if (base::FeatureList::IsEnabled(
          contextual_tasks::kContextualTasksCloseTabExpandsSidePanel)) {
    ContextualTasksCloseButtonController* const close_button_controller =
        ContextualTasksCloseButtonController::From(browser);
    if (close_button_controller &&
        close_button_controller->ShouldShowCloseButton()) {
      close_button_controller->MaybeCloseTabExpandSidePanel();
      return;
    }
  }
#endif

  ToastController* toast_controller = browser->GetFeatures().toast_controller();
  if (!toast_controller) {
    CloseSelectedTabAndRecordTabCountMetric(browser);
    return;
  }

  const auto& selected_tabs =
      browser->GetTabStripModel()->selection_model().selected_tabs();
  bool all_selected_tabs_pinned = !selected_tabs.empty();
  for (const tabs::TabInterface* tab : selected_tabs) {
    if (!tab->IsPinned()) {
      all_selected_tabs_pinned = false;
      break;
    }
  }

  // If all selected tabs are pinned, show a confirmation toast if one isn't
  // already shown.
  if (all_selected_tabs_pinned &&
      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());
    params.body_string_cardinality_param = selected_tabs.size();
    toast_controller->MaybeShowToast(std::move(params));
  } else {
    CloseSelectedTabAndRecordTabCountMetric(browser);
    if (all_selected_tabs_pinned) {
      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(BrowserWindowInterface* browser,
                   TabStripUserGestureDetails gesture_detail) {
  base::RecordAction(UserMetricsAction("SelectNextTab"));
  browser->GetTabStripModel()->SelectNextTab(gesture_detail);
}

void SelectPreviousTab(BrowserWindowInterface* browser,
                       TabStripUserGestureDetails gesture_detail) {
  base::RecordAction(UserMetricsAction("SelectPrevTab"));
  browser->GetTabStripModel()->SelectPreviousTab(gesture_detail);
}

bool IsCtrlTabMruEnabled(BrowserWindowInterface* browser) {
  return base::FeatureList::IsEnabled(features::kCtrlTabMru) &&
         browser->GetProfile()->GetPrefs()->GetBoolean(prefs::kCtrlTabMru);
}

void CycleToMruTab(BrowserWindowInterface* browser,
                   TabStripUserGestureDetails gesture_detail) {
  auto mru_result = GetGlobalMruTab(
      ProfileBrowserCollection::GetForProfile(browser->GetProfile()), browser);
  if (mru_result) {
    if (mru_result->browser != browser) {
      mru_result->browser->GetWindow()->Activate();
    }
    ActivateTab(mru_result->browser->GetTabStripModel(), mru_result->index,
                gesture_detail);
  }
}

void MoveTabNext(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("MoveTabNext"));
  browser->GetTabStripModel()->MoveTabNext();
}

void MoveTabPrevious(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("MoveTabPrevious"));
  browser->GetTabStripModel()->MoveTabPrevious();
}

void SelectNumberedTab(BrowserWindowInterface* browser,
                       int index,
                       TabStripUserGestureDetails gesture_detail) {
  TabStripModel* model = browser->GetTabStripModel();
  std::optional<tab_groups::TabGroupId> focused_group =
      model->GetFocusedGroup();
  int visible_count = 0;
  for (tabs::TabInterface* tab : *model) {
    if (!IsTabSelectable(model, tab, focused_group)) {
      continue;
    }

    if (visible_count == index) {
      base::RecordAction(UserMetricsAction("SelectNumberedTab"));
      model->ActivateTab(tab, gesture_detail);
      break;
    }
    visible_count += 1;
  }
}

void SelectLastTab(BrowserWindowInterface* browser,
                   TabStripUserGestureDetails gesture_detail) {
  TabStripModel* model = browser->GetTabStripModel();
  std::optional<tab_groups::TabGroupId> focused_group =
      model->GetFocusedGroup();
  for (tabs::TabInterface* tab : base::Reversed(*model)) {
    if (!IsTabSelectable(model, tab, focused_group)) {
      continue;
    }

    base::RecordAction(UserMetricsAction("SelectLastTab"));
    model->ActivateTab(tab, gesture_detail);
    break;
  }
}

void DuplicateTab(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("Duplicate"));
  DuplicateTabAt(browser, browser->GetTabStripModel()->active_index());
}

bool CanDuplicateTab(const BrowserWindowInterface* browser) {
  return CanDuplicateTabAt(browser,
                           browser->GetTabStripModel()->active_index());
}

bool CanDuplicateKeyboardFocusedTab(const BrowserWindowInterface* browser) {
  if (!HasKeyboardFocusedTab(browser)) {
    return false;
  }
  return CanDuplicateTabAt(browser, *GetKeyboardFocusedTabIndex(browser));
}

bool CanMoveActiveTabToNewWindow(BrowserWindowInterface* browser) {
  const ui::ListSelectionModel::SelectedIndices selection =
      browser->GetTabStripModel()
          ->selection_model()
          .GetListSelectionModel()
          .selected_indices();
  return CanMoveTabsToNewWindow(
      browser, std::vector<int>(selection.begin(), selection.end()));
}

// TODO(crbug.com/435178910) Remove this usage of ListSelectionModel.
void MoveActiveTabToNewWindow(BrowserWindowInterface* browser) {
  const ui::ListSelectionModel::SelectedIndices selection =
      browser->GetTabStripModel()
          ->selection_model()
          .GetListSelectionModel()
          .selected_indices();
  MoveTabsToNewWindow(browser,
                      std::vector<int>(selection.begin(), selection.end()));
}

bool CanMoveTabsToNewWindow(BrowserWindowInterface* browser,
                            const std::vector<int>& tab_indices) {
  if (browser->GetType() == BrowserWindowInterface::TYPE_APP) {
    for (int index : tab_indices) {
      if (web_app::IsPinnedHomeTab(browser->GetTabStripModel(), index)) {
        return false;
      }
    }
  }
  return browser->GetTabStripModel()->count() >
         static_cast<int>(tab_indices.size());
}

void MoveGroupToNewWindow(BrowserWindowInterface* browser,
                          tab_groups::TabGroupId group) {
  Browser* current_browser = browser->GetBrowserForMigrationOnly();
  BrowserWindowInterface* new_browser;
  if (current_browser->GetType() == BrowserWindowInterface::Type::TYPE_APP &&
      web_app::AppBrowserController::From(current_browser)->has_tab_strip()) {
    auto* app_controller = web_app::AppBrowserController::From(current_browser);
    new_browser = CreateBrowserWindow(BrowserWindowCreateParams::CreateForApp(
        BrowserInitState::From(current_browser)->create_params().app_name,
        app_controller->IsTrustedSource(), gfx::Rect(),
        current_browser->GetProfile(), true));
    web_app::MaybeAddPinnedHomeTab(new_browser->GetBrowserForMigrationOnly(),
                                   app_controller->app_id());
  } else {
    new_browser = CreateNewBrowser(browser, true);
  }

  MoveGroupToWindowImpl(browser, new_browser, group);
}

void MoveTabsToNewWindow(BrowserWindowInterface* browser,
                         const std::vector<int>& tab_indices) {
  if (tab_indices.empty()) {
    return;
  }

  Browser* current_browser = browser->GetBrowserForMigrationOnly();
  BrowserWindowInterface* new_browser;
  base::TimeTicks now = base::TimeTicks::Now();
  if (current_browser->GetType() == BrowserWindowInterface::Type::TYPE_APP &&
      web_app::AppBrowserController::From(current_browser)->has_tab_strip()) {
    auto* app_controller = web_app::AppBrowserController::From(current_browser);
    new_browser = CreateBrowserWindow(BrowserWindowCreateParams::CreateForApp(
        BrowserInitState::From(current_browser)->create_params().app_name,
        app_controller->IsTrustedSource(), gfx::Rect(),
        current_browser->GetProfile(), true));
    web_app::MaybeAddPinnedHomeTab(new_browser->GetBrowserForMigrationOnly(),
                                   app_controller->app_id());
  } else {
    new_browser = CreateNewBrowser(browser, true);
  }
  if (auto* manager = InitialWebUIWindowMetricsManager::From(new_browser)) {
    manager->SetWindowCreationInfo(
        waap::NewWindowCreationSource::kBrowserInitiated, now);
  }

  MoveTabsToWindowImpl(browser, new_browser, tab_indices);
}

bool CanCloseTabsToRight(const BrowserWindowInterface* browser) {
  return browser->GetTabStripModel()->IsContextMenuCommandEnabled(
      browser->GetTabStripModel()->active_index(),
      TabStripModel::CommandCloseTabsToRight);
}

bool CanCloseOtherTabs(const BrowserWindowInterface* browser) {
  return browser->GetTabStripModel()->IsContextMenuCommandEnabled(
      browser->GetTabStripModel()->active_index(),
      TabStripModel::CommandCloseOtherTabs);
}

WebContents* DuplicateTabAt(BrowserWindowInterface* browser, int index) {
  return ::DuplicateTabAt(browser, index, index + 1);
}

void DuplicateSplit(BrowserWindowInterface* browser,
                    split_tabs::SplitTabId split) {
  CHECK(WindowFeatureController::From(browser)->CanSupportWindowFeature(
      WindowFeatureController::WindowFeature::kFeatureTabStrip));

  TabStripModel* model = browser->GetTabStripModel();
  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 BrowserWindowInterface* browser, int index) {
  if (browser->GetType() == BrowserWindowInterface::TYPE_PICTURE_IN_PICTURE) {
    return false;
  }
  WebContents* contents = browser->GetTabStripModel()->GetWebContentsAt(index);
  return contents;
}

void MoveTabsToExistingWindow(BrowserWindowInterface* source,
                              BrowserWindowInterface* target,
                              const std::vector<int>& tab_indices) {
  MoveTabsToWindowImpl(source, target, tab_indices);
}

void MoveGroupToExistingWindow(BrowserWindowInterface* source,
                               BrowserWindowInterface* target,
                               tab_groups::TabGroupId group) {
  MoveGroupToWindowImpl(source, target, group);
}

void PinTab(BrowserWindowInterface* browser) {
  browser->GetTabStripModel()->ExecuteContextMenuCommand(
      browser->GetTabStripModel()->active_index(),
      TabStripModel::ContextMenuCommand::CommandTogglePinned);
}

void GroupTab(BrowserWindowInterface* browser) {
  browser->GetTabStripModel()->ExecuteContextMenuCommand(
      browser->GetTabStripModel()->active_index(),
      TabStripModel::ContextMenuCommand::CommandToggleGrouped);
}

void NewSplitTab(BrowserWindowInterface* browser,
                 split_tabs::SplitTabLayout layout,
                 split_tabs::SplitTabCreatedSource source) {
  TabStripModel* const tab_strip_model = browser->GetTabStripModel();
  const int active_index = tab_strip_model->active_index();
  // In Incognito mode, we can't show the regular Split View NTP so default to
  // the regular NTP which renders special content when in Incognito.
  const GURL new_tab_url = !browser->GetProfile()->IsIncognitoProfile() &&
                                   tab_strip_model->count() > 1
                               ? GURL(chrome::kChromeUISplitViewNewTabPageURL)
                               : chrome::ChromeUINewTabURLAsGURL();
  tab_strip_model->delegate()->AddTabAt(
      new_tab_url, 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(layout), source);

  tab_strip_model->ActivateTabAt(active_index + 1);
}

void AddNewTabToGroup(BrowserWindowInterface* browser) {
  if (!browser->GetTabStripModel()->SupportsTabGroups()) {
    return;
  }

  int index = browser->GetTabStripModel()->active_index();
  std::optional<tab_groups::TabGroupId> group_id =
      browser->GetTabStripModel()->GetTabGroupForTab(index);
  if (!group_id) {
    return;
  }

  AddTabAt(browser, GURL(), -1, true, group_id);
}

void CreateNewTabGroup(BrowserWindowInterface* browser) {
  NewTab(browser, NewTabTypes::kNewTabCommand);
  browser->GetTabStripModel()->ExecuteContextMenuCommand(
      browser->GetTabStripModel()->active_index(),
      TabStripModel::ContextMenuCommand::CommandAddToNewGroupFromMenuItem);
}

void CloseTabGroup(BrowserWindowInterface* browser) {
  const int index = browser->GetTabStripModel()->active_index();
  std::optional<tab_groups::TabGroupId> group_id =
      browser->GetTabStripModel()->GetTabGroupForTab(index);
  if (!group_id) {
    return;
  }

  browser->GetTabStripModel()->CloseAllTabsInGroup(group_id.value());
}

void FocusNextTabGroup(BrowserWindowInterface* browser) {
  TabStripModel* tab_strip_model = browser->GetTabStripModel();
  if (!tab_strip_model->SupportsTabGroups()) {
    return;
  }

  if (FocusAdjacentTabGroupInFocusMode(tab_strip_model, /*next=*/true)) {
    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->count();
  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(BrowserWindowInterface* browser) {
  TabStripModel* tab_strip_model = browser->GetTabStripModel();
  if (!tab_strip_model->SupportsTabGroups()) {
    return;
  }

  if (FocusAdjacentTabGroupInFocusMode(tab_strip_model, /*next=*/false)) {
    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->count();
  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 CanGroupAllUngroupedTabs(BrowserWindowInterface* browser) {
  return !GetUngroupedTabIndices(browser).empty();
}

bool GroupAllUngroupedTabs(BrowserWindowInterface* browser) {
  std::vector<int> indices = GetUngroupedTabIndices(browser);
  if (indices.empty()) {
    return false;
  }

  TabStripModel* tab_strip_model = browser->GetTabStripModel();
  tab_groups::TabGroupId group = tab_strip_model->AddToNewGroup(indices);
  tab_strip_model->OpenTabGroupEditor(group);
  return true;
}

void AddNewTabToRecentGroup(BrowserWindowInterface* browser) {
  if (!base::FeatureList::IsEnabled(features::kNewTabButtonContextMenu)) {
    return;
  }

  TabStripModel* tab_strip_model = browser->GetTabStripModel();

  if (!tab_strip_model->SupportsTabGroups()) {
    return;
  }

  std::optional<tab_groups::TabGroupId> group_id;

  // 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 UnfocusTabGroup(BrowserWindowInterface* browser,
                     TabGroupFocusExitReason exit_reason) {
  if (base::FeatureList::IsEnabled(features::kTabGroupsFocusing)) {
    base::UmaHistogramEnumeration("TabGroups.Focus.ExitReason", exit_reason);
    browser->GetTabStripModel()->SetFocusedGroup(std::nullopt);
  }
}

void MuteSite(BrowserWindowInterface* browser) {
  browser->GetTabStripModel()->ExecuteContextMenuCommand(
      browser->GetTabStripModel()->active_index(),
      TabStripModel::ContextMenuCommand::CommandToggleSiteMuted);
}

void MuteSiteForKeyboardFocusedTab(BrowserWindowInterface* browser) {
  if (!HasKeyboardFocusedTab(browser)) {
    return;
  }
  browser->GetTabStripModel()->ExecuteContextMenuCommand(
      *GetKeyboardFocusedTabIndex(browser),
      TabStripModel::ContextMenuCommand::CommandToggleSiteMuted);
}

void PinKeyboardFocusedTab(BrowserWindowInterface* browser) {
  if (!HasKeyboardFocusedTab(browser)) {
    return;
  }
  browser->GetTabStripModel()->ExecuteContextMenuCommand(
      *GetKeyboardFocusedTabIndex(browser),
      TabStripModel::ContextMenuCommand::CommandTogglePinned);
}

void GroupKeyboardFocusedTab(BrowserWindowInterface* browser) {
  if (!HasKeyboardFocusedTab(browser)) {
    return;
  }
  browser->GetTabStripModel()->ExecuteContextMenuCommand(
      *GetKeyboardFocusedTabIndex(browser),
      TabStripModel::ContextMenuCommand::CommandToggleGrouped);
}

void DuplicateKeyboardFocusedTab(BrowserWindowInterface* browser) {
  if (HasKeyboardFocusedTab(browser)) {
    DuplicateTabAt(browser, *GetKeyboardFocusedTabIndex(browser));
  }
}

bool HasKeyboardFocusedTab(const BrowserWindowInterface* browser) {
  return GetKeyboardFocusedTabIndex(browser).has_value();
}

void ConvertPopupToTabbedBrowser(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("ShowAsTab"));
  TabStripModel* tab_strip = browser->GetTabStripModel();
  // 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.
  BrowserWindowInterface* new_tabbed_browser =
      CreateBrowserWindow(BrowserWindowCreateParams(
          *browser->GetProfile(), /*from_user_gesture=*/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.
  new_tabbed_browser->GetTabStripModel()->AppendWebContents(
      std::move(contents_move), true);
  new_tabbed_browser->GetWindow()->Show();
}

void CloseTabsToRight(BrowserWindowInterface* browser) {
  browser->GetTabStripModel()->ExecuteContextMenuCommand(
      browser->GetTabStripModel()->active_index(),
      TabStripModel::CommandCloseTabsToRight);
}

void CloseOtherTabs(BrowserWindowInterface* browser) {
  browser->GetTabStripModel()->ExecuteContextMenuCommand(
      browser->GetTabStripModel()->active_index(),
      TabStripModel::CommandCloseOtherTabs);
}

void Exit() {
  base::RecordAction(UserMetricsAction("Exit"));
  chrome::AttemptUserExit();
}

void BookmarkCurrentTab(BrowserWindowInterface* browser) {
  base::RecordAction(base::UserMetricsAction("Star"));
  BookmarkModel* model =
      BookmarkModelFactory::GetForBrowserContext(browser->GetProfile());
  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->GetWindow()->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.
    BrowserWindow::FromBrowser(browser)->ShowBookmarkBubble(
        url, was_bookmarked_by_user);
  }

  if (!was_bookmarked_by_user && is_bookmarked_by_user) {
    RecordBookmarksAdded(browser->GetProfile());
  }
}

void BookmarkCurrentTabInFolder(BrowserWindowInterface* 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->GetProfile());
    }
  }
}

bool CanBookmarkCurrentTab(BrowserWindowInterface* browser) {
  BookmarkModel* model =
      BookmarkModelFactory::GetForBrowserContext(browser->GetProfile());
  return browser_defaults::bookmarks_enabled &&
         browser->GetProfile()->GetPrefs()->GetBoolean(
             bookmarks::prefs::kEditBookmarksEnabled) &&
         model && model->loaded() &&
         browser->GetType() == BrowserWindowInterface::TYPE_NORMAL;
}

void BookmarkAllTabs(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("BookmarkAllTabs"));

  bookmarks::ShowBookmarkAllTabsDialog(browser);
}

bool CanBookmarkAllTabs(BrowserWindowInterface* browser) {
  return browser->GetTabStripModel()->count() > 1 &&
         CanBookmarkCurrentTab(browser);
}

bool CanMoveActiveTabToReadLater(BrowserWindowInterface* browser) {
  GURL url;
  std::u16string title;
  WebContents* web_contents =
      browser->GetTabStripModel()->GetActiveWebContents();
  ReadingListModel* model = GetReadingListModel(browser);
  return CanMoveWebContentsToReadLater(browser, web_contents, model, &url,
                                       &title);
}

void MoveCurrentTabToReadLater(BrowserWindowInterface* browser) {
  MoveTabsToReadLater(browser,
                      {browser->GetTabStripModel()->GetActiveWebContents()});
}

void MoveTabsToReadLater(BrowserWindowInterface* 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);
    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.
    if (browser->GetFeatures().side_panel_ui()->IsSidePanelEntryShowing(
            SidePanelEntryKey(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(BrowserWindowInterface* browser) {
  GURL url;
  std::u16string title;
  ReadingListModel* model = GetReadingListModel(browser);
  WebContents* web_contents =
      browser->GetTabStripModel()->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(BrowserWindowInterface* browser) {
  GURL url;
  std::u16string title;
  ReadingListModel* model = GetReadingListModel(browser);
  WebContents* web_contents =
      browser->GetTabStripModel()->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* browser) {
  WebContents* const web_contents =
      browser->GetTabStripModel()->GetActiveWebContents();
  autofill::OfferNotificationBubbleControllerImpl* controller =
      autofill::OfferNotificationBubbleControllerImpl::FromWebContents(
          web_contents);
  DCHECK(controller);
  controller->ReshowBubble();
}

void SaveCreditCard(BrowserWindowInterface* browser) {
  WebContents* web_contents =
      browser->GetTabStripModel()->GetActiveWebContents();
  autofill::SaveCardBubbleControllerImpl* controller =
      autofill::SaveCardBubbleControllerImpl::FromWebContents(web_contents);
  controller->ReshowBubble(/*is_user_gesture=*/true);
}

void SaveIban(BrowserWindowInterface* browser) {
  WebContents* web_contents =
      browser->GetTabStripModel()->GetActiveWebContents();
  autofill::IbanBubbleControllerImpl* controller =
      autofill::IbanBubbleControllerImpl::FromWebContents(web_contents);
  controller->ReshowBubble();
}

void ShowMandatoryReauthOptInPrompt(BrowserWindowInterface* browser) {
  WebContents* web_contents =
      browser->GetTabStripModel()->GetActiveWebContents();
  autofill::MandatoryReauthBubbleControllerImpl* controller =
      autofill::MandatoryReauthBubbleControllerImpl::FromWebContents(
          web_contents);
  controller->ReshowBubble();
}

void SaveAutofillAddress(BrowserWindowInterface* browser) {
  WebContents* web_contents =
      browser->GetTabStripModel()->GetActiveWebContents();
  autofill::AddressBubblesController* controller =
      autofill::AddressBubblesController::FromWebContents(web_contents);
  controller->OnIconClicked();
}

void ShowFilledCardInformationBubble(BrowserWindowInterface* browser) {
  WebContents* web_contents =
      browser->GetTabStripModel()->GetActiveWebContents();
  auto* controller =
      autofill::FilledCardInformationBubbleControllerImpl::FromWebContents(
          web_contents);
  if (controller) {
    controller->ReshowBubble();
  }
}

void ShowVirtualCardEnrollBubble(BrowserWindowInterface* browser) {
  WebContents* web_contents =
      browser->GetTabStripModel()->GetActiveWebContents();
  autofill::VirtualCardEnrollBubbleControllerImpl* controller =
      autofill::VirtualCardEnrollBubbleControllerImpl::FromWebContents(
          web_contents);
  if (controller) {
    controller->ReshowBubble();
  }
}

void ShowTranslateBubble(BrowserWindowInterface* browser) {
  if (!browser->GetWindow()->IsActive()) {
    return;
  }

  WebContents* const web_contents =
      browser->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;
  }
  BrowserWindow::FromBrowser(browser)->ShowTranslateBubble(
      web_contents, step, source_language, target_language,
      translate::TranslateErrors::NONE, true);
}

void ManagePasswordsForPage(BrowserWindowInterface* browser) {
  auto* const user_education = BrowserUserEducationInterface::From(browser);
  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 =
      browser->GetTabStripModel()->GetActiveWebContents();
  ManagePasswordsUIController* controller =
      ManagePasswordsUIController::FromWebContents(web_contents);
  controller->QueueOrShowBubble(
      /*user_action=*/!controller->IsAutomaticallyOpeningBubble());
}

bool CanSendTabToSelf(BrowserWindowInterface* browser) {
  return send_tab_to_self::ShouldDisplayEntryPoint(
      browser->GetTabStripModel()->GetActiveWebContents());
}

void SendTabToSelf(BrowserWindowInterface* browser) {
  WebContents* web_contents =
      browser->GetTabStripModel()->GetActiveWebContents();
  send_tab_to_self::ShowBubble(web_contents,
                               send_tab_to_self::ShareEntryPoint::kShareMenu);
}

bool CanGenerateQrCode(BrowserWindowInterface* browser) {
  return !sharing_hub::SharingIsDisabledByPolicy(browser->GetProfile()) &&
         qrcode_generator::QRCodeGeneratorBubbleController::
             IsGeneratorAvailable(browser->GetTabStripModel()
                                      ->GetActiveWebContents()
                                      ->GetController()
                                      .GetLastCommittedEntry()
                                      ->GetURL());
}

void GenerateQRCode(BrowserWindowInterface* browser) {
  WebContents* web_contents =
      browser->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(BrowserWindowInterface* browser) {
  WebContents* web_contents =
      browser->GetTabStripModel()->GetActiveWebContents();
  sharing_hub::SharingHubBubbleController* controller =
      sharing_hub::SharingHubBubbleController::CreateOrGetFromWebContents(
          web_contents);
  controller->ShowBubble(share::ShareAttempt(web_contents));
}

void ScreenshotCapture(BrowserWindowInterface* browser) {
  WebContents* web_contents =
      browser->GetTabStripModel()->GetActiveWebContents();
  sharing_hub::ScreenshotCapturedBubbleController* controller =
      sharing_hub::ScreenshotCapturedBubbleController::Get(web_contents);
  controller->Capture(browser);
}

void SavePage(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("SavePage"));
  WebContents* current_tab =
      browser->GetTabStripModel()->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 BrowserWindowInterface* 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->GetProfile()->GetPrefs()->GetInteger(
              policy::policy_prefs::kDownloadRestrictions)) ==
      policy::DownloadRestriction::ALL_FILES) {
    return false;
  }
  return (browser->GetType() != BrowserWindowInterface::Type::TYPE_DEVTOOLS) &&
         !(GetContentRestrictions(browser) & CONTENT_RESTRICTION_SAVE);
}

void Print(BrowserWindowInterface* browser) {
#if BUILDFLAG(ENABLE_PRINTING)
  auto* const web_contents =
      browser->GetTabStripModel()->GetActiveWebContents();

  printing::StartPrint(web_contents,
#if BUILDFLAG(IS_CHROMEOS)
                       /*print_renderer=*/mojo::NullAssociatedRemote(),
#endif
                       browser->GetProfile()->GetPrefs()->GetBoolean(
                           prefs::kPrintPreviewDisabled),
                       /*has_selection=*/false);
#endif  // BUILDFLAG(ENABLE_PRINTING)
}

bool CanPrint(BrowserWindowInterface* browser) {
#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 =
      browser->GetTabStripModel()->GetActiveWebContents();
  return browser->GetProfile()->GetPrefs()->GetBoolean(
             prefs::kPrintingEnabled) &&
         (current_tab && !current_tab->IsCrashed()) &&
         !(IsShowingWebContentsModalDialog(browser) ||
           GetContentRestrictions(browser) & CONTENT_RESTRICTION_PRINT);
#else   // BUILDFLAG(ENABLE_PRINTING)
  return false;
#endif  // BUILDFLAG(ENABLE_PRINTING)
}

#if BUILDFLAG(ENABLE_PRINTING)
void BasicPrint(BrowserWindowInterface* browser) {
  printing::StartBasicPrint(
      browser->GetTabStripModel()->GetActiveWebContents());
}

bool CanBasicPrint(BrowserWindowInterface* 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->GetProfile()->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* browser) {
  // 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(browser->GetProfile()) &&
         !IsShowingWebContentsModalDialog(browser);
}

void RouteMediaInvokedFromAppMenu(BrowserWindowInterface* browser) {
  DCHECK(CanRouteMedia(browser));

  media_router::MediaRouterDialogController* dialog_controller =
      media_router::MediaRouterDialogController::GetOrCreateForWebContents(
          browser->GetTabStripModel()->GetActiveWebContents());
  if (!dialog_controller) {
    return;
  }

  dialog_controller->ShowMediaRouterDialog(
      media_router::MediaRouterDialogActivationLocation::APP_MENU);
}

void Find(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("Find"));
  FindInPage(browser, false, true);
}

void FindNext(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("FindNext"));
  FindInPage(browser, true, true);
}

void FindPrevious(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("FindPrevious"));
  FindInPage(browser, true, false);
}

void FindInPage(BrowserWindowInterface* browser,
                bool find_next,
                bool forward_direction) {
  browser->GetFeatures().GetFindBarController()->Show(find_next,
                                                      forward_direction);
}

void ShowTabSearch(BrowserWindowInterface* browser) {
  BrowserWindow::FromBrowser(browser)->CreateTabSearchBubble();
}

void CloseTabSearch(BrowserWindowInterface* browser) {
  BrowserWindow::FromBrowser(browser)->CloseTabSearchBubble();
}

void ToggleTabSearchPin(BrowserWindowInterface* browser) {
  PrefService* prefs = browser->GetProfile()->GetPrefs();
  const bool is_pinned = prefs->GetBoolean(prefs::kTabSearchPinnedToTabstrip);
  base::RecordAction(base::UserMetricsAction(
      is_pinned ? "TabStripComboButton.TabSearch.Unpinned"
                : "TabStripComboButton.TabSearch.Pinned"));
  prefs->SetBoolean(prefs::kTabSearchPinnedToTabstrip, !is_pinned);
}

void ToggleTabScrollButtonsPin(BrowserWindowInterface* browser) {
  PrefService* prefs = browser->GetProfile()->GetPrefs();
  const bool is_pinned =
      prefs->GetBoolean(prefs::kTabScrollButtonsPinnedToTabstrip);
  prefs->SetBoolean(prefs::kTabScrollButtonsPinnedToTabstrip, !is_pinned);
}

void ToggleContextualTasksSidePanel(BrowserWindowInterface* browser) {
  auto* controller =
      contextual_tasks::ContextualTasksPanelController::From(browser);
  CHECK(controller);
  if (controller->IsPanelOpenForContextualTask()) {
    controller->Close();
  } else {
    controller->Show();
  }
}

void ToggleContextualTasksSidePanelZeroState(BrowserWindowInterface* browser) {
  auto* controller =
      contextual_tasks::ContextualTasksPanelController::From(browser);
  CHECK(controller);
  if (controller->IsPanelOpenForContextualTask()) {
    controller->Close();
  } else {
    controller->OpenInZeroState();
  }
}

void ToggleVerticalTabs(BrowserWindowInterface* browser) {
  tabs::VerticalTabStripStateController* controller =
      tabs::VerticalTabStripStateController::From(browser);
  if (!controller) {
    return;
  }
  controller->SetVerticalTabsEnabled(!controller->ShouldDisplayVerticalTabs());
}

void ToggleVerticalTabsExpandOnHover(BrowserWindowInterface* browser) {
  tabs::VerticalTabStripStateController* controller =
      tabs::VerticalTabStripStateController::From(browser);
  if (!controller) {
    return;
  }
  controller->SetExpandOnHoverEnabled(!controller->IsExpandOnHoverEnabled());
}

void ToggleCollapseVerticalTabs(BrowserWindowInterface* browser) {
  tabs::VerticalTabStripStateController* controller =
      tabs::VerticalTabStripStateController::From(browser);
  if (!controller) {
    return;
  }
  bool collapse = controller->GetCollapseState() ==
                  tabs::VerticalTabStripCollapseState::kExpanded;
  controller->RequestCollapse(collapse);
}

bool CanCloseFind(BrowserWindowInterface* browser) {
  WebContents* current_tab =
      browser->GetTabStripModel()->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(BrowserWindowInterface* browser) {
  browser->GetFeatures().GetFindBarController()->EndFindSession(
      find_in_page::SelectionAction::kKeep, find_in_page::ResultAction::kKeep);
}

void Zoom(BrowserWindowInterface* browser, content::PageZoom zoom) {
  zoom::PageZoom::Zoom(browser->GetTabStripModel()->GetActiveWebContents(),
                       zoom);
}

void FocusToolbar(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("FocusToolbar"));
  BrowserWindow::FromBrowser(browser)->FocusToolbar();
}

void FocusLocationBar(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("FocusLocation"));
  BrowserWindow::FromBrowser(browser)->SetFocusToLocationBar(true);
}

void FocusSearch(BrowserWindowInterface* browser) {
  // TODO(beng): replace this with FocusLocationBar
  base::RecordAction(UserMetricsAction("FocusSearch"));
  BrowserWindow::FromBrowser(browser)->GetLocationBar()->FocusSearch();
}

void FocusAppMenu(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("FocusAppMenu"));
  BrowserWindow::FromBrowser(browser)->FocusAppMenu();
}

void FocusBookmarksToolbar(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("FocusBookmarksToolbar"));
  BookmarkBarController::From(browser)->FocusBookmarksToolbar();
}

void FocusInactivePopupForAccessibility(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("FocusInactivePopupForAccessibility"));
  BrowserFocusController::From(browser)->FocusInactivePopupForAccessibility();
}

void FocusNextPane(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("FocusNextPane"));
  BrowserFocusController::From(browser)->RotatePaneFocus(true);
}

void FocusPreviousPane(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("FocusPreviousPane"));
  BrowserFocusController::From(browser)->RotatePaneFocus(false);
}

void FocusWebContentsPane(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("FocusWebContentsPane"));
  BrowserFocusController::From(browser)->FocusWebContentsPane();
}

void ToggleDevToolsWindow(BrowserWindowInterface* browser,
                          DevToolsToggleAction action,
                          DevToolsOpenedByAction opened_by) {
  if (action.type() == DevToolsToggleAction::kShowConsolePanel) {
    base::RecordAction(UserMetricsAction("DevTools_ToggleConsole"));
  } else {
    base::RecordAction(UserMetricsAction("DevTools_ToggleWindow"));
  }
  DevToolsWindow::ToggleDevToolsWindow(browser, action, opened_by);
}

bool CanOpenTaskManager() {
#if !BUILDFLAG(IS_ANDROID)
  return true;
#else
  return false;
#endif
}

void OpenTaskManager(BrowserWindowInterface* browser,
                     task_manager::StartAction start_action) {
#if !BUILDFLAG(IS_ANDROID)
  base::RecordAction(UserMetricsAction("TaskManager"));
  chrome::ShowTaskManager(
      browser ? browser->GetBrowserForMigrationOnly() : nullptr, start_action);
#else
  NOTREACHED();
#endif
}

void OpenFeedbackDialog(BrowserWindowInterface* browser,
                        feedback::FeedbackSource source,
                        const std::string& description_template,
                        const std::string& category_tag) {
  base::RecordAction(UserMetricsAction("Feedback"));
  chrome::ShowFeedbackPage(browser, source, description_template,
                           std::string() /* description_placeholder_text */,
                           category_tag, std::string() /* extra_diagnostics */);
}

#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
void OpenReportUnsafeSiteDialog(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("ReportUnsafeSite"));
  feedback::ReportUnsafeSiteDialog::Show(browser->GetBrowserForMigrationOnly());
}
#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)

void ToggleBookmarkBar(BrowserWindowInterface* browser) {
  base::RecordAction(UserMetricsAction("ShowBookmarksBar"));
  ToggleBookmarkBarWhenVisible(browser->GetProfile());
}

void SetBookmarkBarVisibilityState(
    BrowserWindowInterface* browser,
    bookmarks::BookmarkBarVisibilityState state) {
  browser->GetProfile()->GetPrefs()->SetInteger(
      bookmarks::prefs::kBookmarkBarVisibilityState, static_cast<int>(state));
}

void ToggleShowFullURLs(BrowserWindowInterface* browser) {
  bool pref_enabled = browser->GetProfile()->GetPrefs()->GetBoolean(
      omnibox::kPreventUrlElisionsInOmnibox);
  browser->GetProfile()->GetPrefs()->SetBoolean(
      omnibox::kPreventUrlElisionsInOmnibox, !pref_enabled);
}

void ToggleShowGoogleLensShortcut(BrowserWindowInterface* browser) {
  bool pref_enabled = browser->GetProfile()->GetPrefs()->GetBoolean(
      omnibox::kShowGoogleLensShortcut);
  browser->GetProfile()->GetPrefs()->SetBoolean(
      omnibox::kShowGoogleLensShortcut, !pref_enabled);
}

void ToggleShowAiModeOmniboxButton(BrowserWindowInterface* browser) {
  bool pref_enabled = browser->GetProfile()->GetPrefs()->GetBoolean(
      omnibox::kShowAiModeOmniboxButton);
  browser->GetProfile()->GetPrefs()->SetBoolean(
      omnibox::kShowAiModeOmniboxButton, !pref_enabled);
}

void ToggleShowSearchTools(BrowserWindowInterface* browser) {
  bool pref_enabled =
      browser->GetProfile()->GetPrefs()->GetBoolean(omnibox::kShowSearchTools);
  browser->GetProfile()->GetPrefs()->SetBoolean(omnibox::kShowSearchTools,
                                                !pref_enabled);
}

void ShowAppMenu(BrowserWindowInterface* browser) {
  // We record the user metric for this event in AppMenu::RunMenu.
  BrowserWindow::FromBrowser(browser)->ShowAppMenu();
}

void ShowAvatarMenu(BrowserWindowInterface* browser) {
  BrowserWindow::FromBrowser(browser)->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(BrowserWindowInterface* browser) {
  UpgradeDetector* detector = UpgradeDetector::GetInstance();
  if (detector->is_outdated_install()) {
    ShowOutdatedUpgradeBubble(browser, browser,
                              /*auto_update_enabled=*/true);
  } else if (detector->is_outdated_install_no_au()) {
    ShowOutdatedUpgradeBubble(browser, browser,
                              /*auto_update_enabled=*/false);
  } 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"));
    BrowserWindow::FromBrowser(browser)->ShowUpdateChromeDialog();
  }
}

bool CanRequestTabletSite(WebContents* current_tab) {
  return current_tab &&
         current_tab->GetController().GetLastCommittedEntry() != nullptr;
}

bool IsRequestingTabletSite(BrowserWindowInterface* browser) {
  WebContents* current_tab =
      browser->GetTabStripModel()->GetActiveWebContents();
  if (!current_tab) {
    return false;
  }
  content::NavigationEntry* entry =
      current_tab->GetController().GetLastCommittedEntry();
  if (!entry) {
    return false;
  }
  return entry->GetIsOverridingUserAgent();
}

void ToggleRequestTabletSite(BrowserWindowInterface* browser) {
  WebContents* current_tab =
      browser->GetTabStripModel()->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(BrowserWindowInterface* browser) {
  content::BrowsingDataRemover* remover =
      browser->GetProfile()->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(BrowserWindowInterface* browser) {
  WebContents* contents = browser->GetTabStripModel()->GetActiveWebContents();
  return contents ? content::DevToolsAgentHost::IsDebuggerAttached(contents)
                  : false;
}

void CopyURL(BrowserWindowInterface* browser,
             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 =
        browser->GetFeatures().toast_controller();
    if (toast_controller) {
      toast_controller->MaybeShowToast(ToastParams(ToastId::kLinkCopied));
    }
  }
#endif
}

bool CanCopyUrl(BrowserWindowInterface* browser) {
  return IsWebAppOrCustomTab(browser) ||
         !sharing_hub::SharingIsDisabledByPolicy(browser->GetProfile());
}

bool IsWebAppOrCustomTab(const BrowserWindowInterface* browser) {
  return web_app::AppBrowserController::IsWebApp(browser);
}

BrowserWindowInterface* OpenInChrome(
    BrowserWindowInterface* hosted_app_browser) {
  // Find a non-incognito browser.
  BrowserWindowInterface* target_browser =
      ProfileBrowserCollection::GetForProfile(hosted_app_browser->GetProfile())
          ->FindTabbedBrowser();

  if (!target_browser) {
    target_browser = CreateBrowserWindow(
        BrowserWindowCreateParams(hosted_app_browser->GetProfile(), true));
  }

  web_app::ReparentWebContentsIntoBrowserImpl(
      hosted_app_browser,
      hosted_app_browser->GetTabStripModel()->GetActiveWebContents(),
      target_browser);
  return target_browser;
}

bool CanViewSource(BrowserWindowInterface* browser) {
  if (browser->GetType() == BrowserWindowInterface::TYPE_DEVTOOLS) {
    return false;
  }

  WebContents* web_contents =
      browser->GetTabStripModel()->GetActiveWebContents();

  // Disallow ViewSource if DevTools are disabled.
  if (!DevToolsWindow::AllowDevToolsFor(browser->GetProfile(), web_contents)) {
    return false;
  }
  return web_contents->GetController().CanViewSource();
}

bool CanToggleCaretBrowsing(BrowserWindowInterface* 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/40725478
  WebContents* web_contents =
      browser->GetTabStripModel()->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(BrowserWindowInterface* browser) {
  if (!CanToggleCaretBrowsing(browser)) {
    return;
  }

  PrefService* prefService = browser->GetProfile()->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)) {
    BrowserWindow::FromBrowser(browser)->ShowCaretBrowsingDialog();
  } else {
    base::RecordAction(base::UserMetricsAction(
        "Accessibility.CaretBrowsing.EnableWithKeyboard"));
    prefService->SetBoolean(prefs::kCaretBrowsingEnabled, true);
  }
}

void PromptToNameWindow(BrowserWindowInterface* browser) {
  chrome::ShowWindowNamePrompt(browser);
}

#if BUILDFLAG(IS_CHROMEOS)
void ToggleMultitaskMenu(BrowserWindowInterface* browser) {
  BrowserWindow::FromBrowser(browser)->ToggleMultitaskMenu();
}
#endif

#if !defined(TOOLKIT_VIEWS)
std::optional<int> GetKeyboardFocusedTabIndex(
    const BrowserWindowInterface* browser) {
  return std::nullopt;
}
#endif

void ShowIncognitoClearBrowsingDataDialog(BrowserWindowInterface* browser) {
  BrowserWindow::FromBrowser(browser)->ShowIncognitoClearBrowsingDataDialog();
}

void ShowIncognitoHistoryDisclaimerDialog(BrowserWindowInterface* browser) {
  BrowserWindow::FromBrowser(browser)->ShowIncognitoHistoryDisclaimerDialog();
}

bool ShouldInterceptChromeURLNavigationInIncognito(
    BrowserWindowInterface* browser,
    const GURL& url) {
  if (!browser || !browser->GetProfile()->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(
    BrowserWindowInterface* 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(BrowserWindowInterface* browser) {
  content::WebContents* web_contents =
      browser->GetTabStripModel()->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(BrowserWindowInterface* browser) {
#if BUILDFLAG(ENABLE_LENS_DESKTOP_GOOGLE_BRANDED_FEATURES)
  Profile* profile = browser->GetProfile();
  TemplateURLService* service =
      TemplateURLServiceFactory::GetForProfile(profile);
  WebContents* contents = browser->GetTabStripModel()->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;
    lens::LensRegionSearchController::From(browser)->Start(
        contents,
        /*use_fullscreen_capture=*/false, is_google_dsp, entry_point);
  }
#endif  // BUILDFLAG(ENABLE_LENS_DESKTOP_GOOGLE_BRANDED_FEATURES)
}

}  // namespace chrome
