Tab.waitUntilLoaded() (markanywhere-browse 0.4.0, WaitUntilLoaded.kt) is documented as best-effort:
On a never-quiet page the [timeout] cap inside each waiter trips and capture proceeds anyway.
@return true if both the network and the DOM went quiet within [timeout], false if either hit its cap (capture should still proceed — false means "best effort", not "failed").
Its first step does not keep that promise:
waitForReadyState(ReadyState.COMPLETE, timeout = timeout.inWholeMilliseconds)
kdriver's waitForReadyState throws TimeoutWaitingForReadyStateException when the cap is reached, where the other two waiters (waitForNetworkIdle, waitForDomIdle) return false.
So on any page whose document.readyState never reaches complete, the call throws after 15 s instead of returning false.
Where it bites
A page whose document has fully arrived but one subresource never finishes, e.g. an <img> the server never answers.
In the wild: Wayback Machine captures (web.archive.org/web/<ts>/…), whose toolbar assets can hang.
In umwelt, the exception escaped after a navigation or click had already happened.
The caller got an untyped 502 for a page that had in fact loaded, and an agent would retry a click that had already gone through.
Minimal repro
<!doctype html><html><body><h1>Stalled</h1><img src="/stalled.png"></body></html>
With /stalled.png never answered, tab.waitUntilLoaded() throws TimeoutWaitingForReadyStateException: Timeout waiting for readyState == COMPLETE after 15 s.
Suggested fix
Catch the timeout and fold it into the boolean result, so waitUntilLoaded() returns false like the other two waiters:
val ready = try {
waitForReadyState(ReadyState.COMPLETE, timeout = timeout.inWholeMilliseconds)
} catch (_: TimeoutWaitingForReadyStateException) {
false
}
Workaround in umwelt
LocalUmweltClient.settle() catches the exception around every waitUntilLoaded() call, and umwelt-e2e pins it with a /stalled.html fixture.
Once this is fixed upstream, that wrapper can go.
Tab.waitUntilLoaded()(markanywhere-browse 0.4.0,WaitUntilLoaded.kt) is documented as best-effort:Its first step does not keep that promise:
kdriver's
waitForReadyStatethrowsTimeoutWaitingForReadyStateExceptionwhen the cap is reached, where the other two waiters (waitForNetworkIdle,waitForDomIdle) returnfalse.So on any page whose
document.readyStatenever reachescomplete, the call throws after 15 s instead of returningfalse.Where it bites
A page whose document has fully arrived but one subresource never finishes, e.g. an
<img>the server never answers.In the wild: Wayback Machine captures (
web.archive.org/web/<ts>/…), whose toolbar assets can hang.In umwelt, the exception escaped after a navigation or click had already happened.
The caller got an untyped
502for a page that had in fact loaded, and an agent would retry a click that had already gone through.Minimal repro
With
/stalled.pngnever answered,tab.waitUntilLoaded()throwsTimeoutWaitingForReadyStateException: Timeout waiting for readyState == COMPLETEafter 15 s.Suggested fix
Catch the timeout and fold it into the boolean result, so
waitUntilLoaded()returnsfalselike the other two waiters:Workaround in umwelt
LocalUmweltClient.settle()catches the exception around everywaitUntilLoaded()call, and umwelt-e2e pins it with a/stalled.htmlfixture.Once this is fixed upstream, that wrapper can go.