OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/extensions/speech_input/extension_speech_input_manager.
h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/json/json_writer.h" | |
9 #include "base/utf_string_conversions.h" | |
10 #include "base/values.h" | |
11 #include "chrome/browser/extensions/extension_event_router.h" | |
12 #include "chrome/browser/extensions/speech_input/extension_speech_input_api_cons
tants.h" | |
13 #include "chrome/browser/profiles/profile.h" | |
14 #include "chrome/browser/profiles/profile_dependency_manager.h" | |
15 #include "chrome/browser/profiles/profile_keyed_service.h" | |
16 #include "chrome/browser/profiles/profile_keyed_service_factory.h" | |
17 #include "chrome/common/chrome_notification_types.h" | |
18 #include "chrome/common/extensions/extension.h" | |
19 #include "content/public/browser/browser_thread.h" | |
20 #include "content/public/browser/notification_service.h" | |
21 | |
22 using content::BrowserThread; | |
23 | |
24 using namespace speech_input; | |
25 | |
26 namespace constants = extension_speech_input_api_constants; | |
27 | |
28 namespace { | |
29 | |
30 // Caller id provided to the speech recognizer. Since only one extension can | |
31 // be recording on the same time a constant value is enough as id. | |
32 static const int kSpeechCallerId = 1; | |
33 | |
34 // Wrap an ExtensionSpeechInputManager using scoped_refptr to avoid | |
35 // assertion failures on destruction because of not using release(). | |
36 class ExtensionSpeechInputManagerWrapper : public ProfileKeyedService { | |
37 public: | |
38 explicit ExtensionSpeechInputManagerWrapper( | |
39 ExtensionSpeechInputManager* manager) | |
40 : manager_(manager) {} | |
41 | |
42 virtual ~ExtensionSpeechInputManagerWrapper() {} | |
43 | |
44 ExtensionSpeechInputManager* manager() const { return manager_.get(); } | |
45 | |
46 private: | |
47 // Methods from ProfileKeyedService. | |
48 virtual void Shutdown() OVERRIDE { | |
49 manager()->ShutdownOnUIThread(); | |
50 } | |
51 | |
52 scoped_refptr<ExtensionSpeechInputManager> manager_; | |
53 }; | |
54 | |
55 } | |
56 | |
57 // Factory for ExtensionSpeechInputManagers as profile keyed services. | |
58 class ExtensionSpeechInputManager::Factory : public ProfileKeyedServiceFactory { | |
59 public: | |
60 static void Initialize(); | |
61 static Factory* GetInstance(); | |
62 | |
63 ExtensionSpeechInputManagerWrapper* GetForProfile(Profile* profile); | |
64 | |
65 private: | |
66 friend struct DefaultSingletonTraits<Factory>; | |
67 | |
68 Factory(); | |
69 virtual ~Factory(); | |
70 | |
71 // ProfileKeyedServiceFactory methods: | |
72 virtual ProfileKeyedService* BuildServiceInstanceFor( | |
73 Profile* profile) const OVERRIDE; | |
74 virtual bool ServiceRedirectedInIncognito() OVERRIDE { return false; } | |
75 virtual bool ServiceIsNULLWhileTesting() OVERRIDE { return true; } | |
76 virtual bool ServiceIsCreatedWithProfile() OVERRIDE { return true; } | |
77 | |
78 DISALLOW_COPY_AND_ASSIGN(Factory); | |
79 }; | |
80 | |
81 void ExtensionSpeechInputManager::Factory::Initialize() { | |
82 GetInstance(); | |
83 } | |
84 | |
85 ExtensionSpeechInputManager::Factory* | |
86 ExtensionSpeechInputManager::Factory::GetInstance() { | |
87 return Singleton<ExtensionSpeechInputManager::Factory>::get(); | |
88 } | |
89 | |
90 ExtensionSpeechInputManagerWrapper* | |
91 ExtensionSpeechInputManager::Factory::GetForProfile( | |
92 Profile* profile) { | |
93 return static_cast<ExtensionSpeechInputManagerWrapper*>( | |
94 GetServiceForProfile(profile, true)); | |
95 } | |
96 | |
97 ExtensionSpeechInputManager::Factory::Factory() | |
98 : ProfileKeyedServiceFactory(ProfileDependencyManager::GetInstance()) { | |
99 } | |
100 | |
101 ExtensionSpeechInputManager::Factory::~Factory() { | |
102 } | |
103 | |
104 ProfileKeyedService* | |
105 ExtensionSpeechInputManager::Factory::BuildServiceInstanceFor( | |
106 Profile* profile) const { | |
107 scoped_refptr<ExtensionSpeechInputManager> manager( | |
108 new ExtensionSpeechInputManager(profile)); | |
109 return new ExtensionSpeechInputManagerWrapper(manager); | |
110 } | |
111 | |
112 ExtensionSpeechInterface::ExtensionSpeechInterface() { | |
113 } | |
114 | |
115 ExtensionSpeechInterface::~ExtensionSpeechInterface() { | |
116 } | |
117 | |
118 ExtensionSpeechInputManager::ExtensionSpeechInputManager(Profile* profile) | |
119 : profile_(profile), | |
120 state_(kIdle), | |
121 speech_interface_(NULL) { | |
122 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, | |
123 content::Source<Profile>(profile_)); | |
124 } | |
125 | |
126 ExtensionSpeechInputManager::~ExtensionSpeechInputManager() { | |
127 } | |
128 | |
129 ExtensionSpeechInputManager* ExtensionSpeechInputManager::GetForProfile( | |
130 Profile* profile) { | |
131 ExtensionSpeechInputManagerWrapper *wrapper = | |
132 Factory::GetInstance()->GetForProfile(profile); | |
133 if (!wrapper) | |
134 return NULL; | |
135 return wrapper->manager(); | |
136 } | |
137 | |
138 void ExtensionSpeechInputManager::InitializeFactory() { | |
139 Factory::Initialize(); | |
140 } | |
141 | |
142 void ExtensionSpeechInputManager::Observe(int type, | |
143 const content::NotificationSource& source, | |
144 const content::NotificationDetails& details) { | |
145 if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) { | |
146 ExtensionUnloaded( | |
147 content::Details<UnloadedExtensionInfo>(details)->extension->id()); | |
148 } else { | |
149 NOTREACHED(); | |
150 } | |
151 } | |
152 | |
153 void ExtensionSpeechInputManager::ShutdownOnUIThread() { | |
154 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
155 VLOG(1) << "Profile shutting down."; | |
156 | |
157 base::AutoLock auto_lock(state_lock_); | |
158 DCHECK(state_ != kShutdown); | |
159 if (state_ != kIdle) { | |
160 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
161 base::Bind(&ExtensionSpeechInputManager::ForceStopOnIOThread, this)); | |
162 } | |
163 state_ = kShutdown; | |
164 VLOG(1) << "Entering the shutdown sink state."; | |
165 registrar_.RemoveAll(); | |
166 profile_ = NULL; | |
167 } | |
168 | |
169 void ExtensionSpeechInputManager::ExtensionUnloaded( | |
170 const std::string& extension_id) { | |
171 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
172 | |
173 base::AutoLock auto_lock(state_lock_); | |
174 if (state_ == kShutdown) | |
175 return; | |
176 | |
177 VLOG(1) << "Extension unloaded. Requesting to enforce stop..."; | |
178 if (extension_id_in_use_ == extension_id) { | |
179 if (state_ != kIdle) { | |
180 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
181 base::Bind(&ExtensionSpeechInputManager::ForceStopOnIOThread, this)); | |
182 } | |
183 } | |
184 } | |
185 | |
186 void ExtensionSpeechInputManager::SetExtensionSpeechInterface( | |
187 ExtensionSpeechInterface* interface) { | |
188 speech_interface_ = interface; | |
189 } | |
190 | |
191 ExtensionSpeechInterface* | |
192 ExtensionSpeechInputManager::GetExtensionSpeechInterface() { | |
193 return speech_interface_ ? speech_interface_ : this; | |
194 } | |
195 | |
196 void ExtensionSpeechInputManager::ResetToIdleState() { | |
197 VLOG(1) << "State changed to idle. Deassociating any extensions."; | |
198 state_ = kIdle; | |
199 extension_id_in_use_.clear(); | |
200 } | |
201 | |
202 void ExtensionSpeechInputManager::SetRecognitionResult( | |
203 int caller_id, | |
204 const SpeechInputResult& result) { | |
205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
206 DCHECK_EQ(caller_id, kSpeechCallerId); | |
207 | |
208 // Stopping will start the disassociation with the extension. | |
209 // Make a copy to report the results to the proper one. | |
210 std::string extension_id = extension_id_in_use_; | |
211 ForceStopOnIOThread(); | |
212 | |
213 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
214 base::Bind(&ExtensionSpeechInputManager::SetRecognitionResultOnUIThread, | |
215 this, result, extension_id)); | |
216 } | |
217 | |
218 void ExtensionSpeechInputManager::SetRecognitionResultOnUIThread( | |
219 const SpeechInputResult& result, const std::string& extension_id) { | |
220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
221 | |
222 ListValue args; | |
223 DictionaryValue* js_event = new DictionaryValue(); | |
224 args.Append(js_event); | |
225 | |
226 ListValue* js_hypothesis_array = new ListValue(); | |
227 js_event->Set(constants::kHypothesesKey, js_hypothesis_array); | |
228 | |
229 for (size_t i = 0; i < result.hypotheses.size(); ++i) { | |
230 const SpeechInputHypothesis& hypothesis = result.hypotheses[i]; | |
231 | |
232 DictionaryValue* js_hypothesis_object = new DictionaryValue(); | |
233 js_hypothesis_array->Append(js_hypothesis_object); | |
234 | |
235 js_hypothesis_object->SetString(constants::kUtteranceKey, | |
236 UTF16ToUTF8(hypothesis.utterance)); | |
237 js_hypothesis_object->SetDouble(constants::kConfidenceKey, | |
238 hypothesis.confidence); | |
239 } | |
240 | |
241 std::string json_args; | |
242 base::JSONWriter::Write(&args, false, &json_args); | |
243 VLOG(1) << "Results: " << json_args; | |
244 DispatchEventToExtension(extension_id, constants::kOnResultEvent, json_args); | |
245 } | |
246 | |
247 void ExtensionSpeechInputManager::DidStartReceivingAudio(int caller_id) { | |
248 VLOG(1) << "DidStartReceivingAudio"; | |
249 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
250 DCHECK_EQ(caller_id, kSpeechCallerId); | |
251 | |
252 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
253 base::Bind(&ExtensionSpeechInputManager::DidStartReceivingAudioOnUIThread, | |
254 this)); | |
255 } | |
256 | |
257 void ExtensionSpeechInputManager::DidCompleteRecording(int caller_id) { | |
258 DCHECK_EQ(caller_id, kSpeechCallerId); | |
259 } | |
260 | |
261 void ExtensionSpeechInputManager::DidCompleteRecognition(int caller_id) { | |
262 DCHECK_EQ(caller_id, kSpeechCallerId); | |
263 } | |
264 | |
265 void ExtensionSpeechInputManager::DidStartReceivingAudioOnUIThread() { | |
266 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
267 | |
268 base::AutoLock auto_lock(state_lock_); | |
269 if (state_ == kShutdown) | |
270 return; | |
271 | |
272 DCHECK_EQ(state_, kStarting); | |
273 VLOG(1) << "State changed to recording"; | |
274 state_ = kRecording; | |
275 | |
276 VLOG(1) << "Sending start notification"; | |
277 content::NotificationService::current()->Notify( | |
278 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STARTED, | |
279 content::Source<Profile>(profile_), | |
280 content::Details<std::string>(&extension_id_in_use_)); | |
281 } | |
282 | |
283 void ExtensionSpeechInputManager::OnRecognizerError( | |
284 int caller_id, SpeechInputError error) { | |
285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
286 DCHECK_EQ(caller_id, kSpeechCallerId); | |
287 VLOG(1) << "OnRecognizerError: " << error; | |
288 | |
289 base::AutoLock auto_lock(state_lock_); | |
290 if (state_ == kShutdown) | |
291 return; | |
292 | |
293 // Release the recognizer object. | |
294 GetExtensionSpeechInterface()->StopRecording(true); | |
295 | |
296 std::string event_error_code; | |
297 bool report_to_event = true; | |
298 | |
299 switch (error) { | |
300 case kErrorNone: | |
301 break; | |
302 | |
303 case kErrorAudio: | |
304 if (state_ == kStarting) { | |
305 event_error_code = constants::kErrorUnableToStart; | |
306 report_to_event = false; | |
307 } else { | |
308 event_error_code = constants::kErrorCaptureError; | |
309 } | |
310 break; | |
311 | |
312 case kErrorNetwork: | |
313 event_error_code = constants::kErrorNetworkError; | |
314 break; | |
315 | |
316 case kErrorBadGrammar: | |
317 // No error is returned on invalid language, for example. | |
318 // To avoid confusion about when this is would be fired, the invalid | |
319 // params error is not being exposed to the onError event. | |
320 event_error_code = constants::kErrorUnableToStart; | |
321 break; | |
322 | |
323 case kErrorNoSpeech: | |
324 event_error_code = constants::kErrorNoSpeechHeard; | |
325 break; | |
326 | |
327 case kErrorNoMatch: | |
328 event_error_code = constants::kErrorNoResults; | |
329 break; | |
330 | |
331 // The remaining kErrorAborted case should never be returned by the server. | |
332 default: | |
333 NOTREACHED(); | |
334 } | |
335 | |
336 if (!event_error_code.empty()) { | |
337 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
338 base::Bind(&ExtensionSpeechInputManager::DispatchError, | |
339 this, event_error_code, report_to_event)); | |
340 } | |
341 } | |
342 | |
343 void ExtensionSpeechInputManager::DidCompleteEnvironmentEstimation( | |
344 int caller_id) { | |
345 DCHECK_EQ(caller_id, kSpeechCallerId); | |
346 } | |
347 | |
348 void ExtensionSpeechInputManager::DidStartReceivingSpeech(int caller_id) { | |
349 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
350 DCHECK_EQ(caller_id, kSpeechCallerId); | |
351 VLOG(1) << "DidStartReceivingSpeech"; | |
352 | |
353 std::string json_args; | |
354 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
355 base::Bind(&ExtensionSpeechInputManager::DispatchEventToExtension, | |
356 this, extension_id_in_use_, std::string(constants::kOnSoundStartEvent), | |
357 json_args)); | |
358 } | |
359 | |
360 void ExtensionSpeechInputManager::DidStopReceivingSpeech(int caller_id) { | |
361 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
362 DCHECK_EQ(caller_id, kSpeechCallerId); | |
363 VLOG(1) << "DidStopReceivingSpeech"; | |
364 | |
365 std::string json_args; | |
366 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
367 base::Bind(&ExtensionSpeechInputManager::DispatchEventToExtension, | |
368 this, extension_id_in_use_, std::string(constants::kOnSoundEndEvent), | |
369 json_args)); | |
370 } | |
371 | |
372 void ExtensionSpeechInputManager::DispatchEventToExtension( | |
373 const std::string& extension_id, const std::string& event, | |
374 const std::string& json_args) { | |
375 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
376 | |
377 base::AutoLock auto_lock(state_lock_); | |
378 if (state_ == kShutdown) | |
379 return; | |
380 | |
381 if (profile_ && profile_->GetExtensionEventRouter()) { | |
382 std::string final_args; | |
383 if (json_args.empty()) { | |
384 ListValue args; | |
385 base::JSONWriter::Write(&args, false, &final_args); | |
386 } else { | |
387 final_args = json_args; | |
388 } | |
389 | |
390 profile_->GetExtensionEventRouter()->DispatchEventToExtension( | |
391 extension_id, event, final_args, profile_, GURL()); | |
392 } | |
393 } | |
394 | |
395 void ExtensionSpeechInputManager::DispatchError( | |
396 const std::string& error, bool dispatch_event) { | |
397 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
398 | |
399 std::string extension_id; | |
400 { | |
401 base::AutoLock auto_lock(state_lock_); | |
402 if (state_ == kShutdown) | |
403 return; | |
404 | |
405 extension_id = extension_id_in_use_; | |
406 ResetToIdleState(); | |
407 | |
408 // Will set the error property in the ongoing extension function calls. | |
409 ExtensionError details(extension_id, error); | |
410 content::NotificationService::current()->Notify( | |
411 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_FAILED, | |
412 content::Source<Profile>(profile_), | |
413 content::Details<ExtensionError>(&details)); | |
414 } | |
415 | |
416 // Used for errors that are also reported via the onError event. | |
417 if (dispatch_event) { | |
418 ListValue args; | |
419 DictionaryValue *js_error = new DictionaryValue(); | |
420 args.Append(js_error); | |
421 js_error->SetString(constants::kErrorCodeKey, error); | |
422 std::string json_args; | |
423 base::JSONWriter::Write(&args, false, &json_args); | |
424 DispatchEventToExtension(extension_id, | |
425 constants::kOnErrorEvent, json_args); | |
426 } | |
427 } | |
428 | |
429 bool ExtensionSpeechInputManager::Start(const std::string& extension_id, | |
430 const std::string& language, const std::string& grammar, | |
431 bool filter_profanities, std::string* error) { | |
432 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
433 DCHECK(error); | |
434 VLOG(1) << "Requesting start (UI thread)"; | |
435 | |
436 base::AutoLock auto_lock(state_lock_); | |
437 if (state_ == kShutdown || | |
438 (!extension_id_in_use_.empty() && extension_id_in_use_ != extension_id)) { | |
439 *error = constants::kErrorRequestDenied; | |
440 return false; | |
441 } | |
442 | |
443 switch (state_) { | |
444 case kIdle: | |
445 break; | |
446 | |
447 case kStarting: | |
448 *error = constants::kErrorRequestInProgress; | |
449 return false; | |
450 | |
451 case kRecording: | |
452 case kStopping: | |
453 *error = constants::kErrorInvalidOperation; | |
454 return false; | |
455 | |
456 default: | |
457 NOTREACHED(); | |
458 } | |
459 | |
460 extension_id_in_use_ = extension_id; | |
461 VLOG(1) << "State changed to starting"; | |
462 state_ = kStarting; | |
463 | |
464 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
465 base::Bind(&ExtensionSpeechInputManager::StartOnIOThread, this, | |
466 profile_->GetRequestContext(), language, grammar, filter_profanities)); | |
467 return true; | |
468 } | |
469 | |
470 void ExtensionSpeechInputManager::StartOnIOThread( | |
471 net::URLRequestContextGetter* context_getter, | |
472 const std::string& language, const std::string& grammar, | |
473 bool filter_profanities) { | |
474 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
475 VLOG(1) << "Requesting start (IO thread)"; | |
476 | |
477 // Everything put inside the lock to ensure the validity of context_getter, | |
478 // guaranteed while not in the shutdown state. Any ongoing or recognition | |
479 // request will be requested to be aborted when entering the shutdown state. | |
480 base::AutoLock auto_lock(state_lock_); | |
481 if (state_ == kShutdown) | |
482 return; | |
483 | |
484 if (!GetExtensionSpeechInterface()->HasAudioInputDevices()) { | |
485 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
486 base::Bind(&ExtensionSpeechInputManager::DispatchError, this, | |
487 std::string(constants::kErrorNoRecordingDeviceFound), false)); | |
488 return; | |
489 } | |
490 | |
491 if (GetExtensionSpeechInterface()->IsRecordingInProcess()) { | |
492 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
493 base::Bind(&ExtensionSpeechInputManager::DispatchError, this, | |
494 std::string(constants::kErrorRecordingDeviceInUse), false)); | |
495 return; | |
496 } | |
497 | |
498 GetExtensionSpeechInterface()->StartRecording(this, context_getter, | |
499 kSpeechCallerId, language, grammar, filter_profanities); | |
500 } | |
501 | |
502 bool ExtensionSpeechInputManager::HasAudioInputDevices() { | |
503 return AudioManager::GetAudioManager()->HasAudioInputDevices(); | |
504 } | |
505 | |
506 bool ExtensionSpeechInputManager::IsRecordingInProcess() { | |
507 // Thread-safe query. | |
508 return AudioManager::GetAudioManager()->IsRecordingInProcess(); | |
509 } | |
510 | |
511 bool ExtensionSpeechInputManager::IsRecording() { | |
512 return GetExtensionSpeechInterface()->IsRecordingInProcess(); | |
513 } | |
514 | |
515 void ExtensionSpeechInputManager::StartRecording( | |
516 speech_input::SpeechRecognizerDelegate* delegate, | |
517 net::URLRequestContextGetter* context_getter, int caller_id, | |
518 const std::string& language, const std::string& grammar, | |
519 bool filter_profanities) { | |
520 DCHECK(!recognizer_); | |
521 recognizer_ = new SpeechRecognizer(delegate, caller_id, language, grammar, | |
522 context_getter, filter_profanities, "", ""); | |
523 recognizer_->StartRecording(); | |
524 } | |
525 | |
526 bool ExtensionSpeechInputManager::HasValidRecognizer() { | |
527 // Conditional expression used to avoid a performance warning on windows. | |
528 return recognizer_ ? true : false; | |
529 } | |
530 | |
531 bool ExtensionSpeechInputManager::Stop(const std::string& extension_id, | |
532 std::string* error) { | |
533 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
534 DCHECK(error); | |
535 VLOG(1) << "Requesting stop (UI thread)"; | |
536 | |
537 base::AutoLock auto_lock(state_lock_); | |
538 if (state_ == kShutdown || | |
539 (!extension_id_in_use_.empty() && extension_id_in_use_ != extension_id)) { | |
540 *error = constants::kErrorRequestDenied; | |
541 return false; | |
542 } | |
543 | |
544 switch (state_) { | |
545 case kRecording: | |
546 break; | |
547 | |
548 case kStopping: | |
549 *error = constants::kErrorRequestInProgress; | |
550 return false; | |
551 | |
552 case kIdle: | |
553 case kStarting: | |
554 *error = constants::kErrorInvalidOperation; | |
555 return false; | |
556 | |
557 default: | |
558 NOTREACHED(); | |
559 } | |
560 | |
561 // Guarded by the state lock. | |
562 DCHECK(GetExtensionSpeechInterface()->HasValidRecognizer()); | |
563 | |
564 VLOG(1) << "State changed to stopping"; | |
565 state_ = kStopping; | |
566 | |
567 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
568 base::Bind(&ExtensionSpeechInputManager::ForceStopOnIOThread, this)); | |
569 return true; | |
570 } | |
571 | |
572 void ExtensionSpeechInputManager::ForceStopOnIOThread() { | |
573 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
574 VLOG(1) << "Requesting forced stop (IO thread)"; | |
575 | |
576 base::AutoLock auto_lock(state_lock_); | |
577 DCHECK(state_ != kIdle); | |
578 | |
579 GetExtensionSpeechInterface()->StopRecording(false); | |
580 | |
581 if (state_ == kShutdown) | |
582 return; | |
583 | |
584 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
585 base::Bind(&ExtensionSpeechInputManager::StopSucceededOnUIThread, this)); | |
586 } | |
587 | |
588 void ExtensionSpeechInputManager::StopRecording(bool recognition_failed) { | |
589 if (recognizer_) { | |
590 // Recognition is already cancelled in case of failure. | |
591 // Double-cancelling leads to assertion failures. | |
592 if (!recognition_failed) | |
593 recognizer_->CancelRecognition(); | |
594 recognizer_.release(); | |
595 } | |
596 } | |
597 | |
598 void ExtensionSpeechInputManager::StopSucceededOnUIThread() { | |
599 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
600 VLOG(1) << "Stop succeeded (UI thread)"; | |
601 | |
602 base::AutoLock auto_lock(state_lock_); | |
603 if (state_ == kShutdown) | |
604 return; | |
605 | |
606 std::string extension_id = extension_id_in_use_; | |
607 ResetToIdleState(); | |
608 | |
609 content::NotificationService::current()->Notify( | |
610 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STOPPED, | |
611 // Guarded by the state_ == kShutdown check. | |
612 content::Source<Profile>(profile_), | |
613 content::Details<std::string>(&extension_id)); | |
614 } | |
OLD | NEW |