Skip to content

Commit bae0968

Browse files
committed
fix(cdk/overlay): avoid leaking memory through afterNextRender
The `OverlayRef` was triggering an `afterEachRender` and passing in an `EnvironmentInjector`. Under the hood this uses a `DestroyRef` that is never destroyed, because the `EnvironmentInjector` is almost never destroyed. These changes add an explicit `destroy` call to avoid the issue. Fixes #29696.
1 parent 35173f1 commit bae0968

File tree

1 file changed

+11
-1
lines changed

1 file changed

+11
-1
lines changed

src/cdk/overlay/overlay-ref.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import {Direction, Directionality} from '@angular/cdk/bidi';
1010
import {ComponentPortal, Portal, PortalOutlet, TemplatePortal} from '@angular/cdk/portal';
1111
import {
12+
AfterRenderRef,
1213
ComponentRef,
1314
EmbeddedViewRef,
1415
EnvironmentInjector,
@@ -62,6 +63,9 @@ export class OverlayRef implements PortalOutlet {
6263
/** Stream of mouse outside events dispatched to this overlay. */
6364
readonly _outsidePointerEvents = new Subject<MouseEvent>();
6465

66+
/** Reference to the currently-running `afterNextRender` call. */
67+
private _afterNextRenderRef: AfterRenderRef | undefined;
68+
6569
constructor(
6670
private _portalOutlet: PortalOutlet,
6771
private _host: HTMLElement,
@@ -134,9 +138,14 @@ export class OverlayRef implements PortalOutlet {
134138
this._scrollStrategy.enable();
135139
}
136140

141+
// We need to clean this up ourselves, because we're passing in an
142+
// `EnvironmentInjector` below which won't ever be destroyed.
143+
// Otherwise it causes some callbacks to be retained (see #29696).
144+
this._afterNextRenderRef?.destroy();
145+
137146
// Update the position once the overlay is fully rendered before attempting to position it,
138147
// as the position may depend on the size of the rendered content.
139-
afterNextRender(
148+
this._afterNextRenderRef = afterNextRender(
140149
() => {
141150
// The overlay could've been detached before the callback executed.
142151
if (this.hasAttached()) {
@@ -250,6 +259,7 @@ export class OverlayRef implements PortalOutlet {
250259
this._outsidePointerEvents.complete();
251260
this._outsideClickDispatcher.remove(this);
252261
this._host?.remove();
262+
this._afterNextRenderRef?.destroy();
253263

254264
this._previousHostParent = this._pane = this._host = null!;
255265

0 commit comments

Comments
 (0)