Skip to content

Commit 392b259

Browse files
authored
[web] Migrate Flutter Web DOM usage to JS static interop - 43. (flutter#33379)
1 parent c1eeb6a commit 392b259

File tree

11 files changed

+332
-307
lines changed

11 files changed

+332
-307
lines changed

lib/web_ui/lib/src/engine/dom.dart

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6+
import 'dart:math' as math;
67
import 'dart:typed_data';
78

89
import 'package:js/js.dart';
@@ -98,6 +99,7 @@ extension DomDocumentExtension on DomDocument {
9899
external DomText createTextNode(String data);
99100
external DomEvent createEvent(String eventType);
100101
external DomElement? get activeElement;
102+
external DomElement? elementFromPoint(int x, int y);
101103
}
102104

103105
@JS()
@@ -109,10 +111,12 @@ extension DomHTMLDocumentExtension on DomHTMLDocument {
109111
external DomHTMLHeadElement? get head;
110112
external DomHTMLBodyElement? get body;
111113
external set title(String? value);
114+
external String? get title;
112115
Iterable<DomElement> getElementsByTagName(String tag) =>
113116
createDomListWrapper<DomElement>(js_util
114117
.callMethod<_DomList>(this, 'getElementsByTagName', <Object>[tag]));
115118
external DomElement? get activeElement;
119+
external DomElement? getElementById(String id);
116120
}
117121

118122
@JS('document')
@@ -265,6 +269,7 @@ extension DomElementExtension on DomElement {
265269
js_util.setProperty<num>(this, 'scrollLeft', value.round());
266270
external DomTokenList get classList;
267271
external set className(String value);
272+
external String get className;
268273
external void blur();
269274
List<DomNode> getElementsByTagName(String tag) =>
270275
js_util.callMethod<List<Object?>>(
@@ -686,7 +691,7 @@ extension DomCanvasGradientExtension on DomCanvasGradient {
686691
@staticInterop
687692
class DomXMLHttpRequestEventTarget extends DomEventTarget {}
688693

689-
@JS('XMLHttpRequest')
694+
@JS()
690695
@staticInterop
691696
class DomXMLHttpRequest extends DomXMLHttpRequestEventTarget {}
692697

@@ -696,18 +701,20 @@ DomXMLHttpRequest createDomXMLHttpRequest() =>
696701

697702
extension DomXMLHttpRequestExtension on DomXMLHttpRequest {
698703
external dynamic get response;
704+
external String? get responseText;
699705
external String get responseType;
700706
external int? get status;
701707
external set responseType(String value);
702708
void open(String method, String url, [bool? async]) => js_util.callMethod(
703709
this, 'open', <Object>[method, url, if (async != null) async]);
704-
external void send();
710+
void send([Object? bodyOrData]) => js_util
711+
.callMethod(this, 'send', <Object>[if (bodyOrData != null) bodyOrData]);
705712
}
706713

707-
Future<DomXMLHttpRequest> domHttpRequest(String url, {String? responseType}) {
714+
Future<DomXMLHttpRequest> domHttpRequest(String url,
715+
{String? responseType, String method = 'GET', dynamic sendData}) {
708716
final Completer<DomXMLHttpRequest> completer = Completer<DomXMLHttpRequest>();
709717
final DomXMLHttpRequest xhr = createDomXMLHttpRequest();
710-
const String method = 'GET';
711718
xhr.open(method, url, /* async */ true);
712719
if (responseType != null) {
713720
xhr.responseType = responseType;
@@ -727,7 +734,7 @@ Future<DomXMLHttpRequest> domHttpRequest(String url, {String? responseType}) {
727734
}));
728735

729736
xhr.addEventListener('error', allowInterop(completer.completeError));
730-
xhr.send();
737+
xhr.send(sendData);
731738
return completer.future;
732739
}
733740

@@ -778,6 +785,15 @@ extension DomRectReadOnlyExtension on DomRectReadOnly {
778785
external num get left;
779786
}
780787

788+
DomRect createDomRectFromPoints(DomPoint a, DomPoint b) {
789+
final num left = math.min(a.x, b.x);
790+
final num width = math.max(a.x, b.x) - left;
791+
final num top = math.min(a.y, b.y);
792+
final num height = math.max(a.y, b.y) - top;
793+
return domCallConstructorString(
794+
'DOMRect', <Object>[left, top, width, height])! as DomRect;
795+
}
796+
781797
@JS()
782798
@staticInterop
783799
class DomRect extends DomRectReadOnly {}
@@ -836,6 +852,8 @@ extension DomHTMLTextAreaElementExtension on DomHTMLTextAreaElement {
836852
external set name(String value);
837853
external int? get selectionStart;
838854
external int? get selectionEnd;
855+
external set selectionStart(int? value);
856+
external set selectionEnd(int? value);
839857
external String? get value;
840858
void setSelectionRange(int start, int end, [String? direction]) =>
841859
js_util.callMethod(this, 'setSelectionRange',
@@ -1098,18 +1116,27 @@ extension DomTouchExtension on DomTouch {
10981116
DomPoint get client => DomPoint(clientX, clientY);
10991117
}
11001118

1119+
DomTouch createDomTouch([Map<dynamic, dynamic>? init]) =>
1120+
js_util.callConstructor(domGetConstructor('Touch')!,
1121+
<Object>[if (init != null) js_util.jsify(init)]) as DomTouch;
1122+
11011123
DomTouchEvent createDomTouchEvent(String type, [Map<dynamic, dynamic>? init]) =>
11021124
js_util.callConstructor(domGetConstructor('TouchEvent')!,
11031125
<Object>[type, if (init != null) js_util.jsify(init)]);
11041126

11051127
@JS()
11061128
@staticInterop
1107-
class DomCompositionEvent {}
1129+
class DomCompositionEvent extends DomUIEvent {}
11081130

11091131
extension DomCompositionEventExtension on DomCompositionEvent {
11101132
external String? get data;
11111133
}
11121134

1135+
DomCompositionEvent createDomCompositionEvent(String type, [Map<dynamic,
1136+
dynamic>? options]) =>
1137+
js_util.callConstructor(domGetConstructor('CompositionEvent')!,
1138+
<Object>[type, if (options != null) js_util.jsify(options)]);
1139+
11131140
@JS()
11141141
@staticInterop
11151142
class DomHTMLInputElement extends DomHTMLElement {}
@@ -1127,6 +1154,8 @@ extension DomHTMLInputElementExtension on DomHTMLInputElement {
11271154
external set autocomplete(String value);
11281155
external int? get selectionStart;
11291156
external int? get selectionEnd;
1157+
external set selectionStart(int? value);
1158+
external set selectionEnd(int? value);
11301159
void setSelectionRange(int start, int end, [String? direction]) =>
11311160
js_util.callMethod(this, 'setSelectionRange',
11321161
<Object>[start, end, if (direction != null) direction]);
@@ -1225,6 +1254,7 @@ extension DomShadowRootExtension on DomShadowRoot {
12251254
external DomElement? get host;
12261255
external String? get mode;
12271256
external bool? get delegatesFocus;
1257+
external DomElement? elementFromPoint(int x, int y);
12281258
}
12291259

12301260
@JS()

lib/web_ui/test/composition_test.dart

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6-
import 'dart:html' as html;
76

87
import 'package:test/bootstrap/browser.dart';
98
import 'package:test/test.dart';
@@ -27,8 +26,8 @@ class _MockWithCompositionAwareMixin with CompositionAwareMixin {
2726
static const String _kCompositionEnd = 'compositionend';
2827
}
2928

30-
html.InputElement get _inputElement {
31-
return defaultTextEditingRoot.querySelectorAll('input').first as html.InputElement;
29+
DomHTMLInputElement get _inputElement {
30+
return defaultTextEditingRoot.querySelectorAll('input').first as DomHTMLInputElement;
3231
}
3332

3433
GloballyPositionedTextEditingStrategy _enableEditingStrategy({
@@ -69,10 +68,9 @@ Future<void> testMain() async {
6968
final _MockWithCompositionAwareMixin mockWithCompositionAwareMixin =
7069
_MockWithCompositionAwareMixin();
7170
mockWithCompositionAwareMixin.composingText = fakeComposingText;
72-
mockWithCompositionAwareMixin.addCompositionEventHandlers(_inputElement
73-
as DomHTMLElement);
71+
mockWithCompositionAwareMixin.addCompositionEventHandlers(_inputElement);
7472

75-
_inputElement.dispatchEvent(html.Event(_MockWithCompositionAwareMixin._kCompositionEnd));
73+
_inputElement.dispatchEvent(createDomEvent('Event', _MockWithCompositionAwareMixin._kCompositionEnd));
7674

7775
expect(mockWithCompositionAwareMixin.composingText, null);
7876
});
@@ -83,10 +81,9 @@ Future<void> testMain() async {
8381
final _MockWithCompositionAwareMixin mockWithCompositionAwareMixin =
8482
_MockWithCompositionAwareMixin();
8583
mockWithCompositionAwareMixin.composingText = fakeComposingText;
86-
mockWithCompositionAwareMixin.addCompositionEventHandlers(_inputElement
87-
as DomHTMLElement);
84+
mockWithCompositionAwareMixin.addCompositionEventHandlers(_inputElement);
8885

89-
_inputElement.dispatchEvent(html.Event(_MockWithCompositionAwareMixin._kCompositionStart));
86+
_inputElement.dispatchEvent(createDomEvent('Event', _MockWithCompositionAwareMixin._kCompositionStart));
9087

9188
expect(mockWithCompositionAwareMixin.composingText, null);
9289
});
@@ -98,12 +95,11 @@ Future<void> testMain() async {
9895
final _MockWithCompositionAwareMixin mockWithCompositionAwareMixin =
9996
_MockWithCompositionAwareMixin();
10097
mockWithCompositionAwareMixin.composingText = fakeComposingText;
101-
mockWithCompositionAwareMixin.addCompositionEventHandlers(_inputElement
102-
as DomHTMLElement);
98+
mockWithCompositionAwareMixin.addCompositionEventHandlers(_inputElement);
10399

104-
_inputElement.dispatchEvent(html.CompositionEvent(
100+
_inputElement.dispatchEvent(createDomCompositionEvent(
105101
_MockWithCompositionAwareMixin._kCompositionUpdate,
106-
data: fakeEventText
102+
<Object?, Object?>{ 'data': fakeEventText }
107103
));
108104

109105
expect(mockWithCompositionAwareMixin.composingText, fakeEventText);
@@ -150,11 +146,13 @@ Future<void> testMain() async {
150146
test('should be [0, compostionStrLength] on new composition', () {
151147
const String composingText = 'hi';
152148

153-
_inputElement.dispatchEvent(html.CompositionEvent(_MockWithCompositionAwareMixin._kCompositionUpdate, data: composingText));
149+
_inputElement.dispatchEvent(createDomCompositionEvent(
150+
_MockWithCompositionAwareMixin._kCompositionUpdate,
151+
<Object?, Object?>{'data': composingText}));
154152

155153
// Set the selection text.
156154
_inputElement.value = composingText;
157-
_inputElement.dispatchEvent(html.Event.eventType('Event', 'input'));
155+
_inputElement.dispatchEvent(createDomEvent('Event', 'input'));
158156

159157
expect(
160158
editingStrategy.lastEditingState,
@@ -176,13 +174,13 @@ Future<void> testMain() async {
176174
_inputElement.value = '$beforeComposingText$afterComposingText';
177175
_inputElement.setSelectionRange(beforeComposingText.length, beforeComposingText.length);
178176

179-
_inputElement.dispatchEvent(html.CompositionEvent(
177+
_inputElement.dispatchEvent(createDomCompositionEvent(
180178
_MockWithCompositionAwareMixin._kCompositionUpdate,
181-
data: composingText
179+
<Object?, Object?>{ 'data': composingText }
182180
));
183181

184182
// Flush editing state (since we did not compositionend).
185-
_inputElement.dispatchEvent(html.Event.eventType('Event', 'input'));
183+
_inputElement.dispatchEvent(createDomEvent('Event', 'input'));
186184

187185
expect(
188186
editingStrategy.lastEditingState,
@@ -224,9 +222,9 @@ Future<void> testMain() async {
224222
));
225223

226224

227-
_inputElement.dispatchEvent(html.CompositionEvent(
225+
_inputElement.dispatchEvent(createDomCompositionEvent(
228226
_MockWithCompositionAwareMixin._kCompositionUpdate,
229-
data: newComposingText));
227+
<Object?, Object?>{ 'data': newComposingText }));
230228

231229
await containExpect;
232230
});
@@ -248,9 +246,9 @@ Future<void> testMain() async {
248246
.having((TextEditingDeltaState deltaState) => deltaState.composingExtent, 'composingExtent', currCharIndex + 1)
249247
));
250248

251-
_inputElement.dispatchEvent(html.CompositionEvent(
249+
_inputElement.dispatchEvent(createDomCompositionEvent(
252250
_MockWithCompositionAwareMixin._kCompositionUpdate,
253-
data: currComposingSubstr));
251+
<Object?, Object?>{ 'data': currComposingSubstr }));
254252

255253
await containExpect;
256254
}

0 commit comments

Comments
 (0)