Skip to content

Commit

Permalink
fix(common): remove load on image once it fails to load (#52990)
Browse files Browse the repository at this point in the history
This commit adds an `error` listener to image elements and removes both
`load` and `error` listeners once the image loads or fails to load. The `load`
listener would never have been removed if the image failed to load.

PR Close #52990
  • Loading branch information
arturovt authored and AndrewKushnir committed Nov 20, 2023
1 parent fafcb0d commit 7f1c557
Showing 1 changed file with 21 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -746,8 +746,9 @@ function assertGreaterThanZero(dir: NgOptimizedImage, inputValue: unknown, input
*/
function assertNoImageDistortion(
dir: NgOptimizedImage, img: HTMLImageElement, renderer: Renderer2) {
const removeListenerFn = renderer.listen(img, 'load', () => {
removeListenerFn();
const removeLoadListenerFn = renderer.listen(img, 'load', () => {
removeLoadListenerFn();
removeErrorListenerFn();
const computedStyle = window.getComputedStyle(img);
let renderedWidth = parseFloat(computedStyle.getPropertyValue('width'));
let renderedHeight = parseFloat(computedStyle.getPropertyValue('height'));
Expand Down Expand Up @@ -828,6 +829,15 @@ function assertNoImageDistortion(
}
}
});

// We only listen to the `error` event to remove the `load` event listener because it will not be
// fired if the image fails to load. This is done to prevent memory leaks in development mode
// because image elements aren't garbage-collected properly. It happens because zone.js stores the
// event listener directly on the element and closures capture `dir`.
const removeErrorListenerFn = renderer.listen(img, 'error', () => {
removeLoadListenerFn();
removeErrorListenerFn();
});
}

/**
Expand Down Expand Up @@ -870,8 +880,9 @@ function assertEmptyWidthAndHeight(dir: NgOptimizedImage) {
*/
function assertNonZeroRenderedHeight(
dir: NgOptimizedImage, img: HTMLImageElement, renderer: Renderer2) {
const removeListenerFn = renderer.listen(img, 'load', () => {
removeListenerFn();
const removeLoadListenerFn = renderer.listen(img, 'load', () => {
removeLoadListenerFn();
removeErrorListenerFn();
const renderedHeight = img.clientHeight;
if (dir.fill && renderedHeight === 0) {
console.warn(formatRuntimeError(
Expand All @@ -883,6 +894,12 @@ function assertNonZeroRenderedHeight(
`property defined and the height of the element is not zero.`));
}
});

// See comments in the `assertNoImageDistortion`.
const removeErrorListenerFn = renderer.listen(img, 'error', () => {
removeLoadListenerFn();
removeErrorListenerFn();
});
}

/**
Expand Down

0 comments on commit 7f1c557

Please sign in to comment.