From c52b28964090204b87634b396823c3709855079c Mon Sep 17 00:00:00 2001 From: "primiano@chromium.org" Date: Wed, 7 Mar 2012 11:01:02 +0000 Subject: Renamed speech recognition code in preparation for introducing a new speech implementation. - The general-purpose speech recognition classes (that will be needed also by future code) have been renamed to SpeechRecognition*; - The IPC Messages and dispatcher(host) classes that are only related to the current input tag (x-webkit-speech) support have been renamed to InputTagSpeech*; - The namespace has been renamed from speech_input to speech; BUG=116954 TEST= Review URL: https://chromiumcodereview.appspot.com/9568002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@125378 0039d316-1c4b-4281-b951-d872f2087c98 --- content/browser/mock_content_browser_client.cc | 4 +- content/browser/mock_content_browser_client.h | 3 +- .../renderer_host/render_process_host_impl.cc | 6 +- .../browser/renderer_host/render_view_host_impl.cc | 4 +- content/browser/speech/audio_encoder.cc | 10 +- content/browser/speech/audio_encoder.h | 6 +- content/browser/speech/endpointer/endpointer.cc | 4 +- content/browser/speech/endpointer/endpointer.h | 6 +- .../speech/endpointer/endpointer_unittest.cc | 6 +- .../browser/speech/endpointer/energy_endpointer.cc | 4 +- .../browser/speech/endpointer/energy_endpointer.h | 6 +- .../speech/endpointer/energy_endpointer_params.cc | 6 +- .../speech/endpointer/energy_endpointer_params.h | 6 +- .../speech/input_tag_speech_dispatcher_host.cc | 239 ++++++++++++++ .../speech/input_tag_speech_dispatcher_host.h | 78 +++++ content/browser/speech/speech_input_browsertest.cc | 252 --------------- .../browser/speech/speech_input_dispatcher_host.cc | 239 -------------- .../browser/speech/speech_input_dispatcher_host.h | 78 ----- .../browser/speech/speech_input_manager_impl.cc | 351 -------------------- content/browser/speech/speech_input_manager_impl.h | 129 -------- .../speech/speech_recognition_browsertest.cc | 254 +++++++++++++++ .../speech/speech_recognition_manager_impl.cc | 353 +++++++++++++++++++++ .../speech/speech_recognition_manager_impl.h | 129 ++++++++ .../browser/speech/speech_recognition_request.cc | 24 +- .../browser/speech/speech_recognition_request.h | 10 +- .../speech/speech_recognition_request_unittest.cc | 26 +- content/browser/speech/speech_recognizer_impl.cc | 21 +- content/browser/speech/speech_recognizer_impl.h | 15 +- .../speech/speech_recognizer_impl_unittest.cc | 42 +-- 29 files changed, 1159 insertions(+), 1152 deletions(-) create mode 100644 content/browser/speech/input_tag_speech_dispatcher_host.cc create mode 100644 content/browser/speech/input_tag_speech_dispatcher_host.h delete mode 100644 content/browser/speech/speech_input_browsertest.cc delete mode 100644 content/browser/speech/speech_input_dispatcher_host.cc delete mode 100644 content/browser/speech/speech_input_dispatcher_host.h delete mode 100644 content/browser/speech/speech_input_manager_impl.cc delete mode 100644 content/browser/speech/speech_input_manager_impl.h create mode 100644 content/browser/speech/speech_recognition_browsertest.cc create mode 100644 content/browser/speech/speech_recognition_manager_impl.cc create mode 100644 content/browser/speech/speech_recognition_manager_impl.h (limited to 'content/browser') diff --git a/content/browser/mock_content_browser_client.cc b/content/browser/mock_content_browser_client.cc index 0de277c..395ade5 100644 --- a/content/browser/mock_content_browser_client.cc +++ b/content/browser/mock_content_browser_client.cc @@ -251,8 +251,8 @@ std::string MockContentBrowserClient::GetWorkerProcessTitle( void MockContentBrowserClient::ResourceDispatcherHostCreated() { } -SpeechInputManagerDelegate* - MockContentBrowserClient::GetSpeechInputManagerDelegate() { +SpeechRecognitionManagerDelegate* + MockContentBrowserClient::GetSpeechRecognitionManagerDelegate() { return NULL; } diff --git a/content/browser/mock_content_browser_client.h b/content/browser/mock_content_browser_client.h index e1a523c..e6fe8cb 100644 --- a/content/browser/mock_content_browser_client.h +++ b/content/browser/mock_content_browser_client.h @@ -139,7 +139,8 @@ class MockContentBrowserClient : public ContentBrowserClient { virtual std::string GetWorkerProcessTitle(const GURL& url, ResourceContext* context) OVERRIDE; virtual void ResourceDispatcherHostCreated() OVERRIDE; - virtual SpeechInputManagerDelegate* GetSpeechInputManagerDelegate() OVERRIDE; + virtual SpeechRecognitionManagerDelegate* + GetSpeechRecognitionManagerDelegate() OVERRIDE; virtual ui::Clipboard* GetClipboard() OVERRIDE; virtual net::NetLog* GetNetLog() OVERRIDE; virtual AccessTokenStore* CreateAccessTokenStore() OVERRIDE; diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index f02f5b1..ed47da7 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -114,7 +114,7 @@ #endif #if defined(ENABLE_INPUT_SPEECH) -#include "content/browser/speech/speech_input_dispatcher_host.h" +#include "content/browser/speech/input_tag_speech_dispatcher_host.h" #endif #include "third_party/skia/include/core/SkBitmap.h" @@ -493,9 +493,9 @@ void RenderProcessHostImpl::CreateMessageFilters() { channel_->AddFilter(new PepperMessageFilter(PepperMessageFilter::RENDERER, GetID(), resource_context)); #if defined(ENABLE_INPUT_SPEECH) - channel_->AddFilter(new speech_input::SpeechInputDispatcherHost( + channel_->AddFilter(new speech::InputTagSpeechDispatcherHost( GetID(), browser_context->GetRequestContext(), - browser_context->GetSpeechInputPreferences(), + browser_context->GetSpeechRecognitionPreferences(), content::BrowserMainLoop::GetAudioManager())); #endif channel_->AddFilter(new FileAPIMessageFilter( diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc index 12d5e04..faf2a96 100644 --- a/content/browser/renderer_host/render_view_host_impl.cc +++ b/content/browser/renderer_host/render_view_host_impl.cc @@ -28,7 +28,7 @@ #include "content/common/accessibility_messages.h" #include "content/common/desktop_notification_messages.h" #include "content/common/drag_messages.h" -#include "content/common/speech_input_messages.h" +#include "content/common/speech_recognition_messages.h" #include "content/common/swapped_out_messages.h" #include "content/common/view_messages.h" #include "content/port/browser/render_widget_host_view_port.h" @@ -1405,7 +1405,7 @@ void RenderViewHostImpl::DidCancelPopupMenu() { #endif void RenderViewHostImpl::ToggleSpeechInput() { - Send(new SpeechInputMsg_ToggleSpeechInput(GetRoutingID())); + Send(new InputTagSpeechMsg_ToggleSpeechInput(GetRoutingID())); } void RenderViewHostImpl::FilterURL(ChildProcessSecurityPolicyImpl* policy, diff --git a/content/browser/speech/audio_encoder.cc b/content/browser/speech/audio_encoder.cc index 683ec0e..83e0475 100644 --- a/content/browser/speech/audio_encoder.cc +++ b/content/browser/speech/audio_encoder.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -21,7 +21,7 @@ namespace { const char* const kContentTypeFLAC = "audio/x-flac; rate="; const int kFLACCompressionLevel = 0; // 0 for speed -class FLACEncoder : public speech_input::AudioEncoder { +class FLACEncoder : public speech::AudioEncoder { public: FLACEncoder(int sampling_rate, int bits_per_sample); virtual ~FLACEncoder(); @@ -107,7 +107,7 @@ const int kMaxSpeexFrameLength = 110; // (44kbps rate sampled at 32kHz). // make sure it is within the byte range. COMPILE_ASSERT(kMaxSpeexFrameLength <= 0xFF, invalidLength); -class SpeexEncoder : public speech_input::AudioEncoder { +class SpeexEncoder : public speech::AudioEncoder { public: explicit SpeexEncoder(int sampling_rate); virtual ~SpeexEncoder(); @@ -163,7 +163,7 @@ void SpeexEncoder::Encode(const short* samples, int num_samples) { } // namespace -namespace speech_input { +namespace speech { AudioEncoder* AudioEncoder::Create(Codec codec, int sampling_rate, @@ -205,4 +205,4 @@ void AudioEncoder::AppendToBuffer(std::string* item) { audio_buffers_.push_back(item); } -} // namespace speech_input +} // namespace speech diff --git a/content/browser/speech/audio_encoder.h b/content/browser/speech/audio_encoder.h index 7b0a246..92bc645 100644 --- a/content/browser/speech/audio_encoder.h +++ b/content/browser/speech/audio_encoder.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,7 +10,7 @@ #include "base/basictypes.h" -namespace speech_input { +namespace speech { // Provides a simple interface to encode raw audio using the various speech // codecs. @@ -54,6 +54,6 @@ class AudioEncoder { DISALLOW_COPY_AND_ASSIGN(AudioEncoder); }; -} // namespace speech_input +} // namespace speech #endif // CONTENT_BROWSER_SPEECH_AUDIO_ENCODER_H_ diff --git a/content/browser/speech/endpointer/endpointer.cc b/content/browser/speech/endpointer/endpointer.cc index 69c79a6..fe3e0bf 100644 --- a/content/browser/speech/endpointer/endpointer.cc +++ b/content/browser/speech/endpointer/endpointer.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -12,7 +12,7 @@ namespace { static const int kFrameRate = 50; // 1 frame = 20ms of audio. } -namespace speech_input { +namespace speech { Endpointer::Endpointer(int sample_rate) : speech_input_possibly_complete_silence_length_us_(-1), diff --git a/content/browser/speech/endpointer/endpointer.h b/content/browser/speech/endpointer/endpointer.h index 2d74839..9ba2018 100644 --- a/content/browser/speech/endpointer/endpointer.h +++ b/content/browser/speech/endpointer/endpointer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -11,7 +11,7 @@ class EpStatus; -namespace speech_input { +namespace speech { // A simple interface to the underlying energy-endpointer implementation, this // class lets callers provide audio as being recorded and let them poll to find @@ -147,6 +147,6 @@ class CONTENT_EXPORT Endpointer { int32 frame_size_; }; -} // namespace speech_input +} // namespace speech #endif // CONTENT_BROWSER_SPEECH_ENDPOINTER_ENDPOINTER_H_ diff --git a/content/browser/speech/endpointer/endpointer_unittest.cc b/content/browser/speech/endpointer/endpointer_unittest.cc index b71c01a..240e5dc 100644 --- a/content/browser/speech/endpointer/endpointer_unittest.cc +++ b/content/browser/speech/endpointer/endpointer_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -15,7 +15,7 @@ const int kFrameSize = kSampleRate / kFrameRate; // 160 samples. COMPILE_ASSERT(kFrameSize == 160, invalid_frame_size); } -namespace speech_input { +namespace speech { class FrameProcessor { public: @@ -142,4 +142,4 @@ TEST(EndpointerTest, TestEmbeddedEndpointerEvents) { endpointer.EndSession(); } -} // namespace speech_input +} // namespace speech diff --git a/content/browser/speech/endpointer/energy_endpointer.cc b/content/browser/speech/endpointer/energy_endpointer.cc index 39cf617..b88d7a3 100644 --- a/content/browser/speech/endpointer/energy_endpointer.cc +++ b/content/browser/speech/endpointer/energy_endpointer.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // @@ -41,7 +41,7 @@ float GetDecibel(float value) { } // namespace -namespace speech_input { +namespace speech { // Stores threshold-crossing histories for making decisions about the speech // state. diff --git a/content/browser/speech/endpointer/energy_endpointer.h b/content/browser/speech/endpointer/energy_endpointer.h index b440c4d..9db927a 100644 --- a/content/browser/speech/endpointer/energy_endpointer.h +++ b/content/browser/speech/endpointer/energy_endpointer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -44,7 +44,7 @@ #include "content/browser/speech/endpointer/energy_endpointer_params.h" #include "content/common/content_export.h" -namespace speech_input { +namespace speech { // Endpointer status codes enum EpStatus { @@ -150,6 +150,6 @@ class CONTENT_EXPORT EnergyEndpointer { DISALLOW_COPY_AND_ASSIGN(EnergyEndpointer); }; -} // namespace speech_input +} // namespace speech #endif // CONTENT_BROWSER_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_H_ diff --git a/content/browser/speech/endpointer/energy_endpointer_params.cc b/content/browser/speech/endpointer/energy_endpointer_params.cc index e110b24..89852f1 100644 --- a/content/browser/speech/endpointer/energy_endpointer_params.cc +++ b/content/browser/speech/endpointer/energy_endpointer_params.cc @@ -1,10 +1,10 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/speech/endpointer/energy_endpointer_params.h" -namespace speech_input { +namespace speech { EnergyEndpointerParams::EnergyEndpointerParams() { SetDefaults(); @@ -50,4 +50,4 @@ void EnergyEndpointerParams::operator=(const EnergyEndpointerParams& source) { contamination_rejection_period_ = source.contamination_rejection_period(); } -} // namespace speech_input +} // namespace speech diff --git a/content/browser/speech/endpointer/energy_endpointer_params.h b/content/browser/speech/endpointer/energy_endpointer_params.h index 5a3ea42..9641d9d 100644 --- a/content/browser/speech/endpointer/energy_endpointer_params.h +++ b/content/browser/speech/endpointer/energy_endpointer_params.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,7 +8,7 @@ #include "base/basictypes.h" #include "content/common/content_export.h" -namespace speech_input { +namespace speech { // Input parameters for the EnergyEndpointer class. class CONTENT_EXPORT EnergyEndpointerParams { @@ -133,6 +133,6 @@ class CONTENT_EXPORT EnergyEndpointerParams { float contamination_rejection_period_; }; -} // namespace speech_input +} // namespace speech #endif // CONTENT_BROWSER_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_PARAMS_H_ diff --git a/content/browser/speech/input_tag_speech_dispatcher_host.cc b/content/browser/speech/input_tag_speech_dispatcher_host.cc new file mode 100644 index 0000000..7c10270 --- /dev/null +++ b/content/browser/speech/input_tag_speech_dispatcher_host.cc @@ -0,0 +1,239 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/speech/input_tag_speech_dispatcher_host.h" + +#include "base/lazy_instance.h" +#include "content/browser/speech/speech_recognition_manager_impl.h" +#include "content/browser/speech/speech_recognizer_impl.h" +#include "content/common/speech_recognition_messages.h" +#include "content/public/browser/speech_recognition_preferences.h" + +using content::BrowserThread; + +namespace speech { + +//----------------------------- Callers ----------------------------- + +// A singleton class to map the tuple +// (render-process-id, render-view-id, requestid) to a single ID which is passed +// through rest of the speech code. +class InputTagSpeechDispatcherHost::Callers { + public: + // Creates a new ID for a given tuple. + int CreateId(int render_process_id, int render_view_id, int request_id); + + // Returns the ID for a tuple assuming the ID was created earlier. + int GetId(int render_process_id, int render_view_id, int request_id); + + // Removes the ID and associated tuple from the map. + void RemoveId(int id); + + // Getters for the various tuple elements for the given ID. + int render_process_id(int id); + int render_view_id(int id); + int request_id(int id); + + private: + struct CallerInfo { + int render_process_id; + int render_view_id; + int request_id; + }; + friend struct base::DefaultLazyInstanceTraits; + + Callers(); + + std::map callers_; + int next_id_; +}; + +static base::LazyInstance + g_callers = LAZY_INSTANCE_INITIALIZER; + +InputTagSpeechDispatcherHost::Callers::Callers() + : next_id_(1) { +} + +int InputTagSpeechDispatcherHost::Callers::GetId(int render_process_id, + int render_view_id, + int request_id) { + for (std::map::iterator it = callers_.begin(); + it != callers_.end(); it++) { + const CallerInfo& item = it->second; + if (item.render_process_id == render_process_id && + item.render_view_id == render_view_id && + item.request_id == request_id) { + return it->first; + } + } + + // Not finding an entry here is valid since a cancel/stop may have been issued + // by the renderer and before it received our response the user may have + // clicked the button to stop again. The caller of this method should take + // care of this case. + return 0; +} + +int InputTagSpeechDispatcherHost::Callers::CreateId(int render_process_id, + int render_view_id, + int request_id) { + CallerInfo info; + info.render_process_id = render_process_id; + info.render_view_id = render_view_id; + info.request_id = request_id; + callers_[next_id_] = info; + return next_id_++; +} + +void InputTagSpeechDispatcherHost::Callers::RemoveId(int id) { + callers_.erase(id); +} + +int InputTagSpeechDispatcherHost::Callers::render_process_id( + int id) { + return callers_[id].render_process_id; +} + +int InputTagSpeechDispatcherHost::Callers::render_view_id( + int id) { + return callers_[id].render_view_id; +} + +int InputTagSpeechDispatcherHost::Callers::request_id(int id) { + return callers_[id].request_id; +} + +//----------------------- InputTagSpeechDispatcherHost ---------------------- + +SpeechRecognitionManagerImpl* InputTagSpeechDispatcherHost::manager_; + +void InputTagSpeechDispatcherHost::set_manager( + SpeechRecognitionManagerImpl* manager) { + manager_ = manager; +} + +InputTagSpeechDispatcherHost::InputTagSpeechDispatcherHost( + int render_process_id, + net::URLRequestContextGetter* context_getter, + content::SpeechRecognitionPreferences* recognition_preferences, + AudioManager* audio_manager) + : render_process_id_(render_process_id), + may_have_pending_requests_(false), + context_getter_(context_getter), + recognition_preferences_(recognition_preferences), + audio_manager_(audio_manager) { + // This is initialized by Browser. Do not add any non-trivial + // initialization here, instead do it lazily when required (e.g. see the + // method |manager()|) or add an Init() method. +} + +InputTagSpeechDispatcherHost::~InputTagSpeechDispatcherHost() { + // If the renderer crashed for some reason or if we didn't receive a proper + // Cancel/Stop call for an existing session, cancel such active sessions now. + // We first check if this dispatcher received any speech IPC requst so that + // we don't end up creating the speech input manager for web pages which don't + // use speech input. + if (may_have_pending_requests_) + manager()->CancelAllRequestsWithDelegate(this); +} + +SpeechRecognitionManagerImpl* InputTagSpeechDispatcherHost::manager() { + if (manager_) + return manager_; +#if defined(ENABLE_INPUT_SPEECH) + return SpeechRecognitionManagerImpl::GetInstance(); +#else + return NULL; +#endif +} + +bool InputTagSpeechDispatcherHost::OnMessageReceived( + const IPC::Message& message, bool* message_was_ok) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + bool handled = true; + IPC_BEGIN_MESSAGE_MAP_EX(InputTagSpeechDispatcherHost, message, + *message_was_ok) + IPC_MESSAGE_HANDLER(InputTagSpeechHostMsg_StartRecognition, + OnStartRecognition) + IPC_MESSAGE_HANDLER(InputTagSpeechHostMsg_CancelRecognition, + OnCancelRecognition) + IPC_MESSAGE_HANDLER(InputTagSpeechHostMsg_StopRecording, + OnStopRecording) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + if (handled) + may_have_pending_requests_ = true; + return handled; +} + +void InputTagSpeechDispatcherHost::OnStartRecognition( + const InputTagSpeechHostMsg_StartRecognition_Params ¶ms) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + int caller_id = g_callers.Get().CreateId( + render_process_id_, params.render_view_id, params.request_id); + manager()->StartRecognition(this, caller_id, + render_process_id_, + params.render_view_id, params.element_rect, + params.language, params.grammar, + params.origin_url, + context_getter_.get(), + recognition_preferences_.get()); +} + +void InputTagSpeechDispatcherHost::OnCancelRecognition(int render_view_id, + int request_id) { + int caller_id = g_callers.Get().GetId( + render_process_id_, render_view_id, request_id); + if (caller_id) { + manager()->CancelRecognition(caller_id); + // Request sequence ended so remove mapping. + g_callers.Get().RemoveId(caller_id); + } +} + +void InputTagSpeechDispatcherHost::OnStopRecording(int render_view_id, + int request_id) { + int caller_id = g_callers.Get().GetId( + render_process_id_, render_view_id, request_id); + if (caller_id) + manager()->StopRecording(caller_id); +} + +void InputTagSpeechDispatcherHost::SetRecognitionResult( + int caller_id, const content::SpeechRecognitionResult& result) { + VLOG(1) << "InputTagSpeechDispatcherHost::SetRecognitionResult enter"; + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + int caller_render_view_id = g_callers.Get().render_view_id(caller_id); + int caller_request_id = g_callers.Get().request_id(caller_id); + Send(new InputTagSpeechMsg_SetRecognitionResult(caller_render_view_id, + caller_request_id, + result)); + VLOG(1) << "InputTagSpeechDispatcherHost::SetRecognitionResult exit"; +} + +void InputTagSpeechDispatcherHost::DidCompleteRecording(int caller_id) { + VLOG(1) << "InputTagSpeechDispatcherHost::DidCompleteRecording enter"; + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + int caller_render_view_id = g_callers.Get().render_view_id(caller_id); + int caller_request_id = g_callers.Get().request_id(caller_id); + Send(new InputTagSpeechMsg_RecordingComplete(caller_render_view_id, + caller_request_id)); + VLOG(1) << "InputTagSpeechDispatcherHost::DidCompleteRecording exit"; +} + +void InputTagSpeechDispatcherHost::DidCompleteRecognition(int caller_id) { + VLOG(1) << "InputTagSpeechDispatcherHost::DidCompleteRecognition enter"; + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + int caller_render_view_id = + g_callers.Get().render_view_id(caller_id); + int caller_request_id = g_callers.Get().request_id(caller_id); + Send(new InputTagSpeechMsg_RecognitionComplete(caller_render_view_id, + caller_request_id)); + // Request sequence ended, so remove mapping. + g_callers.Get().RemoveId(caller_id); + VLOG(1) << "InputTagSpeechDispatcherHost::DidCompleteRecognition exit"; +} + +} // namespace speech diff --git a/content/browser/speech/input_tag_speech_dispatcher_host.h b/content/browser/speech/input_tag_speech_dispatcher_host.h new file mode 100644 index 0000000..61d68b0 --- /dev/null +++ b/content/browser/speech/input_tag_speech_dispatcher_host.h @@ -0,0 +1,78 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_SPEECH_INPUT_TAG_SPEECH_DISPATCHER_HOST_H_ +#define CONTENT_BROWSER_SPEECH_INPUT_TAG_SPEECH_DISPATCHER_HOST_H_ + +#include "base/memory/scoped_ptr.h" +#include "content/common/content_export.h" +#include "content/public/browser/browser_message_filter.h" +#include "net/url_request/url_request_context_getter.h" + +class AudioManager; +struct InputTagSpeechHostMsg_StartRecognition_Params; + +namespace content { +class SpeechRecognitionPreferences; +struct SpeechRecognitionResult; +} + +namespace speech { + +class SpeechRecognitionManagerImpl; + +// InputTagSpeechDispatcherHost is a delegate for Speech API messages used by +// RenderMessageFilter. +// It's the complement of InputTagSpeechDispatcher (owned by RenderView). +class CONTENT_EXPORT InputTagSpeechDispatcherHost + : public content::BrowserMessageFilter { + public: + class Callers; + + InputTagSpeechDispatcherHost( + int render_process_id, + net::URLRequestContextGetter* context_getter, + content::SpeechRecognitionPreferences* recognition_preferences, + AudioManager* audio_manager); + + // Methods called by SpeechRecognitionManagerImpl. + void SetRecognitionResult(int caller_id, + const content::SpeechRecognitionResult& result); + void DidCompleteRecording(int caller_id); + void DidCompleteRecognition(int caller_id); + + // content::BrowserMessageFilter implementation. + virtual bool OnMessageReceived(const IPC::Message& message, + bool* message_was_ok) OVERRIDE; + + // Singleton manager setter useful for tests. + static void set_manager(SpeechRecognitionManagerImpl* manager); + + private: + virtual ~InputTagSpeechDispatcherHost(); + + void OnStartRecognition( + const InputTagSpeechHostMsg_StartRecognition_Params ¶ms); + void OnCancelRecognition(int render_view_id, int request_id); + void OnStopRecording(int render_view_id, int request_id); + + // Returns the speech recognition manager to forward events to, creating one + // if needed. + SpeechRecognitionManagerImpl* manager(); + + int render_process_id_; + bool may_have_pending_requests_; // Set if we received any speech IPC request + + scoped_refptr context_getter_; + scoped_refptr recognition_preferences_; + AudioManager* audio_manager_; + + static SpeechRecognitionManagerImpl* manager_; + + DISALLOW_COPY_AND_ASSIGN(InputTagSpeechDispatcherHost); +}; + +} // namespace speech + +#endif // CONTENT_BROWSER_SPEECH_INPUT_TAG_SPEECH_DISPATCHER_HOST_H_ diff --git a/content/browser/speech/speech_input_browsertest.cc b/content/browser/speech/speech_input_browsertest.cc deleted file mode 100644 index 6083ca0..0000000 --- a/content/browser/speech/speech_input_browsertest.cc +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/bind.h" -#include "base/command_line.h" -#include "base/file_path.h" -#include "base/string_number_conversions.h" -#include "base/synchronization/waitable_event.h" -#include "base/utf_string_conversions.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/test/base/in_process_browser_test.h" -#include "chrome/test/base/ui_test_utils.h" -#include "content/browser/renderer_host/render_view_host_impl.h" -#include "content/browser/speech/speech_input_dispatcher_host.h" -#include "content/browser/speech/speech_input_manager_impl.h" -#include "content/browser/tab_contents/tab_contents.h" -#include "content/public/browser/notification_types.h" -#include "content/public/common/content_switches.h" -#include "content/public/common/speech_input_result.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" - -using content::NavigationController; -using content::WebContents; - -namespace speech_input { -class FakeSpeechInputManager; -} - -namespace speech_input { - -const char kTestResult[] = "Pictures of the moon"; - -class FakeSpeechInputManager : public SpeechInputManagerImpl { - public: - FakeSpeechInputManager() - : caller_id_(0), - delegate_(NULL), - did_cancel_all_(false), - should_send_fake_response_(true), - recognition_started_event_(false, false) { - } - - std::string grammar() { - return grammar_; - } - - bool did_cancel_all() { - return did_cancel_all_; - } - - void set_should_send_fake_response(bool send) { - should_send_fake_response_ = send; - } - - bool should_send_fake_response() { - return should_send_fake_response_; - } - - base::WaitableEvent& recognition_started_event() { - return recognition_started_event_; - } - - // SpeechInputManager methods. - virtual void StartRecognition( - SpeechInputDispatcherHost* delegate, - int caller_id, - int render_process_id, - int render_view_id, - const gfx::Rect& element_rect, - const std::string& language, - const std::string& grammar, - const std::string& origin_url, - net::URLRequestContextGetter* context_getter, - content::SpeechInputPreferences* speech_input_prefs) OVERRIDE { - VLOG(1) << "StartRecognition invoked."; - EXPECT_EQ(0, caller_id_); - EXPECT_EQ(NULL, delegate_); - caller_id_ = caller_id; - delegate_ = delegate; - grammar_ = grammar; - if (should_send_fake_response_) { - // Give the fake result in a short while. - MessageLoop::current()->PostTask(FROM_HERE, base::Bind( - &FakeSpeechInputManager::SetFakeRecognitionResult, - // This class does not need to be refcounted (typically done by - // PostTask) since it will outlive the test and gets released only - // when the test shuts down. Disabling refcounting here saves a bit - // of unnecessary code and the factory method can return a plain - // pointer below as required by the real code. - base::Unretained(this))); - } - recognition_started_event_.Signal(); - } - virtual void CancelRecognition(int caller_id) OVERRIDE { - VLOG(1) << "CancelRecognition invoked."; - EXPECT_EQ(caller_id_, caller_id); - caller_id_ = 0; - delegate_ = NULL; - } - virtual void StopRecording(int caller_id) OVERRIDE { - VLOG(1) << "StopRecording invoked."; - EXPECT_EQ(caller_id_, caller_id); - // Nothing to do here since we aren't really recording. - } - virtual void CancelAllRequestsWithDelegate( - SpeechInputDispatcherHost* delegate) OVERRIDE { - VLOG(1) << "CancelAllRequestsWithDelegate invoked."; - // delegate_ is set to NULL if a fake result was received (see below), so - // check that delegate_ matches the incoming parameter only when there is - // no fake result sent. - EXPECT_TRUE(should_send_fake_response_ || delegate_ == delegate); - did_cancel_all_ = true; - } - - private: - void SetFakeRecognitionResult() { - if (caller_id_) { // Do a check in case we were cancelled.. - VLOG(1) << "Setting fake recognition result."; - delegate_->DidCompleteRecording(caller_id_); - content::SpeechInputResult results; - results.hypotheses.push_back(content::SpeechInputHypothesis( - ASCIIToUTF16(kTestResult), 1.0)); - delegate_->SetRecognitionResult(caller_id_, results); - delegate_->DidCompleteRecognition(caller_id_); - caller_id_ = 0; - delegate_ = NULL; - VLOG(1) << "Finished setting fake recognition result."; - } - } - - int caller_id_; - SpeechInputDispatcherHost* delegate_; - std::string grammar_; - bool did_cancel_all_; - bool should_send_fake_response_; - base::WaitableEvent recognition_started_event_; -}; - -class SpeechInputBrowserTest : public InProcessBrowserTest { - public: - // InProcessBrowserTest methods - virtual void SetUpCommandLine(CommandLine* command_line) { - EXPECT_TRUE(!command_line->HasSwitch(switches::kDisableSpeechInput)); - } - - GURL testUrl(const FilePath::CharType* filename) { - const FilePath kTestDir(FILE_PATH_LITERAL("speech")); - return ui_test_utils::GetTestUrl(kTestDir, FilePath(filename)); - } - - protected: - void LoadAndStartSpeechInputTest(const FilePath::CharType* filename) { - // The test page calculates the speech button's coordinate in the page on - // load & sets that coordinate in the URL fragment. We send mouse down & up - // events at that coordinate to trigger speech recognition. - GURL test_url = testUrl(filename); - ui_test_utils::NavigateToURL(browser(), test_url); - - WebKit::WebMouseEvent mouse_event; - mouse_event.type = WebKit::WebInputEvent::MouseDown; - mouse_event.button = WebKit::WebMouseEvent::ButtonLeft; - mouse_event.x = 0; - mouse_event.y = 0; - mouse_event.clickCount = 1; - WebContents* web_contents = browser()->GetSelectedWebContents(); - - ui_test_utils::WindowedNotificationObserver observer( - content::NOTIFICATION_LOAD_STOP, - content::Source(&web_contents->GetController())); - web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event); - mouse_event.type = WebKit::WebInputEvent::MouseUp; - web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event); - fake_speech_input_manager_.recognition_started_event().Wait(); - - // We should wait for a navigation event, raised by the test page JS code - // upon the onwebkitspeechchange event, in all cases except when the - // speech response is inhibited. - if (fake_speech_input_manager_.should_send_fake_response()) - observer.Wait(); - } - - void RunSpeechInputTest(const FilePath::CharType* filename) { - // The fake speech input manager would receive the speech input - // request and return the test string as recognition result. The test page - // then sets the URL fragment as 'pass' if it received the expected string. - LoadAndStartSpeechInputTest(filename); - - EXPECT_EQ("pass", browser()->GetSelectedWebContents()->GetURL().ref()); - } - - // InProcessBrowserTest methods. - virtual void SetUpInProcessBrowserTestFixture() { - fake_speech_input_manager_.set_should_send_fake_response(true); - speech_input_manager_ = &fake_speech_input_manager_; - - // Inject the fake manager factory so that the test result is returned to - // the web page. - SpeechInputDispatcherHost::set_manager(speech_input_manager_); - } - - virtual void TearDownInProcessBrowserTestFixture() { - speech_input_manager_ = NULL; - } - - FakeSpeechInputManager fake_speech_input_manager_; - - // This is used by the static |fakeManager|, and it is a pointer rather than a - // direct instance per the style guide. - static SpeechInputManagerImpl* speech_input_manager_; -}; - -SpeechInputManagerImpl* SpeechInputBrowserTest::speech_input_manager_ = NULL; - -// TODO(satish): Once this flakiness has been fixed, add a second test here to -// check for sending many clicks in succession to the speech button and verify -// that it doesn't cause any crash but works as expected. This should act as the -// test for http://crbug.com/59173 -// -// TODO(satish): Similar to above, once this flakiness has been fixed add -// another test here to check that when speech recognition is in progress and -// a renderer crashes, we get a call to -// SpeechInputManager::CancelAllRequestsWithDelegate. -IN_PROC_BROWSER_TEST_F(SpeechInputBrowserTest, TestBasicRecognition) { - RunSpeechInputTest(FILE_PATH_LITERAL("basic_recognition.html")); - EXPECT_TRUE(fake_speech_input_manager_.grammar().empty()); -} - -IN_PROC_BROWSER_TEST_F(SpeechInputBrowserTest, GrammarAttribute) { - RunSpeechInputTest(FILE_PATH_LITERAL("grammar_attribute.html")); - EXPECT_EQ("http://example.com/grammar.xml", - fake_speech_input_manager_.grammar()); -} - -IN_PROC_BROWSER_TEST_F(SpeechInputBrowserTest, TestCancelAll) { - // The test checks that the cancel-all callback gets issued when a session - // is pending, so don't send a fake response. - // We are not expecting a navigation event being raised from the JS of the - // test page JavaScript in this case. - fake_speech_input_manager_.set_should_send_fake_response(false); - - LoadAndStartSpeechInputTest(FILE_PATH_LITERAL("basic_recognition.html")); - - // Make the renderer crash. This should trigger SpeechInputDispatcherHost to - // cancel all pending sessions. - GURL test_url("about:crash"); - ui_test_utils::NavigateToURL(browser(), test_url); - - EXPECT_TRUE(fake_speech_input_manager_.did_cancel_all()); -} - -} // namespace speech_input diff --git a/content/browser/speech/speech_input_dispatcher_host.cc b/content/browser/speech/speech_input_dispatcher_host.cc deleted file mode 100644 index 3883969..0000000 --- a/content/browser/speech/speech_input_dispatcher_host.cc +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/speech/speech_input_dispatcher_host.h" - -#include "base/lazy_instance.h" -#include "content/browser/speech/speech_input_manager_impl.h" -#include "content/browser/speech/speech_recognizer_impl.h" -#include "content/common/speech_input_messages.h" -#include "content/public/browser/speech_input_preferences.h" - -using content::BrowserThread; - -namespace speech_input { - -//----------------------------- SpeechInputCallers ----------------------------- - -// A singleton class to map the tuple -// (render-process-id, render-view-id, requestid) to a single ID which is passed -// through rest of the speech code. -class SpeechInputDispatcherHost::SpeechInputCallers { - public: - // Creates a new ID for a given tuple. - int CreateId(int render_process_id, int render_view_id, int request_id); - - // Returns the ID for a tuple assuming the ID was created earlier. - int GetId(int render_process_id, int render_view_id, int request_id); - - // Removes the ID and associated tuple from the map. - void RemoveId(int id); - - // Getters for the various tuple elements for the given ID. - int render_process_id(int id); - int render_view_id(int id); - int request_id(int id); - - private: - struct CallerInfo { - int render_process_id; - int render_view_id; - int request_id; - }; - friend struct base::DefaultLazyInstanceTraits; - - SpeechInputCallers(); - - std::map callers_; - int next_id_; -}; - -static base::LazyInstance - g_speech_input_callers = LAZY_INSTANCE_INITIALIZER; - -SpeechInputDispatcherHost::SpeechInputCallers::SpeechInputCallers() - : next_id_(1) { -} - -int SpeechInputDispatcherHost::SpeechInputCallers::GetId(int render_process_id, - int render_view_id, - int request_id) { - for (std::map::iterator it = callers_.begin(); - it != callers_.end(); it++) { - const CallerInfo& item = it->second; - if (item.render_process_id == render_process_id && - item.render_view_id == render_view_id && - item.request_id == request_id) { - return it->first; - } - } - - // Not finding an entry here is valid since a cancel/stop may have been issued - // by the renderer and before it received our response the user may have - // clicked the button to stop again. The caller of this method should take - // care of this case. - return 0; -} - -int SpeechInputDispatcherHost::SpeechInputCallers::CreateId( - int render_process_id, - int render_view_id, - int request_id) { - CallerInfo info; - info.render_process_id = render_process_id; - info.render_view_id = render_view_id; - info.request_id = request_id; - callers_[next_id_] = info; - return next_id_++; -} - -void SpeechInputDispatcherHost::SpeechInputCallers::RemoveId(int id) { - callers_.erase(id); -} - -int SpeechInputDispatcherHost::SpeechInputCallers::render_process_id(int id) { - return callers_[id].render_process_id; -} - -int SpeechInputDispatcherHost::SpeechInputCallers::render_view_id(int id) { - return callers_[id].render_view_id; -} - -int SpeechInputDispatcherHost::SpeechInputCallers::request_id(int id) { - return callers_[id].request_id; -} - -//-------------------------- SpeechInputDispatcherHost ------------------------- - -SpeechInputManagerImpl* SpeechInputDispatcherHost::manager_; - -void SpeechInputDispatcherHost::set_manager(SpeechInputManagerImpl* manager) { - manager_ = manager; -} - -SpeechInputDispatcherHost::SpeechInputDispatcherHost( - int render_process_id, - net::URLRequestContextGetter* context_getter, - content::SpeechInputPreferences* speech_input_preferences, - AudioManager* audio_manager) - : render_process_id_(render_process_id), - may_have_pending_requests_(false), - context_getter_(context_getter), - speech_input_preferences_(speech_input_preferences), - audio_manager_(audio_manager) { - // This is initialized by Browser. Do not add any non-trivial - // initialization here, instead do it lazily when required (e.g. see the - // method |manager()|) or add an Init() method. -} - -SpeechInputDispatcherHost::~SpeechInputDispatcherHost() { - // If the renderer crashed for some reason or if we didn't receive a proper - // Cancel/Stop call for an existing session, cancel such active sessions now. - // We first check if this dispatcher received any speech IPC requst so that - // we don't end up creating the speech input manager for web pages which don't - // use speech input. - if (may_have_pending_requests_) - manager()->CancelAllRequestsWithDelegate(this); -} - -SpeechInputManagerImpl* SpeechInputDispatcherHost::manager() { - if (manager_) - return manager_; -#if defined(ENABLE_INPUT_SPEECH) - return SpeechInputManagerImpl::GetInstance(); -#else - return NULL; -#endif -} - -bool SpeechInputDispatcherHost::OnMessageReceived( - const IPC::Message& message, bool* message_was_ok) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - bool handled = true; - IPC_BEGIN_MESSAGE_MAP_EX(SpeechInputDispatcherHost, message, - *message_was_ok) - IPC_MESSAGE_HANDLER(SpeechInputHostMsg_StartRecognition, - OnStartRecognition) - IPC_MESSAGE_HANDLER(SpeechInputHostMsg_CancelRecognition, - OnCancelRecognition) - IPC_MESSAGE_HANDLER(SpeechInputHostMsg_StopRecording, - OnStopRecording) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - if (handled) - may_have_pending_requests_ = true; - return handled; -} - -void SpeechInputDispatcherHost::OnStartRecognition( - const SpeechInputHostMsg_StartRecognition_Params ¶ms) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - int caller_id = g_speech_input_callers.Get().CreateId( - render_process_id_, params.render_view_id, params.request_id); - manager()->StartRecognition(this, caller_id, - render_process_id_, - params.render_view_id, params.element_rect, - params.language, params.grammar, - params.origin_url, - context_getter_.get(), - speech_input_preferences_.get()); -} - -void SpeechInputDispatcherHost::OnCancelRecognition(int render_view_id, - int request_id) { - int caller_id = g_speech_input_callers.Get().GetId( - render_process_id_, render_view_id, request_id); - if (caller_id) { - manager()->CancelRecognition(caller_id); - // Request sequence ended so remove mapping. - g_speech_input_callers.Get().RemoveId(caller_id); - } -} - -void SpeechInputDispatcherHost::OnStopRecording(int render_view_id, - int request_id) { - int caller_id = g_speech_input_callers.Get().GetId( - render_process_id_, render_view_id, request_id); - if (caller_id) - manager()->StopRecording(caller_id); -} - -void SpeechInputDispatcherHost::SetRecognitionResult( - int caller_id, const content::SpeechInputResult& result) { - VLOG(1) << "SpeechInputDispatcherHost::SetRecognitionResult enter"; - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - int caller_render_view_id = - g_speech_input_callers.Get().render_view_id(caller_id); - int caller_request_id = g_speech_input_callers.Get().request_id(caller_id); - Send(new SpeechInputMsg_SetRecognitionResult(caller_render_view_id, - caller_request_id, - result)); - VLOG(1) << "SpeechInputDispatcherHost::SetRecognitionResult exit"; -} - -void SpeechInputDispatcherHost::DidCompleteRecording(int caller_id) { - VLOG(1) << "SpeechInputDispatcherHost::DidCompleteRecording enter"; - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - int caller_render_view_id = - g_speech_input_callers.Get().render_view_id(caller_id); - int caller_request_id = g_speech_input_callers.Get().request_id(caller_id); - Send(new SpeechInputMsg_RecordingComplete(caller_render_view_id, - caller_request_id)); - VLOG(1) << "SpeechInputDispatcherHost::DidCompleteRecording exit"; -} - -void SpeechInputDispatcherHost::DidCompleteRecognition(int caller_id) { - VLOG(1) << "SpeechInputDispatcherHost::DidCompleteRecognition enter"; - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - int caller_render_view_id = - g_speech_input_callers.Get().render_view_id(caller_id); - int caller_request_id = g_speech_input_callers.Get().request_id(caller_id); - Send(new SpeechInputMsg_RecognitionComplete(caller_render_view_id, - caller_request_id)); - // Request sequence ended, so remove mapping. - g_speech_input_callers.Get().RemoveId(caller_id); - VLOG(1) << "SpeechInputDispatcherHost::DidCompleteRecognition exit"; -} - -} // namespace speech_input diff --git a/content/browser/speech/speech_input_dispatcher_host.h b/content/browser/speech/speech_input_dispatcher_host.h deleted file mode 100644 index 5e38f37..0000000 --- a/content/browser/speech/speech_input_dispatcher_host.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_SPEECH_SPEECH_INPUT_DISPATCHER_HOST_H_ -#define CONTENT_BROWSER_SPEECH_SPEECH_INPUT_DISPATCHER_HOST_H_ - -#include "base/memory/scoped_ptr.h" -#include "content/common/content_export.h" -#include "content/public/browser/browser_message_filter.h" -#include "net/url_request/url_request_context_getter.h" - -class AudioManager; -struct SpeechInputHostMsg_StartRecognition_Params; - -namespace content { -class SpeechInputPreferences; -struct SpeechInputResult; -} - -namespace speech_input { - -class SpeechInputManagerImpl; - -// SpeechInputDispatcherHost is a delegate for Speech API messages used by -// RenderMessageFilter. -// It's the complement of SpeechInputDispatcher (owned by RenderView). -class CONTENT_EXPORT SpeechInputDispatcherHost - : public content::BrowserMessageFilter { - public: - class SpeechInputCallers; - - SpeechInputDispatcherHost( - int render_process_id, - net::URLRequestContextGetter* context_getter, - content::SpeechInputPreferences* speech_input_preferences, - AudioManager* audio_manager); - - // Methods called by SpeechInputManagerImpl. - void SetRecognitionResult(int caller_id, - const content::SpeechInputResult& result); - void DidCompleteRecording(int caller_id); - void DidCompleteRecognition(int caller_id); - - // content::BrowserMessageFilter implementation. - virtual bool OnMessageReceived(const IPC::Message& message, - bool* message_was_ok) OVERRIDE; - - // Singleton manager setter useful for tests. - static void set_manager(SpeechInputManagerImpl* manager); - - private: - virtual ~SpeechInputDispatcherHost(); - - void OnStartRecognition( - const SpeechInputHostMsg_StartRecognition_Params ¶ms); - void OnCancelRecognition(int render_view_id, int request_id); - void OnStopRecording(int render_view_id, int request_id); - - // Returns the speech input manager to forward events to, creating one if - // needed. - SpeechInputManagerImpl* manager(); - - int render_process_id_; - bool may_have_pending_requests_; // Set if we received any speech IPC request - - scoped_refptr context_getter_; - scoped_refptr speech_input_preferences_; - AudioManager* audio_manager_; - - static SpeechInputManagerImpl* manager_; - - DISALLOW_COPY_AND_ASSIGN(SpeechInputDispatcherHost); -}; - -} // namespace speech_input - -#endif // CONTENT_BROWSER_SPEECH_SPEECH_INPUT_DISPATCHER_HOST_H_ diff --git a/content/browser/speech/speech_input_manager_impl.cc b/content/browser/speech/speech_input_manager_impl.cc deleted file mode 100644 index 6df0b3d..0000000 --- a/content/browser/speech/speech_input_manager_impl.cc +++ /dev/null @@ -1,351 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/speech/speech_input_manager_impl.h" - -#include "base/bind.h" -#include "content/browser/browser_main_loop.h" -#include "content/browser/renderer_host/render_view_host_impl.h" -#include "content/browser/speech/speech_input_dispatcher_host.h" -#include "content/browser/speech/speech_recognizer_impl.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/content_browser_client.h" -#include "content/public/browser/render_view_host_delegate.h" -#include "content/public/browser/resource_context.h" -#include "content/public/browser/speech_input_manager_delegate.h" -#include "content/public/browser/speech_input_preferences.h" -#include "content/public/common/view_type.h" - -using content::BrowserMainLoop; -using content::BrowserThread; -using content::SpeechInputManagerDelegate; - -content::SpeechInputManager* content::SpeechInputManager::GetInstance() { - return speech_input::SpeechInputManagerImpl::GetInstance(); -} - -namespace speech_input { - -struct SpeechInputManagerImpl::SpeechInputParams { - SpeechInputParams(SpeechInputDispatcherHost* delegate, - int caller_id, - int render_process_id, - int render_view_id, - const gfx::Rect& element_rect, - const std::string& language, - const std::string& grammar, - const std::string& origin_url, - net::URLRequestContextGetter* context_getter, - content::SpeechInputPreferences* speech_input_prefs) - : delegate(delegate), - caller_id(caller_id), - render_process_id(render_process_id), - render_view_id(render_view_id), - element_rect(element_rect), - language(language), - grammar(grammar), - origin_url(origin_url), - context_getter(context_getter), - speech_input_prefs(speech_input_prefs) { - } - - SpeechInputDispatcherHost* delegate; - int caller_id; - int render_process_id; - int render_view_id; - gfx::Rect element_rect; - std::string language; - std::string grammar; - std::string origin_url; - net::URLRequestContextGetter* context_getter; - content::SpeechInputPreferences* speech_input_prefs; -}; - -SpeechInputManagerImpl* SpeechInputManagerImpl::GetInstance() { - return Singleton::get(); -} - -SpeechInputManagerImpl::SpeechInputManagerImpl() - : can_report_metrics_(false), - recording_caller_id_(0) { - delegate_ = - content::GetContentClient()->browser()->GetSpeechInputManagerDelegate(); -} - -SpeechInputManagerImpl::~SpeechInputManagerImpl() { - while (requests_.begin() != requests_.end()) - CancelRecognition(requests_.begin()->first); -} - -bool SpeechInputManagerImpl::HasAudioInputDevices() { - return BrowserMainLoop::GetAudioManager()->HasAudioInputDevices(); -} - -bool SpeechInputManagerImpl::IsRecordingInProcess() { - return BrowserMainLoop::GetAudioManager()->IsRecordingInProcess(); -} - -string16 SpeechInputManagerImpl::GetAudioInputDeviceModel() { - return BrowserMainLoop::GetAudioManager()->GetAudioInputDeviceModel(); -} - -bool SpeechInputManagerImpl::HasPendingRequest(int caller_id) const { - return requests_.find(caller_id) != requests_.end(); -} - -SpeechInputDispatcherHost* SpeechInputManagerImpl::GetDelegate( - int caller_id) const { - return requests_.find(caller_id)->second.delegate; -} - -void SpeechInputManagerImpl::ShowAudioInputSettings() { - // Since AudioManager::ShowAudioInputSettings can potentially launch external - // processes, do that in the FILE thread to not block the calling threads. - if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&SpeechInputManagerImpl::ShowAudioInputSettings, - base::Unretained(this))); - return; - } - - AudioManager* audio_manager = BrowserMainLoop::GetAudioManager(); - DCHECK(audio_manager->CanShowAudioInputSettings()); - if (audio_manager->CanShowAudioInputSettings()) - audio_manager->ShowAudioInputSettings(); -} - -void SpeechInputManagerImpl::StartRecognition( - SpeechInputDispatcherHost* delegate, - int caller_id, - int render_process_id, - int render_view_id, - const gfx::Rect& element_rect, - const std::string& language, - const std::string& grammar, - const std::string& origin_url, - net::URLRequestContextGetter* context_getter, - content::SpeechInputPreferences* speech_input_prefs) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind( - &SpeechInputManagerImpl::CheckRenderViewTypeAndStartRecognition, - base::Unretained(this), - SpeechInputParams( - delegate, caller_id, render_process_id, render_view_id, - element_rect, language, grammar, origin_url, context_getter, - speech_input_prefs))); -} - -void SpeechInputManagerImpl::CheckRenderViewTypeAndStartRecognition( - const SpeechInputParams& params) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - RenderViewHostImpl* render_view_host = RenderViewHostImpl::FromID( - params.render_process_id, params.render_view_id); - if (!render_view_host || !render_view_host->GetDelegate()) - return; - - // For host delegates other than TabContents we can't reliably show a popup, - // including the speech input bubble. In these cases for privacy reasons we - // don't want to start recording if the user can't be properly notified. - // An example of this is trying to show the speech input bubble within an - // extension popup: http://crbug.com/92083. In these situations the speech - // input extension API should be used instead. - if (render_view_host->GetDelegate()->GetRenderViewType() == - content::VIEW_TYPE_TAB_CONTENTS) { - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&SpeechInputManagerImpl::ProceedStartingRecognition, - base::Unretained(this), params)); - } -} - -void SpeechInputManagerImpl::ProceedStartingRecognition( - const SpeechInputParams& params) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - DCHECK(!HasPendingRequest(params.caller_id)); - - if (delegate_) { - delegate_->ShowRecognitionRequested( - params.caller_id, params.render_process_id, params.render_view_id, - params.element_rect); - delegate_->GetRequestInfo(&can_report_metrics_, &request_info_); - } - - SpeechInputRequest* request = &requests_[params.caller_id]; - request->delegate = params.delegate; - request->recognizer = new SpeechRecognizerImpl( - this, params.caller_id, params.language, params.grammar, - params.context_getter, params.speech_input_prefs->FilterProfanities(), - request_info_, can_report_metrics_ ? params.origin_url : ""); - request->is_active = false; - - StartRecognitionForRequest(params.caller_id); -} - -void SpeechInputManagerImpl::StartRecognitionForRequest(int caller_id) { - SpeechRecognizerMap::iterator request = requests_.find(caller_id); - if (request == requests_.end()) { - NOTREACHED(); - return; - } - - // We should not currently be recording for the caller. - CHECK(recording_caller_id_ != caller_id); - - // If we are currently recording audio for another caller, abort that cleanly. - if (recording_caller_id_) - CancelRecognitionAndInformDelegate(recording_caller_id_); - - if (!HasAudioInputDevices()) { - if (delegate_) { - delegate_->ShowMicError( - caller_id, SpeechInputManagerDelegate::MIC_ERROR_NO_DEVICE_AVAILABLE); - } - } else if (IsRecordingInProcess()) { - if (delegate_) { - delegate_->ShowMicError( - caller_id, SpeechInputManagerDelegate::MIC_ERROR_DEVICE_IN_USE); - } - } else { - recording_caller_id_ = caller_id; - requests_[caller_id].is_active = true; - requests_[caller_id].recognizer->StartRecording(); - if (delegate_) - delegate_->ShowWarmUp(caller_id); - } -} - -void SpeechInputManagerImpl::CancelRecognitionForRequest(int caller_id) { - // Ignore if the caller id was not in our active recognizers list because the - // user might have clicked more than once, or recognition could have been - // ended due to other reasons before the user click was processed. - if (!HasPendingRequest(caller_id)) - return; - - CancelRecognitionAndInformDelegate(caller_id); -} - -void SpeechInputManagerImpl::FocusLostForRequest(int caller_id) { - // See above comment. - if (!HasPendingRequest(caller_id)) - return; - - // If this is an ongoing recording or if we were displaying an error message - // to the user, abort it since user has switched focus. Otherwise - // recognition has started and keep that going so user can start speaking to - // another element while this gets the results in parallel. - if (recording_caller_id_ == caller_id || !requests_[caller_id].is_active) - CancelRecognitionAndInformDelegate(caller_id); -} - -void SpeechInputManagerImpl::CancelRecognition(int caller_id) { - DCHECK(HasPendingRequest(caller_id)); - if (requests_[caller_id].is_active) - requests_[caller_id].recognizer->CancelRecognition(); - requests_.erase(caller_id); - if (recording_caller_id_ == caller_id) - recording_caller_id_ = 0; - if (delegate_) - delegate_->DoClose(caller_id); -} - -void SpeechInputManagerImpl::CancelAllRequestsWithDelegate( - SpeechInputDispatcherHost* delegate) { - SpeechRecognizerMap::iterator it = requests_.begin(); - while (it != requests_.end()) { - if (it->second.delegate == delegate) { - CancelRecognition(it->first); - // This map will have very few elements so it is simpler to restart. - it = requests_.begin(); - } else { - ++it; - } - } -} - -void SpeechInputManagerImpl::StopRecording(int caller_id) { - // No pending requests on extension popups. - if (!HasPendingRequest(caller_id)) - return; - - requests_[caller_id].recognizer->StopRecording(); -} - -void SpeechInputManagerImpl::SetRecognitionResult( - int caller_id, const content::SpeechInputResult& result) { - DCHECK(HasPendingRequest(caller_id)); - GetDelegate(caller_id)->SetRecognitionResult(caller_id, result); -} - -void SpeechInputManagerImpl::DidCompleteRecording(int caller_id) { - DCHECK(recording_caller_id_ == caller_id); - DCHECK(HasPendingRequest(caller_id)); - recording_caller_id_ = 0; - GetDelegate(caller_id)->DidCompleteRecording(caller_id); - if (delegate_) - delegate_->ShowRecognizing(caller_id); -} - -void SpeechInputManagerImpl::DidCompleteRecognition(int caller_id) { - GetDelegate(caller_id)->DidCompleteRecognition(caller_id); - requests_.erase(caller_id); - if (delegate_) - delegate_->DoClose(caller_id); -} - -void SpeechInputManagerImpl::DidStartReceivingSpeech(int caller_id) { -} - -void SpeechInputManagerImpl::DidStopReceivingSpeech(int caller_id) { -} - -void SpeechInputManagerImpl::OnRecognizerError( - int caller_id, content::SpeechInputError error) { - if (caller_id == recording_caller_id_) - recording_caller_id_ = 0; - requests_[caller_id].is_active = false; - if (delegate_) - delegate_->ShowRecognizerError(caller_id, error); -} - -void SpeechInputManagerImpl::DidStartReceivingAudio(int caller_id) { - DCHECK(HasPendingRequest(caller_id)); - DCHECK(recording_caller_id_ == caller_id); - if (delegate_) - delegate_->ShowRecording(caller_id); -} - -void SpeechInputManagerImpl::DidCompleteEnvironmentEstimation(int caller_id) { - DCHECK(HasPendingRequest(caller_id)); - DCHECK(recording_caller_id_ == caller_id); -} - -void SpeechInputManagerImpl::SetInputVolume(int caller_id, float volume, - float noise_volume) { - DCHECK(HasPendingRequest(caller_id)); - DCHECK_EQ(recording_caller_id_, caller_id); - if (delegate_) - delegate_->ShowInputVolume(caller_id, volume, noise_volume); -} - -void SpeechInputManagerImpl::CancelRecognitionAndInformDelegate( - int caller_id) { - SpeechInputDispatcherHost* cur_delegate = GetDelegate(caller_id); - CancelRecognition(caller_id); - cur_delegate->DidCompleteRecording(caller_id); - cur_delegate->DidCompleteRecognition(caller_id); -} - -SpeechInputManagerImpl::SpeechInputRequest::SpeechInputRequest() - : delegate(NULL), - is_active(false) { -} - -SpeechInputManagerImpl::SpeechInputRequest::~SpeechInputRequest() { -} - -} // namespace speech_input diff --git a/content/browser/speech/speech_input_manager_impl.h b/content/browser/speech/speech_input_manager_impl.h deleted file mode 100644 index 6675fb8..0000000 --- a/content/browser/speech/speech_input_manager_impl.h +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_SPEECH_SPEECH_INPUT_MANAGER_H_ -#define CONTENT_BROWSER_SPEECH_SPEECH_INPUT_MANAGER_H_ - -#include -#include - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/memory/ref_counted.h" -#include "base/memory/singleton.h" -#include "content/public/browser/speech_input_manager.h" -#include "content/public/browser/speech_recognizer_delegate.h" -#include "ui/gfx/rect.h" - -class AudioManager; - -namespace content { -class ResourceContext; -class SpeechInputManagerDelegate; -class SpeechInputPreferences; -struct SpeechInputResult; -} - -namespace net { -class URLRequestContextGetter; -} - -namespace speech_input { - -class SpeechInputDispatcherHost; -class SpeechRecognizerImpl; - -class CONTENT_EXPORT SpeechInputManagerImpl - : NON_EXPORTED_BASE(public content::SpeechInputManager), - NON_EXPORTED_BASE(public content::SpeechRecognizerDelegate) { - public: - static SpeechInputManagerImpl* GetInstance(); - - // SpeechInputManager implementation: - virtual void StartRecognitionForRequest(int caller_id) OVERRIDE; - virtual void CancelRecognitionForRequest(int caller_id) OVERRIDE; - virtual void FocusLostForRequest(int caller_id) OVERRIDE; - virtual bool HasAudioInputDevices() OVERRIDE; - virtual bool IsRecordingInProcess() OVERRIDE; - virtual string16 GetAudioInputDeviceModel() OVERRIDE; - virtual void ShowAudioInputSettings() OVERRIDE; - - // Handlers for requests from render views. - - // |delegate| is a weak pointer and should remain valid until - // its |DidCompleteRecognition| method is called or recognition is cancelled. - // |render_process_id| is the ID of the renderer process initiating the - // request. - // |element_rect| is the display bounds of the html element requesting speech - // input (in page coordinates). - virtual void StartRecognition( - SpeechInputDispatcherHost* delegate, - int caller_id, - int render_process_id, - int render_view_id, - const gfx::Rect& element_rect, - const std::string& language, - const std::string& grammar, - const std::string& origin_url, - net::URLRequestContextGetter* context_getter, - content::SpeechInputPreferences* speech_input_prefs); - virtual void CancelRecognition(int caller_id); - virtual void CancelAllRequestsWithDelegate( - SpeechInputDispatcherHost* delegate); - virtual void StopRecording(int caller_id); - - // Overridden from content::SpeechRecognizerDelegate: - virtual void DidStartReceivingAudio(int caller_id) OVERRIDE; - virtual void SetRecognitionResult( - int caller_id, - const content::SpeechInputResult& result) OVERRIDE; - virtual void DidCompleteRecording(int caller_id) OVERRIDE; - virtual void DidCompleteRecognition(int caller_id) OVERRIDE; - virtual void DidStartReceivingSpeech(int caller_id) OVERRIDE; - virtual void DidStopReceivingSpeech(int caller_id) OVERRIDE; - - virtual void OnRecognizerError(int caller_id, - content::SpeechInputError error) OVERRIDE; - virtual void DidCompleteEnvironmentEstimation(int caller_id) OVERRIDE; - virtual void SetInputVolume(int caller_id, float volume, - float noise_volume) OVERRIDE; - - protected: - // Private constructor to enforce singleton. - friend struct DefaultSingletonTraits; - SpeechInputManagerImpl(); - virtual ~SpeechInputManagerImpl(); - - bool HasPendingRequest(int caller_id) const; - - private: - struct SpeechInputRequest { - SpeechInputRequest(); - ~SpeechInputRequest(); - - SpeechInputDispatcherHost* delegate; - scoped_refptr recognizer; - bool is_active; // Set to true when recording or recognition is going on. - }; - - struct SpeechInputParams; - - SpeechInputDispatcherHost* GetDelegate(int caller_id) const; - - void CheckRenderViewTypeAndStartRecognition(const SpeechInputParams& params); - void ProceedStartingRecognition(const SpeechInputParams& params); - - void CancelRecognitionAndInformDelegate(int caller_id); - - typedef std::map SpeechRecognizerMap; - SpeechRecognizerMap requests_; - std::string request_info_; - bool can_report_metrics_; - int recording_caller_id_; - content::SpeechInputManagerDelegate* delegate_; -}; - -} // namespace speech_input - -#endif // CONTENT_BROWSER_SPEECH_SPEECH_INPUT_MANAGER_H_ diff --git a/content/browser/speech/speech_recognition_browsertest.cc b/content/browser/speech/speech_recognition_browsertest.cc new file mode 100644 index 0000000..0b56226 --- /dev/null +++ b/content/browser/speech/speech_recognition_browsertest.cc @@ -0,0 +1,254 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/file_path.h" +#include "base/string_number_conversions.h" +#include "base/synchronization/waitable_event.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "content/browser/renderer_host/render_view_host_impl.h" +#include "content/browser/speech/input_tag_speech_dispatcher_host.h" +#include "content/browser/speech/speech_recognition_manager_impl.h" +#include "content/browser/tab_contents/tab_contents.h" +#include "content/public/browser/notification_types.h" +#include "content/public/common/content_switches.h" +#include "content/public/common/speech_recognition_result.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" + +using content::NavigationController; +using content::WebContents; + +namespace speech { +class FakeSpeechRecognitionManager; +} + +namespace speech { + +const char kTestResult[] = "Pictures of the moon"; + +class FakeSpeechRecognitionManager : public SpeechRecognitionManagerImpl { + public: + FakeSpeechRecognitionManager() + : caller_id_(0), + delegate_(NULL), + did_cancel_all_(false), + should_send_fake_response_(true), + recognition_started_event_(false, false) { + } + + std::string grammar() { + return grammar_; + } + + bool did_cancel_all() { + return did_cancel_all_; + } + + void set_should_send_fake_response(bool send) { + should_send_fake_response_ = send; + } + + bool should_send_fake_response() { + return should_send_fake_response_; + } + + base::WaitableEvent& recognition_started_event() { + return recognition_started_event_; + } + + // SpeechRecognitionManager methods. + virtual void StartRecognition( + InputTagSpeechDispatcherHost* delegate, + int caller_id, + int render_process_id, + int render_view_id, + const gfx::Rect& element_rect, + const std::string& language, + const std::string& grammar, + const std::string& origin_url, + net::URLRequestContextGetter* context_getter, + content::SpeechRecognitionPreferences* recognition_prefs) OVERRIDE { + VLOG(1) << "StartRecognition invoked."; + EXPECT_EQ(0, caller_id_); + EXPECT_EQ(NULL, delegate_); + caller_id_ = caller_id; + delegate_ = delegate; + grammar_ = grammar; + if (should_send_fake_response_) { + // Give the fake result in a short while. + MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + &FakeSpeechRecognitionManager::SetFakeRecognitionResult, + // This class does not need to be refcounted (typically done by + // PostTask) since it will outlive the test and gets released only + // when the test shuts down. Disabling refcounting here saves a bit + // of unnecessary code and the factory method can return a plain + // pointer below as required by the real code. + base::Unretained(this))); + } + recognition_started_event_.Signal(); + } + virtual void CancelRecognition(int caller_id) OVERRIDE { + VLOG(1) << "CancelRecognition invoked."; + EXPECT_EQ(caller_id_, caller_id); + caller_id_ = 0; + delegate_ = NULL; + } + virtual void StopRecording(int caller_id) OVERRIDE { + VLOG(1) << "StopRecording invoked."; + EXPECT_EQ(caller_id_, caller_id); + // Nothing to do here since we aren't really recording. + } + virtual void CancelAllRequestsWithDelegate( + InputTagSpeechDispatcherHost* delegate) OVERRIDE { + VLOG(1) << "CancelAllRequestsWithDelegate invoked."; + // delegate_ is set to NULL if a fake result was received (see below), so + // check that delegate_ matches the incoming parameter only when there is + // no fake result sent. + EXPECT_TRUE(should_send_fake_response_ || delegate_ == delegate); + did_cancel_all_ = true; + } + + private: + void SetFakeRecognitionResult() { + if (caller_id_) { // Do a check in case we were cancelled.. + VLOG(1) << "Setting fake recognition result."; + delegate_->DidCompleteRecording(caller_id_); + content::SpeechRecognitionResult results; + results.hypotheses.push_back(content::SpeechRecognitionHypothesis( + ASCIIToUTF16(kTestResult), 1.0)); + delegate_->SetRecognitionResult(caller_id_, results); + delegate_->DidCompleteRecognition(caller_id_); + caller_id_ = 0; + delegate_ = NULL; + VLOG(1) << "Finished setting fake recognition result."; + } + } + + int caller_id_; + InputTagSpeechDispatcherHost* delegate_; + std::string grammar_; + bool did_cancel_all_; + bool should_send_fake_response_; + base::WaitableEvent recognition_started_event_; +}; + +class SpeechRecognitionBrowserTest : public InProcessBrowserTest { + public: + // InProcessBrowserTest methods + virtual void SetUpCommandLine(CommandLine* command_line) { + EXPECT_TRUE(!command_line->HasSwitch(switches::kDisableSpeechInput)); + } + + GURL testUrl(const FilePath::CharType* filename) { + const FilePath kTestDir(FILE_PATH_LITERAL("speech")); + return ui_test_utils::GetTestUrl(kTestDir, FilePath(filename)); + } + + protected: + void LoadAndStartSpeechRecognitionTest(const FilePath::CharType* filename) { + // The test page calculates the speech button's coordinate in the page on + // load & sets that coordinate in the URL fragment. We send mouse down & up + // events at that coordinate to trigger speech recognition. + GURL test_url = testUrl(filename); + ui_test_utils::NavigateToURL(browser(), test_url); + + WebKit::WebMouseEvent mouse_event; + mouse_event.type = WebKit::WebInputEvent::MouseDown; + mouse_event.button = WebKit::WebMouseEvent::ButtonLeft; + mouse_event.x = 0; + mouse_event.y = 0; + mouse_event.clickCount = 1; + WebContents* web_contents = browser()->GetSelectedWebContents(); + + ui_test_utils::WindowedNotificationObserver observer( + content::NOTIFICATION_LOAD_STOP, + content::Source(&web_contents->GetController())); + web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event); + mouse_event.type = WebKit::WebInputEvent::MouseUp; + web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event); + fake_speech_recognition_manager_.recognition_started_event().Wait(); + + // We should wait for a navigation event, raised by the test page JS code + // upon the onwebkitspeechchange event, in all cases except when the + // speech response is inhibited. + if (fake_speech_recognition_manager_.should_send_fake_response()) + observer.Wait(); + } + + void RunSpeechRecognitionTest(const FilePath::CharType* filename) { + // The fake speech input manager would receive the speech input + // request and return the test string as recognition result. The test page + // then sets the URL fragment as 'pass' if it received the expected string. + LoadAndStartSpeechRecognitionTest(filename); + + EXPECT_EQ("pass", browser()->GetSelectedWebContents()->GetURL().ref()); + } + + // InProcessBrowserTest methods. + virtual void SetUpInProcessBrowserTestFixture() { + fake_speech_recognition_manager_.set_should_send_fake_response(true); + speech_recognition_manager_ = &fake_speech_recognition_manager_; + + // Inject the fake manager factory so that the test result is returned to + // the web page. + InputTagSpeechDispatcherHost::set_manager(speech_recognition_manager_); + } + + virtual void TearDownInProcessBrowserTestFixture() { + speech_recognition_manager_ = NULL; + } + + FakeSpeechRecognitionManager fake_speech_recognition_manager_; + + // This is used by the static |fakeManager|, and it is a pointer rather than a + // direct instance per the style guide. + static SpeechRecognitionManagerImpl* speech_recognition_manager_; +}; + +SpeechRecognitionManagerImpl* + SpeechRecognitionBrowserTest::speech_recognition_manager_ = NULL; + +// TODO(satish): Once this flakiness has been fixed, add a second test here to +// check for sending many clicks in succession to the speech button and verify +// that it doesn't cause any crash but works as expected. This should act as the +// test for http://crbug.com/59173 +// +// TODO(satish): Similar to above, once this flakiness has been fixed add +// another test here to check that when speech recognition is in progress and +// a renderer crashes, we get a call to +// SpeechRecognitionManager::CancelAllRequestsWithDelegate. +IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest, TestBasicRecognition) { + RunSpeechRecognitionTest(FILE_PATH_LITERAL("basic_recognition.html")); + EXPECT_TRUE(fake_speech_recognition_manager_.grammar().empty()); +} + +IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest, GrammarAttribute) { + RunSpeechRecognitionTest(FILE_PATH_LITERAL("grammar_attribute.html")); + EXPECT_EQ("http://example.com/grammar.xml", + fake_speech_recognition_manager_.grammar()); +} + +IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest, TestCancelAll) { + // The test checks that the cancel-all callback gets issued when a session + // is pending, so don't send a fake response. + // We are not expecting a navigation event being raised from the JS of the + // test page JavaScript in this case. + fake_speech_recognition_manager_.set_should_send_fake_response(false); + + LoadAndStartSpeechRecognitionTest( + FILE_PATH_LITERAL("basic_recognition.html")); + + // Make the renderer crash. This should trigger + // InputTagSpeechDispatcherHost to cancel all pending sessions. + GURL test_url("about:crash"); + ui_test_utils::NavigateToURL(browser(), test_url); + + EXPECT_TRUE(fake_speech_recognition_manager_.did_cancel_all()); +} + +} // namespace speech diff --git a/content/browser/speech/speech_recognition_manager_impl.cc b/content/browser/speech/speech_recognition_manager_impl.cc new file mode 100644 index 0000000..9ced53f --- /dev/null +++ b/content/browser/speech/speech_recognition_manager_impl.cc @@ -0,0 +1,353 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/speech/speech_recognition_manager_impl.h" + +#include "base/bind.h" +#include "content/browser/browser_main_loop.h" +#include "content/browser/renderer_host/render_view_host_impl.h" +#include "content/browser/speech/input_tag_speech_dispatcher_host.h" +#include "content/browser/speech/speech_recognizer_impl.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/content_browser_client.h" +#include "content/public/browser/render_view_host_delegate.h" +#include "content/public/browser/resource_context.h" +#include "content/public/browser/speech_recognition_manager_delegate.h" +#include "content/public/browser/speech_recognition_preferences.h" +#include "content/public/common/view_type.h" + +using content::BrowserMainLoop; +using content::BrowserThread; +using content::SpeechRecognitionManager; +using content::SpeechRecognitionManagerDelegate; + +SpeechRecognitionManager* SpeechRecognitionManager::GetInstance() { + return speech::SpeechRecognitionManagerImpl::GetInstance(); +} + +namespace speech { + +struct SpeechRecognitionManagerImpl::SpeechRecognitionParams { + SpeechRecognitionParams(InputTagSpeechDispatcherHost* delegate, + int caller_id, + int render_process_id, + int render_view_id, + const gfx::Rect& element_rect, + const std::string& language, + const std::string& grammar, + const std::string& origin_url, + net::URLRequestContextGetter* context_getter, + content::SpeechRecognitionPreferences* recognition_prefs) + : delegate(delegate), + caller_id(caller_id), + render_process_id(render_process_id), + render_view_id(render_view_id), + element_rect(element_rect), + language(language), + grammar(grammar), + origin_url(origin_url), + context_getter(context_getter), + recognition_prefs(recognition_prefs) { + } + + InputTagSpeechDispatcherHost* delegate; + int caller_id; + int render_process_id; + int render_view_id; + gfx::Rect element_rect; + std::string language; + std::string grammar; + std::string origin_url; + net::URLRequestContextGetter* context_getter; + content::SpeechRecognitionPreferences* recognition_prefs; +}; + +SpeechRecognitionManagerImpl* SpeechRecognitionManagerImpl::GetInstance() { + return Singleton::get(); +} + +SpeechRecognitionManagerImpl::SpeechRecognitionManagerImpl() + : can_report_metrics_(false), + recording_caller_id_(0) { + delegate_ = content::GetContentClient()->browser()-> + GetSpeechRecognitionManagerDelegate(); +} + +SpeechRecognitionManagerImpl::~SpeechRecognitionManagerImpl() { + while (requests_.begin() != requests_.end()) + CancelRecognition(requests_.begin()->first); +} + +bool SpeechRecognitionManagerImpl::HasAudioInputDevices() { + return BrowserMainLoop::GetAudioManager()->HasAudioInputDevices(); +} + +bool SpeechRecognitionManagerImpl::IsCapturingAudio() { + return BrowserMainLoop::GetAudioManager()->IsRecordingInProcess(); +} + +string16 SpeechRecognitionManagerImpl::GetAudioInputDeviceModel() { + return BrowserMainLoop::GetAudioManager()->GetAudioInputDeviceModel(); +} + +bool SpeechRecognitionManagerImpl::HasPendingRequest(int caller_id) const { + return requests_.find(caller_id) != requests_.end(); +} + +InputTagSpeechDispatcherHost* SpeechRecognitionManagerImpl::GetDelegate( + int caller_id) const { + return requests_.find(caller_id)->second.delegate; +} + +void SpeechRecognitionManagerImpl::ShowAudioInputSettings() { + // Since AudioManager::ShowAudioInputSettings can potentially launch external + // processes, do that in the FILE thread to not block the calling threads. + if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + base::Bind(&SpeechRecognitionManagerImpl::ShowAudioInputSettings, + base::Unretained(this))); + return; + } + + AudioManager* audio_manager = BrowserMainLoop::GetAudioManager(); + DCHECK(audio_manager->CanShowAudioInputSettings()); + if (audio_manager->CanShowAudioInputSettings()) + audio_manager->ShowAudioInputSettings(); +} + +void SpeechRecognitionManagerImpl::StartRecognition( + InputTagSpeechDispatcherHost* delegate, + int caller_id, + int render_process_id, + int render_view_id, + const gfx::Rect& element_rect, + const std::string& language, + const std::string& grammar, + const std::string& origin_url, + net::URLRequestContextGetter* context_getter, + content::SpeechRecognitionPreferences* recognition_prefs) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind( + &SpeechRecognitionManagerImpl::CheckRenderViewTypeAndStartRecognition, + base::Unretained(this), + SpeechRecognitionParams( + delegate, caller_id, render_process_id, render_view_id, + element_rect, language, grammar, origin_url, context_getter, + recognition_prefs))); +} + +void SpeechRecognitionManagerImpl::CheckRenderViewTypeAndStartRecognition( + const SpeechRecognitionParams& params) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + RenderViewHostImpl* render_view_host = RenderViewHostImpl::FromID( + params.render_process_id, params.render_view_id); + if (!render_view_host || !render_view_host->GetDelegate()) + return; + + // For host delegates other than TabContents we can't reliably show a popup, + // including the speech input bubble. In these cases for privacy reasons we + // don't want to start recording if the user can't be properly notified. + // An example of this is trying to show the speech input bubble within an + // extension popup: http://crbug.com/92083. In these situations the speech + // input extension API should be used instead. + if (render_view_host->GetDelegate()->GetRenderViewType() == + content::VIEW_TYPE_TAB_CONTENTS) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&SpeechRecognitionManagerImpl::ProceedStartingRecognition, + base::Unretained(this), params)); + } +} + +void SpeechRecognitionManagerImpl::ProceedStartingRecognition( + const SpeechRecognitionParams& params) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(!HasPendingRequest(params.caller_id)); + + if (delegate_) { + delegate_->ShowRecognitionRequested( + params.caller_id, params.render_process_id, params.render_view_id, + params.element_rect); + delegate_->GetRequestInfo(&can_report_metrics_, &request_info_); + } + + Request* request = &requests_[params.caller_id]; + request->delegate = params.delegate; + request->recognizer = new SpeechRecognizerImpl( + this, params.caller_id, params.language, params.grammar, + params.context_getter, params.recognition_prefs->FilterProfanities(), + request_info_, can_report_metrics_ ? params.origin_url : ""); + request->is_active = false; + + StartRecognitionForRequest(params.caller_id); +} + +void SpeechRecognitionManagerImpl::StartRecognitionForRequest(int caller_id) { + SpeechRecognizerMap::iterator request = requests_.find(caller_id); + if (request == requests_.end()) { + NOTREACHED(); + return; + } + + // We should not currently be recording for the caller. + CHECK(recording_caller_id_ != caller_id); + + // If we are currently recording audio for another caller, abort that cleanly. + if (recording_caller_id_) + CancelRecognitionAndInformDelegate(recording_caller_id_); + + if (!HasAudioInputDevices()) { + if (delegate_) { + delegate_->ShowMicError(caller_id, + SpeechRecognitionManagerDelegate::MIC_ERROR_NO_DEVICE_AVAILABLE); + } + } else if (IsCapturingAudio()) { + if (delegate_) { + delegate_->ShowMicError( + caller_id, SpeechRecognitionManagerDelegate::MIC_ERROR_DEVICE_IN_USE); + } + } else { + recording_caller_id_ = caller_id; + requests_[caller_id].is_active = true; + requests_[caller_id].recognizer->StartRecording(); + if (delegate_) + delegate_->ShowWarmUp(caller_id); + } +} + +void SpeechRecognitionManagerImpl::CancelRecognitionForRequest(int caller_id) { + // Ignore if the caller id was not in our active recognizers list because the + // user might have clicked more than once, or recognition could have been + // ended due to other reasons before the user click was processed. + if (!HasPendingRequest(caller_id)) + return; + + CancelRecognitionAndInformDelegate(caller_id); +} + +void SpeechRecognitionManagerImpl::FocusLostForRequest(int caller_id) { + // See above comment. + if (!HasPendingRequest(caller_id)) + return; + + // If this is an ongoing recording or if we were displaying an error message + // to the user, abort it since user has switched focus. Otherwise + // recognition has started and keep that going so user can start speaking to + // another element while this gets the results in parallel. + if (recording_caller_id_ == caller_id || !requests_[caller_id].is_active) + CancelRecognitionAndInformDelegate(caller_id); +} + +void SpeechRecognitionManagerImpl::CancelRecognition(int caller_id) { + DCHECK(HasPendingRequest(caller_id)); + if (requests_[caller_id].is_active) + requests_[caller_id].recognizer->CancelRecognition(); + requests_.erase(caller_id); + if (recording_caller_id_ == caller_id) + recording_caller_id_ = 0; + if (delegate_) + delegate_->DoClose(caller_id); +} + +void SpeechRecognitionManagerImpl::CancelAllRequestsWithDelegate( + InputTagSpeechDispatcherHost* delegate) { + SpeechRecognizerMap::iterator it = requests_.begin(); + while (it != requests_.end()) { + if (it->second.delegate == delegate) { + CancelRecognition(it->first); + // This map will have very few elements so it is simpler to restart. + it = requests_.begin(); + } else { + ++it; + } + } +} + +void SpeechRecognitionManagerImpl::StopRecording(int caller_id) { + // No pending requests on extension popups. + if (!HasPendingRequest(caller_id)) + return; + + requests_[caller_id].recognizer->StopRecording(); +} + +void SpeechRecognitionManagerImpl::SetRecognitionResult( + int caller_id, const content::SpeechRecognitionResult& result) { + DCHECK(HasPendingRequest(caller_id)); + GetDelegate(caller_id)->SetRecognitionResult(caller_id, result); +} + +void SpeechRecognitionManagerImpl::DidCompleteRecording(int caller_id) { + DCHECK(recording_caller_id_ == caller_id); + DCHECK(HasPendingRequest(caller_id)); + recording_caller_id_ = 0; + GetDelegate(caller_id)->DidCompleteRecording(caller_id); + if (delegate_) + delegate_->ShowRecognizing(caller_id); +} + +void SpeechRecognitionManagerImpl::DidCompleteRecognition(int caller_id) { + GetDelegate(caller_id)->DidCompleteRecognition(caller_id); + requests_.erase(caller_id); + if (delegate_) + delegate_->DoClose(caller_id); +} + +void SpeechRecognitionManagerImpl::DidStartReceivingSpeech(int caller_id) { +} + +void SpeechRecognitionManagerImpl::DidStopReceivingSpeech(int caller_id) { +} + +void SpeechRecognitionManagerImpl::OnRecognizerError( + int caller_id, content::SpeechRecognitionErrorCode error) { + if (caller_id == recording_caller_id_) + recording_caller_id_ = 0; + requests_[caller_id].is_active = false; + if (delegate_) + delegate_->ShowRecognizerError(caller_id, error); +} + +void SpeechRecognitionManagerImpl::DidStartReceivingAudio(int caller_id) { + DCHECK(HasPendingRequest(caller_id)); + DCHECK(recording_caller_id_ == caller_id); + if (delegate_) + delegate_->ShowRecording(caller_id); +} + +void SpeechRecognitionManagerImpl::DidCompleteEnvironmentEstimation( + int caller_id) { + DCHECK(HasPendingRequest(caller_id)); + DCHECK(recording_caller_id_ == caller_id); +} + +void SpeechRecognitionManagerImpl::SetInputVolume(int caller_id, float volume, + float noise_volume) { + DCHECK(HasPendingRequest(caller_id)); + DCHECK_EQ(recording_caller_id_, caller_id); + if (delegate_) + delegate_->ShowInputVolume(caller_id, volume, noise_volume); +} + +void SpeechRecognitionManagerImpl::CancelRecognitionAndInformDelegate( + int caller_id) { + InputTagSpeechDispatcherHost* cur_delegate = GetDelegate(caller_id); + CancelRecognition(caller_id); + cur_delegate->DidCompleteRecording(caller_id); + cur_delegate->DidCompleteRecognition(caller_id); +} + +SpeechRecognitionManagerImpl::Request::Request() + : delegate(NULL), + is_active(false) { +} + +SpeechRecognitionManagerImpl::Request::~Request() { +} + +} // namespace speech diff --git a/content/browser/speech/speech_recognition_manager_impl.h b/content/browser/speech/speech_recognition_manager_impl.h new file mode 100644 index 0000000..8b91029 --- /dev/null +++ b/content/browser/speech/speech_recognition_manager_impl.h @@ -0,0 +1,129 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_SPEECH_SPEECH_RECOGNITION_MANAGER_H_ +#define CONTENT_BROWSER_SPEECH_SPEECH_RECOGNITION_MANAGER_H_ + +#include +#include + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/memory/singleton.h" +#include "content/public/browser/speech_recognition_manager.h" +#include "content/public/browser/speech_recognizer_delegate.h" +#include "ui/gfx/rect.h" + +class AudioManager; + +namespace content { +class ResourceContext; +class SpeechRecognitionManagerDelegate; +class SpeechRecognitionPreferences; +struct SpeechRecognitionResult; +} + +namespace net { +class URLRequestContextGetter; +} + +namespace speech { + +class InputTagSpeechDispatcherHost; +class SpeechRecognizerImpl; + +class CONTENT_EXPORT SpeechRecognitionManagerImpl + : NON_EXPORTED_BASE(public content::SpeechRecognitionManager), + NON_EXPORTED_BASE(public content::SpeechRecognizerDelegate) { + public: + static SpeechRecognitionManagerImpl* GetInstance(); + + // SpeechRecognitionManager implementation: + virtual void StartRecognitionForRequest(int caller_id) OVERRIDE; + virtual void CancelRecognitionForRequest(int caller_id) OVERRIDE; + virtual void FocusLostForRequest(int caller_id) OVERRIDE; + virtual bool HasAudioInputDevices() OVERRIDE; + virtual bool IsCapturingAudio() OVERRIDE; + virtual string16 GetAudioInputDeviceModel() OVERRIDE; + virtual void ShowAudioInputSettings() OVERRIDE; + + // Handlers for requests from render views. + + // |delegate| is a weak pointer and should remain valid until + // its |DidCompleteRecognition| method is called or recognition is cancelled. + // |render_process_id| is the ID of the renderer process initiating the + // request. + // |element_rect| is the display bounds of the html element requesting speech + // input (in page coordinates). + virtual void StartRecognition( + InputTagSpeechDispatcherHost* delegate, + int caller_id, + int render_process_id, + int render_view_id, + const gfx::Rect& element_rect, + const std::string& language, + const std::string& grammar, + const std::string& origin_url, + net::URLRequestContextGetter* context_getter, + content::SpeechRecognitionPreferences* speech_recognition_prefs); + virtual void CancelRecognition(int caller_id); + virtual void CancelAllRequestsWithDelegate( + InputTagSpeechDispatcherHost* delegate); + virtual void StopRecording(int caller_id); + + // Overridden from content::SpeechRecognizerDelegate: + virtual void DidStartReceivingAudio(int caller_id) OVERRIDE; + virtual void SetRecognitionResult( + int caller_id, const content::SpeechRecognitionResult& result) OVERRIDE; + virtual void DidCompleteRecording(int caller_id) OVERRIDE; + virtual void DidCompleteRecognition(int caller_id) OVERRIDE; + virtual void DidStartReceivingSpeech(int caller_id) OVERRIDE; + virtual void DidStopReceivingSpeech(int caller_id) OVERRIDE; + + virtual void OnRecognizerError( + int caller_id, content::SpeechRecognitionErrorCode error) OVERRIDE; + virtual void DidCompleteEnvironmentEstimation(int caller_id) OVERRIDE; + virtual void SetInputVolume(int caller_id, float volume, + float noise_volume) OVERRIDE; + + protected: + // Private constructor to enforce singleton. + friend struct DefaultSingletonTraits; + SpeechRecognitionManagerImpl(); + virtual ~SpeechRecognitionManagerImpl(); + + bool HasPendingRequest(int caller_id) const; + + private: + struct Request { + Request(); + ~Request(); + + InputTagSpeechDispatcherHost* delegate; + scoped_refptr recognizer; + bool is_active; // Set to true when recording or recognition is going on. + }; + + struct SpeechRecognitionParams; + + InputTagSpeechDispatcherHost* GetDelegate(int caller_id) const; + + void CheckRenderViewTypeAndStartRecognition( + const SpeechRecognitionParams& params); + void ProceedStartingRecognition(const SpeechRecognitionParams& params); + + void CancelRecognitionAndInformDelegate(int caller_id); + + typedef std::map SpeechRecognizerMap; + SpeechRecognizerMap requests_; + std::string request_info_; + bool can_report_metrics_; + int recording_caller_id_; + content::SpeechRecognitionManagerDelegate* delegate_; +}; + +} // namespace speech + +#endif // CONTENT_BROWSER_SPEECH_SPEECH_RECOGNITION_MANAGER_H_ diff --git a/content/browser/speech/speech_recognition_request.cc b/content/browser/speech/speech_recognition_request.cc index 8176128..89dde84 100644 --- a/content/browser/speech/speech_recognition_request.cc +++ b/content/browser/speech/speech_recognition_request.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -11,7 +11,7 @@ #include "base/string_util.h" #include "base/values.h" #include "content/common/net/url_fetcher_impl.h" -#include "content/public/common/speech_input_result.h" +#include "content/public/common/speech_recognition_result.h" #include "net/base/escape.h" #include "net/base/load_flags.h" #include "net/url_request/url_request_context.h" @@ -32,7 +32,7 @@ const char* const kConfidenceString = "confidence"; const int kMaxResults = 6; bool ParseServerResponse(const std::string& response_body, - content::SpeechInputResult* result) { + content::SpeechRecognitionResult* result) { if (response_body.empty()) { LOG(WARNING) << "ParseServerResponse: Response was empty."; return false; @@ -66,9 +66,9 @@ bool ParseServerResponse(const std::string& response_body, // Process the status. switch (status) { - case content::SPEECH_INPUT_ERROR_NONE: - case content::SPEECH_INPUT_ERROR_NO_SPEECH: - case content::SPEECH_INPUT_ERROR_NO_MATCH: + case content::SPEECH_RECOGNITION_ERROR_NONE: + case content::SPEECH_RECOGNITION_ERROR_NO_SPEECH: + case content::SPEECH_RECOGNITION_ERROR_NO_MATCH: break; default: @@ -77,7 +77,7 @@ bool ParseServerResponse(const std::string& response_body, return false; } - result->error = static_cast(status); + result->error = static_cast(status); // Get the hypotheses. Value* hypotheses_value = NULL; @@ -121,7 +121,7 @@ bool ParseServerResponse(const std::string& response_body, double confidence = 0.0; hypothesis_value->GetDouble(kConfidenceString, &confidence); - result->hypotheses.push_back(content::SpeechInputHypothesis( + result->hypotheses.push_back(content::SpeechRecognitionHypothesis( utterance, confidence)); } @@ -135,7 +135,7 @@ bool ParseServerResponse(const std::string& response_body, } // namespace -namespace speech_input { +namespace speech { int SpeechRecognitionRequest::url_fetcher_id_for_tests = 0; @@ -211,12 +211,12 @@ void SpeechRecognitionRequest::OnURLFetchComplete( const content::URLFetcher* source) { DCHECK_EQ(url_fetcher_.get(), source); - content::SpeechInputResult result; + content::SpeechRecognitionResult result; std::string data; if (!source->GetStatus().is_success() || source->GetResponseCode() != 200 || !source->GetResponseAsString(&data) || !ParseServerResponse(data, &result)) { - result.error = content::SPEECH_INPUT_ERROR_NETWORK; + result.error = content::SPEECH_RECOGNITION_ERROR_NETWORK; } DVLOG(1) << "SpeechRecognitionRequest: Invoking delegate with result."; @@ -224,4 +224,4 @@ void SpeechRecognitionRequest::OnURLFetchComplete( delegate_->SetRecognitionResult(result); } -} // namespace speech_input +} // namespace speech diff --git a/content/browser/speech/speech_recognition_request.h b/content/browser/speech/speech_recognition_request.h index 65a9733..b6ce077 100644 --- a/content/browser/speech/speech_recognition_request.h +++ b/content/browser/speech/speech_recognition_request.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -18,14 +18,14 @@ class URLFetcher; namespace content { -struct SpeechInputResult; +struct SpeechRecognitionResult; } namespace net { class URLRequestContextGetter; } -namespace speech_input { +namespace speech { // Provides a simple interface for sending recorded speech data to the server // and get back recognition results. @@ -38,7 +38,7 @@ class SpeechRecognitionRequest : public content::URLFetcherDelegate { class CONTENT_EXPORT Delegate { public: virtual void SetRecognitionResult( - const content::SpeechInputResult& result) = 0; + const content::SpeechRecognitionResult& result) = 0; protected: virtual ~Delegate() {} @@ -83,6 +83,6 @@ class SpeechRecognitionRequest : public content::URLFetcherDelegate { // the workaround was not needed for my machine). typedef SpeechRecognitionRequest::Delegate SpeechRecognitionRequestDelegate; -} // namespace speech_input +} // namespace speech #endif // CONTENT_BROWSER_SPEECH_SPEECH_RECOGNITION_REQUEST_H_ diff --git a/content/browser/speech/speech_recognition_request_unittest.cc b/content/browser/speech/speech_recognition_request_unittest.cc index 9221085..37b82f8 100644 --- a/content/browser/speech/speech_recognition_request_unittest.cc +++ b/content/browser/speech/speech_recognition_request_unittest.cc @@ -1,17 +1,17 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/message_loop.h" #include "base/utf_string_conversions.h" #include "content/browser/speech/speech_recognition_request.h" -#include "content/public/common/speech_input_result.h" +#include "content/public/common/speech_recognition_result.h" #include "content/test/test_url_fetcher_factory.h" #include "net/url_request/url_request_context_getter.h" #include "net/url_request/url_request_status.h" #include "testing/gtest/include/gtest/gtest.h" -namespace speech_input { +namespace speech { class SpeechRecognitionRequestTest : public SpeechRecognitionRequestDelegate, public testing::Test { @@ -24,14 +24,14 @@ class SpeechRecognitionRequestTest : public SpeechRecognitionRequestDelegate, // SpeechRecognitionRequestDelegate methods. virtual void SetRecognitionResult( - const content::SpeechInputResult& result) OVERRIDE { + const content::SpeechRecognitionResult& result) OVERRIDE { result_ = result; } protected: MessageLoop message_loop_; TestURLFetcherFactory url_fetcher_factory_; - content::SpeechInputResult result_; + content::SpeechRecognitionResult result_; }; void SpeechRecognitionRequestTest::CreateAndTestRequest( @@ -60,7 +60,7 @@ TEST_F(SpeechRecognitionRequestTest, BasicTest) { CreateAndTestRequest(true, "{\"status\":0,\"hypotheses\":" "[{\"utterance\":\"123456\",\"confidence\":0.9}]}"); - EXPECT_EQ(result_.error, content::SPEECH_INPUT_ERROR_NONE); + EXPECT_EQ(result_.error, content::SPEECH_RECOGNITION_ERROR_NONE); EXPECT_EQ(1U, result_.hypotheses.size()); EXPECT_EQ(ASCIIToUTF16("123456"), result_.hypotheses[0].utterance); EXPECT_EQ(0.9, result_.hypotheses[0].confidence); @@ -70,7 +70,7 @@ TEST_F(SpeechRecognitionRequestTest, BasicTest) { "{\"status\":0,\"hypotheses\":[" "{\"utterance\":\"hello\",\"confidence\":0.9}," "{\"utterance\":\"123456\",\"confidence\":0.5}]}"); - EXPECT_EQ(result_.error, content::SPEECH_INPUT_ERROR_NONE); + EXPECT_EQ(result_.error, content::SPEECH_RECOGNITION_ERROR_NONE); EXPECT_EQ(2u, result_.hypotheses.size()); EXPECT_EQ(ASCIIToUTF16("hello"), result_.hypotheses[0].utterance); EXPECT_EQ(0.9, result_.hypotheses[0].confidence); @@ -79,29 +79,29 @@ TEST_F(SpeechRecognitionRequestTest, BasicTest) { // Zero results. CreateAndTestRequest(true, "{\"status\":0,\"hypotheses\":[]}"); - EXPECT_EQ(result_.error, content::SPEECH_INPUT_ERROR_NONE); + EXPECT_EQ(result_.error, content::SPEECH_RECOGNITION_ERROR_NONE); EXPECT_EQ(0U, result_.hypotheses.size()); // Http failure case. CreateAndTestRequest(false, ""); - EXPECT_EQ(result_.error, content::SPEECH_INPUT_ERROR_NETWORK); + EXPECT_EQ(result_.error, content::SPEECH_RECOGNITION_ERROR_NETWORK); EXPECT_EQ(0U, result_.hypotheses.size()); // Invalid status case. CreateAndTestRequest(true, "{\"status\":\"invalid\",\"hypotheses\":[]}"); - EXPECT_EQ(result_.error, content::SPEECH_INPUT_ERROR_NETWORK); + EXPECT_EQ(result_.error, content::SPEECH_RECOGNITION_ERROR_NETWORK); EXPECT_EQ(0U, result_.hypotheses.size()); // Server-side error case. CreateAndTestRequest(true, "{\"status\":1,\"hypotheses\":[]}"); - EXPECT_EQ(result_.error, content::SPEECH_INPUT_ERROR_NETWORK); + EXPECT_EQ(result_.error, content::SPEECH_RECOGNITION_ERROR_NETWORK); EXPECT_EQ(0U, result_.hypotheses.size()); // Malformed JSON case. CreateAndTestRequest(true, "{\"status\":0,\"hypotheses\":" "[{\"unknownkey\":\"hello\"}]}"); - EXPECT_EQ(result_.error, content::SPEECH_INPUT_ERROR_NETWORK); + EXPECT_EQ(result_.error, content::SPEECH_RECOGNITION_ERROR_NETWORK); EXPECT_EQ(0U, result_.hypotheses.size()); } -} // namespace speech_input +} // namespace speech diff --git a/content/browser/speech/speech_recognizer_impl.cc b/content/browser/speech/speech_recognizer_impl.cc index 9d4ed1c..bbcfbd2 100644 --- a/content/browser/speech/speech_recognizer_impl.cc +++ b/content/browser/speech/speech_recognizer_impl.cc @@ -9,7 +9,7 @@ #include "content/browser/browser_main_loop.h" #include "content/public/browser/speech_recognizer_delegate.h" #include "content/public/browser/browser_thread.h" -#include "content/public/common/speech_input_result.h" +#include "content/public/common/speech_recognition_result.h" #include "net/url_request/url_request_context_getter.h" using content::BrowserMainLoop; @@ -61,12 +61,12 @@ SpeechRecognizer* SpeechRecognizer::Create( bool filter_profanities, const std::string& hardware_info, const std::string& origin_url) { - return new speech_input::SpeechRecognizerImpl( + return new speech::SpeechRecognizerImpl( delegate, caller_id, language, grammar, context_getter, filter_profanities, hardware_info, origin_url); } -namespace speech_input { +namespace speech { const int SpeechRecognizerImpl::kAudioSampleRate = 16000; const int SpeechRecognizerImpl::kAudioPacketIntervalMs = 100; @@ -208,7 +208,7 @@ void SpeechRecognizerImpl::HandleOnError(int error_code) { if (!audio_controller_.get()) return; - InformErrorAndCancelRecognition(content::SPEECH_INPUT_ERROR_AUDIO); + InformErrorAndCancelRecognition(content::SPEECH_RECOGNITION_ERROR_AUDIO); } void SpeechRecognizerImpl::OnData(AudioInputController* controller, @@ -272,7 +272,8 @@ void SpeechRecognizerImpl::HandleOnData(string* data) { bool speech_was_heard_after_packet = endpointer_.DidStartReceivingSpeech(); if (!speech_was_heard_after_packet && num_samples_recorded_ >= kNoSpeechTimeoutSec * kAudioSampleRate) { - InformErrorAndCancelRecognition(content::SPEECH_INPUT_ERROR_NO_SPEECH); + InformErrorAndCancelRecognition( + content::SPEECH_RECOGNITION_ERROR_NO_SPEECH); return; } @@ -303,8 +304,8 @@ void SpeechRecognizerImpl::HandleOnData(string* data) { } void SpeechRecognizerImpl::SetRecognitionResult( - const content::SpeechInputResult& result) { - if (result.error != content::SPEECH_INPUT_ERROR_NONE) { + const content::SpeechRecognitionResult& result) { + if (result.error != content::SPEECH_RECOGNITION_ERROR_NONE) { InformErrorAndCancelRecognition(result.error); return; } @@ -316,8 +317,8 @@ void SpeechRecognizerImpl::SetRecognitionResult( } void SpeechRecognizerImpl::InformErrorAndCancelRecognition( - content::SpeechInputError error) { - DCHECK_NE(error, content::SPEECH_INPUT_ERROR_NONE); + content::SpeechRecognitionErrorCode error) { + DCHECK_NE(error, content::SPEECH_RECOGNITION_ERROR_NONE); CancelRecognition(); // Guard against the delegate freeing us until we finish our job. @@ -343,4 +344,4 @@ void SpeechRecognizerImpl::SetAudioManagerForTesting( audio_manager_ = audio_manager; } -} // namespace speech_input +} // namespace speech diff --git a/content/browser/speech/speech_recognizer_impl.h b/content/browser/speech/speech_recognizer_impl.h index 0273ccb..25e0c0c 100644 --- a/content/browser/speech/speech_recognizer_impl.h +++ b/content/browser/speech/speech_recognizer_impl.h @@ -14,12 +14,12 @@ #include "content/browser/speech/endpointer/endpointer.h" #include "content/browser/speech/speech_recognition_request.h" #include "content/public/browser/speech_recognizer.h" -#include "content/public/common/speech_input_result.h" +#include "content/public/common/speech_recognition_result.h" #include "media/audio/audio_input_controller.h" class AudioManager; -namespace speech_input { +namespace speech { // Records audio, sends recorded audio to server and translates server response // to recognition result. @@ -47,8 +47,8 @@ class CONTENT_EXPORT SpeechRecognizerImpl void StopRecording(); // AudioInputController::EventHandler methods. - virtual void OnCreated(media::AudioInputController* controller) OVERRIDE { } - virtual void OnRecording(media::AudioInputController* controller) OVERRIDE { } + virtual void OnCreated(media::AudioInputController* controller) OVERRIDE {} + virtual void OnRecording(media::AudioInputController* controller) OVERRIDE {} virtual void OnError(media::AudioInputController* controller, int error_code) OVERRIDE; virtual void OnData(media::AudioInputController* controller, @@ -57,7 +57,7 @@ class CONTENT_EXPORT SpeechRecognizerImpl // SpeechRecognitionRequest::Delegate methods. virtual void SetRecognitionResult( - const content::SpeechInputResult& result) OVERRIDE; + const content::SpeechRecognitionResult& result) OVERRIDE; static const int kAudioSampleRate; static const int kAudioPacketIntervalMs; // Duration of each audio packet. @@ -69,7 +69,8 @@ class CONTENT_EXPORT SpeechRecognizerImpl private: friend class SpeechRecognizerTest; - void InformErrorAndCancelRecognition(content::SpeechInputError error); + void InformErrorAndCancelRecognition( + content::SpeechRecognitionErrorCode error); void SendRecordedAudioToServer(); void HandleOnError(int error_code); // Handles OnError in the IO thread. @@ -103,6 +104,6 @@ class CONTENT_EXPORT SpeechRecognizerImpl DISALLOW_COPY_AND_ASSIGN(SpeechRecognizerImpl); }; -} // namespace speech_input +} // namespace speech #endif // CONTENT_BROWSER_SPEECH_SPEECH_RECOGNIZER_IMPL_H_ diff --git a/content/browser/speech/speech_recognizer_impl_unittest.cc b/content/browser/speech/speech_recognizer_impl_unittest.cc index 7388914..cfd2c33 100644 --- a/content/browser/speech/speech_recognizer_impl_unittest.cc +++ b/content/browser/speech/speech_recognizer_impl_unittest.cc @@ -20,7 +20,7 @@ using media::AudioInputController; using media::TestAudioInputController; using media::TestAudioInputControllerFactory; -namespace speech_input { +namespace speech { class SpeechRecognizerTest : public content::SpeechRecognizerDelegate, public testing::Test { @@ -32,7 +32,7 @@ class SpeechRecognizerTest : public content::SpeechRecognizerDelegate, recognition_complete_(false), result_received_(false), audio_received_(false), - error_(content::SPEECH_INPUT_ERROR_NONE), + error_(content::SPEECH_RECOGNITION_ERROR_NONE), volume_(-1.0f) { recognizer_ = new SpeechRecognizerImpl( this, 1, std::string(), std::string(), NULL, false, std::string(), @@ -49,7 +49,7 @@ class SpeechRecognizerTest : public content::SpeechRecognizerDelegate, // Overridden from content::SpeechRecognizerDelegate: virtual void SetRecognitionResult( int caller_id, - const content::SpeechInputResult& result) OVERRIDE { + const content::SpeechRecognitionResult& result) OVERRIDE { result_received_ = true; } @@ -74,8 +74,8 @@ class SpeechRecognizerTest : public content::SpeechRecognizerDelegate, virtual void DidStopReceivingSpeech(int caller_id) OVERRIDE { } - virtual void OnRecognizerError(int caller_id, - content::SpeechInputError error) OVERRIDE { + virtual void OnRecognizerError( + int caller_id, content::SpeechRecognitionErrorCode error) OVERRIDE { error_ = error; } @@ -119,7 +119,7 @@ class SpeechRecognizerTest : public content::SpeechRecognizerDelegate, bool recognition_complete_; bool result_received_; bool audio_received_; - content::SpeechInputError error_; + content::SpeechRecognitionErrorCode error_; TestURLFetcherFactory url_fetcher_factory_; TestAudioInputControllerFactory audio_input_controller_factory_; std::vector audio_packet_; @@ -135,7 +135,7 @@ TEST_F(SpeechRecognizerTest, StopNoData) { EXPECT_FALSE(recognition_complete_); EXPECT_FALSE(result_received_); EXPECT_FALSE(audio_received_); - EXPECT_EQ(content::SPEECH_INPUT_ERROR_NONE, error_); + EXPECT_EQ(content::SPEECH_RECOGNITION_ERROR_NONE, error_); } TEST_F(SpeechRecognizerTest, CancelNoData) { @@ -147,7 +147,7 @@ TEST_F(SpeechRecognizerTest, CancelNoData) { EXPECT_TRUE(recognition_complete_); EXPECT_FALSE(result_received_); EXPECT_FALSE(audio_received_); - EXPECT_EQ(content::SPEECH_INPUT_ERROR_NONE, error_); + EXPECT_EQ(content::SPEECH_RECOGNITION_ERROR_NONE, error_); } TEST_F(SpeechRecognizerTest, StopWithData) { @@ -177,7 +177,7 @@ TEST_F(SpeechRecognizerTest, StopWithData) { EXPECT_TRUE(recording_complete_); EXPECT_FALSE(recognition_complete_); EXPECT_FALSE(result_received_); - EXPECT_EQ(content::SPEECH_INPUT_ERROR_NONE, error_); + EXPECT_EQ(content::SPEECH_RECOGNITION_ERROR_NONE, error_); // Issue the network callback to complete the process. TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0); @@ -194,7 +194,7 @@ TEST_F(SpeechRecognizerTest, StopWithData) { EXPECT_TRUE(recognition_complete_); EXPECT_TRUE(result_received_); - EXPECT_EQ(content::SPEECH_INPUT_ERROR_NONE, error_); + EXPECT_EQ(content::SPEECH_RECOGNITION_ERROR_NONE, error_); } TEST_F(SpeechRecognizerTest, CancelWithData) { @@ -213,7 +213,7 @@ TEST_F(SpeechRecognizerTest, CancelWithData) { EXPECT_FALSE(recording_complete_); EXPECT_FALSE(recognition_complete_); EXPECT_FALSE(result_received_); - EXPECT_EQ(content::SPEECH_INPUT_ERROR_NONE, error_); + EXPECT_EQ(content::SPEECH_RECOGNITION_ERROR_NONE, error_); } TEST_F(SpeechRecognizerTest, ConnectionError) { @@ -234,7 +234,7 @@ TEST_F(SpeechRecognizerTest, ConnectionError) { EXPECT_TRUE(recording_complete_); EXPECT_FALSE(recognition_complete_); EXPECT_FALSE(result_received_); - EXPECT_EQ(content::SPEECH_INPUT_ERROR_NONE, error_); + EXPECT_EQ(content::SPEECH_RECOGNITION_ERROR_NONE, error_); // Issue the network callback to complete the process. fetcher->set_url(fetcher->GetOriginalURL()); @@ -248,7 +248,7 @@ TEST_F(SpeechRecognizerTest, ConnectionError) { EXPECT_FALSE(recognition_complete_); EXPECT_FALSE(result_received_); - EXPECT_EQ(content::SPEECH_INPUT_ERROR_NETWORK, error_); + EXPECT_EQ(content::SPEECH_RECOGNITION_ERROR_NETWORK, error_); } TEST_F(SpeechRecognizerTest, ServerError) { @@ -269,7 +269,7 @@ TEST_F(SpeechRecognizerTest, ServerError) { EXPECT_TRUE(recording_complete_); EXPECT_FALSE(recognition_complete_); EXPECT_FALSE(result_received_); - EXPECT_EQ(content::SPEECH_INPUT_ERROR_NONE, error_); + EXPECT_EQ(content::SPEECH_RECOGNITION_ERROR_NONE, error_); // Issue the network callback to complete the process. fetcher->set_url(fetcher->GetOriginalURL()); @@ -282,7 +282,7 @@ TEST_F(SpeechRecognizerTest, ServerError) { EXPECT_FALSE(recognition_complete_); EXPECT_FALSE(result_received_); - EXPECT_EQ(content::SPEECH_INPUT_ERROR_NETWORK, error_); + EXPECT_EQ(content::SPEECH_RECOGNITION_ERROR_NETWORK, error_); } TEST_F(SpeechRecognizerTest, AudioControllerErrorNoData) { @@ -297,7 +297,7 @@ TEST_F(SpeechRecognizerTest, AudioControllerErrorNoData) { EXPECT_FALSE(recording_complete_); EXPECT_FALSE(recognition_complete_); EXPECT_FALSE(result_received_); - EXPECT_EQ(content::SPEECH_INPUT_ERROR_AUDIO, error_); + EXPECT_EQ(content::SPEECH_RECOGNITION_ERROR_AUDIO, error_); } TEST_F(SpeechRecognizerTest, AudioControllerErrorWithData) { @@ -316,7 +316,7 @@ TEST_F(SpeechRecognizerTest, AudioControllerErrorWithData) { EXPECT_FALSE(recording_complete_); EXPECT_FALSE(recognition_complete_); EXPECT_FALSE(result_received_); - EXPECT_EQ(content::SPEECH_INPUT_ERROR_AUDIO, error_); + EXPECT_EQ(content::SPEECH_RECOGNITION_ERROR_AUDIO, error_); } TEST_F(SpeechRecognizerTest, NoSpeechCallbackIssued) { @@ -341,7 +341,7 @@ TEST_F(SpeechRecognizerTest, NoSpeechCallbackIssued) { EXPECT_FALSE(recording_complete_); EXPECT_FALSE(recognition_complete_); EXPECT_FALSE(result_received_); - EXPECT_EQ(content::SPEECH_INPUT_ERROR_NO_SPEECH, error_); + EXPECT_EQ(content::SPEECH_RECOGNITION_ERROR_NO_SPEECH, error_); } TEST_F(SpeechRecognizerTest, NoSpeechCallbackNotIssued) { @@ -372,7 +372,7 @@ TEST_F(SpeechRecognizerTest, NoSpeechCallbackNotIssued) { } MessageLoop::current()->RunAllPending(); - EXPECT_EQ(content::SPEECH_INPUT_ERROR_NONE, error_); + EXPECT_EQ(content::SPEECH_RECOGNITION_ERROR_NONE, error_); EXPECT_TRUE(audio_received_); EXPECT_FALSE(recording_complete_); EXPECT_FALSE(recognition_complete_); @@ -415,10 +415,10 @@ TEST_F(SpeechRecognizerTest, SetInputVolumeCallback) { EXPECT_FLOAT_EQ(0.89926866f, volume_); EXPECT_FLOAT_EQ(0.75071919f, noise_volume_); - EXPECT_EQ(content::SPEECH_INPUT_ERROR_NONE, error_); + EXPECT_EQ(content::SPEECH_RECOGNITION_ERROR_NONE, error_); EXPECT_FALSE(recording_complete_); EXPECT_FALSE(recognition_complete_); recognizer_->CancelRecognition(); } -} // namespace speech_input +} // namespace speech -- cgit v1.1