File: hats_next_web_dialog.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 (201 lines) | stat: -rw-r--r-- 8,315 bytes parent folder | download | duplicates (5)
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
// Copyright 2020 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_UI_VIEWS_HATS_HATS_NEXT_WEB_DIALOG_H_
#define CHROME_BROWSER_UI_VIEWS_HATS_HATS_NEXT_WEB_DIALOG_H_

#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/profiles/profile_observer.h"
#include "chrome/browser/ui/hats/hats_service.h"
#include "content/public/browser/web_contents_observer.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/controls/webview/web_dialog_view.h"
#include "ui/views/window/dialog_delegate.h"
#include "ui/web_dialogs/web_dialog_delegate.h"

class Browser;
class Profile;

namespace views {
class Widget;
}  // namespace views

// A dialog for displaying a Happiness Tracking Survey (HaTS) NEXT survey to
// the user. The dialog presents a WebContents which connects to a publicly
// accessible, Chrome specific, webpage which is responsible for displaying the
// survey to users. The webpage has additional logic to provide information to
// this dialog via URL fragments, such as whether a survey is ready to be shown
// to the user.
class HatsNextWebDialog : public views::BubbleDialogDelegateView,
                          public content::WebContentsDelegate,
                          public ProfileObserver {
  METADATA_HEADER(HatsNextWebDialog, views::BubbleDialogDelegateView)

 public:
  HatsNextWebDialog(Browser* browser,
                    const std::string& trigger_id,
                    const std::optional<std::string>& hats_histogram_name,
                    const std::optional<uint64_t> hats_survey_ukm_id,
                    base::OnceClosure success_callback,
                    base::OnceClosure failure_callback,
                    const SurveyBitsData& product_specific_bits_data,
                    const SurveyStringData& product_specific_string_data);
  ~HatsNextWebDialog() override;
  HatsNextWebDialog(const HatsNextWebDialog&) = delete;
  HatsNextWebDialog& operator=(const HatsNextWebDialog&) = delete;

  // BubbleDialogDelegateView:
  gfx::Size CalculatePreferredSize(
      const views::SizeBounds& available_size) const override;

  // ProfileObserver:
  void OnProfileWillBeDestroyed(Profile* profile) override;

  std::optional<std::string> GetHistogramName();
  void OnSurveyLoaded();
  void OnSurveyCompleted();
  void OnSurveyClosed();
  void OnSurveyQuestionAnswered(const std::string& state);

  static bool ParseSurveyQuestionAnswer(const std::string& input,
                                        int* question,
                                        std::vector<int>* answers);

  static uint64_t EncodeUkmQuestionAnswers(
      const std::vector<int>& question_answers);

  enum class SurveyHistogramEnumeration {
    kSurveyLoadedEnumeration = 2,
    kSurveyCompletedEnumeration = 3,
    kSurveyQuestionAnswerParseError = 8,
    kSurveyUnknownState = 9
  };

 protected:
  friend class MockHatsNextWebDialog;
  FRIEND_TEST_ALL_PREFIXES(HatsNextWebDialogBrowserTest, SurveyLoaded);
  FRIEND_TEST_ALL_PREFIXES(HatsNextWebDialogBrowserTest,
                           SurveyLoadedWithHistogramName);
  FRIEND_TEST_ALL_PREFIXES(HatsNextWebDialogBrowserTest,
                           SurveyQuestionAnsweredFirstQuestionHistograms);
  FRIEND_TEST_ALL_PREFIXES(
      HatsNextWebDialogBrowserTest,
      SurveyQuestionAnsweredSingleSelectQuestionHistograms);
  FRIEND_TEST_ALL_PREFIXES(
      HatsNextWebDialogBrowserTest,
      SurveyQuestionAnsweredMultipleSelectQuestionHistograms);
  FRIEND_TEST_ALL_PREFIXES(HatsNextWebDialogBrowserTest,
                           SurveyQuestionAnsweredMultipleQuestionsHistograms);
  FRIEND_TEST_ALL_PREFIXES(HatsNextWebDialogBrowserTest,
                           SurveyLoadedWithHistogramName);
  FRIEND_TEST_ALL_PREFIXES(HatsNextWebDialogBrowserTest,
                           SurveyQuestionAnsweredMultipleQuestions);
  FRIEND_TEST_ALL_PREFIXES(HatsNextWebDialogBrowserTest, DialogResize);
  FRIEND_TEST_ALL_PREFIXES(HatsNextWebDialogBrowserTest, MaximumSize);
  FRIEND_TEST_ALL_PREFIXES(HatsNextWebDialogBrowserTest, ZoomLevel);

  HatsNextWebDialog(Browser* browser,
                    const std::string& trigger_id,
                    const std::optional<std::string>& hats_histogram_name,
                    const std::optional<uint64_t> hats_survey_ukm_id,
                    const GURL& hats_survey_url_,
                    const base::TimeDelta& timeout,
                    base::OnceClosure success_callback,
                    base::OnceClosure failure_callback,
                    const SurveyBitsData& product_specific_bits_data,
                    const SurveyStringData& product_specific_string_data);

  class HatsWebView;

  // Returns the URL for the HaTS wrapper website, with the appropriate query
  // parameters to request the triggered survey.
  GURL GetParameterizedHatsURL() const;

  // Called by |loading_timer_| after |timeout_| time has passed without getting
  // an appropriate response from the HaTS service after creating the widget.
  void LoadTimedOut();

  // Fired by the observer when the survey page has pushed state to the window
  // via URL fragments.
  void OnSurveyStateUpdateReceived(std::string state);

  // Provides mechanism to override URL requested by the dialog. Must be called
  // before CreateWebDialog() to take effect.
  void SetHatsSurveyURLforTesting(GURL url);

  // Displays the widget to the user, called when the dialog believes a survey
  // ready for display. Virtual to allow mocking in tests.
  virtual void ShowWidget();

  // Called by the dialog to close the widget due to timeout or the survey being
  // closed. After the widget is closed, both the widget and this class are
  // destroyed. Virtual to allow mocking in tests.
  virtual void CloseWidget();

  // Returns whether the dialog is still waiting for the survey to load.
  bool IsWaitingForSurveyForTesting();

  // Returns the UMA histogram bucket for a question/answer combination.
  int GetHistogramBucket(int question, int answer);

 private:
  // A timer to prevent unresponsive loading of survey dialog.
  base::OneShotTimer loading_timer_;

  // The off-the-record profile used for browsing to the Chrome HaTS webpage.
  raw_ptr<Profile> otr_profile_;

  raw_ptr<Browser> browser_;

  // The HaTS Next survey trigger ID that is provided to the HaTS webpage.
  const std::string trigger_id_;

  // The UMA histogram name associated with the HaTS survey.
  const std::optional<std::string> hats_histogram_name_;

  // The UKM id associated with the HaTS survey.
  const std::optional<uint64_t> hats_survey_ukm_id_;

  // Whether the web contents has communicated a loaded state.
  bool received_survey_loaded_ = false;

  // Whether the request timed out. The async timeout callback loading_timer_
  // may be run just before a navigation-triggered
  // `OnSurveyStateUpdateReceived`. A no longer running timer can't be used for
  // synchronization, because it can't differentiate between a stopped and a ran
  // callback.
  bool load_timed_out_ = false;

  // The maximum size of the dialog should never exceed the dummy window size
  // provided to the HaTS library by the wrapper website. This is defined
  // in the website source at google3/chrome/hats/website/www/index.html. The
  // minimum size is set at an arbitrary non-zero size as creation of zero sized
  // windows is disallowed on OSX.
  static constexpr gfx::Size kMinSize = gfx::Size(10, 10);
  static constexpr gfx::Size kMaxSize = gfx::Size(800, 600);

  raw_ptr<views::WebView> web_view_ = nullptr;
  raw_ptr<views::Widget> widget_ = nullptr;

  GURL hats_survey_url_;

  base::TimeDelta timeout_;

  base::OnceClosure success_callback_;
  base::OnceClosure failure_callback_;

  SurveyBitsData product_specific_bits_data_;
  SurveyStringData product_specific_string_data_;

  ukm::builders::Feedback_HappinessTrackingSurvey ukm_hats_builder_;

  base::WeakPtrFactory<HatsNextWebDialog> weak_factory_{this};
};

#endif  // CHROME_BROWSER_UI_VIEWS_HATS_HATS_NEXT_WEB_DIALOG_H_