1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "third_party/blink/renderer/modules/speech/speech_synthesis_utterance.h"
27 
28 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
29 #include "third_party/blink/renderer/modules/speech/speech_synthesis.h"
30 #include "third_party/blink/renderer/platform/heap/heap.h"
31 #include "third_party/blink/renderer/platform/heap/persistent.h"
32 
33 namespace blink {
34 
Create(ExecutionContext * context)35 SpeechSynthesisUtterance* SpeechSynthesisUtterance::Create(
36     ExecutionContext* context) {
37   return MakeGarbageCollected<SpeechSynthesisUtterance>(context, String());
38 }
39 
Create(ExecutionContext * context,const String & text)40 SpeechSynthesisUtterance* SpeechSynthesisUtterance::Create(
41     ExecutionContext* context,
42     const String& text) {
43   return MakeGarbageCollected<SpeechSynthesisUtterance>(context, text);
44 }
45 
SpeechSynthesisUtterance(ExecutionContext * context,const String & text)46 SpeechSynthesisUtterance::SpeechSynthesisUtterance(ExecutionContext* context,
47                                                    const String& text)
48     : ExecutionContextClient(context),
49       receiver_(this, context),
50       mojom_utterance_(mojom::blink::SpeechSynthesisUtterance::New()) {
51   // Set default values. |voice| intentionally left null.
52   mojom_utterance_->text = text;
53   mojom_utterance_->lang = String("");
54   mojom_utterance_->volume = mojom::blink::kSpeechSynthesisDoublePrefNotSet;
55   mojom_utterance_->rate = mojom::blink::kSpeechSynthesisDoublePrefNotSet;
56   mojom_utterance_->pitch = mojom::blink::kSpeechSynthesisDoublePrefNotSet;
57 }
58 
59 SpeechSynthesisUtterance::~SpeechSynthesisUtterance() = default;
60 
InterfaceName() const61 const AtomicString& SpeechSynthesisUtterance::InterfaceName() const {
62   return event_target_names::kSpeechSynthesisUtterance;
63 }
64 
voice() const65 SpeechSynthesisVoice* SpeechSynthesisUtterance::voice() const {
66   return voice_;
67 }
68 
setVoice(SpeechSynthesisVoice * voice)69 void SpeechSynthesisUtterance::setVoice(SpeechSynthesisVoice* voice) {
70   // Cache our own version of the SpeechSynthesisVoice so that we don't have to
71   // do some lookup to go from the platform voice back to the speech synthesis
72   // voice in the read property.
73   voice_ = voice;
74 
75   mojom_utterance_->voice = voice_ ? voice_->name() : String();
76 }
77 
Trace(Visitor * visitor) const78 void SpeechSynthesisUtterance::Trace(Visitor* visitor) const {
79   visitor->Trace(receiver_);
80   visitor->Trace(synthesis_);
81   visitor->Trace(voice_);
82   ExecutionContextClient::Trace(visitor);
83   EventTargetWithInlineData::Trace(visitor);
84 }
85 
OnStartedSpeaking()86 void SpeechSynthesisUtterance::OnStartedSpeaking() {
87   DCHECK(synthesis_);
88   synthesis_->DidStartSpeaking(this);
89 }
90 
OnFinishedSpeaking()91 void SpeechSynthesisUtterance::OnFinishedSpeaking() {
92   DCHECK(synthesis_);
93   finished_ = true;
94   synthesis_->DidFinishSpeaking(this);
95 }
96 
OnPausedSpeaking()97 void SpeechSynthesisUtterance::OnPausedSpeaking() {
98   DCHECK(synthesis_);
99   synthesis_->DidPauseSpeaking(this);
100 }
101 
OnResumedSpeaking()102 void SpeechSynthesisUtterance::OnResumedSpeaking() {
103   DCHECK(synthesis_);
104   synthesis_->DidResumeSpeaking(this);
105 }
106 
OnEncounteredWordBoundary(uint32_t char_index,uint32_t char_length)107 void SpeechSynthesisUtterance::OnEncounteredWordBoundary(uint32_t char_index,
108                                                          uint32_t char_length) {
109   DCHECK(synthesis_);
110   synthesis_->WordBoundaryEventOccurred(this, char_index, char_length);
111 }
112 
OnEncounteredSentenceBoundary(uint32_t char_index,uint32_t char_length)113 void SpeechSynthesisUtterance::OnEncounteredSentenceBoundary(
114     uint32_t char_index,
115     uint32_t char_length) {
116   DCHECK(synthesis_);
117   synthesis_->SentenceBoundaryEventOccurred(this, char_index, char_length);
118 }
119 
OnEncounteredSpeakingError()120 void SpeechSynthesisUtterance::OnEncounteredSpeakingError() {
121   DCHECK(synthesis_);
122   finished_ = true;
123   synthesis_->SpeakingErrorOccurred(this);
124 }
125 
Start(SpeechSynthesis * synthesis)126 void SpeechSynthesisUtterance::Start(SpeechSynthesis* synthesis) {
127   ExecutionContext* context = GetExecutionContext();
128   if (!context)
129     return;
130 
131   finished_ = false;
132 
133   mojom::blink::SpeechSynthesisUtterancePtr mojom_utterance_to_send =
134       mojom_utterance_->Clone();
135   if (mojom_utterance_to_send->voice.IsNull())
136     mojom_utterance_to_send->voice = String("");
137   if (mojom_utterance_to_send->text.IsNull())
138     mojom_utterance_to_send->text = String("");
139 
140   receiver_.reset();
141 
142   synthesis_ = synthesis;
143   synthesis_->MojomSynthesis()->Speak(
144       std::move(mojom_utterance_to_send),
145       receiver_.BindNewPipeAndPassRemote(
146           context->GetTaskRunner(TaskType::kMiscPlatformAPI)));
147 
148   // Add a disconnect handler so we can cleanup appropriately.
149   receiver_.set_disconnect_handler(WTF::Bind(
150       &SpeechSynthesisUtterance::OnDisconnected, WrapWeakPersistent(this)));
151 }
152 
OnDisconnected()153 void SpeechSynthesisUtterance::OnDisconnected() {
154   // If the remote end disconnects, just simulate that we finished normally.
155   if (!finished_)
156     OnFinishedSpeaking();
157 }
158 
159 }  // namespace blink
160