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

WIP: [[nomerge]] adapt to location changes happening outside of Angular #8405

Closed
wants to merge 4 commits into from
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
38 changes: 36 additions & 2 deletions src/ng/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ function Browser(window, document, $log, $sniffer) {
history = window.history,
setTimeout = window.setTimeout,
clearTimeout = window.clearTimeout,
pendingDeferIds = {};
pendingDeferIds = {},
hasChangedOutside = false;

self.isMock = false;

Expand Down Expand Up @@ -146,6 +147,7 @@ function Browser(window, document, $log, $sniffer) {
* @param {boolean=} replace Should new url replace current history record ?
*/
self.url = function(url, replace) {
var currentHref;
// Android Browser BFCache causes location, history reference to become stale.
if (location !== window.location) location = window.location;
if (history !== window.history) history = window.history;
Expand Down Expand Up @@ -175,7 +177,38 @@ function Browser(window, document, $log, $sniffer) {
// - newLocation is a workaround for an IE7-9 issue with location.replace and location.href
// methods not updating location.href synchronously.
// - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
return newLocation || location.href.replace(/%27/g,"'");
if (newLocation) {
return newLocation;
}
if (lastBrowserUrl !== (currentHref = location.href.replace(/%27/g,"'"))) {
hasChangedOutside = true;
}
return currentHref;
}
};

/**
* @name $browser#urlChangedOutsideAngular
*
* @description
* GETTER:
* Without any argument, this method just returns current value of urlChangedOutsideAngular.
*
* SETTER:
* With at least one argument, this method sets urlChangedOutsideAngular to new value.
*
* NOTE: this api is intended for use only by the $location service inside of $locationWatch.
*
* @param {boolean} val New value to set as urlChangedOutsideAngular,
* typically used to reset value to false.
*/
self.urlChangedOutsideAngular = function(hasChanged) {
if (isDefined(hasChanged)) {
hasChangedOutside = hasChanged;
return self;
}
else {
return hasChangedOutside;
}
};

Expand All @@ -184,6 +217,7 @@ function Browser(window, document, $log, $sniffer) {

function fireUrlChange() {
newLocation = null;
hasChangedOutside = false;
if (lastBrowserUrl == self.url()) return;

lastBrowserUrl = self.url();
Expand Down
23 changes: 18 additions & 5 deletions src/ng/location.js
Original file line number Diff line number Diff line change
Expand Up @@ -738,18 +738,31 @@ function $LocationProvider(){
// update browser
var changeCounter = 0;
$rootScope.$watch(function $locationWatch() {
var oldUrl = $browser.url();
var browserUrl = $browser.url();
var currentReplace = $location.$$replace;

if (!changeCounter || oldUrl != $location.absUrl()) {
if (!changeCounter || browserUrl != $location.absUrl()) {
changeCounter++;
$rootScope.$evalAsync(function() {
if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl).
/**
* $browser should detect if url was changed outside of Angular, in which
* case $location should automatically update to the new url.
*
* NOTE: Detecting outside changes to location happens automatically in $browser via
* window events (or polling if events aren't supported), but those methods are
* async. For this reason, $browser.url() will perform a comparison with each call
* to the method as a getter.
*/
if ($browser.urlChangedOutsideAngular()) {
$location.$$parse(browserUrl);
$browser.urlChangedOutsideAngular(false);
}
else if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), browserUrl).
defaultPrevented) {
$location.$$parse(oldUrl);
$location.$$parse(browserUrl);
} else {
$browser.url($location.absUrl(), currentReplace);
afterLocationChange(oldUrl);
afterLocationChange(browserUrl);
}
});
}
Expand Down
15 changes: 15 additions & 0 deletions src/ngMock/angular-mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ angular.mock.$Browser = function() {
self.$$url = "http://server/";
self.$$lastUrl = self.$$url; // used by url polling fn
self.pollFns = [];
self.$$hasChangedOutside = false;

// TODO(vojta): remove this temporary api
self.$$completeOutstandingRequest = angular.noop;
Expand Down Expand Up @@ -68,6 +69,16 @@ angular.mock.$Browser = function() {
return self.deferredNextId++;
};

self.urlChangedOutsideAngular = function (hasChanged) {
if (isDefined(hasChanged)) {
self.$$hasChangedOutside = hasChanged;
return self;
}
else {
return self.$$hasChangedOutside;
}
};


/**
* @name $browser#defer.now
Expand Down Expand Up @@ -148,6 +159,10 @@ angular.mock.$Browser.prototype = {
return this;
}

if (this.$$mockLocation && this.$$mockLocation.href && url !== (this.$$url = this.$$mockLocation.href.replace(/%27/g,"'"))) {
this.hasChangedOutside = true;
}

return this.$$url;
},

Expand Down
23 changes: 23 additions & 0 deletions test/ng/locationSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,29 @@ describe('$location', function() {
});


it('should update location when location changed outside of Angular', function() {
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){
if ($sniffer.history) {
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(
Expand Down