#include "chrome/browser/ui/omnibox/omnibox_tab_helper.h"
#include <optional>
#include <string>
#include <string_view>
#include "base/metrics/histogram_functions.h"
#include "base/observer_list.h"
#include "base/strings/strcat.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "chrome/browser/page_content_annotations/page_content_extraction_service.h"
#include "chrome/browser/page_content_annotations/page_content_extraction_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/location_bar/location_bar.h"
#include "chrome/browser/ui/omnibox/omnibox_view.h"
#include "components/omnibox/browser/lens_suggest_inputs_utils.h"
#include "components/omnibox/common/omnibox_feature_configs.h"
#include "components/omnibox/common/omnibox_focus_state.h"
#include "content/public/browser/page.h"
#include "content/public/browser/render_frame_host.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/metrics_proto/omnibox_event.pb.h"
WEB_CONTENTS_USER_DATA_KEY_IMPL(OmniboxTabHelper);
namespace {
constexpr char kNavigationToPopupShownHistogramPrefix[] =
"Omnibox.NavigationToPopupShown";
constexpr char kMainDocumentElementAvailableHistogramSuffix[] =
"MainDocumentElementAvailable";
constexpr char kPrimaryPageChangedHistogramSuffix[] = "PrimaryPageChanged";
constexpr char kDomContentLoadedHistogramSuffix[] = "DomContentLoaded";
constexpr char kByPageContextHistogramPrefix[] = "ByPageContext";
void LogNavigationToPopupUma(std::string_view event_name,
std::string_view page_context,
base::TimeDelta time_to_log) {
base::UmaHistogramCustomTimes(
base::StrCat({kNavigationToPopupShownHistogramPrefix, ".", event_name}),
time_to_log, base::Milliseconds(0), base::Seconds(60), 60);
base::UmaHistogramCustomTimes(
base::StrCat({kNavigationToPopupShownHistogramPrefix, ".", event_name,
".", kByPageContextHistogramPrefix, ".", page_context}),
time_to_log, base::Milliseconds(0), base::Seconds(60), 60);
}
PaywallSignal ToPaywallSignal(std::optional<bool> paywall_signal) {
if (paywall_signal.has_value()) {
return paywall_signal.value() ? PaywallSignal::kSignalPresent
: PaywallSignal::kSignalNotPresent;
}
return PaywallSignal::kUnknown;
}
}
OmniboxTabHelper::~OmniboxTabHelper() = default;
OmniboxTabHelper::OmniboxTabHelper(content::WebContents* contents,
Profile* profile)
: content::WebContentsUserData<OmniboxTabHelper>(*contents),
content::WebContentsObserver(contents) {
if (omnibox_feature_configs::ContextualSearch::Get().use_apc_paywall_signal &&
!base::FeatureList::IsEnabled(blink::features::kFrameMetadataObserver)) {
if (auto* service = page_content_annotations::
PageContentExtractionServiceFactory::GetForProfile(profile)) {
page_content_service_observation_.Observe(service);
}
}
}
void OmniboxTabHelper::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void OmniboxTabHelper::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void OmniboxTabHelper::OnInputStateChanged() {
for (auto& observer : observers_) {
observer.OnOmniboxInputStateChanged();
}
}
void OmniboxTabHelper::OnInputInProgress(bool in_progress) {
for (auto& observer : observers_) {
observer.OnOmniboxInputInProgress(in_progress);
}
}
void OmniboxTabHelper::OnFocusChanged(OmniboxFocusState state,
OmniboxFocusChangeReason reason) {
for (auto& observer : observers_) {
observer.OnOmniboxFocusChanged(state, reason);
}
}
void OmniboxTabHelper::OnPopupVisibilityChanged(
bool popup_is_open,
metrics::OmniboxEventProto::PageClassification page_classification) {
for (auto& observer : observers_) {
observer.OnOmniboxPopupVisibilityChanged(popup_is_open);
}
if (popup_is_open) {
MaybeLogPaywallSignal();
MaybeLogNavigationToPopupShownTimings(page_classification);
}
}
std::optional<bool> OmniboxTabHelper::IsPagePaywalled() {
return page_has_apc_paywall_signal_;
}
void OmniboxTabHelper::OnPageContentExtracted(
content::Page& page,
const optimization_guide::proto::AnnotatedPageContent& page_content) {
if (&page != &(GetWebContents().GetPrimaryPage())) {
return;
}
page_has_apc_paywall_signal_ =
page_content.has_main_frame_data() &&
page_content.main_frame_data().has_paid_content_metadata() &&
page_content.main_frame_data()
.paid_content_metadata()
.contains_paid_content();
}
void OmniboxTabHelper::PrimaryPageChanged(content::Page& page) {
page_has_apc_paywall_signal_.reset();
primary_main_document_element_available_time_.reset();
dom_content_loaded_time_.reset();
logged_current_navigation_timings_ = false;
primary_page_changed_time_ = base::ElapsedTimer();
AddMetadataObserver(page);
}
void OmniboxTabHelper::AddMetadataObserver(content::Page& page) {
if (!base::FeatureList::IsEnabled(blink::features::kFrameMetadataObserver) ||
!omnibox_feature_configs::ContextualSearch::Get()
.use_apc_paywall_signal) {
return;
}
paid_content_metadata_observer_receiver_.reset();
mojo::Remote<blink::mojom::FrameMetadataObserverRegistry>
frame_metadata_observer_registry;
auto& render_frame_host = page.GetMainDocument();
render_frame_host.GetRemoteInterfaces()->GetInterface(
frame_metadata_observer_registry.BindNewPipeAndPassReceiver());
mojo::PendingRemote<blink::mojom::PaidContentMetadataObserver> remote;
paid_content_metadata_observer_receiver_.Bind(
remote.InitWithNewPipeAndPassReceiver());
frame_metadata_observer_registry->AddPaidContentMetadataObserver(
std::move(remote));
}
void OmniboxTabHelper::PrimaryMainDocumentElementAvailable() {
primary_main_document_element_available_time_ = base::ElapsedTimer();
}
void OmniboxTabHelper::DOMContentLoaded(
content::RenderFrameHost* render_frame_host) {
if (render_frame_host->GetParent()) {
return;
}
dom_content_loaded_time_ = base::ElapsedTimer();
}
void OmniboxTabHelper::MaybeLogNavigationToPopupShownTimings(
metrics::OmniboxEventProto::PageClassification page_classification) {
if (logged_current_navigation_timings_) {
return;
}
logged_current_navigation_timings_ = true;
if (!primary_page_changed_time_.has_value()) {
return;
}
const std::string page_context =
metrics::OmniboxEventProto::PageClassification_Name(page_classification);
LogNavigationToPopupUma(kPrimaryPageChangedHistogramSuffix, page_context,
primary_page_changed_time_->Elapsed());
if (primary_main_document_element_available_time_.has_value()) {
LogNavigationToPopupUma(
kMainDocumentElementAvailableHistogramSuffix, page_context,
primary_main_document_element_available_time_->Elapsed());
}
if (dom_content_loaded_time_.has_value()) {
LogNavigationToPopupUma(kDomContentLoadedHistogramSuffix, page_context,
dom_content_loaded_time_->Elapsed());
}
}
void OmniboxTabHelper::MaybeLogPaywallSignal() {
if (!page_content_service_observation_.IsObserving() &&
!paid_content_metadata_observer_receiver_.is_bound()) {
return;
}
const auto paywall_signal = ToPaywallSignal(page_has_apc_paywall_signal_);
base::UmaHistogramEnumeration("Omnibox.OnPopupOpen.PaywallSignal",
paywall_signal);
}
void OmniboxTabHelper::OnPaidContentMetadataChanged(bool has_paid_content) {
page_has_apc_paywall_signal_ = has_paid_content;
}