1 // Copyright 2018 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
5 #include "chrome/browser/resource_coordinator/decision_details.h"
6
7 #include "base/stl_util.h"
8 #include "services/metrics/public/cpp/ukm_builders.h"
9
10 namespace resource_coordinator {
11
12 namespace {
13
14 // These are intended to be human readable descriptions of the various failure
15 // reasons. They don't need to be localized as they are for a developer-only
16 // WebUI.
17 const char* kDecisionFailureReasonStrings[] = {
18 "Browser opted out via enterprise policy",
19 "Tab opted out via origin trial",
20 "Origin is in global disallowlist",
21 "Origin has been observed playing audio while backgrounded",
22 "Origin has been observed updating favicon while backgrounded",
23 "Origin is temporarily protected while under observation",
24 "Origin has been observed updating title while backgrounded",
25 "Tab is currently capturing the camera and/or microphone",
26 "Tab has been protected by an extension",
27 "Tab contains unsubmitted text form entry",
28 "Tab contains a PDF",
29 "Tab content is being mirrored/cast",
30 "Tab is currently emitting audio",
31 "Tab is currently using WebSockets",
32 "Tab is currently using WebUSB",
33 "Tab is currently visible",
34 "Tab is currently using DevTools",
35 "Tab is currently capturing a window or screen",
36 "Tab is sharing its BrowsingInstance with another tab",
37 "Tab is currently connected to a bluetooth device",
38 "Tab is currently holding a WebLock",
39 "Tab is currently holding an IndexedDB lock",
40 "Tab has notification permission ",
41 "Tab is a web application window",
42 };
43 static_assert(base::size(kDecisionFailureReasonStrings) ==
44 static_cast<size_t>(DecisionFailureReason::MAX),
45 "kDecisionFailureReasonStrings not up to date with enum");
46
47 const char* kDecisionSuccessReasonStrings[] = {
48 "Tab opted in via origin trial",
49 "Origin is in global allowlist",
50 "Origin has locally been observed to be safe via heuristic logic",
51 };
52 static_assert(base::size(kDecisionSuccessReasonStrings) ==
53 static_cast<size_t>(DecisionSuccessReason::MAX),
54 "kDecisionSuccessReasonStrings not up to date with enum");
55
PopulateSuccessReason(DecisionSuccessReason success_reason,ukm::builders::TabManager_LifecycleStateChange * ukm)56 void PopulateSuccessReason(
57 DecisionSuccessReason success_reason,
58 ukm::builders::TabManager_LifecycleStateChange* ukm) {
59 switch (success_reason) {
60 case DecisionSuccessReason::INVALID:
61 break;
62 case DecisionSuccessReason::ORIGIN_TRIAL_OPT_IN:
63 ukm->SetSuccessOriginTrialOptIn(1);
64 break;
65 case DecisionSuccessReason::GLOBAL_ALLOWLIST:
66 ukm->SetSuccessGlobalAllowlist(1);
67 break;
68 case DecisionSuccessReason::HEURISTIC_OBSERVED_TO_BE_SAFE:
69 ukm->SetSuccessHeuristic(1);
70 break;
71 case DecisionSuccessReason::MAX:
72 NOTREACHED();
73 break;
74 }
75 }
76
PopulateFailureReason(DecisionFailureReason failure_reason,ukm::builders::TabManager_LifecycleStateChange * ukm)77 void PopulateFailureReason(
78 DecisionFailureReason failure_reason,
79 ukm::builders::TabManager_LifecycleStateChange* ukm) {
80 switch (failure_reason) {
81 case DecisionFailureReason::INVALID:
82 break;
83 case DecisionFailureReason::LIFECYCLES_ENTERPRISE_POLICY_OPT_OUT:
84 ukm->SetFailureLifecyclesEnterprisePolicyOptOut(1);
85 break;
86 case DecisionFailureReason::ORIGIN_TRIAL_OPT_OUT:
87 ukm->SetFailureOriginTrialOptOut(1);
88 break;
89 case DecisionFailureReason::GLOBAL_DISALLOWLIST:
90 ukm->SetFailureGlobalDisallowlist(1);
91 break;
92 case DecisionFailureReason::HEURISTIC_AUDIO:
93 ukm->SetFailureHeuristicAudio(1);
94 break;
95 case DecisionFailureReason::HEURISTIC_FAVICON:
96 ukm->SetFailureHeuristicFavicon(1);
97 break;
98 case DecisionFailureReason::HEURISTIC_INSUFFICIENT_OBSERVATION:
99 ukm->SetFailureHeuristicInsufficientObservation(1);
100 break;
101 case DecisionFailureReason::HEURISTIC_TITLE:
102 ukm->SetFailureHeuristicTitle(1);
103 break;
104 case DecisionFailureReason::LIVE_STATE_CAPTURING:
105 ukm->SetFailureLiveStateCapturing(1);
106 break;
107 case DecisionFailureReason::LIVE_STATE_EXTENSION_DISALLOWED:
108 ukm->SetFailureLiveStateExtensionDisallowed(1);
109 break;
110 case DecisionFailureReason::LIVE_STATE_FORM_ENTRY:
111 ukm->SetFailureLiveStateFormEntry(1);
112 break;
113 case DecisionFailureReason::LIVE_STATE_IS_PDF:
114 ukm->SetFailureLiveStateIsPDF(1);
115 break;
116 case DecisionFailureReason::LIVE_STATE_MIRRORING:
117 ukm->SetFailureLiveStateMirroring(1);
118 break;
119 case DecisionFailureReason::LIVE_STATE_PLAYING_AUDIO:
120 ukm->SetFailureLiveStatePlayingAudio(1);
121 break;
122 case DecisionFailureReason::LIVE_STATE_USING_WEB_SOCKETS:
123 ukm->SetFailureLiveStateUsingWebSockets(1);
124 break;
125 case DecisionFailureReason::LIVE_STATE_USING_WEB_USB:
126 ukm->SetFailureLiveStateUsingWebUSB(1);
127 break;
128 case DecisionFailureReason::LIVE_STATE_VISIBLE:
129 ukm->SetFailureLiveStateVisible(1);
130 break;
131 case DecisionFailureReason::LIVE_STATE_DEVTOOLS_OPEN:
132 ukm->SetFailureLiveStateDevToolsOpen(1);
133 break;
134 case DecisionFailureReason::LIVE_STATE_DESKTOP_CAPTURE:
135 ukm->SetFailureLiveStateDesktopCapture(1);
136 break;
137 case DecisionFailureReason::LIVE_STATE_SHARING_BROWSING_INSTANCE:
138 ukm->SetFailureLiveStateSharingBrowsingInstance(1);
139 break;
140 case DecisionFailureReason::LIVE_STATE_USING_BLUETOOTH:
141 ukm->SetFailureLiveStateUsingBluetooth(1);
142 break;
143 case DecisionFailureReason::LIVE_STATE_USING_WEBLOCK:
144 ukm->SetFailureLiveStateUsingWebLock(1);
145 break;
146 case DecisionFailureReason::LIVE_STATE_USING_INDEXEDDB_LOCK:
147 ukm->SetFailureLiveStateUsingIndexedDBLock(1);
148 break;
149 case DecisionFailureReason::LIVE_STATE_HAS_NOTIFICATIONS_PERMISSION:
150 ukm->SetFailureLiveStateHasNotificationsPermission(1);
151 break;
152 case DecisionFailureReason::LIVE_WEB_APP:
153 ukm->SetFailureLiveWebApp(1);
154 break;
155 case DecisionFailureReason::MAX:
156 NOTREACHED();
157 break;
158 }
159 }
160
161 } // namespace
162
ToString(DecisionFailureReason failure_reason)163 const char* ToString(DecisionFailureReason failure_reason) {
164 if (failure_reason == DecisionFailureReason::INVALID ||
165 failure_reason == DecisionFailureReason::MAX)
166 return nullptr;
167 return kDecisionFailureReasonStrings[static_cast<size_t>(failure_reason)];
168 }
169
ToString(DecisionSuccessReason success_reason)170 const char* ToString(DecisionSuccessReason success_reason) {
171 if (success_reason == DecisionSuccessReason::INVALID ||
172 success_reason == DecisionSuccessReason::MAX)
173 return nullptr;
174 return kDecisionSuccessReasonStrings[static_cast<size_t>(success_reason)];
175 }
176
Reason()177 DecisionDetails::Reason::Reason()
178 : success_reason_(DecisionSuccessReason::INVALID),
179 failure_reason_(DecisionFailureReason::INVALID) {}
180
Reason(DecisionSuccessReason success_reason)181 DecisionDetails::Reason::Reason(DecisionSuccessReason success_reason)
182 : success_reason_(success_reason),
183 failure_reason_(DecisionFailureReason::INVALID) {
184 DCHECK(IsSuccess());
185 }
186
Reason(DecisionFailureReason failure_reason)187 DecisionDetails::Reason::Reason(DecisionFailureReason failure_reason)
188 : success_reason_(DecisionSuccessReason::INVALID),
189 failure_reason_(failure_reason) {
190 DCHECK(IsFailure());
191 }
192
193 DecisionDetails::Reason::Reason(const Reason& rhs) = default;
194 DecisionDetails::Reason::~Reason() = default;
195
196 DecisionDetails::Reason& DecisionDetails::Reason::operator=(const Reason& rhs) =
197 default;
198
IsValid() const199 bool DecisionDetails::Reason::IsValid() const {
200 return IsSuccess() || IsFailure();
201 }
202
IsSuccess() const203 bool DecisionDetails::Reason::IsSuccess() const {
204 if (success_reason_ == DecisionSuccessReason::INVALID ||
205 success_reason_ == DecisionSuccessReason::MAX ||
206 failure_reason_ != DecisionFailureReason::INVALID)
207 return false;
208 return true;
209 }
210
IsFailure() const211 bool DecisionDetails::Reason::IsFailure() const {
212 if (failure_reason_ == DecisionFailureReason::INVALID ||
213 failure_reason_ == DecisionFailureReason::MAX ||
214 success_reason_ != DecisionSuccessReason::INVALID)
215 return false;
216 return true;
217 }
218
success_reason() const219 DecisionSuccessReason DecisionDetails::Reason::success_reason() const {
220 DCHECK(IsSuccess());
221 return success_reason_;
222 }
223
failure_reason() const224 DecisionFailureReason DecisionDetails::Reason::failure_reason() const {
225 DCHECK(IsFailure());
226 return failure_reason_;
227 }
228
ToString() const229 const char* DecisionDetails::Reason::ToString() const {
230 if (!IsValid())
231 return nullptr;
232 if (IsSuccess())
233 return ::resource_coordinator::ToString(success_reason_);
234 DCHECK(IsFailure());
235 return ::resource_coordinator::ToString(failure_reason_);
236 }
237
operator ==(const Reason & rhs) const238 bool DecisionDetails::Reason::operator==(const Reason& rhs) const {
239 return success_reason_ == rhs.success_reason_ &&
240 failure_reason_ == rhs.failure_reason_;
241 }
242
operator !=(const Reason & rhs) const243 bool DecisionDetails::Reason::operator!=(const Reason& rhs) const {
244 return !(*this == rhs);
245 }
246
DecisionDetails()247 DecisionDetails::DecisionDetails() : toggled_(false) {}
248
249 DecisionDetails::~DecisionDetails() = default;
250
operator =(DecisionDetails && rhs)251 DecisionDetails& DecisionDetails::operator=(DecisionDetails&& rhs) {
252 toggled_ = rhs.toggled_;
253 reasons_ = std::move(rhs.reasons_);
254 rhs.Clear();
255 return *this;
256 }
257
AddReason(const Reason & reason)258 bool DecisionDetails::AddReason(const Reason& reason) {
259 reasons_.push_back(reason);
260 return CheckIfToggled();
261 }
262
AddReason(DecisionFailureReason failure_reason)263 bool DecisionDetails::AddReason(DecisionFailureReason failure_reason) {
264 reasons_.push_back(Reason(failure_reason));
265 return CheckIfToggled();
266 }
267
AddReason(DecisionSuccessReason success_reason)268 bool DecisionDetails::AddReason(DecisionSuccessReason success_reason) {
269 reasons_.push_back(Reason(success_reason));
270 return CheckIfToggled();
271 }
272
IsPositive() const273 bool DecisionDetails::IsPositive() const {
274 // A decision without supporting reasons is negative by default.
275 if (reasons_.empty())
276 return false;
277 return reasons_.front().IsSuccess();
278 }
279
SuccessReason() const280 DecisionSuccessReason DecisionDetails::SuccessReason() const {
281 DCHECK(!reasons_.empty());
282 return reasons_.front().success_reason();
283 }
284
FailureReason() const285 DecisionFailureReason DecisionDetails::FailureReason() const {
286 DCHECK(!reasons_.empty());
287 return reasons_.front().failure_reason();
288 }
289
Populate(ukm::builders::TabManager_LifecycleStateChange * ukm) const290 void DecisionDetails::Populate(
291 ukm::builders::TabManager_LifecycleStateChange* ukm) const {
292 DCHECK(!reasons_.empty());
293 bool positive = IsPositive();
294 ukm->SetOutcome(positive);
295 for (const auto& reason : reasons_) {
296 // Stop adding reasons once all of the initial reasons of the same type
297 // have been added.
298 bool success = reason.IsSuccess();
299 if (success != positive)
300 break;
301 if (success) {
302 PopulateSuccessReason(reason.success_reason(), ukm);
303 } else {
304 PopulateFailureReason(reason.failure_reason(), ukm);
305 }
306 }
307 }
308
GetFailureReasonStrings() const309 std::vector<std::string> DecisionDetails::GetFailureReasonStrings() const {
310 std::vector<std::string> reasons;
311 for (const auto& reason : reasons_) {
312 if (reason.IsSuccess())
313 break;
314 reasons.push_back(reason.ToString());
315 }
316 return reasons;
317 }
318
Clear()319 void DecisionDetails::Clear() {
320 reasons_.clear();
321 toggled_ = false;
322 }
323
CheckIfToggled()324 bool DecisionDetails::CheckIfToggled() {
325 if (toggled_)
326 return true;
327 if (reasons_.size() <= 1)
328 return false;
329 // Determine if the last reason is of a different type than the one before. If
330 // so, then the toggle has occurred.
331 toggled_ = reasons_[reasons_.size() - 1].IsSuccess() !=
332 reasons_[reasons_.size() - 2].IsSuccess();
333 return toggled_;
334 }
335
336 } // namespace resource_coordinator
337