Bug 1254378 - Implement nsISynthVoiceRegistry.notifyVoicesChanged. r=smaug
MozReview-Commit-ID: DIRcnw5Rfnc
--- a/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.mm
+++ b/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.mm
@@ -245,16 +245,19 @@ RegisterVoicesRunnable::Run()
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
if (voice.mIsDefault) {
registry->SetDefaultVoice(voice.mUri, true);
}
}
+
+ registry->NotifyVoicesChanged();
+
return NS_OK;
}
class EnumVoicesRunnable final : public nsRunnable
{
public:
explicit EnumVoicesRunnable(OSXSpeechSynthesizerService* aSpeechService)
: mSpeechService(aSpeechService)
--- a/dom/media/webspeech/synth/ipc/PSpeechSynthesis.ipdl
+++ b/dom/media/webspeech/synth/ipc/PSpeechSynthesis.ipdl
@@ -28,16 +28,18 @@ child:
async VoiceAdded(RemoteVoice aVoice);
async VoiceRemoved(nsString aUri);
async SetDefaultVoice(nsString aUri, bool aIsDefault);
async IsSpeakingChanged(bool aIsSpeaking);
+ async NotifyVoicesChanged();
+
parent:
async __delete__();
async PSpeechSynthesisRequest(nsString aText, nsString aUri, nsString aLang,
float aVolume, float aRate, float aPitch);
sync ReadVoicesAndState() returns (RemoteVoice[] aVoices,
nsString[] aDefaults, bool aIsSpeaking);
--- a/dom/media/webspeech/synth/ipc/SpeechSynthesisChild.cpp
+++ b/dom/media/webspeech/synth/ipc/SpeechSynthesisChild.cpp
@@ -42,16 +42,23 @@ SpeechSynthesisChild::RecvSetDefaultVoic
bool
SpeechSynthesisChild::RecvIsSpeakingChanged(const bool& aIsSpeaking)
{
nsSynthVoiceRegistry::RecvIsSpeakingChanged(aIsSpeaking);
return true;
}
+bool
+SpeechSynthesisChild::RecvNotifyVoicesChanged()
+{
+ nsSynthVoiceRegistry::RecvNotifyVoicesChanged();
+ return true;
+}
+
PSpeechSynthesisRequestChild*
SpeechSynthesisChild::AllocPSpeechSynthesisRequestChild(const nsString& aText,
const nsString& aLang,
const nsString& aUri,
const float& aVolume,
const float& aRate,
const float& aPitch)
{
--- a/dom/media/webspeech/synth/ipc/SpeechSynthesisChild.h
+++ b/dom/media/webspeech/synth/ipc/SpeechSynthesisChild.h
@@ -25,16 +25,18 @@ public:
bool RecvVoiceAdded(const RemoteVoice& aVoice) override;
bool RecvVoiceRemoved(const nsString& aUri) override;
bool RecvSetDefaultVoice(const nsString& aUri, const bool& aIsDefault) override;
bool RecvIsSpeakingChanged(const bool& aIsSpeaking) override;
+ bool RecvNotifyVoicesChanged() override;
+
protected:
SpeechSynthesisChild();
virtual ~SpeechSynthesisChild();
PSpeechSynthesisRequestChild* AllocPSpeechSynthesisRequestChild(const nsString& aLang,
const nsString& aUri,
const nsString& aText,
const float& aVolume,
--- a/dom/media/webspeech/synth/nsISynthVoiceRegistry.idl
+++ b/dom/media/webspeech/synth/nsISynthVoiceRegistry.idl
@@ -2,17 +2,17 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface nsISpeechService;
-[scriptable, builtinclass, uuid(dac09c3a-156e-4025-a4ab-bc88b0ea92e7)]
+[scriptable, builtinclass, uuid(5d7a0b38-77e5-4ee5-897c-ce5db9b85d44)]
interface nsISynthVoiceRegistry : nsISupports
{
/**
* Register a speech synthesis voice.
*
* @param aService the service that provides this voice.
* @param aUri a unique identifier for this voice.
* @param aName human-readable name for this voice.
@@ -28,16 +28,22 @@ interface nsISynthVoiceRegistry : nsISup
* Remove a speech synthesis voice.
*
* @param aService the service that was used to add the voice.
* @param aUri a unique identifier of an existing voice.
*/
void removeVoice(in nsISpeechService aService, in DOMString aUri);
/**
+ * Notify content of voice availability changes. This allows content
+ * to be notified of voice catalog changes in real time.
+ */
+ void notifyVoicesChanged();
+
+ /**
* Set a voice as default.
*
* @param aUri a unique identifier of an existing voice.
* @param aIsDefault true if this voice should be toggled as default.
*/
void setDefaultVoice(in DOMString aUri, in boolean aIsDefault);
readonly attribute uint32_t voiceCount;
--- a/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
+++ b/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
@@ -281,16 +281,27 @@ nsSynthVoiceRegistry::RecvIsSpeakingChan
// speaking state on construction.
if(!gSynthVoiceRegistry) {
return;
}
gSynthVoiceRegistry->mIsSpeaking = aIsSpeaking;
}
+void
+nsSynthVoiceRegistry::RecvNotifyVoicesChanged()
+{
+ // If we dont have a local instance of the registry yet, we don't care.
+ if(!gSynthVoiceRegistry) {
+ return;
+ }
+
+ gSynthVoiceRegistry->NotifyVoicesChanged();
+}
+
NS_IMETHODIMP
nsSynthVoiceRegistry::AddVoice(nsISpeechService* aService,
const nsAString& aUri,
const nsAString& aName,
const nsAString& aLang,
bool aLocalService,
bool aQueuesUtterances)
{
@@ -352,16 +363,37 @@ nsSynthVoiceRegistry::RemoveVoice(nsISpe
for (uint32_t i = 0; i < ssplist.Length(); ++i)
Unused << ssplist[i]->SendVoiceRemoved(nsString(aUri));
return NS_OK;
}
NS_IMETHODIMP
+nsSynthVoiceRegistry::NotifyVoicesChanged()
+{
+ if (XRE_IsParentProcess()) {
+ nsTArray<SpeechSynthesisParent*> ssplist;
+ GetAllSpeechSynthActors(ssplist);
+
+ for (uint32_t i = 0; i < ssplist.Length(); ++i)
+ Unused << ssplist[i]->SendNotifyVoicesChanged();
+ }
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if(NS_WARN_IF(!(obs))) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ obs->NotifyObservers(nullptr, "synth-voices-changed", nullptr);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
nsSynthVoiceRegistry::SetDefaultVoice(const nsAString& aUri,
bool aIsDefault)
{
bool found = false;
VoiceData* retval = mUriVoiceMap.GetWeak(aUri, &found);
if(NS_WARN_IF(!(found))) {
return NS_ERROR_NOT_AVAILABLE;
}
--- a/dom/media/webspeech/synth/nsSynthVoiceRegistry.h
+++ b/dom/media/webspeech/synth/nsSynthVoiceRegistry.h
@@ -59,16 +59,18 @@ public:
static void RecvRemoveVoice(const nsAString& aUri);
static void RecvAddVoice(const RemoteVoice& aVoice);
static void RecvSetDefaultVoice(const nsAString& aUri, bool aIsDefault);
static void RecvIsSpeakingChanged(bool aIsSpeaking);
+ static void RecvNotifyVoicesChanged();
+
static void Shutdown();
private:
virtual ~nsSynthVoiceRegistry();
VoiceData* FindBestMatch(const nsAString& aUri, const nsAString& lang);
bool FindVoiceByLang(const nsAString& aLang, VoiceData** aRetval);
--- a/dom/media/webspeech/synth/speechd/SpeechDispatcherService.cpp
+++ b/dom/media/webspeech/synth/speechd/SpeechDispatcherService.cpp
@@ -417,17 +417,17 @@ SpeechDispatcherService::Setup()
//mInitialized = true;
}
// private methods
void
SpeechDispatcherService::RegisterVoices()
{
- nsSynthVoiceRegistry* registry = nsSynthVoiceRegistry::GetInstance();
+ RefPtr<nsSynthVoiceRegistry> registry = nsSynthVoiceRegistry::GetInstance();
for (auto iter = mVoices.Iter(); !iter.Done(); iter.Next()) {
RefPtr<SpeechDispatcherVoice>& voice = iter.Data();
// This service can only speak one utterance at a time, so we set
// aQueuesUtterances to true in order to track global state and schedule
// access to this service.
DebugOnly<nsresult> rv =
registry->AddVoice(this, iter.Key(), voice->mName, voice->mLanguage,
@@ -435,16 +435,18 @@ SpeechDispatcherService::RegisterVoices(
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to add voice");
}
mInitThread->Shutdown();
mInitThread = nullptr;
mInitialized = true;
+
+ registry->NotifyVoicesChanged();
}
// nsIObserver
NS_IMETHODIMP
SpeechDispatcherService::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
--- a/dom/media/webspeech/synth/windows/SapiService.cpp
+++ b/dom/media/webspeech/synth/windows/SapiService.cpp
@@ -308,16 +308,18 @@ SapiService::RegisterVoices()
CoTaskMemFree(description);
if (NS_FAILED(rv)) {
continue;
}
mVoices.Put(uri, voiceToken);
}
+ registry->NotifyVoicesChanged();
+
return true;
}
NS_IMETHODIMP
SapiService::Speak(const nsAString& aText, const nsAString& aUri,
float aVolume, float aRate, float aPitch,
nsISpeechTask* aTask)
{