diff --git a/src/ng/browser.js b/src/ng/browser.js index 3ca4a7c0a86c..7cc38e807a9c 100644 --- a/src/ng/browser.js +++ b/src/ng/browser.js @@ -234,6 +234,13 @@ function Browser(window, document, $log, $sniffer) { return callback; }; + /** + * Checks whether the url has changed outside of Angular. + * Needs to be exported to be able to check for changes that have been done in sync, + * as hashchange/popstate events fire in async. + */ + self.$$checkUrlChangedOutsideAngular = fireUrlChange; + ////////////////////////////////////////////////////////////// // Misc API ////////////////////////////////////////////////////////////// diff --git a/src/ng/rootScope.js b/src/ng/rootScope.js index 9938240a60e6..c4dc88a8209b 100644 --- a/src/ng/rootScope.js +++ b/src/ng/rootScope.js @@ -687,6 +687,8 @@ function $RootScopeProvider(){ logIdx, logMsg, asyncTask; beginPhase('$digest'); + // Check for changes to browser url that happened in sync with the call to $digest + $browser.$$checkUrlChangedOutsideAngular(); lastDirtyWatch = null; diff --git a/src/ngMock/angular-mocks.js b/src/ngMock/angular-mocks.js index ba9790539ff8..93aa5f8f463a 100644 --- a/src/ngMock/angular-mocks.js +++ b/src/ngMock/angular-mocks.js @@ -56,6 +56,8 @@ angular.mock.$Browser = function() { return listener; }; + self.$$checkUrlChangedOutsideAngular = angular.noop; + self.cookieHash = {}; self.lastCookieHash = {}; self.deferredFns = []; diff --git a/test/ng/locationSpec.js b/test/ng/locationSpec.js index ff36ce42a888..a0d816df3550 100644 --- a/test/ng/locationSpec.js +++ b/test/ng/locationSpec.js @@ -769,6 +769,60 @@ describe('$location', function() { }); + it('should update url when location changed outside of Angular by changing hash', function() { + var fakeWindow = { + addEventListener: angular.noop, + location: { + replace: angular.noop, + href: '/hello' + }, + history: { + } + }; + + module(function($windowProvider, $locationProvider, $browserProvider) { + $locationProvider.html5Mode(true); + $browserProvider.$get = function($document, $log, $sniffer) { + var b = new Browser(fakeWindow, $document, $log, $sniffer); + b.pollFns = []; + return b; + }; + }); + + inject(function($rootScope, $location) { + fakeWindow.location.href = '/hello#goodbye'; + expect(function() { + $rootScope.$digest(); + }).not.toThrow(); + expect($location.hash()).toBe('goodbye'); + expect($location.url()).toBe('/hello#goodbye'); + }); + }); + + + it('should update url when location changed outside of Angular with replaceState', function() { + if (window.history.replaceState) { + module(function($windowProvider, $locationProvider, $browserProvider) { + $locationProvider.html5Mode(true); + $browserProvider.$get = function($document, $window, $log, $sniffer) { + var b = new Browser($window, $document, $log, $sniffer); + b.pollFns = []; + return b; + }; + }); + + inject(function($rootScope, $browser, $location, $sniffer){ + window.history.replaceState(null, '', '/hello'); + // Verify that infinite digest reported in #6976 no longer occurs + expect(function() { + $rootScope.$digest(); + }).not.toThrow(); + expect($location.path()).toBe('/hello'); + }); + } + }); + + it('should rewrite when hashbang url given', function() { initService(true, '!', true); inject(