From 45f3034e1a12187ef139ed74e9737cd3524059f1 Mon Sep 17 00:00:00 2001 From: Eric O'Connell Date: Thu, 19 Jan 2012 14:55:33 -0800 Subject: [PATCH 1/3] Fix safari poll timing bug during pushState If a pushState has just occurred and the Safari poller checks to see if the hash has changed before the hashchange propagates, then it sees the new state as being different from the actual state, thus triggering a pushState. This patch sets a flag (waitForPropagation) which is cleared by an interval after the hashchange finally propagates. The question.. what happens if a second pushState occurs before the hashchange propagates? --- scripts/uncompressed/history.js | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/scripts/uncompressed/history.js b/scripts/uncompressed/history.js index 0e7cb1ec..12997297 100644 --- a/scripts/uncompressed/history.js +++ b/scripts/uncompressed/history.js @@ -386,7 +386,7 @@ baseHref = baseElement.href.replace(/[^\/]+$/,''); } - // Adjust trailing slash + // Adjusts trailing slash baseHref = baseHref.replace(/\/+$/,''); if ( baseHref ) baseHref += '/'; @@ -1442,8 +1442,12 @@ * @return {History} */ History.safariStatePoll = function(){ - // Poll the URL + // check if we just pushed state, if so.. bail + if (History.waitForPropagation) { + return; + } + // Poll the URL // Get the Last State which has the new URL var urlState = History.extractState(document.location.href), @@ -1472,6 +1476,19 @@ return History; }; + History.blockSafariPollUntilPropagation = function(newState){ + History.waitForPropagation = true; + History.waitForPropagationInterval = setInterval(function() { + var urlState = History.extractState(document.location.href); + if (!urlState || (urlState.id == newState.id)) { + //History.debug('url state propagated, un-blocking Safari poll'); + History.waitForPropagation = false; + clearInterval(History.waitForPropagationInterval); + } + }, 250); + History.intervalList.push(History.waitForPropagationInterval); + }; + // ==================================================================== // State Aliases @@ -1613,7 +1630,7 @@ // Reset the double check History.doubleCheckComplete(); - // Check for a Hash, and handle apporiatly + // Check for a Hash, and handle appropriately currentHash = History.getHash(); if ( currentHash ) { // Expand Hash @@ -1728,6 +1745,11 @@ History.busy(false); } else { + if (History.bugs.safariPoll) { + //History.debug('Blocking safariPoll until url state propagation'); + History.blockSafariPollUntilPropagation(newState); + } + // Store the newState History.storeState(newState); History.expectedStateId = newState.id; @@ -1908,6 +1930,7 @@ * Setup Safari Fix */ if ( History.bugs.safariPoll ) { + History.waitForPropagation = false; History.intervalList.push(setInterval(History.safariStatePoll, History.options.safariPollInterval)); } From 0145f47bff6ef0d120846bcf814e13f6f4fd68c2 Mon Sep 17 00:00:00 2001 From: Eric O'Connell Date: Fri, 20 Jan 2012 12:37:37 -0800 Subject: [PATCH 2/3] add guard for pushState occurring while already waiting for propagation --- scripts/uncompressed/history.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/uncompressed/history.js b/scripts/uncompressed/history.js index 12997297..bd869d7e 100644 --- a/scripts/uncompressed/history.js +++ b/scripts/uncompressed/history.js @@ -1477,6 +1477,10 @@ }; History.blockSafariPollUntilPropagation = function(newState){ + // clear existing interval in preparation for the new one + if (History.waitForPropagationInterval) { + clearInterval(History.waitForPropagationInterval); + } History.waitForPropagation = true; History.waitForPropagationInterval = setInterval(function() { var urlState = History.extractState(document.location.href); From caec51b35c68e9e05a6d32c51f05e223fc6aab70 Mon Sep 17 00:00:00 2001 From: Eric O'Connell Date: Sat, 21 Jan 2012 15:05:58 -0800 Subject: [PATCH 3/3] fix storeState and getIdByUrl for Safari 5.0.x History.storeState was only updating the urlToId mapping when new states were stored during the session, and then updating the idToState and stateToId mappings on unload, so when the safariStatePoll attempted to check if the current state was the lastSavedState, that function's dependency on extractState would fail as it was using the idToState mapping. --- scripts/uncompressed/history.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/scripts/uncompressed/history.js b/scripts/uncompressed/history.js index bd869d7e..c7c80a63 100644 --- a/scripts/uncompressed/history.js +++ b/scripts/uncompressed/history.js @@ -895,7 +895,9 @@ */ History.getIdByUrl = function(url){ // Fetch - var id = History.urlToId[url] || History.store.urlToId[url] || undefined; + var unescaped = History.unescapeString(url); + var id = History.urlToId[url] || History.store.urlToId[url] || + History.urlToId[unescaped] || History.store.urlToId[unescaped] || undefined; // Return return id; @@ -948,7 +950,9 @@ */ History.storeState = function(newState){ // Store the State - History.urlToId[newState.url] = newState.id; + History.urlToId[newState.cleanUrl] = newState.id; + History.idToState[newState.id] = newState; + History.stateToId[History.getStateString(newState)] = newState.id; // Push the State History.storedStates.push(History.cloneObject(newState)); @@ -1749,15 +1753,15 @@ History.busy(false); } else { + // Store the newState + History.storeState(newState); + History.expectedStateId = newState.id; + if (History.bugs.safariPoll) { //History.debug('Blocking safariPoll until url state propagation'); History.blockSafariPollUntilPropagation(newState); } - // Store the newState - History.storeState(newState); - History.expectedStateId = newState.id; - // Push the newState history.pushState(newState.id,newState.title,newState.url);