| 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 |