Bug 1591462 - [2.1] Combine autofill mechanics in Java API. r=snorp,geckoview-reviewers,droeh

Differential Revision: https://phabricator.services.mozilla.com/D50631

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Eugen Sawin 2019-11-11 17:22:33 +00:00
Родитель 97c8819903
Коммит 0d2c69ca71
8 изменённых файлов: 1265 добавлений и 789 удалений

Просмотреть файл

@ -59,7 +59,7 @@ import java.util.List;
import java.util.Map;
import org.json.JSONObject;
import org.mozilla.geckoview.AllowOrDeny;
import org.mozilla.geckoview.AutofillElement;
import org.mozilla.geckoview.Autofill;
import org.mozilla.geckoview.CompositorController;
import org.mozilla.geckoview.ContentBlocking;
import org.mozilla.geckoview.ContentBlockingController;
@ -105,28 +105,65 @@ package org.mozilla.geckoview {
enum_constant public static final AllowOrDeny DENY;
}
public class AutofillElement {
ctor protected AutofillElement();
field public static final int HINT_EMAIL_ADDRESS = 0;
field public static final int HINT_NONE = -1;
field public static final int HINT_PASSWORD = 1;
field public static final int HINT_URL = 2;
field public static final int HINT_USERNAME = 3;
field public static final int INPUT_TYPE_NONE = -1;
field public static final int INPUT_TYPE_NUMBER = 1;
field public static final int INPUT_TYPE_PHONE = 2;
field public static final int INPUT_TYPE_TEXT = 0;
field @NonNull public final Map<String,String> attributes;
field @NonNull public final Collection<AutofillElement> children;
field @NonNull public final Rect dimensions;
field @NonNull public final String domain;
field public final boolean enabled;
field public final boolean focusable;
field public final boolean focused;
field public final int hint;
field public final int id;
field public final int inputType;
field @NonNull public final String tag;
public class Autofill {
ctor public Autofill();
}
public static interface Autofill.Delegate {
method @UiThread default public void onAutofill(@NonNull GeckoSession, int, @Nullable Autofill.Node);
}
public static final class Autofill.Hint {
method @AnyThread @Nullable public static String toString(int);
field public static final int EMAIL_ADDRESS = 0;
field public static final int NONE = -1;
field public static final int PASSWORD = 1;
field public static final int URI = 2;
field public static final int USERNAME = 3;
}
public static final class Autofill.InputType {
method @AnyThread @Nullable public static String toString(int);
field public static final int NONE = -1;
field public static final int NUMBER = 1;
field public static final int PHONE = 2;
field public static final int TEXT = 0;
}
public static final class Autofill.Node {
method @UiThread public void fillViewStructure(@NonNull View, @NonNull ViewStructure, int);
method @AnyThread @Nullable public String getAttribute(@NonNull String);
method @AnyThread @NonNull public Map<String,String> getAttributes();
method @AnyThread @NonNull public Collection<Autofill.Node> getChildren();
method @AnyThread @NonNull public Rect getDimensions();
method @AnyThread @NonNull public String getDomain();
method @AnyThread public boolean getEnabled();
method @AnyThread public boolean getFocusable();
method @AnyThread public boolean getFocused();
method @AnyThread public int getHint();
method @AnyThread public int getId();
method @AnyThread public int getInputType();
method @AnyThread @NonNull public String getTag();
method @AnyThread @NonNull public String getValue();
method @AnyThread public boolean getVisible();
}
public static final class Autofill.Notify {
method @AnyThread @Nullable public static String toString(int);
field public static final int NODE_ADDED = 3;
field public static final int NODE_BLURRED = 7;
field public static final int NODE_FOCUSED = 6;
field public static final int NODE_REMOVED = 4;
field public static final int NODE_UPDATED = 5;
field public static final int SESSION_CANCELED = 2;
field public static final int SESSION_COMMITTED = 1;
field public static final int SESSION_STARTED = 0;
}
public static final class Autofill.Session {
method @UiThread public void fillViewStructure(@NonNull View, @NonNull ViewStructure, int);
method @AnyThread @NonNull public Rect getDefaultDimensions();
method @AnyThread @NonNull public Autofill.Node getRoot();
}
@UiThread public class BasicSelectionActionDelegate implements ActionMode.Callback GeckoSession.SelectionActionDelegate {
@ -541,8 +578,8 @@ package org.mozilla.geckoview {
method @AnyThread @NonNull public static String createDataUri(@NonNull String, @Nullable String);
method @AnyThread public void exitFullScreen();
method @UiThread @NonNull public SessionAccessibility getAccessibility();
method @UiThread @Nullable public GeckoSession.AutofillDelegate getAutofillDelegate();
method @UiThread @NonNull public AutofillElement getAutofillElements();
method @UiThread @Nullable public Autofill.Delegate getAutofillDelegate();
method @UiThread @NonNull public Autofill.Session getAutofillSession();
method @UiThread public void getClientBounds(@NonNull RectF);
method @UiThread public void getClientToScreenMatrix(@NonNull Matrix);
method @UiThread public void getClientToSurfaceMatrix(@NonNull Matrix);
@ -594,7 +631,7 @@ package org.mozilla.geckoview {
method @AnyThread public void reload();
method @AnyThread public void restoreState(@NonNull GeckoSession.SessionState);
method @AnyThread public void setActive(boolean);
method @UiThread public void setAutofillDelegate(@Nullable GeckoSession.AutofillDelegate);
method @UiThread public void setAutofillDelegate(@Nullable Autofill.Delegate);
method @AnyThread public void setContentBlockingDelegate(@Nullable ContentBlocking.Delegate);
method @UiThread public void setContentDelegate(@Nullable GeckoSession.ContentDelegate);
method @AnyThread public void setFocused(boolean);
@ -628,18 +665,6 @@ package org.mozilla.geckoview {
field @Nullable protected GeckoSession.Window mWindow;
}
public static interface GeckoSession.AutofillDelegate {
method @UiThread default public void onAutofill(@NonNull GeckoSession, int, int);
field public static final int AUTOFILL_NOTIFY_CANCELED = 2;
field public static final int AUTOFILL_NOTIFY_COMMITTED = 1;
field public static final int AUTOFILL_NOTIFY_STARTED = 0;
field public static final int AUTOFILL_NOTIFY_VIEW_ADDED = 3;
field public static final int AUTOFILL_NOTIFY_VIEW_ENTERED = 6;
field public static final int AUTOFILL_NOTIFY_VIEW_EXITED = 7;
field public static final int AUTOFILL_NOTIFY_VIEW_REMOVED = 4;
field public static final int AUTOFILL_NOTIFY_VIEW_UPDATED = 5;
}
public static interface GeckoSession.ContentDelegate {
method @UiThread default public void onCloseRequest(@NonNull GeckoSession);
method @UiThread default public void onContextMenu(@NonNull GeckoSession, int, int, @NonNull GeckoSession.ContentDelegate.ContextElement);

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,242 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.geckoview;
import android.graphics.Rect;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.v4.util.ArrayMap;
import android.view.View;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Map;
/**
* Represents a single autofill element.
*/
public class AutofillElement {
@Retention(RetentionPolicy.SOURCE)
@IntDef({HINT_NONE, HINT_EMAIL_ADDRESS, HINT_PASSWORD, HINT_URL, HINT_USERNAME})
/* package */ @interface AutofillHint {}
/**
* Hint indicating that no special handling is required.
*/
public static final int HINT_NONE = -1;
/**
* Hint indicating that an element represents an email address.
*/
public static final int HINT_EMAIL_ADDRESS = 0;
/**
* Hint indicating that an element represents a password.
*/
public static final int HINT_PASSWORD = 1;
/**
* Hint indicating that an element represents an URL.
*/
public static final int HINT_URL = 2;
/**
* Hint indicating that an element represents a username.
*/
public static final int HINT_USERNAME = 3;
@Retention(RetentionPolicy.SOURCE)
@IntDef({INPUT_TYPE_NONE, INPUT_TYPE_TEXT, INPUT_TYPE_NUMBER, INPUT_TYPE_PHONE})
/* package */ @interface AutofillInputType {}
/**
* Indicates that an element is not a known input type.
*/
public static final int INPUT_TYPE_NONE = -1;
/**
* Indicates that an element is a text input type (e.g., {@code <input type="text">})
*/
public static final int INPUT_TYPE_TEXT = 0;
/**
* Indicates that an element is a number input type (e.g., {@code <input type="number">})
*/
public static final int INPUT_TYPE_NUMBER = 1;
/**
* Indicates that an element is a phone input type (e.g., {@code <input type="tel">})
*/
public static final int INPUT_TYPE_PHONE = 2;
/**
* A unique (within this page) id for this element.
*/
public final int id;
/**
* The dimensions of this element in CSS coordinates.
*/
public final @NonNull Rect dimensions;
/**
* The collection of child elements for this element.
*/
public final @NonNull Collection<AutofillElement> children;
/**
* The HTML attributes for this element.
*/
public final @NonNull Map<String, String> attributes;
/**
* Whether or not this element is enabled.
*/
public final boolean enabled;
/**
* Whether or not this element is focusable.
*/
public final boolean focusable;
/**
* Whether or not this element is focused.
*/
public final boolean focused;
/**
* A hint for the type of data contained in this element, if any.
*/
public final @AutofillHint int hint;
/**
* The input type of this element, if any.
*/
public final @AutofillInputType int inputType;
/**
* The HTML tag for this element.
*/
public final @NonNull String tag;
/**
* The web domain for this element.
*/
public final @NonNull String domain;
private AutofillElement(final Builder builder) {
id = builder.mId;
dimensions = builder.mDimensions != null ? builder.mDimensions : new Rect(0, 0, 0, 0);
attributes = Collections.unmodifiableMap(builder.mAttributes != null ? builder.mAttributes : new ArrayMap<>());
enabled = builder.mEnabled;
focusable = builder.mFocusable;
focused = builder.mFocused;
hint = builder.mHint;
inputType = builder.mInputType;
tag = builder.mTag;
domain = builder.mDomain;
if (builder.mChildren != null) {
LinkedList<AutofillElement> children = new LinkedList<>();
for (Builder child : builder.mChildren) {
children.add(child.build());
}
this.children = children;
} else {
this.children = new LinkedList<>();
}
}
protected AutofillElement() {
id = View.NO_ID;
dimensions = new Rect(0, 0, 0, 0);
attributes = Collections.unmodifiableMap(new ArrayMap<>());
enabled = false;
focusable = false;
focused = false;
hint = HINT_NONE;
inputType = INPUT_TYPE_NONE;
tag = "";
domain = "";
children = new LinkedList<>();
}
/* package */ static class Builder {
private int mId = View.NO_ID;
private Rect mDimensions;
private LinkedList<Builder> mChildren;
private ArrayMap<String, String> mAttributes;
private boolean mEnabled;
private boolean mFocusable;
private boolean mFocused;
private int mHint = HINT_NONE;
private int mInputType = INPUT_TYPE_NONE;
private String mTag = "";
private String mDomain = "";
public void dimensions(final Rect rect) {
mDimensions = rect;
}
public AutofillElement build() {
return new AutofillElement(this);
}
public void id(final int id) {
mId = id;
}
public Builder child() {
if (mChildren == null) {
mChildren = new LinkedList<>();
}
final Builder child = new Builder();
mChildren.add(child);
return child;
}
public void attribute(final String key, final String value) {
if (mAttributes == null) {
mAttributes = new ArrayMap<>();
}
mAttributes.put(key, value);
}
public void enabled(final boolean enabled) {
mEnabled = enabled;
}
public void focusable(final boolean focusable) {
mFocusable = focusable;
}
public void focused(final boolean focused) {
mFocused = focused;
}
public void hint(final int hint) {
mHint = hint;
}
public void inputType(final int inputType) {
mInputType = inputType;
}
public void tag(final String tag) {
mTag = tag;
}
public void domain(final String domain) {
mDomain = domain;
}
}
}

Просмотреть файл

@ -1,321 +0,0 @@
package org.mozilla.geckoview;
import android.graphics.Rect;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle;
/* package */ class AutofillSupport {
private static final String LOGTAG = "AutofillSupport";
private static final boolean DEBUG = false;
private final GeckoSession mSession;
private GeckoSession.AutofillDelegate mDelegate;
private SparseArray<GeckoBundle> mAutofillNodes;
private SparseArray<EventCallback> mAutofillRoots;
private int mAutofillFocusedId = View.NO_ID;
private int mAutofillFocusedRoot = View.NO_ID;
public AutofillSupport(final GeckoSession session) {
mSession = session;
session.getEventDispatcher().registerUiThreadListener(
new BundleEventListener() {
@Override
public void handleMessage(final String event, final GeckoBundle message,
final EventCallback callback) {
if ("GeckoView:AddAutofill".equals(event)) {
addAutofill(message, callback);
} else if ("GeckoView:ClearAutofill".equals(event)) {
clearAutofill();
} else if ("GeckoView:OnAutofillFocus".equals(event)) {
onAutofillFocus(message);
}
}
},
"GeckoView:AddAutofill",
"GeckoView:ClearAutofill",
"GeckoView:OnAutofillFocus",
null);
}
/**
* Perform auto-fill using the specified values.
*
* @param values Map of auto-fill IDs to values.
*/
public void autofill(final SparseArray<CharSequence> values) {
if (mAutofillRoots == null) {
return;
}
GeckoBundle response = null;
EventCallback callback = null;
for (int i = 0; i < values.size(); i++) {
final int id = values.keyAt(i);
final CharSequence value = values.valueAt(i);
if (DEBUG) {
Log.d(LOGTAG, "autofill(" + id + ')');
}
int rootId = id;
for (int currentId = id; currentId != View.NO_ID; ) {
final GeckoBundle bundle = mAutofillNodes.get(currentId);
if (bundle == null) {
return;
}
rootId = currentId;
currentId = bundle.getInt("parent", View.NO_ID);
}
final EventCallback newCallback = mAutofillRoots.get(rootId);
if (callback == null || newCallback != callback) {
if (callback != null) {
callback.sendSuccess(response);
}
response = new GeckoBundle(values.size() - i);
callback = newCallback;
}
response.putString(String.valueOf(id), String.valueOf(value));
}
if (callback != null) {
callback.sendSuccess(response);
}
}
public void setDelegate(final @Nullable GeckoSession.AutofillDelegate delegate) {
mDelegate = delegate;
}
public @Nullable GeckoSession.AutofillDelegate getDelegate() {
return mDelegate;
}
public @NonNull AutofillElement getAutofillElements() {
final AutofillElement.Builder builder = new AutofillElement.Builder();
final Rect rect = getDummyAutofillRect(mSession, false, null);
builder.dimensions(rect);
if (mAutofillRoots != null) {
final int size = mAutofillRoots.size();
for (int i = 0; i < size; i++) {
final int id = mAutofillRoots.keyAt(i);
final GeckoBundle root = mAutofillNodes.get(id);
fillAutofillElement(id, root, rect, builder.child());
}
}
return builder.build();
}
private void fillAutofillElement(final int id, final GeckoBundle bundle, final Rect rect, final AutofillElement.Builder builder) {
builder.id(id);
builder.domain(bundle.getString("origin"));
if (mAutofillFocusedRoot != View.NO_ID && mAutofillFocusedRoot == bundle.getInt("root", View.NO_ID)) {
builder.dimensions(rect);
}
final GeckoBundle[] children = bundle.getBundleArray("children");
if (children != null) {
for (final GeckoBundle childBundle : children) {
final int childId = childBundle.getInt("id");
fillAutofillElement(childId, childBundle, rect, builder.child());
mAutofillNodes.append(childId, childBundle);
}
}
String tag = bundle.getString("tag", "");
builder.tag(tag.toLowerCase());
final String type = bundle.getString("type", "text");
final GeckoBundle attrs = bundle.getBundle("attributes");
for (final String key : attrs.keys()) {
builder.attribute(key, String.valueOf(attrs.get(key)));
}
if ("INPUT".equals(tag) && !bundle.getBoolean("editable", false)) {
tag = ""; // Don't process non-editable inputs (e.g. type="button").
}
switch (tag) {
case "INPUT":
case "TEXTAREA": {
final boolean disabled = bundle.getBoolean("disabled");
builder.enabled(!disabled);
builder.focusable(!disabled);
builder.focused(id == mAutofillFocusedId);
break;
}
default:
break;
}
switch (type) {
case "email":
builder.hint(AutofillElement.HINT_EMAIL_ADDRESS);
builder.inputType(AutofillElement.INPUT_TYPE_TEXT);
break;
case "number":
builder.inputType(AutofillElement.INPUT_TYPE_NUMBER);
break;
case "password":
builder.hint(AutofillElement.HINT_PASSWORD);
builder.inputType(AutofillElement.INPUT_TYPE_TEXT);
break;
case "tel":
builder.inputType(AutofillElement.INPUT_TYPE_PHONE);
break;
case "url":
builder.hint(AutofillElement.HINT_URL);
builder.inputType(AutofillElement.INPUT_TYPE_TEXT);
break;
case "text":
final String autofillhint = bundle.getString("autofillhint", "");
if (autofillhint.equals("username")) {
builder.hint(AutofillElement.HINT_USERNAME);
builder.inputType(AutofillElement.INPUT_TYPE_TEXT);
}
break;
}
}
/* package */ void addAutofill(@NonNull final GeckoBundle message,
@NonNull final EventCallback callback) {
final boolean initializing;
if (mAutofillRoots == null) {
mAutofillRoots = new SparseArray<>();
mAutofillNodes = new SparseArray<>();
initializing = true;
} else {
initializing = false;
}
final int id = message.getInt("id");
if (DEBUG) {
Log.d(LOGTAG, "addAutofill(" + id + ')');
}
mAutofillRoots.append(id, callback);
populateAutofillNodes(message);
if (mDelegate == null) {
return;
}
if (initializing) {
mDelegate.onAutofill(
mSession, GeckoSession.AutofillDelegate.AUTOFILL_NOTIFY_STARTED, id);
} else {
mDelegate.onAutofill(
mSession, GeckoSession.AutofillDelegate.AUTOFILL_NOTIFY_VIEW_ADDED, id);
}
}
private void populateAutofillNodes(final GeckoBundle bundle) {
final int id = bundle.getInt("id");
mAutofillNodes.append(id, bundle);
final GeckoBundle[] children = bundle.getBundleArray("children");
if (children != null) {
for (GeckoBundle child : children) {
populateAutofillNodes(child);
}
}
}
/* package */ void clearAutofill() {
if (mAutofillRoots == null) {
return;
}
if (DEBUG) {
Log.d(LOGTAG, "clearAutofill()");
}
mAutofillRoots = null;
mAutofillNodes = null;
mAutofillFocusedId = View.NO_ID;
mAutofillFocusedRoot = View.NO_ID;
if (mDelegate != null) {
mDelegate.onAutofill(
mSession, GeckoSession.AutofillDelegate.AUTOFILL_NOTIFY_CANCELED, View.NO_ID);
}
}
/* package */ void onAutofillFocus(@Nullable final GeckoBundle message) {
if (mAutofillRoots == null) {
return;
}
final int id;
final int root;
if (message != null) {
id = message.getInt("id");
root = message.getInt("root");
} else {
id = root = View.NO_ID;
}
if (DEBUG) {
Log.d(LOGTAG, "onAutofillFocus(" + id + ')');
}
if (mAutofillFocusedId == id) {
return;
}
if (mDelegate != null && mAutofillFocusedId != View.NO_ID) {
mDelegate.onAutofill(
mSession, GeckoSession.AutofillDelegate.AUTOFILL_NOTIFY_VIEW_EXITED,
mAutofillFocusedId);
}
mAutofillFocusedId = id;
mAutofillFocusedRoot = root;
if (mDelegate != null && mAutofillFocusedId != View.NO_ID) {
mDelegate.onAutofill(
mSession, GeckoSession.AutofillDelegate.AUTOFILL_NOTIFY_VIEW_ENTERED,
mAutofillFocusedId);
}
}
/* package */ static Rect getDummyAutofillRect(@NonNull final GeckoSession session,
final boolean screen,
@Nullable final View view) {
final Rect rect = new Rect();
session.getSurfaceBounds(rect);
if (screen) {
if (view == null) {
throw new IllegalArgumentException();
}
final int[] offset = new int[2];
view.getLocationOnScreen(offset);
rect.offset(offset[0], offset[1]);
}
return rect;
}
public void onActiveChanged(final boolean active) {
if (mDelegate == null || mAutofillFocusedId == View.NO_ID) {
return;
}
// We blur/focus the active element (if we have one) when the document is made inactive/active.
getDelegate().onAutofill(
mSession,
active ? GeckoSession.AutofillDelegate.AUTOFILL_NOTIFY_VIEW_ENTERED
: GeckoSession.AutofillDelegate.AUTOFILL_NOTIFY_VIEW_EXITED,
mAutofillFocusedId);
}
}

Просмотреть файл

@ -64,7 +64,6 @@ import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.View;
import android.view.ViewStructure;
import android.view.autofill.AutofillManager;
public class GeckoSession implements Parcelable {
private static final String LOGTAG = "GeckoSession";
@ -124,7 +123,7 @@ public class GeckoSession implements Parcelable {
private OverscrollEdgeEffect mOverscroll;
private DynamicToolbarAnimator mToolbar;
private CompositorController mController;
private AutofillSupport mAutofillSupport;
private Autofill.Support mAutofillSupport;
private boolean mAttachedCompositor;
private boolean mCompositorReady;
@ -1523,7 +1522,7 @@ public class GeckoSession implements Parcelable {
mTextInput.onWindowChanged(mWindow);
}
if ((change == WINDOW_CLOSE || change == WINDOW_TRANSFER_OUT) && !inProgress) {
getAutofillSupport().clearAutofill();
getAutofillSupport().clear();
}
}
@ -5691,59 +5690,10 @@ public class GeckoSession implements Parcelable {
})
/* package */ @interface VisitFlags {}
@Retention(RetentionPolicy.SOURCE)
@IntDef({
AutofillDelegate.AUTOFILL_NOTIFY_STARTED,
AutofillDelegate.AUTOFILL_NOTIFY_COMMITTED,
AutofillDelegate.AUTOFILL_NOTIFY_CANCELED,
AutofillDelegate.AUTOFILL_NOTIFY_VIEW_ADDED,
AutofillDelegate.AUTOFILL_NOTIFY_VIEW_REMOVED,
AutofillDelegate.AUTOFILL_NOTIFY_VIEW_UPDATED,
AutofillDelegate.AUTOFILL_NOTIFY_VIEW_ENTERED,
AutofillDelegate.AUTOFILL_NOTIFY_VIEW_EXITED})
/* package */ @interface AutofillNotification {}
public interface AutofillDelegate {
/** An autofill session has started, usually as a result of loading a page. */
int AUTOFILL_NOTIFY_STARTED = 0;
/** An autofill session has been committed, usually as a result of submitting a form. */
int AUTOFILL_NOTIFY_COMMITTED = 1;
/** An autofill session has been canceled, usually as a result of unloading a page. */
int AUTOFILL_NOTIFY_CANCELED = 2;
/** A view within the autofill session has been added. */
int AUTOFILL_NOTIFY_VIEW_ADDED = 3;
/** A view within the autofill session has been removed. */
int AUTOFILL_NOTIFY_VIEW_REMOVED = 4;
/** A view within the autofill session has been updated (e.g. change in state). */
int AUTOFILL_NOTIFY_VIEW_UPDATED = 5;
/** A view within the autofill session has gained focus. */
int AUTOFILL_NOTIFY_VIEW_ENTERED = 6;
/** A view within the autofill session has lost focus. */
int AUTOFILL_NOTIFY_VIEW_EXITED = 7;
/**
* Notify that an autofill event has occurred. The default implementation forwards the
* notification to the system {@link AutofillManager}. This method is
* only called on Android 6.0 and above, and it is called in viewless mode as well.
*
* @param session Session instance.
* @param notification Notification type as one of the {@link #AUTOFILL_NOTIFY_STARTED
* AUTO_FILL_NOTIFY_*} constants.
* @param virtualId Virtual ID of the target, or {@link View#NO_ID} if not
* applicable. The ID matches one of the virtual IDs provided by {@link
* GeckoSession#getAutofillElements()} and can be used
* with {@link GeckoSession#autofill}.
*/
@UiThread
default void onAutofill(@NonNull GeckoSession session,
@GeckoSession.AutofillNotification int notification,
int virtualId) {}
}
private AutofillSupport getAutofillSupport() {
private Autofill.Support getAutofillSupport() {
if (mAutofillSupport == null) {
mAutofillSupport = new AutofillSupport(this);
mAutofillSupport = new Autofill.Support(this);
}
return mAutofillSupport;
@ -5752,18 +5702,18 @@ public class GeckoSession implements Parcelable {
/**
* Sets the autofill delegate for this session.
*
* @param delegate An instance of {@link AutofillDelegate}.
* @param delegate An instance of {@link Autofill.Delegate}.
*/
@UiThread
public void setAutofillDelegate(final @Nullable AutofillDelegate delegate) {
public void setAutofillDelegate(final @Nullable Autofill.Delegate delegate) {
getAutofillSupport().setDelegate(delegate);
}
/**
* @return The current {@link AutofillDelegate} for this session, if any.
* @return The current {@link Autofill.Delegate} for this session, if any.
*/
@UiThread
public @Nullable AutofillDelegate getAutofillDelegate() {
public @Nullable Autofill.Delegate getAutofillDelegate() {
return getAutofillSupport().getDelegate();
}
@ -5786,7 +5736,7 @@ public class GeckoSession implements Parcelable {
* @return The elements available for autofill.
*/
@UiThread
public @NonNull AutofillElement getAutofillElements() {
return getAutofillSupport().getAutofillElements();
public @NonNull Autofill.Session getAutofillSession() {
return getAutofillSupport().getAutofillSession();
}
}

Просмотреть файл

@ -33,7 +33,6 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.v4.view.ViewCompat;
import android.text.InputType;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.SparseArray;
@ -68,7 +67,7 @@ public class GeckoView extends FrameLayout {
private boolean mAutofillEnabled = true;
private GeckoSession.SelectionActionDelegate mSelectionActionDelegate;
private GeckoSession.AutofillDelegate mAutofillDelegate;
private Autofill.Delegate mAutofillDelegate;
private static class SavedState extends BaseSavedState {
public final GeckoSession session;
@ -734,96 +733,8 @@ public class GeckoView extends FrameLayout {
return;
}
final AutofillElement root = mSession.getAutofillElements();
fillViewStructure(root, structure, flags);
}
@TargetApi(23)
private void fillViewStructure(final AutofillElement element, final ViewStructure structure, final int flags) {
if (Build.VERSION.SDK_INT >= 26) {
structure.setAutofillId(getAutofillId(), element.id);
structure.setWebDomain(element.domain);
}
structure.setId(element.id, null, null, null);
structure.setDimens(0, 0, 0, 0, element.dimensions.width(), element.dimensions.height());
if (Build.VERSION.SDK_INT >= 26) {
final ViewStructure.HtmlInfo.Builder htmlBuilder = structure.newHtmlInfoBuilder(element.tag);
for (final String key : element.attributes.keySet()) {
htmlBuilder.addAttribute(key, String.valueOf(element.attributes.get(key)));
}
structure.setHtmlInfo(htmlBuilder.build());
}
structure.setChildCount(element.children.size());
int childCount = 0;
for (final AutofillElement child : element.children) {
final ViewStructure childStructure = structure.newChild(childCount);
fillViewStructure(child, childStructure, flags);
childCount++;
}
switch (element.tag) {
case "input":
case "textarea":
structure.setClassName("android.widget.EditText");
structure.setEnabled(element.enabled);
structure.setFocusable(element.focusable);
structure.setFocused(element.focused);
structure.setVisibility(View.VISIBLE);
if (Build.VERSION.SDK_INT >= 26) {
structure.setAutofillType(View.AUTOFILL_TYPE_TEXT);
}
break;
default:
if (childCount > 0) {
structure.setClassName("android.view.ViewGroup");
} else {
structure.setClassName("android.view.View");
}
break;
}
if (Build.VERSION.SDK_INT >= 26 && "input".equals(element.tag)) {
// LastPass will fill password to the field that setAutofillHints is unset and setInputType is set.
switch (element.hint) {
case AutofillElement.HINT_EMAIL_ADDRESS:
structure.setAutofillHints(new String[] { View.AUTOFILL_HINT_EMAIL_ADDRESS });
structure.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
break;
case AutofillElement.HINT_PASSWORD:
structure.setAutofillHints(new String[] { View.AUTOFILL_HINT_PASSWORD });
structure.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD);
break;
case AutofillElement.HINT_URL:
structure.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_VARIATION_URI);
break;
case AutofillElement.HINT_USERNAME:
structure.setAutofillHints(new String[] { View.AUTOFILL_HINT_USERNAME });
structure.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT);
break;
}
switch (element.inputType) {
case AutofillElement.INPUT_TYPE_NUMBER:
structure.setInputType(InputType.TYPE_CLASS_NUMBER);
break;
case AutofillElement.INPUT_TYPE_PHONE:
structure.setAutofillHints(new String[] { View.AUTOFILL_HINT_PHONE });
structure.setInputType(InputType.TYPE_CLASS_PHONE);
break;
default:
break;
}
}
final Autofill.Session autofillSession = mSession.getAutofillSession();
autofillSession.fillViewStructure(this, structure, flags);
}
@Override
@ -862,7 +773,7 @@ public class GeckoView extends FrameLayout {
/**
* Sets whether or not this View participates in Android autofill.
*
* When enabled, this will set an {@link GeckoSession.AutofillDelegate} on the
* When enabled, this will set an {@link Autofill.Delegate} on the
* {@link GeckoSession} for this instance.
*
* @param enabled Whether or not Android autofill is enabled for this view.
@ -888,35 +799,16 @@ public class GeckoView extends FrameLayout {
return mAutofillEnabled;
}
private class AndroidAutofillDelegate implements GeckoSession.AutofillDelegate {
private AutofillElement findElementWithId(final AutofillElement root, final int id) {
if (root.id == id) {
return root;
}
for (AutofillElement child : root.children) {
final AutofillElement found = findElementWithId(child, id);
if (found != null) {
return found;
}
}
return null;
}
private class AndroidAutofillDelegate implements Autofill.Delegate {
private Rect displayRectForId(@NonNull final GeckoSession session,
@NonNull final int virtualId,
@Nullable final View view) {
final AutofillElement structure = session.getAutofillElements();
final AutofillElement element = findElementWithId(structure, virtualId);
if (element == null) {
@NonNull final Autofill.Node node) {
if (node == null) {
return new Rect(0, 0, 0, 0);
}
final Matrix matrix = new Matrix();
final RectF rectF = new RectF(element.dimensions);
final RectF rectF = new RectF(node.getDimensions());
session.getPageToScreenMatrix(matrix);
matrix.mapRect(rectF);
@ -927,8 +819,8 @@ public class GeckoView extends FrameLayout {
@Override
public void onAutofill(@NonNull final GeckoSession session,
@GeckoSession.AutofillNotification final int notification,
final int virtualId) {
final int notification,
final Autofill.Node node) {
ThreadUtils.assertOnUiThread();
if (Build.VERSION.SDK_INT < 26) {
return;
@ -941,21 +833,21 @@ public class GeckoView extends FrameLayout {
}
switch (notification) {
case AUTOFILL_NOTIFY_STARTED:
case Autofill.Notify.SESSION_STARTED:
// This line seems necessary for auto-fill to work on the initial page.
case Autofill.Notify.SESSION_CANCELED:
manager.cancel();
break;
case AUTOFILL_NOTIFY_COMMITTED:
case Autofill.Notify.SESSION_COMMITTED:
manager.commit();
break;
case AUTOFILL_NOTIFY_CANCELED:
manager.cancel();
case Autofill.Notify.NODE_FOCUSED:
manager.notifyViewEntered(
GeckoView.this, node.getId(),
displayRectForId(session, node));
break;
case AUTOFILL_NOTIFY_VIEW_ENTERED:
manager.notifyViewEntered(GeckoView.this, virtualId, displayRectForId(session, virtualId, GeckoView.this));
break;
case AUTOFILL_NOTIFY_VIEW_EXITED:
manager.notifyViewExited(GeckoView.this, virtualId);
case Autofill.Notify.NODE_BLURRED:
manager.notifyViewExited(GeckoView.this, node.getId());
break;
}
}

Просмотреть файл

@ -69,6 +69,9 @@ exclude: true
([bug 1402369]({{bugzilla}}1402369))
- Added [`GeckoDisplay.screenshot`][71.23] allowing apps finer grain control over screenshots.
([bug 1577192]({{bugzilla}}1577192))
- ⚠️ Refactored `AutofillElement` and `AutofillSupport` into the
[`Autofill`][71.24] API.
([bug 1591462]({{bugzilla}}1591462))
[71.1]: {{javadoc_uri}}/RuntimeTelemetry.Delegate.html#onBooleanScalar-org.mozilla.geckoview.RuntimeTelemetry.Metric-
[71.2]: {{javadoc_uri}}/RuntimeTelemetry.Delegate.html#onLongScalar-org.mozilla.geckoview.RuntimeTelemetry.Metric-
@ -92,6 +95,7 @@ exclude: true
[71.21]: {{javadoc_uri}}/GeckoView.html#setAutofillEnabled-boolean-
[71.22]: {{javadoc_uri}}/GeckoSession.PromptDelegate.html#onSharePrompt-org.mozilla.geckoview.GeckoSession-org.mozilla.geckoview.GeckoSession.PromptDelegate.SharePrompt-
[71.23]: {{javadoc_uri}}/GeckoDisplay.html#screenshot--
[71.24]: {{javadoc_uri}}/Autofill.html
## v70
- Added API for session context assignment
@ -414,4 +418,4 @@ exclude: true
[65.24]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
[65.25]: {{javadoc_uri}}/GeckoResult.html
[api-version]: c01c3629db0185ce919b026400328804d27985a6
[api-version]: b8934264da833b5d36110e5d8136a0feef40623d

Просмотреть файл

@ -94,6 +94,7 @@ class GeckoViewAutofill {
root,
tag: element.tagName,
type: element instanceof window.HTMLInputElement ? element.type : null,
value: element.value,
editable:
element instanceof window.HTMLInputElement &&
[