Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(114)

Side by Side Diff: tools/dom/src/EventStreamProvider.dart

Issue 2022263002: Make Dom events run through zone tasks. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Fixes and rebase. Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of html; 5 part of html;
6 6
7 /** 7 /**
8 * A factory to expose DOM events as Streams. 8 * A factory to expose DOM events as Streams.
9 */ 9 */
10 class EventStreamProvider<T extends Event> { 10 class EventStreamProvider<T extends Event> {
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
112 * 112 *
113 * ## Other resources 113 * ## Other resources
114 * 114 *
115 * * [Event Capture](http://www.w3.org/TR/DOM-Level-2-Events/events.html#Event s-flow-capture) 115 * * [Event Capture](http://www.w3.org/TR/DOM-Level-2-Events/events.html#Event s-flow-capture)
116 * from the W3C DOM Events specification. 116 * from the W3C DOM Events specification.
117 */ 117 */
118 StreamSubscription<T> capture(void onData(T event)); 118 StreamSubscription<T> capture(void onData(T event));
119 } 119 }
120 120
121 /** 121 /**
122 * Task specification for Dom Events.
Lasse Reichstein Nielsen 2016/06/07 12:21:44 Dom -> DOM
floitsch 2016/06/10 15:43:58 Done.
123 */
Lasse Reichstein Nielsen 2016/06/07 12:21:44 I noticed you used /// below. Feel free to use it
floitsch 2016/06/10 15:43:58 done.
124 class EventSubscriptionSpecification<T extends Event>
125 implements TaskSpecification {
126 final String name;
Lasse Reichstein Nielsen 2016/06/07 12:21:44 Document the name. What is it? What values will it
floitsch 2016/06/10 15:43:58 Should inherit the documentation from `TaskSpecifi
127 final bool isOneShot;
Lasse Reichstein Nielsen 2016/06/07 12:21:44 Document: /// A one-shot event task is guaranteed
floitsch 2016/06/10 15:43:58 ditto.
128
129 final EventTarget target;
130 final String eventType;
Lasse Reichstein Nielsen 2016/06/07 12:21:44 I assume this is the eventType of the event, so "c
floitsch 2016/06/10 15:43:58 yes. added a bit of documentation (not a lot, thou
131 // TODO(floitsch): the first generic argument should be 'void'.
132 final ZoneUnaryCallback<dynamic, T> onData;
133 final bool useCapture;
134
135 EventSubscriptionSpecification({this.name, this.isOneShot, this.target,
136 this.eventType, void this.onData(T event), this.useCapture});
137
138 /// Returns a copy of this instance, with every non-null argument replaced
139 /// by the given value.
140 EventSubscriptionSpecification<T> replace(
141 {String name, bool isOneShot, EventTarget target,
142 String eventType, void onData(T event), bool useCapture}) {
143 return new EventSubscriptionSpecification<T>(
144 name: name ?? this.name,
145 isOneShot: isOneShot ?? this.isOneShot,
146 target: target ?? this.target,
147 eventType: eventType ?? this.eventType,
148 onData: onData ?? this.onData,
149 useCapture: useCapture ?? this.useCapture);
150 }
151 }
152
153 /**
122 * Adapter for exposing DOM events as Dart streams. 154 * Adapter for exposing DOM events as Dart streams.
123 */ 155 */
124 class _EventStream<T extends Event> extends Stream<T> { 156 class _EventStream<T extends Event> extends Stream<T> {
125 final EventTarget _target; 157 final EventTarget _target;
126 final String _eventType; 158 final String _eventType;
127 final bool _useCapture; 159 final bool _useCapture;
160 /// The name that is used in the task specification.
161 final String _name;
162 /// Whether the stream can trigger multiple times.
163 final bool _isOneShot;
128 164
129 _EventStream(this._target, this._eventType, this._useCapture); 165 _EventStream(this._target, String eventType, this._useCapture,
166 {String name, bool isOneShot: false})
167 : _eventType = eventType,
168 _isOneShot = isOneShot,
169 _name = name ?? "dart.html.event.$eventType";
Lasse Reichstein Nielsen 2016/06/07 12:21:44 That looks potentially expensive if you have many
floitsch 2016/06/10 15:43:58 I think the EventStreamProvider (which is a `const
130 170
131 // DOM events are inherently multi-subscribers. 171 // DOM events are inherently multi-subscribers.
132 Stream<T> asBroadcastStream({void onListen(StreamSubscription<T> subscription) , 172 Stream<T> asBroadcastStream({void onListen(StreamSubscription<T> subscription) ,
133 void onCancel(StreamSubscription<T> subscription) }) 173 void onCancel(StreamSubscription<T> subscription) })
134 => this; 174 => this;
135 bool get isBroadcast => true; 175 bool get isBroadcast => true;
136 176
137 StreamSubscription<T> listen(void onData(T event), 177 StreamSubscription<T> listen(void onData(T event),
138 { Function onError, 178 { Function onError,
139 void onDone(), 179 void onDone(),
140 bool cancelOnError}) { 180 bool cancelOnError}) {
141 181
142 return new _EventStreamSubscription<T>( 182 if (identical(Zone.current, Zone.ROOT)) {
143 this._target, this._eventType, onData, this._useCapture); 183 return new _EventStreamSubscription<T>(
184 this._target, this._eventType, onData, this._useCapture,
185 Zone.current);
186 }
187
188 var specification = new EventSubscriptionSpecification<T>(
189 name: this._name, isOneShot: this._isOneShot,
190 target: this._target, eventType: this._eventType,
191 onData: onData, useCapture: this._useCapture);
192 // We need to wrap the _createStreamSubscription call, since a tear-off
193 // would not bind the generic type 'T'.
194 return Zone.current.createTask((spec, Zone zone) {
195 return _createStreamSubscription/*<T>*/(spec, zone);
196 }, specification);
144 } 197 }
145 } 198 }
146 199
147 bool _matchesWithAncestors(Event event, String selector) { 200 bool _matchesWithAncestors(Event event, String selector) {
148 var target = event.target; 201 var target = event.target;
149 return target is Element ? target.matchesWithAncestors(selector) : false; 202 return target is Element ? target.matchesWithAncestors(selector) : false;
150 } 203 }
151 204
152 /** 205 /**
153 * Adapter for exposing DOM Element events as streams, while also allowing 206 * Adapter for exposing DOM Element events as streams, while also allowing
154 * event delegation. 207 * event delegation.
155 */ 208 */
156 class _ElementEventStreamImpl<T extends Event> extends _EventStream<T> 209 class _ElementEventStreamImpl<T extends Event> extends _EventStream<T>
157 implements ElementStream<T> { 210 implements ElementStream<T> {
158 _ElementEventStreamImpl(target, eventType, useCapture) : 211 _ElementEventStreamImpl(target, eventType, useCapture,
159 super(target, eventType, useCapture); 212 {String name, bool isOneShot: false}) :
213 super(target, eventType, useCapture, name: name, isOneShot: isOneShot);
160 214
161 Stream<T> matches(String selector) => this.where( 215 Stream<T> matches(String selector) => this.where(
162 (event) => _matchesWithAncestors(event, selector)).map((e) { 216 (event) => _matchesWithAncestors(event, selector)).map((e) {
163 e._selector = selector; 217 e._selector = selector;
164 return e; 218 return e;
165 }); 219 });
166 220
167 StreamSubscription<T> capture(void onData(T event)) => 221 StreamSubscription<T> capture(void onData(T event)) {
168 new _EventStreamSubscription<T>( 222
169 this._target, this._eventType, onData, true); 223 if (identical(Zone.current, Zone.ROOT)) {
224 return new _EventStreamSubscription<T>(
225 this._target, this._eventType, onData, true, Zone.current);
226 }
227
228 var specification = new EventSubscriptionSpecification<T>(
229 name: this._name, isOneShot: this._isOneShot,
230 target: this._target, eventType: this._eventType,
231 onData: onData, useCapture: true);
232 // We need to wrap the _createStreamSubscription call, since a tear-off
233 // would not bind the generic type 'T'.
234 return Zone.current.createTask((spec, Zone zone) {
235 return _createStreamSubscription/*<T>*/(spec, zone);
236 }, specification);
237 }
Lasse Reichstein Nielsen 2016/06/07 12:21:44 This function is almost identical to listen above.
floitsch 2016/06/10 15:43:58 Done.
170 } 238 }
171 239
172 /** 240 /**
173 * Adapter for exposing events on a collection of DOM Elements as streams, 241 * Adapter for exposing events on a collection of DOM Elements as streams,
174 * while also allowing event delegation. 242 * while also allowing event delegation.
175 */ 243 */
176 class _ElementListEventStreamImpl<T extends Event> extends Stream<T> 244 class _ElementListEventStreamImpl<T extends Event> extends Stream<T>
177 implements ElementStream<T> { 245 implements ElementStream<T> {
178 final Iterable<Element> _targetList; 246 final Iterable<Element> _targetList;
179 final bool _useCapture; 247 final bool _useCapture;
(...skipping 28 matching lines...) Expand all
208 } 276 }
209 return pool.stream.listen(onData); 277 return pool.stream.listen(onData);
210 } 278 }
211 279
212 Stream<T> asBroadcastStream({void onListen(StreamSubscription<T> subscription) , 280 Stream<T> asBroadcastStream({void onListen(StreamSubscription<T> subscription) ,
213 void onCancel(StreamSubscription<T> subscription) }) 281 void onCancel(StreamSubscription<T> subscription) })
214 => this; 282 => this;
215 bool get isBroadcast => true; 283 bool get isBroadcast => true;
216 } 284 }
217 285
218 // We would like this to just be EventListener<T> but that typdef cannot 286 StreamSubscription/*<T>*/ _createStreamSubscription/*<T>*/(
287 EventSubscriptionSpecification/*<T>*/ spec, Zone zone) {
288 return new _EventStreamSubscription/*<T>*/(spec.target, spec.eventType,
289 spec.onData, spec.useCapture, zone);
290 }
291
292 // We would like this to just be EventListener<T> but that typedef cannot
219 // use generics until dartbug/26276 is fixed. 293 // use generics until dartbug/26276 is fixed.
220 typedef _EventListener<T extends Event>(T event); 294 typedef _EventListener<T extends Event>(T event);
221 295
222 class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> { 296 class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
223 int _pauseCount = 0; 297 int _pauseCount = 0;
224 EventTarget _target; 298 EventTarget _target;
225 final String _eventType; 299 final String _eventType;
226 EventListener _onData; 300 EventListener _onData;
301 EventListener _domCallback;
227 final bool _useCapture; 302 final bool _useCapture;
303 final Zone _zone;
228 304
229 // TODO(jacobr): for full strong mode correctness we should write 305 // TODO(jacobr): for full strong mode correctness we should write
230 // _onData = onData == null ? null : _wrapZone/*<Event, dynamic>*/((e) => onDa ta(e as T)) 306 // _onData = onData == null ? null : _wrapZone/*<dynamic, Event>*/((e) => onDa ta(e as T))
231 // but that breaks 114 co19 tests as well as multiple html tests as it is reas onable 307 // but that breaks 114 co19 tests as well as multiple html tests as it is reas onable
232 // to pass the wrong type of event object to an event listener as part of a 308 // to pass the wrong type of event object to an event listener as part of a
233 // test. 309 // test.
234 _EventStreamSubscription(this._target, this._eventType, void onData(T event), 310 _EventStreamSubscription(this._target, this._eventType, void onData(T event),
235 this._useCapture) : _onData = _wrapZone/*<Event, dynamic>*/(onData) { 311 this._useCapture, Zone zone)
312 : _zone = zone,
313 _onData = _registerZone/*<dynamic, Event>*/(zone, onData) {
236 _tryResume(); 314 _tryResume();
237 } 315 }
238 316
239 Future cancel() { 317 Future cancel() {
240 if (_canceled) return null; 318 if (_canceled) return null;
241 319
242 _unlisten(); 320 _unlisten();
243 // Clear out the target to indicate this is complete. 321 // Clear out the target to indicate this is complete.
244 _target = null; 322 _target = null;
245 _onData = null; 323 _onData = null;
246 return null; 324 return null;
247 } 325 }
248 326
249 bool get _canceled => _target == null; 327 bool get _canceled => _target == null;
250 328
251 void onData(void handleData(T event)) { 329 void onData(void handleData(T event)) {
252 if (_canceled) { 330 if (_canceled) {
253 throw new StateError("Subscription has been canceled."); 331 throw new StateError("Subscription has been canceled.");
254 } 332 }
255 // Remove current event listener. 333 // Remove current event listener.
256 _unlisten(); 334 _unlisten();
257 _onData = _wrapZone/*<Event, dynamic>*/(handleData); 335 _onData = _registerZone/*<dynamic, Event>*/(_zone, handleData);
258 _tryResume(); 336 _tryResume();
259 } 337 }
260 338
261 /// Has no effect. 339 /// Has no effect.
262 void onError(Function handleError) {} 340 void onError(Function handleError) {}
263 341
264 /// Has no effect. 342 /// Has no effect.
265 void onDone(void handleDone()) {} 343 void onDone(void handleDone()) {}
266 344
267 void pause([Future resumeSignal]) { 345 void pause([Future resumeSignal]) {
268 if (_canceled) return; 346 if (_canceled) return;
269 ++_pauseCount; 347 ++_pauseCount;
270 _unlisten(); 348 _unlisten();
271 349
272 if (resumeSignal != null) { 350 if (resumeSignal != null) {
273 resumeSignal.whenComplete(resume); 351 resumeSignal.whenComplete(resume);
274 } 352 }
275 } 353 }
276 354
277 bool get isPaused => _pauseCount > 0; 355 bool get isPaused => _pauseCount > 0;
278 356
279 void resume() { 357 void resume() {
280 if (_canceled || !isPaused) return; 358 if (_canceled || !isPaused) return;
281 --_pauseCount; 359 --_pauseCount;
282 _tryResume(); 360 _tryResume();
283 } 361 }
284 362
285 void _tryResume() { 363 void _tryResume() {
286 if (_onData != null && !isPaused) { 364 if (_onData == null || isPaused) return;
287 _target.addEventListener(_eventType, _onData, _useCapture); 365 if (identical(_zone, Zone.ROOT)) {
366 _domCallback = _onData;
367 } else {
368 _domCallback = (event) {
369 _zone.runTask(_runEventNotification, this, event);
370 };
288 } 371 }
372 _target.addEventListener(_eventType, _domCallback, _useCapture);
373 }
374
375 static void _runEventNotification/*<T>*/(
376 _EventStreamSubscription/*<T>*/ subscription, /*=T*/ event) {
377 subscription._onData(event);
289 } 378 }
290 379
291 void _unlisten() { 380 void _unlisten() {
292 if (_onData != null) { 381 if (_onData != null) {
293 _target.removeEventListener(_eventType, _onData, _useCapture); 382 _target.removeEventListener(_eventType, _domCallback, _useCapture);
294 } 383 }
295 } 384 }
296 385
297 Future/*<E>*/ asFuture/*<E>*/([var/*=E*/ futureValue]) { 386 Future/*<E>*/ asFuture/*<E>*/([var/*=E*/ futureValue]) {
298 // We just need a future that will never succeed or fail. 387 // We just need a future that will never succeed or fail.
299 var completer = new Completer/*<E>*/(); 388 var completer = new Completer/*<E>*/();
300 return completer.future; 389 return completer.future;
301 } 390 }
302 } 391 }
303 392
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after
437 return new _ElementListEventStreamImpl<T>(e, _eventTypeGetter(e), useCapture ); 526 return new _ElementListEventStreamImpl<T>(e, _eventTypeGetter(e), useCapture );
438 } 527 }
439 528
440 String getEventType(EventTarget target) { 529 String getEventType(EventTarget target) {
441 return _eventTypeGetter(target); 530 return _eventTypeGetter(target);
442 } 531 }
443 532
444 String get _eventType => 533 String get _eventType =>
445 throw new UnsupportedError('Access type through getEventType method.'); 534 throw new UnsupportedError('Access type through getEventType method.');
446 } 535 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698