Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: 527353138b7cb1b6d6f8b5c8c9fe5bf52c63c266
https://github.com/WebKit/WebKit/commit/527353138b7cb1b6d6f8b5c8c9fe5bf52c63c266
Author: Kristian Monsen <[email protected]>
Date: 2026-08-20 (Thu, 20 Aug 2026)
Changed paths:
M LayoutTests/fast/speechsynthesis/speech-synthesis-speak-fuzzer-crash.html
A
LayoutTests/fast/speechsynthesis/speech-synthesis-stop-cross-document-utterance-crash-expected.txt
A
LayoutTests/fast/speechsynthesis/speech-synthesis-stop-cross-document-utterance-crash.html
A
LayoutTests/platform/mac/fast/speechsynthesis/speech-synthesis-speak-fuzzer-crash-expected.png
M Source/WebCore/Modules/speech/SpeechSynthesis.cpp
M Source/WebCore/Modules/speech/SpeechSynthesis.h
Log Message:
-----------
SpeechSynthesis::stop() must not synchronously dispatch error events; fix
crash when platform cancel() calls back synchronously
https://bugs.webkit.org/show_bug.cgi?id=315529
rdar://177692338
Reviewed by Per Arne Vollan, Zak Ridouh, and Chris Dumez.
ActiveDOMObject::stop() and suspend() are called from within
ScriptExecutionContext::forEachActiveDOMObject, which holds a
ScriptDisallowedScope that prohibits JavaScript from running. The previous
SpeechSynthesis::stop() and suspend() implementations both called
cancel(), which synchronously fires error events on every queued utterance. In
a debug build this trips the ScriptDisallowedScope assertion;
in a release build it silently executes JS listeners during a scope that is
supposed to forbid it.
The fix introduces stopPlatformSpeech(), called by both stop() and suspend(),
which clears the utterance queue and nulls
m_currentSpeechUtterance without firing any events, then tells the
platform/client to cancel. Any subsequent completion callbacks from the
platform find m_currentSpeechUtterance null and return early in
handleSpeakingCompleted().
A secondary crash was found where PlatformSpeechSynthesizerMock::cancel() calls
speakingErrorOccurred() synchronously, re-entering
handleSpeakingCompleted() before the caller has returned. Since
stopPlatformSpeech() nulls m_currentSpeechUtterance before calling cancel, the
re-entrant call arrived with a null current utterance and hit the
ASSERT(m_currentSpeechUtterance) in handleSpeakingCompleted(). The assert is
replaced with an early return for the null case.
Two layout tests are added: one verifying that speech is silently cancelled
when a page enters the back/forward cache (triggering suspend()), and
one verifying no crash when stop() is called on a SpeechSynthesis that holds an
utterance created in a different document. An existing fuzzer-crash
test is also fixed: it was previously passing only because a teardown crash
happened to occur after notifyDone() was already called, masking the
crash. It is rewritten to wait for the utterance's end or error event before
calling notifyDone(), so any crash in the speech completion path is
caught as a real failure.
Test:
fast/speechsynthesis/speech-synthesis-stop-cross-document-utterance-crash.html
* LayoutTests/fast/speechsynthesis/speech-synthesis-speak-fuzzer-crash.html:
*
LayoutTests/fast/speechsynthesis/speech-synthesis-stop-cross-document-utterance-crash-expected.txt:
Added.
*
LayoutTests/fast/speechsynthesis/speech-synthesis-stop-cross-document-utterance-crash.html:
Added.
*
LayoutTests/platform/mac/fast/speechsynthesis/speech-synthesis-speak-fuzzer-crash-expected.png:
Added.
* Source/WebCore/Modules/speech/SpeechSynthesis.cpp:
(WebCore::SpeechSynthesis::handleSpeakingCompleted):
(WebCore::SpeechSynthesis::suspend):
(WebCore::SpeechSynthesis::stop):
(WebCore::SpeechSynthesis::stopPlatformSpeech):
* Source/WebCore/Modules/speech/SpeechSynthesis.h:
Originally-landed-as: [email protected] (f1fe25fdbdb4).
rdar://184744736
Canonical link: https://commits.webkit.org/319573@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications