diff --git a/src/ng/browser.js b/src/ng/browser.js index a698302edce2..4a2d25fe08ab 100644 --- a/src/ng/browser.js +++ b/src/ng/browser.js @@ -92,6 +92,13 @@ function Browser(window, document, $log, $sniffer) { cacheState(); lastHistoryState = cachedState; + self.forceReloadLocationUpdate = function(url) { + if (reloadLocation) { + reloadLocation = url; + } + }; + + /** * @name $browser#url * diff --git a/src/ng/location.js b/src/ng/location.js index 18786721a7b2..c24b6a143e1c 100644 --- a/src/ng/location.js +++ b/src/ng/location.js @@ -894,7 +894,7 @@ function $LocationProvider() { $browser.url($location.absUrl(), true); } - var initializing = true; + var initializing = true, previousOldUrl = null, previousNewUrl = null; // update $location when $browser url changes $browser.onUrlChange(function(newUrl, newState) { @@ -929,6 +929,15 @@ function $LocationProvider() { $rootScope.$watch(function $locationWatch() { var oldUrl = trimEmptyHash($browser.url()); var newUrl = trimEmptyHash($location.absUrl()); + if ($location.$$html5 && !$sniffer.history) { + if (previousOldUrl === oldUrl && previousNewUrl === newUrl) { + // break out of infinite $digest loops caused by default routes in hashbang mode + $browser.forceReloadLocationUpdate(newUrl); + previousOldUrl = previousNewUrl = null; + return; + } + previousOldUrl = oldUrl, previousNewUrl = newUrl; + } var oldState = $browser.state(); var currentReplace = $location.$$replace; var urlOrStateChanged = oldUrl !== newUrl || diff --git a/test/ng/locationSpec.js b/test/ng/locationSpec.js index bbeef29f88e6..02839edaa222 100644 --- a/test/ng/locationSpec.js +++ b/test/ng/locationSpec.js @@ -996,6 +996,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(); + } + ); + }); });