feat(web-workers) Add WebWorker Renderer

Allows angular apps to be rendered from the webworker!
Closes #3052, #3053, and #3097
This commit is contained in:
Jason Teplitz
2015-07-10 16:09:18 -07:00
parent 21b988f554
commit 771c0170d9
45 changed files with 3864 additions and 476 deletions

View File

@ -106,7 +106,7 @@ export class Parser {
}
}
class _ParseAST {
export class _ParseAST {
index: int = 0;
constructor(public input: string, public location: any, public tokens: List<any>,
public reflector: Reflector, public parseAction: boolean) {}

View File

@ -45,6 +45,7 @@ import {HammerGesturesPlugin} from 'angular2/src/render/dom/events/hammer_gestur
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
import {UrlResolver} from 'angular2/src/services/url_resolver';
import {AppRootUrl} from 'angular2/src/services/app_root_url';
import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url';
import {
ComponentRef,
DynamicComponentLoader
@ -136,14 +137,14 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
StyleInliner,
DynamicComponentLoader,
Testability,
AppRootUrl
AnchorBasedAppRootUrl,
bind(AppRootUrl).toAlias(AnchorBasedAppRootUrl)
];
}
function _createNgZone(): NgZone {
export function createNgZone(handler: ExceptionHandler): NgZone {
// bootstrapErrorReporter is needed because we cannot use custom exception handler
// configured via DI until the root Injector has been created.
var handler = new ExceptionHandler();
var bootstrapErrorReporter = (exception, stackTrace) => handler.call(exception, stackTrace);
var zone = new NgZone({enableLongStackTrace: assertionsEnabled()});
zone.overrideOnErrorHandler(bootstrapErrorReporter);
@ -282,7 +283,7 @@ export function commonBootstrap(
BrowserDomAdapter.makeCurrent();
var bootstrapProcess = PromiseWrapper.completer();
var zone = _createNgZone();
var zone = createNgZone(new ExceptionHandler());
zone.run(() => {
// TODO(rado): prepopulate template cache, so applications with only
// index.html and main.js are possible.

View File

@ -39,6 +39,19 @@ bool isDate(obj) => obj is DateTime;
String stringify(obj) => obj.toString();
int serializeEnum(val) {
return val.index;
}
/**
* Deserializes an enum
* val should be the indexed value of the enum (sa returned from @Link{serializeEnum})
* values should be a map from indexes to values for the enum that you want to deserialize.
*/
dynamic deserializeEnum(int val, Map<int, dynamic> values) {
return values[val];
}
class StringWrapper {
static String fromCharCode(int code) {
return new String.fromCharCode(code);

View File

@ -130,6 +130,17 @@ export function stringify(token): string {
return (newLineIndex === -1) ? res : res.substring(0, newLineIndex);
}
// serialize / deserialize enum exist only for consistency with dart API
// enums in typescript don't need to be serialized
export function serializeEnum(val): int {
return val;
}
export function deserializeEnum(val, values: Map<int, any>): any {
return val;
}
export class StringWrapper {
static fromCharCode(code: int): string { return String.fromCharCode(code); }

View File

@ -0,0 +1,21 @@
import {AppRootUrl} from "angular2/src/services/app_root_url";
import {DOM} from "angular2/src/dom/dom_adapter";
import {Injectable} from "angular2/di";
/**
* Extension of {@link AppRootUrl} that uses a DOM anchor tag to set the root url to
* the current page's url.
*/
@Injectable()
export class AnchorBasedAppRootUrl extends AppRootUrl {
constructor() {
super("");
// compute the root url to pass to AppRootUrl
var rootUrl: string;
var a = DOM.createElement('a');
DOM.resolveAndSetHref(a, './', null);
rootUrl = DOM.getHref(a);
this.value = rootUrl;
}
}

View File

@ -1,6 +1,5 @@
import {Injectable} from 'angular2/di';
import {isBlank} from 'angular2/src/facade/lang';
import {DOM} from 'angular2/src/dom/dom_adapter';
/**
* Specifies app root url for the application.
@ -15,16 +14,12 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
export class AppRootUrl {
private _value: string;
constructor(value: string) { this._value = value; }
/**
* Returns the base URL of the currently running application.
*/
get value() {
if (isBlank(this._value)) {
var a = DOM.createElement('a');
DOM.resolveAndSetHref(a, './', null);
this._value = DOM.getHref(a);
}
get value() { return this._value; }
return this._value;
}
set value(value: string) { this._value = value; }
}

View File

@ -23,6 +23,7 @@ import {XHR} from 'angular2/src/render/xhr';
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
import {UrlResolver} from 'angular2/src/services/url_resolver';
import {AppRootUrl} from 'angular2/src/services/app_root_url';
import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url';
import {StyleUrlResolver} from 'angular2/src/render/dom/compiler/style_url_resolver';
import {StyleInliner} from 'angular2/src/render/dom/compiler/style_inliner';
import {NgZone} from 'angular2/src/core/zone/ng_zone';
@ -56,6 +57,7 @@ import {
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES
} from 'angular2/src/render/dom/dom_renderer';
import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
import {Serializer} from "angular2/src/web-workers/shared/serializer";
import {Log} from './utils';
/**
@ -88,6 +90,7 @@ function _getAppBindings() {
} catch (e) {
appDoc = null;
}
return [
bind(DOCUMENT_TOKEN)
.toValue(appDoc),
@ -102,6 +105,7 @@ function _getAppBindings() {
AppViewPool,
AppViewManager,
AppViewManagerUtils,
Serializer,
ELEMENT_PROBE_CONFIG,
bind(APP_VIEW_POOL_CAPACITY).toValue(500),
Compiler,
@ -120,7 +124,8 @@ function _getAppBindings() {
bind(XHR).toClass(MockXHR),
ComponentUrlMapper,
UrlResolver,
AppRootUrl,
AnchorBasedAppRootUrl,
bind(AppRootUrl).toAlias(AnchorBasedAppRootUrl),
StyleUrlResolver,
StyleInliner,
TestComponentBuilder,

View File

@ -0,0 +1,9 @@
import {CONST_EXPR} from "angular2/src/facade/lang";
import {OpaqueToken} from "angular2/di";
import {RenderElementRef, RenderViewRef} from "angular2/src/render/api";
export const ON_WEBWORKER = CONST_EXPR(new OpaqueToken('WebWorker.onWebWorker'));
export class WorkerElementRef implements RenderElementRef {
constructor(public renderView: RenderViewRef, public renderBoundElementIndex: number) {}
}

View File

@ -13,6 +13,14 @@ export interface SourceListener {
(data: any): void; // TODO: Replace this any type with the type of a real messaging protocol
}
export interface MessageBusSource { listen(fn: SourceListener): void; }
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 {@link removeListener}
*/
addListener(fn: SourceListener): number;
removeListener(index: number);
}
export interface MessageBusSink { send(message: Object): void; }

View File

@ -0,0 +1,56 @@
import {Injectable, Inject} from "angular2/di";
import {RenderProtoViewRef} from "angular2/src/render/api";
import {ON_WEBWORKER} from "angular2/src/web-workers/shared/api";
@Injectable()
export class RenderProtoViewRefStore {
private _lookupByIndex: Map<number, RenderProtoViewRef> = new Map<number, RenderProtoViewRef>();
private _lookupByProtoView: Map<RenderProtoViewRef, number> =
new Map<RenderProtoViewRef, number>();
private _nextIndex: number = 0;
private _onWebworker: boolean;
constructor(@Inject(ON_WEBWORKER) onWebworker) { this._onWebworker = onWebworker; }
storeRenderProtoViewRef(ref: RenderProtoViewRef): number {
if (this._lookupByProtoView.has(ref)) {
return this._lookupByProtoView.get(ref);
} else {
this._lookupByIndex.set(this._nextIndex, ref);
this._lookupByProtoView.set(ref, this._nextIndex);
return this._nextIndex++;
}
}
retreiveRenderProtoViewRef(index: number): RenderProtoViewRef {
return this._lookupByIndex.get(index);
}
deserialize(index: number): RenderProtoViewRef {
if (index == null) {
return null;
}
if (this._onWebworker) {
return new WebworkerRenderProtoViewRef(index);
} else {
return this.retreiveRenderProtoViewRef(index);
}
}
serialize(ref: RenderProtoViewRef): number {
if (ref == null) {
return null;
}
if (this._onWebworker) {
return (<WebworkerRenderProtoViewRef>ref).refNumber;
} else {
return this.storeRenderProtoViewRef(ref);
}
}
}
export class WebworkerRenderProtoViewRef extends RenderProtoViewRef {
constructor(public refNumber: number) { super(); }
}

View File

@ -0,0 +1,149 @@
import {Injectable, Inject} from "angular2/di";
import {RenderViewRef, RenderFragmentRef, RenderViewWithFragments} from "angular2/src/render/api";
import {ON_WEBWORKER} from "angular2/src/web-workers/shared/api";
import {List, ListWrapper} from "angular2/src/facade/collection";
@Injectable()
export class RenderViewWithFragmentsStore {
private _nextIndex: number = 0;
private _onWebWorker: boolean;
private _lookupByIndex: Map<number, RenderViewRef | RenderFragmentRef>;
private _lookupByView: Map<RenderViewRef | RenderFragmentRef, number>;
constructor(@Inject(ON_WEBWORKER) onWebWorker) {
this._onWebWorker = onWebWorker;
if (!onWebWorker) {
this._lookupByIndex = new Map<number, RenderViewRef | RenderFragmentRef>();
this._lookupByView = new Map<RenderViewRef | RenderFragmentRef, number>();
}
}
allocate(fragmentCount: number): RenderViewWithFragments {
var viewRef = new WorkerRenderViewRef(this._nextIndex++);
var fragmentRefs = ListWrapper.createGrowableSize(fragmentCount);
for (var i = 0; i < fragmentCount; i++) {
fragmentRefs[i] = new WorkerRenderFragmentRef(this._nextIndex++);
}
return new RenderViewWithFragments(viewRef, fragmentRefs);
}
store(view: RenderViewWithFragments, startIndex: number) {
this._lookupByIndex.set(startIndex, view.viewRef);
this._lookupByView.set(view.viewRef, startIndex);
startIndex++;
ListWrapper.forEach(view.fragmentRefs, (ref) => {
this._lookupByIndex.set(startIndex, ref);
this._lookupByView.set(ref, startIndex);
startIndex++;
});
}
retreive(ref: number): RenderViewRef | RenderFragmentRef {
if (ref == null) {
return null;
}
return this._lookupByIndex.get(ref);
}
serializeRenderViewRef(viewRef: RenderViewRef): number {
return this._serializeRenderFragmentOrViewRef(viewRef);
}
serializeRenderFragmentRef(fragmentRef: RenderFragmentRef): number {
return this._serializeRenderFragmentOrViewRef(fragmentRef);
}
deserializeRenderViewRef(ref: number): RenderViewRef {
if (ref == null) {
return null;
}
if (this._onWebWorker) {
return WorkerRenderViewRef.deserialize(ref);
} else {
return this.retreive(ref);
}
}
deserializeRenderFragmentRef(ref: number): RenderFragmentRef {
if (ref == null) {
return null;
}
if (this._onWebWorker) {
return WorkerRenderFragmentRef.deserialize(ref);
} else {
return this.retreive(ref);
}
}
private _serializeRenderFragmentOrViewRef(ref: RenderViewRef | RenderFragmentRef): number {
if (ref == null) {
return null;
}
if (this._onWebWorker) {
return (<WorkerRenderFragmentRef | WorkerRenderViewRef>ref).serialize();
} else {
return this._lookupByView.get(ref);
}
}
serializeViewWithFragments(view: RenderViewWithFragments): StringMap<string, any> {
if (view == null) {
return null;
}
if (this._onWebWorker) {
return {
'viewRef': (<WorkerRenderViewRef>view.viewRef).serialize(),
'fragmentRefs': ListWrapper.map(view.fragmentRefs, (val) => val.serialize())
};
} else {
return {
'viewRef': this._lookupByView.get(view.viewRef),
'fragmentRefs': ListWrapper.map(view.fragmentRefs, (val) => this._lookupByView.get(val))
};
}
}
deserializeViewWithFragments(obj: StringMap<string, any>): RenderViewWithFragments {
if (obj == null) {
return null;
}
var viewRef: RenderViewRef | RenderFragmentRef;
var fragments: List<RenderViewRef | RenderFragmentRef>;
if (this._onWebWorker) {
viewRef = WorkerRenderViewRef.deserialize(obj['viewRef']);
fragments =
ListWrapper.map(obj['fragmentRefs'], (val) => WorkerRenderFragmentRef.deserialize(val));
return new RenderViewWithFragments(viewRef, fragments);
} else {
viewRef = this.retreive(obj['viewRef']);
fragments = ListWrapper.map(obj['fragmentRefs'], (val) => this.retreive(val));
return new RenderViewWithFragments(viewRef, fragments);
}
}
}
export class WorkerRenderViewRef extends RenderViewRef {
constructor(public refNumber: number) { super(); }
serialize(): number { return this.refNumber; }
static deserialize(ref: number): WorkerRenderViewRef { return new WorkerRenderViewRef(ref); }
}
export class WorkerRenderFragmentRef extends RenderFragmentRef {
constructor(public refNumber: number) { super(); }
serialize(): number { return this.refNumber; }
static deserialize(ref: number): WorkerRenderFragmentRef {
return new WorkerRenderFragmentRef(ref);
}
}

View File

@ -1,4 +1,4 @@
import {Type, isArray, isPresent} from "angular2/src/facade/lang";
import {Type, isArray, isPresent, serializeEnum, deserializeEnum} from "angular2/src/facade/lang";
import {List, ListWrapper, Map, StringMapWrapper, MapWrapper} from "angular2/src/facade/collection";
import {
ProtoViewDto,
@ -7,75 +7,122 @@ import {
DirectiveBinder,
ElementPropertyBinding,
EventBinding,
ViewDefinition
ViewDefinition,
RenderProtoViewRef,
RenderProtoViewMergeMapping,
RenderViewRef,
RenderFragmentRef,
RenderElementRef,
ViewType
} from "angular2/src/render/api";
import {WorkerElementRef} from 'angular2/src/web-workers/shared/api';
import {AST, ASTWithSource} from "angular2/change_detection";
import {Parser} from "angular2/src/change_detection/parser/parser";
import {Injectable} from "angular2/di";
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';
@Injectable()
export class Serializer {
static parser: Parser = null;
private _enumRegistry: Map<any, Map<int, any>>;
constructor(private _parser: Parser, private _protoViewStore: RenderProtoViewRefStore,
private _renderViewStore: RenderViewWithFragmentsStore) {
this._enumRegistry = new Map<any, Map<int, any>>();
var viewTypeMap = new Map<int, any>();
viewTypeMap[0] = ViewType.HOST;
viewTypeMap[1] = ViewType.COMPONENT;
viewTypeMap[2] = ViewType.EMBEDDED;
this._enumRegistry.set(ViewType, viewTypeMap);
}
static serialize(obj: any, type: Type): Object {
serialize(obj: any, type: Type): Object {
if (!isPresent(obj)) {
return null;
}
if (isArray(obj)) {
var serializedObj = [];
ListWrapper.forEach(obj, (val) => { serializedObj.push(Serializer.serialize(val, type)); });
ListWrapper.forEach(obj, (val) => { serializedObj.push(this.serialize(val, type)); });
return serializedObj;
}
if (type == String) {
return obj;
}
if (type == ViewDefinition) {
return ViewDefinitionSerializer.serialize(obj);
return this._serializeViewDefinition(obj);
} else if (type == DirectiveBinder) {
return DirectiveBinderSerializer.serialize(obj);
return this._serializeDirectiveBinder(obj);
} else if (type == ProtoViewDto) {
return ProtoViewDtoSerializer.serialize(obj);
return this._serializeProtoViewDto(obj);
} else if (type == ElementBinder) {
return ElementBinderSerializer.serialize(obj);
return this._serializeElementBinder(obj);
} else if (type == DirectiveMetadata) {
return DirectiveMetadataSerializer.serialize(obj);
return this._serializeDirectiveMetadata(obj);
} else if (type == ASTWithSource) {
return ASTWithSourceSerializer.serialize(obj);
return this._serializeASTWithSource(obj);
} else if (type == RenderProtoViewRef) {
return this._protoViewStore.serialize(obj);
} else if (type == RenderProtoViewMergeMapping) {
return this._serializeRenderProtoViewMergeMapping(obj);
} else if (type == RenderViewRef) {
return this._renderViewStore.serializeRenderViewRef(obj);
} else if (type == RenderFragmentRef) {
return this._renderViewStore.serializeRenderFragmentRef(obj);
} else if (type == WorkerElementRef) {
return this._serializeWorkerElementRef(obj);
} else {
throw "No serializer for " + type.toString();
}
}
// TODO: template this to return the type that is passed if possible
static deserialize(map: List<any>, type: Type, data?: any): any {
deserialize(map: any, type: Type, data?: any): any {
if (!isPresent(map)) {
return null;
}
if (isArray(map)) {
var obj: List<any> = new List<any>();
ListWrapper.forEach(map, (val) => { obj.push(Serializer.deserialize(val, type, data)); });
ListWrapper.forEach(map, (val) => { obj.push(this.deserialize(val, type, data)); });
return obj;
}
if (type == String) {
return map;
}
if (type == ViewDefinition) {
return ViewDefinitionSerializer.deserialize(map);
return this._deserializeViewDefinition(map);
} else if (type == DirectiveBinder) {
return DirectiveBinderSerializer.deserialize(map);
return this._deserializeDirectiveBinder(map);
} else if (type == ProtoViewDto) {
return ProtoViewDtoSerializer.deserialize(map);
return this._deserializeProtoViewDto(map);
} else if (type == DirectiveMetadata) {
return DirectiveMetadataSerializer.deserialize(map);
return this._deserializeDirectiveMetadata(map);
} else if (type == ElementBinder) {
return ElementBinderSerializer.deserialize(map);
return this._deserializeElementBinder(map);
} else if (type == ASTWithSource) {
return ASTWithSourceSerializer.deserialize(map, data);
return this._deserializeASTWithSource(map, data);
} else if (type == RenderProtoViewRef) {
return this._protoViewStore.deserialize(map);
} else if (type == RenderProtoViewMergeMapping) {
return this._deserializeRenderProtoViewMergeMapping(map);
} else if (type == RenderViewRef) {
return this._renderViewStore.deserializeRenderViewRef(map);
} else if (type == RenderFragmentRef) {
return this._renderViewStore.deserializeRenderFragmentRef(map);
} else if (type == WorkerElementRef) {
return this._deserializeWorkerElementRef(map);
} else {
throw "No deserializer for " + type.toString();
}
}
static mapToObject(map: Map<any, any>, type?: Type): Object {
mapToObject(map: Map<string, any>, type?: Type): Object {
var object = {};
var serialize = isPresent(type);
MapWrapper.forEach(map, (value, key) => {
if (serialize) {
object[key] = Serializer.serialize(value, type);
object[key] = this.serialize(value, type);
} else {
object[key] = value;
}
@ -84,191 +131,213 @@ export class Serializer {
}
/*
* Transforms a Javascript object into a Map<string, V>
* Transforms a Javascript object (StringMap) into a Map<string, V>
* If the values need to be deserialized pass in their type
* and they will be deserialized before being placed in the map
*/
static objectToMap(obj: Object, type?: Type, data?: any): Map<string, any> {
objectToMap(obj: StringMap<string, any>, type?: Type, data?: any): Map<string, any> {
if (isPresent(type)) {
var map: Map<string, any> = new Map();
StringMapWrapper.forEach(
obj, (key, val) => { map.set(key, Serializer.deserialize(val, type, data)); });
StringMapWrapper.forEach(obj,
(key, val) => { map.set(key, this.deserialize(val, type, data)); });
return map;
} else {
return MapWrapper.createFromStringMap(obj);
}
}
}
class ASTWithSourceSerializer {
static serialize(tree: ASTWithSource): Object {
allocateRenderViews(fragmentCount: number) { this._renderViewStore.allocate(fragmentCount); }
private _serializeWorkerElementRef(elementRef: RenderElementRef): StringMap<string, any> {
return {
'renderView': this.serialize(elementRef.renderView, RenderViewRef),
'renderBoundElementIndex': elementRef.renderBoundElementIndex
};
}
private _deserializeWorkerElementRef(map: StringMap<string, any>): RenderElementRef {
return new WorkerElementRef(this.deserialize(map['renderView'], RenderViewRef),
map['renderBoundElementIndex']);
}
private _serializeRenderProtoViewMergeMapping(mapping: RenderProtoViewMergeMapping): Object {
return {
'mergedProtoViewRef': this._protoViewStore.serialize(mapping.mergedProtoViewRef),
'fragmentCount': mapping.fragmentCount,
'mappedElementIndices': mapping.mappedElementIndices,
'mappedElementCount': mapping.mappedElementCount,
'mappedTextIndices': mapping.mappedTextIndices,
'hostElementIndicesByViewIndex': mapping.hostElementIndicesByViewIndex,
'nestedViewCountByViewIndex': mapping.nestedViewCountByViewIndex
};
}
private _deserializeRenderProtoViewMergeMapping(obj: StringMap<string, any>):
RenderProtoViewMergeMapping {
return new RenderProtoViewMergeMapping(
this._protoViewStore.deserialize(obj['mergedProtoViewRef']), obj['fragmentCount'],
obj['mappedElementIndices'], obj['mappedElementCount'], obj['mappedTextIndices'],
obj['hostElementIndicesByViewIndex'], obj['nestedViewCountByViewIndex']);
}
private _serializeASTWithSource(tree: ASTWithSource): Object {
return {'input': tree.source, 'location': tree.location};
}
static deserialize(obj: any, data: string): AST {
private _deserializeASTWithSource(obj: StringMap<string, any>, data: string): AST {
// TODO: make ASTs serializable
var ast: AST;
switch (data) {
case "interpolation":
ast = Serializer.parser.parseInterpolation(obj.input, obj.location);
ast = this._parser.parseInterpolation(obj['input'], obj['location']);
break;
case "binding":
ast = Serializer.parser.parseBinding(obj.input, obj.location);
ast = this._parser.parseBinding(obj['input'], obj['location']);
break;
case "simpleBinding":
ast = Serializer.parser.parseSimpleBinding(obj.input, obj.location);
ast = this._parser.parseSimpleBinding(obj['input'], obj['location']);
break;
/*case "templateBindings":
ast = Serializer.parser.parseTemplateBindings(obj.input, obj.location);
break;*/
case "interpolation":
ast = Serializer.parser.parseInterpolation(obj.input, obj.location);
ast = this._parser.parseInterpolation(obj['input'], obj['location']);
break;
default:
throw "No AST deserializer for " + data;
}
return ast;
}
}
class ViewDefinitionSerializer {
static serialize(view: ViewDefinition): Object {
private _serializeViewDefinition(view: ViewDefinition): Object {
return {
'componentId': view.componentId,
'templateAbsUrl': view.templateAbsUrl,
'template': view.template,
'directives': Serializer.serialize(view.directives, DirectiveMetadata),
'directives': this.serialize(view.directives, DirectiveMetadata),
'styleAbsUrls': view.styleAbsUrls,
'styles': view.styles
};
}
static deserialize(obj: any): ViewDefinition {
private _deserializeViewDefinition(obj: StringMap<string, any>): ViewDefinition {
return new ViewDefinition({
componentId: obj.componentId,
templateAbsUrl: obj.templateAbsUrl, template: obj.template,
directives: Serializer.deserialize(obj.directives, DirectiveMetadata),
styleAbsUrls: obj.styleAbsUrls,
styles: obj.styles
componentId: obj['componentId'],
templateAbsUrl: obj['templateAbsUrl'], template: obj['template'],
directives: this.deserialize(obj['directives'], DirectiveMetadata),
styleAbsUrls: obj['styleAbsUrls'],
styles: obj['styles']
});
}
}
class DirectiveBinderSerializer {
static serialize(binder: DirectiveBinder): Object {
private _serializeDirectiveBinder(binder: DirectiveBinder): Object {
return {
'directiveIndex': binder.directiveIndex,
'propertyBindings': Serializer.mapToObject(binder.propertyBindings, ASTWithSource),
'eventBindings': Serializer.serialize(binder.eventBindings, EventBinding),
'hostPropertyBindings':
Serializer.serialize(binder.hostPropertyBindings, ElementPropertyBinding)
'propertyBindings': this.mapToObject(binder.propertyBindings, ASTWithSource),
'eventBindings': this.serialize(binder.eventBindings, EventBinding),
'hostPropertyBindings': this.serialize(binder.hostPropertyBindings, ElementPropertyBinding)
};
}
static deserialize(obj: any): DirectiveBinder {
private _deserializeDirectiveBinder(obj: StringMap<string, any>): DirectiveBinder {
return new DirectiveBinder({
directiveIndex: obj.directiveIndex,
propertyBindings: Serializer.objectToMap(obj.propertyBindings, ASTWithSource, "binding"),
eventBindings: Serializer.deserialize(obj.eventBindings, EventBinding),
hostPropertyBindings: Serializer.deserialize(obj.hostPropertyBindings, ElementPropertyBinding)
directiveIndex: obj['directiveIndex'],
propertyBindings: this.objectToMap(obj['propertyBindings'], ASTWithSource, "binding"),
eventBindings: this.deserialize(obj['eventBindings'], EventBinding),
hostPropertyBindings: this.deserialize(obj['hostPropertyBindings'], ElementPropertyBinding)
});
}
}
class ElementBinderSerializer {
static serialize(binder: ElementBinder): Object {
private _serializeElementBinder(binder: ElementBinder): Object {
return {
'index': binder.index,
'parentIndex': binder.parentIndex,
'distanceToParent': binder.distanceToParent,
'directives': Serializer.serialize(binder.directives, DirectiveBinder),
'nestedProtoView': Serializer.serialize(binder.nestedProtoView, ProtoViewDto),
'propertyBindings': Serializer.serialize(binder.propertyBindings, ElementPropertyBinding),
'variableBindings': Serializer.mapToObject(binder.variableBindings),
'eventBindings': Serializer.serialize(binder.eventBindings, EventBinding),
'readAttributes': Serializer.mapToObject(binder.readAttributes)
'directives': this.serialize(binder.directives, DirectiveBinder),
'nestedProtoView': this.serialize(binder.nestedProtoView, ProtoViewDto),
'propertyBindings': this.serialize(binder.propertyBindings, ElementPropertyBinding),
'variableBindings': this.mapToObject(binder.variableBindings),
'eventBindings': this.serialize(binder.eventBindings, EventBinding),
'readAttributes': this.mapToObject(binder.readAttributes)
};
}
static deserialize(obj: any): ElementBinder {
private _deserializeElementBinder(obj: StringMap<string, any>): ElementBinder {
return new ElementBinder({
index: obj.index,
parentIndex: obj.parentIndex,
distanceToParent: obj.distanceToParent,
directives: Serializer.deserialize(obj.directives, DirectiveBinder),
nestedProtoView: Serializer.deserialize(obj.nestedProtoView, ProtoViewDto),
propertyBindings: Serializer.deserialize(obj.propertyBindings, ElementPropertyBinding),
variableBindings: Serializer.objectToMap(obj.variableBindings),
eventBindings: Serializer.deserialize(obj.eventBindings, EventBinding),
readAttributes: Serializer.objectToMap(obj.readAttributes)
index: obj['index'],
parentIndex: obj['parentIndex'],
distanceToParent: obj['distanceToParent'],
directives: this.deserialize(obj['directives'], DirectiveBinder),
nestedProtoView: this.deserialize(obj['nestedProtoView'], ProtoViewDto),
propertyBindings: this.deserialize(obj['propertyBindings'], ElementPropertyBinding),
variableBindings: this.objectToMap(obj['variableBindings']),
eventBindings: this.deserialize(obj['eventBindings'], EventBinding),
readAttributes: this.objectToMap(obj['readAttributes'])
});
}
}
class ProtoViewDtoSerializer {
static serialize(view: ProtoViewDto): Object {
// TODO: fix render refs and write a serializer for them
private _serializeProtoViewDto(view: ProtoViewDto): Object {
return {
'render': null,
'elementBinders': Serializer.serialize(view.elementBinders, ElementBinder),
'variableBindings': Serializer.mapToObject(view.variableBindings),
'textBindings': Serializer.serialize(view.textBindings, ASTWithSource),
'type': view.type
'render': this._protoViewStore.serialize(view.render),
'elementBinders': this.serialize(view.elementBinders, ElementBinder),
'variableBindings': this.mapToObject(view.variableBindings),
'type': serializeEnum(view.type),
'textBindings': this.serialize(view.textBindings, ASTWithSource),
'transitiveNgContentCount': view.transitiveNgContentCount
};
}
static deserialize(obj: any): ProtoViewDto {
private _deserializeProtoViewDto(obj: StringMap<string, any>): ProtoViewDto {
return new ProtoViewDto({
render: null, // TODO: fix render refs and write a serializer for them
elementBinders: Serializer.deserialize(obj.elementBinders, ElementBinder),
variableBindings: Serializer.objectToMap(obj.variableBindings),
textBindings: Serializer.deserialize(obj.textBindings, ASTWithSource, "interpolation"),
type: obj.type
render: this._protoViewStore.deserialize(obj["render"]),
elementBinders: this.deserialize(obj['elementBinders'], ElementBinder),
variableBindings: this.objectToMap(obj['variableBindings']),
textBindings: this.deserialize(obj['textBindings'], ASTWithSource, "interpolation"),
type: deserializeEnum(obj['type'], this._enumRegistry.get(ViewType)),
transitiveNgContentCount: obj['transitivengContentCount']
});
}
}
class DirectiveMetadataSerializer {
static serialize(meta: DirectiveMetadata): Object {
private _serializeDirectiveMetadata(meta: DirectiveMetadata): Object {
var obj = {
'id': meta.id,
'selector': meta.selector,
'compileChildren': meta.compileChildren,
'hostProperties': Serializer.mapToObject(meta.hostProperties),
'hostListeners': Serializer.mapToObject(meta.hostListeners),
'hostActions': Serializer.mapToObject(meta.hostActions),
'hostAttributes': Serializer.mapToObject(meta.hostAttributes),
'events': meta.events,
'properties': meta.properties,
'readAttributes': meta.readAttributes,
'type': meta.type,
'exportAs': meta.exportAs,
'callOnDestroy': meta.callOnDestroy,
'callOnChange': meta.callOnChange,
'callOnCheck': meta.callOnCheck,
'callOnInit': meta.callOnInit,
'callOnAllChangesDone': meta.callOnAllChangesDone,
'changeDetection': meta.changeDetection,
'events': meta.events
'exportAs': meta.exportAs,
'hostProperties': this.mapToObject(meta.hostProperties),
'hostListeners': this.mapToObject(meta.hostListeners),
'hostActions': this.mapToObject(meta.hostActions),
'hostAttributes': this.mapToObject(meta.hostAttributes)
};
return obj;
}
static deserialize(obj: any): DirectiveMetadata {
private _deserializeDirectiveMetadata(obj: StringMap<string, any>): DirectiveMetadata {
return new DirectiveMetadata({
id: obj.id,
selector: obj.selector,
compileChildren: obj.compileChildren,
hostProperties: Serializer.objectToMap(obj.hostProperties),
hostListeners: Serializer.objectToMap(obj.hostListeners),
hostActions: Serializer.objectToMap(obj.hostActions),
hostAttributes: Serializer.objectToMap(obj.hostAttributes),
properties: obj.properties,
readAttributes: obj.readAttributes,
type: obj.type,
exportAs: obj.exportAs,
callOnDestroy: obj.callOnDestroy,
callOnCheck: obj.callOnCheck,
callOnInit: obj.callOnInit,
callOnAllChangesDone: obj.callOnAllChangesDone,
changeDetection: obj.changeDetection,
events: obj.events
id: obj['id'],
selector: obj['selector'],
compileChildren: obj['compileChildren'],
hostProperties: this.objectToMap(obj['hostProperties']),
hostListeners: this.objectToMap(obj['hostListeners']),
hostActions: this.objectToMap(obj['hostActions']),
hostAttributes: this.objectToMap(obj['hostAttributes']),
properties: obj['properties'],
readAttributes: obj['readAttributes'],
type: obj['type'],
exportAs: obj['exportAs'],
callOnDestroy: obj['callOnDestroy'],
callOnChange: obj['callOnChange'],
callOnCheck: obj['callOnCheck'],
callOnInit: obj['callOnInit'],
callOnAllChangesDone: obj['callOnAllChangesDone'],
changeDetection: obj['changeDetection'],
events: obj['events']
});
}
}

View File

@ -1,275 +0,0 @@
import {Type, isArray, isPresent} from "angular2/src/facade/lang";
import {List, ListWrapper, Map, StringMapWrapper, MapWrapper} from "angular2/src/facade/collection";
import {
ProtoViewDto,
DirectiveMetadata,
ElementBinder,
DirectiveBinder,
ElementPropertyBinding,
EventBinding,
ViewDefinition
} from "angular2/src/render/api";
import {AST, ASTWithSource} from "angular2/change_detection";
import {Parser} from "angular2/src/change_detection/parser/parser";
export class Serializer {
static parser: Parser = null;
static serialize(obj: any, type: Type): Object {
if (!isPresent(obj)) {
return null;
}
if (isArray(obj)) {
var serializedObj = [];
ListWrapper.forEach(obj, (val) => { serializedObj.push(Serializer.serialize(val, type)); });
return serializedObj;
}
if (type == ViewDefinition) {
return ViewDefinitionSerializer.serialize(obj);
} else if (type == DirectiveBinder) {
return DirectiveBinderSerializer.serialize(obj);
} else if (type == ProtoViewDto) {
return ProtoViewDtoSerializer.serialize(obj);
} else if (type == ElementBinder) {
return ElementBinderSerializer.serialize(obj);
} else if (type == DirectiveMetadata) {
return DirectiveMetadataSerializer.serialize(obj);
} else if (type == ASTWithSource) {
return ASTWithSourceSerializer.serialize(obj);
} else {
throw "No serializer for " + type.toString();
}
}
// TODO: template this to return the type that is passed if possible
static deserialize(map, type: Type, data?): any {
if (!isPresent(map)) {
return null;
}
if (isArray(map)) {
var obj: List<any> = new List<any>();
ListWrapper.forEach(map, (val) => { obj.push(Serializer.deserialize(val, type, data)); });
return obj;
}
if (type == ViewDefinition) {
return ViewDefinitionSerializer.deserialize(map);
} else if (type == DirectiveBinder) {
return DirectiveBinderSerializer.deserialize(map);
} else if (type == ProtoViewDto) {
return ProtoViewDtoSerializer.deserialize(map);
} else if (type == DirectiveMetadata) {
return DirectiveMetadataSerializer.deserialize(map);
} else if (type == ElementBinder) {
return ElementBinderSerializer.deserialize(map);
} else if (type == ASTWithSource) {
return ASTWithSourceSerializer.deserialize(map, data);
} else {
throw "No deserializer for " + type.toString();
}
}
static mapToObject(map, type?: Type): Object {
var object = {};
var serialize = isPresent(type);
MapWrapper.forEach(map, (value, key) => {
if (serialize) {
object[key] = Serializer.serialize(value, type);
} else {
object[key] = value;
}
});
return object;
}
/*
* Transforms a Javascript object into a Map<string, V>
* If the values need to be deserialized pass in their type
* and they will be deserialized before being placed in the map
*/
static objectToMap(obj, type?: Type, data?): Map<string, any> {
if (isPresent(type)) {
var map: Map<string, any> = new Map();
StringMapWrapper.forEach(
obj, (key, val) => { map.set(key, Serializer.deserialize(val, type, data)); });
return map;
} else {
return MapWrapper.createFromStringMap(obj);
}
}
}
class ASTWithSourceSerializer {
static serialize(tree: ASTWithSource): Object {
return { 'input': tree.source, 'location': tree.location }
}
static deserialize(obj: any, data: string): AST {
// TODO: make ASTs serializable
var ast: AST;
switch (data) {
case "interpolation":
ast = Serializer.parser.parseInterpolation(obj.input, obj.location);
break;
case "binding":
ast = Serializer.parser.parseBinding(obj.input, obj.location);
break;
case "simpleBinding":
ast = Serializer.parser.parseSimpleBinding(obj.input, obj.location);
break;
/*case "templateBindings":
ast = Serializer.parser.parseTemplateBindings(obj.input, obj.location);
break;*/
case "interpolation":
ast = Serializer.parser.parseInterpolation(obj.input, obj.location);
break;
default:
throw "No AST deserializer for " + data;
}
return ast;
}
}
class ViewDefinitionSerializer {
static serialize(view: ViewDefinition): Object{
return {
'componentId': view.componentId,
'templateAbsUrl': view.templateAbsUrl,
'template': view.template,
'directives': Serializer.serialize(view.directives, DirectiveMetadata),
'styleAbsUrls': view.styleAbsUrls,
'styles': view.styles
};
}
static deserialize(obj): ViewDefinition {
return new ViewDefinition({
'componentId': obj.componentId,
'templateAbsUrl': obj.templateAbsUrl,
'template': obj.template,
'directives': Serializer.deserialize(obj.directives, DirectiveMetadata),
'styleAbsUrls': obj.styleAbsUrls,
'styles': obj.styles
});
}
}
class DirectiveBinderSerializer {
static serialize(binder: DirectiveBinder): Object {
return {
'directiveIndex': binder.directiveIndex,
'propertyBindings': Serializer.mapToObject(binder.propertyBindings, ASTWithSource),
'eventBindings': Serializer.serialize(binder.eventBindings, EventBinding),
'hostPropertyBindings':
Serializer.serialize(binder.hostPropertyBindings, ElementPropertyBinding)
};
}
static deserialize(obj): DirectiveBinder {
return new DirectiveBinder({
'directiveIndex': obj.directiveIndex,
'propertyBindings': Serializer.objectToMap(obj.propertyBindings, ASTWithSource, "binding"),
'eventBindings': Serializer.deserialize(obj.eventBindings, EventBinding),
'hostPropertyBindings':
Serializer.deserialize(obj.hostPropertyBindings, ElementPropertyBinding)
});
}
}
class ElementBinderSerializer {
static serialize(binder: ElementBinder): Object {
return {
'index': binder.index,
'parentIndex': binder.parentIndex,
'distanceToParent': binder.distanceToParent,
'directives': Serializer.serialize(binder.directives, DirectiveBinder),
'nestedProtoView': Serializer.serialize(binder.nestedProtoView, ProtoViewDto),
'propertyBindings': Serializer.serialize(binder.propertyBindings, ElementPropertyBinding),
'variableBindings': Serializer.mapToObject(binder.variableBindings),
'eventBindings': Serializer.serialize(binder.eventBindings, EventBinding),
'textBindings': Serializer.serialize(binder.textBindings, ASTWithSource),
'readAttributes': Serializer.mapToObject(binder.readAttributes)
}
}
static deserialize(obj): ElementBinder {
return new ElementBinder({
'index': obj.index,
'parentIndex': obj.parentIndex,
'distanceToParent': obj.distanceToParent,
'directives': Serializer.deserialize(obj.directives, DirectiveBinder),
'nestedProtoView': Serializer.deserialize(obj.nestedProtoView, ProtoViewDto),
'propertyBindings': Serializer.deserialize(obj.propertyBindings, ElementPropertyBinding),
'variableBindings': Serializer.objectToMap(obj.variableBindings),
'eventBindings': Serializer.deserialize(obj.eventBindings, EventBinding),
'textBindings': Serializer.deserialize(obj.textBindings, ASTWithSource, "interpolation"),
'readAttributes': Serializer.objectToMap(obj.readAttributes)
});
}
}
class ProtoViewDtoSerializer {
static serialize(view: ProtoViewDto): Object {
return {
'render': null, // TODO: fix render refs and write a serializer for them
'elementBinders': Serializer.serialize(view.elementBinders, ElementBinder),
'variableBindings': Serializer.mapToObject(view.variableBindings),
'type': view.type
}
}
static deserialize(obj): ProtoViewDto {
return new ProtoViewDto({
'render': null, // TODO: fix render refs and write a serializer for them
'elementBinders': Serializer.deserialize(obj.elementBinders, ElementBinder),
'variableBindings': Serializer.objectToMap(obj.variableBindings),
'type': obj.type
});
}
}
class DirectiveMetadataSerializer {
static serialize(meta: DirectiveMetadata): Object {
var obj = {
'id': meta.id,
'selector': meta.selector,
'compileChildren': meta.compileChildren,
'hostProperties': Serializer.mapToObject(meta.hostProperties),
'hostListeners': Serializer.mapToObject(meta.hostListeners),
'hostActions': Serializer.mapToObject(meta.hostActions),
'hostAttributes': Serializer.mapToObject(meta.hostAttributes),
'properties': meta.properties,
'readAttributes': meta.readAttributes,
'type': meta.type,
'exportAs': meta.exportAs,
'callOnDestroy': meta.callOnDestroy,
'callOnCheck': meta.callOnCheck,
'callOnInit': meta.callOnInit,
'callOnAllChangesDone': meta.callOnAllChangesDone,
'changeDetection': meta.changeDetection,
'events': meta.events
};
return obj;
}
static deserialize(obj): DirectiveMetadata {
return new DirectiveMetadata({
'id': obj.id,
'selector': obj.selector,
'compileChildren': obj.compileChildren,
'hostProperties': Serializer.objectToMap(obj.hostProperties),
'hostListeners': Serializer.objectToMap(obj.hostListeners),
'hostActions': Serializer.objectToMap(obj.hostActions),
'hostAttributes': Serializer.objectToMap(obj.hostAttributes),
'properties': obj.properties,
'readAttributes': obj.readAttributes,
'type': obj.type,
'exportAs': obj.exportAs,
'callOnDestroy': obj.callOnDestroy,
'callOnCheck': obj.callOnCheck,
'callOnInit': obj.callOnInit,
'callOnAllChangesDone': obj.callOnAllChangesDone,
'changeDetection': obj.changeDetection,
'events': obj.events
});
}
}

View File

@ -2,23 +2,28 @@ library angular2.src.web_workers.ui;
import 'dart:isolate';
import 'dart:async';
import "package:angular2/src/web-workers/shared/message_bus.dart"
import 'dart:core';
import 'package:angular2/src/web-workers/shared/message_bus.dart'
show MessageBus, MessageBusSink, MessageBusSource;
import 'package:angular2/src/web-workers/ui/impl.dart'
show bootstrapUICommon;
/**
* Bootstrapping a WebWorker
*
*
* You instantiate a WebWorker application by calling bootstrap with the URI of your worker's index script
* Note: The WebWorker script must call bootstrapWebworker once it is set up to complete the bootstrapping process
* Note: The WebWorker script must call bootstrapWebworker once it is set up to complete the bootstrapping process
*/
void bootstrap(String uri) {
throw "Not Implemented";
spawnWorker(Uri.parse(uri)).then((bus) {
bootstrapUICommon(bus);
});
}
/**
* To be called from the main thread to spawn and communicate with the worker thread
*/
Future<UIMessageBus> spawnWorker(Uri uri) {
Future<UIMessageBus> spawnWorker(Uri uri){
var receivePort = new ReceivePort();
var isolateEndSendPort = receivePort.sendPort;
return Isolate.spawnUri(uri, const [], isolateEndSendPort).then((_) {
@ -52,6 +57,8 @@ class UIMessageBusSink extends MessageBusSink {
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,
@ -61,9 +68,17 @@ class UIMessageBusSource extends MessageBusSource {
return message is SendPort;
});
void listen(Function fn) {
rawDataStream.listen((message) {
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);
}
}

View File

@ -5,6 +5,7 @@ import {
SourceListener
} from "angular2/src/web-workers/shared/message_bus";
import {BaseException} from "angular2/src/facade/lang";
import {bootstrapUICommon} from "angular2/src/web-workers/ui/impl";
/**
* Bootstrapping a WebWorker
@ -15,7 +16,8 @@ import {BaseException} from "angular2/src/facade/lang";
* bootstrapping process
*/
export function bootstrap(uri: string): void {
throw new BaseException("Not Implemented");
var messageBus = spawnWorker(uri);
bootstrapUICommon(messageBus);
}
export function spawnWorker(uri: string): MessageBus {
@ -34,7 +36,19 @@ export class UIMessageBusSink implements MessageBusSink {
}
export class UIMessageBusSource implements MessageBusSource {
private _listenerStore: Map<int, SourceListener> = new Map<int, SourceListener>();
private _numListeners: int = 0;
constructor(private _worker: Worker) {}
listen(fn: SourceListener): void { this._worker.addEventListener("message", fn); }
public addListener(fn: SourceListener): int {
this._worker.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);
}
}

View File

@ -0,0 +1,136 @@
// TODO: This whole file is nearly identical to core/application.ts.
// There should be a way to refactor application so that this file is unnecessary
import {Injector, bind, Binding} from "angular2/di";
import {Type, isBlank, isPresent} from "angular2/src/facade/lang";
import {Reflector, reflector} from 'angular2/src/reflection/reflection';
import {List, ListWrapper} from 'angular2/src/facade/collection';
import {
Parser,
Lexer,
ChangeDetection,
DynamicChangeDetection,
JitChangeDetection,
PreGeneratedChangeDetection,
Pipes,
defaultPipes
} from 'angular2/change_detection';
import {EventManager, DomEventsPlugin} from 'angular2/src/render/dom/events/event_manager';
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
import {KeyEventsPlugin} from 'angular2/src/render/dom/events/key_events';
import {HammerGesturesPlugin} from 'angular2/src/render/dom/events/hammer_gestures';
import {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_pool';
import {Renderer, RenderCompiler} from 'angular2/src/render/api';
import {
DomRenderer,
DOCUMENT_TOKEN,
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES
} from 'angular2/src/render/dom/dom_renderer';
import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {NgZone} from 'angular2/src/core/zone/ng_zone';
import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy';
import {
EmulatedUnscopedShadowDomStrategy
} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy';
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
import {AppViewListener} from 'angular2/src/core/compiler/view_listener';
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
import {ViewLoader} from 'angular2/src/render/dom/compiler/view_loader';
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
import {StyleInliner} from 'angular2/src/render/dom/compiler/style_inliner';
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
import {StyleUrlResolver} from 'angular2/src/render/dom/compiler/style_url_resolver';
import {UrlResolver} from 'angular2/src/services/url_resolver';
import {Testability} from 'angular2/src/core/testability/testability';
import {XHR} from 'angular2/src/render/xhr';
import {XHRImpl} from 'angular2/src/render/xhr_impl';
import {Serializer} from 'angular2/src/web-workers/shared/serializer';
import {ON_WEBWORKER} 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';
import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url';
import {WebWorkerMain} from 'angular2/src/web-workers/ui/impl';
var _rootInjector: Injector;
// Contains everything that is safe to share between applications.
var _rootBindings = [bind(Reflector).toValue(reflector)];
// TODO: This code is nearly identitcal to core/application. There should be a way to only write it
// once
function _injectorBindings(): List<Type | Binding | List<any>> {
var bestChangeDetection: Type = DynamicChangeDetection;
if (PreGeneratedChangeDetection.isSupported()) {
bestChangeDetection = PreGeneratedChangeDetection;
} else if (JitChangeDetection.isSupported()) {
bestChangeDetection = JitChangeDetection;
}
// compute the root url to pass to AppRootUrl
/*var rootUrl: string;
var a = DOM.createElement('a');
DOM.resolveAndSetHref(a, './', null);
rootUrl = DOM.getHref(a);*/
return [
bind(DOCUMENT_TOKEN)
.toValue(DOM.defaultDoc()),
bind(EventManager)
.toFactory(
(ngZone) => {
var plugins =
[new HammerGesturesPlugin(), new KeyEventsPlugin(), new DomEventsPlugin()];
return new EventManager(plugins, ngZone);
},
[NgZone]),
bind(ShadowDomStrategy)
.toFactory((doc) => new EmulatedUnscopedShadowDomStrategy(doc.head), [DOCUMENT_TOKEN]),
bind(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES).toValue(false),
DomRenderer,
DefaultDomCompiler,
Serializer,
bind(Renderer).toAlias(DomRenderer),
bind(RenderCompiler).toAlias(DefaultDomCompiler),
bind(ON_WEBWORKER).toValue(false),
RenderViewWithFragmentsStore,
RenderProtoViewRefStore,
ProtoViewFactory,
AppViewPool,
bind(APP_VIEW_POOL_CAPACITY).toValue(10000),
AppViewManager,
AppViewManagerUtils,
AppViewListener,
Compiler,
CompilerCache,
ViewResolver,
bind(Pipes).toValue(defaultPipes),
bind(ChangeDetection).toClass(bestChangeDetection),
ViewLoader,
DirectiveResolver,
Parser,
Lexer,
ExceptionHandler,
bind(XHR).toValue(new XHRImpl()),
ComponentUrlMapper,
UrlResolver,
StyleUrlResolver,
StyleInliner,
DynamicComponentLoader,
Testability,
AnchorBasedAppRootUrl,
WebWorkerMain
];
}
export function createInjector(zone: NgZone): Injector {
BrowserDomAdapter.makeCurrent();
_rootBindings.push(bind(NgZone).toValue(zone));
var injector: Injector = Injector.resolveAndCreate(_rootBindings);
return injector.resolveAndCreateChild(_injectorBindings());
}

View File

@ -0,0 +1,234 @@
/*
* This file is the entry point for the main thread
* It takes care of spawning the worker and sending it the initial init message
* It also acts and the messenger between the worker thread and the renderer running on the UI
* thread
* TODO: This class might need to be refactored to match application.ts...
*/
import {createInjector} from "./di_bindings";
import {
Renderer,
RenderCompiler,
DirectiveMetadata,
ProtoViewDto,
ViewDefinition,
RenderProtoViewRef,
RenderProtoViewMergeMapping,
RenderViewRef,
RenderFragmentRef
} from "angular2/src/render/api";
import {Type, print, BaseException} from "angular2/src/facade/lang";
import {Promise, PromiseWrapper} from "angular2/src/facade/async";
import {Serializer} from "angular2/src/web-workers/shared/serializer";
import {MessageBus} 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 {WorkerElementRef} from 'angular2/src/web-workers/shared/api';
import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url';
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
import {Injectable} from 'angular2/di';
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
/**
* Creates a zone, sets up the DI bindings
* And then creates a new WebWorkerMain object to handle messages from the worker
*/
export function bootstrapUICommon(bus: MessageBus) {
BrowserDomAdapter.makeCurrent();
var zone = createNgZone(new ExceptionHandler());
zone.run(() => {
var injector = createInjector(zone);
var webWorkerMain = injector.get(WebWorkerMain);
webWorkerMain.attachToWorker(bus);
});
}
@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) {
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.
*/
attachToWorker(bus: MessageBus) {
this._bus = bus;
this._bus.source.addListener((message) => { this._handleWorkerMessage(message); });
}
private _sendInitMessage() { this._sendWorkerMessage("init", {"rootUrl": this._rootUrl}); }
/*
* Sends an error back to the worker thread in response to an opeartion on the UI thread
*/
private _sendWorkerError(id: string, error: any) {
this._sendWorkerMessage("error", {"id": id, "error": error});
}
private _sendWorkerMessage(type: string, data: StringMap<string, any>) {
this._bus.sink.send({'type': type, 'value': data});
}
// 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], DirectiveMetadata);
promise = this._renderCompiler.compileHost(directiveMetadata);
this._wrapWorkerPromise(data.id, promise, ProtoViewDto);
break;
case "compile":
var view = this._serializer.deserialize(data.args[0], ViewDefinition);
promise = this._renderCompiler.compile(view);
this._wrapWorkerPromise(data.id, promise, ProtoViewDto);
break;
case "mergeProtoViewsRecursively":
var views = this._serializer.deserialize(data.args[0], RenderProtoViewRef);
promise = this._renderCompiler.mergeProtoViewsRecursively(views);
this._wrapWorkerPromise(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], WorkerElementRef);
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], WorkerElementRef);
var propName = args[1];
var propValue = args[2];
this._renderer.setElementProperty(elementRef, propName, propValue);
break;
case "setElementAttribute":
var elementRef = this._serializer.deserialize(args[0], WorkerElementRef);
var attributeName = args[1];
var attributeValue = args[2];
this._renderer.setElementAttribute(elementRef, attributeName, attributeValue);
break;
case "setElementClass":
var elementRef = this._serializer.deserialize(args[0], WorkerElementRef);
var className = args[1];
var isAdd = args[2];
this._renderer.setElementClass(elementRef, className, isAdd);
break;
case "setElementStyle":
var elementRef = this._serializer.deserialize(args[0], WorkerElementRef);
var styleName = args[1];
var styleValue = args[2];
this._renderer.setElementStyle(elementRef, styleName, styleValue);
break;
case "invokeElementMethod":
var elementRef = this._serializer.deserialize(args[0], WorkerElementRef);
var methodName = args[1];
var methodArgs = args[2];
this._renderer.invokeElementMethod(elementRef, methodName, methodArgs);
break;
default:
throw new BaseException("Not Implemented");
}
}
// TODO: Create message type
private _handleWorkerMessage(message: StringMap<string, any>) {
var data: ReceivedMessage = new ReceivedMessage(message['data']);
switch (data.type) {
case "ready":
return this._sendInitMessage();
case "compiler":
return this._handleCompilerMessage(data);
case "renderer":
return this._handleRendererMessage(data);
}
}
private _wrapWorkerPromise(id: string, promise: Promise<any>, type: Type): void {
PromiseWrapper.then(promise, (result: any) => {
try {
this._sendWorkerMessage("result",
{"id": id, "value": this._serializer.serialize(result, type)});
} catch (e) {
print(e);
}
}, (error: any) => { this._sendWorkerError(id, error); });
}
}
class ReceivedMessage {
method: string;
args: List<any>;
id: string;
type: string;
constructor(data: StringMap<string, any>) {
this.method = data['method'];
this.args = data['args'];
this.id = data['id'];
this.type = data['type'];
}
}

View File

@ -2,15 +2,18 @@ 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/worker/application_common.dart"
show bootstrapWebworkerCommon;
import "package:angular2/src/facade/async.dart" show Future;
import "package:angular2/src/core/application.dart" show ApplicationRef;
import "package:angular2/src/facade/lang.dart" show Type, BaseException;
import "dart:isolate";
import "dart:async";
import 'dart:core';
/**
* Bootstrapping a Webworker Application
*
*
* You instantiate the application side by calling bootstrapWebworker from your webworker index
* script.
* You must supply a SendPort for communicating with the UI side in order to instantiate
@ -21,9 +24,11 @@ import "dart:async";
*/
Future<ApplicationRef> bootstrapWebworker(
SendPort replyTo, Type appComponentType,
[List<dynamic> componentInjectableBindings = null,
Function errorReporter = null]) {
throw new BaseException("Not implemented");
[List<dynamic> componentInjectableBindings = null]) {
ReceivePort rPort = new ReceivePort();
WorkerMessageBus bus = new WorkerMessageBus.fromPorts(replyTo, rPort);
return bootstrapWebworkerCommon(appComponentType, bus,
componentInjectableBindings);
}
class WorkerMessageBus extends MessageBus {
@ -52,14 +57,24 @@ class WorkerMessageBusSink extends MessageBusSink {
class WorkerMessageBusSource extends MessageBusSource {
final ReceivePort _port;
final Stream rawDataStream;
Map<int, StreamSubscription> _listenerStore = new Map<int, StreamSubscription>();
int _numListeners = 0;
WorkerMessageBusSource(ReceivePort rPort)
: _port = rPort,
rawDataStream = rPort.asBroadcastStream();
void listen(Function fn) {
rawDataStream.listen((message) {
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);
}
}

View File

@ -7,7 +7,9 @@ import {
import {Type, BaseException} from "angular2/src/facade/lang";
import {Binding} from "angular2/di";
import {bootstrapWebworkerCommon} from "angular2/src/web-workers/worker/application_common";
import {ApplicationRef} from "angular2/src/core/application";
import {Injectable} from "angular2/di";
/**
* Bootstrapping a Webworker Application
@ -19,11 +21,15 @@ import {ApplicationRef} from "angular2/src/core/application";
* See the bootstrap() docs for more details.
*/
export function bootstrapWebworker(
appComponentType: Type, componentInjectableBindings: List<Type | Binding | List<any>> = null,
errorReporter: Function = null): Promise<ApplicationRef> {
throw new BaseException("Not Implemented");
appComponentType: Type, componentInjectableBindings: List<Type | Binding | List<any>> = null):
Promise<ApplicationRef> {
var bus: WorkerMessageBus =
new WorkerMessageBus(new WorkerMessageBusSink(), new WorkerMessageBusSource());
return bootstrapWebworkerCommon(appComponentType, bus, componentInjectableBindings);
}
@Injectable()
export class WorkerMessageBus implements MessageBus {
sink: WorkerMessageBusSink;
source: WorkerMessageBusSource;
@ -39,5 +45,22 @@ export class WorkerMessageBusSink implements MessageBusSink {
}
export class WorkerMessageBusSource implements MessageBusSource {
public listen(fn: SourceListener) { addEventListener("message", fn); }
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);
}
}

View File

@ -0,0 +1,195 @@
import {Injector, bind, OpaqueToken, Binding} from 'angular2/di';
import {
NumberWrapper,
Type,
isBlank,
isPresent,
BaseException,
assertionsEnabled,
print,
stringify
} from 'angular2/src/facade/lang';
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
import {Reflector, reflector} from 'angular2/src/reflection/reflection';
import {
Parser,
Lexer,
ChangeDetection,
DynamicChangeDetection,
JitChangeDetection,
Pipes,
defaultPipes,
PreGeneratedChangeDetection
} from 'angular2/change_detection';
import {StyleUrlResolver} from 'angular2/src/render/dom/compiler/style_url_resolver';
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
import {List, ListWrapper} from 'angular2/src/facade/collection';
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
import {NgZone} from 'angular2/src/core/zone/ng_zone';
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
import {XHR} from 'angular2/src/render/xhr';
import {XHRImpl} from 'angular2/src/render/xhr_impl';
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
import {UrlResolver} from 'angular2/src/services/url_resolver';
import {AppRootUrl} from 'angular2/src/services/app_root_url';
import {
ComponentRef,
DynamicComponentLoader
} from 'angular2/src/core/compiler/dynamic_component_loader';
import {Testability} from 'angular2/src/core/testability/testability';
import {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_pool';
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
import {AppViewListener} from 'angular2/src/core/compiler/view_listener';
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
import {WorkerRenderer, WorkerCompiler} 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 {WorkerMessageBus} from 'angular2/src/web-workers/worker/application';
import {
appComponentRefPromiseToken,
appComponentTypeToken
} from 'angular2/src/core/application_tokens';
import {ApplicationRef} from 'angular2/src/core/application';
import {createNgZone} from 'angular2/src/core/application_common';
import {Serializer} from "angular2/src/web-workers/shared/serializer";
import {ON_WEBWORKER} 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';
import {WorkerExceptionHandler} from 'angular2/src/web-workers/worker/exception_handler';
var _rootInjector: Injector;
// Contains everything that is safe to share between applications.
var _rootBindings = [bind(Reflector).toValue(reflector)];
function _injectorBindings(appComponentType, bus: WorkerMessageBus,
initData: StringMap<string, any>): List<Type | Binding | List<any>> {
var bestChangeDetection: Type = DynamicChangeDetection;
if (PreGeneratedChangeDetection.isSupported()) {
bestChangeDetection = PreGeneratedChangeDetection;
} else if (JitChangeDetection.isSupported()) {
bestChangeDetection = JitChangeDetection;
}
return [
bind(appComponentTypeToken)
.toValue(appComponentType),
bind(appComponentRefPromiseToken)
.toFactory(
(dynamicComponentLoader, injector) => {
// TODO(rado): investigate whether to support bindings on root component.
return dynamicComponentLoader.loadAsRoot(appComponentType, null, injector)
.then((componentRef) => { return componentRef; });
},
[DynamicComponentLoader, Injector]),
bind(appComponentType).toFactory((ref) => ref.instance, [appComponentRefPromiseToken]),
bind(LifeCycle).toFactory((exceptionHandler) => new LifeCycle(null, assertionsEnabled()),
[ExceptionHandler]),
Serializer,
bind(WorkerMessageBus).toValue(bus),
bind(MessageBroker)
.toFactory((a, b) => new MessageBroker(a, b), [WorkerMessageBus, Serializer]),
WorkerRenderer,
bind(Renderer).toAlias(WorkerRenderer),
WorkerCompiler,
bind(RenderCompiler).toAlias(WorkerCompiler),
bind(ON_WEBWORKER).toValue(true),
RenderViewWithFragmentsStore,
RenderProtoViewRefStore,
ProtoViewFactory,
AppViewPool,
bind(APP_VIEW_POOL_CAPACITY).toValue(10000),
AppViewManager,
AppViewManagerUtils,
AppViewListener,
Compiler,
CompilerCache,
ViewResolver,
bind(Pipes).toValue(defaultPipes),
bind(ChangeDetection).toClass(bestChangeDetection),
DirectiveResolver,
Parser,
Lexer,
WorkerExceptionHandler,
bind(ExceptionHandler).toAlias(WorkerExceptionHandler),
bind(XHR).toValue(new XHRImpl()),
ComponentUrlMapper,
UrlResolver,
StyleUrlResolver,
DynamicComponentLoader,
Testability,
bind(AppRootUrl).toValue(new AppRootUrl(initData['rootUrl']))
];
}
export function bootstrapWebworkerCommon(
appComponentType: Type, bus: WorkerMessageBus,
componentInjectableBindings: List<Type | Binding | List<any>> = null): Promise<ApplicationRef> {
var bootstrapProcess = PromiseWrapper.completer();
var zone = createNgZone(new WorkerExceptionHandler());
zone.run(() => {
// TODO(rado): prepopulate template cache, so applications with only
// 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 compRefToken = PromiseWrapper.wrap(() => {
try {
return appInjector.get(appComponentRefPromiseToken);
} catch (e) {
throw e;
}
});
var tick = (componentRef) => {
var appChangeDetector = internalView(componentRef.hostView).changeDetector;
// retrieve life cycle: may have already been created if injected in root component
var lc = appInjector.get(LifeCycle);
lc.registerWith(zone, appChangeDetector);
lc.tick(); // the first tick that will bootstrap the app
bootstrapProcess.resolve(new ApplicationRef(componentRef, appComponentType, appInjector));
};
PromiseWrapper.then(compRefToken, tick,
(err, stackTrace) => { bootstrapProcess.reject(err, stackTrace); });
PromiseWrapper.catchError(compRefToken, (err) => {
print(err);
bootstrapProcess.reject(err, err.stack);
});
bus.source.removeListener(listenerId);
});
bus.sink.send({'type': "ready"});
});
return bootstrapProcess.promise;
}
function _createAppInjector(appComponentType: Type, bindings: List<Type | Binding | List<any>>,
zone: NgZone, bus: WorkerMessageBus, initData: StringMap<string, any>):
Injector {
if (isBlank(_rootInjector)) _rootInjector = Injector.resolveAndCreate(_rootBindings);
var mergedBindings: any[] =
isPresent(bindings) ?
ListWrapper.concat(_injectorBindings(appComponentType, bus, initData), bindings) :
_injectorBindings(appComponentType, bus, initData);
mergedBindings.push(bind(NgZone).toValue(zone));
return _rootInjector.resolveAndCreateChild(mergedBindings);
}

View File

@ -4,12 +4,15 @@ import {print, isPresent, DateWrapper, stringify} from "../../facade/lang";
import {Promise, PromiseCompleter, PromiseWrapper} 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";
@Injectable()
export class MessageBroker {
private _pending: Map<string, Function> = new Map<string, Function>();
constructor(private _messageBus: MessageBus) {
this._messageBus.source.listen((data) => this._handleMessage(data['data']));
constructor(private _messageBus: MessageBus, protected _serializer: Serializer) {
this._messageBus.source.addListener((data) => this._handleMessage(data['data']));
}
private _generateMessageId(name: string): string {
@ -23,26 +26,48 @@ export class MessageBroker {
return id;
}
runOnUiThread(args: UiArguments): Promise<any> {
var completer = PromiseWrapper.completer();
var id: string = this._generateMessageId(args.type + args.method);
this._pending.set(id, completer.resolve);
PromiseWrapper.catchError(completer.promise, (err, stack?) => {
print(err);
completer.reject(err, stack);
});
runOnUiThread(args: UiArguments, returnType: Type): Promise<any> {
var fnArgs = [];
if (isPresent(args.args)) {
ListWrapper.forEach(args.args, (argument) => {
fnArgs.push(Serializer.serialize(argument.value, argument.type));
if (argument.type != null) {
fnArgs.push(this._serializer.serialize(argument.value, argument.type));
} else {
fnArgs.push(argument.value);
}
});
}
var promise: Promise<any>;
var id: string = null;
if (returnType != null) {
var completer = PromiseWrapper.completer();
id = this._generateMessageId(args.type + args.method);
this._pending.set(id, completer.resolve);
PromiseWrapper.catchError(completer.promise, (err, stack?) => {
print(err);
completer.reject(err, stack);
});
promise = PromiseWrapper.then(completer.promise, (data: MessageResult) => {
if (this._serializer == null) {
return data.value;
} else {
return this._serializer.deserialize(data.value, returnType);
}
});
} else {
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, 'id': id};
var message = {'type': args.type, 'method': args.method, 'args': fnArgs};
if (id != null) {
message['id'] = id;
}
this._messageBus.sink.send(message);
return completer.promise;
return promise;
}
private _handleMessage(message: StringMap<string, any>): void {

View File

@ -0,0 +1,35 @@
import {isPresent, print, BaseException} from 'angular2/src/facade/lang';
import {ListWrapper, isListLikeIterable} from 'angular2/src/facade/collection';
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
import {Injectable} from 'angular2/di';
@Injectable()
export class WorkerExceptionHandler implements ExceptionHandler {
logError: Function = print;
call(exception: Object, stackTrace: any = null, reason: string = null) {
var longStackTrace = isListLikeIterable(stackTrace) ?
(<any>stackTrace).join("\n\n-----async gap-----\n") :
stackTrace;
this.logError(`${exception}\n\n${longStackTrace}`);
if (isPresent(reason)) {
this.logError(`Reason: ${reason}`);
}
var context = this._findContext(exception);
if (isPresent(context)) {
this.logError("Error Context:");
this.logError(context);
}
throw exception;
}
_findContext(exception: any): any {
if (!(exception instanceof BaseException)) return null;
return isPresent(exception.context) ? exception.context :
this._findContext(exception.originalException);
}
}

View File

View File

@ -0,0 +1,260 @@
import {
Renderer,
RenderCompiler,
DirectiveMetadata,
ProtoViewDto,
ViewDefinition,
RenderProtoViewRef,
RenderViewRef,
RenderElementRef,
RenderEventDispatcher,
RenderProtoViewMergeMapping,
RenderViewWithFragments,
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 {isPresent, print, BaseException} from "angular2/src/facade/lang";
import {Injectable} from "angular2/di";
import {
RenderViewWithFragmentsStore,
WorkerRenderViewRef
} from 'angular2/src/web-workers/shared/render_view_with_fragments_store';
import {WorkerElementRef} from 'angular2/src/web-workers/shared/api';
@Injectable()
export class WorkerCompiler implements RenderCompiler {
constructor(private _messageBroker: MessageBroker) {}
/**
* Creats a ProtoViewDto that contains a single nested component with the given componentId.
*/
compileHost(directiveMetadata: DirectiveMetadata): Promise<ProtoViewDto> {
var fnArgs: List<FnArg> = [new FnArg(directiveMetadata, DirectiveMetadata)];
var args: UiArguments = new UiArguments("compiler", "compileHost", fnArgs);
return this._messageBroker.runOnUiThread(args, ProtoViewDto);
}
/**
* Compiles a single DomProtoView. Non recursive so that
* we don't need to serialize all possible components over the wire,
* but only the needed ones based on previous calls.
*/
compile(view: ViewDefinition): Promise<ProtoViewDto> {
var fnArgs: List<FnArg> = [new FnArg(view, ViewDefinition)];
var args: UiArguments = new UiArguments("compiler", "compile", fnArgs);
return this._messageBroker.runOnUiThread(args, ProtoViewDto);
}
/**
* Merges ProtoViews.
* The first entry of the array is the protoview into which all the other entries of the array
* should be merged.
* If the array contains other arrays, they will be merged before processing the parent array.
* The array must contain an entry for every component and embedded ProtoView of the first entry.
* @param protoViewRefs List of ProtoViewRefs or nested
* @return the merge result for every input array in depth first order.
*/
mergeProtoViewsRecursively(
protoViewRefs: List<RenderProtoViewRef | List<any>>): Promise<RenderProtoViewMergeMapping> {
var fnArgs: List<FnArg> = [new FnArg(protoViewRefs, RenderProtoViewRef)];
var args: UiArguments = new UiArguments("compiler", "mergeProtoViewsRecursively", fnArgs);
return this._messageBroker.runOnUiThread(args, RenderProtoViewMergeMapping);
}
}
@Injectable()
export class WorkerRenderer implements Renderer {
constructor(private _messageBroker: MessageBroker,
private _renderViewStore: RenderViewWithFragmentsStore) {}
/**
* 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
* synchronously even when dealing with webworkers!
*
* @param {RenderProtoViewRef} hostProtoViewRef a RenderProtoViewRef of type
* ProtoViewDto.HOST_VIEW_TYPE
* @param {any} hostElementSelector css selector for the host element (will be queried against the
* main document)
* @return {RenderViewRef} the created view
*/
createRootHostView(hostProtoViewRef: RenderProtoViewRef, fragmentCount: number,
hostElementSelector: string): RenderViewWithFragments {
return this._createViewHelper(hostProtoViewRef, fragmentCount, hostElementSelector);
}
/**
* Creates a regular view out of the given ProtoView
* Note that the fragmentCount needs to be passed in so that we can create a result
* synchronously even when dealing with webworkers!
*/
createView(protoViewRef: RenderProtoViewRef, fragmentCount: number): RenderViewWithFragments {
return this._createViewHelper(protoViewRef, fragmentCount);
}
private _createViewHelper(protoViewRef: RenderProtoViewRef, fragmentCount: number,
hostElementSelector?: string): RenderViewWithFragments {
var renderViewWithFragments = this._renderViewStore.allocate(fragmentCount);
var startIndex = (<WorkerRenderViewRef>(renderViewWithFragments.viewRef)).refNumber;
var fnArgs: List<FnArg> = [
new FnArg(protoViewRef, RenderProtoViewRef),
new FnArg(fragmentCount, null),
];
var method = "createView";
if (isPresent(hostElementSelector) && hostElementSelector != null) {
fnArgs.push(new FnArg(hostElementSelector, null));
method = "createRootHostView";
}
fnArgs.push(new FnArg(startIndex, null));
var args = new UiArguments("renderer", method, fnArgs);
this._messageBroker.runOnUiThread(args, null);
return renderViewWithFragments;
}
/**
* Destroys the given view after it has been dehydrated and detached
*/
destroyView(viewRef: RenderViewRef) {
var fnArgs = [new FnArg(viewRef, RenderViewRef)];
var args = new UiArguments("renderer", "destroyView", fnArgs);
this._messageBroker.runOnUiThread(args, null);
}
/**
* Attaches a fragment after another fragment.
*/
attachFragmentAfterFragment(previousFragmentRef: RenderFragmentRef,
fragmentRef: RenderFragmentRef) {
var fnArgs = [
new FnArg(previousFragmentRef, RenderFragmentRef),
new FnArg(fragmentRef, RenderFragmentRef)
];
var args = new UiArguments("renderer", "attachFragmentAfterFragment", fnArgs);
this._messageBroker.runOnUiThread(args, null);
}
/**
* Attaches a fragment after an element.
*/
attachFragmentAfterElement(elementRef: RenderElementRef, fragmentRef: RenderFragmentRef) {
var fnArgs =
[new FnArg(elementRef, WorkerElementRef), new FnArg(fragmentRef, RenderFragmentRef)];
var args = new UiArguments("renderer", "attachFragmentAfterElement", fnArgs);
this._messageBroker.runOnUiThread(args, null);
}
/**
* Detaches a fragment.
*/
detachFragment(fragmentRef: RenderFragmentRef) {
var fnArgs = [new FnArg(fragmentRef, RenderFragmentRef)];
var args = new UiArguments("renderer", "detachFragment", fnArgs);
this._messageBroker.runOnUiThread(args, null);
}
/**
* Hydrates a view after it has been attached. Hydration/dehydration is used for reusing views
* inside of the view pool.
*/
hydrateView(viewRef: RenderViewRef) {
var fnArgs = [new FnArg(viewRef, RenderViewRef)];
var args = new UiArguments("renderer", "hydrateView", fnArgs);
this._messageBroker.runOnUiThread(args, null);
}
/**
* Dehydrates a view after it has been attached. Hydration/dehydration is used for reusing views
* inside of the view pool.
*/
dehydrateView(viewRef: RenderViewRef) {
var fnArgs = [new FnArg(viewRef, RenderViewRef)];
var args = new UiArguments("renderer", "deyhdrateView", fnArgs);
this._messageBroker.runOnUiThread(args, null);
}
/**
* Returns the native element at the given location.
* Attention: In a WebWorker scenario, this should always return null!
*/
getNativeElementSync(location: RenderElementRef): any { return null; }
/**
* Sets a property on an element.
*/
setElementProperty(location: RenderElementRef, propertyName: string, propertyValue: any) {
var fnArgs = [
new FnArg(location, WorkerElementRef),
new FnArg(propertyName, null),
new FnArg(propertyValue, null)
];
var args = new UiArguments("renderer", "setElementProperty", fnArgs);
this._messageBroker.runOnUiThread(args, null);
}
/**
* Sets an attribute on an element.
*/
setElementAttribute(location: RenderElementRef, attributeName: string, attributeValue: string) {
var fnArgs = [
new FnArg(location, WorkerElementRef),
new FnArg(attributeName, null),
new FnArg(attributeValue, null)
];
var args = new UiArguments("renderer", "setElementAttribute", fnArgs);
this._messageBroker.runOnUiThread(args, null);
}
/**
* Sets a class on an element.
*/
setElementClass(location: RenderElementRef, className: string, isAdd: boolean) {
var fnArgs =
[new FnArg(location, WorkerElementRef), new FnArg(className, null), new FnArg(isAdd, null)];
var args = new UiArguments("renderer", "setElementClass", fnArgs);
this._messageBroker.runOnUiThread(args, null);
}
/**
* Sets a style on an element.
*/
setElementStyle(location: RenderElementRef, styleName: string, styleValue: string) {
var fnArgs = [
new FnArg(location, WorkerElementRef),
new FnArg(styleName, null),
new FnArg(styleValue, null)
];
var args = new UiArguments("renderer", "setElementStyle", fnArgs);
this._messageBroker.runOnUiThread(args, null);
}
/**
* Calls a method on an element.
* Note: For now we're assuming that everything in the args list are primitive
*/
invokeElementMethod(location: RenderElementRef, methodName: string, args: List<any>) {
var fnArgs =
[new FnArg(location, WorkerElementRef), new FnArg(methodName, null), new FnArg(args, null)];
var uiArgs = new UiArguments("renderer", "invokeElementMethod", fnArgs);
this._messageBroker.runOnUiThread(uiArgs, null);
}
/**
* Sets the value of a text node.
*/
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);
this._messageBroker.runOnUiThread(args, null);
}
/**
* Sets the dispatcher for all events of the given view
*/
setEventDispatcher(viewRef: RenderViewRef, dispatcher: RenderEventDispatcher) {
// TODO(jteplitz602) support dom events in web worker. See #3046
}
}