Skip to content

Commit a07003e

Browse files
committed
[fixed] URL hash consistency across browsers
Unlike other browsers, Firefox will pre-decode the value retrieved from window.location.hash. This causes the parameters passed down to React components from the router to potentially be different in Firefox vs. other browsers. Additionally, hashes containing the string '%25' can cause react-router to throw exceptions, since this value will be decoded to '%' by Firefox, and then passed by react-router to the `decodeURI` function, which will consider it invalid unless it is followed by two more hex digits. In order to fix these problems, reads of window.location.hash have been replaced with window.location.href.split('#')[1], which is consistently the un-decoded string in all browsers.
1 parent 61fe033 commit a07003e

File tree

2 files changed

+49
-2
lines changed

2 files changed

+49
-2
lines changed

modules/locations/HashLocation.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ var LocationActions = require('../actions/LocationActions');
44
var Path = require('../utils/Path');
55

66
/**
7-
* Returns the current URL path from `window.location.hash`, including query string
7+
* Returns the current URL path from the `hash` section of the URL, including query string
88
*/
99
function getHashPath() {
1010
invariant(
@@ -13,7 +13,9 @@ function getHashPath() {
1313
);
1414

1515
return Path.decode(
16-
window.location.hash.substr(1)
16+
//cannot use window.location.hash because its not consistent
17+
//across browsers - Firefox will pre-decode it
18+
window.location.href.split('#')[1] || ''
1719
);
1820
}
1921

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
var expect = require('expect');
2+
var HashLocation = require('../HashLocation');
3+
4+
describe('HashLocation.getCurrentPath', function() {
5+
6+
//this test is needed because Firefox will pre-decode the value retrieved from
7+
//window.location.hash
8+
it('returns a properly decoded equivalent of what window.location.hash is set to', function() {
9+
window.location.hash = '';
10+
expect(HashLocation.getCurrentPath()).toBe('');
11+
12+
window.location.hash = 'asdf';
13+
expect(HashLocation.getCurrentPath()).toBe('asdf');
14+
15+
window.location.hash = 'test+spaces';
16+
expect(HashLocation.getCurrentPath()).toBe('test spaces');
17+
18+
window.location.hash = 'first%2Fsecond';
19+
expect(HashLocation.getCurrentPath()).toBe('first%2Fsecond');
20+
21+
window.location.hash = 'first/second';
22+
expect(HashLocation.getCurrentPath()).toBe('first/second');
23+
24+
window.location.hash = 'first%252Fsecond';
25+
expect(HashLocation.getCurrentPath()).toBe('first%2Fsecond');
26+
27+
//decodeURI doesn't handle lone percents
28+
window.location.hash = '%';
29+
expect(function() {
30+
HashLocation.getCurrentPath();
31+
}).toThrow(URIError);
32+
33+
window.location.hash = '%25';
34+
expect(HashLocation.getCurrentPath()).toBe('%');
35+
36+
window.location.hash =
37+
'complicated+string/full%2Fof%3Fspecial%25chars%2520and%23escapes%E1%88%B4';
38+
expect(HashLocation.getCurrentPath())
39+
.toBe('complicated string/full%2Fof%3Fspecial%chars%20and%23escapesሴ');
40+
});
41+
42+
afterEach(function() {
43+
window.location.hash = '';
44+
});
45+
});

0 commit comments

Comments
 (0)