зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1770010 - Keep reference to autofill session instead of using mSession. r=jonalmeida,calu
The android autofill framework requests a virtual structure for the webpage in onProvideAutofillVirtualStructure and then at a later time will call autofill when the user selects a value in the autofill app. These method calls happen on the GeckoView, but our data is stored in the GeckoSession (or rather, in the window that the GeckoSession represents). Because of the asynchronicity of this process, we're not guaranteed that the app hasn't switched the sesssion behind us in between the onProvideAutofillVirtualStructure and autofill call, so we need to keep a reference to the current autofill session when onProvide is called so that we're sure that we are autofilling the right session. We use a WeakReference to avoid keeping a window alive more than necessary, as if the window is unloaded, there is no point in autofilling anyway. Differential Revision: https://phabricator.services.mozilla.com/D146724
This commit is contained in:
Родитель
b4111998a3
Коммит
31f8c98911
|
@ -374,6 +374,7 @@ package org.mozilla.geckoview {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Autofill.Session {
|
public static final class Autofill.Session {
|
||||||
|
method @UiThread public void autofill(@NonNull SparseArray<CharSequence>);
|
||||||
method @NonNull @UiThread public Autofill.NodeData dataFor(@NonNull Autofill.Node);
|
method @NonNull @UiThread public Autofill.NodeData dataFor(@NonNull Autofill.Node);
|
||||||
method @UiThread public void fillViewStructure(@NonNull View, @NonNull ViewStructure, int);
|
method @UiThread public void fillViewStructure(@NonNull View, @NonNull ViewStructure, int);
|
||||||
method @UiThread public void fillViewStructure(@NonNull Autofill.Node, @NonNull View, @NonNull ViewStructure, int);
|
method @UiThread public void fillViewStructure(@NonNull Autofill.Node, @NonNull View, @NonNull ViewStructure, int);
|
||||||
|
@ -886,7 +887,7 @@ package org.mozilla.geckoview {
|
||||||
ctor public GeckoSession();
|
ctor public GeckoSession();
|
||||||
ctor public GeckoSession(@Nullable GeckoSessionSettings);
|
ctor public GeckoSession(@Nullable GeckoSessionSettings);
|
||||||
method @NonNull @UiThread public GeckoDisplay acquireDisplay();
|
method @NonNull @UiThread public GeckoDisplay acquireDisplay();
|
||||||
method @UiThread public void autofill(@NonNull SparseArray<CharSequence>);
|
method @Deprecated @DeprecationSchedule(id="autofill-node",version=104) @UiThread public void autofill(@NonNull SparseArray<CharSequence>);
|
||||||
method @UiThread public void close();
|
method @UiThread public void close();
|
||||||
method @AnyThread public void exitFullScreen();
|
method @AnyThread public void exitFullScreen();
|
||||||
method @NonNull @UiThread public SessionAccessibility getAccessibility();
|
method @NonNull @UiThread public SessionAccessibility getAccessibility();
|
||||||
|
|
|
@ -219,7 +219,7 @@ class AutofillDelegateTest : BaseSessionTest() {
|
||||||
val nodes = mainSession.autofillSession.root
|
val nodes = mainSession.autofillSession.root
|
||||||
checkAutofillChild(nodes, "")
|
checkAutofillChild(nodes, "")
|
||||||
|
|
||||||
mainSession.autofill(autofillValues)
|
mainSession.autofillSession.autofill(autofillValues)
|
||||||
|
|
||||||
// Wait on the promises and check for correct values.
|
// Wait on the promises and check for correct values.
|
||||||
for (values in promises.map { it.value.asJsonArray() }) {
|
for (values in promises.map { it.value.asJsonArray() }) {
|
||||||
|
@ -252,7 +252,7 @@ class AutofillDelegateTest : BaseSessionTest() {
|
||||||
|
|
||||||
val autofillValues = SparseArray<CharSequence>()
|
val autofillValues = SparseArray<CharSequence>()
|
||||||
autofillValues.append(-1, "lobster")
|
autofillValues.append(-1, "lobster")
|
||||||
mainSession.autofill(autofillValues)
|
mainSession.autofillSession.autofill(autofillValues)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun countAutofillNodes(cond: (Autofill.Node) -> Boolean =
|
private fun countAutofillNodes(cond: (Autofill.Node) -> Boolean =
|
||||||
|
|
|
@ -1,9 +1,21 @@
|
||||||
package org.mozilla.geckoview.test
|
package org.mozilla.geckoview.test
|
||||||
|
|
||||||
|
import android.graphics.Matrix
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.LocaleList
|
||||||
|
import android.text.InputType
|
||||||
|
import android.util.Pair
|
||||||
|
import android.util.SparseArray
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewStructure
|
||||||
|
import android.view.autofill.AutofillId
|
||||||
|
import android.view.autofill.AutofillValue
|
||||||
import androidx.test.filters.LargeTest
|
import androidx.test.filters.LargeTest
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
||||||
|
import androidx.test.filters.SdkSuppress
|
||||||
|
|
||||||
import org.hamcrest.Matchers.equalTo
|
import org.hamcrest.Matchers.equalTo
|
||||||
import org.junit.*
|
import org.junit.*
|
||||||
|
@ -228,4 +240,204 @@ class GeckoViewTest : BaseSessionTest() {
|
||||||
high = listOf(mainSession), low = listOf(otherSession)
|
high = listOf(mainSession), low = listOf(otherSession)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun visit(node: MockViewStructure, callback: (MockViewStructure) -> Unit) {
|
||||||
|
callback(node)
|
||||||
|
|
||||||
|
for (child in node.children) {
|
||||||
|
if (child != null) {
|
||||||
|
visit(child, callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@NullDelegate(Autofill.Delegate::class)
|
||||||
|
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
|
||||||
|
fun autofillWithNoSession() {
|
||||||
|
mainSession.loadTestPath(FORMS_XORIGIN_HTML_PATH)
|
||||||
|
mainSession.waitForPageStop()
|
||||||
|
|
||||||
|
val autofills = mapOf(
|
||||||
|
"#user1" to "username@example.com",
|
||||||
|
"#user2" to "username@example.com",
|
||||||
|
"#pass1" to "test-password",
|
||||||
|
"#pass2" to "test-password")
|
||||||
|
|
||||||
|
// Set up promises to monitor the values changing.
|
||||||
|
val promises = autofills.map { entry ->
|
||||||
|
// Repeat each test with both the top document and the iframe document.
|
||||||
|
mainSession.evaluatePromiseJS("""
|
||||||
|
window.getDataForAllFrames('${entry.key}', '${entry.value}')
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
activityRule.scenario.onActivity {
|
||||||
|
val root = MockViewStructure(View.NO_ID)
|
||||||
|
it.view.onProvideAutofillVirtualStructure(root, 0)
|
||||||
|
|
||||||
|
val data = SparseArray<AutofillValue>()
|
||||||
|
visit(root) { node ->
|
||||||
|
if (node.hints?.indexOf(View.AUTOFILL_HINT_USERNAME) != -1) {
|
||||||
|
data.set(node.id, AutofillValue.forText("username@example.com"))
|
||||||
|
} else if (node.hints?.indexOf(View.AUTOFILL_HINT_PASSWORD) != -1) {
|
||||||
|
data.set(node.id, AutofillValue.forText("test-password"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Releasing the session will set mSession in GeckoView to null
|
||||||
|
// this test verifies that we can still autofill correctly even in released state
|
||||||
|
val session = it.view.releaseSession()!!
|
||||||
|
it.view.autofill(data)
|
||||||
|
|
||||||
|
// Put back the session and verifies that the autofill went through anyway
|
||||||
|
it.view.setSession(session)
|
||||||
|
|
||||||
|
// Wait on the promises and check for correct values.
|
||||||
|
for (values in promises.map { p -> p.value.asJsonArray() }) {
|
||||||
|
for (i in 0 until values.length()) {
|
||||||
|
val (key, actual, expected, eventInterface) = values.get(i).asJSList<String>()
|
||||||
|
|
||||||
|
assertThat("Auto-filled value must match ($key)", actual, equalTo(expected))
|
||||||
|
assertThat(
|
||||||
|
"input event should be dispatched with InputEvent interface",
|
||||||
|
eventInterface,
|
||||||
|
equalTo("InputEvent")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockViewStructure(var id: Int, var parent: MockViewStructure? = null) : ViewStructure() {
|
||||||
|
private var enabled: Boolean = false
|
||||||
|
private var inputType = 0
|
||||||
|
var children = Array<MockViewStructure?>(0, { null })
|
||||||
|
var childIndex = 0
|
||||||
|
var hints : Array<out String>? = null
|
||||||
|
|
||||||
|
override fun setId(p0: Int, p1: String?, p2: String?, p3: String?) {
|
||||||
|
id = p0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setEnabled(p0: Boolean) {
|
||||||
|
enabled = p0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setChildCount(p0: Int) {
|
||||||
|
children = Array(p0, { null })
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getChildCount(): Int {
|
||||||
|
return children.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newChild(p0: Int): ViewStructure {
|
||||||
|
val child = MockViewStructure(p0, this)
|
||||||
|
children[childIndex++] = child
|
||||||
|
return child
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun asyncNewChild(p0: Int): ViewStructure {
|
||||||
|
return newChild(p0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setInputType(p0: Int) {
|
||||||
|
inputType = p0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getInputType() : Int {
|
||||||
|
return inputType
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setAutofillHints(p0: Array<out String>?) {
|
||||||
|
hints = p0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addChildCount(p0: Int): Int {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setDimens(p0: Int, p1: Int, p2: Int, p3: Int, p4: Int, p5: Int) {}
|
||||||
|
override fun setTransformation(p0: Matrix?) {}
|
||||||
|
override fun setElevation(p0: Float) {}
|
||||||
|
override fun setAlpha(p0: Float) {}
|
||||||
|
override fun setVisibility(p0: Int) {}
|
||||||
|
override fun setClickable(p0: Boolean) {}
|
||||||
|
override fun setLongClickable(p0: Boolean) {}
|
||||||
|
override fun setContextClickable(p0: Boolean) {}
|
||||||
|
override fun setFocusable(p0: Boolean) {}
|
||||||
|
override fun setFocused(p0: Boolean) {}
|
||||||
|
override fun setAccessibilityFocused(p0: Boolean) {}
|
||||||
|
override fun setCheckable(p0: Boolean) {}
|
||||||
|
override fun setChecked(p0: Boolean) {}
|
||||||
|
override fun setSelected(p0: Boolean) {}
|
||||||
|
override fun setActivated(p0: Boolean) {}
|
||||||
|
override fun setOpaque(p0: Boolean) {}
|
||||||
|
override fun setClassName(p0: String?) {}
|
||||||
|
override fun setContentDescription(p0: CharSequence?) {}
|
||||||
|
override fun setText(p0: CharSequence?) {}
|
||||||
|
override fun setText(p0: CharSequence?, p1: Int, p2: Int) {}
|
||||||
|
override fun setTextStyle(p0: Float, p1: Int, p2: Int, p3: Int) {}
|
||||||
|
override fun setTextLines(p0: IntArray?, p1: IntArray?) {}
|
||||||
|
override fun setHint(p0: CharSequence?) {}
|
||||||
|
override fun getText(): CharSequence {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
override fun getTextSelectionStart(): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
override fun getTextSelectionEnd(): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
override fun getHint(): CharSequence {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
override fun getExtras(): Bundle {
|
||||||
|
return Bundle()
|
||||||
|
}
|
||||||
|
override fun hasExtras(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAutofillId(): AutofillId? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
override fun setAutofillId(p0: AutofillId) {}
|
||||||
|
override fun setAutofillId(p0: AutofillId, p1: Int) {}
|
||||||
|
override fun setAutofillType(p0: Int) {}
|
||||||
|
override fun setAutofillValue(p0: AutofillValue?) {}
|
||||||
|
override fun setAutofillOptions(p0: Array<out CharSequence>?) {}
|
||||||
|
override fun setDataIsSensitive(p0: Boolean) {}
|
||||||
|
override fun asyncCommit() {}
|
||||||
|
override fun setWebDomain(p0: String?) {}
|
||||||
|
override fun setLocaleList(p0: LocaleList?) {}
|
||||||
|
|
||||||
|
override fun newHtmlInfoBuilder(p0: String): HtmlInfo.Builder {
|
||||||
|
return MockHtmlInfoBuilder()
|
||||||
|
}
|
||||||
|
override fun setHtmlInfo(p0: HtmlInfo) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockHtmlInfoBuilder : ViewStructure.HtmlInfo.Builder() {
|
||||||
|
override fun addAttribute(p0: String, p1: String): ViewStructure.HtmlInfo.Builder {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun build(): ViewStructure.HtmlInfo {
|
||||||
|
return MockHtmlInfo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockHtmlInfo : ViewStructure.HtmlInfo() {
|
||||||
|
override fun getTag(): String {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAttributes(): MutableList<Pair<String, String>>? {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -257,6 +257,57 @@ public class Autofill {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform auto-fill using the specified values.
|
||||||
|
*
|
||||||
|
* @param values Map of auto-fill IDs to values.
|
||||||
|
*/
|
||||||
|
@UiThread
|
||||||
|
public void autofill(@NonNull final SparseArray<CharSequence> values) {
|
||||||
|
ThreadUtils.assertOnUiThread();
|
||||||
|
|
||||||
|
if (isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final HashMap<Node, GeckoBundle> valueBundles = new HashMap<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < values.size(); i++) {
|
||||||
|
final int id = values.keyAt(i);
|
||||||
|
final Node node = getNode(id);
|
||||||
|
if (node == null) {
|
||||||
|
Log.w(LOGTAG, "Could not find node id=" + id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final CharSequence value = values.valueAt(i);
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(LOGTAG, "Process autofill for id=" + id + ", value=" + value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node == getRoot()) {
|
||||||
|
// We cannot autofill the session root as it does not correspond to a
|
||||||
|
// real element on the page.
|
||||||
|
Log.w(LOGTAG, "Ignoring autofill on session root.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Node root = node.getRoot();
|
||||||
|
if (!valueBundles.containsKey(root)) {
|
||||||
|
valueBundles.put(root, new GeckoBundle());
|
||||||
|
}
|
||||||
|
valueBundles.get(root).putString(node.getUuid(), String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final Node root : valueBundles.keySet()) {
|
||||||
|
final NodeData data = dataFor(root);
|
||||||
|
Objects.requireNonNull(data);
|
||||||
|
final EventCallback callback = data.callback;
|
||||||
|
callback.sendSuccess(valueBundles.get(root));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* package */ void addRoot(@NonNull final Node node, final EventCallback callback) {
|
/* package */ void addRoot(@NonNull final Node node, final EventCallback callback) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(LOGTAG, "addRoot: " + node);
|
Log.d(LOGTAG, "addRoot: " + node);
|
||||||
|
@ -1085,57 +1136,6 @@ public class Autofill {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform auto-fill using the specified values.
|
|
||||||
*
|
|
||||||
* @param values Map of auto-fill IDs to values.
|
|
||||||
*/
|
|
||||||
@UiThread
|
|
||||||
public void autofill(final SparseArray<CharSequence> values) {
|
|
||||||
ThreadUtils.assertOnUiThread();
|
|
||||||
|
|
||||||
if (getAutofillSession().isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final HashMap<Node, GeckoBundle> valueBundles = new HashMap<>();
|
|
||||||
|
|
||||||
for (int i = 0; i < values.size(); i++) {
|
|
||||||
final int id = values.keyAt(i);
|
|
||||||
final Node node = getAutofillSession().getNode(id);
|
|
||||||
if (node == null) {
|
|
||||||
Log.w(LOGTAG, "Could not find node id=" + id);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
final CharSequence value = values.valueAt(i);
|
|
||||||
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(LOGTAG, "Process autofill for id=" + id + ", value=" + value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node == getAutofillSession().getRoot()) {
|
|
||||||
// We cannot autofill the session root as it does not correspond to a
|
|
||||||
// real element on the page.
|
|
||||||
Log.w(LOGTAG, "Ignoring autofill on session root.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Node root = node.getRoot();
|
|
||||||
if (!valueBundles.containsKey(root)) {
|
|
||||||
valueBundles.put(root, new GeckoBundle());
|
|
||||||
}
|
|
||||||
valueBundles.get(root).putString(node.getUuid(), String.valueOf(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final Node root : valueBundles.keySet()) {
|
|
||||||
final NodeData data = getAutofillSession().dataFor(root);
|
|
||||||
Objects.requireNonNull(data);
|
|
||||||
final EventCallback callback = data.callback;
|
|
||||||
callback.sendSuccess(valueBundles.get(root));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
public void setDelegate(final @Nullable Delegate delegate) {
|
public void setDelegate(final @Nullable Delegate delegate) {
|
||||||
ThreadUtils.assertOnUiThread();
|
ThreadUtils.assertOnUiThread();
|
||||||
|
|
|
@ -6482,10 +6482,13 @@ public class GeckoSession {
|
||||||
* Perform autofill using the specified values.
|
* Perform autofill using the specified values.
|
||||||
*
|
*
|
||||||
* @param values Map of autofill IDs to values.
|
* @param values Map of autofill IDs to values.
|
||||||
|
* @deprecated Use {@link Autofill.Session#autofill} instead.
|
||||||
*/
|
*/
|
||||||
@UiThread
|
@UiThread
|
||||||
|
@Deprecated
|
||||||
|
@DeprecationSchedule(id = "autofill-node", version = 104)
|
||||||
public void autofill(final @NonNull SparseArray<CharSequence> values) {
|
public void autofill(final @NonNull SparseArray<CharSequence> values) {
|
||||||
getAutofillSupport().autofill(values);
|
getAutofillSession().autofill(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -49,6 +49,7 @@ import androidx.annotation.UiThread;
|
||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import org.mozilla.gecko.AndroidGamepadManager;
|
import org.mozilla.gecko.AndroidGamepadManager;
|
||||||
import org.mozilla.gecko.EventDispatcher;
|
import org.mozilla.gecko.EventDispatcher;
|
||||||
|
@ -65,6 +66,8 @@ public class GeckoView extends FrameLayout {
|
||||||
|
|
||||||
private Integer mLastCoverColor;
|
private Integer mLastCoverColor;
|
||||||
protected @Nullable GeckoSession mSession;
|
protected @Nullable GeckoSession mSession;
|
||||||
|
WeakReference<Autofill.Session> mAutofillSession = new WeakReference<>(null);
|
||||||
|
|
||||||
// Whether this GeckoView instance has a session that is no longer valid, e.g. because the session
|
// Whether this GeckoView instance has a session that is no longer valid, e.g. because the session
|
||||||
// associated to this GeckoView was attached to a different GeckoView instance.
|
// associated to this GeckoView was attached to a different GeckoView instance.
|
||||||
private boolean mIsSessionPoisoned = false;
|
private boolean mIsSessionPoisoned = false;
|
||||||
|
@ -852,13 +855,20 @@ public class GeckoView extends FrameLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
final Autofill.Session autofillSession = mSession.getAutofillSession();
|
final Autofill.Session autofillSession = mSession.getAutofillSession();
|
||||||
|
|
||||||
|
// Let's store the session here in case we need to autofill it later
|
||||||
|
mAutofillSession = new WeakReference<>(autofillSession);
|
||||||
autofillSession.fillViewStructure(this, structure, flags);
|
autofillSession.fillViewStructure(this, structure, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@TargetApi(26)
|
@TargetApi(26)
|
||||||
public void autofill(@NonNull final SparseArray<AutofillValue> values) {
|
public void autofill(@NonNull final SparseArray<AutofillValue> values) {
|
||||||
if (mSession == null) {
|
// Note: we can't use mSession.getAutofillSession() because the app might have swapped
|
||||||
|
// the session under us between the onProvideAutofillVirtualStructure and this call
|
||||||
|
// so mSession could refer to a different session or we might not have a session at all.
|
||||||
|
final Autofill.Session session = mAutofillSession.get();
|
||||||
|
if (session == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final SparseArray<CharSequence> strValues = new SparseArray<>(values.size());
|
final SparseArray<CharSequence> strValues = new SparseArray<>(values.size());
|
||||||
|
@ -869,7 +879,7 @@ public class GeckoView extends FrameLayout {
|
||||||
strValues.put(values.keyAt(i), value.getTextValue());
|
strValues.put(values.keyAt(i), value.getTextValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mSession.autofill(strValues);
|
session.autofill(strValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -28,6 +28,8 @@ exclude: true
|
||||||
[`onSessionStart`][102.16].
|
[`onSessionStart`][102.16].
|
||||||
- Added [`PromptInstanceDelegate.onPromptUpdate`][102.17] to allow GeckoView to update current prompts.
|
- Added [`PromptInstanceDelegate.onPromptUpdate`][102.17] to allow GeckoView to update current prompts.
|
||||||
([bug 1758800]({{bugzilla}}1758800))
|
([bug 1758800]({{bugzilla}}1758800))
|
||||||
|
- Deprecated [`GeckoSession.autofill`][102.18], use [`Autofill.Session.autofill`][102.19] instead.
|
||||||
|
([bug 1770010]({{bugzilla}}1770010))
|
||||||
|
|
||||||
[102.1]: {{javadoc_uri}}/GeckoSession.PromptDelegate.DateTimePrompt.html#stepValue
|
[102.1]: {{javadoc_uri}}/GeckoSession.PromptDelegate.DateTimePrompt.html#stepValue
|
||||||
[102.2]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date#step
|
[102.2]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date#step
|
||||||
|
@ -46,6 +48,8 @@ exclude: true
|
||||||
[102.15]: {{javadoc_uri}}/Autofill.Delegate.html#onSessionCommit(org.mozilla.geckoview.GeckoSession,org.mozilla.geckoview.Autofill.Node,org.mozilla.geckoview.Autofill.NodeData)
|
[102.15]: {{javadoc_uri}}/Autofill.Delegate.html#onSessionCommit(org.mozilla.geckoview.GeckoSession,org.mozilla.geckoview.Autofill.Node,org.mozilla.geckoview.Autofill.NodeData)
|
||||||
[102.16]: {{javadoc_uri}}/Autofill.Delegate.html#onSessionStart(org.mozilla.geckoview.GeckoSession)
|
[102.16]: {{javadoc_uri}}/Autofill.Delegate.html#onSessionStart(org.mozilla.geckoview.GeckoSession)
|
||||||
[102.17]: {{javadoc_uri}}/GeckoSession.PromptDelegate.PromptInstanceDelegate.html#onPromptUpdate(org.mozilla.geckoview.GeckoSession.PromptDelegate.BasePrompt)
|
[102.17]: {{javadoc_uri}}/GeckoSession.PromptDelegate.PromptInstanceDelegate.html#onPromptUpdate(org.mozilla.geckoview.GeckoSession.PromptDelegate.BasePrompt)
|
||||||
|
[102.18]: {{javadoc_uri}}/GeckoSession.html#autofill(android.util.SparseArray)
|
||||||
|
[102.19]: {{javadoc_uri}}/Autofill.Session.html#autofill(android.util.SparseArray)
|
||||||
|
|
||||||
## v101
|
## v101
|
||||||
- Added [`GeckoDisplay.surfaceChanged`][101.1] function taking new type [`GeckoDisplay.SurfaceInfo`][101.2].
|
- Added [`GeckoDisplay.surfaceChanged`][101.1] function taking new type [`GeckoDisplay.SurfaceInfo`][101.2].
|
||||||
|
@ -1200,4 +1204,4 @@ to allow adding gecko profiler markers.
|
||||||
[65.24]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport(android.content.Context,android.os.Bundle,java.lang.String)
|
[65.24]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport(android.content.Context,android.os.Bundle,java.lang.String)
|
||||||
[65.25]: {{javadoc_uri}}/GeckoResult.html
|
[65.25]: {{javadoc_uri}}/GeckoResult.html
|
||||||
|
|
||||||
[api-version]: 1d08fc97ff3a0f8c0fc6c90b1ec1829da437ad90
|
[api-version]: dc9516b62971d881de4059eae2ea1b88a1ebb3d1
|
||||||
|
|
Загрузка…
Ссылка в новой задаче