Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CP-stable][web] Use eventTarget when computing pointer offset #57246

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand All @@ -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**.
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -58,6 +59,7 @@ void doTests() {
});

tearDown(() {
EnginePlatformDispatcher.instance.viewManager.unregisterView(view.viewId);
view.dispose();
});

Expand Down Expand Up @@ -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!');
Expand Down
Loading