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 #ifndef CHROME_BROWSER_RESOURCE_COORDINATOR_DECISION_DETAILS_H_
6 #define CHROME_BROWSER_RESOURCE_COORDINATOR_DECISION_DETAILS_H_
7 
8 #include <string>
9 #include <vector>
10 
11 #include "base/macros.h"
12 
13 namespace ukm {
14 namespace builders {
15 class TabManager_LifecycleStateChange;
16 }
17 }  // namespace ukm
18 
19 namespace resource_coordinator {
20 
21 // An enumeration of reasons why a particular intervention or lifecycle state
22 // changes can be denied. This is a superset of all failure reasons that can
23 // apply for any particular intervention. New reasons can freely be added to
24 // this enum as necessary, but UKM plumbing and string conversion needs to be
25 // maintained as well.
26 enum class DecisionFailureReason : int32_t {
27   // An invalid failure reason. This must remain first.
28   INVALID = -1,
29   // The browser was opted out of the intervention via enterprise policy.
30   LIFECYCLES_ENTERPRISE_POLICY_OPT_OUT,
31   // A frame on the page opted itself out of the intervention via origin trial.
32   ORIGIN_TRIAL_OPT_OUT,
33   // The origin was opted out of the intervention in the global disallowlist.
34   GLOBAL_DISALLOWLIST,
35   // The local heuristic opted the origin out of the intervention due to its use
36   // of audio while in the background.
37   HEURISTIC_AUDIO,
38   // The local heuristic opted the origin out of the intervention due to its use
39   // of favicon updates while in the background.
40   HEURISTIC_FAVICON,
41   // The local heuristic is temporarily opting the origin out of the
42   // intervention due to a lack of sufficient observation time.
43   HEURISTIC_INSUFFICIENT_OBSERVATION,
44   // The local heuristic opted the origin out of the intervention due to its use
45   // of title updates while in the background.
46   HEURISTIC_TITLE,
47   // The tab is opted out of the intervention as it is currently capturing user
48   // media (webcam, microphone, etc).
49   LIVE_STATE_CAPTURING,
50   // The tab is opted out of the intervention by an extension.
51   LIVE_STATE_EXTENSION_DISALLOWED,
52   // The tab is opted out of the intervention as it contains text form entry.
53   LIVE_STATE_FORM_ENTRY,
54   // The tab is opted out of the intervention as it is currently hosting a PDF.
55   LIVE_STATE_IS_PDF,
56   // The tab is opted out of the intervention as it is currently being mirrored
57   // (casting, etc).
58   LIVE_STATE_MIRRORING,
59   // The tab is opted out of the intervention as it is currently playing audio.
60   LIVE_STATE_PLAYING_AUDIO,
61   // The tab is opted out of the intervention as it is currently using
62   // WebSockets.
63   // NOTE: This heuristic isn't used in the freezing/discarding interventions.
64   LIVE_STATE_USING_WEB_SOCKETS,
65   // The tab is opted out of the intervention as it is currently WebUSB.
66   LIVE_STATE_USING_WEB_USB,
67   // The tab is opted out of the intervention as it is currently visible.
68   LIVE_STATE_VISIBLE,
69   // The tab is opted out of the intervention as it's currently using DevTools.
70   LIVE_STATE_DEVTOOLS_OPEN,
71   // The tab is opted out of the intervention as it's currently capturing a
72   // window or screen.
73   LIVE_STATE_DESKTOP_CAPTURE,
74   // This tab is sharing its BrowsingInstance with another tab, and so could
75   // want to communicate with it.
76   LIVE_STATE_SHARING_BROWSING_INSTANCE,
77   // The tab is opted out of the intervention as it's currently connected to a
78   // bluetooth device.
79   LIVE_STATE_USING_BLUETOOTH,
80   // The tab is opted out of the intervention as it's currently holding at least
81   // one WebLock.
82   LIVE_STATE_USING_WEBLOCK,
83   // The tab is opted out of the intervention as it's currently holding at least
84   // one IndexedDB lock.
85   LIVE_STATE_USING_INDEXEDDB_LOCK,
86   // The tab is opted out of the intervention as it has the permission to use
87   // notifications.
88   LIVE_STATE_HAS_NOTIFICATIONS_PERMISSION,
89   // The tab is a standalone desktop PWA window.
90   LIVE_WEB_APP,
91   // This must remain last.
92   MAX,
93 };
94 
95 // An enumeration of reasons why a particular intervention or lifecycle state
96 // change can be approved. The fact that no "live state" failures are blocking
97 // the intervention is implicit, and doesn't need to be explicitly encoded.
98 enum class DecisionSuccessReason : int32_t {
99   // An invalid failure reason. This must remain first.
100   INVALID = -1,
101   // A frame on the page opted itself in the intervention via origin trial.
102   ORIGIN_TRIAL_OPT_IN,
103   // The origin was opted into the intervention via the global allowlist.
104   GLOBAL_ALLOWLIST,
105   // The origin has been observed to be safe for the intervention using local
106   // database observations.
107   HEURISTIC_OBSERVED_TO_BE_SAFE,
108   // This must remain last.
109   MAX,
110 };
111 
112 // Helper function for converting a reason to a string representation.
113 const char* ToString(DecisionFailureReason failure_reason);
114 const char* ToString(DecisionSuccessReason success_reason);
115 
116 // Describes the detailed reasons why a particular intervention decision was
117 // made. This is populated by the various policy bits of policy logic that
118 // decide whether a particular intervention or lifecycle state transition can be
119 // performed. It can populate various related UKM builders and also be converted
120 // to a collection of user readable strings for the purposes of displaying in
121 // in web UI.
122 //
123 // A decision can contain multiple reasons for success or failure, and policy
124 // allows some success reasons to override some failure reasons and vice versa.
125 // The first reason posted to this object determines whether or not the overall
126 // outcome is positive or negative. It is assumed that reasons are posted in
127 // order of decreasing priority.
128 //
129 // For logging and inspection it is useful to know of all possible failure
130 // reasons blocking a success. Similarly, it is interesting to know of all of
131 // the possible success reasons blocking a failure. To this end, policy logic
132 // should continue populating the decision with details until is has "toggled".
133 // That is, a success reason has followed a chain of failures or vice versa.
134 // The "toggling" of the decision chain is indicated by the return value from
135 // AddReason. This allows writing code like the following:
136 //
137 // bool DecideIfCanDoSomething(DecisionDetails* details) {
138 //   if (some_condition_is_false) {
139 //     if (details->AddReason(kSomeFailureReason))
140 //       return details->IsPositive();
141 //   }
142 //   if (some_other_condition) {
143 //     if (details->AddReason(kSomeOtherFailureReason))
144 //       return details->IsPositive();
145 //   }
146 //   ...
147 // }
148 class DecisionDetails {
149  public:
150   // A union of success/failure reasons. This is allowed to be copied in order
151   // to be compatible with STL containers.
152   class Reason {
153    public:
154     Reason();
155     explicit Reason(DecisionSuccessReason success_reason);
156     explicit Reason(DecisionFailureReason failure_reason);
157     Reason(const Reason& rhs);
158     ~Reason();
159 
160     Reason& operator=(const Reason& rhs);
161 
162     bool IsValid() const;
163     bool IsSuccess() const;
164     bool IsFailure() const;
165     DecisionSuccessReason success_reason() const;
166     DecisionFailureReason failure_reason() const;
167 
168     const char* ToString() const;
169 
170     bool operator==(const Reason& rhs) const;
171     bool operator!=(const Reason& rhs) const;
172 
173    private:
174     DecisionSuccessReason success_reason_;
175     DecisionFailureReason failure_reason_;
176   };
177 
178   DecisionDetails();
179   ~DecisionDetails();
180 
181   // Allow move assignment.
182   DecisionDetails& operator=(DecisionDetails&& rhs);
183 
184   // Adds a success or failure reason. Returns true if the chain of reasons has
185   // "toggled", false otherwise.
186   bool AddReason(const Reason& reason);
187   bool AddReason(DecisionFailureReason failure_reason);
188   bool AddReason(DecisionSuccessReason success_reason);
189 
190   // Returns the outcome of the decision. This is implicit from the reasons that
191   // have been posted to this object.
192   bool IsPositive() const;
193 
194   // Returns the main success reason. This is only valid to call if IsPositive
195   // is true.
196   DecisionSuccessReason SuccessReason() const;
197 
198   // Returns the main failure reason. This is only valid to call if IsPositive
199   // is false.
200   DecisionFailureReason FailureReason() const;
201 
202   // Returns the full vector of reasons.
reasons()203   const std::vector<Reason>& reasons() const { return reasons_; }
204 
205   // Returns whether or not the chain of reasons has toggled.
toggled()206   bool toggled() const { return toggled_; }
207 
208   // Populates the provided "TabManager.LifecycleStateChange" UKM builder with
209   // information from this object.
210   void Populate(ukm::builders::TabManager_LifecycleStateChange* ukm) const;
211 
212   // Returns a collection of failure reason strings, from most important failure
213   // reason to least important. This is empty if the outcome is positive, and
214   // will only be populated with failure reasons that are not overridden by any
215   // success reasons.
216   std::vector<std::string> GetFailureReasonStrings() const;
217 
218   void Clear();
219 
220  private:
221   bool CheckIfToggled();
222 
223   // This is true if the vector of success reasons has "toggled" from all
224   // failures to some successes, or vice versa. Continuing to collect additional
225   // reasons after this toggle isn't very informative.
226   bool toggled_;
227   std::vector<Reason> reasons_;
228 
229   DISALLOW_COPY_AND_ASSIGN(DecisionDetails);
230 };
231 
232 }  // namespace resource_coordinator
233 
234 #endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_DECISION_DETAILS_H_
235