Skip to content

Commit

Permalink
[CP-stable][web] Use eventTarget when computing pointer offset (#57246
Browse files Browse the repository at this point in the history
)

This pull request is created by [automatic cherry pick workflow](https://github.com/flutter/flutter/blob/main/docs/releases/Flutter-Cherrypick-Process.md#automatically-creates-a-cherry-pick-request)
Please fill in the form below, and a flutter domain expert will evaluate this cherry pick request.

### Issue Links:

flutter/flutter#160155
flutter/flutter#159804

### Changelog Description:

Work around this Chromium bug: https://issues.chromium.org/issues/382473107

### Impact Description:

This bug is causing issues when mouse interacts with text fields in Flutter Web.

### Workaround:

This CP *is* the workaround.

### Risk:
What is the risk level of this cherry-pick?

### Test Coverage:
Are you confident that your fix is well-tested by automated tests?

### Validation Steps:

- Open a Flutter Web app in Chrome.
- Click on a text field.
- Move the mouse over the text field.
- There should be no errors in the console.
  • Loading branch information
flutteractionsbot authored Jan 9, 2025
1 parent ce26d8b commit 9c0d322
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 7 deletions.
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

0 comments on commit 9c0d322

Please sign in to comment.