Skip to content

Commit

Permalink
Add initial support for JS SpeechSynthesisErrorEvent
Browse files Browse the repository at this point in the history
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
  • Loading branch information
asurdej-comcast committed Apr 6, 2022
1 parent adec775 commit 09a1549
Show file tree
Hide file tree
Showing 12 changed files with 226 additions and 15 deletions.
1 change: 1 addition & 0 deletions Source/WebCore/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
43 changes: 35 additions & 8 deletions Source/WebCore/Modules/speech/SpeechSynthesis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <wtf/NeverDestroyed.h>
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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<SpeechSynthesisUtterance> 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<SpeechSynthesisUtterance> firstUtterance = m_utteranceQueue.takeFirst();
Expand Down Expand Up @@ -293,13 +320,13 @@ void SpeechSynthesis::didResumeSpeaking(PlatformSpeechSynthesisUtterance& uttera
void SpeechSynthesis::didFinishSpeaking(PlatformSpeechSynthesisUtterance& utterance)
{
if (utterance.client())
handleSpeakingCompleted(static_cast<SpeechSynthesisUtterance&>(*utterance.client()), false);
handleSpeakingCompleted(static_cast<SpeechSynthesisUtterance&>(*utterance.client()), SpeechErrorNone);
}

void SpeechSynthesis::speakingErrorOccurred(PlatformSpeechSynthesisUtterance& utterance)
void SpeechSynthesis::speakingErrorOccurred(PlatformSpeechSynthesisUtterance& utterance, SpeechError error)
{
if (utterance.client())
handleSpeakingCompleted(static_cast<SpeechSynthesisUtterance&>(*utterance.client()), true);
handleSpeakingCompleted(static_cast<SpeechSynthesisUtterance&>(*utterance.client()), error);
}

} // namespace WebCore
Expand Down
5 changes: 3 additions & 2 deletions Source/WebCore/Modules/speech/SpeechSynthesis.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down
53 changes: 53 additions & 0 deletions Source/WebCore/Modules/speech/SpeechSynthesisErrorEvent.cpp
Original file line number Diff line number Diff line change
@@ -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 <wtf/IsoMallocInlines.h>

namespace WebCore {

WTF_MAKE_ISO_ALLOCATED_IMPL(SpeechSynthesisErrorEvent);

Ref<SpeechSynthesisErrorEvent> 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)
63 changes: 63 additions & 0 deletions Source/WebCore/Modules/speech/SpeechSynthesisErrorEvent.h
Original file line number Diff line number Diff line change
@@ -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<SpeechSynthesisErrorEvent> 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)
46 changes: 46 additions & 0 deletions Source/WebCore/Modules/speech/SpeechSynthesisErrorEvent.idl
Original file line number Diff line number Diff line change
@@ -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;
};
5 changes: 3 additions & 2 deletions Source/WebCore/Modules/speech/SpeechSynthesisEvent.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

namespace WebCore {

class SpeechSynthesisEvent final : public Event {
class SpeechSynthesisEvent : public Event {
WTF_MAKE_ISO_ALLOCATED(SpeechSynthesisEvent);
public:
static Ref<SpeechSynthesisEvent> create(const AtomString& type, unsigned charIndex, float elapsedTime, const String& name);
Expand All @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions Source/WebCore/Sources.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -3412,6 +3413,7 @@ JSSourceBuffer.cpp
JSSourceBufferList.cpp
JSSpeechSynthesis.cpp
JSSpeechSynthesisEvent.cpp
JSSpeechSynthesisErrorEvent.cpp
JSSpeechSynthesisUtterance.cpp
JSSpeechSynthesisVoice.cpp
JSStaticRange.cpp
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/dom/EventNames.in
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 17 additions & 1 deletion Source/WebCore/platform/PlatformSpeechSynthesizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ void PlatformSpeechSynthesizerMock::cancel()
return;

m_speakingFinishedTimer.stop();
client()->speakingErrorOccurred(*m_utterance);
client()->speakingErrorOccurred(*m_utterance, SpeechErrorCanceled);
m_utterance = nullptr;
}

Expand Down
2 changes: 1 addition & 1 deletion Source/WebKit/UIProcess/WebPageProxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -2225,7 +2225,7 @@ class WebPageProxy : public API::ObjectImpl<API::Object::Type::Page>
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;

Expand Down

0 comments on commit 09a1549

Please sign in to comment.