blob: 24c87d332d1195bafc7afc18f033b015a990fbe3 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.h"
#include <string>
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "build/build_config.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_features.h"
#include "content/public/browser/navigation_entry.h"
namespace {
void OnSafetyTipClosed(security_state::SafetyTipStatus safety_tip_status,
base::Time start_time,
safety_tips::SafetyTipInteraction action) {
std::string action_suffix;
bool warning_dismissed = false;
switch (action) {
case safety_tips::SafetyTipInteraction::kNoAction:
action_suffix = "NoAction";
break;
case safety_tips::SafetyTipInteraction::kLeaveSite:
action_suffix = "LeaveSite";
break;
case safety_tips::SafetyTipInteraction::kDismiss:
NOTREACHED();
// Do nothing because the dismissal action passed to this method should
// be the more specific version (esc, close, or ignore).
break;
case safety_tips::SafetyTipInteraction::kDismissWithEsc:
action_suffix = "DismissWithEsc";
warning_dismissed = true;
break;
case safety_tips::SafetyTipInteraction::kDismissWithClose:
action_suffix = "DismissWithClose";
warning_dismissed = true;
break;
case safety_tips::SafetyTipInteraction::kDismissWithIgnore:
action_suffix = "DismissWithIgnore";
warning_dismissed = true;
break;
case safety_tips::SafetyTipInteraction::kLearnMore:
action_suffix = "LearnMore";
break;
}
if (warning_dismissed) {
base::UmaHistogramCustomTimes(
security_state::GetSafetyTipHistogramName(
std::string("Security.SafetyTips.OpenTime.Dismiss"),
safety_tip_status),
base::Time::Now() - start_time, base::TimeDelta::FromMilliseconds(1),
base::TimeDelta::FromHours(1), 100);
}
base::UmaHistogramCustomTimes(
security_state::GetSafetyTipHistogramName(
std::string("Security.SafetyTips.OpenTime.") + action_suffix,
safety_tip_status),
base::Time::Now() - start_time, base::TimeDelta::FromMilliseconds(1),
base::TimeDelta::FromHours(1), 100);
}
} // namespace
namespace safety_tips {
ReputationWebContentsObserver::~ReputationWebContentsObserver() {}
void ReputationWebContentsObserver::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInMainFrame() ||
navigation_handle->IsSameDocument() ||
!navigation_handle->HasCommitted()) {
return;
}
last_navigation_safety_tip_status_ = security_state::SafetyTipStatus::kNone;
last_safety_tip_navigation_entry_id_ = 0;
MaybeShowSafetyTip();
}
void ReputationWebContentsObserver::OnVisibilityChanged(
content::Visibility visibility) {
MaybeShowSafetyTip();
}
security_state::SafetyTipStatus
ReputationWebContentsObserver::GetSafetyTipStatusForVisibleNavigation() const {
content::NavigationEntry* entry =
web_contents()->GetController().GetVisibleEntry();
if (!entry)
return security_state::SafetyTipStatus::kUnknown;
return last_safety_tip_navigation_entry_id_ == entry->GetUniqueID()
? last_navigation_safety_tip_status_
: security_state::SafetyTipStatus::kNone;
}
void ReputationWebContentsObserver::RegisterReputationCheckCallbackForTesting(
base::OnceClosure callback) {
reputation_check_callback_for_testing_ = std::move(callback);
}
ReputationWebContentsObserver::ReputationWebContentsObserver(
content::WebContents* web_contents)
: WebContentsObserver(web_contents),
profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())),
weak_factory_(this) {}
void ReputationWebContentsObserver::MaybeShowSafetyTip() {
if (web_contents()->GetMainFrame()->GetVisibilityState() !=
content::PageVisibilityState::kVisible) {
return;
}
const GURL& url = web_contents()->GetLastCommittedURL();
if (!url.SchemeIsHTTPOrHTTPS()) {
return;
}
ReputationService* service = ReputationService::Get(profile_);
service->GetReputationStatus(
url, base::BindRepeating(
&ReputationWebContentsObserver::HandleReputationCheckResult,
weak_factory_.GetWeakPtr()));
}
void ReputationWebContentsObserver::HandleReputationCheckResult(
security_state::SafetyTipStatus safety_tip_status,
bool user_ignored,
const GURL& url,
const GURL& suggested_url) {
UMA_HISTOGRAM_ENUMERATION("Security.SafetyTips.SafetyTipShown",
safety_tip_status);
if (safety_tip_status == security_state::SafetyTipStatus::kNone) {
MaybeCallReputationCheckCallback();
return;
}
// TODO(crbug/987754): Record metrics here.
if (user_ignored) {
UMA_HISTOGRAM_ENUMERATION("Security.SafetyTips.SafetyTipIgnoredPageLoad",
safety_tip_status);
MaybeCallReputationCheckCallback();
return;
}
// Set this field independent of whether the feature to show the UI is
// enabled/disabled. Metrics code uses this field and we want to record
// metrics regardless of the feature being enabled/disabled.
last_navigation_safety_tip_status_ = safety_tip_status;
// A navigation entry should always exist because reputation checks are only
// triggered when a committed navigation finishes.
last_safety_tip_navigation_entry_id_ =
web_contents()->GetController().GetLastCommittedEntry()->GetUniqueID();
MaybeCallReputationCheckCallback();
if (!base::FeatureList::IsEnabled(features::kSafetyTipUI)) {
return;
}
ShowSafetyTipDialog(
web_contents(), safety_tip_status, url, suggested_url,
base::BindOnce(OnSafetyTipClosed, safety_tip_status, base::Time::Now()));
}
void ReputationWebContentsObserver::MaybeCallReputationCheckCallback() {
if (reputation_check_callback_for_testing_.is_null())
return;
std::move(reputation_check_callback_for_testing_).Run();
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(ReputationWebContentsObserver)
} // namespace safety_tips