| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 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. |
| 4 |
| 5 part of html; |
| 6 |
| 7 typedef void _MicrotaskCallback(); |
| 8 |
| 9 /** |
| 10 * This class attempts to invoke a callback as soon as the current event stack |
| 11 * unwinds, but before the browser repaints. |
| 12 */ |
| 13 abstract class _MicrotaskScheduler { |
| 14 bool _nextMicrotaskFrameScheduled = false; |
| 15 final _MicrotaskCallback _callback; |
| 16 |
| 17 _MicrotaskScheduler(this._callback); |
| 18 |
| 19 /** |
| 20 * Creates the best possible microtask scheduler for the current platform. |
| 21 */ |
| 22 factory _MicrotaskScheduler.best(_MicrotaskCallback callback) { |
| 23 if (Window._supportsSetImmediate) { |
| 24 return new _SetImmediateScheduler(callback); |
| 25 } else if (MutationObserver.supported) { |
| 26 return new _MutationObserverScheduler(callback); |
| 27 } |
| 28 return new _PostMessageScheduler(callback); |
| 29 } |
| 30 |
| 31 /** |
| 32 * Schedules a microtask callback if one has not been scheduled already. |
| 33 */ |
| 34 void maybeSchedule() { |
| 35 if (this._nextMicrotaskFrameScheduled) { |
| 36 return; |
| 37 } |
| 38 this._nextMicrotaskFrameScheduled = true; |
| 39 this._schedule(); |
| 40 } |
| 41 |
| 42 /** |
| 43 * Does the actual scheduling of the callback. |
| 44 */ |
| 45 void _schedule(); |
| 46 |
| 47 /** |
| 48 * Handles the microtask callback and forwards it if necessary. |
| 49 */ |
| 50 void _onCallback() { |
| 51 // Ignore spurious messages. |
| 52 if (!_nextMicrotaskFrameScheduled) { |
| 53 return; |
| 54 } |
| 55 _nextMicrotaskFrameScheduled = false; |
| 56 this._callback(); |
| 57 } |
| 58 } |
| 59 |
| 60 /** |
| 61 * Scheduler which uses window.postMessage to schedule events. |
| 62 */ |
| 63 class _PostMessageScheduler extends _MicrotaskScheduler { |
| 64 final _MICROTASK_MESSAGE = "DART-MICROTASK"; |
| 65 |
| 66 _PostMessageScheduler(_MicrotaskCallback callback): super(callback) { |
| 67 // Messages from other windows do not cause a security risk as |
| 68 // all we care about is that _handleMessage is called |
| 69 // after the current event loop is unwound and calling the function is |
| 70 // a noop when zero requests are pending. |
| 71 window.onMessage.listen(this._handleMessage); |
| 72 } |
| 73 |
| 74 void _schedule() { |
| 75 window.postMessage(_MICROTASK_MESSAGE, "*"); |
| 76 } |
| 77 |
| 78 void _handleMessage(e) { |
| 79 this._onCallback(); |
| 80 } |
| 81 } |
| 82 |
| 83 /** |
| 84 * Scheduler which uses a MutationObserver to schedule events. |
| 85 */ |
| 86 class _MutationObserverScheduler extends _MicrotaskScheduler { |
| 87 MutationObserver _observer; |
| 88 Element _dummy; |
| 89 |
| 90 _MutationObserverScheduler(_MicrotaskCallback callback): super(callback) { |
| 91 // Mutation events get fired as soon as the current event stack is unwound |
| 92 // so we just make a dummy event and listen for that. |
| 93 _observer = new MutationObserver(this._handleMutation); |
| 94 _dummy = new DivElement(); |
| 95 _observer.observe(_dummy, attributes: true); |
| 96 } |
| 97 |
| 98 void _schedule() { |
| 99 // Toggle it to trigger the mutation event. |
| 100 _dummy.hidden = !_dummy.hidden; |
| 101 } |
| 102 |
| 103 _handleMutation(List<MutationRecord> mutations, MutationObserver observer) { |
| 104 this._onCallback(); |
| 105 } |
| 106 } |
| 107 |
| 108 /** |
| 109 * Scheduler which uses window.setImmediate to schedule events. |
| 110 */ |
| 111 class _SetImmediateScheduler extends _MicrotaskScheduler { |
| 112 _SetImmediateScheduler(_MicrotaskCallback callback): super(callback); |
| 113 |
| 114 void _schedule() { |
| 115 window._setImmediate(_handleImmediate); |
| 116 } |
| 117 |
| 118 void _handleImmediate() { |
| 119 this._onCallback(); |
| 120 } |
| 121 } |
| 122 |
| 123 List<TimeoutHandler> _pendingMicrotasks; |
| 124 _MicrotaskScheduler _microtaskScheduler = null; |
| 125 |
| 126 void _maybeScheduleMicrotaskFrame() { |
| 127 if (_microtaskScheduler == null) { |
| 128 _microtaskScheduler = |
| 129 new _MicrotaskScheduler.best(_completeMicrotasks); |
| 130 } |
| 131 _microtaskScheduler.maybeSchedule(); |
| 132 } |
| 133 |
| 134 /** |
| 135 * Registers a [callback] which is called after the current execution stack |
| 136 * unwinds. |
| 137 */ |
| 138 void _addMicrotaskCallback(TimeoutHandler callback) { |
| 139 if (_pendingMicrotasks == null) { |
| 140 _pendingMicrotasks = <TimeoutHandler>[]; |
| 141 _maybeScheduleMicrotaskFrame(); |
| 142 } |
| 143 _pendingMicrotasks.add(callback); |
| 144 } |
| 145 |
| 146 |
| 147 /** |
| 148 * Complete all pending microtasks. |
| 149 */ |
| 150 void _completeMicrotasks() { |
| 151 var callbacks = _pendingMicrotasks; |
| 152 _pendingMicrotasks = null; |
| 153 for (var callback in callbacks) { |
| 154 callback(); |
| 155 } |
| 156 } |
| OLD | NEW |