File: popup_view_views.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 (327 lines) | stat: -rw-r--r-- 14,369 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
// Copyright 2017 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_AUTOFILL_POPUP_POPUP_VIEW_VIEWS_H_
#define CHROME_BROWSER_UI_VIEWS_AUTOFILL_POPUP_POPUP_VIEW_VIEWS_H_

#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <variant>
#include <vector>

#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "chrome/browser/ui/autofill/autofill_popup_view.h"
#include "chrome/browser/ui/views/autofill/popup/password_favicon_loader.h"
#include "chrome/browser/ui/views/autofill/popup/popup_base_view.h"
#include "chrome/browser/ui/views/autofill/popup/popup_row_view.h"
#include "chrome/browser/ui/views/autofill/popup/popup_search_bar_view.h"
#include "components/autofill/core/common/aliases.h"
#include "components/input/native_web_keyboard_event.h"
#include "ui/accessibility/ax_action_data.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/events/event.h"
#include "ui/views/widget/widget.h"

namespace views {
class BoxLayoutView;
class ScrollView;
}  // namespace views

namespace autofill {

class AutofillPopupController;
class AutofillSuggestionController;
class PopupSeparatorView;
class PopupTitleView;
class PopupWarningView;

// Sub-popups and their parent popups are connected by providing children
// with links to their parents. This interface defines the API exposed by
// these links.
class ExpandablePopupParentView {
 private:
  friend class PopupViewViews;

  // Callbacks to notify the parent of the children about hover state changes.
  // The calls are also propagated to grandparents, so that, no matter how
  // long the chain of sub-popups is, lower level popups know the hover
  // status in (grand)children.
  virtual void OnMouseEnteredInChildren() = 0;
  virtual void OnMouseExitedInChildren() = 0;
};

// Views implementation for the autofill and password suggestion.
class PopupViewViews : public PopupBaseView,
                       public AutofillPopupView,
                       public PopupRowView::SelectionDelegate,
                       public ExpandablePopupParentView,
                       public PopupSearchBarView::Delegate {
  METADATA_HEADER(PopupViewViews, PopupBaseView)

 public:
  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(
      kAutofillBnplAffirmOrZipSuggestionElementId);
  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kAutofillCreditCardBenefitElementId);
  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(
      kAutofillCreditCardSuggestionEntryElementId);
  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kAutofillAiOptInIphElementId);
  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(
      kAutofillStandaloneCvcSuggestionElementId);
  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kAutofillSuggestionElementId);
  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kAutofillHomeWorkSuggestionElementId);
  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kAutofillEnableLoyaltyCardsElementId);

  using RowPointer = std::variant<PopupRowView*,
                                  PopupSeparatorView*,
                                  PopupTitleView*,
                                  PopupWarningView*>;

  // The time it takes for a selected cell to open a sub-popup if it has one.
  static constexpr base::TimeDelta kMouseOpenSubPopupDelay =
      base::Milliseconds(250);
  static constexpr base::TimeDelta kNonMouseOpenSubPopupDelay =
      kMouseOpenSubPopupDelay / 10;

  // The delay for closing the sub-popup after having no cell selected,
  // sub-popup cells are also taken into account.
  static constexpr base::TimeDelta kNoSelectionHideSubPopupDelay =
      base::Milliseconds(2500);

  // Constructor for creating sub-popups.
  PopupViewViews(base::WeakPtr<AutofillPopupController> controller,
                 base::WeakPtr<ExpandablePopupParentView> parent,
                 views::Widget* parent_widget);

  // Constructor for creating root level popups. Providing `std::nullopt` to
  // the `search_bar_config` results in creating a popup without a search bar.
  explicit PopupViewViews(
      base::WeakPtr<AutofillPopupController> controller,
      std::optional<const AutofillPopupView::SearchBarConfig>
          search_bar_config = std::nullopt);
  PopupViewViews(const PopupViewViews&) = delete;
  PopupViewViews& operator=(const PopupViewViews&) = delete;
  ~PopupViewViews() override;

  base::WeakPtr<AutofillPopupController> controller() { return controller_; }
  // Gets and sets the currently selected cell. If an invalid `cell_index` is
  // passed, `GetSelectedCell()` will return `std::nullopt` afterwards.
  std::optional<CellIndex> GetSelectedCell() const override;
  void SetSelectedCell(std::optional<CellIndex> cell_index,
                       PopupCellSelectionSource source) override;

  // views::View:
  void OnMouseEntered(const ui::MouseEvent& event) override;
  void OnMouseExited(const ui::MouseEvent& event) override;
  void OnPaint(gfx::Canvas* canvas) override;

  // AutofillPopupView:
  bool Show(AutoselectFirstSuggestion autoselect_first_suggestion) override;
  void Hide() override;
  bool OverlapsWithPictureInPictureWindow() const override;
  std::optional<int32_t> GetAxUniqueId() override;
  void AxAnnounce(const std::u16string& text) override;
  base::WeakPtr<AutofillPopupView> CreateSubPopupView(
      base::WeakPtr<AutofillSuggestionController> controller) override;
  std::optional<AutofillClient::PopupScreenLocation> GetPopupScreenLocation()
      const override;
  bool HasFocus() const override;
  base::WeakPtr<AutofillPopupView> GetWeakPtr() override;

  // PopupBaseView:
  gfx::Size CalculatePreferredSize(
      const views::SizeBounds& available_size) const override;
  void OnWidgetVisibilityChanged(views::Widget* widget, bool visible) override;

  // PopupSearchBarView::Delegate:
  void SearchBarOnInputChanged(std::u16string_view text) override;
  void SearchBarOnFocusLost() override;
  bool SearchBarHandleKeyPressed(const ui::KeyEvent& event) override;

 private:
  friend class PopupViewViewsTestApi;

  // Sets the `cell_index` cell as selected (or just unselects the selected
  // cell if `nullopt` is passed). Only one cell can be selected at a time.
  // Depending on the cell type and the suggestion, it makes the cell or
  // the whole row highlighted. It may also trigger the form preview or
  // a sub-popup to open, which depends on the suggestion acceptability, whether
  // it has children children and the cell type. `autoselect_first_suggestion`
  // and `suppress_popup` are relevant for cases when setting the cell triggers
  // a sub-popup to open. Setting `suppress_popup` to `true` prevents
  // the sub-popup from opening in such cases. `autoselect_first_suggestion`
  // controls whether the first suggestion in the sub-popup will be selected,
  // also capturing the keyboard focus if it is `true`.
  void SetSelectedCell(std::optional<CellIndex> cell_index,
                       PopupCellSelectionSource source,
                       AutoselectFirstSuggestion autoselect_first_suggestion,
                       bool suppress_popup = false);

  // Shows any available in-product-help (IPH) promos associated with the
  // current suggestions. This function iterates through the suggestions and
  // displays a feature promo bubble if the suggestion has associated IPH
  // metadata.
  void ShowIPHFeaturePromos();

  // Returns the `PopupRowView` at line number `index`. Assumes that there is
  // such a view at that line number - otherwise the underlying variant will
  // check false.
  PopupRowView& GetPopupRowViewAt(size_t index) {
    return *std::get<PopupRowView*>(rows_[index]);
  }
  const PopupRowView& GetPopupRowViewAt(size_t index) const {
    return *std::get<PopupRowView*>(rows_[index]);
  }

  void UpdateAccessibleStates() const;

  // Returns whether the row at `index` exists, is a `PopupRowView` and is
  // selectable.
  bool HasSelectablePopupRowViewAt(size_t index) const;

  // Instantiates the content of the popup.
  void InitViews();

  // Creates child views based on the suggestions given by |controller_|.
  // This method expects that all non-footer suggestions precede footer
  // suggestions.
  void CreateSuggestionViews();

  // Selects the first row prior to the currently selected one that is
  // selectable (e.g. not a separator). If no row is selected or no row prior to
  // the current one is selectable, it tries to select the last row. If that one
  // is unselectable, no row is selected.
  void SelectPreviousRow();

  // Analogous to previous row, just in the opposite direction: Tries to find
  // the next selectable row after the currently selected one and selects it
  // with the given selection source. If no row is selected or no row following
  // the currently selected one is selectable, it tries to select the first
  // row. If that one is unselectable, no row is selected.
  void SelectNextRow(PopupCellSelectionSource source);

  // Selects the next/previous in horizontal direction (i.e. left to right or
  // vice versa) cell, if there is one. Otherwise leaves the current selection.
  // Does not wrap.
  bool SelectNextHorizontalCell();
  bool SelectPreviousHorizontalCell();

  // Attempts to accept the selected cell. It will return false if there is no
  // selected cell or the cell does not trigger field filling or scanning a
  // credit card.
  bool AcceptSelectedContentOrCreditCardCell();

  // Attempts to remove the selected cell. Only content cells are allowed to be
  // selected.
  bool RemoveSelectedCell();

  // Reacts to key events under the assumption that the currently shown popup
  // contains Compose content.
  bool HandleKeyPressEventForCompose(
      const input::NativeWebKeyboardEvent& event);

  // AutofillPopupView:
  bool HandleKeyPressEvent(const input::NativeWebKeyboardEvent& event) override;
  void OnSuggestionsChanged(bool prefer_prev_arrow_side) override;

  // PopupBaseView:
  [[nodiscard]] bool DoUpdateBoundsAndRedrawPopup() override;

  // If `prefer_prev_arrow_side` is `true`, the view takes prev arrow side as
  // the first preferred when recalculating the popup position.
  [[nodiscard]] bool DoUpdateBoundsAndRedrawPopup(bool prefer_prev_arrow_side);

  // ExpandablePopupParentView:
  void OnMouseEnteredInChildren() override;
  void OnMouseExitedInChildren() override;

  // Returns whether the footer container is scrollable with other suggestions
  // or it is "sticky" (i.e. it has a fixed position, always visible and
  // the non-footer suggestions are scrolled independently).
  bool IsFooterScrollable() const;

  bool CanShowDropdownInBounds(const gfx::Rect& bounds) const;

  // Opens a sub-popup on a new row (and closes the open one if any), or just
  // closes the existing if `std::nullopt` is passed.
  void SetRowWithOpenSubPopup(
      std::optional<size_t> row_index,
      AutoselectFirstSuggestion autoselect_first_suggestion =
          AutoselectFirstSuggestion(false));

  // Attempts to select the content cell of the row with the currently open
  // sub-popup. This closes the sub-popup and has the effect of going one menu
  // level up. Returns whether this was successful.
  bool SelectParentPopupContentCell();

  // The popup can be used for informing the user without providing suggestions
  // to select, e.g. when the suggestions are loading. It has only one
  // suggestion with a special type in this case. This method makes sure
  // the suggestion's message is being announced to the user by focusing the row
  // view (which must be selectable). Currently, only `PopupWarningView` is
  // supported.
  void MaybeA11yFocusInformationalSuggestion();

  // Controller for this view.
  base::WeakPtr<AutofillPopupController> controller_ = nullptr;

  // Parent's popup view. Present in sub-popups (non-root) only.
  std::optional<base::WeakPtr<ExpandablePopupParentView>> parent_;

  std::unique_ptr<PasswordFaviconLoaderImpl> password_favicon_loader_;

  // The implementation of the a11y announcer. When testing the announcements,
  // it's replaced with a mock function.
  base::RepeatingCallback<void(const std::u16string& message, bool polite)>
      a11y_announcer_;

  // The index of the row with a selected cell.
  std::optional<size_t> row_with_selected_cell_;

  // The latest row which was set as having a sub-popup open. Storing it
  // is required to maintain the invariant of at most one such a row.
  std::optional<size_t> row_with_open_sub_popup_;

  std::vector<RowPointer> rows_;
  const std::optional<const AutofillPopupView::SearchBarConfig>
      search_bar_config_;
  raw_ptr<PopupSearchBarView> search_bar_ = nullptr;
  raw_ptr<views::BoxLayoutView> suggestions_container_ = nullptr;
  raw_ptr<views::ScrollView> scroll_view_ = nullptr;
  raw_ptr<views::BoxLayoutView> body_container_ = nullptr;
  raw_ptr<views::BoxLayoutView> footer_container_ = nullptr;

  base::OneShotTimer open_sub_popup_timer_;
  base::OneShotTimer no_selection_sub_popup_close_timer_;

  // Defines whether the popup handles keyboard events like UP/DOWN/ESC/etc.
  // This value is important for defining which popup handles the event when
  // a chain of (sub-)popups is open: having no focus for a sub-popup means
  // that its parent will take care of handling it.
  // It's automatically set `true` for the root popup (so that it always handles
  // events) and when something is selected in sub-popups. The initial value is
  // set in `Show()`, but after that once it is `true` the value never gets back
  // to `false.`
  bool has_keyboard_focus_ = false;

  // A boolean variable tracking the execution state of the `Show()` method.
  // It's set to `true` when the method starts and `false` before it finishes.
  // Required for the `HasFocus()` method, that might be called during
  // the `Show()` execution. In this case, `GetWidget()->IsActive()` may not yet
  // be `true`, and the `HasFocus()` returning `false` may potentially cause
  // the popup to hide.
  bool show_in_progress_ = false;

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

}  // namespace autofill

#endif  // CHROME_BROWSER_UI_VIEWS_AUTOFILL_POPUP_POPUP_VIEW_VIEWS_H_