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

Commit 1ee9b4e

Browse files
committed
fix($location): revert erroneous logic and backport refactorings from master
Backport of 2294880 without enforcing the `<base>` tag and without the new handling for links that only contain hash fragments. Related to #6162 Closes #8492
1 parent 430082e commit 1ee9b4e

File tree

2 files changed

+92
-118
lines changed

2 files changed

+92
-118
lines changed

src/ng/location.js

+35-55
Original file line numberDiff line numberDiff line change
@@ -127,21 +127,26 @@ function LocationHtml5Url(appBase, basePrefix) {
127127
this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
128128
};
129129

130-
this.$$rewrite = function(url) {
130+
this.$$parseLinkUrl = function(url, relHref) {
131131
var appUrl, prevAppUrl;
132+
var rewrittenUrl;
132133

133134
if ( (appUrl = beginsWith(appBase, url)) !== undefined ) {
134135
prevAppUrl = appUrl;
135136
if ( (appUrl = beginsWith(basePrefix, appUrl)) !== undefined ) {
136-
return appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
137+
rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
137138
} else {
138-
return appBase + prevAppUrl;
139+
rewrittenUrl = appBase + prevAppUrl;
139140
}
140141
} else if ( (appUrl = beginsWith(appBaseNoFile, url)) !== undefined ) {
141-
return appBaseNoFile + appUrl;
142+
rewrittenUrl = appBaseNoFile + appUrl;
142143
} else if (appBaseNoFile == url + '/') {
143-
return appBaseNoFile;
144+
rewrittenUrl = appBaseNoFile;
144145
}
146+
if (rewrittenUrl) {
147+
this.$$parse(rewrittenUrl);
148+
}
149+
return !!rewrittenUrl;
145150
};
146151
}
147152

@@ -231,10 +236,12 @@ function LocationHashbangUrl(appBase, hashPrefix) {
231236
this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
232237
};
233238

234-
this.$$rewrite = function(url) {
239+
this.$$parseLinkUrl = function(url, relHref) {
235240
if(stripHash(appBase) == stripHash(url)) {
236-
return url;
241+
this.$$parse(url);
242+
return true;
237243
}
244+
return false;
238245
};
239246
}
240247

@@ -254,16 +261,21 @@ function LocationHashbangInHtml5Url(appBase, hashPrefix) {
254261

255262
var appBaseNoFile = stripFile(appBase);
256263

257-
this.$$rewrite = function(url) {
264+
this.$$parseLinkUrl = function(url, relHref) {
265+
var rewrittenUrl;
258266
var appUrl;
259267

260268
if ( appBase == stripHash(url) ) {
261-
return url;
269+
rewrittenUrl = url;
262270
} else if ( (appUrl = beginsWith(appBaseNoFile, url)) ) {
263-
return appBase + hashPrefix + appUrl;
271+
rewrittenUrl = appBase + hashPrefix + appUrl;
264272
} else if ( appBaseNoFile === url + '/') {
265-
return appBaseNoFile;
273+
rewrittenUrl = appBaseNoFile;
274+
}
275+
if (rewrittenUrl) {
276+
this.$$parse(rewrittenUrl);
266277
}
278+
return !!rewrittenUrl;
267279
};
268280

269281
this.$$compose = function() {
@@ -636,7 +648,7 @@ function $LocationProvider(){
636648
LocationMode = LocationHashbangUrl;
637649
}
638650
$location = new LocationMode(appBase, '#' + hashPrefix);
639-
$location.$$parse($location.$$rewrite(initialUrl));
651+
$location.$$parseLinkUrl(initialUrl, initialUrl);
640652

641653
var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
642654

@@ -655,6 +667,9 @@ function $LocationProvider(){
655667
}
656668

657669
var absHref = elm.prop('href');
670+
// get the actual href attribute - see
671+
// http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
672+
var relHref = elm.attr('href') || elm.attr('xlink:href');
658673

659674
if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
660675
// SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
@@ -665,50 +680,15 @@ function $LocationProvider(){
665680
// Ignore when url is started with javascript: or mailto:
666681
if (IGNORE_URI_REGEXP.test(absHref)) return;
667682

668-
// Make relative links work in HTML5 mode for legacy browsers (or at least IE8 & 9)
669-
// The href should be a regular url e.g. /link/somewhere or link/somewhere or ../somewhere or
670-
// somewhere#anchor or http://example.com/somewhere
671-
if (LocationMode === LocationHashbangInHtml5Url) {
672-
// get the actual href attribute - see
673-
// http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
674-
var href = elm.attr('href') || elm.attr('xlink:href');
675-
676-
if (href && href.indexOf('://') < 0) { // Ignore absolute URLs
677-
var prefix = '#' + hashPrefix;
678-
if (href[0] == '/') {
679-
// absolute path - replace old path
680-
absHref = appBase + prefix + href;
681-
} else if (href[0] == '#') {
682-
// local anchor
683-
absHref = appBase + prefix + ($location.path() || '/') + href;
684-
} else {
685-
// relative path - join with current path
686-
var stack = $location.path().split("/"),
687-
parts = href.split("/");
688-
if (stack.length === 2 && !stack[1]) stack.length = 1;
689-
for (var i=0; i<parts.length; i++) {
690-
if (parts[i] == ".")
691-
continue;
692-
else if (parts[i] == "..")
693-
stack.pop();
694-
else if (parts[i].length)
695-
stack.push(parts[i]);
696-
}
697-
absHref = appBase + prefix + stack.join('/');
698-
}
699-
}
700-
}
701-
702-
var rewrittenUrl = $location.$$rewrite(absHref);
703-
704-
if (absHref && !elm.attr('target') && rewrittenUrl && !event.isDefaultPrevented()) {
705-
event.preventDefault();
706-
if (rewrittenUrl != $browser.url()) {
683+
if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
684+
if ($location.$$parseLinkUrl(absHref, relHref)) {
685+
event.preventDefault();
707686
// update location manually
708-
$location.$$parse(rewrittenUrl);
709-
$rootScope.$apply();
710-
// hack to work around FF6 bug 684208 when scenario runner clicks on links
711-
window.angular['ff-684208-preventDefault'] = true;
687+
if ($location.absUrl() != $browser.url()) {
688+
$rootScope.$apply();
689+
// hack to work around FF6 bug 684208 when scenario runner clicks on links
690+
window.angular['ff-684208-preventDefault'] = true;
691+
}
712692
}
713693
}
714694
});

test/ng/locationSpec.js

+57-63
Original file line numberDiff line numberDiff line change
@@ -858,12 +858,12 @@ describe('$location', function() {
858858
module(function($provide, $locationProvider) {
859859
attrs = attrs ? ' ' + attrs + ' ' : '';
860860

861-
// fake the base behavior
862861
if (typeof linkHref === 'string') {
863862
if (!relLink) {
864863
if (linkHref[0] == '/') {
865864
linkHref = 'http://host.com' + linkHref;
866865
} else if(!linkHref.match(/:\/\//)) {
866+
// fake the behavior of <base> tag
867867
linkHref = 'http://host.com/base/' + linkHref;
868868
}
869869
}
@@ -888,8 +888,8 @@ describe('$location', function() {
888888
}
889889

890890
function initBrowser() {
891-
return function($browser){
892-
$browser.url('http://host.com/base');
891+
return function($browser, $document){
892+
$browser.url('http://host.com/base/index.html');
893893
$browser.$$baseHref = '/base/index.html';
894894
};
895895
}
@@ -1003,12 +1003,14 @@ describe('$location', function() {
10031003

10041004

10051005
it('should produce relative paths correctly when $location.path() is "/" when history enabled on old browser', function() {
1006-
configureService({linkHref: 'partial1', html5Mode: true, supportHist: false, relLink: true});
1006+
configureService({linkHref: 'partial1', html5Mode: true, supportHist: false});
10071007
inject(
10081008
initBrowser(),
10091009
initLocation(),
1010-
function($browser, $location) {
1011-
$location.path('/');
1010+
function($browser, $location, $rootScope) {
1011+
$rootScope.$apply(function() {
1012+
$location.path('/');
1013+
});
10121014
browserTrigger(link, 'click');
10131015
expectRewriteTo($browser, 'http://host.com/base/index.html#!/partial1');
10141016
}
@@ -1203,52 +1205,22 @@ describe('$location', function() {
12031205
);
12041206
});
12051207

1206-
1207-
it('should rewrite relative links relative to current path when history disabled', function() {
1208-
configureService({linkHref: 'link', html5Mode: true, supportHist: false, relLink: true});
1209-
inject(
1210-
initBrowser(),
1211-
initLocation(),
1212-
function($browser, $location) {
1213-
$location.path('/some');
1214-
browserTrigger(link, 'click');
1215-
expectRewriteTo($browser, 'http://host.com/base/index.html#!/some/link');
1216-
}
1217-
);
1218-
});
1219-
1220-
1221-
it('should replace current path when link begins with "/" and history disabled', function() {
1222-
configureService({linkHref: '/link', html5Mode: true, supportHist: false, relLink: true});
1223-
inject(
1224-
initBrowser(),
1225-
initLocation(),
1226-
function($browser, $location) {
1227-
$location.path('/some');
1228-
browserTrigger(link, 'click');
1229-
expectRewriteTo($browser, 'http://host.com/base/index.html#!/link');
1230-
}
1231-
);
1232-
});
1233-
1234-
12351208
it('should replace current hash fragment when link begins with "#" history disabled', function() {
1236-
configureService({linkHref: '#link', html5Mode: true, supportHist: false, relLink: true});
1209+
configureService({linkHref: '#link', html5Mode: true, supportHist: false, relLink: false});
12371210
inject(
12381211
initBrowser(),
12391212
initLocation(),
1240-
function($browser, $location) {
1241-
// Initialize browser URL
1242-
$location.path('/some');
1243-
$location.hash('foo');
1213+
function($browser, $location, $rootScope) {
1214+
$rootScope.$apply(function() {
1215+
$location.hash('foo');
1216+
});
12441217
browserTrigger(link, 'click');
12451218
expect($location.hash()).toBe('link');
1246-
expectRewriteTo($browser, 'http://host.com/base/index.html#!/some#link');
1219+
expectRewriteTo($browser, 'http://host.com/base/index.html#!#link');
12471220
}
12481221
);
12491222
});
12501223

1251-
12521224
// don't run next tests on IE<9, as browserTrigger does not simulate pressed keys
12531225
if (!msie || msie >= 9) {
12541226

@@ -1360,7 +1332,8 @@ describe('$location', function() {
13601332

13611333
var event = {
13621334
target: jqLite(window.document.body).find('a')[0],
1363-
preventDefault: jasmine.createSpy('preventDefault')
1335+
preventDefault: jasmine.createSpy('preventDefault'),
1336+
isDefaultPrevented: jasmine.createSpy().andReturn(false)
13641337
};
13651338

13661339

@@ -1390,7 +1363,8 @@ describe('$location', function() {
13901363

13911364
var event = {
13921365
target: jqLite(window.document.body).find('a')[0],
1393-
preventDefault: jasmine.createSpy('preventDefault')
1366+
preventDefault: jasmine.createSpy('preventDefault'),
1367+
isDefaultPrevented: jasmine.createSpy().andReturn(false)
13941368
};
13951369

13961370

@@ -1555,8 +1529,12 @@ describe('$location', function() {
15551529

15561530

15571531
it('should listen on click events on href and prevent browser default in html5 mode', function() {
1558-
module(function($locationProvider) {
1532+
module(function($locationProvider, $provide) {
15591533
$locationProvider.html5Mode(true);
1534+
$provide.decorator('$browser', function($delegate) {
1535+
$delegate.$$baseHref = '/';
1536+
return $delegate;
1537+
});
15601538
return function($rootElement, $compile, $rootScope) {
15611539
$rootElement.html('<a href="http://server/somePath">link</a>');
15621540
$compile($rootElement)($rootScope);
@@ -1613,6 +1591,13 @@ describe('$location', function() {
16131591
);
16141592
});
16151593

1594+
function parseLinkAndReturn(location, url, relHref) {
1595+
if (location.$$parseLinkUrl(url, relHref)) {
1596+
return location.absUrl();
1597+
}
1598+
return undefined;
1599+
}
1600+
16161601
describe('LocationHtml5Url', function() {
16171602
var location, locationIndex;
16181603

@@ -1622,28 +1607,36 @@ describe('$location', function() {
16221607
});
16231608

16241609
it('should rewrite URL', function() {
1625-
expect(location.$$rewrite('http://other')).toEqual(undefined);
1626-
expect(location.$$rewrite('http://server/pre')).toEqual('http://server/pre/');
1627-
expect(location.$$rewrite('http://server/pre/')).toEqual('http://server/pre/');
1628-
expect(location.$$rewrite('http://server/pre/otherPath')).toEqual('http://server/pre/otherPath');
1629-
expect(locationIndex.$$rewrite('http://server/pre')).toEqual('http://server/pre/');
1630-
expect(locationIndex.$$rewrite('http://server/pre/')).toEqual('http://server/pre/');
1631-
expect(locationIndex.$$rewrite('http://server/pre/otherPath')).toEqual('http://server/pre/otherPath');
1610+
expect(parseLinkAndReturn(location, 'http://other')).toEqual(undefined);
1611+
expect(parseLinkAndReturn(location, 'http://server/pre')).toEqual('http://server/pre/');
1612+
expect(parseLinkAndReturn(location, 'http://server/pre/')).toEqual('http://server/pre/');
1613+
expect(parseLinkAndReturn(location, 'http://server/pre/otherPath')).toEqual('http://server/pre/otherPath');
1614+
1615+
expect(parseLinkAndReturn(locationIndex, 'http://server/pre')).toEqual('http://server/pre/');
1616+
expect(parseLinkAndReturn(locationIndex, 'http://server/pre/')).toEqual('http://server/pre/');
1617+
expect(parseLinkAndReturn(locationIndex, 'http://server/pre/otherPath')).toEqual('http://server/pre/otherPath');
16321618
});
16331619
});
16341620

16351621

16361622
describe('LocationHashbangUrl', function() {
16371623
var location;
16381624

1625+
function parseLinkAndReturn(location, url, relHref) {
1626+
if (location.$$parseLinkUrl(url, relHref)) {
1627+
return location.absUrl();
1628+
}
1629+
return undefined;
1630+
}
1631+
16391632
it('should rewrite URL', function() {
16401633
/* jshint scripturl: true */
16411634
location = new LocationHashbangUrl('http://server/pre/', '#');
16421635

1643-
expect(location.$$rewrite('http://other')).toEqual(undefined);
1644-
expect(location.$$rewrite('http://server/pre/')).toEqual('http://server/pre/');
1645-
expect(location.$$rewrite('http://server/pre/#otherPath')).toEqual('http://server/pre/#otherPath');
1646-
expect(location.$$rewrite('javascript:void(0)')).toEqual(undefined);
1636+
expect(parseLinkAndReturn(location, 'http://other')).toEqual(undefined);
1637+
expect(parseLinkAndReturn(location, 'http://server/pre/')).toEqual('http://server/pre/');
1638+
expect(parseLinkAndReturn(location, 'http://server/pre/#otherPath')).toEqual('http://server/pre/#/otherPath');
1639+
expect(parseLinkAndReturn(location, 'javascript:void(0)')).toEqual(undefined);
16471640
});
16481641

16491642
it("should not set hash if one was not originally specified", function() {
@@ -1693,13 +1686,14 @@ describe('$location', function() {
16931686
});
16941687

16951688
it('should rewrite URL', function() {
1696-
expect(location.$$rewrite('http://other')).toEqual(undefined);
1697-
expect(location.$$rewrite('http://server/pre')).toEqual('http://server/pre/');
1698-
expect(location.$$rewrite('http://server/pre/')).toEqual('http://server/pre/');
1699-
expect(location.$$rewrite('http://server/pre/otherPath')).toEqual('http://server/pre/#!otherPath');
1700-
expect(locationIndex.$$rewrite('http://server/pre')).toEqual('http://server/pre/');
1701-
expect(locationIndex.$$rewrite('http://server/pre/')).toEqual(undefined);
1702-
expect(locationIndex.$$rewrite('http://server/pre/otherPath')).toEqual('http://server/pre/index.html#!otherPath');
1689+
expect(parseLinkAndReturn(location, 'http://other')).toEqual(undefined);
1690+
expect(parseLinkAndReturn(location, 'http://server/pre')).toEqual('http://server/pre/#!');
1691+
expect(parseLinkAndReturn(location, 'http://server/pre/')).toEqual('http://server/pre/#!');
1692+
expect(parseLinkAndReturn(location, 'http://server/pre/otherPath')).toEqual('http://server/pre/#!/otherPath');
1693+
1694+
expect(parseLinkAndReturn(locationIndex, 'http://server/pre')).toEqual('http://server/pre/index.html#!');
1695+
expect(parseLinkAndReturn(locationIndex, 'http://server/pre/')).toEqual(undefined);
1696+
expect(parseLinkAndReturn(locationIndex, 'http://server/pre/otherPath')).toEqual('http://server/pre/index.html#!/otherPath');
17031697
});
17041698
});
17051699
});

0 commit comments

Comments
 (0)