blob: 7d2a0436e193449e8dfdb365062645a33fc646df [file] [log] [blame]
// 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/ui/side_search/side_search_config.h"
#include "base/observer_list.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/ui/side_search/side_search_utils.h"
#include "chrome/common/webui_url_constants.h"
#include "components/google/core/common/google_util.h"
#include "components/search_engines/template_url_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/common/url_utils.h"
#include "net/base/url_util.h"
#include "url/gurl.h"
namespace {
// The currently supported version of side search.
constexpr char kSideSearchVersion[] = "1";
// The key used to store the config user data on the profile.
constexpr char kSideSearchConfigKey[] = "side_search_config_key";
void ApplyDSEConfiguration(Profile* profile, SideSearchConfig& config) {
// If DSE support is enabled we should only allow navigations in the side
// panel under the following conditions:
// 1. The default search engine supports side search.
// 2. The `url` is a SRP belonging to the default search engine.
config.SetShouldNavigateInSidePanelCallback(base::BindRepeating(
[](Profile* profile, const GURL& url) {
const auto* template_url_service =
TemplateURLServiceFactory::GetForProfile(profile);
return template_url_service &&
template_url_service
->IsSideSearchSupportedForDefaultSearchProvider() &&
template_url_service
->IsSearchResultsPageFromDefaultSearchProvider(url);
},
base::Unretained(profile)));
// If DSE support is enabled we should only show the side panel under the
// following conditions:
// 1. The default search engine supports Side Search.
// 2. The `url` is NOT a SRP belonging to the default search engine.
// 3. The `url` is NOT the New Tab Page.
config.SetCanShowSidePanelForURLCallback(base::BindRepeating(
[](Profile* profile, const GURL& url) {
const auto* template_url_service =
TemplateURLServiceFactory::GetForProfile(profile);
return template_url_service &&
template_url_service
->IsSideSearchSupportedForDefaultSearchProvider() &&
!template_url_service
->IsSearchResultsPageFromDefaultSearchProvider(url) &&
!content::HasWebUIScheme(url);
},
base::Unretained(profile)));
// Leverage the TemplateURLService for DSE urls.
config.SetGenerateSideSearchURLCallback(base::BindRepeating(
[](Profile* profile, const GURL& url) {
const auto* template_url_service =
TemplateURLServiceFactory::GetForProfile(profile);
DCHECK(template_url_service
->IsSideSearchSupportedForDefaultSearchProvider());
return template_url_service
->GenerateSideSearchURLForDefaultSearchProvider(url,
kSideSearchVersion);
},
base::Unretained(profile)));
}
void ApplyGoogleSearchConfiguration(SideSearchConfig& config) {
// If using the Google Search configuration only navigations in the side panel
// when:
// 1. The `url` is a Google Search SRP.
config.SetShouldNavigateInSidePanelCallback(
base::BindRepeating(google_util::IsGoogleSearchUrl));
// If using the Google Search configuration only show the side panel when:
// 1. The `url` is NOT a Google SRP.
// 2. The `url` is NOT a Google home page.
// 3. The `url` is NOT the New Tab Page.
config.SetCanShowSidePanelForURLCallback(
base::BindRepeating([](const GURL& url) {
return !google_util::IsGoogleSearchUrl(url) &&
!google_util::IsGoogleHomePageUrl(url) &&
url.spec() != chrome::kChromeUINewTabURL;
}));
// Use the original predefined 'sidesearch' URL parameter for Google Search
// support.
config.SetGenerateSideSearchURLCallback(
base::BindRepeating([](const GURL& url) {
constexpr char kSideSearchQueryParam[] = "sidesearch";
return net::AppendQueryParameter(url, kSideSearchQueryParam,
kSideSearchVersion);
}));
}
} // namespace
SideSearchConfig::SideSearchConfig(Profile* profile) : profile_(profile) {
// `template_url_service` may be null in tests.
if (auto* template_url_service =
TemplateURLServiceFactory::GetForProfile(profile_)) {
template_url_service_observation_.Observe(template_url_service);
// Call this initially in case the default URL has already been set.
OnTemplateURLServiceChanged();
}
ApplyDSEConfiguration(profile_, *this);
}
SideSearchConfig::~SideSearchConfig() = default;
// static
SideSearchConfig* SideSearchConfig::Get(content::BrowserContext* context) {
SideSearchConfig* data = static_cast<SideSearchConfig*>(
context->GetUserData(kSideSearchConfigKey));
if (!data) {
auto new_data =
std::make_unique<SideSearchConfig>(static_cast<Profile*>(context));
data = new_data.get();
context->SetUserData(kSideSearchConfigKey, std::move(new_data));
}
return data;
}
void SideSearchConfig::OnTemplateURLServiceChanged() {
if (skip_on_template_url_changed_)
return;
const auto* default_template_url =
TemplateURLServiceFactory::GetForProfile(profile_)
->GetDefaultSearchProvider();
// If there is currently no default search engine set, but there was one set
// previously, reset `default_template_url_id_` and propagate the change.
if (!default_template_url &&
default_template_url_id_ != kInvalidTemplateURLID) {
default_template_url_id_ = kInvalidTemplateURLID;
ResetStateAndNotifyConfigChanged();
return;
}
// Propagate an update only if the current default search provider has
// changed.
if (!default_template_url ||
default_template_url->id() == default_template_url_id_)
return;
default_template_url_id_ = default_template_url->id();
ResetStateAndNotifyConfigChanged();
}
void SideSearchConfig::OnTemplateURLServiceShuttingDown() {
template_url_service_observation_.Reset();
}
bool SideSearchConfig::ShouldNavigateInSidePanel(const GURL& url) {
return should_navigate_in_side_panel_callback_.Run(url);
}
void SideSearchConfig::SetShouldNavigateInSidePanelCallback(
URLTestConditionCallback callback) {
should_navigate_in_side_panel_callback_ = std::move(callback);
}
bool SideSearchConfig::CanShowSidePanelForURL(const GURL& url) {
return can_show_side_panel_for_url_callback_.Run(url);
}
void SideSearchConfig::SetCanShowSidePanelForURLCallback(
URLTestConditionCallback callback) {
can_show_side_panel_for_url_callback_ = std::move(callback);
}
GURL SideSearchConfig::GenerateSideSearchURL(const GURL& search_url) {
return generate_side_search_url_callack_.Run(search_url);
}
void SideSearchConfig::SetGenerateSideSearchURLCallback(
GenerateURLCallback callback) {
generate_side_search_url_callack_ = std::move(callback);
}
void SideSearchConfig::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void SideSearchConfig::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void SideSearchConfig::ResetStateAndNotifyConfigChanged() {
for (auto& observer : observers_)
observer.OnSideSearchConfigChanged();
}
void SideSearchConfig::ApplyGoogleSearchConfigurationForTesting() {
ApplyGoogleSearchConfiguration(*this);
}