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