Tue, 06 Jan 2015 21:39:09 +0100
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