feat(WebWorker) Add channel support to MessageBus
closes #3661 and #3686
This commit is contained in:
parent
104302a958
commit
0b59e664ec
|
@ -81,13 +81,13 @@ class ObservableWrapper {
|
|||
}
|
||||
|
||||
class EventEmitter extends Stream {
|
||||
StreamController<String> _controller;
|
||||
StreamController<dynamic> _controller;
|
||||
|
||||
EventEmitter() {
|
||||
_controller = new StreamController.broadcast();
|
||||
}
|
||||
|
||||
StreamSubscription listen(void onData(String line),
|
||||
StreamSubscription listen(void onData(dynamic line),
|
||||
{void onError(Error error), void onDone(), bool cancelOnError}) {
|
||||
return _controller.stream.listen(onData,
|
||||
onError: onError, onDone: onDone, cancelOnError: cancelOnError);
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
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/facade/async.dart';
|
||||
|
||||
class IsolateMessageBus implements MessageBus {
|
||||
final IsolateMessageBusSink sink;
|
||||
final IsolateMessageBusSource source;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
class IsolateMessageBusSink implements MessageBusSink {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IsolateMessageBusSource extends MessageBusSource {
|
||||
final Stream rawDataStream;
|
||||
final Map<String, EventEmitter> _channels = new Map<String, EventEmitter>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,26 +1,52 @@
|
|||
// TODO(jteplitz602) to be idiomatic these should be releated to Observable's or Streams
|
||||
/**
|
||||
* Message Bus is a low level API used to communicate between the UI and the worker.
|
||||
* It smooths out the differences between Javascript's postMessage and Dart's Isolate
|
||||
* allowing you to work with one consistent API.
|
||||
*/
|
||||
export interface MessageBus {
|
||||
sink: MessageBusSink;
|
||||
source: MessageBusSource;
|
||||
import {EventEmitter} from 'angular2/src/facade/async';
|
||||
import {BaseException} from 'angular2/src/facade/lang';
|
||||
// TODO(jteplitz602): Replace both the interface and the exported class with an abstract class #3683
|
||||
|
||||
function _abstract() {
|
||||
throw new BaseException("This method is abstract");
|
||||
}
|
||||
|
||||
export interface SourceListener {
|
||||
(data: any): void; // TODO: Replace this any type with the type of a real messaging protocol
|
||||
/**
|
||||
* Message Bus is a low level API used to communicate between the UI and the background.
|
||||
* Communication is based on a channel abstraction. Messages published in a
|
||||
* given channel to one MessageBusSink are received on the same channel
|
||||
* by the corresponding MessageBusSource.
|
||||
* TODO(jteplitz602): This should just extend both the source and the sink once
|
||||
* https://github.com/angular/ts2dart/issues/263 is closed.
|
||||
*/
|
||||
export interface MessageBusInterface {
|
||||
/**
|
||||
* Returns an {@link EventEmitter} that emits every time a messsage
|
||||
* is received on the given channel.
|
||||
*/
|
||||
from(channel: string): EventEmitter;
|
||||
|
||||
/**
|
||||
* 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(channel: string): EventEmitter;
|
||||
}
|
||||
|
||||
export interface MessageBusSource {
|
||||
/**
|
||||
* Attaches the SourceListener to this source.
|
||||
* The SourceListener will get called whenever the bus receives a message
|
||||
* Returns a listener id that can be passed to {removeListener}
|
||||
* Returns an {@link EventEmitter} that emits every time a messsage
|
||||
* is received on the given channel.
|
||||
*/
|
||||
addListener(fn: SourceListener): number;
|
||||
removeListener(index: number);
|
||||
from(channel: string): EventEmitter;
|
||||
}
|
||||
|
||||
export interface MessageBusSink { send(message: Object): void; }
|
||||
export interface MessageBusSink {
|
||||
/**
|
||||
* 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(channel: string): EventEmitter;
|
||||
}
|
||||
|
||||
// TODO(jteplitz602): Remove this class once we have abstract classes #3683
|
||||
export class MessageBus implements MessageBusInterface {
|
||||
from(channel: string): EventEmitter { throw _abstract(); }
|
||||
|
||||
to(channel: string): EventEmitter { throw _abstract(); }
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* All channels used by angular's WebWorker components are listed here.
|
||||
* You should not use these channels in your application code.
|
||||
*/
|
||||
export const SETUP_CHANNEL = "ng-WebWorkerSetup";
|
||||
export const RENDER_COMPILER_CHANNEL = "ng-RenderCompiler";
|
||||
export const RENDERER_CHANNEL = "ng-Renderer";
|
||||
export const XHR_CHANNEL = "ng-XHR";
|
||||
export const EVENT_CHANNEL = "ng-events";
|
|
@ -0,0 +1,3 @@
|
|||
// PostMessageBus can't be implemented in dart since dart doesn't use postMessage
|
||||
// This file is only here to prevent ts2dart from trying to transpile the PostMessageBus
|
||||
library angular2.src.web_workers.shared.post_message_bus;
|
|
@ -0,0 +1,75 @@
|
|||
import {
|
||||
MessageBusInterface,
|
||||
MessageBusSource,
|
||||
MessageBusSink
|
||||
} from "angular2/src/web-workers/shared/message_bus";
|
||||
import {EventEmitter} from 'angular2/src/facade/async';
|
||||
import {StringMap, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {Injectable} from "angular2/di";
|
||||
|
||||
/**
|
||||
* A TypeScript implementation of {@link MessageBus} for communicating via JavaScript's
|
||||
* postMessage API.
|
||||
*/
|
||||
@Injectable()
|
||||
export class PostMessageBus implements MessageBusInterface {
|
||||
constructor(private _sink: PostMessageBusSink, private _source: PostMessageBusSource) {}
|
||||
|
||||
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();
|
||||
|
||||
constructor(private _postMessageTarget: PostMessageTarget) {}
|
||||
|
||||
public to(channel: string): EventEmitter {
|
||||
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});
|
||||
}
|
||||
});
|
||||
return emitter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(jteplitz602) Replace this with the definition in lib.webworker.d.ts(#3492)
|
||||
export interface PostMessageTarget { postMessage: (message: any, transfer?:[ArrayBuffer]) => void; }
|
|
@ -4,8 +4,9 @@ import 'dart:isolate';
|
|||
import 'dart:async';
|
||||
import 'dart:core';
|
||||
import 'package:angular2/src/web-workers/shared/message_bus.dart'
|
||||
show MessageBus, MessageBusSink, MessageBusSource;
|
||||
show MessageBus;
|
||||
import 'package:angular2/src/web-workers/ui/impl.dart' show bootstrapUICommon;
|
||||
import 'package:angular2/src/web-workers/shared/isolate_message_bus.dart';
|
||||
|
||||
/**
|
||||
* Bootstrapping a WebWorker
|
||||
|
@ -23,63 +24,22 @@ Future<MessageBus> bootstrap(String uri) {
|
|||
/**
|
||||
* To be called from the main thread to spawn and communicate with the worker thread
|
||||
*/
|
||||
Future<UIMessageBus> spawnWebWorker(Uri uri) {
|
||||
Future<MessageBus> spawnWebWorker(Uri uri) {
|
||||
var receivePort = new ReceivePort();
|
||||
var isolateEndSendPort = receivePort.sendPort;
|
||||
return Isolate.spawnUri(uri, const [], isolateEndSendPort).then((_) {
|
||||
var source = new UIMessageBusSource(receivePort);
|
||||
return source.sink.then((sendPort) {
|
||||
var sink = new UIMessageBusSink(sendPort);
|
||||
return new UIMessageBus(sink, source);
|
||||
var sink = new IsolateMessageBusSink(sendPort);
|
||||
return new IsolateMessageBus(sink, source);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class UIMessageBus extends MessageBus {
|
||||
final UIMessageBusSink sink;
|
||||
final UIMessageBusSource source;
|
||||
|
||||
UIMessageBus(UIMessageBusSink sink, UIMessageBusSource source)
|
||||
: sink = sink,
|
||||
source = source;
|
||||
}
|
||||
|
||||
class UIMessageBusSink extends MessageBusSink {
|
||||
final SendPort _port;
|
||||
|
||||
UIMessageBusSink(SendPort port) : _port = port;
|
||||
|
||||
void send(message) {
|
||||
_port.send(message);
|
||||
}
|
||||
}
|
||||
|
||||
class UIMessageBusSource extends MessageBusSource {
|
||||
final ReceivePort _port;
|
||||
final Stream rawDataStream;
|
||||
Map<int, StreamSubscription> _listenerStore =
|
||||
new Map<int, StreamSubscription>();
|
||||
int _numListeners = 0;
|
||||
|
||||
UIMessageBusSource(ReceivePort port)
|
||||
: _port = port,
|
||||
rawDataStream = port.asBroadcastStream();
|
||||
class UIMessageBusSource extends IsolateMessageBusSource {
|
||||
UIMessageBusSource(ReceivePort port) : super(port);
|
||||
|
||||
Future<SendPort> get sink => rawDataStream.firstWhere((message) {
|
||||
return message is SendPort;
|
||||
});
|
||||
|
||||
int addListener(Function fn) {
|
||||
var subscription = rawDataStream.listen((message) {
|
||||
fn({"data": message});
|
||||
});
|
||||
|
||||
_listenerStore[++_numListeners] = subscription;
|
||||
return _numListeners;
|
||||
}
|
||||
|
||||
void removeListener(int index) {
|
||||
_listenerStore[index].cancel();
|
||||
_listenerStore.remove(index);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import {
|
||||
MessageBus,
|
||||
MessageBusSource,
|
||||
MessageBusSink,
|
||||
SourceListener
|
||||
} from "angular2/src/web-workers/shared/message_bus";
|
||||
PostMessageBus,
|
||||
PostMessageBusSink,
|
||||
PostMessageBusSource
|
||||
} from 'angular2/src/web-workers/shared/post_message_bus';
|
||||
import {MessageBus} from 'angular2/src/web-workers/shared/message_bus';
|
||||
import {BaseException} from "angular2/src/facade/lang";
|
||||
import {bootstrapUICommon} from "angular2/src/web-workers/ui/impl";
|
||||
|
||||
|
@ -23,33 +23,7 @@ export function bootstrap(uri: string): MessageBus {
|
|||
|
||||
export function spawnWebWorker(uri: string): MessageBus {
|
||||
var webWorker: Worker = new Worker(uri);
|
||||
return new UIMessageBus(new UIMessageBusSink(webWorker), new UIMessageBusSource(webWorker));
|
||||
}
|
||||
|
||||
export class UIMessageBus implements MessageBus {
|
||||
constructor(public sink: UIMessageBusSink, public source: UIMessageBusSource) {}
|
||||
}
|
||||
|
||||
export class UIMessageBusSink implements MessageBusSink {
|
||||
constructor(private _webWorker: Worker) {}
|
||||
|
||||
send(message: Object): void { this._webWorker.postMessage(message); }
|
||||
}
|
||||
|
||||
export class UIMessageBusSource implements MessageBusSource {
|
||||
private _listenerStore: Map<int, SourceListener> = new Map<int, SourceListener>();
|
||||
private _numListeners: int = 0;
|
||||
|
||||
constructor(private _webWorker: Worker) {}
|
||||
|
||||
public addListener(fn: SourceListener): int {
|
||||
this._webWorker.addEventListener("message", fn);
|
||||
this._listenerStore[++this._numListeners] = fn;
|
||||
return this._numListeners;
|
||||
}
|
||||
|
||||
public removeListener(index: int): void {
|
||||
removeEventListener("message", this._listenerStore[index]);
|
||||
this._listenerStore.delete(index);
|
||||
}
|
||||
var sink = new PostMessageBusSink(webWorker);
|
||||
var source = new PostMessageBusSource(webWorker);
|
||||
return new PostMessageBus(sink, source);
|
||||
}
|
||||
|
|
|
@ -62,6 +62,11 @@ import {
|
|||
} from 'angular2/src/web-workers/shared/render_view_with_fragments_store';
|
||||
import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url';
|
||||
import {WebWorkerMain} from 'angular2/src/web-workers/ui/impl';
|
||||
import {MessageBus, MessageBusInterface} from 'angular2/src/web-workers/shared/message_bus';
|
||||
import {MessageBasedRenderCompiler} from 'angular2/src/web-workers/ui/render_compiler';
|
||||
import {MessageBasedRenderer} from 'angular2/src/web-workers/ui/renderer';
|
||||
import {MessageBasedXHRImpl} from 'angular2/src/web-workers/ui/xhr_impl';
|
||||
import {WebWorkerSetup} from 'angular2/src/web-workers/ui/setup';
|
||||
|
||||
var _rootInjector: Injector;
|
||||
|
||||
|
@ -129,13 +134,18 @@ function _injectorBindings(): List<Type | Binding | List<any>> {
|
|||
Testability,
|
||||
AnchorBasedAppRootUrl,
|
||||
bind(AppRootUrl).toAlias(AnchorBasedAppRootUrl),
|
||||
WebWorkerMain
|
||||
WebWorkerMain,
|
||||
WebWorkerSetup,
|
||||
MessageBasedRenderCompiler,
|
||||
MessageBasedXHRImpl,
|
||||
MessageBasedRenderer
|
||||
];
|
||||
}
|
||||
|
||||
export function createInjector(zone: NgZone): Injector {
|
||||
export function createInjector(zone: NgZone, bus: MessageBusInterface): Injector {
|
||||
BrowserDomAdapter.makeCurrent();
|
||||
_rootBindings.push(bind(NgZone).toValue(zone));
|
||||
_rootBindings.push(bind(MessageBus).toValue(bus));
|
||||
var injector: Injector = Injector.resolveAndCreate(_rootBindings);
|
||||
return injector.resolveAndCreateChild(_injectorBindings());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
import {
|
||||
RenderViewRef,
|
||||
RenderEventDispatcher,
|
||||
} from 'angular2/src/render/api';
|
||||
import {Serializer} from 'angular2/src/web-workers/shared/serializer';
|
||||
import {
|
||||
serializeMouseEvent,
|
||||
serializeKeyboardEvent,
|
||||
serializeGenericEvent,
|
||||
serializeEventWithTarget
|
||||
} from 'angular2/src/web-workers/ui/event_serializer';
|
||||
import {BaseException} from "angular2/src/facade/lang";
|
||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
export class EventDispatcher implements RenderEventDispatcher {
|
||||
constructor(private _viewRef: RenderViewRef, private _sink: EventEmitter,
|
||||
private _serializer: Serializer) {}
|
||||
|
||||
dispatchRenderEvent(elementIndex: number, eventName: string, locals: Map<string, any>) {
|
||||
var e = locals.get('$event');
|
||||
var serializedEvent;
|
||||
// TODO (jteplitz602): support custom events #3350
|
||||
switch (e.type) {
|
||||
case "click":
|
||||
case "mouseup":
|
||||
case "mousedown":
|
||||
case "dblclick":
|
||||
case "contextmenu":
|
||||
case "mouseenter":
|
||||
case "mouseleave":
|
||||
case "mousemove":
|
||||
case "mouseout":
|
||||
case "mouseover":
|
||||
case "show":
|
||||
serializedEvent = serializeMouseEvent(e);
|
||||
break;
|
||||
case "keydown":
|
||||
case "keypress":
|
||||
case "keyup":
|
||||
serializedEvent = serializeKeyboardEvent(e);
|
||||
break;
|
||||
case "input":
|
||||
case "change":
|
||||
case "blur":
|
||||
serializedEvent = serializeEventWithTarget(e);
|
||||
break;
|
||||
case "abort":
|
||||
case "afterprint":
|
||||
case "beforeprint":
|
||||
case "cached":
|
||||
case "canplay":
|
||||
case "canplaythrough":
|
||||
case "chargingchange":
|
||||
case "chargingtimechange":
|
||||
case "close":
|
||||
case "dischargingtimechange":
|
||||
case "DOMContentLoaded":
|
||||
case "downloading":
|
||||
case "durationchange":
|
||||
case "emptied":
|
||||
case "ended":
|
||||
case "error":
|
||||
case "fullscreenchange":
|
||||
case "fullscreenerror":
|
||||
case "invalid":
|
||||
case "languagechange":
|
||||
case "levelfchange":
|
||||
case "loadeddata":
|
||||
case "loadedmetadata":
|
||||
case "obsolete":
|
||||
case "offline":
|
||||
case "online":
|
||||
case "open":
|
||||
case "orientatoinchange":
|
||||
case "pause":
|
||||
case "pointerlockchange":
|
||||
case "pointerlockerror":
|
||||
case "play":
|
||||
case "playing":
|
||||
case "ratechange":
|
||||
case "readystatechange":
|
||||
case "reset":
|
||||
case "seeked":
|
||||
case "seeking":
|
||||
case "stalled":
|
||||
case "submit":
|
||||
case "success":
|
||||
case "suspend":
|
||||
case "timeupdate":
|
||||
case "updateready":
|
||||
case "visibilitychange":
|
||||
case "volumechange":
|
||||
case "waiting":
|
||||
serializedEvent = serializeGenericEvent(e);
|
||||
break;
|
||||
default:
|
||||
throw new BaseException(eventName + " not supported on WebWorkers");
|
||||
}
|
||||
var serializedLocals = StringMapWrapper.create();
|
||||
StringMapWrapper.set(serializedLocals, '$event', serializedEvent);
|
||||
|
||||
ObservableWrapper.callNext(this._sink, {
|
||||
"viewRef": this._serializer.serialize(this._viewRef, RenderViewRef),
|
||||
"elementIndex": elementIndex,
|
||||
"eventName": eventName,
|
||||
"locals": serializedLocals
|
||||
});
|
||||
}
|
||||
}
|
|
@ -6,39 +6,15 @@
|
|||
*/
|
||||
|
||||
import {createInjector} from "./di_bindings";
|
||||
import {
|
||||
Renderer,
|
||||
RenderCompiler,
|
||||
RenderDirectiveMetadata,
|
||||
ProtoViewDto,
|
||||
ViewDefinition,
|
||||
RenderProtoViewRef,
|
||||
RenderProtoViewMergeMapping,
|
||||
RenderViewRef,
|
||||
RenderEventDispatcher,
|
||||
RenderFragmentRef
|
||||
} from "angular2/src/render/api";
|
||||
import {Type, print, BaseException, isFunction} from "angular2/src/facade/lang";
|
||||
import {Promise, PromiseWrapper} from "angular2/src/facade/async";
|
||||
import {StringMapWrapper, SetWrapper} from 'angular2/src/facade/collection';
|
||||
import {Serializer} from "angular2/src/web-workers/shared/serializer";
|
||||
import {MessageBus, MessageBusSink} from "angular2/src/web-workers/shared/message_bus";
|
||||
import {
|
||||
RenderViewWithFragmentsStore
|
||||
} from 'angular2/src/web-workers/shared/render_view_with_fragments_store';
|
||||
import {createNgZone} from 'angular2/src/core/application_common';
|
||||
import {WebWorkerElementRef} from 'angular2/src/web-workers/shared/api';
|
||||
import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url';
|
||||
import {Injectable} from 'angular2/di';
|
||||
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
|
||||
import {XHR} from 'angular2/src/render/xhr';
|
||||
import {
|
||||
serializeMouseEvent,
|
||||
serializeKeyboardEvent,
|
||||
serializeGenericEvent,
|
||||
serializeEventWithTarget
|
||||
} from 'angular2/src/web-workers/ui/event_serializer';
|
||||
import {wtfInit} from 'angular2/src/profile/wtf_init';
|
||||
import {WebWorkerSetup} from 'angular2/src/web-workers/ui/setup';
|
||||
import {MessageBasedRenderCompiler} from 'angular2/src/web-workers/ui/render_compiler';
|
||||
import {MessageBasedRenderer} from 'angular2/src/web-workers/ui/renderer';
|
||||
import {MessageBasedXHRImpl} from 'angular2/src/web-workers/ui/xhr_impl';
|
||||
|
||||
/**
|
||||
* Creates a zone, sets up the DI bindings
|
||||
|
@ -49,305 +25,20 @@ export function bootstrapUICommon(bus: MessageBus) {
|
|||
var zone = createNgZone();
|
||||
wtfInit();
|
||||
zone.run(() => {
|
||||
var injector = createInjector(zone);
|
||||
var webWorkerMain = injector.get(WebWorkerMain);
|
||||
webWorkerMain.attachToWebWorker(bus);
|
||||
var injector = createInjector(zone, bus);
|
||||
// necessary to kick off all the message based components
|
||||
injector.get(WebWorkerMain);
|
||||
});
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class WebWorkerMain {
|
||||
private _rootUrl: string;
|
||||
private _bus: MessageBus;
|
||||
|
||||
constructor(private _renderCompiler: RenderCompiler, private _renderer: Renderer,
|
||||
private _renderViewWithFragmentsStore: RenderViewWithFragmentsStore,
|
||||
private _serializer: Serializer, rootUrl: AnchorBasedAppRootUrl, private _xhr: XHR) {
|
||||
this._rootUrl = rootUrl.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach's this WebWorkerMain instance to the given MessageBus
|
||||
* This instance will now listen for all messages from the worker and handle them appropriately
|
||||
* Note: Don't attach more than one WebWorkerMain instance to the same MessageBus.
|
||||
*/
|
||||
attachToWebWorker(bus: MessageBus) {
|
||||
this._bus = bus;
|
||||
this._bus.source.addListener((message) => { this._handleWebWorkerMessage(message); });
|
||||
}
|
||||
|
||||
private _sendInitMessage() { this._sendWebWorkerMessage("init", {"rootUrl": this._rootUrl}); }
|
||||
|
||||
/*
|
||||
* Sends an error back to the worker thread in response to an opeartion on the UI thread
|
||||
*/
|
||||
private _sendWebWorkerError(id: string, error: any) {
|
||||
this._sendWebWorkerMessage("error", {"error": error}, id);
|
||||
}
|
||||
|
||||
private _sendWebWorkerMessage(type: string, value: StringMap<string, any>, id?: string) {
|
||||
this._bus.sink.send({'type': type, 'id': id, 'value': value});
|
||||
}
|
||||
|
||||
// TODO: Transfer the types with the serialized data so this can be automated?
|
||||
private _handleCompilerMessage(data: ReceivedMessage) {
|
||||
var promise: Promise<any>;
|
||||
switch (data.method) {
|
||||
case "compileHost":
|
||||
var directiveMetadata = this._serializer.deserialize(data.args[0], RenderDirectiveMetadata);
|
||||
promise = this._renderCompiler.compileHost(directiveMetadata);
|
||||
this._wrapWebWorkerPromise(data.id, promise, ProtoViewDto);
|
||||
break;
|
||||
case "compile":
|
||||
var view = this._serializer.deserialize(data.args[0], ViewDefinition);
|
||||
promise = this._renderCompiler.compile(view);
|
||||
this._wrapWebWorkerPromise(data.id, promise, ProtoViewDto);
|
||||
break;
|
||||
case "mergeProtoViewsRecursively":
|
||||
var views = this._serializer.deserialize(data.args[0], RenderProtoViewRef);
|
||||
promise = this._renderCompiler.mergeProtoViewsRecursively(views);
|
||||
this._wrapWebWorkerPromise(data.id, promise, RenderProtoViewMergeMapping);
|
||||
break;
|
||||
default:
|
||||
throw new BaseException("not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
private _createViewHelper(args: List<any>, method) {
|
||||
var hostProtoView = this._serializer.deserialize(args[0], RenderProtoViewRef);
|
||||
var fragmentCount = args[1];
|
||||
var startIndex, renderViewWithFragments;
|
||||
if (method == "createView") {
|
||||
startIndex = args[2];
|
||||
renderViewWithFragments = this._renderer.createView(hostProtoView, fragmentCount);
|
||||
} else {
|
||||
var selector = args[2];
|
||||
startIndex = args[3];
|
||||
renderViewWithFragments =
|
||||
this._renderer.createRootHostView(hostProtoView, fragmentCount, selector);
|
||||
}
|
||||
this._renderViewWithFragmentsStore.store(renderViewWithFragments, startIndex);
|
||||
}
|
||||
|
||||
private _handleRendererMessage(data: ReceivedMessage) {
|
||||
var args = data.args;
|
||||
switch (data.method) {
|
||||
case "createRootHostView":
|
||||
case "createView":
|
||||
this._createViewHelper(args, data.method);
|
||||
break;
|
||||
case "destroyView":
|
||||
var viewRef = this._serializer.deserialize(args[0], RenderViewRef);
|
||||
this._renderer.destroyView(viewRef);
|
||||
break;
|
||||
case "attachFragmentAfterFragment":
|
||||
var previousFragment = this._serializer.deserialize(args[0], RenderFragmentRef);
|
||||
var fragment = this._serializer.deserialize(args[1], RenderFragmentRef);
|
||||
this._renderer.attachFragmentAfterFragment(previousFragment, fragment);
|
||||
break;
|
||||
case "attachFragmentAfterElement":
|
||||
var element = this._serializer.deserialize(args[0], WebWorkerElementRef);
|
||||
var fragment = this._serializer.deserialize(args[1], RenderFragmentRef);
|
||||
this._renderer.attachFragmentAfterElement(element, fragment);
|
||||
break;
|
||||
case "detachFragment":
|
||||
var fragment = this._serializer.deserialize(args[0], RenderFragmentRef);
|
||||
this._renderer.detachFragment(fragment);
|
||||
break;
|
||||
case "hydrateView":
|
||||
var viewRef = this._serializer.deserialize(args[0], RenderViewRef);
|
||||
this._renderer.hydrateView(viewRef);
|
||||
break;
|
||||
case "dehydrateView":
|
||||
var viewRef = this._serializer.deserialize(args[0], RenderViewRef);
|
||||
this._renderer.dehydrateView(viewRef);
|
||||
break;
|
||||
case "setText":
|
||||
var viewRef = this._serializer.deserialize(args[0], RenderViewRef);
|
||||
var textNodeIndex = args[1];
|
||||
var text = args[2];
|
||||
this._renderer.setText(viewRef, textNodeIndex, text);
|
||||
break;
|
||||
case "setElementProperty":
|
||||
var elementRef = this._serializer.deserialize(args[0], WebWorkerElementRef);
|
||||
var propName = args[1];
|
||||
var propValue = args[2];
|
||||
this._renderer.setElementProperty(elementRef, propName, propValue);
|
||||
break;
|
||||
case "setElementAttribute":
|
||||
var elementRef = this._serializer.deserialize(args[0], WebWorkerElementRef);
|
||||
var attributeName = args[1];
|
||||
var attributeValue = args[2];
|
||||
this._renderer.setElementAttribute(elementRef, attributeName, attributeValue);
|
||||
break;
|
||||
case "setElementClass":
|
||||
var elementRef = this._serializer.deserialize(args[0], WebWorkerElementRef);
|
||||
var className = args[1];
|
||||
var isAdd = args[2];
|
||||
this._renderer.setElementClass(elementRef, className, isAdd);
|
||||
break;
|
||||
case "setElementStyle":
|
||||
var elementRef = this._serializer.deserialize(args[0], WebWorkerElementRef);
|
||||
var styleName = args[1];
|
||||
var styleValue = args[2];
|
||||
this._renderer.setElementStyle(elementRef, styleName, styleValue);
|
||||
break;
|
||||
case "invokeElementMethod":
|
||||
var elementRef = this._serializer.deserialize(args[0], WebWorkerElementRef);
|
||||
var methodName = args[1];
|
||||
var methodArgs = args[2];
|
||||
this._renderer.invokeElementMethod(elementRef, methodName, methodArgs);
|
||||
break;
|
||||
case "setEventDispatcher":
|
||||
var viewRef = this._serializer.deserialize(args[0], RenderViewRef);
|
||||
var dispatcher = new EventDispatcher(viewRef, this._bus.sink, this._serializer);
|
||||
this._renderer.setEventDispatcher(viewRef, dispatcher);
|
||||
break;
|
||||
default:
|
||||
throw new BaseException("Not Implemented");
|
||||
}
|
||||
}
|
||||
|
||||
private _handleXhrMessage(data: ReceivedMessage) {
|
||||
var args = data.args;
|
||||
switch (data.method) {
|
||||
case "get":
|
||||
var url = args[0];
|
||||
var promise = this._xhr.get(url);
|
||||
this._wrapWebWorkerPromise(data.id, promise, String);
|
||||
break;
|
||||
default:
|
||||
throw new BaseException(data.method + " Not Implemented");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(jteplitz602): Create message type enum #3044
|
||||
private _handleWebWorkerMessage(message: StringMap<string, any>) {
|
||||
var data: ReceivedMessage = new ReceivedMessage(message['data']);
|
||||
// TODO(jteplitz602): Replace these with MessageBUs channels #3661
|
||||
switch (data.type) {
|
||||
case "ready":
|
||||
return this._sendInitMessage();
|
||||
case "compiler":
|
||||
return this._handleCompilerMessage(data);
|
||||
case "renderer":
|
||||
return this._handleRendererMessage(data);
|
||||
case "xhr":
|
||||
return this._handleXhrMessage(data);
|
||||
}
|
||||
}
|
||||
|
||||
private _wrapWebWorkerPromise(id: string, promise: Promise<any>, type: Type): void {
|
||||
PromiseWrapper.then(promise, (result: any) => {
|
||||
try {
|
||||
this._sendWebWorkerMessage("result", this._serializer.serialize(result, type), id);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}, (error: any) => { this._sendWebWorkerError(id, error); });
|
||||
}
|
||||
constructor(public renderCompiler: MessageBasedRenderCompiler,
|
||||
public renderer: MessageBasedRenderer, public xhr: MessageBasedXHRImpl,
|
||||
public setup: WebWorkerSetup) {}
|
||||
}
|
||||
|
||||
class EventDispatcher implements RenderEventDispatcher {
|
||||
constructor(private _viewRef: RenderViewRef, private _sink: MessageBusSink,
|
||||
private _serializer: Serializer) {}
|
||||
|
||||
dispatchRenderEvent(elementIndex: number, eventName: string, locals: Map<string, any>) {
|
||||
var e = locals.get('$event');
|
||||
var serializedEvent;
|
||||
// TODO (jteplitz602): support custom events #3350
|
||||
switch (e.type) {
|
||||
case "click":
|
||||
case "mouseup":
|
||||
case "mousedown":
|
||||
case "dblclick":
|
||||
case "contextmenu":
|
||||
case "mouseenter":
|
||||
case "mouseleave":
|
||||
case "mousemove":
|
||||
case "mouseout":
|
||||
case "mouseover":
|
||||
case "show":
|
||||
serializedEvent = serializeMouseEvent(e);
|
||||
break;
|
||||
case "keydown":
|
||||
case "keypress":
|
||||
case "keyup":
|
||||
serializedEvent = serializeKeyboardEvent(e);
|
||||
break;
|
||||
case "input":
|
||||
case "change":
|
||||
case "blur":
|
||||
serializedEvent = serializeEventWithTarget(e);
|
||||
break;
|
||||
case "abort":
|
||||
case "afterprint":
|
||||
case "beforeprint":
|
||||
case "cached":
|
||||
case "canplay":
|
||||
case "canplaythrough":
|
||||
case "chargingchange":
|
||||
case "chargingtimechange":
|
||||
case "close":
|
||||
case "dischargingtimechange":
|
||||
case "DOMContentLoaded":
|
||||
case "downloading":
|
||||
case "durationchange":
|
||||
case "emptied":
|
||||
case "ended":
|
||||
case "error":
|
||||
case "fullscreenchange":
|
||||
case "fullscreenerror":
|
||||
case "invalid":
|
||||
case "languagechange":
|
||||
case "levelfchange":
|
||||
case "loadeddata":
|
||||
case "loadedmetadata":
|
||||
case "obsolete":
|
||||
case "offline":
|
||||
case "online":
|
||||
case "open":
|
||||
case "orientatoinchange":
|
||||
case "pause":
|
||||
case "pointerlockchange":
|
||||
case "pointerlockerror":
|
||||
case "play":
|
||||
case "playing":
|
||||
case "ratechange":
|
||||
case "readystatechange":
|
||||
case "reset":
|
||||
case "seeked":
|
||||
case "seeking":
|
||||
case "stalled":
|
||||
case "submit":
|
||||
case "success":
|
||||
case "suspend":
|
||||
case "timeupdate":
|
||||
case "updateready":
|
||||
case "visibilitychange":
|
||||
case "volumechange":
|
||||
case "waiting":
|
||||
serializedEvent = serializeGenericEvent(e);
|
||||
break;
|
||||
default:
|
||||
throw new BaseException(eventName + " not supported on WebWorkers");
|
||||
}
|
||||
var serializedLocals = StringMapWrapper.create();
|
||||
StringMapWrapper.set(serializedLocals, '$event', serializedEvent);
|
||||
|
||||
this._sink.send({
|
||||
"type": "event",
|
||||
"value": {
|
||||
"viewRef": this._serializer.serialize(this._viewRef, RenderViewRef),
|
||||
"elementIndex": elementIndex,
|
||||
"eventName": eventName,
|
||||
"locals": serializedLocals
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ReceivedMessage {
|
||||
export class ReceivedMessage {
|
||||
method: string;
|
||||
args: List<any>;
|
||||
id: string;
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
import {Injectable} from 'angular2/di';
|
||||
import {MessageBus} from 'angular2/src/web-workers/shared/message_bus';
|
||||
import {Serializer} from 'angular2/src/web-workers/shared/serializer';
|
||||
import {
|
||||
RenderDirectiveMetadata,
|
||||
ProtoViewDto,
|
||||
ViewDefinition,
|
||||
RenderProtoViewRef,
|
||||
RenderProtoViewMergeMapping,
|
||||
RenderCompiler
|
||||
} from 'angular2/src/render/api';
|
||||
import {EventEmitter, ObservableWrapper, PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {RENDER_COMPILER_CHANNEL} from 'angular2/src/web-workers/shared/messaging_api';
|
||||
import {ReceivedMessage} from 'angular2/src/web-workers/ui/impl';
|
||||
import {BaseException, Type} from 'angular2/src/facade/lang';
|
||||
|
||||
// TODO(jteplitz602): Create parent UIComponent class #3703
|
||||
@Injectable()
|
||||
export class MessageBasedRenderCompiler {
|
||||
private _sink: EventEmitter;
|
||||
private _source: EventEmitter;
|
||||
|
||||
constructor(bus: MessageBus, private _serializer: Serializer,
|
||||
private _renderCompiler: RenderCompiler) {
|
||||
this._sink = bus.to(RENDER_COMPILER_CHANNEL);
|
||||
this._source = bus.from(RENDER_COMPILER_CHANNEL);
|
||||
ObservableWrapper.subscribe(this._source,
|
||||
(message: StringMap<string, any>) => this._handleMessage(message));
|
||||
}
|
||||
|
||||
private _handleMessage(map: StringMap<string, any>): void {
|
||||
var message = new ReceivedMessage(map);
|
||||
var args = message.args;
|
||||
var promise: Promise<any>;
|
||||
switch (message.method) {
|
||||
case "compileHost":
|
||||
var directiveMetadata = this._serializer.deserialize(args[0], RenderDirectiveMetadata);
|
||||
promise = this._renderCompiler.compileHost(directiveMetadata);
|
||||
this._wrapWebWorkerPromise(message.id, promise, ProtoViewDto);
|
||||
break;
|
||||
case "compile":
|
||||
var view = this._serializer.deserialize(args[0], ViewDefinition);
|
||||
promise = this._renderCompiler.compile(view);
|
||||
this._wrapWebWorkerPromise(message.id, promise, ProtoViewDto);
|
||||
break;
|
||||
case "mergeProtoViewsRecursively":
|
||||
var views = this._serializer.deserialize(args[0], RenderProtoViewRef);
|
||||
promise = this._renderCompiler.mergeProtoViewsRecursively(views);
|
||||
this._wrapWebWorkerPromise(message.id, promise, RenderProtoViewMergeMapping);
|
||||
break;
|
||||
default:
|
||||
throw new BaseException("not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
private _wrapWebWorkerPromise(id: string, promise: Promise<any>, type: Type): void {
|
||||
PromiseWrapper.then(promise, (result: any) => {
|
||||
ObservableWrapper.callNext(
|
||||
this._sink,
|
||||
{'type': 'result', 'value': this._serializer.serialize(result, type), 'id': id});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
import {Injectable} from 'angular2/di';
|
||||
import {MessageBus} from 'angular2/src/web-workers/shared/message_bus';
|
||||
import {Serializer} from 'angular2/src/web-workers/shared/serializer';
|
||||
import {
|
||||
RenderViewRef,
|
||||
RenderFragmentRef,
|
||||
RenderProtoViewRef,
|
||||
Renderer
|
||||
} from 'angular2/src/render/api';
|
||||
import {WebWorkerElementRef} from 'angular2/src/web-workers/shared/api';
|
||||
import {EventEmitter, ObservableWrapper, PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {EVENT_CHANNEL, RENDERER_CHANNEL} from 'angular2/src/web-workers/shared/messaging_api';
|
||||
import {ReceivedMessage} from 'angular2/src/web-workers/ui/impl';
|
||||
import {BaseException, Type} from 'angular2/src/facade/lang';
|
||||
import {EventDispatcher} from 'angular2/src/web-workers/ui/event_dispatcher';
|
||||
import {
|
||||
RenderViewWithFragmentsStore
|
||||
} from 'angular2/src/web-workers/shared/render_view_with_fragments_store';
|
||||
|
||||
@Injectable()
|
||||
export class MessageBasedRenderer {
|
||||
constructor(private _bus: MessageBus, private _serializer: Serializer,
|
||||
private _renderViewWithFragmentsStore: RenderViewWithFragmentsStore,
|
||||
private _renderer: Renderer) {
|
||||
var source = _bus.from(RENDERER_CHANNEL);
|
||||
ObservableWrapper.subscribe(source,
|
||||
(message: StringMap<string, any>) => this._handleMessage(message));
|
||||
}
|
||||
|
||||
private _createViewHelper(args: List<any>, method) {
|
||||
var hostProtoView = this._serializer.deserialize(args[0], RenderProtoViewRef);
|
||||
var fragmentCount = args[1];
|
||||
var startIndex, renderViewWithFragments;
|
||||
if (method == "createView") {
|
||||
startIndex = args[2];
|
||||
renderViewWithFragments = this._renderer.createView(hostProtoView, fragmentCount);
|
||||
} else {
|
||||
var selector = args[2];
|
||||
startIndex = args[3];
|
||||
renderViewWithFragments =
|
||||
this._renderer.createRootHostView(hostProtoView, fragmentCount, selector);
|
||||
}
|
||||
this._renderViewWithFragmentsStore.store(renderViewWithFragments, startIndex);
|
||||
}
|
||||
|
||||
|
||||
private _handleMessage(map: StringMap<string, any>): void {
|
||||
var data = new ReceivedMessage(map);
|
||||
var args = data.args;
|
||||
switch (data.method) {
|
||||
case "createRootHostView":
|
||||
case "createView":
|
||||
this._createViewHelper(args, data.method);
|
||||
break;
|
||||
case "destroyView":
|
||||
var viewRef = this._serializer.deserialize(args[0], RenderViewRef);
|
||||
this._renderer.destroyView(viewRef);
|
||||
break;
|
||||
case "attachFragmentAfterFragment":
|
||||
var previousFragment = this._serializer.deserialize(args[0], RenderFragmentRef);
|
||||
var fragment = this._serializer.deserialize(args[1], RenderFragmentRef);
|
||||
this._renderer.attachFragmentAfterFragment(previousFragment, fragment);
|
||||
break;
|
||||
case "attachFragmentAfterElement":
|
||||
var element = this._serializer.deserialize(args[0], WebWorkerElementRef);
|
||||
var fragment = this._serializer.deserialize(args[1], RenderFragmentRef);
|
||||
this._renderer.attachFragmentAfterElement(element, fragment);
|
||||
break;
|
||||
case "detachFragment":
|
||||
var fragment = this._serializer.deserialize(args[0], RenderFragmentRef);
|
||||
this._renderer.detachFragment(fragment);
|
||||
break;
|
||||
case "hydrateView":
|
||||
var viewRef = this._serializer.deserialize(args[0], RenderViewRef);
|
||||
this._renderer.hydrateView(viewRef);
|
||||
break;
|
||||
case "dehydrateView":
|
||||
var viewRef = this._serializer.deserialize(args[0], RenderViewRef);
|
||||
this._renderer.dehydrateView(viewRef);
|
||||
break;
|
||||
case "setText":
|
||||
var viewRef = this._serializer.deserialize(args[0], RenderViewRef);
|
||||
var textNodeIndex = args[1];
|
||||
var text = args[2];
|
||||
this._renderer.setText(viewRef, textNodeIndex, text);
|
||||
break;
|
||||
case "setElementProperty":
|
||||
var elementRef = this._serializer.deserialize(args[0], WebWorkerElementRef);
|
||||
var propName = args[1];
|
||||
var propValue = args[2];
|
||||
this._renderer.setElementProperty(elementRef, propName, propValue);
|
||||
break;
|
||||
case "setElementAttribute":
|
||||
var elementRef = this._serializer.deserialize(args[0], WebWorkerElementRef);
|
||||
var attributeName = args[1];
|
||||
var attributeValue = args[2];
|
||||
this._renderer.setElementAttribute(elementRef, attributeName, attributeValue);
|
||||
break;
|
||||
case "setElementClass":
|
||||
var elementRef = this._serializer.deserialize(args[0], WebWorkerElementRef);
|
||||
var className = args[1];
|
||||
var isAdd = args[2];
|
||||
this._renderer.setElementClass(elementRef, className, isAdd);
|
||||
break;
|
||||
case "setElementStyle":
|
||||
var elementRef = this._serializer.deserialize(args[0], WebWorkerElementRef);
|
||||
var styleName = args[1];
|
||||
var styleValue = args[2];
|
||||
this._renderer.setElementStyle(elementRef, styleName, styleValue);
|
||||
break;
|
||||
case "invokeElementMethod":
|
||||
var elementRef = this._serializer.deserialize(args[0], WebWorkerElementRef);
|
||||
var methodName = args[1];
|
||||
var methodArgs = args[2];
|
||||
this._renderer.invokeElementMethod(elementRef, methodName, methodArgs);
|
||||
break;
|
||||
case "setEventDispatcher":
|
||||
var viewRef = this._serializer.deserialize(args[0], RenderViewRef);
|
||||
var dispatcher =
|
||||
new EventDispatcher(viewRef, this._bus.to(EVENT_CHANNEL), this._serializer);
|
||||
this._renderer.setEventDispatcher(viewRef, dispatcher);
|
||||
break;
|
||||
default:
|
||||
throw new BaseException("Not Implemented");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import {SETUP_CHANNEL} from 'angular2/src/web-workers/shared/messaging_api';
|
||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {MessageBus} from 'angular2/src/web-workers/shared/message_bus';
|
||||
import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url';
|
||||
import {Injectable} from 'angular2/di';
|
||||
|
||||
@Injectable()
|
||||
export class WebWorkerSetup {
|
||||
constructor(bus: MessageBus, anchorBasedAppRootUrl: AnchorBasedAppRootUrl) {
|
||||
var rootUrl = anchorBasedAppRootUrl.value;
|
||||
var sink = bus.to(SETUP_CHANNEL);
|
||||
var source = bus.from(SETUP_CHANNEL);
|
||||
|
||||
ObservableWrapper.subscribe(source, (message: string) => {
|
||||
if (message === "ready") {
|
||||
ObservableWrapper.callNext(sink, {"rootUrl": rootUrl});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
import {Injectable} from 'angular2/di';
|
||||
import {MessageBus} from 'angular2/src/web-workers/shared/message_bus';
|
||||
import {Serializer} from 'angular2/src/web-workers/shared/serializer';
|
||||
import {EventEmitter, ObservableWrapper, PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {XHR_CHANNEL} from 'angular2/src/web-workers/shared/messaging_api';
|
||||
import {ReceivedMessage} from 'angular2/src/web-workers/ui/impl';
|
||||
import {BaseException, Type} from 'angular2/src/facade/lang';
|
||||
import {XHR} from 'angular2/src/render/xhr';
|
||||
|
||||
// TODO(jteplitz602): Create parent UIComponent class #3703
|
||||
@Injectable()
|
||||
export class MessageBasedXHRImpl {
|
||||
private _sink: EventEmitter;
|
||||
private _source: EventEmitter;
|
||||
|
||||
constructor(bus: MessageBus, private _serializer: Serializer, private _xhr: XHR) {
|
||||
this._sink = bus.to(XHR_CHANNEL);
|
||||
this._source = bus.from(XHR_CHANNEL);
|
||||
ObservableWrapper.subscribe(this._source,
|
||||
(message: StringMap<string, any>) => this._handleMessage(message));
|
||||
}
|
||||
|
||||
private _handleMessage(map: StringMap<string, any>) {
|
||||
var message = new ReceivedMessage(map);
|
||||
var args = message.args;
|
||||
switch (message.method) {
|
||||
case "get":
|
||||
var url = args[0];
|
||||
var promise = this._xhr.get(url);
|
||||
this._wrapWebWorkerPromise(message.id, promise, String);
|
||||
break;
|
||||
default:
|
||||
throw new BaseException(message.method + " Not Implemented");
|
||||
}
|
||||
}
|
||||
|
||||
private _wrapWebWorkerPromise(id: string, promise: Promise<any>, type: Type): void {
|
||||
PromiseWrapper.then(promise, (result: any) => {
|
||||
ObservableWrapper.callNext(
|
||||
this._sink,
|
||||
{'type': 'result', 'value': this._serializer.serialize(result, type), 'id': id});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
library angular2.src.web_workers.worker;
|
||||
|
||||
import "package:angular2/src/web-workers/shared/message_bus.dart"
|
||||
show MessageBus, MessageBusSource, MessageBusSink;
|
||||
import "package:angular2/src/web-workers/shared/isolate_message_bus.dart";
|
||||
import "package:angular2/src/web-workers/worker/application_common.dart"
|
||||
show bootstrapWebWorkerCommon;
|
||||
import "package:angular2/src/facade/async.dart" show Future;
|
||||
|
@ -26,56 +25,15 @@ Future<ApplicationRef> bootstrapWebWorker(
|
|||
SendPort replyTo, Type appComponentType,
|
||||
[List<dynamic> componentInjectableBindings = null]) {
|
||||
ReceivePort rPort = new ReceivePort();
|
||||
WebWorkerMessageBus bus = new WebWorkerMessageBus.fromPorts(replyTo, rPort);
|
||||
var sink = new WebWorkerMessageBusSink(replyTo, rPort);
|
||||
var source = new IsolateMessageBusSource(rPort);
|
||||
IsolateMessageBus bus = new IsolateMessageBus(sink, source);
|
||||
return bootstrapWebWorkerCommon(
|
||||
appComponentType, bus, componentInjectableBindings);
|
||||
}
|
||||
|
||||
class WebWorkerMessageBus extends MessageBus {
|
||||
final WebWorkerMessageBusSink sink;
|
||||
final WebWorkerMessageBusSource source;
|
||||
|
||||
WebWorkerMessageBus(this.sink, this.source);
|
||||
|
||||
WebWorkerMessageBus.fromPorts(SendPort sPort, ReceivePort rPort)
|
||||
: sink = new WebWorkerMessageBusSink(sPort, rPort),
|
||||
source = new WebWorkerMessageBusSource(rPort);
|
||||
}
|
||||
|
||||
class WebWorkerMessageBusSink extends MessageBusSink {
|
||||
final SendPort _port;
|
||||
|
||||
WebWorkerMessageBusSink(SendPort sPort, ReceivePort rPort) : _port = sPort {
|
||||
this.send(rPort.sendPort);
|
||||
}
|
||||
|
||||
void send(dynamic message) {
|
||||
this._port.send(message);
|
||||
}
|
||||
}
|
||||
|
||||
class WebWorkerMessageBusSource extends MessageBusSource {
|
||||
final ReceivePort _port;
|
||||
final Stream rawDataStream;
|
||||
Map<int, StreamSubscription> _listenerStore =
|
||||
new Map<int, StreamSubscription>();
|
||||
int _numListeners = 0;
|
||||
|
||||
WebWorkerMessageBusSource(ReceivePort rPort)
|
||||
: _port = rPort,
|
||||
rawDataStream = rPort.asBroadcastStream();
|
||||
|
||||
int addListener(Function fn) {
|
||||
var subscription = rawDataStream.listen((message) {
|
||||
fn({"data": message});
|
||||
});
|
||||
|
||||
_listenerStore[++_numListeners] = subscription;
|
||||
return _numListeners;
|
||||
}
|
||||
|
||||
void removeListener(int index) {
|
||||
_listenerStore[index].cancel();
|
||||
_listenerStore.remove(index);
|
||||
class WebWorkerMessageBusSink extends IsolateMessageBusSink {
|
||||
WebWorkerMessageBusSink(SendPort sPort, ReceivePort rPort) : super(sPort) {
|
||||
sPort.send(rPort.sendPort);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
import {
|
||||
MessageBus,
|
||||
MessageBusSource,
|
||||
MessageBusSink,
|
||||
SourceListener
|
||||
} from "angular2/src/web-workers/shared/message_bus";
|
||||
PostMessageBus,
|
||||
PostMessageBusSink,
|
||||
PostMessageBusSource
|
||||
} from 'angular2/src/web-workers/shared/post_message_bus';
|
||||
import {Type, BaseException} from "angular2/src/facade/lang";
|
||||
import {Binding} from "angular2/di";
|
||||
|
||||
import {Map} from 'angular2/src/facade/collection';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
import {bootstrapWebWorkerCommon} from "angular2/src/web-workers/worker/application_common";
|
||||
import {ApplicationRef} from "angular2/src/core/application";
|
||||
import {Injectable} from "angular2/di";
|
||||
|
||||
// TODO(jteplitz602) remove this and compile with lib.webworker.d.ts (#3492)
|
||||
var _postMessage: (message: any, transferrables?:[ArrayBuffer]) => void = <any>postMessage;
|
||||
interface PostMessageInterface {
|
||||
(message: any, transferrables?:[ArrayBuffer]): void;
|
||||
}
|
||||
var _postMessage: PostMessageInterface = <any>postMessage;
|
||||
|
||||
/**
|
||||
* Bootstrapping a Webworker Application
|
||||
|
@ -26,44 +29,12 @@ var _postMessage: (message: any, transferrables?:[ArrayBuffer]) => void = <any>p
|
|||
export function bootstrapWebWorker(
|
||||
appComponentType: Type, componentInjectableBindings: List<Type | Binding | List<any>> = null):
|
||||
Promise<ApplicationRef> {
|
||||
var bus: WebWorkerMessageBus =
|
||||
new WebWorkerMessageBus(new WebWorkerMessageBusSink(), new WebWorkerMessageBusSource());
|
||||
var sink = new PostMessageBusSink({
|
||||
postMessage:
|
||||
(message: any, transferrables?:[ArrayBuffer]) => { _postMessage(message, transferrables); }
|
||||
});
|
||||
var source = new PostMessageBusSource();
|
||||
var bus = new PostMessageBus(sink, source);
|
||||
|
||||
return bootstrapWebWorkerCommon(appComponentType, bus, componentInjectableBindings);
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class WebWorkerMessageBus implements MessageBus {
|
||||
sink: WebWorkerMessageBusSink;
|
||||
source: WebWorkerMessageBusSource;
|
||||
|
||||
constructor(sink: WebWorkerMessageBusSink, source: WebWorkerMessageBusSource) {
|
||||
this.sink = sink;
|
||||
this.source = source;
|
||||
}
|
||||
}
|
||||
|
||||
export class WebWorkerMessageBusSink implements MessageBusSink {
|
||||
public send(message: Object) { _postMessage(message); }
|
||||
}
|
||||
|
||||
export class WebWorkerMessageBusSource implements MessageBusSource {
|
||||
private listenerStore: Map<int, SourceListener>;
|
||||
private numListeners: int;
|
||||
|
||||
constructor() {
|
||||
this.numListeners = 0;
|
||||
this.listenerStore = new Map<int, SourceListener>();
|
||||
}
|
||||
|
||||
public addListener(fn: SourceListener): int {
|
||||
addEventListener("message", fn);
|
||||
this.listenerStore[++this.numListeners] = fn;
|
||||
return this.numListeners;
|
||||
}
|
||||
|
||||
public removeListener(index: int): void {
|
||||
removeEventListener("message", this.listenerStore[index]);
|
||||
this.listenerStore.delete(index);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,8 +52,8 @@ import {WebWorkerRenderer, WebWorkerCompiler} from './renderer';
|
|||
import {Renderer, RenderCompiler} from 'angular2/src/render/api';
|
||||
import {internalView} from 'angular2/src/core/compiler/view_ref';
|
||||
|
||||
import {MessageBroker} from 'angular2/src/web-workers/worker/broker';
|
||||
import {WebWorkerMessageBus} from 'angular2/src/web-workers/worker/application';
|
||||
import {MessageBrokerFactory} from 'angular2/src/web-workers/worker/broker';
|
||||
import {MessageBus, MessageBusInterface} from 'angular2/src/web-workers/shared/message_bus';
|
||||
import {APP_COMPONENT_REF_PROMISE, APP_COMPONENT} from 'angular2/src/core/application_tokens';
|
||||
import {ApplicationRef} from 'angular2/src/core/application';
|
||||
import {createNgZone} from 'angular2/src/core/application_common';
|
||||
|
@ -63,6 +63,9 @@ import {RenderProtoViewRefStore} from 'angular2/src/web-workers/shared/render_pr
|
|||
import {
|
||||
RenderViewWithFragmentsStore
|
||||
} from 'angular2/src/web-workers/shared/render_view_with_fragments_store';
|
||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {SETUP_CHANNEL} from 'angular2/src/web-workers/shared/messaging_api';
|
||||
import {WebWorkerEventDispatcher} from 'angular2/src/web-workers/worker/event_dispatcher';
|
||||
|
||||
var _rootInjector: Injector;
|
||||
|
||||
|
@ -75,7 +78,7 @@ class PrintLogger {
|
|||
logGroupEnd() {}
|
||||
}
|
||||
|
||||
function _injectorBindings(appComponentType, bus: WebWorkerMessageBus,
|
||||
function _injectorBindings(appComponentType, bus: MessageBusInterface,
|
||||
initData: StringMap<string, any>): List<Type | Binding | List<any>> {
|
||||
var bestChangeDetection: Type = DynamicChangeDetection;
|
||||
if (PreGeneratedChangeDetection.isSupported()) {
|
||||
|
@ -100,10 +103,8 @@ function _injectorBindings(appComponentType, bus: WebWorkerMessageBus,
|
|||
bind(LifeCycle).toFactory((exceptionHandler) => new LifeCycle(null, assertionsEnabled()),
|
||||
[ExceptionHandler]),
|
||||
Serializer,
|
||||
bind(WebWorkerMessageBus).toValue(bus),
|
||||
bind(MessageBroker)
|
||||
.toFactory((a, b, c) => new MessageBroker(a, b, c),
|
||||
[WebWorkerMessageBus, Serializer, NgZone]),
|
||||
bind(MessageBus).toValue(bus),
|
||||
MessageBrokerFactory,
|
||||
WebWorkerRenderer,
|
||||
bind(Renderer).toAlias(WebWorkerRenderer),
|
||||
WebWorkerCompiler,
|
||||
|
@ -136,12 +137,13 @@ function _injectorBindings(appComponentType, bus: WebWorkerMessageBus,
|
|||
StyleUrlResolver,
|
||||
DynamicComponentLoader,
|
||||
Testability,
|
||||
bind(AppRootUrl).toValue(new AppRootUrl(initData['rootUrl']))
|
||||
bind(AppRootUrl).toValue(new AppRootUrl(initData['rootUrl'])),
|
||||
WebWorkerEventDispatcher
|
||||
];
|
||||
}
|
||||
|
||||
export function bootstrapWebWorkerCommon(
|
||||
appComponentType: Type, bus: WebWorkerMessageBus,
|
||||
appComponentType: Type, bus: MessageBusInterface,
|
||||
componentInjectableBindings: List<Type | Binding | List<any>> = null): Promise<ApplicationRef> {
|
||||
var bootstrapProcess: PromiseCompleter<any> = PromiseWrapper.completer();
|
||||
|
||||
|
@ -151,14 +153,12 @@ export function bootstrapWebWorkerCommon(
|
|||
// index.html and main.js are possible.
|
||||
//
|
||||
|
||||
var listenerId: int;
|
||||
listenerId = bus.source.addListener((message: StringMap<string, any>) => {
|
||||
if (message["data"]["type"] !== "init") {
|
||||
return;
|
||||
}
|
||||
|
||||
var appInjector = _createAppInjector(appComponentType, componentInjectableBindings, zone, bus,
|
||||
message["data"]["value"]);
|
||||
var subscription: any;
|
||||
var emitter = bus.from(SETUP_CHANNEL);
|
||||
subscription = ObservableWrapper.subscribe(emitter, (message: StringMap<string, any>) => {
|
||||
var appInjector =
|
||||
_createAppInjector(appComponentType, componentInjectableBindings, zone, bus, message);
|
||||
var compRefToken = PromiseWrapper.wrap(() => {
|
||||
try {
|
||||
return appInjector.get(APP_COMPONENT_REF_PROMISE);
|
||||
|
@ -178,17 +178,17 @@ export function bootstrapWebWorkerCommon(
|
|||
PromiseWrapper.then(compRefToken, tick,
|
||||
(err, stackTrace) => { bootstrapProcess.reject(err, stackTrace); });
|
||||
|
||||
bus.source.removeListener(listenerId);
|
||||
ObservableWrapper.dispose(subscription);
|
||||
});
|
||||
|
||||
bus.sink.send({'type': "ready"});
|
||||
ObservableWrapper.callNext(bus.to(SETUP_CHANNEL), "ready");
|
||||
});
|
||||
|
||||
return bootstrapProcess.promise;
|
||||
}
|
||||
|
||||
function _createAppInjector(appComponentType: Type, bindings: List<Type | Binding | List<any>>,
|
||||
zone: NgZone, bus: WebWorkerMessageBus,
|
||||
zone: NgZone, bus: MessageBusInterface,
|
||||
initData: StringMap<string, any>): Injector {
|
||||
if (isBlank(_rootInjector)) _rootInjector = Injector.resolveAndCreate(_rootBindings);
|
||||
var mergedBindings: any[] =
|
||||
|
@ -197,4 +197,4 @@ function _createAppInjector(appComponentType: Type, bindings: List<Type | Bindin
|
|||
_injectorBindings(appComponentType, bus, initData);
|
||||
mergedBindings.push(bind(NgZone).toValue(zone));
|
||||
return _rootInjector.resolveAndCreateChild(mergedBindings);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,35 @@
|
|||
/// <reference path="../../../globals.d.ts" />
|
||||
import {MessageBus} from "angular2/src/web-workers/shared/message_bus";
|
||||
import {print, isPresent, DateWrapper, stringify} from "../../facade/lang";
|
||||
import {Promise, PromiseCompleter, PromiseWrapper} from "angular2/src/facade/async";
|
||||
import {
|
||||
Promise,
|
||||
PromiseCompleter,
|
||||
PromiseWrapper,
|
||||
ObservableWrapper,
|
||||
EventEmitter
|
||||
} from "angular2/src/facade/async";
|
||||
import {ListWrapper, StringMapWrapper, MapWrapper} from "../../facade/collection";
|
||||
import {Serializer} from "angular2/src/web-workers/shared/serializer";
|
||||
import {Injectable} from "angular2/di";
|
||||
import {Type} from "angular2/src/facade/lang";
|
||||
import {RenderViewRef, RenderEventDispatcher} from 'angular2/src/render/api';
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
import {deserializeGenericEvent} from './event_deserializer';
|
||||
|
||||
@Injectable()
|
||||
export class MessageBrokerFactory {
|
||||
constructor(private _messageBus: MessageBus, protected _serializer: Serializer) {}
|
||||
|
||||
createMessageBroker(channel: string): MessageBroker {
|
||||
return new MessageBroker(this._messageBus, this._serializer, channel);
|
||||
}
|
||||
}
|
||||
|
||||
export class MessageBroker {
|
||||
private _pending: Map<string, PromiseCompleter<any>> = new Map<string, PromiseCompleter<any>>();
|
||||
private _eventDispatchRegistry: Map<RenderViewRef, RenderEventDispatcher> =
|
||||
new Map<RenderViewRef, RenderEventDispatcher>();
|
||||
private _sink: EventEmitter;
|
||||
|
||||
constructor(private _messageBus: MessageBus, protected _serializer: Serializer,
|
||||
private _zone: NgZone) {
|
||||
this._messageBus.source.addListener((data) => this._handleMessage(data['data']));
|
||||
constructor(messageBus: MessageBus, protected _serializer: Serializer, public channel) {
|
||||
this._sink = messageBus.to(channel);
|
||||
var source = messageBus.from(channel);
|
||||
ObservableWrapper.subscribe(source, (message) => this._handleMessage(message));
|
||||
}
|
||||
|
||||
private _generateMessageId(name: string): string {
|
||||
|
@ -48,7 +59,7 @@ export class MessageBroker {
|
|||
var id: string = null;
|
||||
if (returnType != null) {
|
||||
var completer: PromiseCompleter<any> = PromiseWrapper.completer();
|
||||
id = this._generateMessageId(args.type + args.method);
|
||||
id = this._generateMessageId(args.method);
|
||||
this._pending.set(id, completer);
|
||||
PromiseWrapper.catchError(completer.promise, (err, stack?) => {
|
||||
print(err);
|
||||
|
@ -66,22 +77,20 @@ export class MessageBroker {
|
|||
promise = null;
|
||||
}
|
||||
|
||||
// TODO(jteplitz602): Create a class for these messages so we don't keep using StringMap
|
||||
var message = {'type': args.type, 'method': args.method, 'args': fnArgs};
|
||||
// TODO(jteplitz602): Create a class for these messages so we don't keep using StringMap #3685
|
||||
var message = {'method': args.method, 'args': fnArgs};
|
||||
if (id != null) {
|
||||
message['id'] = id;
|
||||
}
|
||||
this._messageBus.sink.send(message);
|
||||
ObservableWrapper.callNext(this._sink, message);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
private _handleMessage(message: StringMap<string, any>): void {
|
||||
var data = new MessageData(message);
|
||||
// TODO(jteplitz602): replace these strings with messaging constants
|
||||
if (data.type === "event") {
|
||||
this._dispatchEvent(new RenderEventData(data.value, this._serializer));
|
||||
} else if (data.type === "result" || data.type === "error") {
|
||||
// TODO(jteplitz602): replace these strings with messaging constants #3685
|
||||
if (data.type === "result" || data.type === "error") {
|
||||
var id = data.id;
|
||||
if (this._pending.has(id)) {
|
||||
if (data.type === "result") {
|
||||
|
@ -93,32 +102,6 @@ export class MessageBroker {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
registerEventDispatcher(viewRef: RenderViewRef, dispatcher: RenderEventDispatcher): void {
|
||||
this._eventDispatchRegistry.set(viewRef, dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
class RenderEventData {
|
||||
viewRef: RenderViewRef;
|
||||
elementIndex: number;
|
||||
eventName: string;
|
||||
locals: Map<string, any>;
|
||||
|
||||
constructor(message: StringMap<string, any>, serializer: Serializer) {
|
||||
this.viewRef = serializer.deserialize(message['viewRef'], RenderViewRef);
|
||||
this.elementIndex = message['elementIndex'];
|
||||
this.eventName = message['eventName'];
|
||||
this.locals = MapWrapper.createFromStringMap(message['locals']);
|
||||
}
|
||||
}
|
||||
|
||||
class MessageData {
|
||||
|
@ -149,5 +132,5 @@ export class FnArg {
|
|||
}
|
||||
|
||||
export class UiArguments {
|
||||
constructor(public type: string, public method: string, public args?: List<FnArg>) {}
|
||||
constructor(public method: string, public args?: List<FnArg>) {}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
import {Injectable} from 'angular2/di';
|
||||
import {Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {RenderViewRef, RenderEventDispatcher} from 'angular2/src/render/api';
|
||||
import {Serializer} from 'angular2/src/web-workers/shared/serializer';
|
||||
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/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) {
|
||||
var source = bus.from(EVENT_CHANNEL);
|
||||
ObservableWrapper.subscribe(
|
||||
source, (message) => this._dispatchEvent(new RenderEventData(message, _serializer)));
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
registerEventDispatcher(viewRef: RenderViewRef, dispatcher: RenderEventDispatcher): void {
|
||||
this._eventDispatchRegistry.set(viewRef, dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class RenderEventData {
|
||||
viewRef: RenderViewRef;
|
||||
elementIndex: number;
|
||||
eventName: string;
|
||||
locals: Map<string, any>;
|
||||
|
||||
constructor(message: StringMap<string, any>, serializer: Serializer) {
|
||||
this.viewRef = serializer.deserialize(message['viewRef'], RenderViewRef);
|
||||
this.elementIndex = message['elementIndex'];
|
||||
this.eventName = message['eventName'];
|
||||
this.locals = MapWrapper.createFromStringMap(message['locals']);
|
||||
}
|
||||
}
|
|
@ -13,7 +13,12 @@ import {
|
|||
RenderFragmentRef
|
||||
} from 'angular2/src/render/api';
|
||||
import {Promise, PromiseWrapper} from "angular2/src/facade/async";
|
||||
import {MessageBroker, FnArg, UiArguments} from "angular2/src/web-workers/worker/broker";
|
||||
import {
|
||||
MessageBroker,
|
||||
MessageBrokerFactory,
|
||||
FnArg,
|
||||
UiArguments
|
||||
} from "angular2/src/web-workers/worker/broker";
|
||||
import {isPresent, print, BaseException} from "angular2/src/facade/lang";
|
||||
import {Injectable} from "angular2/di";
|
||||
import {
|
||||
|
@ -21,16 +26,24 @@ import {
|
|||
WebWorkerRenderViewRef
|
||||
} from 'angular2/src/web-workers/shared/render_view_with_fragments_store';
|
||||
import {WebWorkerElementRef} from 'angular2/src/web-workers/shared/api';
|
||||
import {
|
||||
RENDER_COMPILER_CHANNEL,
|
||||
RENDERER_CHANNEL
|
||||
} from 'angular2/src/web-workers/shared/messaging_api';
|
||||
import {WebWorkerEventDispatcher} from 'angular2/src/web-workers/worker/event_dispatcher';
|
||||
|
||||
@Injectable()
|
||||
export class WebWorkerCompiler implements RenderCompiler {
|
||||
constructor(private _messageBroker: MessageBroker) {}
|
||||
private _messageBroker;
|
||||
constructor(messageBrokerFactory: MessageBrokerFactory) {
|
||||
this._messageBroker = messageBrokerFactory.createMessageBroker(RENDER_COMPILER_CHANNEL);
|
||||
}
|
||||
/**
|
||||
* Creats a ProtoViewDto that contains a single nested component with the given componentId.
|
||||
*/
|
||||
compileHost(directiveMetadata: RenderDirectiveMetadata): Promise<ProtoViewDto> {
|
||||
var fnArgs: List<FnArg> = [new FnArg(directiveMetadata, RenderDirectiveMetadata)];
|
||||
var args: UiArguments = new UiArguments("compiler", "compileHost", fnArgs);
|
||||
var args: UiArguments = new UiArguments("compileHost", fnArgs);
|
||||
return this._messageBroker.runOnUiThread(args, ProtoViewDto);
|
||||
}
|
||||
|
||||
|
@ -41,7 +54,7 @@ export class WebWorkerCompiler implements RenderCompiler {
|
|||
*/
|
||||
compile(view: ViewDefinition): Promise<ProtoViewDto> {
|
||||
var fnArgs: List<FnArg> = [new FnArg(view, ViewDefinition)];
|
||||
var args: UiArguments = new UiArguments("compiler", "compile", fnArgs);
|
||||
var args: UiArguments = new UiArguments("compile", fnArgs);
|
||||
return this._messageBroker.runOnUiThread(args, ProtoViewDto);
|
||||
}
|
||||
|
||||
|
@ -57,7 +70,7 @@ export class WebWorkerCompiler implements RenderCompiler {
|
|||
mergeProtoViewsRecursively(
|
||||
protoViewRefs: List<RenderProtoViewRef | List<any>>): Promise<RenderProtoViewMergeMapping> {
|
||||
var fnArgs: List<FnArg> = [new FnArg(protoViewRefs, RenderProtoViewRef)];
|
||||
var args: UiArguments = new UiArguments("compiler", "mergeProtoViewsRecursively", fnArgs);
|
||||
var args: UiArguments = new UiArguments("mergeProtoViewsRecursively", fnArgs);
|
||||
return this._messageBroker.runOnUiThread(args, RenderProtoViewMergeMapping);
|
||||
}
|
||||
}
|
||||
|
@ -65,8 +78,12 @@ export class WebWorkerCompiler implements RenderCompiler {
|
|||
|
||||
@Injectable()
|
||||
export class WebWorkerRenderer implements Renderer {
|
||||
constructor(private _messageBroker: MessageBroker,
|
||||
private _renderViewStore: RenderViewWithFragmentsStore) {}
|
||||
private _messageBroker;
|
||||
constructor(messageBrokerFactory: MessageBrokerFactory,
|
||||
private _renderViewStore: RenderViewWithFragmentsStore,
|
||||
private _eventDispatcher: WebWorkerEventDispatcher) {
|
||||
this._messageBroker = messageBrokerFactory.createMessageBroker(RENDERER_CHANNEL);
|
||||
}
|
||||
/**
|
||||
* Creates a root host view that includes the given element.
|
||||
* Note that the fragmentCount needs to be passed in so that we can create a result
|
||||
|
@ -108,7 +125,7 @@ export class WebWorkerRenderer implements Renderer {
|
|||
}
|
||||
fnArgs.push(new FnArg(startIndex, null));
|
||||
|
||||
var args = new UiArguments("renderer", method, fnArgs);
|
||||
var args = new UiArguments(method, fnArgs);
|
||||
this._messageBroker.runOnUiThread(args, null);
|
||||
|
||||
return renderViewWithFragments;
|
||||
|
@ -119,7 +136,7 @@ export class WebWorkerRenderer implements Renderer {
|
|||
*/
|
||||
destroyView(viewRef: RenderViewRef) {
|
||||
var fnArgs = [new FnArg(viewRef, RenderViewRef)];
|
||||
var args = new UiArguments("renderer", "destroyView", fnArgs);
|
||||
var args = new UiArguments("destroyView", fnArgs);
|
||||
this._messageBroker.runOnUiThread(args, null);
|
||||
}
|
||||
|
||||
|
@ -132,7 +149,7 @@ export class WebWorkerRenderer implements Renderer {
|
|||
new FnArg(previousFragmentRef, RenderFragmentRef),
|
||||
new FnArg(fragmentRef, RenderFragmentRef)
|
||||
];
|
||||
var args = new UiArguments("renderer", "attachFragmentAfterFragment", fnArgs);
|
||||
var args = new UiArguments("attachFragmentAfterFragment", fnArgs);
|
||||
this._messageBroker.runOnUiThread(args, null);
|
||||
}
|
||||
|
||||
|
@ -142,7 +159,7 @@ export class WebWorkerRenderer implements Renderer {
|
|||
attachFragmentAfterElement(elementRef: RenderElementRef, fragmentRef: RenderFragmentRef) {
|
||||
var fnArgs =
|
||||
[new FnArg(elementRef, WebWorkerElementRef), new FnArg(fragmentRef, RenderFragmentRef)];
|
||||
var args = new UiArguments("renderer", "attachFragmentAfterElement", fnArgs);
|
||||
var args = new UiArguments("attachFragmentAfterElement", fnArgs);
|
||||
this._messageBroker.runOnUiThread(args, null);
|
||||
}
|
||||
|
||||
|
@ -151,7 +168,7 @@ export class WebWorkerRenderer implements Renderer {
|
|||
*/
|
||||
detachFragment(fragmentRef: RenderFragmentRef) {
|
||||
var fnArgs = [new FnArg(fragmentRef, RenderFragmentRef)];
|
||||
var args = new UiArguments("renderer", "detachFragment", fnArgs);
|
||||
var args = new UiArguments("detachFragment", fnArgs);
|
||||
this._messageBroker.runOnUiThread(args, null);
|
||||
}
|
||||
|
||||
|
@ -161,7 +178,7 @@ export class WebWorkerRenderer implements Renderer {
|
|||
*/
|
||||
hydrateView(viewRef: RenderViewRef) {
|
||||
var fnArgs = [new FnArg(viewRef, RenderViewRef)];
|
||||
var args = new UiArguments("renderer", "hydrateView", fnArgs);
|
||||
var args = new UiArguments("hydrateView", fnArgs);
|
||||
this._messageBroker.runOnUiThread(args, null);
|
||||
}
|
||||
|
||||
|
@ -171,7 +188,7 @@ export class WebWorkerRenderer implements Renderer {
|
|||
*/
|
||||
dehydrateView(viewRef: RenderViewRef) {
|
||||
var fnArgs = [new FnArg(viewRef, RenderViewRef)];
|
||||
var args = new UiArguments("renderer", "dehydrateView", fnArgs);
|
||||
var args = new UiArguments("dehydrateView", fnArgs);
|
||||
this._messageBroker.runOnUiThread(args, null);
|
||||
}
|
||||
|
||||
|
@ -190,7 +207,7 @@ export class WebWorkerRenderer implements Renderer {
|
|||
new FnArg(propertyName, null),
|
||||
new FnArg(propertyValue, null)
|
||||
];
|
||||
var args = new UiArguments("renderer", "setElementProperty", fnArgs);
|
||||
var args = new UiArguments("setElementProperty", fnArgs);
|
||||
this._messageBroker.runOnUiThread(args, null);
|
||||
}
|
||||
|
||||
|
@ -203,7 +220,7 @@ export class WebWorkerRenderer implements Renderer {
|
|||
new FnArg(attributeName, null),
|
||||
new FnArg(attributeValue, null)
|
||||
];
|
||||
var args = new UiArguments("renderer", "setElementAttribute", fnArgs);
|
||||
var args = new UiArguments("setElementAttribute", fnArgs);
|
||||
this._messageBroker.runOnUiThread(args, null);
|
||||
}
|
||||
|
||||
|
@ -216,7 +233,7 @@ export class WebWorkerRenderer implements Renderer {
|
|||
new FnArg(className, null),
|
||||
new FnArg(isAdd, null)
|
||||
];
|
||||
var args = new UiArguments("renderer", "setElementClass", fnArgs);
|
||||
var args = new UiArguments("setElementClass", fnArgs);
|
||||
this._messageBroker.runOnUiThread(args, null);
|
||||
}
|
||||
|
||||
|
@ -229,7 +246,7 @@ export class WebWorkerRenderer implements Renderer {
|
|||
new FnArg(styleName, null),
|
||||
new FnArg(styleValue, null)
|
||||
];
|
||||
var args = new UiArguments("renderer", "setElementStyle", fnArgs);
|
||||
var args = new UiArguments("setElementStyle", fnArgs);
|
||||
this._messageBroker.runOnUiThread(args, null);
|
||||
}
|
||||
|
||||
|
@ -243,7 +260,7 @@ export class WebWorkerRenderer implements Renderer {
|
|||
new FnArg(methodName, null),
|
||||
new FnArg(args, null)
|
||||
];
|
||||
var uiArgs = new UiArguments("renderer", "invokeElementMethod", fnArgs);
|
||||
var uiArgs = new UiArguments("invokeElementMethod", fnArgs);
|
||||
this._messageBroker.runOnUiThread(uiArgs, null);
|
||||
}
|
||||
|
||||
|
@ -253,7 +270,7 @@ export class WebWorkerRenderer implements Renderer {
|
|||
setText(viewRef: RenderViewRef, textNodeIndex: number, text: string) {
|
||||
var fnArgs =
|
||||
[new FnArg(viewRef, RenderViewRef), new FnArg(textNodeIndex, null), new FnArg(text, null)];
|
||||
var args = new UiArguments("renderer", "setText", fnArgs);
|
||||
var args = new UiArguments("setText", fnArgs);
|
||||
this._messageBroker.runOnUiThread(args, null);
|
||||
}
|
||||
|
||||
|
@ -262,8 +279,8 @@ export class WebWorkerRenderer implements Renderer {
|
|||
*/
|
||||
setEventDispatcher(viewRef: RenderViewRef, dispatcher: RenderEventDispatcher) {
|
||||
var fnArgs = [new FnArg(viewRef, RenderViewRef)];
|
||||
var args = new UiArguments("renderer", "setEventDispatcher", fnArgs);
|
||||
this._messageBroker.registerEventDispatcher(viewRef, dispatcher);
|
||||
var args = new UiArguments("setEventDispatcher", fnArgs);
|
||||
this._eventDispatcher.registerEventDispatcher(viewRef, dispatcher);
|
||||
this._messageBroker.runOnUiThread(args, null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
import {Injectable} from 'angular2/di';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
import {XHR} from 'angular2/src/render/xhr';
|
||||
import {FnArg, UiArguments, MessageBroker} from 'angular2/src/web-workers/worker/broker';
|
||||
import {
|
||||
FnArg,
|
||||
UiArguments,
|
||||
MessageBroker,
|
||||
MessageBrokerFactory
|
||||
} from 'angular2/src/web-workers/worker/broker';
|
||||
import {XHR_CHANNEL} from 'angular2/src/web-workers/shared/messaging_api';
|
||||
|
||||
/**
|
||||
* Implementation of render/xhr that relays XHR requests to the UI side where they are sent
|
||||
|
@ -9,11 +15,16 @@ import {FnArg, UiArguments, MessageBroker} from 'angular2/src/web-workers/worker
|
|||
*/
|
||||
@Injectable()
|
||||
export class WebWorkerXHRImpl extends XHR {
|
||||
constructor(private _messageBroker: MessageBroker) { super(); }
|
||||
private _messageBroker: MessageBroker;
|
||||
|
||||
constructor(messageBrokerFactory: MessageBrokerFactory) {
|
||||
super();
|
||||
this._messageBroker = messageBrokerFactory.createMessageBroker(XHR_CHANNEL);
|
||||
}
|
||||
|
||||
get(url: string): Promise<string> {
|
||||
var fnArgs: List<FnArg> = [new FnArg(url, null)];
|
||||
var args: UiArguments = new UiArguments("xhr", "get", fnArgs);
|
||||
var args: UiArguments = new UiArguments("get", fnArgs);
|
||||
return this._messageBroker.runOnUiThread(args, String);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
import {
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
beforeEach,
|
||||
createTestInjector,
|
||||
beforeEachBindings,
|
||||
SpyObject,
|
||||
proxy
|
||||
} from 'angular2/test_lib';
|
||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {MessageBusInterface} from 'angular2/src/web-workers/shared/message_bus';
|
||||
import {createConnectedMessageBus} from './message_bus_util';
|
||||
|
||||
export function main() {
|
||||
/**
|
||||
* Tests the PostMessageBus in TypeScript and the IsolateMessageBus in Dart
|
||||
*/
|
||||
describe("MessageBus", () => {
|
||||
var bus: MessageBusInterface;
|
||||
|
||||
beforeEach(() => { bus = createConnectedMessageBus(); });
|
||||
|
||||
it("should pass messages in the same channel from sink to source",
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
const CHANNEL = "CHANNEL 1";
|
||||
const MESSAGE = "Test message";
|
||||
|
||||
var fromEmitter = bus.from(CHANNEL);
|
||||
ObservableWrapper.subscribe(fromEmitter, (message: any) => {
|
||||
expect(message).toEqual(MESSAGE);
|
||||
async.done();
|
||||
});
|
||||
var toEmitter = bus.to(CHANNEL);
|
||||
ObservableWrapper.callNext(toEmitter, MESSAGE);
|
||||
}));
|
||||
|
||||
it("should broadcast", inject([AsyncTestCompleter], (async) => {
|
||||
const CHANNEL = "CHANNEL 1";
|
||||
const MESSAGE = "TESTING";
|
||||
const NUM_LISTENERS = 2;
|
||||
|
||||
var callCount = 0;
|
||||
var emitHandler = (message: any) => {
|
||||
expect(message).toEqual(MESSAGE);
|
||||
callCount++;
|
||||
if (callCount == NUM_LISTENERS) {
|
||||
async.done();
|
||||
}
|
||||
};
|
||||
|
||||
for (var i = 0; i < NUM_LISTENERS; i++) {
|
||||
var emitter = bus.from(CHANNEL);
|
||||
ObservableWrapper.subscribe(emitter, emitHandler);
|
||||
}
|
||||
|
||||
var toEmitter = bus.to(CHANNEL);
|
||||
ObservableWrapper.callNext(toEmitter, MESSAGE);
|
||||
}));
|
||||
|
||||
it("should keep channels independent", inject([AsyncTestCompleter], (async) => {
|
||||
const CHANNEL_ONE = "CHANNEL 1";
|
||||
const CHANNEL_TWO = "CHANNEL 2";
|
||||
const MESSAGE_ONE = "This is a message on CHANNEL 1";
|
||||
const MESSAGE_TWO = "This is a message on CHANNEL 2";
|
||||
var callCount = 0;
|
||||
|
||||
var firstFromEmitter = bus.from(CHANNEL_ONE);
|
||||
ObservableWrapper.subscribe(firstFromEmitter, (message) => {
|
||||
expect(message).toEqual(MESSAGE_ONE);
|
||||
callCount++;
|
||||
if (callCount == 2) {
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
var secondFromEmitter = bus.from(CHANNEL_TWO);
|
||||
ObservableWrapper.subscribe(secondFromEmitter, (message) => {
|
||||
expect(message).toEqual(MESSAGE_TWO);
|
||||
callCount++;
|
||||
if (callCount == 2) {
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
|
||||
var firstToEmitter = bus.to(CHANNEL_ONE);
|
||||
ObservableWrapper.callNext(firstToEmitter, MESSAGE_ONE);
|
||||
|
||||
var secondToEmitter = bus.to(CHANNEL_TWO);
|
||||
ObservableWrapper.callNext(secondToEmitter, MESSAGE_TWO);
|
||||
}));
|
||||
});
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
library angular2.test.web_workers.shared.message_bus_util;
|
||||
|
||||
import 'dart:isolate';
|
||||
import 'package:angular2/src/web-workers/shared/message_bus.dart'
|
||||
show MessageBusInterface;
|
||||
import 'package:angular2/src/web-workers/shared/isolate_message_bus.dart';
|
||||
|
||||
/*
|
||||
* Returns an IsolateMessageBus thats sink is connected to its own source.
|
||||
* Useful for testing the sink and source.
|
||||
*/
|
||||
MessageBusInterface createConnectedMessageBus() {
|
||||
var receivePort = new ReceivePort();
|
||||
var sendPort = receivePort.sendPort;
|
||||
|
||||
var sink = new IsolateMessageBusSink(sendPort);
|
||||
var source = new IsolateMessageBusSource(receivePort);
|
||||
|
||||
return new IsolateMessageBus(sink, source);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
import {
|
||||
PostMessageBusSource,
|
||||
PostMessageBusSink,
|
||||
PostMessageBus
|
||||
} from 'angular2/src/web-workers/shared/post_message_bus';
|
||||
import {MessageBusInterface} from 'angular2/src/web-workers/shared/message_bus';
|
||||
|
||||
/*
|
||||
* Returns a PostMessageBus thats sink is connected to its own source.
|
||||
* Useful for testing the sink and source.
|
||||
*/
|
||||
export function createConnectedMessageBus(): MessageBusInterface {
|
||||
var mockPostMessage = new MockPostMessage();
|
||||
var source = new PostMessageBusSource(<any>mockPostMessage);
|
||||
var sink = new PostMessageBusSink(mockPostMessage);
|
||||
|
||||
return new PostMessageBus(sink, source);
|
||||
}
|
||||
|
||||
class MockPostMessage {
|
||||
private _listener: EventListener;
|
||||
|
||||
addEventListener(type: string, listener: EventListener, useCapture?: boolean): void {
|
||||
if (type === "message") {
|
||||
this._listener = listener;
|
||||
}
|
||||
}
|
||||
|
||||
postMessage(data: any, transfer?:[ArrayBuffer]): void { this._listener(<any>{data: data}); }
|
||||
}
|
|
@ -13,8 +13,6 @@ import {
|
|||
import {IMPLEMENTS} from 'angular2/src/facade/lang';
|
||||
import {Serializer} from 'angular2/src/web-workers/shared/serializer';
|
||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||
import {MessageBroker} from 'angular2/src/web-workers/worker/broker';
|
||||
import {MockMessageBus, MockMessageBusSink, MockMessageBusSource} from './worker_test_util';
|
||||
import {ON_WEB_WORKER} from 'angular2/src/web-workers/shared/api';
|
||||
import {bind} from 'angular2/di';
|
||||
import {RenderProtoViewRefStore} from 'angular2/src/web-workers/shared/render_proto_view_ref_store';
|
||||
|
@ -23,9 +21,13 @@ import {
|
|||
WebWorkerRenderViewRef
|
||||
} from 'angular2/src/web-workers/shared/render_view_with_fragments_store';
|
||||
import {RenderEventDispatcher, RenderViewRef} from 'angular2/src/render/api';
|
||||
import {createPairedMessageBuses} from './worker_test_util';
|
||||
import {WebWorkerEventDispatcher} from 'angular2/src/web-workers/worker/event_dispatcher';
|
||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {EVENT_CHANNEL} from 'angular2/src/web-workers/shared/messaging_api';
|
||||
|
||||
export function main() {
|
||||
describe("MessageBroker", () => {
|
||||
describe("EventDispatcher", () => {
|
||||
beforeEachBindings(() => [
|
||||
bind(ON_WEB_WORKER)
|
||||
.toValue(true),
|
||||
|
@ -33,47 +35,41 @@ export function main() {
|
|||
RenderViewWithFragmentsStore
|
||||
]);
|
||||
|
||||
it("should dispatch events", inject([Serializer, NgZone], (serializer, zone) => {
|
||||
var bus = new MockMessageBus(new MockMessageBusSink(), new MockMessageBusSource());
|
||||
var broker = new MessageBroker(bus, serializer, zone);
|
||||
|
||||
var eventDispatcher = new SpyEventDispatcher();
|
||||
var viewRef = new WebWorkerRenderViewRef(0);
|
||||
serializer.allocateRenderViews(0); // serialize the ref so it's in the store
|
||||
viewRef =
|
||||
serializer.deserialize(serializer.serialize(viewRef, RenderViewRef), RenderViewRef);
|
||||
broker.registerEventDispatcher(viewRef, eventDispatcher);
|
||||
it("should dispatch events",
|
||||
inject([Serializer, NgZone, AsyncTestCompleter], (serializer, zone, async) => {
|
||||
var messageBuses = createPairedMessageBuses();
|
||||
var webWorkerEventDispatcher =
|
||||
new WebWorkerEventDispatcher(messageBuses.worker, serializer, zone);
|
||||
|
||||
var elementIndex = 15;
|
||||
var eventName = 'click';
|
||||
|
||||
bus.source.receive({
|
||||
'data': {
|
||||
'type': 'event',
|
||||
'value': {
|
||||
'viewRef': viewRef.serialize(),
|
||||
'elementIndex': elementIndex,
|
||||
'eventName': eventName,
|
||||
'locals': {'$event': {'target': {value: null}}}
|
||||
}
|
||||
}
|
||||
var eventDispatcher = new SpyEventDispatcher((elementIndex, eventName, locals) => {
|
||||
expect(elementIndex).toEqual(elementIndex);
|
||||
expect(eventName).toEqual(eventName);
|
||||
async.done();
|
||||
});
|
||||
|
||||
expect(eventDispatcher.wasDispatched).toBeTruthy();
|
||||
expect(eventDispatcher.elementIndex).toEqual(elementIndex);
|
||||
expect(eventDispatcher.eventName).toEqual(eventName);
|
||||
var viewRef = new WebWorkerRenderViewRef(0);
|
||||
serializer.allocateRenderViews(0); // serialize the ref so it's in the store
|
||||
viewRef =
|
||||
serializer.deserialize(serializer.serialize(viewRef, RenderViewRef), RenderViewRef);
|
||||
webWorkerEventDispatcher.registerEventDispatcher(viewRef, eventDispatcher);
|
||||
|
||||
ObservableWrapper.callNext(messageBuses.ui.to(EVENT_CHANNEL), {
|
||||
'viewRef': viewRef.serialize(),
|
||||
'elementIndex': elementIndex,
|
||||
'eventName': eventName,
|
||||
'locals': {'$event': {'target': {value: null}}}
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
class SpyEventDispatcher implements RenderEventDispatcher {
|
||||
wasDispatched: boolean = false;
|
||||
elementIndex: number;
|
||||
eventName: string;
|
||||
constructor(private _callback: Function) {}
|
||||
|
||||
dispatchRenderEvent(elementIndex: number, eventName: string, locals: Map<string, any>) {
|
||||
this.wasDispatched = true;
|
||||
this.elementIndex = elementIndex;
|
||||
this.eventName = eventName;
|
||||
this._callback(elementIndex, eventName, locals);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
library angular2.test.web_workers.worker.mock_event_emitter;
|
||||
|
||||
import 'dart:core';
|
||||
import 'dart:async';
|
||||
import "package:angular2/src/facade/async.dart";
|
||||
|
||||
class MockEventEmitter extends EventEmitter {
|
||||
List<Function> _nextFns = new List();
|
||||
|
||||
@override
|
||||
StreamSubscription listen(void onData(dynamic line),
|
||||
{void onError(Error error), void onDone(), bool cancelOnError}) {
|
||||
_nextFns.add(onData);
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
void add(value) {
|
||||
_nextFns.forEach((fn) => fn(value));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import {EventEmitter} from 'angular2/src/facade/async';
|
||||
import * as Rx from 'rx';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
export class MockEventEmitter extends EventEmitter {
|
||||
private _nextFns: List<Function> = [];
|
||||
|
||||
constructor() { super(); }
|
||||
|
||||
observer(generator: any): Rx.IDisposable {
|
||||
this._nextFns.push(generator.next);
|
||||
return null;
|
||||
}
|
||||
|
||||
next(value: any) {
|
||||
ListWrapper.forEach(this._nextFns, (fn) => { fn(value); });
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
|
|||
import {DomTestbed, TestRootView, elRef} from '../../render/dom/dom_testbed';
|
||||
import {bind} from 'angular2/di';
|
||||
import {WebWorkerCompiler, WebWorkerRenderer} from "angular2/src/web-workers/worker/renderer";
|
||||
import {MessageBroker, UiArguments, FnArg} from "angular2/src/web-workers/worker/broker";
|
||||
import {MessageBrokerFactory, UiArguments, FnArg} from "angular2/src/web-workers/worker/broker";
|
||||
import {Serializer} from "angular2/src/web-workers/shared/serializer";
|
||||
import {isPresent, isBlank, BaseException, Type} from "angular2/src/facade/lang";
|
||||
import {MapWrapper, ListWrapper} from "angular2/src/facade/collection";
|
||||
|
@ -37,42 +37,47 @@ import {
|
|||
import {resolveInternalDomProtoView, DomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||
import {someComponent} from '../../render/dom/dom_renderer_integration_spec';
|
||||
import {WebWorkerMain} from 'angular2/src/web-workers/ui/impl';
|
||||
import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url';
|
||||
import {MockMessageBus, MockMessageBusSink, MockMessageBusSource} from './worker_test_util';
|
||||
import {MessageBasedRenderCompiler} from 'angular2/src/web-workers/ui/render_compiler';
|
||||
import {MessageBasedRenderer} from 'angular2/src/web-workers/ui/renderer';
|
||||
import {
|
||||
createPairedMessageBuses
|
||||
} from './worker_test_util'
|
||||
|
||||
export function main() {
|
||||
function createBroker(workerSerializer: Serializer, uiSerializer: Serializer, tb: DomTestbed,
|
||||
uiRenderViewStore: RenderViewWithFragmentsStore,
|
||||
workerRenderViewStore: RenderViewWithFragmentsStore): MessageBroker {
|
||||
// set up the two message buses to pass messages to each other
|
||||
var uiMessageBus = new MockMessageBus(new MockMessageBusSink(), new MockMessageBusSource());
|
||||
var workerMessageBus = new MockMessageBus(new MockMessageBusSink(), new MockMessageBusSource());
|
||||
uiMessageBus.attachToBus(workerMessageBus);
|
||||
workerMessageBus.attachToBus(uiMessageBus);
|
||||
export function
|
||||
main() {
|
||||
function createBrokerFactory(workerSerializer: Serializer, uiSerializer: Serializer,
|
||||
tb: DomTestbed, uiRenderViewStore: RenderViewWithFragmentsStore,
|
||||
workerRenderViewStore: RenderViewWithFragmentsStore):
|
||||
MessageBrokerFactory {
|
||||
var messageBuses = createPairedMessageBuses();
|
||||
var uiMessageBus = messageBuses.ui;
|
||||
var workerMessageBus = messageBuses.worker;
|
||||
|
||||
// set up the worker side
|
||||
var broker = new MessageBroker(workerMessageBus, workerSerializer, null);
|
||||
var brokerFactory = new MessageBrokerFactory(workerMessageBus, workerSerializer);
|
||||
|
||||
// set up the ui side
|
||||
var webWorkerMain = new WebWorkerMain(tb.compiler, tb.renderer, uiRenderViewStore, uiSerializer,
|
||||
new AnchorBasedAppRootUrl(), null);
|
||||
webWorkerMain.attachToWebWorker(uiMessageBus);
|
||||
return broker;
|
||||
var renderCompiler = new MessageBasedRenderCompiler(uiMessageBus, uiSerializer, tb.compiler);
|
||||
var renderer =
|
||||
new MessageBasedRenderer(uiMessageBus, uiSerializer, uiRenderViewStore, tb.renderer);
|
||||
new WebWorkerMain(renderCompiler, renderer, null, null);
|
||||
|
||||
return brokerFactory;
|
||||
}
|
||||
|
||||
function createWorkerRenderer(workerSerializer: Serializer, uiSerializer: Serializer,
|
||||
tb: DomTestbed, uiRenderViewStore: RenderViewWithFragmentsStore,
|
||||
workerRenderViewStore: RenderViewWithFragmentsStore):
|
||||
WebWorkerRenderer {
|
||||
var broker =
|
||||
createBroker(workerSerializer, uiSerializer, tb, uiRenderViewStore, workerRenderViewStore);
|
||||
return new WebWorkerRenderer(broker, workerRenderViewStore);
|
||||
var brokerFactory = createBrokerFactory(workerSerializer, uiSerializer, tb, uiRenderViewStore,
|
||||
workerRenderViewStore);
|
||||
return new WebWorkerRenderer(brokerFactory, workerRenderViewStore, null);
|
||||
}
|
||||
|
||||
function createWorkerCompiler(workerSerializer: Serializer, uiSerializer: Serializer,
|
||||
tb: DomTestbed): WebWorkerCompiler {
|
||||
var broker = createBroker(workerSerializer, uiSerializer, tb, null, null);
|
||||
return new WebWorkerCompiler(broker);
|
||||
var brokerFactory = createBrokerFactory(workerSerializer, uiSerializer, tb, null, null);
|
||||
return new WebWorkerCompiler(brokerFactory);
|
||||
}
|
||||
|
||||
describe("Web Worker Compiler", function() {
|
|
@ -1,36 +1,60 @@
|
|||
import {StringMap, StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {
|
||||
MessageBus,
|
||||
MessageBusSource,
|
||||
MessageBusInterface,
|
||||
MessageBusSink,
|
||||
SourceListener
|
||||
} from "angular2/src/web-workers/shared/message_bus";
|
||||
import {MapWrapper} from "angular2/src/facade/collection";
|
||||
MessageBusSource,
|
||||
MessageBus
|
||||
} from 'angular2/src/web-workers/shared/message_bus';
|
||||
import {MockEventEmitter} from './mock_event_emitter';
|
||||
|
||||
/**
|
||||
* Returns two MessageBus instances that are attached to each other.
|
||||
* Such that whatever goes into one's sink comes out the others source.
|
||||
*/
|
||||
export function createPairedMessageBuses(): PairedMessageBuses {
|
||||
var firstChannels: StringMap<string, MockEventEmitter> = {};
|
||||
var workerMessageBusSink = new MockMessageBusSink(firstChannels);
|
||||
var uiMessageBusSource = new MockMessageBusSource(firstChannels);
|
||||
|
||||
var secondChannels: StringMap<string, MockEventEmitter> = {};
|
||||
var uiMessageBusSink = new MockMessageBusSink(secondChannels);
|
||||
var workerMessageBusSource = new MockMessageBusSource(secondChannels);
|
||||
|
||||
return new PairedMessageBuses(new MockMessageBus(uiMessageBusSink, uiMessageBusSource),
|
||||
new MockMessageBus(workerMessageBusSink, workerMessageBusSource));
|
||||
}
|
||||
|
||||
export class PairedMessageBuses {
|
||||
constructor(public ui: MessageBusInterface, public worker: MessageBusInterface) {}
|
||||
}
|
||||
|
||||
export class MockMessageBusSource implements MessageBusSource {
|
||||
private _listenerStore: Map<int, SourceListener> = new Map<int, SourceListener>();
|
||||
private _numListeners: number = 0;
|
||||
constructor(private _channels: StringMap<string, MockEventEmitter>) {}
|
||||
|
||||
addListener(fn: SourceListener): int {
|
||||
this._listenerStore.set(++this._numListeners, fn);
|
||||
return this._numListeners;
|
||||
}
|
||||
|
||||
removeListener(index: int): void { MapWrapper.delete(this._listenerStore, index); }
|
||||
|
||||
receive(message: Object): void {
|
||||
MapWrapper.forEach(this._listenerStore, (fn: SourceListener, key: int) => { fn(message); });
|
||||
from(channel: string): MockEventEmitter {
|
||||
if (!StringMapWrapper.contains(this._channels, channel)) {
|
||||
this._channels[channel] = new MockEventEmitter();
|
||||
}
|
||||
return this._channels[channel];
|
||||
}
|
||||
}
|
||||
|
||||
export class MockMessageBusSink implements MessageBusSink {
|
||||
private _sendTo: MockMessageBusSource;
|
||||
constructor(private _channels: StringMap<string, MockEventEmitter>) {}
|
||||
|
||||
send(message: Object): void { this._sendTo.receive({'data': message}); }
|
||||
|
||||
attachToSource(source: MockMessageBusSource) { this._sendTo = source; }
|
||||
to(channel: string): MockEventEmitter {
|
||||
if (!StringMapWrapper.contains(this._channels, channel)) {
|
||||
this._channels[channel] = new MockEventEmitter();
|
||||
}
|
||||
return this._channels[channel];
|
||||
}
|
||||
}
|
||||
|
||||
export class MockMessageBus implements MessageBus {
|
||||
constructor(public sink: MockMessageBusSink, public source: MockMessageBusSource) {}
|
||||
attachToBus(bus: MockMessageBus) { this.sink.attachToSource(bus.source); }
|
||||
export class MockMessageBus extends MessageBus {
|
||||
constructor(public sink: MockMessageBusSink, public source: MockMessageBusSource) { super(); }
|
||||
|
||||
|
||||
to(channel: string): MockEventEmitter { return this.sink.to(channel); }
|
||||
|
||||
from(channel: string): MockEventEmitter { return this.source.from(channel); }
|
||||
}
|
||||
|
|
|
@ -11,7 +11,11 @@ import {
|
|||
proxy
|
||||
} from 'angular2/test_lib';
|
||||
import {IMPLEMENTS, Type} from 'angular2/src/facade/lang';
|
||||
import {MessageBroker, UiArguments} from 'angular2/src/web-workers/worker/broker';
|
||||
import {
|
||||
MessageBroker,
|
||||
UiArguments,
|
||||
MessageBrokerFactory
|
||||
} from 'angular2/src/web-workers/worker/broker';
|
||||
import {WebWorkerXHRImpl} from "angular2/src/web-workers/worker/xhr_impl";
|
||||
import {PromiseWrapper} from "angular2/src/facade/async";
|
||||
|
||||
|
@ -25,14 +29,13 @@ export function main() {
|
|||
var messageBroker: any = new SpyMessageBroker();
|
||||
messageBroker.spy("runOnUiThread")
|
||||
.andCallFake((args: UiArguments, returnType: Type) => {
|
||||
expect(args.type).toEqual("xhr");
|
||||
expect(args.method).toEqual("get");
|
||||
expect(args.args.length).toEqual(1);
|
||||
expect(args.args[0].value).toEqual(URL);
|
||||
return PromiseWrapper.wrap(() => { return RESPONSE; });
|
||||
});
|
||||
|
||||
var xhrImpl = new WebWorkerXHRImpl(messageBroker);
|
||||
var xhrImpl = new WebWorkerXHRImpl(new MockMessageBrokerFactory(messageBroker));
|
||||
xhrImpl.get(URL).then((response) => {
|
||||
expect(response).toEqual(RESPONSE);
|
||||
async.done();
|
||||
|
@ -47,3 +50,8 @@ class SpyMessageBroker extends SpyObject {
|
|||
constructor() { super(MessageBroker); }
|
||||
noSuchMethod(m) { return super.noSuchMethod(m); }
|
||||
}
|
||||
|
||||
class MockMessageBrokerFactory extends MessageBrokerFactory {
|
||||
constructor(private _messageBroker: MessageBroker) { super(null, null); }
|
||||
createMessageBroker(channel: string) { return this._messageBroker; }
|
||||
}
|
||||
|
|
|
@ -1,28 +1,29 @@
|
|||
library angular2.examples.message_broker.background_index;
|
||||
|
||||
import "package:angular2/src/web-workers/worker/application.dart"
|
||||
show WebWorkerMessageBus;
|
||||
import "package:angular2/src/web-workers/worker/broker.dart"
|
||||
show MessageBroker, UiArguments;
|
||||
import "package:angular2/src/web-workers/shared/serializer.dart"
|
||||
show Serializer;
|
||||
|
||||
import "package:angular2/src/web-workers/shared/isolate_message_bus.dart";
|
||||
import "package:angular2/src/web-workers/worker/application.dart"
|
||||
show WebWorkerMessageBusSink;
|
||||
import "package:angular2/src/facade/async.dart";
|
||||
import "dart:isolate";
|
||||
|
||||
main(List<String> args, SendPort replyTo) {
|
||||
ReceivePort rPort = new ReceivePort();
|
||||
WebWorkerMessageBus bus = new WebWorkerMessageBus.fromPorts(replyTo, rPort);
|
||||
bus.source.addListener((message) {
|
||||
if (identical(message['data']['type'], "echo")) {
|
||||
bus.sink
|
||||
.send({"type": "echo_response", "value": message['data']['value']});
|
||||
}
|
||||
var sink = new WebWorkerMessageBusSink(replyTo, rPort);
|
||||
var source = new IsolateMessageBusSource(rPort);
|
||||
IsolateMessageBus bus = new IsolateMessageBus(sink, source);
|
||||
|
||||
ObservableWrapper.subscribe(bus.from("echo"), (value) {
|
||||
ObservableWrapper.callNext(bus.to("echo"), value);
|
||||
});
|
||||
|
||||
MessageBroker broker =
|
||||
new MessageBroker(bus, new Serializer(null, null, null), null);
|
||||
var args = new UiArguments("test", "tester");
|
||||
new MessageBroker(bus, new Serializer(null, null, null), "test");
|
||||
var args = new UiArguments("tester");
|
||||
broker.runOnUiThread(args, String).then((data) {
|
||||
bus.sink.send({"type": "result", "value": data});
|
||||
ObservableWrapper.callNext(bus.to("result"), data);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,21 +1,30 @@
|
|||
import {
|
||||
WebWorkerMessageBus,
|
||||
WebWorkerMessageBusSource,
|
||||
WebWorkerMessageBusSink
|
||||
} from "angular2/src/web-workers/worker/application";
|
||||
PostMessageBus,
|
||||
PostMessageBusSink,
|
||||
PostMessageBusSource
|
||||
} from 'angular2/src/web-workers/shared/post_message_bus';
|
||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {MessageBroker, UiArguments} from "angular2/src/web-workers/worker/broker";
|
||||
import {Serializer} from "angular2/src/web-workers/shared/serializer";
|
||||
|
||||
export function main() {
|
||||
var bus = new WebWorkerMessageBus(new WebWorkerMessageBusSink(), new WebWorkerMessageBusSource());
|
||||
bus.source.addListener((message) => {
|
||||
if (message.data.type === "echo") {
|
||||
bus.sink.send({type: "echo_response", 'value': message.data.value});
|
||||
}
|
||||
});
|
||||
|
||||
var broker = new MessageBroker(bus, new Serializer(null, null, null), null);
|
||||
var args = new UiArguments("test", "tester");
|
||||
broker.runOnUiThread(args, String)
|
||||
.then((data: string) => { bus.sink.send({type: "result", value: data}); });
|
||||
interface PostMessageInterface {
|
||||
(message: any, transferrables?:[ArrayBuffer]): void;
|
||||
}
|
||||
var _postMessage: PostMessageInterface = <any>postMessage;
|
||||
|
||||
export function main() {
|
||||
var sink = new PostMessageBusSink({
|
||||
postMessage:
|
||||
(message: any, transferrables?:[ArrayBuffer]) => { _postMessage(message, transferrables); }
|
||||
});
|
||||
var source = new PostMessageBusSource();
|
||||
var bus = new PostMessageBus(sink, source);
|
||||
|
||||
ObservableWrapper.subscribe(bus.from("echo"),
|
||||
(value) => { ObservableWrapper.callNext(bus.to("echo"), value); });
|
||||
|
||||
var broker = new MessageBroker(bus, new Serializer(null, null, null), "test");
|
||||
var args = new UiArguments("tester");
|
||||
broker.runOnUiThread(args, String)
|
||||
.then((data: string) => { ObservableWrapper.callNext(bus.to("result"), data); });
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
library angular2.examples.message_broker.index;
|
||||
|
||||
import "package:angular2/src/web-workers/ui/application.dart"
|
||||
show spawnWebWorker, UIMessageBus, UIMessageBusSink, UIMessageBusSource;
|
||||
|
||||
show spawnWebWorker;
|
||||
import "package:angular2/src/facade/async.dart";
|
||||
import "dart:html";
|
||||
|
||||
main() {
|
||||
|
@ -10,21 +10,21 @@ main() {
|
|||
spawnWebWorker(Uri.parse("background_index.dart")).then((bus) {
|
||||
querySelector("#send_echo").addEventListener("click", (e) {
|
||||
var val = (querySelector("#echo_input") as InputElement).value;
|
||||
bus.sink.send({'type': 'echo', 'value': val});
|
||||
ObservableWrapper.callNext(bus.to("echo"), val);
|
||||
});
|
||||
bus.source.addListener((message) {
|
||||
var data = message['data'];
|
||||
if (identical(data['type'], "echo_response")) {
|
||||
querySelector("#echo_result")
|
||||
.appendHtml("<span class='response'>${data['value']}</span>");
|
||||
} else if (identical(data['type'], "test")) {
|
||||
bus.sink.send({'type': "result", 'id': data['id'], 'value': VALUE});
|
||||
} else if (identical(data['type'], "result")) {
|
||||
querySelector("#ui_result")
|
||||
.appendHtml("<span class='result'>${data['value']}</span>");
|
||||
} else if (identical(data['type'], "ready")) {
|
||||
bus.sink.send({'type': "init"});
|
||||
}
|
||||
|
||||
ObservableWrapper.subscribe(bus.from("echo"), (message) {
|
||||
querySelector("#echo_result")
|
||||
.appendHtml("<span class='response'>${message}</span>");
|
||||
});
|
||||
ObservableWrapper.subscribe(bus.from("result"), (message) {
|
||||
querySelector("#ui_result")
|
||||
.appendHtml("<span class='result'>${message}</span>");
|
||||
});
|
||||
ObservableWrapper.subscribe(bus.from("test"),
|
||||
(Map<String, dynamic> message) {
|
||||
ObservableWrapper.callNext(bus.to("test"),
|
||||
{'id': message['id'], 'type': "result", 'value': VALUE});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,29 +1,28 @@
|
|||
import {
|
||||
UIMessageBus,
|
||||
UIMessageBusSink,
|
||||
UIMessageBusSource
|
||||
} from "angular2/src/web-workers/ui/application";
|
||||
PostMessageBus,
|
||||
PostMessageBusSink,
|
||||
PostMessageBusSource
|
||||
} from 'angular2/src/web-workers/shared/post_message_bus';
|
||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
var worker = new Worker("loader.js");
|
||||
var bus = new UIMessageBus(new UIMessageBusSink(worker), new UIMessageBusSource(worker));
|
||||
var VALUE = 5;
|
||||
var webWorker = new Worker("loader.js");
|
||||
var sink = new PostMessageBusSink(webWorker);
|
||||
var source = new PostMessageBusSource(webWorker);
|
||||
var bus = new PostMessageBus(sink, source);
|
||||
const VALUE = 5;
|
||||
|
||||
document.getElementById("send_echo")
|
||||
.addEventListener("click", (e) => {
|
||||
var val = (<HTMLInputElement>document.getElementById("echo_input")).value;
|
||||
bus.sink.send({type: "echo", value: val});
|
||||
ObservableWrapper.callNext(bus.to("echo"), val);
|
||||
});
|
||||
|
||||
bus.source.addListener((message) => {
|
||||
if (message.data.type === "echo_response") {
|
||||
document.getElementById("echo_result").innerHTML =
|
||||
`<span class='response'>${message.data.value}</span>`;
|
||||
} else if (message.data.type === "test") {
|
||||
bus.sink.send({type: "result", id: message.data.id, value: VALUE});
|
||||
} else if (message.data.type == "result") {
|
||||
document.getElementById("ui_result").innerHTML =
|
||||
`<span class='result'>${message.data.value}</span>`;
|
||||
} else if (message.data.type == "ready") {
|
||||
bus.sink.send({type: "init"});
|
||||
}
|
||||
ObservableWrapper.subscribe(bus.from("echo"), (message) => {
|
||||
document.getElementById("echo_result").innerHTML = `<span class='response'>${message}</span>`;
|
||||
});
|
||||
ObservableWrapper.subscribe(bus.from("result"), (message) => {
|
||||
document.getElementById("ui_result").innerHTML = `<span class='result'>${message}</span>`;
|
||||
});
|
||||
ObservableWrapper.subscribe(bus.from("test"), (message: StringMap<string, any>) => {
|
||||
ObservableWrapper.callNext(bus.to("test"), {id: message['id'], type: "result", value: VALUE});
|
||||
});
|
||||
|
|
|
@ -5,19 +5,19 @@ import 'dart:typed_data';
|
|||
|
||||
// TODO(jteplitz602) Implement this class #3493
|
||||
class BitmapService {
|
||||
ImageData applySepia (ImageData imageData) {
|
||||
ImageData applySepia(ImageData imageData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String arrayBufferToDataUri (Uint8ClampedList data) {
|
||||
String arrayBufferToDataUri(Uint8ClampedList data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ImageData convertToImageData (ByteBuffer buffer) {
|
||||
ImageData convertToImageData(ByteBuffer buffer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String toDataUri (ImageData imageData) {
|
||||
String toDataUri(ImageData imageData) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue