From 2b6fcf260b2fb13afe2abd052cff5dea184ef398 Mon Sep 17 00:00:00 2001 From: Jiabin Peng Date: Tue, 27 Oct 2020 21:52:55 +0800 Subject: [PATCH] fix: onerror/onload memory leak (#640) --- src/CssLoadingRuntimeModule.js | 28 +++++++++++-------- src/index.js | 26 +++++++++++------ test/cases/hmr/expected/webpack-5/main.js | 24 ++++++++++------ .../expected/webpack-4/main.js | 24 ++++++++++------ .../expected/webpack-5/main.js | 24 ++++++++++------ .../insert-string/expected/webpack-4/main.js | 24 ++++++++++------ .../insert-string/expected/webpack-5/main.js | 24 ++++++++++------ .../expected/webpack-4/main.js | 24 ++++++++++------ .../expected/webpack-5/main.js | 24 ++++++++++------ 9 files changed, 139 insertions(+), 83 deletions(-) diff --git a/src/CssLoadingRuntimeModule.js b/src/CssLoadingRuntimeModule.js index be0ff24e..1063351e 100644 --- a/src/CssLoadingRuntimeModule.js +++ b/src/CssLoadingRuntimeModule.js @@ -60,17 +60,23 @@ module.exports = class CssLoadingRuntimeModule extends RuntimeModule { this.runtimeOptions.linkType ? `linkTag.type = ${JSON.stringify(this.runtimeOptions.linkType)};` : '', - 'linkTag.onload = resolve;', - 'linkTag.onerror = function(event) {', - Template.indent([ - 'var request = event && event.target && event.target.href || fullhref;', - 'var err = new Error("Loading CSS chunk " + chunkId + " failed.\\n(" + request + ")");', - 'err.code = "CSS_CHUNK_LOAD_FAILED";', - 'err.request = request;', - 'linkTag.parentNode.removeChild(linkTag)', - 'reject(err);', - ]), - '};', + `var onLinkComplete = ${runtimeTemplate.basicFunction('event', [ + '// avoid mem leaks.', + 'linkTag.onerror = linkTag.onload = null;', + "if (event.type === 'load') {", + Template.indent(['resolve();']), + '} else {', + Template.indent([ + 'var request = event && event.target && event.target.href || fullhref;', + 'var err = new Error("Loading CSS chunk " + chunkId + " failed.\\n(" + request + ")");', + 'err.code = "CSS_CHUNK_LOAD_FAILED";', + 'err.request = request;', + 'linkTag.parentNode.removeChild(linkTag)', + 'reject(err);', + ]), + '}', + ])}`, + 'linkTag.onerror = linkTag.onload = onLinkComplete;', 'linkTag.href = fullhref;', crossOriginLoading ? Template.asString([ diff --git a/src/index.js b/src/index.js index b4a975c3..b00b060d 100644 --- a/src/index.js +++ b/src/index.js @@ -405,18 +405,26 @@ class MiniCssExtractPlugin { this.runtimeOptions.linkType )};` : '', - 'linkTag.onload = resolve;', - 'linkTag.onerror = function(event) {', + 'var onLinkComplete = function (event) {', Template.indent([ - 'var request = event && event.target && event.target.href || fullhref;', - 'var err = new Error("Loading CSS chunk " + chunkId + " failed.\\n(" + request + ")");', - 'err.code = "CSS_CHUNK_LOAD_FAILED";', - 'err.request = request;', - 'delete installedCssChunks[chunkId]', - 'linkTag.parentNode.removeChild(linkTag)', - 'reject(err);', + '// avoid mem leaks.', + 'linkTag.onerror = linkTag.onload = null;', + "if (event.type === 'load') {", + Template.indent(['resolve();']), + '} else {', + Template.indent([ + 'var request = event && event.target && event.target.href || fullhref;', + 'var err = new Error("Loading CSS chunk " + chunkId + " failed.\\n(" + request + ")");', + 'err.code = "CSS_CHUNK_LOAD_FAILED";', + 'err.request = request;', + 'delete installedCssChunks[chunkId]', + 'linkTag.parentNode.removeChild(linkTag)', + 'reject(err);', + ]), + '}', ]), '};', + 'linkTag.onerror = linkTag.onload = onLinkComplete;', 'linkTag.href = fullhref;', crossOriginLoading ? Template.asString([ diff --git a/test/cases/hmr/expected/webpack-5/main.js b/test/cases/hmr/expected/webpack-5/main.js index fea4f8c6..b746eb64 100644 --- a/test/cases/hmr/expected/webpack-5/main.js +++ b/test/cases/hmr/expected/webpack-5/main.js @@ -827,15 +827,21 @@ module.exports = function (urlString) { /******/ /******/ linkTag.rel = "stylesheet"; /******/ linkTag.type = "text/css"; -/******/ linkTag.onload = resolve; -/******/ linkTag.onerror = function(event) { -/******/ var request = event && event.target && event.target.href || fullhref; -/******/ var err = new Error("Loading CSS chunk " + chunkId + " failed.\n(" + request + ")"); -/******/ err.code = "CSS_CHUNK_LOAD_FAILED"; -/******/ err.request = request; -/******/ linkTag.parentNode.removeChild(linkTag) -/******/ reject(err); -/******/ }; +/******/ var onLinkComplete = (event) => { +/******/ // avoid mem leaks. +/******/ linkTag.onerror = linkTag.onload = null; +/******/ if (event.type === 'load') { +/******/ resolve(); +/******/ } else { +/******/ var request = event && event.target && event.target.href || fullhref; +/******/ var err = new Error("Loading CSS chunk " + chunkId + " failed.\n(" + request + ")"); +/******/ err.code = "CSS_CHUNK_LOAD_FAILED"; +/******/ err.request = request; +/******/ linkTag.parentNode.removeChild(linkTag) +/******/ reject(err); +/******/ } +/******/ } +/******/ linkTag.onerror = linkTag.onload = onLinkComplete; /******/ linkTag.href = fullhref; /******/ /******/ document.head.appendChild(linkTag); diff --git a/test/cases/insert-function/expected/webpack-4/main.js b/test/cases/insert-function/expected/webpack-4/main.js index a23de8ce..45c2e3dd 100644 --- a/test/cases/insert-function/expected/webpack-4/main.js +++ b/test/cases/insert-function/expected/webpack-4/main.js @@ -104,16 +104,22 @@ /******/ /******/ linkTag.rel = "stylesheet"; /******/ linkTag.type = "text/css"; -/******/ linkTag.onload = resolve; -/******/ linkTag.onerror = function(event) { -/******/ var request = event && event.target && event.target.href || fullhref; -/******/ var err = new Error("Loading CSS chunk " + chunkId + " failed.\n(" + request + ")"); -/******/ err.code = "CSS_CHUNK_LOAD_FAILED"; -/******/ err.request = request; -/******/ delete installedCssChunks[chunkId] -/******/ linkTag.parentNode.removeChild(linkTag) -/******/ reject(err); +/******/ var onLinkComplete = function (event) { +/******/ // avoid mem leaks. +/******/ linkTag.onerror = linkTag.onload = null; +/******/ if (event.type === 'load') { +/******/ resolve(); +/******/ } else { +/******/ var request = event && event.target && event.target.href || fullhref; +/******/ var err = new Error("Loading CSS chunk " + chunkId + " failed.\n(" + request + ")"); +/******/ err.code = "CSS_CHUNK_LOAD_FAILED"; +/******/ err.request = request; +/******/ delete installedCssChunks[chunkId] +/******/ linkTag.parentNode.removeChild(linkTag) +/******/ reject(err); +/******/ } /******/ }; +/******/ linkTag.onerror = linkTag.onload = onLinkComplete; /******/ linkTag.href = fullhref; /******/ /******/ (function (linkTag) { diff --git a/test/cases/insert-function/expected/webpack-5/main.js b/test/cases/insert-function/expected/webpack-5/main.js index 12c5aded..55507ce3 100644 --- a/test/cases/insert-function/expected/webpack-5/main.js +++ b/test/cases/insert-function/expected/webpack-5/main.js @@ -162,15 +162,21 @@ /******/ /******/ linkTag.rel = "stylesheet"; /******/ linkTag.type = "text/css"; -/******/ linkTag.onload = resolve; -/******/ linkTag.onerror = function(event) { -/******/ var request = event && event.target && event.target.href || fullhref; -/******/ var err = new Error("Loading CSS chunk " + chunkId + " failed.\n(" + request + ")"); -/******/ err.code = "CSS_CHUNK_LOAD_FAILED"; -/******/ err.request = request; -/******/ linkTag.parentNode.removeChild(linkTag) -/******/ reject(err); -/******/ }; +/******/ var onLinkComplete = (event) => { +/******/ // avoid mem leaks. +/******/ linkTag.onerror = linkTag.onload = null; +/******/ if (event.type === 'load') { +/******/ resolve(); +/******/ } else { +/******/ var request = event && event.target && event.target.href || fullhref; +/******/ var err = new Error("Loading CSS chunk " + chunkId + " failed.\n(" + request + ")"); +/******/ err.code = "CSS_CHUNK_LOAD_FAILED"; +/******/ err.request = request; +/******/ linkTag.parentNode.removeChild(linkTag) +/******/ reject(err); +/******/ } +/******/ } +/******/ linkTag.onerror = linkTag.onload = onLinkComplete; /******/ linkTag.href = fullhref; /******/ /******/ (function (linkTag) { diff --git a/test/cases/insert-string/expected/webpack-4/main.js b/test/cases/insert-string/expected/webpack-4/main.js index 3da28c65..fa478446 100644 --- a/test/cases/insert-string/expected/webpack-4/main.js +++ b/test/cases/insert-string/expected/webpack-4/main.js @@ -104,16 +104,22 @@ /******/ /******/ linkTag.rel = "stylesheet"; /******/ linkTag.type = "text/css"; -/******/ linkTag.onload = resolve; -/******/ linkTag.onerror = function(event) { -/******/ var request = event && event.target && event.target.href || fullhref; -/******/ var err = new Error("Loading CSS chunk " + chunkId + " failed.\n(" + request + ")"); -/******/ err.code = "CSS_CHUNK_LOAD_FAILED"; -/******/ err.request = request; -/******/ delete installedCssChunks[chunkId] -/******/ linkTag.parentNode.removeChild(linkTag) -/******/ reject(err); +/******/ var onLinkComplete = function (event) { +/******/ // avoid mem leaks. +/******/ linkTag.onerror = linkTag.onload = null; +/******/ if (event.type === 'load') { +/******/ resolve(); +/******/ } else { +/******/ var request = event && event.target && event.target.href || fullhref; +/******/ var err = new Error("Loading CSS chunk " + chunkId + " failed.\n(" + request + ")"); +/******/ err.code = "CSS_CHUNK_LOAD_FAILED"; +/******/ err.request = request; +/******/ delete installedCssChunks[chunkId] +/******/ linkTag.parentNode.removeChild(linkTag) +/******/ reject(err); +/******/ } /******/ }; +/******/ linkTag.onerror = linkTag.onload = onLinkComplete; /******/ linkTag.href = fullhref; /******/ /******/ var target = document.querySelector("script[src='1.js']"); diff --git a/test/cases/insert-string/expected/webpack-5/main.js b/test/cases/insert-string/expected/webpack-5/main.js index 54d848cc..9d78db3c 100644 --- a/test/cases/insert-string/expected/webpack-5/main.js +++ b/test/cases/insert-string/expected/webpack-5/main.js @@ -162,15 +162,21 @@ /******/ /******/ linkTag.rel = "stylesheet"; /******/ linkTag.type = "text/css"; -/******/ linkTag.onload = resolve; -/******/ linkTag.onerror = function(event) { -/******/ var request = event && event.target && event.target.href || fullhref; -/******/ var err = new Error("Loading CSS chunk " + chunkId + " failed.\n(" + request + ")"); -/******/ err.code = "CSS_CHUNK_LOAD_FAILED"; -/******/ err.request = request; -/******/ linkTag.parentNode.removeChild(linkTag) -/******/ reject(err); -/******/ }; +/******/ var onLinkComplete = (event) => { +/******/ // avoid mem leaks. +/******/ linkTag.onerror = linkTag.onload = null; +/******/ if (event.type === 'load') { +/******/ resolve(); +/******/ } else { +/******/ var request = event && event.target && event.target.href || fullhref; +/******/ var err = new Error("Loading CSS chunk " + chunkId + " failed.\n(" + request + ")"); +/******/ err.code = "CSS_CHUNK_LOAD_FAILED"; +/******/ err.request = request; +/******/ linkTag.parentNode.removeChild(linkTag) +/******/ reject(err); +/******/ } +/******/ } +/******/ linkTag.onerror = linkTag.onload = onLinkComplete; /******/ linkTag.href = fullhref; /******/ /******/ var target = document.querySelector("script[src='1.js']"); diff --git a/test/cases/insert-undefined/expected/webpack-4/main.js b/test/cases/insert-undefined/expected/webpack-4/main.js index 4f296ab3..4ad5b421 100644 --- a/test/cases/insert-undefined/expected/webpack-4/main.js +++ b/test/cases/insert-undefined/expected/webpack-4/main.js @@ -104,16 +104,22 @@ /******/ /******/ linkTag.rel = "stylesheet"; /******/ linkTag.type = "text/css"; -/******/ linkTag.onload = resolve; -/******/ linkTag.onerror = function(event) { -/******/ var request = event && event.target && event.target.href || fullhref; -/******/ var err = new Error("Loading CSS chunk " + chunkId + " failed.\n(" + request + ")"); -/******/ err.code = "CSS_CHUNK_LOAD_FAILED"; -/******/ err.request = request; -/******/ delete installedCssChunks[chunkId] -/******/ linkTag.parentNode.removeChild(linkTag) -/******/ reject(err); +/******/ var onLinkComplete = function (event) { +/******/ // avoid mem leaks. +/******/ linkTag.onerror = linkTag.onload = null; +/******/ if (event.type === 'load') { +/******/ resolve(); +/******/ } else { +/******/ var request = event && event.target && event.target.href || fullhref; +/******/ var err = new Error("Loading CSS chunk " + chunkId + " failed.\n(" + request + ")"); +/******/ err.code = "CSS_CHUNK_LOAD_FAILED"; +/******/ err.request = request; +/******/ delete installedCssChunks[chunkId] +/******/ linkTag.parentNode.removeChild(linkTag) +/******/ reject(err); +/******/ } /******/ }; +/******/ linkTag.onerror = linkTag.onload = onLinkComplete; /******/ linkTag.href = fullhref; /******/ /******/ document.head.appendChild(linkTag); diff --git a/test/cases/insert-undefined/expected/webpack-5/main.js b/test/cases/insert-undefined/expected/webpack-5/main.js index d105a4b9..5a4b5b25 100644 --- a/test/cases/insert-undefined/expected/webpack-5/main.js +++ b/test/cases/insert-undefined/expected/webpack-5/main.js @@ -162,15 +162,21 @@ /******/ /******/ linkTag.rel = "stylesheet"; /******/ linkTag.type = "text/css"; -/******/ linkTag.onload = resolve; -/******/ linkTag.onerror = function(event) { -/******/ var request = event && event.target && event.target.href || fullhref; -/******/ var err = new Error("Loading CSS chunk " + chunkId + " failed.\n(" + request + ")"); -/******/ err.code = "CSS_CHUNK_LOAD_FAILED"; -/******/ err.request = request; -/******/ linkTag.parentNode.removeChild(linkTag) -/******/ reject(err); -/******/ }; +/******/ var onLinkComplete = (event) => { +/******/ // avoid mem leaks. +/******/ linkTag.onerror = linkTag.onload = null; +/******/ if (event.type === 'load') { +/******/ resolve(); +/******/ } else { +/******/ var request = event && event.target && event.target.href || fullhref; +/******/ var err = new Error("Loading CSS chunk " + chunkId + " failed.\n(" + request + ")"); +/******/ err.code = "CSS_CHUNK_LOAD_FAILED"; +/******/ err.request = request; +/******/ linkTag.parentNode.removeChild(linkTag) +/******/ reject(err); +/******/ } +/******/ } +/******/ linkTag.onerror = linkTag.onload = onLinkComplete; /******/ linkTag.href = fullhref; /******/ /******/ document.head.appendChild(linkTag);