summaryrefslogtreecommitdiffstats
path: root/chromium/content/browser/speech
diff options
context:
space:
mode:
authorAndras Becsi <andras.becsi@digia.com>2014-03-18 13:16:26 +0100
committerFrederik Gladhorn <frederik.gladhorn@digia.com>2014-03-20 15:55:39 +0100
commit3f0f86b0caed75241fa71c95a5d73bc0164348c5 (patch)
tree92b9fb00f2e9e90b0be2262093876d4f43b6cd13 /chromium/content/browser/speech
parente90d7c4b152c56919d963987e2503f9909a666d2 (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')
-rw-r--r--chromium/content/browser/speech/OWNERS1
-rw-r--r--chromium/content/browser/speech/google_one_shot_remote_engine.cc2
-rw-r--r--chromium/content/browser/speech/google_streaming_remote_engine.cc13
-rw-r--r--chromium/content/browser/speech/google_streaming_remote_engine.h13
-rw-r--r--chromium/content/browser/speech/google_streaming_remote_engine_unittest.cc4
-rw-r--r--chromium/content/browser/speech/input_tag_speech_browsertest.cc10
-rw-r--r--chromium/content/browser/speech/input_tag_speech_dispatcher_host.cc45
-rw-r--r--chromium/content/browser/speech/input_tag_speech_dispatcher_host.h16
-rw-r--r--chromium/content/browser/speech/speech_recognition_browsertest.cc202
-rw-r--r--chromium/content/browser/speech/speech_recognition_dispatcher_host.cc60
-rw-r--r--chromium/content/browser/speech/speech_recognition_dispatcher_host.h14
-rw-r--r--chromium/content/browser/speech/speech_recognition_manager_impl.cc35
-rw-r--r--chromium/content/browser/speech/speech_recognition_manager_impl.h6
-rw-r--r--chromium/content/browser/speech/speech_recognizer_impl.cc2
-rw-r--r--chromium/content/browser/speech/speech_recognizer_impl.h2
-rw-r--r--chromium/content/browser/speech/speech_recognizer_impl_android.cc4
-rw-r--r--chromium/content/browser/speech/speech_recognizer_impl_unittest.cc2
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 *