• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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_input_bubble_controller.h"
6 
7 #include "chrome/browser/tab_contents/tab_util.h"
8 #include "content/browser/browser_thread.h"
9 #include "content/browser/tab_contents/tab_contents.h"
10 #include "content/common/notification_registrar.h"
11 #include "content/common/notification_source.h"
12 #include "content/common/notification_type.h"
13 #include "ui/gfx/rect.h"
14 
15 namespace speech_input {
16 
SpeechInputBubbleController(Delegate * delegate)17 SpeechInputBubbleController::SpeechInputBubbleController(Delegate* delegate)
18     : delegate_(delegate),
19       current_bubble_caller_id_(0),
20       registrar_(new NotificationRegistrar) {
21 }
22 
~SpeechInputBubbleController()23 SpeechInputBubbleController::~SpeechInputBubbleController() {
24   DCHECK(bubbles_.empty());
25 }
26 
CreateBubble(int caller_id,int render_process_id,int render_view_id,const gfx::Rect & element_rect)27 void SpeechInputBubbleController::CreateBubble(int caller_id,
28                                                int render_process_id,
29                                                int render_view_id,
30                                                const gfx::Rect& element_rect) {
31   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
32     BrowserThread::PostTask(
33         BrowserThread::UI, FROM_HERE,
34         NewRunnableMethod(this, &SpeechInputBubbleController::CreateBubble,
35                           caller_id, render_process_id, render_view_id,
36                           element_rect));
37     return;
38   }
39   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
40   TabContents* tab_contents = tab_util::GetTabContentsByID(render_process_id,
41                                                            render_view_id);
42 
43   DCHECK_EQ(0u, bubbles_.count(caller_id));
44   SpeechInputBubble* bubble = SpeechInputBubble::Create(tab_contents, this,
45                                                         element_rect);
46   if (!bubble)  // could be null if tab or display rect were invalid.
47     return;
48 
49   bubbles_[caller_id] = bubble;
50 
51   UpdateTabContentsSubscription(caller_id, BUBBLE_ADDED);
52 }
53 
CloseBubble(int caller_id)54 void SpeechInputBubbleController::CloseBubble(int caller_id) {
55   ProcessRequestInUiThread(caller_id, REQUEST_CLOSE, string16(), 0, 0);
56 }
57 
SetBubbleWarmUpMode(int caller_id)58 void SpeechInputBubbleController::SetBubbleWarmUpMode(int caller_id) {
59   ProcessRequestInUiThread(caller_id, REQUEST_SET_WARM_UP_MODE,
60                            string16(), 0, 0);
61 }
62 
SetBubbleRecordingMode(int caller_id)63 void SpeechInputBubbleController::SetBubbleRecordingMode(int caller_id) {
64   ProcessRequestInUiThread(caller_id, REQUEST_SET_RECORDING_MODE,
65                            string16(), 0, 0);
66 }
67 
SetBubbleRecognizingMode(int caller_id)68 void SpeechInputBubbleController::SetBubbleRecognizingMode(int caller_id) {
69   ProcessRequestInUiThread(caller_id, REQUEST_SET_RECOGNIZING_MODE,
70                            string16(), 0, 0);
71 }
72 
SetBubbleInputVolume(int caller_id,float volume,float noise_volume)73 void SpeechInputBubbleController::SetBubbleInputVolume(int caller_id,
74                                                        float volume,
75                                                        float noise_volume) {
76   ProcessRequestInUiThread(caller_id, REQUEST_SET_INPUT_VOLUME, string16(),
77                            volume, noise_volume);
78 }
79 
SetBubbleMessage(int caller_id,const string16 & text)80 void SpeechInputBubbleController::SetBubbleMessage(int caller_id,
81                                                    const string16& text) {
82   ProcessRequestInUiThread(caller_id, REQUEST_SET_MESSAGE, text, 0, 0);
83 }
84 
UpdateTabContentsSubscription(int caller_id,ManageSubscriptionAction action)85 void SpeechInputBubbleController::UpdateTabContentsSubscription(
86     int caller_id, ManageSubscriptionAction action) {
87   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
88 
89   // If there are any other bubbles existing for the same TabContents, we would
90   // have subscribed to tab close notifications on their behalf and we need to
91   // stay registered. So we don't change the subscription in such cases.
92   TabContents* tab_contents = bubbles_[caller_id]->tab_contents();
93   for (BubbleCallerIdMap::iterator iter = bubbles_.begin();
94        iter != bubbles_.end(); ++iter) {
95     if (iter->second->tab_contents() == tab_contents &&
96         iter->first != caller_id) {
97       // At least one other bubble exists for the same TabContents. So don't
98       // make any change to the subscription.
99       return;
100     }
101   }
102 
103   if (action == BUBBLE_ADDED) {
104     registrar_->Add(this, NotificationType::TAB_CONTENTS_DESTROYED,
105                     Source<TabContents>(tab_contents));
106   } else {
107     registrar_->Remove(this, NotificationType::TAB_CONTENTS_DESTROYED,
108                     Source<TabContents>(tab_contents));
109   }
110 }
111 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)112 void SpeechInputBubbleController::Observe(NotificationType type,
113                                           const NotificationSource& source,
114                                           const NotificationDetails& details) {
115   if (type == NotificationType::TAB_CONTENTS_DESTROYED) {
116     // Cancel all bubbles and active recognition sessions for this tab.
117     TabContents* tab_contents = Source<TabContents>(source).ptr();
118     BubbleCallerIdMap::iterator iter = bubbles_.begin();
119     while (iter != bubbles_.end()) {
120       if (iter->second->tab_contents() == tab_contents) {
121         BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
122             NewRunnableMethod(
123                 this,
124                 &SpeechInputBubbleController::InvokeDelegateButtonClicked,
125                 iter->first, SpeechInputBubble::BUTTON_CANCEL));
126         CloseBubble(iter->first);
127         // We expect to have a very small number of items in this map so
128         // redo-ing from start is ok.
129         iter = bubbles_.begin();
130       } else {
131         ++iter;
132       }
133     }
134   } else {
135     NOTREACHED() << "Unknown notification";
136   }
137 }
138 
ProcessRequestInUiThread(int caller_id,RequestType type,const string16 & text,float volume,float noise_volume)139 void SpeechInputBubbleController::ProcessRequestInUiThread(
140     int caller_id, RequestType type, const string16& text, float volume,
141     float noise_volume) {
142   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
143     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, NewRunnableMethod(
144         this, &SpeechInputBubbleController::ProcessRequestInUiThread,
145         caller_id, type, text, volume, noise_volume));
146     return;
147   }
148   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
149   // The bubble may have been closed before we got a chance to process this
150   // request. So check before proceeding.
151   if (!bubbles_.count(caller_id))
152     return;
153 
154   bool change_active_bubble = (type == REQUEST_SET_WARM_UP_MODE ||
155                                type == REQUEST_SET_MESSAGE);
156   if (change_active_bubble) {
157     if (current_bubble_caller_id_ && current_bubble_caller_id_ != caller_id)
158       bubbles_[current_bubble_caller_id_]->Hide();
159     current_bubble_caller_id_ = caller_id;
160   }
161 
162   SpeechInputBubble* bubble = bubbles_[caller_id];
163   switch (type) {
164     case REQUEST_SET_WARM_UP_MODE:
165       bubble->SetWarmUpMode();
166       break;
167     case REQUEST_SET_RECORDING_MODE:
168       bubble->SetRecordingMode();
169       break;
170     case REQUEST_SET_RECOGNIZING_MODE:
171       bubble->SetRecognizingMode();
172       break;
173     case REQUEST_SET_MESSAGE:
174       bubble->SetMessage(text);
175       break;
176     case REQUEST_SET_INPUT_VOLUME:
177       bubble->SetInputVolume(volume, noise_volume);
178       break;
179     case REQUEST_CLOSE:
180       if (current_bubble_caller_id_ == caller_id)
181         current_bubble_caller_id_ = 0;
182       UpdateTabContentsSubscription(caller_id, BUBBLE_REMOVED);
183       delete bubble;
184       bubbles_.erase(caller_id);
185       break;
186     default:
187       NOTREACHED();
188       break;
189   }
190 
191   if (change_active_bubble)
192     bubble->Show();
193 }
194 
InfoBubbleButtonClicked(SpeechInputBubble::Button button)195 void SpeechInputBubbleController::InfoBubbleButtonClicked(
196     SpeechInputBubble::Button button) {
197   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
198   DCHECK(current_bubble_caller_id_);
199 
200   BrowserThread::PostTask(
201       BrowserThread::IO, FROM_HERE,
202       NewRunnableMethod(
203           this,
204           &SpeechInputBubbleController::InvokeDelegateButtonClicked,
205           current_bubble_caller_id_, button));
206 }
207 
InfoBubbleFocusChanged()208 void SpeechInputBubbleController::InfoBubbleFocusChanged() {
209   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
210   DCHECK(current_bubble_caller_id_);
211 
212   int old_bubble_caller_id = current_bubble_caller_id_;
213   current_bubble_caller_id_ = 0;
214 
215   BrowserThread::PostTask(
216       BrowserThread::IO, FROM_HERE,
217       NewRunnableMethod(
218           this,
219           &SpeechInputBubbleController::InvokeDelegateFocusChanged,
220           old_bubble_caller_id));
221 }
222 
InvokeDelegateButtonClicked(int caller_id,SpeechInputBubble::Button button)223 void SpeechInputBubbleController::InvokeDelegateButtonClicked(
224     int caller_id, SpeechInputBubble::Button button) {
225   delegate_->InfoBubbleButtonClicked(caller_id, button);
226 }
227 
InvokeDelegateFocusChanged(int caller_id)228 void SpeechInputBubbleController::InvokeDelegateFocusChanged(int caller_id) {
229   delegate_->InfoBubbleFocusChanged(caller_id);
230 }
231 
232 }  // namespace speech_input
233