Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
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
49 changes: 6 additions & 43 deletions lib/web_ui/lib/src/engine/text_editing/text_editing.dart
Original file line number Diff line number Diff line change
Expand Up @@ -603,31 +603,10 @@ abstract class DefaultTextEditingStrategy implements TextEditingStrategy {

_subscriptions.add(html.document.onSelectionChange.listen(_handleChange));

// The behavior for blur in DOM elements changes depending on the reason of
// blur:
//
// (1) If the blur is triggered due to tab change or browser minimize, same
// element receives the focus as soon as the page reopens. Hence, text
// editing connection does not need to be closed. In this case we dot blur
// the DOM element.
//
// (2) On the other hand if the blur is triggered due to interaction with
// another element on the page, the current text connection is obsolete so
// connection close request is send to Flutter.
//
// See [HybridTextEditing.sendTextConnectionClosedToFlutterIfAny].
//
// In order to detect between these two cases, after a blur event is
// triggered [domRenderer.windowHasFocus] method which checks the window
// focus is called.
// Refocus on the domElement after blur, so that user can keep editing the
// text field.
_subscriptions.add(domElement.onBlur.listen((_) {
if (domRenderer.windowHasFocus) {
// Focus is still on the body. Continue with blur.
owner.sendTextConnectionClosedToFrameworkIfAny();
} else {
// Refocus.
domElement.focus();
}
domElement.focus();
}));

preventDefaultForMouseEvents();
Expand Down Expand Up @@ -1000,26 +979,10 @@ class FirefoxTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
// enough for covering "Select All" functionality.
_subscriptions.add(domElement.onSelect.listen(_handleChange));

// For Firefox, we also use the same approach as the parent class.
//
// Do not blur the DOM element if the user goes to another tab or minimizes
// the browser. See [super.addEventHandlers] for more comments.
//
// The different part is, in Firefox, we are not able to get correct value
// when we check the window focus like [domRendered.windowHasFocus].
//
// However [document.activeElement] always equals to [domElement] if the
// user goes to another tab, minimizes the browser or opens the dev tools.
// Hence [document.activeElement] is checked in this listener.
// Refocus on the domElement after blur, so that user can keep editing the
// text field.
_subscriptions.add(domElement.onBlur.listen((_) {
html.Element activeElement = html.document.activeElement;
if (activeElement != domElement) {
// Focus is still on the body. Continue with blur.
owner.sendTextConnectionClosedToFrameworkIfAny();
} else {
// Refocus.
domElement.focus();
}
domElement.focus();
}));

preventDefaultForMouseEvents();
Expand Down
63 changes: 28 additions & 35 deletions lib/web_ui/test/text_editing_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ void main() {
);
});

test('Does not re-acquire focus', () {
test('Re-acquire focus', () {
editingElement =
SemanticsTextEditingStrategy(HybridTextEditing(), testInputElement);

Expand All @@ -403,16 +403,17 @@ void main() {
);
expect(document.activeElement, testInputElement);

// The input should lose focus now.
// The input should refocus after blur.
editingElement.domElement.blur();
expect(document.activeElement, document.body);
expect(document.activeElement, editingElement.domElement);

editingElement.disable();
},
// TODO(nurhan): https://github.com/flutter/flutter/issues/50590
// TODO(nurhan): https://github.com/flutter/flutter/issues/50769
skip: (browserEngine == BrowserEngine.webkit ||
browserEngine == BrowserEngine.edge));
browserEngine == BrowserEngine.edge ||
browserEngine == BrowserEngine.firefox));

test('Does not dispose and recreate dom elements in persistent mode', () {
editingElement =
Expand Down Expand Up @@ -447,8 +448,12 @@ void main() {
// It doesn't remove the DOM element.
expect(editingElement.domElement, testInputElement);
expect(document.body.contains(editingElement.domElement), isTrue);
// But the DOM element loses focus.
expect(document.activeElement, document.body);
// The textArea does not lose focus.
// Even though this passes on manual tests it does not work on
// Firefox automated unit tests.
if (browserEngine != BrowserEngine.firefox) {
expect(document.activeElement, editingElement.domElement);
}
},
// TODO(nurhan): https://github.com/flutter/flutter/issues/50590
// TODO(nurhan): https://github.com/flutter/flutter/issues/50769
Expand All @@ -465,12 +470,7 @@ void main() {
onChange: trackEditingState,
onAction: trackInputAction,
);
expect(document.activeElement, testInputElement);

editingElement.domElement.blur();
expect(document.activeElement, document.body);

// The input should regain focus now.
// The input will have focus after editing state is set.
editingElement.setEditingState(EditingState(text: 'foo'));
expect(document.activeElement, testInputElement);

Expand Down Expand Up @@ -508,20 +508,17 @@ void main() {
// Focuses the textarea.
expect(document.activeElement, textarea);

// Doesn't re-acquire focus.
textarea.blur();
expect(document.activeElement, document.body);

// Re-focuses when setting editing state
editingElement.setEditingState(EditingState(text: 'foo'));
expect(document.activeElement, textarea);
// The textArea does not lose focus.
// Even though this passes on manual tests it does not work on
// Firefox automated unit tests.
if (browserEngine != BrowserEngine.firefox) {
expect(document.activeElement, textarea);
}

editingElement.disable();
// It doesn't remove the textarea from the DOM.
expect(editingElement.domElement, textarea);
expect(document.body.contains(editingElement.domElement), isTrue);
// But the textarea loses focus.
expect(document.activeElement, document.body);
},
// TODO(nurhan): https://github.com/flutter/flutter/issues/50590
// TODO(nurhan): https://github.com/flutter/flutter/issues/50769
Expand Down Expand Up @@ -673,7 +670,7 @@ void main() {
expect(spy.messages, isEmpty);
});

test('close connection on blur', () async {
test('do not close connection on blur', () async {
final MethodCall setClient = MethodCall(
'TextInput.setClient', <dynamic>[123, flutterSinglelineConfig]);
sendFrameworkMessage(codec.encodeMethodCall(setClient));
Expand All @@ -698,18 +695,14 @@ void main() {
// DOM element is blurred.
textEditing.editingElement.domElement.blur();

expect(spy.messages, hasLength(1));
expect(spy.messages[0].channel, 'flutter/textinput');
expect(spy.messages[0].methodName, 'TextInputClient.onConnectionClosed');
expect(
spy.messages[0].methodArguments,
<dynamic>[
123, // Client ID
],
);
spy.messages.clear();
// Input element is removed from DOM.
expect(document.getElementsByTagName('input'), hasLength(0));
expect(spy.messages, hasLength(0));

// DOM element still has focus.
// Even though this passes on manual tests it does not work on
// Firefox automated unit tests.
if (browserEngine != BrowserEngine.firefox) {
expect(document.activeElement, textEditing.editingElement.domElement);
}
},
// TODO(nurhan): https://github.com/flutter/flutter/issues/50590
// TODO(nurhan): https://github.com/flutter/flutter/issues/50769
Expand Down Expand Up @@ -1482,7 +1475,7 @@ void main() {
BrowserAutofillHints.instance.flutterToEngine(testHint));
expect(testInputElement.id, testId);
expect(testInputElement.type, 'text');
if (browserEngine == BrowserEngine.firefox) {
if (browserEngine == BrowserEngine.firefox) {
expect(testInputElement.name,
BrowserAutofillHints.instance.flutterToEngine(testHint));
} else {
Expand Down