@@ -124,7 +124,8 @@ function Browser(window, document, $log, $sniffer) {
124
124
//////////////////////////////////////////////////////////////
125
125
126
126
var lastBrowserUrl = location . href ,
127
- lastHistoryState = history . state ,
127
+ cachedState = history . state ,
128
+ lastHistoryState = cachedState ,
128
129
baseElement = document . find ( 'base' ) ,
129
130
reloadLocation = null ;
130
131
@@ -165,7 +166,7 @@ function Browser(window, document, $log, $sniffer) {
165
166
// Don't change anything if previous and current URLs and states match. This also prevents
166
167
// IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
167
168
// See https://github.com/angular/angular.js/commit/ffb2701
168
- if ( lastBrowserUrl === url && ( ! $sniffer . history || history . state === state ) ) {
169
+ if ( lastBrowserUrl === url && ( ! $sniffer . history || cachedState === state ) ) {
169
170
return ;
170
171
}
171
172
var sameBase = lastBrowserUrl && stripHash ( lastBrowserUrl ) === stripHash ( url ) ;
@@ -174,9 +175,10 @@ function Browser(window, document, $log, $sniffer) {
174
175
// due to a bug in IE10/IE11 which leads
175
176
// to not firing a `hashchange` nor `popstate` event
176
177
// in some cases (see #9143).
177
- if ( $sniffer . history && ( ! sameBase || history . state !== state ) ) {
178
+ if ( $sniffer . history && ( ! sameBase || cachedState !== state ) ) {
178
179
history [ replace ? 'replaceState' : 'pushState' ] ( state , '' , url ) ;
179
- lastHistoryState = history . state ;
180
+ lastHistoryState = cachedState ;
181
+ cachedState = state ;
180
182
} else {
181
183
if ( ! sameBase ) {
182
184
reloadLocation = url ;
@@ -208,20 +210,39 @@ function Browser(window, document, $log, $sniffer) {
208
210
* @returns {object } state
209
211
*/
210
212
self . state = function ( ) {
211
- return isUndefined ( history . state ) ? null : history . state ;
213
+ return cachedState ;
212
214
} ;
213
215
214
216
var urlChangeListeners = [ ] ,
215
217
urlChangeInit = false ;
216
218
219
+ function cacheStateAndFireUrlChange ( ) {
220
+ cacheState ( ) ;
221
+ fireUrlChange ( ) ;
222
+ }
223
+
224
+ // This variable should be used *only* inside the cacheState function.
225
+ var lastCachedState ;
226
+ function cacheState ( ) {
227
+ cachedState = window . history . state ;
228
+ cachedState = isUndefined ( cachedState ) ? null : cachedState ;
229
+
230
+ // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
231
+ if ( equals ( cachedState , lastCachedState ) ) {
232
+ cachedState = lastCachedState ;
233
+ }
234
+ lastCachedState = cachedState ;
235
+ }
236
+
217
237
function fireUrlChange ( ) {
218
- if ( lastBrowserUrl === self . url ( ) && lastHistoryState === history . state ) {
238
+ if ( lastBrowserUrl === self . url ( ) && lastHistoryState === cachedState ) {
219
239
return ;
220
240
}
221
241
222
242
lastBrowserUrl = self . url ( ) ;
243
+ lastHistoryState = cachedState ;
223
244
forEach ( urlChangeListeners , function ( listener ) {
224
- listener ( self . url ( ) , history . state ) ;
245
+ listener ( self . url ( ) , cachedState ) ;
225
246
} ) ;
226
247
}
227
248
@@ -254,9 +275,9 @@ function Browser(window, document, $log, $sniffer) {
254
275
// changed by push/replaceState
255
276
256
277
// html5 history api - popstate event
257
- if ( $sniffer . history ) jqLite ( window ) . on ( 'popstate' , fireUrlChange ) ;
278
+ if ( $sniffer . history ) jqLite ( window ) . on ( 'popstate' , cacheStateAndFireUrlChange ) ;
258
279
// hashchange event
259
- jqLite ( window ) . on ( 'hashchange' , fireUrlChange ) ;
280
+ jqLite ( window ) . on ( 'hashchange' , cacheStateAndFireUrlChange ) ;
260
281
261
282
urlChangeInit = true ;
262
283
}
0 commit comments