blob: 0b7d54a7a64a89774efc0cb7c40b51b3dddbf1e6 [file] [log] [blame]
Joe DeBlasio1ed86402019-07-26 23:42:301// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Emily Stark474b8ca2019-10-24 15:35:395#include "chrome/browser/reputation/reputation_web_contents_observer.h"
Joe DeBlasio1ed86402019-07-26 23:42:306
Livvie Lin753db88c2019-09-26 21:16:507#include <string>
Aidan Beggsdead92c2019-10-08 21:37:278#include <utility>
Livvie Lin753db88c2019-09-26 21:16:509
Emily Starka939e572019-09-05 23:18:0810#include "base/metrics/histogram_functions.h"
Livvie Lind2539b42019-09-04 18:37:5511#include "base/metrics/histogram_macros.h"
Joe DeBlasio1ed86402019-07-26 23:42:3012#include "build/build_config.h"
13#include "chrome/browser/profiles/profile.h"
Aidan Beggsbe93a332019-10-28 17:29:4114#include "chrome/browser/reputation/reputation_service.h"
Joe DeBlasio92c30032019-10-11 16:48:0715#include "components/security_state/core/features.h"
Emily Starkca844c02019-08-28 04:28:4416#include "content/public/browser/navigation_entry.h"
Joe DeBlasio1ed86402019-07-26 23:42:3017
Emily Starka939e572019-09-05 23:18:0818namespace {
19
20void OnSafetyTipClosed(security_state::SafetyTipStatus safety_tip_status,
21 base::Time start_time,
Emily Stark53b29892019-10-24 16:30:4122 SafetyTipInteraction action) {
Emily Starka939e572019-09-05 23:18:0823 std::string action_suffix;
Livvie Lin753db88c2019-09-26 21:16:5024 bool warning_dismissed = false;
Emily Starka939e572019-09-05 23:18:0825 switch (action) {
Emily Stark53b29892019-10-24 16:30:4126 case SafetyTipInteraction::kNoAction:
Emily Starka939e572019-09-05 23:18:0827 action_suffix = "NoAction";
28 break;
Emily Stark53b29892019-10-24 16:30:4129 case SafetyTipInteraction::kLeaveSite:
Emily Starka939e572019-09-05 23:18:0830 action_suffix = "LeaveSite";
31 break;
Emily Stark53b29892019-10-24 16:30:4132 case SafetyTipInteraction::kDismiss:
Livvie Lin753db88c2019-09-26 21:16:5033 NOTREACHED();
34 // Do nothing because the dismissal action passed to this method should
35 // be the more specific version (esc, close, or ignore).
Emily Starka939e572019-09-05 23:18:0836 break;
Emily Stark53b29892019-10-24 16:30:4137 case SafetyTipInteraction::kDismissWithEsc:
Livvie Lin753db88c2019-09-26 21:16:5038 action_suffix = "DismissWithEsc";
39 warning_dismissed = true;
40 break;
Emily Stark53b29892019-10-24 16:30:4141 case SafetyTipInteraction::kDismissWithClose:
Livvie Lin753db88c2019-09-26 21:16:5042 action_suffix = "DismissWithClose";
43 warning_dismissed = true;
44 break;
Emily Stark53b29892019-10-24 16:30:4145 case SafetyTipInteraction::kDismissWithIgnore:
Livvie Lin753db88c2019-09-26 21:16:5046 action_suffix = "DismissWithIgnore";
47 warning_dismissed = true;
48 break;
Emily Stark53b29892019-10-24 16:30:4149 case SafetyTipInteraction::kLearnMore:
Joe DeBlasio29a2ab22019-10-04 23:47:1050 action_suffix = "LearnMore";
51 break;
Livvie Lin753db88c2019-09-26 21:16:5052 }
53 if (warning_dismissed) {
54 base::UmaHistogramCustomTimes(
55 security_state::GetSafetyTipHistogramName(
56 std::string("Security.SafetyTips.OpenTime.Dismiss"),
57 safety_tip_status),
58 base::Time::Now() - start_time, base::TimeDelta::FromMilliseconds(1),
59 base::TimeDelta::FromHours(1), 100);
Emily Starka939e572019-09-05 23:18:0860 }
61 base::UmaHistogramCustomTimes(
62 security_state::GetSafetyTipHistogramName(
63 std::string("Security.SafetyTips.OpenTime.") + action_suffix,
64 safety_tip_status),
65 base::Time::Now() - start_time, base::TimeDelta::FromMilliseconds(1),
66 base::TimeDelta::FromHours(1), 100);
67}
68
69} // namespace
70
Aidan Beggse6a7dae72019-10-25 20:14:4971ReputationWebContentsObserver::~ReputationWebContentsObserver() = default;
Joe DeBlasio1ed86402019-07-26 23:42:3072
73void ReputationWebContentsObserver::DidFinishNavigation(
74 content::NavigationHandle* navigation_handle) {
Joe DeBlasio7d604922019-08-08 19:56:3675 if (!navigation_handle->IsInMainFrame() ||
Joe DeBlasio7f15aad2019-08-22 01:39:0876 navigation_handle->IsSameDocument() ||
Aidan Beggse6a7dae72019-10-25 20:14:4977 !navigation_handle->HasCommitted() || navigation_handle->IsErrorPage()) {
78 MaybeCallReputationCheckCallback();
Joe DeBlasio7f15aad2019-08-22 01:39:0879 return;
80 }
81
meacerb66107d2019-10-17 20:00:1982 last_navigation_safety_tip_info_ = {security_state::SafetyTipStatus::kNone,
83 GURL()};
Emily Starkca844c02019-08-28 04:28:4484 last_safety_tip_navigation_entry_id_ = 0;
85
Joe DeBlasio7f15aad2019-08-22 01:39:0886 MaybeShowSafetyTip();
87}
88
89void ReputationWebContentsObserver::OnVisibilityChanged(
90 content::Visibility visibility) {
91 MaybeShowSafetyTip();
92}
93
meacerb66107d2019-10-17 20:00:1994security_state::SafetyTipInfo
95ReputationWebContentsObserver::GetSafetyTipInfoForVisibleNavigation() const {
Emily Starkca844c02019-08-28 04:28:4496 content::NavigationEntry* entry =
97 web_contents()->GetController().GetVisibleEntry();
98 if (!entry)
meacerb66107d2019-10-17 20:00:1999 return {security_state::SafetyTipStatus::kUnknown, GURL()};
Emily Starkca844c02019-08-28 04:28:44100 return last_safety_tip_navigation_entry_id_ == entry->GetUniqueID()
meacerb66107d2019-10-17 20:00:19101 ? last_navigation_safety_tip_info_
102 : security_state::SafetyTipInfo(
103 {security_state::SafetyTipStatus::kNone, GURL()});
Emily Starkca844c02019-08-28 04:28:44104}
105
Emily Stark0790bb002019-08-31 04:25:43106void ReputationWebContentsObserver::RegisterReputationCheckCallbackForTesting(
107 base::OnceClosure callback) {
108 reputation_check_callback_for_testing_ = std::move(callback);
109}
110
Joe DeBlasio7f15aad2019-08-22 01:39:08111ReputationWebContentsObserver::ReputationWebContentsObserver(
112 content::WebContents* web_contents)
113 : WebContentsObserver(web_contents),
114 profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())),
meacerb66107d2019-10-17 20:00:19115 weak_factory_(this) {
116 last_navigation_safety_tip_info_ = {security_state::SafetyTipStatus::kNone,
117 GURL()};
118}
Joe DeBlasio7f15aad2019-08-22 01:39:08119
120void ReputationWebContentsObserver::MaybeShowSafetyTip() {
121 if (web_contents()->GetMainFrame()->GetVisibilityState() !=
122 content::PageVisibilityState::kVisible) {
Joe DeBlasio1ed86402019-07-26 23:42:30123 return;
124 }
125
126 const GURL& url = web_contents()->GetLastCommittedURL();
127 if (!url.SchemeIsHTTPOrHTTPS()) {
128 return;
129 }
130
131 ReputationService* service = ReputationService::Get(profile_);
132 service->GetReputationStatus(
133 url, base::BindRepeating(
134 &ReputationWebContentsObserver::HandleReputationCheckResult,
135 weak_factory_.GetWeakPtr()));
136}
137
Joe DeBlasio1ed86402019-07-26 23:42:30138void ReputationWebContentsObserver::HandleReputationCheckResult(
Aidan Beggsbe93a332019-10-28 17:29:41139 ReputationCheckResult result) {
Livvie Lind2539b42019-09-04 18:37:55140 UMA_HISTOGRAM_ENUMERATION("Security.SafetyTips.SafetyTipShown",
Aidan Beggsbe93a332019-10-28 17:29:41141 result.safety_tip_status);
Livvie Lind2539b42019-09-04 18:37:55142
Aidan Beggsbe93a332019-10-28 17:29:41143 if (result.safety_tip_status == security_state::SafetyTipStatus::kNone ||
144 result.safety_tip_status ==
145 security_state::SafetyTipStatus::kBadKeyword) {
Emily Stark0790bb002019-08-31 04:25:43146 MaybeCallReputationCheckCallback();
Joe DeBlasio1ed86402019-07-26 23:42:30147 return;
148 }
149
Aidan Beggsbe93a332019-10-28 17:29:41150 if (result.user_previously_ignored) {
Livvie Lind2539b42019-09-04 18:37:55151 UMA_HISTOGRAM_ENUMERATION("Security.SafetyTips.SafetyTipIgnoredPageLoad",
Aidan Beggsbe93a332019-10-28 17:29:41152 result.safety_tip_status);
Emily Stark0790bb002019-08-31 04:25:43153 MaybeCallReputationCheckCallback();
Joe DeBlasio1ed86402019-07-26 23:42:30154 return;
155 }
Livvie Lind2539b42019-09-04 18:37:55156
Emily Starkca844c02019-08-28 04:28:44157 // Set this field independent of whether the feature to show the UI is
158 // enabled/disabled. Metrics code uses this field and we want to record
159 // metrics regardless of the feature being enabled/disabled.
Aidan Beggsbe93a332019-10-28 17:29:41160 last_navigation_safety_tip_info_ = {result.safety_tip_status,
161 result.suggested_url};
meacerb66107d2019-10-17 20:00:19162
Emily Starkca844c02019-08-28 04:28:44163 // A navigation entry should always exist because reputation checks are only
164 // triggered when a committed navigation finishes.
165 last_safety_tip_navigation_entry_id_ =
166 web_contents()->GetController().GetLastCommittedEntry()->GetUniqueID();
Joe DeBlasio92c30032019-10-11 16:48:07167 // Update the visible security state, since we downgrade indicator when a
168 // safety tip is triggered. This has to happen after
169 // last_safety_tip_navigation_entry_id_ is updated.
170 web_contents()->DidChangeVisibleSecurityState();
Joe DeBlasio1ed86402019-07-26 23:42:30171
Emily Stark0790bb002019-08-31 04:25:43172 MaybeCallReputationCheckCallback();
173
Joe DeBlasio92c30032019-10-11 16:48:07174 if (!base::FeatureList::IsEnabled(security_state::features::kSafetyTipUI)) {
Emily Starkca844c02019-08-28 04:28:44175 return;
176 }
Emily Starka939e572019-09-05 23:18:08177 ShowSafetyTipDialog(
Aidan Beggsbe93a332019-10-28 17:29:41178 web_contents(), result.safety_tip_status, result.url,
179 result.suggested_url,
180 base::BindOnce(OnSafetyTipClosed, result.safety_tip_status,
181 base::Time::Now()));
Joe DeBlasio1ed86402019-07-26 23:42:30182}
183
Emily Stark0790bb002019-08-31 04:25:43184void ReputationWebContentsObserver::MaybeCallReputationCheckCallback() {
185 if (reputation_check_callback_for_testing_.is_null())
186 return;
187 std::move(reputation_check_callback_for_testing_).Run();
188}
189
Joe DeBlasio1ed86402019-07-26 23:42:30190WEB_CONTENTS_USER_DATA_KEY_IMPL(ReputationWebContentsObserver)