CFTurnstileHandler is only constructed on DOMContentLoaded/pageshow, but the
test iframe posts "ready" from its inline script while the document is still
parsing. The test replies immediately, so the fail/success div can be shown
before the handler exists.
A MutationObserver only reports mutations made after observe(), so when that
happens the callback never runs, CaptchaState:Update is never sent, and the
test waits for a metric that never arrives until it times out. The same
applies outside of tests: a Turnstile widget that resolves before
DOMContentLoaded is never recorded.
Check the current state once right after observe() so an already-visible
result is still reported, guarded by a flag so a later mutation cannot report
it twice.
While here, store the observer before calling observe(). The previous code
assigned the result of observe(), which is undefined, so onActorDestroy()
threw a TypeError and the shadow root observer was never disconnected.
The regression test drives this deterministically: cf_turnstile.html now
accepts ?state=success|fail to show the result while it is still parsing, so
the result is already visible when the observer is registered. Without the
preceding fix the test hangs with the same signature as the intermittent.
Differential Revision: https://phabricator.services.mozilla.com/D317295