From 09a1549d9c0f2455cc50f1a59b39d300ea23eed3 Mon Sep 17 00:00:00 2001 From: Andrzej Surdej Date: Wed, 6 Apr 2022 09:39:46 +0200 Subject: [PATCH] Add initial support for JS SpeechSynthesisErrorEvent 1) Add new type SpeechSynthesisErrorEvent and expose it to JS 2) Extend PlatformSpeechSynthesizer speakingErrorOccured with error type arg 3) Send proper JS event on error condiditon --- Source/WebCore/CMakeLists.txt | 1 + .../Modules/speech/SpeechSynthesis.cpp | 43 ++++++++++--- .../WebCore/Modules/speech/SpeechSynthesis.h | 5 +- .../speech/SpeechSynthesisErrorEvent.cpp | 53 ++++++++++++++++ .../speech/SpeechSynthesisErrorEvent.h | 63 +++++++++++++++++++ .../speech/SpeechSynthesisErrorEvent.idl | 46 ++++++++++++++ .../Modules/speech/SpeechSynthesisEvent.h | 5 +- Source/WebCore/Sources.txt | 2 + Source/WebCore/dom/EventNames.in | 1 + .../platform/PlatformSpeechSynthesizer.h | 18 +++++- .../mock/PlatformSpeechSynthesizerMock.cpp | 2 +- Source/WebKit/UIProcess/WebPageProxy.h | 2 +- 12 files changed, 226 insertions(+), 15 deletions(-) create mode 100644 Source/WebCore/Modules/speech/SpeechSynthesisErrorEvent.cpp create mode 100644 Source/WebCore/Modules/speech/SpeechSynthesisErrorEvent.h create mode 100644 Source/WebCore/Modules/speech/SpeechSynthesisErrorEvent.idl diff --git a/Source/WebCore/CMakeLists.txt b/Source/WebCore/CMakeLists.txt index 1137e0dc74d74..c4b7a97792426 100644 --- a/Source/WebCore/CMakeLists.txt +++ b/Source/WebCore/CMakeLists.txt @@ -418,6 +418,7 @@ set(WebCore_NON_SVG_IDL_FILES Modules/speech/DOMWindowSpeechSynthesis.idl Modules/speech/SpeechSynthesis.idl Modules/speech/SpeechSynthesisEvent.idl + Modules/speech/SpeechSynthesisErrorEvent.idl Modules/speech/SpeechSynthesisUtterance.idl Modules/speech/SpeechSynthesisVoice.idl diff --git a/Source/WebCore/Modules/speech/SpeechSynthesis.cpp b/Source/WebCore/Modules/speech/SpeechSynthesis.cpp index 668d3cbfd419c..f40c0e2e1cfb1 100644 --- a/Source/WebCore/Modules/speech/SpeechSynthesis.cpp +++ b/Source/WebCore/Modules/speech/SpeechSynthesis.cpp @@ -31,7 +31,7 @@ #include "EventNames.h" #include "PlatformSpeechSynthesisVoice.h" #include "PlatformSpeechSynthesizer.h" -#include "SpeechSynthesisEvent.h" +#include "SpeechSynthesisErrorEvent.h" #include "SpeechSynthesisUtterance.h" #include "UserGestureIndicator.h" #include @@ -116,7 +116,7 @@ void SpeechSynthesis::startSpeakingImmediately(SpeechSynthesisUtterance& utteran // Zero lengthed strings should immediately notify that the event is complete. if (utterance.text().isEmpty()) { - handleSpeakingCompleted(utterance, false); + handleSpeakingCompleted(utterance, SpeechErrorNone); return; } @@ -180,19 +180,46 @@ void SpeechSynthesis::resume() } } +SpeechSynthesisErrorEvent::Code toSpeechSynthesisErrorEventCode(SpeechError error) { + switch(error) { + case SpeechErrorCanceled: return SpeechSynthesisErrorEvent::Code::Canceled; + case SpeechErrorInterrupted: return SpeechSynthesisErrorEvent::Code::Interrupted; + case SpeechErrorAudioBusy: return SpeechSynthesisErrorEvent::Code::AudioBusy; + case SpeechErrorAudioHardware: return SpeechSynthesisErrorEvent::Code::AudioHardware; + case SpeechErrorNetwork: return SpeechSynthesisErrorEvent::Code::Network; + case SpeechErrorSynthesisUnavailable: return SpeechSynthesisErrorEvent::Code::SynthesisUnavailable; + case SpeechErrorSynthesisFailed: return SpeechSynthesisErrorEvent::Code::SynthesisFailed; + case SpeechErrorLanguageUnavailable: return SpeechSynthesisErrorEvent::Code::LanguageUnavailable; + case SpeechErrorVoiceUnavailable: return SpeechSynthesisErrorEvent::Code::VoiceUnavailable; + case SpeechErrorTextTooLong: return SpeechSynthesisErrorEvent::Code::TextTooLong; + case SpeechErrorInvalidArgument: return SpeechSynthesisErrorEvent::Code::InvalidArgument; + case SpeechErrorNotAllowed: return SpeechSynthesisErrorEvent::Code::NotAllowed; + default: return SpeechSynthesisErrorEvent::Code::Interrupted; + } +} + void SpeechSynthesis::fireEvent(const AtomString& type, SpeechSynthesisUtterance& utterance, unsigned long charIndex, const String& name) { utterance.dispatchEvent(SpeechSynthesisEvent::create(type, charIndex, (MonotonicTime::now() - utterance.startTime()).seconds(), name)); } -void SpeechSynthesis::handleSpeakingCompleted(SpeechSynthesisUtterance& utterance, bool errorOccurred) +void SpeechSynthesis::fireErrorEvent(SpeechSynthesisUtterance& utterance, SpeechError error) +{ + utterance.dispatchEvent(SpeechSynthesisErrorEvent::create(toSpeechSynthesisErrorEventCode(error))); +} + +void SpeechSynthesis::handleSpeakingCompleted(SpeechSynthesisUtterance& utterance, SpeechError error) { ASSERT(m_currentSpeechUtterance); Ref protect(utterance); - m_currentSpeechUtterance = nullptr; + if(m_currentSpeechUtterance == &utterance) + m_currentSpeechUtterance = nullptr; - fireEvent(errorOccurred ? eventNames().errorEvent : eventNames().endEvent, utterance, 0, String()); + if(error == SpeechErrorNone) + fireEvent(eventNames().endEvent, utterance, 0, String()); + else + fireErrorEvent(utterance, error); if (m_utteranceQueue.size()) { Ref firstUtterance = m_utteranceQueue.takeFirst(); @@ -293,13 +320,13 @@ void SpeechSynthesis::didResumeSpeaking(PlatformSpeechSynthesisUtterance& uttera void SpeechSynthesis::didFinishSpeaking(PlatformSpeechSynthesisUtterance& utterance) { if (utterance.client()) - handleSpeakingCompleted(static_cast(*utterance.client()), false); + handleSpeakingCompleted(static_cast(*utterance.client()), SpeechErrorNone); } -void SpeechSynthesis::speakingErrorOccurred(PlatformSpeechSynthesisUtterance& utterance) +void SpeechSynthesis::speakingErrorOccurred(PlatformSpeechSynthesisUtterance& utterance, SpeechError error) { if (utterance.client()) - handleSpeakingCompleted(static_cast(*utterance.client()), true); + handleSpeakingCompleted(static_cast(*utterance.client()), error); } } // namespace WebCore diff --git a/Source/WebCore/Modules/speech/SpeechSynthesis.h b/Source/WebCore/Modules/speech/SpeechSynthesis.h index 63cfc6549abe2..c2d7c72c4df2e 100644 --- a/Source/WebCore/Modules/speech/SpeechSynthesis.h +++ b/Source/WebCore/Modules/speech/SpeechSynthesis.h @@ -67,7 +67,7 @@ class SpeechSynthesis : public PlatformSpeechSynthesizerClient, public SpeechSyn void didPauseSpeaking(PlatformSpeechSynthesisUtterance&) override; void didResumeSpeaking(PlatformSpeechSynthesisUtterance&) override; void didFinishSpeaking(PlatformSpeechSynthesisUtterance&) override; - void speakingErrorOccurred(PlatformSpeechSynthesisUtterance&) override; + void speakingErrorOccurred(PlatformSpeechSynthesisUtterance&, SpeechError) override; void boundaryEventOccurred(PlatformSpeechSynthesisUtterance&, SpeechBoundary, unsigned charIndex) override; // SpeechSynthesisClient override methods @@ -80,8 +80,9 @@ class SpeechSynthesis : public PlatformSpeechSynthesizerClient, public SpeechSyn void voicesChanged() override; void startSpeakingImmediately(SpeechSynthesisUtterance&); - void handleSpeakingCompleted(SpeechSynthesisUtterance&, bool errorOccurred); + void handleSpeakingCompleted(SpeechSynthesisUtterance&, SpeechError); void fireEvent(const AtomString& type, SpeechSynthesisUtterance&, unsigned long charIndex, const String& name); + void fireErrorEvent(SpeechSynthesisUtterance&, SpeechError); #if PLATFORM(IOS_FAMILY) // Restrictions to change default behaviors. diff --git a/Source/WebCore/Modules/speech/SpeechSynthesisErrorEvent.cpp b/Source/WebCore/Modules/speech/SpeechSynthesisErrorEvent.cpp new file mode 100644 index 0000000000000..90500eac7902c --- /dev/null +++ b/Source/WebCore/Modules/speech/SpeechSynthesisErrorEvent.cpp @@ -0,0 +1,53 @@ +/* Copyright (C) 2019 RDK Management. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS. OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "config.h" +#include "SpeechSynthesisErrorEvent.h" + +#if ENABLE(SPEECH_SYNTHESIS) + +#include "EventNames.h" +#include + +namespace WebCore { + +WTF_MAKE_ISO_ALLOCATED_IMPL(SpeechSynthesisErrorEvent); + +Ref SpeechSynthesisErrorEvent::create(Code error) +{ + return adoptRef(*new SpeechSynthesisErrorEvent(error)); +} + +SpeechSynthesisErrorEvent::SpeechSynthesisErrorEvent(Code error) + : SpeechSynthesisEvent(eventNames().errorEvent, 0, 0.0, String()), m_error(error) +{ +} + +SpeechSynthesisErrorEvent::Code SpeechSynthesisErrorEvent::error() const { + return m_error; +} + +} // namespace WebCore + +#endif // ENABLE(SPEECH_SYNTHESIS) diff --git a/Source/WebCore/Modules/speech/SpeechSynthesisErrorEvent.h b/Source/WebCore/Modules/speech/SpeechSynthesisErrorEvent.h new file mode 100644 index 0000000000000..205436e5ec9df --- /dev/null +++ b/Source/WebCore/Modules/speech/SpeechSynthesisErrorEvent.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2019 RDK Management. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS. OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#if ENABLE(SPEECH_SYNTHESIS) + +#include "SpeechSynthesisEvent.h" + +namespace WebCore { + +class SpeechSynthesisErrorEvent final : public SpeechSynthesisEvent { + WTF_MAKE_ISO_ALLOCATED(SpeechSynthesisErrorEvent); +public: + enum class Code { + Canceled, + Interrupted, + AudioBusy, + AudioHardware, + Network, + SynthesisUnavailable, + SynthesisFailed, + LanguageUnavailable, + VoiceUnavailable, + TextTooLong, + InvalidArgument, + NotAllowed + }; + + static Ref create(const Code error); + virtual EventInterface eventInterface() const { return SpeechSynthesisErrorEventInterfaceType; } + Code error() const; + +private: + SpeechSynthesisErrorEvent(const Code error); + + Code m_error; +}; + +} // namespace WebCore + +#endif // ENABLE(SPEECH_SYNTHESIS) diff --git a/Source/WebCore/Modules/speech/SpeechSynthesisErrorEvent.idl b/Source/WebCore/Modules/speech/SpeechSynthesisErrorEvent.idl new file mode 100644 index 0000000000000..03cc5289fe541 --- /dev/null +++ b/Source/WebCore/Modules/speech/SpeechSynthesisErrorEvent.idl @@ -0,0 +1,46 @@ +/* Copyright (C) 2019 RDK Management. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS. OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// https://w3c.github.io/speech-api/#enumdef-speechsynthesiserrorcode +[ + Conditional=SPEECH_SYNTHESIS, +] enum SpeechSynthesisErrorCode { + "canceled", + "interrupted", + "audio-busy", + "audio-hardware", + "network", + "synthesis-unavailable", + "synthesis-failed", + "language-unavailable", + "voice-unavailable", + "text-too-long", + "invalid-argument", + "not-allowed" +}; + +[ + Conditional=SPEECH_SYNTHESIS +] interface SpeechSynthesisErrorEvent : SpeechSynthesisEvent { + readonly attribute SpeechSynthesisErrorCode error; +}; diff --git a/Source/WebCore/Modules/speech/SpeechSynthesisEvent.h b/Source/WebCore/Modules/speech/SpeechSynthesisEvent.h index 3c16c3ef87672..bbc945cc9fedb 100644 --- a/Source/WebCore/Modules/speech/SpeechSynthesisEvent.h +++ b/Source/WebCore/Modules/speech/SpeechSynthesisEvent.h @@ -31,7 +31,7 @@ namespace WebCore { -class SpeechSynthesisEvent final : public Event { +class SpeechSynthesisEvent : public Event { WTF_MAKE_ISO_ALLOCATED(SpeechSynthesisEvent); public: static Ref create(const AtomString& type, unsigned charIndex, float elapsedTime, const String& name); @@ -42,9 +42,10 @@ class SpeechSynthesisEvent final : public Event { virtual EventInterface eventInterface() const { return SpeechSynthesisEventInterfaceType; } -private: +protected: SpeechSynthesisEvent(const AtomString& type, unsigned charIndex, float elapsedTime, const String& name); +private: unsigned long m_charIndex; float m_elapsedTime; String m_name; diff --git a/Source/WebCore/Sources.txt b/Source/WebCore/Sources.txt index 35d8a3c9d3e59..59fb0a46a57ac 100644 --- a/Source/WebCore/Sources.txt +++ b/Source/WebCore/Sources.txt @@ -217,6 +217,7 @@ Modules/remoteplayback/RemotePlayback.cpp Modules/speech/DOMWindowSpeechSynthesis.cpp Modules/speech/SpeechSynthesis.cpp Modules/speech/SpeechSynthesisEvent.cpp +Modules/speech/SpeechSynthesisErrorEvent.cpp Modules/speech/SpeechSynthesisUtterance.cpp Modules/speech/SpeechSynthesisVoice.cpp @@ -3412,6 +3413,7 @@ JSSourceBuffer.cpp JSSourceBufferList.cpp JSSpeechSynthesis.cpp JSSpeechSynthesisEvent.cpp +JSSpeechSynthesisErrorEvent.cpp JSSpeechSynthesisUtterance.cpp JSSpeechSynthesisVoice.cpp JSStaticRange.cpp diff --git a/Source/WebCore/dom/EventNames.in b/Source/WebCore/dom/EventNames.in index 157f331c1ba73..c84b682e81bd0 100644 --- a/Source/WebCore/dom/EventNames.in +++ b/Source/WebCore/dom/EventNames.in @@ -62,6 +62,7 @@ RTCDTMFToneChangeEvent conditional=WEB_RTC RTCPeerConnectionIceEvent conditional=WEB_RTC RTCTrackEvent conditional=WEB_RTC SpeechSynthesisEvent conditional=SPEECH_SYNTHESIS +SpeechSynthesisErrorEvent conditional=SPEECH_SYNTHESIS WebGLContextEvent conditional=WEBGL StorageEvent SVGEvents interfaceName=Event diff --git a/Source/WebCore/platform/PlatformSpeechSynthesizer.h b/Source/WebCore/platform/PlatformSpeechSynthesizer.h index b4b8d08a0cc31..7e301e0ffa5a8 100644 --- a/Source/WebCore/platform/PlatformSpeechSynthesizer.h +++ b/Source/WebCore/platform/PlatformSpeechSynthesizer.h @@ -43,6 +43,22 @@ enum class SpeechBoundary : uint8_t { SpeechSentenceBoundary }; +enum SpeechError { + SpeechErrorNone, + SpeechErrorCanceled, + SpeechErrorInterrupted, + SpeechErrorAudioBusy, + SpeechErrorAudioHardware, + SpeechErrorNetwork, + SpeechErrorSynthesisUnavailable, + SpeechErrorSynthesisFailed, + SpeechErrorLanguageUnavailable, + SpeechErrorVoiceUnavailable, + SpeechErrorTextTooLong, + SpeechErrorInvalidArgument, + SpeechErrorNotAllowed +}; + class PlatformSpeechSynthesisUtterance; class PlatformSpeechSynthesizerClient { @@ -51,7 +67,7 @@ class PlatformSpeechSynthesizerClient { virtual void didFinishSpeaking(PlatformSpeechSynthesisUtterance&) = 0; virtual void didPauseSpeaking(PlatformSpeechSynthesisUtterance&) = 0; virtual void didResumeSpeaking(PlatformSpeechSynthesisUtterance&) = 0; - virtual void speakingErrorOccurred(PlatformSpeechSynthesisUtterance&) = 0; + virtual void speakingErrorOccurred(PlatformSpeechSynthesisUtterance&, SpeechError) = 0; virtual void boundaryEventOccurred(PlatformSpeechSynthesisUtterance&, SpeechBoundary, unsigned charIndex) = 0; virtual void voicesDidChange() = 0; protected: diff --git a/Source/WebCore/platform/mock/PlatformSpeechSynthesizerMock.cpp b/Source/WebCore/platform/mock/PlatformSpeechSynthesizerMock.cpp index 69919cde9842f..c53ab29fb0aa8 100644 --- a/Source/WebCore/platform/mock/PlatformSpeechSynthesizerMock.cpp +++ b/Source/WebCore/platform/mock/PlatformSpeechSynthesizerMock.cpp @@ -75,7 +75,7 @@ void PlatformSpeechSynthesizerMock::cancel() return; m_speakingFinishedTimer.stop(); - client()->speakingErrorOccurred(*m_utterance); + client()->speakingErrorOccurred(*m_utterance, SpeechErrorCanceled); m_utterance = nullptr; } diff --git a/Source/WebKit/UIProcess/WebPageProxy.h b/Source/WebKit/UIProcess/WebPageProxy.h index dc833c9b9d16d..6f14ed7faee12 100644 --- a/Source/WebKit/UIProcess/WebPageProxy.h +++ b/Source/WebKit/UIProcess/WebPageProxy.h @@ -2225,7 +2225,7 @@ class WebPageProxy : public API::ObjectImpl void didFinishSpeaking(WebCore::PlatformSpeechSynthesisUtterance&) override; void didPauseSpeaking(WebCore::PlatformSpeechSynthesisUtterance&) override; void didResumeSpeaking(WebCore::PlatformSpeechSynthesisUtterance&) override; - void speakingErrorOccurred(WebCore::PlatformSpeechSynthesisUtterance&) override; + void speakingErrorOccurred(WebCore::PlatformSpeechSynthesisUtterance&, WebCore::SpeechError) override; void boundaryEventOccurred(WebCore::PlatformSpeechSynthesisUtterance&, WebCore::SpeechBoundary, unsigned charIndex) override; void voicesDidChange() override;