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_
|