From cb85cbcec1c876db6062a0dc0bad80f842782194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Niemel=C3=A4?= Date: Thu, 2 Oct 2014 12:41:36 +0200 Subject: [PATCH] fix($animate): clear the GCS cache even when no animation is detected $animate will cache subsequent calls to GCS in the event that the element with the same CSS classes and the same parentNode is being animated. Once the animation is started then $animate waits for one rAF before flushing the GCS lookup cache. Prior to this fix, if GCS was unable to detect any transitions or keyframes on the element then it would simply close the animation, but it would not trigger the rAF code to flush the cache. This issue caused a bug which made it difficult to detect why certain animations are not allowed to fire if the element didn't contain any CSS-based animations beforehand. Closes #8813 --- src/ngAnimate/animate.js | 14 +++++++++ test/ngAnimate/animateSpec.js | 58 +++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/src/ngAnimate/animate.js b/src/ngAnimate/animate.js index e66fcd6e..f78adcdc 100644 --- a/src/ngAnimate/animate.js +++ b/src/ngAnimate/animate.js @@ -1419,6 +1419,16 @@ angular.module('ngAnimate', ['ng']) var parentCounter = 0; var animationReflowQueue = []; var cancelAnimationReflow; + function clearCacheAfterReflow() { + if (!cancelAnimationReflow) { + cancelAnimationReflow = $$animateReflow(function() { + animationReflowQueue = []; + cancelAnimationReflow = null; + lookupCache = {}; + }); + } + } + function afterReflow(element, callback) { if (cancelAnimationReflow) { cancelAnimationReflow(); @@ -1764,6 +1774,7 @@ angular.module('ngAnimate', ['ng']) //to perform at all var preReflowCancellation = animateBefore(animationEvent, element, className); if (!preReflowCancellation) { + clearCacheAfterReflow(); animationComplete(); return; } @@ -1820,6 +1831,7 @@ angular.module('ngAnimate', ['ng']) afterReflow(element, animationCompleted); return cancellationMethod; } + clearCacheAfterReflow(); animationCompleted(); }, @@ -1829,6 +1841,7 @@ angular.module('ngAnimate', ['ng']) afterReflow(element, animationCompleted); return cancellationMethod; } + clearCacheAfterReflow(); animationCompleted(); }, @@ -1838,6 +1851,7 @@ angular.module('ngAnimate', ['ng']) afterReflow(element, animationCompleted); return cancellationMethod; } + clearCacheAfterReflow(); animationCompleted(); }, diff --git a/test/ngAnimate/animateSpec.js b/test/ngAnimate/animateSpec.js index a571464e..b738f1e1 100644 --- a/test/ngAnimate/animateSpec.js +++ b/test/ngAnimate/animateSpec.js @@ -3757,6 +3757,64 @@ describe("ngAnimate", function() { expect(inner.hasClass('on-add-active')).toBe(false); })); + it("should reset the getComputedStyle lookup cache even when no animation is found", + inject(function($compile, $rootScope, $animate, $sniffer, $document) { + + if (!$sniffer.transitions) return; + + $animate.enabled(); + + var html = '
' + + '
On or Off
' + + '
'; + + ss.addRule('.activated .toggle', '-webkit-transition:1s linear all;' + + 'transition:1s linear all;'); + + var child, element = $compile(html)($rootScope); + + $rootElement.append(element); + jqLite($document[0].body).append($rootElement); + + $rootScope.onOff = true; + $rootScope.$digest(); + + child = element.find('div'); + expect(child).not.toHaveClass('ng-enter'); + expect(child.parent()[0]).toEqual(element[0]); + $animate.triggerReflow(); + + $rootScope.onOff = false; + $rootScope.$digest(); + + child = element.find('div'); + expect(child.parent().length).toBe(0); + $animate.triggerReflow(); + + element.addClass('activated'); + $rootScope.$digest(); + $animate.triggerReflow(); + + $rootScope.onOff = true; + $rootScope.$digest(); + + child = element.find('div'); + expect(child).toHaveClass('ng-enter'); + $animate.triggerReflow(); + expect(child).toHaveClass('ng-enter-active'); + + browserTrigger(child, 'transitionend', + { timeStamp: Date.now() + 1000, elapsedTime: 2000 }); + + $animate.triggerCallbacks(); + + $rootScope.onOff = false; + $rootScope.$digest(); + + expect(child).toHaveClass('ng-leave'); + $animate.triggerReflow(); + expect(child).toHaveClass('ng-leave-active'); + })); it("should cancel and perform the dom operation only after the reflow has run", inject(function($compile, $rootScope, $animate, $sniffer) {