| Index: tools/dom/src/EventStreamProvider.dart
|
| diff --git a/tools/dom/src/EventStreamProvider.dart b/tools/dom/src/EventStreamProvider.dart
|
| index 474a1431b9defe83d3cc156678d63c763044a1a9..8ca17f252d7695710b7107823d3c64d057c142cb 100644
|
| --- a/tools/dom/src/EventStreamProvider.dart
|
| +++ b/tools/dom/src/EventStreamProvider.dart
|
| @@ -119,14 +119,54 @@ abstract class ElementStream<T extends Event> implements Stream<T> {
|
| }
|
|
|
| /**
|
| + * Task specification for Dom Events.
|
| + */
|
| +class EventSubscriptionSpecification<T extends Event>
|
| + implements TaskSpecification {
|
| + final String name;
|
| + final bool isOneShot;
|
| +
|
| + final EventTarget target;
|
| + final String eventType;
|
| + // TODO(floitsch): the first generic argument should be 'void'.
|
| + final ZoneUnaryCallback<dynamic, T> onData;
|
| + final bool useCapture;
|
| +
|
| + EventSubscriptionSpecification({this.name, this.isOneShot, this.target,
|
| + this.eventType, void this.onData(T event), this.useCapture});
|
| +
|
| + /// Returns a copy of this instance, with every non-null argument replaced
|
| + /// by the given value.
|
| + EventSubscriptionSpecification<T> replace(
|
| + {String name, bool isOneShot, EventTarget target,
|
| + String eventType, void onData(T event), bool useCapture}) {
|
| + return new EventSubscriptionSpecification<T>(
|
| + name: name ?? this.name,
|
| + isOneShot: isOneShot ?? this.isOneShot,
|
| + target: target ?? this.target,
|
| + eventType: eventType ?? this.eventType,
|
| + onData: onData ?? this.onData,
|
| + useCapture: useCapture ?? this.useCapture);
|
| + }
|
| +}
|
| +
|
| +/**
|
| * Adapter for exposing DOM events as Dart streams.
|
| */
|
| class _EventStream<T extends Event> extends Stream<T> {
|
| final EventTarget _target;
|
| final String _eventType;
|
| final bool _useCapture;
|
| + /// The name that is used in the task specification.
|
| + final String _name;
|
| + /// Whether the stream can trigger multiple times.
|
| + final bool _isOneShot;
|
|
|
| - _EventStream(this._target, this._eventType, this._useCapture);
|
| + _EventStream(this._target, String eventType, this._useCapture,
|
| + {String name, bool isOneShot: false})
|
| + : _eventType = eventType,
|
| + _isOneShot = isOneShot,
|
| + _name = name ?? "dart.html.event.$eventType";
|
|
|
| // DOM events are inherently multi-subscribers.
|
| Stream<T> asBroadcastStream({void onListen(StreamSubscription<T> subscription),
|
| @@ -139,8 +179,21 @@ class _EventStream<T extends Event> extends Stream<T> {
|
| void onDone(),
|
| bool cancelOnError}) {
|
|
|
| - return new _EventStreamSubscription<T>(
|
| - this._target, this._eventType, onData, this._useCapture);
|
| + if (identical(Zone.current, Zone.ROOT)) {
|
| + return new _EventStreamSubscription<T>(
|
| + this._target, this._eventType, onData, this._useCapture,
|
| + Zone.current);
|
| + }
|
| +
|
| + var specification = new EventSubscriptionSpecification<T>(
|
| + name: this._name, isOneShot: this._isOneShot,
|
| + target: this._target, eventType: this._eventType,
|
| + onData: onData, useCapture: this._useCapture);
|
| + // We need to wrap the _createStreamSubscription call, since a tear-off
|
| + // would not bind the generic type 'T'.
|
| + return Zone.current.createTask((spec, Zone zone) {
|
| + return _createStreamSubscription/*<T>*/(spec, zone);
|
| + }, specification);
|
| }
|
| }
|
|
|
| @@ -155,8 +208,9 @@ bool _matchesWithAncestors(Event event, String selector) {
|
| */
|
| class _ElementEventStreamImpl<T extends Event> extends _EventStream<T>
|
| implements ElementStream<T> {
|
| - _ElementEventStreamImpl(target, eventType, useCapture) :
|
| - super(target, eventType, useCapture);
|
| + _ElementEventStreamImpl(target, eventType, useCapture,
|
| + {String name, bool isOneShot: false}) :
|
| + super(target, eventType, useCapture, name: name, isOneShot: isOneShot);
|
|
|
| Stream<T> matches(String selector) => this.where(
|
| (event) => _matchesWithAncestors(event, selector)).map((e) {
|
| @@ -164,9 +218,23 @@ class _ElementEventStreamImpl<T extends Event> extends _EventStream<T>
|
| return e;
|
| });
|
|
|
| - StreamSubscription<T> capture(void onData(T event)) =>
|
| - new _EventStreamSubscription<T>(
|
| - this._target, this._eventType, onData, true);
|
| + StreamSubscription<T> capture(void onData(T event)) {
|
| +
|
| + if (identical(Zone.current, Zone.ROOT)) {
|
| + return new _EventStreamSubscription<T>(
|
| + this._target, this._eventType, onData, true, Zone.current);
|
| + }
|
| +
|
| + var specification = new EventSubscriptionSpecification<T>(
|
| + name: this._name, isOneShot: this._isOneShot,
|
| + target: this._target, eventType: this._eventtype,
|
| + onData: onData, useCapture: true);
|
| + // We need to wrap the _createStreamSubscription call, since a tear-off
|
| + // would not bind the generic type 'T'.
|
| + return Zone.current.createTask((spec, Zone zone) {
|
| + return _createStreamSubscription/*<T>*/(spec, zone);
|
| + }, specification);
|
| + }
|
| }
|
|
|
| /**
|
| @@ -215,7 +283,13 @@ class _ElementListEventStreamImpl<T extends Event> extends Stream<T>
|
| bool get isBroadcast => true;
|
| }
|
|
|
| -// We would like this to just be EventListener<T> but that typdef cannot
|
| +StreamSubscription/*<T>*/ _createStreamSubscription/*<T>*/(
|
| + EventSubscriptionSpecification/*<T>*/ spec, Zone zone) {
|
| + return new _EventStreamSubscription/*<T>*/(spec.target, spec.eventType,
|
| + spec.onData, spec.useCapture, zone);
|
| +}
|
| +
|
| +// We would like this to just be EventListener<T> but that typedef cannot
|
| // use generics until dartbug/26276 is fixed.
|
| typedef _EventListener<T extends Event>(T event);
|
|
|
| @@ -224,15 +298,19 @@ class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
|
| EventTarget _target;
|
| final String _eventType;
|
| EventListener _onData;
|
| + EventListener _domCallback;
|
| final bool _useCapture;
|
| + final Zone _zone;
|
|
|
| // TODO(jacobr): for full strong mode correctness we should write
|
| - // _onData = onData == null ? null : _wrapZone/*<Event, dynamic>*/((e) => onData(e as T))
|
| + // _onData = onData == null ? null : _wrapZone/*<dynamic, Event>*/((e) => onData(e as T))
|
| // but that breaks 114 co19 tests as well as multiple html tests as it is reasonable
|
| // to pass the wrong type of event object to an event listener as part of a
|
| // test.
|
| _EventStreamSubscription(this._target, this._eventType, void onData(T event),
|
| - this._useCapture) : _onData = _wrapZone/*<Event, dynamic>*/(onData) {
|
| + this._useCapture, Zone zone)
|
| + : _zone = zone,
|
| + _onData = _registerZone/*<dynamic, Event>*/(zone, onData) {
|
| _tryResume();
|
| }
|
|
|
| @@ -243,6 +321,7 @@ class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
|
| // Clear out the target to indicate this is complete.
|
| _target = null;
|
| _onData = null;
|
| + if (!identical(_zone, Zone.ROOT)) _zone.cancelTask(this);
|
| return null;
|
| }
|
|
|
| @@ -254,7 +333,7 @@ class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
|
| }
|
| // Remove current event listener.
|
| _unlisten();
|
| - _onData = _wrapZone/*<Event, dynamic>*/(handleData);
|
| + _onData = _registerZone/*<dynamic, Event>*/(_zone, handleData);
|
| _tryResume();
|
| }
|
|
|
| @@ -283,14 +362,25 @@ class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
|
| }
|
|
|
| void _tryResume() {
|
| - if (_onData != null && !isPaused) {
|
| - _target.addEventListener(_eventType, _onData, _useCapture);
|
| + if (_onData == null || isPaused) return;
|
| + if (identical(_zone, Zone.ROOT)) {
|
| + _domCallback = _onData;
|
| + } else {
|
| + _domCallback = (event) {
|
| + _zone.runTask(_runEventNotification, this, event);
|
| + };
|
| }
|
| + _target.addEventListener(_eventType, _domCallback, _useCapture);
|
| + }
|
| +
|
| + static void _runEventNotification/*<T>*/(
|
| + _EventStreamSubscription/*<T>*/ subscription, /*=T*/ event) {
|
| + subscription._onData(event);
|
| }
|
|
|
| void _unlisten() {
|
| if (_onData != null) {
|
| - _target.removeEventListener(_eventType, _onData, _useCapture);
|
| + _target.removeEventListener(_eventType, _domCallback, _useCapture);
|
| }
|
| }
|
|
|
|
|