blob: bef6a41c9f0528af5ac46b830fe1edbac835011c [file] [log] [blame] [edit]
// 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 "chrome/browser/autocomplete/tab_matcher_desktop.h"
#include "base/feature_list.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/common/omnibox_features.h"
#include "content/public/browser/web_contents_user_data.h"
namespace {
class AutocompleteClientWebContentsUserData
: public content::WebContentsUserData<
AutocompleteClientWebContentsUserData> {
public:
~AutocompleteClientWebContentsUserData() override = default;
int GetLastCommittedEntryIndex() { return last_committed_entry_index_; }
const GURL& GetLastCommittedStrippedURL() {
return last_committed_stripped_url_;
}
void UpdateLastCommittedStrippedURL(
int last_committed_index,
const GURL& last_committed_url,
const TemplateURLService* template_url_service,
const bool keep_search_intent_params) {
if (last_committed_url.is_valid()) {
last_committed_entry_index_ = last_committed_index;
// Use a blank input as the stripped URL will be reused with other inputs.
// Also keep the search intent params. Otherwise, this can result in over
// triggering of the Switch to Tab action on plain-text suggestions for
// open entity SRPs, or vice versa, on entity suggestions for open
// plain-text SRPs.
last_committed_stripped_url_ = AutocompleteMatch::GURLToStrippedGURL(
last_committed_url, AutocompleteInput(), template_url_service,
std::u16string(), keep_search_intent_params);
}
}
private:
explicit AutocompleteClientWebContentsUserData(
content::WebContents* contents);
friend class content::WebContentsUserData<
AutocompleteClientWebContentsUserData>;
int last_committed_entry_index_ = -1;
GURL last_committed_stripped_url_;
WEB_CONTENTS_USER_DATA_KEY_DECL();
};
AutocompleteClientWebContentsUserData::AutocompleteClientWebContentsUserData(
content::WebContents* web_contents)
: content::WebContentsUserData<AutocompleteClientWebContentsUserData>(
*web_contents) {}
WEB_CONTENTS_USER_DATA_KEY_IMPL(AutocompleteClientWebContentsUserData);
} // namespace
bool TabMatcherDesktop::IsTabOpenWithURL(const GURL& url,
const AutocompleteInput* input) const {
const AutocompleteInput empty_input;
if (!input)
input = &empty_input;
// Use a blank input as the stripped URL will be reused with other inputs.
// Also keep the search intent params. Otherwise, this can result in over
// triggering of the Switch to Tab action on plain-text suggestions for
// open entity SRPs, or vice versa, on entity suggestions for open plain-text
// SRPs.
const bool keep_search_intent_params = base::FeatureList::IsEnabled(
omnibox::kDisambiguateTabMatchingForEntitySuggestions);
const GURL stripped_url = AutocompleteMatch::GURLToStrippedGURL(
url, *input, template_url_service_, std::u16string(),
keep_search_intent_params);
for (auto* web_contents : GetOpenWebContents()) {
if (IsStrippedURLEqualToWebContentsURL(stripped_url, web_contents,
keep_search_intent_params)) {
return true;
}
}
return false;
}
std::vector<TabMatcher::TabWrapper> TabMatcherDesktop::GetOpenTabs(
const AutocompleteInput* input,
bool exclude_active_tab) const {
std::vector<TabMatcher::TabWrapper> open_tabs;
for (auto* web_contents : GetOpenWebContents(exclude_active_tab)) {
open_tabs.emplace_back(web_contents->GetTitle(),
web_contents->GetLastCommittedURL(),
web_contents->GetLastActiveTime());
}
return open_tabs;
}
std::vector<content::WebContents*> TabMatcherDesktop::GetOpenWebContents(
bool exclude_active_tab) const {
Browser* active_browser = BrowserList::GetInstance()->GetLastActive();
content::WebContents* active_tab = nullptr;
if (active_browser)
active_tab = active_browser->tab_strip_model()->GetActiveWebContents();
std::vector<content::WebContents*> all_tabs;
for (Browser* browser : *BrowserList::GetInstance()) {
if (profile_ != browser->profile()) {
// Only look at the same profile (and anonymity level).
continue;
}
for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
auto* web_contents = browser->tab_strip_model()->GetWebContentsAt(i);
if (web_contents != active_tab || !exclude_active_tab) {
all_tabs.push_back(web_contents);
}
}
}
return all_tabs;
}
bool TabMatcherDesktop::IsStrippedURLEqualToWebContentsURL(
const GURL& stripped_url,
content::WebContents* web_contents,
const bool keep_search_intent_params) const {
AutocompleteClientWebContentsUserData::CreateForWebContents(web_contents);
AutocompleteClientWebContentsUserData* user_data =
AutocompleteClientWebContentsUserData::FromWebContents(web_contents);
DCHECK(user_data);
if (user_data->GetLastCommittedEntryIndex() !=
web_contents->GetController().GetLastCommittedEntryIndex()) {
user_data->UpdateLastCommittedStrippedURL(
web_contents->GetController().GetLastCommittedEntryIndex(),
web_contents->GetLastCommittedURL(), template_url_service_,
keep_search_intent_params);
}
return stripped_url == user_data->GetLastCommittedStrippedURL();
}