Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(253)

Side by Side Diff: chrome/browser/speech/speech_input_extension_manager.cc

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

Powered by Google App Engine
This is Rietveld 408576698