380 lines
17 KiB
Diff
380 lines
17 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: fgei <fgei@gmail.com>
|
|
Date: Mon, 20 Feb 2023 07:10:55 +0000
|
|
Subject: [PATCH] Support native Android autofill at browser
|
|
|
|
This enables support for Android Autofil on tabs showing fillable
|
|
entries, reusing the codebase used for webview's android autofill
|
|
support.
|
|
---
|
|
android_webview/browser/aw_autofill_client.cc | 4 ++
|
|
chrome/android/BUILD.gn | 1 +
|
|
.../chromium/chrome/browser/tab/TabImpl.java | 45 +++++++++++++++++
|
|
.../browser/tab/TabViewAndroidDelegate.java | 13 +++++
|
|
chrome/browser/BUILD.gn | 7 +++
|
|
.../ui/autofill/chrome_autofill_client.cc | 14 +++++-
|
|
.../embedder_support/view/ContentView.java | 48 +++++++++++++++++++
|
|
.../chromium/ui/base/ViewAndroidDelegate.java | 8 ++++
|
|
8 files changed, 139 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/android_webview/browser/aw_autofill_client.cc b/android_webview/browser/aw_autofill_client.cc
|
|
index 449fe65d0bdda..6f0cf1553b07e 100644
|
|
--- a/android_webview/browser/aw_autofill_client.cc
|
|
+++ b/android_webview/browser/aw_autofill_client.cc
|
|
@@ -86,6 +86,7 @@ AwAutofillClient::GetURLLoaderFactory() {
|
|
}
|
|
|
|
autofill::AutofillDownloadManager* AwAutofillClient::GetDownloadManager() {
|
|
+#if defined(USE_BROWSER_AUTOFILL_ONLY)
|
|
if (autofill::AutofillProvider::is_download_manager_disabled_for_testing()) {
|
|
return nullptr;
|
|
}
|
|
@@ -95,6 +96,9 @@ autofill::AutofillDownloadManager* AwAutofillClient::GetDownloadManager() {
|
|
this, GetChannel(), GetLogManager());
|
|
}
|
|
return download_manager_.get();
|
|
+#else
|
|
+ return nullptr;
|
|
+#endif // defined(USE_BROWSER_AUTOFILL_ONLY)
|
|
}
|
|
|
|
autofill::PersonalDataManager* AwAutofillClient::GetPersonalDataManager() {
|
|
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
|
|
index f56c41b506f45..06cd1e491c1d7 100644
|
|
--- a/chrome/android/BUILD.gn
|
|
+++ b/chrome/android/BUILD.gn
|
|
@@ -452,6 +452,7 @@ if (current_toolchain == default_toolchain) {
|
|
"//chrome/browser/xsurface:java",
|
|
"//chrome/browser/xsurface_provider:dependency_provider_impl_java",
|
|
"//chrome/browser/xsurface_provider:java",
|
|
+ "//components/android_autofill/browser:java",
|
|
"//components/autofill/android:autofill_java",
|
|
"//components/background_task_scheduler:background_task_scheduler_java",
|
|
"//components/background_task_scheduler:background_task_scheduler_task_ids_java",
|
|
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
|
|
index d9f1d1008e623..18f23b198448e 100644
|
|
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
|
|
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
|
|
@@ -10,10 +10,14 @@ import android.annotation.SuppressLint;
|
|
import android.app.Activity;
|
|
import android.content.Context;
|
|
import android.graphics.Rect;
|
|
+import android.os.Build;
|
|
import android.text.TextUtils;
|
|
+import android.util.SparseArray;
|
|
import android.view.View;
|
|
import android.view.View.OnAttachStateChangeListener;
|
|
+import android.view.ViewStructure;
|
|
import android.view.accessibility.AccessibilityEvent;
|
|
+import android.view.autofill.AutofillValue;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
@@ -52,6 +56,8 @@ import org.chromium.chrome.browser.tab.TabUtils.LoadIfNeededCaller;
|
|
import org.chromium.chrome.browser.tab.TabUtils.UseDesktopUserAgentCaller;
|
|
import org.chromium.chrome.browser.ui.native_page.FrozenNativePage;
|
|
import org.chromium.chrome.browser.ui.native_page.NativePage;
|
|
+import org.chromium.components.autofill.AutofillProvider;
|
|
+import org.chromium.components.autofill.AutofillSelectionMenuItemProvider;
|
|
import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
|
|
import org.chromium.components.embedder_support.util.UrlConstants;
|
|
import org.chromium.components.embedder_support.view.ContentView;
|
|
@@ -65,9 +71,11 @@ import org.chromium.content_public.browser.ChildProcessImportance;
|
|
import org.chromium.content_public.browser.ContentFeatureList;
|
|
import org.chromium.content_public.browser.ContentFeatureMap;
|
|
import org.chromium.content_public.browser.LoadUrlParams;
|
|
+import org.chromium.content_public.browser.SelectionPopupController;
|
|
import org.chromium.content_public.browser.WebContents;
|
|
import org.chromium.content_public.browser.WebContentsAccessibility;
|
|
import org.chromium.content_public.browser.navigation_controller.UserAgentOverrideOption;
|
|
+import org.chromium.ui.base.EventOffsetHandler;
|
|
import org.chromium.ui.base.PageTransition;
|
|
import org.chromium.ui.base.ViewAndroidDelegate;
|
|
import org.chromium.ui.base.WindowAndroid;
|
|
@@ -209,6 +217,7 @@ public class TabImpl implements Tab {
|
|
private int mParentId = INVALID_TAB_ID;
|
|
private int mRootId;
|
|
private @TabUserAgent int mUserAgent = TabUserAgent.DEFAULT;
|
|
+ AutofillProvider mAutofillProvider;
|
|
/**
|
|
* Navigation state of the WebContents as returned by nativeGetContentsStateAsByteBuffer(),
|
|
* stored to be inflated on demand using unfreezeContents(). If this is not null, there is no
|
|
@@ -271,12 +280,18 @@ public class TabImpl implements Tab {
|
|
public void onViewAttachedToWindow(View view) {
|
|
mIsViewAttachedToWindow = true;
|
|
updateInteractableState();
|
|
+ if (mAutofillProvider != null) {
|
|
+ mAutofillProvider.onContainerViewChanged(mContentView);
|
|
+ }
|
|
}
|
|
|
|
@Override
|
|
public void onViewDetachedFromWindow(View view) {
|
|
mIsViewAttachedToWindow = false;
|
|
updateInteractableState();
|
|
+ if (mAutofillProvider != null) {
|
|
+ mAutofillProvider.onContainerViewChanged(mContentView);
|
|
+ }
|
|
}
|
|
};
|
|
mTabViewManager = new TabViewManagerImpl(this);
|
|
@@ -817,6 +832,11 @@ public class TabImpl implements Tab {
|
|
for (TabObserver observer : mObservers) observer.onDestroyed(this);
|
|
mObservers.clear();
|
|
|
|
+ if (mAutofillProvider != null) {
|
|
+ mAutofillProvider.destroy();
|
|
+ mAutofillProvider = null;
|
|
+ }
|
|
+
|
|
mUserDataHost.destroy();
|
|
mTabViewManager.destroy();
|
|
hideNativePage(false, null);
|
|
@@ -1395,6 +1415,18 @@ public class TabImpl implements Tab {
|
|
return mWebContentsState == null ? -1 : mWebContentsState.version();
|
|
}
|
|
|
|
+ public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {
|
|
+ if (mAutofillProvider != null) {
|
|
+ mAutofillProvider.onProvideAutoFillVirtualStructure(structure, flags);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void autofill(final SparseArray<AutofillValue> values) {
|
|
+ if (mAutofillProvider != null) {
|
|
+ mAutofillProvider.autofill(values);
|
|
+ }
|
|
+ }
|
|
+
|
|
/**
|
|
* Initializes the {@link WebContents}. Completes the browser content components initialization
|
|
* around a native WebContents pointer.
|
|
@@ -1438,10 +1470,23 @@ public class TabImpl implements Tab {
|
|
mWebContentsDelegate = createWebContentsDelegate();
|
|
|
|
assert mNativeTabAndroid != 0;
|
|
+ SelectionPopupController selectionController = null;
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
+ selectionController = SelectionPopupController.fromWebContents(mWebContents);
|
|
+ mAutofillProvider = new AutofillProvider(
|
|
+ getContext(), cv, webContents, "NativeAutofillRenderer");
|
|
+ }
|
|
TabImplJni.get().initWebContents(mNativeTabAndroid, mIncognito,
|
|
TabUtils.isDetached(this), webContents, mWebContentsDelegate,
|
|
new TabContextMenuPopulatorFactory(
|
|
mDelegateFactory.createContextMenuPopulatorFactory(this), this));
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && selectionController != null) {
|
|
+ mAutofillProvider.setWebContents(webContents);
|
|
+ cv.setWebContents(webContents);
|
|
+ selectionController.setNonSelectionAdditionalMenuItemProvider(
|
|
+ new AutofillSelectionMenuItemProvider(
|
|
+ mThemedApplicationContext, mAutofillProvider));
|
|
+ }
|
|
|
|
mWebContents.notifyRendererPreferenceUpdate();
|
|
TabHelpers.initWebContentsHelpers(this);
|
|
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java
|
|
index 16c12477bbcb2..a5c9b7fffe05e 100644
|
|
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java
|
|
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabViewAndroidDelegate.java
|
|
@@ -4,7 +4,10 @@
|
|
|
|
package org.chromium.chrome.browser.tab;
|
|
|
|
+import android.util.SparseArray;
|
|
import android.view.ViewGroup;
|
|
+import android.view.ViewStructure;
|
|
+import android.view.autofill.AutofillValue;
|
|
|
|
import androidx.annotation.Nullable;
|
|
|
|
@@ -86,6 +89,16 @@ public class TabViewAndroidDelegate extends ViewAndroidDelegate {
|
|
mTab.onBackgroundColorChanged(color);
|
|
}
|
|
|
|
+ @Override
|
|
+ public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {
|
|
+ mTab.onProvideAutofillVirtualStructure(structure, flags);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void autofill(final SparseArray<AutofillValue> values) {
|
|
+ mTab.autofill(values);
|
|
+ }
|
|
+
|
|
@Override
|
|
public void onTopControlsChanged(
|
|
int topControlsOffsetY, int contentOffsetY, int topControlsMinHeightOffsetY) {
|
|
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
|
|
index 7cb8d3fc03da2..3a15d9e907b60 100644
|
|
--- a/chrome/browser/BUILD.gn
|
|
+++ b/chrome/browser/BUILD.gn
|
|
@@ -2651,6 +2651,13 @@ static_library("browser") {
|
|
deps += [ "//chrome/browser/error_reporting" ]
|
|
}
|
|
|
|
+ if (is_android) {
|
|
+ deps += [
|
|
+ "//components/android_autofill/browser",
|
|
+ "//components/android_autofill/browser:android"
|
|
+ ]
|
|
+ }
|
|
+
|
|
if (use_ozone) {
|
|
deps += [
|
|
"//ui/events/ozone",
|
|
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
|
|
index 9e4c108ec5bc5..afa1ade208467 100644
|
|
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
|
|
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
|
|
@@ -58,6 +58,9 @@
|
|
#include "chrome/browser/web_data_service_factory.h"
|
|
#include "chrome/common/channel_info.h"
|
|
#include "chrome/common/url_constants.h"
|
|
+#if BUILDFLAG(IS_ANDROID)
|
|
+#include "components/android_autofill/browser/android_autofill_manager.h"
|
|
+#endif // BUILDFLAG(IS_ANDROID)
|
|
#include "components/autofill/content/browser/autofill_log_router_factory.h"
|
|
#include "components/autofill/content/browser/content_autofill_driver.h"
|
|
#include "components/autofill/content/browser/content_autofill_driver_factory.h"
|
|
@@ -215,12 +218,16 @@ ChromeAutofillClient::GetURLLoaderFactory() {
|
|
}
|
|
|
|
AutofillDownloadManager* ChromeAutofillClient::GetDownloadManager() {
|
|
+#if defined(USE_BROWSER_AUTOFILL_ONLY)
|
|
if (!download_manager_) {
|
|
// Lazy initialization to avoid virtual function calls in the constructor.
|
|
download_manager_ = std::make_unique<AutofillDownloadManager>(
|
|
this, GetChannel(), GetLogManager());
|
|
}
|
|
return download_manager_.get();
|
|
+#else
|
|
+ return nullptr;
|
|
+#endif // defined(USE_BROWSER_AUTOFILL_ONLY)
|
|
}
|
|
|
|
AutofillOptimizationGuide* ChromeAutofillClient::GetAutofillOptimizationGuide()
|
|
@@ -1323,7 +1330,12 @@ void ChromeAutofillClient::OnZoomChanged(
|
|
ChromeAutofillClient::ChromeAutofillClient(content::WebContents* web_contents)
|
|
: ContentAutofillClient(
|
|
web_contents,
|
|
- base::BindRepeating(&BrowserDriverInitHook,
|
|
+ base::BindRepeating(
|
|
+#if BUILDFLAG(IS_ANDROID)
|
|
+ &AndroidAndBrowserDriverInitHook,
|
|
+#else
|
|
+ &BrowserDriverInitHook,
|
|
+#endif // BUILDFLAG(IS_ANDROID)
|
|
this,
|
|
g_browser_process->GetApplicationLocale())),
|
|
content::WebContentsObserver(web_contents),
|
|
diff --git a/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java b/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java
|
|
index aefde08a5b224..e08ce993314e2 100644
|
|
--- a/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java
|
|
+++ b/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java
|
|
@@ -9,6 +9,7 @@ import android.content.res.Configuration;
|
|
import android.graphics.Rect;
|
|
import android.os.Build;
|
|
import android.os.Handler;
|
|
+import android.util.SparseArray;
|
|
import android.view.DragEvent;
|
|
import android.view.KeyEvent;
|
|
import android.view.MotionEvent;
|
|
@@ -19,6 +20,7 @@ import android.view.View.OnSystemUiVisibilityChangeListener;
|
|
import android.view.ViewGroup.OnHierarchyChangeListener;
|
|
import android.view.ViewStructure;
|
|
import android.view.accessibility.AccessibilityNodeProvider;
|
|
+import android.view.autofill.AutofillValue;
|
|
import android.view.inputmethod.EditorInfo;
|
|
import android.view.inputmethod.InputConnection;
|
|
import android.widget.FrameLayout;
|
|
@@ -38,6 +40,7 @@ import org.chromium.ui.accessibility.AccessibilityState;
|
|
import org.chromium.ui.base.EventForwarder;
|
|
import org.chromium.ui.base.EventOffsetHandler;
|
|
import org.chromium.ui.dragdrop.DragEventDispatchHelper.DragEventDispatchDestination;
|
|
+import org.chromium.ui.base.ViewAndroidDelegate;
|
|
|
|
import java.util.function.Supplier;
|
|
|
|
@@ -92,6 +95,9 @@ public class ContentView extends FrameLayout
|
|
*/
|
|
public static ContentView createContentView(Context context,
|
|
@Nullable EventOffsetHandler eventOffsetHandler, @Nullable WebContents webContents) {
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
+ return new ContentViewWithAutofill(context, eventOffsetHandler, webContents);
|
|
+ }
|
|
return new ContentView(context, eventOffsetHandler, webContents);
|
|
}
|
|
|
|
@@ -638,4 +644,46 @@ public class ContentView extends FrameLayout
|
|
mDragDropEventOffsetHandler.onPostDispatchDragEvent(event.getAction());
|
|
return ret;
|
|
}
|
|
+
|
|
+ /**
|
|
+ * API level 26 implementation that includes autofill.
|
|
+ */
|
|
+ private static class ContentViewWithAutofill extends ContentView {
|
|
+ private ViewAndroidDelegate viewAndroidDelegate;
|
|
+
|
|
+ private ContentViewWithAutofill(
|
|
+ Context context, EventOffsetHandler eventOffsetHandler, WebContents webContents) {
|
|
+ super(context, eventOffsetHandler, webContents);
|
|
+
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
+ // The Autofill system-level infrastructure has heuristics for which Views it
|
|
+ // considers important for autofill; only these Views will be queried for their
|
|
+ // autofill structure on notifications that a new (virtual) View was entered. By
|
|
+ // default, FrameLayout is not considered important for autofill. Thus, for
|
|
+ // ContentView to be queried for its autofill structure, we must explicitly inform
|
|
+ // the autofill system that this View is important for autofill.
|
|
+ setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_YES);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setWebContents(WebContents webContents) {
|
|
+ viewAndroidDelegate = webContents.getViewAndroidDelegate();
|
|
+ super.setWebContents(webContents);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {
|
|
+ if (viewAndroidDelegate != null) {
|
|
+ viewAndroidDelegate.onProvideAutofillVirtualStructure(structure, flags);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void autofill(final SparseArray<AutofillValue> values) {
|
|
+ if (viewAndroidDelegate != null) {
|
|
+ viewAndroidDelegate.autofill(values);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
}
|
|
diff --git a/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java b/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java
|
|
index 1e1d4ea42cf90..595e6a703fff4 100644
|
|
--- a/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java
|
|
+++ b/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java
|
|
@@ -31,6 +31,10 @@ import org.chromium.ui.dragdrop.DragStateTracker;
|
|
import org.chromium.ui.dragdrop.DropDataAndroid;
|
|
import org.chromium.ui.mojom.CursorType;
|
|
|
|
+import android.util.SparseArray;
|
|
+import android.view.autofill.AutofillValue;
|
|
+import android.view.ViewStructure;
|
|
+
|
|
/**
|
|
* Class to acquire, position, and remove anchor views from the implementing View.
|
|
*/
|
|
@@ -578,4 +582,8 @@ public class ViewAndroidDelegate {
|
|
sDragAndDropDelegateForTesting = testDelegate;
|
|
ResettersForTesting.register(() -> sDragAndDropDelegateForTesting = null);
|
|
}
|
|
+
|
|
+ public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {}
|
|
+
|
|
+ public void autofill(final SparseArray<AutofillValue> values) {}
|
|
}
|