[web] Use eventTarget when computing pointer offset (flutter/engine#56949)

These changes are mainly things I missed in https://github.com/flutter/engine/pull/56719

Fixes https://github.com/flutter/flutter/issues/159804
This commit is contained in:
Mouad Debbar 2024-12-05 13:07:20 -05:00 committed by GitHub
parent ba21393f49
commit a3ed100995
2 changed files with 48 additions and 7 deletions

View File

@ -18,9 +18,12 @@ import '../window.dart';
/// The offset is *not* multiplied by DPR or anything else, it's the closest
/// to what the DOM would return if we had currentTarget readily available.
///
/// This needs an `eventTarget`, because the `event.target` (which is what
/// this would really need to use) gets lost when the `event` comes from a
/// "coalesced" event (see https://github.com/flutter/flutter/issues/155987).
/// This takes an optional `eventTarget`, because the `event.target` may have
/// the wrong value for "coalesced" events. See:
///
/// - https://github.com/flutter/flutter/issues/155987
/// - https://github.com/flutter/flutter/issues/159804
/// - https://g-issues.chromium.org/issues/382473107
///
/// It also takes into account semantics being enabled to fix the case where
/// offsetX, offsetY == 0 (TalkBack events).
@ -41,12 +44,12 @@ ui.Offset computeEventOffsetToTarget(
if (isInput) {
final EditableTextGeometry? inputGeometry = textEditing.strategy.geometry;
if (inputGeometry != null) {
return _computeOffsetForInputs(event, inputGeometry);
return _computeOffsetForInputs(event, eventTarget, inputGeometry);
}
}
// On another DOM Element (normally a platform view)
final bool isTargetOutsideOfShadowDOM = event.target != actualTarget;
final bool isTargetOutsideOfShadowDOM = eventTarget != actualTarget;
if (isTargetOutsideOfShadowDOM) {
final DomRect origin = actualTarget.getBoundingClientRect();
// event.clientX/Y and origin.x/y are relative **to the viewport**.
@ -70,8 +73,14 @@ ui.Offset computeEventOffsetToTarget(
/// sent from the framework, which includes information on how to transform the
/// underlying input element. We transform the `event.offset` points we receive
/// using the values from the input's transform matrix.
ui.Offset _computeOffsetForInputs(DomMouseEvent event, EditableTextGeometry inputGeometry) {
final DomElement targetElement = event.target! as DomHTMLElement;
///
/// See [computeEventOffsetToTarget] for more information about `eventTarget`.
ui.Offset _computeOffsetForInputs(
DomMouseEvent event,
DomEventTarget eventTarget,
EditableTextGeometry inputGeometry,
) {
final DomElement targetElement = eventTarget as DomElement;
final DomHTMLElement domElement = textEditing.strategy.activeDomElement;
assert(targetElement == domElement, 'The targeted input element must be the active input element');
final Float32List transformValues = inputGeometry.globalTransform;

View File

@ -32,6 +32,7 @@ void doTests() {
group('computeEventOffsetToTarget', () {
setUp(() {
view = EngineFlutterView(EnginePlatformDispatcher.instance, domDocument.body!);
EnginePlatformDispatcher.instance.viewManager.registerView(view);
rootElement = view.dom.rootElement;
eventSource = createDomElement('div-event-source');
rootElement.append(eventSource);
@ -58,6 +59,7 @@ void doTests() {
});
tearDown(() {
EnginePlatformDispatcher.instance.viewManager.unregisterView(view.viewId);
view.dispose();
});
@ -101,6 +103,36 @@ void doTests() {
expect(offset.dy, 110);
});
test('eventTarget takes precedence', () async {
final input = view.dom.textEditingHost.appendChild(createDomElement('input'));
textEditing.strategy.enable(
InputConfiguration(viewId: view.viewId),
onChange: (_, __) {},
onAction: (_) {},
);
addTearDown(() {
textEditing.strategy.disable();
});
final moveEvent = createDomPointerEvent('pointermove', <String, Object>{
'bubbles': true,
'clientX': 10,
'clientY': 20,
});
expect(
() => computeEventOffsetToTarget(moveEvent, view),
throwsA(anything),
);
expect(
() => computeEventOffsetToTarget(moveEvent, view, eventTarget: input),
returnsNormally,
);
});
test('Event dispatched by TalkBack gets a computed offset', () async {
// Fill this in to test _computeOffsetForTalkbackEvent
}, skip: 'To be implemented!');