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

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

Issue 10663018: Changing tab closure handling logic in speech recognition code and cleaning bubble controller. (Spe… (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Slight refactor to fix a bug on mac + rebase Created 8 years, 5 months 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) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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/speech/chrome_speech_recognition_manager_delegate.h" 5 #include "chrome/browser/speech/chrome_speech_recognition_manager_delegate.h"
6 6
7 #include <string> 7 #include <string>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/synchronization/lock.h" 10 #include "base/synchronization/lock.h"
11 #include "base/threading/thread_restrictions.h" 11 #include "base/threading/thread_restrictions.h"
12 #include "base/utf_string_conversions.h" 12 #include "base/utf_string_conversions.h"
13 #include "chrome/browser/browser_process.h" 13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/extensions/extension_service.h" 14 #include "chrome/browser/extensions/extension_service.h"
15 #include "chrome/browser/prefs/pref_service.h" 15 #include "chrome/browser/prefs/pref_service.h"
16 #include "chrome/browser/profiles/profile_manager.h" 16 #include "chrome/browser/profiles/profile_manager.h"
17 #include "chrome/browser/speech/chrome_speech_recognition_preferences.h" 17 #include "chrome/browser/speech/chrome_speech_recognition_preferences.h"
18 #include "chrome/browser/speech/speech_recognition_tray_icon_controller.h" 18 #include "chrome/browser/speech/speech_recognition_tray_icon_controller.h"
19 #include "chrome/browser/speech/tab_watcher.h"
19 #include "chrome/browser/tab_contents/tab_util.h" 20 #include "chrome/browser/tab_contents/tab_util.h"
20 #include "chrome/browser/view_type_utils.h" 21 #include "chrome/browser/view_type_utils.h"
21 #include "chrome/common/pref_names.h" 22 #include "chrome/common/pref_names.h"
22 #include "content/public/browser/browser_thread.h" 23 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/render_process_host.h" 24 #include "content/public/browser/render_process_host.h"
24 #include "content/public/browser/render_view_host.h" 25 #include "content/public/browser/render_view_host.h"
25 #include "content/public/browser/resource_context.h" 26 #include "content/public/browser/resource_context.h"
26 #include "content/public/browser/speech_recognition_manager.h" 27 #include "content/public/browser/speech_recognition_manager.h"
27 #include "content/public/browser/speech_recognition_session_config.h" 28 #include "content/public/browser/speech_recognition_session_config.h"
28 #include "content/public/browser/speech_recognition_session_context.h" 29 #include "content/public/browser/speech_recognition_session_context.h"
29 #include "content/public/browser/web_contents.h" 30 #include "content/public/browser/web_contents.h"
30 #include "content/public/common/speech_recognition_error.h" 31 #include "content/public/common/speech_recognition_error.h"
31 #include "content/public/common/speech_recognition_result.h" 32 #include "content/public/common/speech_recognition_result.h"
32 #include "grit/generated_resources.h" 33 #include "grit/generated_resources.h"
33 #include "net/url_request/url_request_context_getter.h" 34 #include "net/url_request/url_request_context_getter.h"
34 #include "ui/base/l10n/l10n_util.h" 35 #include "ui/base/l10n/l10n_util.h"
35 36
36 #if defined(OS_WIN) 37 #if defined(OS_WIN)
37 #include "chrome/installer/util/wmi.h" 38 #include "chrome/installer/util/wmi.h"
38 #endif 39 #endif
39 40
40 using content::BrowserThread; 41 using content::BrowserThread;
41 using content::SpeechRecognitionManager; 42 using content::SpeechRecognitionManager;
42 using content::SpeechRecognitionSessionContext;
43 using content::WebContents; 43 using content::WebContents;
44 44
45 namespace { 45 namespace {
46 const int kNoActiveBubble =
47 content::SpeechRecognitionManager::kSessionIDInvalid;
48 46
49 const char kExtensionPrefix[] = "chrome-extension://"; 47 const char kExtensionPrefix[] = "chrome-extension://";
50 48
51 bool RequiresBubble(int session_id) { 49 bool RequiresBubble(int session_id) {
52 return SpeechRecognitionManager::GetInstance()-> 50 return SpeechRecognitionManager::GetInstance()->
53 GetSessionContext(session_id).requested_by_page_element; 51 GetSessionContext(session_id).requested_by_page_element;
54 } 52 }
55 53
56 bool RequiresTrayIcon(int session_id) { 54 bool RequiresTrayIcon(int session_id) {
57 return !RequiresBubble(session_id); 55 return !RequiresBubble(session_id);
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
117 ~OptionalRequestInfo() {} 115 ~OptionalRequestInfo() {}
118 116
119 base::Lock lock_; 117 base::Lock lock_;
120 std::string value_; 118 std::string value_;
121 bool can_report_metrics_; 119 bool can_report_metrics_;
122 120
123 DISALLOW_COPY_AND_ASSIGN(OptionalRequestInfo); 121 DISALLOW_COPY_AND_ASSIGN(OptionalRequestInfo);
124 }; 122 };
125 123
126 ChromeSpeechRecognitionManagerDelegate::ChromeSpeechRecognitionManagerDelegate() 124 ChromeSpeechRecognitionManagerDelegate::ChromeSpeechRecognitionManagerDelegate()
127 : active_bubble_session_id_(kNoActiveBubble) { 125 {
128 } 126 }
129 127
130 ChromeSpeechRecognitionManagerDelegate:: 128 ChromeSpeechRecognitionManagerDelegate::
131 ~ChromeSpeechRecognitionManagerDelegate() { 129 ~ChromeSpeechRecognitionManagerDelegate() {
132 if (tray_icon_controller_.get()) 130 if (tray_icon_controller_.get())
133 tray_icon_controller_->Hide(); 131 tray_icon_controller_->Hide();
134 if (active_bubble_session_id_ != kNoActiveBubble) { 132 if (bubble_controller_.get() && bubble_controller_->GetActiveSessionID())
135 DCHECK(bubble_controller_.get()); 133 bubble_controller_->CloseBubble();
136 bubble_controller_->CloseBubble(active_bubble_session_id_);
137 }
138 } 134 }
139 135
140 void ChromeSpeechRecognitionManagerDelegate::InfoBubbleButtonClicked( 136 void ChromeSpeechRecognitionManagerDelegate::InfoBubbleButtonClicked(
141 int session_id, SpeechRecognitionBubble::Button button) { 137 int session_id, SpeechRecognitionBubble::Button button) {
142 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 138 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
143 DCHECK_EQ(active_bubble_session_id_, session_id);
144 139
145 // Note, the session might have been destroyed, therefore avoid calls to the 140 // Note, the session might have been destroyed, therefore avoid calls to the
146 // manager which imply its existance (e.g., GetSessionContext()). 141 // manager which imply its existance (e.g., GetSessionContext()).
147 142
148 if (button == SpeechRecognitionBubble::BUTTON_CANCEL) { 143 if (button == SpeechRecognitionBubble::BUTTON_CANCEL) {
149 GetBubbleController()->CloseBubble(session_id); 144 GetBubbleController()->CloseBubble();
150 last_session_config_.reset(); 145 last_session_config_.reset();
151 active_bubble_session_id_ = kNoActiveBubble;
152 146
153 // We can safely call AbortSession even if the session has already ended, 147 // We can safely call AbortSession even if the session has already ended,
154 // the manager's public methods are reliable and will handle it properly. 148 // the manager's public methods are reliable and will handle it properly.
155 SpeechRecognitionManager::GetInstance()->AbortSession(session_id); 149 SpeechRecognitionManager::GetInstance()->AbortSession(session_id);
156 } else if (button == SpeechRecognitionBubble::BUTTON_TRY_AGAIN) { 150 } else if (button == SpeechRecognitionBubble::BUTTON_TRY_AGAIN) {
157 GetBubbleController()->CloseBubble(session_id); 151 GetBubbleController()->CloseBubble();
158 active_bubble_session_id_ = kNoActiveBubble;
159 RestartLastSession(); 152 RestartLastSession();
160 } 153 }
161 } 154 }
162 155
163 void ChromeSpeechRecognitionManagerDelegate::InfoBubbleFocusChanged( 156 void ChromeSpeechRecognitionManagerDelegate::InfoBubbleFocusChanged(
164 int session_id) { 157 int session_id) {
165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
166 DCHECK_EQ(active_bubble_session_id_, session_id); 159
160 // This check is needed since on some systems (MacOS), in rare cases, if the
161 // user clicks repeatedly and fast on the input element, the FocusChanged
162 // event (corresponding to the old session that should be aborted) can be
163 // received after a new session (corresponding to the 2nd click) is started.
164 if (GetBubbleController()->GetActiveSessionID() != session_id)
165 return;
167 166
168 // Note, the session might have been destroyed, therefore avoid calls to the 167 // Note, the session might have been destroyed, therefore avoid calls to the
169 // manager which imply its existance (e.g., GetSessionContext()). 168 // manager which imply its existance (e.g., GetSessionContext()).
170 169 GetBubbleController()->CloseBubble();
171 GetBubbleController()->CloseBubble(session_id);
172 last_session_config_.reset(); 170 last_session_config_.reset();
173 active_bubble_session_id_ = kNoActiveBubble;
174 171
175 // If the user clicks outside the bubble while capturing audio we abort the 172 // If the user clicks outside the bubble while capturing audio we abort the
176 // session. Otherwise, i.e. audio capture is ended and we are just waiting for 173 // session. Otherwise, i.e. audio capture is ended and we are just waiting for
177 // results, this activity is carried silently in background. 174 // results, this activity is carried silently in background.
178 if (SpeechRecognitionManager::GetInstance()->IsCapturingAudio()) 175 if (SpeechRecognitionManager::GetInstance()->IsCapturingAudio())
179 SpeechRecognitionManager::GetInstance()->AbortSession(session_id); 176 SpeechRecognitionManager::GetInstance()->AbortSession(session_id);
180 } 177 }
181 178
182 void ChromeSpeechRecognitionManagerDelegate::RestartLastSession() { 179 void ChromeSpeechRecognitionManagerDelegate::RestartLastSession() {
183 DCHECK(last_session_config_.get()); 180 DCHECK(last_session_config_.get());
184 SpeechRecognitionManager* manager = SpeechRecognitionManager::GetInstance(); 181 SpeechRecognitionManager* manager = SpeechRecognitionManager::GetInstance();
185 const int new_session_id = manager->CreateSession(*last_session_config_); 182 const int new_session_id = manager->CreateSession(*last_session_config_);
186 DCHECK_NE(new_session_id, kNoActiveBubble); 183 DCHECK_NE(SpeechRecognitionManager::kSessionIDInvalid, new_session_id);
187 last_session_config_.reset(); 184 last_session_config_.reset();
188 manager->StartSession(new_session_id); 185 manager->StartSession(new_session_id);
189 } 186 }
190 187
188 void ChromeSpeechRecognitionManagerDelegate::TabClosedCallback(
189 int render_process_id, int render_view_id) {
190 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
191
192 if (SpeechRecognitionManager* mgr = SpeechRecognitionManager::GetInstance())
193 mgr->AbortAllSessionsForRenderer(render_process_id, render_view_id);
194
195 if (bubble_controller_.get() &&
196 bubble_controller_->IsShowingBubbleOn(render_process_id,
197 render_view_id)) {
198 bubble_controller_->CloseBubble();
199 }
200 }
201
191 void ChromeSpeechRecognitionManagerDelegate::OnRecognitionStart( 202 void ChromeSpeechRecognitionManagerDelegate::OnRecognitionStart(
192 int session_id) { 203 int session_id) {
193 const content::SpeechRecognitionSessionContext& context = 204 const content::SpeechRecognitionSessionContext& context =
194 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); 205 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
195 206
196 if (RequiresBubble(session_id)) { 207 if (RequiresBubble(session_id)) {
197 // Copy the configuration of the session (for the "try again" button). 208 // Copy the configuration of the session (for the "try again" button).
198 last_session_config_.reset(new content::SpeechRecognitionSessionConfig( 209 last_session_config_.reset(new content::SpeechRecognitionSessionConfig(
199 SpeechRecognitionManager::GetInstance()->GetSessionConfig(session_id))); 210 SpeechRecognitionManager::GetInstance()->GetSessionConfig(session_id)));
200 211
201 // Create and show the bubble. 212 // Create and show the bubble.
202 DCHECK_EQ(active_bubble_session_id_, kNoActiveBubble);
203 active_bubble_session_id_ = session_id;
204 GetBubbleController()->CreateBubble(session_id, 213 GetBubbleController()->CreateBubble(session_id,
205 context.render_process_id, 214 context.render_process_id,
206 context.render_view_id, 215 context.render_view_id,
207 context.element_rect); 216 context.element_rect);
217 }
208 218
209 // TODO(primiano) Why not creating directly the bubble in warmup mode? 219 // Register callback to auto abort session on tab closure.
210 GetBubbleController()->SetBubbleWarmUpMode(session_id); 220 // |tab_watcher_| is lazyly istantiated on the first call.
221 if (!tab_watcher_.get()) {
222 tab_watcher_ = new TabWatcher(
223 base::Bind(&ChromeSpeechRecognitionManagerDelegate::TabClosedCallback,
224 base::Unretained(this)),
225 BrowserThread::IO);
211 } 226 }
227 tab_watcher_->Watch(context.render_process_id, context.render_view_id);
212 } 228 }
213 229
214 void ChromeSpeechRecognitionManagerDelegate::OnAudioStart(int session_id) { 230 void ChromeSpeechRecognitionManagerDelegate::OnAudioStart(int session_id) {
215 if (RequiresBubble(session_id)) { 231 if (RequiresBubble(session_id)) {
216 GetBubbleController()->SetBubbleRecordingMode(session_id); 232 DCHECK_EQ(session_id, GetBubbleController()->GetActiveSessionID());
233 GetBubbleController()->SetBubbleRecordingMode();
217 } else if (RequiresTrayIcon(session_id)) { 234 } else if (RequiresTrayIcon(session_id)) {
218 // We post the action to the UI thread for sessions requiring a tray icon, 235 // We post the action to the UI thread for sessions requiring a tray icon,
219 // since ChromeSpeechRecognitionPreferences (which requires UI thread) is 236 // since ChromeSpeechRecognitionPreferences (which requires UI thread) is
220 // involved for determining whether a security alert balloon is required. 237 // involved for determining whether a security alert balloon is required.
221 const content::SpeechRecognitionSessionContext& context = 238 const content::SpeechRecognitionSessionContext& context =
222 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); 239 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
223 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( 240 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
224 &ChromeSpeechRecognitionManagerDelegate::ShowTrayIconOnUIThread, 241 &ChromeSpeechRecognitionManagerDelegate::ShowTrayIconOnUIThread,
225 context.context_name, 242 context.context_name,
226 context.render_process_id, 243 context.render_process_id,
227 scoped_refptr<SpeechRecognitionTrayIconController>( 244 scoped_refptr<SpeechRecognitionTrayIconController>(
228 GetTrayIconController()))); 245 GetTrayIconController())));
229 } 246 }
230 } 247 }
231 248
232 void ChromeSpeechRecognitionManagerDelegate::OnEnvironmentEstimationComplete( 249 void ChromeSpeechRecognitionManagerDelegate::OnEnvironmentEstimationComplete(
233 int session_id) { 250 int session_id) {
234 } 251 }
235 252
236 void ChromeSpeechRecognitionManagerDelegate::OnSoundStart(int session_id) { 253 void ChromeSpeechRecognitionManagerDelegate::OnSoundStart(int session_id) {
237 } 254 }
238 255
239 void ChromeSpeechRecognitionManagerDelegate::OnSoundEnd(int session_id) { 256 void ChromeSpeechRecognitionManagerDelegate::OnSoundEnd(int session_id) {
240 } 257 }
241 258
242 void ChromeSpeechRecognitionManagerDelegate::OnAudioEnd(int session_id) { 259 void ChromeSpeechRecognitionManagerDelegate::OnAudioEnd(int session_id) {
243 // OnAudioEnd can be also raised after an abort, when the bubble has already 260 // OnAudioEnd can be also raised after an abort, when the bubble has already
244 // been closed. 261 // been closed.
245 if (RequiresBubble(session_id) && active_bubble_session_id_ == session_id) { 262 if (GetBubbleController()->GetActiveSessionID() == session_id) {
246 GetBubbleController()->SetBubbleRecognizingMode(session_id); 263 DCHECK(RequiresBubble(session_id));
264 GetBubbleController()->SetBubbleRecognizingMode();
247 } else if (RequiresTrayIcon(session_id)) { 265 } else if (RequiresTrayIcon(session_id)) {
248 GetTrayIconController()->Hide(); 266 GetTrayIconController()->Hide();
249 } 267 }
250 } 268 }
251 269
252 void ChromeSpeechRecognitionManagerDelegate::OnRecognitionResult( 270 void ChromeSpeechRecognitionManagerDelegate::OnRecognitionResult(
253 int session_id, const content::SpeechRecognitionResult& result) { 271 int session_id, const content::SpeechRecognitionResult& result) {
254 // A result can be dispatched when the bubble is not visible anymore (e.g., 272 // The bubble will be closed upon the OnEnd event, which will follow soon.
255 // lost focus while waiting for a result, thus continuing in background).
256 if (RequiresBubble(session_id) && active_bubble_session_id_ == session_id) {
257 GetBubbleController()->CloseBubble(session_id);
258 last_session_config_.reset();
259 active_bubble_session_id_ = kNoActiveBubble;
260 }
261 } 273 }
262 274
263 void ChromeSpeechRecognitionManagerDelegate::OnRecognitionError( 275 void ChromeSpeechRecognitionManagerDelegate::OnRecognitionError(
264 int session_id, const content::SpeechRecognitionError& error) { 276 int session_id, const content::SpeechRecognitionError& error) {
265 // An error can be dispatched when the bubble is not visible anymore. 277 // An error can be dispatched when the bubble is not visible anymore.
266 if (active_bubble_session_id_ != session_id) 278 if (GetBubbleController()->GetActiveSessionID() != session_id)
267 return; 279 return;
268 DCHECK(RequiresBubble(session_id)); 280 DCHECK(RequiresBubble(session_id));
269 281
270 int error_message_id = 0; 282 int error_message_id = 0;
271 switch (error.code) { 283 switch (error.code) {
272 case content::SPEECH_RECOGNITION_ERROR_AUDIO: 284 case content::SPEECH_RECOGNITION_ERROR_AUDIO:
273 switch (error.details) { 285 switch (error.details) {
274 case content::SPEECH_AUDIO_ERROR_DETAILS_NO_MIC: 286 case content::SPEECH_AUDIO_ERROR_DETAILS_NO_MIC:
275 error_message_id = IDS_SPEECH_INPUT_NO_MIC; 287 error_message_id = IDS_SPEECH_INPUT_NO_MIC;
276 break; 288 break;
(...skipping 15 matching lines...) Expand all
292 error_message_id = IDS_SPEECH_INPUT_NO_RESULTS; 304 error_message_id = IDS_SPEECH_INPUT_NO_RESULTS;
293 break; 305 break;
294 case content::SPEECH_RECOGNITION_ERROR_NETWORK: 306 case content::SPEECH_RECOGNITION_ERROR_NETWORK:
295 error_message_id = IDS_SPEECH_INPUT_NET_ERROR; 307 error_message_id = IDS_SPEECH_INPUT_NET_ERROR;
296 break; 308 break;
297 default: 309 default:
298 NOTREACHED() << "unknown error " << error.code; 310 NOTREACHED() << "unknown error " << error.code;
299 return; 311 return;
300 } 312 }
301 GetBubbleController()->SetBubbleMessage( 313 GetBubbleController()->SetBubbleMessage(
302 session_id, l10n_util::GetStringUTF16(error_message_id)); 314 l10n_util::GetStringUTF16(error_message_id));
303 } 315 }
304 316
305 void ChromeSpeechRecognitionManagerDelegate::OnAudioLevelsChange( 317 void ChromeSpeechRecognitionManagerDelegate::OnAudioLevelsChange(
306 int session_id, float volume, float noise_volume) { 318 int session_id, float volume, float noise_volume) {
307 if (active_bubble_session_id_ == session_id) { 319 if (GetBubbleController()->GetActiveSessionID() == session_id) {
308 DCHECK(RequiresBubble(session_id)); 320 DCHECK(RequiresBubble(session_id));
309 GetBubbleController()->SetBubbleInputVolume(session_id, 321 GetBubbleController()->SetBubbleInputVolume(volume, noise_volume);
310 volume, noise_volume);
311 } else if (RequiresTrayIcon(session_id)) { 322 } else if (RequiresTrayIcon(session_id)) {
312 GetTrayIconController()->SetVUMeterVolume(volume); 323 GetTrayIconController()->SetVUMeterVolume(volume);
313 } 324 }
314 } 325 }
315 326
316 void ChromeSpeechRecognitionManagerDelegate::OnRecognitionEnd(int session_id) { 327 void ChromeSpeechRecognitionManagerDelegate::OnRecognitionEnd(int session_id) {
317 // No need to remove the bubble here, since either one of the following events 328 // The only case in which the OnRecognitionEnd should not close the bubble is
318 // must have happened prior to this callback: 329 // when we are showing an error. In this case the bubble will be closed by
319 // - A previous OnRecognitionResult event already closed the bubble. 330 // the |InfoBubbleFocusChanged| method, when the users clicks either the
320 // - An error occurred, so the bubble is showing the error and will be closed 331 // "Cancel" button or outside of the bubble.
321 // when it will lose focus (by InfoBubbleFocusChanged()). 332 if (GetBubbleController()->GetActiveSessionID() == session_id &&
322 // - The bubble lost focus or the user pressed the Cancel button, thus it has 333 !GetBubbleController()->IsShowingMessage()) {
323 // been closed by InfoBubbleFocusChanged(), which triggered an AbortSession. 334 DCHECK(RequiresBubble(session_id));
335 GetBubbleController()->CloseBubble();
336 }
324 } 337 }
325 338
326 void ChromeSpeechRecognitionManagerDelegate::GetDiagnosticInformation( 339 void ChromeSpeechRecognitionManagerDelegate::GetDiagnosticInformation(
327 bool* can_report_metrics, 340 bool* can_report_metrics,
328 std::string* hardware_info) { 341 std::string* hardware_info) {
329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 342 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
330 if (!optional_request_info_.get()) { 343 if (!optional_request_info_.get()) {
331 optional_request_info_ = new OptionalRequestInfo(); 344 optional_request_info_ = new OptionalRequestInfo();
332 // Since hardware info is optional with speech input requests, we start an 345 // Since hardware info is optional with speech input requests, we start an
333 // asynchronous fetch here and move on with recording audio. This first 346 // asynchronous fetch here and move on with recording audio. This first
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
447 460
448 SpeechRecognitionTrayIconController* 461 SpeechRecognitionTrayIconController*
449 ChromeSpeechRecognitionManagerDelegate::GetTrayIconController() { 462 ChromeSpeechRecognitionManagerDelegate::GetTrayIconController() {
450 if (!tray_icon_controller_.get()) 463 if (!tray_icon_controller_.get())
451 tray_icon_controller_ = new SpeechRecognitionTrayIconController(); 464 tray_icon_controller_ = new SpeechRecognitionTrayIconController();
452 return tray_icon_controller_.get(); 465 return tray_icon_controller_.get();
453 } 466 }
454 467
455 468
456 } // namespace speech 469 } // namespace speech
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698