File: content_analysis_dialog_controller.h

package info (click to toggle)
chromium 138.0.7204.183-1~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-proposed-updates
  • size: 6,080,960 kB
  • sloc: cpp: 34,937,079; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,954; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,811; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (380 lines) | stat: -rw-r--r-- 15,244 bytes parent folder | download | duplicates (2)
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
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
// Copyright 2019 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_ENTERPRISE_CONNECTORS_ANALYSIS_CONTENT_ANALYSIS_DIALOG_CONTROLLER_H_
#define CHROME_BROWSER_ENTERPRISE_CONNECTORS_ANALYSIS_CONTENT_ANALYSIS_DIALOG_CONTROLLER_H_

#include <cstddef>
#include <memory>

#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "chrome/browser/enterprise/connectors/analysis/content_analysis_delegate_base.h"
#include "chrome/browser/enterprise/connectors/common.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h"
#include "components/download/public/common/download_item.h"
#include "content/public/browser/web_contents_observer.h"
#include "ui/base/mojom/ui_base_types.mojom-shared.h"
#include "ui/views/animation/bounds_animator.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/styled_label.h"
#include "ui/views/controls/textfield/textfield_controller.h"
#include "ui/views/window/dialog_delegate.h"

namespace content {
class WebContents;
}  // namespace content

namespace views {
class BoxLayoutView;
class ImageView;
class Label;
class Link;
class TableLayoutView;
class Textarea;
class Throbber;
class Widget;
}  // namespace views

namespace enterprise_connectors {
class DeepScanningTopImageView;
class DeepScanningSideIconImageView;
class DeepScanningSideIconSpinnerView;

// Dialog shown for Deep Scanning to offer the possibility of cancelling the
// upload to the user.
class ContentAnalysisDialogController
    : public views::DialogDelegate,
      public content::WebContentsObserver,
      public views::TextfieldController,
      public download::DownloadItem::Observer {
 public:
  // TestObserver should be implemented by tests that need to track when certain
  // ContentAnalysisDialogController functions are called. The test can add
  // itself as an observer by using SetObserverForTesting.
  class TestObserver {
   public:
    virtual ~TestObserver() = default;

    // Called at the start of ContentAnalysisDialogController's constructor.
    // `dialog` is a pointer to the newly constructed
    // ContentAnalysisDialogController and should be kept in memory by the test
    // in order to validate its state.
    virtual void ConstructorCalled(ContentAnalysisDialogController* dialog,
                                   base::TimeTicks timestamp) {}

    // Called at the end of ContentAnalysisDialogController::Show. `timestamp`
    // is the time used by ContentAnalysisDialogController to decide whether the
    // pending state has been shown for long enough. The test can keep this time
    // in memory and validate the pending time was sufficient in DialogUpdated.
    virtual void ViewsFirstShown(ContentAnalysisDialogController* dialog,
                                 base::TimeTicks timestamp) {}

    // Called at the end of ContentAnalysisDialogController::UpdateDialog.
    // `result` is the value that UpdatedDialog used to transition from the
    // pending state to the success/failure/warning state.
    virtual void DialogUpdated(ContentAnalysisDialogController* dialog,
                               FinalContentAnalysisResult result) {}

    // Called at the start of CancelDialogAndDelete(). `dialog` is a pointer
    // that will soon be destructed. Along with `result`, it is used by the test
    // to validate the dialog should be canceled or deleted.
    virtual void CancelDialogAndDeleteCalled(
        ContentAnalysisDialogController* dialog,
        FinalContentAnalysisResult result) {}

    // Called at the end of ContentAnalysisDialogController's destructor.
    // `dialog` is a pointer to the ContentAnalysisDialogController being
    // destructed. It can be used to compare it to the pointer obtained from
    // ConstructorCalled to ensure which view is being destroyed.
    virtual void DestructorCalled(ContentAnalysisDialogController* dialog) {}
  };

  static void SetObserverForTesting(TestObserver* observer);

  static void SetMinimumPendingDialogTimeForTesting(base::TimeDelta delta);
  static void SetSuccessDialogTimeoutForTesting(base::TimeDelta delta);
  static void SetShowDialogDelayForTesting(base::TimeDelta delta);

  static base::TimeDelta GetMinimumPendingDialogTime();
  static base::TimeDelta GetSuccessDialogTimeout();
  static base::TimeDelta ShowDialogDelay();

  ContentAnalysisDialogController(
      std::unique_ptr<ContentAnalysisDelegateBase> delegate,
      bool is_cloud,
      content::WebContents* web_contents,
      safe_browsing::DeepScanAccessPoint access_point,
      int files_count,
      FinalContentAnalysisResult final_result =
          FinalContentAnalysisResult::SUCCESS,
      download::DownloadItem* download_item = nullptr);

  // views::DialogDelegate:
  std::u16string GetWindowTitle() const override;
  bool ShouldShowCloseButton() const override;
  views::View* GetContentsView() override;
  views::Widget* GetWidget() override;
  const views::Widget* GetWidget() const override;
  ui::mojom::ModalType GetModalType() const override;

  // content::WebContentsObserver:
  void WebContentsDestroyed() override;
  void PrimaryPageChanged(content::Page& page) override;

  // Updates the dialog with the result, and simply delete it from memory if
  // nothing should be shown.
  void ShowResult(FinalContentAnalysisResult result);

  // Accessors to simplify `dialog_state_` checking.
  inline bool is_success() const { return dialog_state_ == State::SUCCESS; }

  inline bool is_failure() const { return dialog_state_ == State::FAILURE; }

  inline bool is_warning() const { return dialog_state_ == State::WARNING; }

  inline bool is_result() const { return !is_pending(); }

  inline bool is_pending() const { return dialog_state_ == State::PENDING; }

  inline bool is_cloud() const { return is_cloud_; }

  bool has_custom_message() const {
    return delegate_base_->GetCustomMessage().has_value();
  }

  bool has_learn_more_url() const {
    return delegate_base_->GetCustomLearnMoreUrl().has_value();
  }

  bool has_custom_message_ranges() const {
    return delegate_base_->GetCustomRuleMessageRanges().has_value();
  }

  bool bypass_requires_justification() const {
    return delegate_base_->BypassRequiresJustification();
  }

  // Cancels the dialog an schedules it for deletion if visible, otherwise
  // simply deletes it soon.
  void CancelDialogAndDelete();

  // Returns the side image's logo color depending on `dialog_state_`.
  ui::ColorId GetSideImageLogoColor() const;

  // Returns the side image's background circle color depending on
  // `dialog_state_`.
  ui::ColorId GetSideImageBackgroundColor() const;

  // Returns true if should use dark version of top image.
  bool ShouldUseDarkTopImage() const;

  // Returns the appropriate top image depending on `dialog_state_`.
  ui::ImageModel GetTopImage() const;

  // Accessors used to validate the views in tests.
  views::ImageView* GetTopImageForTesting() const;
  views::Throbber* GetSideIconSpinnerForTesting() const;
  views::StyledLabel* GetMessageForTesting() const;
  views::Link* GetLearnMoreLinkForTesting() const;
  views::Label* GetBypassJustificationLabelForTesting() const;
  views::Textarea* GetBypassJustificationTextareaForTesting() const;
  views::Label* GetJustificationTextLengthForTesting() const;

 private:
  // Friend the unit test class for this so it can call the private dtor.
  friend class ContentAnalysisDialogPlainTest;

  // Friend to allow use of TaskRunner::DeleteSoon().
  friend class base::DeleteHelper<ContentAnalysisDialogController>;

  // Enum used to represent what the dialog is currently showing.
  enum class State {
    // The dialog is shown with an explanation that the scan is being performed
    // and that the result is pending.
    PENDING,

    // The dialog is shown with a short message indicating that the scan was a
    // success and that the user may proceed with their upload, drag-and-drop or
    // paste.
    SUCCESS,

    // The dialog is shown with a message indicating that the scan was a failure
    // and that the user may not proceed with their upload, drag-and-drop or
    // paste.
    FAILURE,

    // The dialog is shown with a message indicating that the scan was a
    // failure, but that the user may proceed with their upload, drag-and-drop
    // or paste if they want to.
    WARNING,
  };

  ~ContentAnalysisDialogController() override;

  // Callback function of delayed timer to make the dialog visible.
  void ShowDialogNow();

  void UpdateStateFromFinalResult(FinalContentAnalysisResult final_result);

  // Updates the views in the dialog to put them in the correct state for
  // `dialog_state_`. This doesn't trigger the same events/resizes as
  // UpdateDialog(), and doesn't require the presence of a widget. This is safe
  // to use in the first GetContentsView() call, before the dialog is shown.
  void UpdateViews();

  // Update the UI depending on `dialog_state_`. This also triggers resizes and
  // fires some events. It's meant to be called to update the entire dialog when
  // it's already showing.
  // This function can only be called after the dialog widget is initialized.
  void UpdateDialog();

  // Helper function to determine whether dialog should be shown immediately.
  bool ShouldShowDialogNow();

  // Resizes the already shown dialog to accommodate changes in its content.
  void Resize(int height_to_add);

  // Setup the appropriate buttons depending on `dialog_state_`.
  void SetupButtons();

  // Returns a newly created side icon.
  std::unique_ptr<views::View> CreateSideIcon();

  // Returns the appropriate dialog message depending on `dialog_state_`.
  std::u16string GetDialogMessage() const;

  // Returns the text for the Cancel button depending on `dialog_state_`.
  std::u16string GetCancelButtonText() const;

  // Returns the text for the Ok button for the warning case.
  std::u16string GetBypassWarningButtonText() const;

  // Returns the appropriate top image ID depending on `dialog_state_`.
  int GetTopImageId(bool use_dark) const;

  // Returns the appropriate pending message depending on `files_count_`.
  std::u16string GetPendingMessage() const;

  // Returns the appropriate failure message depending on `final_result_` and
  // `files_count_`.
  std::u16string GetFailureMessage() const;

  // Returns the appropriate warning message depending on `files_count_`.
  std::u16string GetWarningMessage() const;

  // Returns the appropriate success message depending on `files_count_`.
  std::u16string GetSuccessMessage() const;

  std::u16string GetCustomMessage() const;

  // Helper methods to add views to `contents_view_` and `contents_layout_` that
  // are not used for every state of the dialog.
  void AddLearnMoreLinkToDialog();
  void AddJustificationTextLabelToDialog();
  void AddJustificationTextAreaToDialog();
  void AddJustificationTextLengthToDialog();

  // Helper that indicates if the dialog corresponds to a print scan.
  bool is_print_scan() const;

  // Helper methods to get the admin message shown in dialog.
  void AddLinksToDialogMessage();
  void UpdateDialogMessage(std::u16string new_message);

  void AcceptButtonCallback();
  void CancelButtonCallback();
  void LearnMoreLinkClickedCallback(const ui::Event& event);

  // This callback used by DialogDelegate::SetCancelCallback and is used to
  // ensure the auto-closing success dialog handles focus correctly.
  void SuccessCallback();

  // views::TextfieldController:
  void ContentsChanged(views::Textfield* sender,
                       const std::u16string& new_contents) override;

  // download::DownloadItem::Observer:
  void OnDownloadUpdated(download::DownloadItem* download) override;
  void OnDownloadOpened(download::DownloadItem* download) override;
  void OnDownloadDestroyed(download::DownloadItem* download) override;

  // Several conditions can lead to the dialog being no longer useful, so this
  // method is shared for those different conditions to close the dialog.
  void CancelDialogWithoutCallback();

  std::unique_ptr<ContentAnalysisDelegateBase> delegate_base_;

  // Views above the buttons. `contents_view_` owns every other view.
  raw_ptr<views::BoxLayoutView> contents_view_ = nullptr;
  raw_ptr<DeepScanningTopImageView> image_ = nullptr;
  raw_ptr<DeepScanningSideIconImageView> side_icon_image_ = nullptr;
  raw_ptr<DeepScanningSideIconSpinnerView> side_icon_spinner_ = nullptr;
  raw_ptr<views::StyledLabel> message_ = nullptr;

  // The following views are also owned by `contents_view_`, but remain nullptr
  // if they aren't required to be initialized.
  raw_ptr<views::Link> learn_more_link_ = nullptr;
  raw_ptr<views::Label> justification_text_label_ = nullptr;
  raw_ptr<views::Textarea> bypass_justification_ = nullptr;
  raw_ptr<views::Label> bypass_justification_text_length_ = nullptr;

  // Table layout owned by `contents_view_`.
  raw_ptr<views::TableLayoutView> contents_layout_ = nullptr;

  base::TimeTicks first_shown_timestamp_;

  // Used to show the appropriate dialog depending on the scan's status.
  State dialog_state_ = State::PENDING;

  // Used to show the appropriate message.
  FinalContentAnalysisResult final_result_;

  // Used to animate dialog height changes.
  std::unique_ptr<views::BoundsAnimator> bounds_animator_;

  // The access point that caused this dialog to open. This changes what text
  // and top image are shown to the user.
  safe_browsing::DeepScanAccessPoint access_point_;

  // Indicates whether the scan being done is for files (files_count_>0) or for
  // text (files_count_==0). This changes what text and top image are shown to
  // the user.
  int files_count_;

  // `DownloadItem` for dialogs corresponding to a download with a reviewable
  // verdict. nullptr otherwise.
  raw_ptr<download::DownloadItem> download_item_ = nullptr;

  // Set to true once the dialog is either accepted or cancelled by the user.
  // This is used to decide whether the dialog should go away without user input
  // or not.
  bool accepted_or_cancelled_ = false;

  // True when performing a cloud-based content analysis, false when performing
  // a locally based content analysis.
  bool is_cloud_ = true;

  // Set to true once `DeleteSoon()` is called in `CancelDialogAndDelete()`.
  // This is used by other pending tasks, such as `ShowDialogNow()` to do
  // nothing if the dialog has been scheduled for deletion.
  bool will_be_deleted_soon_ = false;

  // If input events for our `WebContents` have been ignored, then this is the
  // closure to re-enable them.
  std::optional<content::WebContents::ScopedIgnoreInputEvents>
      scoped_ignore_input_events_;

  // A reference to the top level web contents of the tab whose content is
  // being analyzed.  Input events of this contents are ignored for the life
  // time of the dialog.
  base::WeakPtr<content::WebContents> top_level_contents_;

  base::WeakPtrFactory<ContentAnalysisDialogController> weak_ptr_factory_{this};
};

}  // namespace enterprise_connectors

#endif  // CHROME_BROWSER_ENTERPRISE_CONNECTORS_ANALYSIS_CONTENT_ANALYSIS_DIALOG_CONTROLLER_H_