parent
3b9c08676a
commit
f3da37c92f
|
@ -215,14 +215,15 @@ class NgZone {
|
|||
_inVmTurnDone = true;
|
||||
parent.run(_innerZone, _onTurnDone);
|
||||
|
||||
if (_pendingMicrotasks == 0 && _onEventDone != null) {
|
||||
runOutsideAngular(_onEventDone);
|
||||
}
|
||||
} finally {
|
||||
_inVmTurnDone = false;
|
||||
_hasExecutedCodeInInnerZone = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_pendingMicrotasks == 0 && _onEventDone != null) {
|
||||
runOutsideAngular(_onEventDone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ export class NgZone {
|
|||
*
|
||||
* This hook is useful for validating application state (e.g. in a test).
|
||||
*/
|
||||
overrideOnEventDone(onEventDoneFn: Function, opt_waitForAsync: boolean): void {
|
||||
overrideOnEventDone(onEventDoneFn: Function, opt_waitForAsync: boolean = false): void {
|
||||
var normalizedOnEventDone = normalizeBlank(onEventDoneFn);
|
||||
if (opt_waitForAsync) {
|
||||
this._onEventDone = () => {
|
||||
|
@ -212,14 +212,15 @@ export class NgZone {
|
|||
try {
|
||||
this._inVmTurnDone = true;
|
||||
parentRun.call(ngZone._innerZone, ngZone._onTurnDone);
|
||||
if (ngZone._pendingMicrotasks === 0 && isPresent(ngZone._onEventDone)) {
|
||||
ngZone.runOutsideAngular(ngZone._onEventDone);
|
||||
}
|
||||
} finally {
|
||||
this._inVmTurnDone = false;
|
||||
ngZone._hasExecutedCodeInInnerZone = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ngZone._pendingMicrotasks === 0 && isPresent(ngZone._onEventDone)) {
|
||||
ngZone.runOutsideAngular(ngZone._onEventDone);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
|
||||
export class MockNgZone extends NgZone {
|
||||
_onEventDone: () => void;
|
||||
|
||||
constructor() { super({enableLongStackTrace: false}); }
|
||||
|
||||
run(fn: Function): any { return fn(); }
|
||||
|
||||
runOutsideAngular(fn: Function): any { return fn(); }
|
||||
|
||||
overrideOnEventDone(fn: () => void, opt_waitForAsync: boolean = false): void {
|
||||
this._onEventDone = fn;
|
||||
}
|
||||
|
||||
simulateZoneExit(): void { this._onEventDone(); }
|
||||
}
|
||||
|
|
|
@ -1,25 +1,29 @@
|
|||
library angular2.src.web_workers.debug_tools.multi_client_server_message_bus;
|
||||
|
||||
import "package:angular2/src/web_workers/shared/message_bus.dart"
|
||||
show MessageBus, MessageBusSink, MessageBusSource;
|
||||
import 'dart:io';
|
||||
import 'dart:convert' show JSON;
|
||||
import 'dart:async';
|
||||
import 'package:angular2/src/core/facade/async.dart' show EventEmitter;
|
||||
import 'package:angular2/src/web_workers/shared/messaging_api.dart';
|
||||
import 'package:angular2/src/web_workers/shared/generic_message_bus.dart';
|
||||
|
||||
// TODO(jteplitz602): Remove hard coded result type and
|
||||
// clear messageHistory once app is done with it #3859
|
||||
class MultiClientServerMessageBus implements MessageBus {
|
||||
final MultiClientServerMessageBusSink sink;
|
||||
MultiClientServerMessageBusSource source;
|
||||
class MultiClientServerMessageBus extends GenericMessageBus {
|
||||
bool hasPrimary = false;
|
||||
|
||||
MultiClientServerMessageBus(this.sink, this.source);
|
||||
@override
|
||||
MultiClientServerMessageBusSink get sink => super.sink;
|
||||
@override
|
||||
MultiClientServerMessageBusSource get source => super.source;
|
||||
|
||||
MultiClientServerMessageBus(MultiClientServerMessageBusSink sink,
|
||||
MultiClientServerMessageBusSource source)
|
||||
: super(sink, source);
|
||||
|
||||
MultiClientServerMessageBus.fromHttpServer(HttpServer server)
|
||||
: sink = new MultiClientServerMessageBusSink() {
|
||||
source = new MultiClientServerMessageBusSource(resultReceived);
|
||||
: super(new MultiClientServerMessageBusSink(),
|
||||
new MultiClientServerMessageBusSource()) {
|
||||
source.onResult.listen(_resultReceived);
|
||||
server.listen((HttpRequest request) {
|
||||
if (request.uri.path == "/ws") {
|
||||
WebSocketTransformer.upgrade(request).then((WebSocket socket) {
|
||||
|
@ -38,18 +42,10 @@ class MultiClientServerMessageBus implements MessageBus {
|
|||
});
|
||||
}
|
||||
|
||||
void resultReceived() {
|
||||
void _resultReceived(_) {
|
||||
sink.resultReceived();
|
||||
}
|
||||
|
||||
EventEmitter from(String channel) {
|
||||
return source.from(channel);
|
||||
}
|
||||
|
||||
EventEmitter to(String channel) {
|
||||
return sink.to(channel);
|
||||
}
|
||||
|
||||
Function _handleDisconnect(WebSocketWrapper wrapper) {
|
||||
return () {
|
||||
sink.removeConnection(wrapper);
|
||||
|
@ -72,12 +68,15 @@ class WebSocketWrapper {
|
|||
WebSocketWrapper(this._messageHistory, this._resultMarkers, this.socket) {
|
||||
stream = socket.asBroadcastStream();
|
||||
stream.listen((encodedMessage) {
|
||||
var message = JSON.decode(encodedMessage)['message'];
|
||||
if (message is Map && message.containsKey("type")) {
|
||||
if (message['type'] == 'result') {
|
||||
resultReceived();
|
||||
var messages = JSON.decode(encodedMessage);
|
||||
messages.forEach((data) {
|
||||
var message = data['message'];
|
||||
if (message is Map && message.containsKey("type")) {
|
||||
if (message['type'] == 'result') {
|
||||
resultReceived();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -121,10 +120,9 @@ class WebSocketWrapper {
|
|||
}
|
||||
}
|
||||
|
||||
class MultiClientServerMessageBusSink implements MessageBusSink {
|
||||
class MultiClientServerMessageBusSink extends GenericMessageBusSink {
|
||||
final List<String> messageHistory = new List<String>();
|
||||
final Set<WebSocketWrapper> openConnections = new Set<WebSocketWrapper>();
|
||||
final Map<String, EventEmitter> _channels = new Map<String, EventEmitter>();
|
||||
final List<int> resultMarkers = new List<int>();
|
||||
|
||||
void resultReceived() {
|
||||
|
@ -141,76 +139,77 @@ class MultiClientServerMessageBusSink implements MessageBusSink {
|
|||
openConnections.remove(webSocket);
|
||||
}
|
||||
|
||||
EventEmitter to(String channel) {
|
||||
if (_channels.containsKey(channel)) {
|
||||
return _channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
emitter.listen((message) {
|
||||
_send({'channel': channel, 'message': message});
|
||||
});
|
||||
return emitter;
|
||||
}
|
||||
}
|
||||
|
||||
void _send(dynamic message) {
|
||||
String encodedMessage = JSON.encode(message);
|
||||
@override
|
||||
void sendMessages(List<dynamic> messages) {
|
||||
String encodedMessages = JSON.encode(messages);
|
||||
openConnections.forEach((WebSocketWrapper webSocket) {
|
||||
if (webSocket.caughtUp) {
|
||||
webSocket.socket.add(encodedMessage);
|
||||
webSocket.socket.add(encodedMessages);
|
||||
}
|
||||
});
|
||||
messageHistory.add(encodedMessage);
|
||||
messageHistory.add(encodedMessages);
|
||||
}
|
||||
}
|
||||
|
||||
class MultiClientServerMessageBusSource implements MessageBusSource {
|
||||
final Map<String, EventEmitter> _channels = new Map<String, EventEmitter>();
|
||||
class MultiClientServerMessageBusSource extends GenericMessageBusSource {
|
||||
Function onResultReceived;
|
||||
final StreamController mainController;
|
||||
final StreamController resultController = new StreamController();
|
||||
|
||||
MultiClientServerMessageBusSource(this.onResultReceived);
|
||||
MultiClientServerMessageBusSource._(controller)
|
||||
: mainController = controller,
|
||||
super(controller.stream);
|
||||
|
||||
EventEmitter from(String channel) {
|
||||
if (_channels.containsKey(channel)) {
|
||||
return _channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
_channels[channel] = emitter;
|
||||
return emitter;
|
||||
}
|
||||
factory MultiClientServerMessageBusSource() {
|
||||
return new MultiClientServerMessageBusSource._(
|
||||
new StreamController.broadcast());
|
||||
}
|
||||
|
||||
Stream get onResult => resultController.stream;
|
||||
|
||||
void addConnection(WebSocketWrapper webSocket) {
|
||||
if (webSocket.isPrimary) {
|
||||
webSocket.stream.listen((encodedMessage) {
|
||||
var decodedMessage = decodeMessage(encodedMessage);
|
||||
var channel = decodedMessage['channel'];
|
||||
var message = decodedMessage['message'];
|
||||
if (message is Map && message.containsKey("type")) {
|
||||
if (message['type'] == 'result') {
|
||||
// tell the bus that a result was received on the primary
|
||||
onResultReceived();
|
||||
webSocket.stream.listen((encodedMessages) {
|
||||
var decodedMessages = _decodeMessages(encodedMessages);
|
||||
decodedMessages.forEach((decodedMessage) {
|
||||
var message = decodedMessage['message'];
|
||||
if (message is Map && message.containsKey("type")) {
|
||||
if (message['type'] == 'result') {
|
||||
// tell the bus that a result was received on the primary
|
||||
resultController.add(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (_channels.containsKey(channel)) {
|
||||
_channels[channel].add(message);
|
||||
}
|
||||
mainController.add(decodedMessages);
|
||||
});
|
||||
} else {
|
||||
webSocket.stream.listen((encodedMessage) {
|
||||
// handle events from non-primary browser
|
||||
var decodedMessage = decodeMessage(encodedMessage);
|
||||
var channel = decodedMessage['channel'];
|
||||
var message = decodedMessage['message'];
|
||||
if (_channels.containsKey(EVENT_CHANNEL) && channel == EVENT_CHANNEL) {
|
||||
_channels[channel].add(message);
|
||||
webSocket.stream.listen((encodedMessages) {
|
||||
// handle events from non-primary connection.
|
||||
var decodedMessages = _decodeMessages(encodedMessages);
|
||||
var eventMessages = new List<Map<String, dynamic>>();
|
||||
decodedMessages.forEach((decodedMessage) {
|
||||
var channel = decodedMessage['channel'];
|
||||
if (channel == EVENT_CHANNEL) {
|
||||
eventMessages.add(decodedMessage);
|
||||
}
|
||||
});
|
||||
if (eventMessages.length > 0) {
|
||||
mainController.add(eventMessages);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> decodeMessage(dynamic message) {
|
||||
return JSON.decode(message);
|
||||
List<dynamic> _decodeMessages(dynamic messages) {
|
||||
return JSON.decode(messages);
|
||||
}
|
||||
|
||||
// This is a noop for the MultiClientBus because it has to decode the JSON messages before
|
||||
// the generic bus receives them in order to check for results and forward events
|
||||
// from the non-primary connection.
|
||||
@override
|
||||
List<dynamic> decodeMessages(dynamic messages) {
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
library angular2.src.web_workers.debug_tools.single_client_server_message_bus;
|
||||
|
||||
import "package:angular2/src/web_workers/shared/message_bus.dart"
|
||||
show MessageBus, MessageBusSink, MessageBusSource;
|
||||
import 'dart:io';
|
||||
import 'dart:convert' show JSON;
|
||||
import 'dart:async';
|
||||
import "package:angular2/src/core/facade/async.dart" show EventEmitter;
|
||||
import 'package:angular2/src/web_workers/shared/generic_message_bus.dart';
|
||||
|
||||
class SingleClientServerMessageBus implements MessageBus {
|
||||
final SingleClientServerMessageBusSink sink;
|
||||
SingleClientServerMessageBusSource source;
|
||||
class SingleClientServerMessageBus extends GenericMessageBus {
|
||||
bool connected = false;
|
||||
|
||||
SingleClientServerMessageBus(this.sink, this.source);
|
||||
@override
|
||||
SingleClientServerMessageBusSink get sink => super.sink;
|
||||
@override
|
||||
SingleClientServerMessageBusSource get source => super.source;
|
||||
|
||||
SingleClientServerMessageBus(SingleClientServerMessageBusSink sink,
|
||||
SingleClientServerMessageBusSource source)
|
||||
: super(sink, source);
|
||||
|
||||
SingleClientServerMessageBus.fromHttpServer(HttpServer server)
|
||||
: sink = new SingleClientServerMessageBusSink() {
|
||||
source = new SingleClientServerMessageBusSource();
|
||||
: super(new SingleClientServerMessageBusSink(),
|
||||
new SingleClientServerMessageBusSource()) {
|
||||
server.listen((HttpRequest request) {
|
||||
if (request.uri.path == "/ws") {
|
||||
if (!connected) {
|
||||
|
@ -24,7 +26,7 @@ class SingleClientServerMessageBus implements MessageBus {
|
|||
sink.setConnection(socket);
|
||||
|
||||
var stream = socket.asBroadcastStream();
|
||||
source.setConnectionFromStream(stream);
|
||||
source.attachTo(stream);
|
||||
stream.listen(null, onDone: _handleDisconnect);
|
||||
}).catchError((error) {
|
||||
throw error;
|
||||
|
@ -43,51 +45,30 @@ class SingleClientServerMessageBus implements MessageBus {
|
|||
|
||||
void _handleDisconnect() {
|
||||
sink.removeConnection();
|
||||
source.removeConnection();
|
||||
connected = false;
|
||||
}
|
||||
|
||||
EventEmitter from(String channel) {
|
||||
return source.from(channel);
|
||||
}
|
||||
|
||||
EventEmitter to(String channel) {
|
||||
return sink.to(channel);
|
||||
}
|
||||
}
|
||||
|
||||
class SingleClientServerMessageBusSink implements MessageBusSink {
|
||||
class SingleClientServerMessageBusSink extends GenericMessageBusSink {
|
||||
final List<String> _messageBuffer = new List<String>();
|
||||
WebSocket _socket = null;
|
||||
final Map<String, EventEmitter> _channels = new Map<String, EventEmitter>();
|
||||
|
||||
void setConnection(WebSocket webSocket) {
|
||||
_socket = webSocket;
|
||||
_sendBufferedMessages();
|
||||
}
|
||||
|
||||
EventEmitter to(String channel) {
|
||||
if (_channels.containsKey(channel)) {
|
||||
return _channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
emitter.listen((message) {
|
||||
_send({'channel': channel, 'message': message});
|
||||
});
|
||||
return emitter;
|
||||
}
|
||||
}
|
||||
|
||||
void removeConnection() {
|
||||
_socket = null;
|
||||
}
|
||||
|
||||
void _send(dynamic message) {
|
||||
String encodedMessage = JSON.encode(message);
|
||||
@override
|
||||
void sendMessages(List<dynamic> message) {
|
||||
String encodedMessages = JSON.encode(message);
|
||||
if (_socket != null) {
|
||||
_socket.add(encodedMessage);
|
||||
_socket.add(encodedMessages);
|
||||
} else {
|
||||
_messageBuffer.add(encodedMessage);
|
||||
_messageBuffer.add(encodedMessages);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,44 +78,11 @@ class SingleClientServerMessageBusSink implements MessageBusSink {
|
|||
}
|
||||
}
|
||||
|
||||
class SingleClientServerMessageBusSource implements MessageBusSource {
|
||||
final Map<String, EventEmitter> _channels = new Map<String, EventEmitter>();
|
||||
Stream _stream;
|
||||
class SingleClientServerMessageBusSource extends GenericMessageBusSource {
|
||||
SingleClientServerMessageBusSource() : super(null);
|
||||
|
||||
SingleClientServerMessageBusSource();
|
||||
|
||||
EventEmitter from(String channel) {
|
||||
if (_channels.containsKey(channel)) {
|
||||
return _channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
_channels[channel] = emitter;
|
||||
return emitter;
|
||||
}
|
||||
}
|
||||
|
||||
void setConnectionFromWebSocket(WebSocket socket) {
|
||||
setConnectionFromStream(socket.asBroadcastStream());
|
||||
}
|
||||
|
||||
void setConnectionFromStream(Stream stream) {
|
||||
_stream = stream;
|
||||
_stream.listen((encodedMessage) {
|
||||
var decodedMessage = decodeMessage(encodedMessage);
|
||||
var channel = decodedMessage['channel'];
|
||||
var message = decodedMessage['message'];
|
||||
|
||||
if (_channels.containsKey(channel)) {
|
||||
_channels[channel].add(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void removeConnection() {
|
||||
_stream = null;
|
||||
}
|
||||
|
||||
Map<String, dynamic> decodeMessage(dynamic message) {
|
||||
return JSON.decode(message);
|
||||
@override
|
||||
List<dynamic> decodeMessages(dynamic messages) {
|
||||
return JSON.decode(messages);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,77 +2,33 @@ library angular2.src.web_workers.worker.web_socket_message_bus;
|
|||
|
||||
import 'dart:html';
|
||||
import 'dart:convert' show JSON;
|
||||
import "package:angular2/src/web_workers/shared/message_bus.dart"
|
||||
show MessageBus, MessageBusSink, MessageBusSource;
|
||||
import 'package:angular2/src/core/facade/async.dart' show EventEmitter;
|
||||
import 'package:angular2/src/web_workers/shared/generic_message_bus.dart';
|
||||
|
||||
class WebSocketMessageBus implements MessageBus {
|
||||
final WebSocketMessageBusSink sink;
|
||||
final WebSocketMessageBusSource source;
|
||||
|
||||
WebSocketMessageBus(this.sink, this.source);
|
||||
class WebSocketMessageBus extends GenericMessageBus {
|
||||
WebSocketMessageBus(
|
||||
WebSocketMessageBusSink sink, WebSocketMessageBusSource source)
|
||||
: super(sink, source);
|
||||
|
||||
WebSocketMessageBus.fromWebSocket(WebSocket webSocket)
|
||||
: sink = new WebSocketMessageBusSink(webSocket),
|
||||
source = new WebSocketMessageBusSource(webSocket);
|
||||
|
||||
EventEmitter from(String channel) {
|
||||
return source.from(channel);
|
||||
}
|
||||
|
||||
EventEmitter to(String channel) {
|
||||
return sink.to(channel);
|
||||
}
|
||||
: super(new WebSocketMessageBusSink(webSocket),
|
||||
new WebSocketMessageBusSource(webSocket));
|
||||
}
|
||||
|
||||
class WebSocketMessageBusSink implements MessageBusSink {
|
||||
class WebSocketMessageBusSink extends GenericMessageBusSink {
|
||||
final WebSocket _webSocket;
|
||||
final Map<String, EventEmitter> _channels = new Map<String, EventEmitter>();
|
||||
|
||||
WebSocketMessageBusSink(this._webSocket);
|
||||
|
||||
EventEmitter to(String channel) {
|
||||
if (_channels.containsKey(channel)) {
|
||||
return _channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
emitter.listen((message) {
|
||||
_send({'channel': channel, 'message': message});
|
||||
});
|
||||
_channels[channel] = emitter;
|
||||
return emitter;
|
||||
}
|
||||
}
|
||||
|
||||
void _send(message) {
|
||||
_webSocket.send(JSON.encode(message));
|
||||
void sendMessages(List<dynamic> messages) {
|
||||
_webSocket.send(JSON.encode(messages));
|
||||
}
|
||||
}
|
||||
|
||||
class WebSocketMessageBusSource implements MessageBusSource {
|
||||
final Map<String, EventEmitter> _channels = new Map<String, EventEmitter>();
|
||||
class WebSocketMessageBusSource extends GenericMessageBusSource {
|
||||
WebSocketMessageBusSource(WebSocket webSocket) : super(webSocket.onMessage);
|
||||
|
||||
WebSocketMessageBusSource(WebSocket webSocket) {
|
||||
webSocket.onMessage.listen((MessageEvent encodedMessage) {
|
||||
var message = decodeMessage(encodedMessage.data);
|
||||
var channel = message['channel'];
|
||||
if (_channels.containsKey(channel)) {
|
||||
_channels[channel].add(message['message']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
EventEmitter from(String channel) {
|
||||
if (_channels.containsKey(channel)) {
|
||||
return _channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
_channels[channel] = emitter;
|
||||
return emitter;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> decodeMessage(dynamic message) {
|
||||
return JSON.decode(message);
|
||||
List<dynamic> decodeMessages(MessageEvent event) {
|
||||
var messages = event.data;
|
||||
return JSON.decode(messages);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,11 @@ export {Type} from "angular2/src/core/facade/lang";
|
|||
export class ClientMessageBrokerFactory {
|
||||
constructor(private _messageBus: MessageBus, protected _serializer: Serializer) {}
|
||||
|
||||
createMessageBroker(channel: string): ClientMessageBroker {
|
||||
/**
|
||||
* Initializes the given channel and attaches a new {@link ClientMessageBroker} to it.
|
||||
*/
|
||||
createMessageBroker(channel: string, runInZone: boolean = true): ClientMessageBroker {
|
||||
this._messageBus.initChannel(channel, runInZone);
|
||||
return new ClientMessageBroker(this._messageBus, this._serializer, channel);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
library angular2.src.web_workers.shared.generic_message_bus;
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:angular2/src/core/facade/async.dart' show EventEmitter;
|
||||
import 'package:angular2/src/web_workers/shared/message_bus.dart'
|
||||
show MessageBus, MessageBusSink, MessageBusSource;
|
||||
import 'package:angular2/src/core/zone/ng_zone.dart';
|
||||
import 'package:angular2/src/core/facade/lang.dart';
|
||||
|
||||
class GenericMessageBus implements MessageBus {
|
||||
final MessageBusSink _sink;
|
||||
final MessageBusSource _source;
|
||||
|
||||
MessageBusSink get sink => _sink;
|
||||
MessageBusSource get source => _source;
|
||||
|
||||
GenericMessageBus(MessageBusSink sink, MessageBusSource source)
|
||||
: _sink = sink,
|
||||
_source = source;
|
||||
|
||||
void attachToZone(NgZone zone) {
|
||||
sink.attachToZone(zone);
|
||||
source.attachToZone(zone);
|
||||
}
|
||||
|
||||
void initChannel(String channel, [bool runInZone = true]) {
|
||||
sink.initChannel(channel, runInZone);
|
||||
source.initChannel(channel, runInZone);
|
||||
}
|
||||
|
||||
EventEmitter from(String channel) {
|
||||
return source.from(channel);
|
||||
}
|
||||
|
||||
EventEmitter to(String channel) {
|
||||
return sink.to(channel);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class GenericMessageBusSink implements MessageBusSink {
|
||||
NgZone _zone;
|
||||
final _channels = new Map<String, _Channel>();
|
||||
final _messageBuffer = new List<dynamic>();
|
||||
|
||||
void attachToZone(NgZone zone) {
|
||||
_zone = zone;
|
||||
_zone.overrideOnEventDone(() {
|
||||
sendMessages(_messageBuffer);
|
||||
_messageBuffer.clear();
|
||||
}, false);
|
||||
}
|
||||
|
||||
void initChannel(String channelName, [bool runInZone = true]) {
|
||||
if (_channels.containsKey(channelName)) {
|
||||
throw new BaseException("${channelName} has already been initialized.");
|
||||
}
|
||||
|
||||
var emitter = new EventEmitter();
|
||||
var channel = new _Channel(emitter, runInZone);
|
||||
|
||||
emitter.listen((data) {
|
||||
var message = {'channel': channelName, 'message': data};
|
||||
if (runInZone) {
|
||||
_messageBuffer.add(message);
|
||||
} else {
|
||||
sendMessages([message]);
|
||||
}
|
||||
});
|
||||
|
||||
_channels[channelName] = channel;
|
||||
}
|
||||
|
||||
EventEmitter to(String channelName) {
|
||||
if (_channels.containsKey(channelName)) {
|
||||
return _channels[channelName].emitter;
|
||||
} else {
|
||||
throw new BaseException(
|
||||
"${channelName} is not set up. Did you forget to call initChannel?");
|
||||
}
|
||||
}
|
||||
|
||||
void sendMessages(List<dynamic> messages);
|
||||
}
|
||||
|
||||
abstract class GenericMessageBusSource implements MessageBusSource {
|
||||
Stream _stream;
|
||||
final _channels = new Map<String, _Channel>();
|
||||
NgZone _zone;
|
||||
|
||||
Stream get stream => _stream;
|
||||
|
||||
GenericMessageBusSource(Stream stream) {
|
||||
attachTo(stream);
|
||||
}
|
||||
|
||||
void attachTo(Stream stream) {
|
||||
_stream = stream;
|
||||
if (stream != null) {
|
||||
stream.listen((messages) {
|
||||
List<dynamic> decodedMessages = decodeMessages(messages);
|
||||
if (decodedMessages != null) {
|
||||
decodedMessages.forEach((message) => _handleMessage(message));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void attachToZone(NgZone zone) {
|
||||
_zone = zone;
|
||||
}
|
||||
|
||||
void initChannel(String channelName, [bool runInZone = true]) {
|
||||
if (_channels.containsKey(channelName)) {
|
||||
throw new BaseException("${channelName} has already been initialized.");
|
||||
}
|
||||
|
||||
var emitter = new EventEmitter();
|
||||
var channelInfo = new _Channel(emitter, runInZone);
|
||||
_channels[channelName] = channelInfo;
|
||||
}
|
||||
|
||||
EventEmitter from(String channelName) {
|
||||
if (_channels.containsKey(channelName)) {
|
||||
return _channels[channelName].emitter;
|
||||
} else {
|
||||
throw new BaseException(
|
||||
"${channelName} is not set up. Did you forget to call initChannel?");
|
||||
}
|
||||
}
|
||||
|
||||
void _handleMessage(dynamic data) {
|
||||
var channelName = data['channel'];
|
||||
if (_channels.containsKey(channelName)) {
|
||||
var channelInfo = _channels[channelName];
|
||||
if (channelInfo.runInZone) {
|
||||
_zone.run(() => channelInfo.emitter.add(data['message']));
|
||||
} else {
|
||||
channelInfo.emitter.add(data['message']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<dynamic> decodeMessages(dynamic message);
|
||||
}
|
||||
|
||||
class _Channel {
|
||||
EventEmitter emitter;
|
||||
bool runInZone;
|
||||
|
||||
_Channel(this.emitter, this.runInZone);
|
||||
}
|
|
@ -1,76 +1,33 @@
|
|||
library angular2.src.web_workers.shared.isolate_message_bus;
|
||||
|
||||
import 'dart:isolate';
|
||||
import 'dart:async';
|
||||
import 'dart:core';
|
||||
import 'package:angular2/src/web_workers/shared/message_bus.dart'
|
||||
show MessageBus, MessageBusSink, MessageBusSource;
|
||||
import 'package:angular2/src/core/facade/async.dart';
|
||||
|
||||
class IsolateMessageBus implements MessageBus {
|
||||
final IsolateMessageBusSink sink;
|
||||
final IsolateMessageBusSource source;
|
||||
import 'package:angular2/src/web_workers/shared/generic_message_bus.dart';
|
||||
|
||||
class IsolateMessageBus extends GenericMessageBus {
|
||||
IsolateMessageBus(IsolateMessageBusSink sink, IsolateMessageBusSource source)
|
||||
: sink = sink,
|
||||
source = source;
|
||||
|
||||
EventEmitter from(String channel) {
|
||||
return source.from(channel);
|
||||
}
|
||||
|
||||
EventEmitter to(String channel) {
|
||||
return sink.to(channel);
|
||||
}
|
||||
: super(sink, source);
|
||||
}
|
||||
|
||||
class IsolateMessageBusSink implements MessageBusSink {
|
||||
class IsolateMessageBusSink extends GenericMessageBusSink {
|
||||
final SendPort _port;
|
||||
final Map<String, EventEmitter> _channels = new Map<String, EventEmitter>();
|
||||
|
||||
IsolateMessageBusSink(SendPort port) : _port = port;
|
||||
|
||||
EventEmitter to(String channel) {
|
||||
if (_channels.containsKey(channel)) {
|
||||
return _channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
emitter.listen((message) {
|
||||
_port.send({'channel': channel, 'message': message});
|
||||
});
|
||||
_channels[channel] = emitter;
|
||||
return emitter;
|
||||
}
|
||||
@override
|
||||
void sendMessages(List<dynamic> messages) {
|
||||
_port.send(messages);
|
||||
}
|
||||
}
|
||||
|
||||
class IsolateMessageBusSource extends MessageBusSource {
|
||||
final Stream rawDataStream;
|
||||
final Map<String, EventEmitter> _channels = new Map<String, EventEmitter>();
|
||||
class IsolateMessageBusSource extends GenericMessageBusSource {
|
||||
IsolateMessageBusSource(ReceivePort port) : super(port.asBroadcastStream());
|
||||
|
||||
IsolateMessageBusSource(ReceivePort port)
|
||||
: rawDataStream = port.asBroadcastStream() {
|
||||
rawDataStream.listen((message) {
|
||||
if (message is SendPort) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.containsKey("channel")) {
|
||||
var channel = message['channel'];
|
||||
if (_channels.containsKey(channel)) {
|
||||
_channels[channel].add(message['message']);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
EventEmitter from(String channel) {
|
||||
if (_channels.containsKey(channel)) {
|
||||
return _channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
_channels[channel] = emitter;
|
||||
return emitter;
|
||||
@override
|
||||
List<dynamic> decodeMessages(dynamic messages) {
|
||||
if (messages is SendPort) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {EventEmitter} from 'angular2/src/core/facade/async';
|
||||
import {BaseException} from 'angular2/src/core/facade/lang';
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
export {EventEmitter, Observable} from 'angular2/src/core/facade/async';
|
||||
|
||||
function _abstract() {
|
||||
|
@ -13,6 +14,23 @@ function _abstract() {
|
|||
* by the corresponding MessageBusSource.
|
||||
*/
|
||||
export /* abstract (with TS 1.6) */ class MessageBus implements MessageBusSource, MessageBusSink {
|
||||
/**
|
||||
* Sets up a new channel on the MessageBus.
|
||||
* MUST be called before calling from or to on the channel.
|
||||
* If runInZone is true then the source will emit events inside the angular zone
|
||||
* and the sink will buffer messages and send only once the zone exits.
|
||||
* if runInZone is false then the source will emit events inside the global zone
|
||||
* and the sink will send messages immediatly.
|
||||
*/
|
||||
initChannel(channel: string, runInZone: boolean = true): void { throw _abstract(); }
|
||||
|
||||
/**
|
||||
* Assigns this bus to the given zone.
|
||||
* Any callbacks attached to channels where runInZone was set to true on initialization
|
||||
* will be executed in the given zone.
|
||||
*/
|
||||
attachToZone(zone: NgZone): void { throw _abstract(); }
|
||||
|
||||
/**
|
||||
* Returns an {@link EventEmitter} that emits every time a messsage
|
||||
* is received on the given channel.
|
||||
|
@ -28,6 +46,21 @@ export /* abstract (with TS 1.6) */ class MessageBus implements MessageBusSource
|
|||
}
|
||||
|
||||
export interface MessageBusSource {
|
||||
/**
|
||||
* Sets up a new channel on the MessageBusSource.
|
||||
* MUST be called before calling from on the channel.
|
||||
* If runInZone is true then the source will emit events inside the angular zone.
|
||||
* if runInZone is false then the source will emit events inside the global zone.
|
||||
*/
|
||||
initChannel(channel: string, runInZone: boolean): void;
|
||||
|
||||
/**
|
||||
* Assigns this source to the given zone.
|
||||
* Any channels which are initialized with runInZone set to true will emit events that will be
|
||||
* executed within the given zone.
|
||||
*/
|
||||
attachToZone(zone: NgZone): void;
|
||||
|
||||
/**
|
||||
* Returns an {@link EventEmitter} that emits every time a messsage
|
||||
* is received on the given channel.
|
||||
|
@ -36,6 +69,21 @@ export interface MessageBusSource {
|
|||
}
|
||||
|
||||
export interface MessageBusSink {
|
||||
/**
|
||||
* Sets up a new channel on the MessageBusSink.
|
||||
* MUST be called before calling to on the channel.
|
||||
* If runInZone is true the sink will buffer messages and send only once the zone exits.
|
||||
* if runInZone is false the sink will send messages immediatly.
|
||||
*/
|
||||
initChannel(channel: string, runInZone: boolean): void;
|
||||
|
||||
/**
|
||||
* Assigns this sink to the given zone.
|
||||
* Any channels which are initilialized with runInZone set to true will wait for the given zone
|
||||
* to exit before sending messages.
|
||||
*/
|
||||
attachToZone(zone: NgZone): void;
|
||||
|
||||
/**
|
||||
* Returns an {@link EventEmitter} for the given channel
|
||||
* To publish methods to that channel just call next (or add in dart) on the returned emitter
|
||||
|
|
|
@ -3,9 +3,11 @@ import {
|
|||
MessageBusSource,
|
||||
MessageBusSink
|
||||
} from "angular2/src/web_workers/shared/message_bus";
|
||||
import {BaseException} from 'angular2/src/core/facade/lang';
|
||||
import {EventEmitter} from 'angular2/src/core/facade/async';
|
||||
import {StringMap, StringMapWrapper} from 'angular2/src/core/facade/collection';
|
||||
import {Injectable} from "angular2/src/core/di";
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
|
||||
/**
|
||||
* A TypeScript implementation of {@link MessageBus} for communicating via JavaScript's
|
||||
|
@ -13,62 +15,131 @@ import {Injectable} from "angular2/src/core/di";
|
|||
*/
|
||||
@Injectable()
|
||||
export class PostMessageBus implements MessageBus {
|
||||
constructor(private _sink: PostMessageBusSink, private _source: PostMessageBusSource) {}
|
||||
constructor(public sink: PostMessageBusSink, public source: PostMessageBusSource) {}
|
||||
|
||||
from(channel: string): EventEmitter { return this._source.from(channel); }
|
||||
attachToZone(zone: NgZone): void {
|
||||
this.source.attachToZone(zone);
|
||||
this.sink.attachToZone(zone);
|
||||
}
|
||||
|
||||
to(channel: string): EventEmitter { return this._sink.to(channel); }
|
||||
initChannel(channel: string, runInZone: boolean = true): void {
|
||||
this.source.initChannel(channel, runInZone);
|
||||
this.sink.initChannel(channel, runInZone);
|
||||
}
|
||||
|
||||
from(channel: string): EventEmitter { return this.source.from(channel); }
|
||||
|
||||
to(channel: string): EventEmitter { return this.sink.to(channel); }
|
||||
}
|
||||
|
||||
export class PostMessageBusSink implements MessageBusSink {
|
||||
private _channels: StringMap<string, EventEmitter> = StringMapWrapper.create();
|
||||
private _zone: NgZone;
|
||||
private _channels: StringMap<string, _Channel> = StringMapWrapper.create();
|
||||
private _messageBuffer: Array<Object> = [];
|
||||
|
||||
constructor(private _postMessageTarget: PostMessageTarget) {}
|
||||
|
||||
public to(channel: string): EventEmitter {
|
||||
attachToZone(zone: NgZone): void {
|
||||
this._zone = zone;
|
||||
this._zone.overrideOnEventDone(() => this._handleOnEventDone(), false);
|
||||
}
|
||||
|
||||
initChannel(channel: string, runInZone: boolean = true): void {
|
||||
if (StringMapWrapper.contains(this._channels, channel)) {
|
||||
return this._channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
emitter.observer({
|
||||
next: (message: Object) => {
|
||||
this._postMessageTarget.postMessage({channel: channel, message: message});
|
||||
throw new BaseException(`${channel} has already been initialized`);
|
||||
}
|
||||
|
||||
var emitter = new EventEmitter();
|
||||
var channelInfo = new _Channel(emitter, runInZone);
|
||||
this._channels[channel] = channelInfo;
|
||||
emitter.observer({
|
||||
next: (data: Object) => {
|
||||
var message = {channel: channel, message: data};
|
||||
if (runInZone) {
|
||||
this._messageBuffer.push(message);
|
||||
} else {
|
||||
this._sendMessages([message]);
|
||||
}
|
||||
});
|
||||
return emitter;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
to(channel: string): EventEmitter {
|
||||
if (StringMapWrapper.contains(this._channels, channel)) {
|
||||
return this._channels[channel].emitter;
|
||||
} else {
|
||||
throw new BaseException(`${channel} is not set up. Did you forget to call initChannel?`);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleOnEventDone() {
|
||||
// TODO: Send all buffered messages in one postMessage call
|
||||
this._sendMessages(this._messageBuffer);
|
||||
this._messageBuffer = [];
|
||||
}
|
||||
|
||||
private _sendMessages(messages: Array<Object>) { this._postMessageTarget.postMessage(messages); }
|
||||
}
|
||||
|
||||
export class PostMessageBusSource implements MessageBusSource {
|
||||
private _zone: NgZone;
|
||||
private _channels: StringMap<string, _Channel> = StringMapWrapper.create();
|
||||
|
||||
constructor(eventTarget?: EventTarget) {
|
||||
if (eventTarget) {
|
||||
eventTarget.addEventListener("message", (ev: MessageEvent) => this._handleMessages(ev));
|
||||
} else {
|
||||
// if no eventTarget is given we assume we're in a WebWorker and listen on the global scope
|
||||
addEventListener("message", (ev: MessageEvent) => this._handleMessages(ev));
|
||||
}
|
||||
}
|
||||
|
||||
attachToZone(zone: NgZone) { this._zone = zone; }
|
||||
|
||||
initChannel(channel: string, runInZone: boolean = true) {
|
||||
if (StringMapWrapper.contains(this._channels, channel)) {
|
||||
throw new BaseException(`${channel} has already been initialized`);
|
||||
}
|
||||
|
||||
var emitter = new EventEmitter();
|
||||
var channelInfo = new _Channel(emitter, runInZone);
|
||||
this._channels[channel] = channelInfo;
|
||||
}
|
||||
|
||||
from(channel: string): EventEmitter {
|
||||
if (StringMapWrapper.contains(this._channels, channel)) {
|
||||
return this._channels[channel].emitter;
|
||||
} else {
|
||||
throw new BaseException(`${channel} is not set up. Did you forget to call initChannel?`);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleMessages(ev: MessageEvent): void {
|
||||
var messages = ev.data;
|
||||
for (var i = 0; i < messages.length; i++) {
|
||||
this._handleMessage(messages[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleMessage(data: any): void {
|
||||
var channel = data.channel;
|
||||
if (StringMapWrapper.contains(this._channels, channel)) {
|
||||
var channelInfo = this._channels[channel];
|
||||
if (channelInfo.runInZone) {
|
||||
this._zone.run(() => { channelInfo.emitter.next(data.message); });
|
||||
} else {
|
||||
channelInfo.emitter.next(data.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class PostMessageBusSource implements MessageBusSource {
|
||||
private _channels: StringMap<string, EventEmitter> = StringMapWrapper.create();
|
||||
|
||||
constructor(eventTarget?: EventTarget) {
|
||||
if (eventTarget) {
|
||||
eventTarget.addEventListener("message", (ev: MessageEvent) => this._handleMessage(ev));
|
||||
} else {
|
||||
// if no eventTarget is given we assume we're in a WebWorker and listen on the global scope
|
||||
addEventListener("message", (ev: MessageEvent) => this._handleMessage(ev));
|
||||
}
|
||||
}
|
||||
|
||||
private _handleMessage(ev: MessageEvent) {
|
||||
var data = ev.data;
|
||||
var channel = data.channel;
|
||||
if (StringMapWrapper.contains(this._channels, channel)) {
|
||||
this._channels[channel].next(data.message);
|
||||
}
|
||||
}
|
||||
|
||||
public from(channel: string): EventEmitter {
|
||||
if (StringMapWrapper.contains(this._channels, channel)) {
|
||||
return this._channels[channel];
|
||||
} else {
|
||||
var emitter = new EventEmitter();
|
||||
this._channels[channel] = emitter;
|
||||
return emitter;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Helper class that wraps a channel's {@link EventEmitter} and
|
||||
* keeps track of if it should run in the zone.
|
||||
*/
|
||||
class _Channel {
|
||||
constructor(public emitter: EventEmitter, public runInZone: boolean) {}
|
||||
}
|
||||
|
||||
// TODO(jteplitz602) Replace this with the definition in lib.webworker.d.ts(#3492)
|
||||
|
|
|
@ -14,7 +14,11 @@ import {
|
|||
export class ServiceMessageBrokerFactory {
|
||||
constructor(private _messageBus: MessageBus, protected _serializer: Serializer) {}
|
||||
|
||||
createMessageBroker(channel: string): ServiceMessageBroker {
|
||||
/**
|
||||
* Initializes the given channel and attaches a new {@link ServiceMessageBroker} to it.
|
||||
*/
|
||||
createMessageBroker(channel: string, runInZone: boolean = true): ServiceMessageBroker {
|
||||
this._messageBus.initChannel(channel, runInZone);
|
||||
return new ServiceMessageBroker(this._messageBus, this._serializer, channel);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@ import 'dart:async';
|
|||
import 'dart:core';
|
||||
import 'package:angular2/src/web_workers/shared/message_bus.dart'
|
||||
show MessageBus;
|
||||
import 'package:angular2/src/web_workers/ui/impl.dart' show bootstrapUICommon, WebWorkerApplication;
|
||||
import 'package:angular2/src/web_workers/ui/impl.dart'
|
||||
show bootstrapUICommon, WebWorkerApplication;
|
||||
import 'package:angular2/src/web_workers/shared/isolate_message_bus.dart';
|
||||
|
||||
/**
|
||||
|
@ -37,7 +38,7 @@ Future<IsolateInstance> spawnWebWorker(Uri uri) async {
|
|||
class UIMessageBusSource extends IsolateMessageBusSource {
|
||||
UIMessageBusSource(ReceivePort port) : super(port);
|
||||
|
||||
Future<SendPort> get sink => rawDataStream.firstWhere((message) {
|
||||
Future<SendPort> get sink => stream.firstWhere((message) {
|
||||
return message is SendPort;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ export function bootstrapUICommon(bus: MessageBus): WebWorkerApplication {
|
|||
BrowserDomAdapter.makeCurrent();
|
||||
var zone = createNgZone();
|
||||
wtfInit();
|
||||
bus.attachToZone(zone);
|
||||
return zone.run(() => {
|
||||
var injector = createInjector(zone, bus);
|
||||
injector.get(MessageBasedRenderCompiler).start();
|
||||
|
@ -47,11 +48,11 @@ export class WebWorkerApplication {
|
|||
constructor(private _clientMessageBrokerFactory: ClientMessageBrokerFactory,
|
||||
private _serviceMessageBrokerFactory: ServiceMessageBrokerFactory) {}
|
||||
|
||||
createClientMessageBroker(channel: string): ClientMessageBroker {
|
||||
return this._clientMessageBrokerFactory.createMessageBroker(channel);
|
||||
createClientMessageBroker(channel: string, runInZone: boolean = true): ClientMessageBroker {
|
||||
return this._clientMessageBrokerFactory.createMessageBroker(channel, runInZone);
|
||||
}
|
||||
|
||||
createServiceMessageBroker(channel: string): ServiceMessageBroker {
|
||||
return this._serviceMessageBrokerFactory.createMessageBroker(channel);
|
||||
createServiceMessageBroker(channel: string, runInZone: boolean = true): ServiceMessageBroker {
|
||||
return this._serviceMessageBrokerFactory.createMessageBroker(channel, runInZone);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ export class MessageBasedRenderCompiler {
|
|||
private _renderCompiler: RenderCompiler) {}
|
||||
|
||||
start(): void {
|
||||
var broker = this._brokerFactory.createMessageBroker(RENDER_COMPILER_CHANNEL);
|
||||
var broker = this._brokerFactory.createMessageBroker(RENDER_COMPILER_CHANNEL, false);
|
||||
broker.registerMethod("compileHost", [RenderDirectiveMetadata],
|
||||
bind(this._renderCompiler.compileHost, this._renderCompiler),
|
||||
ProtoViewDto);
|
||||
|
|
|
@ -26,6 +26,7 @@ export class MessageBasedRenderer {
|
|||
|
||||
start(): void {
|
||||
var broker = this._brokerFactory.createMessageBroker(RENDERER_CHANNEL);
|
||||
this._bus.initChannel(EVENT_CHANNEL);
|
||||
broker.registerMethod("createRootHostView",
|
||||
[RenderProtoViewRef, PRIMITIVE, PRIMITIVE, PRIMITIVE],
|
||||
bind(this._createRootHostView, this));
|
||||
|
|
|
@ -14,6 +14,7 @@ export class WebWorkerSetup {
|
|||
}
|
||||
|
||||
start(): void {
|
||||
this._bus.initChannel(SETUP_CHANNEL, false);
|
||||
var sink = this._bus.to(SETUP_CHANNEL);
|
||||
var source = this._bus.from(SETUP_CHANNEL);
|
||||
|
||||
|
|
|
@ -30,8 +30,10 @@ export function bootstrapWebWorker(
|
|||
appComponentType: Type, componentInjectableBindings: Array<Type | Binding | any[]> = null):
|
||||
Promise<ApplicationRef> {
|
||||
var sink = new PostMessageBusSink({
|
||||
postMessage:
|
||||
(message: any, transferrables?:[ArrayBuffer]) => { _postMessage(message, transferrables); }
|
||||
postMessage: (message: any, transferrables?:[ArrayBuffer]) => {
|
||||
console.log("Sending", message);
|
||||
_postMessage(message, transferrables);
|
||||
}
|
||||
});
|
||||
var source = new PostMessageBusSource();
|
||||
var bus = new PostMessageBus(sink, source);
|
||||
|
|
|
@ -146,14 +146,13 @@ export function bootstrapWebWorkerCommon(
|
|||
var bootstrapProcess: PromiseCompleter<any> = PromiseWrapper.completer();
|
||||
|
||||
var zone = new NgZone({enableLongStackTrace: assertionsEnabled()});
|
||||
zone.run(() => {
|
||||
// TODO(rado): prepopulate template cache, so applications with only
|
||||
// index.html and main.js are possible.
|
||||
//
|
||||
bus.attachToZone(zone);
|
||||
|
||||
var subscription: any;
|
||||
var emitter = bus.from(SETUP_CHANNEL);
|
||||
subscription = ObservableWrapper.subscribe(emitter, (message: StringMap<string, any>) => {
|
||||
var subscription: any;
|
||||
bus.initChannel(SETUP_CHANNEL, false);
|
||||
var emitter = bus.from(SETUP_CHANNEL);
|
||||
subscription = ObservableWrapper.subscribe(emitter, (message: StringMap<string, any>) => {
|
||||
zone.run(() => {
|
||||
var exceptionHandler;
|
||||
try {
|
||||
var appInjector =
|
||||
|
@ -167,7 +166,6 @@ export function bootstrapWebWorkerCommon(
|
|||
var lc = appInjector.get(LifeCycle);
|
||||
lc.registerWith(zone, appChangeDetector);
|
||||
lc.tick(); // the first tick that will bootstrap the app
|
||||
|
||||
bootstrapProcess.resolve(new ApplicationRef(componentRef, appComponentType, appInjector));
|
||||
};
|
||||
|
||||
|
@ -185,9 +183,8 @@ export function bootstrapWebWorkerCommon(
|
|||
bootstrapProcess.reject(e, e.stack);
|
||||
}
|
||||
});
|
||||
|
||||
ObservableWrapper.callNext(bus.to(SETUP_CHANNEL), "ready");
|
||||
});
|
||||
ObservableWrapper.callNext(bus.to(SETUP_CHANNEL), "ready");
|
||||
|
||||
return bootstrapProcess.promise;
|
||||
}
|
||||
|
|
|
@ -6,14 +6,14 @@ import {EVENT_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
|||
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/core/facade/async';
|
||||
import {deserializeGenericEvent} from './event_deserializer';
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
|
||||
@Injectable()
|
||||
export class WebWorkerEventDispatcher {
|
||||
private _eventDispatchRegistry: Map<RenderViewRef, RenderEventDispatcher> =
|
||||
new Map<RenderViewRef, RenderEventDispatcher>();
|
||||
|
||||
constructor(bus: MessageBus, private _serializer: Serializer, private _zone: NgZone) {
|
||||
constructor(bus: MessageBus, private _serializer: Serializer) {
|
||||
bus.initChannel(EVENT_CHANNEL);
|
||||
var source = bus.from(EVENT_CHANNEL);
|
||||
ObservableWrapper.subscribe(
|
||||
source, (message) => this._dispatchEvent(new RenderEventData(message, _serializer)));
|
||||
|
@ -22,10 +22,8 @@ export class WebWorkerEventDispatcher {
|
|||
|
||||
private _dispatchEvent(eventData: RenderEventData): void {
|
||||
var dispatcher = this._eventDispatchRegistry.get(eventData.viewRef);
|
||||
this._zone.run(() => {
|
||||
eventData.locals['$event'] = deserializeGenericEvent(eventData.locals['$event']);
|
||||
dispatcher.dispatchRenderEvent(eventData.elementIndex, eventData.eventName, eventData.locals);
|
||||
});
|
||||
eventData.locals['$event'] = deserializeGenericEvent(eventData.locals['$event']);
|
||||
dispatcher.dispatchRenderEvent(eventData.elementIndex, eventData.eventName, eventData.locals);
|
||||
}
|
||||
|
||||
registerEventDispatcher(viewRef: RenderViewRef, dispatcher: RenderEventDispatcher): void {
|
||||
|
|
|
@ -186,6 +186,20 @@ function commonTests() {
|
|||
}, 80);
|
||||
}));
|
||||
|
||||
it('should call standalone onEventDone', inject([AsyncTestCompleter], (async) => {
|
||||
_zone.overrideOnTurnStart(null);
|
||||
_zone.overrideOnEventDone(() => { _log.add('onEventDone'); });
|
||||
|
||||
_zone.overrideOnTurnDone(null);
|
||||
|
||||
macroTask(() => { _zone.run(_log.fn('run')); });
|
||||
|
||||
macroTask(() => {
|
||||
expect(_log.result()).toEqual('run; onEventDone');
|
||||
async.done();
|
||||
}, 80);
|
||||
}));
|
||||
|
||||
it('should not allow onEventDone to cause further digests',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
_zone.overrideOnTurnStart(null);
|
||||
|
|
|
@ -19,5 +19,5 @@ void expectSinkSendsEncodedJson(SpyObject socket, MessageBusSink sink,
|
|||
|
||||
void expectMessageEquality(String message, Map expectedData, String channel) {
|
||||
expect(JSON.decode(message))
|
||||
.toEqual({'channel': channel, 'message': expectedData});
|
||||
.toEqual([{'channel': channel, 'message': expectedData}]);
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ main() {
|
|||
inject([AsyncTestCompleter], (async) {
|
||||
const NUM_CLIENTS = 5;
|
||||
var sink = new MultiClientServerMessageBusSink();
|
||||
sink.initChannel(CHANNEL, false);
|
||||
int numMessagesSent = 0;
|
||||
// initialize all the sockets
|
||||
var sockets = new List<WebSocketWrapper>(NUM_CLIENTS);
|
||||
|
@ -78,7 +79,7 @@ main() {
|
|||
for (var i = 0; i < numMessages; i++) {
|
||||
var message = {'value': random.nextInt(MAX)};
|
||||
messageHistory
|
||||
.add(JSON.encode({'channel': CHANNEL, 'message': message}));
|
||||
.add(JSON.encode([{'channel': CHANNEL, 'message': message}]));
|
||||
}
|
||||
// copy the message history to ensure the test fails if the wrapper modifies the list
|
||||
return new List.from(messageHistory);
|
||||
|
@ -136,7 +137,7 @@ main() {
|
|||
});
|
||||
|
||||
void sendMessage(StreamController controller, dynamic message) {
|
||||
controller.add(JSON.encode(message));
|
||||
controller.add(JSON.encode([message]));
|
||||
}
|
||||
|
||||
void testForwardingMessages(bool primary, bool events, Function done) {
|
||||
|
@ -146,10 +147,11 @@ main() {
|
|||
new WebSocketWrapper(messageHistory, resultMarkers, result.socket);
|
||||
socket.setPrimary(primary);
|
||||
|
||||
var source = new MultiClientServerMessageBusSource(null);
|
||||
var source = new MultiClientServerMessageBusSource();
|
||||
source.addConnection(socket);
|
||||
|
||||
var channel = events ? EVENT_CHANNEL : CHANNEL;
|
||||
source.initChannel(channel, false);
|
||||
source.from(channel).listen((message) {
|
||||
expect(message).toEqual(MESSAGE);
|
||||
done();
|
||||
|
@ -187,7 +189,9 @@ main() {
|
|||
socket.setPrimary(true);
|
||||
|
||||
var source =
|
||||
new MultiClientServerMessageBusSource(() => async.done());
|
||||
new MultiClientServerMessageBusSource();
|
||||
source.onResult.listen((result) => async.done());
|
||||
source.initChannel(CHANNEL, false);
|
||||
source.addConnection(socket);
|
||||
|
||||
var message = {
|
||||
|
|
|
@ -28,6 +28,8 @@ main() {
|
|||
inject([AsyncTestCompleter], (async) {
|
||||
var socket = new SpyWebSocket();
|
||||
var sink = new SingleClientServerMessageBusSink();
|
||||
sink.initChannel(CHANNEL, false);
|
||||
|
||||
sink.setConnection(socket);
|
||||
expectSinkSendsEncodedJson(socket, sink, "add", async);
|
||||
}));
|
||||
|
@ -36,6 +38,7 @@ main() {
|
|||
"should buffer messages before connect",
|
||||
inject([AsyncTestCompleter], (async) {
|
||||
var sink = new SingleClientServerMessageBusSink();
|
||||
sink.initChannel(CHANNEL, false);
|
||||
sink.to(CHANNEL).add(MESSAGE);
|
||||
|
||||
var socket = new SpyWebSocket();
|
||||
|
@ -51,6 +54,8 @@ main() {
|
|||
inject([AsyncTestCompleter], (async) {
|
||||
var SECOND_MESSAGE = const {'test': 12, 'second': 'hi'};
|
||||
var sink = new SingleClientServerMessageBusSink();
|
||||
sink.initChannel(CHANNEL, false);
|
||||
|
||||
sink.to(CHANNEL).add(MESSAGE);
|
||||
|
||||
var socket = new SpyWebSocket();
|
||||
|
@ -78,20 +83,19 @@ main() {
|
|||
it(
|
||||
"should decode JSON messages and emit them",
|
||||
inject([AsyncTestCompleter], (async) {
|
||||
var socket = new SpyWebSocket();
|
||||
StreamController<String> controller =
|
||||
new StreamController.broadcast();
|
||||
socket.spy("asBroadcastStream").andCallFake(() => controller.stream);
|
||||
|
||||
var source = new SingleClientServerMessageBusSource();
|
||||
source.setConnectionFromWebSocket(socket);
|
||||
source.initChannel(CHANNEL, false);
|
||||
source.attachTo(controller.stream);
|
||||
source.from(CHANNEL).listen((message) {
|
||||
expect(message).toEqual(MESSAGE);
|
||||
async.done();
|
||||
});
|
||||
|
||||
controller
|
||||
.add(JSON.encode({'channel': CHANNEL, 'message': MESSAGE}));
|
||||
.add(JSON.encode([{'channel': CHANNEL, 'message': MESSAGE}]));
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ main() {
|
|||
inject([AsyncTestCompleter], (async) {
|
||||
var socket = new SpyWebSocket();
|
||||
var sink = new WebSocketMessageBusSink(socket);
|
||||
sink.initChannel(CHANNEL, false);
|
||||
expectSinkSendsEncodedJson(socket, sink, "send", async);
|
||||
}));
|
||||
});
|
||||
|
@ -41,6 +42,7 @@ main() {
|
|||
new StreamController.broadcast();
|
||||
socket.spy("get:onMessage").andCallFake(() => controller.stream);
|
||||
var source = new WebSocketMessageBusSource(socket);
|
||||
source.initChannel(CHANNEL, false);
|
||||
|
||||
source.from(CHANNEL).listen((message) {
|
||||
expect(message).toEqual(MESSAGE);
|
||||
|
@ -49,7 +51,7 @@ main() {
|
|||
|
||||
var event = new SpyMessageEvent();
|
||||
event.spy("get:data").andCallFake(
|
||||
() => JSON.encode({'channel': CHANNEL, 'message': MESSAGE}));
|
||||
() => JSON.encode([{'channel': CHANNEL, 'message': MESSAGE}]));
|
||||
controller.add(event);
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -10,9 +10,11 @@ import {
|
|||
SpyObject,
|
||||
proxy
|
||||
} from 'angular2/test_lib';
|
||||
import {ObservableWrapper} from 'angular2/src/core/facade/async';
|
||||
import {ObservableWrapper, TimerWrapper} from 'angular2/src/core/facade/async';
|
||||
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
||||
import {createConnectedMessageBus} from './message_bus_util';
|
||||
import {MockNgZone} from 'angular2/src/mock/ng_zone_mock';
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
|
||||
export function main() {
|
||||
/**
|
||||
|
@ -27,6 +29,7 @@ export function main() {
|
|||
inject([AsyncTestCompleter], (async) => {
|
||||
const CHANNEL = "CHANNEL 1";
|
||||
const MESSAGE = "Test message";
|
||||
bus.initChannel(CHANNEL, false);
|
||||
|
||||
var fromEmitter = bus.from(CHANNEL);
|
||||
ObservableWrapper.subscribe(fromEmitter, (message: any) => {
|
||||
|
@ -41,6 +44,7 @@ export function main() {
|
|||
const CHANNEL = "CHANNEL 1";
|
||||
const MESSAGE = "TESTING";
|
||||
const NUM_LISTENERS = 2;
|
||||
bus.initChannel(CHANNEL, false);
|
||||
|
||||
var callCount = 0;
|
||||
var emitHandler = (message: any) => {
|
||||
|
@ -66,6 +70,8 @@ export function main() {
|
|||
const MESSAGE_ONE = "This is a message on CHANNEL 1";
|
||||
const MESSAGE_TWO = "This is a message on CHANNEL 2";
|
||||
var callCount = 0;
|
||||
bus.initChannel(CHANNEL_ONE, false);
|
||||
bus.initChannel(CHANNEL_TWO, false);
|
||||
|
||||
var firstFromEmitter = bus.from(CHANNEL_ONE);
|
||||
ObservableWrapper.subscribe(firstFromEmitter, (message) => {
|
||||
|
@ -91,4 +97,55 @@ export function main() {
|
|||
ObservableWrapper.callNext(secondToEmitter, MESSAGE_TWO);
|
||||
}));
|
||||
});
|
||||
|
||||
describe("PostMessageBusSink", () => {
|
||||
var bus: MessageBus;
|
||||
const CHANNEL = "Test Channel";
|
||||
|
||||
function setup(runInZone: boolean, zone: NgZone) {
|
||||
bus.attachToZone(zone);
|
||||
bus.initChannel(CHANNEL, runInZone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes pending messages and then runs the given function.
|
||||
*/
|
||||
function flushMessages(fn: () => void) { TimerWrapper.setTimeout(fn, 10); }
|
||||
|
||||
beforeEach(() => { bus = createConnectedMessageBus(); });
|
||||
|
||||
it("should buffer messages and wait for the zone to exit before sending",
|
||||
inject([AsyncTestCompleter, NgZone], (async, zone: MockNgZone) => {
|
||||
setup(true, zone);
|
||||
|
||||
var wasCalled = false;
|
||||
ObservableWrapper.subscribe(bus.from(CHANNEL), (message) => { wasCalled = true; });
|
||||
ObservableWrapper.callNext(bus.to(CHANNEL), "hi");
|
||||
|
||||
|
||||
flushMessages(() => {
|
||||
expect(wasCalled).toBeFalsy();
|
||||
|
||||
zone.simulateZoneExit();
|
||||
flushMessages(() => {
|
||||
expect(wasCalled).toBeTruthy();
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it("should send messages immediatly when run outside the zone",
|
||||
inject([AsyncTestCompleter, NgZone], (async, zone: MockNgZone) => {
|
||||
setup(false, zone);
|
||||
|
||||
var wasCalled = false;
|
||||
ObservableWrapper.subscribe(bus.from(CHANNEL), (message) => { wasCalled = true; });
|
||||
ObservableWrapper.callNext(bus.to(CHANNEL), "hi");
|
||||
|
||||
flushMessages(() => {
|
||||
expect(wasCalled).toBeTruthy();
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -39,7 +39,11 @@ export function main() {
|
|||
describe("UIMessageBroker", () => {
|
||||
var messageBuses;
|
||||
|
||||
beforeEach(() => { messageBuses = createPairedMessageBuses(); });
|
||||
beforeEach(() => {
|
||||
messageBuses = createPairedMessageBuses();
|
||||
messageBuses.ui.initChannel(CHANNEL);
|
||||
messageBuses.worker.initChannel(CHANNEL);
|
||||
});
|
||||
it("should call registered method with correct arguments",
|
||||
inject([Serializer], (serializer) => {
|
||||
var broker = new ServiceMessageBroker(messageBuses.ui, serializer, CHANNEL);
|
||||
|
|
|
@ -5,6 +5,8 @@ import {
|
|||
MessageBus
|
||||
} from 'angular2/src/web_workers/shared/message_bus';
|
||||
import {MockEventEmitter} from './mock_event_emitter';
|
||||
import {BaseException} from 'angular2/src/core/facade/lang';
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
|
||||
/**
|
||||
* Returns two MessageBus instances that are attached to each other.
|
||||
|
@ -30,30 +32,56 @@ export class PairedMessageBuses {
|
|||
export class MockMessageBusSource implements MessageBusSource {
|
||||
constructor(private _channels: StringMap<string, MockEventEmitter>) {}
|
||||
|
||||
from(channel: string): MockEventEmitter {
|
||||
initChannel(channel: string, runInZone = true) {
|
||||
if (!StringMapWrapper.contains(this._channels, channel)) {
|
||||
this._channels[channel] = new MockEventEmitter();
|
||||
}
|
||||
}
|
||||
|
||||
from(channel: string): MockEventEmitter {
|
||||
if (!StringMapWrapper.contains(this._channels, channel)) {
|
||||
throw new BaseException(`${channel} is not set up. Did you forget to call initChannel?`);
|
||||
}
|
||||
return this._channels[channel];
|
||||
}
|
||||
|
||||
attachToZone(zone: NgZone) {}
|
||||
}
|
||||
|
||||
export class MockMessageBusSink implements MessageBusSink {
|
||||
constructor(private _channels: StringMap<string, MockEventEmitter>) {}
|
||||
|
||||
initChannel(channel: string, runInZone = true) {
|
||||
if (!StringMapWrapper.contains(this._channels, channel)) {
|
||||
this._channels[channel] = new MockEventEmitter();
|
||||
}
|
||||
}
|
||||
|
||||
to(channel: string): MockEventEmitter {
|
||||
if (!StringMapWrapper.contains(this._channels, channel)) {
|
||||
this._channels[channel] = new MockEventEmitter();
|
||||
}
|
||||
return this._channels[channel];
|
||||
}
|
||||
|
||||
attachToZone(zone: NgZone) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link MessageBus} for tests.
|
||||
* Runs syncronously, and does not support running within the zone.
|
||||
*/
|
||||
export class MockMessageBus extends MessageBus {
|
||||
constructor(public sink: MockMessageBusSink, public source: MockMessageBusSource) { super(); }
|
||||
|
||||
initChannel(channel: string, runInZone = true) {
|
||||
this.sink.initChannel(channel, runInZone);
|
||||
this.source.initChannel(channel, runInZone);
|
||||
}
|
||||
|
||||
to(channel: string): MockEventEmitter { return this.sink.to(channel); }
|
||||
|
||||
from(channel: string): MockEventEmitter { return this.source.from(channel); }
|
||||
|
||||
attachToZone(zone: NgZone) {}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import {
|
|||
proxy
|
||||
} from 'angular2/test_lib';
|
||||
import {Serializer} from 'angular2/src/web_workers/shared/serializer';
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
import {ON_WEB_WORKER} from 'angular2/src/web_workers/shared/api';
|
||||
import {bind} from 'angular2/core';
|
||||
import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_proto_view_ref_store';
|
||||
|
@ -34,11 +33,10 @@ export function main() {
|
|||
RenderViewWithFragmentsStore
|
||||
]);
|
||||
|
||||
it("should dispatch events",
|
||||
inject([Serializer, NgZone, AsyncTestCompleter], (serializer, zone, async) => {
|
||||
it("should dispatch events", inject([Serializer, AsyncTestCompleter], (serializer, async) => {
|
||||
var messageBuses = createPairedMessageBuses();
|
||||
var webWorkerEventDispatcher =
|
||||
new WebWorkerEventDispatcher(messageBuses.worker, serializer, zone);
|
||||
new WebWorkerEventDispatcher(messageBuses.worker, serializer);
|
||||
|
||||
var elementIndex = 15;
|
||||
var eventName = 'click';
|
||||
|
|
|
@ -45,5 +45,5 @@ export function main() {
|
|||
|
||||
class MockMessageBrokerFactory extends ClientMessageBrokerFactory {
|
||||
constructor(private _messageBroker: ClientMessageBroker) { super(null, null); }
|
||||
createMessageBroker(channel: string) { return this._messageBroker; }
|
||||
createMessageBroker(channel: string, runInZone = true) { return this._messageBroker; }
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ main() {
|
|||
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
||||
const ECHO_CHANNEL = "ECHO";
|
||||
bootstrap("background_index.dart").then((instance) {
|
||||
var broker = instance.app.createClientMessageBroker(ECHO_CHANNEL);
|
||||
var broker = instance.app.createClientMessageBroker(ECHO_CHANNEL, false);
|
||||
querySelector("#send_echo").addEventListener("click", (e) {
|
||||
var val = (querySelector("#echo_input") as InputElement).value;
|
||||
var args = new UiArguments("echo", [new FnArg(val, PRIMITIVE)]);
|
||||
|
|
|
@ -3,7 +3,7 @@ import {bootstrap, UiArguments, FnArg, PRIMITIVE} from "angular2/web_worker/ui";
|
|||
const ECHO_CHANNEL = "ECHO";
|
||||
|
||||
var instance = bootstrap("loader.js");
|
||||
var broker = instance.app.createClientMessageBroker(ECHO_CHANNEL);
|
||||
var broker = instance.app.createClientMessageBroker(ECHO_CHANNEL, false);
|
||||
|
||||
document.getElementById("send_echo")
|
||||
.addEventListener("click", (e) => {
|
||||
|
|
|
@ -7,7 +7,7 @@ const ECHO_CHANNEL = "ECHO";
|
|||
@View({template: "<h1>WebWorker MessageBroker Test</h1>"})
|
||||
export class App {
|
||||
constructor(private _serviceBrokerFactory: ServiceMessageBrokerFactory) {
|
||||
var broker = _serviceBrokerFactory.createMessageBroker(ECHO_CHANNEL);
|
||||
var broker = _serviceBrokerFactory.createMessageBroker(ECHO_CHANNEL, false);
|
||||
broker.registerMethod("echo", [PRIMITIVE], this._echo, PRIMITIVE);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,9 @@ import 'dart:html'
|
|||
main() {
|
||||
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
||||
var webSocket = new WebSocket("ws://127.0.0.1:1337/ws");
|
||||
var bus = new WebSocketMessageBus.fromWebSocket(webSocket);
|
||||
webSocket.onOpen.listen((e) {
|
||||
var bus = new WebSocketMessageBus.fromWebSocket(webSocket);
|
||||
|
||||
bootstrapUICommon(bus);
|
||||
bootstrapUICommon(bus);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -12,5 +12,6 @@ void main() {
|
|||
HttpServer.bind('127.0.0.1', 1337).then((HttpServer server) {
|
||||
var bus = new MultiClientServerMessageBus.fromHttpServer(server);
|
||||
bootstrapWebWorkerCommon(TodoApp, bus).catchError((error) => throw error);
|
||||
print ("Server Listening for requests on 127.0.0.1:1337");
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue