зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1763570 - Wait for APZ state to set autofill information. r=geckoview-reviewers,owlish
When setting focus to input element, Gecko sets focused element to central via `zoomToFocusedInput`. So when we receives `focusin` event, content may be scrolled and zoomed. To pass correct element rectangle, we have to wait until it is completed. Fennec added `PanZoom:StateChange` event to listen APZ state. So GV should use same way. Differential Revision: https://phabricator.services.mozilla.com/D150453
This commit is contained in:
Родитель
f81ba5c480
Коммит
c69c9f0882
|
@ -1473,13 +1473,19 @@ bool BrowserChild::NotifyAPZStateChange(
|
|||
const layers::GeckoContentController::APZStateChange& aChange,
|
||||
const int& aArg) {
|
||||
mAPZEventState->ProcessAPZStateChange(aViewId, aChange, aArg);
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (aChange ==
|
||||
layers::GeckoContentController::APZStateChange::eTransformEnd) {
|
||||
// This is used by tests to determine when the APZ is done doing whatever
|
||||
// it's doing. XXX generify this as needed when writing additional tests.
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
observerService->NotifyObservers(nullptr, "APZ:TransformEnd", nullptr);
|
||||
observerService->NotifyObservers(nullptr, "PanZoom:StateChange",
|
||||
u"NOTHING");
|
||||
} else if (aChange ==
|
||||
layers::GeckoContentController::APZStateChange::eTransformBegin) {
|
||||
observerService->NotifyObservers(nullptr, "PanZoom:StateChange",
|
||||
u"PANNING");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -47,11 +47,22 @@ class GeckoViewAutoFillChild extends GeckoViewActorChild {
|
|||
break;
|
||||
}
|
||||
case "focusin": {
|
||||
if (
|
||||
this.contentWindow.HTMLInputElement.isInstance(aEvent.composedTarget)
|
||||
) {
|
||||
this.onFocus(aEvent.composedTarget);
|
||||
const element = aEvent.composedTarget;
|
||||
if (!this.contentWindow.HTMLInputElement.isInstance(element)) {
|
||||
break;
|
||||
}
|
||||
GeckoViewUtils.waitForPanZoomState(this.contentWindow).finally(() => {
|
||||
if (Cu.isDeadWrapper(element)) {
|
||||
// Focus element is removed or document is navigated to new page.
|
||||
return;
|
||||
}
|
||||
const focusedElement =
|
||||
Services.focus.focusedElement ||
|
||||
element.ownerDocument?.activeElement;
|
||||
if (element == focusedElement) {
|
||||
this.onFocus(focusedElement);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "focusout": {
|
||||
|
|
|
@ -6,6 +6,7 @@ package org.mozilla.geckoview.test
|
|||
|
||||
import androidx.test.filters.MediumTest
|
||||
import android.util.SparseArray
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import org.hamcrest.Matchers.*
|
||||
import org.junit.Test
|
||||
|
@ -13,6 +14,7 @@ import org.junit.runner.RunWith
|
|||
import org.junit.runners.Parameterized
|
||||
import org.mozilla.geckoview.Autofill
|
||||
import org.mozilla.geckoview.GeckoSession
|
||||
import org.mozilla.geckoview.GeckoSession.TextInputDelegate
|
||||
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.*
|
||||
|
||||
@RunWith(Parameterized::class)
|
||||
|
@ -510,4 +512,26 @@ class AutofillDelegateTest : BaseSessionTest() {
|
|||
assertThat("autofill hint count",
|
||||
checkAutofillChild(root), equalTo(6))
|
||||
}
|
||||
|
||||
@WithDisplay(width = 100, height = 100)
|
||||
@Test fun autofillWaitForKeyboard() {
|
||||
// Wait for the accessibility nodes to populate.
|
||||
mainSession.loadUri(pageUrl)
|
||||
mainSession.waitForPageStop()
|
||||
|
||||
mainSession.pressKey(KeyEvent.KEYCODE_CTRL_LEFT)
|
||||
mainSession.evaluateJS("document.querySelector('#pass2').focus()")
|
||||
|
||||
sessionRule.waitUntilCalled(object : Autofill.Delegate, TextInputDelegate {
|
||||
@AssertCalled(order = [2])
|
||||
override fun onNodeFocus(session: GeckoSession,
|
||||
node: Autofill.Node,
|
||||
data: Autofill.NodeData) {
|
||||
assertThat("ID should be valid", node, notNullValue())
|
||||
}
|
||||
|
||||
@AssertCalled(order = [1])
|
||||
override fun showSoftInput(session: GeckoSession) {}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,9 @@ const { XPCOMUtils } = ChromeUtils.importESModule(
|
|||
"resource://gre/modules/XPCOMUtils.sys.mjs"
|
||||
);
|
||||
const { Log } = ChromeUtils.import("resource://gre/modules/Log.jsm");
|
||||
const { clearTimeout, setTimeout } = ChromeUtils.import(
|
||||
"resource://gre/modules/Timer.jsm"
|
||||
);
|
||||
|
||||
const lazy = {};
|
||||
|
||||
|
@ -325,6 +328,54 @@ var GeckoViewUtils = {
|
|||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return promise for waiting for finishing PanZoomState.
|
||||
*
|
||||
* @param aWindow a DOM window.
|
||||
* @return promise
|
||||
*/
|
||||
waitForPanZoomState(aWindow) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (
|
||||
!aWindow?.windowUtils.asyncPanZoomEnabled ||
|
||||
!Services.prefs.getBoolPref("apz.zoom-to-focused-input.enabled")
|
||||
) {
|
||||
// No zoomToFocusedInput.
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
let timerId = 0;
|
||||
|
||||
const panZoomState = (aSubject, aTopic, aData) => {
|
||||
if (timerId != 0) {
|
||||
// aWindow may be dead object now.
|
||||
try {
|
||||
clearTimeout(timerId);
|
||||
} catch (e) {}
|
||||
timerId = 0;
|
||||
}
|
||||
|
||||
if (aData === "NOTHING") {
|
||||
Services.obs.removeObserver(panZoomState, "PanZoom:StateChange");
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
|
||||
Services.obs.addObserver(panZoomState, "PanZoom:StateChange");
|
||||
|
||||
// "GeckoView:ZoomToInput" has the timeout as 500ms when window isn't
|
||||
// resized (it means on-screen-keyboard is already shown).
|
||||
// So after up to 500ms, APZ event is sent. So we need to wait for more
|
||||
// 500ms.
|
||||
timerId = setTimeout(() => {
|
||||
// PanZoom state isn't changed. zoomToFocusedInput will return error.
|
||||
Services.obs.removeObserver(panZoomState, "PanZoom:StateChange");
|
||||
reject();
|
||||
}, 600);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Add logging functions to the specified scope that forward to the given
|
||||
* Log.jsm logger. Currently "debug" and "warn" functions are supported. To
|
||||
|
|
Загрузка…
Ссылка в новой задаче