Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 6b2de88

Browse files
authored
[web] - fix Safari textfield selection bug (#47917)
In order to fix Safari autofill, we've had to give inactive elements non-zero size because Safari does not respect offscreen or 0-sized inputs, and this leads to broken autofill behavior (see: #43058). As part of these changes, we needed to disable pointer events for the parent `<form>` element so that we don't have pointer event collisions if users hover over or click into the invisible autofill elements within that form. This led to an issue where offsets weren't being calculated correctly for "active" inputs because they relied on pointer events bubbling up and being caught by the form. The fix is to explicitly set pointer events on the active inputs, so that we can correctly discern when our pointer event target is actually the input and correctly calculate the offsets. Fixes flutter/flutter#136006
1 parent 0e3a62e commit 6b2de88

File tree

2 files changed

+48
-0
lines changed

2 files changed

+48
-0
lines changed

lib/web_ui/lib/src/engine/text_editing/text_editing.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,14 @@ class EngineAutofillForm {
321321
}
322322

323323
void placeForm(DomHTMLElement mainTextEditingElement) {
324+
// Since we're disabling pointer events on the form to fix Safari autofill,
325+
// we need to explicitly set pointer events on the active input element in
326+
// order to calculate the correct pointer event offsets.
327+
// See: https://github.com/flutter/flutter/issues/136006
328+
if(textEditing.strategy is SafariDesktopTextEditingStrategy) {
329+
mainTextEditingElement.style.pointerEvents = 'all';
330+
}
331+
324332
formElement.insertBefore(mainTextEditingElement, insertionReferenceNode);
325333
defaultTextEditingRoot.append(formElement);
326334
}

lib/web_ui/test/engine/text_editing_test.dart

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2551,6 +2551,46 @@ Future<void> testMain() async {
25512551
expect(autofillForm.formElement.style.pointerEvents, 'none');
25522552
}, skip: !isSafari);
25532553

2554+
test(
2555+
'the focused element within a form should explicitly set pointer events on Safari',
2556+
() {
2557+
final List<dynamic> fields = createFieldValues(<String>[
2558+
'email',
2559+
'username',
2560+
'password',
2561+
], <String>[
2562+
'field1',
2563+
'field2',
2564+
'field3'
2565+
]);
2566+
final EngineAutofillForm autofillForm =
2567+
EngineAutofillForm.fromFrameworkMessage(
2568+
createAutofillInfo('email', 'field1'), fields)!;
2569+
2570+
final DomHTMLInputElement testInputElement = createDomHTMLInputElement();
2571+
testInputElement.name = 'email';
2572+
autofillForm.placeForm(testInputElement);
2573+
2574+
final List<DomHTMLInputElement> formChildNodes =
2575+
autofillForm.formElement.childNodes.toList()
2576+
as List<DomHTMLInputElement>;
2577+
final DomHTMLInputElement email = formChildNodes[0];
2578+
final DomHTMLInputElement username = formChildNodes[1];
2579+
final DomHTMLInputElement password = formChildNodes[2];
2580+
2581+
expect(email.name, 'email');
2582+
expect(username.name, 'username');
2583+
expect(password.name, 'current-password');
2584+
2585+
// pointer events are none on the form and all non-focused elements
2586+
expect(autofillForm.formElement.style.pointerEvents, 'none');
2587+
expect(username.style.pointerEvents, 'none');
2588+
expect(password.style.pointerEvents, 'none');
2589+
2590+
// pointer events are set to all on the activeDomElement
2591+
expect(email.style.pointerEvents, 'all');
2592+
}, skip: !isSafari);
2593+
25542594
tearDown(() {
25552595
clearForms();
25562596
});

0 commit comments

Comments
 (0)