@@ -123,11 +123,13 @@ function Browser(window, document, $log, $sniffer) {
123
123
// URL API
124
124
//////////////////////////////////////////////////////////////
125
125
126
- var lastBrowserUrl = location . href ,
127
- lastHistoryState = history . state ,
126
+ var cachedState , lastHistoryState ,
127
+ lastBrowserUrl = location . href ,
128
128
baseElement = document . find ( 'base' ) ,
129
129
reloadLocation = null ;
130
130
131
+ cacheState ( ) ;
132
+
131
133
/**
132
134
* @name $browser#url
133
135
*
@@ -165,7 +167,7 @@ function Browser(window, document, $log, $sniffer) {
165
167
// Don't change anything if previous and current URLs and states match. This also prevents
166
168
// IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
167
169
// 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 ) ) {
169
171
return ;
170
172
}
171
173
var sameBase = lastBrowserUrl && stripHash ( lastBrowserUrl ) === stripHash ( url ) ;
@@ -174,9 +176,10 @@ function Browser(window, document, $log, $sniffer) {
174
176
// due to a bug in IE10/IE11 which leads
175
177
// to not firing a `hashchange` nor `popstate` event
176
178
// in some cases (see #9143).
177
- if ( $sniffer . history && ( ! sameBase || history . state !== state ) ) {
179
+ if ( $sniffer . history && ( ! sameBase || cachedState !== state ) ) {
178
180
history [ replace ? 'replaceState' : 'pushState' ] ( state , '' , url ) ;
179
- lastHistoryState = history . state ;
181
+ lastHistoryState = cachedState ;
182
+ cachedState = state ;
180
183
} else {
181
184
if ( ! sameBase ) {
182
185
reloadLocation = url ;
@@ -208,20 +211,40 @@ function Browser(window, document, $log, $sniffer) {
208
211
* @returns {object } state
209
212
*/
210
213
self . state = function ( ) {
211
- return isUndefined ( history . state ) ? null : history . state ;
214
+ return cachedState ;
212
215
} ;
213
216
214
217
var urlChangeListeners = [ ] ,
215
218
urlChangeInit = false ;
216
219
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
+
217
239
function fireUrlChange ( ) {
218
- if ( lastBrowserUrl === self . url ( ) && lastHistoryState === history . state ) {
240
+ if ( lastBrowserUrl === self . url ( ) && lastHistoryState === cachedState ) {
219
241
return ;
220
242
}
221
243
222
244
lastBrowserUrl = self . url ( ) ;
245
+ lastHistoryState = cachedState ;
223
246
forEach ( urlChangeListeners , function ( listener ) {
224
- listener ( self . url ( ) , history . state ) ;
247
+ listener ( self . url ( ) , cachedState ) ;
225
248
} ) ;
226
249
}
227
250
@@ -254,9 +277,9 @@ function Browser(window, document, $log, $sniffer) {
254
277
// changed by push/replaceState
255
278
256
279
// html5 history api - popstate event
257
- if ( $sniffer . history ) jqLite ( window ) . on ( 'popstate' , fireUrlChange ) ;
280
+ if ( $sniffer . history ) jqLite ( window ) . on ( 'popstate' , cacheStateAndFireUrlChange ) ;
258
281
// hashchange event
259
- jqLite ( window ) . on ( 'hashchange' , fireUrlChange ) ;
282
+ jqLite ( window ) . on ( 'hashchange' , cacheStateAndFireUrlChange ) ;
260
283
261
284
urlChangeInit = true ;
262
285
}
0 commit comments