From f7b1a6c9dcd6c3cab590365b11c062426304b5df Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin Date: Thu, 11 Jun 2015 13:27:43 +0100 Subject: [PATCH 1/4] fix($location): do not get caught in infinite digest in IE9 Closes #11439 Closes #11935 --- src/ng/browser.js | 2 +- test/ng/locationSpec.js | 215 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+), 1 deletion(-) diff --git a/src/ng/browser.js b/src/ng/browser.js index a698302edce2..d665d4386c32 100644 --- a/src/ng/browser.js +++ b/src/ng/browser.js @@ -147,7 +147,7 @@ function Browser(window, document, $log, $sniffer) { // Do the assignment again so that those two variables are referentially identical. lastHistoryState = cachedState; } else { - if (!sameBase) { + if (!sameBase || reloadLocation) { reloadLocation = url; } if (replace) { diff --git a/test/ng/locationSpec.js b/test/ng/locationSpec.js index bca0c62e86c6..5af7dbbce035 100644 --- a/test/ng/locationSpec.js +++ b/test/ng/locationSpec.js @@ -663,6 +663,22 @@ describe('$location', function() { }); }); + + it('should not get caught in infinite digest when replacing path in locationChangeSuccess handler', function() { + initService({html5Mode:true,supportHistory:false}); + mockUpBrowser({initialUrl:'http://server/base/home', baseHref:'/base/'}); + inject( + function($browser, $location, $rootScope, $window) { + $rootScope.$on('$locationChangeSuccess', function() { + if ($location.path() !== '/') { + $location.path('/').replace(); + } + }); + $rootScope.$digest(); + } + ); + }); + it('should not infinitely digest when using a semicolon in initial path', function() { initService({html5Mode:true,supportHistory:true}); mockUpBrowser({initialUrl:'http://localhost:9876/;jsessionid=foo', baseHref:'/'}); @@ -672,6 +688,162 @@ describe('$location', function() { }).not.toThrow(); }); }); + + + function updatePathOnLocationChangeSuccessTo(newPath) { + inject(function($rootScope, $location) { + $rootScope.$on('$locationChangeSuccess', function(event, newUrl, oldUrl) { + $location.path(newPath); + }); + }); + } + + + describe('location watch for hashbang browsers', function() { + + it('should not infinite $digest when going to base URL without trailing slash when $locationChangeSuccess watcher changes path to /Home', function() { + initService({html5Mode: true, supportHistory: false}); + mockUpBrowser({initialUrl:'http://server/app/', baseHref:'/app/'}); + inject(function($rootScope, $location, $browser) { + var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough(); + + updatePathOnLocationChangeSuccessTo('/Home'); + + $rootScope.$digest(); + + expect($browser.url()).toEqual('http://server/app/#/Home'); + expect($location.path()).toEqual('/Home'); + expect($browserUrl.calls.length).toEqual(1); + }); + }); + + it('should not infinite $digest when going to base URL without trailing slash when $locationChangeSuccess watcher changes path to /', function() { + initService({html5Mode: true, supportHistory: false}); + mockUpBrowser({initialUrl:'http://server/app/Home', baseHref:'/app/'}); + inject(function($rootScope, $location, $browser, $window) { + var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough(); + + updatePathOnLocationChangeSuccessTo('/'); + + $rootScope.$digest(); + + expect($browser.url()).toEqual('http://server/app/#/'); + expect($location.path()).toEqual('/'); + expect($browserUrl.calls.length).toEqual(1); + expect($browserUrl.calls[0].args).toEqual(['http://server/app/#/', false, null]); + }); + }); + + it('should not infinite $digest when going to base URL with trailing slash when $locationChangeSuccess watcher changes path to /Home', function() { + initService({html5Mode: true, supportHistory: false}); + mockUpBrowser({initialUrl:'http://server/app/', baseHref:'/app/'}); + inject(function($rootScope, $location, $browser) { + var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough(); + + updatePathOnLocationChangeSuccessTo('/Home'); + $rootScope.$digest(); + + expect($browser.url()).toEqual('http://server/app/#/Home'); + expect($location.path()).toEqual('/Home'); + expect($browserUrl.calls.length).toEqual(1); + expect($browserUrl.calls[0].args).toEqual(['http://server/app/#/Home', false, null]); + }); + }); + + it('should not infinite $digest when going to base URL with trailing slash when $locationChangeSuccess watcher changes path to /', function() { + initService({html5Mode: true, supportHistory: false}); + mockUpBrowser({initialUrl:'http://server/app/', baseHref:'/app/'}); + inject(function($rootScope, $location, $browser) { + var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough(); + + updatePathOnLocationChangeSuccessTo('/'); + $rootScope.$digest(); + + expect($browser.url()).toEqual('http://server/app/#/'); + expect($location.path()).toEqual('/'); + expect($browserUrl.calls.length).toEqual(1); + }); + }); + }); + + + describe('location watch for HTML5 browsers', function() { + beforeEach(initService({html5Mode: true, supportHistory: true})); + beforeEach(inject(initBrowser({basePath: '/app/'}))); + + it('should not infinite $digest when going to base URL without trailing slash when $locationChangeSuccess watcher changes path to /Home', function() { + inject(function($rootScope, $injector, $browser) { + var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough(); + + $browser.url('http://server/app'); + $browser.poll(); + + var $location = $injector.get('$location'); + updatePathOnLocationChangeSuccessTo('/Home'); + + $rootScope.$digest(); + + expect($browser.url()).toEqual('http://server/app/Home'); + expect($location.path()).toEqual('/Home'); + expect($browserUrl.calls.length).toEqual(3); + }); + }); + + it('should not infinite $digest when going to base URL without trailing slash when $locationChangeSuccess watcher changes path to /', function() { + inject(function($rootScope, $injector, $browser) { + var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough(); + + $browser.url('http://server/app'); + $browser.poll(); + + var $location = $injector.get('$location'); + updatePathOnLocationChangeSuccessTo('/'); + + $rootScope.$digest(); + + expect($browser.url()).toEqual('http://server/app/'); + expect($location.path()).toEqual('/'); + expect($browserUrl.calls.length).toEqual(2); + }); + }); + + it('should not infinite $digest when going to base URL with trailing slash when $locationChangeSuccess watcher changes path to /Home', function() { + inject(function($rootScope, $injector, $browser) { + var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough(); + + $browser.url('http://server/app/'); + $browser.poll(); + + var $location = $injector.get('$location'); + updatePathOnLocationChangeSuccessTo('/Home'); + + $rootScope.$digest(); + + expect($browser.url()).toEqual('http://server/app/Home'); + expect($location.path()).toEqual('/Home'); + expect($browserUrl.calls.length).toEqual(2); + }); + }); + + it('should not infinite $digest when going to base URL with trailing slash when $locationChangeSuccess watcher changes path to /', function() { + inject(function($rootScope, $injector, $browser) { + var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough(); + + $browser.url('http://server/app/'); + $browser.poll(); + + var $location = $injector.get('$location'); + updatePathOnLocationChangeSuccessTo('/'); + + $rootScope.$digest(); + + expect($browser.url()).toEqual('http://server/app/'); + expect($location.path()).toEqual('/'); + expect($browserUrl.calls.length).toEqual(1); + }); + }); + }); + }); describe('wiring', function() { @@ -1040,6 +1212,49 @@ describe('$location', function() { } ); }); + + + function mockUpBrowser(options) { + module(function($windowProvider, $browserProvider) { + $windowProvider.$get = function() { + var win = {}; + angular.extend(win, window); + win.addEventListener = angular.noop; + win.removeEventListener = angular.noop; + win.location = { + href: options.initialUrl, + replace: function(val) { + //win.location.href = val; + } + }; + return win; + }; + $browserProvider.$get = function($document, $window, $log, $sniffer) { + /* global Browser: false */ + var b = new Browser($window, $document, $log, $sniffer); + b.baseHref = function() { + return options.baseHref; + }; + return b; + }; + }); + } + + + it('should not get caught in infinite digest when replacing empty path with slash', function() { + initService({html5Mode:true,supportHistory:false}); + mockUpBrowser({initialUrl:'http://server/base', baseHref:'/base/'}); + inject( + function($browser, $location, $rootScope, $window) { + $rootScope.$on('$locationChangeSuccess', function() { + if ($location.path() !== '/') { + $location.path('/').replace(); + } + }); + $rootScope.$digest(); + } + ); + }); }); From 3381fec96335cc24baaa0306daed8c2ad69e1d99 Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin Date: Thu, 11 Jun 2015 22:17:51 +0100 Subject: [PATCH 2/4] fix($location): do not get caught in infinite digest in IE9 Closes #11439 Closes #11675 Closes #11935 --- test/ng/locationSpec.js | 58 +++++++++-------------------------------- 1 file changed, 13 insertions(+), 45 deletions(-) diff --git a/test/ng/locationSpec.js b/test/ng/locationSpec.js index 5af7dbbce035..e5ed85969992 100644 --- a/test/ng/locationSpec.js +++ b/test/ng/locationSpec.js @@ -669,12 +669,17 @@ describe('$location', function() { mockUpBrowser({initialUrl:'http://server/base/home', baseHref:'/base/'}); inject( function($browser, $location, $rootScope, $window) { + var handlerCalled = false; $rootScope.$on('$locationChangeSuccess', function() { + handlerCalled = true; if ($location.path() !== '/') { $location.path('/').replace(); } }); + expect($browser.url()).toEqual('http://server/base/#/home'); $rootScope.$digest(); + expect(handlerCalled).toEqual(true); + expect($browser.url()).toEqual('http://server/base/#/'); } ); }); @@ -768,10 +773,10 @@ describe('$location', function() { describe('location watch for HTML5 browsers', function() { - beforeEach(initService({html5Mode: true, supportHistory: true})); - beforeEach(inject(initBrowser({basePath: '/app/'}))); it('should not infinite $digest when going to base URL without trailing slash when $locationChangeSuccess watcher changes path to /Home', function() { + initService({html5Mode: true, supportHistory: true}); + mockUpBrowser({initialUrl:'http://server/app/', baseHref:'/app/'}); inject(function($rootScope, $injector, $browser) { var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough(); @@ -790,6 +795,8 @@ describe('$location', function() { }); it('should not infinite $digest when going to base URL without trailing slash when $locationChangeSuccess watcher changes path to /', function() { + initService({html5Mode: true, supportHistory: true}); + mockUpBrowser({initialUrl:'http://server/app/', baseHref:'/app/'}); inject(function($rootScope, $injector, $browser) { var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough(); @@ -808,6 +815,8 @@ describe('$location', function() { }); it('should not infinite $digest when going to base URL with trailing slash when $locationChangeSuccess watcher changes path to /Home', function() { + initService({html5Mode: true, supportHistory: true}); + mockUpBrowser({initialUrl:'http://server/app/', baseHref:'/app/'}); inject(function($rootScope, $injector, $browser) { var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough(); @@ -826,6 +835,8 @@ describe('$location', function() { }); it('should not infinite $digest when going to base URL with trailing slash when $locationChangeSuccess watcher changes path to /', function() { + initService({html5Mode: true, supportHistory: true}); + mockUpBrowser({initialUrl:'http://server/app/', baseHref:'/app/'}); inject(function($rootScope, $injector, $browser) { var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough(); @@ -1212,49 +1223,6 @@ describe('$location', function() { } ); }); - - - function mockUpBrowser(options) { - module(function($windowProvider, $browserProvider) { - $windowProvider.$get = function() { - var win = {}; - angular.extend(win, window); - win.addEventListener = angular.noop; - win.removeEventListener = angular.noop; - win.location = { - href: options.initialUrl, - replace: function(val) { - //win.location.href = val; - } - }; - return win; - }; - $browserProvider.$get = function($document, $window, $log, $sniffer) { - /* global Browser: false */ - var b = new Browser($window, $document, $log, $sniffer); - b.baseHref = function() { - return options.baseHref; - }; - return b; - }; - }); - } - - - it('should not get caught in infinite digest when replacing empty path with slash', function() { - initService({html5Mode:true,supportHistory:false}); - mockUpBrowser({initialUrl:'http://server/base', baseHref:'/base/'}); - inject( - function($browser, $location, $rootScope, $window) { - $rootScope.$on('$locationChangeSuccess', function() { - if ($location.path() !== '/') { - $location.path('/').replace(); - } - }); - $rootScope.$digest(); - } - ); - }); }); From e1d9e9f932ace21737bcf68768f082db9cb9a180 Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin Date: Thu, 11 Jun 2015 22:29:44 +0100 Subject: [PATCH 3/4] fix($location): do not get caught in infinite digest in IE9 Closes #11439 Closes #11675 Closes #11935 --- test/ng/locationSpec.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/ng/locationSpec.js b/test/ng/locationSpec.js index e5ed85969992..ba92bd42c379 100644 --- a/test/ng/locationSpec.js +++ b/test/ng/locationSpec.js @@ -780,9 +780,6 @@ describe('$location', function() { inject(function($rootScope, $injector, $browser) { var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough(); - $browser.url('http://server/app'); - $browser.poll(); - var $location = $injector.get('$location'); updatePathOnLocationChangeSuccessTo('/Home'); @@ -800,9 +797,6 @@ describe('$location', function() { inject(function($rootScope, $injector, $browser) { var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough(); - $browser.url('http://server/app'); - $browser.poll(); - var $location = $injector.get('$location'); updatePathOnLocationChangeSuccessTo('/'); @@ -820,9 +814,6 @@ describe('$location', function() { inject(function($rootScope, $injector, $browser) { var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough(); - $browser.url('http://server/app/'); - $browser.poll(); - var $location = $injector.get('$location'); updatePathOnLocationChangeSuccessTo('/Home'); @@ -840,9 +831,6 @@ describe('$location', function() { inject(function($rootScope, $injector, $browser) { var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough(); - $browser.url('http://server/app/'); - $browser.poll(); - var $location = $injector.get('$location'); updatePathOnLocationChangeSuccessTo('/'); From 8f1a81e53ac5d7e41c25be3c2e6cbbe3bdd25ec4 Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin Date: Thu, 11 Jun 2015 22:40:14 +0100 Subject: [PATCH 4/4] fix($location): do not get caught in infinite digest in IE9 Closes #11439 Closes #11675 Closes #11935 --- test/ng/locationSpec.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/ng/locationSpec.js b/test/ng/locationSpec.js index ba92bd42c379..557adc63090b 100644 --- a/test/ng/locationSpec.js +++ b/test/ng/locationSpec.js @@ -787,7 +787,7 @@ describe('$location', function() { expect($browser.url()).toEqual('http://server/app/Home'); expect($location.path()).toEqual('/Home'); - expect($browserUrl.calls.length).toEqual(3); + expect($browserUrl.calls.length).toEqual(1); }); }); @@ -804,7 +804,7 @@ describe('$location', function() { expect($browser.url()).toEqual('http://server/app/'); expect($location.path()).toEqual('/'); - expect($browserUrl.calls.length).toEqual(2); + expect($browserUrl.calls.length).toEqual(0); }); }); @@ -821,7 +821,7 @@ describe('$location', function() { expect($browser.url()).toEqual('http://server/app/Home'); expect($location.path()).toEqual('/Home'); - expect($browserUrl.calls.length).toEqual(2); + expect($browserUrl.calls.length).toEqual(1); }); }); @@ -838,7 +838,7 @@ describe('$location', function() { expect($browser.url()).toEqual('http://server/app/'); expect($location.path()).toEqual('/'); - expect($browserUrl.calls.length).toEqual(1); + expect($browserUrl.calls.length).toEqual(0); }); }); });