Skip to content

Commit af10b10

Browse files
committed
fix($browser): account for IE deserializing history.state on each read
IE 10-11+ deserialize history.state on every read, causing simple comparisons against history.state always return false. Account for that caching `history.state` on every hashchange or popstate event. Also, prevent firing onUrlChange callbacks twice if both popstate and hashchange event were fired. Closes angular#9587 Refs angular#9545
1 parent 874cac8 commit af10b10

File tree

2 files changed

+220
-72
lines changed

2 files changed

+220
-72
lines changed

src/ng/browser.js

+33-10
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,13 @@ function Browser(window, document, $log, $sniffer) {
123123
// URL API
124124
//////////////////////////////////////////////////////////////
125125

126-
var lastBrowserUrl = location.href,
127-
lastHistoryState = history.state,
126+
var cachedState, lastHistoryState,
127+
lastBrowserUrl = location.href,
128128
baseElement = document.find('base'),
129129
reloadLocation = null;
130130

131+
cacheState();
132+
131133
/**
132134
* @name $browser#url
133135
*
@@ -165,7 +167,7 @@ function Browser(window, document, $log, $sniffer) {
165167
// Don't change anything if previous and current URLs and states match. This also prevents
166168
// IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
167169
// See https://github.com/angular/angular.js/commit/ffb2701
168-
if (lastBrowserUrl === url && (!$sniffer.history || history.state === state)) {
170+
if (lastBrowserUrl === url && (!$sniffer.history || cachedState === state)) {
169171
return;
170172
}
171173
var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
@@ -174,9 +176,10 @@ function Browser(window, document, $log, $sniffer) {
174176
// due to a bug in IE10/IE11 which leads
175177
// to not firing a `hashchange` nor `popstate` event
176178
// in some cases (see #9143).
177-
if ($sniffer.history && (!sameBase || history.state !== state)) {
179+
if ($sniffer.history && (!sameBase || cachedState !== state)) {
178180
history[replace ? 'replaceState' : 'pushState'](state, '', url);
179-
lastHistoryState = history.state;
181+
lastHistoryState = cachedState;
182+
cachedState = state;
180183
} else {
181184
if (!sameBase) {
182185
reloadLocation = url;
@@ -208,20 +211,40 @@ function Browser(window, document, $log, $sniffer) {
208211
* @returns {object} state
209212
*/
210213
self.state = function() {
211-
return isUndefined(history.state) ? null : history.state;
214+
return cachedState;
212215
};
213216

214217
var urlChangeListeners = [],
215218
urlChangeInit = false;
216219

220+
function cacheStateAndFireUrlChange() {
221+
cacheState();
222+
fireUrlChange();
223+
}
224+
225+
// This variable should be used *only* inside the cacheState function.
226+
var lastCachedState = null;
227+
function cacheState() {
228+
// This should be the only place in $browser where `history.state` is read.
229+
cachedState = window.history.state;
230+
cachedState = isUndefined(cachedState) ? null : cachedState;
231+
232+
// Prevent callbacks fo fire twice if both hashchange & popstate were fired.
233+
if (equals(cachedState, lastCachedState)) {
234+
cachedState = lastCachedState;
235+
}
236+
lastCachedState = cachedState;
237+
}
238+
217239
function fireUrlChange() {
218-
if (lastBrowserUrl === self.url() && lastHistoryState === history.state) {
240+
if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
219241
return;
220242
}
221243

222244
lastBrowserUrl = self.url();
245+
lastHistoryState = cachedState;
223246
forEach(urlChangeListeners, function(listener) {
224-
listener(self.url(), history.state);
247+
listener(self.url(), cachedState);
225248
});
226249
}
227250

@@ -254,9 +277,9 @@ function Browser(window, document, $log, $sniffer) {
254277
// changed by push/replaceState
255278

256279
// html5 history api - popstate event
257-
if ($sniffer.history) jqLite(window).on('popstate', fireUrlChange);
280+
if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
258281
// hashchange event
259-
jqLite(window).on('hashchange', fireUrlChange);
282+
jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
260283

261284
urlChangeInit = true;
262285
}

0 commit comments

Comments
 (0)