@@ -124,7 +124,8 @@ function Browser(window, document, $log, $sniffer) {
124124 //////////////////////////////////////////////////////////////
125125
126126 var lastBrowserUrl = location . href ,
127- lastHistoryState = history . state ,
127+ cachedState = history . state ,
128+ lastHistoryState = cachedState ,
128129 baseElement = document . find ( 'base' ) ,
129130 reloadLocation = null ;
130131
@@ -165,7 +166,7 @@ function Browser(window, document, $log, $sniffer) {
165166 // Don't change anything if previous and current URLs and states match. This also prevents
166167 // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
167168 // 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 ) ) {
169170 return ;
170171 }
171172 var sameBase = lastBrowserUrl && stripHash ( lastBrowserUrl ) === stripHash ( url ) ;
@@ -174,9 +175,10 @@ function Browser(window, document, $log, $sniffer) {
174175 // due to a bug in IE10/IE11 which leads
175176 // to not firing a `hashchange` nor `popstate` event
176177 // in some cases (see #9143).
177- if ( $sniffer . history && ( ! sameBase || history . state !== state ) ) {
178+ if ( $sniffer . history && ( ! sameBase || cachedState !== state ) ) {
178179 history [ replace ? 'replaceState' : 'pushState' ] ( state , '' , url ) ;
179- lastHistoryState = history . state ;
180+ lastHistoryState = cachedState ;
181+ cachedState = state ;
180182 } else {
181183 if ( ! sameBase ) {
182184 reloadLocation = url ;
@@ -208,20 +210,39 @@ function Browser(window, document, $log, $sniffer) {
208210 * @returns {object } state
209211 */
210212 self . state = function ( ) {
211- return isUndefined ( history . state ) ? null : history . state ;
213+ return cachedState ;
212214 } ;
213215
214216 var urlChangeListeners = [ ] ,
215217 urlChangeInit = false ;
216218
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+
217237 function fireUrlChange ( ) {
218- if ( lastBrowserUrl === self . url ( ) && lastHistoryState === history . state ) {
238+ if ( lastBrowserUrl === self . url ( ) && lastHistoryState === cachedState ) {
219239 return ;
220240 }
221241
222242 lastBrowserUrl = self . url ( ) ;
243+ lastHistoryState = cachedState ;
223244 forEach ( urlChangeListeners , function ( listener ) {
224- listener ( self . url ( ) , history . state ) ;
245+ listener ( self . url ( ) , cachedState ) ;
225246 } ) ;
226247 }
227248
@@ -254,9 +275,9 @@ function Browser(window, document, $log, $sniffer) {
254275 // changed by push/replaceState
255276
256277 // html5 history api - popstate event
257- if ( $sniffer . history ) jqLite ( window ) . on ( 'popstate' , fireUrlChange ) ;
278+ if ( $sniffer . history ) jqLite ( window ) . on ( 'popstate' , cacheStateAndFireUrlChange ) ;
258279 // hashchange event
259- jqLite ( window ) . on ( 'hashchange' , fireUrlChange ) ;
280+ jqLite ( window ) . on ( 'hashchange' , cacheStateAndFireUrlChange ) ;
260281
261282 urlChangeInit = true ;
262283 }
0 commit comments