Commit 54ace2f4 authored by Colin Blundell's avatar Colin Blundell Committed by Commit Bot

Componentize //android_webview files that integrate with system autofill

In preparation for implementing integration with system autofill in
WebLayer, this CL componentizes //android_webview's infrastructure that
does the heavy lifting for this integration in WebView. The
infrastructure is self-contained and will be reusable out of the box.

This CL also suitably generalizes the context of the files:
- Renames them appropriately
- Removes/generalizes most references to WebView (a few need to be left
  for legacy compatibility; these are documented).

We note that it would be possible to consider merging
AutofillProviderImpl.java into AutofillProvider.java in a followup; as
far as we know, there are no other implementations of the
AutofillProvider interface in the codebase. We did not do that here as
(a) it would have introduced a large amount of noise into the CL, and
(b) it is debatable whether it is better, as there is some value to
having separation between the tight AutofillProvider.java interface
exposed to Chromium's autofill and the sprawling AutofillProviderImpl
implementation necessary to integrate with the system AutofillService.

This CL also adds michaelbai@ as OWNER of the relevant files for
integration with system autofill on Android under
//components/autofill/android.

TBR=michaelbai@chromium.org

Bug: 1025617
Change-Id: I4ca7bbd03983d8b46558db1cd0f2d632d36f787c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1980738
Commit-Queue: Colin Blundell <blundell@chromium.org>
Reviewed-by: default avatarBo <boliu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#728479}
parent b9333852
......@@ -294,9 +294,6 @@ android_library("browser_java") {
"java/src/org/chromium/android_webview/AutofillActionModeCallback.java",
"java/src/org/chromium/android_webview/AwActionModeCallback.java",
"java/src/org/chromium/android_webview/AwAutofillClient.java",
"java/src/org/chromium/android_webview/AwAutofillManager.java",
"java/src/org/chromium/android_webview/AwAutofillProvider.java",
"java/src/org/chromium/android_webview/AwAutofillUMA.java",
"java/src/org/chromium/android_webview/AwBrowserContext.java",
"java/src/org/chromium/android_webview/AwBrowserProcess.java",
"java/src/org/chromium/android_webview/AwConsoleMessage.java",
......
......@@ -30,7 +30,6 @@ import android.webkit.WebViewProvider;
import com.android.webview.chromium.WebViewDelegateFactory.WebViewDelegate;
import org.chromium.android_webview.AwAutofillProvider;
import org.chromium.android_webview.AwBrowserContext;
import org.chromium.android_webview.AwBrowserProcess;
import org.chromium.android_webview.AwSettings;
......@@ -52,6 +51,7 @@ import org.chromium.base.library_loader.NativeLibraries;
import org.chromium.base.metrics.CachedMetrics.TimesHistogramSample;
import org.chromium.base.metrics.ScopedSysTraceEvent;
import org.chromium.components.autofill.AutofillProvider;
import org.chromium.components.autofill.AutofillProviderImpl;
import org.chromium.components.embedder_support.application.ClassLoaderContextWrapperFactory;
import org.chromium.content_public.browser.LGEmailActionModeWorkaround;
......@@ -577,7 +577,7 @@ public class WebViewChromiumFactoryProvider implements WebViewFactoryProvider {
AutofillProvider createAutofillProvider(Context context, ViewGroup containerView) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return null;
return new AwAutofillProvider(context, containerView);
return new AutofillProviderImpl(context, containerView);
}
void startYourEngines(boolean onMainThread) {
......
......@@ -54,10 +54,14 @@ android_library("provider_java") {
"//components/autofill/core/common/mojom:mojo_types_java",
"//content/public/android:content_java",
"//third_party/android_deps:androidx_annotation_annotation_java",
"//ui/android:ui_java",
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
sources = [
"java/src/org/chromium/components/autofill/AutofillManagerWrapper.java",
"java/src/org/chromium/components/autofill/AutofillProvider.java",
"java/src/org/chromium/components/autofill/AutofillProviderImpl.java",
"java/src/org/chromium/components/autofill/AutofillProviderUMA.java",
"java/src/org/chromium/components/autofill/FormData.java",
"java/src/org/chromium/components/autofill/FormFieldData.java",
]
......
file://ui/android/OWNERS
# Files related to integration with system autofill
per-file *autofill_provider*=michaelbai@chromium.org
per-file *AutofillProvider*=michaelbai@chromium.org
per-file *AutofillManagerWrapper*=michaelbai@chromium.org
per-file *form*=michaelbai@chromium.org
per-file *Form*=michaelbai@chromium.org
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.android_webview;
package org.chromium.components.autofill;
import android.annotation.TargetApi;
import android.content.Context;
......@@ -24,8 +24,10 @@ import java.util.Iterator;
* The class to call Android's AutofillManager.
*/
@TargetApi(Build.VERSION_CODES.O)
public class AwAutofillManager {
public class AutofillManagerWrapper {
// Don't change TAG, it is used for runtime log.
// NOTE: As a result of the above, the tag below still references the name of this class from
// when it was originally developed specifically for Android WebView.
public static final String TAG = "AwAutofillManager";
/**
......@@ -34,15 +36,15 @@ public class AwAutofillManager {
public static interface InputUIObserver { void onInputUIShown(); }
private static class AutofillInputUIMonitor extends AutofillManager.AutofillCallback {
private WeakReference<AwAutofillManager> mManager;
private WeakReference<AutofillManagerWrapper> mManager;
public AutofillInputUIMonitor(AwAutofillManager manager) {
mManager = new WeakReference<AwAutofillManager>(manager);
public AutofillInputUIMonitor(AutofillManagerWrapper manager) {
mManager = new WeakReference<AutofillManagerWrapper>(manager);
}
@Override
public void onAutofillEvent(View view, int virtualId, int event) {
AwAutofillManager manager = mManager.get();
AutofillManagerWrapper manager = mManager.get();
if (manager == null) return;
manager.mIsAutofillInputUIShowing = (event == EVENT_INPUT_SHOWN);
if (event == EVENT_INPUT_SHOWN) manager.notifyInputUIChange();
......@@ -57,7 +59,7 @@ public class AwAutofillManager {
private boolean mDisabled;
private ArrayList<WeakReference<InputUIObserver>> mInputUIObservers;
public AwAutofillManager(Context context) {
public AutofillManagerWrapper(Context context) {
updateLogStat();
if (isLoggable()) log("constructor");
mAutofillManager = context.getSystemService(AutofillManager.class);
......@@ -92,9 +94,7 @@ public class AwAutofillManager {
public void notifyVirtualViewEntered(View parent, int childId, Rect absBounds) {
// Log warning only when the autofill is triggered.
if (mDisabled) {
Log.w(TAG,
"WebView autofill is disabled because WebView isn't created with "
+ "activity context.");
Log.w(TAG, "Autofill is disabled: AutofillManager isn't available in given Context.");
return;
}
if (checkAndWarnIfDestroyed()) return;
......@@ -134,7 +134,7 @@ public class AwAutofillManager {
private boolean checkAndWarnIfDestroyed() {
if (mDestroyed) {
Log.w(TAG, "Application attempted to call on a destroyed AwAutofillManager",
Log.w(TAG, "Application attempted to call on a destroyed AutofillManagerWrapper",
new Throwable());
}
return mDestroyed;
......@@ -190,6 +190,7 @@ public class AwAutofillManager {
private static void updateLogStat() {
// Use 'setprop log.tag.AwAutofillManager DEBUG' to enable the log at runtime.
// NOTE: See the comment on TAG above for why this is still AwAutofillManager.
sIsLoggable = Log.isLoggable(TAG, Log.DEBUG);
}
}
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.android_webview;
package org.chromium.components.autofill;
import android.annotation.TargetApi;
import android.content.Context;
......@@ -21,9 +21,6 @@ import androidx.annotation.VisibleForTesting;
import org.chromium.base.ThreadUtils;
import org.chromium.base.annotations.DoNotInline;
import org.chromium.base.metrics.ScopedSysTraceEvent;
import org.chromium.components.autofill.AutofillProvider;
import org.chromium.components.autofill.FormData;
import org.chromium.components.autofill.FormFieldData;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.display.DisplayAndroid;
......@@ -32,16 +29,17 @@ import org.chromium.ui.display.DisplayAndroid;
* This class uses Android autofill service to fill web form. All methods are
* supposed to be called in UI thread.
*
* This class doesn't have 1:1 mapping to native AutofillProviderAndroid and is
* same as how AwContents.java mapping to native AwContents, AwAutofillProvider
* is owned by AwContents.java and AutofillProviderAndroid is owned by native
* AwContents.
* This class doesn't have 1:1 mapping to native AutofillProviderAndroid; the
* normal ownership model is that this object is owned by the embedder-specific
* Java WebContents wrapper (e.g., AwContents.java in //android_webview), and
* AutofillProviderAndroid is owned by the embedder-specific C++ WebContents
* wrapper (e.g., native AwContents in //android_webview).
*
* DoNotInline since it causes class verification errors, see crbug.com/991851.
*/
@DoNotInline
@TargetApi(Build.VERSION_CODES.O)
public class AwAutofillProvider extends AutofillProvider {
public class AutofillProviderImpl extends AutofillProvider {
private static class FocusField {
public final short fieldIndex;
public final Rect absBound;
......@@ -216,28 +214,30 @@ public class AwAutofillProvider extends AutofillProvider {
}
}
private AwAutofillManager mAutofillManager;
private AutofillManagerWrapper mAutofillManager;
private ViewGroup mContainerView;
private WebContents mWebContents;
private AutofillRequest mRequest;
private long mNativeAutofillProvider;
private AwAutofillUMA mAutofillUMA;
private AwAutofillManager.InputUIObserver mInputUIObserver;
private AutofillProviderUMA mAutofillUMA;
private AutofillManagerWrapper.InputUIObserver mInputUIObserver;
private long mAutofillTriggeredTimeMillis;
public AwAutofillProvider(Context context, ViewGroup containerView) {
this(containerView, new AwAutofillManager(context), context);
public AutofillProviderImpl(Context context, ViewGroup containerView) {
this(containerView, new AutofillManagerWrapper(context), context);
}
@VisibleForTesting
public AwAutofillProvider(ViewGroup containerView, AwAutofillManager manager, Context context) {
try (ScopedSysTraceEvent e = ScopedSysTraceEvent.scoped("AwAutofillProvider.constructor")) {
public AutofillProviderImpl(
ViewGroup containerView, AutofillManagerWrapper manager, Context context) {
try (ScopedSysTraceEvent e =
ScopedSysTraceEvent.scoped("AutofillProviderImpl.constructor")) {
assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
mAutofillManager = manager;
mContainerView = containerView;
mAutofillUMA = new AwAutofillUMA(context);
mInputUIObserver = new AwAutofillManager.InputUIObserver() {
mAutofillUMA = new AutofillProviderUMA(context);
mInputUIObserver = new AutofillManagerWrapper.InputUIObserver() {
@Override
public void onInputUIShown() {
// Not need to report suggestion window displayed if there is no live autofill
......@@ -259,12 +259,12 @@ public class AwAutofillProvider extends AutofillProvider {
@Override
public void onProvideAutoFillVirtualStructure(ViewStructure structure, int flags) {
// This method could be called for the session started by the native
// control outside of WebView, e.g. the URL bar, in this case, we simply
// control outside of the scope of autofill, e.g. the URL bar, in this case, we simply
// return.
if (mRequest == null) return;
mRequest.fillViewStructure(structure);
if (AwAutofillManager.isLoggable()) {
AwAutofillManager.log(
if (AutofillManagerWrapper.isLoggable()) {
AutofillManagerWrapper.log(
"onProvideAutoFillVirtualStructure fields:" + structure.getChildCount());
}
mAutofillUMA.onVirtualStructureProvided();
......@@ -274,8 +274,8 @@ public class AwAutofillProvider extends AutofillProvider {
public void autofill(final SparseArray<AutofillValue> values) {
if (mNativeAutofillProvider != 0 && mRequest != null && mRequest.autofill((values))) {
autofill(mNativeAutofillProvider, mRequest.mFormData);
if (AwAutofillManager.isLoggable()) {
AwAutofillManager.log("autofill values:" + values.size());
if (AutofillManagerWrapper.isLoggable()) {
AutofillManagerWrapper.log("autofill values:" + values.size());
}
mAutofillUMA.onAutofill();
}
......@@ -300,7 +300,7 @@ public class AwAutofillProvider extends AutofillProvider {
public void startAutofillSession(
FormData formData, int focus, float x, float y, float width, float height) {
// Check focusField inside short value?
// Autofill Manager might have session that wasn't started by WebView,
// Autofill Manager might have session that wasn't started by AutofillProviderImpl,
// we just always cancel existing session here.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
mAutofillManager.cancel();
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.android_webview;
package org.chromium.components.autofill;
import android.content.Context;
......@@ -13,20 +13,22 @@ import org.chromium.base.metrics.RecordHistogram;
import java.util.concurrent.TimeUnit;
/**
* The class for WebView autofill UMA.
* The class for AutofillProvider-related UMA. Note that most of the concrete histogram
* names include "WebView"; when this class was originally developed it was WebView-specific,
* and when generalizing it we did not change these names to maintain continuity when
* analyzing the histograms.
*/
public class AwAutofillUMA {
public class AutofillProviderUMA {
// Records whether the Autofill service is enabled or not.
public static final String UMA_AUTOFILL_WEBVIEW_ENABLED = "Autofill.WebView.Enabled";
public static final String UMA_AUTOFILL_ENABLED = "Autofill.WebView.Enabled";
// Records whether the Autofill provider is created by activity context or not.
public static final String UMA_AUTOFILL_WEBVIEW_CREATED_BY_ACTIVITY_CONTEXT =
public static final String UMA_AUTOFILL_CREATED_BY_ACTIVITY_CONTEXT =
"Autofill.WebView.CreatedByActivityContext";
// Records what happened in an autofill session.
public static final String UMA_AUTOFILL_WEBVIEW_AUTOFILL_SESSION =
"Autofill.WebView.AutofillSession";
// The possible value of UMA_AUTOFILL_WEBVIEW_AUTOFILL_SESSION.
public static final String UMA_AUTOFILL_AUTOFILL_SESSION = "Autofill.WebView.AutofillSession";
// The possible value of UMA_AUTOFILL_AUTOFILL_SESSION.
public static final int SESSION_UNKNOWN = 0;
public static final int NO_CALLBACK_FORM_FRAMEWORK = 1;
public static final int NO_SUGGESTION_USER_CHANGE_FORM_FORM_SUBMITTED = 2;
......@@ -48,9 +50,8 @@ public class AwAutofillUMA {
public static final String UMA_AUTOFILL_USER_CHANGED_AUTOFILLED_FIELD =
"Autofill.WebView.UserChangedAutofilledField";
public static final String UMA_AUTOFILL_WEBVIEW_SUBMISSION_SOURCE =
"Autofill.WebView.SubmissionSource";
// The possible value of UMA_AUTOFILL_WEBVIEW_SUBMISSION_SOURCE.
public static final String UMA_AUTOFILL_SUBMISSION_SOURCE = "Autofill.WebView.SubmissionSource";
// The possible value of UMA_AUTOFILL_SUBMISSION_SOURCE.
public static final int SAME_DOCUMENT_NAVIGATION = 0;
public static final int XHR_SUCCEEDED = 1;
public static final int FRAME_DETACHED = 2;
......@@ -59,13 +60,11 @@ public class AwAutofillUMA {
public static final int FORM_SUBMISSION = 5;
public static final int SUBMISSION_SOURCE_HISTOGRAM_COUNT = 6;
// The million seconds from user touched the field to WebView started autofill session.
public static final String UMA_AUTOFILL_WEBVIEW_TRIGGERING_TIME =
"Autofill.WebView.TriggeringTime";
// The million seconds from user touched the field to the autofill session starting.
public static final String UMA_AUTOFILL_TRIGGERING_TIME = "Autofill.WebView.TriggeringTime";
// The million seconds from WebView started autofill session to suggestion was displayed.
public static final String UMA_AUTOFILL_WEBVIEW_SUGGESTION_TIME =
"Autofill.WebView.SuggestionTime";
// The million seconds from the autofill session starting to the suggestion being displayed.
public static final String UMA_AUTOFILL_SUGGESTION_TIME = "Autofill.WebView.SuggestionTime";
// The expected time range of time is from 10ms to 2 seconds, and 50 buckets is sufficient.
private static final long MIN_TIME_MILLIS = 10;
......@@ -111,7 +110,7 @@ public class AwAutofillUMA {
}
public void recordHistogram() {
RecordHistogram.recordEnumeratedHistogram(UMA_AUTOFILL_WEBVIEW_AUTOFILL_SESSION,
RecordHistogram.recordEnumeratedHistogram(UMA_AUTOFILL_AUTOFILL_SESSION,
toUMAAutofillSessionValue(), AUTOFILL_SESSION_HISTOGRAM_COUNT);
// Only record if user ever changed form.
if (mUserChangedAutofilledField != null) {
......@@ -119,7 +118,7 @@ public class AwAutofillUMA {
UMA_AUTOFILL_USER_CHANGED_AUTOFILLED_FIELD, mUserChangedAutofilledField);
}
if (mSuggestionTimeMillis != null) {
recordTimesHistogram(UMA_AUTOFILL_WEBVIEW_SUGGESTION_TIME, mSuggestionTimeMillis);
recordTimesHistogram(UMA_AUTOFILL_SUGGESTION_TIME, mSuggestionTimeMillis);
}
}
......@@ -180,8 +179,8 @@ public class AwAutofillUMA {
private SessionRecorder mRecorder;
private Boolean mAutofillDisabled;
public AwAutofillUMA(Context context) {
RecordHistogram.recordBooleanHistogram(UMA_AUTOFILL_WEBVIEW_CREATED_BY_ACTIVITY_CONTEXT,
public AutofillProviderUMA(Context context) {
RecordHistogram.recordBooleanHistogram(UMA_AUTOFILL_CREATED_BY_ACTIVITY_CONTEXT,
ContextUtils.activityFromContext(context) != null);
}
......@@ -189,14 +188,14 @@ public class AwAutofillUMA {
if (mRecorder != null) mRecorder.record(SessionRecorder.EVENT_FORM_SUBMITTED);
recordSession();
// We record this no matter autofill service is disabled or not.
RecordHistogram.recordEnumeratedHistogram(UMA_AUTOFILL_WEBVIEW_SUBMISSION_SOURCE,
RecordHistogram.recordEnumeratedHistogram(UMA_AUTOFILL_SUBMISSION_SOURCE,
toUMASubmissionSource(submissionSource), SUBMISSION_SOURCE_HISTOGRAM_COUNT);
}
public void onSessionStarted(boolean autofillDisabled) {
// Record autofill status once per instance and only if user triggers the autofill.
if (mAutofillDisabled == null || mAutofillDisabled.booleanValue() != autofillDisabled) {
RecordHistogram.recordBooleanHistogram(UMA_AUTOFILL_WEBVIEW_ENABLED, !autofillDisabled);
RecordHistogram.recordBooleanHistogram(UMA_AUTOFILL_ENABLED, !autofillDisabled);
mAutofillDisabled = Boolean.valueOf(autofillDisabled);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment