refactor(WebWorker): Use the new generic bootstrap.

BREAKING CHANGE:

You can no longer bootstrap a WebWorker or Isolate using `bootstrap` or `bootstrapWebWorker`. Instead you have to do the following:

In TypeScript:
```TypeScript
// index.js
import {WORKER_RENDER_PLATFORM, WORKER_RENDER_APPLICATION, WORKER_SCRIPT} from "angular2/platforms/worker_render";
import {platform} from "angular2/platform";

platform([WORKER_RENDER_PLATFORM])
.application([WORKER_RENDER_APPLICATION, new Provider(WORKER_SCRIPT, {useValue: "loader.js"});
```
```JavaScript
// loader.js
importScripts("https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.33.3/es6-shim.js", "https://jspm.io/system@0.16.js", "angular2/web_worker/worker.js");
System.import("app");
```
```TypeScript
// app.ts
import {Component, View} from "angular2/core";
import {WORKER_APP_PLATFORM, setupWebWorker} from "angular2/platforms/worker_app";
import {platform} from "angular2/platform";

@Component({
  selector: "hello-world"
})
@View({
  template: "<h1>Hello {{name}}</h1>
})
export class HelloWorld {
  name: string = "Jane";
}

platform([WORKER_APP_PLATFORM])
.asyncApplication(setupWebWorker, optionalProviders?)
.then((ref) => ref.bootstrap(RootComponent));
```

In Dart:
```Dart
// index.dart
import "angular2/platform.dart";
import "angular2/platforms/worker_render.dart";

main() {
  platform([WORKER_RENDER_PLATFORM])
  .asyncApplication(initIsolate("my_worker.dart"));
}
```
```Dart
// background_index.dart
import "angular2/platform.dart";
import "angular2/platforms/worker_app.dart";
import "package:angular2/src/core/reflection/reflection.dart";
import "package:angular2/src/core/reflection/reflection_capabilities.dart";

@Component(
  selector: "hello-world"
)
@View(
  template: "<h1>Hello {{name}}</h1>"
)
class HelloWorld {
  String name = "Jane";
}

main(List<String> args, SendPort replyTo) {
  reflector.reflectionCapabilities = new ReflectionCapabilities();
  platform([WORKER_APP_PLATFORM])
    .asyncApplication(setupIsolate(replyTo))
      .then((ref) => ref.bootstrap(RootComponent));
}

```

You should no longer import from the `angular2/web_worker/worker` and `angular2/web_worker/ui` paths. Instead you can now import directly from core, directives, etc..

The WebWorkerApplication class has been removed. If you want to use ServiceMessageBroker or ClientMessageBroker on the render thread, you must inject their factories via DI.
If you need to use the MessageBus on the render thread you must also obtain it through DI.

closes #3277
closes #5473

Closes #5519
This commit is contained in:
Jason Teplitz
2015-12-02 20:25:24 -08:00
parent 93a1ec29e1
commit 1710272b3c
48 changed files with 827 additions and 719 deletions

View File

@ -79,4 +79,4 @@ export function initDomAdapter() {
BrowserDomAdapter.makeCurrent();
wtfInit();
BrowserGetTestability.init();
}
}

View File

@ -0,0 +1,25 @@
library angular2.src.platform.worker_app;
import 'package:angular2/src/core/zone/ng_zone.dart';
import 'package:angular2/src/platform/server/webworker_adapter.dart';
import 'package:angular2/src/platform/worker_app_common.dart';
import 'package:angular2/src/web_workers/shared/isolate_message_bus.dart';
import 'dart:isolate';
setupIsolate(SendPort replyTo) {
return (NgZone zone) {
WebWorkerDomAdapter.makeCurrent();
ReceivePort rPort = new ReceivePort();
var sink = new WebWorkerMessageBusSink(replyTo, rPort);
var source = new IsolateMessageBusSource(rPort);
IsolateMessageBus bus = new IsolateMessageBus(sink, source);
return genericWorkerAppProviders(bus, zone);
};
}
class WebWorkerMessageBusSink extends IsolateMessageBusSink {
WebWorkerMessageBusSink(SendPort sPort, ReceivePort rPort) : super(sPort) {
sPort.send(rPort.sendPort);
}
}

View File

@ -0,0 +1,28 @@
import {NgZone} from 'angular2/src/core/zone/ng_zone';
import {Type, CONST_EXPR, isPresent} from 'angular2/src/facade/lang';
import {Provider} from 'angular2/src/core/di';
import {Parse5DomAdapter} from 'angular2/src/platform/server/parse5_adapter';
import {
PostMessageBus,
PostMessageBusSink,
PostMessageBusSource
} from 'angular2/src/web_workers/shared/post_message_bus';
import {genericWorkerAppProviders} from './worker_app_common';
// TODO(jteplitz602) remove this and compile with lib.webworker.d.ts (#3492)
interface PostMessageInterface {
(message: any, transferrables?:[ArrayBuffer]): void;
}
var _postMessage: PostMessageInterface = <any>postMessage;
export function setupWebWorker(zone: NgZone): Promise<Array<Type | Provider | any[]>> {
Parse5DomAdapter.makeCurrent();
var sink = new PostMessageBusSink({
postMessage:
(message: any, transferrables?:[ArrayBuffer]) => { _postMessage(message, transferrables); }
});
var source = new PostMessageBusSource();
var bus = new PostMessageBus(sink, source);
return genericWorkerAppProviders(bus, zone);
}

View File

@ -0,0 +1,97 @@
import {XHR} from 'angular2/src/compiler/xhr';
import {WebWorkerXHRImpl} from 'angular2/src/web_workers/worker/xhr_impl';
import {ListWrapper} from 'angular2/src/facade/collection';
import {AppRootUrl} from 'angular2/src/compiler/app_root_url';
import {WebWorkerRenderer} from 'angular2/src/web_workers/worker/renderer';
import {print, Type, CONST_EXPR, isPresent} from 'angular2/src/facade/lang';
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
import {Renderer} from 'angular2/src/core/render/api';
import {
PLATFORM_DIRECTIVES,
PLATFORM_PIPES,
ExceptionHandler,
APPLICATION_COMMON_PROVIDERS,
PLATFORM_COMMON_PROVIDERS,
} from 'angular2/core';
import {COMMON_DIRECTIVES, COMMON_PIPES, FORM_PROVIDERS} from "angular2/common";
import {
ClientMessageBrokerFactory,
ClientMessageBrokerFactory_
} from 'angular2/src/web_workers/shared/client_message_broker';
import {
ServiceMessageBrokerFactory,
ServiceMessageBrokerFactory_
} from 'angular2/src/web_workers/shared/service_message_broker';
import {COMPILER_PROVIDERS} from 'angular2/src/compiler/compiler';
import {Serializer} from "angular2/src/web_workers/shared/serializer";
import {ON_WEB_WORKER} from "angular2/src/web_workers/shared/api";
import {Provider} from 'angular2/src/core/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';
import {WebWorkerEventDispatcher} from 'angular2/src/web_workers/worker/event_dispatcher';
import {NgZone} from 'angular2/src/core/zone/ng_zone';
import {Promise, PromiseWrapper, PromiseCompleter} from 'angular2/src/facade/async';
import {SETUP_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
import {ObservableWrapper} from 'angular2/src/facade/async';
class PrintLogger {
log = print;
logError = print;
logGroup = print;
logGroupEnd() {}
}
export const WORKER_APP_PLATFORM: Array<any /*Type | Provider | any[]*/> =
CONST_EXPR([PLATFORM_COMMON_PROVIDERS]);
export const WORKER_APP_COMMON_PROVIDERS: Array<any /*Type | Provider | any[]*/> = CONST_EXPR([
APPLICATION_COMMON_PROVIDERS,
COMPILER_PROVIDERS,
FORM_PROVIDERS,
Serializer,
new Provider(PLATFORM_PIPES, {useValue: COMMON_PIPES, multi: true}),
new Provider(PLATFORM_DIRECTIVES, {useValue: COMMON_DIRECTIVES, multi: true}),
new Provider(ClientMessageBrokerFactory, {useClass: ClientMessageBrokerFactory_}),
new Provider(ServiceMessageBrokerFactory, {useClass: ServiceMessageBrokerFactory_}),
WebWorkerRenderer,
new Provider(Renderer, {useExisting: WebWorkerRenderer}),
new Provider(ON_WEB_WORKER, {useValue: true}),
RenderViewWithFragmentsStore,
RenderProtoViewRefStore,
new Provider(ExceptionHandler, {useFactory: _exceptionHandler, deps: []}),
WebWorkerXHRImpl,
new Provider(XHR, {useExisting: WebWorkerXHRImpl}),
WebWorkerEventDispatcher
]);
function _exceptionHandler(): ExceptionHandler {
return new ExceptionHandler(new PrintLogger());
}
/**
* Asynchronously returns a list of providers that can be used to initialize the
* Application injector.
* Also takes care of attaching the {@link MessageBus} to the given {@link NgZone}.
*/
export function genericWorkerAppProviders(bus: MessageBus,
zone: NgZone): Promise<Array<Type | Provider | any[]>> {
var bootstrapProcess: PromiseCompleter<any> = PromiseWrapper.completer();
bus.attachToZone(zone);
bus.initChannel(SETUP_CHANNEL, false);
var subscription: any;
var emitter = bus.from(SETUP_CHANNEL);
subscription = ObservableWrapper.subscribe(emitter, (initData: {[key: string]: any}) => {
var bindings = ListWrapper.concat(WORKER_APP_COMMON_PROVIDERS, [
new Provider(MessageBus, {useValue: bus}),
new Provider(AppRootUrl, {useValue: new AppRootUrl(initData['rootUrl'])}),
]);
bootstrapProcess.resolve(bindings);
ObservableWrapper.dispose(subscription);
});
ObservableWrapper.callNext(bus.to(SETUP_CHANNEL), "ready");
return bootstrapProcess.promise;
}

View File

@ -0,0 +1,68 @@
library angular2.src.platform.worker_render;
import 'package:angular2/src/platform/worker_render_common.dart'
show
WORKER_RENDER_APP_COMMON,
WORKER_RENDER_MESSAGING_PROVIDERS,
WORKER_SCRIPT,
initializeGenericWorkerRenderer;
import 'package:angular2/src/web_workers/shared/isolate_message_bus.dart';
import 'package:angular2/src/web_workers/shared/message_bus.dart';
import 'package:angular2/core.dart';
import 'package:angular2/src/core/di.dart';
import 'package:angular2/src/core/zone/ng_zone.dart';
import 'dart:isolate';
import 'dart:async';
const WORKER_RENDER_APP = WORKER_RENDER_APP_COMMON;
initIsolate(String scriptUri) {
return (NgZone zone) async {
var instance = await spawnIsolate(Uri.parse(scriptUri));
return [
WORKER_RENDER_APP_COMMON,
new Provider(WebWorkerInstance, useValue: instance),
new Provider(APP_INITIALIZER,
useFactory: (injector) => () => initializeGenericWorkerRenderer(injector),
multi: true,
deps: [Injector]),
new Provider(MessageBus, useValue: instance.bus)
];
};
}
/**
* Spawns a new class and initializes the WebWorkerInstance
*/
Future<WebWorkerInstance> spawnIsolate(Uri uri) async {
var receivePort = new ReceivePort();
var isolateEndSendPort = receivePort.sendPort;
var isolate = await Isolate.spawnUri(uri, const [], isolateEndSendPort);
var source = new UIMessageBusSource(receivePort);
var sendPort = await source.sink;
var sink = new IsolateMessageBusSink(sendPort);
var bus = new IsolateMessageBus(sink, source);
return new WebWorkerInstance(isolate, bus);
}
class UIMessageBusSource extends IsolateMessageBusSource {
UIMessageBusSource(ReceivePort port) : super(port);
Future<SendPort> get sink => stream.firstWhere((message) {
return message is SendPort;
});
}
/**
* Wrapper class that exposes the Isolate
* and underlying {@link MessageBus} for lower level message passing.
*/
@Injectable()
class WebWorkerInstance {
Isolate worker;
MessageBus bus;
WebWorkerInstance(this.worker, this.bus);
}

View File

@ -0,0 +1,77 @@
import {
PostMessageBus,
PostMessageBusSink,
PostMessageBusSource
} from 'angular2/src/web_workers/shared/post_message_bus';
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
import {APP_INITIALIZER} from 'angular2/core';
import {Injector, Injectable, Provider} from 'angular2/src/core/di';
import {WebWorkerSetup} from 'angular2/src/web_workers/ui/setup';
import {MessageBasedRenderer} from 'angular2/src/web_workers/ui/renderer';
import {MessageBasedXHRImpl} from 'angular2/src/web_workers/ui/xhr_impl';
import {
WORKER_RENDER_APP_COMMON,
WORKER_RENDER_MESSAGING_PROVIDERS,
WORKER_SCRIPT,
initializeGenericWorkerRenderer
} from 'angular2/src/platform/worker_render_common';
import {BaseException} from 'angular2/src/facade/exceptions';
import {CONST_EXPR} from 'angular2/src/facade/lang';
/**
* Wrapper class that exposes the Worker
* and underlying {@link MessageBus} for lower level message passing.
*/
@Injectable()
export class WebWorkerInstance {
public worker: Worker;
public bus: MessageBus;
/** @internal */
public init(worker: Worker, bus: MessageBus) {
this.worker = worker;
this.bus = bus;
}
}
/**
* An array of providers that should be passed into `application()` when initializing a new Worker.
*/
export const WORKER_RENDER_APP: Array<any /*Type | Provider | any[]*/> = CONST_EXPR([
WORKER_RENDER_APP_COMMON,
WebWorkerInstance,
new Provider(APP_INITIALIZER,
{
useFactory: (injector) => () => initWebWorkerApplication(injector),
multi: true,
deps: [Injector]
}),
new Provider(MessageBus, {useFactory: (instance) => instance.bus, deps: [WebWorkerInstance]})
]);
function initWebWorkerApplication(injector: Injector): void {
var scriptUri: string;
try {
scriptUri = injector.get(WORKER_SCRIPT);
} catch (e) {
throw new BaseException(
"You must provide your WebWorker's initialization script with the WORKER_SCRIPT token");
}
let instance = injector.get(WebWorkerInstance);
spawnWebWorker(scriptUri, instance);
initializeGenericWorkerRenderer(injector);
}
/**
* Spawns a new class and initializes the WebWorkerInstance
*/
function spawnWebWorker(uri: string, instance: WebWorkerInstance): void {
var webWorker: Worker = new Worker(uri);
var sink = new PostMessageBusSink(webWorker);
var source = new PostMessageBusSource(webWorker);
var bus = new PostMessageBus(sink, source);
instance.init(webWorker, bus);
}

View File

@ -0,0 +1,120 @@
import {CONST_EXPR} from 'angular2/src/facade/lang';
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
import {NgZone} from 'angular2/src/core/zone/ng_zone';
import {AnchorBasedAppRootUrl} from 'angular2/src/compiler/anchor_based_app_root_url';
import {AppRootUrl} from 'angular2/src/compiler/app_root_url';
import {
PLATFORM_DIRECTIVES,
PLATFORM_PIPES,
ComponentRef,
platform,
ExceptionHandler,
Reflector,
reflector,
APPLICATION_COMMON_PROVIDERS,
PLATFORM_COMMON_PROVIDERS,
Renderer,
PLATFORM_INITIALIZER,
APP_INITIALIZER
} from 'angular2/core';
import {EVENT_MANAGER_PLUGINS, EventManager} from 'angular2/platform/common_dom';
import {provide, Provider, Injector, OpaqueToken} from 'angular2/src/core/di';
// TODO change these imports once dom_adapter is moved out of core
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
import {DomEventsPlugin} from 'angular2/src/platform/dom/events/dom_events';
import {KeyEventsPlugin} from 'angular2/src/platform/dom/events/key_events';
import {HammerGesturesPlugin} from 'angular2/src/platform/dom/events/hammer_gestures';
import {DOCUMENT} from 'angular2/src/platform/dom/dom_tokens';
import {DomRenderer, DomRenderer_} from 'angular2/src/platform/dom/dom_renderer';
import {DomSharedStylesHost} from 'angular2/src/platform/dom/shared_styles_host';
import {SharedStylesHost} from "angular2/src/platform/dom/shared_styles_host";
import {BrowserDetails} from 'angular2/src/animate/browser_details';
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
import {XHR} from 'angular2/compiler';
import {XHRImpl} from 'angular2/src/platform/browser/xhr_impl';
import {Testability} from 'angular2/src/core/testability/testability';
import {BrowserGetTestability} from 'angular2/src/platform/browser/testability';
import {BrowserDomAdapter} from './browser/browser_adapter';
import {wtfInit} from 'angular2/src/core/profile/wtf_init';
import {WebWorkerSetup} from 'angular2/src/web_workers/ui/setup';
import {MessageBasedRenderer} from 'angular2/src/web_workers/ui/renderer';
import {MessageBasedXHRImpl} from 'angular2/src/web_workers/ui/xhr_impl';
import {
ServiceMessageBrokerFactory,
ServiceMessageBrokerFactory_
} from 'angular2/src/web_workers/shared/service_message_broker';
import {
ClientMessageBrokerFactory,
ClientMessageBrokerFactory_
} from 'angular2/src/web_workers/shared/client_message_broker';
import {Serializer} from 'angular2/src/web_workers/shared/serializer';
import {ON_WEB_WORKER} from 'angular2/src/web_workers/shared/api';
import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_proto_view_ref_store';
import {
RenderViewWithFragmentsStore
} from 'angular2/src/web_workers/shared/render_view_with_fragments_store';
export const WORKER_SCRIPT: OpaqueToken = CONST_EXPR(new OpaqueToken("WebWorkerScript"));
// Message based Worker classes that listen on the MessageBus
export const WORKER_RENDER_MESSAGING_PROVIDERS: Array<any /*Type | Provider | any[]*/> =
CONST_EXPR([MessageBasedRenderer, MessageBasedXHRImpl, WebWorkerSetup]);
export const WORKER_RENDER_PLATFORM: Array<any /*Type | Provider | any[]*/> = CONST_EXPR([
PLATFORM_COMMON_PROVIDERS,
new Provider(PLATFORM_INITIALIZER, {useValue: initWebWorkerRenderPlatform, multi: true})
]);
export const WORKER_RENDER_APP_COMMON: Array<any /*Type | Provider | any[]*/> = CONST_EXPR([
APPLICATION_COMMON_PROVIDERS,
WORKER_RENDER_MESSAGING_PROVIDERS,
new Provider(ExceptionHandler, {useFactory: _exceptionHandler, deps: []}),
new Provider(DOCUMENT, {useFactory: _document, deps: []}),
// TODO(jteplitz602): Investigate if we definitely need EVENT_MANAGER on the render thread
// #5298
new Provider(EVENT_MANAGER_PLUGINS, {useClass: DomEventsPlugin, multi: true}),
new Provider(EVENT_MANAGER_PLUGINS, {useClass: KeyEventsPlugin, multi: true}),
new Provider(EVENT_MANAGER_PLUGINS, {useClass: HammerGesturesPlugin, multi: true}),
new Provider(DomRenderer, {useClass: DomRenderer_}),
new Provider(Renderer, {useExisting: DomRenderer}),
new Provider(SharedStylesHost, {useExisting: DomSharedStylesHost}),
new Provider(XHR, {useClass: XHRImpl}),
MessageBasedXHRImpl,
new Provider(ServiceMessageBrokerFactory, {useClass: ServiceMessageBrokerFactory_}),
new Provider(ClientMessageBrokerFactory, {useClass: ClientMessageBrokerFactory_}),
AnchorBasedAppRootUrl,
new Provider(AppRootUrl, {useExisting: AnchorBasedAppRootUrl}),
Serializer,
new Provider(ON_WEB_WORKER, {useValue: false}),
RenderViewWithFragmentsStore,
RenderProtoViewRefStore,
DomSharedStylesHost,
Testability,
BrowserDetails,
AnimationBuilder,
EventManager
]);
export function initializeGenericWorkerRenderer(injector: Injector) {
var bus = injector.get(MessageBus);
let zone = injector.get(NgZone);
bus.attachToZone(zone);
zone.run(() => {
WORKER_RENDER_MESSAGING_PROVIDERS.forEach((token) => { injector.get(token).start(); });
});
}
export function initWebWorkerRenderPlatform(): void {
BrowserDomAdapter.makeCurrent();
wtfInit();
BrowserGetTestability.init();
}
function _exceptionHandler(): ExceptionHandler {
return new ExceptionHandler(DOM, false);
}
function _document(): any {
return DOM.defaultDoc();
}