fix(WebWorker): WebWorkerRenderer removes views after they're destroyed

closes #3240

Closes #3894
This commit is contained in:
Jason Teplitz 2015-08-27 10:39:39 -07:00
parent fa2c6791b4
commit 9619636ba7
5 changed files with 62 additions and 19 deletions

View File

@ -5,7 +5,7 @@ import {
RenderViewWithFragments RenderViewWithFragments
} from "angular2/src/core/render/api"; } from "angular2/src/core/render/api";
import {ON_WEB_WORKER} from "angular2/src/web_workers/shared/api"; import {ON_WEB_WORKER} from "angular2/src/web_workers/shared/api";
import {List, ListWrapper} from "angular2/src/core/facade/collection"; import {List, MapWrapper, ListWrapper} from "angular2/src/core/facade/collection";
@Injectable() @Injectable()
export class RenderViewWithFragmentsStore { export class RenderViewWithFragmentsStore {
@ -13,11 +13,13 @@ export class RenderViewWithFragmentsStore {
private _onWebWorker: boolean; private _onWebWorker: boolean;
private _lookupByIndex: Map<number, RenderViewRef | RenderFragmentRef>; private _lookupByIndex: Map<number, RenderViewRef | RenderFragmentRef>;
private _lookupByView: Map<RenderViewRef | RenderFragmentRef, number>; private _lookupByView: Map<RenderViewRef | RenderFragmentRef, number>;
private _viewFragments: Map<RenderViewRef, List<RenderFragmentRef>>;
constructor(@Inject(ON_WEB_WORKER) onWebWorker) { constructor(@Inject(ON_WEB_WORKER) onWebWorker) {
this._onWebWorker = onWebWorker; this._onWebWorker = onWebWorker;
this._lookupByIndex = new Map<number, RenderViewRef | RenderFragmentRef>(); this._lookupByIndex = new Map<number, RenderViewRef | RenderFragmentRef>();
this._lookupByView = new Map<RenderViewRef | RenderFragmentRef, number>(); this._lookupByView = new Map<RenderViewRef | RenderFragmentRef, number>();
this._viewFragments = new Map<RenderViewRef, List<RenderFragmentRef>>();
} }
allocate(fragmentCount: number): RenderViewWithFragments { allocate(fragmentCount: number): RenderViewWithFragments {
@ -34,7 +36,7 @@ export class RenderViewWithFragmentsStore {
return renderViewWithFragments; return renderViewWithFragments;
} }
store(view: RenderViewWithFragments, startIndex: number) { store(view: RenderViewWithFragments, startIndex: number): void {
this._lookupByIndex.set(startIndex, view.viewRef); this._lookupByIndex.set(startIndex, view.viewRef);
this._lookupByView.set(view.viewRef, startIndex); this._lookupByView.set(view.viewRef, startIndex);
startIndex++; startIndex++;
@ -44,13 +46,21 @@ export class RenderViewWithFragmentsStore {
this._lookupByView.set(ref, startIndex); this._lookupByView.set(ref, startIndex);
startIndex++; startIndex++;
}); });
this._viewFragments.set(view.viewRef, view.fragmentRefs);
} }
retreive(ref: number): RenderViewRef | RenderFragmentRef { remove(view: RenderViewRef): void {
if (ref == null) { this._removeRef(view);
return null; var fragments = this._viewFragments.get(view);
} fragments.forEach((fragment) => { this._removeRef(fragment); });
return this._lookupByIndex.get(ref); MapWrapper.delete(this._viewFragments, view);
}
private _removeRef(ref: RenderViewRef | RenderFragmentRef) {
var index = this._lookupByView.get(ref);
MapWrapper.delete(this._lookupByView, ref);
MapWrapper.delete(this._lookupByIndex, index);
} }
serializeRenderViewRef(viewRef: RenderViewRef): number { serializeRenderViewRef(viewRef: RenderViewRef): number {
@ -66,7 +76,7 @@ export class RenderViewWithFragmentsStore {
return null; return null;
} }
return this.retreive(ref); return this._retrieve(ref);
} }
deserializeRenderFragmentRef(ref: number): RenderFragmentRef { deserializeRenderFragmentRef(ref: number): RenderFragmentRef {
@ -74,9 +84,22 @@ export class RenderViewWithFragmentsStore {
return null; return null;
} }
return this.retreive(ref); return this._retrieve(ref);
} }
private _retrieve(ref: number): RenderViewRef | RenderFragmentRef {
if (ref == null) {
return null;
}
if (!this._lookupByIndex.has(ref)) {
return null;
}
return this._lookupByIndex.get(ref);
}
private _serializeRenderFragmentOrViewRef(ref: RenderViewRef | RenderFragmentRef): number { private _serializeRenderFragmentOrViewRef(ref: RenderViewRef | RenderFragmentRef): number {
if (ref == null) { if (ref == null) {
return null; return null;

View File

@ -29,8 +29,7 @@ export class MessageBasedRenderer {
bind(this._createRootHostView, this)); bind(this._createRootHostView, this));
broker.registerMethod("createView", [RenderProtoViewRef, PRIMITIVE, PRIMITIVE], broker.registerMethod("createView", [RenderProtoViewRef, PRIMITIVE, PRIMITIVE],
bind(this._createView, this)); bind(this._createView, this));
broker.registerMethod("destroyView", [RenderViewRef], broker.registerMethod("destroyView", [RenderViewRef], bind(this._destroyView, this));
bind(this._renderer.destroyView, this._renderer));
broker.registerMethod("attachFragmentAfterFragment", [RenderFragmentRef, RenderFragmentRef], broker.registerMethod("attachFragmentAfterFragment", [RenderFragmentRef, RenderFragmentRef],
bind(this._renderer.attachFragmentAfterFragment, this._renderer)); bind(this._renderer.attachFragmentAfterFragment, this._renderer));
broker.registerMethod("attachFragmentAfterElement", [WebWorkerElementRef, RenderFragmentRef], broker.registerMethod("attachFragmentAfterElement", [WebWorkerElementRef, RenderFragmentRef],
@ -57,6 +56,11 @@ export class MessageBasedRenderer {
bind(this._setEventDispatcher, this)); bind(this._setEventDispatcher, this));
} }
private _destroyView(viewRef: RenderViewRef): void {
this._renderer.destroyView(viewRef);
this._renderViewWithFragmentsStore.remove(viewRef);
}
private _createRootHostView(ref: RenderProtoViewRef, fragmentCount: number, selector: string, private _createRootHostView(ref: RenderProtoViewRef, fragmentCount: number, selector: string,
startIndex: number) { startIndex: number) {
var renderViewWithFragments = this._renderer.createRootHostView(ref, fragmentCount, selector); var renderViewWithFragments = this._renderer.createRootHostView(ref, fragmentCount, selector);

View File

@ -138,6 +138,7 @@ export class WebWorkerRenderer implements Renderer {
var fnArgs = [new FnArg(viewRef, RenderViewRef)]; var fnArgs = [new FnArg(viewRef, RenderViewRef)];
var args = new UiArguments("destroyView", fnArgs); var args = new UiArguments("destroyView", fnArgs);
this._messageBroker.runOnUiThread(args, null); this._messageBroker.runOnUiThread(args, null);
this._renderViewStore.remove(viewRef);
} }
/** /**

View File

@ -14,7 +14,7 @@ import {List, ListWrapper} from "angular2/src/core/facade/collection";
export function main() { export function main() {
describe("RenderViewWithFragmentsStore", () => { describe("RenderViewWithFragmentsStore", () => {
describe("on WebWorker", () => { describe("on WebWorker", () => {
var store; var store: RenderViewWithFragmentsStore;
beforeEach(() => { store = new RenderViewWithFragmentsStore(true); }); beforeEach(() => { store = new RenderViewWithFragmentsStore(true); });
it("should allocate fragmentCount + 1 refs", () => { it("should allocate fragmentCount + 1 refs", () => {
@ -44,10 +44,22 @@ export function main() {
expect(store.deserializeViewWithFragments(store.serializeViewWithFragments(view))) expect(store.deserializeViewWithFragments(store.serializeViewWithFragments(view)))
.toEqual(view); .toEqual(view);
}); });
it("should remove a view and all attached fragments", () => {
const NUM_FRAGMENTS = 5;
var view = store.allocate(NUM_FRAGMENTS);
var viewRef = (<WebWorkerRenderViewRef>view.viewRef).refNumber;
store.remove(view.viewRef);
expect(store.deserializeRenderViewRef(viewRef++)).toBeNull();
for (var i = 0; i < NUM_FRAGMENTS; i++) {
expect(store.deserializeRenderFragmentRef(viewRef++)).toBeNull();
}
});
}); });
describe("on UI", () => { describe("on UI", () => {
var store; var store: RenderViewWithFragmentsStore;
beforeEach(() => { store = new RenderViewWithFragmentsStore(false); }); beforeEach(() => { store = new RenderViewWithFragmentsStore(false); });
function createMockRenderViewWithFragments(): RenderViewWithFragments { function createMockRenderViewWithFragments(): RenderViewWithFragments {
var view = new MockRenderViewRef(); var view = new MockRenderViewRef();
@ -62,10 +74,11 @@ export function main() {
var renderViewWithFragments = createMockRenderViewWithFragments(); var renderViewWithFragments = createMockRenderViewWithFragments();
store.store(renderViewWithFragments, 100); store.store(renderViewWithFragments, 100);
expect(store.retreive(100)).toBe(renderViewWithFragments.viewRef); expect(store.deserializeRenderViewRef(100)).toBe(renderViewWithFragments.viewRef);
for (var i = 0; i < renderViewWithFragments.fragmentRefs.length; i++) { for (var i = 0; i < renderViewWithFragments.fragmentRefs.length; i++) {
expect(store.retreive(101 + i)).toBe(renderViewWithFragments.fragmentRefs[i]); expect(store.deserializeRenderFragmentRef(101 + i))
.toBe(renderViewWithFragments.fragmentRefs[i]);
} }
}); });

View File

@ -328,12 +328,14 @@ export function main() {
} }
class WorkerTestRootView extends TestRootView { class WorkerTestRootView extends TestRootView {
constructor(workerViewWithFragments: RenderViewWithFragments, uiRenderViewStore) { constructor(workerViewWithFragments: RenderViewWithFragments,
uiRenderViewStore: RenderViewWithFragmentsStore) {
super(new RenderViewWithFragments( super(new RenderViewWithFragments(
uiRenderViewStore.retreive( uiRenderViewStore.deserializeRenderViewRef(
(<WebWorkerRenderViewRef>workerViewWithFragments.viewRef).refNumber), (<WebWorkerRenderViewRef>workerViewWithFragments.viewRef).refNumber),
ListWrapper.map(workerViewWithFragments.fragmentRefs, ListWrapper.map(workerViewWithFragments.fragmentRefs, (val) => {
(val) => { return uiRenderViewStore.retreive(val.refNumber); }))); return uiRenderViewStore.deserializeRenderFragmentRef(val.refNumber);
})));
} }
} }