Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

fix($location): Infinite digest in $location replace in IE9 #11935

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/ng/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ function Browser(window, document, $log, $sniffer) {
cacheState();
lastHistoryState = cachedState;

self.forceReloadLocationUpdate = function(url) {
if (reloadLocation) {
reloadLocation = url;
}
};


/**
* @name $browser#url
*
Expand Down
11 changes: 10 additions & 1 deletion src/ng/location.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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 ||
Expand Down
43 changes: 43 additions & 0 deletions test/ng/locationSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() !== '/') {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to assert that this handler was actually called

$location.path('/').replace();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add an assertion here

});
$rootScope.$digest();
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to check that the $location actually got updated after the digest

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add an assertion that we executed the locationChangeSuccess callback

);
});
});


Expand Down