Eliminate the dependency of Profile from TtsMessageFilter.

Eliminating the dependency of Profile from TtsMessageFilter by
delegating GetProfile function to TtsEngineDelegate.
This is part of an effort to move TTS to content.

BUG=347045

Review URL: https://codereview.chromium.org/448033002

Cr-Commit-Position: refs/heads/master@{#289245}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@289245 0039d316-1c4b-4281-b951-d872f2087c98
parent 961eca11
...@@ -813,7 +813,7 @@ void ChromeContentBrowserClient::RenderProcessWillLaunch( ...@@ -813,7 +813,7 @@ void ChromeContentBrowserClient::RenderProcessWillLaunch(
#endif #endif
host->AddFilter(new ChromeNetBenchmarkingMessageFilter(profile, context)); host->AddFilter(new ChromeNetBenchmarkingMessageFilter(profile, context));
host->AddFilter(new prerender::PrerenderMessageFilter(id, profile)); host->AddFilter(new prerender::PrerenderMessageFilter(id, profile));
host->AddFilter(new TtsMessageFilter(id, profile)); host->AddFilter(new TtsMessageFilter(id, host->GetBrowserContext()));
#if defined(ENABLE_WEBRTC) #if defined(ENABLE_WEBRTC)
WebRtcLoggingHandlerHost* webrtc_logging_handler_host = WebRtcLoggingHandlerHost* webrtc_logging_handler_host =
new WebRtcLoggingHandlerHost(profile); new WebRtcLoggingHandlerHost(profile);
......
...@@ -70,8 +70,9 @@ TtsExtensionEngine* TtsExtensionEngine::GetInstance() { ...@@ -70,8 +70,9 @@ TtsExtensionEngine* TtsExtensionEngine::GetInstance() {
return Singleton<TtsExtensionEngine>::get(); return Singleton<TtsExtensionEngine>::get();
} }
void TtsExtensionEngine::GetVoices(Profile* profile, void TtsExtensionEngine::GetVoices(content::BrowserContext* browser_context,
std::vector<VoiceData>* out_voices) { std::vector<VoiceData>* out_voices) {
Profile* profile = Profile::FromBrowserContext(browser_context);
EventRouter* event_router = EventRouter::Get(profile); EventRouter* event_router = EventRouter::Get(profile);
DCHECK(event_router); DCHECK(event_router);
...@@ -176,8 +177,9 @@ void TtsExtensionEngine::Speak(Utterance* utterance, ...@@ -176,8 +177,9 @@ void TtsExtensionEngine::Speak(Utterance* utterance,
scoped_ptr<extensions::Event> event(new extensions::Event( scoped_ptr<extensions::Event> event(new extensions::Event(
tts_engine_events::kOnSpeak, args.Pass())); tts_engine_events::kOnSpeak, args.Pass()));
event->restrict_to_browser_context = utterance->profile(); Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
EventRouter::Get(utterance->profile()) event->restrict_to_browser_context = profile;
EventRouter::Get(profile)
->DispatchEventToExtension(utterance->extension_id(), event.Pass()); ->DispatchEventToExtension(utterance->extension_id(), event.Pass());
} }
...@@ -185,8 +187,9 @@ void TtsExtensionEngine::Stop(Utterance* utterance) { ...@@ -185,8 +187,9 @@ void TtsExtensionEngine::Stop(Utterance* utterance) {
scoped_ptr<base::ListValue> args(new base::ListValue()); scoped_ptr<base::ListValue> args(new base::ListValue());
scoped_ptr<extensions::Event> event(new extensions::Event( scoped_ptr<extensions::Event> event(new extensions::Event(
tts_engine_events::kOnStop, args.Pass())); tts_engine_events::kOnStop, args.Pass()));
event->restrict_to_browser_context = utterance->profile(); Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
EventRouter::Get(utterance->profile()) event->restrict_to_browser_context = profile;
EventRouter::Get(profile)
->DispatchEventToExtension(utterance->extension_id(), event.Pass()); ->DispatchEventToExtension(utterance->extension_id(), event.Pass());
} }
...@@ -194,7 +197,7 @@ void TtsExtensionEngine::Pause(Utterance* utterance) { ...@@ -194,7 +197,7 @@ void TtsExtensionEngine::Pause(Utterance* utterance) {
scoped_ptr<base::ListValue> args(new base::ListValue()); scoped_ptr<base::ListValue> args(new base::ListValue());
scoped_ptr<extensions::Event> event(new extensions::Event( scoped_ptr<extensions::Event> event(new extensions::Event(
tts_engine_events::kOnPause, args.Pass())); tts_engine_events::kOnPause, args.Pass()));
Profile* profile = utterance->profile(); Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
event->restrict_to_browser_context = profile; event->restrict_to_browser_context = profile;
EventRouter* event_router = EventRouter::Get(profile); EventRouter* event_router = EventRouter::Get(profile);
std::string id = utterance->extension_id(); std::string id = utterance->extension_id();
...@@ -206,7 +209,7 @@ void TtsExtensionEngine::Resume(Utterance* utterance) { ...@@ -206,7 +209,7 @@ void TtsExtensionEngine::Resume(Utterance* utterance) {
scoped_ptr<base::ListValue> args(new base::ListValue()); scoped_ptr<base::ListValue> args(new base::ListValue());
scoped_ptr<extensions::Event> event(new extensions::Event( scoped_ptr<extensions::Event> event(new extensions::Event(
tts_engine_events::kOnResume, args.Pass())); tts_engine_events::kOnResume, args.Pass()));
Profile* profile = utterance->profile(); Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
event->restrict_to_browser_context = profile; event->restrict_to_browser_context = profile;
EventRouter* event_router = EventRouter::Get(profile); EventRouter* event_router = EventRouter::Get(profile);
std::string id = utterance->extension_id(); std::string id = utterance->extension_id();
...@@ -214,8 +217,10 @@ void TtsExtensionEngine::Resume(Utterance* utterance) { ...@@ -214,8 +217,10 @@ void TtsExtensionEngine::Resume(Utterance* utterance) {
WarnIfMissingPauseOrResumeListener(profile, event_router, id); WarnIfMissingPauseOrResumeListener(profile, event_router, id);
} }
bool TtsExtensionEngine::LoadBuiltInTtsExtension(Profile* profile) { bool TtsExtensionEngine::LoadBuiltInTtsExtension(
content::BrowserContext* browser_context) {
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
Profile* profile = Profile::FromBrowserContext(browser_context);
// Check to see if the engine was previously loaded. // Check to see if the engine was previously loaded.
if (TtsEngineExtensionObserver::GetInstance(profile)->SawExtensionLoad( if (TtsEngineExtensionObserver::GetInstance(profile)->SawExtensionLoad(
extension_misc::kSpeechSynthesisExtensionId, true)) { extension_misc::kSpeechSynthesisExtensionId, true)) {
......
...@@ -17,6 +17,10 @@ namespace base { ...@@ -17,6 +17,10 @@ namespace base {
class ListValue; class ListValue;
} }
namespace content {
class BrowserContext;
}
namespace extensions { namespace extensions {
class Extension; class Extension;
} }
...@@ -34,15 +38,15 @@ class TtsExtensionEngine : public TtsEngineDelegate { ...@@ -34,15 +38,15 @@ class TtsExtensionEngine : public TtsEngineDelegate {
static TtsExtensionEngine* GetInstance(); static TtsExtensionEngine* GetInstance();
// Overridden from TtsEngineDelegate: // Overridden from TtsEngineDelegate:
virtual void GetVoices(Profile* profile, virtual void GetVoices(content::BrowserContext* browser_context,
std::vector<VoiceData>* out_voices) OVERRIDE; std::vector<VoiceData>* out_voices) OVERRIDE;
virtual void Speak(Utterance* utterance, const VoiceData& voice) OVERRIDE; virtual void Speak(Utterance* utterance, const VoiceData& voice) OVERRIDE;
virtual void Stop(Utterance* utterance) OVERRIDE; virtual void Stop(Utterance* utterance) OVERRIDE;
virtual void Pause(Utterance* utterance) OVERRIDE; virtual void Pause(Utterance* utterance) OVERRIDE;
virtual void Resume(Utterance* utterance) OVERRIDE; virtual void Resume(Utterance* utterance) OVERRIDE;
virtual bool LoadBuiltInTtsExtension(Profile* profile) OVERRIDE; virtual bool LoadBuiltInTtsExtension(
content::BrowserContext* browser_context) OVERRIDE;
}; };
// Hidden/internal extension function used to allow TTS engine extensions // Hidden/internal extension function used to allow TTS engine extensions
// to send events back to the client that's calling tts.speak(). // to send events back to the client that's calling tts.speak().
class ExtensionTtsEngineSendTtsEventFunction : public SyncExtensionFunction { class ExtensionTtsEngineSendTtsEventFunction : public SyncExtensionFunction {
......
...@@ -127,9 +127,9 @@ void TtsExtensionEventHandler::OnTtsEvent(Utterance* utterance, ...@@ -127,9 +127,9 @@ void TtsExtensionEventHandler::OnTtsEvent(Utterance* utterance,
scoped_ptr<extensions::Event> event( scoped_ptr<extensions::Event> event(
new extensions::Event(events::kOnEvent, arguments.Pass())); new extensions::Event(events::kOnEvent, arguments.Pass()));
event->restrict_to_browser_context = utterance->profile(); event->restrict_to_browser_context = utterance->browser_context();
event->event_url = utterance->src_url(); event->event_url = utterance->src_url();
extensions::EventRouter::Get(utterance->profile()) extensions::EventRouter::Get(utterance->browser_context())
->DispatchEventToExtension(utterance->src_extension_id(), event.Pass()); ->DispatchEventToExtension(utterance->src_extension_id(), event.Pass());
if (utterance->finished()) if (utterance->finished())
......
...@@ -9,8 +9,6 @@ ...@@ -9,8 +9,6 @@
// an implementation of LoadBuiltInTtsExtension and dummy implementations of // an implementation of LoadBuiltInTtsExtension and dummy implementations of
// everything else. // everything else.
class Profile;
class TtsPlatformImplChromeOs : public TtsPlatformImpl { class TtsPlatformImplChromeOs : public TtsPlatformImpl {
public: public:
// TtsPlatformImpl overrides: // TtsPlatformImpl overrides:
...@@ -18,11 +16,12 @@ class TtsPlatformImplChromeOs : public TtsPlatformImpl { ...@@ -18,11 +16,12 @@ class TtsPlatformImplChromeOs : public TtsPlatformImpl {
return false; return false;
} }
virtual bool LoadBuiltInTtsExtension(Profile* profile) OVERRIDE { virtual bool LoadBuiltInTtsExtension(
content::BrowserContext* browser_context) OVERRIDE {
TtsEngineDelegate* tts_engine_delegate = TtsEngineDelegate* tts_engine_delegate =
TtsController::GetInstance()->GetTtsEngineDelegate(); TtsController::GetInstance()->GetTtsEngineDelegate();
if (tts_engine_delegate) if (tts_engine_delegate)
return tts_engine_delegate->LoadBuiltInTtsExtension(profile); return tts_engine_delegate->LoadBuiltInTtsExtension(browser_context);
return false; return false;
} }
......
...@@ -17,12 +17,15 @@ ...@@ -17,12 +17,15 @@
class Utterance; class Utterance;
class TtsPlatformImpl; class TtsPlatformImpl;
class Profile;
namespace base { namespace base {
class Value; class Value;
} }
namespace content {
class BrowserContext;
}
// Events sent back from the TTS engine indicating the progress. // Events sent back from the TTS engine indicating the progress.
enum TtsEventType { enum TtsEventType {
TTS_EVENT_START, TTS_EVENT_START,
...@@ -83,7 +86,7 @@ class TtsEngineDelegate { ...@@ -83,7 +86,7 @@ class TtsEngineDelegate {
virtual ~TtsEngineDelegate() {} virtual ~TtsEngineDelegate() {}
// Return a list of all available voices registered. // Return a list of all available voices registered.
virtual void GetVoices(Profile* profile, virtual void GetVoices(content::BrowserContext* browser_context,
std::vector<VoiceData>* out_voices) = 0; std::vector<VoiceData>* out_voices) = 0;
// Speak the given utterance by sending an event to the given TTS engine. // Speak the given utterance by sending an event to the given TTS engine.
...@@ -100,7 +103,8 @@ class TtsEngineDelegate { ...@@ -100,7 +103,8 @@ class TtsEngineDelegate {
virtual void Resume(Utterance* utterance) = 0; virtual void Resume(Utterance* utterance) = 0;
// Load the built-in component extension for ChromeOS. // Load the built-in component extension for ChromeOS.
virtual bool LoadBuiltInTtsExtension(Profile* profile) = 0; virtual bool LoadBuiltInTtsExtension(
content::BrowserContext* browser_context) = 0;
}; };
// Class that wants to receive events on utterances. // Class that wants to receive events on utterances.
...@@ -127,7 +131,7 @@ class Utterance { ...@@ -127,7 +131,7 @@ class Utterance {
// Construct an utterance given a profile and a completion task to call // Construct an utterance given a profile and a completion task to call
// when the utterance is done speaking. Before speaking this utterance, // when the utterance is done speaking. Before speaking this utterance,
// its other parameters like text, rate, pitch, etc. should all be set. // its other parameters like text, rate, pitch, etc. should all be set.
explicit Utterance(Profile* profile); explicit Utterance(content::BrowserContext* browser_context);
~Utterance(); ~Utterance();
// Sends an event to the delegate. If the event type is TTS_EVENT_END // Sends an event to the delegate. If the event type is TTS_EVENT_END
...@@ -211,13 +215,13 @@ class Utterance { ...@@ -211,13 +215,13 @@ class Utterance {
} }
// Getters and setters for internal state. // Getters and setters for internal state.
Profile* profile() const { return profile_; } content::BrowserContext* browser_context() const { return browser_context_; }
int id() const { return id_; } int id() const { return id_; }
bool finished() const { return finished_; } bool finished() const { return finished_; }
private: private:
// The profile that initiated this utterance. // The BrowserContext that initiated this utterance.
Profile* profile_; content::BrowserContext* browser_context_;
// The extension ID of the extension providing TTS for this utterance, or // The extension ID of the extension providing TTS for this utterance, or
// empty if native TTS is being used. // empty if native TTS is being used.
...@@ -301,13 +305,13 @@ class TtsController { ...@@ -301,13 +305,13 @@ class TtsController {
// trigger finishing the current utterance and starting the next one, if // trigger finishing the current utterance and starting the next one, if
// any. // any.
virtual void OnTtsEvent(int utterance_id, virtual void OnTtsEvent(int utterance_id,
TtsEventType event_type, TtsEventType event_type,
int char_index, int char_index,
const std::string& error_message) = 0; const std::string& error_message) = 0;
// Return a list of all available voices, including the native voice, // Return a list of all available voices, including the native voice,
// if supported, and all voices registered by extensions. // if supported, and all voices registered by extensions.
virtual void GetVoices(Profile* profile, virtual void GetVoices(content::BrowserContext* browser_context,
std::vector<VoiceData>* out_voices) = 0; std::vector<VoiceData>* out_voices) = 0;
// Called by the extension system or platform implementation when the // Called by the extension system or platform implementation when the
......
...@@ -65,8 +65,8 @@ VoiceData::~VoiceData() {} ...@@ -65,8 +65,8 @@ VoiceData::~VoiceData() {}
// static // static
int Utterance::next_utterance_id_ = 0; int Utterance::next_utterance_id_ = 0;
Utterance::Utterance(Profile* profile) Utterance::Utterance(content::BrowserContext* browser_context)
: profile_(profile), : browser_context_(browser_context),
id_(next_utterance_id_++), id_(next_utterance_id_++),
src_id_(-1), src_id_(-1),
gender_(TTS_GENDER_NONE), gender_(TTS_GENDER_NONE),
...@@ -153,11 +153,11 @@ void TtsControllerImpl::SpeakNow(Utterance* utterance) { ...@@ -153,11 +153,11 @@ void TtsControllerImpl::SpeakNow(Utterance* utterance) {
// Ensure we have all built-in voices loaded. This is a no-op if already // Ensure we have all built-in voices loaded. This is a no-op if already
// loaded. // loaded.
bool loaded_built_in = bool loaded_built_in =
GetPlatformImpl()->LoadBuiltInTtsExtension(utterance->profile()); GetPlatformImpl()->LoadBuiltInTtsExtension(utterance->browser_context());
// Get all available voices and try to find a matching voice. // Get all available voices and try to find a matching voice.
std::vector<VoiceData> voices; std::vector<VoiceData> voices;
GetVoices(utterance->profile(), &voices); GetVoices(utterance->browser_context(), &voices);
int index = GetMatchingVoice(utterance, voices); int index = GetMatchingVoice(utterance, voices);
VoiceData voice; VoiceData voice;
...@@ -305,18 +305,18 @@ void TtsControllerImpl::OnTtsEvent(int utterance_id, ...@@ -305,18 +305,18 @@ void TtsControllerImpl::OnTtsEvent(int utterance_id,
} }
} }
void TtsControllerImpl::GetVoices(Profile* profile, void TtsControllerImpl::GetVoices(content::BrowserContext* browser_context,
std::vector<VoiceData>* out_voices) { std::vector<VoiceData>* out_voices) {
#if !defined(OS_ANDROID) #if !defined(OS_ANDROID)
if (profile && tts_engine_delegate_) if (browser_context && tts_engine_delegate_)
tts_engine_delegate_->GetVoices(profile, out_voices); tts_engine_delegate_->GetVoices(browser_context, out_voices);
#endif #endif
TtsPlatformImpl* platform_impl = GetPlatformImpl(); TtsPlatformImpl* platform_impl = GetPlatformImpl();
if (platform_impl) { if (platform_impl) {
// Ensure we have all built-in voices loaded. This is a no-op if already // Ensure we have all built-in voices loaded. This is a no-op if already
// loaded. // loaded.
platform_impl->LoadBuiltInTtsExtension(profile); platform_impl->LoadBuiltInTtsExtension(browser_context);
if (platform_impl->PlatformImplAvailable()) if (platform_impl->PlatformImplAvailable())
platform_impl->GetVoices(out_voices); platform_impl->GetVoices(out_voices);
} }
......
...@@ -16,6 +16,10 @@ ...@@ -16,6 +16,10 @@
#include "chrome/browser/speech/tts_controller.h" #include "chrome/browser/speech/tts_controller.h"
#include "url/gurl.h" #include "url/gurl.h"
namespace content {
class BrowserContext;
}
// Singleton class that manages text-to-speech for the TTS and TTS engine // Singleton class that manages text-to-speech for the TTS and TTS engine
// extension APIs, maintaining a queue of pending utterances and keeping // extension APIs, maintaining a queue of pending utterances and keeping
// track of all state. // track of all state.
...@@ -34,7 +38,7 @@ class TtsControllerImpl : public TtsController { ...@@ -34,7 +38,7 @@ class TtsControllerImpl : public TtsController {
TtsEventType event_type, TtsEventType event_type,
int char_index, int char_index,
const std::string& error_message) OVERRIDE; const std::string& error_message) OVERRIDE;
virtual void GetVoices(Profile* profile, virtual void GetVoices(content::BrowserContext* browser_context,
std::vector<VoiceData>* out_voices) OVERRIDE; std::vector<VoiceData>* out_voices) OVERRIDE;
virtual void VoicesChanged() OVERRIDE; virtual void VoicesChanged() OVERRIDE;
virtual void AddVoicesChangedDelegate( virtual void AddVoicesChangedDelegate(
......
...@@ -11,10 +11,11 @@ ...@@ -11,10 +11,11 @@
using content::BrowserThread; using content::BrowserThread;
TtsMessageFilter::TtsMessageFilter(int render_process_id, Profile* profile) TtsMessageFilter::TtsMessageFilter(int render_process_id,
content::BrowserContext* browser_context)
: BrowserMessageFilter(TtsMsgStart), : BrowserMessageFilter(TtsMsgStart),
render_process_id_(render_process_id), render_process_id_(render_process_id),
profile_(profile) { browser_context_(browser_context) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TtsController::GetInstance()->AddVoicesChangedDelegate(this); TtsController::GetInstance()->AddVoicesChangedDelegate(this);
} }
...@@ -59,7 +60,7 @@ void TtsMessageFilter::OnInitializeVoiceList() { ...@@ -59,7 +60,7 @@ void TtsMessageFilter::OnInitializeVoiceList() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TtsController* tts_controller = TtsController::GetInstance(); TtsController* tts_controller = TtsController::GetInstance();
std::vector<VoiceData> voices; std::vector<VoiceData> voices;
tts_controller->GetVoices(profile_, &voices); tts_controller->GetVoices(browser_context_, &voices);
std::vector<TtsVoice> out_voices; std::vector<TtsVoice> out_voices;
out_voices.resize(voices.size()); out_voices.resize(voices.size());
...@@ -76,7 +77,8 @@ void TtsMessageFilter::OnInitializeVoiceList() { ...@@ -76,7 +77,8 @@ void TtsMessageFilter::OnInitializeVoiceList() {
void TtsMessageFilter::OnSpeak(const TtsUtteranceRequest& request) { void TtsMessageFilter::OnSpeak(const TtsUtteranceRequest& request) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
scoped_ptr<Utterance> utterance(new Utterance(profile_));
scoped_ptr<Utterance> utterance(new Utterance(browser_context_));
utterance->set_src_id(request.id); utterance->set_src_id(request.id);
utterance->set_text(request.text); utterance->set_text(request.text);
utterance->set_lang(request.lang); utterance->set_lang(request.lang);
......
...@@ -10,7 +10,9 @@ ...@@ -10,7 +10,9 @@
#include "chrome/common/tts_messages.h" #include "chrome/common/tts_messages.h"
#include "content/public/browser/browser_message_filter.h" #include "content/public/browser/browser_message_filter.h"
class Profile; namespace content {
class BrowserContext;
}
class TtsMessageFilter class TtsMessageFilter
: public content::BrowserMessageFilter, : public content::BrowserMessageFilter,
...@@ -18,7 +20,8 @@ class TtsMessageFilter ...@@ -18,7 +20,8 @@ class TtsMessageFilter
public VoicesChangedDelegate, public VoicesChangedDelegate,
public base::SupportsWeakPtr<TtsMessageFilter> { public base::SupportsWeakPtr<TtsMessageFilter> {
public: public:
TtsMessageFilter(int render_process_id, Profile* profile); explicit TtsMessageFilter(int render_process_id,
content::BrowserContext* browser_context);
// content::BrowserMessageFilter implementation. // content::BrowserMessageFilter implementation.
virtual void OverrideThreadForMessage( virtual void OverrideThreadForMessage(
...@@ -52,7 +55,7 @@ class TtsMessageFilter ...@@ -52,7 +55,7 @@ class TtsMessageFilter
void OnChannelClosingInUIThread(); void OnChannelClosingInUIThread();
int render_process_id_; int render_process_id_;
Profile* profile_; content::BrowserContext* browser_context_;
DISALLOW_COPY_AND_ASSIGN(TtsMessageFilter); DISALLOW_COPY_AND_ASSIGN(TtsMessageFilter);
}; };
......
...@@ -6,7 +6,8 @@ ...@@ -6,7 +6,8 @@
#include <string> #include <string>
bool TtsPlatformImpl::LoadBuiltInTtsExtension(Profile* profile) { bool TtsPlatformImpl::LoadBuiltInTtsExtension(
content::BrowserContext* browser_context) {
return false; return false;
} }
......
...@@ -23,7 +23,8 @@ class TtsPlatformImpl { ...@@ -23,7 +23,8 @@ class TtsPlatformImpl {
// false if it's already loaded or if there's no extension to load. // false if it's already loaded or if there's no extension to load.
// Will call TtsController::RetrySpeakingQueuedUtterances when // Will call TtsController::RetrySpeakingQueuedUtterances when
// the extension finishes loading. // the extension finishes loading.
virtual bool LoadBuiltInTtsExtension(Profile* profile); virtual bool LoadBuiltInTtsExtension(
content::BrowserContext* browser_context);
// Speak the given utterance with the given parameters if possible, // Speak the given utterance with the given parameters if possible,
// and return true on success. Utterance will always be nonempty. // and return true on success. Utterance will always be nonempty.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment