fix(core): remove application from the testability registry when the … · angular/angular@3680ad1
@@ -10,7 +10,7 @@ import {ViewEncapsulation} from '../metadata/view';
1010import {Renderer2} from '../render/api';
1111import {RendererStyleFlags2} from '../render/api_flags';
1212import {addToArray, removeFromArray} from '../util/array_utils';
13-import {assertDefined, assertDomNode, assertEqual, assertString} from '../util/assert';
13+import {assertDefined, assertDomNode, assertEqual, assertFunction, assertString} from '../util/assert';
1414import {assertLContainer, assertLView, assertTNodeForLView} from './assert';
1515import {attachPatchData} from './context_discovery';
1616import {icuContainerIterate} from './i18n/i18n_tree_shaking';
@@ -418,7 +418,7 @@ function cleanUpView(tView: TView, lView: LView): void {
418418lView[FLAGS] |= LViewFlags.Destroyed;
419419420420executeOnDestroys(tView, lView);
421-removeListeners(tView, lView);
421+processCleanups(tView, lView);
422422// For component views only, the local renderer is destroyed at clean up time.
423423if (lView[TVIEW].type === TViewType.Component && isProceduralRenderer(lView[RENDERER])) {
424424ngDevMode && ngDevMode.rendererDestroy++;
@@ -443,38 +443,49 @@ function cleanUpView(tView: TView, lView: LView): void {
443443}
444444445445/** Removes listeners and unsubscribes from output subscriptions */
446-function removeListeners(tView: TView, lView: LView): void {
446+function processCleanups(tView: TView, lView: LView): void {
447447const tCleanup = tView.cleanup;
448+const lCleanup = lView[CLEANUP]!;
449+// `LCleanup` contains both share information with `TCleanup` as well as instance specific
450+// information appended at the end. We need to know where the end of the `TCleanup` information
451+// is, and we track this with `lastLCleanupIndex`.
452+let lastLCleanupIndex = -1;
448453if (tCleanup !== null) {
449-const lCleanup = lView[CLEANUP]!;
450454for (let i = 0; i < tCleanup.length - 1; i += 2) {
451455if (typeof tCleanup[i] === 'string') {
452456// This is a native DOM listener
453457const idxOrTargetGetter = tCleanup[i + 1];
454458const target = typeof idxOrTargetGetter === 'function' ?
455459idxOrTargetGetter(lView) :
456460unwrapRNode(lView[idxOrTargetGetter]);
457-const listener = lCleanup[tCleanup[i + 2]];
461+const listener = lCleanup[lastLCleanupIndex = tCleanup[i + 2]];
458462const useCaptureOrSubIdx = tCleanup[i + 3];
459463if (typeof useCaptureOrSubIdx === 'boolean') {
460464// native DOM listener registered with Renderer3
461465target.removeEventListener(tCleanup[i], listener, useCaptureOrSubIdx);
462466} else {
463467if (useCaptureOrSubIdx >= 0) {
464468// unregister
465-lCleanup[useCaptureOrSubIdx]();
469+lCleanup[lastLCleanupIndex = useCaptureOrSubIdx]();
466470} else {
467471// Subscription
468-lCleanup[-useCaptureOrSubIdx].unsubscribe();
472+lCleanup[lastLCleanupIndex = -useCaptureOrSubIdx].unsubscribe();
469473}
470474}
471475i += 2;
472476} else {
473477// This is a cleanup function that is grouped with the index of its context
474-const context = lCleanup[tCleanup[i + 1]];
478+const context = lCleanup[lastLCleanupIndex = tCleanup[i + 1]];
475479tCleanup[i].call(context);
476480}
477481}
482+if (lCleanup !== null) {
483+for (let i = lastLCleanupIndex + 1; i < lCleanup.length; i++) {
484+const instanceCleanupFn = lCleanup[i];
485+ngDevMode && assertFunction(instanceCleanupFn, 'Expecting instance cleanup function.');
486+instanceCleanupFn();
487+}
488+}
478489lView[CLEANUP] = null;
479490}
480491}