LCOV - code coverage report
Current view: top level - dom/media/webspeech/synth/speechd - SpeechDispatcherService.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 2 204 1.0 %
Date: 2017-07-14 16:53:18 Functions: 2 43 4.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "SpeechDispatcherService.h"
       8             : 
       9             : #include "mozilla/dom/nsSpeechTask.h"
      10             : #include "mozilla/dom/nsSynthVoiceRegistry.h"
      11             : #include "mozilla/Preferences.h"
      12             : #include "nsEscape.h"
      13             : #include "nsISupports.h"
      14             : #include "nsPrintfCString.h"
      15             : #include "nsReadableUtils.h"
      16             : #include "nsServiceManagerUtils.h"
      17             : #include "nsThreadUtils.h"
      18             : #include "prlink.h"
      19             : 
      20             : #include <math.h>
      21             : #include <stdlib.h>
      22             : 
      23             : #define URI_PREFIX "urn:moz-tts:speechd:"
      24             : 
      25             : #define MAX_RATE static_cast<float>(2.5)
      26             : #define MIN_RATE static_cast<float>(0.5)
      27             : 
      28             : // Some structures for libspeechd
      29             : typedef enum {
      30             :   SPD_EVENT_BEGIN,
      31             :   SPD_EVENT_END,
      32             :   SPD_EVENT_INDEX_MARK,
      33             :   SPD_EVENT_CANCEL,
      34             :   SPD_EVENT_PAUSE,
      35             :   SPD_EVENT_RESUME
      36             : } SPDNotificationType;
      37             : 
      38             : typedef enum {
      39             :   SPD_BEGIN = 1,
      40             :   SPD_END = 2,
      41             :   SPD_INDEX_MARKS = 4,
      42             :   SPD_CANCEL = 8,
      43             :   SPD_PAUSE = 16,
      44             :   SPD_RESUME = 32,
      45             : 
      46             :   SPD_ALL = 0x3f
      47             : } SPDNotification;
      48             : 
      49             : typedef enum {
      50             :   SPD_MODE_SINGLE = 0,
      51             :   SPD_MODE_THREADED = 1
      52             : } SPDConnectionMode;
      53             : 
      54             : typedef void (*SPDCallback) (size_t msg_id, size_t client_id,
      55             :                              SPDNotificationType state);
      56             : 
      57             : typedef void (*SPDCallbackIM) (size_t msg_id, size_t client_id,
      58             :                                SPDNotificationType state, char* index_mark);
      59             : 
      60             : struct SPDConnection
      61             : {
      62             :   SPDCallback callback_begin;
      63             :   SPDCallback callback_end;
      64             :   SPDCallback callback_cancel;
      65             :   SPDCallback callback_pause;
      66             :   SPDCallback callback_resume;
      67             :   SPDCallbackIM callback_im;
      68             : 
      69             :   /* partial, more private fields in structure */
      70             : };
      71             : 
      72             : struct SPDVoice
      73             : {
      74             :   char* name;
      75             :   char* language;
      76             :   char* variant;
      77             : };
      78             : 
      79             : typedef enum {
      80             :   SPD_IMPORTANT = 1,
      81             :   SPD_MESSAGE = 2,
      82             :   SPD_TEXT = 3,
      83             :   SPD_NOTIFICATION = 4,
      84             :   SPD_PROGRESS = 5
      85             : } SPDPriority;
      86             : 
      87             : #define SPEECHD_FUNCTIONS \
      88             :   FUNC(spd_open, SPDConnection*, (const char*, const char*, const char*, SPDConnectionMode)) \
      89             :   FUNC(spd_close, void, (SPDConnection*)) \
      90             :   FUNC(spd_list_synthesis_voices, SPDVoice**, (SPDConnection*)) \
      91             :   FUNC(spd_say, int, (SPDConnection*, SPDPriority, const char*)) \
      92             :   FUNC(spd_cancel, int, (SPDConnection*)) \
      93             :   FUNC(spd_set_volume, int, (SPDConnection*, int)) \
      94             :   FUNC(spd_set_voice_rate, int, (SPDConnection*, int)) \
      95             :   FUNC(spd_set_voice_pitch, int, (SPDConnection*, int)) \
      96             :   FUNC(spd_set_synthesis_voice, int, (SPDConnection*, const char*)) \
      97             :   FUNC(spd_set_notification_on, int, (SPDConnection*, SPDNotification))
      98             : 
      99             : #define FUNC(name, type, params) \
     100             :   typedef type (*_##name##_fn) params; \
     101             :   static _##name##_fn _##name;
     102             : 
     103             : SPEECHD_FUNCTIONS
     104             : 
     105             : #undef FUNC
     106             : 
     107             : #define spd_open _spd_open
     108             : #define spd_close _spd_close
     109             : #define spd_list_synthesis_voices _spd_list_synthesis_voices
     110             : #define spd_say _spd_say
     111             : #define spd_cancel _spd_cancel
     112             : #define spd_set_volume _spd_set_volume
     113             : #define spd_set_voice_rate _spd_set_voice_rate
     114             : #define spd_set_voice_pitch _spd_set_voice_pitch
     115             : #define spd_set_synthesis_voice _spd_set_synthesis_voice
     116             : #define spd_set_notification_on _spd_set_notification_on
     117             : 
     118             : static PRLibrary* speechdLib = nullptr;
     119             : 
     120             : typedef void (*nsSpeechDispatcherFunc)();
     121             : struct nsSpeechDispatcherDynamicFunction
     122             : {
     123             :   const char* functionName;
     124             :   nsSpeechDispatcherFunc* function;
     125             : };
     126             : 
     127             : namespace mozilla {
     128             : namespace dom {
     129             : 
     130           3 : StaticRefPtr<SpeechDispatcherService> SpeechDispatcherService::sSingleton;
     131             : 
     132             : class SpeechDispatcherVoice
     133             : {
     134             : public:
     135             : 
     136           0 :   SpeechDispatcherVoice(const nsAString& aName, const nsAString& aLanguage)
     137           0 :     : mName(aName), mLanguage(aLanguage) {}
     138             : 
     139           0 :   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SpeechDispatcherVoice)
     140             : 
     141             :   // Voice name
     142             :   nsString mName;
     143             : 
     144             :   // Voice language, in BCP-47 syntax
     145             :   nsString mLanguage;
     146             : 
     147             : private:
     148           0 :   ~SpeechDispatcherVoice() {}
     149             : };
     150             : 
     151             : 
     152             : class SpeechDispatcherCallback final : public nsISpeechTaskCallback
     153             : {
     154             : public:
     155           0 :   SpeechDispatcherCallback(nsISpeechTask* aTask, SpeechDispatcherService* aService)
     156           0 :     : mTask(aTask)
     157           0 :     , mService(aService) {}
     158             : 
     159             :   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     160           0 :   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(SpeechDispatcherCallback, nsISpeechTaskCallback)
     161             : 
     162             :   NS_DECL_NSISPEECHTASKCALLBACK
     163             : 
     164             :   bool OnSpeechEvent(SPDNotificationType state);
     165             : 
     166             : private:
     167           0 :   ~SpeechDispatcherCallback() { }
     168             : 
     169             :   // This pointer is used to dispatch events
     170             :   nsCOMPtr<nsISpeechTask> mTask;
     171             : 
     172             :   // By holding a strong reference to the service we guarantee that it won't be
     173             :   // destroyed before this runnable.
     174             :   RefPtr<SpeechDispatcherService> mService;
     175             : 
     176             :   TimeStamp mStartTime;
     177             : };
     178             : 
     179           0 : NS_IMPL_CYCLE_COLLECTION(SpeechDispatcherCallback, mTask);
     180             : 
     181           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SpeechDispatcherCallback)
     182           0 :   NS_INTERFACE_MAP_ENTRY(nsISpeechTaskCallback)
     183           0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISpeechTaskCallback)
     184           0 : NS_INTERFACE_MAP_END
     185             : 
     186           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(SpeechDispatcherCallback)
     187           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(SpeechDispatcherCallback)
     188             : 
     189             : NS_IMETHODIMP
     190           0 : SpeechDispatcherCallback::OnPause()
     191             : {
     192             :   // XXX: Speech dispatcher does not pause immediately, but waits for the speech
     193             :   // to reach an index mark so that it could resume from that offset.
     194             :   // There is no support for word or sentence boundaries, so index marks would
     195             :   // only occur in explicit SSML marks, and we don't support that yet.
     196             :   // What in actuality happens, is that if you call spd_pause(), it will speak
     197             :   // the utterance in its entirety, dispatch an end event, and then put speechd
     198             :   // in a 'paused' state. Since it is after the utterance ended, we don't get
     199             :   // that state change, and our speech api is in an unrecoverable state.
     200             :   // So, since it is useless anyway, I am not implementing pause.
     201           0 :   return NS_OK;
     202             : }
     203             : 
     204             : NS_IMETHODIMP
     205           0 : SpeechDispatcherCallback::OnResume()
     206             : {
     207             :   // XXX: Unsupported, see OnPause().
     208           0 :   return NS_OK;
     209             : }
     210             : 
     211             : NS_IMETHODIMP
     212           0 : SpeechDispatcherCallback::OnCancel()
     213             : {
     214           0 :   if (spd_cancel(mService->mSpeechdClient) < 0) {
     215           0 :     return NS_ERROR_FAILURE;
     216             :   }
     217             : 
     218           0 :   return NS_OK;
     219             : }
     220             : 
     221             : NS_IMETHODIMP
     222           0 : SpeechDispatcherCallback::OnVolumeChanged(float aVolume)
     223             : {
     224             :   // XXX: This currently does not change the volume mid-utterance, but it
     225             :   // doesn't do anything bad either. So we could put this here with the hopes
     226             :   // that speechd supports this in the future.
     227           0 :   if (spd_set_volume(mService->mSpeechdClient, static_cast<int>(aVolume * 100)) < 0) {
     228           0 :     return NS_ERROR_FAILURE;
     229             :   }
     230             : 
     231           0 :   return NS_OK;
     232             : }
     233             : 
     234             : bool
     235           0 : SpeechDispatcherCallback::OnSpeechEvent(SPDNotificationType state)
     236             : {
     237           0 :   bool remove = false;
     238             : 
     239           0 :   switch (state) {
     240             :     case SPD_EVENT_BEGIN:
     241           0 :       mStartTime = TimeStamp::Now();
     242           0 :       mTask->DispatchStart();
     243           0 :       break;
     244             : 
     245             :     case SPD_EVENT_PAUSE:
     246           0 :       mTask->DispatchPause((TimeStamp::Now() - mStartTime).ToSeconds(), 0);
     247           0 :       break;
     248             : 
     249             :     case SPD_EVENT_RESUME:
     250           0 :       mTask->DispatchResume((TimeStamp::Now() - mStartTime).ToSeconds(), 0);
     251           0 :       break;
     252             : 
     253             :     case SPD_EVENT_CANCEL:
     254             :     case SPD_EVENT_END:
     255           0 :       mTask->DispatchEnd((TimeStamp::Now() - mStartTime).ToSeconds(), 0);
     256           0 :       remove = true;
     257           0 :       break;
     258             : 
     259             :     case SPD_EVENT_INDEX_MARK:
     260             :       // Not yet supported
     261           0 :       break;
     262             : 
     263             :     default:
     264           0 :       break;
     265             :   }
     266             : 
     267           0 :   return remove;
     268             : }
     269             : 
     270             : static void
     271           0 : speechd_cb(size_t msg_id, size_t client_id, SPDNotificationType state)
     272             : {
     273           0 :   SpeechDispatcherService* service = SpeechDispatcherService::GetInstance(false);
     274             : 
     275           0 :   if (service) {
     276           0 :     NS_DispatchToMainThread(NewRunnableMethod<uint32_t, SPDNotificationType>(
     277             :       "dom::SpeechDispatcherService::EventNotify",
     278             :       service,
     279             :       &SpeechDispatcherService::EventNotify,
     280           0 :       static_cast<uint32_t>(msg_id),
     281           0 :       state));
     282             :   }
     283           0 : }
     284             : 
     285             : 
     286           0 : NS_INTERFACE_MAP_BEGIN(SpeechDispatcherService)
     287           0 :   NS_INTERFACE_MAP_ENTRY(nsISpeechService)
     288           0 :   NS_INTERFACE_MAP_ENTRY(nsIObserver)
     289           0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
     290           0 : NS_INTERFACE_MAP_END
     291             : 
     292           0 : NS_IMPL_ADDREF(SpeechDispatcherService)
     293           0 : NS_IMPL_RELEASE(SpeechDispatcherService)
     294             : 
     295           0 : SpeechDispatcherService::SpeechDispatcherService()
     296             :   : mInitialized(false)
     297           0 :   , mSpeechdClient(nullptr)
     298             : {
     299           0 : }
     300             : 
     301             : void
     302           0 : SpeechDispatcherService::Init()
     303             : {
     304           0 :   if (!Preferences::GetBool("media.webspeech.synth.enabled") ||
     305           0 :       Preferences::GetBool("media.webspeech.synth.test")) {
     306           0 :     return;
     307             :   }
     308             : 
     309             :   // While speech dispatcher has a "threaded" mode, only spd_say() is async.
     310             :   // Since synchronous socket i/o could impact startup time, we do
     311             :   // initialization in a separate thread.
     312           0 :   DebugOnly<nsresult> rv = NS_NewNamedThread("speechd init",
     313           0 :                                              getter_AddRefs(mInitThread));
     314           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
     315           0 :   rv = mInitThread->Dispatch(
     316           0 :     NewRunnableMethod("dom::SpeechDispatcherService::Setup",
     317             :                       this,
     318             :                       &SpeechDispatcherService::Setup),
     319           0 :     NS_DISPATCH_NORMAL);
     320           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
     321             : }
     322             : 
     323           0 : SpeechDispatcherService::~SpeechDispatcherService()
     324             : {
     325           0 :   if (mInitThread) {
     326           0 :     mInitThread->Shutdown();
     327             :   }
     328             : 
     329           0 :   if (mSpeechdClient) {
     330           0 :     spd_close(mSpeechdClient);
     331             :   }
     332           0 : }
     333             : 
     334             : void
     335           0 : SpeechDispatcherService::Setup()
     336             : {
     337             : #define FUNC(name, type, params) { #name, (nsSpeechDispatcherFunc *)&_##name },
     338             :   static const nsSpeechDispatcherDynamicFunction kSpeechDispatcherSymbols[] = {
     339             :     SPEECHD_FUNCTIONS
     340             :   };
     341             : #undef FUNC
     342             : 
     343           0 :   MOZ_ASSERT(!mInitialized);
     344             : 
     345           0 :   speechdLib = PR_LoadLibrary("libspeechd.so.2");
     346             : 
     347           0 :   if (!speechdLib) {
     348           0 :     NS_WARNING("Failed to load speechd library");
     349           0 :     return;
     350             :   }
     351             : 
     352           0 :   if (!PR_FindFunctionSymbol(speechdLib, "spd_get_volume")) {
     353             :     // There is no version getter function, so we rely on a symbol that was
     354             :     // introduced in release 0.8.2 in order to check for ABI compatibility.
     355           0 :     NS_WARNING("Unsupported version of speechd detected");
     356           0 :     return;
     357             :   }
     358             : 
     359           0 :   for (uint32_t i = 0; i < ArrayLength(kSpeechDispatcherSymbols); i++) {
     360           0 :     *kSpeechDispatcherSymbols[i].function =
     361           0 :       PR_FindFunctionSymbol(speechdLib, kSpeechDispatcherSymbols[i].functionName);
     362             : 
     363           0 :     if (!*kSpeechDispatcherSymbols[i].function) {
     364           0 :       NS_WARNING(nsPrintfCString("Failed to find speechd symbol for'%s'",
     365           0 :                                  kSpeechDispatcherSymbols[i].functionName).get());
     366           0 :       return;
     367             :     }
     368             :   }
     369             : 
     370           0 :   mSpeechdClient = spd_open("firefox", "web speech api", "who", SPD_MODE_THREADED);
     371           0 :   if (!mSpeechdClient) {
     372           0 :     NS_WARNING("Failed to call spd_open");
     373           0 :     return;
     374             :   }
     375             : 
     376             :   // Get all the voices from sapi and register in the SynthVoiceRegistry
     377           0 :   SPDVoice** list = spd_list_synthesis_voices(mSpeechdClient);
     378             : 
     379           0 :   mSpeechdClient->callback_begin = speechd_cb;
     380           0 :   mSpeechdClient->callback_end = speechd_cb;
     381           0 :   mSpeechdClient->callback_cancel = speechd_cb;
     382           0 :   mSpeechdClient->callback_pause = speechd_cb;
     383           0 :   mSpeechdClient->callback_resume = speechd_cb;
     384             : 
     385           0 :   spd_set_notification_on(mSpeechdClient, SPD_BEGIN);
     386           0 :   spd_set_notification_on(mSpeechdClient, SPD_END);
     387           0 :   spd_set_notification_on(mSpeechdClient, SPD_CANCEL);
     388             : 
     389           0 :   if (list != NULL) {
     390           0 :     for (int i = 0; list[i]; i++) {
     391           0 :       nsAutoString uri;
     392             : 
     393           0 :       uri.AssignLiteral(URI_PREFIX);
     394           0 :       nsAutoCString name;
     395           0 :       NS_EscapeURL(list[i]->name, -1, esc_OnlyNonASCII | esc_AlwaysCopy, name);
     396           0 :       uri.Append(NS_ConvertUTF8toUTF16(name));;
     397           0 :       uri.AppendLiteral("?");
     398             : 
     399           0 :       nsAutoCString lang(list[i]->language);
     400             : 
     401           0 :       if (strcmp(list[i]->variant, "none") != 0) {
     402             :         // In speech dispatcher, the variant will usually be the locale subtag
     403             :         // with another, non-standard suptag after it. We keep the first one
     404             :         // and convert it to uppercase.
     405           0 :         const char* v = list[i]->variant;
     406           0 :         const char* hyphen = strchr(v, '-');
     407           0 :         nsDependentCSubstring variant(v, hyphen ? hyphen - v : strlen(v));
     408           0 :         ToUpperCase(variant);
     409             : 
     410             :         // eSpeak uses UK which is not a valid region subtag in BCP47.
     411           0 :         if (variant.Equals("UK")) {
     412           0 :           variant.AssignLiteral("GB");
     413             :         }
     414             : 
     415           0 :         lang.AppendLiteral("-");
     416           0 :         lang.Append(variant);
     417             :       }
     418             : 
     419           0 :       uri.Append(NS_ConvertUTF8toUTF16(lang));
     420             : 
     421           0 :       mVoices.Put(uri, new SpeechDispatcherVoice(
     422           0 :                     NS_ConvertUTF8toUTF16(list[i]->name),
     423           0 :                     NS_ConvertUTF8toUTF16(lang)));
     424             :     }
     425             :   }
     426             : 
     427           0 :   NS_DispatchToMainThread(
     428           0 :     NewRunnableMethod("dom::SpeechDispatcherService::RegisterVoices",
     429             :                       this,
     430           0 :                       &SpeechDispatcherService::RegisterVoices));
     431             : 
     432             :   //mInitialized = true;
     433             : }
     434             : 
     435             : // private methods
     436             : 
     437             : void
     438           0 : SpeechDispatcherService::RegisterVoices()
     439             : {
     440           0 :   RefPtr<nsSynthVoiceRegistry> registry = nsSynthVoiceRegistry::GetInstance();
     441           0 :   for (auto iter = mVoices.Iter(); !iter.Done(); iter.Next()) {
     442           0 :     RefPtr<SpeechDispatcherVoice>& voice = iter.Data();
     443             : 
     444             :     // This service can only speak one utterance at a time, so we set
     445             :     // aQueuesUtterances to true in order to track global state and schedule
     446             :     // access to this service.
     447             :     DebugOnly<nsresult> rv =
     448           0 :       registry->AddVoice(this, iter.Key(), voice->mName, voice->mLanguage,
     449           0 :                          voice->mName.EqualsLiteral("default"), true);
     450             : 
     451           0 :     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to add voice");
     452             :   }
     453             : 
     454           0 :   mInitThread->Shutdown();
     455           0 :   mInitThread = nullptr;
     456             : 
     457           0 :   mInitialized = true;
     458             : 
     459           0 :   registry->NotifyVoicesChanged();
     460           0 : }
     461             : 
     462             : // nsIObserver
     463             : 
     464             : NS_IMETHODIMP
     465           0 : SpeechDispatcherService::Observe(nsISupports* aSubject, const char* aTopic,
     466             :                                  const char16_t* aData)
     467             : {
     468           0 :   return NS_OK;
     469             : }
     470             : 
     471             : // nsISpeechService
     472             : 
     473             : // TODO: Support SSML
     474             : NS_IMETHODIMP
     475           0 : SpeechDispatcherService::Speak(const nsAString& aText, const nsAString& aUri,
     476             :                                float aVolume, float aRate, float aPitch,
     477             :                                nsISpeechTask* aTask)
     478             : {
     479           0 :   if (NS_WARN_IF(!mInitialized)) {
     480           0 :     return NS_ERROR_NOT_AVAILABLE;
     481             :   }
     482             : 
     483             :   RefPtr<SpeechDispatcherCallback> callback =
     484           0 :     new SpeechDispatcherCallback(aTask, this);
     485             : 
     486           0 :   bool found = false;
     487           0 :   SpeechDispatcherVoice* voice = mVoices.GetWeak(aUri, &found);
     488             : 
     489           0 :   if(NS_WARN_IF(!(found))) {
     490           0 :     return NS_ERROR_NOT_AVAILABLE;
     491             :   }
     492             : 
     493           0 :   spd_set_synthesis_voice(mSpeechdClient,
     494           0 :                           NS_ConvertUTF16toUTF8(voice->mName).get());
     495             : 
     496             :   // We provide a volume of 0.0 to 1.0, speech-dispatcher expects 0 - 100.
     497           0 :   spd_set_volume(mSpeechdClient, static_cast<int>(aVolume * 100));
     498             : 
     499             :   // aRate is a value of 0.1 (0.1x) to 10 (10x) with 1 (1x) being normal rate.
     500             :   // speechd expects -100 to 100 with 0 being normal rate.
     501           0 :   float rate = 0;
     502           0 :   if (aRate > 1) {
     503             :     // Each step to 100 is logarithmically distributed up to 2.5x.
     504           0 :     rate = log10(std::min(aRate, MAX_RATE)) / log10(MAX_RATE) * 100;
     505           0 :   } else if (aRate < 1) {
     506             :     // Each step to -100 is logarithmically distributed down to 0.5x.
     507           0 :     rate = log10(std::max(aRate, MIN_RATE)) / log10(MIN_RATE) * -100;
     508             :   }
     509             : 
     510           0 :   spd_set_voice_rate(mSpeechdClient, static_cast<int>(rate));
     511             : 
     512             :   // We provide a pitch of 0 to 2 with 1 being the default.
     513             :   // speech-dispatcher expects -100 to 100 with 0 being default.
     514           0 :   spd_set_voice_pitch(mSpeechdClient, static_cast<int>((aPitch - 1) * 100));
     515             : 
     516             :   // The last three parameters don't matter for an indirect service
     517           0 :   nsresult rv = aTask->Setup(callback, 0, 0, 0);
     518             : 
     519           0 :   if (NS_FAILED(rv)) {
     520           0 :     return rv;
     521             :   }
     522             : 
     523           0 :   if (aText.Length()) {
     524           0 :     int msg_id = spd_say(
     525           0 :       mSpeechdClient, SPD_MESSAGE, NS_ConvertUTF16toUTF8(aText).get());
     526             : 
     527           0 :     if (msg_id < 0) {
     528           0 :       return NS_ERROR_FAILURE;
     529             :     }
     530             : 
     531           0 :     mCallbacks.Put(msg_id, callback);
     532             :   } else {
     533             :     // Speech dispatcher does not work well with empty strings.
     534             :     // In that case, don't send empty string to speechd,
     535             :     // and just emulate a speechd start and end event.
     536           0 :     NS_DispatchToMainThread(NewRunnableMethod<SPDNotificationType>(
     537             :       "dom::SpeechDispatcherCallback::OnSpeechEvent",
     538             :       callback,
     539             :       &SpeechDispatcherCallback::OnSpeechEvent,
     540           0 :       SPD_EVENT_BEGIN));
     541             : 
     542           0 :     NS_DispatchToMainThread(NewRunnableMethod<SPDNotificationType>(
     543             :       "dom::SpeechDispatcherCallback::OnSpeechEvent",
     544             :       callback,
     545             :       &SpeechDispatcherCallback::OnSpeechEvent,
     546           0 :       SPD_EVENT_END));
     547             :   }
     548             : 
     549           0 :   return NS_OK;
     550             : }
     551             : 
     552             : NS_IMETHODIMP
     553           0 : SpeechDispatcherService::GetServiceType(SpeechServiceType* aServiceType)
     554             : {
     555           0 :   *aServiceType = nsISpeechService::SERVICETYPE_INDIRECT_AUDIO;
     556           0 :   return NS_OK;
     557             : }
     558             : 
     559             : SpeechDispatcherService*
     560           0 : SpeechDispatcherService::GetInstance(bool create)
     561             : {
     562           0 :   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     563           0 :     MOZ_ASSERT(false,
     564             :                "SpeechDispatcherService can only be started on main gecko process");
     565             :     return nullptr;
     566             :   }
     567             : 
     568           0 :   if (!sSingleton && create) {
     569           0 :     sSingleton = new SpeechDispatcherService();
     570           0 :     sSingleton->Init();
     571             :   }
     572             : 
     573           0 :   return sSingleton;
     574             : }
     575             : 
     576             : already_AddRefed<SpeechDispatcherService>
     577           0 : SpeechDispatcherService::GetInstanceForService()
     578             : {
     579           0 :   MOZ_ASSERT(NS_IsMainThread());
     580           0 :   RefPtr<SpeechDispatcherService> sapiService = GetInstance();
     581           0 :   return sapiService.forget();
     582             : }
     583             : 
     584             : void
     585           0 : SpeechDispatcherService::EventNotify(uint32_t aMsgId, uint32_t aState)
     586             : {
     587           0 :   SpeechDispatcherCallback* callback = mCallbacks.GetWeak(aMsgId);
     588             : 
     589           0 :   if (callback) {
     590           0 :     if (callback->OnSpeechEvent((SPDNotificationType)aState)) {
     591           0 :       mCallbacks.Remove(aMsgId);
     592             :     }
     593             :   }
     594           0 : }
     595             : 
     596             : void
     597           0 : SpeechDispatcherService::Shutdown()
     598             : {
     599           0 :   if (!sSingleton) {
     600           0 :     return;
     601             :   }
     602             : 
     603           0 :   sSingleton = nullptr;
     604             : }
     605             : 
     606             : } // namespace dom
     607           9 : } // namespace mozilla

Generated by: LCOV version 1.13