From a07003e10e9b3cb48f7a3b2a4601ca684ae0f5db Mon Sep 17 00:00:00 2001 From: Ross Pokorny Date: Tue, 2 Dec 2014 08:56:18 -0700 Subject: [PATCH] [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. --- modules/locations/HashLocation.js | 6 ++- .../locations/__tests__/HashLocation-test.js | 45 +++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 modules/locations/__tests__/HashLocation-test.js diff --git a/modules/locations/HashLocation.js b/modules/locations/HashLocation.js index aa4ae25902..7f1a503391 100644 --- a/modules/locations/HashLocation.js +++ b/modules/locations/HashLocation.js @@ -4,7 +4,7 @@ var LocationActions = require('../actions/LocationActions'); var Path = require('../utils/Path'); /** - * Returns the current URL path from `window.location.hash`, including query string + * Returns the current URL path from the `hash` section of the URL, including query string */ function getHashPath() { invariant( @@ -13,7 +13,9 @@ function getHashPath() { ); return Path.decode( - window.location.hash.substr(1) + //cannot use window.location.hash because its not consistent + //across browsers - Firefox will pre-decode it + window.location.href.split('#')[1] || '' ); } diff --git a/modules/locations/__tests__/HashLocation-test.js b/modules/locations/__tests__/HashLocation-test.js new file mode 100644 index 0000000000..b425dd5aeb --- /dev/null +++ b/modules/locations/__tests__/HashLocation-test.js @@ -0,0 +1,45 @@ +var expect = require('expect'); +var HashLocation = require('../HashLocation'); + +describe('HashLocation.getCurrentPath', function() { + + //this test is needed because Firefox will pre-decode the value retrieved from + //window.location.hash + it('returns a properly decoded equivalent of what window.location.hash is set to', function() { + window.location.hash = ''; + expect(HashLocation.getCurrentPath()).toBe(''); + + window.location.hash = 'asdf'; + expect(HashLocation.getCurrentPath()).toBe('asdf'); + + window.location.hash = 'test+spaces'; + expect(HashLocation.getCurrentPath()).toBe('test spaces'); + + window.location.hash = 'first%2Fsecond'; + expect(HashLocation.getCurrentPath()).toBe('first%2Fsecond'); + + window.location.hash = 'first/second'; + expect(HashLocation.getCurrentPath()).toBe('first/second'); + + window.location.hash = 'first%252Fsecond'; + expect(HashLocation.getCurrentPath()).toBe('first%2Fsecond'); + + //decodeURI doesn't handle lone percents + window.location.hash = '%'; + expect(function() { + HashLocation.getCurrentPath(); + }).toThrow(URIError); + + window.location.hash = '%25'; + expect(HashLocation.getCurrentPath()).toBe('%'); + + window.location.hash = + 'complicated+string/full%2Fof%3Fspecial%25chars%2520and%23escapes%E1%88%B4'; + expect(HashLocation.getCurrentPath()) + .toBe('complicated string/full%2Fof%3Fspecial%chars%20and%23escapesሴ'); + }); + + afterEach(function() { + window.location.hash = ''; + }); +});