diff options
author | Andras Becsi <andras.becsi@digia.com> | 2014-03-18 13:16:26 +0100 |
---|---|---|
committer | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2014-03-20 15:55:39 +0100 |
commit | 3f0f86b0caed75241fa71c95a5d73bc0164348c5 (patch) | |
tree | 92b9fb00f2e9e90b0be2262093876d4f43b6cd13 /chromium/content/browser/speech | |
parent | e90d7c4b152c56919d963987e2503f9909a666d2 (diff) |
Update to new stable branch 1750
This also includes an updated ninja and chromium dependencies
needed on Windows.
Change-Id: Icd597d80ed3fa4425933c9f1334c3c2e31291c42
Reviewed-by: Zoltan Arvai <zarvai@inf.u-szeged.hu>
Reviewed-by: Zeno Albisser <zeno.albisser@digia.com>
Diffstat (limited to 'chromium/content/browser/speech')
17 files changed, 370 insertions, 61 deletions
diff --git a/chromium/content/browser/speech/OWNERS b/chromium/content/browser/speech/OWNERS index 2c6195ec94b..b38caa6f1ff 100644 --- a/chromium/content/browser/speech/OWNERS +++ b/chromium/content/browser/speech/OWNERS @@ -1,4 +1,3 @@ hans@chromium.org -primiano@chromium.org tommi@chromium.org xians@chromium.org diff --git a/chromium/content/browser/speech/google_one_shot_remote_engine.cc b/chromium/content/browser/speech/google_one_shot_remote_engine.cc index a421e79c228..575dd5fda6d 100644 --- a/chromium/content/browser/speech/google_one_shot_remote_engine.cc +++ b/chromium/content/browser/speech/google_one_shot_remote_engine.cc @@ -121,7 +121,7 @@ bool ParseServerResponse(const std::string& response_body, const base::DictionaryValue* hypothesis_value = static_cast<const base::DictionaryValue*>(hypothesis); - string16 utterance; + base::string16 utterance; if (!hypothesis_value->GetString(kUtteranceString, &utterance)) { LOG(WARNING) << "ParseServerResponse: Missing utterance value."; diff --git a/chromium/content/browser/speech/google_streaming_remote_engine.cc b/chromium/content/browser/speech/google_streaming_remote_engine.cc index beacb22be94..b6b68d17ced 100644 --- a/chromium/content/browser/speech/google_streaming_remote_engine.cc +++ b/chromium/content/browser/speech/google_streaming_remote_engine.cc @@ -35,10 +35,9 @@ const char kWebServiceBaseUrl[] = "https://www.google.com/speech-api/full-duplex/v1"; const char kDownstreamUrl[] = "/down?"; const char kUpstreamUrl[] = "/up?"; -const int kAudioPacketIntervalMs = 100; const AudioEncoder::Codec kDefaultAudioCodec = AudioEncoder::CODEC_FLAC; -// This mathces the maximum maxAlternatives value supported by the server. +// This matches the maximum maxAlternatives value supported by the server. const uint32 kMaxMaxAlternatives = 30; // TODO(hans): Remove this and other logging when we don't need it anymore. @@ -86,8 +85,9 @@ std::string GetAPIKey() { } // namespace -const int GoogleStreamingRemoteEngine::kUpstreamUrlFetcherIdForTests = 0; -const int GoogleStreamingRemoteEngine::kDownstreamUrlFetcherIdForTests = 1; +const int GoogleStreamingRemoteEngine::kAudioPacketIntervalMs = 100; +const int GoogleStreamingRemoteEngine::kUpstreamUrlFetcherIdForTesting = 0; +const int GoogleStreamingRemoteEngine::kDownstreamUrlFetcherIdForTesting = 1; const int GoogleStreamingRemoteEngine::kWebserviceStatusNoError = 0; const int GoogleStreamingRemoteEngine::kWebserviceStatusErrorNoMatch = 5; @@ -324,7 +324,8 @@ GoogleStreamingRemoteEngine::ConnectBothStreams(const FSMEventArgs&) { JoinString(downstream_args, '&')); downstream_fetcher_.reset(URLFetcher::Create( - kDownstreamUrlFetcherIdForTests, downstream_url, URLFetcher::GET, this)); + kDownstreamUrlFetcherIdForTesting, downstream_url, URLFetcher::GET, + this)); downstream_fetcher_->SetRequestContext(url_context_.get()); downstream_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES | @@ -363,7 +364,7 @@ GoogleStreamingRemoteEngine::ConnectBothStreams(const FSMEventArgs&) { JoinString(upstream_args, '&')); upstream_fetcher_.reset(URLFetcher::Create( - kUpstreamUrlFetcherIdForTests, upstream_url, URLFetcher::POST, this)); + kUpstreamUrlFetcherIdForTesting, upstream_url, URLFetcher::POST, this)); upstream_fetcher_->SetChunkedUpload(encoder_->mime_type()); upstream_fetcher_->SetRequestContext(url_context_.get()); upstream_fetcher_->SetReferrer(config_.origin_url); diff --git a/chromium/content/browser/speech/google_streaming_remote_engine.h b/chromium/content/browser/speech/google_streaming_remote_engine.h index 488971ba1a6..11afae00e6c 100644 --- a/chromium/content/browser/speech/google_streaming_remote_engine.h +++ b/chromium/content/browser/speech/google_streaming_remote_engine.h @@ -49,6 +49,13 @@ class CONTENT_EXPORT GoogleStreamingRemoteEngine public net::URLFetcherDelegate, public NON_EXPORTED_BASE(base::NonThreadSafe) { public: + // Duration of each audio packet. + static const int kAudioPacketIntervalMs; + + // IDs passed to URLFetcher::Create(). Used for testing. + static const int kUpstreamUrlFetcherIdForTesting; + static const int kDownstreamUrlFetcherIdForTesting; + explicit GoogleStreamingRemoteEngine(net::URLRequestContextGetter* context); virtual ~GoogleStreamingRemoteEngine(); @@ -67,12 +74,6 @@ class CONTENT_EXPORT GoogleStreamingRemoteEngine int64 current, int64 total) OVERRIDE; private: - friend class GoogleStreamingRemoteEngineTest; - - // IDs passed to URLFetcher::Create(). Used for testing. - static const int kUpstreamUrlFetcherIdForTests; - static const int kDownstreamUrlFetcherIdForTests; - // Response status codes from the speech recognition webservice. static const int kWebserviceStatusNoError; static const int kWebserviceStatusErrorNoMatch; diff --git a/chromium/content/browser/speech/google_streaming_remote_engine_unittest.cc b/chromium/content/browser/speech/google_streaming_remote_engine_unittest.cc index 397250631fb..dd0ef89cad9 100644 --- a/chromium/content/browser/speech/google_streaming_remote_engine_unittest.cc +++ b/chromium/content/browser/speech/google_streaming_remote_engine_unittest.cc @@ -337,12 +337,12 @@ void GoogleStreamingRemoteEngineTest::TearDown() { TestURLFetcher* GoogleStreamingRemoteEngineTest::GetUpstreamFetcher() { return url_fetcher_factory_.GetFetcherByID( - GoogleStreamingRemoteEngine::kUpstreamUrlFetcherIdForTests); + GoogleStreamingRemoteEngine::kUpstreamUrlFetcherIdForTesting); } TestURLFetcher* GoogleStreamingRemoteEngineTest::GetDownstreamFetcher() { return url_fetcher_factory_.GetFetcherByID( - GoogleStreamingRemoteEngine::kDownstreamUrlFetcherIdForTests); + GoogleStreamingRemoteEngine::kDownstreamUrlFetcherIdForTesting); } // Starts recognition on the engine, ensuring that both stream fetchers are diff --git a/chromium/content/browser/speech/input_tag_speech_browsertest.cc b/chromium/content/browser/speech/input_tag_speech_browsertest.cc index 0a1b2a9ca86..10c18785dd9 100644 --- a/chromium/content/browser/speech/input_tag_speech_browsertest.cc +++ b/chromium/content/browser/speech/input_tag_speech_browsertest.cc @@ -40,9 +40,9 @@ class InputTagSpeechBrowserTest : public ContentBrowserTest { GURL test_url = GetTestUrl("speech", filename); NavigateToURL(shell(), test_url); - WebKit::WebMouseEvent mouse_event; - mouse_event.type = WebKit::WebInputEvent::MouseDown; - mouse_event.button = WebKit::WebMouseEvent::ButtonLeft; + blink::WebMouseEvent mouse_event; + mouse_event.type = blink::WebInputEvent::MouseDown; + mouse_event.button = blink::WebMouseEvent::ButtonLeft; mouse_event.x = 0; mouse_event.y = 0; mouse_event.clickCount = 1; @@ -52,7 +52,7 @@ class InputTagSpeechBrowserTest : public ContentBrowserTest { NOTIFICATION_LOAD_STOP, Source<NavigationController>(&web_contents->GetController())); web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event); - mouse_event.type = WebKit::WebInputEvent::MouseUp; + mouse_event.type = blink::WebInputEvent::MouseUp; web_contents->GetRenderViewHost()->ForwardMouseEvent(mouse_event); fake_speech_recognition_manager_.WaitForRecognitionStarted(); @@ -79,7 +79,7 @@ class InputTagSpeechBrowserTest : public ContentBrowserTest { // Inject the fake manager factory so that the test result is returned to // the web page. - SpeechRecognitionManager::SetManagerForTests(speech_recognition_manager_); + SpeechRecognitionManager::SetManagerForTesting(speech_recognition_manager_); } virtual void TearDownInProcessBrowserTestFixture() OVERRIDE { diff --git a/chromium/content/browser/speech/input_tag_speech_dispatcher_host.cc b/chromium/content/browser/speech/input_tag_speech_dispatcher_host.cc index 4a3af54ef37..23169c78105 100644 --- a/chromium/content/browser/speech/input_tag_speech_dispatcher_host.cc +++ b/chromium/content/browser/speech/input_tag_speech_dispatcher_host.cc @@ -22,19 +22,27 @@ const uint32 kMaxHypothesesForSpeechInputTag = 6; namespace content { InputTagSpeechDispatcherHost::InputTagSpeechDispatcherHost( - bool guest, + bool is_guest, int render_process_id, net::URLRequestContextGetter* url_request_context_getter) - : guest_(guest), + : is_guest_(is_guest), render_process_id_(render_process_id), - url_request_context_getter_(url_request_context_getter) { + url_request_context_getter_(url_request_context_getter), + weak_factory_(this) { // Do not add any non-trivial initialization here, instead do it lazily when // required (e.g. see the method |SpeechRecognitionManager::GetInstance()|) or // add an Init() method. } InputTagSpeechDispatcherHost::~InputTagSpeechDispatcherHost() { - SpeechRecognitionManager::GetInstance()->AbortAllSessionsForListener(this); + SpeechRecognitionManager::GetInstance()->AbortAllSessionsForRenderProcess( + render_process_id_); +} + +base::WeakPtr<InputTagSpeechDispatcherHost> +InputTagSpeechDispatcherHost::AsWeakPtr() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + return weak_factory_.GetWeakPtr(); } bool InputTagSpeechDispatcherHost::OnMessageReceived( @@ -60,15 +68,19 @@ void InputTagSpeechDispatcherHost::OverrideThreadForMessage( *thread = BrowserThread::UI; } +void InputTagSpeechDispatcherHost::OnChannelClosing() { + weak_factory_.InvalidateWeakPtrs(); +} + void InputTagSpeechDispatcherHost::OnStartRecognition( - const InputTagSpeechHostMsg_StartRecognition_Params& params) { + const InputTagSpeechHostMsg_StartRecognition_Params& params) { InputTagSpeechHostMsg_StartRecognition_Params input_params(params); int render_process_id = render_process_id_; // The chrome layer is mostly oblivious to BrowserPlugin guests and so it // cannot correctly place the speech bubble relative to a guest. Thus, we // set up the speech recognition context relative to the embedder. - int guest_render_view_id = 0; - if (guest_) { + int guest_render_view_id = MSG_ROUTING_NONE; + if (is_guest_) { RenderViewHostImpl* render_view_host = RenderViewHostImpl::FromID(render_process_id_, params.render_view_id); WebContentsImpl* web_contents = static_cast<WebContentsImpl*>( @@ -108,6 +120,8 @@ void InputTagSpeechDispatcherHost::StartRecognitionOnIO( context.render_process_id = render_process_id; context.render_view_id = params.render_view_id; context.guest_render_view_id = guest_render_view_id; + // Keep context.embedder_render_process_id and context.embedder_render_view_id + // unset. context.request_id = params.request_id; context.element_rect = params.element_rect; @@ -121,7 +135,7 @@ void InputTagSpeechDispatcherHost::StartRecognitionOnIO( config.initial_context = context; config.url_request_context_getter = url_request_context_getter_.get(); config.filter_profanities = filter_profanities; - config.event_listener = this; + config.event_listener = AsWeakPtr(); int session_id = SpeechRecognitionManager::GetInstance()->CreateSession( config); @@ -162,8 +176,9 @@ void InputTagSpeechDispatcherHost::OnRecognitionResults( const SpeechRecognitionSessionContext& context = SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); - int render_view_id = context.guest_render_view_id ? - context.guest_render_view_id : context.render_view_id; + int render_view_id = + context.guest_render_view_id == MSG_ROUTING_NONE ? + context.render_view_id : context.guest_render_view_id; Send(new InputTagSpeechMsg_SetRecognitionResults( render_view_id, context.request_id, @@ -176,8 +191,9 @@ void InputTagSpeechDispatcherHost::OnAudioEnd(int session_id) { const SpeechRecognitionSessionContext& context = SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); - int render_view_id = context.guest_render_view_id ? - context.guest_render_view_id : context.render_view_id; + int render_view_id = + context.guest_render_view_id == MSG_ROUTING_NONE ? + context.render_view_id : context.guest_render_view_id; Send(new InputTagSpeechMsg_RecordingComplete(render_view_id, context.request_id)); DVLOG(1) << "InputTagSpeechDispatcherHost::OnAudioEnd exit"; @@ -187,8 +203,9 @@ void InputTagSpeechDispatcherHost::OnRecognitionEnd(int session_id) { DVLOG(1) << "InputTagSpeechDispatcherHost::OnRecognitionEnd enter"; const SpeechRecognitionSessionContext& context = SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); - int render_view_id = context.guest_render_view_id ? - context.guest_render_view_id : context.render_view_id; + int render_view_id = + context.guest_render_view_id == MSG_ROUTING_NONE ? + context.render_view_id : context.guest_render_view_id; Send(new InputTagSpeechMsg_RecognitionComplete(render_view_id, context.request_id)); DVLOG(1) << "InputTagSpeechDispatcherHost::OnRecognitionEnd exit"; diff --git a/chromium/content/browser/speech/input_tag_speech_dispatcher_host.h b/chromium/content/browser/speech/input_tag_speech_dispatcher_host.h index cb0bf3ba4f0..4d5eb60bf17 100644 --- a/chromium/content/browser/speech/input_tag_speech_dispatcher_host.h +++ b/chromium/content/browser/speech/input_tag_speech_dispatcher_host.h @@ -7,6 +7,7 @@ #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" #include "content/common/content_export.h" #include "content/public/browser/browser_message_filter.h" #include "content/public/browser/speech_recognition_event_listener.h" @@ -32,6 +33,8 @@ class CONTENT_EXPORT InputTagSpeechDispatcherHost int render_process_id, net::URLRequestContextGetter* url_request_context_getter); + base::WeakPtr<InputTagSpeechDispatcherHost> AsWeakPtr(); + // SpeechRecognitionEventListener methods. virtual void OnRecognitionStart(int session_id) OVERRIDE; virtual void OnAudioStart(int session_id) OVERRIDE; @@ -57,6 +60,8 @@ class CONTENT_EXPORT InputTagSpeechDispatcherHost const IPC::Message& message, BrowserThread::ID* thread) OVERRIDE; + virtual void OnChannelClosing() OVERRIDE; + private: virtual ~InputTagSpeechDispatcherHost(); @@ -66,15 +71,20 @@ class CONTENT_EXPORT InputTagSpeechDispatcherHost void OnStopRecording(int render_view_id, int request_id); void StartRecognitionOnIO( - int render_process_id, - int guest_render_view_id, + int embedder_render_process_id, + int embedder_render_view_id, const InputTagSpeechHostMsg_StartRecognition_Params& params, bool filter_profanities); - bool guest_; + bool is_guest_; int render_process_id_; scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_; + // Used for posting asynchronous tasks (on the IO thread) without worrying + // about this class being destroyed in the meanwhile (due to browser shutdown) + // since tasks pending on a destroyed WeakPtr are automatically discarded. + base::WeakPtrFactory<InputTagSpeechDispatcherHost> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(InputTagSpeechDispatcherHost); }; diff --git a/chromium/content/browser/speech/speech_recognition_browsertest.cc b/chromium/content/browser/speech/speech_recognition_browsertest.cc new file mode 100644 index 00000000000..82daf323ff3 --- /dev/null +++ b/chromium/content/browser/speech/speech_recognition_browsertest.cc @@ -0,0 +1,202 @@ +// Copyright 2013 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 <list> + +#include "base/bind.h" +#include "base/memory/scoped_ptr.h" +#include "base/run_loop.h" +#include "base/strings/utf_string_conversions.h" +#include "content/browser/speech/google_streaming_remote_engine.h" +#include "content/browser/speech/speech_recognition_manager_impl.h" +#include "content/browser/speech/speech_recognizer_impl.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/notification_types.h" +#include "content/public/browser/web_contents.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/test_utils.h" +#include "content/shell/browser/shell.h" +#include "content/test/content_browser_test.h" +#include "content/test/content_browser_test_utils.h" +#include "content/test/mock_google_streaming_server.h" +#include "media/audio/mock_audio_manager.h" +#include "media/audio/test_audio_input_controller_factory.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::RunLoop; + +namespace content { + +class SpeechRecognitionBrowserTest : + public ContentBrowserTest, + public MockGoogleStreamingServer::Delegate, + public media::TestAudioInputControllerDelegate { + public: + enum StreamingServerState { + kIdle, + kTestAudioControllerOpened, + kClientConnected, + kClientAudioUpload, + kClientAudioUploadComplete, + kTestAudioControllerClosed, + kClientDisconnected + }; + + // MockGoogleStreamingServerDelegate methods. + virtual void OnClientConnected() OVERRIDE { + ASSERT_EQ(kTestAudioControllerOpened, streaming_server_state_); + streaming_server_state_ = kClientConnected; + } + + virtual void OnClientAudioUpload() OVERRIDE { + if (streaming_server_state_ == kClientConnected) + streaming_server_state_ = kClientAudioUpload; + } + + virtual void OnClientAudioUploadComplete() OVERRIDE { + ASSERT_EQ(kTestAudioControllerClosed, streaming_server_state_); + streaming_server_state_ = kClientAudioUploadComplete; + } + + virtual void OnClientDisconnected() OVERRIDE { + ASSERT_EQ(kClientAudioUploadComplete, streaming_server_state_); + streaming_server_state_ = kClientDisconnected; + } + + // media::TestAudioInputControllerDelegate methods. + virtual void TestAudioControllerOpened( + media::TestAudioInputController* controller) OVERRIDE { + ASSERT_EQ(kIdle, streaming_server_state_); + streaming_server_state_ = kTestAudioControllerOpened; + const int capture_packet_interval_ms = + (1000 * controller->audio_parameters().frames_per_buffer()) / + controller->audio_parameters().sample_rate(); + ASSERT_EQ(GoogleStreamingRemoteEngine::kAudioPacketIntervalMs, + capture_packet_interval_ms); + FeedAudioController(500 /* ms */, /*noise=*/ false); + FeedAudioController(1000 /* ms */, /*noise=*/ true); + FeedAudioController(1000 /* ms */, /*noise=*/ false); + } + + virtual void TestAudioControllerClosed( + media::TestAudioInputController* controller) OVERRIDE { + ASSERT_EQ(kClientAudioUpload, streaming_server_state_); + streaming_server_state_ = kTestAudioControllerClosed; + mock_streaming_server_->MockGoogleStreamingServer::SimulateResult( + GetGoodSpeechResult()); + } + + // Helper methods used by test fixtures. + GURL GetTestUrlFromFragment(const std::string fragment) { + return GURL(GetTestUrl("speech", "web_speech_recognition.html").spec() + + "#" + fragment); + } + + std::string GetPageFragment() { + return shell()->web_contents()->GetURL().ref(); + } + + const StreamingServerState &streaming_server_state() { + return streaming_server_state_; + } + + protected: + // ContentBrowserTest methods. + virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { + test_audio_input_controller_factory_.set_delegate(this); + media::AudioInputController::set_factory_for_testing( + &test_audio_input_controller_factory_); + mock_streaming_server_.reset(new MockGoogleStreamingServer(this)); + streaming_server_state_ = kIdle; + } + + virtual void SetUpOnMainThread() OVERRIDE { + ASSERT_TRUE(SpeechRecognitionManagerImpl::GetInstance()); + SpeechRecognizerImpl::SetAudioManagerForTesting( + new media::MockAudioManager(BrowserThread::GetMessageLoopProxyForThread( + BrowserThread::IO))); + } + + virtual void TearDownOnMainThread() OVERRIDE { + SpeechRecognizerImpl::SetAudioManagerForTesting(NULL); + } + + virtual void TearDownInProcessBrowserTestFixture() OVERRIDE { + test_audio_input_controller_factory_.set_delegate(NULL); + mock_streaming_server_.reset(); + } + + private: + static void FeedSingleBufferToAudioController( + scoped_refptr<media::TestAudioInputController> controller, + size_t buffer_size, + bool fill_with_noise) { + DCHECK(controller.get()); + scoped_ptr<uint8[]> audio_buffer(new uint8[buffer_size]); + if (fill_with_noise) { + for (size_t i = 0; i < buffer_size; ++i) + audio_buffer[i] = static_cast<uint8>(127 * sin(i * 3.14F / + (16 * buffer_size))); + } else { + memset(audio_buffer.get(), 0, buffer_size); + } + controller->event_handler()->OnData(controller, + audio_buffer.get(), + buffer_size); + } + + void FeedAudioController(int duration_ms, bool feed_with_noise) { + media::TestAudioInputController* controller = + test_audio_input_controller_factory_.controller(); + ASSERT_TRUE(controller); + const media::AudioParameters& audio_params = controller->audio_parameters(); + const size_t buffer_size = audio_params.GetBytesPerBuffer(); + const int ms_per_buffer = audio_params.frames_per_buffer() * 1000 / + audio_params.sample_rate(); + // We can only simulate durations that are integer multiples of the + // buffer size. In this regard see + // SpeechRecognitionEngine::GetDesiredAudioChunkDurationMs(). + ASSERT_EQ(0, duration_ms % ms_per_buffer); + + const int n_buffers = duration_ms / ms_per_buffer; + for (int i = 0; i < n_buffers; ++i) { + base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + &FeedSingleBufferToAudioController, + scoped_refptr<media::TestAudioInputController>(controller), + buffer_size, + feed_with_noise)); + } + } + + SpeechRecognitionResult GetGoodSpeechResult() { + SpeechRecognitionResult result; + result.hypotheses.push_back(SpeechRecognitionHypothesis( + UTF8ToUTF16("Pictures of the moon"), 1.0F)); + return result; + } + + StreamingServerState streaming_server_state_; + scoped_ptr<MockGoogleStreamingServer> mock_streaming_server_; + media::TestAudioInputControllerFactory test_audio_input_controller_factory_; +}; + +// Simply loads the test page and checks if it was able to create a Speech +// Recognition object in JavaScript, to make sure the Web Speech API is enabled. +IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest, Precheck) { + NavigateToURLBlockUntilNavigationsComplete( + shell(), GetTestUrlFromFragment("precheck"), 2); + + EXPECT_EQ(kIdle, streaming_server_state()); + EXPECT_EQ("success", GetPageFragment()); +} + +IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest, OneShotRecognition) { + NavigateToURLBlockUntilNavigationsComplete( + shell(), GetTestUrlFromFragment("oneshot"), 2); + + EXPECT_EQ(kClientDisconnected, streaming_server_state()); + EXPECT_EQ("goodresult1", GetPageFragment()); +} + +} // namespace content diff --git a/chromium/content/browser/speech/speech_recognition_dispatcher_host.cc b/chromium/content/browser/speech/speech_recognition_dispatcher_host.cc index e506675c816..5b08dd2529f 100644 --- a/chromium/content/browser/speech/speech_recognition_dispatcher_host.cc +++ b/chromium/content/browser/speech/speech_recognition_dispatcher_host.cc @@ -7,7 +7,10 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/lazy_instance.h" +#include "content/browser/browser_plugin/browser_plugin_guest.h" +#include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/speech/speech_recognition_manager_impl.h" +#include "content/browser/web_contents/web_contents_impl.h" #include "content/common/speech_recognition_messages.h" #include "content/public/browser/speech_recognition_manager_delegate.h" #include "content/public/browser/speech_recognition_session_config.h" @@ -17,17 +20,26 @@ namespace content { SpeechRecognitionDispatcherHost::SpeechRecognitionDispatcherHost( + bool is_guest, int render_process_id, net::URLRequestContextGetter* context_getter) - : render_process_id_(render_process_id), - context_getter_(context_getter) { + : is_guest_(is_guest), + render_process_id_(render_process_id), + context_getter_(context_getter), + weak_factory_(this) { // Do not add any non-trivial initialization here, instead do it lazily when // required (e.g. see the method |SpeechRecognitionManager::GetInstance()|) or // add an Init() method. } SpeechRecognitionDispatcherHost::~SpeechRecognitionDispatcherHost() { - SpeechRecognitionManager::GetInstance()->AbortAllSessionsForListener(this); + SpeechRecognitionManager::GetInstance()->AbortAllSessionsForRenderProcess( + render_process_id_); +} + +base::WeakPtr<SpeechRecognitionDispatcherHost> +SpeechRecognitionDispatcherHost::AsWeakPtr() { + return weak_factory_.GetWeakPtr(); } bool SpeechRecognitionDispatcherHost::OnMessageReceived( @@ -53,8 +65,36 @@ void SpeechRecognitionDispatcherHost::OverrideThreadForMessage( *thread = BrowserThread::UI; } +void SpeechRecognitionDispatcherHost::OnChannelClosing() { + weak_factory_.InvalidateWeakPtrs(); +} + void SpeechRecognitionDispatcherHost::OnStartRequest( const SpeechRecognitionHostMsg_StartRequest_Params& params) { + SpeechRecognitionHostMsg_StartRequest_Params input_params(params); + + int embedder_render_process_id = 0; + int embedder_render_view_id = MSG_ROUTING_NONE; + if (is_guest_) { + // If the speech API request was from a guest, save the context of the + // embedder since we will use it to decide permission. + RenderViewHostImpl* render_view_host = + RenderViewHostImpl::FromID(render_process_id_, params.render_view_id); + WebContentsImpl* web_contents = static_cast<WebContentsImpl*>( + WebContents::FromRenderViewHost(render_view_host)); + BrowserPluginGuest* guest = web_contents->GetBrowserPluginGuest(); + + embedder_render_process_id = + guest->embedder_web_contents()->GetRenderProcessHost()->GetID(); + DCHECK_NE(embedder_render_process_id, 0); + embedder_render_view_id = + guest->embedder_web_contents()->GetRenderViewHost()->GetRoutingID(); + DCHECK_NE(embedder_render_view_id, MSG_ROUTING_NONE); + } + + // TODO(lazyboy): Check if filter_profanities should use |render_process_id| + // instead of |render_process_id_|. We are also using the same value in + // input_tag_dispatcher_host.cc bool filter_profanities = SpeechRecognitionManagerImpl::GetInstance() && SpeechRecognitionManagerImpl::GetInstance()->delegate() && @@ -65,16 +105,26 @@ void SpeechRecognitionDispatcherHost::OnStartRequest( BrowserThread::IO, FROM_HERE, base::Bind(&SpeechRecognitionDispatcherHost::OnStartRequestOnIO, - this, params, filter_profanities)); + this, + embedder_render_process_id, + embedder_render_view_id, + input_params, + filter_profanities)); } void SpeechRecognitionDispatcherHost::OnStartRequestOnIO( + int embedder_render_process_id, + int embedder_render_view_id, const SpeechRecognitionHostMsg_StartRequest_Params& params, bool filter_profanities) { SpeechRecognitionSessionContext context; context.context_name = params.origin_url; context.render_process_id = render_process_id_; context.render_view_id = params.render_view_id; + context.embedder_render_process_id = embedder_render_process_id; + context.embedder_render_view_id = embedder_render_view_id; + if (embedder_render_process_id) + context.guest_render_view_id = params.render_view_id; context.request_id = params.request_id; context.requested_by_page_element = false; @@ -89,7 +139,7 @@ void SpeechRecognitionDispatcherHost::OnStartRequestOnIO( config.filter_profanities = filter_profanities; config.continuous = params.continuous; config.interim_results = params.interim_results; - config.event_listener = this; + config.event_listener = AsWeakPtr(); int session_id = SpeechRecognitionManager::GetInstance()->CreateSession( config); diff --git a/chromium/content/browser/speech/speech_recognition_dispatcher_host.h b/chromium/content/browser/speech/speech_recognition_dispatcher_host.h index 1bc12a10580..e74447139a9 100644 --- a/chromium/content/browser/speech/speech_recognition_dispatcher_host.h +++ b/chromium/content/browser/speech/speech_recognition_dispatcher_host.h @@ -6,6 +6,7 @@ #define CONTENT_BROWSER_SPEECH_SPEECH_RECOGNITION_DISPATCHER_HOST_H_ #include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" #include "content/common/content_export.h" #include "content/public/browser/browser_message_filter.h" #include "content/public/browser/speech_recognition_event_listener.h" @@ -27,9 +28,12 @@ class CONTENT_EXPORT SpeechRecognitionDispatcherHost public SpeechRecognitionEventListener { public: SpeechRecognitionDispatcherHost( + bool is_guest, int render_process_id, net::URLRequestContextGetter* context_getter); + base::WeakPtr<SpeechRecognitionDispatcherHost> AsWeakPtr(); + // SpeechRecognitionEventListener methods. virtual void OnRecognitionStart(int session_id) OVERRIDE; virtual void OnAudioStart(int session_id) OVERRIDE; @@ -55,20 +59,30 @@ class CONTENT_EXPORT SpeechRecognitionDispatcherHost const IPC::Message& message, BrowserThread::ID* thread) OVERRIDE; + virtual void OnChannelClosing() OVERRIDE; + private: virtual ~SpeechRecognitionDispatcherHost(); void OnStartRequest( const SpeechRecognitionHostMsg_StartRequest_Params& params); void OnStartRequestOnIO( + int embedder_render_process_id, + int embedder_render_view_id, const SpeechRecognitionHostMsg_StartRequest_Params& params, bool filter_profanities); void OnAbortRequest(int render_view_id, int request_id); void OnStopCaptureRequest(int render_view_id, int request_id); + bool is_guest_; int render_process_id_; scoped_refptr<net::URLRequestContextGetter> context_getter_; + // Used for posting asynchronous tasks (on the IO thread) without worrying + // about this class being destroyed in the meanwhile (due to browser shutdown) + // since tasks pending on a destroyed WeakPtr are automatically discarded. + base::WeakPtrFactory<SpeechRecognitionDispatcherHost> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(SpeechRecognitionDispatcherHost); }; diff --git a/chromium/content/browser/speech/speech_recognition_manager_impl.cc b/chromium/content/browser/speech/speech_recognition_manager_impl.cc index 2c1b31735a9..c88cc9fca9f 100644 --- a/chromium/content/browser/speech/speech_recognition_manager_impl.cc +++ b/chromium/content/browser/speech/speech_recognition_manager_impl.cc @@ -51,7 +51,7 @@ SpeechRecognitionManager* SpeechRecognitionManager::GetInstance() { return SpeechRecognitionManagerImpl::GetInstance(); } -void SpeechRecognitionManager::SetManagerForTests( +void SpeechRecognitionManager::SetManagerForTesting( SpeechRecognitionManager* manager) { manager_for_tests_ = manager; } @@ -184,15 +184,20 @@ void SpeechRecognitionManagerImpl::RecognitionAllowedCallback(int session_id, if (!SessionExists(session_id)) return; + SessionsTable::iterator iter = sessions_.find(session_id); + DCHECK(iter != sessions_.end()); + Session* session = iter->second; + + if (session->abort_requested) + return; + if (ask_user) { - SessionsTable::iterator iter = sessions_.find(session_id); - DCHECK(iter != sessions_.end()); - SpeechRecognitionSessionContext& context = iter->second->context; + SpeechRecognitionSessionContext& context = session->context; context.label = media_stream_manager_->MakeMediaAccessRequest( context.render_process_id, context.render_view_id, context.request_id, - StreamOptions(MEDIA_DEVICE_AUDIO_CAPTURE, MEDIA_NO_SERVICE), + StreamOptions(true, false), GURL(context.context_name), base::Bind( &SpeechRecognitionManagerImpl::MediaRequestPermissionCallback, @@ -253,6 +258,11 @@ void SpeechRecognitionManagerImpl::AbortSession(int session_id) { SessionsTable::iterator iter = sessions_.find(session_id); iter->second->ui.reset(); + if (iter->second->abort_requested) + return; + + iter->second->abort_requested = true; + base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&SpeechRecognitionManagerImpl::DispatchEvent, @@ -439,8 +449,8 @@ SpeechRecognitionManagerImpl::GetSessionContext(int session_id) const { return GetSession(session_id)->context; } -void SpeechRecognitionManagerImpl::AbortAllSessionsForListener( - SpeechRecognitionEventListener* listener) { +void SpeechRecognitionManagerImpl::AbortAllSessionsForRenderProcess( + int render_process_id) { // This method gracefully destroys sessions for the listener. However, since // the listener itself is likely to be destroyed after this call, we avoid // dispatching further events to it, marking the |listener_is_active| flag. @@ -448,7 +458,7 @@ void SpeechRecognitionManagerImpl::AbortAllSessionsForListener( for (SessionsTable::iterator it = sessions_.begin(); it != sessions_.end(); ++it) { Session* session = it->second; - if (session->config.event_listener == listener) { + if (session->context.render_process_id == render_process_id) { AbortSession(session->id); session->listener_is_active = false; } @@ -609,6 +619,8 @@ void SpeechRecognitionManagerImpl::SessionDelete(Session* session) { DCHECK(session->recognizer.get() == NULL || !session->recognizer->IsActive()); if (primary_session_id_ == session->id) primary_session_id_ = kSessionIDInvalid; + if (!session->context.label.empty()) + media_stream_manager_->CancelRequest(session->context.label); sessions_.erase(session->id); delete session; } @@ -643,7 +655,9 @@ SpeechRecognitionManagerImpl::GetSession(int session_id) const { SpeechRecognitionEventListener* SpeechRecognitionManagerImpl::GetListener( int session_id) const { Session* session = GetSession(session_id); - return session->listener_is_active ? session->config.event_listener : NULL; + if (session->listener_is_active && session->config.event_listener) + return session->config.event_listener.get(); + return NULL; } SpeechRecognitionEventListener* @@ -660,7 +674,7 @@ bool SpeechRecognitionManagerImpl::HasAudioInputDevices() { return audio_manager_->HasAudioInputDevices(); } -string16 SpeechRecognitionManagerImpl::GetAudioInputDeviceModel() { +base::string16 SpeechRecognitionManagerImpl::GetAudioInputDeviceModel() { return audio_manager_->GetAudioInputDeviceModel(); } @@ -674,6 +688,7 @@ void SpeechRecognitionManagerImpl::ShowAudioInputSettings() { SpeechRecognitionManagerImpl::Session::Session() : id(kSessionIDInvalid), + abort_requested(false), listener_is_active(true) { } diff --git a/chromium/content/browser/speech/speech_recognition_manager_impl.h b/chromium/content/browser/speech/speech_recognition_manager_impl.h index 043257024cb..9a60e077437 100644 --- a/chromium/content/browser/speech/speech_recognition_manager_impl.h +++ b/chromium/content/browser/speech/speech_recognition_manager_impl.h @@ -62,8 +62,7 @@ class CONTENT_EXPORT SpeechRecognitionManagerImpl : const SpeechRecognitionSessionConfig& config) OVERRIDE; virtual void StartSession(int session_id) OVERRIDE; virtual void AbortSession(int session_id) OVERRIDE; - virtual void AbortAllSessionsForListener( - SpeechRecognitionEventListener* listener) OVERRIDE; + virtual void AbortAllSessionsForRenderProcess(int render_process_id) OVERRIDE; virtual void AbortAllSessionsForRenderView(int render_process_id, int render_view_id) OVERRIDE; virtual void StopAudioCaptureForSession(int session_id) OVERRIDE; @@ -75,7 +74,7 @@ class CONTENT_EXPORT SpeechRecognitionManagerImpl : int render_view_id, int request_id) const OVERRIDE; virtual bool HasAudioInputDevices() OVERRIDE; - virtual string16 GetAudioInputDeviceModel() OVERRIDE; + virtual base::string16 GetAudioInputDeviceModel() OVERRIDE; virtual void ShowAudioInputSettings() OVERRIDE; // SpeechRecognitionEventListener methods. @@ -127,6 +126,7 @@ class CONTENT_EXPORT SpeechRecognitionManagerImpl : ~Session(); int id; + bool abort_requested; bool listener_is_active; SpeechRecognitionSessionConfig config; SpeechRecognitionSessionContext context; diff --git a/chromium/content/browser/speech/speech_recognizer_impl.cc b/chromium/content/browser/speech/speech_recognizer_impl.cc index 2dae43087eb..ecd24ab9d87 100644 --- a/chromium/content/browser/speech/speech_recognizer_impl.cc +++ b/chromium/content/browser/speech/speech_recognizer_impl.cc @@ -802,7 +802,7 @@ void SpeechRecognizerImpl::UpdateSignalAndNoiseLevels(const float& rms, session_id(), clip_detected ? 1.0f : audio_level_, noise_level); } -void SpeechRecognizerImpl::SetAudioManagerForTests( +void SpeechRecognizerImpl::SetAudioManagerForTesting( AudioManager* audio_manager) { audio_manager_for_tests_ = audio_manager; } diff --git a/chromium/content/browser/speech/speech_recognizer_impl.h b/chromium/content/browser/speech/speech_recognizer_impl.h index 4945132deb7..c2abb339e65 100644 --- a/chromium/content/browser/speech/speech_recognizer_impl.h +++ b/chromium/content/browser/speech/speech_recognizer_impl.h @@ -37,7 +37,7 @@ class CONTENT_EXPORT SpeechRecognizerImpl static const int kNoSpeechTimeoutMs; static const int kEndpointerEstimationTimeMs; - static void SetAudioManagerForTests(media::AudioManager* audio_manager); + static void SetAudioManagerForTesting(media::AudioManager* audio_manager); SpeechRecognizerImpl(SpeechRecognitionEventListener* listener, int session_id, diff --git a/chromium/content/browser/speech/speech_recognizer_impl_android.cc b/chromium/content/browser/speech/speech_recognizer_impl_android.cc index eb3f7934b7e..2e8c57e87b2 100644 --- a/chromium/content/browser/speech/speech_recognizer_impl_android.cc +++ b/chromium/content/browser/speech/speech_recognizer_impl_android.cc @@ -55,7 +55,7 @@ void SpeechRecognizerImplAndroid::StartRecognitionOnUIThread( DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); JNIEnv* env = AttachCurrentThread(); j_recognition_.Reset(Java_SpeechRecognition_createSpeechRecognition(env, - GetApplicationContext(), reinterpret_cast<jint>(this))); + GetApplicationContext(), reinterpret_cast<intptr_t>(this))); Java_SpeechRecognition_startRecognition(env, j_recognition_.obj(), ConvertUTF8ToJavaString(env, language).obj(), continuous, interim_results); @@ -146,7 +146,7 @@ void SpeechRecognizerImplAndroid::OnAudioEnd(JNIEnv* env, jobject obj) { void SpeechRecognizerImplAndroid::OnRecognitionResults(JNIEnv* env, jobject obj, jobjectArray strings, jfloatArray floats, jboolean provisional) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - std::vector<string16> options; + std::vector<base::string16> options; AppendJavaStringArrayToStringVector(env, strings, &options); std::vector<float> scores(options.size(), 0.0); if (floats != NULL) diff --git a/chromium/content/browser/speech/speech_recognizer_impl_unittest.cc b/chromium/content/browser/speech/speech_recognizer_impl_unittest.cc index dcd4d5be538..54d970906da 100644 --- a/chromium/content/browser/speech/speech_recognizer_impl_unittest.cc +++ b/chromium/content/browser/speech/speech_recognizer_impl_unittest.cc @@ -59,7 +59,7 @@ class SpeechRecognizerImplTest : public SpeechRecognitionEventListener, this, kTestingSessionId, kOneShotMode, sr_engine); audio_manager_.reset(new media::MockAudioManager( base::MessageLoop::current()->message_loop_proxy().get())); - recognizer_->SetAudioManagerForTests(audio_manager_.get()); + recognizer_->SetAudioManagerForTesting(audio_manager_.get()); int audio_packet_length_bytes = (SpeechRecognizerImpl::kAudioSampleRate * |