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;