- Affected version
- 2.3.13
JavaScript:
getLocalLoadTime: () =>
{
if (XF._localLoadTime)
{
return XF._localLoadTime
}
const loadCacheEl = document.querySelector('#_xfClientLoadTime')
const loadCache = loadCacheEl.value.split(',')
if (loadCache.length === 2 && parseInt(loadCache[1]) === XF.config.time.now)
{
XF._localLoadTime = parseInt(loadCache[0])
return XF._localLoadTime
}
const navigationTiming = window.performance.getEntriesByType('navigation')[0]
if (!navigationTiming)
{
XF._localLoadTime = Math.floor(new Date().getTime() / 1000) - 1
loadCacheEl.value = XF._localLoadTime + ',' + XF.config.time.now
return XF._localLoadTime
}
// avg between request and response is likely close to server start
const loadTime = (navigationTiming.requestStart + navigationTiming.responseStart) / 2
XF._localLoadTime = Math.floor((window.performance.timeOrigin + loadTime) / 1000)
loadCacheEl.value = XF._localLoadTime + ',' + XF.config.time.now
return XF._localLoadTime
}
This code blindly trusts the navigation entry timing data if it does exist.
Unfortunatley this data may be incomplete (or nonsense) leading to incorrect load time calculation.
The code also blindly takes the cached value from form field
_xfClientLoadTime, if this is newer than current navigation smth. certainly isn't (or wasn't) correct. Suggested Fix
JavaScript:
getLocalLoadTime: () =>
{
if (XF._localLoadTime)
{
return XF._localLoadTime
}
const loadCacheEl = document.querySelector('#_xfClientLoadTime')
const navigationTiming = window.performance.getEntriesByType('navigation')[0]
const hasUsableNavigationTiming = navigationTiming
&& Number.isFinite(navigationTiming.requestStart)
&& Number.isFinite(navigationTiming.responseStart)
&& navigationTiming.requestStart > 0
&& navigationTiming.responseStart > 0
&& navigationTiming.responseStart >= navigationTiming.requestStart
const navigationLoadTime = hasUsableNavigationTiming
? Math.floor((window.performance.timeOrigin + (navigationTiming.requestStart + navigationTiming.responseStart) / 2) / 1000)
: null
const loadCache = loadCacheEl.value.split(',')
if (loadCache.length === 2 && parseInt(loadCache[1]) === XF.config.time.now)
{
const cachedLoadTime = parseInt(loadCache[0])
if (Number.isFinite(cachedLoadTime)
&& (navigationLoadTime === null || cachedLoadTime <= navigationLoadTime + 2)
)
{
XF._localLoadTime = cachedLoadTime
return XF._localLoadTime
}
// a matching cache entry means this document was restored rather than freshly
// parsed, so the navigation may be much later than the render it would have to be
// paired with. assuming no clock skew at least keeps the pair consistent, which is
// the smaller error once the client clock is known to be unreliable
XF._localLoadTime = XF.config.time.now
loadCacheEl.value = XF._localLoadTime + ',' + XF.config.time.now
return XF._localLoadTime
}
if (navigationLoadTime === null)
{
XF._localLoadTime = Math.floor(Date.now() / 1000) - 1
loadCacheEl.value = XF._localLoadTime + ',' + XF.config.time.now
return XF._localLoadTime
}
XF._localLoadTime = navigationLoadTime
loadCacheEl.value = XF._localLoadTime + ',' + XF.config.time.now
return XF._localLoadTime
}