Unverified Commit 191465ac authored by joshualitt's avatar joshualitt Committed by GitHub

[web] Migrate the bulk of JS interop to JS types. (#123286)

parent 1a98273a
...@@ -2,12 +2,13 @@ ...@@ -2,12 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:js_interop';
import 'package:js/js.dart'; import 'package:js/js.dart';
// This value is set by the engine. It is used to determine if the application is // This value is set by the engine. It is used to determine if the application is
// using canvaskit. // using canvaskit.
@JS('window.flutterCanvasKit') @JS('window.flutterCanvasKit')
external Object? get _windowFlutterCanvasKit; external JSAny? get _windowFlutterCanvasKit;
/// The web implementation of [isCanvasKit] /// The web implementation of [isCanvasKit]
bool get isCanvasKit => _windowFlutterCanvasKit != null; bool get isCanvasKit => _windowFlutterCanvasKit != null;
...@@ -3,11 +3,10 @@ ...@@ -3,11 +3,10 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'dart:async';
import 'dart:typed_data'; import 'dart:js_interop';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:js/js.dart';
import '../services/dom.dart'; import '../services/dom.dart';
import 'image_provider.dart' as image_provider; import 'image_provider.dart' as image_provider;
...@@ -18,7 +17,7 @@ typedef HttpRequestFactory = DomXMLHttpRequest Function(); ...@@ -18,7 +17,7 @@ typedef HttpRequestFactory = DomXMLHttpRequest Function();
/// Default HTTP client. /// Default HTTP client.
DomXMLHttpRequest _httpClient() { DomXMLHttpRequest _httpClient() {
return createDomXMLHttpRequest(); return DomXMLHttpRequest();
} }
/// Creates an overridable factory function. /// Creates an overridable factory function.
...@@ -148,7 +147,7 @@ class NetworkImage ...@@ -148,7 +147,7 @@ class NetworkImage
}); });
} }
request.addEventListener('load', allowInterop((DomEvent e) { request.addEventListener('load', createDomEventListener((DomEvent e) {
final int? status = request.status; final int? status = request.status;
final bool accepted = status! >= 200 && status < 300; final bool accepted = status! >= 200 && status < 300;
final bool fileUri = status == 0; // file:// URIs have status of 0. final bool fileUri = status == 0; // file:// URIs have status of 0.
...@@ -166,13 +165,14 @@ class NetworkImage ...@@ -166,13 +165,14 @@ class NetworkImage
} }
})); }));
request.addEventListener('error', allowInterop(completer.completeError)); request.addEventListener('error',
createDomEventListener(completer.completeError));
request.send(); request.send();
await completer.future; await completer.future;
final Uint8List bytes = (request.response as ByteBuffer).asUint8List(); final Uint8List bytes = (request.response! as JSArrayBuffer).toDart.asUint8List();
if (bytes.lengthInBytes == 0) { if (bytes.lengthInBytes == 0) {
throw image_provider.NetworkImageLoadException( throw image_provider.NetworkImageLoadException(
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:js_interop';
import 'package:js/js.dart'; import 'package:js/js.dart';
import 'package:js/js_util.dart' as js_util;
/// This file includes static interop helpers for Flutter Web. /// This file includes static interop helpers for Flutter Web.
// TODO(joshualitt): This file will eventually be removed, // TODO(joshualitt): This file will eventually be removed,
...@@ -16,8 +16,11 @@ class DomWindow {} ...@@ -16,8 +16,11 @@ class DomWindow {}
/// [DomWindow] required extension. /// [DomWindow] required extension.
extension DomWindowExtension on DomWindow { extension DomWindowExtension on DomWindow {
@JS('matchMedia')
external DomMediaQueryList _matchMedia(JSString? query);
/// Returns a [DomMediaQueryList] of the media that matches [query]. /// Returns a [DomMediaQueryList] of the media that matches [query].
external DomMediaQueryList matchMedia(String? query); DomMediaQueryList matchMedia(String? query) => _matchMedia(query?.toJS);
/// Returns the [DomNavigator] associated with this window. /// Returns the [DomNavigator] associated with this window.
external DomNavigator get navigator; external DomNavigator get navigator;
...@@ -37,8 +40,11 @@ class DomMediaQueryList {} ...@@ -37,8 +40,11 @@ class DomMediaQueryList {}
/// [DomMediaQueryList] required extension. /// [DomMediaQueryList] required extension.
extension DomMediaQueryListExtension on DomMediaQueryList { extension DomMediaQueryListExtension on DomMediaQueryList {
@JS('matches')
external JSBoolean get _matches;
/// Whether or not the query matched. /// Whether or not the query matched.
external bool get matches; bool get matches => _matches.toDart;
} }
/// [DomNavigator] interop object. /// [DomNavigator] interop object.
...@@ -48,8 +54,11 @@ class DomNavigator {} ...@@ -48,8 +54,11 @@ class DomNavigator {}
/// [DomNavigator] required extension. /// [DomNavigator] required extension.
extension DomNavigatorExtension on DomNavigator { extension DomNavigatorExtension on DomNavigator {
@JS('platform')
external JSString? get _platform;
/// The underyling platform string. /// The underyling platform string.
external String? get platform; String? get platform => _platform?.toDart;
} }
/// A DOM event target. /// A DOM event target.
...@@ -59,56 +68,93 @@ class DomEventTarget {} ...@@ -59,56 +68,93 @@ class DomEventTarget {}
/// [DomEventTarget]'s required extension. /// [DomEventTarget]'s required extension.
extension DomEventTargetExtension on DomEventTarget { extension DomEventTargetExtension on DomEventTarget {
@JS('addEventListener')
external JSVoid _addEventListener1(JSString type, DomEventListener? listener);
@JS('addEventListener')
external JSVoid _addEventListener2(
JSString type, DomEventListener? listener, JSBoolean useCapture);
/// Adds an event listener to this event target. /// Adds an event listener to this event target.
@JS('addEventListener')
void addEventListener(String type, DomEventListener? listener, void addEventListener(String type, DomEventListener? listener,
[bool? useCapture]) { [bool? useCapture]) {
if (listener != null) { if (listener != null) {
js_util.callMethod(this, 'addEventListener', if (useCapture == null) {
<Object>[type, listener, if (useCapture != null) useCapture]); _addEventListener1(type.toJS, listener);
} else {
_addEventListener2(type.toJS, listener, useCapture.toJS);
}
} }
} }
} }
/// [DomXMLHttpRequest] interop class. /// [DomXMLHttpRequest] interop class.
@JS() @JS('XMLHttpRequest')
@staticInterop @staticInterop
class DomXMLHttpRequest extends DomEventTarget {} class DomXMLHttpRequest extends DomEventTarget {
/// Constructor for [DomXMLHttpRequest].
external factory DomXMLHttpRequest();
}
/// [DomXMLHttpRequest] extension. /// [DomXMLHttpRequest] extension.
extension DomXMLHttpRequestExtension on DomXMLHttpRequest { extension DomXMLHttpRequestExtension on DomXMLHttpRequest {
/// Gets the response. /// Gets the response.
external dynamic get response; external JSAny? get response;
@JS('responseText')
external JSString? get _responseText;
/// Gets the response text. /// Gets the response text.
external String? get responseText; String? get responseText => _responseText?.toDart;
@JS('responseType')
external JSString get _responseType;
/// Gets the response type. /// Gets the response type.
external String get responseType; String get responseType => _responseType.toDart;
@JS('status')
external JSNumber? get _status;
/// Gets the status. /// Gets the status.
external int? get status; int? get status => _status?.toDart.toInt();
@JS('responseType')
external set _responseType(JSString value);
/// Set the response type. /// Set the response type.
external set responseType(String value); set responseType(String value) => _responseType = value.toJS;
@JS('setRequestHeader')
external void _setRequestHeader(JSString header, JSString value);
/// Set the request header. /// Set the request header.
external void setRequestHeader(String header, String value); void setRequestHeader(String header, String value) =>
_setRequestHeader(header.toJS, value.toJS);
@JS('open')
external JSVoid _open(JSString method, JSString url, JSBoolean isAsync);
/// Open the request. /// Open the request.
void open(String method, String url, bool isAsync) => js_util.callMethod( void open(String method, String url, bool isAsync) =>
this, 'open', <Object>[method, url, isAsync]); _open(method.toJS, url.toJS, isAsync.toJS);
/// Send the request. /// Send the request.
void send() => js_util.callMethod(this, 'send', <Object>[]); external JSVoid send();
} }
/// Factory function for creating [DomXMLHttpRequest].
DomXMLHttpRequest createDomXMLHttpRequest() =>
domCallConstructorString('XMLHttpRequest', <Object?>[])!
as DomXMLHttpRequest;
/// Type for event listener. /// Type for event listener.
typedef DomEventListener = void Function(DomEvent event); typedef DartDomEventListener = JSVoid Function(DomEvent event);
/// The type of [JSFunction] expected as an `EventListener`.
@JS()
@staticInterop
class DomEventListener {}
/// Creates a [DomEventListener] from a [DartDomEventListener].
DomEventListener createDomEventListener(DartDomEventListener listener) =>
listener.toJS as DomEventListener;
/// [DomEvent] interop object. /// [DomEvent] interop object.
@JS() @JS()
...@@ -117,16 +163,15 @@ class DomEvent {} ...@@ -117,16 +163,15 @@ class DomEvent {}
/// [DomEvent] required extension. /// [DomEvent] required extension.
extension DomEventExtension on DomEvent { extension DomEventExtension on DomEvent {
@JS('type')
external JSString get _type;
/// Get the event type. /// Get the event type.
external String get type; String get type => _type.toDart;
/// Initialize an event. /// Initialize an event.
void initEvent(String type, [bool? bubbles, bool? cancelable]) => external JSVoid initEvent(
js_util.callMethod(this, 'initEvent', <Object>[ JSString type, JSBoolean bubbles, JSBoolean cancelable);
type,
if (bubbles != null) bubbles,
if (cancelable != null) cancelable
]);
} }
/// [DomProgressEvent] interop object. /// [DomProgressEvent] interop object.
...@@ -136,24 +181,17 @@ class DomProgressEvent extends DomEvent {} ...@@ -136,24 +181,17 @@ class DomProgressEvent extends DomEvent {}
/// [DomProgressEvent] required extension. /// [DomProgressEvent] required extension.
extension DomProgressEventExtension on DomProgressEvent { extension DomProgressEventExtension on DomProgressEvent {
/// Amount of work done. @JS('loaded')
external int? get loaded; external JSNumber? get _loaded;
/// Total amount of work. /// Amount of work done.
external int? get total; int? get loaded => _loaded?.toDart.toInt();
}
/// Gets a constructor from a [String]. @JS('total')
Object? domGetConstructor(String constructorName) => external JSNumber? get _total;
js_util.getProperty(domWindow, constructorName);
/// Calls a constructor as a [String]. /// Total amount of work.
Object? domCallConstructorString(String constructorName, List<Object?> args) { int? get total => _total?.toDart.toInt();
final Object? constructor = domGetConstructor(constructorName);
if (constructor == null) {
return null;
}
return js_util.callConstructor(constructor, args);
} }
/// The underlying DOM document. /// The underlying DOM document.
...@@ -163,8 +201,11 @@ class DomDocument {} ...@@ -163,8 +201,11 @@ class DomDocument {}
/// [DomDocument]'s required extension. /// [DomDocument]'s required extension.
extension DomDocumentExtension on DomDocument { extension DomDocumentExtension on DomDocument {
@JS('createEvent')
external DomEvent _createEvent(JSString eventType);
/// Creates an event. /// Creates an event.
external DomEvent createEvent(String eventType); DomEvent createEvent(String eventType) => _createEvent(eventType.toJS);
/// Creates a range. /// Creates a range.
external DomRange createRange(); external DomRange createRange();
...@@ -172,10 +213,9 @@ extension DomDocumentExtension on DomDocument { ...@@ -172,10 +213,9 @@ extension DomDocumentExtension on DomDocument {
/// Gets the head element. /// Gets the head element.
external DomHTMLHeadElement? get head; external DomHTMLHeadElement? get head;
/// Creates a new element. /// Creates a [DomElement].
DomElement createElement(String name, [Object? options]) => @JS('createElement')
js_util.callMethod(this, 'createElement', external DomElement createElement(JSString name);
<Object>[name, if (options != null) options]) as DomElement;
} }
/// Returns the top level document. /// Returns the top level document.
...@@ -185,14 +225,10 @@ external DomDocument get domDocument; ...@@ -185,14 +225,10 @@ external DomDocument get domDocument;
/// Creates a new DOM event. /// Creates a new DOM event.
DomEvent createDomEvent(String type, String name) { DomEvent createDomEvent(String type, String name) {
final DomEvent event = domDocument.createEvent(type); final DomEvent event = domDocument.createEvent(type);
event.initEvent(name, true, true); event.initEvent(name.toJS, true.toJS, true.toJS);
return event; return event;
} }
/// Defines a new property on an Object.
@JS('Object.defineProperty')
external void objectDefineProperty(Object o, String symbol, dynamic desc);
/// A Range object. /// A Range object.
@JS() @JS()
@staticInterop @staticInterop
...@@ -201,7 +237,7 @@ class DomRange {} ...@@ -201,7 +237,7 @@ class DomRange {}
/// [DomRange]'s required extension. /// [DomRange]'s required extension.
extension DomRangeExtension on DomRange { extension DomRangeExtension on DomRange {
/// Selects the provided node. /// Selects the provided node.
external void selectNode(DomNode node); external JSVoid selectNode(DomNode node);
} }
/// A node in the DOM. /// A node in the DOM.
...@@ -211,11 +247,14 @@ class DomNode extends DomEventTarget {} ...@@ -211,11 +247,14 @@ class DomNode extends DomEventTarget {}
/// [DomNode]'s required extension. /// [DomNode]'s required extension.
extension DomNodeExtension on DomNode { extension DomNodeExtension on DomNode {
@JS('innerText')
external set _innerText(JSString text);
/// Sets the innerText of this node. /// Sets the innerText of this node.
external set innerText(String text); set innerText(String text) => _innerText = text.toJS;
/// Appends a node this node. /// Appends a node this node.
external void append(DomNode node); external JSVoid append(DomNode node);
} }
/// An element in the DOM. /// An element in the DOM.
...@@ -249,14 +288,23 @@ class DomMouseEvent extends DomUIEvent {} ...@@ -249,14 +288,23 @@ class DomMouseEvent extends DomUIEvent {}
/// [DomMouseEvent]'s required extension. /// [DomMouseEvent]'s required extension.
extension DomMouseEventExtension on DomMouseEvent { extension DomMouseEventExtension on DomMouseEvent {
@JS('offsetX')
external JSNumber get _offsetX;
/// Returns the current x offset. /// Returns the current x offset.
external num get offsetX; num get offsetX => _offsetX.toDart;
@JS('offsetY')
external JSNumber get _offsetY;
/// Returns the current y offset. /// Returns the current y offset.
external num get offsetY; num get offsetY => _offsetY.toDart;
@JS('button')
external JSNumber get _button;
/// Returns the current button. /// Returns the current button.
external int get button; int get button => _button.toDart.toInt();
} }
/// A DOM selection. /// A DOM selection.
...@@ -267,10 +315,10 @@ class DomSelection {} ...@@ -267,10 +315,10 @@ class DomSelection {}
/// [DomSelection]'s required extension. /// [DomSelection]'s required extension.
extension DomSelectionExtension on DomSelection { extension DomSelectionExtension on DomSelection {
/// Removes all ranges from this selection. /// Removes all ranges from this selection.
external void removeAllRanges(); external JSVoid removeAllRanges();
/// Adds a range to this selection. /// Adds a range to this selection.
external void addRange(DomRange range); external JSVoid addRange(DomRange range);
} }
/// A DOM html div element. /// A DOM html div element.
...@@ -280,7 +328,7 @@ class DomHTMLDivElement extends DomHTMLElement {} ...@@ -280,7 +328,7 @@ class DomHTMLDivElement extends DomHTMLElement {}
/// Factory constructor for [DomHTMLDivElement]. /// Factory constructor for [DomHTMLDivElement].
DomHTMLDivElement createDomHTMLDivElement() => DomHTMLDivElement createDomHTMLDivElement() =>
domDocument.createElement('div') as DomHTMLDivElement; domDocument.createElement('div'.toJS) as DomHTMLDivElement;
/// An html style element. /// An html style element.
@JS() @JS()
...@@ -295,7 +343,7 @@ extension DomHTMLStyleElementExtension on DomHTMLStyleElement { ...@@ -295,7 +343,7 @@ extension DomHTMLStyleElementExtension on DomHTMLStyleElement {
/// Factory constructor for [DomHTMLStyleElement]. /// Factory constructor for [DomHTMLStyleElement].
DomHTMLStyleElement createDomHTMLStyleElement() => DomHTMLStyleElement createDomHTMLStyleElement() =>
domDocument.createElement('style') as DomHTMLStyleElement; domDocument.createElement('style'.toJS) as DomHTMLStyleElement;
/// CSS styles. /// CSS styles.
@JS() @JS()
...@@ -310,11 +358,14 @@ extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration { ...@@ -310,11 +358,14 @@ extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration {
/// Sets the height. /// Sets the height.
set height(String value) => setProperty('height', value); set height(String value) => setProperty('height', value);
@JS('setProperty')
external JSVoid _setProperty(
JSString propertyName, JSString value, JSString priority);
/// Sets a CSS property by name. /// Sets a CSS property by name.
void setProperty(String propertyName, String value, [String? priority]) { void setProperty(String propertyName, String value, [String? priority]) {
priority ??= ''; priority ??= '';
js_util.callMethod( _setProperty(propertyName.toJS, value.toJS, priority.toJS);
this, 'setProperty', <Object>[propertyName, value, priority]);
} }
} }
...@@ -335,12 +386,20 @@ class DomCSSStyleSheet extends DomStyleSheet {} ...@@ -335,12 +386,20 @@ class DomCSSStyleSheet extends DomStyleSheet {}
/// [DomCSSStyleSheet]'s required extension. /// [DomCSSStyleSheet]'s required extension.
extension DomCSSStyleSheetExtension on DomCSSStyleSheet { extension DomCSSStyleSheetExtension on DomCSSStyleSheet {
@JS('insertRule')
external JSNumber _insertRule1(JSString rule);
@JS('insertRule')
external JSNumber _insertRule2(JSString rule, JSNumber index);
/// Inserts a rule into this style sheet. /// Inserts a rule into this style sheet.
int insertRule(String rule, [int? index]) => int insertRule(String rule, [int? index]) {
js_util.callMethod<double>(this, 'insertRule', <Object>[ if (index == null) {
rule, return _insertRule1(rule.toJS).toDart.toInt();
if (index != null) index.toDouble() } else {
]).toInt(); return _insertRule2(rule.toJS, index.toDouble().toJS).toDart.toInt();
}
}
} }
/// A list of token. /// A list of token.
...@@ -350,6 +409,9 @@ class DomTokenList {} ...@@ -350,6 +409,9 @@ class DomTokenList {}
/// [DomTokenList]'s required extension. /// [DomTokenList]'s required extension.
extension DomTokenListExtension on DomTokenList { extension DomTokenListExtension on DomTokenList {
@JS('add')
external JSVoid _add(JSString value);
/// Adds a token to this token list. /// Adds a token to this token list.
external void add(String value); void add(String value) => _add(value.toJS);
} }
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:js/js.dart';
import '../services/dom.dart'; import '../services/dom.dart';
import 'basic.dart'; import 'basic.dart';
...@@ -115,7 +114,7 @@ class PlatformSelectableRegionContextMenu extends StatelessWidget { ...@@ -115,7 +114,7 @@ class PlatformSelectableRegionContextMenu extends StatelessWidget {
sheet.insertRule(_kClassRule, 0); sheet.insertRule(_kClassRule, 0);
sheet.insertRule(_kClassSelectionRule, 1); sheet.insertRule(_kClassSelectionRule, 1);
htmlElement.addEventListener('mousedown', allowInterop((DomEvent event) { htmlElement.addEventListener('mousedown', createDomEventListener((DomEvent event) {
final DomMouseEvent mouseEvent = event as DomMouseEvent; final DomMouseEvent mouseEvent = event as DomMouseEvent;
if (mouseEvent.button != _kRightClickButton) { if (mouseEvent.button != _kRightClickButton) {
return; return;
......
...@@ -2,19 +2,25 @@ ...@@ -2,19 +2,25 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:js_interop';
import 'package:flutter/src/services/dom.dart'; import 'package:flutter/src/services/dom.dart';
import 'package:js/js.dart'; import 'package:js/js.dart';
import 'package:js/js_util.dart' as js_util; import 'package:js/js_util.dart' as js_util;
void createGetter<T>(Object mock, String key, T Function() get) { /// Defines a new property on an Object.
@JS('Object.defineProperty')
external JSVoid objectDefineProperty(JSAny o, JSString symbol, JSAny desc);
void createGetter(JSAny mock, String key, JSAny? Function() get) {
objectDefineProperty( objectDefineProperty(
mock, mock,
key, key.toJS,
js_util.jsify( js_util.jsify(
<dynamic, dynamic>{ <String, Object>{
'get': allowInterop(() => get()) 'get': () { return get(); }.toJS
} }
)); ) as JSAny);
} }
@JS() @JS()
...@@ -22,46 +28,47 @@ void createGetter<T>(Object mock, String key, T Function() get) { ...@@ -22,46 +28,47 @@ void createGetter<T>(Object mock, String key, T Function() get) {
@anonymous @anonymous
class DomXMLHttpRequestMock { class DomXMLHttpRequestMock {
external factory DomXMLHttpRequestMock({ external factory DomXMLHttpRequestMock({
void Function(String method, String url, bool async)? open, JSFunction? open,
String responseType = 'invalid', JSString responseType,
int timeout = 10, JSNumber timeout,
bool withCredentials = false, JSBoolean withCredentials,
void Function()? send, JSFunction? send,
void Function(String name, String value)? setRequestHeader, JSFunction? setRequestHeader,
void Function(String type, DomEventListener listener) addEventListener, JSFunction addEventListener,
}); });
} }
class TestHttpRequest { class TestHttpRequest {
TestHttpRequest() { TestHttpRequest() {
_mock = DomXMLHttpRequestMock( _mock = DomXMLHttpRequestMock(
open: allowInterop(open), open: open.toJS,
send: allowInterop(send), send: send.toJS,
setRequestHeader: allowInterop(setRequestHeader), setRequestHeader: setRequestHeader.toJS,
addEventListener: allowInterop(addEventListener), addEventListener: addEventListener.toJS,
); );
createGetter(_mock, 'headers', () => headers); createGetter(_mock, 'headers', () => js_util.jsify(headers) as JSAny);
createGetter(_mock, 'responseHeaders', () => responseHeaders); createGetter(_mock,
createGetter(_mock, 'status', () => status); 'responseHeaders', () => js_util.jsify(responseHeaders) as JSAny);
createGetter(_mock, 'response', () => response); createGetter(_mock, 'status', () => status.toJS);
createGetter(_mock, 'response', () => js_util.jsify(response) as JSAny);
} }
late DomXMLHttpRequestMock _mock; late DomXMLHttpRequestMock _mock;
MockEvent? mockEvent; MockEvent? mockEvent;
Map<String, String> headers = <String, String>{}; Map<String, String> headers = <String, String>{};
int status = -1; int status = -1;
dynamic response; Object? response;
Map<String, String> get responseHeaders => headers; Map<String, String> get responseHeaders => headers;
void open(String method, String url, bool async) {} JSVoid open(JSString method, JSString url, JSBoolean async) {}
void send() {} JSVoid send() {}
void setRequestHeader(String name, String value) { JSVoid setRequestHeader(JSString name, JSString value) {
headers[name] = value; headers[name.toDart] = value.toDart;
} }
void addEventListener(String type, DomEventListener listener) { JSVoid addEventListener(JSString type, DomEventListener listener) {
if (type == mockEvent?.type) { if (type.toDart == mockEvent?.type) {
listener(mockEvent!.event); (listener.toDart as DartDomEventListener)(mockEvent!.event);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment