1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/speech/speech_recognition_bubble_controller.h"
6
7#include "base/bind.h"
8#include "chrome/browser/tab_contents/tab_util.h"
9#include "content/public/browser/browser_thread.h"
10#include "content/public/browser/notification_registrar.h"
11#include "content/public/browser/notification_source.h"
12#include "content/public/browser/notification_types.h"
13#include "content/public/browser/render_process_host.h"
14#include "content/public/browser/render_view_host.h"
15#include "content/public/browser/web_contents.h"
16
17using content::BrowserThread;
18using content::WebContents;
19
20namespace {
21const int kInvalidSessionId = 0;
22}
23
24namespace speech {
25
26SpeechRecognitionBubbleController::SpeechRecognitionBubbleController(
27    Delegate* delegate)
28    : delegate_(delegate),
29      last_request_issued_(REQUEST_CLOSE),
30      current_bubble_session_id_(kInvalidSessionId),
31      current_bubble_render_process_id_(0),
32      current_bubble_render_view_id_(0) {
33}
34
35SpeechRecognitionBubbleController::~SpeechRecognitionBubbleController() {
36  DCHECK_EQ(kInvalidSessionId, current_bubble_session_id_);
37}
38
39void SpeechRecognitionBubbleController::CreateBubble(
40    int session_id,
41    int render_process_id,
42    int render_view_id,
43    const gfx::Rect& element_rect) {
44  {
45    base::AutoLock auto_lock(lock_);
46    current_bubble_session_id_ = session_id;
47    current_bubble_render_process_id_ = render_process_id;
48    current_bubble_render_view_id_ = render_view_id;
49  }
50
51  UIRequest request(REQUEST_CREATE);
52  request.render_process_id = render_process_id;
53  request.render_view_id = render_view_id;
54  request.element_rect = element_rect;
55  ProcessRequestInUiThread(request);
56}
57
58void SpeechRecognitionBubbleController::SetBubbleRecordingMode() {
59  ProcessRequestInUiThread(UIRequest(REQUEST_SET_RECORDING_MODE));
60}
61
62void SpeechRecognitionBubbleController::SetBubbleRecognizingMode() {
63  ProcessRequestInUiThread(UIRequest(REQUEST_SET_RECOGNIZING_MODE));
64}
65
66void SpeechRecognitionBubbleController::SetBubbleMessage(
67    const base::string16& text) {
68  UIRequest request(REQUEST_SET_MESSAGE);
69  request.message = text;
70  ProcessRequestInUiThread(request);
71}
72
73bool SpeechRecognitionBubbleController::IsShowingMessage() const {
74  return last_request_issued_ == REQUEST_SET_MESSAGE;
75}
76
77void SpeechRecognitionBubbleController::SetBubbleInputVolume(
78    float volume,
79    float noise_volume) {
80  UIRequest request(REQUEST_SET_INPUT_VOLUME);
81  request.volume = volume;
82  request.noise_volume = noise_volume;
83  ProcessRequestInUiThread(request);
84}
85
86void SpeechRecognitionBubbleController::CloseBubble() {
87  {
88    base::AutoLock auto_lock(lock_);
89    current_bubble_session_id_ = kInvalidSessionId;
90  }
91  ProcessRequestInUiThread(UIRequest(REQUEST_CLOSE));
92}
93
94void SpeechRecognitionBubbleController::CloseBubbleForRenderViewOnUIThread(
95    int render_process_id, int render_view_id) {
96  {
97    base::AutoLock auto_lock(lock_);
98    if (current_bubble_session_id_ == kInvalidSessionId ||
99        current_bubble_render_process_id_ != render_process_id ||
100        current_bubble_render_view_id_ != render_view_id) {
101      return;
102    }
103    current_bubble_session_id_ = kInvalidSessionId;
104  }
105  ProcessRequestInUiThread(UIRequest(REQUEST_CLOSE));
106}
107
108int SpeechRecognitionBubbleController::GetActiveSessionID() {
109  base::AutoLock auto_lock(lock_);
110  return current_bubble_session_id_;
111}
112
113void SpeechRecognitionBubbleController::InfoBubbleButtonClicked(
114    SpeechRecognitionBubble::Button button) {
115  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
116  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
117      base::Bind(
118          &SpeechRecognitionBubbleController::InvokeDelegateButtonClicked, this,
119          button));
120}
121
122void SpeechRecognitionBubbleController::InfoBubbleFocusChanged() {
123  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
124  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
125      base::Bind(&SpeechRecognitionBubbleController::InvokeDelegateFocusChanged,
126                 this));
127}
128
129void SpeechRecognitionBubbleController::InvokeDelegateButtonClicked(
130    SpeechRecognitionBubble::Button button) {
131  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
132  {
133    base::AutoLock auto_lock(lock_);
134    if (kInvalidSessionId == current_bubble_session_id_)
135      return;
136  }
137  delegate_->InfoBubbleButtonClicked(current_bubble_session_id_, button);
138}
139
140void SpeechRecognitionBubbleController::InvokeDelegateFocusChanged() {
141  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
142  {
143    base::AutoLock auto_lock(lock_);
144    if (kInvalidSessionId == current_bubble_session_id_)
145      return;
146  }
147  delegate_->InfoBubbleFocusChanged(current_bubble_session_id_);
148}
149
150void SpeechRecognitionBubbleController::ProcessRequestInUiThread(
151    const UIRequest& request) {
152  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
153    last_request_issued_ = request.type;
154    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
155        base::Bind(&SpeechRecognitionBubbleController::ProcessRequestInUiThread,
156                   this, request));
157    return;
158  }
159
160  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
161
162  // In the case of a tab closed or crashed, the bubble can have been destroyed
163  // earlier on the UI thread, while other tasks were being enqueued from the IO
164  // to the UI thread. Simply return in such cases.
165  if (request.type != REQUEST_CREATE && !bubble_.get())
166    return;
167
168  switch (request.type) {
169    case REQUEST_CREATE:
170      bubble_.reset(SpeechRecognitionBubble::Create(
171          request.render_process_id, request.render_view_id,
172          this, request.element_rect));
173
174      if (!bubble_.get()) {
175        // Could be null if tab or display rect were invalid.
176        // Simulate the cancel button being clicked to inform the delegate.
177        BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
178            base::Bind(
179                &SpeechRecognitionBubbleController::InvokeDelegateButtonClicked,
180                this, SpeechRecognitionBubble::BUTTON_CANCEL));
181        return;
182      }
183      bubble_->Show();
184      bubble_->SetWarmUpMode();
185      break;
186    case REQUEST_SET_RECORDING_MODE:
187      bubble_->SetRecordingMode();
188      break;
189    case REQUEST_SET_RECOGNIZING_MODE:
190      bubble_->SetRecognizingMode();
191      break;
192    case REQUEST_SET_MESSAGE:
193      bubble_->SetMessage(request.message);
194      break;
195    case REQUEST_SET_INPUT_VOLUME:
196      bubble_->SetInputVolume(request.volume, request.noise_volume);
197      break;
198    case REQUEST_CLOSE:
199      bubble_.reset();
200      break;
201    default:
202      NOTREACHED();
203      break;
204  }
205}
206
207SpeechRecognitionBubbleController::UIRequest::UIRequest(RequestType type_value)
208    : type(type_value),
209      volume(0.0F),
210      noise_volume(0.0F),
211      render_process_id(0),
212      render_view_id(0) {
213}
214
215SpeechRecognitionBubbleController::UIRequest::~UIRequest() {
216}
217
218}  // namespace speech
219