From 296851797ba0848d1cd518c927ec0d04574eca11 Mon Sep 17 00:00:00 2001 From: Jason Teplitz Date: Fri, 14 Aug 2015 15:09:58 -0700 Subject: [PATCH] fix(WebWorkers): Run XHR requests on the UI Fixes issues in dart where dart:html is not available in isolates and allows for better profiling of XHR requests Closes #3652 --- modules/angular2/src/web-workers/ui/impl.ts | 19 ++++++- .../web-workers/worker/application_common.ts | 5 +- .../src/web-workers/worker/xhr_impl.ts | 19 +++++++ .../test/web-workers/worker/renderer_spec.ts | 2 +- .../test/web-workers/worker/xhr_impl_spec.ts | 49 +++++++++++++++++++ 5 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 modules/angular2/src/web-workers/worker/xhr_impl.ts create mode 100644 modules/angular2/test/web-workers/worker/xhr_impl_spec.ts diff --git a/modules/angular2/src/web-workers/ui/impl.ts b/modules/angular2/src/web-workers/ui/impl.ts index 490eecda17..41eafa4ea2 100644 --- a/modules/angular2/src/web-workers/ui/impl.ts +++ b/modules/angular2/src/web-workers/ui/impl.ts @@ -31,6 +31,7 @@ import {WebWorkerElementRef} from 'angular2/src/web-workers/shared/api'; import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url'; import {Injectable} from 'angular2/di'; import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter'; +import {XHR} from 'angular2/src/render/xhr'; import { serializeMouseEvent, serializeKeyboardEvent, @@ -61,7 +62,7 @@ export class WebWorkerMain { constructor(private _renderCompiler: RenderCompiler, private _renderer: Renderer, private _renderViewWithFragmentsStore: RenderViewWithFragmentsStore, - private _serializer: Serializer, rootUrl: AnchorBasedAppRootUrl) { + private _serializer: Serializer, rootUrl: AnchorBasedAppRootUrl, private _xhr: XHR) { this._rootUrl = rootUrl.value; } @@ -207,9 +208,23 @@ export class WebWorkerMain { } } + private _handleXhrMessage(data: ReceivedMessage) { + var args = data.args; + switch (data.method) { + case "get": + var url = args[0]; + var promise = this._xhr.get(url); + this._wrapWebWorkerPromise(data.id, promise, String); + break; + default: + throw new BaseException(data.method + " Not Implemented"); + } + } + // TODO(jteplitz602): Create message type enum #3044 private _handleWebWorkerMessage(message: StringMap) { var data: ReceivedMessage = new ReceivedMessage(message['data']); + // TODO(jteplitz602): Replace these with MessageBUs channels #3661 switch (data.type) { case "ready": return this._sendInitMessage(); @@ -217,6 +232,8 @@ export class WebWorkerMain { return this._handleCompilerMessage(data); case "renderer": return this._handleRendererMessage(data); + case "xhr": + return this._handleXhrMessage(data); } } diff --git a/modules/angular2/src/web-workers/worker/application_common.ts b/modules/angular2/src/web-workers/worker/application_common.ts index fe4c7cd57d..839bba6015 100644 --- a/modules/angular2/src/web-workers/worker/application_common.ts +++ b/modules/angular2/src/web-workers/worker/application_common.ts @@ -34,7 +34,7 @@ import {Promise, PromiseWrapper, PromiseCompleter} from 'angular2/src/facade/asy 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 {WebWorkerXHRImpl} from 'angular2/src/web-workers/worker/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'; @@ -129,7 +129,8 @@ function _injectorBindings(appComponentType, bus: WebWorkerMessageBus, Parser, Lexer, bind(ExceptionHandler).toFactory(() => new ExceptionHandler(new PrintLogger()), []), - bind(XHR).toValue(new XHRImpl()), + WebWorkerXHRImpl, + bind(XHR).toAlias(WebWorkerXHRImpl), ComponentUrlMapper, UrlResolver, StyleUrlResolver, diff --git a/modules/angular2/src/web-workers/worker/xhr_impl.ts b/modules/angular2/src/web-workers/worker/xhr_impl.ts new file mode 100644 index 0000000000..14eea042e5 --- /dev/null +++ b/modules/angular2/src/web-workers/worker/xhr_impl.ts @@ -0,0 +1,19 @@ +import {Injectable} from 'angular2/di'; +import {Promise} from 'angular2/src/facade/async'; +import {XHR} from 'angular2/src/render/xhr'; +import {FnArg, UiArguments, MessageBroker} from 'angular2/src/web-workers/worker/broker'; + +/** + * Implementation of render/xhr that relays XHR requests to the UI side where they are sent + * and the result is proxied back to the worker + */ +@Injectable() +export class WebWorkerXHRImpl extends XHR { + constructor(private _messageBroker: MessageBroker) { super(); } + + get(url: string): Promise { + var fnArgs: List = [new FnArg(url, null)]; + var args: UiArguments = new UiArguments("xhr", "get", fnArgs); + return this._messageBroker.runOnUiThread(args, String); + } +} diff --git a/modules/angular2/test/web-workers/worker/renderer_spec.ts b/modules/angular2/test/web-workers/worker/renderer_spec.ts index 3f5dc38ca6..bed65f80f1 100644 --- a/modules/angular2/test/web-workers/worker/renderer_spec.ts +++ b/modules/angular2/test/web-workers/worker/renderer_spec.ts @@ -55,7 +55,7 @@ export function main() { // set up the ui side var webWorkerMain = new WebWorkerMain(tb.compiler, tb.renderer, uiRenderViewStore, uiSerializer, - new AnchorBasedAppRootUrl()); + new AnchorBasedAppRootUrl(), null); webWorkerMain.attachToWebWorker(uiMessageBus); return broker; } diff --git a/modules/angular2/test/web-workers/worker/xhr_impl_spec.ts b/modules/angular2/test/web-workers/worker/xhr_impl_spec.ts new file mode 100644 index 0000000000..13271e2997 --- /dev/null +++ b/modules/angular2/test/web-workers/worker/xhr_impl_spec.ts @@ -0,0 +1,49 @@ +import { + AsyncTestCompleter, + inject, + describe, + it, + expect, + beforeEach, + createTestInjector, + beforeEachBindings, + SpyObject, + proxy +} from 'angular2/test_lib'; +import {IMPLEMENTS, Type} from 'angular2/src/facade/lang'; +import {MessageBroker, UiArguments} from 'angular2/src/web-workers/worker/broker'; +import {WebWorkerXHRImpl} from "angular2/src/web-workers/worker/xhr_impl"; +import {PromiseWrapper} from "angular2/src/facade/async"; + +export function main() { + describe("WebWorkerXHRImpl", () => { + it("should pass requests through the broker and return the response", + inject([AsyncTestCompleter], (async) => { + const URL = "http://www.example.com/test"; + const RESPONSE = "Example response text"; + + var messageBroker: any = new SpyMessageBroker(); + messageBroker.spy("runOnUiThread") + .andCallFake((args: UiArguments, returnType: Type) => { + expect(args.type).toEqual("xhr"); + expect(args.method).toEqual("get"); + expect(args.args.length).toEqual(1); + expect(args.args[0].value).toEqual(URL); + return PromiseWrapper.wrap(() => { return RESPONSE; }); + }); + + var xhrImpl = new WebWorkerXHRImpl(messageBroker); + xhrImpl.get(URL).then((response) => { + expect(response).toEqual(RESPONSE); + async.done(); + }); + })); + }); +} + +@proxy +@IMPLEMENTS(MessageBroker) +class SpyMessageBroker extends SpyObject { + constructor() { super(MessageBroker); } + noSuchMethod(m) { return super.noSuchMethod(m); } +}