Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit e21c2f5

Browse files
committed
fix($compile): Resolve leak with asynchronous compilation
Stop an asynchronous compilation when this is performed on an already destroyed scope Closes #9199
1 parent 47f6d46 commit e21c2f5

File tree

2 files changed

+55
-0
lines changed

2 files changed

+55
-0
lines changed

src/ng/compile.js

+3
Original file line numberDiff line numberDiff line change
@@ -1992,6 +1992,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
19921992
boundTranscludeFn = linkQueue.shift(),
19931993
linkNode = $compileNode[0];
19941994

1995+
if (scope.$$destroyed) continue;
1996+
19951997
if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
19961998
var oldClasses = beforeTemplateLinkNode.className;
19971999

@@ -2018,6 +2020,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
20182020

20192021
return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
20202022
var childBoundTranscludeFn = boundTranscludeFn;
2023+
if (scope.$$destroyed) return;
20212024
if (linkQueue) {
20222025
linkQueue.push(scope);
20232026
linkQueue.push(node);

test/ng/compileSpec.js

+52
Original file line numberDiff line numberDiff line change
@@ -4470,6 +4470,58 @@ describe('$compile', function() {
44704470
});
44714471
});
44724472

4473+
it('should not leak when continuing the compilation of elements on a scope that was destroyed', function() {
4474+
if (jQuery) {
4475+
// jQuery 2.x doesn't expose the cache storage.
4476+
return;
4477+
}
4478+
4479+
var linkFn = jasmine.createSpy('linkFn');
4480+
4481+
module(function($controllerProvider, $compileProvider) {
4482+
$controllerProvider.register('Leak', function ($scope, $timeout) {
4483+
$scope.code = 'red';
4484+
$timeout(function () {
4485+
$scope.code = 'blue';
4486+
});
4487+
});
4488+
$compileProvider.directive('red', function() {
4489+
return {
4490+
restrict: 'A',
4491+
templateUrl: 'red.html',
4492+
scope: {},
4493+
link: linkFn
4494+
};
4495+
});
4496+
});
4497+
4498+
inject(function($compile, $rootScope, $httpBackend, $timeout, $templateCache) {
4499+
$httpBackend.whenGET('red.html').respond('<p>red.html</p>');
4500+
var template = $compile(
4501+
'<div ng-controller="Leak">' +
4502+
'<div ng-switch="code">' +
4503+
'<div ng-switch-when="red">' +
4504+
'<div red></div>' +
4505+
'</div>' +
4506+
'</div>' +
4507+
'</div>');
4508+
element = template($rootScope);
4509+
$rootScope.$digest();
4510+
$timeout.flush();
4511+
$httpBackend.flush();
4512+
expect(linkFn).not.toHaveBeenCalled();
4513+
expect(jqLiteCacheSize()).toEqual(2);
4514+
4515+
$templateCache.removeAll();
4516+
var destroyedScope = $rootScope.$new();
4517+
destroyedScope.$destroy();
4518+
var clone = template(destroyedScope);
4519+
$rootScope.$digest();
4520+
$timeout.flush();
4521+
expect(linkFn).not.toHaveBeenCalled();
4522+
});
4523+
});
4524+
44734525
if (jQuery) {
44744526
describe('cleaning up after a replaced element', function () {
44754527
var $compile, xs;

0 commit comments

Comments
 (0)