fix(WebWorker): WebWorkerRenderer removes views after they're destroyed
closes #3240 Closes #3894
This commit is contained in:
parent
fa2c6791b4
commit
9619636ba7
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
})));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue