-
Sean Hammond authored
This commit fixes an issue that refresh token requests were being sent too late if a laptop was suspended, even if the laptop was resumed again in time to do the refresh. For example if a laptop was suspended for 10 mins, then the refresh request would be sent 10 mins later than it should have been and the access token and refresh token would have already expired before the refresh request was attempted. The bug was due to a misunderstanding about how `window.setTimeout()` works: If you, say, use `setTimeout` to call a function in 10 minutes time, then wait 5 minutes, then put the laptop to sleep for an hour, then wake it up, **it will wait another 5 minutes and then call the function**. That is, the time that the laptop spent asleep does not count towards the timeout, it pauses the timer and then continues the timer again when the laptop wakes. This will also happen if the browser or OS suspends the tab or app. I've verified this behaviour in Chrome and Firefox on Linux, and it also agrees with [this StackOverflow answer](http://stackoverflow.com/questions/6346849/what-happens-to-settimeout-when-the-computer-goes-to-sleep/6347336#6347336) and [the HTML5 spec on `setTimeout`](https://www.w3.org/TR/2010/WD-html5-20101019/timers.html#timers): "wait until the Document associated with the method context has been fully active for a further timeout milliseconds (not necessarily consecutively)." This means that if you want to definitely call a function at a given time (say 55 mins in the future) you can't just use `setTimeout` to set a single timer for 55 mins like our code was doing - if the laptop is suspended during those 55 mins then the timer won't go off until 55 mins + however long the laptop was suspended for. To fix this change the code to repeatedly poll, every few seconds, whether the current time is later than the access token expiry time minus 5 minutes. If it is, then try to refresh the access token. I'm using repeated calls to `window.setTimeout()` to implement this, rather than one call to `window.setInterval()`, because with `setTimeout()` it's easier to do a couple of things that the previous implementation was already doing: 1. Not make any more refresh token requests while one is still in flight 2. Stop making further refresh token requests when one fails