blob: ae43dc0033188faf48595d132594878b06d40e92 [file] [log] [blame]
// 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/search/search_tab_helper.h"
#include <memory>
#include "base/containers/flat_map.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/bookmarks/bookmark_model_factory.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/instant_service.h"
#include "chrome/browser/search/instant_service_factory.h"
#include "chrome/browser/search/search.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/ui/bookmarks/bookmark_stats.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_select_file_policy.h"
#include "chrome/browser/ui/color/chrome_color_id.h"
#include "chrome/browser/ui/location_bar/location_bar.h"
#include "chrome/browser/ui/search/omnibox_utils.h"
#include "chrome/browser/ui/search/search_ipc_router_policy_impl.h"
#include "chrome/browser/ui/tab_contents/core_tab_helper.h"
#include "chrome/browser/ui/tab_modal_confirm_dialog.h"
#include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/feature_engagement/public/feature_constants.h"
#include "components/google/core/common/google_util.h"
#include "components/navigation_metrics/navigation_metrics.h"
#include "components/profile_metrics/browser_profile_type.h"
#include "components/search/ntp_features.h"
#include "components/search/search.h"
#include "components/search_engines/template_url_service.h"
#include "components/sessions/content/session_tab_helper.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/strings/grit/components_strings.h"
#include "components/sync/base/user_selectable_type.h"
#include "components/sync/service/sync_service.h"
#include "components/sync/service/sync_user_settings.h"
#include "components/vector_icons/vector_icons.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/reload_type.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "extensions/common/constants.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/color/color_provider.h"
#include "ui/gfx/vector_icon_types.h"
#include "url/gurl.h"
namespace {
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class NewTabPageConcretePage {
kOther = 0,
k1PWebUiNtp = 1,
k3PWebUiNtp = 2,
k3PRemoteNtp = 3,
kExtensionNtp = 4,
kOffTheRecordNtp = 5,
kMaxValue = kOffTheRecordNtp,
};
bool IsCacheableNTP(content::WebContents* contents) {
content::NavigationEntry* entry =
contents->GetController().GetLastCommittedEntry();
return search::NavEntryIsInstantNTP(contents, entry);
}
// Returns true if |contents| are rendered inside an Instant process.
bool InInstantProcess(const InstantService* instant_service,
content::WebContents* contents) {
if (!instant_service || !contents)
return false;
return instant_service->IsInstantProcess(
contents->GetPrimaryMainFrame()->GetProcess()->GetID());
}
// Called when an NTP finishes loading. If the load start time was noted,
// calculates and logs the total load time.
void RecordNewTabLoadTime(content::WebContents* contents) {
CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(contents);
// CoreTabHelper can be null in unittests.
if (!core_tab_helper)
return;
if (core_tab_helper->new_tab_start_time().is_null())
return;
base::TimeDelta duration =
base::TimeTicks::Now() - core_tab_helper->new_tab_start_time();
if (IsCacheableNTP(contents)) {
if (google_util::IsGoogleDomainUrl(
contents->GetController().GetLastCommittedEntry()->GetURL(),
google_util::ALLOW_SUBDOMAIN,
google_util::DISALLOW_NON_STANDARD_PORTS)) {
UMA_HISTOGRAM_TIMES("Tab.NewTabOnload.Google", duration);
} else {
UMA_HISTOGRAM_TIMES("Tab.NewTabOnload.Other", duration);
}
} else {
UMA_HISTOGRAM_TIMES("Tab.NewTabOnload.Local", duration);
}
core_tab_helper->set_new_tab_start_time(base::TimeTicks());
}
void RecordConcreteNtp(content::NavigationHandle* navigation_handle) {
NewTabPageConcretePage concrete_page = NewTabPageConcretePage::kOther;
if (navigation_handle->GetURL().DeprecatedGetOriginAsURL() ==
GURL(chrome::kChromeUINewTabPageURL).DeprecatedGetOriginAsURL()) {
concrete_page = NewTabPageConcretePage::k1PWebUiNtp;
} else if (navigation_handle->GetURL().DeprecatedGetOriginAsURL() ==
GURL(chrome::kChromeUINewTabPageThirdPartyURL)
.DeprecatedGetOriginAsURL()) {
concrete_page = NewTabPageConcretePage::k3PWebUiNtp;
} else if (search::IsInstantNTP(navigation_handle->GetWebContents())) {
concrete_page = NewTabPageConcretePage::k3PRemoteNtp;
} else if (navigation_handle->GetURL().SchemeIs(
extensions::kExtensionScheme)) {
concrete_page = NewTabPageConcretePage::kExtensionNtp;
} else if (Profile::FromBrowserContext(
navigation_handle->GetWebContents()->GetBrowserContext())
->IsOffTheRecord() &&
navigation_handle->GetURL().DeprecatedGetOriginAsURL() ==
GURL(chrome::kChromeUINewTabURL).DeprecatedGetOriginAsURL()) {
concrete_page = NewTabPageConcretePage::kOffTheRecordNtp;
}
base::UmaHistogramEnumeration("NewTabPage.ConcretePage", concrete_page);
}
} // namespace
SearchTabHelper::SearchTabHelper(content::WebContents* web_contents)
: WebContentsObserver(web_contents),
content::WebContentsUserData<SearchTabHelper>(*web_contents),
ipc_router_(web_contents,
this,
std::make_unique<SearchIPCRouterPolicyImpl>(web_contents)),
instant_service_(nullptr) {
DCHECK(search::IsInstantExtendedAPIEnabled());
instant_service_ = InstantServiceFactory::GetForProfile(profile());
if (instant_service_)
instant_service_->AddObserver(this);
OmniboxTabHelper::CreateForWebContents(web_contents);
OmniboxTabHelper::FromWebContents(web_contents)->AddObserver(this);
}
SearchTabHelper::~SearchTabHelper() {
if (instant_service_)
instant_service_->RemoveObserver(this);
if (auto* helper = OmniboxTabHelper::FromWebContents(&GetWebContents()))
helper->RemoveObserver(this);
}
void SearchTabHelper::BindEmbeddedSearchConnecter(
mojo::PendingAssociatedReceiver<search::mojom::EmbeddedSearchConnector>
receiver,
content::RenderFrameHost* rfh) {
auto* web_contents = content::WebContents::FromRenderFrameHost(rfh);
if (!web_contents)
return;
auto* tab_helper = SearchTabHelper::FromWebContents(web_contents);
if (!tab_helper)
return;
tab_helper->ipc_router_.BindEmbeddedSearchConnecter(std::move(receiver), rfh);
}
void SearchTabHelper::OnTabActivated() {
ipc_router_.OnTabActivated();
if (search::IsInstantNTP(web_contents()) && instant_service_)
instant_service_->OnNewTabPageOpened();
CloseNTPCustomizeChromeFeaturePromo();
}
void SearchTabHelper::OnTabDeactivated() {
ipc_router_.OnTabDeactivated();
}
void SearchTabHelper::DidStartNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInPrimaryMainFrame())
return;
if (navigation_handle->IsSameDocument())
return;
if (web_contents()->GetVisibleURL().DeprecatedGetOriginAsURL() ==
GURL(chrome::kChromeUINewTabURL).DeprecatedGetOriginAsURL()) {
RecordConcreteNtp(navigation_handle);
}
if (search::IsNTPOrRelatedURL(navigation_handle->GetURL(), profile())) {
// Set the title on any pending entry corresponding to the NTP. This
// prevents any flickering of the tab title.
content::NavigationEntry* entry =
web_contents()->GetController().GetPendingEntry();
if (entry) {
web_contents()->UpdateTitleForEntry(
entry, l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
}
}
CloseNTPCustomizeChromeFeaturePromo();
}
void SearchTabHelper::TitleWasSet(content::NavigationEntry* entry) {
if (is_setting_title_ || !entry)
return;
// Always set the title on the new tab page to be the one from our UI
// resources. This check ensures that the title is properly set to the string
// defined by the Chrome UI language (rather than the server language) in all
// cases.
//
// We only override the title when it's nonempty to allow the page to set the
// title if it really wants. An empty title means to use the default. There's
// also a race condition between this code and the page's SetTitle call which
// this rule avoids.
if (entry->GetTitle().empty() &&
search::NavEntryIsInstantNTP(web_contents(), entry)) {
is_setting_title_ = true;
web_contents()->UpdateTitleForEntry(
entry, l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
is_setting_title_ = false;
}
}
void SearchTabHelper::DidFinishLoad(content::RenderFrameHost* render_frame_host,
const GURL& /* validated_url */) {
if (render_frame_host->IsInPrimaryMainFrame() &&
search::IsInstantNTP(web_contents())) {
RecordNewTabLoadTime(web_contents());
}
}
void SearchTabHelper::NavigationEntryCommitted(
const content::LoadCommittedDetails& load_details) {
if (!load_details.is_main_frame)
return;
if (search::IsInstantNTP(web_contents()))
ipc_router_.SetInputInProgress(IsInputInProgress());
if (InInstantProcess(instant_service_, web_contents()))
ipc_router_.OnNavigationEntryCommitted();
}
void SearchTabHelper::NtpThemeChanged(NtpTheme theme) {
// Populate theme colors for this tab.
const auto& color_provider = web_contents()->GetColorProvider();
theme.background_color = color_provider.GetColor(kColorNewTabPageBackground);
theme.text_color = color_provider.GetColor(kColorNewTabPageText);
theme.text_color_light = color_provider.GetColor(kColorNewTabPageTextLight);
ipc_router_.SendNtpTheme(theme);
}
void SearchTabHelper::MostVisitedInfoChanged(
const InstantMostVisitedInfo& most_visited_info) {
ipc_router_.SendMostVisitedInfo(most_visited_info);
}
void SearchTabHelper::FocusOmnibox(bool focus) {
search::FocusOmnibox(focus, web_contents());
}
void SearchTabHelper::OnDeleteMostVisitedItem(const GURL& url) {
DCHECK(!url.is_empty());
if (instant_service_)
instant_service_->DeleteMostVisitedItem(url);
}
void SearchTabHelper::OnUndoMostVisitedDeletion(const GURL& url) {
DCHECK(!url.is_empty());
if (instant_service_)
instant_service_->UndoMostVisitedDeletion(url);
}
void SearchTabHelper::OnUndoAllMostVisitedDeletions() {
if (instant_service_)
instant_service_->UndoAllMostVisitedDeletions();
}
void SearchTabHelper::OnOmniboxInputStateChanged() {
ipc_router_.SetInputInProgress(IsInputInProgress());
}
void SearchTabHelper::OnOmniboxFocusChanged(OmniboxFocusState state,
OmniboxFocusChangeReason reason) {
ipc_router_.OmniboxFocusChanged(state, reason);
// Don't send oninputstart/oninputend updates in response to focus changes
// if there's a navigation in progress. This prevents Chrome from sending
// a spurious oninputend when the user accepts a match in the omnibox.
if (web_contents()->GetController().GetPendingEntry() == nullptr)
ipc_router_.SetInputInProgress(IsInputInProgress());
}
Profile* SearchTabHelper::profile() const {
return Profile::FromBrowserContext(web_contents()->GetBrowserContext());
}
bool SearchTabHelper::IsInputInProgress() const {
return search::IsOmniboxInputInProgress(web_contents());
}
void SearchTabHelper::CloseNTPCustomizeChromeFeaturePromo() {
const base::Feature& customize_chrome_feature =
feature_engagement::kIPHDesktopCustomizeChromeFeature;
if (!base::FeatureList::IsEnabled(customize_chrome_feature) ||
web_contents()->GetController().GetVisibleEntry()->GetURL() ==
GURL(chrome::kChromeUINewTabPageURL)) {
return;
}
auto* browser_window =
BrowserWindow::FindBrowserWindowWithWebContents(web_contents());
if (browser_window) {
browser_window->CloseFeaturePromo(customize_chrome_feature);
}
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(SearchTabHelper);