File: touch_to_fill_keyboard_suppressor.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 (148 lines) | stat: -rw-r--r-- 6,741 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
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_AUTOFILL_ANDROID_TOUCH_TO_FILL_KEYBOARD_SUPPRESSOR_H_
#define COMPONENTS_AUTOFILL_ANDROID_TOUCH_TO_FILL_KEYBOARD_SUPPRESSOR_H_

#include "base/memory/weak_ptr.h"
#include "base/scoped_multi_source_observation.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "components/autofill/content/browser/content_autofill_driver_factory.h"
#include "components/autofill/core/browser/foundations/autofill_manager.h"

#if !BUILDFLAG(IS_ANDROID)
#error "Android-only header"
#endif

namespace autofill {

class ContentAutofillClient;

// Suppresses the Android IME.
//
// If a TTF surface intends to be displayed, it must suppress the keyboard
// preemptively before Autofill has parsed the form. Otherwise, the keyboard
// could be displayed while Autofill is parsing the form, which would lead to
// flickering.
//
// TouchToFillKeyboardSuppressor takes two callbacks from a TTF controller:
// - `is_showing()` returns true iff the TTF is currently shown;
// - `intends_to_show()` returns true iff the TTF is intended to be shown
//   once parsing is completed.
//
// With TouchToFillKeyboardSuppressor, the controller can implement the
// following behavior:
//
//                  +--------------------------------------------------------+
//                  |      After parsing: controller wants to show TTF?      |
//                  +--------------------------+-----------------------------+
//                  |            No            |            Yes              |
// +----------+-----+--------------------------+-----------------------------+
// | Before   | No  |                   Don't suppress.                      |
// | parsing: |     |                   Don't show TTF.                      |
// | con-     +-----+--------------------------+-----------------------------+
// | troller  | Yes | Suppress before parsing. | Suppress before parsing.    |
// | intends  |     | Unsuppress after parsing | Unsuppress after timeout if |
// | to show  |     | or after timeout.        | parsing is slow. Otherwise, |
// | TTF?     |     | Don't show TTF.          | show TTF, then unsuppress.  |
// +----------+-----+--------------------------+-----------------------------+
//
// The timeout mechanism is intended for cases where parsing takes extremely
// long.
//
// The TTF controller  must satisfy the following conditions:
// - The `is_showing()` callback must be true after parsing only if
//   `is_showing() || intends_to_show()` has been true before parsing.
// - The TTF controller must show the TTF only if `is_suppressing()` is true.
// - The TTF controller must call `Unsuppress()` after the TTF was shown.
//
// The lifecycle of a TouchToFillKeyboardSuppressor must be aligned to the
// lifecycle of a tab (represented here by a ContentAutofillClient).
// It must be created before any ContentAutofillDrivers of the tab have been
// created: it won't suppress the keyboard in frames that were created already.
//
// TouchToFillKeyboardSuppressor observes all AutofillManagers of a given tab
// (represented by the ContentAutofillClient). The event structure is this:
// 1. AutofillManager::Observer::OnBeforeAskForValuesToFill().
// 2. Asynchronous parsing.
// 3. Controller's shows TTF only if
//    - `intends_to_show()` had been true in Step 1, and
//    - `is_suppressing()` is true now.
// 4. AutofillManager::Observer::OnAfterAskForValuesToFill().
class TouchToFillKeyboardSuppressor
    : public ContentAutofillDriverFactory::Observer,
      public AutofillManager::Observer {
 public:
  explicit TouchToFillKeyboardSuppressor(
      ContentAutofillClient* autofill_client,
      base::RepeatingCallback<bool(AutofillManager&)> is_showing,
      base::RepeatingCallback<
          bool(AutofillManager&, FormGlobalId, FieldGlobalId, const FormData&)>
          intends_to_show,
      base::TimeDelta timeout);
  TouchToFillKeyboardSuppressor(const TouchToFillKeyboardSuppressor&) = delete;
  TouchToFillKeyboardSuppressor& operator=(
      const TouchToFillKeyboardSuppressor&) = delete;
  ~TouchToFillKeyboardSuppressor() override;

  // ContentAutofillDriverFactory::Observer:
  void OnContentAutofillDriverFactoryDestroyed(
      ContentAutofillDriverFactory& factory) override;
  void OnContentAutofillDriverCreated(ContentAutofillDriverFactory& factory,
                                      ContentAutofillDriver& driver) override;

  // AutofillManager::Observer:
  void OnAutofillManagerStateChanged(
      AutofillManager& manager,
      AutofillManager::LifecycleState old_state,
      AutofillManager::LifecycleState new_state) override;
  void OnBeforeAskForValuesToFill(AutofillManager& manager,
                                  FormGlobalId form_id,
                                  FieldGlobalId field_id,
                                  const FormData& form_data) override;
  void OnAfterAskForValuesToFill(AutofillManager& manager,
                                 FormGlobalId form_id,
                                 FieldGlobalId field_id) override;

  // Returns true iff some AutofillManager's keyboard is currently suppressed.
  bool is_suppressing() { return suppressed_manager_.get(); }

  // Unsuppresses the keyboard if it's currently being suppressed.
  // This does not raise the keyboard itself.
  void Unsuppress();

 private:
  void KeepSuppressing() { unsuppress_timer_.Stop(); }
  void Suppress(AutofillManager& manager);

  base::RepeatingCallback<bool(AutofillManager&)> is_showing_;
  base::RepeatingCallback<
      bool(AutofillManager&, FormGlobalId, FieldGlobalId, const FormData&)>
      intends_to_show_;

  base::ScopedObservation<ContentAutofillDriverFactory,
                          ContentAutofillDriverFactory::Observer>
      driver_factory_observation_{this};
  base::ScopedMultiSourceObservation<AutofillManager, AutofillManager::Observer>
      autofill_manager_observations_{this};

  // The single AutofillManager whose keyboard is currently suppressed by this
  // suppressor; `nullptr` if no AutofillManager's keyboard is suppressed.
  // A raw pointer suffices because because OnAutofillManagerStateChanged()
  // resets it if necessary.
  raw_ptr<AutofillManager> suppressed_manager_ = nullptr;

  // Unsuppresses the keyboard after `timeout_`.
  base::OneShotTimer unsuppress_timer_;
  base::TimeDelta timeout_;

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

}  // namespace autofill

#endif  // COMPONENTS_AUTOFILL_ANDROID_TOUCH_TO_FILL_KEYBOARD_SUPPRESSOR_H_