Skip to content

Commit 1ba0144

Browse files
ROULjeffbcross
ROUL
authored andcommitted
fix(urlUtils): urlUtils doesn't return right path for file:// on win
Chrome and other browsers on Windows often append the drive name to the pathname, as described in angular#4680. This would cause the location service to browse to odd URLs, such as /C:/myfile.html, when opening apps using file://. Fixes angular#4680
1 parent c5c7538 commit 1ba0144

File tree

2 files changed

+89
-5
lines changed

2 files changed

+89
-5
lines changed

src/ng/urlUtils.js

+46-5
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,14 @@
77
// exactly the behavior needed here. There is little value is mocking these out for this
88
// service.
99
var urlParsingNode = document.createElement("a");
10+
/*
11+
Matches paths for file protocol on windows,
12+
such as /C:/foo/bar, and captures only /foo/bar.
13+
*/
14+
var windowsFilePathExp = /^\/?.*?:(\/.*)/;
1015
var originUrl = urlResolve(window.location.href, true);
1116

17+
1218
/**
1319
*
1420
* Implementation Notes for non-IE browsers
@@ -27,7 +33,7 @@ var originUrl = urlResolve(window.location.href, true);
2733
* browsers. However, the parsed components will not be set if the URL assigned did not specify
2834
* them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We
2935
* work around that by performing the parsing in a 2nd step by taking a previously normalized
30-
* URL (e.g. by assining to a.href) and assigning it a.href again. This correctly populates the
36+
* URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the
3137
* properties such as protocol, hostname, port, etc.
3238
*
3339
* IE7 does not normalize the URL when assigned to an anchor node. (Apparently, it does, if one
@@ -62,7 +68,9 @@ var originUrl = urlResolve(window.location.href, true);
6268
*
6369
*/
6470
function urlResolve(url) {
65-
var href = url;
71+
var href = url,
72+
pathname;
73+
6674
if (msie) {
6775
// Normalize before parse. Refer Implementation Notes on why this is
6876
// done in two steps on IE.
@@ -72,7 +80,34 @@ function urlResolve(url) {
7280

7381
urlParsingNode.setAttribute('href', href);
7482

75-
// $$urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
83+
// ":"" protocol occurs on IE/Win for file://
84+
/* jeffbcross and tbosch say:
85+
Per section 3.3 of http://www.ietf.org/rfc/rfc3986.txt,
86+
the first path segment of a relative path reference
87+
cannot contain a colon, so it is right to assume that
88+
if the first section of the path contains a colon, it
89+
is invalid.
90+
91+
However, to be safe, this check is only obliberating
92+
the first segment if the LAST character of the segment
93+
is a colon.
94+
95+
In Windows, on an anchor node on documents loaded from
96+
the filesystem, the browser will return a pathname
97+
prefixed with the drive name ('/C:/path') when a
98+
pathname without a drive is set:
99+
* a.setAttribute('href', '/foo')
100+
* a.pathname === '/C:/foo' //true
101+
102+
Inside of Angular, we're always using pathnames that
103+
do not include drive names for routing.
104+
*/
105+
106+
pathname = removeWindowsDriveName(urlParsingNode.pathname);
107+
pathname = (pathname.charAt(0) === '/') ? pathname : '/' + pathname;
108+
109+
110+
// urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
76111
return {
77112
href: urlParsingNode.href,
78113
protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
@@ -81,9 +116,15 @@ function urlResolve(url) {
81116
hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
82117
hostname: urlParsingNode.hostname,
83118
port: urlParsingNode.port,
84-
pathname: urlParsingNode.pathname && urlParsingNode.pathname.charAt(0) === '/' ?
85-
urlParsingNode.pathname : '/' + urlParsingNode.pathname
119+
pathname: pathname
86120
};
121+
122+
function removeWindowsDriveName (path) {
123+
var firstPathSegmentMatch;
124+
125+
firstPathSegmentMatch = windowsFilePathExp.exec(path);
126+
return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
127+
}
87128
}
88129

89130

test/ng/locationSpec.js

+43
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,49 @@ describe('$location', function() {
1010
jqLite(document).off('click');
1111
});
1212

13+
14+
describe('File Protocol', function () {
15+
var urlParsingNodePlaceholder;
16+
17+
beforeEach(inject(function ($sniffer) {
18+
if ($sniffer.msie) return;
19+
20+
urlParsingNodePlaceholder = urlParsingNode;
21+
22+
//temporarily overriding the DOM element
23+
//with output from IE, if not in IE
24+
urlParsingNode = {
25+
hash : "#/C:/",
26+
host : "",
27+
hostname : "",
28+
href : "file:///C:/base#!/C:/foo",
29+
pathname : "/C:/foo",
30+
port : "",
31+
protocol : "file:",
32+
search : "",
33+
setAttribute: angular.noop
34+
};
35+
}));
36+
37+
38+
afterEach(inject(function ($sniffer) {
39+
if ($sniffer.msie) return;
40+
//reset urlParsingNode
41+
urlParsingNode = urlParsingNodePlaceholder;
42+
expect(urlParsingNode.pathname).not.toBe('/C:/foo');
43+
}));
44+
45+
46+
it('should not spill over to path() on WIN', function (){
47+
//See issue #4680 for details
48+
url = new LocationHashbangUrl('file:///base', '#!');
49+
url.$$parse('file:///base#!/foo?a=b&c#hash');
50+
51+
expect(url.path()).toBe('/foo');
52+
});
53+
});
54+
55+
1356
describe('NewUrl', function() {
1457
beforeEach(function() {
1558
url = new LocationHtml5Url('http://www.domain.com:9877/');

0 commit comments

Comments
 (0)