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
|
// 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 COMPONENTS_ANDROID_AUTOFILL_BROWSER_ANDROID_AUTOFILL_PROVIDER_H_
#define COMPONENTS_ANDROID_AUTOFILL_BROWSER_ANDROID_AUTOFILL_PROVIDER_H_
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "components/android_autofill/browser/android_autofill_provider_bridge.h"
#include "components/android_autofill/browser/autofill_provider.h"
#include "components/android_autofill/browser/form_data_android.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/foundations/autofill_manager.h"
#include "components/autofill/core/common/unique_ids.h"
#include "components/webauthn/android/webauthn_cred_man_delegate.h"
#include "content/public/browser/web_contents_observer.h"
namespace content {
class WebContents;
} // namespace content
namespace password_manager {
struct PasswordForm;
} // namespace password_manager
namespace autofill {
class TouchToFillKeyboardSuppressor;
// Android implementation of AutofillProvider, it has one instance per
// WebContents, this class is native peer of AutofillProvider.java.
// This class is always instantialized by AutofillProvider Java object.
class AndroidAutofillProvider : public AutofillProvider,
public AndroidAutofillProviderBridge::Delegate,
public content::WebContentsObserver {
public:
// Used as a metric that describes the state of a prefill requests. It is
// emitted for every form deemed suitable for prefill requests prefill
// requests are supported on this Android version (U+).
enum class PrefillRequestState {
// A prefill request was sent, a view structure was requested and provided
// and a bottom sheet was shown.
kRequestSentStructureProvidedBottomSheetShown = 0,
// A prefill request was sent and a view structure was requested and
// provided, but no bottom sheet was shown. The reason for not showing the
// bottom sheet is opaque to WebView (e.g., no suggestions available by
// providers, keyboard suppression not working correctly, etc.)
kRequestSentStructureProvidedBottomSheetNotShown = 1,
// A prefill request was sent, but no view structure was requested by the
// framework.
kRequestSentStructureNotProvided = 2,
// A prefill request was sent, but the form changed substantially between
// sending the cache request and the focus event on the form.
kRequestSentFormChanged = 3,
// A prefill request was not sent because the maximum number of prefill
// requests (currently 1 per session) had already been reached.
kRequestNotSentMaxNumberReached = 4,
// A prefill request was not sent because there was no time - the form was
// only analyzed to be cacheable when a session had already been started.
kRequestNotSentNoTime = 5,
kMaxValue = kRequestNotSentNoTime
};
static constexpr char kPrefillRequestStateUma[] =
"Autofill.WebView.PrefillRequestState";
static void CreateForWebContents(content::WebContents* web_contents);
static AndroidAutofillProvider* FromWebContents(
content::WebContents* web_contents);
AndroidAutofillProvider(const AndroidAutofillProvider&) = delete;
AndroidAutofillProvider& operator=(const AndroidAutofillProvider&) = delete;
~AndroidAutofillProvider() override;
// Attach this detached object to `jcaller`.
void AttachToJavaAutofillProvider(
JNIEnv* env,
const base::android::JavaRef<jobject>& jcaller);
// AutofillProvider:
void OnAskForValuesToFill(
AndroidAutofillManager* manager,
const FormData& form,
const FormFieldData& field,
AutofillSuggestionTriggerSource /*unused_trigger_source*/) override;
void OnTextFieldValueChanged(AndroidAutofillManager* manager,
const FormData& form,
const FormFieldData& field,
const base::TimeTicks timestamp) override;
void OnTextFieldDidScroll(AndroidAutofillManager* manager,
const FormData& form,
const FormFieldData& field) override;
void OnSelectControlSelectionChanged(AndroidAutofillManager* manager,
const FormData& form,
const FormFieldData& field) override;
void OnFormSubmitted(AndroidAutofillManager* manager,
const FormData& form,
mojom::SubmissionSource source) override;
void OnFocusOnNonFormField(AndroidAutofillManager* manager) override;
void OnFocusOnFormField(AndroidAutofillManager* manager,
const FormData& form,
const FormFieldData& field) override;
void OnDidFillAutofillFormData(AndroidAutofillManager* manager,
const FormData& form,
base::TimeTicks timestamp) override;
void OnHidePopup(AndroidAutofillManager* manager) override;
void OnServerPredictionsAvailable(AndroidAutofillManager& manager,
FormGlobalId form_id) override;
void OnManagerResetOrDestroyed(AndroidAutofillManager* manager) override;
bool GetCachedIsAutofilled(const FormFieldData& field) const override;
void MaybeInitKeyboardSuppressor() override;
private:
friend class AndroidAutofillProviderTestApi;
explicit AndroidAutofillProvider(content::WebContents* web_contents);
// AndroidAutofillProviderBridge::Delegate:
void OnAutofillAvailable() override;
void OnAcceptDatalistSuggestion(const std::u16string& value) override;
void SetAnchorViewRect(const base::android::JavaRef<jobject>& anchor,
const gfx::RectF& bounds) override;
void OnShowBottomSheetResult(bool is_shown,
bool provided_autofill_structure) override;
bool HasPasskeyRequest() override;
void OnTriggerPasskeyRequest() override;
// content::WebContentsObserver:
void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override;
void OnVisibilityChanged(content::Visibility visibility) override;
// Returns true the first time it is called after a bottom sheet was shown.
// This is intended to be used only by the keyboard suppressor, which calls it
// once in OnAfterAskForValuesToFill to determine whether it should continue
// to suppress the keyboard. Ideally, this would return whether the bottom
// sheet is currently showing, but Android does not expose that information.
bool WasBottomSheetJustShown(AutofillManager& manager);
// Returns true if there's possibility for the bottom sheet to show, false
// otherwise.
bool IntendsToShowBottomSheet(AutofillManager& manager,
FormGlobalId form,
FieldGlobalId field,
const FormData& form_data) const;
void FireSuccessfulSubmission(mojom::SubmissionSource source);
// Updates fields that changed on native class only. The Android bridge is not
// yet invoked to give preference to a possible CredMan flow.
// Using the given form and field that are newly focused, the method returns
// the necessary field information to continue the focus event later on. If
// continuing the focus event is not possible or necessary, it returns a
// `std::nullopt`.
std::optional<AndroidAutofillProviderBridge::FieldInfo> StartFocusChange(
const FormData& form,
const FormFieldData& field);
// Calls `OnFormFieldDidChange` in the bridge if there is an ongoing Autofill
// session for this `form`.
void MaybeFireFormFieldDidChange(AndroidAutofillManager* manager,
const FormData& form,
const FormFieldData& field);
bool IsLinkedManager(AndroidAutofillManager* manager) const;
// Checks whether a `form_` is linked and whether it's the same form as
// `form`, having same identifier.
bool IsIdOfLinkedForm(FormGlobalId form_id) const;
// Same as `IsLinkedForm`, but also checks that `form` and `form_` are
// similar, using form similarity checks.
bool IsLinkedForm(const FormData& form) const;
gfx::RectF ToClientAreaBound(const gfx::RectF& bounding_box) const;
void StartNewSession(AndroidAutofillManager* manager,
const FormData& form,
const FormFieldData& field);
void Reset();
// Cancels the current Autofill session, resetting cached session data.
void CancelSession();
// Returns a new session id. Session ids are required when creating a
// `FormDataAndroid` object and used to generate virtual ids that identify
// form fields uniquely to the Android Autofill framework.
SessionId CreateSessionId();
// Returns whether prefill requests are supported. This depends on the
// Android version.
bool ArePrefillRequestsSupported() const;
// Sends a prefill request to the Autofill framework if all the below
// conditions are met:
// 1. Prefill requests are supported (correct SDK version & feature flag).
// 2. No prefill request has been sent so far, since the framework only
// supports caching a single form at a time.
// 3. There is no ongoing Autofill session. This is to ensure that the
// `onProvideAutofillStructure` callback from the framework does not
// confuse information requests for caching and for the current Autofill
// session.
// 4. The form is predicted to be a login form.
void MaybeSendPrefillRequest(const AndroidAutofillManager& manager,
FormGlobalId form_id);
// Stores field ids for fields detected by `password_manager::FormDataParser`
// as username or password fields. Currently used only for prefill requests.
struct PasswordParserOverrides {
bool operator==(const PasswordParserOverrides&) const = default;
// Returns the `PasswordParserOverrides` obtained from matching the
// `FieldRendererId`s of username and password fields in `pw_form` to the
// `FieldGlobalId`s in `form_structure`. Returns `std::nullopt` if no unique
// matching could be found. A unique matching may not exist if the form is
// spread across multiple iframes. In practice, this should be extremely
// rare for password forms.
static std::optional<PasswordParserOverrides> FromLoginForm(
const password_manager::PasswordForm& pw_form,
const FormStructure& form_structure);
// Creates a map as expected by `FormDataAndroid::UpdateFieldTypes`.
base::flat_map<FieldGlobalId, FieldType> ToFieldTypeMap() const;
std::optional<FieldGlobalId> username_field_id;
std::optional<FieldGlobalId> password_field_id;
};
// Checks whether `form` is similar to the cached form. `form_structure` must
// be the `form_structure` corresponding to `form` if it is available (i.e.
// cached by the AutofillManager already). The check works as follows:
// - If `form_structure` is not null and
// `kAndroidAutofillSignatureForPrefillRequestSimilarityCheck` is enabled,
// the form is parsed using `password_manager::FormDataParser`. The form
// is classified as similar if the fields classified as username and
// passwords match between the cached and the focused form.
// - Alternatively, it returns the result of `FormDataAndroid::SimilarFormAs`.
bool IsFormSimilarToCachedForm(const FormData& form,
const FormStructure* form_structure) const;
// In some cases we get two AskForValuesToFill events within short time frame
// so we set timer to set the `was_bottom_sheet_just_shown_` to false after it
// gets accessed.
// TODO(crbug.com/40284788): Remove once a fix is landed on the renderer side.
void SetBottomSheetShownOff();
// Stops the keyboard suppression. Called when the CredMan UI was closed. If
// the UI was dismissed without selecting a passkey, `success` will be false.
// If a `field_to_focus` is given and CredMan wasn't able to sign the
// user in, attempt to continue an earlier attempt to focus a field.
void OnCredManUiClosed(
FormGlobalId form_id,
std::optional<AndroidAutofillProviderBridge::FieldInfo> field_to_focus,
webauthn::WebAuthnCredManDelegate::State has_passkeys,
bool success);
// Returns true if CredMan *may* be shown for the given field. It only returns
// false if the sheet was already shown or prefetching concluded and indicated
// that no passkeys are available.
bool IntendsToShowCredMan(const FormFieldData& field,
content::RenderFrameHost* rfh) const;
// Returns true if a passkey request is pending or succeeded for the given
// `rfh` and the CredMan UI should be shown when the given `field` is focused.
bool ShouldShowCredManForField(const FormFieldData& field,
content::RenderFrameHost* rfh);
// Triggers a prefetched passkey request which opens a bottom sheet. The given
// `field_to_focus` is used to continue any interrupted focus event once
// CredMan closes. Returns true if the sheet was opened and false if that
// wasn't possible.
bool ShowCredManSheet(
content::RenderFrameHost* rfh,
FormGlobalId form_id,
std::optional<AndroidAutofillProviderBridge::FieldInfo> field_to_focus);
enum class CredManBottomSheetLifecycle {
kNotShown, // The sheet hasn't been shown. Does not indicate it will be.
kIsShowing, // The sheet was triggered. Does not guarantee it's visible.
kClosed, // The sheet was dismissed and shouldn't be shown again.
};
CredManBottomSheetLifecycle credman_sheet_status_ =
CredManBottomSheetLifecycle::kNotShown;
// This is used by the keyboard suppressor. We update it with the result of
// the platform method call `showAutofillDialog`. Since we are not notified
// when the bottom sheet is dismissed, we set a timer to set it to `false`
// shortly after `WasBottomSheetJustShown()` is called. The timer's function
// is to handle multiple calls related to the same user event correctly, which
// can currently happen (crbug.com/1490581).
bool was_bottom_sheet_just_shown_ = false;
// Sets `was_bottom_sheet_just_shown` to false after a timeout.
base::OneShotTimer was_shown_bottom_sheet_timer_;
// Helper struct that contains cache data used in prefill requests.
struct CachedData {
CachedData();
CachedData(CachedData&&);
CachedData& operator=(CachedData&&);
~CachedData();
std::unique_ptr<FormDataAndroid> cached_form;
PasswordParserOverrides password_parser_overrides;
// The time when the prefill request was sent - used for metrics only.
base::TimeTicks prefill_request_creation_time;
};
std::optional<CachedData> cached_data_;
// Indicates whether we have used the cached form to show a bottom sheet. This
// state is kept because a bottom sheet should only be shown once per cached
// form to allow the user to access the keyboard after focusing on the
// (cached) form a second time.
bool has_used_cached_form_ = false;
// The form of the current session (queried input or changed select box).
std::unique_ptr<FormDataAndroid> form_;
// Properties of the last-focused field of the current session for `form_`
// (queried input or changed select box).
struct {
FieldGlobalId id;
FieldTypeGroup group = {FieldTypeGroup::kNoGroup};
url::Origin origin;
} current_field_;
// The frame of the field for which the last OnAskForValuesToFill() happened.
//
// It is not necessarily the same frame as the current session's
// `last_focused_field_id_.host_frame` because the session may survive
// OnAskForValuesToFill().
//
// It's not necessarily the same frame as `manager_`'s for the same reason as
// `last_focused_field_id_`, and also because `manager_` may refer to an
// ancestor frame of the queried field.
content::GlobalRenderFrameHostId last_queried_field_rfh_id_;
base::WeakPtr<AndroidAutofillManager> manager_;
static constexpr SessionId kMinimumSessionId = SessionId(1);
static constexpr SessionId kMaximumSessionId = SessionId(0xffff);
// The last assigned session id.
SessionId last_session_id_ = kMaximumSessionId;
// The bridge for C++ <-> Java communication.
std::unique_ptr<AndroidAutofillProviderBridge> bridge_;
// Used for handling keyboard suppression in case there's a bottom sheet.
std::unique_ptr<TouchToFillKeyboardSuppressor> keyboard_suppressor_;
base::WeakPtrFactory<AndroidAutofillProvider> weak_ptr_factory_{this};
};
} // namespace autofill
#endif // COMPONENTS_ANDROID_AUTOFILL_BROWSER_ANDROID_AUTOFILL_PROVIDER_H_
|