Files
Claudio DeSouza cffb249298 [cr149] FindLastActiveWithProfile returning BWI
This change allows a couple of more places to be migrated to
`BrowserWindowInterface`.

Chromium changes:
https://chromium.googlesource.com/chromium/src/+/e8d0df41d25900497716dd3a4b070c6436fa1a97

commit e8d0df41d25900497716dd3a4b070c6436fa1a97
Author: Kun Wang <kunwang@microsoft.com>
Date:   Wed Apr 15 23:35:39 2026 -0700

    [bedrock] Migrate FindLastActiveWithProfile Step 1

    Bug: 494010890
    Change-Id: Ie0385deeda2e2f28baf75a810ada0becf5807c78
    Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7753884
    Reviewed-by: Thomas Lukaszewicz <tluk@chromium.org>
    Reviewed-by: Qikai Zhong <qikaizhong@microsoft.com>
    Commit-Queue: Kun Wang <kunwang@microsoft.com>
    Cr-Commit-Position: refs/heads/main@{#1615659}
2026-05-22 16:24:59 -04:00

321 lines
12 KiB
C++

// Copyright (c) 2024 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
// Based on Chromium code subject to the following license:
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "brave/browser/ui/commander/tab_command_source.h"
#include <algorithm>
#include <memory>
#include <numeric>
#include <string>
#include <utility>
#include <vector>
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/strings/utf_string_conversions.h"
#include "brave/app/command_utils.h"
#include "brave/browser/ui/commander/entity_match.h"
#include "brave/browser/ui/commander/fuzzy_finder.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
#include "chrome/browser/ui/accelerator_utils.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_window/public/global_browser_collection.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/grit/generated_resources.h"
#include "components/grit/brave_components_strings.h"
#include "components/sessions/content/session_tab_helper.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/list_selection_model.h"
namespace commander {
namespace {
// TODO(lgrey): It *might* make to pull this out later into a CommandSource
// method or a free function in some common place. Not committing yet.
std::unique_ptr<CommandItem> ItemForTitle(const std::u16string& title,
FuzzyFinder& finder,
std::vector<gfx::Range>& ranges) {
double score = finder.Find(title, ranges);
if (score > 0) {
return std::make_unique<CommandItem>(title, score, ranges);
}
return nullptr;
}
// Returns the tab group that the currently selected tabs can *not* be moved to.
// In practice, this is the tab group that *all* selected tabs belong to, if
// any. In the common special case of single selection, this will return that
// tab's group if it has one.
std::optional<tab_groups::TabGroupId> IneligibleGroupForSelected(
TabStripModel* tab_strip_model) {
std::optional<tab_groups::TabGroupId> excluded_group = std::nullopt;
for (tabs::TabInterface* tab :
tab_strip_model->selection_model().selected_tabs()) {
if (auto group = tab->GetGroup(); group.has_value()) {
if (!excluded_group.has_value()) {
excluded_group = group;
} else if (group != excluded_group) {
// More than one group in the selection, so don't exclude anything.
return std::nullopt;
}
}
}
return excluded_group;
}
// Returns true only if `browser` is alive, and the contents at `index` match
// `tab_session_id`.
bool DoesTabAtIndexMatchSessionId(base::WeakPtr<BrowserWindowInterface> browser,
int index,
int tab_session_id) {
if (!browser.get()) {
return false;
}
auto* tab_strip_model = browser->GetTabStripModel();
if (tab_strip_model->count() <= index) {
return false;
}
content::WebContents* contents = tab_strip_model->GetWebContentsAt(index);
DCHECK(contents);
return sessions::SessionTabHelper::IdForTab(contents).id() == tab_session_id;
}
// Commands:
bool HasUnpinnedTabs(const TabStripModel* model) {
return model->IndexOfFirstNonPinnedTab() < model->count();
}
bool HasPinnedTabs(const TabStripModel* model) {
return model->IndexOfFirstNonPinnedTab() > 0;
}
bool CanMoveTabsToExistingWindow(
const BrowserWindowInterface* browser_to_exclude) {
bool has_found = false;
GlobalBrowserCollection::GetInstance()->ForEach(
[browser_to_exclude, &has_found](BrowserWindowInterface* browser) {
has_found = browser != browser_to_exclude &&
browser->GetType() == BrowserWindowInterface::TYPE_NORMAL &&
browser->GetProfile() == browser_to_exclude->GetProfile();
return !has_found;
});
return has_found;
}
void MoveTabsToExistingWindow(base::WeakPtr<BrowserWindowInterface> source,
base::WeakPtr<BrowserWindowInterface> target) {
if (!source.get() || !target.get()) {
return;
}
const ui::ListSelectionModel::SelectedIndices& sel =
source->GetTabStripModel()
->selection_model()
.GetListSelectionModel()
.selected_indices();
chrome::MoveTabsToExistingWindow(source.get(), target.get(),
std::vector<int>(sel.begin(), sel.end()));
}
void AddSelectedToNewGroup(BrowserWindowInterface* browser) {
TabStripModel* model = browser->GetTabStripModel();
const ui::ListSelectionModel::SelectedIndices& sel =
model->selection_model().GetListSelectionModel().selected_indices();
model->AddToNewGroup(std::vector<int>(sel.begin(), sel.end()));
}
// Multiphase commands:
void TogglePinTab(base::WeakPtr<BrowserWindowInterface> browser,
int tab_index,
int tab_session_id,
bool pin) {
if (!DoesTabAtIndexMatchSessionId(browser, tab_index, tab_session_id)) {
return;
}
browser->GetTabStripModel()->SetTabPinned(tab_index, pin);
}
std::unique_ptr<CommandItem> CreatePinTabItem(const TabMatch& match,
BrowserWindowInterface* browser,
bool pin) {
auto item = match.ToCommandItem();
item->command = base::BindOnce(&TogglePinTab, browser->GetWeakPtr(),
match.index, match.session_id, pin);
return item;
}
CommandSource::CommandResults TogglePinTabCommandsForTabsMatching(
BrowserWindowInterface* browser,
bool pin,
const std::u16string& input) {
CommandSource::CommandResults results;
TabSearchOptions options;
if (pin) {
options.only_unpinned = true;
} else {
options.only_pinned = true;
}
for (auto& match : TabsMatchingInput(browser, input, options)) {
results.push_back(CreatePinTabItem(match, browser, pin));
}
return results;
}
std::unique_ptr<CommandItem> CreateMoveTabsToWindowItem(
BrowserWindowInterface* source,
const WindowMatch& match) {
auto item = match.ToCommandItem();
item->command =
base::BindOnce(&MoveTabsToExistingWindow, source->GetWeakPtr(),
match.browser->GetWeakPtr());
return item;
}
CommandSource::CommandResults MoveTabsToWindowCommandsForWindowsMatching(
BrowserWindowInterface* source,
const std::u16string& input) {
CommandSource::CommandResults results;
// Add "New Window", if appropriate. It should score highest with no input.
std::u16string new_window_title =
base::UTF8ToUTF16(commands::GetCommandName(IDC_NEW_WINDOW));
std::unique_ptr<CommandItem> item;
if (input.empty()) {
item = std::make_unique<CommandItem>(new_window_title, .99,
std::vector<gfx::Range>());
} else {
FuzzyFinder finder(input);
std::vector<gfx::Range> ranges;
item = ItemForTitle(new_window_title, finder, ranges);
}
if (item) {
item->entity_type = CommandItem::Entity::kWindow;
item->command = base::BindOnce(&chrome::MoveActiveTabToNewWindow,
base::Unretained(source));
results.push_back(std::move(item));
}
for (auto& match : WindowsMatchingInput(source, input)) {
results.push_back(CreateMoveTabsToWindowItem(source, match));
}
return results;
}
void AddTabsToGroup(base::WeakPtr<BrowserWindowInterface> browser,
tab_groups::TabGroupId group) {
if (!browser.get()) {
return;
}
const ui::ListSelectionModel::SelectedIndices& sel =
browser->GetTabStripModel()
->selection_model()
.GetListSelectionModel()
.selected_indices();
browser->GetTabStripModel()->AddToExistingGroup(
std::vector<int>(sel.begin(), sel.end()), group);
}
CommandSource::CommandResults AddTabsToGroupCommandsForGroupsMatching(
BrowserWindowInterface* browser,
const std::u16string& input) {
CommandSource::CommandResults results;
TabStripModel* tab_strip_model = browser->GetTabStripModel();
// Add "New Group", if appropriate. It should score highest with no input.
std::u16string new_group_title =
l10n_util::GetStringUTF16(IDS_TAB_CXMENU_SUBMENU_NEW_GROUP);
std::unique_ptr<CommandItem> item;
if (input.empty()) {
item = std::make_unique<CommandItem>(new_group_title, .99,
std::vector<gfx::Range>());
} else {
FuzzyFinder finder(input);
std::vector<gfx::Range> ranges;
item = ItemForTitle(new_group_title, finder, ranges);
}
if (item) {
item->entity_type = CommandItem::Entity::kGroup;
item->command =
base::BindOnce(&AddSelectedToNewGroup, base::Unretained(browser));
results.push_back(std::move(item));
}
for (auto& match : GroupsMatchingInput(
browser, input, IneligibleGroupForSelected(tab_strip_model))) {
auto command_item = match.ToCommandItem();
command_item->command =
base::BindOnce(&AddTabsToGroup, browser->GetWeakPtr(), match.group);
results.push_back(std::move(command_item));
}
return results;
}
} // namespace
TabCommandSource::TabCommandSource() = default;
TabCommandSource::~TabCommandSource() = default;
CommandSource::CommandResults TabCommandSource::GetCommands(
const std::u16string& input,
BrowserWindowInterface* browser) const {
CommandSource::CommandResults results;
FuzzyFinder finder(input);
std::vector<gfx::Range> ranges;
TabStripModel* tab_strip_model = browser->GetTabStripModel();
if (CanMoveTabsToExistingWindow(browser)) {
auto text = l10n_util::GetStringUTF16(IDS_COMMANDER_MOVE_TABS_TO_WINDOW);
if (auto item = ItemForTitle(text, finder, ranges)) {
item->command = std::make_pair(
text, base::BindRepeating(&MoveTabsToWindowCommandsForWindowsMatching,
base::Unretained(browser)));
results.push_back(std::move(item));
}
}
auto add_tab_to_existing_group =
l10n_util::GetStringUTF16(IDS_COMMANDER_ADD_TABS_TO_EXISTING_GROUP);
if (auto item = ItemForTitle(add_tab_to_existing_group, finder, ranges)) {
item->command = std::make_pair(
add_tab_to_existing_group,
base::BindRepeating(&AddTabsToGroupCommandsForGroupsMatching,
base::Unretained(browser)));
results.push_back(std::move(item));
}
if (HasUnpinnedTabs(tab_strip_model)) {
auto text = l10n_util::GetStringUTF16(IDS_COMMANDER_PIN_TAB);
if (auto item = ItemForTitle(text, finder, ranges)) {
item->command = std::make_pair(
text, base::BindRepeating(&TogglePinTabCommandsForTabsMatching,
base::Unretained(browser), true));
results.push_back((std::move(item)));
}
}
if (HasPinnedTabs(tab_strip_model)) {
auto text = l10n_util::GetStringUTF16(IDS_COMMANDER_UNPIN_TAB);
if (auto item = ItemForTitle(text, finder, ranges)) {
item->command = std::make_pair(
text, base::BindRepeating(&TogglePinTabCommandsForTabsMatching,
base::Unretained(browser), false));
results.push_back((std::move(item)));
}
}
return results;
}
} // namespace commander