From 21f60c5dcea83d5c0944101cb145446a790f20f9 Mon Sep 17 00:00:00 2001 From: Jason Teplitz Date: Fri, 21 Aug 2015 16:55:50 -0700 Subject: [PATCH] refactor(WebWorker): Abstract message passing and serialization to UIMessageBroker closes #3703 Closes #3815 --- .../client_message_broker.ts} | 8 +- .../src/web_workers/shared/serializer.ts | 8 +- .../shared/service_message_broker.ts | 78 ++++++++++ modules/angular2/src/web_workers/ui/bind.dart | 11 ++ modules/angular2/src/web_workers/ui/bind.ts | 3 + .../src/web_workers/ui/di_bindings.ts | 4 +- modules/angular2/src/web_workers/ui/impl.ts | 14 -- .../src/web_workers/ui/render_compiler.ts | 61 ++------ .../angular2/src/web_workers/ui/renderer.ts | 146 ++++++------------ .../angular2/src/web_workers/ui/xhr_impl.ts | 42 +---- .../web_workers/worker/application_common.ts | 4 +- .../src/web_workers/worker/renderer.ts | 10 +- .../src/web_workers/worker/xhr_impl.ts | 10 +- .../mock_event_emitter.dart | 0 .../{worker => shared}/mock_event_emitter.ts | 0 .../shared/service_message_broker_spec.ts | 69 +++++++++ .../web_worker_test_util.ts} | 0 .../worker/event_dispatcher_spec.ts | 2 +- .../worker/renderer_integration_spec.ts | 68 +++++--- .../test/web_workers/worker/xhr_impl_spec.ts | 14 +- .../src/message_broker/background_index.dart | 8 +- .../src/message_broker/background_index.ts | 7 +- 22 files changed, 319 insertions(+), 248 deletions(-) rename modules/angular2/src/web_workers/{worker/broker.ts => shared/client_message_broker.ts} (94%) create mode 100644 modules/angular2/src/web_workers/shared/service_message_broker.ts create mode 100644 modules/angular2/src/web_workers/ui/bind.dart create mode 100644 modules/angular2/src/web_workers/ui/bind.ts rename modules/angular2/test/web_workers/{worker => shared}/mock_event_emitter.dart (100%) rename modules/angular2/test/web_workers/{worker => shared}/mock_event_emitter.ts (100%) create mode 100644 modules/angular2/test/web_workers/shared/service_message_broker_spec.ts rename modules/angular2/test/web_workers/{worker/worker_test_util.ts => shared/web_worker_test_util.ts} (100%) diff --git a/modules/angular2/src/web_workers/worker/broker.ts b/modules/angular2/src/web_workers/shared/client_message_broker.ts similarity index 94% rename from modules/angular2/src/web_workers/worker/broker.ts rename to modules/angular2/src/web_workers/shared/client_message_broker.ts index bbeb85e988..3d98f388cb 100644 --- a/modules/angular2/src/web_workers/worker/broker.ts +++ b/modules/angular2/src/web_workers/shared/client_message_broker.ts @@ -14,15 +14,15 @@ import {Injectable} from "angular2/di"; import {Type} from "angular2/src/facade/lang"; @Injectable() -export class MessageBrokerFactory { +export class ClientMessageBrokerFactory { constructor(private _messageBus: MessageBus, protected _serializer: Serializer) {} - createMessageBroker(channel: string): MessageBroker { - return new MessageBroker(this._messageBus, this._serializer, channel); + createMessageBroker(channel: string): ClientMessageBroker { + return new ClientMessageBroker(this._messageBus, this._serializer, channel); } } -export class MessageBroker { +export class ClientMessageBroker { private _pending: Map> = new Map>(); private _sink: EventEmitter; diff --git a/modules/angular2/src/web_workers/shared/serializer.ts b/modules/angular2/src/web_workers/shared/serializer.ts index 4a476247e1..499cb00fc1 100644 --- a/modules/angular2/src/web_workers/shared/serializer.ts +++ b/modules/angular2/src/web_workers/shared/serializer.ts @@ -40,6 +40,10 @@ import { RenderViewWithFragmentsStore } from 'angular2/src/web_workers/shared/render_view_with_fragments_store'; +// PRIMITIVE is any type that does not need to be serialized (string, number, boolean) +// We set it to String so that it is considered a Type. +export const PRIMITIVE: Type = String; + @Injectable() export class Serializer { private _enumRegistry: Map>; @@ -76,7 +80,7 @@ export class Serializer { ListWrapper.forEach(obj, (val) => { serializedObj.push(this.serialize(val, type)); }); return serializedObj; } - if (type == String) { + if (type == PRIMITIVE) { return obj; } if (type == ViewDefinition) { @@ -119,7 +123,7 @@ export class Serializer { ListWrapper.forEach(map, (val) => { obj.push(this.deserialize(val, type, data)); }); return obj; } - if (type == String) { + if (type == PRIMITIVE) { return map; } diff --git a/modules/angular2/src/web_workers/shared/service_message_broker.ts b/modules/angular2/src/web_workers/shared/service_message_broker.ts new file mode 100644 index 0000000000..6cb60ff4bb --- /dev/null +++ b/modules/angular2/src/web_workers/shared/service_message_broker.ts @@ -0,0 +1,78 @@ +import {Injectable} from 'angular2/di'; +import {ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection'; +import {Serializer} from "angular2/src/web_workers/shared/serializer"; +import {isPresent, Type, FunctionWrapper} from "angular2/src/facade/lang"; +import {MessageBus} from "angular2/src/web_workers/shared/message_bus"; +import {EventEmitter, Promise, PromiseWrapper, ObservableWrapper} from 'angular2/src/facade/async'; + +@Injectable() +export class ServiceMessageBrokerFactory { + constructor(private _messageBus: MessageBus, protected _serializer: Serializer) {} + + createMessageBroker(channel: string): ServiceMessageBroker { + return new ServiceMessageBroker(this._messageBus, this._serializer, channel); + } +} + +/** + * Helper class for UIComponents that allows components to register methods. + * If a registered method message is received from the broker on the worker, + * the UIMessageBroker desererializes its arguments and calls the registered method. + * If that method returns a promise, the UIMessageBroker returns the result to the worker. + */ +export class ServiceMessageBroker { + private _sink: EventEmitter; + private _methods: Map = new Map(); + + constructor(messageBus: MessageBus, private _serializer: Serializer, public channel) { + this._sink = messageBus.to(channel); + var source = messageBus.from(channel); + ObservableWrapper.subscribe(source, (message) => this._handleMessage(message)); + } + + registerMethod(methodName: string, signature: List, method: Function, returnType?: Type): + void { + this._methods.set(methodName, (message: ReceivedMessage) => { + var serializedArgs = message.args; + var deserializedArgs: List = ListWrapper.createFixedSize(signature.length); + for (var i = 0; i < signature.length; i++) { + var serializedArg = serializedArgs[i]; + deserializedArgs[i] = this._serializer.deserialize(serializedArg, signature[i]); + } + + var promise = FunctionWrapper.apply(method, deserializedArgs); + if (isPresent(returnType) && isPresent(promise)) { + this._wrapWebWorkerPromise(message.id, promise, returnType); + } + }); + } + + private _handleMessage(map: StringMap): void { + var message = new ReceivedMessage(map); + if (this._methods.has(message.method)) { + this._methods.get(message.method)(message); + } + } + + private _wrapWebWorkerPromise(id: string, promise: Promise, type: Type): void { + PromiseWrapper.then(promise, (result: any) => { + ObservableWrapper.callNext( + this._sink, + {'type': 'result', 'value': this._serializer.serialize(result, type), 'id': id}); + }); + } +} + +export class ReceivedMessage { + method: string; + args: List; + id: string; + type: string; + + constructor(data: StringMap) { + this.method = data['method']; + this.args = data['args']; + this.id = data['id']; + this.type = data['type']; + } +} diff --git a/modules/angular2/src/web_workers/ui/bind.dart b/modules/angular2/src/web_workers/ui/bind.dart new file mode 100644 index 0000000000..0f738006d8 --- /dev/null +++ b/modules/angular2/src/web_workers/ui/bind.dart @@ -0,0 +1,11 @@ +library angular2.src.web_workers.ui.bind; + +/** + * Binding is not necessary in dart. + * This method just returns the passed function regardless of scope. + * It's only here to match the TypeScript implementation. + * TODO(jteplitz602) Have ts2dart remove calls to bind(#3820) + */ +Function bind(Function fn, dynamic scope) { + return fn; +} diff --git a/modules/angular2/src/web_workers/ui/bind.ts b/modules/angular2/src/web_workers/ui/bind.ts new file mode 100644 index 0000000000..f83db63ceb --- /dev/null +++ b/modules/angular2/src/web_workers/ui/bind.ts @@ -0,0 +1,3 @@ +export function bind(fn: Function, scope: any): Function { + return fn.bind(scope); +} diff --git a/modules/angular2/src/web_workers/ui/di_bindings.ts b/modules/angular2/src/web_workers/ui/di_bindings.ts index 79df3b79bf..75ed23b11f 100644 --- a/modules/angular2/src/web_workers/ui/di_bindings.ts +++ b/modules/angular2/src/web_workers/ui/di_bindings.ts @@ -65,6 +65,7 @@ import {MessageBasedRenderCompiler} from 'angular2/src/web_workers/ui/render_com 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'; +import {ServiceMessageBrokerFactory} from 'angular2/src/web_workers/shared/service_message_broker'; var _rootInjector: Injector; @@ -135,7 +136,8 @@ function _injectorBindings(): List { WebWorkerSetup, MessageBasedRenderCompiler, MessageBasedXHRImpl, - MessageBasedRenderer + MessageBasedRenderer, + ServiceMessageBrokerFactory ]; } diff --git a/modules/angular2/src/web_workers/ui/impl.ts b/modules/angular2/src/web_workers/ui/impl.ts index e38f362305..2347ed18a5 100644 --- a/modules/angular2/src/web_workers/ui/impl.ts +++ b/modules/angular2/src/web_workers/ui/impl.ts @@ -37,17 +37,3 @@ export class WebWorkerMain { public renderer: MessageBasedRenderer, public xhr: MessageBasedXHRImpl, public setup: WebWorkerSetup) {} } - -export class ReceivedMessage { - method: string; - args: List; - id: string; - type: string; - - constructor(data: StringMap) { - this.method = data['method']; - this.args = data['args']; - this.id = data['id']; - this.type = data['type']; - } -} diff --git a/modules/angular2/src/web_workers/ui/render_compiler.ts b/modules/angular2/src/web_workers/ui/render_compiler.ts index 6d1dad00d2..8330351945 100644 --- a/modules/angular2/src/web_workers/ui/render_compiler.ts +++ b/modules/angular2/src/web_workers/ui/render_compiler.ts @@ -1,6 +1,4 @@ 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, @@ -9,55 +7,22 @@ import { 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'; +import {bind} from './bind'; +import {ServiceMessageBrokerFactory} from 'angular2/src/web_workers/shared/service_message_broker'; -// 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) => this._handleMessage(message)); - } - - private _handleMessage(map: StringMap): void { - var message = new ReceivedMessage(map); - var args = message.args; - var promise: Promise; - 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, type: Type): void { - PromiseWrapper.then(promise, (result: any) => { - ObservableWrapper.callNext( - this._sink, - {'type': 'result', 'value': this._serializer.serialize(result, type), 'id': id}); - }); + constructor(brokerFactory: ServiceMessageBrokerFactory, private _renderCompiler: RenderCompiler) { + var broker = brokerFactory.createMessageBroker(RENDER_COMPILER_CHANNEL); + broker.registerMethod("compileHost", [RenderDirectiveMetadata], + bind(this._renderCompiler.compileHost, this._renderCompiler), + ProtoViewDto); + broker.registerMethod("compile", [ViewDefinition], + bind(this._renderCompiler.compile, this._renderCompiler), ProtoViewDto); + broker.registerMethod( + "mergeProtoViewsRecursively", [RenderProtoViewRef], + bind(this._renderCompiler.mergeProtoViewsRecursively, this._renderCompiler), + RenderProtoViewMergeMapping); } } diff --git a/modules/angular2/src/web_workers/ui/renderer.ts b/modules/angular2/src/web_workers/ui/renderer.ts index 8d0e0e2a47..21b253ea65 100644 --- a/modules/angular2/src/web_workers/ui/renderer.ts +++ b/modules/angular2/src/web_workers/ui/renderer.ts @@ -1,6 +1,6 @@ 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 {Serializer, PRIMITIVE} from 'angular2/src/web_workers/shared/serializer'; import { RenderViewRef, RenderFragmentRef, @@ -8,120 +8,68 @@ import { 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 {bind} from './bind'; import {EventDispatcher} from 'angular2/src/web_workers/ui/event_dispatcher'; import { RenderViewWithFragmentsStore } from 'angular2/src/web_workers/shared/render_view_with_fragments_store'; +import {ServiceMessageBrokerFactory} from 'angular2/src/web_workers/shared/service_message_broker'; @Injectable() export class MessageBasedRenderer { - constructor(private _bus: MessageBus, private _serializer: Serializer, + constructor(brokerFactory: ServiceMessageBrokerFactory, private _bus: MessageBus, + private _serializer: Serializer, private _renderViewWithFragmentsStore: RenderViewWithFragmentsStore, private _renderer: Renderer) { - var source = _bus.from(RENDERER_CHANNEL); - ObservableWrapper.subscribe(source, - (message: StringMap) => this._handleMessage(message)); + var broker = brokerFactory.createMessageBroker(RENDERER_CHANNEL); + broker.registerMethod("createRootHostView", + [RenderProtoViewRef, PRIMITIVE, PRIMITIVE, PRIMITIVE], + bind(this._createRootHostView, this)); + broker.registerMethod("createView", [RenderProtoViewRef, PRIMITIVE, PRIMITIVE], + bind(this._createView, this)); + broker.registerMethod("destroyView", [RenderViewRef], + bind(this._renderer.destroyView, this._renderer)); + broker.registerMethod("attachFragmentAfterFragment", [RenderFragmentRef, RenderFragmentRef], + bind(this._renderer.attachFragmentAfterFragment, this._renderer)); + broker.registerMethod("attachFragmentAfterElement", [WebWorkerElementRef, RenderFragmentRef], + bind(this._renderer.attachFragmentAfterElement, this._renderer)); + broker.registerMethod("detachFragment", [RenderFragmentRef], + bind(this._renderer.detachFragment, this._renderer)); + broker.registerMethod("hydrateView", [RenderViewRef], + bind(this._renderer.hydrateView, this._renderer)); + broker.registerMethod("dehydrateView", [RenderViewRef], + bind(this._renderer.dehydrateView, this._renderer)); + broker.registerMethod("setText", [RenderViewRef, PRIMITIVE, PRIMITIVE], + bind(this._renderer.setText, this._renderer)); + broker.registerMethod("setElementProperty", [WebWorkerElementRef, PRIMITIVE, PRIMITIVE], + bind(this._renderer.setElementProperty, this._renderer)); + broker.registerMethod("setElementAttribute", [WebWorkerElementRef, PRIMITIVE, PRIMITIVE], + bind(this._renderer.setElementAttribute, this._renderer)); + broker.registerMethod("setElementClass", [WebWorkerElementRef, PRIMITIVE, PRIMITIVE], + bind(this._renderer.setElementClass, this._renderer)); + broker.registerMethod("setElementStyle", [WebWorkerElementRef, PRIMITIVE, PRIMITIVE], + bind(this._renderer.setElementStyle, this._renderer)); + broker.registerMethod("invokeElementMethod", [WebWorkerElementRef, PRIMITIVE, PRIMITIVE], + bind(this._renderer.invokeElementMethod, this._renderer)); + broker.registerMethod("setEventDispatcher", [RenderViewRef], + bind(this._setEventDispatcher, this)); } - private _createViewHelper(args: List, 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); - } + private _createRootHostView(ref: RenderProtoViewRef, fragmentCount: number, selector: string, + startIndex: number) { + var renderViewWithFragments = this._renderer.createRootHostView(ref, fragmentCount, selector); this._renderViewWithFragmentsStore.store(renderViewWithFragments, startIndex); } + private _createView(ref: RenderProtoViewRef, fragmentCount: number, startIndex: number) { + var renderViewWithFragments = this._renderer.createView(ref, fragmentCount); + this._renderViewWithFragmentsStore.store(renderViewWithFragments, startIndex); + } - private _handleMessage(map: StringMap): 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"); - } + private _setEventDispatcher(viewRef: RenderViewRef) { + var dispatcher = new EventDispatcher(viewRef, this._bus.to(EVENT_CHANNEL), this._serializer); + this._renderer.setEventDispatcher(viewRef, dispatcher); } } diff --git a/modules/angular2/src/web_workers/ui/xhr_impl.ts b/modules/angular2/src/web_workers/ui/xhr_impl.ts index 83a8c2177f..a29dc34281 100644 --- a/modules/angular2/src/web_workers/ui/xhr_impl.ts +++ b/modules/angular2/src/web_workers/ui/xhr_impl.ts @@ -1,44 +1,14 @@ 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 {PRIMITIVE} from 'angular2/src/web_workers/shared/serializer'; 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'; +import {ServiceMessageBrokerFactory} from 'angular2/src/web_workers/shared/service_message_broker'; +import {bind} from './bind'; -// 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) => this._handleMessage(message)); - } - - private _handleMessage(map: StringMap) { - 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, type: Type): void { - PromiseWrapper.then(promise, (result: any) => { - ObservableWrapper.callNext( - this._sink, - {'type': 'result', 'value': this._serializer.serialize(result, type), 'id': id}); - }); + constructor(brokerFactory: ServiceMessageBrokerFactory, private _xhr: XHR) { + var broker = brokerFactory.createMessageBroker(XHR_CHANNEL); + broker.registerMethod("get", [PRIMITIVE], bind(this._xhr.get, this._xhr), PRIMITIVE); } } diff --git a/modules/angular2/src/web_workers/worker/application_common.ts b/modules/angular2/src/web_workers/worker/application_common.ts index a214291948..bb126d737d 100644 --- a/modules/angular2/src/web_workers/worker/application_common.ts +++ b/modules/angular2/src/web_workers/worker/application_common.ts @@ -52,7 +52,7 @@ import {WebWorkerRenderer, WebWorkerCompiler} from './renderer'; import {Renderer, RenderCompiler} from 'angular2/src/render/api'; import {internalView} from 'angular2/src/core/compiler/view_ref'; -import {MessageBrokerFactory} from 'angular2/src/web_workers/worker/broker'; +import {ClientMessageBrokerFactory} from 'angular2/src/web_workers/shared/client_message_broker'; import {MessageBus} 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_ref'; @@ -103,7 +103,7 @@ function _injectorBindings(appComponentType, bus: MessageBus, initData: StringMa [ExceptionHandler]), Serializer, bind(MessageBus).toValue(bus), - MessageBrokerFactory, + ClientMessageBrokerFactory, WebWorkerRenderer, bind(Renderer).toAlias(WebWorkerRenderer), WebWorkerCompiler, diff --git a/modules/angular2/src/web_workers/worker/renderer.ts b/modules/angular2/src/web_workers/worker/renderer.ts index 233c74948a..6fa225ecd3 100644 --- a/modules/angular2/src/web_workers/worker/renderer.ts +++ b/modules/angular2/src/web_workers/worker/renderer.ts @@ -14,11 +14,11 @@ import { } from 'angular2/src/render/api'; import {Promise, PromiseWrapper} from "angular2/src/facade/async"; import { - MessageBroker, - MessageBrokerFactory, + ClientMessageBroker, + ClientMessageBrokerFactory, FnArg, UiArguments -} from "angular2/src/web_workers/worker/broker"; +} from "angular2/src/web_workers/shared/client_message_broker"; import {isPresent, print, BaseException} from "angular2/src/facade/lang"; import {Injectable} from "angular2/di"; import { @@ -35,7 +35,7 @@ import {WebWorkerEventDispatcher} from 'angular2/src/web_workers/worker/event_di @Injectable() export class WebWorkerCompiler implements RenderCompiler { private _messageBroker; - constructor(messageBrokerFactory: MessageBrokerFactory) { + constructor(messageBrokerFactory: ClientMessageBrokerFactory) { this._messageBroker = messageBrokerFactory.createMessageBroker(RENDER_COMPILER_CHANNEL); } /** @@ -79,7 +79,7 @@ export class WebWorkerCompiler implements RenderCompiler { @Injectable() export class WebWorkerRenderer implements Renderer { private _messageBroker; - constructor(messageBrokerFactory: MessageBrokerFactory, + constructor(messageBrokerFactory: ClientMessageBrokerFactory, private _renderViewStore: RenderViewWithFragmentsStore, private _eventDispatcher: WebWorkerEventDispatcher) { this._messageBroker = messageBrokerFactory.createMessageBroker(RENDERER_CHANNEL); diff --git a/modules/angular2/src/web_workers/worker/xhr_impl.ts b/modules/angular2/src/web_workers/worker/xhr_impl.ts index 5b4a538b36..1675ba410a 100644 --- a/modules/angular2/src/web_workers/worker/xhr_impl.ts +++ b/modules/angular2/src/web_workers/worker/xhr_impl.ts @@ -4,9 +4,9 @@ import {XHR} from 'angular2/src/render/xhr'; import { FnArg, UiArguments, - MessageBroker, - MessageBrokerFactory -} from 'angular2/src/web_workers/worker/broker'; + ClientMessageBroker, + ClientMessageBrokerFactory +} from 'angular2/src/web_workers/shared/client_message_broker'; import {XHR_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api'; /** @@ -15,9 +15,9 @@ import {XHR_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api'; */ @Injectable() export class WebWorkerXHRImpl extends XHR { - private _messageBroker: MessageBroker; + private _messageBroker: ClientMessageBroker; - constructor(messageBrokerFactory: MessageBrokerFactory) { + constructor(messageBrokerFactory: ClientMessageBrokerFactory) { super(); this._messageBroker = messageBrokerFactory.createMessageBroker(XHR_CHANNEL); } diff --git a/modules/angular2/test/web_workers/worker/mock_event_emitter.dart b/modules/angular2/test/web_workers/shared/mock_event_emitter.dart similarity index 100% rename from modules/angular2/test/web_workers/worker/mock_event_emitter.dart rename to modules/angular2/test/web_workers/shared/mock_event_emitter.dart diff --git a/modules/angular2/test/web_workers/worker/mock_event_emitter.ts b/modules/angular2/test/web_workers/shared/mock_event_emitter.ts similarity index 100% rename from modules/angular2/test/web_workers/worker/mock_event_emitter.ts rename to modules/angular2/test/web_workers/shared/mock_event_emitter.ts diff --git a/modules/angular2/test/web_workers/shared/service_message_broker_spec.ts b/modules/angular2/test/web_workers/shared/service_message_broker_spec.ts new file mode 100644 index 0000000000..9ead3fcbb5 --- /dev/null +++ b/modules/angular2/test/web_workers/shared/service_message_broker_spec.ts @@ -0,0 +1,69 @@ +import { + AsyncTestCompleter, + inject, + describe, + it, + expect, + beforeEach, + createTestInjector, + beforeEachBindings, + SpyObject, + proxy +} from 'angular2/test_lib'; +import {createPairedMessageBuses} from '../shared/web_worker_test_util'; +import {Serializer, PRIMITIVE} from 'angular2/src/web_workers/shared/serializer'; +import {ServiceMessageBroker} from 'angular2/src/web_workers/shared/service_message_broker'; +import {ObservableWrapper, PromiseWrapper} from 'angular2/src/facade/async'; +import {bind} from 'angular2/di'; +import {ON_WEB_WORKER} from 'angular2/src/web_workers/shared/api'; +import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_proto_view_ref_store'; +import { + RenderViewWithFragmentsStore +} from 'angular2/src/web_workers/shared/render_view_with_fragments_store'; + +export function main() { + const CHANNEL = "UIMessageBroker Test Channel"; + const TEST_METHOD = "TEST_METHOD"; + const PASSED_ARG_1 = 5; + const PASSED_ARG_2 = 'TEST'; + const RESULT = 20; + const ID = "methodId"; + + beforeEachBindings(() => [ + bind(ON_WEB_WORKER) + .toValue(true), + RenderProtoViewRefStore, + RenderViewWithFragmentsStore + ]); + + describe("UIMessageBroker", () => { + var messageBuses; + + beforeEach(() => { messageBuses = createPairedMessageBuses(); }); + it("should call registered method with correct arguments", + inject([Serializer], (serializer) => { + var broker = new ServiceMessageBroker(messageBuses.ui, serializer, CHANNEL); + broker.registerMethod(TEST_METHOD, [PRIMITIVE, PRIMITIVE], (arg1, arg2) => { + expect(arg1).toEqual(PASSED_ARG_1); + expect(arg2).toEqual(PASSED_ARG_2); + }); + ObservableWrapper.callNext(messageBuses.worker.to(CHANNEL), + {'method': TEST_METHOD, 'args': [PASSED_ARG_1, PASSED_ARG_2]}); + })); + + it("should return promises to the worker", inject([Serializer], (serializer) => { + var broker = new ServiceMessageBroker(messageBuses.ui, serializer, CHANNEL); + broker.registerMethod(TEST_METHOD, [PRIMITIVE], (arg1) => { + expect(arg1).toEqual(PASSED_ARG_1); + return PromiseWrapper.wrap(() => { return RESULT; }); + }); + ObservableWrapper.callNext(messageBuses.worker.to(CHANNEL), + {'method': TEST_METHOD, 'id': ID, 'args': [PASSED_ARG_1]}); + ObservableWrapper.subscribe(messageBuses.worker.from(CHANNEL), (data: any) => { + expect(data.type).toEqual("result"); + expect(data.id).toEqual(ID); + expect(data.value).toEqual(RESULT); + }); + })); + }); +} diff --git a/modules/angular2/test/web_workers/worker/worker_test_util.ts b/modules/angular2/test/web_workers/shared/web_worker_test_util.ts similarity index 100% rename from modules/angular2/test/web_workers/worker/worker_test_util.ts rename to modules/angular2/test/web_workers/shared/web_worker_test_util.ts diff --git a/modules/angular2/test/web_workers/worker/event_dispatcher_spec.ts b/modules/angular2/test/web_workers/worker/event_dispatcher_spec.ts index 3a9c53b61a..05b829bef7 100644 --- a/modules/angular2/test/web_workers/worker/event_dispatcher_spec.ts +++ b/modules/angular2/test/web_workers/worker/event_dispatcher_spec.ts @@ -21,7 +21,7 @@ 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 {createPairedMessageBuses} from '../shared/web_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'; diff --git a/modules/angular2/test/web_workers/worker/renderer_integration_spec.ts b/modules/angular2/test/web_workers/worker/renderer_integration_spec.ts index 5a9774f781..2ad0c485ec 100644 --- a/modules/angular2/test/web_workers/worker/renderer_integration_spec.ts +++ b/modules/angular2/test/web_workers/worker/renderer_integration_spec.ts @@ -12,7 +12,11 @@ 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 {MessageBrokerFactory, UiArguments, FnArg} from "angular2/src/web_workers/worker/broker"; +import { + ClientMessageBrokerFactory, + UiArguments, + FnArg +} from "angular2/src/web_workers/shared/client_message_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"; @@ -39,44 +43,44 @@ import {someComponent} from '../../render/dom/dom_renderer_integration_spec'; import {WebWorkerMain} from 'angular2/src/web_workers/ui/impl'; 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' +import {createPairedMessageBuses} from '../shared/web_worker_test_util'; +import {ServiceMessageBrokerFactory} from 'angular2/src/web_workers/shared/service_message_broker'; - export function - main() { - function createBrokerFactory(workerSerializer: Serializer, uiSerializer: Serializer, - tb: DomTestbed, uiRenderViewStore: RenderViewWithFragmentsStore, - workerRenderViewStore: RenderViewWithFragmentsStore): - MessageBrokerFactory { +export function main() { + function createWebWorkerBrokerFactory( + workerSerializer: Serializer, uiSerializer: Serializer, tb: DomTestbed, + uiRenderViewStore: RenderViewWithFragmentsStore, + workerRenderViewStore: RenderViewWithFragmentsStore): ClientMessageBrokerFactory { var messageBuses = createPairedMessageBuses(); var uiMessageBus = messageBuses.ui; var workerMessageBus = messageBuses.worker; // set up the worker side - var brokerFactory = new MessageBrokerFactory(workerMessageBus, workerSerializer); + var webWorkerBrokerFactory = new ClientMessageBrokerFactory(workerMessageBus, workerSerializer); // set up the ui side - var renderCompiler = new MessageBasedRenderCompiler(uiMessageBus, uiSerializer, tb.compiler); - var renderer = - new MessageBasedRenderer(uiMessageBus, uiSerializer, uiRenderViewStore, tb.renderer); + var uiMessageBrokerFactory = new ServiceMessageBrokerFactory(uiMessageBus, uiSerializer); + var renderCompiler = new MessageBasedRenderCompiler(uiMessageBrokerFactory, tb.compiler); + var renderer = new MessageBasedRenderer(uiMessageBrokerFactory, uiMessageBus, uiSerializer, + uiRenderViewStore, tb.renderer); new WebWorkerMain(renderCompiler, renderer, null, null); - return brokerFactory; + return webWorkerBrokerFactory; } function createWorkerRenderer(workerSerializer: Serializer, uiSerializer: Serializer, tb: DomTestbed, uiRenderViewStore: RenderViewWithFragmentsStore, workerRenderViewStore: RenderViewWithFragmentsStore): WebWorkerRenderer { - var brokerFactory = createBrokerFactory(workerSerializer, uiSerializer, tb, uiRenderViewStore, - workerRenderViewStore); + var brokerFactory = createWebWorkerBrokerFactory(workerSerializer, uiSerializer, tb, + uiRenderViewStore, workerRenderViewStore); return new WebWorkerRenderer(brokerFactory, workerRenderViewStore, null); } function createWorkerCompiler(workerSerializer: Serializer, uiSerializer: Serializer, tb: DomTestbed): WebWorkerCompiler { - var brokerFactory = createBrokerFactory(workerSerializer, uiSerializer, tb, null, null); + var brokerFactory = + createWebWorkerBrokerFactory(workerSerializer, uiSerializer, tb, null, null); return new WebWorkerCompiler(brokerFactory); } @@ -232,6 +236,34 @@ import { }); })); + it('should add and remove fragments', inject([AsyncTestCompleter], (async) => { + tb.compileAndMerge(someComponent, + [ + new ViewDefinition({ + componentId: 'someComponent', + template: '', + directives: [] + }) + ]) + .then((protoViewMergeMappings) => { + protoViewMergeMappings = + serialize(protoViewMergeMappings, RenderProtoViewMergeMapping); + var rootViewWithFragments = + renderer.createView(protoViewMergeMappings.mergedProtoViewRef, 2); + + var elr = elRef(rootViewWithFragments.viewRef, 1); + var rootView = new WorkerTestRootView(rootViewWithFragments, uiRenderViewStore); + expect(rootView.hostElement).toHaveText(''); + var fragment = rootViewWithFragments.fragmentRefs[1]; + renderer.attachFragmentAfterElement(elr, fragment); + expect(rootView.hostElement).toHaveText('hello'); + renderer.detachFragment(fragment); + expect(rootView.hostElement).toHaveText(''); + + async.done(); + }); + })); + it('should add and remove empty fragments', inject([AsyncTestCompleter], (async) => { tb.compileAndMerge(someComponent, [ diff --git a/modules/angular2/test/web_workers/worker/xhr_impl_spec.ts b/modules/angular2/test/web_workers/worker/xhr_impl_spec.ts index 56bb28247b..c8963ab253 100644 --- a/modules/angular2/test/web_workers/worker/xhr_impl_spec.ts +++ b/modules/angular2/test/web_workers/worker/xhr_impl_spec.ts @@ -12,10 +12,10 @@ import { } from 'angular2/test_lib'; import {IMPLEMENTS, Type} from 'angular2/src/facade/lang'; import { - MessageBroker, + ClientMessageBroker, UiArguments, - MessageBrokerFactory -} from 'angular2/src/web_workers/worker/broker'; + ClientMessageBrokerFactory +} from 'angular2/src/web_workers/shared/client_message_broker'; import {WebWorkerXHRImpl} from "angular2/src/web_workers/worker/xhr_impl"; import {PromiseWrapper} from "angular2/src/facade/async"; @@ -45,13 +45,13 @@ export function main() { } @proxy -@IMPLEMENTS(MessageBroker) +@IMPLEMENTS(ClientMessageBroker) class SpyMessageBroker extends SpyObject { - constructor() { super(MessageBroker); } + constructor() { super(ClientMessageBroker); } noSuchMethod(m) { return super.noSuchMethod(m); } } -class MockMessageBrokerFactory extends MessageBrokerFactory { - constructor(private _messageBroker: MessageBroker) { super(null, null); } +class MockMessageBrokerFactory extends ClientMessageBrokerFactory { + constructor(private _messageBroker: ClientMessageBroker) { super(null, null); } createMessageBroker(channel: string) { return this._messageBroker; } } diff --git a/modules/examples/src/message_broker/background_index.dart b/modules/examples/src/message_broker/background_index.dart index dce4e581da..b8c55a4df7 100644 --- a/modules/examples/src/message_broker/background_index.dart +++ b/modules/examples/src/message_broker/background_index.dart @@ -1,7 +1,7 @@ library angular2.examples.message_broker.background_index; -import "package:angular2/src/web_workers/worker/broker.dart" - show MessageBroker, UiArguments; +import "package:angular2/src/web_workers/shared/client_message_broker.dart" + show ClientMessageBroker, UiArguments; import "package:angular2/src/web_workers/shared/serializer.dart" show Serializer; import "package:angular2/src/web_workers/shared/isolate_message_bus.dart"; @@ -20,8 +20,8 @@ main(List args, SendPort replyTo) { ObservableWrapper.callNext(bus.to("echo"), value); }); - MessageBroker broker = - new MessageBroker(bus, new Serializer(null, null, null), "test"); + ClientMessageBroker broker = + new ClientMessageBroker(bus, new Serializer(null, null, null), "test"); var args = new UiArguments("tester"); broker.runOnUiThread(args, String).then((data) { ObservableWrapper.callNext(bus.to("result"), data); diff --git a/modules/examples/src/message_broker/background_index.ts b/modules/examples/src/message_broker/background_index.ts index 908cb21bce..ce3954b474 100644 --- a/modules/examples/src/message_broker/background_index.ts +++ b/modules/examples/src/message_broker/background_index.ts @@ -4,7 +4,10 @@ import { 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 { + ClientMessageBroker, + UiArguments +} from "angular2/src/web_workers/shared/client_message_broker"; import {Serializer} from "angular2/src/web_workers/shared/serializer"; interface PostMessageInterface { @@ -23,7 +26,7 @@ export function main() { ObservableWrapper.subscribe(bus.from("echo"), (value) => { ObservableWrapper.callNext(bus.to("echo"), value); }); - var broker = new MessageBroker(bus, new Serializer(null, null, null), "test"); + var broker = new ClientMessageBroker(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); });