Revert zone tasks.

Revert "Add tasks to zones."

This reverts commit 85cccde717.

Revert "Make Dom events run through zone tasks."

This reverts commit 6d1f6b2af6.

Revert "Add zone task support for request-anim."

This reverts commit 726b9f8dc7.

Revert "Add zone task support to http-requests."

This reverts commit b40cfcb57f.

Revert "Update status file for jsshell."

This reverts commit 5e05ee9b5f.

Review URL: https://codereview.chromium.org/2120063002 .
This commit is contained in:
Florian Loitsch 2016-07-01 22:36:52 -07:00
parent 5e05ee9b5f
commit 34d3c37233
18 changed files with 221 additions and 3898 deletions

View file

@ -4,84 +4,6 @@
part of dart.async;
abstract class _TimerTask implements Timer {
final Zone _zone;
final Timer _nativeTimer;
_TimerTask(this._nativeTimer, this._zone);
void cancel() {
_nativeTimer.cancel();
}
bool get isActive => _nativeTimer.isActive;
}
class _SingleShotTimerTask extends _TimerTask {
// TODO(floitsch): the generic argument should be 'void'.
final ZoneCallback<dynamic> _callback;
_SingleShotTimerTask(Timer timer, this._callback, Zone zone)
: super(timer, zone);
}
class _PeriodicTimerTask extends _TimerTask {
// TODO(floitsch): the first generic argument should be 'void'.
final ZoneUnaryCallback<dynamic, Timer> _callback;
_PeriodicTimerTask(Timer timer, this._callback, Zone zone)
: super(timer, zone);
}
/**
* A task specification for a single-shot timer.
*
* *Experimental*. Might disappear without notice.
*/
class SingleShotTimerTaskSpecification implements TaskSpecification {
static const String specificationName = "dart.async.timer";
/** The duration after which the timer should invoke the [callback]. */
final Duration duration;
/** The callback that should be run when the timer triggers. */
// TODO(floitsch): the generic argument should be void.
final ZoneCallback<dynamic> callback;
SingleShotTimerTaskSpecification(this.duration, void this.callback());
@override
String get name => specificationName;
@override
bool get isOneShot => true;
}
/**
* A task specification for a periodic timer.
*
* *Experimental*. Might disappear without notice.
*/
class PeriodicTimerTaskSpecification implements TaskSpecification {
static const String specificationName = "dart.async.periodic-timer";
/** The interval at which the periodic timer should invoke the [callback]. */
final Duration duration;
/** The callback that should be run when the timer triggers. */
// TODO(floitsch): the first generic argument should be void.
final ZoneUnaryCallback<dynamic, Timer> callback;
PeriodicTimerTaskSpecification(
this.duration, void this.callback(Timer timer));
@override
String get name => specificationName;
@override
bool get isOneShot => false;
}
/**
* A count-down timer that can be configured to fire once or repeatedly.
*
@ -125,15 +47,10 @@ abstract class Timer {
if (Zone.current == Zone.ROOT) {
// No need to bind the callback. We know that the root's timer will
// be invoked in the root zone.
return Timer._createTimer(duration, callback);
return Zone.current.createTimer(duration, callback);
}
return Zone.current.createTimer(duration, callback);
}
factory Timer._task(Zone zone, Duration duration, void callback()) {
SingleShotTimerTaskSpecification specification =
new SingleShotTimerTaskSpecification(duration, callback);
return zone.createTask(_createSingleShotTimerTask, specification);
return Zone.current.createTimer(
duration, Zone.current.bindCallback(callback, runGuarded: true));
}
/**
@ -153,65 +70,17 @@ abstract class Timer {
* scheduled for - even if the actual callback was delayed.
*/
factory Timer.periodic(Duration duration,
void callback(Timer timer)) {
void callback(Timer timer)) {
if (Zone.current == Zone.ROOT) {
// No need to bind the callback. We know that the root's timer will
// be invoked in the root zone.
return Timer._createPeriodicTimer(duration, callback);
return Zone.current.createPeriodicTimer(duration, callback);
}
return Zone.current.createPeriodicTimer(duration, callback);
}
factory Timer._periodicTask(Zone zone, Duration duration,
void callback(Timer timer)) {
PeriodicTimerTaskSpecification specification =
new PeriodicTimerTaskSpecification(duration, callback);
return zone.createTask(_createPeriodicTimerTask, specification);
}
static Timer _createSingleShotTimerTask(
SingleShotTimerTaskSpecification specification, Zone zone) {
ZoneCallback registeredCallback = identical(_ROOT_ZONE, zone)
? specification.callback
: zone.registerCallback(specification.callback);
_TimerTask timerTask;
Timer nativeTimer = Timer._createTimer(specification.duration, () {
timerTask._zone.runTask(_runSingleShotCallback, timerTask, null);
});
timerTask = new _SingleShotTimerTask(nativeTimer, registeredCallback, zone);
return timerTask;
}
static void _runSingleShotCallback(_SingleShotTimerTask timerTask, Object _) {
timerTask._callback();
}
static Timer _createPeriodicTimerTask(
PeriodicTimerTaskSpecification specification, Zone zone) {
// TODO(floitsch): the return type should be 'void', and the type
// should be inferred.
ZoneUnaryCallback<dynamic, Timer> registeredCallback =
identical(_ROOT_ZONE, zone)
? specification.callback
: zone.registerUnaryCallback/*<dynamic, Timer>*/(
specification.callback);
_TimerTask timerTask;
Timer nativeTimer =
Timer._createPeriodicTimer(specification.duration, (Timer _) {
timerTask._zone.runTask(_runPeriodicCallback, timerTask, null);
});
timerTask = new _PeriodicTimerTask(nativeTimer, registeredCallback, zone);
return timerTask;
}
static void _runPeriodicCallback(_PeriodicTimerTask timerTask, Object _) {
timerTask._callback(timerTask);
var boundCallback = Zone.current.bindUnaryCallback/*<dynamic, Timer>*/(
callback, runGuarded: true);
return Zone.current.createPeriodicTimer(duration, boundCallback);
}
/**

View file

@ -8,13 +8,6 @@ typedef R ZoneCallback<R>();
typedef R ZoneUnaryCallback<R, T>(T arg);
typedef R ZoneBinaryCallback<R, T1, T2>(T1 arg1, T2 arg2);
/// *Experimental*. Might disappear without warning.
typedef T TaskCreate<T, S extends TaskSpecification>(
S specification, Zone zone);
/// *Experimental*. Might disappear without warning.
typedef void TaskRun<T, A>(T task, A arg);
// TODO(floitsch): we are abusing generic typedefs as typedefs for generic
// functions.
/*ABUSE*/
@ -40,30 +33,18 @@ typedef ZoneBinaryCallback<R, T1, T2> RegisterBinaryCallbackHandler<R, T1, T2>(
Zone self, ZoneDelegate parent, Zone zone, R f(T1 arg1, T2 arg2));
typedef AsyncError ErrorCallbackHandler(Zone self, ZoneDelegate parent,
Zone zone, Object error, StackTrace stackTrace);
/// *Experimental*. Might disappear without warning.
/*ABUSE*/
typedef T CreateTaskHandler<T, S extends TaskSpecification>(
Zone self, ZoneDelegate parent, Zone zone,
TaskCreate<T, S> create, S taskSpecification);
/// *Experimental*. Might disappear without warning.
/*ABUSE*/
typedef void RunTaskHandler<T, A>(Zone self, ZoneDelegate parent, Zone zone,
TaskRun<T, A> run, T task, A arg);
typedef void ScheduleMicrotaskHandler(
Zone self, ZoneDelegate parent, Zone zone, void f());
typedef void PrintHandler(
Zone self, ZoneDelegate parent, Zone zone, String line);
typedef Zone ForkHandler(Zone self, ZoneDelegate parent, Zone zone,
ZoneSpecification specification,
Map zoneValues);
// The following typedef declarations are used by functionality which
// will be removed and replaced by tasksif the task experiment is successful.
typedef Timer CreateTimerHandler(
Zone self, ZoneDelegate parent, Zone zone, Duration duration, void f());
typedef Timer CreatePeriodicTimerHandler(
Zone self, ZoneDelegate parent, Zone zone,
Duration period, void f(Timer timer));
typedef void PrintHandler(
Zone self, ZoneDelegate parent, Zone zone, String line);
typedef Zone ForkHandler(Zone self, ZoneDelegate parent, Zone zone,
ZoneSpecification specification,
Map zoneValues);
/** Pair of error and stack trace. Returned by [Zone.errorCallback]. */
class AsyncError implements Error {
@ -75,41 +56,10 @@ class AsyncError implements Error {
String toString() => '$error';
}
/**
* A task specification contains the necessary information to create a task.
*
* See [Zone.createTask] for how a specification is used to create a task.
*
* Task specifications should be public and it should be possible to create
* new instances as a user. That is, custom zones should be able to replace
* an existing specification with a modified one.
*
* *Experimental*. This class might disappear without warning.
*/
abstract class TaskSpecification {
/**
* Description of the task.
*
* This string is unused by the root-zone, but might be used for debugging,
* and testing. As such, it should be relatively unique in its category.
*
* As a general guideline we recommend: "package-name.library.action".
*/
String get name;
/**
* Whether the scheduled task triggers at most once.
*
* If the task is not a one-shot task, it may need to be canceled to prevent
* further iterations of the task.
*/
bool get isOneShot;
}
class _ZoneFunction<T extends Function> {
final _Zone zone;
final T function;
const _ZoneFunction(this.zone, this.function);
}
@ -135,9 +85,6 @@ class _ZoneFunction<T extends Function> {
abstract class ZoneSpecification {
/**
* Creates a specification with the provided handlers.
*
* The task-related parameters ([createTask] and [runTask]) are experimental
* and might be removed without warning.
*/
const factory ZoneSpecification({
HandleUncaughtErrorHandler handleUncaughtError,
@ -149,11 +96,7 @@ abstract class ZoneSpecification {
RegisterBinaryCallbackHandler registerBinaryCallback,
ErrorCallbackHandler errorCallback,
ScheduleMicrotaskHandler scheduleMicrotask,
CreateTaskHandler createTask,
RunTaskHandler runTask,
// TODO(floitsch): mark as deprecated once tasks are non-experimental.
CreateTimerHandler createTimer,
// TODO(floitsch): mark as deprecated once tasks are non-experimental.
CreatePeriodicTimerHandler createPeriodicTimer,
PrintHandler print,
ForkHandler fork
@ -162,9 +105,6 @@ abstract class ZoneSpecification {
/**
* Creates a specification from [other] with the provided handlers overriding
* the ones in [other].
*
* The task-related parameters ([createTask] and [runTask]) are experimental
* and might be removed without warning.
*/
factory ZoneSpecification.from(ZoneSpecification other, {
HandleUncaughtErrorHandler handleUncaughtError: null,
@ -176,11 +116,7 @@ abstract class ZoneSpecification {
RegisterBinaryCallbackHandler registerBinaryCallback: null,
ErrorCallbackHandler errorCallback: null,
ScheduleMicrotaskHandler scheduleMicrotask: null,
CreateTaskHandler createTask: null,
RunTaskHandler runTask: null,
// TODO(floitsch): mark as deprecated once tasks are non-experimental.
CreateTimerHandler createTimer: null,
// TODO(floitsch): mark as deprecated once tasks are non-experimental.
CreatePeriodicTimerHandler createPeriodicTimer: null,
PrintHandler print: null,
ForkHandler fork: null
@ -196,14 +132,11 @@ abstract class ZoneSpecification {
registerBinaryCallback: registerBinaryCallback ??
other.registerBinaryCallback,
errorCallback: errorCallback ?? other.errorCallback,
createTask: createTask ?? other.createTask,
runTask: runTask ?? other.runTask,
print : print ?? other.print,
fork: fork ?? other.fork,
scheduleMicrotask: scheduleMicrotask ?? other.scheduleMicrotask,
createTimer : createTimer ?? other.createTimer,
createPeriodicTimer: createPeriodicTimer ?? other.createPeriodicTimer);
createPeriodicTimer: createPeriodicTimer ?? other.createPeriodicTimer,
print : print ?? other.print,
fork: fork ?? other.fork);
}
HandleUncaughtErrorHandler get handleUncaughtError;
@ -215,17 +148,10 @@ abstract class ZoneSpecification {
RegisterBinaryCallbackHandler get registerBinaryCallback;
ErrorCallbackHandler get errorCallback;
ScheduleMicrotaskHandler get scheduleMicrotask;
/// *Experimental*. Might disappear without warning.
CreateTaskHandler get createTask;
/// *Experimental*. Might disappear without warning.
RunTaskHandler get runTask;
CreateTimerHandler get createTimer;
CreatePeriodicTimerHandler get createPeriodicTimer;
PrintHandler get print;
ForkHandler get fork;
// TODO(floitsch): deprecate once tasks are non-experimental.
CreateTimerHandler get createTimer;
// TODO(floitsch): deprecate once tasks are non-experimental.
CreatePeriodicTimerHandler get createPeriodicTimer;
}
/**
@ -246,14 +172,10 @@ class _ZoneSpecification implements ZoneSpecification {
this.registerBinaryCallback: null,
this.errorCallback: null,
this.scheduleMicrotask: null,
this.createTask: null,
this.runTask: null,
this.print: null,
this.fork: null,
// TODO(floitsch): deprecate once tasks are non-experimental.
this.createTimer: null,
// TODO(floitsch): deprecate once tasks are non-experimental.
this.createPeriodicTimer: null
this.createPeriodicTimer: null,
this.print: null,
this.fork: null
});
final HandleUncaughtErrorHandler handleUncaughtError;
@ -265,15 +187,10 @@ class _ZoneSpecification implements ZoneSpecification {
final RegisterBinaryCallbackHandler registerBinaryCallback;
final ErrorCallbackHandler errorCallback;
final ScheduleMicrotaskHandler scheduleMicrotask;
final CreateTaskHandler createTask;
final RunTaskHandler runTask;
final CreateTimerHandler createTimer;
final CreatePeriodicTimerHandler createPeriodicTimer;
final PrintHandler print;
final ForkHandler fork;
// TODO(floitsch): deprecate once tasks are non-experimental.
final CreateTimerHandler createTimer;
// TODO(floitsch): deprecate once tasks are non-experimental.
final CreatePeriodicTimerHandler createPeriodicTimer;
}
/**
@ -300,23 +217,10 @@ abstract class ZoneDelegate {
Zone zone, /*=R*/ f(/*=T1*/ arg1, /*=T2*/ arg2));
AsyncError errorCallback(Zone zone, Object error, StackTrace stackTrace);
void scheduleMicrotask(Zone zone, void f());
/// *Experimental*. Might disappear without notice.
Object/*=T*/ createTask/*<T, S extends TaskSpecification>*/(
Zone zone, TaskCreate/*<T, S>*/ create,
TaskSpecification/*=S*/ specification);
/// *Experimental*. Might disappear without notice.
void runTask/*<T, A>*/(
Zone zone, TaskRun/*<T, A>*/ run, Object/*=T*/ task,
Object/*=A*/ argument);
Timer createTimer(Zone zone, Duration duration, void f());
Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer));
void print(Zone zone, String line);
Zone fork(Zone zone, ZoneSpecification specification, Map zoneValues);
// TODO(floitsch): deprecate once tasks are non-experimental.
Timer createTimer(Zone zone, Duration duration, void f());
// TODO(floitsch): deprecate once tasks are non-experimental.
Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer));
}
/**
@ -506,101 +410,14 @@ abstract class Zone {
*/
void scheduleMicrotask(void f());
/**
* Creates a task in the current zone.
*
* A task represents an asynchronous operation or process that reports back
* through the event loop.
*
* This function allows the zone to intercept the initialization of the
* task while the [runTask] function is invoked when the task reports back.
*
* By default, in the root zone, the [create] function is invoked with the
* [specification] as argument. It returns a task object which is used for all
* future interactions between the zone and the task. The object is
* a unique instance representing the task. It is generally returned to
* whoever initiated the task.
* For example, the HTML library uses the returned [StreamSubscription] as
* task object when users register an event listener.
*
* Tasks are created when the program starts an operation that reports back
* through the event loop. For example, a timer or an HTTP request both
* return through the event loop and are therefore tasks.
*
* If the [create] function is not invoked (because a custom zone has
* replaced or intercepted it), then the operation is *not* started. This
* means that a custom zone can intercept tasks, like HTTP requests.
*
* A task goes through the following steps:
* - a user invokes a library function that should eventually return through
* the event loop.
* - the library function creates a [TaskSpecification] that contains the
* necessary information to start the operation, and invokes
* `Zone.current.createTask` with the specification and a [create] closure.
* The closure, when invoked, uses the specification to start the operation
* (usually by interacting with the underlying system, or as a native
* extension), and returns a task object that identifies the running task.
* - custom zones handle the request and (unless completely intercepted and
* aborted), end up calling the root zone's [createTask] which runs the
* provided `create` closure, which may have been replaced at this point.
* - later, the asynchronous operation returns through the event loop.
* It invokes [Zone.runTask] on the zone in which the task should run
* (and which was originally passed to the `create` function by
* `createTask`). The [runTask] function receives the
* task object, a `run` function and an argument. As before, custom zones
* may intercept this call. Eventually (unless aborted), the `run` function
* is invoked. This last step may happen multiple times for tasks that are
* not oneshot tasks (see [ZoneSpecification.isOneShot]).
*
* Custom zones may replace the [specification] with a different one, thus
* modifying the task parameters. An operation that wishes to be an
* interceptable task must publicly specify the types that intercepting code
* sees:
* - The specification type (extending [TaskSpecification]) which holds the
* information available when intercepting the `createTask` call.
* - The task object type, returned by `createTask` and [create]. This object
* may simply be typed as [Object].
* - The argument type, if [runTask] takes a meaningful argument.
*
* *Experimental*. Might disappear without notice.
*/
Object/*=T*/ createTask/*<T, S extends TaskSpecification>*/(
/*=T*/ create(TaskSpecification/*=S*/ specification, Zone zone),
TaskSpecification/*=S*/ specification);
/**
* Runs a task callback.
*
* This function is invoked, when an operation, started through [createTask],
* generates an event.
*
* Generally, tasks schedule Dart code in the global event loop when the
* [createTask] function is invoked. Since the
* event loop does not expect any return value from the code it runs, the
* [runTask] function is a void function.
*
* The [task] object must be the same as the one created with [createTask].
*
* It is good practice that task operations provide a meaningful [argument],
* so that custom zones can interact with it. They might want to log or
* replace the argument before calling the [run] function.
*
* *Experimental*. Might disappear without notice.
*/
void runTask/*<T, A>*/(
/*=T*/ run(/*=T*/ task, /*=A*/ argument), Object/*=T*/ task,
Object/*=A*/ argument);
/**
* Creates a Timer where the callback is executed in this zone.
*/
// TODO(floitsch): deprecate once tasks are non-experimental.
Timer createTimer(Duration duration, void callback());
/**
* Creates a periodic Timer where the callback is executed in this zone.
*/
// TODO(floitsch): deprecate once tasks are non-experimental.
Timer createPeriodicTimer(Duration period, void callback(Timer timer));
/**
@ -706,7 +523,7 @@ class _ZoneDelegate implements ZoneDelegate {
// TODO(floitsch): make this a generic method call on '<R>' once it's
// supported. Remove the unnecessary cast.
return handler(implZone, _parentDelegate(implZone), zone, f)
as dynamic/*=ZoneCallback<R>*/;
as Object/*=ZoneCallback<R>*/;
}
ZoneUnaryCallback/*<R, T>*/ registerUnaryCallback/*<R, T>*/(
@ -717,7 +534,7 @@ class _ZoneDelegate implements ZoneDelegate {
// TODO(floitsch): make this a generic method call on '<R, T>' once it's
// supported. Remove the unnecessary cast.
return handler(implZone, _parentDelegate(implZone), zone, f)
as dynamic/*=ZoneUnaryCallback<R, T>*/;
as Object/*=ZoneUnaryCallback<R, T>*/;
}
ZoneBinaryCallback/*<R, T1, T2>*/ registerBinaryCallback/*<R, T1, T2>*/(
@ -728,7 +545,7 @@ class _ZoneDelegate implements ZoneDelegate {
// TODO(floitsch): make this a generic method call on '<R, T1, T2>' once
// it's supported. Remove the unnecessary cast.
return handler(implZone, _parentDelegate(implZone), zone, f)
as dynamic/*=ZoneBinaryCallback<R, T1, T2>*/;
as Object/*=ZoneBinaryCallback<R, T1, T2>*/;
}
AsyncError errorCallback(Zone zone, Object error, StackTrace stackTrace) {
@ -747,25 +564,18 @@ class _ZoneDelegate implements ZoneDelegate {
handler(implZone, _parentDelegate(implZone), zone, f);
}
Object/*=T*/ createTask/*<T, S extends TaskSpecification>*/(
Zone zone, TaskCreate/*<T, S>*/ create, TaskSpecification/*=S*/ specification) {
var implementation = _delegationTarget._createTask;
Timer createTimer(Zone zone, Duration duration, void f()) {
var implementation = _delegationTarget._createTimer;
_Zone implZone = implementation.zone;
// TODO(floitsch): make the handler call a generic method call on '<T, S>'
// once it's supported. Remove the unnecessary cast.
var handler =
implementation.function as CreateTaskHandler/*<T, S>*/;
return handler(
implZone, _parentDelegate(implZone), zone, create, specification);
CreateTimerHandler handler = implementation.function;
return handler(implZone, _parentDelegate(implZone), zone, duration, f);
}
void runTask/*<T, A>*/(Zone zone, TaskRun run, Object /*=T*/ task,
Object /*=A*/ argument) {
var implementation = _delegationTarget._runTask;
Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer)) {
var implementation = _delegationTarget._createPeriodicTimer;
_Zone implZone = implementation.zone;
RunTaskHandler handler = implementation.function;
// TODO(floitsch): make this a generic call on '<T, A>'.
handler(implZone, _parentDelegate(implZone), zone, run, task, argument);
CreatePeriodicTimerHandler handler = implementation.function;
return handler(implZone, _parentDelegate(implZone), zone, period, f);
}
void print(Zone zone, String line) {
@ -783,22 +593,6 @@ class _ZoneDelegate implements ZoneDelegate {
return handler(
implZone, _parentDelegate(implZone), zone, specification, zoneValues);
}
// TODO(floitsch): deprecate once tasks are non-experimental.
Timer createTimer(Zone zone, Duration duration, void f()) {
var implementation = _delegationTarget._createTimer;
_Zone implZone = implementation.zone;
CreateTimerHandler handler = implementation.function;
return handler(implZone, _parentDelegate(implZone), zone, duration, f);
}
// TODO(floitsch): deprecate once tasks are non-experimental.
Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer)) {
var implementation = _delegationTarget._createPeriodicTimer;
_Zone implZone = implementation.zone;
CreatePeriodicTimerHandler handler = implementation.function;
return handler(implZone, _parentDelegate(implZone), zone, period, f);
}
}
@ -816,17 +610,11 @@ abstract class _Zone implements Zone {
_ZoneFunction<RegisterBinaryCallbackHandler> get _registerBinaryCallback;
_ZoneFunction<ErrorCallbackHandler> get _errorCallback;
_ZoneFunction<ScheduleMicrotaskHandler> get _scheduleMicrotask;
_ZoneFunction<CreateTaskHandler> get _createTask;
_ZoneFunction<RunTaskHandler> get _runTask;
_ZoneFunction<CreateTimerHandler> get _createTimer;
_ZoneFunction<CreatePeriodicTimerHandler> get _createPeriodicTimer;
_ZoneFunction<PrintHandler> get _print;
_ZoneFunction<ForkHandler> get _fork;
_ZoneFunction<HandleUncaughtErrorHandler> get _handleUncaughtError;
// TODO(floitsch): deprecate once tasks are non-experimental.
_ZoneFunction<CreateTimerHandler> get _createTimer;
// TODO(floitsch): deprecate once tasks are non-experimental.
_ZoneFunction<CreatePeriodicTimerHandler> get _createPeriodicTimer;
_Zone get parent;
ZoneDelegate get _delegate;
Map get _map;
@ -848,17 +636,12 @@ class _CustomZone extends _Zone {
_ZoneFunction<RegisterBinaryCallbackHandler> _registerBinaryCallback;
_ZoneFunction<ErrorCallbackHandler> _errorCallback;
_ZoneFunction<ScheduleMicrotaskHandler> _scheduleMicrotask;
_ZoneFunction<CreateTaskHandler> _createTask;
_ZoneFunction<RunTaskHandler> _runTask;
_ZoneFunction<CreateTimerHandler> _createTimer;
_ZoneFunction<CreatePeriodicTimerHandler> _createPeriodicTimer;
_ZoneFunction<PrintHandler> _print;
_ZoneFunction<ForkHandler> _fork;
_ZoneFunction<HandleUncaughtErrorHandler> _handleUncaughtError;
// TODO(floitsch): deprecate once tasks are non-experimental.
_ZoneFunction<CreateTimerHandler> _createTimer;
// TODO(floitsch): deprecate once tasks are non-experimental.
_ZoneFunction<CreatePeriodicTimerHandler> _createPeriodicTimer;
// A cached delegate to this zone.
ZoneDelegate _delegateCache;
@ -909,14 +692,13 @@ class _CustomZone extends _Zone {
? new _ZoneFunction<ScheduleMicrotaskHandler>(
this, specification.scheduleMicrotask)
: parent._scheduleMicrotask;
_createTask = (specification.createTask != null)
? new _ZoneFunction<CreateTaskHandler>(
this, specification.createTask)
: parent._createTask;
_runTask = (specification.runTask != null)
? new _ZoneFunction<RunTaskHandler>(
this, specification.runTask)
: parent._runTask;
_createTimer = (specification.createTimer != null)
? new _ZoneFunction<CreateTimerHandler>(this, specification.createTimer)
: parent._createTimer;
_createPeriodicTimer = (specification.createPeriodicTimer != null)
? new _ZoneFunction<CreatePeriodicTimerHandler>(
this, specification.createPeriodicTimer)
: parent._createPeriodicTimer;
_print = (specification.print != null)
? new _ZoneFunction<PrintHandler>(this, specification.print)
: parent._print;
@ -927,16 +709,6 @@ class _CustomZone extends _Zone {
? new _ZoneFunction<HandleUncaughtErrorHandler>(
this, specification.handleUncaughtError)
: parent._handleUncaughtError;
// Deprecated fields, once tasks are non-experimental.
_createTimer = (specification.createTimer != null)
? new _ZoneFunction<CreateTimerHandler>(
this, specification.createTimer)
: parent._createTimer;
_createPeriodicTimer = (specification.createPeriodicTimer != null)
? new _ZoneFunction<CreatePeriodicTimerHandler>(
this, specification.createPeriodicTimer)
: parent._createPeriodicTimer;
}
/**
@ -1087,7 +859,7 @@ class _CustomZone extends _Zone {
// TODO(floitsch): make this a generic method call on '<R>' once it's
// supported. Remove the unnecessary cast.
return handler(implementation.zone, parentDelegate, this, callback)
as dynamic/*=ZoneCallback<R>*/;
as Object/*=ZoneCallback<R>*/;
}
ZoneUnaryCallback/*<R, T>*/ registerUnaryCallback/*<R, T>*/(
@ -1099,7 +871,7 @@ class _CustomZone extends _Zone {
// TODO(floitsch): make this a generic method call on '<R, T>' once it's
// supported. Remove the unnecessary cast.
return handler(implementation.zone, parentDelegate, this, callback)
as dynamic/*=ZoneUnaryCallback<R, T>*/;
as Object/*=ZoneUnaryCallback<R, T>*/;
}
ZoneBinaryCallback/*<R, T1, T2>*/ registerBinaryCallback/*<R, T1, T2>*/(
@ -1111,7 +883,7 @@ class _CustomZone extends _Zone {
// TODO(floitsch): make this a generic method call on '<R, T1, T2>' once
// it's supported. Remove the unnecessary cast.
return handler(implementation.zone, parentDelegate, this, callback)
as dynamic/*=ZoneBinaryCallback<R, T1, T2>*/;
as Object/*=ZoneBinaryCallback<R, T1, T2>*/;
}
AsyncError errorCallback(Object error, StackTrace stackTrace) {
@ -1130,40 +902,9 @@ class _CustomZone extends _Zone {
assert(implementation != null);
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
ScheduleMicrotaskHandler handler = implementation.function;
handler(implementation.zone, parentDelegate, this, f);
return handler(implementation.zone, parentDelegate, this, f);
}
Object/*=T*/ createTask/*<T, S extends TaskSpecification>*/(
TaskCreate/*<T, S>*/ create, TaskSpecification/*=S*/ specification) {
var implementation = this._createTask;
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
// TODO(floitsch): make the handler call a generic method call on '<T, S>'
// once it's supported. Remove the unnecessary cast.
var handler =
implementation.function as CreateTaskHandler/*<T, S>*/;
return handler(
implementation.zone, parentDelegate, this, create, specification);
}
void runTask/*<T, A>*/(
TaskRun/*<T, A>*/ run, Object/*=T*/ task, Object/*=A*/ arg1) {
var implementation = this._runTask;
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
RunTaskHandler handler = implementation.function;
// TODO(floitsch): make this a generic method call on '<T, A>' once it's
// supported.
handler(implementation.zone, parentDelegate, this, run, task, arg1);
}
void print(String line) {
var implementation = this._print;
assert(implementation != null);
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
PrintHandler handler = implementation.function;
return handler(implementation.zone, parentDelegate, this, line);
}
// TODO(floitsch): deprecate once tasks are non-experimental.
Timer createTimer(Duration duration, void f()) {
var implementation = this._createTimer;
assert(implementation != null);
@ -1172,7 +913,6 @@ class _CustomZone extends _Zone {
return handler(implementation.zone, parentDelegate, this, duration, f);
}
// TODO(floitsch): deprecate once tasks are non-experimental.
Timer createPeriodicTimer(Duration duration, void f(Timer timer)) {
var implementation = this._createPeriodicTimer;
assert(implementation != null);
@ -1181,6 +921,14 @@ class _CustomZone extends _Zone {
return handler(
implementation.zone, parentDelegate, this, duration, f);
}
void print(String line) {
var implementation = this._print;
assert(implementation != null);
ZoneDelegate parentDelegate = _parentDelegate(implementation.zone);
PrintHandler handler = implementation.function;
return handler(implementation.zone, parentDelegate, this, line);
}
}
/*=R*/ _rootHandleUncaughtError/*<R>*/(
@ -1258,39 +1006,22 @@ void _rootScheduleMicrotask(Zone self, ZoneDelegate parent, Zone zone, f()) {
_scheduleAsyncCallback(f);
}
Object/*=T*/ _rootCreateTask/*<T, S extends TaskSpecification>*/(
Zone self, ZoneDelegate parent, Zone zone,
TaskCreate/*<T, S>*/ create, TaskSpecification/*=S*/ specification) {
return create(specification, zone);
}
void _rootRunTask/*<T, A>*/(
Zone self, ZoneDelegate parent, Zone zone, TaskRun run/*<T, A>*/,
Object/*=T*/ task, Object/*=A*/ arg) {
if (Zone._current == zone) {
run(task, arg);
return;
}
Zone old = Zone._enter(zone);
try {
run(task, arg);
} catch (e, s) {
zone.handleUncaughtError/*<dynamic>*/(e, s);
} finally {
Zone._leave(old);
}
}
Timer _rootCreateTimer(Zone self, ZoneDelegate parent, Zone zone,
Duration duration, void callback()) {
return new Timer._task(zone, duration, callback);
if (!identical(_ROOT_ZONE, zone)) {
callback = zone.bindCallback(callback);
}
return Timer._createTimer(duration, callback);
}
Timer _rootCreatePeriodicTimer(
Zone self, ZoneDelegate parent, Zone zone,
Duration duration, void callback(Timer timer)) {
return new Timer._periodicTask(zone, duration, callback);
if (!identical(_ROOT_ZONE, zone)) {
// TODO(floitsch): the return type should be 'void'.
callback = zone.bindUnaryCallback/*<dynamic, Timer>*/(callback);
}
return Timer._createPeriodicTimer(duration, callback);
}
void _rootPrint(Zone self, ZoneDelegate parent, Zone zone, String line) {
@ -1351,10 +1082,10 @@ class _RootZone extends _Zone {
_ZoneFunction<ScheduleMicrotaskHandler> get _scheduleMicrotask =>
const _ZoneFunction<ScheduleMicrotaskHandler>(
_ROOT_ZONE, _rootScheduleMicrotask);
_ZoneFunction<CreateTaskHandler> get _createTask =>
const _ZoneFunction<CreateTaskHandler>(_ROOT_ZONE, _rootCreateTask);
_ZoneFunction<RunTaskHandler> get _runTask =>
const _ZoneFunction<RunTaskHandler>(_ROOT_ZONE, _rootRunTask);
_ZoneFunction<CreateTimerHandler> get _createTimer =>
const _ZoneFunction<CreateTimerHandler>(_ROOT_ZONE, _rootCreateTimer);
_ZoneFunction<CreatePeriodicTimerHandler> get _createPeriodicTimer =>
const _ZoneFunction<CreatePeriodicTimerHandler>(_ROOT_ZONE, _rootCreatePeriodicTimer);
_ZoneFunction<PrintHandler> get _print =>
const _ZoneFunction<PrintHandler>(_ROOT_ZONE, _rootPrint);
_ZoneFunction<ForkHandler> get _fork =>
@ -1363,14 +1094,6 @@ class _RootZone extends _Zone {
const _ZoneFunction<HandleUncaughtErrorHandler>(
_ROOT_ZONE, _rootHandleUncaughtError);
// TODO(floitsch): deprecate once tasks are non-experimental.
_ZoneFunction<CreateTimerHandler> get _createTimer =>
const _ZoneFunction<CreateTimerHandler>(_ROOT_ZONE, _rootCreateTimer);
// TODO(floitsch): deprecate once tasks are non-experimental.
_ZoneFunction<CreatePeriodicTimerHandler> get _createPeriodicTimer =>
const _ZoneFunction<CreatePeriodicTimerHandler>(
_ROOT_ZONE, _rootCreatePeriodicTimer);
// The parent zone.
_Zone get parent => null;
@ -1502,16 +1225,6 @@ class _RootZone extends _Zone {
_rootScheduleMicrotask(null, null, this, f);
}
Object/*=T*/ createTask/*<T, S extends TaskSpecification>*/(
TaskCreate/*<T, S>*/ create, TaskSpecification/*=S*/ specification) {
return _rootCreateTask/*<T, S>*/(null, null, this, create, specification);
}
void runTask/*<T, A>*/(
TaskRun/*<T, A>*/ run, Object/*=T*/ task, Object/*=A*/ arg) {
_rootRunTask/*<T, A>*/(null, null, this, run, task, arg);
}
Timer createTimer(Duration duration, void f()) {
return Timer._createTimer(duration, f);
}

View file

@ -19240,109 +19240,6 @@ class HtmlOptionsCollection extends HtmlCollection {
// BSD-style license that can be found in the LICENSE file.
/**
* A task specification for HTTP requests.
*
* This specification is not available when an HTTP request is sent through
* direct use of [HttpRequest.send]. See [HttpRequestSendTaskSpecification].
*
* A task created from this specification is a `Future<HttpRequest>`.
*
* *Experimental*. This class may disappear without notice.
*/
class HttpRequestTaskSpecification extends TaskSpecification {
/// The URL of the request.
final String url;
/// The HTTP request method.
///
/// By default (when `null`) this is a `"GET"` request. Alternatively, the
/// method can be `"POST"`, `"PUT"`, `"DELETE"`, etc.
final String method;
/// Whether the request should send credentials. Credentials are only useful
/// for cross-origin requests.
///
/// See [HttpRequest.request] for more information.
final bool withCredentials;
/// The desired response format.
///
/// Supported types are:
/// - `""`: (same as `"text"`),
/// - `"arraybuffer"`,
/// - `"blob"`,
/// - `"document"`,
/// - `"json"`,
/// - `"text"`
///
/// When no value is provided (when equal to `null`) defaults to `""`.
final String responseType;
/// The desired MIME type.
///
/// This overrides the default MIME type which is set up to transfer textual
/// data.
final String mimeType;
/// The request headers that should be sent with the request.
final Map<String, String> requestHeaders;
/// The data that is sent with the request.
///
/// When data is provided (the value is not `null`), it must be a
/// [ByteBuffer], [Blob], [Document], [String], or [FormData].
final dynamic sendData;
/// The function that is invoked on progress updates. This function is
/// registered as an event listener on the created [HttpRequest] object, and
/// thus has its own task. Further invocations of the progress function do
/// *not* use the HTTP request task as task object.
///
/// Creating an HTTP request automatically registers the on-progress listener.
final ZoneUnaryCallback<dynamic, ProgressEvent> onProgress;
HttpRequestTaskSpecification(this.url,
{String this.method, bool this.withCredentials, String this.responseType,
String this.mimeType, Map<String, String> this.requestHeaders,
this.sendData,
void this.onProgress(ProgressEvent e)});
String get name => "dart.html.http-request";
bool get isOneShot => true;
}
/**
* A task specification for HTTP requests that are initiated through a direct
* invocation of [HttpRequest.send].
*
* This specification serves as signal to zones that an HTTP request has been
* initiated. The created task is the [request] object itself, and
* no callback is ever executed in this task.
*
* Note that event listeners on the HTTP request are also registered in the
* zone (although with their own task creations), and that a zone can thus
* detect when the HTTP request returns.
*
* HTTP requests that are initiated through `request` methods don't use
* this class but use [HttpRequestTaskSpecification].
*
* *Experimental*. This class may disappear without notice.
*/
class HttpRequestSendTaskSpecification extends TaskSpecification {
final HttpRequest request;
final dynamic sendData;
HttpRequestSendTaskSpecification(this.request, this.sendData);
String get name => "dart.html.http-request-send";
/**
* No callback is ever executed in an HTTP request send task.
*/
bool get isOneShot => false;
}
/**
* A client-side XHR request for getting data from a URL,
* formally known as XMLHttpRequest.
@ -19531,34 +19428,7 @@ class HttpRequest extends HttpRequestEventTarget {
{String method, bool withCredentials, String responseType,
String mimeType, Map<String, String> requestHeaders, sendData,
void onProgress(ProgressEvent e)}) {
var spec = new HttpRequestTaskSpecification(
url, method: method,
withCredentials: withCredentials,
responseType: responseType,
mimeType: mimeType,
requestHeaders: requestHeaders,
sendData: sendData,
onProgress: onProgress);
if (identical(Zone.current, Zone.ROOT)) {
return _createHttpRequestTask(spec, null);
}
return Zone.current.createTask(_createHttpRequestTask, spec);
}
static Future<HttpRequest> _createHttpRequestTask(
HttpRequestTaskSpecification spec, Zone zone) {
String url = spec.url;
String method = spec.method;
bool withCredentials = spec.withCredentials;
String responseType = spec.responseType;
String mimeType = spec.mimeType;
Map<String, String> requestHeaders = spec.requestHeaders;
var sendData = spec.sendData;
var onProgress = spec.onProgress;
var completer = new Completer<HttpRequest>();
var task = completer.future;
var xhr = new HttpRequest();
if (method == null) {
@ -19598,42 +19468,23 @@ class HttpRequest extends HttpRequestEventTarget {
// redirect case will be handled by the browser before it gets to us,
// so if we see it we should pass it through to the user.
var unknownRedirect = xhr.status > 307 && xhr.status < 400;
var isSuccessful = accepted || fileUri || notModified || unknownRedirect;
if (zone == null && isSuccessful) {
if (accepted || fileUri || notModified || unknownRedirect) {
completer.complete(xhr);
} else if (zone == null) {
completer.completeError(e);
} else if (isSuccessful) {
zone.runTask((task, value) {
completer.complete(value);
}, task, xhr);
} else {
zone.runTask((task, error) {
completer.completeError(error);
}, task, e);
completer.completeError(e);
}
});
if (zone == null) {
xhr.onError.listen(completer.completeError);
} else {
xhr.onError.listen((error) {
zone.runTask((task, error) {
completer.completeError(error);
}, task, error);
});
}
xhr.onError.listen(completer.completeError);
if (sendData != null) {
// TODO(floitsch): should we go through 'send()' and have nested tasks?
xhr._send(sendData);
xhr.send(sendData);
} else {
xhr._send();
xhr.send();
}
return task;
return completer.future;
}
/**
@ -19687,9 +19538,6 @@ class HttpRequest extends HttpRequestEventTarget {
return xhr.responseText;
});
}
// TODO(floitsch): the following code doesn't go through task zones.
// Since 'XDomainRequest' is an IE9 feature we should probably just remove
// it.
var completer = new Completer<String>();
if (method == null) {
method = 'GET';
@ -19768,43 +19616,13 @@ class HttpRequest extends HttpRequestEventTarget {
*
* Note: Most simple HTTP requests can be accomplished using the [getString],
* [request], [requestCrossOrigin], or [postFormData] methods. Use of this
* `open` method is intended only for more complex HTTP requests where
* `open` method is intended only for more complext HTTP requests where
* finer-grained control is needed.
*/
@DomName('XMLHttpRequest.open')
@DocsEditable()
void open(String method, String url, {bool async, String user, String password}) native;
/**
* Sends the request with any given `data`.
*
* Note: Most simple HTTP requests can be accomplished using the [getString],
* [request], [requestCrossOrigin], or [postFormData] methods. Use of this
* `send` method is intended only for more complex HTTP requests where
* finer-grained control is needed.
*
* ## Other resources
*
* * [XMLHttpRequest.send](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#send%28%29)
* from MDN.
*/
@DomName('XMLHttpRequest.send')
@DocsEditable()
void send([body_OR_data]) {
if (identical(Zone.current, Zone.ROOT)) {
_send(body_OR_data);
} else {
Zone.current.createTask(_createHttpRequestSendTask,
new HttpRequestSendTaskSpecification(this, body_OR_data));
}
}
static HttpRequest _createHttpRequestSendTask(
HttpRequestSendTaskSpecification spec, Zone zone) {
spec.request._send(spec.sendData);
return spec.request;
}
// To suppress missing implicit constructor warnings.
factory HttpRequest._() { throw new UnsupportedError("Not supported"); }
@ -20075,13 +19893,12 @@ class HttpRequest extends HttpRequestEventTarget {
@SupportedBrowser(SupportedBrowser.SAFARI)
void overrideMimeType(String mime) native;
@JSName('send')
/**
* Send the request with any given `data`.
*
* Note: Most simple HTTP requests can be accomplished using the [getString],
* [request], [requestCrossOrigin], or [postFormData] methods. Use of this
* `send` method is intended only for more complex HTTP requests where
* `send` method is intended only for more complext HTTP requests where
* finer-grained control is needed.
*
* ## Other resources
@ -20091,7 +19908,7 @@ class HttpRequest extends HttpRequestEventTarget {
*/
@DomName('XMLHttpRequest.send')
@DocsEditable()
void _send([body_OR_data]) native;
void send([body_OR_data]) native;
/**
* Sets the value of an HTTP requst header.
@ -34668,99 +34485,6 @@ class WheelEvent extends MouseEvent {
// BSD-style license that can be found in the LICENSE file.
typedef void RemoveFrameRequestMapping(int id);
/**
* The task object representing animation-frame requests.
*
* For historical reasons, [Window.requestAnimationFrame] returns an integer
* to users. However, zone tasks must be unique objects, and an integer can
* therefore not be used as task object. The [Window] class thus keeps a mapping
* from the integer ID to the corresponding task object. All zone related
* operations work on this task object, whereas users of
* [Window.requestAnimationFrame] only see the integer ID.
*
* Since this mapping takes up space, it must be removed when the
* animation-frame task has triggered. The default implementation does this
* automatically, but intercepting implementations of `requestAnimationFrame`
* must make sure to call the [AnimationFrameTask.removeMapping]
* function that is provided in the task specification.
*
* *Experimental*. This class may disappear without notice.
*/
abstract class AnimationFrameTask {
/** The ID that is returned to users. */
int get id;
/** The zone in which the task will run. */
Zone get zone;
/**
* Cancels the animation-frame request.
*
* A call to [Window.cancelAnimationFrame] with an `id` argument equal to [id]
* forwards the request to this function.
*
* Zones that intercept animation-frame requests implement this method so
* that they can react to cancelation requests.
*/
void cancel(Window window);
/**
* Maps animation-frame request IDs to their task objects.
*/
static final Map<int, _AnimationFrameTask> _tasks = {};
/**
* Removes the mapping from [id] to [AnimationFrameTask].
*
* This function must be invoked by user-implemented animation-frame
* tasks, before running [callback].
*
* See [AnimationFrameTask].
*/
static void removeMapping(int id) {
_tasks.remove(id);
}
}
class _AnimationFrameTask implements AnimationFrameTask {
final int id;
final Zone zone;
final FrameRequestCallback _callback;
_AnimationFrameTask(this.id, this.zone, this._callback);
void cancel(Window window) {
window._cancelAnimationFrame(this.id);
}
}
/**
* The task specification for an animation-frame request.
*
* *Experimental*. This class may disappear without notice.
*/
class AnimationFrameRequestSpecification implements TaskSpecification {
/**
* The window on which [Window.requestAnimationFrame] was invoked.
*/
final Window window;
/**
* The callback that is executed when the animation-frame is ready.
*
* Note that the callback hasn't been registered in any zone when the `create`
* function (passed to [Zone.createTask]) is invoked.
*/
final FrameRequestCallback callback;
AnimationFrameRequestSpecification(this.window, this.callback);
String get name => "dart.html.request-animation-frame";
bool get isOneShot => true;
}
@DocsEditable()
/**
* Top-level container for the current browser tab or window.
@ -34816,7 +34540,9 @@ class Window extends EventTarget implements WindowEventHandlers, WindowBase, Glo
*/
Future<num> get animationFrame {
var completer = new Completer<num>.sync();
requestAnimationFrame(completer.complete);
requestAnimationFrame((time) {
completer.complete(time);
});
return completer.future;
}
@ -34899,30 +34625,7 @@ class Window extends EventTarget implements WindowEventHandlers, WindowBase, Glo
@DomName('Window.requestAnimationFrame')
int requestAnimationFrame(FrameRequestCallback callback) {
_ensureRequestAnimationFrame();
if (identical(Zone.current, Zone.ROOT)) {
return _requestAnimationFrame(callback);
}
var spec = new AnimationFrameRequestSpecification(this, callback);
var task = Zone.current.createTask/*<AnimationFrameTask>*/(
_createAnimationFrameTask, spec);
AnimationFrameTask._tasks[task.id] = task;
return task.id;
}
static _AnimationFrameTask _createAnimationFrameTask(
AnimationFrameRequestSpecification spec, Zone zone) {
var task;
var id = spec.window._requestAnimationFrame((num time) {
AnimationFrameTask.removeMapping(task.id);
zone.runTask(_runAnimationFrame, task, time);
});
var callback = zone.registerUnaryCallback(spec.callback);
task = new _AnimationFrameTask(id, zone, callback);
return task;
}
static void _runAnimationFrame(_AnimationFrameTask task, num time) {
task._callback(time);
return _requestAnimationFrame(_wrapZone/*<num, dynamic>*/(callback));
}
/**
@ -34935,13 +34638,7 @@ class Window extends EventTarget implements WindowEventHandlers, WindowBase, Glo
*/
void cancelAnimationFrame(int id) {
_ensureRequestAnimationFrame();
var task = AnimationFrameTask._tasks.remove(id);
if (task == null) {
// Assume that the animation frame request wasn't intercepted by a zone.
_cancelAnimationFrame(id);
return;
}
task.cancel(this);
_cancelAnimationFrame(id);
}
@JSName('requestAnimationFrame')
@ -40267,41 +39964,6 @@ abstract class ElementStream<T extends Event> implements Stream<T> {
StreamSubscription<T> capture(void onData(T event));
}
/// Task specification for DOM Events.
///
/// *Experimental*. May disappear without notice.
class EventSubscriptionSpecification<T extends Event>
implements TaskSpecification {
@override
final String name;
@override
final bool isOneShot;
final EventTarget target;
/// The event-type of the event. For example 'click' for click events.
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.
*/
@ -40309,16 +39971,8 @@ 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, String eventType, this._useCapture,
{String name, bool isOneShot: false})
: _eventType = eventType,
_isOneShot = isOneShot,
_name = name ?? "dart.html.event.$eventType";
_EventStream(this._target, this._eventType, this._useCapture);
// DOM events are inherently multi-subscribers.
Stream<T> asBroadcastStream({void onListen(StreamSubscription<T> subscription),
@ -40326,31 +39980,13 @@ class _EventStream<T extends Event> extends Stream<T> {
=> this;
bool get isBroadcast => true;
StreamSubscription<T> _listen(
void onData(T event), {bool 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: 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);
}
StreamSubscription<T> listen(void onData(T event),
{ Function onError,
void onDone(),
bool cancelOnError}) {
return _listen(onData, useCapture: this._useCapture);
return new _EventStreamSubscription<T>(
this._target, this._eventType, onData, this._useCapture);
}
}
@ -40365,9 +40001,8 @@ bool _matchesWithAncestors(Event event, String selector) {
*/
class _ElementEventStreamImpl<T extends Event> extends _EventStream<T>
implements ElementStream<T> {
_ElementEventStreamImpl(target, eventType, useCapture,
{String name, bool isOneShot: false}) :
super(target, eventType, useCapture, name: name, isOneShot: isOneShot);
_ElementEventStreamImpl(target, eventType, useCapture) :
super(target, eventType, useCapture);
Stream<T> matches(String selector) => this.where(
(event) => _matchesWithAncestors(event, selector)).map((e) {
@ -40375,9 +40010,9 @@ class _ElementEventStreamImpl<T extends Event> extends _EventStream<T>
return e;
});
StreamSubscription<T> capture(void onData(T event)) {
return _listen(onData, useCapture: true);
}
StreamSubscription<T> capture(void onData(T event)) =>
new _EventStreamSubscription<T>(
this._target, this._eventType, onData, true);
}
/**
@ -40426,13 +40061,7 @@ class _ElementListEventStreamImpl<T extends Event> extends Stream<T>
bool get isBroadcast => true;
}
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
// We would like this to just be EventListener<T> but that typdef cannot
// use generics until dartbug/26276 is fixed.
typedef _EventListener<T extends Event>(T event);
@ -40441,19 +40070,15 @@ 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/*<dynamic, Event>*/((e) => onData(e as T))
// _onData = onData == null ? null : _wrapZone/*<Event, dynamic>*/((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, Zone zone)
: _zone = zone,
_onData = _registerZone/*<dynamic, Event>*/(zone, onData) {
this._useCapture) : _onData = _wrapZone/*<Event, dynamic>*/(onData) {
_tryResume();
}
@ -40475,7 +40100,7 @@ class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
}
// Remove current event listener.
_unlisten();
_onData = _registerZone/*<dynamic, Event>*/(_zone, handleData);
_onData = _wrapZone/*<Event, dynamic>*/(handleData);
_tryResume();
}
@ -40504,25 +40129,14 @@ class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
}
void _tryResume() {
if (_onData == null || isPaused) return;
if (identical(_zone, Zone.ROOT)) {
_domCallback = _onData;
} else {
_domCallback = (event) {
_zone.runTask(_runEventNotification, this, event);
};
if (_onData != null && !isPaused) {
_target.addEventListener(_eventType, _onData, _useCapture);
}
_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, _domCallback, _useCapture);
_target.removeEventListener(_eventType, _onData, _useCapture);
}
}
@ -43756,26 +43370,31 @@ class _WrappedEvent implements Event {
// BSD-style license that can be found in the LICENSE file.
ZoneUnaryCallback/*<R, T>*/ _registerZone/*<R, T>*/(Zone zone,
ZoneUnaryCallback/*<R, T>*/ callback) {
// For performance reasons avoid registering if we are in the root zone.
if (identical(zone, Zone.ROOT)) return callback;
if (callback == null) return null;
return zone.registerUnaryCallback(callback);
}
// TODO(jacobr): remove these typedefs when dart:async supports generic types.
typedef R _wrapZoneCallback<A, R>(A a);
typedef R _wrapZoneBinaryCallback<A, B, R>(A a, B b);
ZoneUnaryCallback/*<R, T>*/ _wrapZone/*<R, T>*/(ZoneUnaryCallback/*<R, T>*/ callback) {
_wrapZoneCallback/*<A, R>*/ _wrapZone/*<A, R>*/(_wrapZoneCallback/*<A, R>*/ callback) {
// For performance reasons avoid wrapping if we are in the root zone.
if (identical(Zone.current, Zone.ROOT)) return callback;
if (Zone.current == Zone.ROOT) return callback;
if (callback == null) return null;
return Zone.current.bindUnaryCallback(callback, runGuarded: true);
// TODO(jacobr): we cast to _wrapZoneCallback/*<A, R>*/ to hack around missing
// generic method support in zones.
// ignore: STRONG_MODE_DOWN_CAST_COMPOSITE
_wrapZoneCallback/*<A, R>*/ wrapped =
Zone.current.bindUnaryCallback(callback, runGuarded: true);
return wrapped;
}
ZoneBinaryCallback/*<R, A, B>*/ _wrapBinaryZone/*<R, A, B>*/(
ZoneBinaryCallback/*<R, A, B>*/ callback) {
if (identical(Zone.current, Zone.ROOT)) return callback;
_wrapZoneBinaryCallback/*<A, B, R>*/ _wrapBinaryZone/*<A, B, R>*/(_wrapZoneBinaryCallback/*<A, B, R>*/ callback) {
if (Zone.current == Zone.ROOT) return callback;
if (callback == null) return null;
return Zone.current.bindBinaryCallback(callback, runGuarded: true);
// We cast to _wrapZoneBinaryCallback/*<A, B, R>*/ to hack around missing
// generic method support in zones.
// ignore: STRONG_MODE_DOWN_CAST_COMPOSITE
_wrapZoneBinaryCallback/*<A, B, R>*/ wrapped =
Zone.current.bindBinaryCallback(callback, runGuarded: true);
return wrapped;
}
/**

View file

@ -20997,109 +20997,6 @@ class HtmlOptionsCollection extends HtmlCollection {
// BSD-style license that can be found in the LICENSE file.
/**
* A task specification for HTTP requests.
*
* This specification is not available when an HTTP request is sent through
* direct use of [HttpRequest.send]. See [HttpRequestSendTaskSpecification].
*
* A task created from this specification is a `Future<HttpRequest>`.
*
* *Experimental*. This class may disappear without notice.
*/
class HttpRequestTaskSpecification extends TaskSpecification {
/// The URL of the request.
final String url;
/// The HTTP request method.
///
/// By default (when `null`) this is a `"GET"` request. Alternatively, the
/// method can be `"POST"`, `"PUT"`, `"DELETE"`, etc.
final String method;
/// Whether the request should send credentials. Credentials are only useful
/// for cross-origin requests.
///
/// See [HttpRequest.request] for more information.
final bool withCredentials;
/// The desired response format.
///
/// Supported types are:
/// - `""`: (same as `"text"`),
/// - `"arraybuffer"`,
/// - `"blob"`,
/// - `"document"`,
/// - `"json"`,
/// - `"text"`
///
/// When no value is provided (when equal to `null`) defaults to `""`.
final String responseType;
/// The desired MIME type.
///
/// This overrides the default MIME type which is set up to transfer textual
/// data.
final String mimeType;
/// The request headers that should be sent with the request.
final Map<String, String> requestHeaders;
/// The data that is sent with the request.
///
/// When data is provided (the value is not `null`), it must be a
/// [ByteBuffer], [Blob], [Document], [String], or [FormData].
final dynamic sendData;
/// The function that is invoked on progress updates. This function is
/// registered as an event listener on the created [HttpRequest] object, and
/// thus has its own task. Further invocations of the progress function do
/// *not* use the HTTP request task as task object.
///
/// Creating an HTTP request automatically registers the on-progress listener.
final ZoneUnaryCallback<dynamic, ProgressEvent> onProgress;
HttpRequestTaskSpecification(this.url,
{String this.method, bool this.withCredentials, String this.responseType,
String this.mimeType, Map<String, String> this.requestHeaders,
this.sendData,
void this.onProgress(ProgressEvent e)});
String get name => "dart.html.http-request";
bool get isOneShot => true;
}
/**
* A task specification for HTTP requests that are initiated through a direct
* invocation of [HttpRequest.send].
*
* This specification serves as signal to zones that an HTTP request has been
* initiated. The created task is the [request] object itself, and
* no callback is ever executed in this task.
*
* Note that event listeners on the HTTP request are also registered in the
* zone (although with their own task creations), and that a zone can thus
* detect when the HTTP request returns.
*
* HTTP requests that are initiated through `request` methods don't use
* this class but use [HttpRequestTaskSpecification].
*
* *Experimental*. This class may disappear without notice.
*/
class HttpRequestSendTaskSpecification extends TaskSpecification {
final HttpRequest request;
final dynamic sendData;
HttpRequestSendTaskSpecification(this.request, this.sendData);
String get name => "dart.html.http-request-send";
/**
* No callback is ever executed in an HTTP request send task.
*/
bool get isOneShot => false;
}
/**
* A client-side XHR request for getting data from a URL,
* formally known as XMLHttpRequest.
@ -21287,34 +21184,7 @@ class HttpRequest extends HttpRequestEventTarget {
{String method, bool withCredentials, String responseType,
String mimeType, Map<String, String> requestHeaders, sendData,
void onProgress(ProgressEvent e)}) {
var spec = new HttpRequestTaskSpecification(
url, method: method,
withCredentials: withCredentials,
responseType: responseType,
mimeType: mimeType,
requestHeaders: requestHeaders,
sendData: sendData,
onProgress: onProgress);
if (identical(Zone.current, Zone.ROOT)) {
return _createHttpRequestTask(spec, null);
}
return Zone.current.createTask(_createHttpRequestTask, spec);
}
static Future<HttpRequest> _createHttpRequestTask(
HttpRequestTaskSpecification spec, Zone zone) {
String url = spec.url;
String method = spec.method;
bool withCredentials = spec.withCredentials;
String responseType = spec.responseType;
String mimeType = spec.mimeType;
Map<String, String> requestHeaders = spec.requestHeaders;
var sendData = spec.sendData;
var onProgress = spec.onProgress;
var completer = new Completer<HttpRequest>();
var task = completer.future;
var xhr = new HttpRequest();
if (method == null) {
@ -21354,42 +21224,23 @@ class HttpRequest extends HttpRequestEventTarget {
// redirect case will be handled by the browser before it gets to us,
// so if we see it we should pass it through to the user.
var unknownRedirect = xhr.status > 307 && xhr.status < 400;
var isSuccessful = accepted || fileUri || notModified || unknownRedirect;
if (zone == null && isSuccessful) {
if (accepted || fileUri || notModified || unknownRedirect) {
completer.complete(xhr);
} else if (zone == null) {
completer.completeError(e);
} else if (isSuccessful) {
zone.runTask((task, value) {
completer.complete(value);
}, task, xhr);
} else {
zone.runTask((task, error) {
completer.completeError(error);
}, task, e);
completer.completeError(e);
}
});
if (zone == null) {
xhr.onError.listen(completer.completeError);
} else {
xhr.onError.listen((error) {
zone.runTask((task, error) {
completer.completeError(error);
}, task, error);
});
}
xhr.onError.listen(completer.completeError);
if (sendData != null) {
// TODO(floitsch): should we go through 'send()' and have nested tasks?
xhr._send(sendData);
xhr.send(sendData);
} else {
xhr._send();
xhr.send();
}
return task;
return completer.future;
}
/**
@ -21439,9 +21290,6 @@ class HttpRequest extends HttpRequestEventTarget {
return xhr.responseText;
});
}
// TODO(floitsch): the following code doesn't go through task zones.
// Since 'XDomainRequest' is an IE9 feature we should probably just remove
// it.
}
/**
@ -21492,7 +21340,7 @@ class HttpRequest extends HttpRequestEventTarget {
*
* Note: Most simple HTTP requests can be accomplished using the [getString],
* [request], [requestCrossOrigin], or [postFormData] methods. Use of this
* `open` method is intended only for more complex HTTP requests where
* `open` method is intended only for more complext HTTP requests where
* finer-grained control is needed.
*/
@DomName('XMLHttpRequest.open')
@ -21505,36 +21353,6 @@ class HttpRequest extends HttpRequestEventTarget {
}
}
/**
* Sends the request with any given `data`.
*
* Note: Most simple HTTP requests can be accomplished using the [getString],
* [request], [requestCrossOrigin], or [postFormData] methods. Use of this
* `send` method is intended only for more complex HTTP requests where
* finer-grained control is needed.
*
* ## Other resources
*
* * [XMLHttpRequest.send](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#send%28%29)
* from MDN.
*/
@DomName('XMLHttpRequest.send')
@DocsEditable()
void send([body_OR_data]) {
if (identical(Zone.current, Zone.ROOT)) {
_send(body_OR_data);
} else {
Zone.current.createTask(_createHttpRequestSendTask,
new HttpRequestSendTaskSpecification(this, body_OR_data));
}
}
static HttpRequest _createHttpRequestSendTask(
HttpRequestSendTaskSpecification spec, Zone zone) {
spec.request._send(spec.sendData);
return spec.request;
}
// To suppress missing implicit constructor warnings.
factory HttpRequest._() { throw new UnsupportedError("Not supported"); }
@ -21838,7 +21656,7 @@ class HttpRequest extends HttpRequestEventTarget {
@SupportedBrowser(SupportedBrowser.SAFARI)
void overrideMimeType(String mime) => _blink.BlinkXMLHttpRequest.instance.overrideMimeType_Callback_1_(this, mime);
void _send([body_OR_data]) {
void send([body_OR_data]) {
if (body_OR_data != null) {
_blink.BlinkXMLHttpRequest.instance.send_Callback_1_(this, body_OR_data);
return;
@ -37844,10 +37662,10 @@ class Url extends DartHtmlDomObject implements UrlUtils {
if ((blob_OR_source_OR_stream is Blob || blob_OR_source_OR_stream == null)) {
return _blink.BlinkURL.instance.createObjectURL_Callback_1_(blob_OR_source_OR_stream);
}
if ((blob_OR_source_OR_stream is MediaSource)) {
if ((blob_OR_source_OR_stream is MediaStream)) {
return _blink.BlinkURL.instance.createObjectURL_Callback_1_(blob_OR_source_OR_stream);
}
if ((blob_OR_source_OR_stream is MediaStream)) {
if ((blob_OR_source_OR_stream is MediaSource)) {
return _blink.BlinkURL.instance.createObjectURL_Callback_1_(blob_OR_source_OR_stream);
}
throw new ArgumentError("Incorrect number or type of arguments");
@ -39313,99 +39131,6 @@ class WheelEvent extends MouseEvent {
// BSD-style license that can be found in the LICENSE file.
typedef void RemoveFrameRequestMapping(int id);
/**
* The task object representing animation-frame requests.
*
* For historical reasons, [Window.requestAnimationFrame] returns an integer
* to users. However, zone tasks must be unique objects, and an integer can
* therefore not be used as task object. The [Window] class thus keeps a mapping
* from the integer ID to the corresponding task object. All zone related
* operations work on this task object, whereas users of
* [Window.requestAnimationFrame] only see the integer ID.
*
* Since this mapping takes up space, it must be removed when the
* animation-frame task has triggered. The default implementation does this
* automatically, but intercepting implementations of `requestAnimationFrame`
* must make sure to call the [AnimationFrameTask.removeMapping]
* function that is provided in the task specification.
*
* *Experimental*. This class may disappear without notice.
*/
abstract class AnimationFrameTask {
/** The ID that is returned to users. */
int get id;
/** The zone in which the task will run. */
Zone get zone;
/**
* Cancels the animation-frame request.
*
* A call to [Window.cancelAnimationFrame] with an `id` argument equal to [id]
* forwards the request to this function.
*
* Zones that intercept animation-frame requests implement this method so
* that they can react to cancelation requests.
*/
void cancel(Window window);
/**
* Maps animation-frame request IDs to their task objects.
*/
static final Map<int, _AnimationFrameTask> _tasks = {};
/**
* Removes the mapping from [id] to [AnimationFrameTask].
*
* This function must be invoked by user-implemented animation-frame
* tasks, before running [callback].
*
* See [AnimationFrameTask].
*/
static void removeMapping(int id) {
_tasks.remove(id);
}
}
class _AnimationFrameTask implements AnimationFrameTask {
final int id;
final Zone zone;
final FrameRequestCallback _callback;
_AnimationFrameTask(this.id, this.zone, this._callback);
void cancel(Window window) {
window._cancelAnimationFrame(this.id);
}
}
/**
* The task specification for an animation-frame request.
*
* *Experimental*. This class may disappear without notice.
*/
class AnimationFrameRequestSpecification implements TaskSpecification {
/**
* The window on which [Window.requestAnimationFrame] was invoked.
*/
final Window window;
/**
* The callback that is executed when the animation-frame is ready.
*
* Note that the callback hasn't been registered in any zone when the `create`
* function (passed to [Zone.createTask]) is invoked.
*/
final FrameRequestCallback callback;
AnimationFrameRequestSpecification(this.window, this.callback);
String get name => "dart.html.request-animation-frame";
bool get isOneShot => true;
}
@DocsEditable()
/**
* Top-level container for the current browser tab or window.
@ -39460,7 +39185,9 @@ class Window extends EventTarget implements WindowEventHandlers, WindowBase, Glo
*/
Future<num> get animationFrame {
var completer = new Completer<num>.sync();
requestAnimationFrame(completer.complete);
requestAnimationFrame((time) {
completer.complete(time);
});
return completer.future;
}
@ -44765,41 +44492,6 @@ abstract class ElementStream<T extends Event> implements Stream<T> {
StreamSubscription<T> capture(void onData(T event));
}
/// Task specification for DOM Events.
///
/// *Experimental*. May disappear without notice.
class EventSubscriptionSpecification<T extends Event>
implements TaskSpecification {
@override
final String name;
@override
final bool isOneShot;
final EventTarget target;
/// The event-type of the event. For example 'click' for click events.
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.
*/
@ -44807,16 +44499,8 @@ 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, String eventType, this._useCapture,
{String name, bool isOneShot: false})
: _eventType = eventType,
_isOneShot = isOneShot,
_name = name ?? "dart.html.event.$eventType";
_EventStream(this._target, this._eventType, this._useCapture);
// DOM events are inherently multi-subscribers.
Stream<T> asBroadcastStream({void onListen(StreamSubscription<T> subscription),
@ -44824,31 +44508,13 @@ class _EventStream<T extends Event> extends Stream<T> {
=> this;
bool get isBroadcast => true;
StreamSubscription<T> _listen(
void onData(T event), {bool 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: 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);
}
StreamSubscription<T> listen(void onData(T event),
{ Function onError,
void onDone(),
bool cancelOnError}) {
return _listen(onData, useCapture: this._useCapture);
return new _EventStreamSubscription<T>(
this._target, this._eventType, onData, this._useCapture);
}
}
@ -44863,9 +44529,8 @@ bool _matchesWithAncestors(Event event, String selector) {
*/
class _ElementEventStreamImpl<T extends Event> extends _EventStream<T>
implements ElementStream<T> {
_ElementEventStreamImpl(target, eventType, useCapture,
{String name, bool isOneShot: false}) :
super(target, eventType, useCapture, name: name, isOneShot: isOneShot);
_ElementEventStreamImpl(target, eventType, useCapture) :
super(target, eventType, useCapture);
Stream<T> matches(String selector) => this.where(
(event) => _matchesWithAncestors(event, selector)).map((e) {
@ -44873,9 +44538,9 @@ class _ElementEventStreamImpl<T extends Event> extends _EventStream<T>
return e;
});
StreamSubscription<T> capture(void onData(T event)) {
return _listen(onData, useCapture: true);
}
StreamSubscription<T> capture(void onData(T event)) =>
new _EventStreamSubscription<T>(
this._target, this._eventType, onData, true);
}
/**
@ -44924,13 +44589,7 @@ class _ElementListEventStreamImpl<T extends Event> extends Stream<T>
bool get isBroadcast => true;
}
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
// We would like this to just be EventListener<T> but that typdef cannot
// use generics until dartbug/26276 is fixed.
typedef _EventListener<T extends Event>(T event);
@ -44939,19 +44598,15 @@ 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/*<dynamic, Event>*/((e) => onData(e as T))
// _onData = onData == null ? null : _wrapZone/*<Event, dynamic>*/((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, Zone zone)
: _zone = zone,
_onData = _registerZone/*<dynamic, Event>*/(zone, onData) {
this._useCapture) : _onData = _wrapZone/*<Event, dynamic>*/(onData) {
_tryResume();
}
@ -44973,7 +44628,7 @@ class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
}
// Remove current event listener.
_unlisten();
_onData = _registerZone/*<dynamic, Event>*/(_zone, handleData);
_onData = _wrapZone/*<Event, dynamic>*/(handleData);
_tryResume();
}
@ -45002,25 +44657,14 @@ class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
}
void _tryResume() {
if (_onData == null || isPaused) return;
if (identical(_zone, Zone.ROOT)) {
_domCallback = _onData;
} else {
_domCallback = (event) {
_zone.runTask(_runEventNotification, this, event);
};
if (_onData != null && !isPaused) {
_target.addEventListener(_eventType, _onData, _useCapture);
}
_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, _domCallback, _useCapture);
_target.removeEventListener(_eventType, _onData, _useCapture);
}
}
@ -48212,26 +47856,31 @@ class _WrappedEvent implements Event {
// BSD-style license that can be found in the LICENSE file.
ZoneUnaryCallback/*<R, T>*/ _registerZone/*<R, T>*/(Zone zone,
ZoneUnaryCallback/*<R, T>*/ callback) {
// For performance reasons avoid registering if we are in the root zone.
if (identical(zone, Zone.ROOT)) return callback;
if (callback == null) return null;
return zone.registerUnaryCallback(callback);
}
// TODO(jacobr): remove these typedefs when dart:async supports generic types.
typedef R _wrapZoneCallback<A, R>(A a);
typedef R _wrapZoneBinaryCallback<A, B, R>(A a, B b);
ZoneUnaryCallback/*<R, T>*/ _wrapZone/*<R, T>*/(ZoneUnaryCallback/*<R, T>*/ callback) {
_wrapZoneCallback/*<A, R>*/ _wrapZone/*<A, R>*/(_wrapZoneCallback/*<A, R>*/ callback) {
// For performance reasons avoid wrapping if we are in the root zone.
if (identical(Zone.current, Zone.ROOT)) return callback;
if (Zone.current == Zone.ROOT) return callback;
if (callback == null) return null;
return Zone.current.bindUnaryCallback(callback, runGuarded: true);
// TODO(jacobr): we cast to _wrapZoneCallback/*<A, R>*/ to hack around missing
// generic method support in zones.
// ignore: STRONG_MODE_DOWN_CAST_COMPOSITE
_wrapZoneCallback/*<A, R>*/ wrapped =
Zone.current.bindUnaryCallback(callback, runGuarded: true);
return wrapped;
}
ZoneBinaryCallback/*<R, A, B>*/ _wrapBinaryZone/*<R, A, B>*/(
ZoneBinaryCallback/*<R, A, B>*/ callback) {
if (identical(Zone.current, Zone.ROOT)) return callback;
_wrapZoneBinaryCallback/*<A, B, R>*/ _wrapBinaryZone/*<A, B, R>*/(_wrapZoneBinaryCallback/*<A, B, R>*/ callback) {
if (Zone.current == Zone.ROOT) return callback;
if (callback == null) return null;
return Zone.current.bindBinaryCallback(callback, runGuarded: true);
// We cast to _wrapZoneBinaryCallback/*<A, B, R>*/ to hack around missing
// generic method support in zones.
// ignore: STRONG_MODE_DOWN_CAST_COMPOSITE
_wrapZoneBinaryCallback/*<A, B, R>*/ wrapped =
Zone.current.bindBinaryCallback(callback, runGuarded: true);
return wrapped;
}
/**

View file

@ -1,121 +0,0 @@
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library EventTaskZoneTest;
import 'package:unittest/unittest.dart';
import 'package:unittest/html_config.dart';
import 'dart:async';
import 'dart:html';
// Tests event-subscription specifications.
main() {
useHtmlConfiguration();
var defaultTarget = new Element.div();
var defaultOnData = (x) => null;
EventSubscriptionSpecification createSpec({useCapture, isOneShot}) {
return new EventSubscriptionSpecification(
name: "name",
target: defaultTarget,
useCapture: useCapture,
isOneShot: isOneShot,
onData: defaultOnData,
eventType: "eventType");
}
for (var useCapture in [true, false]) {
for (var isOneShot in [true, false]) {
var spec = createSpec(useCapture: useCapture, isOneShot: isOneShot);
test(
"EventSubscriptionSpecification - constructor "
"useCapture: $useCapture isOneShot: $isOneShot", () {
var replaced = spec.replace(eventType: 'replace-eventType');
expect(replaced.name, "name");
expect(replaced.target, defaultTarget);
expect(replaced.useCapture, useCapture);
expect(replaced.isOneShot, isOneShot);
expect(replaced.onData, equals(defaultOnData));
expect(replaced.eventType, "replace-eventType");
});
test(
"replace name "
"useCapture: $useCapture isOneShot: $isOneShot", () {
var replaced = spec.replace(name: 'replace-name');
expect(replaced.name, "replace-name");
expect(replaced.target, defaultTarget);
expect(replaced.useCapture, useCapture);
expect(replaced.isOneShot, isOneShot);
expect(replaced.onData, equals(defaultOnData));
expect(replaced.eventType, "eventType");
});
test(
"replace target "
"useCapture: $useCapture isOneShot: $isOneShot", () {
var replacementTarget = new Element.a();
var replaced = spec.replace(target: replacementTarget);
expect(replaced.name, "name");
expect(replaced.target, replacementTarget);
expect(replaced.useCapture, useCapture);
expect(replaced.isOneShot, isOneShot);
expect(replaced.onData, equals(defaultOnData));
expect(replaced.eventType, "eventType");
});
test(
"replace useCapture "
"useCapture: $useCapture isOneShot: $isOneShot", () {
var replaced = spec.replace(useCapture: !useCapture);
expect(replaced.name, "name");
expect(replaced.target, defaultTarget);
expect(replaced.useCapture, !useCapture);
expect(replaced.isOneShot, isOneShot);
expect(replaced.onData, equals(defaultOnData));
expect(replaced.eventType, "eventType");
});
test(
"replace isOneShot "
"useCapture: $useCapture isOneShot: $isOneShot", () {
var replaced = spec.replace(isOneShot: !isOneShot);
expect(replaced.name, "name");
expect(replaced.target, defaultTarget);
expect(replaced.useCapture, useCapture);
expect(replaced.isOneShot, !isOneShot);
expect(replaced.onData, equals(defaultOnData));
expect(replaced.eventType, "eventType");
});
test(
"replace onData "
"useCapture: $useCapture isOneShot: $isOneShot", () {
var replacementOnData = (x) {};
var replaced = spec.replace(onData: replacementOnData);
expect(replaced.name, "name");
expect(replaced.target, defaultTarget);
expect(replaced.useCapture, useCapture);
expect(replaced.isOneShot, isOneShot);
expect(replaced.onData, equals(replacementOnData));
expect(replaced.eventType, "eventType");
});
test(
"replace eventType "
"useCapture: $useCapture isOneShot: $isOneShot", () {
var replaced = spec.replace(eventType: 'replace-eventType');
expect(replaced.name, "name");
expect(replaced.target, defaultTarget);
expect(replaced.useCapture, useCapture);
expect(replaced.isOneShot, isOneShot);
expect(replaced.onData, equals(defaultOnData));
expect(replaced.eventType, "replace-eventType");
});
}
}
}

View file

@ -1,239 +0,0 @@
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library EventTaskZoneTest;
import 'package:unittest/unittest.dart';
import 'package:unittest/html_config.dart';
import 'dart:async';
import 'dart:html';
// Tests zone tasks with DOM events.
class AbortedEventStreamSubscription implements StreamSubscription<Event> {
final Zone zone;
AbortedEventStreamSubscription(this.zone);
@override
Future asFuture([futureValue]) {
throw new UnsupportedError("asFuture");
}
@override
Future cancel() {
return null;
}
@override
bool get isPaused => throw new UnsupportedError("pause");
@override
void onData(void handleData(Event data)) {
throw new UnsupportedError("cancel");
}
@override
void onDone(void handleDone()) {
throw new UnsupportedError("onDone");
}
@override
void onError(Function handleError) {
throw new UnsupportedError("onError");
}
@override
void pause([Future resumeSignal]) {
throw new UnsupportedError("pause");
}
@override
void resume() {
throw new UnsupportedError("resume");
}
static AbortedEventStreamSubscription _create(
EventSubscriptionSpecification spec, Zone zone) {
return new AbortedEventStreamSubscription(zone);
}
}
eventTest(String name, Event eventFn(), void validate(Event event),
void validateSpec(EventSubscriptionSpecification spec),
{String type: 'foo',
bool abortCreation: false,
EventSubscriptionSpecification modifySpec(
EventSubscriptionSpecification spec),
bool abortEvent: false,
Event modifyEvent(Event event)}) {
test(name, () {
var lastSpec;
var lastTask;
var lastEvent;
Object createTaskHandler(Zone self, ZoneDelegate parent, Zone zone,
TaskCreate create, TaskSpecification specification) {
if (specification is EventSubscriptionSpecification) {
if (abortCreation) {
create = AbortedEventStreamSubscription._create;
}
if (modifySpec != null) {
specification = modifySpec(specification);
}
lastSpec = specification;
return lastTask = parent.createTask(zone, create, specification);
}
return parent.createTask(zone, create, specification);
}
void runTaskHandler(Zone self, ZoneDelegate parent, Zone zone, TaskRun run,
Object task, Object arg) {
if (identical(task, lastTask)) {
if (abortEvent) return;
if (modifyEvent != null) {
arg = modifyEvent(arg);
}
parent.runTask(zone, run, task, arg);
return;
}
parent.runTask(zone, run, task, arg);
}
runZoned(() {
final el = new Element.tag('div');
var fired = false;
var sub = el.on[type].listen((ev) {
lastEvent = ev;
fired = true;
});
el.dispatchEvent(eventFn());
validateSpec(lastSpec);
validate(lastEvent);
if (abortEvent || abortCreation) {
expect(fired, isFalse, reason: 'Expected event to be intercepted.');
} else {
expect(fired, isTrue, reason: 'Expected event to be dispatched.');
}
sub.cancel();
},
zoneSpecification: new ZoneSpecification(
createTask: createTaskHandler,
runTask: runTaskHandler));
});
}
Function checkSpec(
[String expectedType = 'foo', bool expectedUseCapture = false]) {
return (EventSubscriptionSpecification spec) {
expect(spec.eventType, expectedType);
expect(spec.useCapture, expectedUseCapture);
};
}
main() {
useHtmlConfiguration();
eventTest('Event', () => new Event('foo'), (ev) {
expect(ev.type, equals('foo'));
}, checkSpec('foo'));
eventTest(
'WheelEvent',
() => new WheelEvent("mousewheel",
deltaX: 1,
deltaY: 0,
detail: 4,
screenX: 3,
screenY: 4,
clientX: 5,
clientY: 6,
ctrlKey: true,
altKey: true,
shiftKey: true,
metaKey: true), (ev) {
expect(ev.deltaX, 1);
expect(ev.deltaY, 0);
expect(ev.screen.x, 3);
expect(ev.screen.y, 4);
expect(ev.client.x, 5);
expect(ev.client.y, 6);
expect(ev.ctrlKey, isTrue);
expect(ev.altKey, isTrue);
expect(ev.shiftKey, isTrue);
expect(ev.metaKey, isTrue);
}, checkSpec('mousewheel'), type: 'mousewheel');
eventTest('Event - no-create', () => new Event('foo'), (ev) {
expect(ev, isNull);
}, checkSpec('foo'), abortCreation: true);
eventTest(
'WheelEvent - no-create',
() => new WheelEvent("mousewheel",
deltaX: 1,
deltaY: 0,
detail: 4,
screenX: 3,
screenY: 4,
clientX: 5,
clientY: 6,
ctrlKey: true,
altKey: true,
shiftKey: true,
metaKey: true), (ev) {
expect(ev, isNull);
}, checkSpec('mousewheel'), type: 'mousewheel', abortCreation: true);
eventTest('Event - no-run', () => new Event('foo'), (ev) {
expect(ev, isNull);
}, checkSpec('foo'), abortEvent: true);
eventTest(
'WheelEvent - no-run',
() => new WheelEvent("mousewheel",
deltaX: 1,
deltaY: 0,
detail: 4,
screenX: 3,
screenY: 4,
clientX: 5,
clientY: 6,
ctrlKey: true,
altKey: true,
shiftKey: true,
metaKey: true), (ev) {
expect(ev, isNull);
}, checkSpec('mousewheel'), type: 'mousewheel', abortEvent: true);
// Register for 'foo', but receive a 'bar' event, because the specification
// is rewritten.
eventTest(
'Event - replace eventType',
() => new Event('bar'),
(ev) {
expect(ev.type, equals('bar'));
},
checkSpec('bar'),
type: 'foo',
modifySpec: (EventSubscriptionSpecification spec) {
return spec.replace(eventType: 'bar');
});
// Intercept the 'foo' event and replace it with a 'bar' event.
eventTest(
'Event - intercept result',
() => new Event('foo'),
(ev) {
expect(ev.type, equals('bar'));
},
checkSpec('foo'),
type: 'foo',
modifyEvent: (Event event) {
return new Event('bar');
});
}

View file

@ -1,170 +0,0 @@
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library EventTaskZoneTest;
import 'package:unittest/unittest.dart';
import 'package:unittest/html_config.dart';
import 'dart:async';
import 'dart:html';
// Tests zone tasks with window.requestAnimationFrame.
class MockAnimationFrameTask implements AnimationFrameTask {
static int taskId = 499;
final int id;
final Zone zone;
bool _isCanceled = false;
Function _callback;
MockAnimationFrameTask(
this.id, this.zone, this._callback);
void cancel(Window window) {
_isCanceled = true;
}
trigger(num stamp) {
zone.runTask(run, this, stamp);
}
static create(AnimationFrameRequestSpecification spec, Zone zone) {
var callback = zone.registerUnaryCallback(spec.callback);
return new MockAnimationFrameTask(
taskId++, zone, callback);
}
static run(MockAnimationFrameTask task, num arg) {
AnimationFrameTask.removeMapping(task.id);
task._callback(arg);
}
}
animationFrameTest() {
test("animationFrameTest - no intercept", () async {
AnimationFrameTask lastTask;
bool sawRequest = false;
int id;
num providedArg;
Object createTaskHandler(Zone self, ZoneDelegate parent, Zone zone,
TaskCreate create, TaskSpecification specification) {
if (specification is AnimationFrameRequestSpecification) {
sawRequest = true;
lastTask = parent.createTask(zone, create, specification);
id = lastTask.id;
return lastTask;
}
return parent.createTask(zone, create, specification);
}
void runTaskHandler(Zone self, ZoneDelegate parent, Zone zone, TaskRun run,
Object task, Object arg) {
if (identical(task, lastTask)) {
providedArg = arg;
}
parent.runTask(zone, run, task, arg);
}
var completer = new Completer();
var publicId;
runZoned(() {
publicId = window.requestAnimationFrame((num stamp) {
completer.complete(stamp);
});
},
zoneSpecification: new ZoneSpecification(
createTask: createTaskHandler, runTask: runTaskHandler));
var referenceCompleter = new Completer();
window.requestAnimationFrame((num stamp) {
referenceCompleter.complete(stamp);
});
var callbackStamp = await completer.future;
var referenceStamp = await referenceCompleter.future;
expect(callbackStamp, equals(referenceStamp));
expect(providedArg, equals(callbackStamp));
expect(sawRequest, isTrue);
expect(publicId, isNotNull);
expect(publicId, equals(id));
});
}
interceptedAnimationFrameTest() {
test("animationFrameTest - intercepted", () {
List<MockAnimationFrameTask> tasks = [];
List<num> loggedRuns = [];
int executedTaskId;
int executedStamp;
Object createTaskHandler(Zone self, ZoneDelegate parent, Zone zone,
TaskCreate create, TaskSpecification specification) {
if (specification is AnimationFrameRequestSpecification) {
var task = parent.createTask(
zone, MockAnimationFrameTask.create, specification);
tasks.add(task);
return task;
}
return parent.createTask(zone, create, specification);
}
void runTaskHandler(Zone self, ZoneDelegate parent, Zone zone, TaskRun run,
Object task, Object arg) {
if (tasks.contains(task)) {
loggedRuns.add(arg);
}
parent.runTask(zone, run, task, arg);
}
var id0, id1, id2;
runZoned(() {
id0 = window.requestAnimationFrame((num stamp) {
executedTaskId = id0;
executedStamp = stamp;
});
id1 = window.requestAnimationFrame((num stamp) {
executedTaskId = id1;
executedStamp = stamp;
});
id2 = window.requestAnimationFrame((num stamp) {
executedTaskId = id2;
executedStamp = stamp;
});
},
zoneSpecification: new ZoneSpecification(
createTask: createTaskHandler, runTask: runTaskHandler));
expect(tasks.length, 3);
expect(executedTaskId, isNull);
expect(executedStamp, isNull);
expect(loggedRuns.isEmpty, isTrue);
tasks[0].trigger(123.1);
expect(executedTaskId, id0);
expect(executedStamp, 123.1);
tasks[1].trigger(123.2);
expect(executedTaskId, id1);
expect(executedStamp, 123.2);
expect(loggedRuns, equals([123.1, 123.2]));
window.cancelAnimationFrame(id2);
expect(tasks[2]._isCanceled, isTrue);
// Cancel it a second time. Should not crash.
window.cancelAnimationFrame(id2);
expect(tasks[2]._isCanceled, isTrue);
});
}
main() {
useHtmlConfiguration();
animationFrameTest();
interceptedAnimationFrameTest();
}

View file

@ -1,280 +0,0 @@
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library XHRTask2Test;
import 'dart:async';
import 'dart:convert';
import 'dart:html';
import 'dart:typed_data';
import 'package:unittest/html_individual_config.dart';
import 'package:unittest/unittest.dart';
class MockProgressEvent implements ProgressEvent {
final target;
MockProgressEvent(this.target);
noSuchMethod(Invocation invocation) {
throw "missing function in MockProgressEvent";
}
}
class MockHttpRequestTask implements Future<HttpRequest> {
final Completer completer = new Completer<MockHttpRequestTask>();
final HttpRequestTaskSpecification spec;
final Zone zone;
MockHttpRequestTask(this.spec, this.zone);
void trigger(String response) {
var xhr = new MockHttpRequest(spec, response);
var arg;
if (spec.url == "NonExistingFile") {
arg = new MockProgressEvent(xhr);
} else {
arg = xhr;
}
zone.runTask(run, this, arg);
}
then(onData, {onError}) => completer.future.then(onData, onError: onError);
catchError(f, {test}) => completer.future.catchError(f, test: test);
whenComplete(f) => completer.future.whenComplete(f);
asStream() => completer.future.asStream();
timeout(timeLimit, {onTimeout}) =>
completer.future.timeout(timeLimit, onTimeout: onTimeout);
static create(HttpRequestTaskSpecification spec, Zone zone) {
return new MockHttpRequestTask(spec, zone);
}
static run(MockHttpRequestTask task, value) {
if (value is HttpRequest) {
task.completer.complete(value);
} else {
task.completer.completeError(value);
}
}
}
class MockHttpRequest implements HttpRequest {
final HttpRequestTaskSpecification spec;
final response;
MockHttpRequest(this.spec, this.response);
noSuchMethod(Invocation invocation) {
print("isGetter: ${invocation.isGetter}");
print("isMethod: ${invocation.isMethod}");
print("memberName: ${invocation.memberName}");
}
int get status => spec.url == "NonExistingFile" ? 404 : 200;
get readyState => HttpRequest.DONE;
get responseText => "$response";
Map get responseHeaders => {'content-type': 'text/plain; charset=utf-8',};
}
main() {
useHtmlIndividualConfiguration();
unittestConfiguration.timeout = const Duration(milliseconds: 800);
var urlExpando = new Expando();
var url = 'some/url.html';
Function buildCreateTaskHandler(List log, List tasks) {
Object createTaskHandler(Zone self, ZoneDelegate parent, Zone zone,
TaskCreate create, TaskSpecification spec) {
if (spec is HttpRequestTaskSpecification) {
var url = spec.url;
var method = spec.method;
var withCredentials = spec.withCredentials;
var responseType = spec.responseType;
var mimeType = spec.mimeType;
var data = spec.sendData;
log.add("request $url");
var dataLog = data is List<int> ? "binary ${data.length}" : "$data";
log.add(" method: $method withCredentials: $withCredentials "
"responseType: $responseType mimeType: $mimeType data: $dataLog");
var task = parent.createTask(zone, MockHttpRequestTask.create, spec);
urlExpando[task] = url;
tasks.add(task);
return task;
}
if (spec is EventSubscriptionSpecification) {
EventSubscriptionSpecification eventSpec = spec;
if (eventSpec.target is HttpRequest) {
HttpRequest target = eventSpec.target;
log.add("event listener on http-request ${eventSpec.eventType}");
if (eventSpec.eventType == "readystatechange") {
var oldOnData = eventSpec.onData;
spec = eventSpec.replace(onData: (event) {
oldOnData(event);
if (target.readyState == HttpRequest.DONE) {
log.add("unknown request done");
}
});
}
}
}
return parent.createTask(zone, create, spec);
}
return createTaskHandler;
}
Function buildRunTaskHandler(List log, List tasks) {
void runTaskHandler(Zone self, ZoneDelegate parent, Zone zone, TaskRun run,
Object task, Object arg) {
if (tasks.contains(task)) {
var url = urlExpando[task];
if (arg is Error || arg is Exception) {
log.add("failed $url");
} else {
if (arg is ProgressEvent) {
log.add("success $url with progress-event");
} else if (arg is HttpRequest) {
log.add("success $url with http-request");
} else {
log.add("success $url (unknown arg)");
}
}
}
parent.runTask(zone, run, task, arg);
}
return runTaskHandler;
}
Future<List> runMocked(response, fun) async {
var log = [];
var tasks = [];
var future = runZoned(fun,
zoneSpecification: new ZoneSpecification(
createTask: buildCreateTaskHandler(log, tasks),
runTask: buildRunTaskHandler(log, tasks)));
// Wait a full cycle to make sure things settle.
await new Future(() {});
var beforeTriggerLog = log.toList();
log.clear();
expect(tasks.length, 1);
tasks.single.trigger(response);
await future;
return [beforeTriggerLog, log];
}
void validate200Response(xhr) {
expect(xhr.status, equals(200));
var data = JSON.decode(xhr.responseText);
expect(data, contains('feed'));
expect(data['feed'], contains('entry'));
expect(data, isMap);
}
void validate404(xhr) {
expect(xhr.status, equals(404));
// We cannot say much about xhr.responseText, most HTTP servers will
// include an HTML page explaining the error to a human.
String responseText = xhr.responseText;
expect(responseText, isNotNull);
}
group('xhr', () {
test('XHR.request No file', () async {
var log = await runMocked("404", () {
var completer = new Completer();
HttpRequest.request('NonExistingFile').then((_) {
fail('Request should not have succeeded.');
}, onError: expectAsync((error) {
var xhr = error.target;
expect(xhr.readyState, equals(HttpRequest.DONE));
validate404(xhr);
completer.complete('done');
}));
return completer.future;
});
expect(
log,
equals([
[
'request NonExistingFile',
' method: null withCredentials: null responseType: null '
'mimeType: null data: null',
],
['success NonExistingFile with progress-event']
]));
});
test('XHR.request file', () async {
var log = await runMocked('{"feed": {"entry": 499}}', () {
var completer = new Completer();
HttpRequest.request(url).then(expectAsync((xhr) {
expect(xhr.readyState, equals(HttpRequest.DONE));
validate200Response(xhr);
completer.complete('done');
}));
return completer.future;
});
expect(
log,
equals([
[
'request $url',
' method: null withCredentials: null responseType: null '
'mimeType: null data: null'
],
['success $url with http-request']
]));
});
test('XHR.getString file', () async {
var log = await runMocked("foo", () {
return HttpRequest.getString(url).then(expectAsync((str) {}));
});
expect(
log,
equals([
[
'request $url',
' method: null withCredentials: null responseType: null '
'mimeType: null data: null'
],
['success $url with http-request']
]));
});
test('XHR.request responseType arraybuffer', () async {
if (Platform.supportsTypedData) {
var data = new Uint8List(128);
var log = await runMocked(data.buffer, () {
return HttpRequest.request(url,
responseType: 'arraybuffer',
requestHeaders: {
'Content-Type': 'text/xml'
}).then(expectAsync((xhr) {
expect(xhr.status, equals(200));
var byteBuffer = xhr.response;
expect(byteBuffer, new isInstanceOf<ByteBuffer>());
expect(byteBuffer, isNotNull);
}));
});
expect(
log,
equals([
[
'request $url',
' method: null withCredentials: null responseType: arraybuffer'
' mimeType: null data: null'
],
['success $url with http-request']
]));
}
;
});
});
}

View file

@ -1,508 +0,0 @@
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library XHRTaskTest;
import 'dart:async';
import 'dart:convert';
import 'dart:html';
import 'dart:typed_data';
import 'package:unittest/html_individual_config.dart';
import 'package:unittest/unittest.dart';
main() {
useHtmlIndividualConfiguration();
// Cache blocker is a workaround for:
// https://code.google.com/p/dart/issues/detail?id=11834
var cacheBlocker = new DateTime.now().millisecondsSinceEpoch;
var url = '/root_dart/tests/html/xhr_cross_origin_data.txt?'
'cacheBlock=$cacheBlocker';
var urlExpando = new Expando();
Function buildCreateTaskHandler(List log, List tasks) {
Object createTaskHandler(Zone self, ZoneDelegate parent, Zone zone,
TaskCreate create, TaskSpecification spec) {
if (spec is HttpRequestTaskSpecification) {
var url = spec.url;
var method = spec.method;
var withCredentials = spec.withCredentials;
var responseType = spec.responseType;
var mimeType = spec.mimeType;
var data = spec.sendData;
log.add("request $url");
var dataLog = data is List<int> ? "binary ${data.length}" : "$data";
log.add(" method: $method withCredentials: $withCredentials "
"responseType: $responseType mimeType: $mimeType data: $dataLog");
var task = parent.createTask(zone, create, spec);
urlExpando[task] = url;
tasks.add(task);
return task;
}
if (spec is HttpRequestSendTaskSpecification) {
var data = spec.sendData;
var dataLog = data is List<int> ? "binary ${data.length}" : "$data";
log.add("http-request (no info), data: $dataLog");
var task = parent.createTask(zone, create, spec);
tasks.add(task);
urlExpando[task] = "unknown";
return task;
}
if (spec is EventSubscriptionSpecification) {
EventSubscriptionSpecification eventSpec = spec;
if (eventSpec.target is HttpRequest) {
HttpRequest target = eventSpec.target;
log.add("event listener on http-request ${eventSpec.eventType}");
if (eventSpec.eventType == "readystatechange") {
var oldOnData = eventSpec.onData;
spec = eventSpec.replace(onData: (event) {
oldOnData(event);
if (target.readyState == HttpRequest.DONE) {
log.add("unknown request done");
}
});
}
}
}
return parent.createTask(zone, create, spec);
}
return createTaskHandler;
}
Function buildRunTaskHandler(List log, List tasks) {
void runTaskHandler(Zone self, ZoneDelegate parent, Zone zone,
TaskRun run, Object task, Object arg) {
if (tasks.contains(task)) {
var url = urlExpando[task];
if (arg is Error || arg is Exception) {
log.add("failed $url");
} else {
if (arg is ProgressEvent) {
log.add("success $url with progress-event");
} else if (arg is HttpRequest){
log.add("success $url with http-request");
} else {
log.add("success $url (unknown arg)");
}
}
}
parent.runTask(zone, run, task, arg);
}
return runTaskHandler;
}
Future<List> runWithLogging(fun) async {
var log = [];
var tasks = [];
await runZoned(fun, zoneSpecification: new ZoneSpecification(
createTask: buildCreateTaskHandler(log, tasks),
runTask: buildRunTaskHandler(log, tasks)));
return log;
}
void validate200Response(xhr) {
expect(xhr.status, equals(200));
var data = JSON.decode(xhr.responseText);
expect(data, contains('feed'));
expect(data['feed'], contains('entry'));
expect(data, isMap);
}
void validate404(xhr) {
expect(xhr.status, equals(404));
// We cannot say much about xhr.responseText, most HTTP servers will
// include an HTML page explaining the error to a human.
String responseText = xhr.responseText;
expect(responseText, isNotNull);
}
group('xhr', () {
test('XHR No file', () async {
var log = await runWithLogging(() {
var completer = new Completer();
HttpRequest xhr = new HttpRequest();
xhr.open("GET", "NonExistingFile", async: true);
xhr.onReadyStateChange.listen(expectAsyncUntil((event) {
if (xhr.readyState == HttpRequest.DONE) {
validate404(xhr);
completer.complete("done");
}
}, () => xhr.readyState == HttpRequest.DONE));
xhr.send();
return completer.future;
});
expect(log, equals([
'event listener on http-request readystatechange',
'http-request (no info), data: null',
'unknown request done'
]));
});
test('XHR_file', () async {
var log = await runWithLogging(() {
var completer = new Completer();
var loadEndCalled = false;
var xhr = new HttpRequest();
xhr.open('GET', url, async: true);
xhr.onReadyStateChange.listen(expectAsyncUntil((e) {
if (xhr.readyState == HttpRequest.DONE) {
validate200Response(xhr);
Timer.run(expectAsync(() {
expect(loadEndCalled, HttpRequest.supportsLoadEndEvent);
completer.complete("done");
}));
}
}, () => xhr.readyState == HttpRequest.DONE));
xhr.onLoadEnd.listen((ProgressEvent e) {
loadEndCalled = true;
});
xhr.send();
return completer.future;
});
expect(log, equals([
'event listener on http-request readystatechange',
'event listener on http-request loadend',
'http-request (no info), data: null',
'unknown request done'
]));
});
test('XHR.request No file', () async {
var log = await runWithLogging(() {
var completer = new Completer();
HttpRequest.request('NonExistingFile').then(
(_) { fail('Request should not have succeeded.'); },
onError: expectAsync((error) {
var xhr = error.target;
expect(xhr.readyState, equals(HttpRequest.DONE));
validate404(xhr);
completer.complete('done');
}));
return completer.future;
});
expect(log, equals([
'request NonExistingFile',
' method: null withCredentials: null responseType: null '
'mimeType: null data: null',
'event listener on http-request load',
'event listener on http-request error',
'success NonExistingFile with progress-event'
]));
});
test('XHR.request file', () async {
var log = await runWithLogging(() {
var completer = new Completer();
HttpRequest.request(url).then(expectAsync((xhr) {
expect(xhr.readyState, equals(HttpRequest.DONE));
validate200Response(xhr);
completer.complete('done');
}));
return completer.future;
});
expect(log, equals([
'request $url',
' method: null withCredentials: null responseType: null '
'mimeType: null data: null',
'event listener on http-request load',
'event listener on http-request error',
'success $url with http-request'
]));
});
test('XHR.request onProgress', () async {
var log = await runWithLogging(() {
var completer = new Completer();
var progressCalled = false;
HttpRequest.request(url,
onProgress: (_) {
progressCalled = true;
}).then(expectAsync(
(xhr) {
expect(xhr.readyState, equals(HttpRequest.DONE));
expect(progressCalled, HttpRequest.supportsProgressEvent);
validate200Response(xhr);
completer.complete("done");
}));
return completer.future;
});
expect(log, equals([
'request $url',
' method: null withCredentials: null responseType: null '
'mimeType: null data: null',
'event listener on http-request progress',
'event listener on http-request load',
'event listener on http-request error',
'success $url with http-request'
]));
});
test('XHR.request withCredentials No file', () async {
var log = await runWithLogging(() {
var completer = new Completer();
HttpRequest.request('NonExistingFile', withCredentials: true).then(
(_) { fail('Request should not have succeeded.'); },
onError: expectAsync((error) {
var xhr = error.target;
expect(xhr.readyState, equals(HttpRequest.DONE));
validate404(xhr);
completer.complete("done");
}));
return completer.future;
});
expect(log, equals([
'request NonExistingFile',
' method: null withCredentials: true responseType: null '
'mimeType: null data: null',
'event listener on http-request load',
'event listener on http-request error',
'success NonExistingFile with progress-event'
]));
});
test('XHR.request withCredentials file', () async {
var log = await runWithLogging(() {
var completer = new Completer();
HttpRequest.request(url, withCredentials: true).then(expectAsync((xhr) {
expect(xhr.readyState, equals(HttpRequest.DONE));
validate200Response(xhr);
completer.complete("done");
}));
return completer.future;
});
expect(log, equals([
'request $url',
' method: null withCredentials: true responseType: null '
'mimeType: null data: null',
'event listener on http-request load',
'event listener on http-request error',
'success $url with http-request'
]));
});
test('XHR.getString file', () async {
var log = await runWithLogging(() {
return HttpRequest.getString(url).then(expectAsync((str) {}));
});
expect(log, equals([
'request $url',
' method: null withCredentials: null responseType: null '
'mimeType: null data: null',
'event listener on http-request load',
'event listener on http-request error',
'success $url with http-request'
]));
});
test('XHR.getString No file', () async {
var log = await runWithLogging(() {
return HttpRequest.getString('NonExistingFile').then(
(_) { fail('Succeeded for non-existing file.'); },
onError: expectAsync((error) {
validate404(error.target);
}));
});
expect(log, equals([
'request NonExistingFile',
' method: null withCredentials: null responseType: null '
'mimeType: null data: null',
'event listener on http-request load',
'event listener on http-request error',
'success NonExistingFile with progress-event'
]));
});
test('XHR.request responseType arraybuffer', () async {
if (Platform.supportsTypedData) {
var log = await runWithLogging(() {
return HttpRequest.request(url, responseType: 'arraybuffer',
requestHeaders: {'Content-Type': 'text/xml'}).then(
expectAsync((xhr) {
expect(xhr.status, equals(200));
var byteBuffer = xhr.response;
expect(byteBuffer, new isInstanceOf<ByteBuffer>());
expect(byteBuffer, isNotNull);
}));
});
expect(log, equals([
'request $url',
' method: null withCredentials: null responseType: arraybuffer '
'mimeType: null data: null',
'event listener on http-request load',
'event listener on http-request error',
'success $url with http-request'
]));
};
});
test('overrideMimeType', () async {
var expectation =
HttpRequest.supportsOverrideMimeType ? returnsNormally : throws;
var log = await runWithLogging(() {
var completer = new Completer();
expect(() {
HttpRequest.request(url, mimeType: 'application/binary')
.whenComplete(completer.complete);
}, expectation);
return completer.future;
});
expect(log, equals([
'request $url',
' method: null withCredentials: null responseType: null '
'mimeType: application/binary data: null',
'event listener on http-request load',
'event listener on http-request error',
'success $url with http-request'
]));
});
if (Platform.supportsTypedData) {
test('xhr upload', () async {
var log = await runWithLogging(() {
var xhr = new HttpRequest();
var progressCalled = false;
xhr.upload.onProgress.listen((e) {
progressCalled = true;
});
xhr.open('POST',
'${window.location.protocol}//${window.location.host}/echo');
// 10MB of payload data w/ a bit of data to make sure it
// doesn't get compressed to nil.
var data = new Uint8List(1 * 1024 * 1024);
for (var i = 0; i < data.length; ++i) {
data[i] = i & 0xFF;
}
xhr.send(new Uint8List.view(data.buffer));
return xhr.onLoad.first.then((_) {
expect(
progressCalled, isTrue, reason: 'onProgress should be fired');
});
});
expect(log, equals([
'http-request (no info), data: binary 1048576',
'event listener on http-request load',
]));
});
}
test('xhr postFormData', () async {
var url = '${window.location.protocol}//${window.location.host}/echo';
var log = await runWithLogging(() {
var data = { 'name': 'John', 'time': '2 pm'};
var parts = [];
for (var key in data.keys) {
parts.add('${Uri.encodeQueryComponent(key)}='
'${Uri.encodeQueryComponent(data[key])}');
}
var encodedData = parts.join('&');
return HttpRequest.postFormData(url, data).then((xhr) {
expect(xhr.responseText, encodedData);
});
});
expect(log, equals([
'request $url',
' method: POST withCredentials: null responseType: null '
'mimeType: null data: name=John&time=2+pm',
'event listener on http-request load',
'event listener on http-request error',
'success $url with http-request'
]));
});
});
group('xhr_requestBlob', () {
test('XHR.request responseType blob', () async {
if (Platform.supportsTypedData) {
var log = await runWithLogging(() {
return HttpRequest.request(url, responseType: 'blob').then(
(xhr) {
expect(xhr.status, equals(200));
var blob = xhr.response;
expect(blob is Blob, isTrue);
expect(blob, isNotNull);
});
});
expect(log, equals([
'request $url',
' method: null withCredentials: null responseType: blob '
'mimeType: null data: null',
'event listener on http-request load',
'event listener on http-request error',
'success $url with http-request'
]));
}
});
});
group('json', () {
test('xhr responseType json', () async {
var url = '${window.location.protocol}//${window.location.host}/echo';
var log = await runWithLogging(() {
var completer = new Completer();
var data = {
'key': 'value',
'a': 'b',
'one': 2,
};
HttpRequest.request(url,
method: 'POST',
sendData: JSON.encode(data),
responseType: 'json').then(
expectAsync((xhr) {
expect(xhr.status, equals(200));
var json = xhr.response;
expect(json, equals(data));
completer.complete("done");
}));
return completer.future;
});
expect(log, equals([
'request $url',
' method: POST withCredentials: null responseType: json mimeType: null'
' data: {"key":"value","a":"b","one":2}',
'event listener on http-request load',
'event listener on http-request error',
'success $url with http-request'
]));
});
});
group('headers', () {
test('xhr responseHeaders', () async {
var log = await runWithLogging(() {
return HttpRequest.request(url).then(
(xhr) {
var contentTypeHeader = xhr.responseHeaders['content-type'];
expect(contentTypeHeader, isNotNull);
// Should be like: 'text/plain; charset=utf-8'
expect(contentTypeHeader.contains('text/plain'), isTrue);
expect(contentTypeHeader.contains('charset=utf-8'), isTrue);
});
});
expect(log, equals([
'request $url',
' method: null withCredentials: null responseType: null'
' mimeType: null data: null',
'event listener on http-request load',
'event listener on http-request error',
'success $url with http-request'
]));
});
});
}

View file

@ -1,310 +0,0 @@
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Tests basic functionality of tasks in zones.
import 'package:expect/expect.dart';
import 'package:async_helper/async_helper.dart';
import 'dart:async';
List log = [];
class MySpecification extends TaskSpecification {
final Function callback;
final bool isOneShot;
final int value;
MySpecification(void this.callback(), this.isOneShot, this.value);
String get name => "test.specification-name";
}
class MyTask {
final Zone zone;
final Function callback;
final int id;
int invocationCount = 0;
bool shouldStop = false;
MyTask(this.zone, void this.callback(), this.id);
}
void runMyTask(MyTask task, int value) {
log.add("running "
"zone: ${Zone.current['name']} "
"task-id: ${task.id} "
"invocation-count: ${task.invocationCount} "
"value: $value");
task.callback();
task.invocationCount++;
}
MyTask createMyTask(MySpecification spec, Zone zone) {
var task = new MyTask(zone, spec.callback, spec.value);
log.add("creating task: ${spec.value} oneshot?: ${spec.isOneShot}");
if (spec.isOneShot) {
Timer.run(() {
zone.runTask(runMyTask, task, task.id);
});
} else {
new Timer.periodic(const Duration(milliseconds: 10), (Timer timer) {
zone.runTask(runMyTask, task, task.id);
if (task.shouldStop) {
timer.cancel();
}
});
}
return task;
}
MyTask startTask(f, bool oneShot, int value) {
var spec = new MySpecification(f, oneShot, value);
return Zone.current.createTask(createMyTask, spec);
}
/// Makes sure things are working in a simple setting.
/// No interceptions, changes, ...
Future testCustomTask() {
var testCompleter = new Completer();
asyncStart();
Object createTaskHandler(Zone self, ZoneDelegate parent, Zone zone,
TaskCreate create, TaskSpecification specification) {
if (specification is MySpecification) {
log.add("create enter "
"zone: ${self['name']} "
"spec-value: ${specification.value} "
"spec-oneshot?: ${specification.isOneShot}");
MyTask result = parent.createTask(zone, create, specification);
log.add("create leave");
return result;
}
return parent.createTask(zone, create, specification);
}
void runTaskHandler(Zone self, ZoneDelegate parent, Zone zone, TaskRun run,
Object task, Object arg) {
if (task is MyTask) {
log.add("run enter "
"zone: ${self['name']} "
"task-id: ${task.id} "
"invocation-count: ${task.invocationCount} "
"arg: $arg");
parent.runTask(zone, run, task, arg);
log.add("run leave invocation-count: ${task.invocationCount}");
return;
}
parent.runTask(zone, run, task, arg);
}
runZoned(() async {
var completer0 = new Completer();
startTask(() {
completer0.complete("done");
}, true, 0);
await completer0.future;
Expect.listEquals([
'create enter zone: custom zone spec-value: 0 spec-oneshot?: true',
'creating task: 0 oneshot?: true',
'create leave',
'run enter zone: custom zone task-id: 0 invocation-count: 0 arg: 0',
'running zone: custom zone task-id: 0 invocation-count: 0 value: 0',
'run leave invocation-count: 1'
], log);
log.clear();
var completer1 = new Completer();
MyTask task1;
task1 = startTask(() {
if (task1.invocationCount == 1) {
task1.shouldStop = true;
completer1.complete("done");
}
}, false, 1);
await completer1.future;
Expect.listEquals([
'create enter zone: custom zone spec-value: 1 spec-oneshot?: false',
'creating task: 1 oneshot?: false',
'create leave',
'run enter zone: custom zone task-id: 1 invocation-count: 0 arg: 1',
'running zone: custom zone task-id: 1 invocation-count: 0 value: 1',
'run leave invocation-count: 1',
'run enter zone: custom zone task-id: 1 invocation-count: 1 arg: 1',
'running zone: custom zone task-id: 1 invocation-count: 1 value: 1',
'run leave invocation-count: 2',
], log);
log.clear();
testCompleter.complete("done");
asyncEnd();
},
zoneValues: {'name': 'custom zone'},
zoneSpecification: new ZoneSpecification(
createTask: createTaskHandler,
runTask: runTaskHandler));
return testCompleter.future;
}
/// More complicated zone, that intercepts...
Future testCustomTask2() {
var testCompleter = new Completer();
asyncStart();
Object createTaskHandler(Zone self, ZoneDelegate parent, Zone zone,
TaskCreate create, TaskSpecification specification) {
if (specification is MySpecification) {
log.add("create enter "
"zone: ${self['name']} "
"spec-value: ${specification.value} "
"spec-oneshot?: ${specification.isOneShot}");
var replacement = new MySpecification(specification.callback,
specification.isOneShot, specification.value + 1);
MyTask result = parent.createTask(zone, create, replacement);
log.add("create leave");
return result;
}
return parent.createTask(zone, create, specification);
}
void runTaskHandler(Zone self, ZoneDelegate parent, Zone zone, TaskRun run,
Object task, Object arg) {
if (task is MyTask) {
log.add("run enter "
"zone: ${self['name']} "
"task-id: ${task.id} "
"invocation-count: ${task.invocationCount} "
"arg: $arg");
int value = arg;
parent.runTask(zone, run, task, value + 101);
log.add("run leave invocation-count: ${task.invocationCount}");
return;
}
parent.runTask(zone, run, task, arg);
}
runZoned(() async {
var completer0 = new Completer();
startTask(() {
completer0.complete("done");
}, true, 0);
await completer0.future;
Expect.listEquals([
'create enter zone: outer-zone spec-value: 0 spec-oneshot?: true',
'creating task: 1 oneshot?: true',
'create leave',
'run enter zone: outer-zone task-id: 1 invocation-count: 0 arg: 1',
'running zone: outer-zone task-id: 1 invocation-count: 0 value: 102',
'run leave invocation-count: 1'
], log);
log.clear();
var completer1 = new Completer();
MyTask task1;
task1 = startTask(() {
if (task1.invocationCount == 1) {
task1.shouldStop = true;
completer1.complete("done");
}
}, false, 1);
await completer1.future;
Expect.listEquals([
'create enter zone: outer-zone spec-value: 1 spec-oneshot?: false',
'creating task: 2 oneshot?: false',
'create leave',
'run enter zone: outer-zone task-id: 2 invocation-count: 0 arg: 2',
'running zone: outer-zone task-id: 2 invocation-count: 0 value: 103',
'run leave invocation-count: 1',
'run enter zone: outer-zone task-id: 2 invocation-count: 1 arg: 2',
'running zone: outer-zone task-id: 2 invocation-count: 1 value: 103',
'run leave invocation-count: 2',
], log);
log.clear();
var nestedCompleter = new Completer();
runZoned(() async {
var completer0 = new Completer();
startTask(() {
completer0.complete("done");
}, true, 0);
await completer0.future;
Expect.listEquals([
'create enter zone: inner-zone spec-value: 0 spec-oneshot?: true',
'create enter zone: outer-zone spec-value: 1 spec-oneshot?: true',
'creating task: 2 oneshot?: true',
'create leave',
'create leave',
'run enter zone: inner-zone task-id: 2 invocation-count: 0 arg: 2',
'run enter zone: outer-zone task-id: 2 invocation-count: 0 arg: 103',
'running zone: inner-zone task-id: 2 invocation-count: 0 value: 204',
'run leave invocation-count: 1',
'run leave invocation-count: 1'
], log);
log.clear();
var completer1 = new Completer();
MyTask task1;
task1 = startTask(() {
if (task1.invocationCount == 1) {
task1.shouldStop = true;
completer1.complete("done");
}
}, false, 1);
await completer1.future;
Expect.listEquals([
'create enter zone: inner-zone spec-value: 1 spec-oneshot?: false',
'create enter zone: outer-zone spec-value: 2 spec-oneshot?: false',
'creating task: 3 oneshot?: false',
'create leave',
'create leave',
'run enter zone: inner-zone task-id: 3 invocation-count: 0 arg: 3',
'run enter zone: outer-zone task-id: 3 invocation-count: 0 arg: 104',
'running zone: inner-zone task-id: 3 invocation-count: 0 value: 205',
'run leave invocation-count: 1',
'run leave invocation-count: 1',
'run enter zone: inner-zone task-id: 3 invocation-count: 1 arg: 3',
'run enter zone: outer-zone task-id: 3 invocation-count: 1 arg: 104',
'running zone: inner-zone task-id: 3 invocation-count: 1 value: 205',
'run leave invocation-count: 2',
'run leave invocation-count: 2',
], log);
log.clear();
nestedCompleter.complete("done");
},
zoneValues: {'name': 'inner-zone'},
zoneSpecification: new ZoneSpecification(
createTask: createTaskHandler,
runTask: runTaskHandler));
await nestedCompleter.future;
testCompleter.complete("done");
asyncEnd();
},
zoneValues: {'name': 'outer-zone'},
zoneSpecification: new ZoneSpecification(
createTask: createTaskHandler,
runTask: runTaskHandler));
return testCompleter.future;
}
runTests() async {
await testCustomTask();
await testCustomTask2();
}
main() {
asyncStart();
runTests().then((_) {
asyncEnd();
});
}

View file

@ -1,515 +0,0 @@
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Tests timer tasks.
import 'package:expect/expect.dart';
import 'package:async_helper/async_helper.dart';
import 'dart:async';
import 'dart:collection';
class MyTimerSpecification implements SingleShotTimerTaskSpecification {
final Function callback;
final Duration duration;
MyTimerSpecification(this.callback, this.duration);
bool get isOneShot => true;
String get name => "test.timer-override";
}
class MyPeriodicTimerSpecification implements PeriodicTimerTaskSpecification {
final Function callback;
final Duration duration;
MyPeriodicTimerSpecification(this.callback, this.duration);
bool get isOneShot => true;
String get name => "test.periodic-timer-override";
}
/// Makes sure things are working in a simple setting.
/// No interceptions, changes, ...
Future testTimerTask() {
List log = [];
var testCompleter = new Completer();
asyncStart();
int taskIdCounter = 0;
Object createTaskHandler(Zone self, ZoneDelegate parent, Zone zone,
TaskCreate create, TaskSpecification specification) {
var taskMap = self['taskMap'];
var taskIdMap = self['taskIdMap'];
if (specification is SingleShotTimerTaskSpecification) {
log.add("create enter "
"zone: ${self['name']} "
"spec-duration: ${specification.duration} "
"spec-oneshot?: ${specification.isOneShot}");
var result = parent.createTask(zone, create, specification);
taskMap[result] = specification;
taskIdMap[specification] = taskIdCounter++;
log.add("create leave");
return result;
} else if (specification is PeriodicTimerTaskSpecification) {
log.add("create enter "
"zone: ${self['name']} "
"spec-duration: ${specification.duration} "
"spec-oneshot?: ${specification.isOneShot}");
var result = parent.createTask(zone, create, specification);
taskMap[result] = specification;
taskIdMap[specification] = taskIdCounter++;
log.add("create leave");
return result;
}
return parent.createTask(zone, create, specification);
}
void runTaskHandler(Zone self, ZoneDelegate parent, Zone zone, TaskRun run,
Object task, Object arg) {
var taskMap = self['taskMap'];
var taskIdMap = self['taskIdMap'];
if (taskMap.containsKey(task)) {
var spec = taskMap[task];
log.add("run enter "
"zone: ${self['name']} "
"task-id: ${taskIdMap[spec]} "
"arg: $arg");
parent.runTask(zone, run, task, arg);
log.add("run leave");
return;
}
parent.runTask(zone, run, task, arg);
}
runZoned(() async {
var completer0 = new Completer();
Timer.run(() {
completer0.complete("done");
});
await completer0.future;
Expect.listEquals([
'create enter zone: custom zone spec-duration: 0:00:00.000000 '
'spec-oneshot?: true',
'create leave',
'run enter zone: custom zone task-id: 0 arg: null',
'run leave'
], log);
log.clear();
var completer1 = new Completer();
var counter1 = 0;
new Timer.periodic(const Duration(milliseconds: 5), (Timer timer) {
if (counter1++ > 1) {
timer.cancel();
completer1.complete("done");
}
});
await completer1.future;
Expect.listEquals([
'create enter zone: custom zone spec-duration: 0:00:00.005000 '
'spec-oneshot?: false',
'create leave',
'run enter zone: custom zone task-id: 1 arg: null',
'run leave',
'run enter zone: custom zone task-id: 1 arg: null',
'run leave',
'run enter zone: custom zone task-id: 1 arg: null',
'run leave'
], log);
log.clear();
testCompleter.complete("done");
asyncEnd();
},
zoneValues: {'name': 'custom zone', 'taskMap': {}, 'taskIdMap': {}},
zoneSpecification: new ZoneSpecification(
createTask: createTaskHandler,
runTask: runTaskHandler));
return testCompleter.future;
}
/// More complicated zone, that intercepts...
Future testTimerTask2() {
List log = [];
var testCompleter = new Completer();
asyncStart();
int taskIdCounter = 0;
Object createTaskHandler(Zone self, ZoneDelegate parent, Zone zone,
TaskCreate create, TaskSpecification specification) {
var taskMap = self['taskMap'];
var taskIdMap = self['taskIdMap'];
if (specification is SingleShotTimerTaskSpecification) {
log.add("create enter "
"zone: ${self['name']} "
"spec-duration: ${specification.duration} "
"spec-oneshot?: ${specification.isOneShot}");
var mySpec = new MyTimerSpecification(specification.callback,
specification.duration + const Duration(milliseconds: 2));
var result = parent.createTask(zone, create, mySpec);
taskMap[result] = specification;
taskIdMap[specification] = taskIdCounter++;
log.add("create leave");
return result;
} else if (specification is PeriodicTimerTaskSpecification) {
log.add("create enter "
"zone: ${self['name']} "
"spec-duration: ${specification.duration} "
"spec-oneshot?: ${specification.isOneShot}");
var mySpec = new MyPeriodicTimerSpecification(specification.callback,
specification.duration + const Duration(milliseconds: 2));
var result = parent.createTask(zone, create, specification);
taskMap[result] = specification;
taskIdMap[specification] = taskIdCounter++;
log.add("create leave");
return result;
}
return parent.createTask(zone, create, specification);
}
void runTaskHandler(Zone self, ZoneDelegate parent, Zone zone, TaskRun run,
Object task, Object arg) {
var taskMap = self['taskMap'];
var taskIdMap = self['taskIdMap'];
if (taskMap.containsKey(task)) {
var spec = taskMap[task];
log.add("run enter "
"zone: ${self['name']} "
"task-id: ${taskIdMap[spec]} "
"arg: $arg");
parent.runTask(zone, run, task, arg);
log.add("run leave");
return;
}
parent.runTask(zone, run, task, arg);
}
runZoned(() async {
var completer0 = new Completer();
Timer.run(() {
completer0.complete("done");
});
await completer0.future;
// No visible change (except for the zone name) in the log, compared to the
// simple invocations.
Expect.listEquals([
'create enter zone: outer-zone spec-duration: 0:00:00.000000 '
'spec-oneshot?: true',
'create leave',
'run enter zone: outer-zone task-id: 0 arg: null',
'run leave'
], log);
log.clear();
var completer1 = new Completer();
var counter1 = 0;
new Timer.periodic(const Duration(milliseconds: 5), (Timer timer) {
if (counter1++ > 1) {
timer.cancel();
completer1.complete("done");
}
});
await completer1.future;
// No visible change (except for the zone nome) in the log, compared to the
// simple invocations.
Expect.listEquals([
'create enter zone: outer-zone spec-duration: 0:00:00.005000 '
'spec-oneshot?: false',
'create leave',
'run enter zone: outer-zone task-id: 1 arg: null',
'run leave',
'run enter zone: outer-zone task-id: 1 arg: null',
'run leave',
'run enter zone: outer-zone task-id: 1 arg: null',
'run leave'
], log);
log.clear();
var nestedCompleter = new Completer();
runZoned(() async {
var completer0 = new Completer();
Timer.run(() {
completer0.complete("done");
});
await completer0.future;
// The outer zone sees the duration change of the inner zone.
Expect.listEquals([
'create enter zone: inner-zone spec-duration: 0:00:00.000000 '
'spec-oneshot?: true',
'create enter zone: outer-zone spec-duration: 0:00:00.002000 '
'spec-oneshot?: true',
'create leave',
'create leave',
'run enter zone: inner-zone task-id: 3 arg: null',
'run enter zone: outer-zone task-id: 2 arg: null',
'run leave',
'run leave'
], log);
log.clear();
var completer1 = new Completer();
var counter1 = 0;
new Timer.periodic(const Duration(milliseconds: 5), (Timer timer) {
if (counter1++ > 1) {
timer.cancel();
completer1.complete("done");
}
});
await completer1.future;
// The outer zone sees the duration change of the inner zone.
Expect.listEquals([
'create enter zone: inner-zone spec-duration: 0:00:00.005000 '
'spec-oneshot?: false',
'create enter zone: outer-zone spec-duration: 0:00:00.005000 '
'spec-oneshot?: false',
'create leave',
'create leave',
'run enter zone: inner-zone task-id: 5 arg: null',
'run enter zone: outer-zone task-id: 4 arg: null',
'run leave',
'run leave',
'run enter zone: inner-zone task-id: 5 arg: null',
'run enter zone: outer-zone task-id: 4 arg: null',
'run leave',
'run leave',
'run enter zone: inner-zone task-id: 5 arg: null',
'run enter zone: outer-zone task-id: 4 arg: null',
'run leave',
'run leave'
], log);
log.clear();
nestedCompleter.complete("done");
},
zoneValues: {'name': 'inner-zone', 'taskMap': {}, 'taskIdMap': {}},
zoneSpecification: new ZoneSpecification(
createTask: createTaskHandler,
runTask: runTaskHandler));
await nestedCompleter.future;
testCompleter.complete("done");
asyncEnd();
},
zoneValues: {'name': 'outer-zone', 'taskMap': {}, 'taskIdMap': {}},
zoneSpecification: new ZoneSpecification(
createTask: createTaskHandler,
runTask: runTaskHandler));
return testCompleter.future;
}
class TimerEntry {
final int time;
final SimulatedTimer timer;
TimerEntry(this.time, this.timer);
}
class SimulatedTimer implements Timer {
static int _idCounter = 0;
Zone _zone;
final int _id = _idCounter++;
final Duration _duration;
final Function _callback;
final bool _isPeriodic;
bool _isActive = true;
SimulatedTimer(this._zone, this._duration, this._callback, this._isPeriodic);
bool get isActive => _isActive;
void cancel() {
_isActive = false;
}
void _run() {
if (!isActive) return;
_zone.runTask(_runTimer, this, null);
}
static void _runTimer(SimulatedTimer timer, _) {
if (timer._isPeriodic) {
timer._callback(timer);
} else {
timer._callback();
}
}
}
testSimulatedTimer() {
List log = [];
var currentTime = 0;
// Using a simple list as queue. Not very efficient, but the test has only
// very few timers running at the same time.
var queue = new DoubleLinkedQueue<TimerEntry>();
// Schedules the given callback at now + duration.
void schedule(int scheduledTime, SimulatedTimer timer) {
log.add("scheduling timer ${timer._id} for $scheduledTime");
if (queue.isEmpty) {
queue.add(new TimerEntry(scheduledTime, timer));
} else {
DoubleLinkedQueueEntry current = queue.firstEntry();
while (current != null) {
if (current.element.time <= scheduledTime) {
current = current.nextEntry();
} else {
current.prepend(new TimerEntry(scheduledTime, timer));
break;
}
}
if (current == null) {
queue.add(new TimerEntry(scheduledTime, timer));
}
}
}
void runQueue() {
while (queue.isNotEmpty) {
var item = queue.removeFirst();
// If multiple callbacks were scheduled at the same time, increment the
// current time instead of staying at the same time.
currentTime = item.time > currentTime ? item.time : currentTime + 1;
SimulatedTimer timer = item.timer;
log.add("running timer ${timer._id} at $currentTime "
"(active?: ${timer.isActive})");
if (!timer.isActive) continue;
if (timer._isPeriodic) {
schedule(currentTime + timer._duration.inMilliseconds, timer);
}
item.timer._run();
}
}
SimulatedTimer createSimulatedOneShotTimer(
SingleShotTimerTaskSpecification spec, Zone zone) {
var timer = new SimulatedTimer(zone, spec.duration, spec.callback, false);
schedule(currentTime + spec.duration.inMilliseconds, timer);
return timer;
}
SimulatedTimer createSimulatedPeriodicTimer(
PeriodicTimerTaskSpecification spec, Zone zone) {
var timer = new SimulatedTimer(zone, spec.duration, spec.callback, true);
schedule(currentTime + spec.duration.inMilliseconds, timer);
return timer;
}
Object createSimulatedTaskHandler(Zone self, ZoneDelegate parent, Zone zone,
TaskCreate create, TaskSpecification specification) {
var taskMap = self['taskMap'];
var taskIdMap = self['taskIdMap'];
if (specification is SingleShotTimerTaskSpecification) {
log.add("create enter "
"zone: ${self['name']} "
"spec-duration: ${specification.duration} "
"spec-oneshot?: ${specification.isOneShot}");
var result =
parent.createTask(zone, createSimulatedOneShotTimer, specification);
log.add("create leave");
return result;
}
if (specification is PeriodicTimerTaskSpecification) {
log.add("create enter "
"zone: ${self['name']} "
"spec-duration: ${specification.duration} "
"spec-oneshot?: ${specification.isOneShot}");
var result =
parent.createTask(zone, createSimulatedPeriodicTimer, specification);
log.add("create leave");
return result;
}
return parent.createTask(zone, create, specification);
}
runZoned(() {
Timer.run(() {
log.add("running Timer.run");
});
var timer0;
new Timer(const Duration(milliseconds: 10), () {
log.add("running Timer(10)");
timer0.cancel();
log.add("canceled timer0");
});
timer0 = new Timer(const Duration(milliseconds: 15), () {
log.add("running Timer(15)");
});
var counter1 = 0;
new Timer.periodic(const Duration(milliseconds: 5), (Timer timer) {
log.add("running periodic timer $counter1");
if (counter1++ > 1) {
timer.cancel();
}
});
},
zoneSpecification:
new ZoneSpecification(createTask: createSimulatedTaskHandler));
runQueue();
Expect.listEquals([
'create enter zone: null spec-duration: 0:00:00.000000 spec-oneshot?: true',
'scheduling timer 0 for 0',
'create leave',
'create enter zone: null spec-duration: 0:00:00.010000 spec-oneshot?: true',
'scheduling timer 1 for 10',
'create leave',
'create enter zone: null spec-duration: 0:00:00.015000 spec-oneshot?: true',
'scheduling timer 2 for 15',
'create leave',
'create enter zone: null spec-duration: 0:00:00.005000 '
'spec-oneshot?: false',
'scheduling timer 3 for 5',
'create leave',
'running timer 0 at 1 (active?: true)',
'running Timer.run',
'running timer 3 at 5 (active?: true)',
'scheduling timer 3 for 10',
'running periodic timer 0',
'running timer 1 at 10 (active?: true)',
'running Timer(10)',
'canceled timer0',
'running timer 3 at 11 (active?: true)',
'scheduling timer 3 for 16',
'running periodic timer 1',
'running timer 2 at 15 (active?: false)',
'running timer 3 at 16 (active?: true)',
'scheduling timer 3 for 21',
'running periodic timer 2',
'running timer 3 at 21 (active?: false)'
], log);
log.clear();
}
runTests() async {
await testTimerTask();
await testTimerTask2();
testSimulatedTimer();
}
main() {
asyncStart();
runTests().then((_) {
asyncEnd();
});
}

View file

@ -167,8 +167,6 @@ async/stream_asyncmap_test: RuntimeError # Timer interface not supported: Issue
async/stream_transformation_broadcast_test: RuntimeError # Timer interface not supported: Issue 7728.
async/stream_controller_test: Fail # Timer interface not supported: Issue 7728.
async/future_constructor2_test: Fail # Timer interface not supported: Issue 7728.
async/zone_timer_task_test: Fail # Timer interface not supported: Issue 7728.
async/zone_task_test: Fail # Timer interface not supported: Issue 7728.
mirrors/mirrors_reader_test: Skip # Running in v8 suffices. Issue 16589 - RuntimeError. Issue 22130 - Crash (out of memory).
[ $compiler == dart2js && $checked ]

View file

@ -4201,7 +4201,7 @@
" *",
" * Note: Most simple HTTP requests can be accomplished using the [getString],",
" * [request], [requestCrossOrigin], or [postFormData] methods. Use of this",
" * `open` method is intended only for more complex HTTP requests where",
" * `open` method is intended only for more complext HTTP requests where",
" * finer-grained control is needed.",
" */"
],
@ -4298,7 +4298,7 @@
" *",
" * Note: Most simple HTTP requests can be accomplished using the [getString],",
" * [request], [requestCrossOrigin], or [postFormData] methods. Use of this",
" * `send` method is intended only for more complex HTTP requests where",
" * `send` method is intended only for more complext HTTP requests where",
" * finer-grained control is needed.",
" *",
" * ## Other resources",

View file

@ -404,8 +404,6 @@ private_html_members = monitored.Set('htmlrenamer.private_html_members', [
'Window.requestAnimationFrame',
'Window.setInterval',
'Window.setTimeout',
'XMLHttpRequest.send',
])
# Members from the standard dom that exist in the dart:html library with

View file

@ -118,41 +118,6 @@ abstract class ElementStream<T extends Event> implements Stream<T> {
StreamSubscription<T> capture(void onData(T event));
}
/// Task specification for DOM Events.
///
/// *Experimental*. May disappear without notice.
class EventSubscriptionSpecification<T extends Event>
implements TaskSpecification {
@override
final String name;
@override
final bool isOneShot;
final EventTarget target;
/// The event-type of the event. For example 'click' for click events.
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.
*/
@ -160,16 +125,8 @@ 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, String eventType, this._useCapture,
{String name, bool isOneShot: false})
: _eventType = eventType,
_isOneShot = isOneShot,
_name = name ?? "dart.html.event.$eventType";
_EventStream(this._target, this._eventType, this._useCapture);
// DOM events are inherently multi-subscribers.
Stream<T> asBroadcastStream({void onListen(StreamSubscription<T> subscription),
@ -177,31 +134,13 @@ class _EventStream<T extends Event> extends Stream<T> {
=> this;
bool get isBroadcast => true;
StreamSubscription<T> _listen(
void onData(T event), {bool 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: 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);
}
StreamSubscription<T> listen(void onData(T event),
{ Function onError,
void onDone(),
bool cancelOnError}) {
return _listen(onData, useCapture: this._useCapture);
return new _EventStreamSubscription<T>(
this._target, this._eventType, onData, this._useCapture);
}
}
@ -216,9 +155,8 @@ bool _matchesWithAncestors(Event event, String selector) {
*/
class _ElementEventStreamImpl<T extends Event> extends _EventStream<T>
implements ElementStream<T> {
_ElementEventStreamImpl(target, eventType, useCapture,
{String name, bool isOneShot: false}) :
super(target, eventType, useCapture, name: name, isOneShot: isOneShot);
_ElementEventStreamImpl(target, eventType, useCapture) :
super(target, eventType, useCapture);
Stream<T> matches(String selector) => this.where(
(event) => _matchesWithAncestors(event, selector)).map((e) {
@ -226,9 +164,9 @@ class _ElementEventStreamImpl<T extends Event> extends _EventStream<T>
return e;
});
StreamSubscription<T> capture(void onData(T event)) {
return _listen(onData, useCapture: true);
}
StreamSubscription<T> capture(void onData(T event)) =>
new _EventStreamSubscription<T>(
this._target, this._eventType, onData, true);
}
/**
@ -277,13 +215,7 @@ class _ElementListEventStreamImpl<T extends Event> extends Stream<T>
bool get isBroadcast => true;
}
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
// We would like this to just be EventListener<T> but that typdef cannot
// use generics until dartbug/26276 is fixed.
typedef _EventListener<T extends Event>(T event);
@ -292,19 +224,15 @@ 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/*<dynamic, Event>*/((e) => onData(e as T))
// _onData = onData == null ? null : _wrapZone/*<Event, dynamic>*/((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, Zone zone)
: _zone = zone,
_onData = _registerZone/*<dynamic, Event>*/(zone, onData) {
this._useCapture) : _onData = _wrapZone/*<Event, dynamic>*/(onData) {
_tryResume();
}
@ -326,7 +254,7 @@ class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
}
// Remove current event listener.
_unlisten();
_onData = _registerZone/*<dynamic, Event>*/(_zone, handleData);
_onData = _wrapZone/*<Event, dynamic>*/(handleData);
_tryResume();
}
@ -355,25 +283,14 @@ class _EventStreamSubscription<T extends Event> extends StreamSubscription<T> {
}
void _tryResume() {
if (_onData == null || isPaused) return;
if (identical(_zone, Zone.ROOT)) {
_domCallback = _onData;
} else {
_domCallback = (event) {
_zone.runTask(_runEventNotification, this, event);
};
if (_onData != null && !isPaused) {
_target.addEventListener(_eventType, _onData, _useCapture);
}
_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, _domCallback, _useCapture);
_target.removeEventListener(_eventType, _onData, _useCapture);
}
}

View file

@ -4,26 +4,31 @@
part of dart.dom.html;
ZoneUnaryCallback/*<R, T>*/ _registerZone/*<R, T>*/(Zone zone,
ZoneUnaryCallback/*<R, T>*/ callback) {
// For performance reasons avoid registering if we are in the root zone.
if (identical(zone, Zone.ROOT)) return callback;
if (callback == null) return null;
return zone.registerUnaryCallback(callback);
}
// TODO(jacobr): remove these typedefs when dart:async supports generic types.
typedef R _wrapZoneCallback<A, R>(A a);
typedef R _wrapZoneBinaryCallback<A, B, R>(A a, B b);
ZoneUnaryCallback/*<R, T>*/ _wrapZone/*<R, T>*/(ZoneUnaryCallback/*<R, T>*/ callback) {
_wrapZoneCallback/*<A, R>*/ _wrapZone/*<A, R>*/(_wrapZoneCallback/*<A, R>*/ callback) {
// For performance reasons avoid wrapping if we are in the root zone.
if (identical(Zone.current, Zone.ROOT)) return callback;
if (Zone.current == Zone.ROOT) return callback;
if (callback == null) return null;
return Zone.current.bindUnaryCallback(callback, runGuarded: true);
// TODO(jacobr): we cast to _wrapZoneCallback/*<A, R>*/ to hack around missing
// generic method support in zones.
// ignore: STRONG_MODE_DOWN_CAST_COMPOSITE
_wrapZoneCallback/*<A, R>*/ wrapped =
Zone.current.bindUnaryCallback(callback, runGuarded: true);
return wrapped;
}
ZoneBinaryCallback/*<R, A, B>*/ _wrapBinaryZone/*<R, A, B>*/(
ZoneBinaryCallback/*<R, A, B>*/ callback) {
if (identical(Zone.current, Zone.ROOT)) return callback;
_wrapZoneBinaryCallback/*<A, B, R>*/ _wrapBinaryZone/*<A, B, R>*/(_wrapZoneBinaryCallback/*<A, B, R>*/ callback) {
if (Zone.current == Zone.ROOT) return callback;
if (callback == null) return null;
return Zone.current.bindBinaryCallback(callback, runGuarded: true);
// We cast to _wrapZoneBinaryCallback/*<A, B, R>*/ to hack around missing
// generic method support in zones.
// ignore: STRONG_MODE_DOWN_CAST_COMPOSITE
_wrapZoneBinaryCallback/*<A, B, R>*/ wrapped =
Zone.current.bindBinaryCallback(callback, runGuarded: true);
return wrapped;
}
/**

View file

@ -4,99 +4,6 @@
part of $LIBRARYNAME;
typedef void RemoveFrameRequestMapping(int id);
/**
* The task object representing animation-frame requests.
*
* For historical reasons, [Window.requestAnimationFrame] returns an integer
* to users. However, zone tasks must be unique objects, and an integer can
* therefore not be used as task object. The [Window] class thus keeps a mapping
* from the integer ID to the corresponding task object. All zone related
* operations work on this task object, whereas users of
* [Window.requestAnimationFrame] only see the integer ID.
*
* Since this mapping takes up space, it must be removed when the
* animation-frame task has triggered. The default implementation does this
* automatically, but intercepting implementations of `requestAnimationFrame`
* must make sure to call the [AnimationFrameTask.removeMapping]
* function that is provided in the task specification.
*
* *Experimental*. This class may disappear without notice.
*/
abstract class AnimationFrameTask {
/** The ID that is returned to users. */
int get id;
/** The zone in which the task will run. */
Zone get zone;
/**
* Cancels the animation-frame request.
*
* A call to [Window.cancelAnimationFrame] with an `id` argument equal to [id]
* forwards the request to this function.
*
* Zones that intercept animation-frame requests implement this method so
* that they can react to cancelation requests.
*/
void cancel(Window window);
/**
* Maps animation-frame request IDs to their task objects.
*/
static final Map<int, _AnimationFrameTask> _tasks = {};
/**
* Removes the mapping from [id] to [AnimationFrameTask].
*
* This function must be invoked by user-implemented animation-frame
* tasks, before running [callback].
*
* See [AnimationFrameTask].
*/
static void removeMapping(int id) {
_tasks.remove(id);
}
}
class _AnimationFrameTask implements AnimationFrameTask {
final int id;
final Zone zone;
final FrameRequestCallback _callback;
_AnimationFrameTask(this.id, this.zone, this._callback);
void cancel(Window window) {
window._cancelAnimationFrame(this.id);
}
}
/**
* The task specification for an animation-frame request.
*
* *Experimental*. This class may disappear without notice.
*/
class AnimationFrameRequestSpecification implements TaskSpecification {
/**
* The window on which [Window.requestAnimationFrame] was invoked.
*/
final Window window;
/**
* The callback that is executed when the animation-frame is ready.
*
* Note that the callback hasn't been registered in any zone when the `create`
* function (passed to [Zone.createTask]) is invoked.
*/
final FrameRequestCallback callback;
AnimationFrameRequestSpecification(this.window, this.callback);
String get name => "dart.html.request-animation-frame";
bool get isOneShot => true;
}
@DocsEditable()
$if DART2JS
$(ANNOTATIONS)@Native("Window,DOMWindow")
@ -122,7 +29,9 @@ $endif
*/
Future<num> get animationFrame {
var completer = new Completer<num>.sync();
requestAnimationFrame(completer.complete);
requestAnimationFrame((time) {
completer.complete(time);
});
return completer.future;
}
@ -206,30 +115,7 @@ $if DART2JS
@DomName('Window.requestAnimationFrame')
int requestAnimationFrame(FrameRequestCallback callback) {
_ensureRequestAnimationFrame();
if (identical(Zone.current, Zone.ROOT)) {
return _requestAnimationFrame(callback);
}
var spec = new AnimationFrameRequestSpecification(this, callback);
var task = Zone.current.createTask/*<AnimationFrameTask>*/(
_createAnimationFrameTask, spec);
AnimationFrameTask._tasks[task.id] = task;
return task.id;
}
static _AnimationFrameTask _createAnimationFrameTask(
AnimationFrameRequestSpecification spec, Zone zone) {
var task;
var id = spec.window._requestAnimationFrame((num time) {
AnimationFrameTask.removeMapping(task.id);
zone.runTask(_runAnimationFrame, task, time);
});
var callback = zone.registerUnaryCallback(spec.callback);
task = new _AnimationFrameTask(id, zone, callback);
return task;
}
static void _runAnimationFrame(_AnimationFrameTask task, num time) {
task._callback(time);
return _requestAnimationFrame(_wrapZone/*<num, dynamic>*/(callback));
}
/**
@ -242,13 +128,7 @@ $if DART2JS
*/
void cancelAnimationFrame(int id) {
_ensureRequestAnimationFrame();
var task = AnimationFrameTask._tasks.remove(id);
if (task == null) {
// Assume that the animation frame request wasn't intercepted by a zone.
_cancelAnimationFrame(id);
return;
}
task.cancel(this);
_cancelAnimationFrame(id);
}
@JSName('requestAnimationFrame')

View file

@ -4,109 +4,6 @@
part of $LIBRARYNAME;
/**
* A task specification for HTTP requests.
*
* This specification is not available when an HTTP request is sent through
* direct use of [HttpRequest.send]. See [HttpRequestSendTaskSpecification].
*
* A task created from this specification is a `Future<HttpRequest>`.
*
* *Experimental*. This class may disappear without notice.
*/
class HttpRequestTaskSpecification extends TaskSpecification {
/// The URL of the request.
final String url;
/// The HTTP request method.
///
/// By default (when `null`) this is a `"GET"` request. Alternatively, the
/// method can be `"POST"`, `"PUT"`, `"DELETE"`, etc.
final String method;
/// Whether the request should send credentials. Credentials are only useful
/// for cross-origin requests.
///
/// See [HttpRequest.request] for more information.
final bool withCredentials;
/// The desired response format.
///
/// Supported types are:
/// - `""`: (same as `"text"`),
/// - `"arraybuffer"`,
/// - `"blob"`,
/// - `"document"`,
/// - `"json"`,
/// - `"text"`
///
/// When no value is provided (when equal to `null`) defaults to `""`.
final String responseType;
/// The desired MIME type.
///
/// This overrides the default MIME type which is set up to transfer textual
/// data.
final String mimeType;
/// The request headers that should be sent with the request.
final Map<String, String> requestHeaders;
/// The data that is sent with the request.
///
/// When data is provided (the value is not `null`), it must be a
/// [ByteBuffer], [Blob], [Document], [String], or [FormData].
final dynamic sendData;
/// The function that is invoked on progress updates. This function is
/// registered as an event listener on the created [HttpRequest] object, and
/// thus has its own task. Further invocations of the progress function do
/// *not* use the HTTP request task as task object.
///
/// Creating an HTTP request automatically registers the on-progress listener.
final ZoneUnaryCallback<dynamic, ProgressEvent> onProgress;
HttpRequestTaskSpecification(this.url,
{String this.method, bool this.withCredentials, String this.responseType,
String this.mimeType, Map<String, String> this.requestHeaders,
this.sendData,
void this.onProgress(ProgressEvent e)});
String get name => "dart.html.http-request";
bool get isOneShot => true;
}
/**
* A task specification for HTTP requests that are initiated through a direct
* invocation of [HttpRequest.send].
*
* This specification serves as signal to zones that an HTTP request has been
* initiated. The created task is the [request] object itself, and
* no callback is ever executed in this task.
*
* Note that event listeners on the HTTP request are also registered in the
* zone (although with their own task creations), and that a zone can thus
* detect when the HTTP request returns.
*
* HTTP requests that are initiated through `request` methods don't use
* this class but use [HttpRequestTaskSpecification].
*
* *Experimental*. This class may disappear without notice.
*/
class HttpRequestSendTaskSpecification extends TaskSpecification {
final HttpRequest request;
final dynamic sendData;
HttpRequestSendTaskSpecification(this.request, this.sendData);
String get name => "dart.html.http-request-send";
/**
* No callback is ever executed in an HTTP request send task.
*/
bool get isOneShot => false;
}
/**
* A client-side XHR request for getting data from a URL,
* formally known as XMLHttpRequest.
@ -293,34 +190,7 @@ $(ANNOTATIONS)$(NATIVESPEC)$(CLASS_MODIFIERS)class $CLASSNAME$EXTENDS$IMPLEMENTS
{String method, bool withCredentials, String responseType,
String mimeType, Map<String, String> requestHeaders, sendData,
void onProgress(ProgressEvent e)}) {
var spec = new HttpRequestTaskSpecification(
url, method: method,
withCredentials: withCredentials,
responseType: responseType,
mimeType: mimeType,
requestHeaders: requestHeaders,
sendData: sendData,
onProgress: onProgress);
if (identical(Zone.current, Zone.ROOT)) {
return _createHttpRequestTask(spec, null);
}
return Zone.current.createTask(_createHttpRequestTask, spec);
}
static Future<HttpRequest> _createHttpRequestTask(
HttpRequestTaskSpecification spec, Zone zone) {
String url = spec.url;
String method = spec.method;
bool withCredentials = spec.withCredentials;
String responseType = spec.responseType;
String mimeType = spec.mimeType;
Map<String, String> requestHeaders = spec.requestHeaders;
var sendData = spec.sendData;
var onProgress = spec.onProgress;
var completer = new Completer<HttpRequest>();
var task = completer.future;
var xhr = new HttpRequest();
if (method == null) {
@ -360,42 +230,23 @@ $(ANNOTATIONS)$(NATIVESPEC)$(CLASS_MODIFIERS)class $CLASSNAME$EXTENDS$IMPLEMENTS
// redirect case will be handled by the browser before it gets to us,
// so if we see it we should pass it through to the user.
var unknownRedirect = xhr.status > 307 && xhr.status < 400;
var isSuccessful = accepted || fileUri || notModified || unknownRedirect;
if (zone == null && isSuccessful) {
if (accepted || fileUri || notModified || unknownRedirect) {
completer.complete(xhr);
} else if (zone == null) {
completer.completeError(e);
} else if (isSuccessful) {
zone.runTask((task, value) {
completer.complete(value);
}, task, xhr);
} else {
zone.runTask((task, error) {
completer.completeError(error);
}, task, e);
completer.completeError(e);
}
});
if (zone == null) {
xhr.onError.listen(completer.completeError);
} else {
xhr.onError.listen((error) {
zone.runTask((task, error) {
completer.completeError(error);
}, task, error);
});
}
xhr.onError.listen(completer.completeError);
if (sendData != null) {
// TODO(floitsch): should we go through 'send()' and have nested tasks?
xhr._send(sendData);
xhr.send(sendData);
} else {
xhr._send();
xhr.send();
}
return task;
return completer.future;
}
/**
@ -465,9 +316,6 @@ $endif
return xhr.responseText;
});
}
// TODO(floitsch): the following code doesn't go through task zones.
// Since 'XDomainRequest' is an IE9 feature we should probably just remove
// it.
$if DART2JS
var completer = new Completer<String>();
if (method == null) {
@ -548,7 +396,7 @@ $endif
*
* Note: Most simple HTTP requests can be accomplished using the [getString],
* [request], [requestCrossOrigin], or [postFormData] methods. Use of this
* `open` method is intended only for more complex HTTP requests where
* `open` method is intended only for more complext HTTP requests where
* finer-grained control is needed.
*/
@DomName('XMLHttpRequest.open')
@ -565,35 +413,5 @@ $else
void open(String method, String url, {bool async, String user, String password}) native;
$endif
/**
* Sends the request with any given `data`.
*
* Note: Most simple HTTP requests can be accomplished using the [getString],
* [request], [requestCrossOrigin], or [postFormData] methods. Use of this
* `send` method is intended only for more complex HTTP requests where
* finer-grained control is needed.
*
* ## Other resources
*
* * [XMLHttpRequest.send](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#send%28%29)
* from MDN.
*/
@DomName('XMLHttpRequest.send')
@DocsEditable()
void send([body_OR_data]) {
if (identical(Zone.current, Zone.ROOT)) {
_send(body_OR_data);
} else {
Zone.current.createTask(_createHttpRequestSendTask,
new HttpRequestSendTaskSpecification(this, body_OR_data));
}
}
static HttpRequest _createHttpRequestSendTask(
HttpRequestSendTaskSpecification spec, Zone zone) {
spec.request._send(spec.sendData);
return spec.request;
}
$!MEMBERS
}