File: download_warning_desktop_hats_utils.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (312 lines) | stat: -rw-r--r-- 14,011 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_DOWNLOAD_DOWNLOAD_WARNING_DESKTOP_HATS_UTILS_H_
#define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_WARNING_DESKTOP_HATS_UTILS_H_

#include <map>
#include <optional>
#include <string>
#include <vector>

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "chrome/browser/ui/hats/hats_service.h"
#include "components/download/public/common/download_item.h"

// Type of survey (corresponding to a trigger condition) that should be shown.
// Do not renumber.
enum class DownloadWarningHatsType {
  // Kept a suspicious file from the download bubble.
  kDownloadBubbleBypass = 0,
  // Deleted a warned file from the download bubble.
  kDownloadBubbleHeed = 1,
  // User did not act on warning despite being active.
  kDownloadBubbleIgnore = 2,
  // Kept a suspicious/dangerous file from the downloads page or the download
  // danger prompt.
  kDownloadsPageBypass = 3,
  // Deleted a warned file from the downloads page.
  kDownloadsPageHeed = 4,
  // Navigated away from or closed the downloads page without acting on the
  // warning.
  kDownloadsPageIgnore = 5,
  kMaxValue = kDownloadsPageIgnore,
};

// Stores the PSD for the download warning HaTS survey.
class DownloadWarningHatsProductSpecificData {
 public:
  // A namespace for the field labels, which are presented to the user in the
  // privacy and transparency UI.
  struct Fields {
    // Bits data fields:

    // Whether the download bubble partial view was enabled.
    static constexpr char kPartialViewEnabled[] =
        "Automatic download bubble enabled (\"Show downloads when they're "
        "done\" setting)";
    // Whether the user interacted with the partial view (true) or main view
    // (false). Only logged for download bubble triggers. Must be added after
    // Create(). Defaults to false if no value is added.
    static constexpr char kPartialViewInteraction[] =
        "User interacted with automatic download bubble";
    // Whether the download was initiated by a user gesture.
    static constexpr char kUserGesture[] = "Download initiated by user gesture";

    // String data fields:

    // The outcome of the download: "Bypassed warning", "Heeded warning", or
    // "Ignored warning".
    static constexpr char kOutcome[] = "Outcome";
    // The UI surface on which the outcome occurred: "Download bubble" or
    // "Downloads page".
    static constexpr char kSurface[] = "UI surface";
    // A combination of danger type of the download and tailored verdict (if
    // any; see csd.proto).
    static constexpr char kDangerType[] = "Danger type";
    // Danger UI pattern: "Dangerous", "Suspicious", or "None".
    static constexpr char kWarningType[] = "Warning type";
    // What the user's Safe Browsing setting is.
    static constexpr char kSafeBrowsingState[] = "Safe Browsing state";
    // Client channel, "Stable", "Beta", etc.
    static constexpr char kChannel[] = "Chrome channel";
    // How many total warned downloads were on the downloads page. Only logged
    // for downloads page triggers. Must be added after Create(). Defaults to
    // a placeholder if no value is added.
    static constexpr char kNumPageWarnings[] =
        "Number of warnings on downloads page";
    // The user's interactions with this specific warning, as a
    // comma-separated string of timestamped actions.
    // Only logged for users with Enhanced Safe Browsing.
    // Other users will have a placeholder value.
    static constexpr char kWarningInteractions[] =
        "User interactions with this download warning with timestamps (ms)";
    // The time elapsed since the download started, in seconds.
    static constexpr char kSecondsSinceDownloadStarted[] =
        "Time since download started (s)";
    // The time elapsed since the warning was shown, in seconds.
    static constexpr char kSecondsSinceWarningShown[] =
        "Time since warning shown (s)";
    // URLs of the downloaded file and referring page.
    // These are only logged for users with Safe Browsing enabled.
    // Other users will have a placeholder value.
    static constexpr char kUrlDownload[] = "Download URL";
    static constexpr char kUrlReferrer[] = "Referrer URL";
    // Download filename.
    // Only logged for users with Safe Browsing enabled.
    // Other users will have a placeholder value.
    static constexpr char kFilename[] = "Download filename";
    // Timeout used for "ignore" survey trigger. Only added for a download
    // bubble warning that was ignored.
    static constexpr char kIgnoreTimeoutSeconds[] =
        "Threshold for ignored warning (s)";
  };

  // Returns a ProductSpecificData with some basic PSD values filled in.
  // This only fills in certain fields derivable from the DownloadItem and the
  // profile. Other fields may need to be supplied by the caller directly.
  // Note: Caller must ensure that the DownloadItem is dangerous and not done.
  static DownloadWarningHatsProductSpecificData Create(
      DownloadWarningHatsType survey_type,
      download::DownloadItem* download_item);

  // Methods to add PSD fields that the caller supplies:
  // Must be called for any downloads page trigger before sending the survey.
  void AddNumPageWarnings(int num);
  // Must be called for any download bubble trigger before sending the survey.
  void AddPartialViewInteraction(bool partial_view_interaction);

  // Returns the names of the PSD fields, used in creating the survey configs.
  // These are CHECKed against the fields ultimately passed to the HaTS
  // service.
  static std::vector<std::string> GetBitsDataFields(
      DownloadWarningHatsType survey_type);
  static std::vector<std::string> GetStringDataFields(
      DownloadWarningHatsType survey_type);

  // Note that the applicable fields must all be present before using this
  // object, so AddNumPageWarnings or AddPartialViewInteraction must have been
  // called, otherwise using this object will cause a CHECK failure.
  const SurveyBitsData& bits_data() const { return bits_data_; }
  const SurveyStringData& string_data() const { return string_data_; }

  DownloadWarningHatsType survey_type() const { return survey_type_; }

  DownloadWarningHatsProductSpecificData(
      const DownloadWarningHatsProductSpecificData&);
  DownloadWarningHatsProductSpecificData& operator=(
      const DownloadWarningHatsProductSpecificData&);
  DownloadWarningHatsProductSpecificData(
      DownloadWarningHatsProductSpecificData&&);
  DownloadWarningHatsProductSpecificData& operator=(
      DownloadWarningHatsProductSpecificData&&);

  ~DownloadWarningHatsProductSpecificData();

 private:
  explicit DownloadWarningHatsProductSpecificData(
      DownloadWarningHatsType survey_type);

  DownloadWarningHatsType survey_type_;

  SurveyBitsData bits_data_;
  SurveyStringData string_data_;
};

// A class that manages delayed download warning HaTS survey tasks. It can be
// given a DownloadItem to launch a survey for in the future after some delay,
// and these tasks can be canceled explicitly or automatically (in case of
// the DownloadItem getting destroyed or becoming ineligible for a HaTS survey).
// It also records the last time the user interacted with the browser, and the
// survey is withheld if the user was (presumably) idle for the entire period of
// the delay. (Client should inform this object of browser activity.)
// Note: Currently this is only used for download bubble ignore triggers.
class DelayedDownloadWarningHatsLauncher
    : public download::DownloadItem::Observer {
 public:
  // A callback that allows the completion of the PSD (addition of post-Create()
  // fields).
  using PsdCompleter =
      base::RepeatingCallback<void(DownloadWarningHatsProductSpecificData&)>;

  // Bundles the objects used to control the task and its lifetime. Can only be
  // used once per instance. To cancel, delete this object. The `download`
  // and `hats_launcher` must outlive this.
  class Task {
   public:
    // Creates and schedules the task.
    Task(DelayedDownloadWarningHatsLauncher& hats_launcher,
         download::DownloadItem* download,
         base::OnceClosure task,
         base::TimeDelta delay);

    Task(const Task&) = delete;
    Task& operator=(const Task&) = delete;

    ~Task();

   private:
    void RunTask();

    // Controls the observation of the download by the parent object.
    base::ScopedObservation<download::DownloadItem,
                            DelayedDownloadWarningHatsLauncher>
        observation_;
    // Task to show the survey.
    base::OnceClosure task_;
    // Used to cancel the scheduled task.
    base::WeakPtrFactory<Task> weak_factory_{this};
  };

  // `profile` is the profile for which the HaTS surveys should be shown.
  // `delay` is the delay that applies to all surveys launched by this object.
  // `psd_completer` will be called with the product-specific data right before
  // attempting to launch each survey.
  DelayedDownloadWarningHatsLauncher(
      Profile* profile,
      base::TimeDelta delay,
      PsdCompleter psd_completer = base::DoNothing());

  DelayedDownloadWarningHatsLauncher(
      const DelayedDownloadWarningHatsLauncher&) = delete;
  DelayedDownloadWarningHatsLauncher& operator=(
      const DelayedDownloadWarningHatsLauncher&) = delete;

  ~DelayedDownloadWarningHatsLauncher() override;

  // download::DownloadItem::Observer:
  // This object is an observer of every download with an entry in `tasks_`.
  void OnDownloadUpdated(download::DownloadItem* download) override;
  void OnDownloadDestroyed(download::DownloadItem* download) override;

  // Updates the last_activity_ time.
  void RecordBrowserActivity();

  // Schedules a survey to be shown after the delay, if the user has been active
  // in the meantime. Does nothing if a task already exists for the download.
  // Does nothing if the download is not eligible when scheduling. (The
  // scheduled task will also fizzle if the download is not eligible upon
  // execution.) Returns whether task was scheduled.
  bool TryScheduleTask(DownloadWarningHatsType survey_type,
                       download::DownloadItem* download);

  // Cancels and removes the task for `download` from the map. Is a no-op if the
  // download is not in the map.
  void RemoveTaskIfAny(download::DownloadItem* download);

 private:
  // Address of a DownloadItem, derived from a DownloadItem*, but it is not to
  // be dereferenced.
  using TaskKey = std::uintptr_t;

  // Returns the task key to be used for the download, which is just the
  // address.
  TaskKey GetTaskKey(download::DownloadItem* download);

  // Cancels and removes the task from the map. Is a no-op if the key is not in
  // the map. In particular, if the DownloadItem has been freed, its key will
  // not be found in the map, as guaranteed by the DownloadItem::Observer
  // mechanism.
  void RemoveTaskByKeyIfAny(TaskKey key);

  // Launches the actual survey, if all preconditions are met.
  void MaybeLaunchSurveyNow(DownloadWarningHatsType survey_type,
                            download::DownloadItem* download);

  // Returns a callback that is called to clean up after the survey succeeds or
  // fails.
  base::OnceClosure MakeSurveyDoneCallback(download::DownloadItem* download);

  // Whether the user was active in the browser during the delay period.
  bool WasUserActive() const;

  // Profile to show the surveys for. Must outlive this.
  const raw_ptr<Profile> profile_;
  // How long to wait before launching the survey.
  const base::TimeDelta delay_;
  // Time of the most recent user interaction with the browser.
  base::Time last_activity_;
  // Maps DownloadItem addresses to their corresponding pending tasks.
  std::map<TaskKey, Task> tasks_;
  // Callback that is run to stamp the PSD with any additional fields right
  // before attempting to launch the survey.
  PsdCompleter psd_completer_;
  // Needed because the cleanup callback produced by MakeSurveyDoneCallback
  // may outlive this.
  base::WeakPtrFactory<DelayedDownloadWarningHatsLauncher> weak_factory_{this};
};

// Returns if the download item is dangerous and not-done.
bool CanShowDownloadWarningHatsSurvey(download::DownloadItem* download);

// Returns the HaTS trigger string for the survey_type, if the user is eligible
// for that type of survey (according to the fieldtrial config). If the user
// is not eligible, or there is a configuration error, this returns nullopt.
std::optional<std::string> MaybeGetDownloadWarningHatsTrigger(
    DownloadWarningHatsType survey_type);

// Returns the time delay used for kDownloadBubbleIgnore triggers.
base::TimeDelta GetIgnoreDownloadBubbleWarningDelay();

// Launches a HaTS survey using the desktop HaTS service, if all preconditions
// are met. The `psd` object encapsulates the data for the survey, including the
// triggering survey type. `profile` is the profile for which the survey should
// be launched. Note that it is potentially different from the profile under
// which the download was made (in the case of OTR profiles which may care about
// downloads made in their original profile), so it needs to be passed and
// cannot be derived from the DownloadItem. However, when `profile` is OTR and
// differs from the DownloadItem's Profile, a HaTS survey won't be shown anyway
// because HaTS surveys are not shown for OTR profiles, so everything is fine
// as long as we pass the correct `profile` for which we are attempting to
// launch the survey.
void MaybeLaunchDownloadWarningHatsSurvey(
    Profile* profile,
    const DownloadWarningHatsProductSpecificData& psd,
    base::OnceClosure success_callback = base::DoNothing(),
    base::OnceClosure failure_callback = base::DoNothing());

#endif  // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_WARNING_DESKTOP_HATS_UTILS_H_