mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-01-12 22:45:52 +08:00
fix($compile): Resolve leak with asynchronous compilation
Stop an asynchronous compilation when this is performed on an already destroyed scope Closes #9199 Closes #9079 Closes #8504 Closes #9197
This commit is contained in:
committed by
Peter Bacon Darwin
parent
cd2cfafcab
commit
6303c3dcf6
@@ -2011,6 +2011,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
boundTranscludeFn = linkQueue.shift(),
|
||||
linkNode = $compileNode[0];
|
||||
|
||||
if (scope.$$destroyed) continue;
|
||||
|
||||
if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
|
||||
var oldClasses = beforeTemplateLinkNode.className;
|
||||
|
||||
@@ -2037,6 +2039,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
|
||||
var childBoundTranscludeFn = boundTranscludeFn;
|
||||
if (scope.$$destroyed) return;
|
||||
if (linkQueue) {
|
||||
linkQueue.push(scope);
|
||||
linkQueue.push(node);
|
||||
|
||||
@@ -223,6 +223,10 @@ function $RootScopeProvider(){
|
||||
parent.$$childHead = parent.$$childTail = child;
|
||||
}
|
||||
|
||||
// When the new scope is not isolated or we inherit from `this`, and
|
||||
// the parent scope is destroyed, the property `$$destroyed` is inherited
|
||||
// prototypically. In all other cases, this property needs to be set
|
||||
// when the parent scope is destroyed.
|
||||
// The listener needs to be added after the parent is set
|
||||
if (isolate || parent != this) child.$on('$destroy', destroyChild);
|
||||
|
||||
|
||||
@@ -4537,6 +4537,65 @@ describe('$compile', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should not leak when continuing the compilation of elements on a scope that was destroyed', function() {
|
||||
if (jQuery) {
|
||||
// jQuery 2.x doesn't expose the cache storage.
|
||||
return;
|
||||
}
|
||||
|
||||
var linkFn = jasmine.createSpy('linkFn');
|
||||
|
||||
module(function($controllerProvider, $compileProvider) {
|
||||
$controllerProvider.register('Leak', function ($scope, $timeout) {
|
||||
$scope.code = 'red';
|
||||
$timeout(function () {
|
||||
$scope.code = 'blue';
|
||||
});
|
||||
});
|
||||
$compileProvider.directive('isolateRed', function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {},
|
||||
template: '<div red></div>'
|
||||
};
|
||||
});
|
||||
$compileProvider.directive('red', function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
templateUrl: 'red.html',
|
||||
scope: {},
|
||||
link: linkFn
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
inject(function($compile, $rootScope, $httpBackend, $timeout, $templateCache) {
|
||||
$httpBackend.whenGET('red.html').respond('<p>red.html</p>');
|
||||
var template = $compile(
|
||||
'<div ng-controller="Leak">' +
|
||||
'<div ng-switch="code">' +
|
||||
'<div ng-switch-when="red">' +
|
||||
'<div isolate-red></div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>');
|
||||
element = template($rootScope);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$httpBackend.flush();
|
||||
expect(linkFn).not.toHaveBeenCalled();
|
||||
expect(jqLiteCacheSize()).toEqual(2);
|
||||
|
||||
$templateCache.removeAll();
|
||||
var destroyedScope = $rootScope.$new();
|
||||
destroyedScope.$destroy();
|
||||
var clone = template(destroyedScope);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
expect(linkFn).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
if (jQuery) {
|
||||
describe('cleaning up after a replaced element', function () {
|
||||
var $compile, xs;
|
||||
@@ -5117,7 +5176,7 @@ describe('$compile', function() {
|
||||
expect(countScopes($rootScope)).toEqual(1);
|
||||
}));
|
||||
|
||||
it('should destroy mark as destroyed all sub scopes of the scope being destroyed',
|
||||
it('should mark as destroyed all sub scopes of the scope being destroyed',
|
||||
inject(function($compile, $rootScope) {
|
||||
|
||||
element = $compile(
|
||||
|
||||
Reference in New Issue
Block a user