content/media/webspeech/synth/SpeechSynthesis.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
     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/. */
     7 #include "nsSpeechTask.h"
     8 #include "prlog.h"
    10 #include "mozilla/dom/ContentChild.h"
    11 #include "mozilla/dom/Element.h"
    13 #include "mozilla/dom/SpeechSynthesisBinding.h"
    14 #include "SpeechSynthesis.h"
    15 #include "nsSynthVoiceRegistry.h"
    16 #include "nsIDocument.h"
    18 #undef LOG
    19 #ifdef PR_LOGGING
    20 PRLogModuleInfo*
    21 GetSpeechSynthLog()
    22 {
    23   static PRLogModuleInfo* sLog = nullptr;
    25   if (!sLog) {
    26     sLog = PR_NewLogModule("SpeechSynthesis");
    27   }
    29   return sLog;
    30 }
    31 #define LOG(type, msg) PR_LOG(GetSpeechSynthLog(), type, msg)
    32 #else
    33 #define LOG(type, msg)
    34 #endif
    36 namespace mozilla {
    37 namespace dom {
    39 static PLDHashOperator
    40 TraverseCachedVoices(const nsAString& aKey, SpeechSynthesisVoice* aEntry, void* aData)
    41 {
    42   nsCycleCollectionTraversalCallback* cb = static_cast<nsCycleCollectionTraversalCallback*>(aData);
    43   cb->NoteXPCOMChild(aEntry);
    44   return PL_DHASH_NEXT;
    45 }
    47 NS_IMPL_CYCLE_COLLECTION_CLASS(SpeechSynthesis)
    49 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SpeechSynthesis)
    50   NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
    51   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCurrentTask)
    52   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechQueue)
    53   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
    54   tmp->mVoiceCache.Clear();
    55 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    57 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SpeechSynthesis)
    58   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
    59   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentTask)
    60   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechQueue)
    61   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
    62   tmp->mVoiceCache.EnumerateRead(TraverseCachedVoices, &cb);
    63 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    65 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(SpeechSynthesis)
    66   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
    67 NS_IMPL_CYCLE_COLLECTION_TRACE_END
    69 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SpeechSynthesis)
    70   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    71   NS_INTERFACE_MAP_ENTRY(nsISupports)
    72 NS_INTERFACE_MAP_END
    74 NS_IMPL_CYCLE_COLLECTING_ADDREF(SpeechSynthesis)
    75 NS_IMPL_CYCLE_COLLECTING_RELEASE(SpeechSynthesis)
    77 SpeechSynthesis::SpeechSynthesis(nsPIDOMWindow* aParent)
    78   : mParent(aParent)
    79 {
    80   SetIsDOMBinding();
    81 }
    83 SpeechSynthesis::~SpeechSynthesis()
    84 {
    85 }
    87 JSObject*
    88 SpeechSynthesis::WrapObject(JSContext* aCx)
    89 {
    90   return SpeechSynthesisBinding::Wrap(aCx, this);
    91 }
    93 nsIDOMWindow*
    94 SpeechSynthesis::GetParentObject() const
    95 {
    96   return mParent;
    97 }
    99 bool
   100 SpeechSynthesis::Pending() const
   101 {
   102   switch (mSpeechQueue.Length()) {
   103   case 0:
   104     return false;
   106   case 1:
   107     return mSpeechQueue.ElementAt(0)->GetState() == SpeechSynthesisUtterance::STATE_PENDING;
   109   default:
   110     return true;
   111   }
   112 }
   114 bool
   115 SpeechSynthesis::Speaking() const
   116 {
   117   if (mSpeechQueue.IsEmpty()) {
   118     return false;
   119   }
   121   return mSpeechQueue.ElementAt(0)->GetState() == SpeechSynthesisUtterance::STATE_SPEAKING;
   122 }
   124 bool
   125 SpeechSynthesis::Paused() const
   126 {
   127   if (mSpeechQueue.IsEmpty()) {
   128     return false;
   129   }
   131   return mSpeechQueue.ElementAt(0)->IsPaused();
   132 }
   134 void
   135 SpeechSynthesis::Speak(SpeechSynthesisUtterance& aUtterance)
   136 {
   137   if (aUtterance.mState != SpeechSynthesisUtterance::STATE_NONE) {
   138     // XXX: Should probably raise an error
   139     return;
   140   }
   142   mSpeechQueue.AppendElement(&aUtterance);
   143   aUtterance.mState = SpeechSynthesisUtterance::STATE_PENDING;
   145   if (mSpeechQueue.Length() == 1) {
   146     AdvanceQueue();
   147   }
   148 }
   150 void
   151 SpeechSynthesis::AdvanceQueue()
   152 {
   153   LOG(PR_LOG_DEBUG,
   154       ("SpeechSynthesis::AdvanceQueue length=%d", mSpeechQueue.Length()));
   156   if (mSpeechQueue.IsEmpty()) {
   157     return;
   158   }
   160   nsRefPtr<SpeechSynthesisUtterance> utterance = mSpeechQueue.ElementAt(0);
   162   nsAutoString docLang;
   163   nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mParent);
   164   nsIDocument* doc = win->GetExtantDoc();
   166   if (doc) {
   167     Element* elm = doc->GetHtmlElement();
   169     if (elm) {
   170       elm->GetLang(docLang);
   171     }
   172   }
   174   mCurrentTask =
   175     nsSynthVoiceRegistry::GetInstance()->SpeakUtterance(*utterance, docLang);
   177   if (mCurrentTask) {
   178     mCurrentTask->SetSpeechSynthesis(this);
   179   }
   181   return;
   182 }
   184 void
   185 SpeechSynthesis::Cancel()
   186 {
   187   mSpeechQueue.Clear();
   189   if (mCurrentTask) {
   190     mCurrentTask->Cancel();
   191   }
   192 }
   194 void
   195 SpeechSynthesis::Pause()
   196 {
   197   if (mCurrentTask) {
   198     mCurrentTask->Pause();
   199   }
   200 }
   202 void
   203 SpeechSynthesis::Resume()
   204 {
   205   if (mCurrentTask) {
   206     mCurrentTask->Resume();
   207   }
   208 }
   210 void
   211 SpeechSynthesis::OnEnd(const nsSpeechTask* aTask)
   212 {
   213   MOZ_ASSERT(mCurrentTask == aTask);
   215   if (!mSpeechQueue.IsEmpty()) {
   216     mSpeechQueue.RemoveElementAt(0);
   217   }
   219   mCurrentTask = nullptr;
   220   AdvanceQueue();
   221 }
   223 void
   224 SpeechSynthesis::GetVoices(nsTArray< nsRefPtr<SpeechSynthesisVoice> >& aResult)
   225 {
   226   aResult.Clear();
   227   uint32_t voiceCount = 0;
   229   nsresult rv = nsSynthVoiceRegistry::GetInstance()->GetVoiceCount(&voiceCount);
   230   NS_ENSURE_SUCCESS_VOID(rv);
   232   for (uint32_t i = 0; i < voiceCount; i++) {
   233     nsAutoString uri;
   234     rv = nsSynthVoiceRegistry::GetInstance()->GetVoice(i, uri);
   236     if (NS_FAILED(rv)) {
   237       NS_WARNING("Failed to retrieve voice from registry");
   238       continue;
   239     }
   241     SpeechSynthesisVoice* voice = mVoiceCache.GetWeak(uri);
   243     if (!voice) {
   244       voice = new SpeechSynthesisVoice(this, uri);
   245     }
   247     aResult.AppendElement(voice);
   248   }
   250   mVoiceCache.Clear();
   252   for (uint32_t i = 0; i < aResult.Length(); i++) {
   253     SpeechSynthesisVoice* voice = aResult[i];
   254     mVoiceCache.Put(voice->mUri, voice);
   255   }
   256 }
   258 } // namespace dom
   259 } // namespace mozilla

mercurial