refactor: move angular source to /packages rather than modules/@angular
This commit is contained in:
11
packages/platform-webworker/src/web_workers/shared/api.ts
Normal file
11
packages/platform-webworker/src/web_workers/shared/api.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {InjectionToken} from '@angular/core';
|
||||
|
||||
export const ON_WEB_WORKER = new InjectionToken<boolean>('WebWorker.onWebWorker');
|
@ -0,0 +1,168 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {EventEmitter, Injectable, Type, ɵstringify as stringify} from '@angular/core';
|
||||
import {MessageBus} from './message_bus';
|
||||
import {Serializer, SerializerTypes} from './serializer';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @experimental WebWorker support in Angular is experimental.
|
||||
*/
|
||||
export abstract class ClientMessageBrokerFactory {
|
||||
/**
|
||||
* Initializes the given channel and attaches a new {@link ClientMessageBroker} to it.
|
||||
*/
|
||||
abstract createMessageBroker(channel: string, runInZone?: boolean): ClientMessageBroker;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ClientMessageBrokerFactory_ extends ClientMessageBrokerFactory {
|
||||
/** @internal */
|
||||
_serializer: Serializer;
|
||||
constructor(private _messageBus: MessageBus, _serializer: Serializer) {
|
||||
super();
|
||||
this._serializer = _serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the given channel and attaches a new {@link ClientMessageBroker} to it.
|
||||
*/
|
||||
createMessageBroker(channel: string, runInZone: boolean = true): ClientMessageBroker {
|
||||
this._messageBus.initChannel(channel, runInZone);
|
||||
return new ClientMessageBroker_(this._messageBus, this._serializer, channel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental WebWorker support in Angular is experimental.
|
||||
*/
|
||||
export abstract class ClientMessageBroker {
|
||||
abstract runOnService(args: UiArguments, returnType: Type<any>|SerializerTypes): Promise<any>;
|
||||
}
|
||||
|
||||
interface PromiseCompleter {
|
||||
resolve: (result: any) => void;
|
||||
reject: (err: any) => void;
|
||||
}
|
||||
|
||||
export class ClientMessageBroker_ extends ClientMessageBroker {
|
||||
private _pending = new Map<string, PromiseCompleter>();
|
||||
private _sink: EventEmitter<any>;
|
||||
/** @internal */
|
||||
public _serializer: Serializer;
|
||||
|
||||
constructor(messageBus: MessageBus, _serializer: Serializer, public channel: any) {
|
||||
super();
|
||||
this._sink = messageBus.to(channel);
|
||||
this._serializer = _serializer;
|
||||
const source = messageBus.from(channel);
|
||||
|
||||
source.subscribe({next: (message: ResponseMessageData) => this._handleMessage(message)});
|
||||
}
|
||||
|
||||
private _generateMessageId(name: string): string {
|
||||
const time: string = stringify(new Date().getTime());
|
||||
let iteration: number = 0;
|
||||
let id: string = name + time + stringify(iteration);
|
||||
while (this._pending.has(id)) {
|
||||
id = `${name}${time}${iteration}`;
|
||||
iteration++;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
runOnService(args: UiArguments, returnType: Type<any>|SerializerTypes): Promise<any> {
|
||||
const fnArgs: any[] = [];
|
||||
if (args.args) {
|
||||
args.args.forEach(argument => {
|
||||
if (argument.type != null) {
|
||||
fnArgs.push(this._serializer.serialize(argument.value, argument.type));
|
||||
} else {
|
||||
fnArgs.push(argument.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let promise: Promise<any>;
|
||||
let id: string = null;
|
||||
if (returnType != null) {
|
||||
let completer: PromiseCompleter;
|
||||
promise = new Promise((resolve, reject) => { completer = {resolve, reject}; });
|
||||
id = this._generateMessageId(args.method);
|
||||
this._pending.set(id, completer);
|
||||
|
||||
promise.catch((err) => {
|
||||
if (console && console.error) {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
completer.reject(err);
|
||||
});
|
||||
|
||||
promise = promise.then(
|
||||
(v: any) => this._serializer ? this._serializer.deserialize(v, returnType) : v);
|
||||
} else {
|
||||
promise = null;
|
||||
}
|
||||
|
||||
const message: RequestMessageData = {
|
||||
'method': args.method,
|
||||
'args': fnArgs,
|
||||
};
|
||||
if (id != null) {
|
||||
message['id'] = id;
|
||||
}
|
||||
this._sink.emit(message);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
private _handleMessage(message: ResponseMessageData): void {
|
||||
if (message.type === 'result' || message.type === 'error') {
|
||||
const id = message.id;
|
||||
if (this._pending.has(id)) {
|
||||
if (message.type === 'result') {
|
||||
this._pending.get(id).resolve(message.value);
|
||||
} else {
|
||||
this._pending.get(id).reject(message.value);
|
||||
}
|
||||
this._pending.delete(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface RequestMessageData {
|
||||
method: string;
|
||||
args?: any[];
|
||||
id?: string;
|
||||
}
|
||||
|
||||
interface ResponseMessageData {
|
||||
type: 'result'|'error';
|
||||
value?: any;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental WebWorker support in Angular is experimental.
|
||||
*/
|
||||
export class FnArg {
|
||||
constructor(
|
||||
public value: any, public type: Type<any>|SerializerTypes = SerializerTypes.PRIMITIVE) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental WebWorker support in Angular is experimental.
|
||||
*/
|
||||
export class UiArguments {
|
||||
constructor(public method: string, public args?: FnArg[]) {}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {EventEmitter, NgZone} from '@angular/core';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Message Bus is a low level API used to communicate between the UI and the background.
|
||||
* Communication is based on a channel abstraction. Messages published in a
|
||||
* given channel to one MessageBusSink are received on the same channel
|
||||
* by the corresponding MessageBusSource.
|
||||
*
|
||||
* @experimental WebWorker support in Angular is currenlty experimental.
|
||||
*/
|
||||
export abstract class MessageBus implements MessageBusSource, MessageBusSink {
|
||||
/**
|
||||
* Sets up a new channel on the MessageBus.
|
||||
* MUST be called before calling from or to on the channel.
|
||||
* If runInZone is true then the source will emit events inside the angular zone
|
||||
* and the sink will buffer messages and send only once the zone exits.
|
||||
* if runInZone is false then the source will emit events inside the global zone
|
||||
* and the sink will send messages immediately.
|
||||
*/
|
||||
abstract initChannel(channel: string, runInZone?: boolean): void;
|
||||
|
||||
/**
|
||||
* Assigns this bus to the given zone.
|
||||
* Any callbacks attached to channels where runInZone was set to true on initialization
|
||||
* will be executed in the given zone.
|
||||
*/
|
||||
abstract attachToZone(zone: NgZone): void;
|
||||
|
||||
/**
|
||||
* Returns an {@link EventEmitter} that emits every time a message
|
||||
* is received on the given channel.
|
||||
*/
|
||||
abstract from(channel: string): EventEmitter<any>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns an {@link EventEmitter} for the given channel
|
||||
* To publish methods to that channel just call next on the returned emitter
|
||||
*/
|
||||
abstract to(channel: string): EventEmitter<any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental WebWorker support in Angular is currenlty experimental.
|
||||
*/
|
||||
export interface MessageBusSource {
|
||||
/**
|
||||
* Sets up a new channel on the MessageBusSource.
|
||||
* MUST be called before calling from on the channel.
|
||||
* If runInZone is true then the source will emit events inside the angular zone.
|
||||
* if runInZone is false then the source will emit events inside the global zone.
|
||||
*/
|
||||
initChannel(channel: string, runInZone: boolean): void;
|
||||
|
||||
/**
|
||||
* Assigns this source to the given zone.
|
||||
* Any channels which are initialized with runInZone set to true will emit events that will be
|
||||
* executed within the given zone.
|
||||
*/
|
||||
attachToZone(zone: NgZone): void;
|
||||
|
||||
/**
|
||||
* Returns an {@link EventEmitter} that emits every time a message
|
||||
* is received on the given channel.
|
||||
*/
|
||||
from(channel: string): EventEmitter<any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental WebWorker support in Angular is currenlty experimental.
|
||||
*/
|
||||
export interface MessageBusSink {
|
||||
/**
|
||||
* Sets up a new channel on the MessageBusSink.
|
||||
* MUST be called before calling to on the channel.
|
||||
* If runInZone is true the sink will buffer messages and send only once the zone exits.
|
||||
* if runInZone is false the sink will send messages immediatly.
|
||||
*/
|
||||
initChannel(channel: string, runInZone: boolean): void;
|
||||
|
||||
/**
|
||||
* Assigns this sink to the given zone.
|
||||
* Any channels which are initialized with runInZone set to true will wait for the given zone
|
||||
* to exit before sending messages.
|
||||
*/
|
||||
attachToZone(zone: NgZone): void;
|
||||
|
||||
/**
|
||||
* Returns an {@link EventEmitter} for the given channel
|
||||
* To publish methods to that channel just call next on the returned emitter
|
||||
*/
|
||||
to(channel: string): EventEmitter<any>;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* All channels used by angular's WebWorker components are listed here.
|
||||
* You should not use these channels in your application code.
|
||||
*/
|
||||
export const RENDERER_2_CHANNEL = 'v2.ng-Renderer';
|
||||
export const EVENT_2_CHANNEL = 'v2.ng-Events';
|
||||
|
||||
export const ROUTER_CHANNEL = 'ng-Router';
|
@ -0,0 +1,152 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {EventEmitter, Injectable, NgZone} from '@angular/core';
|
||||
|
||||
import {MessageBus, MessageBusSink, MessageBusSource} from './message_bus';
|
||||
|
||||
|
||||
|
||||
// TODO(jteplitz602) Replace this with the definition in lib.webworker.d.ts(#3492)
|
||||
export interface PostMessageTarget {
|
||||
postMessage: (message: any, transfer?: [ArrayBuffer]) => void;
|
||||
}
|
||||
|
||||
export class PostMessageBusSink implements MessageBusSink {
|
||||
private _zone: NgZone;
|
||||
private _channels: {[key: string]: _Channel} = {};
|
||||
private _messageBuffer: Array<Object> = [];
|
||||
|
||||
constructor(private _postMessageTarget: PostMessageTarget) {}
|
||||
|
||||
attachToZone(zone: NgZone): void {
|
||||
this._zone = zone;
|
||||
this._zone.runOutsideAngular(
|
||||
() => { this._zone.onStable.subscribe({next: () => { this._handleOnEventDone(); }}); });
|
||||
}
|
||||
|
||||
initChannel(channel: string, runInZone: boolean = true): void {
|
||||
if (this._channels.hasOwnProperty(channel)) {
|
||||
throw new Error(`${channel} has already been initialized`);
|
||||
}
|
||||
|
||||
const emitter = new EventEmitter(false);
|
||||
const channelInfo = new _Channel(emitter, runInZone);
|
||||
this._channels[channel] = channelInfo;
|
||||
emitter.subscribe((data: Object) => {
|
||||
const message = {channel: channel, message: data};
|
||||
if (runInZone) {
|
||||
this._messageBuffer.push(message);
|
||||
} else {
|
||||
this._sendMessages([message]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
to(channel: string): EventEmitter<any> {
|
||||
if (this._channels.hasOwnProperty(channel)) {
|
||||
return this._channels[channel].emitter;
|
||||
} else {
|
||||
throw new Error(`${channel} is not set up. Did you forget to call initChannel?`);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleOnEventDone() {
|
||||
if (this._messageBuffer.length > 0) {
|
||||
this._sendMessages(this._messageBuffer);
|
||||
this._messageBuffer = [];
|
||||
}
|
||||
}
|
||||
|
||||
private _sendMessages(messages: Array<Object>) { this._postMessageTarget.postMessage(messages); }
|
||||
}
|
||||
|
||||
export class PostMessageBusSource implements MessageBusSource {
|
||||
private _zone: NgZone;
|
||||
private _channels: {[key: string]: _Channel} = {};
|
||||
|
||||
constructor(eventTarget?: EventTarget) {
|
||||
if (eventTarget) {
|
||||
eventTarget.addEventListener('message', (ev: MessageEvent) => this._handleMessages(ev));
|
||||
} else {
|
||||
// if no eventTarget is given we assume we're in a WebWorker and listen on the global scope
|
||||
const workerScope = <EventTarget>self;
|
||||
workerScope.addEventListener('message', (ev: MessageEvent) => this._handleMessages(ev));
|
||||
}
|
||||
}
|
||||
|
||||
attachToZone(zone: NgZone) { this._zone = zone; }
|
||||
|
||||
initChannel(channel: string, runInZone: boolean = true) {
|
||||
if (this._channels.hasOwnProperty(channel)) {
|
||||
throw new Error(`${channel} has already been initialized`);
|
||||
}
|
||||
|
||||
const emitter = new EventEmitter(false);
|
||||
const channelInfo = new _Channel(emitter, runInZone);
|
||||
this._channels[channel] = channelInfo;
|
||||
}
|
||||
|
||||
from(channel: string): EventEmitter<any> {
|
||||
if (this._channels.hasOwnProperty(channel)) {
|
||||
return this._channels[channel].emitter;
|
||||
} else {
|
||||
throw new Error(`${channel} is not set up. Did you forget to call initChannel?`);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleMessages(ev: MessageEvent): void {
|
||||
const messages = ev.data;
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
this._handleMessage(messages[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleMessage(data: any): void {
|
||||
const channel = data.channel;
|
||||
if (this._channels.hasOwnProperty(channel)) {
|
||||
const channelInfo = this._channels[channel];
|
||||
if (channelInfo.runInZone) {
|
||||
this._zone.run(() => { channelInfo.emitter.emit(data.message); });
|
||||
} else {
|
||||
channelInfo.emitter.emit(data.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A TypeScript implementation of {@link MessageBus} for communicating via JavaScript's
|
||||
* postMessage API.
|
||||
*/
|
||||
@Injectable()
|
||||
export class PostMessageBus implements MessageBus {
|
||||
constructor(public sink: PostMessageBusSink, public source: PostMessageBusSource) {}
|
||||
|
||||
attachToZone(zone: NgZone): void {
|
||||
this.source.attachToZone(zone);
|
||||
this.sink.attachToZone(zone);
|
||||
}
|
||||
|
||||
initChannel(channel: string, runInZone: boolean = true): void {
|
||||
this.source.initChannel(channel, runInZone);
|
||||
this.sink.initChannel(channel, runInZone);
|
||||
}
|
||||
|
||||
from(channel: string): EventEmitter<any> { return this.source.from(channel); }
|
||||
|
||||
to(channel: string): EventEmitter<any> { return this.sink.to(channel); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class that wraps a channel's {@link EventEmitter} and
|
||||
* keeps track of if it should run in the zone.
|
||||
*/
|
||||
class _Channel {
|
||||
constructor(public emitter: EventEmitter<any>, public runInZone: boolean) {}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
@Injectable()
|
||||
export class RenderStore {
|
||||
private _nextIndex = 0;
|
||||
private _lookupById = new Map<number, any>();
|
||||
private _lookupByObject = new Map<any, number>();
|
||||
|
||||
allocateId(): number { return this._nextIndex++; }
|
||||
|
||||
store(obj: any, id: number): void {
|
||||
if (id == null) return;
|
||||
this._lookupById.set(id, obj);
|
||||
this._lookupByObject.set(obj, id);
|
||||
}
|
||||
|
||||
remove(obj: any): void {
|
||||
const index = this._lookupByObject.get(obj);
|
||||
if (index != null) {
|
||||
this._lookupByObject.delete(obj);
|
||||
this._lookupById.delete(index);
|
||||
}
|
||||
}
|
||||
|
||||
deserialize(id: number): any {
|
||||
return this._lookupById.has(id) ? this._lookupById.get(id) : null;
|
||||
}
|
||||
|
||||
serialize(obj: any): number { return obj == null ? null : this._lookupByObject.get(obj); }
|
||||
}
|
142
packages/platform-webworker/src/web_workers/shared/serializer.ts
Normal file
142
packages/platform-webworker/src/web_workers/shared/serializer.ts
Normal file
@ -0,0 +1,142 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Injectable, RenderComponentType, RendererType2, Type, ɵstringify as stringify} from '@angular/core';
|
||||
import {RenderStore} from './render_store';
|
||||
|
||||
|
||||
/**
|
||||
* Any type that does not need to be serialized (string, number, boolean)
|
||||
*
|
||||
* @experimental WebWorker support in Angular is currently experimental.
|
||||
* @deprecated in v4. Use SerializerTypes.PRIMITIVE instead
|
||||
*/
|
||||
export const PRIMITIVE = SerializerTypes.PRIMITIVE;
|
||||
|
||||
export class LocationType {
|
||||
constructor(
|
||||
public href: string, public protocol: string, public host: string, public hostname: string,
|
||||
public port: string, public pathname: string, public search: string, public hash: string,
|
||||
public origin: string) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental WebWorker support in Angular is currently experimental.
|
||||
*/
|
||||
export const enum SerializerTypes {
|
||||
// RendererType2
|
||||
RENDERER_TYPE_2,
|
||||
// Primitive types
|
||||
PRIMITIVE,
|
||||
// An object stored in a RenderStore
|
||||
RENDER_STORE_OBJECT,
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class Serializer {
|
||||
constructor(private _renderStore: RenderStore) {}
|
||||
|
||||
serialize(obj: any, type: Type<any>|SerializerTypes = SerializerTypes.PRIMITIVE): Object {
|
||||
if (obj == null || type === SerializerTypes.PRIMITIVE) {
|
||||
return obj;
|
||||
}
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map(v => this.serialize(v, type));
|
||||
}
|
||||
if (type === SerializerTypes.RENDER_STORE_OBJECT) {
|
||||
return this._renderStore.serialize(obj);
|
||||
}
|
||||
if (type === RenderComponentType) {
|
||||
return this._serializeRenderComponentType(obj);
|
||||
}
|
||||
if (type === SerializerTypes.RENDERER_TYPE_2) {
|
||||
return this._serializeRendererType2(obj);
|
||||
}
|
||||
if (type === LocationType) {
|
||||
return this._serializeLocation(obj);
|
||||
}
|
||||
throw new Error(`No serializer for type ${stringify(type)}`);
|
||||
}
|
||||
|
||||
deserialize(map: any, type: Type<any>|SerializerTypes = SerializerTypes.PRIMITIVE, data?: any):
|
||||
any {
|
||||
if (map == null || type === SerializerTypes.PRIMITIVE) {
|
||||
return map;
|
||||
}
|
||||
if (Array.isArray(map)) {
|
||||
return map.map(val => this.deserialize(val, type, data));
|
||||
}
|
||||
if (type === SerializerTypes.RENDER_STORE_OBJECT) {
|
||||
return this._renderStore.deserialize(map);
|
||||
}
|
||||
if (type === RenderComponentType) {
|
||||
return this._deserializeRenderComponentType(map);
|
||||
}
|
||||
if (type === SerializerTypes.RENDERER_TYPE_2) {
|
||||
return this._deserializeRendererType2(map);
|
||||
}
|
||||
if (type === LocationType) {
|
||||
return this._deserializeLocation(map);
|
||||
}
|
||||
throw new Error(`No deserializer for type ${stringify(type)}`);
|
||||
}
|
||||
|
||||
private _serializeLocation(loc: LocationType): Object {
|
||||
return {
|
||||
'href': loc.href,
|
||||
'protocol': loc.protocol,
|
||||
'host': loc.host,
|
||||
'hostname': loc.hostname,
|
||||
'port': loc.port,
|
||||
'pathname': loc.pathname,
|
||||
'search': loc.search,
|
||||
'hash': loc.hash,
|
||||
'origin': loc.origin,
|
||||
};
|
||||
}
|
||||
|
||||
private _deserializeLocation(loc: {[key: string]: any}): LocationType {
|
||||
return new LocationType(
|
||||
loc['href'], loc['protocol'], loc['host'], loc['hostname'], loc['port'], loc['pathname'],
|
||||
loc['search'], loc['hash'], loc['origin']);
|
||||
}
|
||||
|
||||
private _serializeRenderComponentType(type: RenderComponentType): Object {
|
||||
return {
|
||||
'id': type.id,
|
||||
'templateUrl': type.templateUrl,
|
||||
'slotCount': type.slotCount,
|
||||
'encapsulation': this.serialize(type.encapsulation),
|
||||
'styles': this.serialize(type.styles),
|
||||
};
|
||||
}
|
||||
|
||||
private _deserializeRenderComponentType(props: {[key: string]: any}): RenderComponentType {
|
||||
return new RenderComponentType(
|
||||
props['id'], props['templateUrl'], props['slotCount'],
|
||||
this.deserialize(props['encapsulation']), this.deserialize(props['styles']), {});
|
||||
}
|
||||
|
||||
private _serializeRendererType2(type: RendererType2): {[key: string]: any} {
|
||||
return {
|
||||
'id': type.id,
|
||||
'encapsulation': this.serialize(type.encapsulation),
|
||||
'styles': this.serialize(type.styles),
|
||||
'data': this.serialize(type.data),
|
||||
};
|
||||
}
|
||||
|
||||
private _deserializeRendererType2(props: {[key: string]: any}): RendererType2 {
|
||||
return {
|
||||
id: props['id'],
|
||||
encapsulation: props['encapsulation'],
|
||||
styles: this.deserialize(props['styles']),
|
||||
data: this.deserialize(props['data'])
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {EventEmitter, Injectable, Type} from '@angular/core';
|
||||
import {MessageBus} from '../shared/message_bus';
|
||||
import {Serializer, SerializerTypes} from '../shared/serializer';
|
||||
|
||||
|
||||
/**
|
||||
* @experimental WebWorker support in Angular is currently experimental.
|
||||
*/
|
||||
export abstract class ServiceMessageBrokerFactory {
|
||||
/**
|
||||
* Initializes the given channel and attaches a new {@link ServiceMessageBroker} to it.
|
||||
*/
|
||||
abstract createMessageBroker(channel: string, runInZone?: boolean): ServiceMessageBroker;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ServiceMessageBrokerFactory_ extends ServiceMessageBrokerFactory {
|
||||
/** @internal */
|
||||
_serializer: Serializer;
|
||||
|
||||
constructor(private _messageBus: MessageBus, _serializer: Serializer) {
|
||||
super();
|
||||
this._serializer = _serializer;
|
||||
}
|
||||
|
||||
createMessageBroker(channel: string, runInZone: boolean = true): ServiceMessageBroker {
|
||||
this._messageBus.initChannel(channel, runInZone);
|
||||
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 deserializes its arguments and calls the registered method.
|
||||
* If that method returns a promise, the UIMessageBroker returns the result to the worker.
|
||||
*
|
||||
* @experimental WebWorker support in Angular is currently experimental.
|
||||
*/
|
||||
export abstract class ServiceMessageBroker {
|
||||
abstract registerMethod(
|
||||
methodName: string, signature: Array<Type<any>|SerializerTypes>, method: Function,
|
||||
returnType?: Type<any>|SerializerTypes): void;
|
||||
}
|
||||
|
||||
export class ServiceMessageBroker_ extends ServiceMessageBroker {
|
||||
private _sink: EventEmitter<any>;
|
||||
private _methods = new Map<string, Function>();
|
||||
|
||||
constructor(messageBus: MessageBus, private _serializer: Serializer, public channel: string) {
|
||||
super();
|
||||
this._sink = messageBus.to(channel);
|
||||
const source = messageBus.from(channel);
|
||||
source.subscribe({next: (message: any) => this._handleMessage(message)});
|
||||
}
|
||||
|
||||
registerMethod(
|
||||
methodName: string, signature: Array<Type<any>|SerializerTypes>,
|
||||
method: (..._: any[]) => Promise<any>| void, returnType?: Type<any>|SerializerTypes): void {
|
||||
this._methods.set(methodName, (message: ReceivedMessage) => {
|
||||
const serializedArgs = message.args;
|
||||
const numArgs = signature ? signature.length : 0;
|
||||
const deserializedArgs = new Array(numArgs);
|
||||
for (let i = 0; i < numArgs; i++) {
|
||||
const serializedArg = serializedArgs[i];
|
||||
deserializedArgs[i] = this._serializer.deserialize(serializedArg, signature[i]);
|
||||
}
|
||||
|
||||
const promise = method(...deserializedArgs);
|
||||
if (returnType && promise) {
|
||||
this._wrapWebWorkerPromise(message.id, promise, returnType);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _handleMessage(message: ReceivedMessage): void {
|
||||
if (this._methods.has(message.method)) {
|
||||
this._methods.get(message.method)(message);
|
||||
}
|
||||
}
|
||||
|
||||
private _wrapWebWorkerPromise(id: string, promise: Promise<any>, type: Type<any>|SerializerTypes):
|
||||
void {
|
||||
promise.then((result: any) => {
|
||||
this._sink.emit({
|
||||
'type': 'result',
|
||||
'value': this._serializer.serialize(result, type),
|
||||
'id': id,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental WebWorker support in Angular is currently experimental.
|
||||
*/
|
||||
export interface ReceivedMessage {
|
||||
method: string;
|
||||
args: any[];
|
||||
id: string;
|
||||
type: string;
|
||||
}
|
Reference in New Issue
Block a user